I have an output buffer with callback function. When cleaning the buffer the callback function is executed, however, the string returned isn't being altered.
I'm using following code:
<?php
ob_start('callback');
print 'some text';
error_log(ob_get_clean());
function callback($content) {
error_log('callback');
return $content . ' altered';
}
Output:
callback
some text
What I want:
callback
some text altered
What am I missing? I'm using PHP 5.3.10 in CLI.
Edit: the callback is being executed.
From the PHP manual:
The function will be called when the output buffer is flushed (sent)
or cleaned (with ob_flush(), ob_clean() or similar function) or when
the output buffer is flushed to the browser at the end of the request.
I'm not sure if this is a bug or a feature. Looking at the PHP source code, I found that the return value of ob_get_clean is filled before the callback is invoked.
I see at least two workarounds. The first is to manually invoke the callback on the output string yourself. I think this needs no example.
The second is to exploit the possibility to stack output buffering. Since flushing successfully uses the callback, you can wrap the output code inside an additional output buffer and get the modified contents.
ob_start();
function callback($input) { return $input . " altered"; }
ob_start('callback');
echo "foo";
ob_end_flush();
$content = ob_get_clean();
ob_end_clean();
echo $content . "\n"; // prints "foo altered\n"
See the source code for ob_get_clean (main/output.c) if you're curious. You can get the source code at the PHP website. Here are some pointers.
/* {{{ proto bool ob_get_clean(void)
Get current buffer contents and delete current output buffer */
PHP_FUNCTION(ob_get_clean)
{
if (zend_parse_parameters_none() == FAILURE) {
return;
}
// THIS CALL FILLS THE RETURN VALUE
if (php_ob_get_buffer(return_value TSRMLS_CC) == FAILURE) {
RETURN_FALSE;
}
if (!OG(ob_nesting_level)) {
php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer. No buffer to delete");
zval_dtor(return_value);
RETURN_FALSE;
}
if (OG(ob_nesting_level) && !OG(active_ob_buffer).status && !OG(active_ob_buffer).erase) {
php_error_docref("ref.outcontrol" TSRMLS_CC, E_NOTICE, "failed to delete buffer %s", OG(active_ob_buffer).handler_name);
zval_dtor(return_value);
RETURN_FALSE;
}
// THIS CALL KILLS THE CURRENT BUFFER AND EXECUTES THE CALLBACK
php_end_ob_buffer(0, 0 TSRMLS_CC);
}
/* }}} */
php_end_ob_buffer takes the contents of the OB buffer and applies the callback to it. If the first parameter is true, it passes the contents to the next output buffering handler. In this case it's false, so the content is lost even though it executed the callback.
If i had a guess it would be this.
ob_get_clean() is returning the result of the buffer, and THEN cleaning it, triggering the callback which modifies the content.
IE
'some text' is extracted from the buffer and prepared to be returned as per the requirement of the 'get' functionality.
Next, the buffer is cleaned, but before cleaning the callback is triggered against the content, as per the requirement for various ob functions when a callback is present.
The result is that the buffer is returned (as requested) but it is modified after, since the get happens before the clean.
It will be called when buffer is flushed:
ob_start('callback');
print 'some text';
ob_end_flush();
function callback($content) {
return $content . ' altered';
}
P.S.
it works in CLI too.
The CLI for PHP does not use output buffering (or more specifically the buffing is not related to the ob_ functions). So your callback is being skipped.
EDIT: Actually I'm having trouble confirming whether standard output buffering is available at the CLI. I would try ob_end_flush(), ob_flush(), and flush().
I remove ob_get_clean and your code does work.
ob_start('callback');
print 'some text';
//error_log(ob_get_clean());
$buffer = ob_get_flush();
function callback($content) {
return $content . ' altered';
}
I've check the output and it was some text alerted.
why you use ob_get_clean() method? it cleans the buffer.
<?php ob_start('callback'); ?>
Foo Bar Baz
<?php
ob_end_flush();
function callback($content) {
$find = "Baz";
$replace_with = "Foo";
return (
str_replace($find, $replace_with, $content)
);
}
?>
Related
I have this code
<?php
function testFunc($b){
ob_start();
echo "print from callback\n";
return ob_get_clean();
}
ob_start('testFunc');
echo 'print from buffer';
ob_end_flush();
But I have a following error ob_start(): Cannot use output buffering in output buffering display handlers
I expected the result
print from callback
Please, dont suggest simplify this code, because in my codebase I have nested buffers like this
As said. "You're trying to start a output buffer inside a buffer callback. If you use this code, it will generate that error. But if you remove the ob_start() from the callback function it's OK."
And, if you expected the result to print, return the string or anything else you want to print:
function testFunc($b){
//ob_start();
return "print from callback\n";
//return ob_get_clean();
}
ob_start('testFunc');
echo 'print from buffer';
ob_end_flush();
PHP 7.2 :
Is there a way to enforce "automatically" calling of a php function prior to the first byte that is sent out to the client?
(HTML Tags or anything)
Eg: songs.php:
// Please ignore spelling mistakes, and work on concept alone.
require_once('sessionSetup.php');
require_once('setup_Pre_HTML_Tag_Transmission_Enforcer.php');
// The above has a function called: doMyHTMLTags();
doMyStuff(); // Setups, validations
doMoreStuff();
doHTMLContentDisplay();
// I need to execute doMyHTMLTags(), if and when any of the functions starts sending out displayable text.
Example: If doMoreStuff does a DIE('No resources'); or if doMyStuff does a { echo 'unexpected issue'; exit; }, I still need mydoMyHTMLTags() to be executed.
Any help would be appreciated.
Didn't try it, but maybe ob_start would do the trick:
ob_start(
function($buffer) {
// nothing was produced
if (strlen($buffer) === 0) {
return false;
}
// prepend our string
return doMyHTMLTags() . $buffer;
}
);
doMyStuff(); // Setups, validations
doMoreStuff();
doHTMLContentDisplay();
If doMyHTMLTags() doesn't return string, but it is printing it to the browser, you can try this (but it will always call doMyHTMLTags):
// get our string from output
ob_start();
doMyHTMLTags();
$my_html_tags = ob_get_clean();
ob_start(
function($buffer) use ($my_html_tags) {
// nothing was produced
if (strlen($buffer) === 0) {
return $buffer;
}
// prepend our string
return $my_html_tags . $buffer;
}
);
doMyStuff(); // Setups, validations
doMoreStuff();
doHTMLContentDisplay();
So I want to be able to get the live feedback from a shell script in a Symfony project. Normally I would do something like (in filename.php):
<html>
<head>
</head>
<body>
<?php echo getTheStuff(); ?>
</body>
</html>
<?php
function getTheStuff() {
while (# ob_end_flush()); // end all output buffers if any
$cmd = './test.sh';
$proc = popen($cmd, 'r');
echo '<pre>';
while (!feof($proc))
{
echo fread($proc, 4096);
# flush();
}
echo '</pre>';
return "OK";
}
This code will look to the script file and keep sending through the data as available. This means the browser will show the output of test.sh as it is processed.
Because Symfony uses views to output, it waits until the controller is finished processing before outputting any html. So is there any way I can do this sort of a thing in Symfony?
YMMV but Symfony includes a StreamedResponse that might do what you need - you don't necessarily need to return a HTML view. Indeed there are several Response types - JsonResponse, FileResponse etc.
Note that
If ob_start() has been called before or the output_buffering php.ini
option is enabled, you must call ob_flush() before flush().
I have added this to the code below as this is quite often the case, you may be able to remove the ob_flush() lines and have it still work.
As per the docs linked above:
use Symfony\Component\HttpFoundation\StreamedResponse;
// etc.
public function getTheStuffAction(): StreamedResponse
{
$response = new StreamedResponse();
$response->setCallback(function () {
var_dump('Hello World');
ob_flush();
flush();
sleep(2);
var_dump('Hello World');
ob_flush();
flush();
});
return $response->send();
}
Hope this helps :)
When I call a function of another class (in a included file), some text is being output with echo. I need to store this in a variable.
Here is the code:
require_once('../restapis/api.php');
class ApiTest
{
public function testapis(){
$api = new Api();
$api->validate_request();
}
}
$obj = new ApiTest();
$obj->testapis();
And I am getting a JSON string echoed in browser:
{"ERRORCODE":"E032","ERRORMESSAGE":"Invalid URL."}
I don't have permission to change anything in the api.php file, that's why I can't change echo to return.
Is there any way I can do this?
You could use output buffering.
ob_start(); // Activate output buffering
$obj->testapis(); // Whatever code whose output you want to capture
$contents = ob_get_contents(); // Store buffered contents
ob_end_clean(); // Deactivate output buffering
After that, $contents will contain the echoed output.
I've got a script that runs a custom email obfuscation class's Obfuscate() function on the content before displaying it, as follows:
ob_start(array($obfuscator, "Obfuscate"));
include('header.php');
print($html);
include('footer.php');
ob_end_flush();
That all works great. However, I've completely rewritten my view architecture, so I need to run the email obfuscation from within a class function and return that string (which then gets echoed). I initially rewrote the above as:
ob_start(array($this->obfuscator, "Obfuscate"));
include('header.php');
echo($this->content);
include('footer.php');
$wrappedContent = ob_get_contents();
ob_end_clean();
Unfortunately, the $this->obfuscator->Obfuscate() callback is not being fired. I have since learned that ob_get_contents() does not fire the callback, but have tried ob_get_clean() & ob_get_flush() to no avail as well.
So, how can I get the contents of the buffer after the callback has been fired?
Of course, I was overlooking the fact that the only reason to use the callback on ob_start() was because I wanted to run Obfuscate() on the content before it was flushed, but if I'm getting that content back I don't need to run a callback! So, not using a callback and just running ob_get_clean()'s results through Obfuscate() does what I wanted. Doh!
ob_start();
include('header.php');
echo($this->content);
include('footer.php');
return $this->obfuscator->Obfuscate(ob_get_clean());
Change
ob_end_clean();
with
ob_clean()
Will trigger .
This is the code eg I tried
<?php
class Obfuscate {
public function __construct()
{
}
public function getObfuscate()
{
return "obfuscate";
}
}
class Example
{
public function hello( $obfuscator )
{
ob_start(array( $obfuscator, 'getObfuscate' ));
include('header.php');
echo "Thi is a content ";
include('footer.php');
$wrappedContent = ob_get_contents();
ob_clean();
}
}
$obfuscator = new Obfuscate();
$example = new Example;
$example->hello($obfuscator);
ob_clean();