First excuse my english I'm not a native speaker and sorry if it looks rough, this is the first time that I post on this site.
My problem is quite simple I think. Let's say, we have :
class A {
function foo() {
function bar ($arg){
echo $this->baz, $arg;
}
bar("world !");
}
protected $baz = "Hello ";
}
$qux = new A;
$qux->foo();
In this example, "$this" obviously doesn't refer to my object "$qux".
How should I do to make it reffer to "$qux"?
As might be in JavaScript : bar.bind(this, "world !")
PHP doesn't have nested functions, so in your example bar
is essentially global. You can achieve what you want by using closures (=anonymous functions), which support binding as of PHP 5.4:
class A {
function foo() {
$bar = function($arg) {
echo $this->baz, $arg;
};
$bar->bindTo($this);
$bar("world !");
}
protected $baz = "Hello ";
}
$qux = new A;
$qux->foo();
UPD: however, bindTo($this)
doesn't make much sense, because closures automatically inherit this
from the context (again, in 5.4). So your example can be simply:
function foo() {
$bar = function($arg) {
echo $this->baz, $arg;
};
$bar("world !");
}
UPD2: for php 5.3- this seems to be only possible with an ugly hack like this:
class A {
function foo() {
$me = (object) get_object_vars($this);
$bar = function($arg) use($me) {
echo $me->baz, $arg;
};
$bar("world !");
}
protected $baz = "Hello ";
}
Here get_object_vars()
is used to "publish" protected/private properties to make them accessible within the closure.
Actually, $this
does refer to $qux
when called in that context.
You can't use $this
in contexts other than an object method, so if you took something like this:
function test() {
echo $this->baz;
}
It wouldn't work, no matter what you do.
it is a little strange to to it this way, but if you use a function inside of a function, then of course some closuers are in effect.
you can have a similar problem in javascript.
I would suggest not to use them at all, but if for some reason you do not want to, then i would try sth like this (not tested):
class A {
function foo() {
$that = $this;
function bar ($arg){
echo $that->baz, $arg;
}
bar("world !");
}
protected $baz = "Hello ";
}
$qux = new A;
$qux->foo();
UPDATE
if the nested function acts as a global function, then you coud pass your object as a parameter if you need to use it:
function foo() {
function bar ($o,$arg){
echo $o->baz, $arg;
}
bar($this,"world !");
}
If I understand what you're trying to achieve, it could be like this:
<?php
class Foo {
public $prop;
public function __construct($prop) {
$this->prop = $prop;
}
public function __call($name, $arguments) {
if (!isset($this->$name)) {
throw new InvalidArgumentException("No such property: $name");
}
if (! $this->{$name} instanceof Closure) {
throw new InvalidArgumentException("{$name} is not a closure");
}
$c = $this->{$name}->bindTo($this);
return $c(...$arguments);
}
}
$f = new Foo("bar");
$f->bar = function ($arg) {
print_r([$arg, $this->prop]);
};
$f->bar("A");
?>
That way you can essentially monkey-patch any function in a class and call it as if it was local, even including use of $this
. Two limitations, however:
- this seems pointless since Traits are available, except if you are doing adding really dynamically defined on a non-modifiable package
- although the closure can access properties on
$this
it is still external to the object, so can only access public properties. Trying to access protected
/private
properties will fail.