Redirecting I/O in PHP - php

<?php
fclose(STDIN);
fclose(STDOUT);
fclose(STDERR);
$STDIN = fopen("/tmp/some-named-pipe", "r");
$STDOUT = fopen("/tmp/foo.out", "wb");
$STDERR = fopen("/tmp/foo.err", "wb");
echo "Hello, World!"; // goes to /tmp/foo.out, implied STDOUT
fscanf($STDIN, "%s\n", $top_secret); // explicit $STDIN, cant use STDIN
?>
Why is it that the redirection to the new STDOUT works implicitly, yet the redirection from the new STDIN must happen explicitly?

The original STDIN, STDOUT and STDERR are system constants in PHP. They are populated with the file descriptor resources of the standard input, output and error upon initialization of PHP.
The php.net documentation describes the following regarding resources in constants:
The value of the constant; only scalar and null values are allowed.
Scalar values are integer, float, string or boolean values. It is
possible to define resource constants, however it is not recommended
and may cause unpredictable behavior.
When fclose(STDOUT) is called, the file descriptor resource is closed and detached from the STDOUT constant. Contrary to the default behavior of constants, the STDOUT constant is changed.
When a string is echo'ed like echo "Hello, World!", PHP will not use the STDOUT constant, but it will query the current "standard out" file descriptor in real-time from the operating system. Moreover, the STDOUT constant itself is rendered unusable once it is fclose'd. Even a statement like fwrite(STDOUT, "hello") will not work.
When a new file descriptor is configured for the standard output, this new file descriptor is conveniently put in $STDOUT. Note that this is a variable and not a constant. It is by definition not possible to redefine a constant in PHP, and the system constant STDOUT is no exception here. There is currently no way to reassign the new file descriptor to the STDOUT constant.
Ultimately, to make this work more intuitively, the PHP team should consider making convenience functions to reassign these file descriptors or at least make the system constants operate more like "magic constants" in a sense that they auto evaluate to the actual file descriptors.

This is an example for redirecting stdout several times.
The close of STDOUT works for the first time, but for the next time
the close of $STDOUT is needed.
<?php
# Redirecting 10 times ...
# Need to close 'STDOUT' & '$STDOUT', otherwise the 2nd time
# the redirecting fails.
for ( $i = 1; $i <= 10; $i++ )
{
$sLogName = sprintf("reopen_%02d.log", $i);
echo "Redirecting to '" . $sLogName . "' ...\n";
fclose(STDOUT);
fclose($STDOUT); # Needed for reopening after the first time.
$STDOUT = fopen($sLogName, "w");
echo "Now logging in file '" . $sLogName . "' ...\n";
}
?>

This is happening only in UNIX-like systems (linux, etc), because when you open a file unix use the minor file descriptor, so in a process STDIN, STDOUT and STDERR are always 0,1 and 2.
When you do this:
fclose(STDIN);
fclose(STDOUT);
fclose(STDERR);
$STDIN = fopen("/tmp/some-named-pipe", "r");
$STDOUT = fopen("/tmp/foo.out", "wb");
$STDERR = fopen("/tmp/foo.err", "wb");
you are closing STDIN (0), STDOUT (1) and STDERR(2), so when you open your files in the same order they get 0,1 and 2 as file descriptor. If you run the same code on windows it will not work.

What I see first when reading your snippet is that you first try to close the constants of STDIN, STDOUT etc. But then use variables which have the same name to open files; therefore the values you're working with are completely different, $STDIN is not the same as STDIN. You might be able to redefine STDIN using the define function, but I'm not sure and unable to check without a computer atm..

Related

Script reading from STDIN

