Skip to content

Commit b764f4f

Browse files
Handle aggregation and checks within load(), and expose read()
1 parent 43586fe commit b764f4f

File tree

2 files changed

+87
-42
lines changed

2 files changed

+87
-42
lines changed

src/Dotenv.php

Lines changed: 62 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -107,32 +107,71 @@ public static function createImmutable($paths, $names = null, $shortCircuit = tr
107107
}
108108

109109
/**
110-
* Load environment file in given directory.
110+
* Read the environment file(s), and return their raw content only.
111+
*
112+
* We provide the file path as the key, and its content as the value. If
113+
* short circuit mode is enabled, then the returned array with have length
114+
* at most one. File paths that couldn't be read are omitted entirely.
115+
*
116+
* @return array<string,string>
117+
*/
118+
public function read()
119+
{
120+
$output = [];
121+
122+
foreach ($this->filePaths as $filePath) {
123+
$content = self::readFromFile($filePath);
124+
if ($content->isDefined()) {
125+
$output[$filePath] = $content->get();
126+
if ($this->shortCircuit) {
127+
break;
128+
}
129+
}
130+
}
131+
132+
return $output;
133+
}
134+
135+
/**
136+
* Read and load environment file(s).
111137
*
112138
* @throws \Dotenv\Exception\InvalidPathException|\Dotenv\Exception\InvalidFileException
113139
*
114140
* @return array<string,string|null>
115141
*/
116142
public function load()
117143
{
118-
return $this->loader->load($this->repository, self::findAndRead($this->filePaths, $this->shortCircuit));
144+
if ($this->filePaths === []) {
145+
throw new InvalidPathException('At least one environment file path must be provided.');
146+
}
147+
148+
$content = self::aggregate($this->read());
149+
150+
if ($content->isDefined()) {
151+
return $this->loader->load($this->repository, $content->get());
152+
}
153+
154+
throw new InvalidPathException(
155+
sprintf('Unable to read any of the environment file(s) at [%s].', implode(', ', $this->filePaths))
156+
);
119157
}
120158

121159
/**
122-
* Load environment file in given directory, silently failing if it doesn't exist.
160+
* Read and load environment file(s), silently failing if no files can be read.
123161
*
124162
* @throws \Dotenv\Exception\InvalidFileException
125163
*
126164
* @return array<string,string|null>
127165
*/
128166
public function safeLoad()
129167
{
130-
try {
131-
return $this->load();
132-
} catch (InvalidPathException $e) {
133-
// suppressing exception
134-
return [];
168+
$content = self::aggregate($this->read());
169+
170+
if ($content->isDefined()) {
171+
return $this->loader->load($this->repository, $content->get());
135172
}
173+
174+
return [];
136175
}
137176

138177
/**
@@ -181,53 +220,34 @@ private static function getFilePaths(array $paths, $names)
181220
}
182221

183222
/**
184-
* Attempt to read the files in order.
185-
*
186-
* @param string[] $filePaths
187-
* @param bool $shortCircuit
223+
* Read the given file.
188224
*
189-
* @throws \Dotenv\Exception\InvalidPathException
225+
* @param string $filePath
190226
*
191-
* @return string
227+
* @return \PhpOption\Option
192228
*/
193-
private static function findAndRead(array $filePaths, $shortCircuit)
229+
private static function readFromFile($filePath)
194230
{
195-
if ($filePaths === []) {
196-
throw new InvalidPathException('At least one environment file path must be provided.');
197-
}
198-
199-
$output = '';
200-
201-
foreach ($filePaths as $filePath) {
202-
$content = self::readFromFile($filePath);
203-
if ($content->isDefined()) {
204-
$output .= $content->get()."\n";
205-
if ($shortCircuit) {
206-
break;
207-
}
208-
}
209-
}
210-
211-
if (!$output) {
212-
throw new InvalidPathException(
213-
sprintf('Unable to read any of the environment file(s) at [%s].', implode(', ', $filePaths))
214-
);
215-
}
231+
$content = @file_get_contents($filePath);
216232

217-
return $output;
233+
return Option::fromValue($content, false);
218234
}
219235

220236
/**
221-
* Read the given file.
237+
* Aggregate the given raw file contents.
222238
*
223-
* @param string $filePath
239+
* @param array<string,string> $contents
224240
*
225241
* @return \PhpOption\Option
226242
*/
227-
private static function readFromFile($filePath)
243+
private static function aggregate(array $contents)
228244
{
229-
$content = @file_get_contents($filePath);
245+
$output = '';
230246

231-
return Option::fromValue($content, false);
247+
foreach ($contents as $content) {
248+
$output .= $content."\n";
249+
}
250+
251+
return Option::fromValue($output, '');
232252
}
233253
}

tests/Dotenv/DotenvTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,31 @@ public function testDotenvLoadsEnvironmentVars()
7070
$this->assertEmpty(getenv('NULL'));
7171
}
7272

73+
public function testDotenvReadsEnvironmentVarsMultipleNotShortCircuitMode()
74+
{
75+
$dotenv = Dotenv::createImmutable($this->folder, ['.env', 'example.env']);
76+
77+
$this->assertSame(
78+
[
79+
$this->folder.DIRECTORY_SEPARATOR.'.env' => "FOO=bar\nBAR=baz\nSPACED=\"with spaces\"\n\nNULL=\n",
80+
],
81+
$dotenv->read()
82+
);
83+
}
84+
85+
public function testDotenvReadsEnvironmentVarsMultipleWithShortCircuitMode()
86+
{
87+
$dotenv = Dotenv::createImmutable($this->folder, ['.env', 'example.env'], false);
88+
89+
$this->assertSame(
90+
[
91+
$this->folder.DIRECTORY_SEPARATOR.'.env' => "FOO=bar\nBAR=baz\nSPACED=\"with spaces\"\n\nNULL=\n",
92+
$this->folder.DIRECTORY_SEPARATOR.'example.env' => "EG=\"example\"\n",
93+
],
94+
$dotenv->read()
95+
);
96+
}
97+
7398
public function testDotenvLoadsEnvironmentVarsMultipleNotShortCircuitMode()
7499
{
75100
$dotenv = Dotenv::createImmutable($this->folder, ['.env', 'example.env']);

0 commit comments

Comments
 (0)