print progress as looping through php class functions - php

I am doing something with aOuth and some else's server at the moment and while the class I've created works it doesn't keep the HTML page posted of it's progress. For example say I have the following class:
class Something {
function a() {
sleep(2); echo "a()"; return TRUE;
}
function b() {
sleep(2); echo "b()"; return TRUE;
}
function c() {
sleep(2); echo "c()"; return TRUE;
}
}
Then I loop through the class in my HTML:
$something = new Something();
if($something->a()) {
if($something->b()) {
if($something->c()) {
echo "everything completed!!";
}
}
}
The page will render:
a()b()c()everything completed!!
6 seconds later. I want it to update as it goes (i.e. print a() when it's finished processing something->a(), print b() when it's finished processing something->b(), etc...)

Worked for me in Chrome 21, Firefox 15 and IE8:
<?php
header( 'Content-Type: text/html; charset=utf-8' );
class Something {
function a() {
echo "a()" . str_repeat(' ', 1024);
ob_flush(); flush();
sleep(2); return TRUE;
}
function b() {
echo "b()" . str_repeat(' ', 1024);
ob_flush(); flush();
sleep(2); return TRUE;
}
function c() {
echo "c()" . str_repeat(' ', 1024);
ob_flush(); flush();
sleep(2); return TRUE;
}
}
$something = new Something();
if($something->a()) {
if($something->b()) {
if($something->c()) {
echo "everything completed!!";
}
}
}
Explanation: we have to persuade into starting an output immediately...
PHP processor, with ob_flush-flush combo.
Apache (web-server), with sending Content-Type header right at the beginning of the script.
web-browsers, as some of them won't consider drawing a partial output unless it's big enough. The workaround is to append a sizable, but empty string (str_repeat(' ', 1024)) to the output.

Try flushing the output buffer each time you want the progress to be shown, with ob_flush function.
Each time you output some text, if output buffering is enabled, it is added to a buffer rather than being sent immediately to the client. When the request is done processing, the output is sent all at once.
If you flush the buffer, you force PHP to send the text it already have, without waiting for the request to complete.

Related

"ob_flush(): failed to flush buffer. No buffer to flush" on production Laravel 8

I am working on Server Sent Event in laravel. I am getting error on production "ob_flush(): failed to flush buffer. No buffer to flush" its working fine on localhost but throwing error on production
here is the sse code
public function notificationsCount(Request $request)
{
try {
$response = new \Symfony\Component\HttpFoundation\StreamedResponse(function () use ($request) {
while (true) {
$notif_count = Auth::user()->unreadNotifications->count();
echo json_encode(['data' => $notif_count]) . "\n\n";
ob_flush();
flush();
sleep(1);
if (connection_aborted()) {
break;
}
}
ob_end_flush();
});
$response->headers->set('Content-Type', 'text/event-stream');
$response->headers->set('X-Accel-Buffering', 'no');
$response->headers->set('Cach-Control', 'no-cache');
return $response->send();
} catch (\Exception$e) {
return response()->json(['error' => $e->getMessage()]);
}
}
You only need ob_flush() if an output buffer is active (for example by ob_start(), or by php.ini settings)
try
ob_start();
echo "foo";
ob_flush();

Run PHP code after ending the HTTP request

