Closure Object Binding in PHP 5.4
christophh.net
christophh.net
$app->get('/', function() use ($app) {
$request = $app['request'];
});
The use keyword is good for importing anything from the current context into the closure. However, I'm not sure it's a good idea in this case because it seems unnecessary - which makes the example a bit of a straw man: The $app object already knows about itself, so why not do the simpler and more loosely bound version: $app->get('/', function($app) {
$request = $app['request'];
});
then when calling the closure, the object just has to do this: $closure($this);
That way you don't have to remember to "use ($app)" every time you define a closure (and I'm also not wild about overlapping variable names from different contexts).Now we have the new bindTo() method which results in yet more extra code:
$boundClosure = $closure->bindTo($context);
Granted, you could define a general bindTo mechanism once per class implementation, and being able to use $this inside the closure is nice, but I'm not sure this justifies an addition to the language's complexity when a perfectly fine method has been around that does the same thing (and with less code).The new array syntax, closures, binding ... I'm surprised you're not seeing the pattern. All of these features are borrowed from JavaScript. Absolutely fine by me. I already spend most of my time in JavaScript anyway. I'm willing to bet I'm not alone, hence the changes.
Oh well, it's in there now, so I guess there is no point of discussing it. But I do believe, on a more general note, that saying "no" to features is important - and bindTo() would have been an excellent point to say no to. For better or worse, PHP is not JavaScript and vice versa. Their paradigms are not really compatible to begin with. Transplanting stuff from one to the other just because it saves 7 bytes in developers' brains is not enough of a reason to do that in my opinion.
PHP has always been about making coding easy and fun. Since more people are doing JavaScript, it only makes sense to make things familiar.
> Why then have PHP at all?
That's a question that will have to be answered soon.
For now, the answer that I'm coming up with, is that with PHP-FPM you can make PHP properly threaded. All of the server-side JavaScript implementations are event driven. The threaded model has advantages when jobs are cpu or io heavy.
Right now, I'm working on an implementation where Node.js is receiving calls via websockets and handing that off to PHP-FPM via FastCGI for processing. A hybrid system like that has advantages of both the event model and the thread model.
That sounds pretty cool. Personally, I was blown away what difference Nginx<->PHP-FPM makes compared to the old Apache way of doing things. Nginx is an absurdly fast evented web server as well. It would be very interesting to bring this kind of thing to Node.js because it would allow for some awesome customization which just isn't possible with Nginx.
In PHP 5.3, this is not allowed:
class MyClass {
public function doSomething() {
$delegate = new SomeDelegateClass();
// the following line is not allowed
$delegate->doSomethingWithClosure(function($app) use ($this) {
// do something with $app
});
}
You can now solve this problem simply with the bindTo method.So here's how you could solve this "problem" before bindTo(), it's explicit and simple and it doesn't even need use:
class SomeDelegateClass {
function doSomethingWithClosure($callerObj, $closure) {
$closure($callerObj);
}
}
class MyClass {
function hello() { print('hello from MyClass!'); }
function doSomething() {
$delegate = new SomeDelegateClass();
$delegate->doSomethingWithClosure($this, function($myClassObj) {
$myClassObj->hello();
});
}
}The next thing is, that the Silex Framework binds route parameters as arguments to the closure. Additionally the user could forget to declare the $app argument in the passed closure.
$closure = function() {
echo $this->foo;
};
$context = new \StdClass;
$context->foo = "Hello World";
$context->closure = $closure;
$context->closure();
Not sure if this will be possible in 5.4, but haven't seen anything on it. Right now, we have to do
$context->closure = $closure;
$temp = $context->closure;
$temp();
which is kinda ugly.
call_user_func($context->closure);
But yeah, some syntactic sugar would sure be nice. I'd like to see this in a future version of PHP... ($context->closure)();$content->closure()
It could look at the registered methods, then scan for properties of that name that would be closures, then fall back to __call. You could add this to __call with __set() checks for assigned values being closures, but it's rather ugly to have to do that for all class definitions.
class Object {
public function __call($method, $args) {
if(isset($this->$method) && $this->$method instanceof \Closure) {
$this->$method->bindTo($this);
return call_user_func_array($this->$method, $args);
}
}
}
$context = new Object;
$context->foo = "Hello World";
$context->closure = function() {
echo $this->foo;
};
$context->closure();
Once traits are in the language (5.5?) that could just be mixed in.But it ain't present in the current builds of 5.4, AFAIK. Don't know what's happened there...
I'm looking forward to this new feature, as it is cumbersome to pass anonymous functions to object instances in 5.3.