Laravel 2.2 创建 Application 实例

简述

laravel 框架运行

1 第一步,使用 Composer 完成自动加载

2 第二步

  • 创建Application实例,
  • 注册项目路径(如config、public)、
  • 注册项目基础服务(如app实例、Container实例、第三方插件)、
  • 注册项目服务提供者(Event、Log、Routing等)、
  • 注册项目服务提供者别名(auth等)

Illuminate\Foundation\Application 是框架最核心的类,管理整个框架的启动、运行以及整个生命周期,并通过 ServiceProvider 将其他功能的模块载入框架。Application 是 Container 类的子类,所以也管理者框架中其他类的实例化、存储等功能。

<?php
// 第一部分: 创建应用实例
$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

// 第二部分: 完成内核绑定
$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
);

return $app;

构造方法

一共四个方法,逐个讲

/**
 * 注册应用的基础路径并将路径绑定到 APP 容器 、注册基础服务提供者至 APP 容器 、注册核心容器别名至 APP 容器 等基础服务的注册工作
 * Create a new Illuminate application instance.
 *
 * @param  string|null  $basePath
 * @return void
 */
public function __construct($basePath = null)
{
    //设置应用程序的绝对路径
    if ($basePath) {
        $this->setBasePath($basePath);
    }
    //绑定 Application 对象、绑定 Container 对象,绑定 需要加载的第三方的插件
    $this->registerBaseBindings();
    //在容器中注册最基本的服务提供者(即ServiceProvider)
    $this->registerBaseServiceProviders();
    //在容器中注册核心类别名
    $this->registerCoreContainerAliases();
}

setBasePath

  • 设置应用程序的绝对路径
  • 依赖于初始化时传入的路径参数,路径为项目的根目录。在 setBasePath 方法中,bindPathsInContainer 方法被调用。这里又用到了 instance 方法,把路径填入了类的 $instances 数组中。
  • $this->instanceIlluminate\Container的成员方法,instance 函数的作用是绑定一个已有对象到容器中,这个对象在容器中共享并且可以通 过键获取。
/**
 * 设置应用程序的绝对路径..
 *
 * @param  string  $basePath  代码里面传的是   realpath(__DIR__.'/../'),就是根目录
 * @return $this
 */
public function setBasePath($basePath)
{
    $this->basePath = rtrim($basePath, '\/');

    $this->bindPathsInContainer();

    return $this;
}

/**
 * Bind all of the application paths in the container.
 * 绑定容器中的所有应用程序路径
 * @return void
 */
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());
}

registerBaseBindings

这里同样调用了 instance 方法,但与之前不同的是,这里传入的第二个参数是一个类,而之前的是一个字符串。第一行它为 Application 类注册了一个名为 $instance 的静态实例,二三两行则为 Application 实例中的 $instances 数组属性填入了两个 key 为 'app' 和 Container::class,value 为 $this 的值。

前两个主要是绑定 Application 对象和 Container 对象。重点分析 PackageManifest 对象之前,我们看看 $this->getCachedPackagesPath()这个函数:

/**
     * Register the basic bindings into the container.
     * 将 bindings 注册到容器中
     * @return void
     */
    protected function registerBaseBindings()
    {
        //将 $this 赋值给自身的 instance 静态变 量
        static::setInstance($this);
        //绑定 Application 对象
        //注册一个实例到容器中,这个就变成了 $this->instances['app']=application实例
        //你可以使用App::make('app')来取得一个容器对象
        $this->instance('app', $this);
        //绑定 Container 对象
        //注册一个实例到容器中, $this->instances['"Illuminate\Container\Container"]=Application 实例
        $this->instance(Container::class, $this);
        //绑定 需要加载的第三方的插件
        $this->instance(PackageManifest::class, new PackageManifest(
            new Filesystem, $this->basePath(), $this->getCachedPackagesPath()
        ));
    }

getCachedPackagesPath

这个就是 bootstrap/cache/packages.php文件路径

/**
 * Get the path to the cached packages.php file.
 * 这个就是 bootstrap/cache/packages.php文件
 * @return string
 */

public function getCachedPackagesPath()
{
    return $this->bootstrapPath().'/cache/packages.php';
}
  • 通过分析,可以看出这个文件主要是放着我们自己引入第三方的 ServiceProvidersaliases,我们对照项目根路径的 composer.json 你就可以证实了:

bootstrap/cache/packages.php文件

