PHP Redefine a closure with bind() and its scope [duplicate] - php

When using anonymous functions in PHP, you can easily use variables from right outside of its scope by using the use() keyword.
In my case the anonymous functions are already defined somewhere, but called later on (somewhere else) in a class.
The following piece of code is to illustrate the idea:
<?php
$bla = function ( $var1 ) use ($arg)
{
echo $var1;
};
class MyClass
{
private $func;
public function __construct ( $func )
{
$this->func = $func;
}
public function test ( $arg )
{
$closure = $this->func;
$closure ( 'anon func' );
}
}
$c = new MyClass($bla);
$c->test ( 'anon func' );
What i'm doing is i create an anonymous function and store that in a variable. I pass that variable to the method of a class and that is where i want to run the anonymous function.
But i can't use the use() keyword to get the $arg parameter from the method this way. Because the anonymous function was declared outside of the method.
But i really need a way to get the variables from the method where the anonymous function is run from. Is there a way to do that, when the anonymous function is declared somewhere else..?

The point of the use keyword is to inherit/close over a particular environment state from the parent scope into the Closure when it's defined, e.g.
$foo = 1;
$fn = function() use ($foo) {
return $foo;
};
$foo = 2;
echo $fn(); // gives 1
If you want $foo to be closed over at a later point, either define the closure later or, if you want $foo to be always the current value (2), pass $foo as a regular parameter.

FWIW, you can do it if you use a use reference (php.net ex 3.3) and a global, ugly since it uses globals, but just to put it out there:
<?php
$bla = function ( $var1 ) use (&$arg)
{
return "var1:$var1, arg:$arg";
};
class MyClass
{
private $func;
public function __construct ( $func )
{
$this->func = $func;
}
public function test ( $param )
{
global $arg;
$arg=$param;
$closure = $this->func;
return $closure ( 'anon func' );
}
}
$c = new MyClass($bla);
echo $c->test ( 'bla bla' ); //var1:anon func, arg:bla bla

Related

Get a parent variable using a function

I am trying to access $my_var from within a function, I know I can use a global $my_var to do so, but that IMO isn't a good way to do this since if $my_var is outside of the call_user_func it will use that one instead of the one within. I can't use use since the function isn't an anonymous function.
Is there a good way to do this without using a class?
call_user_func(function(){
$my_var = null;
function myFunc($value1, callable $callback){
// Access $my_var
}
});
myFunc('value 1', function(){});
There are several ways. The simplest is to simply pass $var as a parameter, and update it through the function's return value:
$var = null;
function foo( $var ) { $var++; }
$var = foo( $var );
Another way is to declare the parameter as a reference (& $var):
$var = null;
function foo( & $var ) { $var++; }
foo( $var );
A third way is to use an anonymous function and specify the use of the variable:
$var = null;
$foo = function() use( & $var ) { $var++; }
$foo();

Create and use global variable without the 'global' keyword?

As of now I create a variable like this in a file that's loaded on all pages:
<?php
add_action( 'parse_query', 'my_global_vars' );
function my_global_vars() {
/* CUSTOM GLOBAL VARIABLES */
$variable_name = get_query_var('category_name');
}
?>
And every time I want to use it (in other files), I must do it like this:
<?php
global $variable_name;
if( $variable_name = 'news' ){
// Do something
}
?>
And when I need to use the variable multiple times in the same file, I add global $variable_name; at the top of the file, and simply use $variable_name in rest of the instances.
But as the number of variables increase, I find it harder to manage the list of global $variable_names at the top of all files.
Is there a way to define a variable as global at the time of creation and simply use $variable_name everywhere else?
EDIT: One way is to define variable like this:
<?php
add_action( 'parse_query', 'my_global_vars' );
function my_global_vars() {
/* CUSTOM GLOBAL VARIABLES */
global $variable_name;
$variable_name = get_query_var('category_name');
}
?>
And use it like using $GLOBALS[] like this $GLOBALS['variable_name'] elsewhere.
Having a lot of global vars is usually bad. It becomes a mess very quickly.
If you really need global variables the best approach is to use static class variables. They are just as global as the others, but they enforce better naming and pre-declaration so the end result is somewhat better.
If you really need normal global vars you can also use the $GLOBALS variable, it is available in every scope and is basically an array of all the global variables.
global $variable_name;
do_something_with($variable_name);
is the same as
do_something_with($GLOBALS['variable_name']);
You can use a static class for this and keep it OOP:
<?php
class GlobalVariables {
private static $vars = array();
public static function get($name, $default = null) {
return (isset(self::$vars[$name]) ? self::$vars[$name] : $default);
}
public static function set($name, $value) {
self::$vars[$name] = $value;
}
}
function foo() {
GlobalVariables::set('foo', 'oof');
}
function bar() {
var_dump( GlobalVariables::get('foo') );
}
foo();
bar(); //oof
?>
DEMO
Alternatively, you can use a Singleton pattern:
<?php
class GlobalVariables {
private static $instance;
private $vars = array();
public static function get() {
if (empty(self::$instance)) {
self::$instance = new GlobalVariables();
}
return self::$instance;
}
public function __get($name) {
return (isset($this->vars[$name]) ? $this->vars[$name] : null);
}
public function __set($name, $value) {
$this->vars[$name] = $value;
}
}
function foo() {
GlobalVariables::get()->foo = 'oof';
}
function bar() {
var_dump( GlobalVariables::get()->foo );
}
foo();
bar(); //oof
?>
DEMO.
Use whichever you find most readable.
GlobalVariables::set('key', 'value');
GlobalVariables::get('key');
or
GlobalVariables::get()->key = 'value';
GlobalVariables::get()->key;
Completely alternatively, if you hate dynamicness, simply use static variables (this, however, requires you to create all variables beforehand):
<?php
class GlobalVariables {
public static $foo;
}
GlobalVariables::$foo = 'oof';
var_dump( GlobalVariables::$foo );
?>
Using global outside a function doesn't do anything. It's meant for inside functions. You can simply remove your global statements.
Edit: I suggest that you try to reduce the number of global variables you have by structuring them. Group them into arrays or objects. E.g., instead of user_name, user_id, user_is_admin, prefer user['name'], user['id'], user['is_admin'], then you only have one variable (user) to declare as global instead of three.

