Laravel 實例化應用解析

實例化應用解析

文件位於/var/www/laravel/bootstrap/app.php,返回一個$app

實例化應用(參數爲項目根目錄)

$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

public function __construct($basePath = null)
{
    if ($basePath) {
        // 設置基礎路徑
        $this->setBasePath($basePath);
    }
    // 基礎綁定
    $this->registerBaseBindings();
    // 註冊基礎服務
    $this->registerBaseServiceProviders();
    // 核心容器別名設置
    $this->registerCoreContainerAliases();
}

1.1 設置基礎路徑php

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());
}
public function instance($abstract, $instance)
{
    // 移除 abstractAliases
    $this->removeAbstractAlias($abstract);
    // 移除 aliases
    unset($this->aliases[$abstract]);
    // 直接設置容器屬性 $this->instances
    $this->instances[$abstract] = $instance;
    // 當從新綁定時,進行從新構建
    if ($this->bound($abstract)) {
        $this->rebound($abstract);
    }
}
protected function removeAbstractAlias($searched)
{
    if (! isset($this->aliases[$searched])) {
        return;
    }

    foreach ($this->abstractAliases as $abstract => $aliases) {
        foreach ($aliases as $index => $alias) {
            if ($alias == $searched) {
                unset($this->abstractAliases[$abstract][$index]);
            }
        }
    }
}

數組 $this->instances 以下:laravel

$this->instances['path'] = '/var/www/laravel/app';  
$this->instances['path.base'] = '/var/www/laravel';  
$this->instances['path.lang'] = '/var/www/laravel/resources/lang';  
$this->instances['path.config'] = '/var/www/laravel/config';  
$this->instances['path.public'] = '/var/www/laravel/public';  
$this->instances['path.storage'] = '/var/www/laravel/storage';  
$this->instances['path.database'] = '/var/www/laravel/database';  
$this->instances['path.resources'] = '/var/www/laravel/resources';  
$this->instances['path.bootstrap'] = '/var/www/laravel/bootstrap';

1.2 基礎綁定redis

protected function registerBaseBindings()
{
    static::setInstance($this);        // 設置靜態訪問方式

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

    $this->instance(Container::class, $this);
}

一樣追加 $this->instances 以下:bootstrap

$this->instances['app'] = object(Illuminate\Foundation\Application);  
$this->instances['Illuminate\Container\Container'] = object(Illuminate\Foundation\Application);

1.3 註冊基礎服務segmentfault

protected function registerBaseServiceProviders()
{
    $this->register(new EventServiceProvider($this));

    $this->register(new LogServiceProvider($this));

    $this->register(new RoutingServiceProvider($this));
}
public function register($provider, $options = [], $force = false)
{
    // 已註冊,則直接返回
    if (($registered = $this->getProvider($provider)) && ! $force) {
        return $registered;
    }
    // 若傳的是字符串,則直接建立對象
    if (is_string($provider)) {
        $provider = $this->resolveProvider($provider);
    }
    // 服務提供者裏若存在register方法,則直接自動調用
    if (method_exists($provider, 'register')) {
        $provider->register();
    }

    $this->markAsRegistered($provider);
    // 當應用已經徹底boot時,自動執行服務提供者裏面的boot方法
    if ($this->booted) {
        $this->bootProvider($provider);
    }

    return $provider;
}
public function getProvider($provider)
{
    $name = is_string($provider) ? $provider : get_class($provider);
    // 從$this->serviceProviders中取出第一個符合closure的值
    return Arr::first($this->serviceProviders, function ($value) use ($name) {
        return $value instanceof $name;    // $value是否$name的對象
    });
}
protected function markAsRegistered($provider)
{
    $this->serviceProviders[] = $provider;

    $this->loadedProviders[get_class($provider)] = true;
}
protected function bootProvider(ServiceProvider $provider)
{
    // 存在則調用相應服務提供者的boot方法
    if (method_exists($provider, 'boot')) {
        return $this->call([$provider, 'boot']);
    }
}

通常狀況下,服務提供者都會提供兩個方法:register和boot(register先負責註冊服務,boot則負責
隨後的引導工做,此時能夠調用前面已經註冊的服務)。在註冊服務提供者的時候,會相應的調用服務提供
者的register方法,而後再進行boot方法的調用。數組

生成數組以下:cookie

