资讯专栏INFORMATION COLUMN

Laravel学习笔记之IoC Container实例化源码解析

ningwang / 2652人阅读

摘要:说明本文主要学习容器的实例化过程,主要包括等四个过程。看下的源码如果是数组,抽取别名并且注册到中,上文已经讨论实际上就是的。

说明:本文主要学习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文件:

// bootstrap/autoload.php
require __DIR__."/../vendor/autoload.php";

关于composer自动加载原理可看这篇文章:Laravel学习笔记之Composer自动加载

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

$app = new IlluminateFoundationApplication(
    realpath(__DIR__."/../")
);

输入的是project的根路径,研究下IlluminateFoundationApplication的构造函数源码:

    public function __construct($basePath = null)
    {
        $this->registerBaseBindings();

        $this->registerBaseServiceProviders();

        $this->registerCoreContainerAliases();

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

Create Application过程中做了4件事:

 1. register base bindings.

 2. register base service providers(IlluminateEventsEventServiceProvider and IlluminateRoutingRoutingServiceProvider).

 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对象进容器,绑定的是同一对象,但给了两个名字:

$this->instance("app", $this);

$this->instance("IlluminateContainerContainer", $this);

OK, 那instance()是如何绑定服务的?
IlluminateFoundationApplication是extends from the IlluminateContainerContainer,看instance()源码:

    /**
     * Register an existing instance as shared in the container.
     *
     * @param  string  $abstract
     * @param  mixed   $instance
     * @return void
     */
    public function instance($abstract, $instance)
    {
        // $abstract如果是string,截取右边的"", 如IlluminateFoundationApplication => IlluminateFoundationApplication
        $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);
        }
    }

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

    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),则app是alias name,存入Container class的$aliases[ ]属性中,这样存入值是:

$aliases = [
    "app"=> "IlluminateFoundationApplication",
];

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

// 这里加个别名
$this->instances["app" => "IlluminateFoundationApplication"] = (new IlluminateFoundationApplication($path = __DIR__));
$this->instances["IlluminateContainerContainer"] = (new IlluminateFoundationApplication($path = __DIR__));

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

public function testAlias ()
{
    // make()是从Container中解析出service,与instance正好相反
    $object1 = App::make("app");
    $object2 = App::make("IlluminateFoundationApplication");
    $this->assertInstanceOf(IlluminateFoundationApplication::class, $object1);
    $this->assertInstanceOf(IlluminateFoundationApplication::class, $object2);
}

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

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

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:

$this->register(new IlluminateEventsEventServiceProvider($this));
$this->register(new IlluminateRoutingRoutingServiceProvider($this));

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

    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[ ]的值,看下代码:

    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)生成对象,所以上面两个注册可以这么写:

$this->register(IlluminateEventsEventServiceProvider::class);
$this->register(IlluminateRoutingRoutingServiceProvider::class);

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

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

    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()方法源码:

    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()源码:

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($parameters)源码:

    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,看有哪些:

public function register()
{
    $this->app->singleton("events", function ($app) {
        return (new Dispatcher($app))->setQueueResolver(function () use ($app) {
            return $app->make("IlluminateContractsQueueFactory");
        });
    });
}

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

    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:

    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); 只提供了$abstract.

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

$bindings = [
    "events" => [
        "concrete" => function ($app) {
                        return (new Dispatcher($app))->setQueueResolver(function () use ($app) {return $app->make("IlluminateContractsQueueFactory");});
                      },
        "shared"   => true,
    ],
];

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

$bindings = [
    "events" => [
        "concrete" => function ($app) {
                        return (new Dispatcher($app))->setQueueResolver(function () use ($app) {return $app->make("IlluminateContractsQueueFactory");});
                      },
        "shared"   => false,
 
    ],
 
];

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

$this->app["router"] = $this->app->share(function ($app) {
    return new Router($app["events"], $app);
});

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

OK, 看下share()的源码:

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代码类似于:

$this->app["router"] = new Router($app["events"], $app);

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

@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()源码:

public function offsetSet($key, $value)
{
    if (! $value instanceof Closure) {
        $value = function () use ($value) {
            return $value;
        };
    }
 
    $this->bind($key, $value);
}

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

