I am facing a weird problem using Smarty. I am generating an email's body through a template. Most times it works as expected, but from time to time, the returned data is empty. However, I do not see any error in my logs, neither I catch any exception. It is just as if the template was empty.
This is the piece of code I am using to get the email's body:
// $data is an array with template's data
// $tpl is the template's path
$s = new Smarty();
$s->assignArray( $data );
try {
$body = $s->fetch( $tpl );
} catch ( \Exception $e ) {
Debug::Log( $e->getMessage() );
}
// Sometimes $body is empty, but no exception is thrown.
I checked that the template has no errors, after all, it works in most cases.
I also saved $data contents when $body is empty and I ran the code manually to get $body content, but it worked, so I do not think the problem is related to template vars.
Another test I did is to try to process the template up to 5 times, sleeping for a second between the tries, but the result was always empty.
The template's cache path is writable.
I am using PHP 5.6.40, Smarty 3.1.21 and Apache2.
Can you give me a hand to debug this issue?
Update
I have been able to reproduce the problem. Smarty always returns an empty result whenever the fetch method is called after PHP detected that the client closed the connection. For example, take this code:
ignore_user_abort(1); // Continue running even if the connection is closed
set_time_limit(180); // 3 minutes
$s = new Smarty();
$s->assignArray( $data );
// Keep writing data untill PHP realises that connection was closed
while( 1 ) {
if(connection_status() != CONNECTION_NORMAL || connection_aborted( ) ) {
break;
}
echo "123456789";
}
$body = $s->fetch( $tpl );
if ( '' == $body ) {
throw new Exception("Result is empty");
}
die('Code never reaches this point');
If I call the script above and I close the connection immediately, the result of the fetch method is always empty.
However, if PHP did not detect that the connection was closed, even though it really was, the result of fetch is not empty.
ignore_user_abort(1); // Continue running even if the connection is closed
set_time_limit(180); // 3 minutes
$s = new Smarty();
$s->assignArray( $data );
// Sleep to make sure the connection was closed
// PHP do not realise the connection is closed untill it tries to write something
sleep( 60);
$body = $s->fetch( $tpl );
if ( '' == $body ) {
throw new Exception("Result is empty");
}
echo "Now the result is not empty";
This is the code I used to call the above scripts:
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://myhost/test.php');
curl_setopt($ch, CURLOPT_FRESH_CONNECT, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 1);
curl_exec($ch);
curl_close($ch);
echo "all done";
This seems to be related to this question: PHP ob_get_contents "sometimes" returns empty when it should not?
My script does a lot of things so it takes quite a long time to finish. Some users close their browser before the script finished, and that is when Smarty returns an empty result, as it uses ob_start a lot.
Best wishes,
As far as I'm concerned, it turns out to be a bug in PHP 5.6. I made some tests using print_r with the return flag set to true, and the result after closing the connection was never empty with PHP 7.0 and PHP 8.0. However, when I used PHP 5.6 the result was empty.
Example:
<?php
error_reporting( E_ALL );
ini_set('display_errors', 1);
ignore_user_abort(true);// (curl disconnects after 1 second)
ini_set('max_execution_time','180'); // 3 minutes
ini_set('memory_limit','512M'); // 512 MB
function testPrint_r($length)
{
$test1 = array('TEST'=>'SOMETHING');
$test2 = print_r($test1, true);
$test3 = "Array\n(\n [TEST] => SOMETHING\n)\n";
if(strcmp($test2, $test3)!==0) {
throw new Exception("Print_r check failed, output length so far: ".$length);
// consult your error.log then, or use some other reporting means
}
}
$message = "123456789\n";
$length = strlen($message);
$total_length = 0;
while(1)
{
echo $message;
$total_length += $length;
testPrint_r($total_length);
}
die('it should not get here');
Using PHP 5.6, if you call the script and close the connection, the Exception is thrown because print_r returns an empty result. However, using PHP 7.0 or PHP 8.0 the script keeps running until it reaches the maximun execution time.
Kind regards,