侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

在laravel框架中对环境配置文件的加载过程步骤浅析

Laravel  /  管理员 发布于 2年前   695

在laravel中是支持配置加载不同环境配置文件的,下面来看看laravel框架在不同的环境自动加载不同的环境配置过程。


一.环境配置的加载过程:

运行 laravel 框架主要有两种方式, 

一种是作为 web服务的运行的 laravel,

另一种是作为命令行脚本运行的 laravel。


web 服务的入口文件是 public/index.php , 命令行脚本的入口文件是 artisan。

可以说框架的加载也是从 public/index.php 或 artisan 开始的。

我们拿 public/index.php 举例, 可以看到先引用了 laravel 框架, 

然后通过服务提供者 make 出来一个 Kernel , 

然后通过调用 Kernel 的 handle 方法 和 send 方法创建了 $response 和 $request 实例。

# public/index.php 和 artisan 中的这一行代码引入了 laravel 框架。
$app = require_once __DIR__.'/bootstrap/app.php';
$kernel = $app->make(Kernel::class);
$response = $kernel->handle(
    $request = Request::capture()
)->send();

在 /bootstrap/app.php 中通过下面这行代码创建的 laravel 实例。

$app = new Illuminate\Foundation\Application(
    $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);

在 Illuminate\Foundation\Application 中有一个 bootstrapWith 方法, 

这个方法先通过 $this->make() 方法创建了 bootstrapper 的实例, 

然后执行 bootstrapper 的 bootstrap 方法。

 public function bootstrapWith(array $bootstrappers)
    {
        $this->hasBeenBootstrapped = true;
        foreach ($bootstrappers as $bootstrapper) {
            $this['events']->dispatch('bootstrapping: '.$bootstrapper, [$this]);
            $this->make($bootstrapper)->bootstrap($this);
            $this['events']->dispatch('bootstrapped: '.$bootstrapper, [$this]);
        }
    }

内核 Kernel 中 bootstrap 方法调用了 Application 中的 bootstrapWith 方法,

这里还是拿 Illuminate\Foundation\Http\Kernel 举例子, 

可以看到 $bootstrappers 中的第一个 bootstrapper 就是 

\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables, 

这个 bootstrapper 就是用来加载环境配置的 bootstrapper。

protected $bootstrappers = [
        \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
        \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
        \Illuminate\Foundation\Bootstrap\HandleExceptions::class,
        \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
        \Illuminate\Foundation\Bootstrap\RegisterProviders::class,
        \Illuminate\Foundation\Bootstrap\BootProviders::class,
    ];
 ...
public function bootstrap()

{

    if (! $this->app->hasBeenBootstrapped()) {

        $this->app->bootstrapWith($this->bootstrappers());

   }
}  

 

我们接着来看下 

\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables 的 bootstrap 方法,

$app->configurationIsCached() 为 true 表示有配置的缓存文件,如果有的话就不会重复加载。

然后 $this->checkForSpecificEnvironmentFile($app); 检查特定环境配置文件。 

这个方法检查会修改 $app 中环境配置文件的路径, 所以是今天的重点。

public function bootstrap(Application $app)
{
        if ($app->configurationIsCached()) {
            return;
        }
        $this->checkForSpecificEnvironmentFile($app);
        try {
            $this->createDotenv($app)->safeLoad();
        } catch (InvalidFileException $e) {
            $this->writeErrorAndDie($e);
        }
}

首先判断是不是命令行环境,如果是的话, 会获取 --env 的值,

并将这个值和 $app->environmentFile() 和 . 做字符串拼接, 然后调用 setEnvironmentFilePath 修改环境配置文件的文件名, 比如说 --env=local 那最后加载的环境配置文件就是 .env.local。 

这里如果不是命令行环境的话, 后面会通过 Env::get('APP_ENV'); 

获取当前环境, 然后同样做一个字符串拼接, 然后调用 setEnvironmentFilePath 修改。

protected function checkForSpecificEnvironmentFile($app)
{
        if ($app->runningInConsole() &&
            ($input = new ArgvInput)->hasParameterOption('--env') &&
            $this->setEnvironmentFilePath($app, $app->environmentFile().'.'.$input->getParameterOption('--env'))) {
            return;
        }
        $environment = Env::get('APP_ENV');
        if (! $environment) {
            return;
        }
        $this->setEnvironmentFilePath(
            $app, $app->environmentFile().'.'.$environment
        );
}
protected function setEnvironmentFilePath($app, $file)
{
        if (is_file($app->environmentPath().'/'.$file)) {
            $app->loadEnvironmentFrom($file);
            return true;
        }
        return false;
}

如果在 checkForSpecificEnvironmentFile 方法中没有修改环境配置文件的文件名, 则会加载默认的环境配置文件 。

 这个默认的文件名是通过 \Illuminate\Foundation\Application 的 $environmentFile 属性定义的, 

前面的 setEnvironmentFilePath 修改的也是 environmentFile 的值。

protected $environmentFile = '.env';
public function loadEnvironmentFrom($file)
{
    $this->environmentFile = $file;
    return $this;
}

这里再说明一下, Illuminate\Foundation\Http\Kernel的bootstrap方法是在sendRequestThroughRouter方法中调用的, 而 sendRequestThroughRouter 是在 handle 方法中调用的。

