背景:
看了一下laravel 的基础, 然后感觉这个框架也很有意思, 于是就多看了几眼, 没想到栽到了配置数据库上面, laravel配置文件的加载方式和我之前使用过的都不同, 很特别, 真的很特别。
发生了什么:
当我玩完laravel的路由和控制器的时候, 就去看laravel的模型, 然后此时去配置了数据库, 但是,奇怪的事情发生了, 不管我怎么配置都不行, 后来发现了问题是出现在配置文件的函数上面。下面是配置文件,我们可以看到, 他是通过env()这个函数去判断的。
return ['mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'bi'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', '0000'),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => true,
'engine' => null,
],];
// 只摘取了函数的一部分
function env($key, $default = null)
{
$value = getenv($key);
if ($value === false) {
return value($default);
}
}
通过上面可以看到, env() 函数使用getenv() 这个函数去获取配置项的值, 问题就出现在这里了, 由于我是使用laravel内置的命令启动的, 所以环境变量并没有发生重载。用phpinfo()函数查看, 发现还是默认的homestead, 重启了服务之后, 一切正常了。
顺便深究了下laravel是怎么加载这个环境变量的, 摘录的源码比较多。即使这样也不是很容易说清楚。
/** 这个是Appliction.php, 入口文件会去加载他,并在laravel启动事件发生之后回调
* Register a callback to run after loading the environment.
*
* @param \Closure $callback
* @return void
*/
public function afterLoadingEnvironment(Closure $callback)
{
return $this->afterBootstrapping(
LoadEnvironmentVariables::class, $callback
);
}
// 调用的类里面设置了, 用于回调的函数 bootstrap
// 可以看到使用了Dotenv这个类去加载了.env 文件里面的内容, 并设置到了php的环境变量中
class LoadEnvironmentVariables
{
/**
* Bootstrap the given application.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function bootstrap(Application $app)
{
if ($app->configurationIsCached()) {
return;
}
$this->checkForSpecificEnvironmentFile($app);
try {
(new Dotenv($app->environmentPath(), $app->environmentFile()))->load();
} catch (InvalidPathException $e) {
//
}
}
//可以看到Dotenv在构造函数中完成了初始化工作, 并调用了loader的loadData()方法
// loadData最终调用了loader的load()方法
class Dotenv
{
/**
* The file path.
*
* @var string
*/
protected $filePath;
/**
* The loader instance.
*
* @var \Dotenv\Loader|null
*/
protected $loader;
/**
* Create a new dotenv instance.
*
* @param string $path
* @param string $file
*
* @return void
*/
public function __construct($path, $file = '.env')
{
$this->filePath = $this->getFilePath($path, $file);
$this->loader = new Loader($this->filePath, true);
}
/**
* Load environment file in given directory.
*
* @return array
*/
public function load()
{
return $this->loadData();
}
/**
* Actually load the data.
*
* @param bool $overload
*
* @return array
*/
protected function loadData($overload = false)
{
$this->loader = new Loader($this->filePath, !$overload);
return $this->loader->load();
}
// loader->load()方法最终调用这个方法,完成了环境变量的设置工作
public function setEnvironmentVariable($name, $value = null)
{
list($name, $value) = $this->normaliseEnvironmentVariable($name, $value);
// Don't overwrite existing environment variables if we're immutable
// Ruby's dotenv does this with `ENV[key] ||= value`.
if ($this->immutable && $this->getEnvironmentVariable($name) !== null) {
return;
}
// If PHP is running as an Apache module and an existing
// Apache environment variable exists, overwrite it
if (function_exists('apache_getenv') && function_exists('apache_setenv') && apache_getenv($name)) {
apache_setenv($name, $value);
}
if (function_exists('putenv')) {
putenv("$name=$value");
}
$_ENV[$name] = $value;
$_SERVER[$name] = $value;
}
整个过程就是这样的, 如果是nginx和php-fpm的配置, 我估计也有可能会出现这种情况。
- 经测试, 当PHP以apache模块运行时, 不会出现上面的情况。