【问题标题】:php Slim framework - passing parameter to closurephp Slim 框架 - 将参数传递给闭包
【发布时间】:2015-01-29 17:17:24
【问题描述】:

我正在尝试研究 Slim 框架的代码。在 Slim 类的构造函数中,将 $c 传递给闭包(例如,在容器中存储/设置请求/响应对象时):

 public function __construct(array $userSettings = array())
    {
        // Setup IoC container
        $this->container = new \Slim\Helper\Set();
        $this->container['settings'] = array_merge(static::getDefaultSettings(), $userSettings);

        // Default request
        $this->container->singleton('request', function ($c) {
          return new \Slim\Http\Request($c['environment']);
        });

        // Default response
        $this->container->singleton('response', function ($c) {
          return new \Slim\Http\Response();
        });

但是 $c 在此语句之前的任何地方都没有定义/声明,那么它是如何工作的呢?我从一开始就开始跟踪所有内容,并且在以这种方式使用它之前在任何地方都找不到 $c。

【问题讨论】:

    标签: php closures slim


    【解决方案1】:

    $c 是闭包函数的参数。想象一下你自己有一个函数:

    function myFunction($c) {
        echo $c;
    }
    

    在闭包的情况下,您可以将匿名函数存储在变量中:

    $someFunction = function ($c) {
        echo $c;
    }
    $someFunction("hello world");
    

    因此,上面的代码不是直接将闭包存储到变量中,而是将匿名函数作为参数传递给 $this->container->singleton()。所以在调用闭包之前不会创建 $c 。单例方法将其存储在一个名为 $value 的变量中,因此如果该函数运行:

    $value(array('environment'=>'test'));
    

    $c 现在将包含 array('environment'=>'test')

    Slim 还大量使用了 __get() __set() 魔术方法,因此从您设置的示例代码中,可以在 Slim 类中调用:

    $request = $this->container->request(array('environment'=>'test'));
    

    容器属于 Slim\Helper\Set 类。由于 this 没有请求方法,因此它将调用容器的 __get() 方法。它会查找上面为“request”配置的存储方法,并将数组作为 $c

    【讨论】:

    • 是的,我知道匿名函数可以存储在变量中。而且我还可以看到匿名函数作为参数传递给单例方法。但我仍然不明白 $c 是从哪里来的。你是说在调用闭包之前它不会被创建。那么当它被创建时,它等同于什么? $c 变量存储的是什么?谢谢!
    • 当匿名函数被调用时,也就是 $c 的值被赋值的时候。换句话说,为了调用闭包,您需要将要分配给 $c 的值传递给它(请参阅编辑答案的结尾)
    • 谢谢,我会尽力解决这个问题。我试图查找一些文献,但不知道我应该搜索什么术语,我找不到类似的东西。
    【解决方案2】:

    我认为 khartnett 给出了一个完美的答案。为了让你更清楚,举个例子。

    当你定义一个函数时,你只是在描述它是如何工作的。您没有设置任何特定值。比如我写的时候:

    function sum($a, $b) {    
        return $a + $b;
    }
    

    我不是说$a$b 的值在这里。我只是在描述我正在使用这些变量来计算结果。直到我调用这个函数,我才使用实际值:

    sum(3, 4); // returns 7
    

    在您的问题中,$c 变量类似于 $a$b 变量。

    就像 khartnett 在他的回答中显示的那样,它的工作原理是这样的:

    // Definition time
    $someFunction = function ($c) {
        echo $c;
    }
    
    // Calling time
    $someFunction("hello world");
    

    直到调用时间,$c 才获得它的值(在本例中,该值为“hello world”)。

    【讨论】:

      【解决方案3】:

      $c 是对容器本身的引用 - 因此任何依赖项在调用时都会自动解析。

      所以使用例如请求对象:

          // Default request
          $this->container->singleton('request', function ($c) {
              return new \Slim\Http\Request($c['environment']);
          });
      

      查看Request 构造函数,您会看到它需要Environment 的实例,我们刚刚告诉容器应该已经使用密钥“环境”可用。

      【讨论】:

        【解决方案4】:

        答案将在$app->run():
        $this->container->get("service_name") 然后在get() 方法offsetGet($id)

        public function offsetGet($id)
            {
                if (!isset($this->keys[$id])) {
                    throw new UnknownIdentifierException($id);
                }
        
                if (
                    isset($this->raw[$id])
                    || !\is_object($this->values[$id])
                    || isset($this->protected[$this->values[$id]])
                    || !\method_exists($this->values[$id], '__invoke')
                ) {
                    return $this->values[$id];
                }
        
                if (isset($this->factories[$this->values[$id]])) {
                    return $this->values[$id]($this);
                }
        
                $raw = $this->values[$id];
                $val = $this->values[$id] = $raw($this); // THIS LINE CALLS THE CONTAINER ITSELF
                $this->raw[$id] = $raw;
        
                $this->frozen[$id] = true;
        
                return $val;
            }
        

        Pimple Container 的功能有点复杂,因为:
        允许任何 PHP 可调用会导致难以调试的问题 因为函数名(字符串)是可调用的(用 与现有参数同名会破坏您的容器)
        因此引入了唯一标识符。

        下面是ServiceContainer class内部ServiceContainerget()方法的简单实现:

        public function get($serviceName)
            {
                if (!array_key_exists($serviceName, $this->container)) {
                    throw new \http\Exception\InvalidArgumentException("Service not found!");
                }
                $service = $this->container[$serviceName];
                if (is_callable($service)) {
                    $this->container[$serviceName] = $service = $service($this);
         // $this (aka ServiceContainer) will be passed as parameter to Closures
                }
                return $service;
            }
        

        希望它能澄清问题,祝你有美好的一天!

        【讨论】:

          猜你喜欢
          • 2014-12-30
          • 1970-01-01
          • 2015-07-22
          • 2018-09-18
          • 1970-01-01
          • 1970-01-01
          • 2017-09-11
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多