Intermittently echo out data with long-running PHP script - php

I have a PHP script that makes a bunch of cURL requests. After each cURL request, I want to echo out some data, but presently, data only gets echoed out after every 5-10 cURL requests.
I've tried using ob_flush and flush, but it doesn't seem to make a difference. The following is the basic flow of my script:
<?php
header('Content-Type: text/html; charset=UTF-8');
set_time_limit(0);
ob_start();
$arr = array(); // Lots of strings in this array
foreach ($arr as $elem) {
// Use $elem to make cURL request and return HTML.
// Run regexes on returned HTML.
echo '<pre>';
print_r($matches[1]);
print_r($matches[2]);
echo '</pre>';
ob_flush();
flush();
}
Is there anything I can do to force the script to output the echoed/print_r'ed data after each iteration of the foreach loop?
Thank you very much.

You need to move the ob_start() inside the loop, as:
<?php
header('Content-Type: text/html; charset=UTF-8');
set_time_limit(0);
$arr = array(); // Lots of strings in this array
foreach ($arr as $elem) {
ob_start();
// Use $elem to make cURL request and return HTML.
// Run regexes on returned HTML.
echo '<pre>';
print_r($matches[1]);
print_r($matches[2]);
echo '</pre>';
ob_end_flush();
flush();
}
Think of the Output Buffer (ob_*) functions as push and pop on a stack. You specify where you want to start recording by pushing a buffer onto the stack (ob_start()) and then when you want to output, you pop the buffer off the stack and do something with the result (ob_flush(), ob_get_*(), etc). Each ob_start() must have a matching buffer end function.
You'll also want to use ob_end_flush() instead of ob_flush() as I don't think you want keep the buffer after each run.

Try using this at start:
apache_setenv('no-gzip', 1);
ini_set('output_buffering', 0);
ini_set('zlib.output_compression', 0);
ini_set('implicit_flush', 1);
And then do the stuff you already did.

Related

Include a php file, but return output as a string instead of printing

I want to include a file, but instead of printing output I want to get it as string.
For example, I want include a file:
<?php echo "Hello"; ?> world!
But instead of printing Hello world! while including the file I want to get it as a string.
I want to filter some elements from the file, but not from whole php file, but just from the html output.
Is it possible to do something like this?
You can use php buffers like this:
<?php
ob_start();
include('other.php');
$script = ob_get_contents(); // it will hold the output of other.php
ob_end_clean();
EDIT: You can abstract this into a function:
function inlcude2string($file) {
ob_start();
include($file);
$output = ob_get_contents(); // it will hold the output of other.php
ob_end_clean();
return $output;
}
$str = inlcude2string('other.php');

How can I replace braces with <?php ?> in php file?

I wanna replace braces with <?php ?> in a file with php extension.
I have a class as a library and in this class I have three function like these:
function replace_left_delimeter($buffer)
{
return($this->replace_right_delimeter(str_replace("{", "<?php echo $", $buffer)));
}
function replace_right_delimeter($buffer)
{
return(str_replace("}", "; ?> ", $buffer));
}
function parser($view,$data)
{
ob_start(array($this,"replace_left_delimeter"));
include APP_DIR.DS.'view'.DS.$view.'.php';
ob_end_flush();
}
and I have a view file with php extension like this:
{tmp} tmpstr
in output I save just tmpstr and in source code in browser I get
<?php echo $tmp; ?>
tmpstr
In include file <? shown as <!--? and be comment. Why?
What you're trying to do here won't work. The replacements carried out by the output buffering callback occur after PHP code has already been parsed and executed. Introducing new PHP code tags at this stage won't cause them to be executed.
You will need to instead preprocess the PHP source file before evaluating it, e.g.
$tp = file_get_contents(APP_DIR.DS.'view'.DS.$view.'.php');
$tp = str_replace("{", "<?php echo \$", $tp);
$tp = str_replace("}", "; ?>", $tp);
eval($tp);
However, I'd strongly recommend using an existing template engine; this approach will be inefficient and limited. You might want to give Twig a shot, for instance.
do this:
function parser($view,$data)
{
$data=array("data"=>$data);
$template=file_get_contents(APP_DIR.DS.'view'.DS.$view.'.php');
$replace = array();
foreach ($data as $key => $value) {
#if $data is array...
$replace = array_merge(
$replace,array("{".$key."}"=>$value)
);
}
$template=strtr($template,$replace);
echo $template;
}
and ignore other two functions.
How does this work:
process.php:
<?php
$contents = file_get_contents('php://stdin');
$contents = preg_replace('/\{([a-zA-Z_][a-zA-Z_0-9]*)\}/', '<?php echo $\1; ?>', $contents);
echo $contents;
bash script:
process.php < my_file.php
Note that the above works by doing a one-off search and replace. You can easily modify the script if you want to do this on the fly.
Note also, that modifying PHP code from within PHP code is a bad idea. Self-modifying code can lead to hard-to-find bugs, and is often associated with malicious software. If you explain what you are trying to achieve - your purpose - you might get a better response.

