Class object not working inside ob_start callback - php

I don't know why, but this code worked for me a month ago... maybe I upgraded the php but can't remember. Tried this with PHP 5.2.17 and 5.3.6
Why is it not possible to use a class object inside the callback of a ob_start function?
<?php
$f=new stdClass();
$f->title="awesome Title";
function callback($buffer)
{
global $f;
$buffer=str_replace("###TITLE###", $f->title, $buffer);
return $buffer;
}
ob_start("callback");
?>
This is the ###TITLE###
Output is:
PHP Notice: Trying to get property of non-object in /Users/qxxx/Sites/test/test.php on line 8
This is the
should be:
This is the awesome Title

This is because the output buffer is being implicitly flushed by the termination of the script.
At this point PHP has already destroyed unreferenced variables, so when it comes to execute your callback function, the variable $f does not exist in the global scope.
You can solve this by explicitly flushing the buffer before shutdown starts destroying objects, by placing the following line somewhere in your script.
register_shutdown_function('ob_end_flush');
Edit:
I'd like to add that even though this is currently the accepted answer that explains the "why", the solution provided here does not address the root cause of the issue; the fact that global is being used.
Many people will tell you that global is evil, without giving a reason why. Here you can see one of the reasons.
The answer provided by Jack gives a more "best practice" solution (using closures to maintain the variable reference), and should be considered as the proper way to avoid using global in new codebases.

The reason for this has been outlined well by Leigh. Using a closure would work better in this case:
ob_start(function($b) use ($f) {
return str_replace('###TITLE###', $f->title, $b);
});
This is because the closure will keep the reference to $f alive by the end of the script so that it won't get garbage collected before running the callback function.

From the php manual page of ob_start, and a bug report I learned that, since 5.2, all objects are destroyed #ob_start
This function's behaviour has been changed in php 5.2.0:
<?
global $AP;
$AP = new ap;
ob_start("ob_end");
function ob_end()
{
global $AP;
$r = $AP->test();
return $r;
}
class ap
{
function test()
{
return "debug";
}
}
?>
In older versions it shows: "debug".
But latest php version causes error: PHP Fatal error: Call to a member function test() on > a non-object.
And this is not a bug: http://bugs.php.net/bug.php?id=40104
from the man pages

Related

php 7.3 - Class constants as default param values crashing

