Redirecting I/O in PHP
<?php
fclose(STDIN);
f开发者_运维问答close(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..
精彩评论