提示
库版本 vlucas/phpdotenv : ^5.6
phpdotenv
是一个流行的 PHP 库,用于加载应用程序的环境变量。用来读取本地环境变量,以及在运行时动态设置环境变量。 源码地址如下,
Dotenv类
Dotenv
类是vlucas/phpdotenv
库的核心文件,实现了.env
文件的加载和解析逻辑,以及环境变量的设置功能。 查看该类源码,
php
declare(strict_types=1);
namespace Dotenv;
use Dotenv\Exception\InvalidPathException;
use Dotenv\Loader\Loader;
use Dotenv\Loader\LoaderInterface;
use Dotenv\Parser\Parser;
use Dotenv\Parser\ParserInterface;
use Dotenv\Repository\Adapter\ArrayAdapter;
use Dotenv\Repository\Adapter\PutenvAdapter;
use Dotenv\Repository\RepositoryBuilder;
use Dotenv\Repository\RepositoryInterface;
use Dotenv\Store\StoreBuilder;
use Dotenv\Store\StoreInterface;
use Dotenv\Store\StringStore;
class Dotenv
{
/**
* The store instance.
*
* @var \Dotenv\Store\StoreInterface
*/
private $store;
/**
* The parser instance.
*
* @var \Dotenv\Parser\ParserInterface
*/
private $parser;
/**
* The loader instance.
*
* @var \Dotenv\Loader\LoaderInterface
*/
private $loader;
/**
* The repository instance.
*
* @var \Dotenv\Repository\RepositoryInterface
*/
private $repository;
/**
* 构造函数
*/
public function __construct(
StoreInterface $store,
ParserInterface $parser,
LoaderInterface $loader,
RepositoryInterface $repository
) {
$this->store = $store;
$this->parser = $parser;
$this->loader = $loader;
$this->repository = $repository;
}
//...
}
该类实例化时需要传入四个参数,分别是StoreInterface
、ParserInterface
、LoaderInterface
、RepositoryInterface
接口的实现类。 官方示例代码如下,
php
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
createImmutable方法
打开Dotenv
类的createImmutable
方法,查看其具体实现逻辑,
php
/**
*
* @var string|string[] $paths
* @var null|string|string[] $names
* @var bool $shortCircuit
* @var null|string $fileEncoding 找到第一个存在的配置文件后停止加载其他配置文件
* @return Dotenv
*/
public static function createImmutable($paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null)
{
// createWithDefaultAdapters()返回的是一个RepositoryBuilder实例,然后调用make方法,
// 返回一个RepositoryInterface接口的实现类AdapterRepository
$repository = RepositoryBuilder::createWithDefaultAdapters()->immutable()->make();
// 调用create方法,返回一个Dotenv实例
return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding);
}
RepositoryBuilder类
查看静态方法createWithDefaultAdapters
,
php
public static function createWithDefaultAdapters()
{
// iterator_to_array 拷贝迭代器中的元素到数组中
// $adapters该数组变量就会保存DEFAULT_ADAPTERS常量中实例化的类
$adapters = \iterator_to_array(self::defaultAdapters());
// 返回一个RepositoryBuilder实例,
// 这里会触发RepositoryBuilder类的构造函数
return new self($adapters, $adapters);
}
private static function defaultAdapters()
{
foreach (self::DEFAULT_ADAPTERS as $adapter) {
$instance = $adapter::create();
if ($instance->isDefined()) {
// 这里会返回DEFAULT_ADAPTERS常量中实例化的类
// 这里会保存对应的alue,在下面进行迭代时取出
yield $instance->get();
}
}
}
// DEFAULT_ADAPTERS
private const DEFAULT_ADAPTERS = [
ServerConstAdapter::class,
EnvConstAdapter::class,
];
首先理解yield
关键字,它提供了一种简单的方法来遍历数据,具体可参考PHP yield关键字。
immutable方法
该方法只有一行,重新实例化RepositoryBuilder
类,并将第三个参数$$immutable
值改为true
,即创建一个不可变的RepositoryBuilder
实例。(键名不可重复,重复抛错)
php
public function immutable()
{
return new self($this->readers, $this->writers, true, $this->allowList);
}
make方法
php
// 看最后的make方法
$repository = RepositoryBuilder::createWithDefaultAdapters()->immutable()->make();
//
public function make()
{
// 创建读取器和写入器
$reader = new MultiReader($this->readers);
$writer = new MultiWriter($this->writers);
// 如果设置字段为true,则创建一个不可变的写入器,
// 当键相同时,则抛出异常
// 键不能重复
if ($this->immutable) {
$writer = new ImmutableWriter($writer, $reader);
}
// 限制写入的数据只能是允许列表中的键
if ($this->allowList !== null) {
$writer = new GuardedWriter($writer, $this->allowList);
}
// 创建AdapterRepository实例,用于读取和写入数据
return new AdapterRepository($reader, $writer);
}
现在,我们知道了$repository
是一个AdapterRepository
实例,接下来看create
方法。
create方法
查看create
方法的实现逻辑,
php
public static function create(RepositoryInterface $repository, $paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null)
{
$builder = $names === null ? StoreBuilder::createWithDefaultName() : StoreBuilder::createWithNoNames();
foreach ((array) $paths as $path) {
$builder = $builder->addPath($path);
}
foreach ((array) $names as $name) {
$builder = $builder->addName($name);
}
if ($shortCircuit) {
$builder = $builder->shortCircuit();
}
return new self($builder->fileEncoding($fileEncoding)->make(), new Parser(), new Loader(), $repository);
}