Use variables inside an anonymous function, which is defined somewhere else

When using anonymous functions in PHP, you can easily use variables from right outside of its scope by using the use() keyword.
In my case the anonymous functions are already defined somewhere, but called later on (somewhere else) in a class.
The following piece of code is to illustrate the idea:
<?php
$bla = function ( $var1 ) use ($arg)
{
echo $var1;
};
class MyClass
{
private $func;
public function __construct ( $func )
{
$this->func = $func;
}
public function test ( $arg )
{
$closure = $this->func;
$closure ( 'anon func' );
}
}
$c = new MyClass($bla);
$c->test ( 'anon func' );
What i'm doing is i create an anonymous function and store that in a variable. I pass that variable to the method of a class and that is where i want to run the anonymous function.
But i can't use the use() keyword to get the $arg parameter from the method this way. Because the anonymous function was declared outside of the method.
But i really need a way to get the variables from the method where the anonymous function is run from. Is there a way to do that, when the anonymous function is declared somewhere else..?
The point of the use keyword is to inherit/close over a particular environment state from the parent scope into the Closure when it's defined, e.g.
$foo = 1;
$fn = function() use ($foo) {
return $foo;
};
$foo = 2;
echo $fn(); // gives 1
If you want $foo to be closed over at a later point, either define the closure later or, if you want $foo to be always the current value (2), pass $foo as a regular parameter.
FWIW, you can do it if you use a use reference (php.net ex 3.3) and a global, ugly since it uses globals, but just to put it out there:
<?php
$bla = function ( $var1 ) use (&$arg)
{
return "var1:$var1, arg:$arg";
};
class MyClass
{
private $func;
public function __construct ( $func )
{
$this->func = $func;
}
public function test ( $param )
{
global $arg;
$arg=$param;
$closure = $this->func;
return $closure ( 'anon func' );
}
}
$c = new MyClass($bla);
echo $c->test ( 'bla bla' ); //var1:anon func, arg:bla bla

How can I access class constants using `self::` inside an anonymous function defined in a method?

I would like to access a class constant using self from within an anonymous function.
class My_Class {
const CLASS_CONSTANT = 'test value';
private function my_function(){
$lambda_function = function(){
echo self::CLASS_CONSTANT;
};
$lambda_function();
}
}
When I tried this, I get the error:
Fatal error: Cannot access self:: when no class scope is active in ...
Is it possible to pass the parent class into the scope of this anonymous function? Would a use statement work?
>> All versions test of PHP 5.4+ way on 3v4l <<
PHP 5.4+ WAY:
This has become significantly simpler since PHP 5.4, where $this is no longer dirty:
class My_Class {
const CLASS_CONSTANT = 'test value';
private function my_function() {
$lambda_function = function() {
// $this is actually inherited from the parent object, so
// you don't even need a use() statement
echo $this::CLASS_CONSTANT;
// Or just use self, that's inherited too
echo self::CLASS_CONSTANT;
};
$lambda_function();
}
}
PRE 5.4 WAY:
Make the anonymous function a closure -- by introducing scoped variables into the function -- and call the constant from that:
class My_Class {
const CLASS_CONSTANT = 'test value';
private function my_function() {
$self = $this;
$lambda_function = function() use ($self) { // now it's a closure
echo $self::CLASS_CONSTANT;
} // << you forgot a ;
lambda_function(); // << you forgot a $
}
}
Unfortunately you can't use ($this) YET. They're working on it. I expect it to work in PHP >= 5.4.
afaik anonymous functions are just that.. functions. Not class methods, so scope is out. You can pass the constant as an argument or use My_Class::CLASS_CONSTANT.
You're accessing the self inside an anonymous function, this won't work. What you should do is use My_Class::CLASS_CONSTANT instead of the self reference.
No, that's not possible. Similarly, you can not bind $this to an anonymous function. Just passing the necessary values instead should do the trick though?
<?php
class My_Class {
const CLASS_CONSTANT = 'test value';
private function my_function(){
$lambda = function( $yourConstant ){
return $yourConstant;
};
return $lambda( self::CLASS_CONSTANT );
}
public function test( ) {
return $this->my_function( );
}
}
$class = new My_Class( );
echo $class->test( ); // 'test value'

PHP closures and implicit global variable scope

Is there a way that one can implicitly declare top-level variables as global for use in closures?
For example, if working with code such as this:
$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')
)
.
.
.
The closures are called from a method as such:
public function __construct($name, $closure = null){
$this->_name = $name;
is_callable($closure) ? $closure($this) : null;
}
So in summary/TL;DR, is there a way to implicitly declare variables as global for use in closures (or other functions I suppose) without making use of the global keyword or $GLOBALS super-global?
I started this topic at another forum I frequent (http://www.vbforums.com/showthread.php?p=3905718#post3905718)
You have to declare them in the closure definition:
->addBeta('myBeta', function($obj) use ($a) { // ...
Otherwise you must use the global keyword. You have to do this for every closure that uses $a too.

Categories