as 6.2 (instead of 6.3)). (Christian Wenz)
. Fixed bug #67633 (A foreach on an array returned from a function not doing
copy-on-write). (Nikita)
+ . Fixed bug #51800 (proc_open on Windows hangs forever). (Anatol)
- FPM:
. Fixed bug #65641 (PHP-FPM incorrectly defines the SCRIPT_NAME variable
--- /dev/null
+--TEST--
+Bug #51800 proc_open on Windows hangs forever
+--SKIPIF--
+<?php
+ if (getenv("SKIP_SLOW_TESTS")) {
+ die("skip slow test");
+ }
+?>
+--XFAIL--
+pipes have to be read/written simultaneously
+--FILE--
+<?php
+/* This is the wrong way to do it. The parent will block till it has read all the STDIN.
+The smaller the pipe buffer is, the longer it will take. It might even pass at the end,
+after taking inappropriately long. Pipes have to be read simultaneously in smaller chunks,
+so then the pipe buffer is emptied more often and the child has chance to continue its
+write. The behaviour might look some better if write/read in a separate thread, however
+this is much more resource greedy and complexer to integrate into the user script. */
+
+$callee = dirname(__FILE__) . "/process" . md5(uniqid()) . ".php";
+$php = PHP_BINARY;
+$cmd = "$php $callee";
+
+$status;
+$stdout = "";
+$stderr = "";
+$pipes = array();
+
+$descriptors = array(
+ 0 => array("pipe", "rb"), // stdin
+ 1 => array("pipe", "wb"), // stdout
+ 2 => array("pipe", "wb") // stderr
+ );
+
+/* create the proc file */
+$r = file_put_contents($callee, '<?php
+
+$how_much = 10000;
+
+$data0 = str_repeat("a", $how_much);
+$data1 = str_repeat("b", $how_much);
+fwrite(STDOUT, $data0);
+fwrite(STDERR, $data1);
+
+exit(0);
+');
+
+if (!$r) {
+ die("couldn't create helper script '$callee'");
+}
+
+$process = proc_open($cmd, $descriptors, $pipes);
+
+if (is_resource($process))
+{
+ fclose($pipes[0]);
+
+ while (!feof($pipes[1]))
+ $stdout .= fread($pipes[1], 1024);
+ fclose($pipes[1]);
+
+ while (!feof($pipes[2]))
+ $stderr .= fread($pipes[2], 1024);
+ fclose($pipes[2]);
+
+ $status = proc_close($process);
+}
+
+var_dump(array(
+ "status" => $status,
+ "stdout" => $stdout,
+ "stderr" => $stderr,
+), strlen($stdout), strlen($stderr));
+
+unlink($callee);
+
+?>
+===DONE===
+--EXPECTF--
+array(3) {
+ ["status"]=>
+ int(0)
+ ["stdout"]=>
+ string(10000) "a%s"
+ ["stderr"]=>
+ string(10000) "b%s"
+}
+int(10000)
+int(10000)
+===DONE===
+
--- /dev/null
+--TEST--
+Bug #51800 proc_open on Windows hangs forever, the right way to do it
+--FILE--
+<?php
+$callee = dirname(__FILE__) . "/process" . md5(uniqid()) . ".php";
+$php = PHP_BINARY;
+$cmd = "$php $callee";
+
+$status;
+$stdout = "";
+$stderr = "";
+$pipes = array();
+
+$descriptors = array(
+ 0 => array("pipe", "rb"), // stdin
+ 1 => array("pipe", "wb"), // stdout
+ 2 => array("pipe", "wb") // stderr
+ );
+
+/* create the proc file */
+$r = file_put_contents($callee, '<?php
+
+$how_much = 10000;
+
+$data0 = str_repeat("a", $how_much);
+$data1 = str_repeat("b", $how_much);
+fwrite(STDOUT, $data0);
+fwrite(STDERR, $data1);
+
+exit(0);
+');
+
+if (!$r) {
+ die("couldn't create helper script '$callee'");
+}
+
+$process = proc_open($cmd, $descriptors, $pipes);
+
+if (is_resource($process))
+{
+ fclose($pipes[0]);
+
+ while (!feof($pipes[1]) || !feof($pipes[2])) {
+ $stdout .= fread($pipes[1], 1024);
+ $stderr .= fread($pipes[2], 1024);
+ }
+ fclose($pipes[1]);
+ fclose($pipes[2]);
+
+ $status = proc_close($process);
+}
+
+var_dump(array(
+ "status" => $status,
+ "stdout" => $stdout,
+ "stderr" => $stderr,
+), strlen($stdout), strlen($stderr));
+
+unlink($callee);
+
+?>
+===DONE===
+--EXPECTF--
+array(3) {
+ ["status"]=>
+ int(0)
+ ["stdout"]=>
+ string(10000) "a%s"
+ ["stderr"]=>
+ string(10000) "b%s"
+}
+int(10000)
+int(10000)
+===DONE===
+
--- /dev/null
+--TEST--
+Bug #60120 proc_open hangs with stdin/out with 2048+ bytes
+--FILE--
+<?php
+error_reporting(E_ALL);
+
+$cmd = PHP_BINARY . ' -n -r "fwrite(STDOUT, $in = file_get_contents(\'php://stdin\')); fwrite(STDERR, $in);"';
+$descriptors = array(array('pipe', 'r'), array('pipe', 'w'), array('pipe', 'w'));
+$stdin = str_repeat('*', 1024 * 16) . '!';
+$stdin = str_repeat('*', 2049 );
+
+$options = array_merge(array('suppress_errors' => true, 'binary_pipes' => true, 'bypass_shell' => false));
+$process = proc_open($cmd, $descriptors, $pipes, getcwd(), array(), $options);
+
+foreach ($pipes as $pipe) {
+ stream_set_blocking($pipe, false);
+}
+$writePipes = array($pipes[0]);
+$stdinLen = strlen($stdin);
+$stdinOffset = 0;
+
+unset($pipes[0]);
+
+while ($pipes || $writePipes) {
+ $r = $pipes;
+ $w = $writePipes;
+ $e = null;
+ $n = stream_select($r, $w, $e, 60);
+
+ if (false === $n) {
+ break;
+ } elseif ($n === 0) {
+ proc_terminate($process);
+
+ }
+ if ($w) {
+ $written = fwrite($writePipes[0], (binary)substr($stdin, $stdinOffset), 8192);
+ if (false !== $written) {
+ $stdinOffset += $written;
+ }
+ if ($stdinOffset >= $stdinLen) {
+ fclose($writePipes[0]);
+ $writePipes = null;
+ }
+ }
+
+ foreach ($r as $pipe) {
+ $type = array_search($pipe, $pipes);
+ $data = fread($pipe, 8192);
+ var_dump($data);
+ if (false === $data || feof($pipe)) {
+ fclose($pipe);
+ unset($pipes[$type]);
+ }
+ }
+}
+
+
+?>
+===DONE===
+--EXPECTF--
+string(2049) "%s"
+string(2049) "%s"
+string(0) ""
+string(0) ""
+===DONE===
+
--- /dev/null
+--TEST--
+Bug #64438 proc_open hangs with stdin/out with 4097+ bytes
+--FILE--
+<?php
+
+error_reporting(E_ALL);
+
+$cmd = PHP_BINARY . ' -n -r "fwrite(STDOUT, $in = file_get_contents(\'php://stdin\')); fwrite(STDERR, $in);"';
+$descriptors = array(array('pipe', 'r'), array('pipe', 'w'), array('pipe', 'w'));
+$stdin = str_repeat('*', 4097);
+
+$options = array_merge(array('suppress_errors' => true, 'binary_pipes' => true, 'bypass_shell' => false));
+$process = proc_open($cmd, $descriptors, $pipes, getcwd(), array(), $options);
+
+foreach ($pipes as $pipe) {
+ stream_set_blocking($pipe, false);
+}
+$writePipes = array($pipes[0]);
+$stdinLen = strlen($stdin);
+$stdinOffset = 0;
+
+unset($pipes[0]);
+
+while ($pipes || $writePipes) {
+ $r = $pipes;
+ $w = $writePipes;
+ $e = null;
+ $n = stream_select($r, $w, $e, 60);
+
+ if (false === $n) {
+ break;
+ } elseif ($n === 0) {
+ proc_terminate($process);
+
+ }
+ if ($w) {
+ $written = fwrite($writePipes[0], (binary)substr($stdin, $stdinOffset), 8192);
+ if (false !== $written) {
+ $stdinOffset += $written;
+ }
+ if ($stdinOffset >= $stdinLen) {
+ fclose($writePipes[0]);
+ $writePipes = null;
+ }
+ }
+
+ foreach ($r as $pipe) {
+ $type = array_search($pipe, $pipes);
+ $data = fread($pipe, 8192);
+ var_dump($data);
+ if (false === $data || feof($pipe)) {
+ fclose($pipe);
+ unset($pipes[$type]);
+ }
+ }
+}
+
+?>
+===DONE===
+--EXPECTF--
+string(4097) "%s"
+string(4097) "%s"
+string(0) ""
+string(0) ""
+===DONE===
+
assert(data != NULL);
if (data->fd >= 0) {
+#ifdef PHP_WIN32
+ php_stdio_stream_data *self = (php_stdio_stream_data*)stream->abstract;
+
+ if (self->is_pipe || self->is_process_pipe) {
+ HANDLE ph = (HANDLE)_get_osfhandle(data->fd);
+ int retry = 0;
+ DWORD avail_read = 0;
+
+ do {
+ /* Look ahead to get the available data amount to read. Do the same
+ as read() does, however not blocking forever. In case it failed,
+ no data will be read (better than block). */
+ if (!PeekNamedPipe(ph, NULL, 0, NULL, &avail_read, NULL)) {
+ break;
+ }
+ /* If there's nothing to read, wait in 100ms periods. */
+ if (0 == avail_read) {
+ usleep(100000);
+ }
+ } while (0 == avail_read && retry++ < 180);
+
+ /* Reduce the required data amount to what is available, otherwise read()
+ will block.*/
+ if (avail_read < count) {
+ count = avail_read;
+ }
+ }
+#endif
ret = read(data->fd, buf, count);
if (ret == (size_t)-1 && errno == EINTR) {