Как мне запустить строку кода php из Python?

#php #python

#php #python

Вопрос:

Я обнаружил, что вы можете запустить php-файл из Python, используя это:

 import subprocess

proc = subprocess.Popen('php.exe input.php', shell=True, stdout=subprocess.PIPE)
response = proc.stdout.read().decode("utf-8")
print(response)
  

Но есть ли способ запустить php-код из строки, а не из файла? Например:

 <?php
  $a = ['a', 'b', 'c'][0];
  echo($a);
?>
  

Комментарии:

1. ДА. PHP может запускать код из stdin.

2. Можете ли вы продемонстрировать?

3. Проверьте -r --run флаг командной строки на php.net/manual/en/features.commandline.options.php

Ответ №1:

[ПРАВИТЬ]

Используйте php -r "code" с подпроцессом.Popen:

 def php(code):
    p = subprocess.Popen(["php", "-r", code],
                         stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    out = p.communicate() #returns a tuple (stdoutdata, stderrdata)
    if out[1] != b'': raise Exception(out[1].decode('UTF-8'))
    return out[0].decode('UTF-8')

code = """ 
  $a = ['a', 'b', 'c'][2]; 
  echo($a);"""
print(php(code))
  

[Оригинальный ответ]

Я нашел простой класс, который позволяет вам это сделать.
Код не требует пояснений. Класс содержит 3 метода:

  • get_raw(self, code): учитывая блок кода, вызовите код и верните необработанный результат в виде строки
  • get(self, code): учитывая блок кода, который выдает json, вызовите код и интерпретируйте результат как значение Python.
  • get_one(self, code): Учитывая блок кода, который выдает несколько значений json (по одному в строке), выдайте следующее значение.

Пример, который вы написали, будет выглядеть примерно так:

 php = PHP()
code = """ 
  $a = ['a', 'b', 'c'][0]; 
  echo($a);"""
print (php.get_raw(code))
  

Вы также можете добавить префикс и постфикс в код с помощью PHP(prefix="",postfix"")

PS.: Я изменил исходный класс, потому что popen2 устарел. Я также сделал код совместимым с Python 3. Вы можете получить это здесь :

 import json
import subprocess

class PHP:
    """This class provides a stupid simple interface to PHP code."""

    def __init__(self, prefix="", postfix=""):
        """prefix = optional prefix for all code (usually require statements)
        postfix = optional postfix for all code
        Semicolons are not added automatically, so you'll need to make sure to put them in!"""
        self.prefix = prefix
        self.postfix = postfix

    def __submit(self, code):
        code = self.prefix   code   self.postfix
        p = subprocess.Popen(["php","-r",code], shell=True,
                  stdin=subprocess.PIPE, stdout=subprocess.PIPE)
        (child_stdin, child_stdout) = (p.stdin, p.stdout)
        return child_stdout

    def get_raw(self, code):
        """Given a code block, invoke the code and return the raw result as a string."""
        out = self.__submit(code)
        return out.read()

    def get(self, code):
        """Given a code block that emits json, invoke the code and interpret the result as a Python value."""
        out = self.__submit(code)
        return json.loads(out.read())

    def get_one(self, code):
        """Given a code block that emits multiple json values (one per line), yield the next value."""
        out = self.__submit(code)
        for line in out:
            line = line.strip()
            if line:
                yield json.loads(line)
  

Комментарии:

1. Большое спасибо за ваш ответ. Однако я обнаружил две проблемы, но ни одна из них не является серьезной. При close_fds=True включении я получаю ошибку ValueError: close_fds is not supported on Windows platforms if you redirect stdin/stdout/stderr , но при ее удалении она работает нормально. Тогда я все равно получаю выходные данные в байтах с b в начале. Итак, я просто добавил .decode("utf-8") к инструкции print, как в моем первом примере. Если вы внесете эти изменения в ответ, это будет более полезно.

2. Кроме того, функции get и get_one в этом примере не нужны, поэтому их можно было бы удалить для краткости.

3. Из того, что я прочитал : «Обратите внимание, что в Windows вы не можете установить close_fds в значение true, а также перенаправить стандартные дескрипторы, установив stdin, stdout или stderr». Я понял, что у меня это работает, потому что я использую python 3.7 : «Изменено в версии 3.7: теперь можно установить close_fds в True при перенаправлении стандартных дескрипторов»

4. Есть ли какое-либо преимущество использования close_fds?

5. Я думаю, что лучше оставить close_fds и позволить подпроцессу решить, что лучше в соответствии с вашей ОС и версией python. Кроме того, читая документы, я мог бы отметить, что там рекомендуется использовать shell = False, чтобы избежать внедрения кода, и использовать метод communicate() вместо read() при использовании stdout=PIPE. stdin также не требуется, если вам не нужно отправлять потоковый ввод. Я отредактировал свой пост, включив в него гораздо более простую версию. Однако я добавил обработку исключений, которая была упрощена с помощью communicate()

Ответ №2:

Основываясь на ответе Виктора Вэла, вот моя собственная компактная версия.

 import subprocess

def run(code):
    p = subprocess.Popen(['php','-r',code], stdout=subprocess.PIPE)
    return p.stdout.read().decode('utf-8')

code = """ 
  $a = ['a', 'b', 'c'][0]; 
  echo($a);"""
print(run(code))