php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #34972 STDIN won't allow nonblocking.
Submitted: 2005-10-24 23:07 UTC Modified: 2011-01-01 10:48 UTC
Votes:141
Avg. Score:4.7 ± 0.7
Reproduced:131 of 133 (98.5%)
Same Version:45 (34.4%)
Same OS:100 (76.3%)
From: VJTD3 at VJTD3 dot com Assigned: pajoye
Status: Assigned Package: Streams related
PHP Version: 5.2CVS-2008-07-15 OS: win32 only
Private report: No CVE-ID:
Have you experienced this issue?
Rate the importance of this bug to you:

 [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

Add a Patch

Pull Requests

Add a Pull Request

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.
 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Wed Apr 16 07:02:02 2014 UTC