【问题标题】:PHP scope child anonymous function alter variable in parent functionPHP范围子匿名函数更改父函数中的变量
【发布时间】:2015-09-20 23:27:55
【问题描述】:

我有以下代码。

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!";
    }
}

我已经习惯了 JavaScript,您可以在其中更改父范围的值,但显然 php 遵循不同的规则。 我试过阅读http://php.net/manual/en/language.variables.scope.php,但我真的不想使用全局变量。

有没有办法用 php 改变父作用域中的变量,或者在这种情况下我唯一的手段是全局变量吗?

【问题讨论】:

    标签: php scope


    【解决方案1】:

    2021 年更新由George提供

    不再需要了。对于未来的搜索者:如果您对匿名函数使用新的 (PHP>=7.4)“箭头函数”表示法,则您可以按值自动访问父级中的变量。不需要使用标签。 php.net/manual/en/functions.arrow.php


    经过更多挖掘...为什么我总是在发布问题后找到答案...

    使用函数上的 use 子句,您可以使用在“子”范围内声明的变量。 这在 php docs 的范围文档中没有突出显示让我感到震惊。

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

    将变量的作用域扩展为匿名函数

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

    经过一番摆弄,我发现我无法直接修改数组变量。任何修改都保留在函数范围内,但不会转移到父范围内。

    我用 setter 和 getter 制作了一个简单的 holder 对象以使其工作。

    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();
    

    实时样本:http://sandbox.onlinephpfunctions.com/code/d7464356c0712f2606b0f70ab952be4d782374dc

    【讨论】:

    • can't directly modify array variables : function() use (&$ref) {...} 怎么样 - 应该修改传递的 var,因为它作为参考传递
    • 哦哇...这确实有效 :-) 谢谢。你用 php 范围让我的生活变得更轻松:-) 回答解释的东西,我会接受的。
    • 但要注意,IIRC 一旦到位,就不可能摆脱 php &array(..) 中的引用,(如果稍后您想要一个不是引用的副本)
    • 不,设置标志只是短暂的。设置结束后,我取消了整个 shebang。
    • 2021 年更新:这个 Q/A 在搜索中帮我找到并且有效,但后来我发现它不再需要了。因此,对于未来的搜索者:如果您对匿名函数使用新的 (PHP>=7.4)“箭头函数”表示法,则您可以通过值自动访问父级中的变量。不需要use 标签。 php.net/manual/en/functions.arrow.php
    【解决方案2】:

    Closure& 一起使用即可。

    function test()
    {
      $var = 20;
    
      $anonymous = function () use (&$var) {
        $var++;
      };
    
      $anonymous();
    
      echo $var; // 21
    }
    

    如果您只想传递值,请使用不带& 的闭包

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

    【讨论】:

      【解决方案3】:

      如果这个函数在一个类中,如果你将你的变量声明为公共、私有或受保护的,我看不出有什么问题(如果需要,我会使用私有并为此变量创建 set/get 函数)。之后,您可以在匿名函数中执行$this->runafterupdate = true。如果您的函数不在类中,我会使用全局变量,但我真的不建议这样做。

      你试过use关键字吗?

      【讨论】:

        【解决方案4】:

        你有一个匿名函数,在你的类中声明 $runafterupdate 为私有数组,并在匿名函数中使用 $this->runafterupdate 然后检查它。我猜你正在使用 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!";
                }
            }
            ...
        }
        

        这应该可以,如果我没记错的话,我遇到了类似的问题

        【讨论】:

        • uhm.. 你有一个异常函数,它被传递给模式创建,而不是类实例。我必须创建一个类函数并通过它。这不是它的工作原理。我发布了一个正确使用范围方法的正确期望行为的答案。我发现它实际上比将所有内容都传递给子作用域的 javascript 方式更好。
        猜你喜欢
        • 2013-02-09
        • 1970-01-01
        • 2011-10-27
        • 1970-01-01
        • 1970-01-01
        • 2010-12-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多