I'm writing a simple code to simply show to clients, data that is actually loaded from another HTTP server. The problem is that loading it from the remote server can take up to multiple seconds, and I don't want that much page load delay. So, I make my server cache a copy of this data. So that whenever a client sends a request to my server, it sends the ready-loaded copy and then loads a new copy from the remote server to update the local copy in case any changes were made.
So here's my pseudo code:
if(file_exists($cache_path)){
echo file_get_contents($cache_path);
// I need to end the HTTP request and close the connection here while continuing with the code.
$uptodate_content = file_get_contents("https://docs.google.com/document/export?format=pdf&id=$id");
// I don't want the user to wait for nothing, until this line.
}
else {
$uptodate_content = file_get_contents("https://someremotehost.com/someresource");
echo $uptodate_content;
}
echo file_put_contents($cache_path, $uptodate_content);
Hi I think the best solution is using a queue For example if you use the the queue, you can send it to the queue and then your consumer can pick it from the queue when it has time and user do not need to wait for it
This link is helpful
And this link will help you to use redis for this problem
This is a bad practice.
The connection can never end and you should be careful with such code
The better method is to run a cron job/queue every houerget data from remote server, or alternatively the remote server will trigger a trigger when updating data.
<?php
ob_end_clean();
header("Connection: close");
ignore_user_abort();
ob_start();
//your code
//your code
//your code
echo "response foo bar";
$obSize = ob_get_length();
header("Content-Length: $obSize");
ob_end_flush();
flush();
session_write_close();
// Do processing here
request_to_remote_server();
One way of doing it:
First, create a new PHP file, let's call it update.php, and write the following:
if (isset($argv[1])) {
storeDocumentToCache($argv[1]);
}
And in your current file, change the code to:
echo readDocumentFromCache($id) ?? storeDocumentToCache($id);
In old PHP versions (<7) it should be:
$content = readDocumentFromCache($id);
echo isset($content) ? $content : storeDocumentToCache($id);
Then require the following helper functions in both files (and set $cache_path):
function readDocumentFromCache($id, $fetch = true)
{
$cache_path = "?";
if (file_exists($cache_path)) {
return file_get_contents($cache_path);
}
if ($fetch) {
execInBackground("php " . __DIR__ . "/update.php $id");
}
return null;
}
funciton storeDocumentToCache($id)
{
$cache_path = "?";
$uptodate_content = file_get_contents("https://docs.google.com/document/export?format=pdf&id=$id");
file_put_contents($cache_path, $uptodate_content);
return $uptodate_content;
}
function execInBackground($cmd)
{
if (substr(php_uname(), 0, 7) == "Windows") {
pclose(popen("start /B " . $cmd, "r"));
} else {
exec($cmd . ' > /dev/null 2>/dev/null &');
}
}

How can I check if my function print/echo's something?

I am often using echo to debug function code:
public function MyFunc() {
// some code...
echo "OK";
// some code...
}
How can I check that my function print's/echo's something?
(pseudo code):
MyFunc();
if (<when something was printed>){
echo "You forgot to delete echo calls in this function";
}
This should work for you:
Just call your functions, while you have output buffering on and check if the content then is empty, e.g.
ob_start();
//function calls here
MyFunc();
$content = ob_get_contents();
ob_end_clean();
if(!empty($content))
echo "You forgot to delete echos for this function";
You could create a $debug flag and a debuglog() function, which checks for the debug flag and only then echos the message. Then you can toggle your debug messages on and off from one location.
define('DEBUGMODE', true); // somewhere high up in a config
function debuglog($msg){
if( DEBUGMODE ){ echo $msg; }
}
Should you ever want to get rid of your debug echos, you can search for "debuglog(" and delete those lines of code. This way you won't accidentally delete any echo statements that are required in normal execution or miss any debug echo statements that should really have been removed.
It's the bad way checking if something is echoed.
You can set a variable named is_echoed to 1 or you can return the value
public $is_echoed = 0;
//rest
$this->is_echoed = 1;
or
function myFunc()
{
return "OK";
}
if(myFunc() == 'OK')
//rest
You can use var_dump() and die() to debug your code more efficiently.
$test = "debud test";
public function MyFunc($test)
{
// some code...
var_dump($test); die();
// some code...
}
Reference:
http://php.net/manual/en/function.var-dump.php
http://php.net/manual/en/function.die.php
Why do you want to try such an extensive process of seeing if something has been echoed or not?
For debugging you can definitely use echo to see if the particular block is being hit during a particular use-case. But I would suggest you use flags and return the values to the calling function.
function xyz () {
if (something) return some_value;
else return some_other_value;
}
There is no particular need to have variables and use space in storing a 0 or 1 when you can just return a hard-coded literal.
I would suggest to you to use something like log4php [1]
But if not, I use a function like this:
define('DEBUG', true);
function debug($msg){
if(DEBUG){ echo $msg; }
}
Or something like this to see the log in the browser console:
function debug_to_console( $data ) {
if ( is_array( $data ) )
$output = "<script>console.log( 'Debug Objects: " . implode( ',', $data) . "' );</script>";
else
$output = "<script>console.log( 'Debug Objects: " . $data . "' );</script>";
echo $output;
}

While loops for server-sent events are causing page to freeze

I am currently working on a chat that uses Server-Sent Events to receive the messages. However, I am running into a problem. The server-sent event never connects and stays at pending because the page doesn't load.
For example:
<?php
while(true) {
echo "data: This is the message.";
sleep(3);
ob_flush();
flush();
}
?>
I expect that every 3 seconds, "data: This is the message." will be outputted. Instead, the page just doesn't load. However, I need this behavior for server-sent events. Is there a way to fix this?
Edit:
Full Code:
<?php
session_start();
require "connect.php";
require "user.php";
session_write_close();
echo $data["number"];
header("Content-Type: text/event-stream\n\n");
header('Cache-Control: no-cache');
set_time_limit(1200);
$store = new StdClass(); // STORE LATEST MESSAGES TO COMPARE TO NEW ONES
$ms = 200; // REFRESH TIMING (in ms)
$go = true; // MESSAGE CHANGED
function formateNumber ($n) {
$areaCode = substr($n, 0, 3);
$part1 = substr($n, 3, 3);
$part2 = substr($n, 6, 4);
return "($areaCode) $part1-$part2";
}
function shorten ($str, $mLen, $elp) {
if (strlen($str) <= $mLen) {
return $str;
} else {
return rtrim(substr($str, 0, $mLen)) . $elp;
}
}
do {
$number = $data["number"];
$sidebarQ = "
SELECT *
FROM (
SELECT *
FROM messages
WHERE deleted NOT LIKE '%$number%'
AND (
`from`='$number'
OR
`to`='$number'
)
ORDER BY `timestamp` DESC
) as mess
GROUP BY `id`
ORDER BY `timestamp` DESC";
$query = $mysqli->query($sidebarQ);
if ($query->num_rows == 0) {
echo 'data: null' . $number;
echo "\n\n";
} else {
$qr = array();
while($row = $query->fetch_assoc()) {
$qr[] = $row;
}
foreach ($qr as $c) {
$id = $c["id"];
if (!isset($store->{$id})) {
$store->{$id} = $c["messageId"];
$go = true;
} else {
if ($store->{$id} != $c["messageId"]) {
$go = true;
$store->{$id} = $c["messageId"];
}
}
}
if($go == true) {
$el = $n = "";
foreach ($qr as $rows) {
$to = $rows["to"];
$id = $rows["id"];
$choose = $to == $number ? $rows["from"] : $to;
$nameQuery = $mysqli->query("SELECT `savedname` FROM `contacts` WHERE `friend`='$choose' AND `number`='$number'");
$nameGet = $nameQuery->fetch_assoc();
$hasName = $nameQuery->num_rows == 0 ? formateNumber($choose) : $nameGet["savedname"];
$new = $mysqli->query("SELECT `id` FROM `messages` WHERE `to`='$number' AND `tostatus`='0' AND `id`='$id'")->num_rows;
if ($new > 0) {
$n = "<span class='new'>" . $new . "</span>";
}
$side = "<span style='color:#222'>" . ($to == $number ? "To you:" : "From you:") . "</span>";
$el .= "<div class='messageBox sBox" . ($nameQuery->num_rows == 0 ? " noname" : "") . "' onclick=\"GLOBAL.load($id, $choose)\" data-id='$id'><name>$hasName</name><div>$side " . shorten($rows["message"], 25, "...") . "</div>$n</div>";
}
echo 'data: '. $el;
echo "\n\n";
$go = false;
}
}
echo " ";
ob_flush();
flush();
sleep(2);
} while(true);
?>
I would also like to note, that this infinite loop shouldn't be causing this to happen. This is just how SSE's are set up usually and it is even done so on the MDN website.
No doubt by now you have figured this out but on the offchance you have not I used code like the following on a couple of sse scripts and it worked like a charm. The code below is generic and does not feature your sql or recordset processing but the idea is sound(!?)
<?php
set_time_limit( 0 );
ini_set('auto_detect_line_endings', 1);
ini_set('mysql.connect_timeout','7200');
ini_set('max_execution_time', '0');
date_default_timezone_set( 'Europe/London' );
ob_end_clean();
gc_enable();
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Methods: GET');
header('Access-Control-Expose-Headers: X-Events');
if( !function_exists('sse_message') ){
function sse_message( $evtname='chat', $data=null, $retry=1000 ){
if( !is_null( $data ) ){
echo "event:".$evtname."\r\n";
echo "retry:".$retry."\r\n";
echo "data:" . json_encode( $data, JSON_FORCE_OBJECT|JSON_HEX_QUOT|JSON_HEX_TAG|JSON_HEX_AMP|JSON_HEX_APOS );
echo "\r\n\r\n";
}
}
}
$sleep=1;
$c=1;
$pdo=new dbpdo();/* wrapper class for PDO that simplifies using PDO */
while( true ){
if( connection_status() != CONNECTION_NORMAL or connection_aborted() ) {
break;
}
/* Infinite loop is running - perform actions you need */
/* Query database */
/*
$sql='select * from `table`';
$res=$pdo->query($sql);
*/
/* Process recordset from db */
/*
$payload=array();
foreach( $res as $rs ){
$payload[]=array('message'=>$rs->message);
}
*/
/* prepare sse message */
sse_message( 'chat', array('field'=>'blah blah blah','id'=>'XYZ','payload'=>$payload ) );
/* Send output */
if( #ob_get_level() > 0 ) for( $i=0; $i < #ob_get_level(); $i++ ) #ob_flush();
#flush();
/* wait */
sleep( $sleep );
$c++;
if( $c % 1000 == 0 ){/* I used this whilst streaming twitter data to try to reduce memory leaks */
gc_collect_cycles();
$c=1;
}
}
if( #ob_get_level() > 0 ) {
for( $i=0; $i < #ob_get_level(); $i++ ) #ob_flush();
#ob_end_clean();
}
?>
While this is not a direct answer as to the problem, try using this method to find the error.. Your not getting errors, but this should help you find them maybe?
Basically you want to have a simple PHP script which includes your main script, but this page enables errors... Example below..
index.php / Simple Error Includer
<?php
ini_set('display_errors',1);
ini_set('display_startup_errors',1);
error_reporting(-1);
require "other.php";
?>
other.php / You Main Script
<?php
ini_set('display_errors',1);
ini_set('display_startup_errors',1);
error_reporting(-1);
weqwe qweqeq
qweqweqweqwe
?>
If you create a setup like this, if you view index.php you will see the following error Parse error: syntax error, unexpected 'qweqeq' (T_STRING) in /var/www/html/syntax_errors/other.php on line 5 because it does not have an invalid syntax on the main page and allows any includes to be error checked..
But if you where to view other.php, you would simply get a white / blank page because its unable to validate the whole page/script.
I use this method in my projects, that way regardless of what i do in other.php or any linked php pages, i will see an error report for them.
Please understand the code before commenting
to say this disables error control means you did not bother to RTM
Fill the buffer
Another issue in the past that i remember was filling the buffer before it would output to the browser. So try something like this before your loop.
echo str_repeat("\n",4096); // Exceed the required browser threshold
for($i=0;$i<70;$i++) {
echo "something as normal";
flush();
sleep(1);
}
Examples at http://www.sitepoint.com/php-streaming-output-buffering-explained/
It seems like the sleep function is interfering with the output. Putting the sleep function AFTERWARDS did work:
<?php
while(true) {
echo "data: This is the message.";
ob_flush();
flush();
sleep(3);
}
As other people suggest, I would encourage to use AJAX instead of an infinite loop, but that was not your question.
One thing I have noticed here is sleep() function in combination with ob_start() and - THERE IS NO - ob_start() anywhere in the full code example, yet there is flush() and ob_flush() ..
What are you flushing anyway?
And why not simply ob_end_flush() ?
The thing is that sleep() than echo(), than sleep() again, than echo() again, etc, etc.. has no effect when output buffering is turned on. Sleep function works as expected when output buffering is not in play - in between. In fact, it might *(and it will) produce quite unexpected results, and those results won't be the one we want to see.
The following code works fine here, also using Mayhem his str_repeat function to add 4k of data (that is usually the minimum for a tcp packet to be flushed by php)
echo str_repeat(' ', 4096);
while(true)
{
echo "data: This is the message.";
flush();
sleep(3);
}
Instead of using loop try this code given below which is working(tested myself) fine as per your requirement
echo "data: This is the message.";
$url1="<your-page-name>.php";
header("Refresh: 5; URL=$url1");
what this will do is it will call itself every 5 seconds (in your case set it to 3 instead of 5) and echo the output.
I am going to take a chance and state the obvious,
you could query the server every 3 seconds, and let the client do the waiting...
This could be done easily with javascript
for example, try this code and name if file.php
<?php
$action='';
if (array_key_exists('action',$_GET))
{$action=$_GET['action'];}
if ($action=='poll')
{
echo "this message will be sent every 3 sec";
}
else
{
?><HTML><HEAD>
<SCRIPT SRC="http://code.jquery.com/jquery-2.1.3.min.js"></SCRIPT>
<SCRIPT>
function doPoll()
{
$('#response').append($.get("file.php?action=poll"));
setTimeout(doPoll, 3000);
}
doPoll();
</SCRIPT>
</HEAD><BODY><DIV id="response"></DIV></BODY></HTML><?php
}
Could it be as simple as the script timing out?
Eventually PHP scripts self terminate if they run for too long. The solution for when you don't want this to happen is to keep resetting the time out.
So a simple addition might be all you need:
<?php
while(true) {
echo "data: This is the message.";
set_time_limit(30);
sleep(3);
ob_flush();
flush();
}
?>
Of course, that might not be it but my gut instinct is that this is the problem.
http://php.net/manual/en/function.set-time-limit.php
UPDATE: I noticed in the comments that you are using some free hosting. If they are running PHP in safe mode then you cannot reset your timeout.
I had the same issue and finally found the easy and quick solution on kevin choppin's blog:
Session Locks
First and foremost, if you're using sessions for whatever reason you will need to make them read-only on the stream. If they're writable, this will lock them everywhere else, so any page loads will hang while the server waits for them to become writable again. This is easily fixed by calling; session_write_close();
I suggest using if() statement instead of using while. And in your case your condition is always true, hence it is in infinite loop.

PHP Sleep but execute whats before sleep

So I need a little help, is there a better way to have php wait before executing the next line of code?
I tried:
$response = $_POST[response];
echo "</br>".$response;
if (strpos($response,'no') !== false) {
sleep(2);
echo "</br>";
echo 'Why not?';
}
But this method does not display the
echo "</br>".$response;
before it sleeps.
It sleeps for the 2 seconds, then displays the response.
How can I get it to echo $response, then wait 2 seconds before is says "Why Not"
Thank you in advance.
Flush your buffer (UPDATE):
<?
$response = $_POST[response];
echo "</br>".$response;
ob_end_flush();
flush();
if (strpos($response,'no') !== false) {
sleep(2);
echo "</br>";
echo 'Why not?';
}
?>
References: http://php.net/manual/en/function.ob-flush.php, http://php.net/flush
You have to use flush the buffer content to see the results
#apache_setenv('no-gzip', 1);
#ini_set('zlib.output_compression', 0);
ob_start();
$response = $_POST[response];
echo "<br />".$response;
ob_flush();
flush();
if (strpos($response,'no') !== false) {
sleep(2);
echo "<br />";
echo 'Why not?';
}
I am using both ob_flush() and flush() as stated in http://php.net/manual/en/function.flush.php but just see what works. Sometimes only using flush() also works, depending on the server config.
Be aware of gzip/deflate. You can't deflate an output stream and in the middle of it output the buffer. You can either turn it off by using the htaccess or with the 2 first lines of the code
It looks like the output buffer isn't being flushed before the sleep call. You can indeed control this manually as Pentium10 Points out: https://stackoverflow.com/a/3078873/1461223

Categories