event:
    $this->bindings['events'] = [
        'concrete' => function ($app) {
                            return (new Dispatcher($app))->setQueueResolver(function () use ($app) {
                                return $app->make(QueueFactoryContract::class);
                            });
                        }
        'shared' => 'true',
    ];
    log:
    $this->bindings['log'] = [
        'concrete' => function () {
                            return function createLogger()
                                    {
                                        $log = new Writer(
                                            new Monolog($this->channel()), $this->app['events']
                                        );

                                        if ($this->app->hasMonologConfigurator()) {
                                            call_user_func($this->app->getMonologConfigurator(), $log->getMonolog());
                                        } else {
                                            $this->{$this->{'configure'.ucfirst($this->handler()).'Handler'}($log)}($log);
                                        }

                                        return $log;
                                    }
                        }
        'shared' => 'true',
    ];
    router:
    $this->bindings['router'] = [
        'concrete' => function ($app) {
                            return new Router($app['events'], $app);
                        },
        'shared' => 'true',
    ];
    $this->bindings['url'] = [
        'concrete' => function ($app) {
                            $routes = $app['router']->getRoutes();

                            $app->instance('routes', $routes);

                            $url = new UrlGenerator(
                                $routes, $app->rebinding(
                                    'request', $this->requestRebinder()
                                )
                            );

                            $url->setSessionResolver(function () {
                                return $this->app['session'];
                            });

                            $app->rebinding('routes', function ($app, $routes) {
                                $app['url']->setRoutes($routes);
                            });

                            return $url;
                        },
        'shared' => 'true',
    ];
    $this->bindings['redirect'] = [
        'concrete' => function ($app) {
                        $redirector = new Redirector($app['url']);

                        if (isset($app['session.store'])) {
                            $redirector->setSession($app['session.store']);
                        }

                        return $redirector;
                    },
        'shared' => 'true',
    ];
    $this->bindings[ServerRequestInterface::class] = [
        'concrete' => function ($app) {
                            return (new DiactorosFactory)->createRequest($app->make('request'));
                        },
        'shared' => 'true',
    ];
    $this->bindings[ResponseInterface::class] = [
        'concrete' => function ($app) {
                            return new PsrResponse();
                        },
        'shared' => 'true',
    ];
    $this->bindings[ResponseFactoryContract::class] = [
        'concrete' => function ($app) {
                            return new ResponseFactory($app[ViewFactoryContract::class], $app['redirect']);
                        },
        'shared' => 'true',
    ];

注意:session

在register方法中,您只能將事物綁定到 服務容器 。不該該在register方法中嘗試註冊任何事件監聽
器,路由或者任何其餘功能。不然,您可能會意外的使用到還沒有加載的服務提供者提供的服務。

1.4 核心容器別名設置app

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

    foreach ($aliases as $key => $aliases) {
        foreach ($aliases as $alias) {
            $this->alias($key, $alias);
        }
    }
}
public function alias($abstract, $alias)
{
    $this->aliases[$alias] = $abstract;

    $this->abstractAliases[$abstract][] = $alias;
}

生成數組以下:ide

$this->aliases['Illuminate\Foundation\Application'] = 'app';
$this->aliases['Illuminate\Contracts\Container\Container'] = 'app';
$this->aliases['Illuminate\Contracts\Foundation\Application'] = 'app';
$this->abstractAliases['app'][] = 'Illuminate\Foundation\Application';
$this->abstractAliases['app'][] = 'Illuminate\Contracts\Container\Container';
$this->abstractAliases['app'][] = 'Illuminate\Contracts\Foundation\Application';
……

註冊單例 Laravel 服務容器解析

$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);
$app->singleton(
    Illuminate\Contracts\Console\Kernel::class,
    App\Console\Kernel::class
);
$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);

public function singleton($abstract, $concrete = null)
{
    // 
    $this->bind($abstract, $concrete, true);
}

生成數組以下

$this->bindings['Illuminate\Contracts\Http\Kernel'] = [
    'concrete' => function ($container, $parameters = []) {
                        return $container->make('App\Http\Kernel', $parameters);
                    },
    'shared' => 'true',
];
$this->bindings['Illuminate\Contracts\Console\Kernel'] = [
    'concrete' => function ($container, $parameters = []) {
                        return $container->make('App\Console\Kernel', $parameters);
                    },
    'shared' => 'true',
];
$this->bindings['Illuminate\Contracts\Debug\ExceptionHandler'] = [
    'concrete' => function ($container, $parameters = []) {
                        return $container->make('App\Exceptions\Handler', $parameters);
                    },
    'shared' => 'true',
];

總結:

  1. 應用實例化的流程以下

    設置基礎路徑($this->instances['path.*']) => 註冊基礎綁定($this->instances['app|Container']) => 註冊基礎服務($this->bindings[]) => 註冊核心容器別名($this->aliases[],$this->abstractAliases[])。至此,應用的實例化完成。
  2. 服務容器

    服務容器裏面的服務綁定形式有bind、singleton、instance方式,singleton和bind的惟一差異是傳入的share真假問題,經過share變量來肯定singleton只實例化一次。而instance綁定的就是已經實例化的對象。when則是經過上下文來進行綁定的,每次都會進行綁定和實例化。服務容器裏面的服務調用方式:make方法、resolve全局函數、自動注入,都可以經過容器服務來解決依賴關係。
相關文章
相關標籤/搜索