Print while script is executing - php

I have this code snippet:
foreach ($data as $key => $value) {
$result = $this->_restFetch($value);
$mode[$key] = $result[0]->extract;
}
Just some basic code that runs in a for loop sending a request to some API.
It takes time to finish the script, so I'd like to output some text to the user while the script is executing, something like bellow:
foreach ($data as $key => $value) {
print "Started importing $value \r\n";
$result = $this->_restFetch($value);
$mode[$key] = $result[0]->extract;
print "Finished importing $value \r\n";
}
I've tried various approached with ob_ commands but none of them worked.
Any ideas?

After each print line, you have to put flush(), so the content would be printed out immediately. Good to keep in mind that the browser needs to have some minimum data to display (about .5Kb). I have put both ob_flush() and flush() because in that way you are sure that output will be shown. More on flush() and ob_flush() you can see in this topic PHP buffer ob_flush() vs. flush().
usleep function you can use to delay execution of code for debugging purposes.
foreach ($data as $key => $value) {
print "Started importing $value \r\n";
usleep(3600); // if you are debugging, otherwise just comment this part
ob_flush();
flush();
$result = $this->_restFetch($value);
$mode[$key] = $result[0]->extract;
print "Finished importing $value \r\n";
usleep(3600); // if you are debugging, otherwise just comment this part
ob_flush();
flush();
}

Related

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.

Sending an email with PHP

