The up and coming PHP 5.4 NEWS contains a cryptic entry "Added closure $this support back". This inspired me to write a blog post on closures because we might to want use them in Drupal 8*. Here is the simplest closure example:
<?php
function simple_printer($name) {
return function () use ($name) {
print $name;
};
}
function super_printer() {
return function($name) {
print $name;
};
}
$foo_printer = simple_printer('foo');
$foo_printer();
print "\n";
$bar_printer = simple_printer('bar');
$bar_printer();
print "\n";
$super = super_printer();
$super('foo');
print "\n";
$super('bar');
?>
See how 'foo' gets "welded" into $foo_printer
? The technical terms for this is that the closure returned from the simpler_printer
function inherits $name
from the scope of the simple_printer
function. The closure returned from super_printer
does not inherit anything from its parent scope.
In PHP 5.4 you can do more:
<?php
class A {
private $value = 1;
function single_getter($name) {
return function() use ($name) {
return $this->$name;
};
}
function super_getter() {
return function($name) {
return $this->$name;
};
}
}
$a = new A;
$single_getter = $a->single_getter('value');
print $single_getter();
$super_getter = $a->super_getter();
print $super_getter('value');
?>
While there are many ways to interpret what $this
can mean in a closure, in PHP 5.4 it is inherited the same way as $name
is inherited in the single_getter
. Consider the following:
<?php
class A {
private $value = 1;
function single_getter($name) {
return function() use ($name) {
return $this->$name;
};
}
}
class B {
private $value = 2;
function test() {
$a = new A;
$single_getter = $a->single_getter('value');
print $single_getter();
}
}
$b = new B;
$b->test();
?>
This will print 1.
That's all. You still can't extend classes with new methods via closures or any other way. But this way you can crack the door a little by giving a setter to every property be it public or not. And, at least it will be an explicit call. Because PHP 5.4 has Traits (basically, compiler assisted copy-paste):
<?php
trait Sanity {
public function setter() {
return function ($property, $value) {
$this->$property = $value;
}
}
}
class A {
use Sanity;
private $value = 1;
function single_getter($name) {
return function() use ($name) {
return $this->$name;
};
}
}
$a = new A;
$setter = $a->setter();
$setter('value', 2);
$single_getter = $a->single_getter('value');
print $single_getter();
?>
Now, a class that has use Sanity;
can use __call
to implement some extension method. Still better than reflection...
* we definitely want closures in Drupal 8 if we think our developer community has grown too large -- this is a great way to chase a lot of them away and make sure we get few new ones.
Commenting on this Story is closed.
Thanks for the footnote. That's exactly what I was thinking :-)
We already have at least one (fake) closure in Drupal 7, in filter_xss() - we just have to do a silly confusing dance with static variables to work around the fact that PHP 5.2 doesn't actually support closures:
<?php
_filter_xss_split($allowed_tags, TRUE);
// ...
return preg_replace_callback('...', '_filter_xss_split', $string);
function _filter_xss_split($m, $store = FALSE) {
static $allowed_html;
if ($store) {
$allowed_html = array_flip($m);
return;
}
// ...
}
?>
db_query in D6 did the same thing.
Wouldn't this be easier to follow, with a real closure?
<?php
return preg_replace_callback('...', function($m) use ($allowed_tags) {
$allowed_html = array_flip($allowed_tags);
// ...
}, $string);
?>
Of course littering the code with closures just because they're cool would be a terrible idea. But I see nothing wrong with using them when they make things more maintainable than they are now.
thanks for bringing this up, Károly. New possibilities of php should be considered when planning ahead, like for Drupal 8. I am sure, that the Drupal developers will not use new features just because it's more fun.
There's a chance that you have created closures in the past, even without knowing it.
If you've created anonymous functions before, you have met closures :)
"Anonymous functions, also known as closures ..." http://php.net/manual/en/functions.anonymous.php
so adding a closure won't hurt there.
can you use $this in closure with PHP 5.4 ?
in PHP 5.3:
( ! ) Fatal error: Using $this when not in object context in /home/.../public_html/closure.php on line 10
once $this is added in "
<?php
return function() use ($name, $this) {
?>
( ! ) Fatal error: Cannot use $this as lexical variable in /home/.../public_html/closure.php on line 15
and even if you do something like :
<?php
$zis = $this;
return function() use ($name, $zis) {
?>
in both setter and single_setter, the properties cannot be accepted :
( ! ) Fatal error: Cannot access private property A::$value in /home/mrochette/public_html/closure.php on line 11