What is stdin in php

writing command line scripts in php: part 2; reading STDIN

although not a popular choice, php can be a very effective language for writing command line scripts. it is feature-rich, and if you are longer on php skills than, say, bash, or if you have already-existing php code you would like to incorporate into a command line script, it can be an excellent choice. in this series of articles, we will be going through the various techniques and constructs that will help us build quality scripts in php. this installment focuses on reading from standard input.

previous installments

the flyover

we will be designing our example script here to read data piped into it from the STDIN stream. this feature requires us to do two things:

  • test if there is content in STDIN waiting to be read by our script
  • actually reading the STDIN input

if you are not familiar with linux data streams such as STDIN or STDOUT , it is a good idea to spend a few minutes reading up on them first.

reading piped input

command line scripts often take their input as piped in from STDIN . consider this short little pipeline that finds all the users on the system that use the fish shell instead of bash :

$ cat /etc/passwd | grep "bin/fish" gbhorwood:x:1000:1000:grant horwood. /home/gbhorwood:/usr/bin/fish 

the cat command dumps the contents of the file to STDOUT . normally, STDOUT goes to our terminal so we can read it, however in this example the | operator (pipe) traps the contents of STDOUT and uses it as input for the next command to the right. the pipe operator, essentially, ‘pipes’ output from the command on the left to the input for the command on the right. that’s why it’s called a ‘pipe’.

Читайте также:  How do you use javascript

this is a handy feature and one we want to implement in our php script. so let’s do that with this function, which we will add to ourfancyscript.php :

#!/usr/bin/env php  /** * Read contents piped in from STDIN stream * * @return String */ function read_piped_input()  $piped_input = null; while ($line = fgets(STDIN))  // noted STDIN here is not a string $piped_input .= $line; > return (string) $piped_input; > /** * Entry point */ $my_piped_in_content = read_piped_input(); print "piped input is:".PHP_EOL; print $my_piped_in_content; 

let’s look a that read_piped_input() function. the core functionlity here is using fgets to read from the STDIN pointer on a loop, line-by-line, until the content is exhausted. those lines are concatenated together, and returned. mission accomplished.

echo "this is our piped input" | ./ourfancyscript.php piped input is: this is our piped input 

testing for piped input

if we run ourfancyscript.php without any input on STDIN , it hangs. why? because it’s patiently waiting for input that never comes.

to solve this, we are going to write a function that tests whether or not there is any input on STDIN and only read from the pipe if it returns true.

/** * Test if there is input waiting on STDIN * * @return bool */ function test_piped_input()  $streams = [STDIN]; // note STDIN here is not a string $write_array = []; $except_array = []; $seconds = 0; // zero seconds on timeout since this is just for testing stream change $streamCount = @stream_select($streams, $write_array, $except_array, $seconds); return (boolean) $streamCount; > 

the key to this function is the stream_select command. stream_select basically waits for the state of a stream to change, timing out after $seconds have passed.

we pass to it STDIN as the only element of an array, since that’s the stream we’re interested in, and set the timeout $seconds to 0. we use zero seconds because STDIN input is present (or not) before we even run our script. there’s no sense waiting around for it; it’s either there or it isn’t.

if there is data piped in to our command, STDIN has, by definition, ‘changed’ and stream_select returns a non-zero number. we know we have data waiting for us! if there is no data, the stream is unchanged and the return is 0.

putting it together

now that we have test_piped_input() and read_piped_input() , we can put them together in our script:

/** * Entry point */ if (test_piped_input())  $my_stdin_content = read_piped_input(); print "piped input is:".PHP_EOL; print $my_stdin_content; > 

if we now run ourfancyscript.php without a piped-in stream, it proceeds. if we do pipe in data, it handles it.

let’s look at the full script now:

#!/usr/bin/env php  /** * Test if there is input waiting on STDIN * * @return bool */ function test_piped_input()  $streams = [STDIN]; // note STDIN here is not a string $write_array = []; $except_array = []; $seconds = 0; // zero seconds on timeout since this is just for testing stream change $streamCount = @stream_select($streams, $write_array, $except_array, $seconds); return (boolean) $streamCount; > /** * Read contents piped in from STDIN stream * * @return String */ function read_piped_input()  $piped_input = null; while ($line = fgets(STDIN))  $piped_input .= $line; > return (string) $piped_input; > /** * Entry point */ if (test_piped_input())  $my_stdin_content = read_piped_input(); print "piped input is:".PHP_EOL; print $my_stdin_content; > 

next steps

there’s still a lot more ground to cover in effectively geting input to our script. in future installments we will be looking at interactive input.

Источник

What is stdin in php

The CLI SAPI defines a few constants for I/O streams to make programming for the command line a bit easier.

An already opened stream to stdin . This saves opening it with

$line = trim ( fgets ( STDIN )); // reads one line from STDIN
fscanf ( STDIN , «%d\n» , $number ); // reads number from STDIN
?>

An already opened stream to stdout . This saves opening it with

An already opened stream to stderr . This saves opening it with

