I'm starting php directly from the terminal and passing it a .php script to run, I'm not using a web browser.
The documentation for flush() says
Flushes the system write buffers of PHP and whatever backend PHP is
using (CGI, a web server, etc). This attempts to push current output
all the way to the browser with a few caveats.
So based on that, it sounds possible that flush() could still be needed when you need output to immediately get sent to the terminal before the script ends, because of the part where it said "system write buffers of PHP". But I would like to know definitively. I'm using php 8.1 currently, but my question is hopefully not version specific.
I'm wanting to know from someone familiar with PHP's C implementation whether it ever under any circumstances buffers output when started from a terminal, or if that only happens if PHP is started a special way by a server. I know if I run code that intentionally requests buffering by using ob_start() or something like that, then it is supposed to buffer and this will not be ended by calling flush(), that's not what I'm asking about. I'm asking about code that doesn't explicitly request any buffering.
Why not test that yourself?
<?php
// ob_start();
print "Test 1\n";
sleep(1);
print "Test 2";
sleep(5);
print "Test 3";
flush();
sleep(5);
On my test terminal (standard Ubuntu 20.04.5 LTS), ob_start buffers everything, and ignores flush() altogether. Without ob_start, all text is output immediately (even if not ended by a LF, as is the case for the second test).
I haven't a PHP 8.1 handy, but mine says,
> PHP 8.2.1 (cli) (built: Jan 18 2023 10:17:15) (NTS)
> Copyright (c) The PHP Group
> Zend Engine v4.2.1, Copyright (c) Zend Technologies
You might ask a different (opposite, even) question: are there conditions whereby my terminal-run script might start buffering for some reason?
When you call flush() the call gets ultimately redirected to sapi_module, which issues a fflush() on stdout (this on the current source tree on Github, but I don't think this was changed since at least PHP 5). This is enough, I believe, to say that flush() is sufficient.
Is it necessary? On a standard run from the terminal, no. But I didn't include any scriptlets or libraries or Composer modules that might have changed the buffering strategy.
Of course, any of those could have. Just issue an ob_start() (maybe because the module is born to run on a web server) and hey presto!, your script starts buffering and requires flush (actually, in that case, ob_flush() - as said above, flush will not work).
But if that's the case, you've got a completely different beast to deal with - you should probably write your own output function, so that all console writes are routed to some code that can check, say, ob_get_level() and deal with it accordingly. Or, bringing this to the extreme,
declare(ticks=1);
register_tick_function(static function() {
ob_get_level() && ob_flush();
flush();
});
Related
First, as a background I am trying to encrypt data we have on the server when a new entity is made. This is on a legacy system which originated as a Perl CGI system, and has a separate portion which is PHP. The Perl part creates the groups, the PHP was later implemented to encrypt it.
I am trying to execute the PHP encryption script from a Perl CGI file. I have tried using:
exec("/path/to/file arg1 arg2")
system("/path/to/file arg1 arg2")
backtick operator /path/to/file arg1 arg2
open ("/path/to/file arg1 arg2 |)
I have also tried pointing directly to /bin/php and passing the file as an argument with each case. The only things I have had happen so far are:
Printing the output of exec/system... produces the Perl file (not even the php file) as text, which I haven't seen any mention of this happening anywhere else, but all I've seen is Perl, not CGI and Perl together.
No data. If I output $! from Perl I get an illegal seek error when using system, but the others leave it blank. All of them return 0 as if the exec/system/... has run, but nothing server side has changed.
From what I have read online I think that CGI may be running in some form of a "protected" mode which disables the exec/system/open/backtick commands on certain files, but am not certain that is the issue. As far as I can tell though, there is no indication of permission being restricted. If anyone has any insights that is much appreciated. If you need anymore information, let me know.
A few notes:
Show us actual, mimimal and complete test programs along with their output so we can see what you are doing.
system shares its filehandles with the parent program, so if the external program sends something to STDOUT, that's where it's going. If that's before you send your CGI headers, then things will get messed up.
backticks will capture standard output, but not standard error. Stuff might still go to an error log.
exec turns your program into some other program. That is, your Perl program does some setup and then becomes the thing that you exec, then never comes back (unless things fail).
Some things to help with debugging:
Make a small CGI program that simply calls another program you know that should work, such as date or something similar. Verify that you can do that much.
#!/usr/bin/perl
print "Content-type: text/plain\n\n";
my $rc = system '/bin/date';
print "result was <$rc>";
Then, run php to show its version. Verify that you can run php.
#!/usr/bin/perl
print "Content-type: text/plain\n\n";
my $rc = system '/usr/bin/php', '-v';
print "result was <$rc>";
Slowly add to the complexity. Find out where things stop working.
If your arguments to PHP come from user input, consider using taint checking and careful cleansing (perlsec). Notice I use the list version of system so the shell doesn't get a chance to interpret metacharacters.
I run a php script from xampp portable on windows. The script takes over a minute. Recently, the script is having flush() problem, as echo statements are not immediately displayed. The script used to work fine earlier with no buffering problem.
Interestingly, I ran the same script copying the xampp portable to another system and the flush statements were working without any problem. Same code, Same xampp portable.
What could be the reason?
Make the first line of your script ob_implicit_flush();.
or
Change the php.ini file setting implicit_flush = On.
From the documentation:
implicit_flush boolean, FALSE by default. Changing this to TRUE tells
PHP to tell the output layer to flush itself automatically after every
output block. This is equivalent to calling the PHP function flush()
after each and every call to print or echo and each and every HTML
block.
When using PHP within an web environment, turning this option on has
serious performance implications and is generally recommended for
debugging purposes only.
I read other responses and you keep insisting on the fact that your home environment and your work environment are the same. However, you can see that there is a difference. This point of view really help (at least to me) to investigate problems.
Since you did not provide many details about the problem, I would try the following checklist:
Settings
Is your PHP settings really identical? Try to compare results of phpinfo() on both environments.
Data
Do you really test your script on identical data? There are many subtle problems that are described in PHP manual:
Even the browser may buffer its input before displaying it. Netscape, for example, buffers text until it receives an end-of-line or the beginning of a tag, and it won't render tables until the tag of the outermost table is seen.
Some versions of Microsoft Internet Explorer will only start to display the page after they have received 256 bytes of output, so you may need to send extra whitespace before flushing to get those browsers to display the page.
http://php.net/manual/en/function.flush.php
or
http://www.php.net/manual/en/function.ob-flush.php#90529 (commenters point out many problems that you may experience)
Try dummy plaintext data instead of HTML. You may try to output simple lines like current time and check behaviour of your script.
Browsers
Try a few browsers (with cleared cache) to see if the issue is browser-specific or not.
I think it may be your browser. Have you cleaned the temporary settings of Iron Portable browser?
this occurs when you use gzip and there is some output have been send so the browser get confused to solve this , i uses this code always
if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false)
{ob_start('ob_gzhandler'); ob_start();}
else
ob_start();
You have not mentioned about the versions of windows both the systems running. Are the systems completely identical?
You also said that you are running a portable version of XAMPP , if you are using a pen drive/thump drive there is a chance that data transfer speeds can vary due to USB speeds.
I had a similar issue where the First system had USB legacy ports and the second one I tested has usb 2+ ports or higher.
The speed and processing time changed based on the systems, while one system took 20 seconds , other took nearly 60 seconds to process.
The slow system produced undesired results [I was working on an image processor].
I guess your case is similar and had to do a lot with the system vitals.
Cheers
Clain
I figured out that the problem is with antivirus. I recently switched to Bitdefender from Avast. When I switched back to Avast, the problem disappeared miraculously. So, i guess antivirus is also a factor here.
I am trying to print generated forms / receipts through PHP (the printers will be installed on the server, I am not attempting to print to a user's local printer). I have decided to try the following methodology:
IN PHP:
Generate a PDF file and save it on the server.
Call a perl script to print said PDF file.
IN perl:
Use system() to "open" Reader and print the given PDF silently.
What works:
I can generate PDFs in PHP.
I can call a perl script.
If the script has errors, they report to the browser window. ie: If I purposely change file paths it fails, and reports the appropriate reason.
functions such as printf seem to work fine as the output displays in the browser.
The exact same perl script (with the "non-functioning" line mentioned below) works properly when executed from the command line or the IDE.
What doesn't work:
In perl: system('"C:\\Program Files (x86)\\Adobe\\Reader 10.0\\Reader\\AcroRd32.exe" /N /T "C:\\test.pdf" 0-XEROX');
What happens:
NOTHING! I get no errors. It just flat out refuses to open Adobe Reader. All code below this line seems to run fine. It's like the function is being ignored. I am at a loss as to why, but I did try a few things.
What I've tried:
Changed permissions of the AcroRd32.exe to Everyone - Full Control.
Output the $? after the system() call. It is 1, but I don't know what 1 means in this case.
Verified that there are no disable_functions listed in php (though I think this is unrelated as shell_exec seems to be working, since some of the perl code is ran).
Various other configurations that at least got me to the point where I can confirm that PHP is in fact calling the perl script, it just isn't running the system() call.
Other info:
Apache 2.2.1.7
PHP 5.35
Perl 5.12.3 built for MSWin32-x86-multi-thread
WampServer 2.1
I'm at a loss here, and while it seems like this is an Apache / permissions problem, I cannot be sure. My experience with Apache is limited, and most of what I find online is linux commands that don't work in my environment.
Try this:
my #args = ('C:/Program Files (x86)/Adobe/Reader 10.0/Reader/AcroRd32.exe');
if (system(#args) != 0) {
# Can't run acroread. Oh Noes!!!
die "Unable to launch acrobat reader!\n";
}
The thing about system() is that it does two different things
depending on the number and type(s) of argument it gets. If the
argument is an array or if there are multiple arguments, Perl assumes
the first is the program to run with the rest as its arguments and it
launches the program itself.
If, however it's just one string, Perl handles it differently. It
runs your command-line interpreter (typically CMD.EXE on Windows) on
the string and lets it do what it wants with it. This becomes
problematic pretty quickly.
Firstly, both Perl and the shell do various kinds of interpolation on
the string (e.g. replace '//' with '/', tokenize by space, etc.) and
it gets very easy to lose track of what does what. I'm not at all
surprised that your command doesn't work--there are just so many
things that can go wrong.
Secondly, it's hard to know for sure what shell actually gets run on
Windows or what changes Perl makes to it first. On Unix, it usually doesn't matter--every shell does more or
less the same with simple commands. But on Windows, you could be
running raw CMD.EXE, GNU Bash or some intermediate program that
provides Unix-shell-like behaviour. And since there are several
different ports of Perl to Windows, it could well change if you
switch.
But if you use the array form, it all stays in Perl and nothing else
happens under the hood.
By the way, the documentation for system() and $? can be found here and here. It's well worth reading.
I have StartServer.php file that basically starts a server if it is not already started. Everything works perfect except that the StartServer.php will hang forever waiting for the shell_exec()'d file Server.php to finish its execution that it never does.
Is there a way to execute a PHP file and just forget about it -- and not wait for its execution to finish?
Edit: It has to work on Windows and Linux.
This should help you - Asynchronous shell exec in PHP
Basically, you shell_exec("php Server.php > /dev/null 2>/dev/null &")
You'll need something like the pcntl functions. Only problem is that this is a non-windows extension, and not suitable to run inside a web server. The only other possibility I can think of is to use the exec function and fork the current process manually (as suggested in dekomotes's answer), doing OS detection to figure out the command needed. Note that this approach isn't much different to using the pcntl functions (in Linux, at least) - the ampersand character causes the second script to be run inside different process. Multi-threaded / multi-process programming isn't well supported in PHP, particularly when running inside a web server.
I think it's traditional to let the server detach itself form the parent process, ie to "daemonize" itself, rather than having the script starting the server detach itself. Check the server you're starting to see if it has a daemon-option.
If you've written the server yourself, in PHP, you need to detach it. It looks somehting like this:
posix_setsid(); //Start a new session
if(pcntl_fork()) {exit();} //Fork process and kill the old one
I think this works on Windows too. (Not tested.)
I noticed the other day that a new script I wrote for php 5 began outputting html that was viewable before the php script had actually finished. Did this happen with 4?
For instance, I have a long loop that echos something out with each iteration. The output was small in terms of kb, so I dont think it was lag due to the download speed. Can someone explain the difference in output?
Maybe there a difference in the configuration of the output_buffering directive, in php.ini ?
If output_buffering is enabled, PHP will "keep" the generated output in memory (at least, if it doesn't become bigger than the size of the memory buffer), and only send it to the browser when the page's generation is finished.
If output_buffering is disabled, the ouput is sent immediatly when generated, even if the script's execution is not finished yet.
I doubt there is a difference to this regard between PHP 4 and 5, but you can get this behaviour on both versions, namely by enabling/disabling the output_buffer. Maybe the default value for PHP 5 is different than it was for PHP 4? (Haven't checked)
When the data is sent, is dependant on PHP configuration, it's an output buffer, and behaves like a buffer.
Having said that, you can use the function ob_start() and ob_end_flush() to take control of the buffer. The Zend Framework does some clever stuff with output buffering for instance...
The usual suspects are:
Browser and HTML structure
Output buffering or output handlers
HTTP compression handled by PHP or by the web server
A close look at phpinfo() at a tool to see HTTP headers can help you.