Skip to content

Commit a6bd169

Browse files
Merge pull request #394 from vlucas/shortcircuit-config
[4.1] Support multiple file names and disabling short circuit file loading
2 parents 55dc901 + a58c32f commit a6bd169

File tree

11 files changed

+225
-73
lines changed

11 files changed

+225
-73
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
},
3131
"extra": {
3232
"branch-alias": {
33-
"dev-master": "4.0-dev"
33+
"dev-master": "4.1-dev"
3434
}
3535
}
3636
}

src/Dotenv.php

Lines changed: 57 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
namespace Dotenv;
44

55
use Dotenv\Exception\InvalidPathException;
6+
use Dotenv\File\Paths;
7+
use Dotenv\File\Reader;
68
use Dotenv\Loader\Loader;
79
use Dotenv\Loader\LoaderInterface;
810
use Dotenv\Repository\RepositoryBuilder;
@@ -32,93 +34,110 @@ class Dotenv
3234
*/
3335
protected $filePaths;
3436

37+
/**
38+
* Should file loading short circuit?
39+
*
40+
* @var bool
41+
*/
42+
protected $shortCircuit;
43+
3544
/**
3645
* Create a new dotenv instance.
3746
*
3847
* @param \Dotenv\Loader\LoaderInterface $loader
3948
* @param \Dotenv\Repository\RepositoryInterface $repository
4049
* @param string[] $filePaths
50+
* @param bool $shortCircuit
4151
*
4252
* @return void
4353
*/
44-
public function __construct(LoaderInterface $loader, RepositoryInterface $repository, array $filePaths)
54+
public function __construct(LoaderInterface $loader, RepositoryInterface $repository, array $filePaths, $shortCircuit = true)
4555
{
4656
$this->loader = $loader;
4757
$this->repository = $repository;
4858
$this->filePaths = $filePaths;
59+
$this->shortCircuit = $shortCircuit;
4960
}
5061

5162
/**
5263
* Create a new dotenv instance.
5364
*
5465
* @param \Dotenv\Repository\RepositoryInterface $repository
5566
* @param string|string[] $paths
56-
* @param string|null $file
67+
* @param string|string[]|null $names
68+
* @param bool $shortCircuit
5769
*
5870
* @return \Dotenv\Dotenv
5971
*/
60-
public static function create(RepositoryInterface $repository, $paths, $file = null)
72+
public static function create(RepositoryInterface $repository, $paths, $names = null, $shortCircuit = true)
6173
{
62-
return new self(new Loader(), $repository, self::getFilePaths((array) $paths, $file ?: '.env'));
74+
$files = Paths::filePaths((array) $paths, (array) ($names ?: '.env'));
75+
76+
return new self(new Loader(), $repository, $files, $shortCircuit);
6377
}
6478

6579
/**
6680
* Create a new mutable dotenv instance with default repository.
6781
*
68-
* @param string|string[] $paths
69-
* @param string|null $file
82+
* @param string|string[] $paths
83+
* @param string|string[]|null $names
84+
* @param bool $shortCircuit
7085
*
7186
* @return \Dotenv\Dotenv
7287
*/
73-
public static function createMutable($paths, $file = null)
88+
public static function createMutable($paths, $names = null, $shortCircuit = true)
7489
{
7590
$repository = RepositoryBuilder::create()->make();
7691

77-
return self::create($repository, $paths, $file);
92+
return self::create($repository, $paths, $names, $shortCircuit);
7893
}
7994

8095
/**
8196
* Create a new immutable dotenv instance with default repository.
8297
*
83-
* @param string|string[] $paths
84-
* @param string|null $file
98+
* @param string|string[] $paths
99+
* @param string|string[]|null $names
100+
* @param bool $shortCircuit
85101
*
86102
* @return \Dotenv\Dotenv
87103
*/
88-
public static function createImmutable($paths, $file = null)
104+
public static function createImmutable($paths, $names = null, $shortCircuit = true)
89105
{
90106
$repository = RepositoryBuilder::create()->immutable()->make();
91107

92-
return self::create($repository, $paths, $file);
108+
return self::create($repository, $paths, $names, $shortCircuit);
93109
}
94110