$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()的源码:

    public function registerCoreContainerAliases()
    {
        $aliases = [
            "app"                  => ["IlluminateFoundationApplication", "IlluminateContractsContainerContainer", "IlluminateContractsFoundationApplication"],
            "auth"                 => ["IlluminateAuthAuthManager", "IlluminateContractsAuthFactory"],
            "auth.driver"          => ["IlluminateContractsAuthGuard"],
            "blade.compiler"       => ["IlluminateViewCompilersBladeCompiler"],
            "cache"                => ["IlluminateCacheCacheManager", "IlluminateContractsCacheFactory"],
            "cache.store"          => ["IlluminateCacheRepository", "IlluminateContractsCacheRepository"],
            "config"               => ["IlluminateConfigRepository", "IlluminateContractsConfigRepository"],
            "cookie"               => ["IlluminateCookieCookieJar", "IlluminateContractsCookieFactory", "IlluminateContractsCookieQueueingFactory"],
            "encrypter"            => ["IlluminateEncryptionEncrypter", "IlluminateContractsEncryptionEncrypter"],
            "db"                   => ["IlluminateDatabaseDatabaseManager"],
            "db.connection"        => ["IlluminateDatabaseConnection", "IlluminateDatabaseConnectionInterface"],
            "events"               => ["IlluminateEventsDispatcher", "IlluminateContractsEventsDispatcher"],
            "files"                => ["IlluminateFilesystemFilesystem"],
            "filesystem"           => ["IlluminateFilesystemFilesystemManager", "IlluminateContractsFilesystemFactory"],
            "filesystem.disk"      => ["IlluminateContractsFilesystemFilesystem"],
            "filesystem.cloud"     => ["IlluminateContractsFilesystemCloud"],
            "hash"                 => ["IlluminateContractsHashingHasher"],
            "translator"           => ["IlluminateTranslationTranslator", "SymfonyComponentTranslationTranslatorInterface"],
            "log"                  => ["IlluminateLogWriter", "IlluminateContractsLoggingLog", "PsrLogLoggerInterface"],
            "mailer"               => ["IlluminateMailMailer", "IlluminateContractsMailMailer", "IlluminateContractsMailMailQueue"],
            "auth.password"        => ["IlluminateAuthPasswordsPasswordBrokerManager", "IlluminateContractsAuthPasswordBrokerFactory"],
            "auth.password.broker" => ["IlluminateAuthPasswordsPasswordBroker", "IlluminateContractsAuthPasswordBroker"],
            "queue"                => ["IlluminateQueueQueueManager", "IlluminateContractsQueueFactory", "IlluminateContractsQueueMonitor"],
            "queue.connection"     => ["IlluminateContractsQueueQueue"],
            "queue.failer"         => ["IlluminateQueueFailedFailedJobProviderInterface"],
            "redirect"             => ["IlluminateRoutingRedirector"],
            "redis"                => ["IlluminateRedisDatabase", "IlluminateContractsRedisDatabase"],
            "request"              => ["IlluminateHttpRequest", "SymfonyComponentHttpFoundationRequest"],
            "router"               => ["IlluminateRoutingRouter", "IlluminateContractsRoutingRegistrar"],
            "session"              => ["IlluminateSessionSessionManager"],
            "session.store"        => ["IlluminateSessionStore", "SymfonyComponentHttpFoundationSessionSessionInterface"],
            "url"                  => ["IlluminateRoutingUrlGenerator", "IlluminateContractsRoutingUrlGenerator"],
            "validator"            => ["IlluminateValidationFactory", "IlluminateContractsValidationFactory"],
            "view"                 => ["IlluminateViewFactory", "IlluminateContractsViewFactory"],
        ];

        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的当前绝对路径。同时绑定一些常用的文件夹路径供将来使用,看下构造函数中源码:

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[ ]类似于这样:

$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:

$app->singleton(
    IlluminateContractsHttpKernel::class,
    RightCapitalAdminHttpKernel::class
);
 
$app->singleton(
    IlluminateContractsConsoleKernel::class,
    RightCapitalAdminConsoleKernel::class
);
 
$app->singleton(
    IlluminateContractsDebugExceptionHandler::class,
    RightCapitalAdminExceptionsHandler::class
);

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

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

欢迎关注Laravel-China。

RightCapital招聘Laravel DevOps

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/30448.html

相关文章

  • Laravel学习笔记Container解析

    摘要:实际上的绑定主要有三种方式且只是一种的,这些已经在学习笔记之实例化源码解析聊过,其实现方法并不复杂。从以上源码发现的反射是个很好用的技术,这里给出个,看下能干些啥打印结果太长了,就不粘贴了。 说明:本文主要学习Laravel中Container的源码,主要学习Container的绑定和解析过程,和解析过程中的依赖解决。分享自己的研究心得,希望对别人有所帮助。实际上Container的绑...

    huayeluoliuhen 评论0 收藏0
  • Laravel学习笔记Middleware源解析

    摘要:学习笔记之已经聊过使用了来设计,看源码发现其巧妙用了和的一些数组函数来设计。开发环境内置函数和看源码之前,先看下这几个内置函数的使用。学习笔记之实例化源码解析已经聊过的实例化,得到中的变量,即的实例化对象。后面再学习下的源码,到时见。 说明:本文主要学习Laravel的Middleware的源码设计思想,并将学习心得分享出来,希望对别人有所帮助。Laravel学习笔记之Decorato...

    _Dreams 评论0 收藏0
  • Laravel学习笔记bootstrap源解析

    摘要:总结本文主要学习了启动时做的七步准备工作环境检测配置加载日志配置异常处理注册注册启动。 说明:Laravel在把Request通过管道Pipeline送入中间件Middleware和路由Router之前,还做了程序的启动Bootstrap工作,本文主要学习相关源码,看看Laravel启动程序做了哪些具体工作,并将个人的研究心得分享出来,希望对别人有所帮助。Laravel在入口index...

    xiaoxiaozi 评论0 收藏0
  • Laravel核心——Ioc服务容器

    摘要:服务容器在说容器之前,我们需要了解什么是容器。服务容器是一个用于管理类依赖和执行依赖注入的强大工具。几乎所有的服务容器绑定都是在服务提供者中完成,也就是在服务提供者中绑定。 服务容器 在说 Ioc 容器之前,我们需要了解什么是 Ioc 容器。 Laravel 服务容器是一个用于管理类依赖和执行依赖注入的强大工具。 在理解这句话之前,我们需要先了解一下服务容器的来龙去脉: larave...

    arashicage 评论0 收藏0
  • 为什么我们需要 Laravel IoC 容器?

    摘要:哲学的一个重要组成部分就是容器,也可以称为服务容器。那我们要怎么做呢请看下面的例子数据库连接通过上面的代码,如果我们想把改成,根本不需要去修改类构造函数里的依赖。现在我要讲下容器里到底发生了什么。 showImg(https://segmentfault.com/img/remote/1460000018868909); IOC 容器是一个实现依赖注入的便利机制 - Taylor Ot...

    xiaokai 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<