书写技术成长之路

初探 PHP Closure

当看Laravel源码的时候,看到这段代码,竟然没看出来$c$parameters是怎么传过来的,

声明方法

    /**
     * Get the Closure to be used when building a type.
     *
     * @param  string  $abstract
     * @param  string  $concrete
     * @return \Closure
     */
    protected function getClosure($abstract, $concrete)
    {
        return function ($c, $parameters = []) use ($abstract, $concrete) {
            $method = ($abstract == $concrete) ? 'build' : 'make';

            return $c->$method($concrete, $parameters);
        };
    }

首先看这个方法,没有定义变量$c$parameters,怎么会不报错呢。直到搜索后才看到这是Closure的用法,真实长见识了。

Closure是用于代表匿名函数的类,所以它的用法和匿名函数(Anonymous functions)是一样的。 那么Closure是怎么使用的呢?

Closure通过use引用外部变量到当前环境中,不在use后面引用的参数是在Closure被调用的时候传入的。 通过以下例子来说明

<?php

class ClosureTest
{
    public function getClosure($name)
    {
        // use将外部变量引入进来
        return function($score) use ($name) {
            return $name."------>".$score."\n";
        };
    }
}

$my_closure = new ClosureTest;
$closure = $my_closure->getClosure("Jackson");  // 变量$name在实例化一个Closure的时候传进来
echo $closure(80);  // 变量$score在Closure被调用的时候传进来

这是laravel调用Closure的方法

/**
 * Instantiate a concrete instance of the given type.
 *
 * @param  string  $concrete
 * @param  array   $parameters
 * @return mixed
 *
 * @throws \Illuminate\Contracts\Container\BindingResolutionException
 */
public function build($concrete, array $parameters = [])
{
    // If the concrete type is actually a Closure, we will just execute it and
    // hand back the results of the functions, which allows functions to be
    // used as resolvers for more fine-tuned resolution of these objects.
    // Laravel调用closure的方式
    if ($concrete instanceof Closure) {
        return $concrete($this, $parameters);
    }

    $reflector = new ReflectionClass($concrete);

    // If the type is not instantiable, the developer is attempting to resolve
    // an abstract type such as an Interface of Abstract Class and there is
    // no binding registered for the abstractions so we need to bail out.
    if (! $reflector->isInstantiable()) {
        $message = "Target [$concrete] is not instantiable.";

        throw new BindingResolutionContractException($message);
    }

    $this->buildStack[] = $concrete;

    $constructor = $reflector->getConstructor();

    // If there are no constructors, that means there are no dependencies then
    // we can just resolve the instances of the objects right away, without
    // resolving any other types or dependencies out of these containers.
    if (is_null($constructor)) {
        array_pop($this->buildStack);

        return new $concrete;
    }

    $dependencies = $constructor->getParameters();

    // Once we have all the constructor's parameters we can create each of the
    // dependency instances and then use the reflection instances to make a
    // new instance of this class, injecting the created dependencies in.
    $parameters = $this->keyParametersByArgument(
        $dependencies, $parameters
    );

    $instances = $this->getDependencies(
        $dependencies, $parameters
    );

    array_pop($this->buildStack);

    return $reflector->newInstanceArgs($instances);
}

参考地址

  1. stackoverflow whats-the-difference-between-closure-parameters-and-the-use-keyword

  2. php.net anonymous function