Given the above, you don’t need to open e.g. a stream for stderr yourself but simply use the constant instead of the stream resource:

php -r 'fwrite(STDERR, "stderr\n");'

You do not need to explicitly close these streams, as they are closed automatically by PHP when your script ends.

Note:

These constants are not available if reading the PHP script from stdin .

User Contributed Notes 5 notes

Please remember in multi-process applications (which are best suited under CLI), that I/O operations often will BLOCK signals from being processed.

For instance, if you have a parent waiting on fread(STDIN), it won’t handle SIGCHLD, even if you defined a signal handler for it, until after the call to fread has returned.

Your solution in this case is to wait on stream_select() to find out whether reading will block. Waiting on stream_select(), critically, does NOT BLOCK signals from being processed.

Under Linux CLI — STDIN, STDOUT and STDERR can be closed and reconnected to a different php stream such as a file, pipe or even a UDP socket_stream. (I use this technique to send the output/errors of my long running background scripts to a file so I can debug if something goes wrong.)

For example: (The below creates/appends file «/tmp/php_stdout.txt»)
// This only works under CLI in Linux
// Note: Until we have closed it STDOUT will NOT be prefixed with a $

// Get the path to the current console for STDOUT so we can reconnect later!
$strOldSTDOUT =( posix_ttyname ( STDOUT ));

echo( «This will go to the current console\r\n» );
// Close the STDOUT resource
fclose ( STDOUT );

// Reopen $STDOUT as a file Note: All further $STDOUT usage will be prefixed with a $
$STDOUT = fopen ( «/tmp/php_stdout.txt» , «a» ); /
echo( «This should append the file /tmp/php_stdout.txt\r\n» );
// Close stdout again so we can reconnect the console. Note: We are still using
fclose ( $STDOUT );

// Use the path to the console we got earlier
$STDOUT = fopen ( $strOldSTDOUT , «r+» );
echo( «And we are back on the console\r\n» );

The command line interface data in STDIN is not made available until return is pressed.
By adding «readline_callback_handler_install(», function()<>);» before reading STDIN for the first time single key presses can be captured.

Note: This only seems to work under Linux CLI and will not work in Apache or Windows CLI.

This cam be used to obscure a password or used with ‘stream_select’ to make a non blocking keyboard monitor.

// Demo WITHOUT readline_callback_handler_install(», function()<>);
$resSTDIN = fopen ( «php://stdin» , «r» );
echo( «Type ‘x’. Then press return.» );
$strChar = stream_get_contents ( $resSTDIN , 1 );

echo( «\nYou typed: » . $strChar . «\n\n» );
fclose ( $resSTDIN );

// Demo WITH readline_callback_handler_install(», function()<>);
// This line removes the wait for on STDIN
readline_callback_handler_install ( » , function()<>);

$resSTDIN = fopen ( «php://stdin» , «r» );
echo( «We have now run: readline_callback_handler_install(», function()<>);\n» );
echo( «Press the ‘y’ key» );
$strChar = stream_get_contents ( $resSTDIN , 1 );
echo( «\nYou pressed: » . $strChar . «\nBut did not have to press \n» );
fclose ( $resSTDIN );
readline_callback_handler_remove ();
echo( «\nGoodbye\n» )
?>

It also hides text from the CLI so can be used for things like. password obscurification.
eg

readline_callback_handler_install ( » , function()<>);
echo( «Enter password followed by return. (Do not use a real one!)\n» );
echo( «Password: » );
$strObscured = » ;
while( true )
$strChar = stream_get_contents ( STDIN , 1 );
if( $strChar === chr ( 10 ))
break;
>
$strObscured .= $strChar ;
echo( «*» );
>
echo( «\n» );
echo( «You entered: » . $strObscured . «\n» );
?>

The following code shows how to test for input on STDIN. In this case, we were looking for CSV data, so we use fgetcsv to read STDIN, if it creates an array, we assume CVS input on STDIN, if no array was created, we assume there’s no input from STDIN, and look, later, to an argument with a CSV file name.

Note, without the stream_set_blocking() call, fgetcsv() hangs on STDIN, awaiting input from the user, which isn’t useful as we’re looking for a piped file. If it isn’t here already, it isn’t going to be.

stream_set_blocking ( STDIN , 0 );
$csv_ar = fgetcsv ( STDIN );
if ( is_array ( $csv_ar )) print «CVS on STDIN\n» ;
> else print «Look to ARGV for CSV file name.\n» ;
>
?>

I find a BUG with the constant STDIN, I don’t know if it si the Enter/Return key that make this proprem, when I use trim(fgets(STDIN)), that doesn’t trim anything, when I detect the length of fgets(STDIN), in windows, it is 2 characters longer than what I input, in Linux, it makes 1. I tried to trim(fgets(STDIN), ‘ \r\n’), but it still does not work.
So I have to substr the input manually, it seems like this way:
$STDIN = trim ( substr ( fgets ( STDIN ), 0 , ( PHP_OS == ‘WINNT’ ? 2 : 1 )));
?>
then I get what I want really.

Источник

Оцените статью