Reflect Resolution 反射解决方案

One of the most powerful features of the Laravel container is its ability to automatically resolve dependencies via reflection. Reflection is the ability to inspect a classes and methods. For example, the PHP ReflectionClass class allows you to inspect the method avaliable on a given class. The PHP method method_exists is also a form of reflection. To play with PHP's reflection class, try the followring code on one of your classes:

用反射来自动处理依赖是 Laravel 容器的一个最强大的特性。反射是一种运行时探测类和方法的能力。比如,PHP 的 ReflectionClass 可以探测一个类的方法。method_exists 某种意义上说也是一种反射。我们来把玩一下 PHP 的反射类,试试下面的代码吧( StripeBiller 换成你自己定义好的类):

$reflection = new ReflectionClass('StripeBiller');
var_dump($reflection->getMethods());
var_dump($reflection->getConstants());

By leveraging this powerful feature of PHP, the Laravel IoC container can do some interesting things! For instance, consider the following class:

依靠这个强大的 PHP 特性, Laravel 的 IoC 容器可以实现很有趣的功能!考虑接下来这个类:

class UserController extends BaseController
{
    public function __construct(StripBiller $biller)
    {
        $this->biller = $biller;
    }
}

Note that the controller is type-hinting the StripBiller class. We are able to retrieve this type-hint using reflection. When the Laravel container does not have a resolver for a class explictity bound, it will try to resolve the class via reflection. The flow looks like this:

注意这个控制器的构造函数暗示着有一个 StripBiller 类型的参数。使用反射就可以检测到这种类型暗示。当 Laravel 的容器无法解决一个类型的明显绑定时,容器会试着使用反射来解决。程序流程类似于这样的:

  1. Do I have a resolver for StripBiller?

    已经有一个 StripBiller 的绑定了么?

  2. No resolver? Reflect into StripBiller to determin if it has dependencies.

    没绑定?那用反射来探测一下 StripBiller 吧。看看他都需要什么依赖。

  3. Resolve any dependencies needed by StripBiller (recursive).

    解决 StripBiller 需要的所有依赖(递归处理)

  4. Instantiate new StripBiller instance via ReflectionClass->newInstanceArgs().

    使用 ReflectionClass->newInstanceArgs() 来实例化 StripBiller

As you can see, the container is doing a lot of heavy lifting for you, which saves you from having to write resolves for every single one of your classes. This is one of the most powerful and unique features of the Laravel container, and having a strong grasp of this capability is very beneficial when building large Laravel applications.

如你所见, 容器替我们做了好多重活,这能帮你省去写大量绑定的麻烦。这就是Laravel容器最强大也是最独特的特性。熟练掌握这种能力对构建大型Laravel应用是十分有益的。

Now, let's modify our controller a bit. What if it looks like this?

下面我们修改一下控制器, 改成这样会发生什么事儿呢?

class UserController extends BaseController
{
    public function __construct(BillerInterface $biller)
    {
        $this->biller = $biller;
    }
}

Assuming we have not explicitly bound a resolver for BillerInterface, how will the container know what class to inject? Remember, interface can't be instantiated since they are just contracts. Without us giving the container any more information, it will be unable to instantiate this dependency. We need to specify a class that should be used as the default implementation of this interface, and we may do so via the bind method:

假设我们没有为 BillerInterface 做任何绑定, 容器该怎么知道要注入什么类呢?要知道,interface 不能被实例化,因为它只是个约定。如果我们不提供更多信息的话,容器是无法实例化这个依赖的。我们需要明确指出哪个类要实现这个接口,这就需要用到 bind 方法:

App::bind('BillerInterface','StripBiller');

Here, we are passing a string instead of a Closure, and this string tells the container to always use the StripBiller class anytime it needs an implementation of the BillerInterface interface. Again, we're gaining the ability to switch implementations of services with a simple one-line change to our container binding. For example, if we need to switch to Balanced Payments as our billing provider, we simply write a new BalancedBiller implementation of BillerInterface, and change our container binding:

这里我们只传了一个字符串进去,而不是一个匿名函数。 这个字符串告诉容器总是使用 StripBiller 来作为 BillerInterface 的实现类。 此外我们也获得了只改一行代码即可轻松改变实现的能力。比如,假设我们需要切换到 Balanced Payments 作为我们的支付提供商,我们只需要新写一个 BalancedBiller 来实现 BillerInterface 接口,然后这样修改容器代码:

App::bind('BillerInterface', 'BalancedBiller');

Automatically, our new implementation will be used throughout out application!

我们的应用程序就装载上了的新支付实现代码了!

When binding implementations to interfaces, you may also use the singleton method so the container only instantiates one instance of the class per request cycle:

你也可以使用 singleton 方法来实现单例模式。

App::singleton('BillerInterface', 'StripBiller');

Master The Container 掌握容器

Want to learn even more about the container? Read through its source! The container is only one class: Illuminate\Container\Container. Read over the source code to gain a deeper understanding of how the container works under the hood.

想了解更多关于容器的知识?去读源码!容器只有一个类 Illuminate\Container\Container , 读完了你就对容器有更深的认识了。

results matching ""

    No results matching ""