php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #34972 STDIN should allow nonblocking
Submitted: 2005-10-24 23:07 UTC Modified: 2020-07-01 14:31 UTC
Votes:181
Avg. Score:4.7 ± 0.7
Reproduced:166 of 169 (98.2%)
Same Version:52 (31.3%)
Same OS:126 (75.9%)
From: VJTD3 at VJTD3 dot com Assigned:
Status: Open Package: Streams related
PHP Version: 5.2CVS-2008-07-15 OS: win32 only
Private report: No CVE-ID: None
 [2005-10-24 23:07 UTC] VJTD3 at VJTD3 dot com
Description:
------------
STDIN won't allow nonblocking.

Reproduce code:
---------------
<?php

 stream_set_blocking(STDIN, FALSE);
 while (1) {
  echo 'infinate loop showing nonblocking problem'."\n";
  echo 'said:'.fgets(STDIN, 1024);
 }

?>

Expected result:
----------------
The test code should be a infinate loop with "infinate loop showing nonblocking problem" said trillions of times fast like a infinate loop is suposto do when you make a boo boo. instead it will say it once then wait for input then wait again etc as a blocked resource is expected. but "stream_set_blocking(STDIN, FALSE);" should be allowing it to do the loop unblocked resulting in major screen flooding yet unblocking the resource is ignored.

This means you can't do something like a client program in your shell that is "interactive" like a Telnet client or similar.

I can dig the whole not reading as typing because that would be too "raw" for common usage... [ie phrasing the backspaces etc though maybe a seperate abaility? this would allow arrow key kinda stuff and similar...] But waiting for a "\n" or whatever file ending shouldn't have any effect on that loop. It should act just like a TCP or any other connection when unblocked there the resource just has a return of no data.

"php -f test.php" I use to run it.

Actual result:
--------------
uses blocking when "stream_set_blocking(STDIN, FALSE);" was used.

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2005-10-25 01:26 UTC] wez@php.net
FYI, using fgets() with a non-blocking resource makes no sense.

To implement what you're after, use stream_select() and fread(); these work on stdin just fine.
 [2005-10-25 04:23 UTC] VJTD3 at VJTD3 dot com
<?php

 # Make life easier to test bothe versions
 # option:
 #  1: Yahoo HTTP example
 #  2: STDIN example.
 $test = 1;

 # note for php4 or lower:
 #  STDIN isn't a defined "constant" so uncomment this.
 #define('STDIN', fopen('php://stdin', 'r'));

 # ment to paste fread was playing with other methods and
 #  just pasted the wrong version. The result is the same.

 # notice how after the data is done it's still going and
 #  not blocked.
 if($test == '1') {
  $demo = fsockopen('yahoo.com', 80);
  stream_set_blocking($demo, FALSE);
  fwrite($demo, 'GET /'."\n\n");
  while (1) {
   $data = fread($demo, 1500);
   echo (strlen($data) ? 'read:'.$data : "\nread:".'empty'."\n");
   sleep(1);
  }
 }

 # same code with fread and blocked reguardless of setting
 #  to nonblocked.
 if($test == '2') {
  echo 'Watch me stall till you type enter I\'m blocking you!'."\n";
  stream_set_blocking(STDIN, FALSE);
  while (1) {
   $data = fread(STDIN, 10);
   # I do know I could use trim, but if blocking was
   #  working it will act weird if it's mid of a end of
   #  line.
   $data = str_replace("\n", '', $data);
   $data = str_replace("\r", '', $data);
   echo (strlen($data) ? 'read:'.$data : "\nread:".'empty'."\n");
   sleep(1);
  }
 }

 # stream_select() can't be used, when i try the file
 #  descriptor just gets locks and all future selects won't
 #  work. and there is no reason to even select i know what
 #  resource to monitor i just need blocking to stop so i
 #  can monitor and process as i need just liek in the
 #  yahoo http example.

?>
 [2006-03-27 13:32 UTC] VJTD3 at VJTD3 dot com
bug #36030 is very similar to my bug, they might be related.

