The drop is always movingYou know that saying about standing on the shoulders of giants? Drupal is standing on a huge pile of midgetsAll content management systems suck, Drupal just happens to suck less.Popular open source software is more secure than unpopular open source software, because insecure software becomes unpopular fast. [That doesn't happen for proprietary software.]Drupal makes sandwiches happen.There is a module for that

Closures in PHP 5.3 and PHP 5.4

Submitted by nk on Thu, 2011-05-19 03:18

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.

Submitted by Anonymous on Thu, 2011-05-19 06:55.

Thanks for the footnote. That's exactly what I was thinking :-)

Submitted by Anonymous on Thu, 2011-05-19 08:22.

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.

Submitted by Anonymous on Thu, 2011-05-19 17:20.

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

Submitted by nk on Sun, 2011-05-29 02:48.

so adding a closure won't hurt there.

Submitted by Anonymous on Tue, 2011-07-05 12:26.

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