Using '$This' in an Anonymous Function in PHP Pre 5.4.0

Using `$this` in an anonymous function in PHP pre 5.4.0

It will fail when you try to call a protected or private method on it, because using it that way counts as calling from the outside. There is no way to work around this in 5.3 as far as I know, but come PHP 5.4, it will work as expected, out of the box:

class Hello {

private $message = "Hello world\n";

public function createClosure() {
return function() {
echo $this->message;
};
}

}
$hello = new Hello();
$helloPrinter = $hello->createClosure();
$helloPrinter(); // outputs "Hello world"

Even more, you will be able to change what $this points to at runtime, for anonymus functions (closure rebinding):

class Hello {

private $message = "Hello world\n";

public function createClosure() {
return function() {
echo $this->message;
};
}

}

class Bye {

private $message = "Bye world\n";

}

$hello = new Hello();
$helloPrinter = $hello->createClosure();

$bye = new Bye();
$byePrinter = $helloPrinter->bindTo($bye, $bye);
$byePrinter(); // outputs "Bye world"

Effectively, anonymus functions will have a bindTo() method, where the first parameter can be used to specify what $this points to, and the second parameter controls what should the visibility level be. If you omit the second parameter, the visibility will be like calling from the "outside", eg. only public properties can be accessed. Also make note of the way bindTo works, it does not modify the original function, it returns a new one.

PHP function returning anonymous function with use / global

I know that when I modify the above codes to the below one, then the code works perfectly. BUT I do NOT understand the reason why. Hope someone can explain it to me.

The reason it's not working as you expect is because your closure is returning another closure.

You can't call de-reference closures, but consider as an example of how it will work:

$k = function() {
return function() {
global $y;
return $y;
};
};

$first = $k();
print_r($first); // it's a closure, because your main closure returns another closure
$second = $first();
print_r($second); // here is "hello" as you expect

The following will not work:

print_r($k()());

In the case where you use $y when it doesn't exist, the process of returning a closure that uses an undefined variable actually creates a static property on the original closure with a null value, which is why you see this output:

var_dump($u());

object(Closure)#2 (1) {
["static"]=>
array(1) {
["y"]=>
NULL
}
}

Note that if you do the above with error reporting on you will get an undefined variable error as well.

You appear to be aware already, but I'll mention anyway that $y is not accessible inside the closure because it's outside the function's scope. This is why when you register it using global that it does return what you expect, and also when you use it from the outer closure.

Accessing the variables from a PHP Anonymous Function

Not going to happen. You need to make the static function public. The anonymous function doesn't run inside the scope of MyClass, and therefore doesn't have access to private methods contained within it.

Why use anonymous function?

I would say that anonymous functions show their beauty when there is good library classes/functions that use them. They are not that sexy by themselves. In the world of .net there is technology called LINQ that makes huge use of then in very idiomatic manner. Now back to PHP.

First example, sort:

uasort($array, function($a, $b) { return($a > $b); });

You can specify complex logic for sorting:

uasort($array, function($a, $b) { return($a->Age > $b->Age); });

Another example:

$data = array( 
array('id' => 1, 'name' => 'Bob', 'position' => 'Clerk'),
array('id' => 2, 'name' => 'Alan', 'position' => 'Manager'),
array('id' => 3, 'name' => 'James', 'position' => 'Director')
);

$names = array_map(
function($person) { return $person['name']; },
$data
);

You see how nicely you can produce array of names.

Last one:

array_reduce(
array_filter($array, function($val) { return $val % 2 == 0; },
function($reduced, $value) { return $reduced*$value; }
)

It calculates product of even numbers.

Some philosophy. What is function? A unit of functionality that can be invoked and unit of code reuse. Sometimes you need only the first part: ability to invoke and do actions, but you don't want to reuse it at all and even make it visible to other parts of code. That's what anonymous functions essentially do.

Use variables inside an anonymous function, which is defined somewhere else

The point of the use keyword is to inherit/close over a particular environment state from the parent scope into the Closure when it's defined, e.g.

$foo = 1;

$fn = function() use ($foo) {
return $foo;
};

$foo = 2;

echo $fn(); // gives 1

If you want $foo to be closed over at a later point, either define the closure later or, if you want $foo to be always the current value (2), pass $foo as a regular parameter.

Codeigniter Model Fatal error: Using $this when not in object context

Thanks to rexmarc, I was able to workaround the issue and make a similar structure work on PHP 5.3 by use-ing a copy of the object $this in the anonymous function.

I changed the following:

class Manage_plugins extends CI_Model {
public function __construct() {
$this->mediator->attach("manage_load", function($name, $data) { $this->on_manage_load(); });

$this->load->model("automediator");
}

}

into:

class Manage_plugins extends CI_Model {
public function __construct() {
$me =& $this;
$this->mediator->attach("manage_load", function($name, $data) use($me) { $me->on_manage_load(); });

$this->load->model("automediator");
}

}

Another solution for this could have been:

class Manage_plugins extends CI_Model {
public function __construct() {
$this->mediator->attach("manage_load", [$this, 'on_manage_load']);

$this->load->model("automediator");
}

}

The issue was occurring because in PHP versions prior to 5.4, $this was not available in anonymous functions.

5.4.0 - Anonymous functions may use $this, as well as be declared statically

Source: http://php.net/manual/en/functions.anonymous.php

The issue went unnoticed because of differing PHP versions on development (5.5) and production (5.3).

See also: https://stackoverflow.com/a/19432335/3649573

Laravel Cannot use $this Inside Anonymous Function

Replace:

$appointments = Label::with(['appointments' => function ($query) use ($this->labelId, $this->statusId, $this->monthId) {
$query->label($this->labelId)->status($this->statusId)->month($this->monthId)->get();
}])->get();

With:

$appointments = Label::with(['appointments' => function ($query) {
$query->label($this->labelId)->status($this->statusId)->month($this->monthId)->get();
}])->get();

You can't pass properties of the current object via use(), and you can't use($this), however, $this is always available in PHP 5.4+.

For this to work properly, you'll need PHP 5.4+. PHP 5.3 and below had a limitation where the local object context cannot be accessed from inside an anonymous function.

It is not possible to use $this from anonymous function before PHP 5.4.0

You could do something like:

$instance = $this;
$appointments = Label::with(['appointments' => function ($query) use ($instance) { // ... }

But then you couldn't access private or protected members; it would be seen as a public access. You really need PHP 5.4 :)



Related Topics



Leave a reply



Submit