Tschallacka
Tschallacka

Reputation: 28742

PHP scope child anonymous function alter variable in parent function

I have the following code.

function up() {
    $runafterupdate = [];
    Schema::connection('exitproducts')->create('exitcontrol_carmanager_manager',  function($table)
                {
                    $table->engine = 'InnoDB';
                    $table->increments('id');
                    $table->string('code');
                    $runafterupdate['code'] = true;
                    $table->timestamps();
                });
            }
    if(isset($runafterupdate['code'])) {
        echo "it worked!";
    }
}

And i'm used to JavaScript where you can alter the values of the parent scope, but aparently php follows different rules. I've tried reading through http://php.net/manual/en/language.variables.scope.php but I really don't want to use globals.

Is there a way to alter the variables in the parent scope with php or is my only resort in this case a global variable?

Upvotes: 1

Views: 1320

Answers (4)

Tschallacka
Tschallacka

Reputation: 28742

2021 update courtesy of George

It's no longer necessary. For searchers in the future: If you use the new (PHP>=7.4) "arrow function" notation for anonymous functions, variables in the parent are automatically accessible to you by-value. No use tag needed. php.net/manual/en/functions.arrow.php


After some more digging... why do I always find the answers AFTER I post a question...

Using the use clause on the function you can use the variables you declare there in the "child" scope. That this isnt highlighted in the scope documentation in php docs beats me.

Extracted from Reference: What is variable scope, which variables are accessible from where and what are "undefined variable" errors?

Extending the scope of variables into anonymous functions

$foo = 'bar';

$baz = function () use ($foo) {
    echo $foo;
};

$baz();

After some fiddling I found i can't directly modify array variables. Any modifications stay in the function scope but dont lift over to the parent scope.

I made a simple holder object with setters and getters to get it to work.

function scop1() {
/** simple class to hold vars **/
class holder {
 public $held = [];
 /** setter **/
 function update($what, $with) {
        $this->held[$what] = $with;
    }
 /** getter **/
 function get($what) {
    if(isset($this->held[$what])) return $this->held[$what];
    else return null;
 }
}
$var = new holder();
/** works **/
$var->update('hello','bye');
$x = function() use ($var) {
   /** modify parent scope **/
   $var->update('hello','hello');
};
/** call anomynous function **/
$x();
/** it should say hello hello in held **/
var_dump($var);
}
scop1();

live sample: http://sandbox.onlinephpfunctions.com/code/d7464356c0712f2606b0f70ab952be4d782374dc

Upvotes: 2

Utsav Barnwal
Utsav Barnwal

Reputation: 1183

Using a Closure with & will do the trick.

function test()
{
  $var = 20;

  $anonymous = function () use (&$var) {
    $var++;
  };

  $anonymous();

  echo $var; // 21
}

If you just want to pass just the value, use closure without &

$anonymous = function () use ($var) { ... }

Upvotes: 2

Stanimir Dimitrov
Stanimir Dimitrov

Reputation: 1890

If this function is inside a class, I don't see a problem if you declare your variable as public, private or protected ( I would go with private and create set/get functions for this variable if necessary). After that you can do $this->runafterupdate = true inside your anonymous function. If your function is not inside a class I would go with globals, but I really don't suggest that.

Hove you tried with the use keyword`?

Upvotes: 1

ka_lin
ka_lin

Reputation: 9442

You have an anonymous function, declare $runafterupdate as a private array in your class and in the anonymous function use $this->runafterupdate then check it. I`m guessing you are using Laravel

class Demo
{
    private $runafterupdate;
    ...
    public function up() {
        Schema::connection('exitproducts')->create('exitcontrol_carmanager_manager',  function($table)
                    {
                        $table->engine = 'InnoDB';
                        $table->increments('id');
                        $table->string('code');
                        $this->runafterupdate['code'] = true;
                        $table->timestamps();
                    });
                }
        if(isset($this->runafterupdate['code'])) {
            echo "it worked!";
        }
    }
    ...
}

This should work, if I`m not mistaken I had a similar issue

Upvotes: 0

Related Questions