关于WAMP环境下的多线程环境问题
在windows下配置了一套WAMP环境来跑Laravel。发现存在共享“.env”加载的变量污染问题。
下面说下这种情况:
使用两个Laravel项目,一个域名为user.xxx.com;另一个域名为test.xxx.com。
user.xxx.com的.env中的DB_DATABASE设为proj_user数据库,而test.xxx.com的DB_DATABASE设为proj_test数据库。
在user.xxx.com中提供一个获取用户信息的接口,而test.xxx.com项目中用CURL去请求user.xxx.com中的接口。
然后发现user.xxx.com获取用户信息的时候居然使用了proj_test数据库!!!!!!!
之后经过调试发现,在DotEnv项目中(<project dir>/vendor/vlucas/phpdotenv/src/Loader.php),下面的代码是设置环境变量的:
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;
}
但是在if ($this->immutable && $this->getEnvironmentVariable($name) !== null)这句代码中,返回了true。也就是说在设置DB_DATABASE之前就已经存在这个配置了。继续调试后发现getEnvironmentVariable函数中的$value = getenv($name);这句代码返回了DB_DATABASE的值,并且是test.xxx.com的.env中的proj_test。
而我单独去访问user.xxx.com(也就是不通过test.xxx.com来去请求)的时候,却又读取了正确的数据库(proj_user)。
初步判断这个问题应该是test.xxx.com收到请求之后,读取.env并设置环境变量,而因为apache在windows下是以多线程模式运行的;在请求test.xxx.com的线程还没有退出的情况下.env加载的环境变量没有被释放,这时用CURL去请求user.xxx.com的时候就会在同一个进程空间里面读取到test.xxx.com的配置,上面那段设置变量的代码已经明显的说明如果读取到有这个配置,那么就直接return,不再重新加载,所以才会导致user.xxx.com自己的.env没有加载进来。
目前我用的解决方法是用php artisan config:cache命令来发布配置,这样就不会去读.env了。
不知道大家有没有什么更好的解决方案?毕竟在开发环境中每改一次config就要发布一次挺蛋疼的。
1 Comment
php artisan config:cache
是最不折腾的正解。还有一种方案是更换运行环境为
php-fpm
,php-fpm
的多进程运行模式,能有效防止ENV
的污染问题。