Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save marcelaraujo/46a56a6aa58e5f9c617f to your computer and use it in GitHub Desktop.
Save marcelaraujo/46a56a6aa58e5f9c617f to your computer and use it in GitHub Desktop.
<?php
// A closure can be bound ($this will be a specific object), unbound ($this not set) or static ($this cannot be set)
// A closure may be scoped (it can see the private properties of a specific class), or unscoped (no class)
// At present, a closure must be bound or static if it is scoped, and unscoped if it is unbound
// I propose changing this to also allow scoped, unbound closures
// I want to add Closure::call, which lets you call and bind a closure (to avoid creating a new closure for each object you want to bind to)
$foo = function () {
return $this->x;
};
$bar = (object)['x' => 3];
$qux = (object)['x' => 4];
$foo->call($bar); // returns 3
$foo->call($qux); // returns 4
// However, for technical reasons, Closure::call can only bind, it can't change the scope.
// Therefore if you need to be scoped (to see private properties etc.) and want to use ::call, you must created a scoped closure ahead-of-time
// However, at the moment, you cannot have an unbound yet scoped closure, only one that is bound or static
// If we try to do this:
class FooBar {
private $x = 0;
}
$foo = function () {
return $this->x;
};
// ($newthis[, $scope])
$foo->bindTo(NULL, 'FooBar');
// We will get a static closure, which can see FooBar's private variables but can't be bound to anything!
// This means that we actually would have to bound to some dummy object first at the moment, as you can rebind a bound closure:
$foo->bindTo(new StdObject, 'FooBar');
// However, there are some cases where you can't do this
// (e.g. methods on internal classes don't work with other classes, and the binding would fail)
// It would be better to just allow creating a closure that is scoped to a class, but isn't bound:
// new optional argument: $unbound_scoped
$foo->bindTo(NULL, 'FooBar', true);
// This is actually what my function references proposal does internally for methods
// Because it uses :: syntax it doesn't know what to bind to, but it does know the scope
// If we made them static, it'd be useless (you can't bind to anything!)
// If we didn't scope them, it'd be strange (a method should naturally be scoped to its class)
// Thus it has to make unbound but scoped closures
class FooBar {
private $x = 0;
function bar() {
return $this->x;
}
}
// Under my function refs proposal, this gets you an unbound, scoped closure
$foo = &FooBar::bar;
$qux = new FooBar;
$qux->x = 3;
$foo->call($qux); // Returns 3, as call binds it to $qux and $foo is already scoped to FooBar
// I hope this helped!
// - Andrea Faulds (@andreafaulds, ajf.me@ajf.me)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment