phpdotenvのコードリーディングをしました。バージョンは3.3.3です。
このケースで追ってみます
<?php
require 'vendor/autoload.php'
$dotenv = Dotenv\Dotenv::create(__DIR__);
$dotenv->load();
echo getenv('FOO');
echo $_ENV('BAR');
echo $_SERVER('BAZ');
コードリーディング
Dotenv\Dotenv::createは以下のようにLoaderのインスタンスを生成し、それを引数にDotenv\Dotenvオブジェクトを生成して返します。
class Dotenv
{
public static function create($paths, $file = null, FactoryInterface $envFactory = null)
{
$loader = new Loader(
self::getFilePaths((array) $paths, $file ?: '.env'),
$envFactory ?: new DotenvFactory(),
true
);
return new self($loader);
}
Dotenv\Dotenv#load
はDotenv\Loader#setImmutatable
したLoaderインスタンスの#load
をコールします。
class Dotenv
{
public function load()
{
return $this->loadData();
}
protected function loadData($overload = false)
{
return $this->loader->setImmutable(!$overload)->load();
}
LoaderのsetImmutableは以下のような実装になっていて、envVariablesにDotenv\DotenvFactory#createImmutable
で生成したDotenv\DotenvVariables
オブジェクトをセットします。
class Loader
{
public function setImmutable($immutable = false)
{
$this->envVariables = $immutable
? $this->envFactory->createImmutable()
: $this->envFactory->create();
return $this;
}
Loader#load
はパス上のenvファイルをfile_get_contentsで読み込み、Lines::process
で環境変数を書き込んだ各行をstringのArrayにして返します。
class Loader
{
public function load()
{
return $this->loadDirect(
self::findAndRead($this->filePaths)
);
}
public function loadDirect($content)
{
return $this->processEntries(
Lines::process(preg_split("/(\r\n|\n|\r)/", $content))
);
}
private static function findAndRead(array $filePaths)
{
if ($filePaths === []) {
throw new InvalidPathException('At least one environment file path must be provided.');
}
foreach ($filePaths as $filePath) {
$lines = self::readFromFile($filePath);
if ($lines->isDefined()) {
return $lines->get();
}
}
throw new InvalidPathException(
sprintf('Unable to read any of the environment file(s) at [%s].', implode(', ', $filePaths))
);
}
processEntriesでは各行(エントリ)をDotenv\Parser::parse
でパースしてキーバリューに分解してLoader#setEnvironmentVariable
で環境変数にセットします。
class Loader
{
private function processEntries(array $entries)
{
$vars = [];
foreach ($entries as $entry) {
list($name, $value) = Parser::parse($entry);
$vars[$name] = $this->resolveNestedVariables($value);
$this->setEnvironmentVariable($name, $vars[$name]);
}
return $vars;
}
setEnvironmentVariable
はDotenv\DotenvVariables#set
を呼び出します。
Dotenv\DotenvVariables#set
は以下のように実装されています。
class DotenvVariables
{
public function set($name, $value = null)
{
if (!is_string($name)) {
throw new InvalidArgumentException('Expected name to be a string.');
}
// Don't overwrite existing environment variables if we're immutable
// Ruby's dotenv does this with `ENV[key] ||= value`.
if ($this->isImmutable() && $this->get($name) !== null) {
return;
}
foreach ($this->adapters as $adapter) {
$adapter->set($name, $value);
}
}
$adapter->set($name, $value);
のところで実際に環境変数にセットしています。
adapterは以下の4種類が実装されています。
- ApacheAdapter
- ArrayAdapter
- EnvConstAdapter
- PutenvAdapter
- ServerConstAdapter
EnvConstやServerConstはそれぞれ$_ENV, $_SERVERに値をセットするアダプターです。
ApacheAdapterはapache\_{get,set}env
関数をPutenvAdapterは{get,put}env
関数を呼び出して環境変数をセットします。
ArrayAdapterはAdapter内の変数格納用のarrayに環境変数のデータを入れていてデータを出し入れすることになります。
DotenvFactoryではデフォルトで{Apache,EnvConst,Putenv,Server}Adapter
が利用されます
class DotenvFactory
{
public function __construct(array $adapters = null)
{
$this->adapters = array_filter($adapters === null ? [new ApacheAdapter(), new EnvConstAdapter(), new ServerConstAdapter(), new PutenvAdapter()] : $adapters, function (AdapterInterface $adapter) {
return $adapter->isSupported();
});
}