|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[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.
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Sat Oct 25 16:00:02 2025 UTC |
<?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. ?>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.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.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.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.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.