public function handle($request)
    {
        $this->requestStartedAt = Carbon::now();
        try {
            $request->enableHttpMethodParameterOverride();
            $response = $this->sendRequestThroughRouter($request);
        } catch (Throwable $e) {
            $this->reportException($e);
            $response = $this->renderException($request, $e);
        }
        $this->app['events']->dispatch(
            new RequestHandled($request, $response)
        );
        return $response;
    }
    /**
     * Send the given request through the middleware / router.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    protected function sendRequestThroughRouter($request)
    {
        $this->app->instance('request', $request);
        Facade::clearResolvedInstance('request');
        $this->bootstrap();
        return (new Pipeline($this->app))
                    ->send($request)
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                    ->then($this->dispatchToRouter());
    }

我们来整理一下。 

整个环境配置加载过程就是 先有了 $app, 

然后 $kernel = $app->make(Kernel::class) 

再然后 $kernel->handle() 在 handle 方法中调用了 $kernel->bootstrap() 

最后在 bootstrap 中通过 LoadEnvironmentVariables 加载了环境配置.


二.如何使 laravel 在不同的环境自动加载不同的环境配置。


现在 env 的加载过程基本上已经了解了, 

我们接着再详细说明下 checkForSpecificEnvironmentFile 中 Env::get('APP_ENV') 的具体逻辑,

以及如何使 laravel 在不同的环境下自动加载不同环境配置的具体操作。

get 方法没什么好说就是 static::getRepository() 然后尝试获取配置项的值, 

关键是它有内些仓库。

public static function get($key, $default = null)
{
    return Option::fromValue(static::getRepository()->get($key))
        ->map(function ($value) {
            switch (strtolower($value)) {
                case 'true':
                case '(true)':
                    return true;
                case 'false':
                case '(false)':
                    return false;
                case 'empty':
                case '(empty)':
                    return '';
                case 'null':
                case '(null)':
                    return;
  }
            if (preg_match('/\A([\'"])(.*)\1\z/', $value, $matches)) {
                return $matches[2];
  }
            return $value;
  })
        ->getOrCall(fn () => value($default));
}

其实就是 EnvConstAdapter ServerConstAdapter, PutenvAdapter 它们三个。

    public static function getRepository()
    {
        if (static::$repository === null) {
            $builder = RepositoryBuilder::createWithDefaultAdapters();
            if (static::$putenv) {
                $builder = $builder->addAdapter(PutenvAdapter::class);
            }
            static::$repository = $builder->immutable()->make();
        }
        return static::$repository;
    }
final class RepositoryBuilder
{
    /**
     * The set of default adapters.
     */
    private const DEFAULT_ADAPTERS = [
        ServerConstAdapter::class,
        EnvConstAdapter::class,
    ];

Dotenv\Repository\Adapter\ServerConstAdapter 的 read 方法其实就是从 $_SERVER 中读。

    public function read(string $name)
    {
        /** @var \PhpOption\Option<string> */
        return Option::fromArraysValue($_SERVER, $name)
            ->map(static function ($value) {
                if ($value === false) {
                    return 'false';
                }
                if ($value === true) {
                    return 'true';
                }
                return $value;
            })->filter(static function ($value) {
                return \is_string($value);
            });
    }

Dotenv\Repository\Adapter\EnvConstAdapter 的 read 方法是从 $_ENV 中读。

    public function read(string $name)
    {
        /** @var \PhpOption\Option<string> */
        return Option::fromArraysValue($_ENV, $name)
            ->map(static function ($value) {
                if ($value === false) {
                    return 'false';
                }
                if ($value === true) {
                    return 'true';
                }
                return $value;
            })->filter(static function ($value) {
                return \is_string($value);
            });
    }

Dotenv\Repository\Adapter\PutenvAdapter 是从 \getenv() 中读,

\getenv 返回的其实就是 $_SERVER 和 $_ENV 中的值。

public function read(string $name)
    {
        /** @var \PhpOption\Option<string> */
        return Option::fromValue(\getenv($name), false)->filter(static function ($value) {
            return \is_string($value);
        });
    }


总结

使 Laravel 在不同的环境自动加载不同的环境配置其实很简单, 在命令行环境时, 

我们只要在后面加选项 --env=* 就可以使 laravel 加载不同的环境配置文件。

比如

php artisan horizon --env=local

在 web 环境下我们只要修改 $_ENV 或者 $_SERVER 中 APP_ENV 的值就可以使 laravel 加载不同的环境配置文件。


  • 上一条:
    Laravel 10.12版本发布
    下一条:
    在laravel中介绍一个生成假数据的PHP库:FakerPHP
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • Laravel 11.15版本发布 - Eloquent Builder中添加的泛型(0个评论)
    • Laravel 11.14版本发布 - 新的字符串助手和ServeCommand改进(0个评论)
    • Laravel 11.12版本发布 - Artisan的`make`命令自动剪切`.php `扩展(0个评论)
    • Laravel的轻量型购物车扩展包:binafy/laravel-cart(0个评论)
    • Laravel 11.11版本发布 - 查看模型中的第三方关系:show(0个评论)
    • 近期文章
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2017-07
    • 2017-08
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2020-07
    • 2020-08
    • 2020-09
    • 2020-10
    • 2020-11
    • 2021-01
    • 2021-02
    • 2021-03
    • 2021-04
    • 2021-05
    • 2021-06
    • 2021-07
    • 2021-08
    • 2021-09
    • 2021-10
    • 2021-11
    • 2021-12
    • 2022-01
    • 2022-02
    • 2022-03
    • 2022-04
    • 2022-05
    • 2022-06
    • 2022-07
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-08
    • 2023-09
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-02
    • 2024-03
    • 2024-04
    • 2024-05
    • 2024-06
    • 2024-07
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客