php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #47918 stream_set_blocking() does not work with pipes opened with proc_open()
Submitted: 2009-04-07 12:31 UTC Modified: 2014-09-29 14:47 UTC
Votes:80
Avg. Score:4.2 ± 1.3
Reproduced:63 of 66 (95.5%)
Same Version:32 (50.8%)
Same OS:40 (63.5%)
From: RQuadling at GMail dot com Assigned:
Status: Wont fix Package: Streams related
PHP Version: 5.*, 6CVS (2009-06-19 OS: Windows
Private report: No CVE-ID: None
 [2009-04-07 12:31 UTC] RQuadling at GMail dot com
Description:
------------
I'm trying to set non-blocking mode on pipes attached to a process 
opened using proc_open.

The documentation and the user notes suggest that what I am doing 
should work.

Running this with php -n to remove my ini file settings.







Reproduce code:
---------------
<?php
echo PHP_VERSION, ' ', PHP_OS, ' ', PHP_SAPI, PHP_EOL;
echo 'INI:', php_ini_loaded_file(), PHP_EOL;

// Define the descriptors.
$a_Descriptors = array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w'));

// Provide a place for the pipes.
$a_Pipes = array();

// Create the thread.
$r_Thread = proc_open("dir c:\\ /b", $a_Descriptors, $a_Pipes, Null, $_ENV);

// Display the current STDOUT meta data.
print_r(stream_get_meta_data($a_Pipes[1]));

// Try to change the blocking mode to non-blocking.
echo (stream_set_blocking($a_Pipes[1], False) ? 'Successfully' : 'Failed'), ' to set blocking mode to non-blocking', PHP_EOL;

// Display the current STDOUT meta data.
print_r(stream_get_meta_data($a_Pipes[1]));



Expected result:
----------------
5.3.0RC2-dev WINNT cli
INI:
Array
(
    [stream_type] => STDIO
    [mode] => r
    [unread_bytes] => 0
    [seekable] =>
    [timed_out] =>
    [blocked] => 1
    [eof] =>
)
Successfully set blocking mode to non-blocking
Array
(
    [stream_type] => STDIO
    [mode] => r
    [unread_bytes] => 0
    [seekable] =>
    [timed_out] =>
    [blocked] =>
    [eof] =>
)

Actual result:
--------------
5.3.0RC2-dev WINNT cli
INI:
Array
(
    [stream_type] => STDIO
    [mode] => r
    [unread_bytes] => 0
    [seekable] =>
    [timed_out] =>
    [blocked] => 1
    [eof] =>
)
Failed to set blocking mode to non-blocking
Array
(
    [stream_type] => STDIO
    [mode] => r
    [unread_bytes] => 0
    [seekable] =>
    [timed_out] =>
    [blocked] => 1
    [eof] =>
)

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2009-04-07 21:50 UTC] RQuadling at GMail dot com
I tried both 0 and False to set non blocking mode. In all cases the 
function returns false and the meta data reports that the pipe is 
still in blocking mode.

I've just tried it with PHP 5.2.8 (cli) (built: Dec  8 2008 19:31:23) 
(on Windows XP SP3) on my home laptop and the same problem.
 [2009-05-08 09:58 UTC] RQuadling at GMail dot com
I also tried opening the pipes descriptors like ...

$a_Descriptors = array(0 => array('pipe', 'rn'), 1 => array('pipe', 
'wn'), 2 => array('pipe', 'wn'));

but no change.
 [2009-06-19 09:31 UTC] RQuadling at GMail dot com
Only worry about 5.3+
 [2009-06-19 11:12 UTC] jani@php.net
If it exists below 5.3, the version must state it! Do NOT touch it 
again.
 [2009-07-28 09:47 UTC] morrisdavidd at gmail dot com
I tested this on PHP 5.3 (on Linux) and duplicated the error that the print_r command shows blocking as true. HOWEVER, the stream does not block when actually tested... a simple test script can be created by adding a line and slightly modifying the scripts found at:

http://bugs.php.net/bug.php?id=49084
 [2009-08-22 05:51 UTC] nobodyy at mailinator dot com
