|
-
Oct 29th, 2010, 12:06 PM
#1
Thread Starter
Hyperactive Member
Global variables in closure scope
Is there a way to ensure that top-level variables are visible in the scope of a closure, without needing to declare them global in the closure itself?
For example; if I'm working with code using a syntax such as this:
PHP Code:
$a = 0; //A TOP-LEVEL VARIABLE
Alpha::create('myAlpha')
->bind(DataSingleton::getInstance()
->query('c')
)
->addBeta('myBeta', function($obj){
$obj->bind(DataSingleton::getInstance()
->query('d')
)
->addGamma('myGamma', function($obj){
$obj->bind(DataSingleton::getInstance()
->query('a')
)
->addDelta('myDelta', function($obj){
$obj->bind(DataSingleton::getInstance()
->query('b')
);
});
})
->addGamma('myGamma', function($obj){
$a++; //OUT OF MY SCOPE
$obj->bind(DataSingleton::getInstance()
->query('c')
)
.
.
.
Now of course normally one would need to declare it as global or use the $GLOBALS superglobal. The closures are being called from within a method as such:
PHP Code:
public function __construct($name, $closure = null){
$this->_name = $name;
is_callable($closure) ? $closure($this) : null;
}
Providing a jQuery-ish syntax. This is all very simplified for use as an example.
TL;DR: How can I implicitly declare as global the closure scoped variables.
-
Oct 29th, 2010, 12:06 PM
#2
Thread Starter
Hyperactive Member
Re: Global variables in closure scope
Here's the whole example for shits n' gigs':
PHP Code:
abstract class BaseClass{
protected $_name;
protected $_data = array();
protected $_children = array();
protected $_occurance = 0;
//class specific, just a general example
public static function create($name, $closure = null){
return new static($name, $closure);
}
//class specific, just a general example
public function __construct($name, $closure = null){
$this->_name = $name;
is_callable($closure) ? $closure($this) : null;
}
public function addAlpha($name, $closure = null){
$this->_children[] = new Alpha($name, $closure);
//class specific config
return $this;
}
public function addBeta($name, $closure = null){
$this->_children[] = new Beta($name, $closure);
//class specific config
return $this;
}
public function addGamma($name, $closure = null){
$this->_children[] = new Gamma($name, $closure);
//class specific config
return $this;
}
public function addDelta($name, $closure = null){
$this->_children[] = new Delta($name, $closure);
//class specific config
return $this;
}
public function set($name, $value){
$this->_data[$this->_occurance][$name] = $value;
return $this;
}
public function get($name){
return $this->_data[$this->_occurance][$name];
}
public function newOccurance(){
$this->_occurance++;
return $this;
}
public function bind(Array $data){
foreach($data as $record){
$this->newOccurance();
foreach($record as $name => $value){
$this->_data[$this->_occurance][$name] = $value;
}
}
return $this;
}
}
class Alpha extends BaseClass{
public function render(){
echo '<pre>';
echo preg_replace('% \n+ %ix', "\n", print_r($this, true));
echo '</pre>';
}
}
class Beta extends BaseClass{}
class Gamma extends BaseClass{}
class Delta extends BaseClass{}
//just an example of some data source class
class DataSingleton{
private static $_instance;
private $_data = array();
private function __construct(){}
public static function getInstance(){
return self::$_instance ? self::$_instance : self::$_instance = new self;
}
public function import(Array $data){
$this->_data = $data;
}
public function query($sortCharacter){
foreach($this->_data as $record){
foreach($record as $name => $value)
if(preg_match("% ^{$sortCharacter} %ix", $value)){
$set[] = $record;
}
}
return $set;
}
}
///////////////////////////////////////////////////////////////////////////
DataSingleton::getInstance()
->import(array(
array('name' => 'Andrew'), array('name' => 'Andrea'), array('name' => 'Amy'),
array('name' => 'Brandon'), array('name' => 'Betty'), array('name' => 'Bobby'), array('name' => 'Barry'),
array('name' => 'Cameron'), array('name' => 'Camille'),
array('name' => 'Daniel'), array('name' => 'Dale'), array('name' => 'Damien')
));
Alpha::create('myAlpha')
->bind(DataSingleton::getInstance()
->query('c')
)
->addBeta('myBeta', function($obj){
$obj->bind(DataSingleton::getInstance()
->query('d')
)
->addGamma('myGamma', function($obj){
$obj->bind(DataSingleton::getInstance()
->query('a')
)
->addDelta('myDelta', function($obj){
$obj->bind(DataSingleton::getInstance()
->query('b')
);
});
})
->addGamma('myGamma', function($obj){
$obj->bind(DataSingleton::getInstance()
->query('c')
)
->addDelta('myDelta', function($obj){
$obj->bind(DataSingleton::getInstance()
->query('d')
);
});
});
})
->render();
-
Oct 29th, 2010, 12:07 PM
#3
Thread Starter
Hyperactive Member
Re: Global variables in closure scope
Outputs:
Code:
Alpha Object
(
[_name:protected] => myAlpha
[_data:protected] => Array
(
[1] => Array
(
[name] => Cameron
)
[2] => Array
(
[name] => Camille
)
)
[_children:protected] => Array
(
[0] => Beta Object
(
[_name:protected] => myBeta
[_data:protected] => Array
(
[1] => Array
(
[name] => Daniel
)
[2] => Array
(
[name] => Dale
)
[3] => Array
(
[name] => Damien
)
)
[_children:protected] => Array
(
[0] => Gamma Object
(
[_name:protected] => myGamma
[_data:protected] => Array
(
[1] => Array
(
[name] => Andrew
)
[2] => Array
(
[name] => Andrea
)
[3] => Array
(
[name] => Amy
)
)
[_children:protected] => Array
(
[0] => Delta Object
(
[_name:protected] => myDelta
[_data:protected] => Array
(
[1] => Array
(
[name] => Brandon
)
[2] => Array
(
[name] => Betty
)
[3] => Array
(
[name] => Bobby
)
[4] => Array
(
[name] => Barry
)
)
[_children:protected] => Array
(
)
[_occurance:protected] => 4
)
)
[_occurance:protected] => 3
)
[1] => Gamma Object
(
[_name:protected] => myGamma
[_data:protected] => Array
(
[1] => Array
(
[name] => Cameron
)
[2] => Array
(
[name] => Camille
)
)
[_children:protected] => Array
(
[0] => Delta Object
(
[_name:protected] => myDelta
[_data:protected] => Array
(
[1] => Array
(
[name] => Daniel
)
[2] => Array
(
[name] => Dale
)
[3] => Array
(
[name] => Damien
)
)
[_children:protected] => Array
(
)
[_occurance:protected] => 3
)
)
[_occurance:protected] => 2
)
)
[_occurance:protected] => 3
)
)
[_occurance:protected] => 2
)
-
Oct 30th, 2010, 02:06 AM
#4
Re: Global variables in closure scope
PHP is not like JavaScript. In JavaScript, an anonymous function just automatically inherits the entire scope of its parent. In PHP, you must define, explicitly, the scope variables that it should inherit from its parent. And, you must do this by reference, because otherwise you are just copying that scope's variable.
The following is what you want:
PHP Code:
$obj->method(function() use(&$a){ $a++; });
So, to have a nested closure:
PHP Code:
$a = 0; $obj->methodA(function($obj) use(&$a){ echo $a; // prints 0 $obj->methodB(function() use(&$a){ $a++; } }); echo $a; // prints 1
read more about closures in PHP.
Last edited by kows; Oct 30th, 2010 at 02:09 AM.
-
Oct 30th, 2010, 04:12 AM
#5
Thread Starter
Hyperactive Member
Re: Global variables in closure scope
Thanks kows;
I've discovered a few work-a-rounds. Specifically, 'extending' the Closure class (rather, just creating a new class with some Reflection tricks) and calling extract($GLOBALS); prior to an eval(); on the extracted function code. Despite it's functionality, it's hackish. I'm certain that in a production scenario, using the Reflection API tricks in conjunction with the continual re-scoping/extraction of $GLOBALS would be terribly inefficient.
I posted this up and linked to this thread at another forum. It has a link to the blog entry regarding 'extending' Closure. http://stackoverflow.com/questions/4...variable-scope
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|