I am trying to use the php mail function to send an email. However, I am not sure how to structure the message part. I am processing an HTML form and I want that to be the message of the html. How could I wrap all of the output in a variable that I can pass as the message argument to the mail() function?
MY PHP:
//Contact Information
$array = $_POST['contact'];
echo '<hr>CONTACT INFORMATION<hr>';
foreach ($array as $key=>$value) {
if ($value != NULL) {
echo '<strong>' . $contact[$key] . '</strong><br/>';
echo $value . '<br/><br/>';
}
}
//Services Information
$array = $_POST['services'];
echo '<hr>SERVICES INFORMATION<hr>';
foreach ($array as $key=>$value) {
if ($value != NULL) {
echo '<strong>' . $services[$key] . '</strong><br/>';
echo $value . '<br/><br/>';
}
}
//Background Information
$array = $_POST['background'];
echo '<hr>BACKGROUND INFORMATION<hr>';
foreach ($array as $key=>$value) {
if ($value != NULL) {
echo '<strong>' . $background[$key] . '</strong><br/>';
echo $value . '<br/><br/>';
}
}
//Services Needed
$value = $_POST['servicesneeded'];
$value = rtrim($value, ", ");
echo '<hr>WHICH SERVICES ARE YOU INTERESTED IN?<hr>';
echo $value;
//Goals
$value = $_POST['goals'];
$value = rtrim($value, ", ");
echo '<hr>WHAT IS THE CORE PURPOSE OF YOUR PROJECT?<hr>';
echo $value;
if (!empty($_POST['goalsOther'])) {
echo '<br/>OTHER: ' . $_POST['goalsOther'];
}
........ I have about a dozen or so of these codeblocks
This isn't really a question about php mail, but more about concatenation. To solve your problem, create a variable, let's call it $message. Then, instead of echoing things out, append them to $message. So, instead of echo '<strong>' . $background[$key] . '</strong><br/>';, you'd have $message .= '<strong>' . $background[$key] . '</strong><br/>';.
I think it will be much easier for you to use SwiftMailer or PHPMailer
They both have a really easy to use API for sending emails using SMTP or php mail() function.
best of luck.
As mentioned by Brian Ray you should create a variable containing the message text that is later being sent using the mail() function. Depending on your server setup you may retrieve the content of the output buffer (things you have echo'd out before) and instead of sending it to the browser load it into a variable. Here's a sample how this could be achieved:
ob_start(); // enable output buffering
ob_clean(); // if content has been buffered before, discard it
// this would contain your echo() statements
echo(...);
...
// load the data from the output buffer into a variable
$message= ob_get_contents();
ob_clean(); // remove the buffered contents from the output buffer
// send the mail:
// PLEASE add security measures to avoid being used as spambot; I would strongly
// recommend to not allow any user input for the $recipient variable or
// additional header variable (if applicable)
mail($recipient, $subject, $message);
Things to keep in mind: output buffering is not always available and the output buffer differs from server to server. If the buffer limit is reached, the server will automatically flush the data (=send it to the browser), in that case you will find no or truncated data inside the output buffer. The method is OK in cases you are in full control of your server, do not allow direct access to the mail routine and can make sure that the buffer is sufficiently sized to contain the generated output.
Some additional reading on output control and ob_get_contents.

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

PHP, Apache. Output response to browser before script has finished

I have a script that pulls users from the DB, prepares an XMPP message, loops through each user and sends the XMPP message, then logs that the message was sent. Easily running through 1000 users plus. This is a server side API script that is called from a mobile device. The mobile device is waiting to receive a success JSON response. The user experience should be quick, I don't want the mobile user waiting for more than a few seconds for the response.
Problem is its taking a fair amount of time for the script to loop through all users, so my initial solution was to use ob_flush() - note these are merely test scripts to see if the planned method works:
ob_start();
$profiles_ar = array(
"reach" => 30,
);
$return_ar = array(
"success" => $profiles_ar['reach'],
);
echo json_encode($return_ar);
$buffer = str_repeat(" ", 4096); // Note apparently the buffer must be "filled" with 4096 characters (bytes?) for ob_flush() to work before sleep() occurs.
echo $buffer;
ob_flush();
flush();
for($i=0;$i < $profiles_ar['reach']; $i++)
{
sleep(1);
}
echo "finshed: ".$i;
ob_flush();
flush();
That's not working, nothing gets output until the script has finished - have I missed something with OB technique?
...... so did some searching and came up with this:
register_shutdown_function('process_after');
$profiles_ar = array(
"reach" => 10,
);
$return_ar = array(
"success" => $profiles_ar['reach'],
);
echo json_encode($return_ar);
echo $buffer;
exit;
function process_after()
{
global $profiles_ar;
for($i=0;$i < $profiles_ar['reach']; $i++)
{
sleep(1);
}
echo "finshed: ".$i;
}
this one works on mac, but not on the server....
Anyone got any ideas? If i cant get this technique of outputting the response JSON before the main processing to work, then my only hope is to build a queue in the DB and run a cron job........ ;(
Is output_buffering enabled in php.ini?
http://php.net/manual/en/outcontrol.configuration.php
I would refactor the creation of the xmpp message in it's own file and then use ajax calls to send each one. That way you aren't waiting on each message to succeed.
Solution is to add :
echo " ";
ob_flush(); flush;
inside the loop....
ob_start();
$profiles_ar = array(
"reach" => 10,
);
$return_ar = array(
"success" => $profiles_ar['reach'],
);
echo json_encode($return_ar);
for($i = 0; $i < 5000; $i++)
{
echo " ";
ob_flush(); flush;
}
echo $buffer;
ob_flush();
flush();
ob_end_flush();
for($i=0;$i < $profiles_ar['reach']; $i++)
{
sleep(1);
}
echo "finshed: ".$i;
ob_flush();
flush();
ob_end_flush();
This repeatedly forces the output from the first iteration of the loop and eventually kicks in.

Way to send functions as message in php's mail()

I am writing a PHP script that will send via a cron an email every night. In this script, I have multiple functions which output particular text. I am then trying to send the contents of those functions in the email. For some reason the email is going through fine, but the body of the content is showing up empty. If there's a better way to do this, by all means I'm open to it.
function function1() {
global $new;
echo "<p>";
while ($row = mysql_fetch_array($query)) $content = $row["COUNT(column1)"];
if ($content != 0) echo "output1";
else echo "output2";
echo "</p>";
}
$emailMessage = function1().function2().function3();
if ($_GET['version'] == "email") {
mail ($emailTo, $emailSubject, stripslashes($emailMessage));
}
else echo $emailMessage;
Obviously the code is obfuscated a bit, but the general outline is there.
echo sends the output to the standard out, it doesn't return it from the function. Try this.
ob_start()
// run function contents, including echo
var message = ob_get_clean();
return message;
This will capture what you echo into the buffer, prevent the buffer from being sent, and then reading the buffer into a variable. It will then empty the buffer ready for next time.

Categories