getting data from php variable

$content = file_get_contents('file.php');
echo $content;
nothing displays, expect when displaying the page sourcecode in browser the display is this
<? foreach(glob("folder/*.php") as $class_filename) { require_once($class_filename); } ?>
so it wont execute the script when getting the content..
file.php contains this code
<? foreach(glob("folder/*.php") as $class_filename) {
require_once($class_filename);
}
?>
and if I do next
$content = foreach(glob("folder/*.php") as $class_filename) { require_once($class_filename); } ?>
it complains about unexpected foreach...
is there a way to read the folder/.php files content to single $variable and then echo/print all folder/.php files to page where it should be?
thanks for help already.
Is that what you want to do ?
$content = '';
foreach (glob('folder/*.php') as $class){$content .= file_get_contents($class);}
echo $content;
What you're trying won't execute the contents of the "file.php", jsut display the contents of them on screen.
If you want to execute file.php, use eval ($content)
To capture the output, use something like:
ob_start(); // Don't echo anything but buffer it up
$codeToRun=file_get_contents('file.php'); // Get the contents of file.php
eval ($codeToRun); // Run the contents of file.php
$content=ob_get_flush(); // Dump anything that should have been echoed to a variable and stop buffering
echo $content; //echo the stuff that should have been echoed above

"Transfer-Encoding: chunked" header in PHP

i want to add Transfer-Encoding: chunked header to the file that i'm outputing (its just generated plain text), but when i add:
header("Transfer-Encoding: chunked");
flush();
the browser doesn't want to open the file.
The webpage at ... might be
temporarily down or it may have moved
permanently to a new web address.
what i need to do for it to work?
You need to send the Content-Length with every chunk you send. Look at Wikipedia for a first impression, how a chunked encoding looks like. Its not that trivial and in many cases its oversized.
Update:
First you send the headers, because they must always send before any content (also with chunked encoding). Then you send (for every chunk) the size (in hexadecimal) followed by the content. Remember flush() after every chunk. At last you must send a zero-size chunk to make sure, that the connection get closed properly.
Its not tested, but something like this
header("Transfer-Encoding: chunked");
echo "5\r\n";
echo "Hello";
echo "\r\n\r\n";
flush();
echo "5\r\n";
echo "World";
echo "\r\n";
flush();
echo "0\r\n\r\n";
flush();
As previous members said you have to follow chunked transfer encoding format. In next example i will show how you can use one user function to follow format rules:
<?php
//set headers
header('Transfer-Encoding: chunked');
header('Content-Type: text/html');
//browsers collect first 1024 bytes
//and show page only if bytes collected
//so we will use space padding.
//if you cannot understand what it means
//check script with PADDING=0
define("PADDING", 16);
//caret return and new line characters as constant
define("RN", "\r\n");
//user function what get current output buffer data
//and prefixes it with current buffer length.
//next it call flush functions
function flush_data(){
$str=ob_get_contents();
ob_clean();
echo dechex(strlen($str)).RN.$str.RN;
ob_flush();
flush();
}
//default HTML 5 page
echo "<!doctype html><html><head><title>Transfer-Encoding: chunked</title>";
echo "<script>";
//+padding
for($i=0;$i<PADDING;$i++){
//64 spaces (1 block)
echo " ";
}
echo "</script></head><body><div>";
//current output buffer will shown immediately in browser
//after this function
flush_data();
//cycle wait 1 sec before next iteration
for($i=0;$i<5;$i++)
{
//print iteration number
echo "$i<br>";
flush_data();
sleep(1);
}
echo "</div></body></html>".RN;
//terminating part of encoding format
flush_data();
echo "0\r\n\r\n";
ob_flush();
?>
Notes:
Check if «implicit_flush» is On in your php.ini
Know if you overflow output buffer («output_buffering» in php.ini) it will flush automatically.
For me when I was trying something with "Transfer-Encoding: chunked" I had to use this code to make it work:
<?php
echo "data";
header_remove("Transfer-Encoding");
flush();
?>
This code will still have the "Transfer-Encoding: chunked" header.
It automatically sets the Transfer-Encoding heading when you use flush but when it set it manually it fails, so to prevent any problems try to remove it. Also make sure that you remove the heading on the line before you do your first flush to prevent errors.
Use ob_flush(); before flush();
Sample code:
<?php
header('Content-Encoding', 'chunked');
header('Transfer-Encoding', 'chunked');
header('Content-Type', 'text/html');
header('Connection', 'keep-alive');
ob_flush();
flush();
$p = ""; //padding
for ($i=0; $i < 1024; $i++) {
$p .= " ";
};
echo $p;
ob_flush();
flush();
for ($i = 0; $i < 10000; $i++) {
echo "string";
ob_flush();
flush();
sleep(2);
}
?>

