«Правильная» Система() Для Php-Cli

Внимание! Все обсуждения относятся к запуску PHP в режиме CLI, а не в качестве модуля веб-сервера! Если вы когда-либо использовали функцию system() в PHP, вы, вероятно, задавались вопросом: как system() возвращает последнюю строку команды, а также выводит результаты выполнения команды на веб-страницу, а не куда-нибудь в стандартный веб-интерфейс? -серверы? И почему system() работает иначе, чем system() в C? Ответ, в общем-то, проще, чем может показаться.

Фактически, system() в PHP использует ту же функцию, что и exec() ( int php_exec (тип int, char *cmd, pval *array, pval *return_value TSRMLS_DC) ), он просто выводит результат построчно, не накапливая его в массив, как exec(), а используя в качестве буфера только строку.

Кстати, на такое поведение намекает документация к функции system():

Вызов system() также пытается автоматически очистить выходной буфер веб-сервера после каждой строки вывода, если PHP работает как серверный модуль.

А php_exec по сути использует popen() для выполнения соответствующей команды, а это означает, что существует вероятность того, что вызываемая программа может не распознать STDIN как терминал и выдать ответ в другом формате, чем при запуске через оболочку.



Решение проблемы с буферизацией строк system()

Если вы просто хотите, чтобы информация отображалась по мере ее поступления, а не накапливалась построчно, вы можете просто использовать passthru вместо system. Этого достаточно, чтобы, скажем, git clone корректно отображал ход выполнения операции, но при этом ls всё равно будет отображать информацию в одном столбце, так как, по сути, passthru тоже использует php_exec, а значит popen, со всеми вытекающие последствия

Вариант решения с proc_open()

Если вы хотите, скажем, ls выводить информацию так же, как при запуске из терминала, а также если вам нужна поддержка цвета, вы можете использовать proc_open:
  
   

$handle = proc_open("ls", array(0=>STDIN,1=>STDOUT,2=>STDERR), $pipes); $retval = proc_close($handle);



UNIX-путь

В принципе, вышеописанных методов должно быть достаточно, чтобы все у вас заработало.

Но если вам нужны какие-то особые извращения, или вы хотите полностью контролировать то, что происходит при запуске программы, то можно попробовать решить эту проблему старым UNIX-путем: через fork-exec (это будет работать только под *nix и при наличии расширение pcntl):

function libcSystem($cmd) { $pid = pcntl_fork(); if($pid < 0) { return -1; } if($pid == 0) { pcntl_exec('/bin/sh', array('-c', $cmd), $_ENV); exit(127); } pcntl_waitpid($pid, $status); return pcntl_wexitstatus($status); }

Я надеюсь, что эта статья будет полезна тем, кто пишет PHP-скрипты для CLI и хочет понять, как заставить system() работать так, как должно работать в теории.

Теги: #php #system #system #C++ #passthru #proc_exec #php

Вместе с данным постом часто просматривают: