Skip to content

Commit a58c32f

Browse files
Moved reading out of the dotenv class
1 parent b764f4f commit a58c32f

File tree

5 files changed

+157
-95
lines changed

5 files changed

+157
-95
lines changed

src/Dotenv.php

Lines changed: 15 additions & 70 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;
@@ -69,7 +71,7 @@ public function __construct(LoaderInterface $loader, RepositoryInterface $reposi
6971
*/
7072
public static function create(RepositoryInterface $repository, $paths, $names = null, $shortCircuit = true)
7173
{
72-
$files = self::getFilePaths((array) $paths, (array) ($names ?: '.env'));
74+
$files = Paths::filePaths((array) $paths, (array) ($names ?: '.env'));
7375

7476
return new self(new Loader(), $repository, $files, $shortCircuit);
7577
}
@@ -106,32 +108,6 @@ public static function createImmutable($paths, $names = null, $shortCircuit = tr
106108
return self::create($repository, $paths, $names, $shortCircuit);
107109
}
108110

109-
/**
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-
135111
/**
136112
* Read and load environment file(s).
137113
*
@@ -145,15 +121,11 @@ public function load()
145121
throw new InvalidPathException('At least one environment file path must be provided.');
146122
}
147123

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-
);
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+
});
157129
}
158130

159131
/**
@@ -165,13 +137,7 @@ public function load()
165137
*/
166138
public function safeLoad()
167139
{
168-
$content = self::aggregate($this->read());
169-
170-
if ($content->isDefined()) {
171-
return $this->loader->load($this->repository, $content->get());
172-
}
173-
174-
return [];
140+
return $this->tryLoad()->getOrElse([]);
175141
}
176142

177143
/**
@@ -199,38 +165,17 @@ public function ifPresent($variables)
199165
}
200166

201167
/**
202-
* Returns the full paths to the files.
203-
*
204-
* @param string[] $paths
205-
* @param string $file
168+
* Read and load environment file(s), returning an option.
206169
*
207-
* @return string[]
208-
*/
209-
private static function getFilePaths(array $paths, $names)
210-
{
211-
$files = [];
212-
213-
foreach ($paths as $path) {
214-
foreach ($names as $name) {
215-
$files[] = rtrim($path, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$name;
216-
}
217-
}
218-
219-
return $files;
220-
}
221-
222-
/**
223-
* Read the given file.
224-
*
225-
* @param string $filePath
170+
* @throws \Dotenv\Exception\InvalidFileException
226171
*
227172
* @return \PhpOption\Option
228173
*/
229-
private static function readFromFile($filePath)
174+
private function tryLoad()
230175
{
231-
$content = @file_get_contents($filePath);
232-
233-
return Option::fromValue($content, false);
176+
return self::aggregate(Reader::read($this->filePaths, $this->shortCircuit))->map(function ($content) {
177+
return $this->loader->load($this->repository, $content);
178+
});
234179
}
235180

236181
/**

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+
}

tests/Dotenv/DotenvTest.php

Lines changed: 6 additions & 25 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,31 +76,6 @@ public function testDotenvLoadsEnvironmentVars()
7076
$this->assertEmpty(getenv('NULL'));
7177
}
7278

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-
9879
public function testDotenvLoadsEnvironmentVarsMultipleNotShortCircuitMode()
9980
{
10081
$dotenv = Dotenv::createImmutable($this->folder, ['.env', 'example.env']);

tests/Dotenv/FileTest.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
use Dotenv\File\Paths;
4+
use Dotenv\File\Reader;
5+
use PHPUnit\Framework\TestCase;
6+
7+
class FileTest extends TestCase
8+
{
9+
/**
10+
* @var string
11+
*/
12+
private $folder;
13+
14+
public function setUp()
15+
{
16+
$this->folder = dirname(__DIR__).'/fixtures/env';
17+
}
18+
19+
public function testBasicRead()
20+
{
21+
$this->assertSame(
22+
[
23+
$this->folder.DIRECTORY_SEPARATOR.'.env' => "FOO=bar\nBAR=baz\nSPACED=\"with spaces\"\n\nNULL=\n",
24+
],
25+
Reader::read(
26+
Paths::filePaths([$this->folder], ['.env'])
27+
)
28+
);
29+
}
30+
31+
public function testFileReadMultipleNotShortCircuitMode()
32+
{
33+
$this->assertSame(
34+
[
35+
$this->folder.DIRECTORY_SEPARATOR.'.env' => "FOO=bar\nBAR=baz\nSPACED=\"with spaces\"\n\nNULL=\n",
36+
],
37+
Reader::read(
38+
Paths::filePaths([$this->folder], ['.env', 'example.env'])
39+
)
40+
);
41+
}
42+
43+
public function testFileReadMultipleWithShortCircuitMode()
44+
{
45+
$this->assertSame(
46+
[
47+
$this->folder.DIRECTORY_SEPARATOR.'.env' => "FOO=bar\nBAR=baz\nSPACED=\"with spaces\"\n\nNULL=\n",
48+
$this->folder.DIRECTORY_SEPARATOR.'example.env' => "EG=\"example\"\n",
49+
],
50+
Reader::read(
51+
Paths::filePaths([$this->folder], ['.env', 'example.env']),
52+
false
53+
)
54+
);
55+
}
56+
}

0 commit comments

Comments
 (0)