Skip to content

Commit 524c808

Browse files
Support being given a list of paths to try and load from
1 parent 56ffd7b commit 524c808

File tree

5 files changed

+113
-48
lines changed

5 files changed

+113
-48
lines changed

src/Dotenv.php

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,16 @@ public function __construct(Loader $loader)
3636
/**
3737
* Create a new dotenv instance.
3838
*
39-
* @param string $path
39+
* @param string|string[] $paths
4040
* @param string|null $file
4141
* @param \Dotenv\Environment\FactoryInterface|null $envFactory
4242
*
4343
* @return \Dotenv\Dotenv
4444
*/
45-
public static function create($path, $file = null, FactoryInterface $envFactory = null)
45+
public static function create($paths, $file = null, FactoryInterface $envFactory = null)
4646
{
4747
$loader = new Loader(
48-
self::getFilePath($path, $file),
48+
self::getFilePaths((array) $paths, $file ?: '.env'),
4949
$envFactory ?: new DotenvFactory(),
5050
true
5151
);
@@ -54,20 +54,18 @@ public static function create($path, $file = null, FactoryInterface $envFactory
5454
}
5555

5656
/**
57-
* Returns the full path to the file.
57+
* Returns the full paths to the files.
5858
*
59-
* @param string $path
60-
* @param string|null $file
59+
* @param string[] $paths
60+
* @param string $file
6161
*
6262
* @return string
6363
*/
64-
private static function getFilePath($path, $file)
64+
private static function getFilePaths(array $paths, $file)
6565
{
66-
if (!is_string($file)) {
67-
$file = '.env';
68-
}
69-
70-
return rtrim($path, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$file;
66+
return array_map(function ($path) use ($file) {
67+
return rtrim($path, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$file;
68+
}, $paths);
7169
}
7270

7371
/**

src/Loader.php

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44

55
use Dotenv\Environment\FactoryInterface;
66
use Dotenv\Exception\InvalidPathException;
7+
use PhpOption\Option;
78

89
/**
9-
* This is the loaded class.
10+
* This is the loader class.
1011
*
1112
* It's responsible for loading variables by reading a file from disk and:
1213
* - stripping comments beginning with a `#`,
@@ -17,11 +18,11 @@
1718
class Loader
1819
{
1920
/**
20-
* The file path.
21+
* The file paths.
2122
*
22-
* @var string
23+
* @var string[]
2324
*/
24-
protected $filePath;
25+
protected $filePaths;
2526

2627
/**
2728
* The environment factory instance.
@@ -47,15 +48,15 @@ class Loader
4748
/**
4849
* Create a new loader instance.
4950
*
50-
* @param string $filePath
51+
* @param string[] $filePaths
5152
* @param \Dotenv\Environment\FactoryInterface $envFactory
5253
* @param bool $immutable
5354
*
5455
* @return void
5556
*/
56-
public function __construct($filePath, FactoryInterface $envFactory, $immutable = false)
57+
public function __construct(array $filePaths, FactoryInterface $envFactory, $immutable = false)
5758
{
58-
$this->filePath = $filePath;
59+
$this->filePaths = $filePaths;
5960
$this->envFactory = $envFactory;
6061
$this->setImmutable($immutable);
6162
}
@@ -86,33 +87,52 @@ public function setImmutable($immutable = false)
8687
public function load()
8788
{
8889
return $this->processEntries(
89-
Lines::process(self::readLinesFromFile($this->filePath))
90+
Lines::process(self::readLines($this->filePaths))
9091
);
9192
}
9293

9394
/**
94-
* Read lines from the file, auto detecting line endings.
95+
* Attempt to read the lines from the files in order.
9596
*
96-
* @param string $filePath
97+
* @param string[] $filePaths
9798
*
9899
* @throws \Dotenv\Exception\InvalidPathException
99100
*
100101
* @return string[]
101102
*/
102-
private static function readLinesFromFile($filePath)
103+
private static function readLines(array $filePaths)
104+
{
105+
if ($filePaths === []) {
106+
throw new InvalidPathException('At least one environment file path must be provided.');
107+
}
108+
109+
foreach ($filePaths as $filePath) {
110+
$lines = self::readFromFile($filePath);
111+
if ($lines->isDefined()) {
112+
return $lines->get();
113+
}
114+
}
115+
116+
throw new InvalidPathException(
117+
sprintf('Unable to read any of the environment file(s) at [%s].', implode(', ', $filePaths))
118+
);
119+
}
120+
121+
/**
122+
* Read from the file, auto detecting line endings.
123+
*
124+
* @param string $filePath
125+
*
126+
* @return \PhpOption\Option
127+
*/
128+
private static function readFromFile($filePath)
103129
{
104130
$autodetect = ini_get('auto_detect_line_endings');
105131
ini_set('auto_detect_line_endings', '1');
106132
$lines = @file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
107133
ini_set('auto_detect_line_endings', $autodetect);
108134

109-
if ($lines === false) {
110-
throw new InvalidPathException(
111-
sprintf('Unable to read the environment file at %s.', $filePath)
112-
);
113-
}
114-
115-
return $lines;
135+
return Option::fromValue($lines, false);
116136
}
117137

118138
/**

tests/Dotenv/DotenvTest.php

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,34 @@ public function setUp()
1717

1818
/**
1919
* @expectedException \Dotenv\Exception\InvalidPathException
20-
* @expectedExceptionMessage Unable to read the environment file at
20+
* @expectedExceptionMessage Unable to read any of the environment file(s) at
2121
*/
2222
public function testDotenvThrowsExceptionIfUnableToLoadFile()
2323
{
2424
$dotenv = Dotenv::create(__DIR__);
2525
$dotenv->load();
2626
}
2727

28+
/**
29+
* @expectedException \Dotenv\Exception\InvalidPathException
30+
* @expectedExceptionMessage Unable to read any of the environment file(s) at
31+
*/
32+
public function testDotenvThrowsExceptionIfUnableToLoadFiles()
33+
{
34+
$dotenv = Dotenv::create([__DIR__, __DIR__.'/foo/bar']);
35+
$dotenv->load();
36+
}
37+
38+
public function testDotenvTriesPathsToLoad()
39+
{
40+
$dotenv = Dotenv::create([__DIR__, $this->fixturesFolder]);
41+
$this->assertCount(4, $dotenv->load());
42+
}
43+
2844
public function testDotenvSkipsLoadingIfFileIsMissing()
2945
{
3046
$dotenv = Dotenv::create(__DIR__);
31-
$this->assertEmpty($dotenv->safeLoad());
47+
$this->assertSame([], $dotenv->safeLoad());
3248
}
3349

3450
public function testDotenvLoadsEnvironmentVars()

tests/Dotenv/EnvironmentVariablesTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class EnvironmentVariablesTest extends TestCase
1414
protected function setUp()
1515
{
1616
$this->envFactory = new DotenvFactory();
17-
(new Loader(dirname(__DIR__).'/fixtures/env/.env', $this->envFactory))->load();
17+
(new Loader([dirname(__DIR__).'/fixtures/env/.env'], $this->envFactory))->load();
1818
}
1919

2020
public function testCheckingWhetherVariableExists()

tests/Dotenv/LoaderTest.php

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,21 @@
77
class LoaderTest extends TestCase
88
{
99
/**
10-
* @var \Dotenv\Loader
10+
* @var string
1111
*/
12-
private $loader;
12+
protected $folder;
13+
14+
/**
15+
* @var string|null
16+
*/
17+
protected $keyVal;
1318

1419
public function setUp()
1520
{
16-
$folder = dirname(__DIR__).'/fixtures/env';
21+
$this->folder = dirname(__DIR__).'/fixtures/env';
1722
$this->keyVal(true);
18-
$this->loader = new Loader($folder, new DotenvFactory(), false);
1923
}
2024

21-
protected $keyVal;
22-
2325
/**
2426
* Generates a new key/value pair or returns the previous one.
2527
*
@@ -66,31 +68,60 @@ protected function value()
6668

6769
public function testMutableLoaderClearsEnvironmentVars()
6870
{
71+
$loader = new Loader(["{$this->folder}/.env"], new DotenvFactory(), false);
72+
6973
// Set an environment variable.
70-
$this->loader->setEnvironmentVariable($this->key(), $this->value());
74+
$loader->setEnvironmentVariable($this->key(), $this->value());
7175

7276
// Clear the set environment variable.
73-
$this->loader->clearEnvironmentVariable($this->key());
74-
$this->assertSame(null, $this->loader->getEnvironmentVariable($this->key()));
77+
$loader->clearEnvironmentVariable($this->key());
78+
$this->assertSame(null, $loader->getEnvironmentVariable($this->key()));
7579
$this->assertSame(false, getenv($this->key()));
7680
$this->assertSame(false, isset($_ENV[$this->key()]));
7781
$this->assertSame(false, isset($_SERVER[$this->key()]));
78-
$this->assertSame([$this->key()], $this->loader->getEnvironmentVariableNames());
82+
$this->assertSame([$this->key()], $loader->getEnvironmentVariableNames());
7983
}
8084

8185
public function testImmutableLoaderCannotClearEnvironmentVars()
8286
{
83-
$this->loader->setImmutable(true);
87+
$loader = new Loader(["{$this->folder}/.env"], new DotenvFactory(), false);
88+
89+
$loader->setImmutable(true);
8490

8591
// Set an environment variable.
86-
$this->loader->setEnvironmentVariable($this->key(), $this->value());
92+
$loader->setEnvironmentVariable($this->key(), $this->value());
8793

8894
// Attempt to clear the environment variable, check that it fails.
89-
$this->loader->clearEnvironmentVariable($this->key());
90-
$this->assertSame($this->value(), $this->loader->getEnvironmentVariable($this->key()));
95+
$loader->clearEnvironmentVariable($this->key());
96+
$this->assertSame($this->value(), $loader->getEnvironmentVariable($this->key()));
9197
$this->assertSame($this->value(), getenv($this->key()));
9298
$this->assertSame(true, isset($_ENV[$this->key()]));
9399
$this->assertSame(true, isset($_SERVER[$this->key()]));
94-
$this->assertSame([$this->key()], $this->loader->getEnvironmentVariableNames());
100+
$this->assertSame([$this->key()], $loader->getEnvironmentVariableNames());
101+
}
102+
103+
/**
104+
* @expectedException \Dotenv\Exception\InvalidPathException
105+
* @expectedExceptionMessage At least one environment file path must be provided.
106+
*/
107+
public function testLoaderWithNoPaths()
108+
{
109+
(new Loader([], new DotenvFactory(), false))->load();
110+
}
111+
112+
/**
113+
* @expectedException \Dotenv\Exception\InvalidPathException
114+
* @expectedExceptionMessage Unable to read any of the environment file(s) at
115+
*/
116+
public function testLoaderWithBadPaths()
117+
{
118+
(new Loader(["{$this->folder}/BAD1", "{$this->folder}/BAD2"], new DotenvFactory(), false))->load();
119+
}
120+
121+
public function testLoaderWithOneGoodPath()
122+
{
123+
$loader = (new Loader(["{$this->folder}/BAD1", "{$this->folder}/.env"], new DotenvFactory(), false));
124+
125+
$this->assertCount(4, $loader->load());
95126
}
96127
}

0 commit comments

Comments
 (0)