How do you define a PHP include as a string?

I tried:
$test = include 'test.php';
But that just included the file normally
You'll want to look at the output buffering functions.
//get anything that's in the output buffer, and empty the buffer
$oldContent = ob_get_clean();
//start buffering again
ob_start();
//include file, capturing output into the output buffer
include "test.php";
//get current output buffer (output from test.php)
$myContent = ob_get_clean();
//start output buffering again.
ob_start();
//put the old contents of the output buffer back
echo $oldContent;
EDIT:
As Jeremy points out, output buffers stack. So you could theoretically just do something like:
<?PHP
function return_output($file){
ob_start();
include $file;
return ob_get_clean();
}
$content = return_output('some/file.php');
This should be equivalent to my more verbose original solution.
But I haven't bothered to test this one.
Try something like:
ob_start();
include('test.php');
$content = ob_get_clean();
Try file_get_contents().
This function is similar to file(), except that file_get_contents() returns the file in a string.
Solution #1: Make use of include (works like a function): [My best solution]
File index.php:
<?php
$bar = 'BAR';
$php_file = include 'included.php';
print $php_file;
?>
File included.php:
<?php
$foo = 'FOO';
return $foo.' '.$bar;
?>
<p>test HTML</p>
This will output FOO BAR, but
Note: Works like a function, so RETURN passes contents back to variable (<p>test HTML</p> will be lost in the above)
Solution #2: op_buffer():
File index.php:
<?php
$bar = 'BAR';
ob_start();
include 'included.php';
$test_file = ob_get_clean(); //note on ob_get_contents below
print $test_file;
?>
File included.php:
<?php
$foo = 'FOO';
print $foo.' '.$bar;
?>
<p>test HTML</p>
If you use ob_get_contents() it will output FOO BAR<p>test HTML</p> TWICE, make sure you use ob_get_clean()
Solution #3: file_get_contents():
File index.php:
<?php
$bar = 'BAR';
$test_file = eval(file_get_contents('included.php'));
print $test_file;
?>
File included.php:
$foo = 'FOO';
print $foo.' '.$bar;
This will output FOO BAR, but Note: Include.php should not have <?php opening and closing tags as you are running it through eval()
The other answers, for reasons unknown to me, don't quite reach the correct solution.
I suggest using the buffer, but you have to get the contents and then clean the buffer before the end of the page, otherwise it is outputted. Should you wish to use the output from the included file, you should use op_get_contents(), which will return a string of the contents of the buffer.
You also don't need to loop over the includes as each will just add to the buffer (unless you clean it first).
You therefore could use the following;
ob_start();
include_once('test.php');
include_once('test2.php');
$contents = ob_get_contents();
ob_end_clean();
Hope this helps.
You can use the function file_get_contents.

Categories