95111
/**
96-
* Load environment file in given directory.
112+
* Read and load environment file(s).
97113
*
98114
* @throws \Dotenv\Exception\InvalidPathException|\Dotenv\Exception\InvalidFileException
99115
*
100-
* @return array<string|null>
116+
* @return array<string,string|null>
101117
*/
102118
public function load()
103119
{
104-
return $this->loader->load($this->repository, self::findAndRead($this->filePaths));
120+
if ($this->filePaths === []) {
121+
throw new InvalidPathException('At least one environment file path must be provided.');
122+
}
123+
124+
return $this->tryLoad()->getOrCall(function () {
125+
throw new InvalidPathException(
126+
sprintf('Unable to read any of the environment file(s) at [%s].', implode(', ', $this->filePaths))
127+
);
128+
});
105129
}
106130

107131
/**
108-
* Load environment file in given directory, silently failing if it doesn't exist.
132+
* Read and load environment file(s), silently failing if no files can be read.
109133
*
110134
* @throws \Dotenv\Exception\InvalidFileException
111135
*
112-
* @return array<string|null>
136+
* @return array<string,string|null>
113137
*/
114138
public function safeLoad()
115139
{
116-
try {
117-
return $this->load();
118-
} catch (InvalidPathException $e) {
119-
// suppressing exception
120-
return [];
121-
}
140+
return $this->tryLoad()->getOrElse([]);
122141
}
123142

