The "correct" system () for PHP-CLI
Attention! All considerations apply to running PHP in CLI mode, and not as a web server module!
If you have ever used the system () function in PHP, you probably wondered how system () returns the last line for the command, and even displays the results of the command on the web page, and not yourself somewhere in the stdout web -servers? And why does system () not work like system () in C? The answer, in general, is simpler than it might seem.
Actually, system () in PHP uses the same function as exec () ( int php_exec (int type, char * cmd, pval * array, pval * return_value TSRMLS_DC)), it just prints the result line by line, not accumulating it in an array, like exec (), but using only a string as a buffer. By the way, the documentation for the system () function hints at this behavior:
And php_exec basically uses popen () to execute the corresponding command, which means that there is a chance that the called program may not recognize STDIN as a terminal and give a response in a different format than when running through the shell.
If you just want to make sure that you get information as it arrives, without accumulating line by line, you can simply use passthru instead of system. This is enough for, say, git clone to display the progress of the operation correctly, but at the same time, ls will still output information in one column, since, in fact, passthru also uses php_exec, and therefore popen, with all resulting consequences
If you want, say, ls to display the same information as it does when running from the terminal, and also if you need color support, you can use proc_open:
In principle, the above methods should be enough for everything to work for you.
But if you need some special perversions, or you want to completely control what happens when you start the program, you can try to solve this problem with the old UNIX-way: through fork-exec (this will work only under * nix and provided presence of pcntl extension):
I hope this article will be useful to those who write scripts in PHP for the CLI and want to understand how to make system () work the way it should work.
If you have ever used the system () function in PHP, you probably wondered how system () returns the last line for the command, and even displays the results of the command on the web page, and not yourself somewhere in the stdout web -servers? And why does system () not work like system () in C? The answer, in general, is simpler than it might seem.
Actually, system () in PHP uses the same function as exec () ( int php_exec (int type, char * cmd, pval * array, pval * return_value TSRMLS_DC)), it just prints the result line by line, not accumulating it in an array, like exec (), but using only a string as a buffer. By the way, the documentation for the system () function hints at this behavior:
The system () call also tries to automatically flush the web server's output buffer after each line of output if PHP is running as a server module.
And php_exec basically uses popen () to execute the corresponding command, which means that there is a chance that the called program may not recognize STDIN as a terminal and give a response in a different format than when running through the shell.
Solving the problem of line-by-line buffering system ()
If you just want to make sure that you get information as it arrives, without accumulating line by line, you can simply use passthru instead of system. This is enough for, say, git clone to display the progress of the operation correctly, but at the same time, ls will still output information in one column, since, in fact, passthru also uses php_exec, and therefore popen, with all resulting consequences
Solution with proc_open ()
If you want, say, ls to display the same information as it does when running from the terminal, and also if you need color support, you can use proc_open:
$handle = proc_open("ls", array(0=>STDIN,1=>STDOUT,2=>STDERR), $pipes);
$retval = proc_close($handle);Unix way
In principle, the above methods should be enough for everything to work for you.
But if you need some special perversions, or you want to completely control what happens when you start the program, you can try to solve this problem with the old UNIX-way: through fork-exec (this will work only under * nix and provided presence of pcntl extension):
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);
}I hope this article will be useful to those who write scripts in PHP for the CLI and want to understand how to make system () work the way it should work.