Sometimes we need to invoke an external command (program) from within a PHP script. For example, we might need to unzip files using the gunzip command. This can be done easily by invoking the exec() or system() PHP functions. But if our command reads the standard input and writes to the standard output?
We need to execute an external command (program) that reads input from the standard input (stdin) and returns the result to the standard output (stdout). Optionally the command may return error information via standard error (stderr).
We will use the HTML Tidy console application as a sample. The application reads html from the standard input (stdin), performs some tidy operations on it and outputs the clean HTML to the standard output (stdout).
Our aim will be to create a tidyHtml() function:
<?php ob_start(); ?> <h1>Welcome</h1> <a href="">Click to go...</a> <?php // The $buffer variable will hold the content to be passed as stdin when HTML Tidy command is executed. $buffer = ob_get_clean(); // How the tidy command will be executed $tidyCommandLine = 'c:/tidy/tidy.exe -ashtml'; print tidyHtml($buffer, $tidyCommandLine); ?>
Looking at PHP documentation, we see that there is a popen() function that makes a good candidate, but it supports data transfer in only one direction (only one pipe).
There is another function proc_open() that requires more work, but does exactly what we need.
function tidyHtml( $input, $tidyCommandLine, $returnBodyOnly = true ) { $descriptorspec = array( 0 => array("pipe", "r"), // stdin is a pipe that the child will read from 1 => array("pipe", "w"), // stdout is a pipe that the child will write to 2 => array("pipe", "w"), // stdout is a pipe that the child will write to ); $process = proc_open($tidyCommandLine, $descriptorspec, $pipes); if ( ! is_resource($process)) return false; fwrite($pipes[0], $input); fclose($pipes[0]); $result = ''; while ( ! feof($pipes[1]) ) { $result .= fread($pipes[1], 4096); } $errors = ''; while ( ! feof($pipes[2]) ) { $errors .= fread($pipes[2], 4096); } fclose($pipes[1]); proc_close($process); if ( $returnBodyOnly ) { $result = preg_replace('!^.*<body>(.*)</body>.*$!s', '\\1', $result); } return $result; }
The proposed solution does exactly what we need:
fwrite() function.fread() function.fread() function (Although we discard the error result, we show you how you can get access to it).