前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Laravel5.3之IoC Container实例化源码解析

Laravel5.3之IoC Container实例化源码解析

作者头像
botkenni
发布2022-01-10 09:32:55
2K0
发布2022-01-10 09:32:55
举报
文章被收录于专栏:IT码农

说明:本文主要学习Laravel容器的实例化过程,主要包括Register Base Bindings, Register Base Service Providers , Register Core Container Aliases and Set the Base Path等四个过程。同时并把自己的一点研究心得分享出来,希望对别人有所帮助。

开发环境:Laravel5.3 + PHP7 + OS X10.11

Laravel的入口文件是public/index.php文件,首先第一步加载composer的autoload文件:

代码语言:javascript
复制
// bootstrap/autoload.php
require __DIR__.'/../vendor/autoload.php';

关于composer自动加载原理可看这篇文章:Laravel5.2之Composer自动加载

然后开始实例化Application容器得到全局变量$app:

代码语言:javascript
复制
$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

输入的是project的根路径,研究下\Illuminate\Foundation\Application的构造函数源码:

代码语言:javascript
复制
    public function __construct($basePath = null)
    {
        $this->registerBaseBindings();

        $this->registerBaseServiceProviders();

        $this->registerCoreContainerAliases();

        if ($basePath) {
            $this->setBasePath($basePath);
        }
    }

Create Application过程中做了4件事:

代码语言:javascript
复制
 1. register base bindings.

 2. register base service providers(\Illuminate\Events\EventServiceProvider and \Illuminate\Routing\RoutingServiceProvider).

 3. register core service aliases 
('app', 'auth', 'auth.driver', 'blade.compiler', 'cache', 'cache.store', 'config', 'cookie', 'encrypter', 'db', 'db.connection', 
'events', 'files', 'filesystem', 'filesystem.disk', 'filesystem.cloud', 'hash', 'translator', 'log', 'mailer', 
'auth.password', 'auth.password.broker', 'queue', 'queue.connection', 'queue.failer', 'redirect', 'redis', 'request', 
'router', 'session', 'session.store', 'url', 'validator', 'view'), and these core service will be registered later.

 4. set the base path, including 
'path' = __DIR__ . '/app', 'path.base' = __DIR__ , 'path.lang' = __DIR__ . '/resources/lang',
'path.config' = __DIR__ . '/config', 'path.public' = __DIR__ . '/public', 'path.storage' = __DIR__ . '/storage', 
'path.database' = __DIR__ . '/database', 'path.resources' = __DIR__ . '/resources', 
'path.bootstrap' = __DIR__ . '/bootstrap'. U can get theses path everywhere in the way, 
e.g.  public_path('/js/app.js') === __DIR__ . '/public/js/app.js';

1. Register Base Bindings

基础绑定主要是绑定当前Application对象进容器,绑定的是同一对象,但给了两个名字:

代码语言:javascript
复制
$this->instance('app', $this);

$this->instance('Illuminate\Container\Container', $this);

OK, 那instance()是如何绑定服务的?\Illuminate\Foundation\Application是extends from the \Illuminate\Container\Container,看instance()源码:

代码语言:javascript
复制
    /**
     * Register an existing instance as shared in the container.
     *
     * @param  string  $abstract
     * @param  mixed   $instance
     * @return void
     */
    public function instance($abstract, $instance)
    {
        // $abstract如果是string,截取右边的'\', 如\Illuminate\Foundation\Application => Illuminate\Foundation\Application
        $abstract = $this->normalize($abstract);
        
        if (is_array($abstract)) {
            list($abstract, $alias) = $this->extractAlias($abstract);

            $this->alias($abstract, $alias);
        }

        unset($this->aliases[$abstract]);

        $bound = $this->bound($abstract);

        $this->instances[$abstract] = $instance;

        if ($bound) {
            $this->rebound($abstract);
        }
    }

分解代码,看别名的注册:

代码语言:javascript
复制
    if (is_array($abstract)) {
        list($abstract, $alias) = $this->extractAlias($abstract);

        $this->alias($abstract, $alias);
    }
    
        ...
        
    protected function extractAlias(array $definition)
    {
        return [key($definition), current($definition)];
    }   
    public function alias($abstract, $alias)
    {
        $this->aliases[$alias] = $this->normalize($abstract);
    }     