124143
/**
@@ -146,58 +165,34 @@ public function ifPresent($variables)
146165
}
147166

148167
/**
149-
* Returns the full paths to the files.
168+
* Read and load environment file(s), returning an option.
150169
*
151-
* @param string[] $paths
152-
* @param string $file
170+
* @throws \Dotenv\Exception\InvalidFileException
153171
*
154-
* @return string[]
172+
* @return \PhpOption\Option
155173
*/
156-
private static function getFilePaths(array $paths, $file)
174+
private function tryLoad()
157175
{
158-
return array_map(function ($path) use ($file) {
159-
return rtrim($path, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$file;
160-
}, $paths);
176+
return self::aggregate(Reader::read($this->filePaths, $this->shortCircuit))->map(function ($content) {
177+
return $this->loader->load($this->repository, $content);
178+
});
161179
}
162180

163181
/**
164-
* Attempt to read the files in order.
165-
*
166-
* @param string[] $filePaths
182+
* Aggregate the given raw file contents.
167183
*
168-
* @throws \Dotenv\Exception\InvalidPathException
184+
* @param array<string,string> $contents
169185
*
170-
* @return string[]
186+
* @return \PhpOption\Option
171187
*/
172-
private static function findAndRead(array $filePaths)
188+
private static function aggregate(array $contents)
173189
{
174-
if ($filePaths === []) {
175-
throw new InvalidPathException('At least one environment file path must be provided.');
176-
}
190+
$output = '';
177191

178-
foreach ($filePaths as $filePath) {
179-
$lines = self::readFromFile($filePath);
180-
if ($lines->isDefined()) {
181-
return $lines->get();
182-
}
192+
foreach ($contents as $content) {
193+
$output .= $content."\n";
183194
}
184195

185-
throw new InvalidPathException(
186-
sprintf('Unable to read any of the environment file(s) at [%s].', implode(', ', $filePaths))
187-
);
188-
}
189-
190-
/**
191-
* Read the given file.
192-
*
193-
* @param string $filePath
194-
*
195-
* @return \PhpOption\Option
196-
*/
197-
private static function readFromFile($filePath)
198-
{
199-
$content = @file_get_contents($filePath);
200-
201-
return Option::fromValue($content, false);
196+
return Option::fromValue($output, '');
202197
}
203198
}

src/File/Paths.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Dotenv\File;
4+
5+
use PhpOption\Option;
6+
7+
class Paths
8+
{
9+
/**
10+
* Returns the full paths to the files.
11+
*
12+
* @param string[] $paths
13+
* @param string[] $names
14+
*
15+
* @return string[]
16+
*/
17+
public static function filePaths(array $paths, array $names)
18+
{
19+
$files = [];
20+
21+
foreach ($paths as $path) {
22+
foreach ($names as $name) {
23+
$files[] = rtrim($path, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$name;
24+
}
25+
}
26+
27+
return $files;
28+
}
29+
}

src/File/Reader.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
namespace Dotenv\File;
4+
5+
use PhpOption\Option;
6+
7+
class Reader
8+
{
9+
/**
10+
* Read the file(s), and return their raw content.
11+
*
12+
* We provide the file path as the key, and its content as the value. If
13+
* short circuit mode is enabled, then the returned array with have length
14+
* at most one. File paths that couldn't be read are omitted entirely.
15+
*
16+
* @param string[] $filePaths
17+
* @param bool $shortCircuit
18+
*
19+
* @return array<string,string>
20+
*/
21+
public static function read(array $filePaths, $shortCircuit = true)
22+
{
23+
$output = [];
24+
25+
foreach ($filePaths as $filePath) {
26+
$content = self::readFromFile($filePath);
27+
if ($content->isDefined()) {
28+
$output[$filePath] = $content->get();
29+
if ($shortCircuit) {
30+
break;
31+
}
32+
}
33+
}
34+
35+
return $output;
36+
}
37+
38+
/**
39+
* Read the given file.
40+
*
41+
* @param string $filePath
42+
*
43+
* @return \PhpOption\Option
44+
*/
45+
private static function readFromFile($filePath)
46+
{
47+
$content = @file_get_contents($filePath);
48+
49+
return Option::fromValue($content, false);
50+
}
51+
}

src/Loader/Loader.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class Loader implements LoaderInterface
1616
*
1717
* @throws \Dotenv\Exception\InvalidFileException
1818
*
19-
* @return array<string|null>
19+
* @return array<string,string|null>
2020
*/
2121
public function load(RepositoryInterface $repository, $content)
2222
{
@@ -37,7 +37,7 @@ public function load(RepositoryInterface $repository, $content)
3737
*
3838
* @throws \Dotenv\Exception\InvalidFileException
3939
*
40-
* @return array<string|null>
40+
* @return array<string.string|null>
4141
*/
4242
private static function processEntries(RepositoryInterface $repository, array $entries)
4343
{

src/Loader/LoaderInterface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ interface LoaderInterface
1414
*
1515
* @throws \Dotenv\Exception\InvalidFileException
1616
*
17-
* @return array<string|null>
17+
* @return array<string,string|null>
1818
*/
1919
public function load(RepositoryInterface $repository, $content);
2020
}

src/Repository/Adapter/ArrayAdapter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class ArrayAdapter implements AvailabilityInterface, ReaderInterface, WriterInte
1010
/**
1111
* The variables and their values.
1212
*
13-
* @return array<string|null>
13+
* @return array<string,string|null>
1414
*/
1515
private $variables = [];
1616

tests/Dotenv/DotenvTest.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ public function testDotenvTriesPathsToLoad()
5151
$this->assertCount(4, $dotenv->load());
5252
}
5353

54+
public function testDotenvTriesPathsToSafeLoad()
55+
{
56+
$dotenv = Dotenv::createImmutable([__DIR__, $this->folder]);
57+
$this->assertCount(4, $dotenv->safeLoad());
58+
}
59+
5460
public function testDotenvSkipsLoadingIfFileIsMissing()
5561
{
5662
$dotenv = Dotenv::createImmutable(__DIR__);
@@ -70,6 +76,26 @@ public function testDotenvLoadsEnvironmentVars()
7076
$this->assertEmpty(getenv('NULL'));
7177
}
7278

79+
public function testDotenvLoadsEnvironmentVarsMultipleNotShortCircuitMode()
80+
{
81+
$dotenv = Dotenv::createImmutable($this->folder, ['.env', 'example.env']);
82+
83+
$this->assertSame(
84+
['FOO' => 'bar', 'BAR' => 'baz', 'SPACED' => 'with spaces', 'NULL' => ''],
85+
$dotenv->load()
86+
);
87+
}
88+
89+
public function testDotenvLoadsEnvironmentVarsMultipleWithShortCircuitMode()
90+
{
91+
$dotenv = Dotenv::createImmutable($this->folder, ['.env', 'example.env'], false);
92+
93+
$this->assertSame(
94+
['FOO' => 'bar', 'BAR' => 'baz', 'SPACED' => 'with spaces', 'NULL' => '', 'EG' => 'example'],
95+
$dotenv->load()
96+
);
97+
}
98+
7399
public function testCommentedDotenvLoadsEnvironmentVars()
74100
{
75101
$dotenv = Dotenv::createImmutable($this->folder, 'commented.env');

0 commit comments

Comments
 (0)