Same problem here...

If we use proc_open and the output (pipes[1]) is greater than 2kb, 
the application just gets stuck!

I don't have any idea why, but it is never terminated, unless we do 
an FREAD of FGETS at the pipe[1].

But we can't do the FGETS if we don't know if there is content to be 
read. There is when stream_set_blocking should save the day.. But it 
doesn't work! :(
 [2009-08-24 08:59 UTC] RQuadling at GMail dot com
Essentially, non-blocking streams don't exist in win32 PHP.

They work fine for non-win32, so maybe this should become a feature 
request.

From reading the MSDN sites regarding non-blocking/blocking, it seems 
that a completely different mechanism for file handling needs to be 
used.

Other scripting languages have solved this issue in a different fashion, 
using an additional thread to process the stream.
 [2012-09-21 14:40 UTC] schmod at gmail dot com
Is this really true?

I can run a number of console applications via proc_open() without encountering 
blocking on win32.  

However, I have encountered a number of applications that will cause PHP hang on 
fread() until the process closes (regardless of whether or not the buffer has filled).

This needs to either be categorized as a bug, or the documentation needs to be updated 
to explain this unexpected behavior on win32.  

PHP 5.4.3/Win7x64.
 [2014-06-06 20:15 UTC] cxjohnson at gmail dot com
Still broken in 5.4+.

This fails in the same way on Mac OSX 10.9.3 with PHP 5.4.24, and on FreeBSD 9.2-RELEASE with PHP 5.4.25.
 [2014-06-06 20:26 UTC] cxjohnson at gmail dot com
Or more precisely, stream_get_meta_data() is broken.  It reports the mode as blocked, yet a read on the pipe while after stream_set_blocking($pipe, 0) does NOT block, as would be expected.
 [2014-09-29 14:47 UTC] ab@php.net
-Status: Open +Status: Wont fix
 [2014-09-29 14:47 UTC] ab@php.net
I have to disappoint here, the anonymous pipes are plain file descriptors, they don't support asynchronous mode on Windows. The following page sheds more light on this:

http://msdn.microsoft.com/en-us/library/windows/desktop/aa365141%28v=vs.85%29.aspx

Because of them being file descriptors, they're also not supported by select() which requires SOCKETS.

Please take in account the solution brought by bug #51800 . Pipe descriptors MUST be read simultaneously. In this regard, bug #63922 and bug #44908 can be closed as well as they regard to the Windows APIs behaviors which cannot be changed.

Thanks.
 [2016-11-08 18:03 UTC] webmaster_20161108 at cubiclesoft dot com
There is now a workaround for this issue on Windows for all versions of PHP (and other scripting languages with similar problems).

https://github.com/cubiclesoft/createprocess-windows

Before calling proc_open(), start a TCP/IP server in your PHP userland code.  Then pass the IP address and port number to 'createprocess.exe' using the /socketip and /socketport options and use the /w /stdin=socket /stdout=socket /stderr=socket options as well.  Three TCP/IP connections will be established from 'createprocess.exe' and the first byte of data on each stream indicates the file descriptor associated with the stream (0x00 = stdin, 0x01 = stdout, 0x02 = stderr).  The sockets are passed to the final process via the standard handles that 'createprocess.exe' then runs.  The /w option waits for the target process to complete before closing the TCP/IP sockets and exiting.

After each TCP/IP connection has been established and the first byte of data has been received, you can enable non-blocking mode and then proceed as if they were normal, non-blocking pipes on other OSes.

Read the following StackOverflow post if you want to learn more about how this works/is possible:

http://stackoverflow.com/questions/4993119/redirect-io-of-process-to-windows-socket

Tip:  Use "tcp://127.0.0.1:0" in the call to stream_socket_server() to get a random port number assigned to the listening socket (the :0 part) and basically guarantee that the call won't fail.  Then use stream_socket_get_name($fp, false) to retrieve the 'IP:port' associated with the socket to retrieve the port number that was assigned by the OS.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Oct 04 05:01:27 2024 UTC