如果abstract是数组, e.g. this->instance(['app' => 'IlluminateFoundationApplication'], this),则IlluminateFoundationApplication是alias name,存入Container class的aliases[ ]属性中,这样存入值是:

代码语言:javascript
复制
$aliases = [
    '\Illuminate\Foundation\Application' => 'app',
];

然后在注册到属性$instances[ ]中,则上面的绑定代码类似于;

代码语言:javascript
复制
// 这里加个别名
$this->instances['app' => '\Illuminate\Foundation\Application'] = (new \Illuminate\Foundation\Application($path = __DIR__));
$this->instances['Illuminate\Container\Container'] = (new \Illuminate\Foundation\Application($path = __DIR__));

可以PHPUnit测试下别名这个feature:

代码语言:javascript
复制
public function testAlias ()
{
    // make()是从Container中解析出service,与instance正好相反
    $object1 = App::make('app');
    $object2 = App::make('\Illuminate\Foundation\Application');
    $this->assertInstanceOf(\Illuminate\Foundation\Application::class, $object1);
    $this->assertInstanceOf(\Illuminate\Foundation\Application::class, $object2);
}

由于不是单例绑定singleton(),这里object1与object2都是\Illuminate\Foundation\Application的对象,但不是同一对象。singleton()和make()稍后讨论下。

同时检查下之前是否已经绑定了,如果已经绑定了,则执行之前rebinding()的回调函数,主要是执行Container的$reboundCallbacks[ ]属性值。Container提供了rebinding()函数供再一次补充绑定(如再给'app'绑定一些之前绑定没有的的行为),PHPUnit测试下:

代码语言:javascript
复制
public function testReboundCallbacks() 
{
    // Arrange
    $container = new Container;
    
    // Actual
    $container->instance('app', function(){
        return 'app1';
    });
    $a = 0
    $container->rebinding('app', function() use ($a) {
        $a = 1;
    });
    // 再次绑定时,触发上一次rebinding中绑定该'app'的回调
    $container->instance('app', function () {
        return 'app2';
    });
    
    // Assert
    $this->assertEqual(1, $a);
}

Container的作用是供service的绑定和解析,绑定有三种方法:bind(),singleton(),instance();解析是make(),稍后讨论下容器中最重要的这几个feature。

2. Register Base Service Providers

绑定了名为'app','IlluminateContainerContainer'的两个service后(尽管绑定的service相同),看下绑定了两个基础service provider:

代码语言:javascript
复制
$this->register(new \Illuminate\Events\EventServiceProvider($this));
$this->register(new \Illuminate\Routing\RoutingServiceProvider($this));

两个基础的service provider is: IlluminateEventsEventServiceProvider和IlluminateRoutingRoutingServiceProvider。看下是如何注册两个service provider:

代码语言:javascript
复制
    public function register($provider, $options = [], $force = false)
    {
        if (($registered = $this->getProvider($provider)) && ! $force) {
            return $registered;
        }
        
        if (is_string($provider)) {
            $provider = $this->resolveProviderClass($provider);
        }

        if (method_exists($provider, 'register')) {
            $provider->register();
        }
        
        foreach ($options as $key => $value) {
            $this[$key] = $value;
        }

        $this->markAsRegistered($provider);

        // If the application has already booted, we will call this boot method on
        // the provider class so it has an opportunity to do its boot logic and
        // will be ready for any usage by the developer's application logics.
        if ($this->booted) {
            $this->bootProvider($provider);
        }

        return $provider;
    }

首先检查是否已经注册了,如果注册了就直接返回,主要是检查Application class 的$serviceProviders[ ]的值,看下代码:

代码语言:javascript
复制
    if (($registered = $this->getProvider($provider)) && ! $force) {
        return $registered;
    }

    ...

    public function getProvider($provider)
    {
        $name = is_string($provider) ? $provider : get_class($provider);

        return Arr::first($this->serviceProviders, function ($value) use ($name) {
            return $value instanceof $name;
        });
    }    

如果输入的是字符串,就直接new provider(this)生成对象,所以上面两个注册可以这么写:

代码语言:javascript
复制
$this->register(\Illuminate\Events\EventServiceProvider::class);
$this->register(\Illuminate\Routing\RoutingServiceProvider::class);

然后执行service provider中的register()方法,稍后看下两个base service provider注册了哪些service。

