发布于  更新于 

env文件读取

库版本

vlucas/phpdotenv : ^5.6

phpdotenv是一个流行的 PHP 库,用于加载应用程序的环境变量。用来读取本地环境变量,以及在运行时动态设置环境变量。
源码地址如下,

Dotenv类

Dotenv类是vlucas/phpdotenv库的核心文件,实现了.env 文件的加载和解析逻辑,以及环境变量的设置功能。
查看该类源码,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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;
}
//...
}

该类实例化时需要传入四个参数,分别是StoreInterfaceParserInterfaceLoaderInterfaceRepositoryInterface接口的实现类。
官方示例代码如下,

1
2
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();

createImmutable方法

打开Dotenv类的createImmutable方法,查看其具体实现逻辑,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
*
* @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,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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实例。(键名不可重复,重复抛错)

1
2
3
4
public function immutable()
{
return new self($this->readers, $this->writers, true, $this->allowList);
}
make方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 看最后的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方法的实现逻辑,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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);
}