It looks like anything to STDIN (reguardless of access method like php://stdin and constant('STDIN') and STDIN are all effected.)

When doing anything, be it select, timeout, or anything to "monitor" the resource where a window is set (ie to timeout the blocking, set nonblocking, or similar.) the input "stalls" (it's not really a stall, it's just waiting for the input.) or fails (such as timeout setting that fails and returns false.)

Tested on:
php4-STABLE-200603270430
php5.1-200603270630
php6.0-200602191730

Also tested as well on their *nix snaps version and all with the same result. (blocking when they should not be blocking.)

Better code to make debugging easier and includes examples from suggestions that don't work:

<?php

 set_time_limit('0');

 # note for php4 or lower STDIN isn't a defined "constant".
 if (version_compare(phpversion(), '5.0.0', '<')) {
  define('STDIN', fopen('php://stdin', 'r'));
 }

 echo 'notice how the data loads unblocked reguardless if there is data.'."\n";
 $demo1 = @fsockopen('yahoo.com', 80, $errno, $errstr, 60);
 stream_set_blocking($demo1, FALSE);
 fwrite($demo1, 'GET /'."\n\n");
 for ($i=1;$i<11;$i++) {
  $data1 = @fread($demo1, 1500);
  $data1 = str_replace("\r\n", "\n", $data1);
  $data1 = str_replace("\n\r", "\n", $data1);
  $data1 = str_replace("\r", "\n", $data1);
  $data1 = str_replace("\n", '', $data1);
  echo 'pass: '.$i.' -> data: '.(strlen($data1) ? $data1 : 'empty')."\n";
  sleep('1');
 }

 echo 'notice how the data loads unblocked and clearly there is no data because this IP is invalid to connect to.'."\n";
 $demo2 = @fsockopen('0.0.0.0', 80, $errno, $errstr, 1);
 stream_set_blocking($demo2, FALSE);
 for ($i=1;$i<11;$i++) {
  # this is expected to be a invalid resource.
  $data2 = @fread($demo2, 1500);
  $data2 = str_replace("\r\n", "\n", $data2);
  $data2 = str_replace("\n\r", "\n", $data2);
  $data2 = str_replace("\r", "\n", $data2);
  $data2 = str_replace("\n", '', $data2);
  echo 'pass: '.$i.' -> data: '.(strlen($data2) ? $data2 : 'empty')."\n";
  sleep('1');
 }

 echo 'a person said to use select, this shows select won\'t work. (and shouldn\'t it\'s not a socket.) It won\'t even allow input during select calls.'."\n";
 for ($i=1;$i<11;$i++) {
  unset($read);
  $read[] = STDIN;
  socket_select($read, $write = null, $except = null, $tv = 0);
  print_r($read);
  sleep('1');
 }

 echo 'same code as yahoo and 0.0.0.0 with fread and blocked reguardless of setting this to nonblocking.'."\n";
 stream_set_blocking(STDIN, FALSE);
 for ($i=1;$i<11;$i++) {
  echo 'Watch me stall till you type enter I\'m blocking you!'."\n";
  $data3 = fread(STDIN, 10);
  $data3 = str_replace("\r\n", "\n", $data3);
  $data3 = str_replace("\n\r", "\n", $data3);
  $data3 = str_replace("\r", "\n", $data3);
  $data3 = str_replace("\n", '', $data3);
  echo 'pass: '.$i.' -> data: '.(strlen($data3) ? $data3 : 'empty')."\n";
  sleep('1');
 }

?>

anything I can do to help with details/make the bug easier to fix?
 [2006-03-27 14:12 UTC] VJTD3 at VJTD3 dot com
error in last message, "stream_select()" was ment.

code using "stream_select()" that still blocks:

<?php

 set_time_limit('0');

 # note for php4 or lower STDIN isn't a defined "constant".
 if (version_compare(phpversion(), '5.0.0', '<')) {
  define('STDIN', fopen('php://stdin', 'r'));
 }

 for ($i=1;$i<11;$i++) {
  unset($read);
  $read[] = STDIN;
  stream_select($read, $write = null, $except = null, $tv = 0);
  if (count($read)) {
   $data4 = @fread(STDIN, 1500);
   $data4 = str_replace("\r\n", "\n", $data4);
   $data4 = str_replace("\n\r", "\n", $data4);
   $data4 = str_replace("\r", "\n", $data4);
   $data4 = str_replace("\n", '', $data4);
  }
  echo 'pass: '.$i.' -> data: '.(strlen(@$data4) ? $data4 : 'empty')."\n";
  unset($data4);
  sleep('1');
 }

?>

the second you type/click a arrow key/anything it goes into blocking mode again.
 [2007-08-10 13:01 UTC] VJTD3 at VJTD3 dot com
<?php

 stream_set_blocking(STDIN, FALSE);
 echo fread(STDIN, 10);

?>

notice how it just sits there? (blocking) it should exit instantly with a null echo. (same as a tcp connection where if you just do a read that is too fast with nonblocking it never gets a chance to fetch the data.)

what is happening is that php is blocking waiting for input (specifically enter (line ending))

problem:
if you are echoing debug information and want to use key functionality in tandem you can't because everything is blocked till you hit enter.
 [2007-08-19 20:51 UTC] donnyk at gmail dot com
i also have the same problem, i've tried the latest snapshot on windows to no avail.  It works on my linux systems however.
 [2008-03-15 10:57 UTC] VJTD3 at VJTD3 dot com
on *nix it seems to be solved.

easy way to test:
php -r "stream_set_blocking(STDIN, FALSE);echo fread(STDIN, 10);"

on *nix it instantly exits. (correct behavior)
on windows it will hang for input. (incorrect behavior, it's ignoring the non blocking setting.)

tested on 5.2.5
 [2008-10-24 16:11 UTC] jani@php.net
Assigned to the windows port maintainer.
 [2009-04-16 17:48 UTC] frase at cs dot wisc dot edu
I'm having the same trouble.

With php-5.3.0-beta1 on Linux it works fine, both on STDIN and on fopen('/dev/ttyS0').  stream_set_blocking() returns true, stream_get_meta_data() confirms, and the stream behaves as if non-blocking.

But with php-5.2.9 on Windows 2000 it doesn't work for either STDIN or the serial port (COM1 instead of /dev/ttyS0).  stream_set_blocking() returns false, stream_get_meta_data() shows blocking, and the streams do in fact block.  I also tried stream_set_timeout() with a tiny value, to simulate the effect, but that also returns false and doesn't work.
 [2009-07-22 22:58 UTC] xektrum at gmail dot com
I can confirm this bug affects PHP 5.3.0. today at July 22 2009 almost 4 years from its submittion and still no fix.

This is very important since you can't create a really interactive CLI application with this issue, and stops developers from choosing PHP to theirs CLI/CMD/Console application (Although this could affect PHP-GTK users and developers)

Hope this fix soon

Regards
 [2009-11-14 15:31 UTC] raskin at aoeu dot ru
Confirmed on Nov 14th, 2009 with PHP 5.2.11 on WinXp
Yet no useful fix. Linux (Ubuntu 9.10 is just fine)
 [2010-02-26 01:47 UTC] joseph at none-yo-business dot com
Hope this helps someone else, but the workaround I had to do was to fread(stdin) from another script, output to a temp file, then use output in my other script. 

Yeah its ugly, but at least I have an interactive shell (or should that be shells :)

Dear PHP developers. I love php. Its the glue that holds everything I do together.... Please take this in the best way possible, WTF? This bug should not be there... And 4 years after the fact?! Please help me love PHP even more, and fix this bug.
 [2010-08-31 13:13 UTC] me at ben-xo dot com
Experiencing this issue on Win 32 with PHP 5.3.2. stream_set_blocking(STDIN, 0) 
('correctly') returns false and does not allow non-blocking. Also experiencing 
issues using stream_select() on STDIN. STDIN always comes back as ready for 
reading, but reading from it blocks.
 [2010-10-02 17:25 UTC] axellc dot perso at gmail dot com
Have same problem with 5.3.3 on windows (7) 
I still can't use my php5-cli shell applications on Windows :s
 [2010-12-24 10:14 UTC] lgynove at 163 dot com
5 years after the bug should not be there.
 [2011-01-01 10:33 UTC] support at aculane dot com
PHP 5.3 (5.3.4) VC9 x86 Thread Safe (2010-Dec-09 23:14:27) windows 7(64bit os)

Exact same problem.
 [2011-01-01 10:48 UTC] pajoye@php.net
@support at aculane dot com

what makes you think that anything has been done about this feature request?
 [2011-05-14 02:06 UTC] dewi at dewimorgan dot com
As I understand it (and some or all of this post may be wrong: please correct!), this is a platform limitation, since stuff like Windows' SetCommTimeouts() doesn't work for the stdin handle. I believe that this is because of the way pipes are handled under Windows, which makes them line-based rather than character-based. 

Any true "fix" (rather than just editing the docs) is a Hard Problem, since PHP has no control over Windows pipes.

The windows PHP port could internally do something nasty like:
if (ftell(STDIN) === 0) {
  // STDIN is the real STDIN, from a file or piped command.
}
else {
  // STDIN is a faked STDIN, built from keypresses.
}

This would fix the issue for those wanting interactive input, but the issue would still show up for pipes that aren't line separated:
ReplayKeyboardInput.exe | myScript.php

And even ignoring pipes, faking STDIN isn't trivial, either, but would probably involve a call to PeekConsoleInput().

I'm pretty sure if someone has some magic way for the receiving end of a pipe to make the pipe behave in a character-based way under Windows, then they'd happily implement it.

I'm considerably less convinced that they'd be willing to muddy PHP with a STDIN kludge that wouldn't work in some cases.

The NEED most people are expressing here is the need for interactive console input, via nonblocking keyboard reads, rather than the need for character-based pipes.

So, a "more correct" option would likely to be getting the ncurses extension working under Windows - the one platform that really needs it, and doesn't have it :) The public domain pdcurses.sourceforge.net may be useful here.

Workarounds:

As a workaround for PHP programmers on windows in the meantime, an external program that reads a single keypress can be used.

For people who don't mind blocking, this should work:
$KeyPressed = `perl -e "use Term::ReadKey;ReadMode 'cbreak'; print(ReadKey(0)); ReadMode 'normal';"`

To get nonblocking behaviour, where you can just read pending keystrokes, you'd need to launch that in a separate process, give it focus, and get it to send the characters to PHP whenever it read a character, and get your php file to poll the same file/socket for changes. 

If you don't have perl installed, a similar external program could likely be written in PowerShell, running as root and using $host.ui.rawui.KeyAvailable a WMI listener - but that's a crazy hoop to have to leap through.

Unless you have a really pressing need to write your app in PHP, then, odds are, it's easier to just write your script in a language which has functions for reading the keyboard directly (Perl, PowerShell, Python, AutoHotKey...). Or write a php readkey extension that launches a keyboard monitor in a separate thread or something, with start, stop, and flush functions for that key buffer.
 [2011-07-13 21:20 UTC] hans+php at hansdude dot com
I have a use case affected by this bug that others do not mention: parallel processing.

There is no way to spawn a bunch of processes and babysit them until they're done while still capturing output.

While the previous comment makes valid points, I hope someone will still try to find a solution to this.
 [2011-07-14 02:22 UTC] jchan at icebrg dot us
Please fix this. Interactive console applications written in PHP are impossible 
without a committed fix for this SIX YEARS old bug!

I use PHP for consistency across development environments, but this bug is making 
it impossible.

If a bounty can be placed, I'd like to place one! (Please show me a form)

Thanks!
 [2011-07-14 02:32 UTC] Dygear at gmail dot com
Yeah, I second the placing of a bounty on this problem, I'd also be more then 
willing to chip in to have this thing fixed as this still effects the usefulness 
of my software on the daily basis.
 [2015-02-27 07:16 UTC] jwbecher at gmail dot com
Is there any movement on this? Looks to be 10 years old, and last comment was 2011.
 [2016-04-19 15:24 UTC] jan dot bouvrie at gmail dot com
I'm also in the parallel processes boat, still no movement on this? Tested it again on 5.6.20, but no luck.

Perhaps related, my code running on Windows uses stream_select() on proc_open() file descriptors, even though the manual states: "Use of stream_select() on file descriptors returned by proc_open() will fail and return FALSE under Windows."

For kicks I tested the same code on Windows/PHP 7.0.5, and indeed the stream_select() returns FALSE there, just like the manual states.
 [2016-04-19 15:27 UTC] jan dot bouvrie at gmail dot com
Ignore my last comment, posted with the wrong bug. Dupes can be confusing. :)
 [2017-01-18 20:33 UTC] stahlstift at gmail dot com
I want to let a CLI script running until the user inputs "Enter" or CTRL+C. On *nix systems thats not a real problem because I can read async from STDIN or better use PCNTL to listen for SIGINT (CTRL+C).

On Windows there is now no solution... CTRL+C will kill the script like "kill -9" on *nix - so there is no shutdown handler called. A fallback solution could be reading non blocking from STDIN and wait for input but thats currently not possible.
 [2017-04-15 07:50 UTC] procman at interia dot pl
I have the same issue on Windows 7 (64) and PHP 7.0.13

There is no way to read from STDIN in non-blocking mode regardless of whether I run script in cmd or mingw32. I have tried every single solution I found, and finally hit here. Very sad because it is impossible to write simple, portable application in PHP.

This bug has 12 years, so please tell us whether it is at all possible to fix, or not (for example if it is matter of OS, not PHP internals).
 [2017-10-24 07:33 UTC] kalle@php.net
-Status: Assigned +Status: Open -Assigned To: pajoye +Assigned To:
 [2019-06-01 00:51 UTC] lstubbs at techno dot express
I'm using two windows systems:
Windows 10 using PHP 7.2.16 (cli) (built: Mar  6 2019 21:52:05) ( NTS MSVC15 (Visual C++ 2017) x64 )
Windows 7 using PHP 7.1.16 (cli) (built: Mar 28 2018 21:15:31) ( ZTS MSVC14 (Visual C++ 2015) x64 )

The ZTS builds blocks ONCE an key is press, otherwise non-blocking works.
The NTS builds blocks regardless an key is press or not, always blocking.

I know have PHP 7.2 ZTS builds on both systems, and STDIN non-blocking works until, i press an key, then blocks waiting for me to hit enter.

see https://github.com/symplely/coroutine/blob/master/examples/asyncWaitFor.php

It basically will run other routines until the user hits enter. 
On Linux numbers will scroll across until enter is press, pressing any key does not stop the other routine running, the routine actually produces the number printout.

On Windows will scroll the same, but stop on any key being pressed, the routines will pause waiting for enter key.
 [2020-07-01 14:30 UTC] cmb@php.net
-Summary: STDIN won't allow nonblocking. +Summary: STDIN should allow nonblocking
 [2020-07-01 14:30 UTC] cmb@php.net
Making stdio streams non-blocking is not (yet) supported on
Windows[1]; that is not a bug, though.

To implement it, one would have to implement asynchronous I/O[2]
on Windows.  A pull request would be welcome.

Also note that there is an open pull request for socket support
for proc_open()[3], which could be used as partial workaround for
this issue.

[1] <https://github.com/php/php-src/blob/php-7.4.7/main/streams/plain_wrapper.c#L662>
[2] <https://docs.microsoft.com/en-us/windows/win32/fileio/synchronous-and-asynchronous-i-o>
[3] <https://github.com/php/php-src/pull/5777>
 [2020-07-01 14:31 UTC] cmb@php.net
-Type: Bug +Type: Feature/Change Request
 [2023-12-12 02:17 UTC] dutra dot astro at gmail dot com
I wish I could see this feature working on Windows too. 
I started to create a terminal game, and I had to handle keypress with:


$stdin = fopen('php://stdin', 'rb+');

stream_set_blocking($stdin, 0);

while (1) {
    $keypress = fgets($stdin);
    if ($keypress) {
      echo 'Key pressed: ' . $keypress . PHP_EOL;
    }
  }
?>

and I realized it just not works. 
When someone implements this, it will open a whole new world of things possible to do with PHP. Thanks everyone.
 [2024-02-15 11:40 UTC] hani at gmail dot com
rtgfhfhfg
hghg
gfhhf
hfhgf
hfghf
 [2024-03-01 07:59 UTC] michael dot mckeever at bosch dot com
I agree with dutra astro, that this is one of the essential blockers to get PHP into the application landscape for frontend tasks despite the web. This is important for industry companies which focus on rock solid server stacks like WAMP and where the servers don't have internet access. So knowledge of languages like C, C++ and PHP is available. So PHP would be THE cli scripting language for a whole industry complex which are not using hip and hyped scripting languages python and JS (node).
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 08:01:29 2024 UTC