然后把注册过的service provider标记为provided,就是写入到serviceProviders[ ]中,而开始是先检查serviceProviders[ ]中,有没有已经注册过的将要注册的service。看下markAsRegistered()源码:

代码语言:javascript
复制
    protected function markAsRegistered($provider)
    {
        $this['events']->fire($class = get_class($provider), [$provider]);

        $this->serviceProviders[] = $provider;

        $this->loadedProviders[$class] = true;
    }

这里还用了刚注册的'events' service来触发该service provider已经注册的事件,并把该service provider写入到已经加载的属性中loadedProviders[ ].

然后检查程序是否已经启动,如果已经启动完成了,再执行每一个service provider中的boot()方法,这里会发现为啥每一个service provider里经常出现register()和boot()方法,并且register()是注册服务的,等所有服务注册完,再去boot()一些东西。当然,这里程序刚刚注册第一个EventServiceProvider,程序离完全启动还早着呢。不过,可以先看下这里的bootProvider()方法源码:

代码语言:javascript
复制
    protected function bootProvider(ServiceProvider $provider)
    {
        if (method_exists($provider, 'boot')) {
            return $this->call([$provider, 'boot']);
        }
    }
    /**
     * Call the given Closure / class@method and inject its dependencies.
     *
     * @param  callable|string  $callback
     * @param  array  $parameters
     * @param  string|null  $defaultMethod
     * @return mixed
     */
    public function call($callback, array $parameters = [], $defaultMethod = null)
    {
        if ($this->isCallableWithAtSign($callback) || $defaultMethod) {
            return $this->callClass($callback, $parameters, $defaultMethod);
        }

        $dependencies = $this->getMethodDependencies($callback, $parameters);

        return call_user_func_array($callback, $dependencies);
    }

重点看下call()这个Container另一个重要的函数,如果这么调用call(EventServiceProvider@register),那就通过Container::callClass()来解析出class和method,然后在调用call(),看下callClass()源码:

代码语言:javascript
复制
protected function callClass($target, array $parameters = [], $defaultMethod = null)
    {
        $segments = explode('@', $target);
        $method = count($segments) == 2 ? $segments[1] : $defaultMethod;

        if (is_null($method)) {
            throw new InvalidArgumentException('Method not provided.');
        }

        // 然后在这样调用call([$class, $method], $parameters)
        return $this->call([$this->make($segments[0]), $method], $parameters);
    }

