I am writing simple template engine and stack with problem: $this falls into eval scope even after unseting.
class foo
{
public function method()
{
$code = 'var_dump(isset($this));';
unset($this);
var_dump(isset($this)); // produce: boolean false
eval($code); // produce: boolean true
}
}
$foo = new foo;
$foo ->method();
How to avoid this without modification $code value?
I suggest to create unbound closure, where $this can not be accessed or might be replaced.
NOTE:
Closures are availible since PHP 5.3.0, as #invisal mentioned.
Closure::bindTo availible since PHP 5.4.0.
Anonymous functions in PHP 5.3.0 should not inherit $this from execution context. It was enabled since PHP 5.4.0. Just remove $evUl = $evUl->bindTo(null); from example below for PHP 5.3.0 and it will work as expected.
Example:
<?php
header('Content-Type: text/plain');
class foo {
public function method() {
$code = 'var_dump(isset($this));';
$evUl = function()use($code){ eval($code); };
$evUl = $evUl->bindTo(null);
$evUl();
}
}
$foo = new foo();
$foo->method();
?>
Shows:
bool(false)
$this can be unset as you have described.
Zend_Framework 1.x has an instance where they do this in Zend_Service_WindowsAzure_Storage_Batch class
Note this will not unset the instance, only the reference to the instance within the function scope.
Despite documentation this has been the observed behavior since php 5.1.6.
My only guess on why this won't work with an eval is that it must create a new execution context in which $this is restored into the current scope. This sample exhibits the behavior.
class A {
public function test(){
print "Before Unset\n";
print_r($this);
unset($this);
print "After Unset\n";
print_r($this);
print "Evaled\n";
eval("print_r(\$this);");
print "After Eval\n";
print_r($this);
}
}
$a = new A();
$a->test();
The Output of which is:
Before Unset
A Object
(
)
After Unset
PHP Notice: Undefined variable: this in /home/cgray/q.php on line 9
PHP Stack trace:
PHP 1. {main}() /home/cgray/q.php:0
PHP 2. A->test() /home/cgray/q.php:17
Evaled
A Object
(
)
After Eval
A Object
(
)
One way that you can dodge this issue is to unset $this in the evaluated context.
class foo
{
public function method()
{
$code = 'var_dump(isset($this));';
unset($this);
var_dump(isset($this));
eval("unset(\$this);".$code);
}
$foo = new foo;
$foo->method();
UPDATE
It appears as though includeing a file within the function scope will also restore $this
$this is a pseudo-variable, it cannot be unset.
http://php.net/manual/en/language.oop5.basic.php
It seem like unset($this) will only work within the function scope. I have confirmed with the test.
class foo
{
public function method()
{
unset($this);
var_dump(isset($this));
}
public function method2() {
var_dump(isset($this));
}
}
The $code running within function eval does not run within the function method. Confirmed with the test of $code = 'echo __FUNCTION__;'
Related
As I am learning how PHP OOP works, I stumbled upon the following confusion.
class Foo {
static function baz() {
echo 'works';
}
}
# 1
$a = 'Foo';
$a::baz();
# 2
Foo::baz();
PHP manual states that since 5.3.0, it is possible to reference a static class via a string (http://php.net/manual/en/language.oop5.static.php)
What I don't understand is, what is the different between #1 and #2? Aren't they technically the same since both are calling a static function without initiating a class? Where can #1 be applied in practical scenario?
The difference is in a version. Before PHP5.3 you cannot use static methods with variable.
Also, second one 'hide' using of a class. Your IDE could not find class usage.
Also you could use object to call static methods.
class Foo {
static function baz() {
echo 'works';
}
}
eval(
'$a = "Foo";
$a::baz();');
eval(
'$a = new Foo();
$a::baz();');
eval(
'Foo::baz();');
http://3v4l.org/WOK44
$ php --version
PHP 5.5.4 (cli) (built: Sep 19 2013 17:10:06)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2013 Zend Technologies
The following code (similar to example at https://bugs.php.net/bug.php?id=49543):
class Foo
{
public function bar()
{
return function() use ($this)
{
echo "in closure\n";
};
}
}
fails with:
PHP Fatal error: Cannot use $this as lexical variable
Yet according to the PHP docs and a comment on that bug report from Rasmus Lerdorf, using $this in anonymous functions was added as of PHP 5.4. What am I doing wrong?
So it seems $this can be used simply if it isn't specified via the "use" keyword.
The following echoes 'bar':
class Foo
{
private $foo = 'bar';
public function bar()
{
return function()
{
echo $this->foo;
};
}
}
$bar = (new Foo)->bar();
$bar();
This was reported in the php-internals mailing list and is apparently overhang from 5.3's lack of support for this functionality:
http://marc.info/?l=php-internals&m=132592886711725
In PHP 5.3 if you are using a Closure inside of a class, the Closure will not have access to $this.
In PHP 5.4, support has been added for the usage of $this in Closures.
I don't know the answer to your actual question (ie Why can't you do it), but I can give you a work around: Use a temporary copy of $this and use() that instead:
class Foo
{
public function bar()
{
$that = $this;
return function() use($that)
{
print_r($that);
};
}
}
I've just tested it, and this does work.
The issue is that including $this in the use() statement is not allowed.
However if you don't include it then it will work fine.
So the issue isn't whether the use statement is present, it's whether $this is present in the use statement.
This should work (#see https://3v4l.org/smvPt):
class Foo{
private $a;
function getAnon(){
$b = 1;
return function() use ($b) {
echo $b;
echo $this->a;
};
}
}
This shouldn't:
class Foo{
private $a;
function getAnon(){
$b = 1;
return function() use ($this, $b) {
echo $b;
echo $this->a;
};
}
}
I suppose essentially $this is implicitly captured.
You can use this:
class Foo
{
public function bar()
{
$obj = $this;
return function() use ($obj)
{
//$obj->DoStuff();
echo "in closure\n";
};
}
}
I know, this is an old question, but maybe someone from google find this:
The reason why you get the error is, because you can't use an already defined variable name as lexical variable in the same closure.
Since in PHP 5.5 and above you can access $this inside the closure, a variable with the name $this already exists.
Here is another example, where you would get the same error:
$item = "Test 1";
$myFnc = function($item) use ($item) {
...
}
$myFnc("Test 2");
As you can see, the $item is already used as closure parameter, so you can't us it lexical variable.
It may be a bug, but there is no sense in explicit binding $this to a function anyway as it is automatically bound:
PHP documentation says
As of PHP 5.4.0, when declared in the context of a class, the current
class is automatically bound to it, making $this available inside of
the function's scope.
Thus, fatal error is thrown in today's version of PHP:
From PHP 7.1, these variables must not include superglobals, $this, or variables with the same name as a parameter.
I'm using PHP 5.4.25 and actually I am able to use class variables in a closure also with the use keyword as shown below:
class Foo
{
private $_privateBar = 'private bar';
protected $_protectedBar = 'protected bar';
public $_publicBar = 'public bar';
public function bar()
{
$prefix = 'I am a ';
return function() use ($prefix) {
echo $prefix . $this->_privateBar . "\n";
echo $prefix . $this->_protectedBar . "\n";
echo $prefix . $this->_publicBar . "\n";
};
}
}
$foo = new Foo();
$bar = $foo->bar();
$bar();
Output:
I am a private bar
I am a protected bar
I am a public bar
I'm trying to store anonymous functions in a static array property of my class. These functions should be invoked later by their index, but calling
self::$arr['index']()
just doesn't work, while
$a = self::$arr['index'];
$a();
does!
This doesn't work:
class A {
private static $func = array('a' => '');
public function __construct() {
self::$func['a'] = create_function('$str', 'echo "$str";');
}
public function go($str) {
self::$func['a']($str); // Call the function directly
}
}
$a = new A();
$a->go("hooray"); // Outputs "Undefined variable: func"
But this does:
class A {
private static $func = array('a' => '');
public function __construct() {
self::$func['a'] = create_function('$str', 'echo "$str";');
}
public function go($str) {
$a = self::$func['a']; // Pass the function name to a variable
$a($str); // Call the function via the variable
}
}
$a = new A();
$a->go("hooray"); // Outputs "hooray"
Why?
I'm using PHP Version 5.4.3
this is the behavior of php's parser
$functionName['a'] = "hello";
self::$functionName['a']();
calls
self::hello();
... the very sad thing is that in php you can't do this:
(self::$functionName['a'])(); // doesn't work in php :(
as you can do in javascript, for example
what you can do is... use a temporary variable like you said
$a = self::$func['a'];
$a($parameter);
or
call_user_func(self::$func['a'], $parameter);
hope this helps...
in latest phps these features were added
$a['sss'] = function(){ echo 'bla'; };
$a['sss']();
class Bla
{
private $a;
function test()
{
$this->a['sss'] = function(){ echo 'bla2'; };
$this->a['sss']();
}
}
$c = new Bla();
$c->test();
and they work properly... so for some reason, this syntax doesn't work only when using the scope resolution operator ( ClassName:: self:: etc)
Well, in php you simply can not do that, it is a php feature. But you can use call_user_func or its relatives:
return call_user_func(self::$func['$a'], $str);
This is a consequence of how the PHP parser currently works. Since the function call () is evaluated before the static operator ::, you end up with the parser attempting to reference the local variable $func instead, and then giving you the error about $func being undefined (which it is, since there is no variable named $func in the method).
As you've discovered, you can solve this by doing two separate statements.
Helo everyone.
I have a class MyClass and a function escape() that can be called as a static class or as an instantiated Object.
MyClass::_escape('..... some string...');
or
$myclass->escape();
What I would like is not to have the underscore on the staic version and for both just have the same function definition. I trie to do.
class MyClass {
public $_string = "";
public function escape($string = null) {
if($string == null)
return new String(mysql_real_escape_string($this->_string));
else
return new String(mysql_real_escape_string($string));
}
}
but this function fails by the PHP parser. Is there a way of doing what I attempted to above??
So to summarise, I would like the static call to look like;
print Myclass::escape('some string');
and the instantiated call to look like;
print $myobject->escape(); //Which escapes the private variable _string
Hope this was clear.
regards
public function _escape($s){
return self::escape($s);
}
What you're trying to achieve won't work without at least some kind of error:
Example using static:
error_reporting(E_ALL ^ E_STRICT);
class MyClass
{
// note the *static* keyword
public static function escape($string = null) {
// $this is not defined, even if called as object-method
var_dump(isset($this));
}
}
$foo = new MyClass();
$foo->escape(); // => bool(false)
MyClass::escape(); // => bool(false)
So, if you remove the static keyword and try again, you'll get:
$foo->escape(); // => bool(true)
but also:
Strict Standards: Non-static method MyClass::escape() should
not be called statically ...
for
MyClass::escape(); // => bool(false)
There are no parse errors in the code you posted. In fact, it works just as you want it to work, as long as you never pass $string to the escape() method in an object context:
$foo = new MyClass();
$foo->_string = 'foo';
$foo->escape(); // This works.
MyClass::escape('bar'); // This works, too.
$foo->escape('baz'); // Don't do this. It'll escape $string instead of $this->_string.
You could resolve this issue by determining whether or not you're in a static context within the escape() method, instead of checking for the existence of $string.
I have a recursive lambda function in one of my objects, and it needs to access the object's mysqli connection. This attempt
$recfunc = function($id, $name) use($this) {
Produced an unreasonable fatal error
Fatal error: Cannot use $this as lexical variable in C:\Users\Codemonkey1991\Desktop\workspace\melior\objects\databasemanager.php on line 88
Could anyone give me a few pointers?
Edit: Just to clarify context, I'm trying to create this lambda function inside another function.
Because closures are themselves objects, you need to assign $this to a local variable, like:
$host = $this;
$recfunc = function($id, $name) use ($host) { ...
The reference to $this does not need to be explicitly passed to the lambda function.
class Foo {
public $var = '';
public function bar() {
$func = function() {
echo $this->var;
};
$func();
}
}
$foo = new Foo();
$foo->var = 'It works!';
$foo->bar(); // will echo 'It works!'