<?php return array (
  'fideloper/proxy' => 
  array (
    'providers' => 
    array (
      0 => 'Fideloper\\Proxy\\TrustedProxyServiceProvider',
    ),
  ),
  'encore/laravel-admin' => 
  array (
    'providers' => 
    array (
      0 => 'Encore\\Admin\\AdminServiceProvider',
    ),
    'aliases' => 
    array (
      'Admin' => 'Encore\\Admin\\Facades\\Admin',
    ),
  ),
  'laravel/tinker' => 
  array (
    'providers' => 
    array (
      0 => 'Laravel\\Tinker\\TinkerServiceProvider',
    ),
  ),
  'rebing/graphql-laravel' => 
  array (
    'providers' => 
    array (
      0 => 'Rebing\\GraphQL\\GraphQLServiceProvider',
    ),
    'aliases' => 
    array (
      'GraphQL' => 'Rebing\\GraphQL\\Support\\Facades\\GraphQL',
    ),
  ),
  'tymon/jwt-auth' => 
  array (
    'aliases' => 
    array (
      'JWTAuth' => 'Tymon\\JWTAuth\\Facades\\JWTAuth',
      'JWTFactory' => 'Tymon\\JWTAuth\\Facades\\JWTFactory',
    ),
    'providers' => 
    array (
      0 => 'Tymon\\JWTAuth\\Providers\\LaravelServiceProvider',
    ),
  ),
  'noh4ck/graphiql' => 
  array (
    'providers' => 
    array (
      0 => 'Graphiql\\GraphiqlServiceProvider',
    ),
  ),
  'rollbar/rollbar-laravel' => 
  array (
    'providers' => 
    array (
      0 => 'Rollbar\\Laravel\\RollbarServiceProvider',
    ),
    'aliases' => 
    array (
      'Rollbar' => 'Rollbar\\Laravel\\Facades\\Rollbar',
    ),
  ),
  'fanly/log2dingding' => 
  array (
    'providers' => 
    array (
      0 => 'Fanly\\Log2dingding\\FanlyLog2dingdingServiceProvider',
    ),
  ),
);

项目根路径的 composer.json文件

"require": {
    "php": ">=7.0.0",
    "encore/laravel-admin": "1.5.*",
    "fanly/log2dingding": "^0.0.2",
    "fideloper/proxy": "~3.3",
    "guzzlehttp/guzzle": "^6.3",
    "laravel/framework": "5.5.*",
    "laravel/tinker": "~1.0",
    "noh4ck/graphiql": "@dev",
    "overtrue/phplint": "^1.1",
    "rebing/graphql-laravel": "^1.10",
    "rollbar/rollbar-laravel": "^2.3",
    "tymon/jwt-auth": "^1.0@dev"
},

new PackageManifest()

回过头来分析 new PackageManifest()

/**
 * 这个函数是将 package.php 文件的插件数组的 `providers`整合成一个 Collection 输出,什么是 Collection ,看之前的文章 Laravel Collections
 */
public function providers() {}

/**
 * 插件中的 `aliases` 整合成 Collection 输出。
 *
 * @return array
 */
public function aliases() {}

/**
 * 这个是关键,从 verdor/composer/installed.json 文件中获取所有通过 composer 安装的插件数组,然后再通过用 `name` 绑定对应的 `ServiceProvider`,构成数组,然后再排除每个插件的 `dont-discover` 和项目 composer.json 填入的 `dont-discover`。

 * 这也是 Laravel 包自动发现的核心所在。
 * 
 */
public function build()
{
    $packages = [];

    if ($this->files->exists($path = $this->vendorPath.'/composer/installed.json')) {
        $packages = json_decode($this->files->get($path), true);
    }

    $ignoreAll = in_array('*', $ignore = $this->packagesToIgnore());

    $this->write(collect($packages)->mapWithKeys(function ($package) {
        return [$this->format($package['name']) => $package['extra']['laravel'] ?? []];
    })->each(function ($configuration) use (&$ignore) {
        $ignore = array_merge($ignore, $configuration['dont-discover'] ?? []);
    })->reject(function ($configuration, $package) use ($ignore, $ignoreAll) {
        return $ignoreAll || in_array($package, $ignore);
    })->filter()->all());
}

/**
 * 最后就把上面的满足的 ServiceProvider 写入到文件中,就是上文我们说的 `bootstrap/cache/packages.php`
 */
protected function write(array $manifest) {}

registerBaseServiceProviders

这里主要注册三个 ServiceProvider,在注册过程中,ServiceProvider 的 register 方法会被执行,每一个 ServiceProvider 一般都会有 register 方法。

/**
 * Register all of the base service providers.
 *
 * @return void
 */
protected function registerBaseServiceProviders()
{
    //EventServieProvider —— 事件服务提供者
    //EventServiceProvider这个服务提供者,其实是向容器注册了一个key为events的对象
    $this->register(new EventServiceProvider($this));
    //日志服务
    $this->register(new LogServiceProvider($this));
    //RoutingServiceProvider —— 路由服务提供者
    //路由服务,这个里面注册了很多东西,从下图可以看出来,路由以后单独分析
    $this->register(new RoutingServiceProvider($this));
    //最后他们的实例都在 $this->bindings 里面, 如下图

}

registerCoreContainerAliases

这一步是在为一些核心类注册别名,这些数据就会一个有规律,数组都是接口和这个接口实例,这也就是laravel 里的contracts

在调用此方法之前,我们想取得一个容器实例的做法是 App::make('app'),现在我们可以使用
App::make('Illuminate\Foundation\Application')

App::make('Illuminate\Contracts\Container\Container')

App::make('Illuminate\Contracts\Foundation\Application')

三种方法来取得一个容器实例,即Illuminate\Foundation\ApplicationIlluminate\Contracts\Container\ContainerIlluminate\Contracts\Foundation\Application三者都是app的别名;

/**
 * Register the core class aliases in the container.
 * 在容器中注册核心类别名
 * @return void
 */
public function registerCoreContainerAliases()
{
    foreach ([
        'app'                  => [\Illuminate\Foundation\Application::class, \Illuminate\Contracts\Container\Container::class, \Illuminate\Contracts\Foundation\Application::class,  \Psr\Container\ContainerInterface::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],
    ] as $key => $aliases) {
        foreach ($aliases as $alias) {
            $this->alias($key, $alias);
        }
    }
}


Application 实例

最后返回了一个Application 实例

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!