背景

    看了一下laravel 的基础, 然后感觉这个框架也很有意思, 于是就多看了几眼, 没想到栽到了配置数据库上面, laravel配置文件的加载方式和我之前使用过的都不同, 很特别, 真的很特别。

    发生了什么

    当我玩完laravel的路由和控制器的时候, 就去看laravel的模型, 然后此时去配置了数据库, 但是,奇怪的事情发生了, 不管我怎么配置都不行, 后来发现了问题是出现在配置文件的函数上面。下面是配置文件,我们可以看到, 他是通过env()这个函数去判断的。

    1. return ['mysql' => [
    2. 'driver' => 'mysql',
    3. 'host' => env('DB_HOST', '127.0.0.1'),
    4. 'port' => env('DB_PORT', '3306'),
    5. 'database' => env('DB_DATABASE', 'bi'),
    6. 'username' => env('DB_USERNAME', 'root'),
    7. 'password' => env('DB_PASSWORD', '0000'),
    8. 'unix_socket' => env('DB_SOCKET', ''),
    9. 'charset' => 'utf8',
    10. 'collation' => 'utf8_unicode_ci',
    11. 'prefix' => '',
    12. 'strict' => true,
    13. 'engine' => null,
    14. ],];
    1. // 只摘取了函数的一部分
    2. function env($key, $default = null)
    3. {
    4. $value = getenv($key);
    5. if ($value === false) {
    6. return value($default);
    7. }
    8. }

    通过上面可以看到, env() 函数使用getenv() 这个函数去获取配置项的值, 问题就出现在这里了, 由于我是使用laravel内置的命令启动的, 所以环境变量并没有发生重载。用phpinfo()函数查看, 发现还是默认的homestead, 重启了服务之后, 一切正常了。


    顺便深究了下laravel是怎么加载这个环境变量的, 摘录的源码比较多。即使这样也不是很容易说清楚。

    1. /** 这个是Appliction.php, 入口文件会去加载他,并在laravel启动事件发生之后回调
    2. * Register a callback to run after loading the environment.
    3. *
    4. * @param \Closure $callback
    5. * @return void
    6. */
    7. public function afterLoadingEnvironment(Closure $callback)
    8. {
    9. return $this->afterBootstrapping(
    10. LoadEnvironmentVariables::class, $callback
    11. );
    12. }
    1. // 调用的类里面设置了, 用于回调的函数 bootstrap
    2. // 可以看到使用了Dotenv这个类去加载了.env 文件里面的内容, 并设置到了php的环境变量中
    3. class LoadEnvironmentVariables
    4. {
    5. /**
    6. * Bootstrap the given application.
    7. *
    8. * @param \Illuminate\Contracts\Foundation\Application $app
    9. * @return void
    10. */
    11. public function bootstrap(Application $app)
    12. {
    13. if ($app->configurationIsCached()) {
    14. return;
    15. }
    16. $this->checkForSpecificEnvironmentFile($app);
    17. try {
    18. (new Dotenv($app->environmentPath(), $app->environmentFile()))->load();
    19. } catch (InvalidPathException $e) {
    20. //
    21. }
    22. }
    1. //可以看到Dotenv在构造函数中完成了初始化工作, 并调用了loader的loadData()方法
    2. // loadData最终调用了loader的load()方法
    3. class Dotenv
    4. {
    5. /**
    6. * The file path.
    7. *
    8. * @var string
    9. */
    10. protected $filePath;
    11. /**
    12. * The loader instance.
    13. *
    14. * @var \Dotenv\Loader|null
    15. */
    16. protected $loader;
    17. /**
    18. * Create a new dotenv instance.
    19. *
    20. * @param string $path
    21. * @param string $file
    22. *
    23. * @return void
    24. */
    25. public function __construct($path, $file = '.env')
    26. {
    27. $this->filePath = $this->getFilePath($path, $file);
    28. $this->loader = new Loader($this->filePath, true);
    29. }
    30. /**
    31. * Load environment file in given directory.
    32. *
    33. * @return array
    34. */
    35. public function load()
    36. {
    37. return $this->loadData();
    38. }
    39. /**
    40. * Actually load the data.
    41. *
    42. * @param bool $overload
    43. *
    44. * @return array
    45. */
    46. protected function loadData($overload = false)
    47. {
    48. $this->loader = new Loader($this->filePath, !$overload);
    49. return $this->loader->load();
    50. }
    1. // loader->load()方法最终调用这个方法,完成了环境变量的设置工作
    2. public function setEnvironmentVariable($name, $value = null)
    3. {
    4. list($name, $value) = $this->normaliseEnvironmentVariable($name, $value);
    5. // Don't overwrite existing environment variables if we're immutable
    6. // Ruby's dotenv does this with `ENV[key] ||= value`.
    7. if ($this->immutable && $this->getEnvironmentVariable($name) !== null) {
    8. return;
    9. }
    10. // If PHP is running as an Apache module and an existing
    11. // Apache environment variable exists, overwrite it
    12. if (function_exists('apache_getenv') && function_exists('apache_setenv') && apache_getenv($name)) {
    13. apache_setenv($name, $value);
    14. }
    15. if (function_exists('putenv')) {
    16. putenv("$name=$value");
    17. }
    18. $_ENV[$name] = $value;
    19. $_SERVER[$name] = $value;
    20. }

    整个过程就是这样的, 如果是nginx和php-fpm的配置, 我估计也有可能会出现这种情况。

    1. 经测试, 当PHP以apache模块运行时, 不会出现上面的情况。