I have some code that has been working for a long time, but is now producing perplexing errors. I have to assume this was caused by my moving to php 7.3, but I can't find any references that explain what is happening.
I have a method in a class that looks something like:
class Foo {
function bar($param = OtherClass::MY_CONSTANT) {
logger(__METHOD__);
logger(OtherClass::MY_CONSTANT);
logger($param);
logger('ready to do stuff');
// does stuff
}
}
Where OtherClass::MY_CONSTANT = 1.
When I call that method from another class:
$foo = new Foo();
$foo->bar();
the output is something like:
DEBUG - Foo::bar
DEBUG - 1
and then php execution stops abruptly, not even calling my registered shutdown function. I am able to use the class constant directly in the body of the method, but I am not able to use the parameter set to the class constant's value by default.
If I change the method to
function bar($param = 1) {
everything works fine.
Also, if I pass the constant when I call the method it works:
$foo = new Foo();
$foo->bar(OtherClass::MY_CONSTANT);
I get the happy
DEBUG - Foo::bar
DEBUG - 1
DEBUG - 1
DEBUG - ready to do stuff
and execution continues normally.
Something about using a class constant as a default is making the variable poisonous, even though I can use the class constant in the method without any trouble.
I tried to create a simple one-file example to recreate this problem, but it worked just fine. Aargh.
Has something changed in php 7.3 that would cause this behavior? I just upgraded to 7.3.1 but the problem persisted. Is there a better practice I should be using?
EDIT TO ADD:
After a few hours trying to make a simple case to reproduce this, I have to move on. Relevant factors include:
running in php-fpm
code running after closing the connection to the browser.
even constants in the same class (self::MY_CONSTANT) create poison variables
My solution was to roll back to php 7.2.14, which is working properly. I can only assume this in a bug in php 7.3.
As a workaround, you can do it like this:
function bar($param = null) {
if ($param === null) {
$param = OtherClass::MY_CONSTANT;
}
logger(__METHOD__);
logger(OtherClass::MY_CONSTANT);
logger($param);
logger('ready to do stuff');
// does stuff
}
This assumes that null is not a valid value for the parameter; replace that with some other invalid value if it is. If the parameter can legitimately be anything, this workaround won't work.

PHP: class variable visibility

I'm just a once-in-a-while PHP developer. Now, working on a legacy application, I just hit upon the following problem, which seems very stupid. But I can't get $someString class variable to hold the right value:
class MyClass{
var $someString;
function doSomething(){
//$this->setString(); //this is effectively not called here, but later in getIframe()
$this->buildIframe();
echo $this->someString; //actually, I need someString here, but it is empty
}
function setString(){
$this->someString = "something";
}
function buildIframe(){
$content .= <iframe....>;
}
function getIframe(){
$this->setString();
}
}
$myClassInstance = new MyClass();
$myClassInstance->doSomething();
$myClassInstance->getIframe();
As far as I can see, doSomething() is called in a class context, as I did show.
What am I doing wrong?
EDIT:
I reviewed the code and I think I found whats causing this. There is an iframe embedded into the html output, which is generated at one part and called later on. So the setString() method is actually not called imediately, what I thought first but when invoking the iframe code. So thats why it's not available where I need the string output.
I guess like the code is now, there is no way to get the $someString output except inside the getIframe() method.
This code is 100% correct and working. I've checked it on PHP 5. It echos "something" in string. And it is proper behavior.
From manual:
Note: The PHP 4 method of declaring a variable with the var keyword is still supported for compatibility reasons (as a synonym for the public keyword). In PHP 5 before 5.1.3, its usage would generate an E_STRICT warning.

php - Access outer class from an anonymous callback

I have a code like this:
class Server {
private $stopper;
public function setStopper() { $this->stopper = TRUE; }
public function startServer() {
$consumer = new Consumer();
$consumer->onConsume(function($data) {
global $consumer;
// some processing
if( ?? ) { // how to access stopper here??
$consumer->stop();
// also how to access stopServer() here??
}
});
$consumer->consume();
}
public function stopServer() { ... }
}
This code is in a file that should run forever unless the setStopper() is called. So far I have set_time_limit to stop the code after a while. But I need to implement setStopper way so I can stop the server whenever needed, not "after a while".
I need this because, the onConsume is connected to a streaming API and runs the anonymous call back whenever new data is available and I don't want kill the php app on timeout due to some lock issues. I want to gracefully stop the server.
Can anyone please tell how to access the stopper or stopServer inside the callback? Can I use following syntax?
...(function($data) use ($this) {...
I also thought of storing the class value inside callback, but the setStopper is called dynamically and the value might not be updated!
Is there a better way to handle this situation?
Follow Up: php - Dynamically update Singleton class' value
You can create a Closure around the $consumer object as well as the lexical object $this (if you're using PHP < 5.4, you need to rename $this to something else, because you cannot use($this)):
$self = $this;
// You may not need to do this, I cannot remember off-hand whether
// closures have access to private variables or not
$stopper = $this->stopper;
$consumer->onConsume(function($data) use($consumer, $self, $stopper) {
if( $stopper ) {
$consumer->stop();
$self->stopServer();
}
});
See Example #3 on the linked to manual page.
I should also note here for completeness that if this is a long-lived process, then the objects being referenced inside the closure will hang around long after the function exits. For instance:
function makeAdder($x) {
return function($n) use($x) {
return $x + $n;
};
}
$adder = makeAdder(5);
echo $adder(2); // Output 7
echo $adder(5); // Output 10
echo $adder(4); // Output 9
This is a classic example of a closure. Normally, once the makeAdder function returns its inner variable $x will fall out of scope and be ready for garbage collection. Since it is however bound inside the anonymous function's scope, it will hang around indefinitely (until the script's termination) or the reference to the containing scope is also released (i.e. via unset($adder)). This means that once your function is called, extra references to $consumer, $this and $stopper will hang around until the class instance itself is destroyed.
Not being aware of this can lead to some serious performance issues.
the same problem here i use output buffers from ob_start/ob_end_flush
and one function i have should be dynamic (however parameters i push into should insert them in an array for later use for parsing buffers using $buffer)
in the parser associated to ob_start at a time i have these lines of code from one array full of data :
if(!empty($this->__b2))
array_filter ($this->__b2,function($var)use(**&$buffer**){
$buffer = preg_replace("/<\/body>/i", $var.'</body>', $buffer);
});
I use a only one class singleton ,and i use "::" a lot.
How you see in my case
array_filter was out of order without &$buffer

A function inside an if structure

can I put a function in PHP inside a if structure? like this:
<?php
if(true){
function HelloWorld(){
echo "Hello World!!!";
}
HelloWorld();
}
?>
because I have tried and it works, but I don't know if it is correct or not. Thanks
This is perfectly legal - it simply defines the function within the if statement block. That said, quite why you'd want to do this is somewhat of a mystery.
It's also worth noting that this function will be available in the global scope (i.e.: outside of the if statement block), as...
All functions and classes in PHP have
the global scope - they can be called
outside a function even if they were
defined inside and vice versa.
See the PHP User-defined functions manual page for more information.
As middaparka says, this is perfectly legal, but in terms of using it, you might want to check if a function exists before declaring it:
if (!function_exists("foo"))
{
function foo()
{
return "bar";
}
}
function_exists documentation
It looks a bit strange, but yes it's legal
As of PHP 5.3 if you need a function with a limited scope (say for a callback) you can use anonymous functions / closures
I wanted to add an answer here, because there's a caveat that isn't addressed in the other answers.
Based on my testing in 5.3, it appears that functions that are defined inside if structures are defined at runtime, rather than at compile time like other functions.
For example, outside a conditional block, this:
foo();
function foo(){
echo 'foo!';
}
works fine because the function is defined at compile time, and executed at runtime. But calling the function before defining it while inside a condition block:
if(1){
foo(); // Call to undefined function!
function foo(){
echo 'foo!';
}
}
Will produce a call to undefined function error, at least as of version 5.3.
But defining the function in an if block before calling it is valid:
if(1){
function foo(){
echo 'foo!';
}
foo(); // No errors, since function is defined!
}
So there is a major gotcha to be aware of when defining functions inside a conditional: the function must be defined in the code before calling the function, since conditional functions don't seem to get defined at compile time.
The OP also asked about performance. If the conditional function is defined at runtime instead of compile time like most other functions, then this function will not benefit from performance boosters like OpCode caching, which depending on the circumstances, could slow down your application. For example, if your master php file looked like:
if($IsLoggedIn){
// include files/functions and bootstrap here...
}
Then the entire application might not benefit from OpCode caching at all.
Basically yes, according to the manual:
Any valid PHP code may appear inside a
function, even other functions and
class definitions.
Personally I haven't had a situation in which I'd actually do such a thing. Most of the time it would be easier to group your functions on a place where it actually can be found! ;-)

Is there a way to avoid the "redeclared function" fatal error while defining functions within functions in PHP?

I've been implementing a certain plugin (dtabs) on my page in Wordpress but after upgrading to the latest version, I found that I now have an error the 2nd time I call the main function called dtab_list_tabs().
The way it works is, the plugin gets include_once'd but the main function is called however many times you want to place tabs in your layout. I have 2 such calls to dtab_list_tabs().
Now, the problem is, for whatever reason the developer decided to include another function directly inside dtab_list_tabs() called current_tab(). Because it's declared within a function, apparently PHP tries to redeclare it as soon as you call the parent function the 2nd time, which doesn't make any sense to me.
PHP Fatal error: Cannot redeclare current_tab() (previously declared in .../wp-content/plugins/dtabs/dtabs.php:1638) in .../wp-content/plugins/dtabs/dtabs.php on line 1638
The code for that revision is at http://plugins.svn.wordpress.org/!svn/bc/208481/dtabs/trunk/dtabs.php
What I'm trying to figure out is whether there is a way to tell PHP that yeah... it has an internal function, which is a perfectly valid PHP paradigm as far as I know, so don't redeclare it and fail.
As for the situation at hand, I have removed current_tab() as it doesn't appear to be used.
You can use function_exists() to test if a function with that name has already been defined. If you make the definition conditional ( if(something) { function foo() {...} } ) php will "evaluate" the definition only when the condition is met.
function foo() {
if ( !function_exists('bar') ) {
function bar() {
echo 'bar ';
}
}
bar();
}
foo();
foo();
see also: http://docs.php.net/functions.user-defined
(But I'd try to avoid such things all together)
You can wrap your function declaration in an if statement. Use function_exists() to see if the function has been previously declared or not.
if(!function_exists('current_tab')) {
function current_tab() {
myMagicCode();
}
}
You can try this:
if (!function_exists('my_function')) {
function my_function() {
}
}
function_exists() - Return TRUE if the given function has been defined

Categories