I have a php script that reads text files. I use fgetc() to get every character one by one. I open file to read from with fopen(),and then I use file descriptor returned from fopen() as a first argument to fgetc(). I tried to do the same thing with reading from STDIN. I wanted to run the script in a terminal, give it the whole text (that was in a text file before) and press enter. I thought that the script would read it and will run as if it read from a text file, but it doesn't work. It only works when a type every single character alone and press enter after it. Why is that? Is there a possibility to make the script behave the way I wanted? That I can give it the whole text to the terminal at once? Should I use different functions or something?
$inputFile = fopen($path, "w");
while(($char = fgetc($inputFile)) !== false){
dosomething();
}
What I'm trying to do is to replace $inputFile in fgetc()with STDIN.
See http://php.net/manual/en/features.commandline.io-streams.php, second comment
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.
<?php
stream_set_blocking(STDIN, 0);
$csv_ar = fgetcsv(STDIN);
I think it's the same for fgetc. After all it
string fgetc ( resource $handle ) Gets a character from the given file pointer.
Emphasis mine.
See http://php.net/manual/en/function.fgetc.php
...

PHP: pipe and passthru to mix and match external linux program (with STDIN and STDOUT) as components

I am a PHP beginner. I want to invoke an external Unix command, pipe some stuff into it (e.g., string and files), and have the result appear in my output buffer (the browser).
Consider the following:
echo '<h1>stuff for my browser output window</h1>';
$fnmphp= '/tmp/someinputfile';
$sendtoprogram = "myheader: $fnmphp\n\n".get_file_contents($fnmphp);
popen2outputbuf("unixprogram < $sendtoprogram");
echo '<p>done</p>';
An even better solution would let PHP write myheader (into Unix program), then pipe the file $fnmphp (into Unix program); and the output of unixprogram would immediately go to my browser output buffer.
I don't think PHP uses stdout, so that my Unix program STDOUT output would make it into the browser. Otherwise, this would happen to default if I used system(). I can only think of solutions that require writing tempfiles.
I think I am standing on the line here (German idiom; wires crossed)--- this probably has an obvious solution.
update:
here is the entirely inelegant but pretty precise solution that I want to replace:
function pipe2eqb( $text ) {
$whatever= '/tmp/whatever-'.time().'-'.$_SESSION['uid'];
$inf = "$whatever.in";
$outf= "$whatever.out";
assert(!file_exists($inf));
assert(!file_exists($outf));
file_put_contents($inf, $text);
assert(file_exists($inf));
system("unixprog < $inf > $outf");
$fo= file_get_contents($outf);
unlink($infilename);
unlink($outfilename);
return $fo;
}
It is easy to replace either the input or the output, but I want to replace both. I will post a solution when I figure it out.
the best to do this is the proc_open family of functions
<?php
$descriptorspec = array(
0 => array("pipe", "r"), // stdin
1 => array("pipe", "w"), // stdout
2 => array("pipe", "w") // stderr
);
$cwd = NULL;//'/tmp';
$env = NULL;//array();
$cmd='unixprog ';
$process = proc_open($cmd, $descriptorspec, $pipes, $cwd, $env);
assert(false!==$process);
now, to give arguments to unixprog, do like
$cmd='unixprog --arg1='.escapeshellarg($arg1).' --arg2='.escapeshellarg($arg2);
to talk to the program's stdin, do like
assert(strlen($stdinmessage)===fwrite($pipes[0],$stdinmessage));
to read from the process's stdout, do like
$stdout=file_get_contents($pipes[$1])
to read from the process's stderr, do like
$stderr=file_get_contents($pipes[$2])
to check if the program has finished, do like
$status=proc_get_status($process);
if($status['running']){echo 'child process is still running.';}
to check the return code of the process when it has finished,
echo 'return code from child process: '.$status['exitcode'];
to wait for the child process to finish, you CAN do
while(proc_get_status($process)['running']){sleep(1);}
this is a quick and easy way to do it, but it is not optimal. tl;dr: it may be slow or waste cpu. long version:
there is some nigh-optimal event-driven way to do this, but im not sure how to do it. imagine having to run a program 10 times, but the program execute in 100 milliseconds. this code would use 10 seconds! while optimal code would use only 1 second. you can use usleep() for microseconds, but its still not optimal, imagine if you're checking every 100 microseconds, but the program use 10 seconds to execute: you would waste cpu, checking the status 100,000 times, while optimal code would only check it once.. im sure there is a fancy way to let php sleep until the process finishes with some callback/signal, perhaps with stream_select , but i've yet to solve it. (if anybody have the solution, please let me know!)
-- read more at http://php.net/manual/en/book.exec.php