也就是说,如果call(EventServiceProvider@register)这种方式的话先转化成call([class, method], parameters)来调用,当然要是直接这种方式就不用在转换了。这里是通过[(new EventServiceProvider(app)), 'boot']类似这种方式来调用的。在调用boot()时有依赖怎么办?使用[class, method]通过getMethodDependencies(parameters)来获取dependencies,看下getMethodDependencies(

代码语言:javascript
复制
    protected function getMethodDependencies($callback, array $parameters = [])
    {
        $dependencies = [];

        foreach ($this->getCallReflector($callback)->getParameters() as $parameter) {
            $this->addDependencyForCallParameter($parameter, $parameters, $dependencies);
        }

        return array_merge($dependencies, $parameters);
    }
    protected function getCallReflector($callback)
    {
        if (is_string($callback) && strpos($callback, '::') !== false) {
            $callback = explode('::', $callback);
        }

        if (is_array($callback)) {
            return new ReflectionMethod($callback[0], $callback[1]);
        }

        return new ReflectionFunction($callback);
    }
    protected function addDependencyForCallParameter(ReflectionParameter $parameter, array &$parameters, &$dependencies)
    {
        if (array_key_exists($parameter->name, $parameters)) {
            $dependencies[] = $parameters[$parameter->name];

            unset($parameters[$parameter->name]);
        } elseif ($parameter->getClass()) {
            $dependencies[] = $this->make($parameter->getClass()->name);
        } elseif ($parameter->isDefaultValueAvailable()) {
            $dependencies[] = $parameter->getDefaultValue();
        }
    }

这里是通过PHP的Reflector Method来获取依赖,依赖如果是对象的话再继续make()自动解析出service,是个外部传进来的值则代入,有默认值传默认值。反射(Reflector)是PHP的一个重要的高级特性,值得研究。总的来说,在boot()方法中如果有dependency,container会自动解析,不管该dependency是不是某个service。这就是Method Injection,我们知道Dependency Injection有两种:Constructor Injection and Method Injection,这里可看到Method Injection是如何实现的。

OK,然后看下两个service provider注册了些什么?首先注册EventServiceProvider中提供的service,看有哪些:

代码语言:javascript
复制
public function register()
{
    $this->app->singleton('events', function ($app) {
        return (new Dispatcher($app))->setQueueResolver(function () use ($app) {
            return $app->make('Illuminate\Contracts\Queue\Factory');
        });
    });
}

OK,只有一个名为'events'的service注册到容器中了,并且是单例注册的。看下singleton()的源码:

代码语言:javascript
复制
    public function singleton($abstract, $concrete = null)
    {
        $this->bind($abstract, $concrete, true);
    }
    
    public function bind($abstract, $concrete = null, $shared = false)
    {
        $abstract = $this->normalize($abstract);

        $concrete = $this->normalize($concrete);

        // 如果是数组,抽取别名并且注册到$aliases[]中,上文已经讨论
        if (is_array($abstract)) {
            list($abstract, $alias) = $this->extractAlias($abstract);

            $this->alias($abstract, $alias);
        }

        $this->dropStaleInstances($abstract);

        if (is_null($concrete)) {
            $concrete = $abstract;
        }

        // If the factory is not a Closure, it means it is just a class name which is
        // bound into this container to the abstract type and we will just wrap it
        // up inside its own Closure to give us more convenience when extending.
        if (! $concrete instanceof Closure) {
            $concrete = $this->getClosure($abstract, $concrete);
        }

        $this->bindings[$abstract] = compact('concrete', 'shared');

        // If the abstract type was already resolved in this container we'll fire the
        // rebound listener so that any objects which have already gotten resolved
        // can have their copy of the object updated via the listener callbacks.
        if ($this->resolved($abstract)) {
            $this->rebound($abstract);
        }
    }
    
    protected function dropStaleInstances($abstract)
    {
        unset($this->instances[$abstract], $this->aliases[$abstract]);
    }

singleton()实际上就是shared = true 的bind()。同时舍弃掉instances[]中已经注册过的名为abstract的service,当然别名数组也别忘了舍弃。如果concrete没有提供,则使用abstract自动补全concrete,并且使用getClosure()封装下做个Closure:

代码语言:javascript
复制
    protected function getClosure($abstract, $concrete)
    {
        // $c 就是$container,即Container Object,会在回调时传递给这个变量
        return function ($c, $parameters = []) use ($abstract, $concrete) {
            $method = ($abstract == $concrete) ? 'build' : 'make';

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

concrete没有提供绑定的情况,如:this->singleton(IlluminateContainerContainer::class); 只提供了

这里,就是向$bindings[ ]中注册下,现在它的值类似这样:

代码语言:javascript
复制
$bindings = [
    'events' => [
        'concrete' => function ($app) {
                        return (new Dispatcher($app))->setQueueResolver(function () use ($app) {return $app->make('Illuminate\Contracts\Queue\Factory');});
                      },
        'shared'   => true,
    ],
];

已经说了singleton()和binding()注册的区别就是'shared'的值不一样,如果是this->app->binding('events', Closure),则

代码语言:javascript
复制
$bindings = [
    'events' => [
        'concrete' => function ($app) {
                        return (new Dispatcher($app))->setQueueResolver(function () use ($app) {return $app->make('Illuminate\Contracts\Queue\Factory');});
                      },
        'shared'   => false,
 
    ],
 
];

OK,看下RoutingServiceProvider中注册了些什么service? 上文说过,Application中register()会调用service provider中的register()方法,看下\Illuminate\Routing\RoutingServiceProvider源码就发现其注册了几个service:'router', 'url', 'redirect', PsrHttpMessage|ServerRequestInterface::class, PsrHttpMessageResponseInterface::class, IlluminateContractsRoutingResponseFactory::class。 只有IlluminateContractsRoutingResponseFactory::class是singleton(),其余是bind(),e.g. 'router' service source code:

代码语言:javascript
复制
$this->app['router'] = $this->app->share(function ($app) {
    return new Router($app['events'], $app);
});

为什么说是bind()?并且$this->app['router']是啥意思?

OK, 看下share()的源码:

代码语言:javascript
复制
public function share(Closure $closure)
{
    return function ($container) use ($closure) {
        static $object;
 
        if (is_null($object)) {
            $object = $closure($container);
        }
 
        return $object;
    };
}

share()仅仅执行closure()并传入container,所以上面的'router' service代码类似于:

代码语言:javascript
复制
$this->app['router'] = new Router($app['events'], $app);

$this->app是Container对象,而Container implement ArrayAccess这个Interface,实现对类的属性做数组式访问,所以Container必须实现四个方法:

代码语言:javascript
复制
@link http://php.net/manual/en/arrayaccess.offsetset.php
public function offsetExists($offset);
public function offsetGet($offset);
public function offsetSet($offset, $value);
public function offsetUnset($offset);

这里是对$this->app赋值,所以看下offsetSet()源码:

代码语言:javascript
复制
public function offsetSet($key, $value)
{
    if (! $value instanceof Closure) {
        $value = function () use ($value) {
            return $value;
        };
    }
 
    $this->bind($key, $value);
}

这里是用bind()来绑定到container中,所以上文中说是bind(),而不是其他。所上文的代码类似于这样:

代码语言:javascript
复制
$this->app['router'] = new Router($app['events'], $app);
  
is like:
  
$object = new Router($app['events'], $app);
$this->bind('router', function () use ($object) {return $object});

总的来说,就是通过注册EventServiceProvider and RoutingServiceProvider来绑定了一些service, e.g. 'events', 'router' and so on.

3. Register Core Container Aliases

由于PHP使用namespace来命名class,有时类名很长,所以需要做个别名alias图方便。看下registerCoreContainerAliases()的源码:

代码语言:javascript
复制
    public function registerCoreContainerAliases()
    {
        $aliases = [
            'app'                  => ['Illuminate\Foundation\Application', 'Illuminate\Contracts\Container\Container', 'Illuminate\Contracts\Foundation\Application'],
            'auth'                 => ['Illuminate\Auth\AuthManager', 'Illuminate\Contracts\Auth\Factory'],
            'auth.driver'          => ['Illuminate\Contracts\Auth\Guard'],
            'blade.compiler'       => ['Illuminate\View\Compilers\BladeCompiler'],
            'cache'                => ['Illuminate\Cache\CacheManager', 'Illuminate\Contracts\Cache\Factory'],
            'cache.store'          => ['Illuminate\Cache\Repository', 'Illuminate\Contracts\Cache\Repository'],
            'config'               => ['Illuminate\Config\Repository', 'Illuminate\Contracts\Config\Repository'],
            'cookie'               => ['Illuminate\Cookie\CookieJar', 'Illuminate\Contracts\Cookie\Factory', 'Illuminate\Contracts\Cookie\QueueingFactory'],
            'encrypter'            => ['Illuminate\Encryption\Encrypter', 'Illuminate\Contracts\Encryption\Encrypter'],
            'db'                   => ['Illuminate\Database\DatabaseManager'],
            'db.connection'        => ['Illuminate\Database\Connection', 'Illuminate\Database\ConnectionInterface'],
            'events'               => ['Illuminate\Events\Dispatcher', 'Illuminate\Contracts\Events\Dispatcher'],
            'files'                => ['Illuminate\Filesystem\Filesystem'],
            'filesystem'           => ['Illuminate\Filesystem\FilesystemManager', 'Illuminate\Contracts\Filesystem\Factory'],
            'filesystem.disk'      => ['Illuminate\Contracts\Filesystem\Filesystem'],
            'filesystem.cloud'     => ['Illuminate\Contracts\Filesystem\Cloud'],
            'hash'                 => ['Illuminate\Contracts\Hashing\Hasher'],
            'translator'           => ['Illuminate\Translation\Translator', 'Symfony\Component\Translation\TranslatorInterface'],
            'log'                  => ['Illuminate\Log\Writer', 'Illuminate\Contracts\Logging\Log', 'Psr\Log\LoggerInterface'],
            'mailer'               => ['Illuminate\Mail\Mailer', 'Illuminate\Contracts\Mail\Mailer', 'Illuminate\Contracts\Mail\MailQueue'],
            'auth.password'        => ['Illuminate\Auth\Passwords\PasswordBrokerManager', 'Illuminate\Contracts\Auth\PasswordBrokerFactory'],
            'auth.password.broker' => ['Illuminate\Auth\Passwords\PasswordBroker', 'Illuminate\Contracts\Auth\PasswordBroker'],
            'queue'                => ['Illuminate\Queue\QueueManager', 'Illuminate\Contracts\Queue\Factory', 'Illuminate\Contracts\Queue\Monitor'],
            'queue.connection'     => ['Illuminate\Contracts\Queue\Queue'],
            'queue.failer'         => ['Illuminate\Queue\Failed\FailedJobProviderInterface'],
            'redirect'             => ['Illuminate\Routing\Redirector'],
            'redis'                => ['Illuminate\Redis\Database', 'Illuminate\Contracts\Redis\Database'],
            'request'              => ['Illuminate\Http\Request', 'Symfony\Component\HttpFoundation\Request'],
            'router'               => ['Illuminate\Routing\Router', 'Illuminate\Contracts\Routing\Registrar'],
            'session'              => ['Illuminate\Session\SessionManager'],
            'session.store'        => ['Illuminate\Session\Store', 'Symfony\Component\HttpFoundation\Session\SessionInterface'],
            'url'                  => ['Illuminate\Routing\UrlGenerator', 'Illuminate\Contracts\Routing\UrlGenerator'],
            'validator'            => ['Illuminate\Validation\Factory', 'Illuminate\Contracts\Validation\Factory'],
            'view'                 => ['Illuminate\View\Factory', 'Illuminate\Contracts\View\Factory'],
        ];

        foreach ($aliases as $key => $aliases) {
            foreach ($aliases as $alias) {
                $this->alias($key, $alias);
            }
        }
    }

给class name注册个别名,并且在相同数组里有着共同的别名,e.g. 'IlluminateFoundationApplication', 'IlluminateContractsContainerContainer' and 'IlluminateContractsFoundationApplication' share the same alias name 'app'.

4. Set the Base Path

Application Constructor里需要传入一个path这个原料来构造类,这里path是这个project的当前绝对路径。同时绑定一些常用的文件夹路径供将来使用,看下构造函数中源码:

代码语言:javascript
复制
public function __construct($basePath)
{
      ...
      
    if ($basePath) {
        $this->setBasePath($basePath);
    }
}
public function setBasePath($basePath)
{
    $this->basePath = rtrim($basePath, '\/');
 
    $this->bindPathsInContainer();
 
    return $this;
}
protected function bindPathsInContainer()
{
    $this->instance('path', $this->path());
    $this->instance('path.base', $this->basePath());
    $this->instance('path.lang', $this->langPath());
    $this->instance('path.config', $this->configPath());
    $this->instance('path.public', $this->publicPath());
    $this->instance('path.storage', $this->storagePath());
    $this->instance('path.database', $this->databasePath());
    $this->instance('path.resources', $this->resourcePath());
    $this->instance('path.bootstrap', $this->bootstrapPath());
}

instance()上文已经讨论过,所以这里的$instances[ ]类似于这样:

代码语言:javascript
复制
$instances = [
    'path'           => __DIR__ . '/app',
    'path.base'      => __DIR__ . '/',
    'path.lang'      => __DIR__ . '/resources/lang',
    'path.config'    => __DIR__ . '/config',
    'path.public'    => __DIR__ . '/public',
    'path.storage'   => __DIR__ . '/storage',
    'path.database'  => __DIR__ . '/database',
    'path.resources' => __DIR__ . '/resources',
    'path.bootstrap' => __DIR__ . '/bootstrap',
];

OK,看下bootstrap/app.php文件,在得到$app这个实例化对象后,再单例绑定Two Kernel and One Exception:

代码语言:javascript
复制
$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    RightCapital\Admin\Http\Kernel::class
);
 
$app->singleton(
    Illuminate\Contracts\Console\Kernel::class,
    RightCapital\Admin\Console\Kernel::class
);
 
$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    RightCapital\Admin\Exceptions\Handler::class
);

最后,就得到一个塞满好几个service的容器了,而未被实例化前是个空Container.整个的Application的实例化过程分析就OK了。

总结:本文主要学习了Application的实例化过程,主要学习了实例化过程中向这个IoC(Inversion of Control) Container绑定了哪些service,并讨论了绑定的三个方法:bind(),singleton(),instance(),解析方法make()留到单独研究Container时再讨论吧。下次分享下Container学习心得,并写上PHPUnit测试,到时见。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016/10/25 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. Register Base Bindings
  • 2. Register Base Service Providers
  • 3. Register Core Container Aliases
  • 4. Set the Base Path
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档