Execute shell in php using system

I have a problem in using system(), passthru() and exec(), the example below will describe it better, I have a string x that I will use it as argument for system():
$x = ' second_string' .$array[$i];
system("cat $x "); // not working
system("cat ".$x); // not working
system("cat { $x }"); // not working
system('cat "' . $x . '" '); // not working
system("cat second_string_xx.txt" ); //working !!
Your question couldn't be more vague but one of your comments suggest that you expect an error message when you feed cat with an invalid file. Well, that's not how cat actually works. As most classic Unix commands, it writes error messages into standard error (stderr) rather than standard output (stdout). You either need to use a PHP function that allows to capture stderr (such as proc_open) or redirect stderr to somewhere else (such as stdout or a file).
Apart from that, PHP program execution functions have many optional parameters and return values and you are ignoring them all. For instance, system() returns the last line of the command output on success (and FALSE on failure) and allows to capture the return value (which will be non-zero in case of error and is the standard mechanism to detect errors).

How to pass the content of a variable trough an external command in php?

I have a variable that contains a long string. (specifically it contains a few kilobytes of javascript-code)
I want to pass this string trough an external command, in this case a javascript-compressor, and capture the output of the external command (the compressed javascript) in php, assigning it to a variable.
I'm aware that there's classes for compressing javascript in php, but this is merely one example of a general problem.
originally we used:
$newvar = passthru("echo $oldvar | compressor");
This works for small strings, but is insecure. (if oldvar contains characters with special meaning to the shell, then anything could happen)
Escaping with escapeshellarg fixes that, but the solution breaks for longer strings, because of OS-limitations on maximum allowable argument-length.
I tried using popen("command" "w") and writing to the command - this works, but the output from the command silently disappears into the void.
Conceptually, I just want to do the equivalent of:
$newvar = external_command($oldvar);
Using the proc_open-function you can get handles to both stdout and stdin of the process and thus write your data to it and read the result.
Using rumpels suggestion, I was able to device the following solution which seems to work well. Posting it here for the benefit of anyone else interested in the question.
public static function extFilter($command, $content){
$fds = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("pipe", "w") // stderr is a pipe that the child will write to
);
$process = proc_open($command, $fds, $pipes, NULL, NULL);
if (is_resource($process)) {
fwrite($pipes[0], $content);
fclose($pipes[0]);
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
$return_value = proc_close($process);
// Do whatever you want to do with $stderr and the commands exit-code.
} else {
// Do whatever you want to do if the command fails to start
}
return $stdout;
}
There may be deadlock-issues: if the data you send is larger than the combined sizes of the pipes, then the external command will block, waiting for someone to read from it's stdout, while php is blocked, waiting for stdin to be read from to make room for more input.
Possibly PHP takes care of this issue somehow, but it's worth testing out if you plan to send (or receive) more data than fits in the pipes.

Ruby equivalent of PHP's fopen() method

I'm trying to find out if Ruby has en equivalent of php's fopen() method currently used like this:
$fd = fopen("php://stdin", "r");
would that be using ARGV variable?
Basically what I plan on doing is forward raw e-mail messages using the .procmailrc file which I already got working in a test php file, but the project requires the use of Ruby. Therefore I'm not 100% sure if using the ARGV variable would work or if somehow I need to capture the e-mail stream by some other means.
Any help would be greatly appreciated. Thanks :)
ARGV and the (standard) input stream are two different things. ARGV contains the parameters passed to an executable, like someapp a b c where a, b and are parameters. stdin is a file handle. You usually have three standard streams. stdin which is read-only, stdout and stderr which are write-only.
In Ruby you can use the predefined constants STDIN, STDOUT and STDERR to access the default streams. There are also the variables $stdin, $stdout, $stderr which are initialized with the same values as STDIN, STDOOUT and STERR but may be re-assigned other values.
You were probably referring to ARGF variable, have a look:
Best practices with STDIN in Ruby?

Categories