Skip to content

Commit 5831cfb

Browse files
Refactored implementation
1 parent 26b0e9c commit 5831cfb

File tree

7 files changed

+346
-45
lines changed

7 files changed

+346
-45
lines changed

src/Parser.php

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

55
use Dotenv\Exception\InvalidFileException;
6+
use Dotenv\Regex\Regex;
67

78
class Parser
89
{
@@ -154,50 +155,16 @@ private static function processQuotedValue($value)
154155
$quote
155156
);
156157

157-
$value = self::pregReplace($pattern, '$1', $value);
158-
159-
return str_replace('\\\\', '\\', str_replace("\\$quote", $quote, $value));
160-
}
161-
162-
163-
/**
164-
* Perform a preg replace, failing with an exception.
165-
*
166-
* @param string $pattern
167-
* @param string $repalcement
168-
* @param string $subject
169-
*
170-
* @throws \Dotenv\Exception\InvalidFileException
171-
*
172-
* @return string
173-
*/
174-
private static function pregReplace($pattern, $replacement, $subject)
175-
{
176-
$result = (string) @preg_replace($pattern, $replacement, $subject);
177-
178-
if (($e = preg_last_error()) !== PREG_NO_ERROR) {
179-
throw new InvalidFileException(
180-
self::getErrorMessage(sprintf('a quote parsing error (%s)', self::lookupError($e)), $subject)
181-
);
182-
}
183-
184-
return $result;
185-
}
186-
187-
/**
188-
* Lookup the preg error code.
189-
*
190-
* @param int $code
191-
*
192-
* @return string
193-
*/
194-
private static function lookupError($code)
195-
{
196-
$errors = array_filter(get_defined_constants(true)['pcre'], function ($msg) {
197-
return substr($msg, -6) === '_ERROR';
198-
}, ARRAY_FILTER_USE_KEY);
199-
200-
return array_search($code, $errors, true);
158+
return Regex::pregReplace($pattern, '$1', $value)
159+
->mapSuccess(function ($str) use ($quote) {
160+
return str_replace('\\\\', '\\', str_replace("\\$quote", $quote, $str));
161+
})
162+
->mapError(function ($err) use ($value) {
163+
throw new InvalidFileException(
164+
self::getErrorMessage(sprintf('a quote parsing error (%s)', $err), $value)
165+
);
166+
})
167+
->getSuccess();
201168
}
202169

203170
/**

src/Regex/Error.php

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
namespace Dotenv\Regex;
4+
5+
use PhpOption\None;
6+
use PhpOption\Some;
7+
8+
class Error extends Result
9+
{
10+
/**
11+
* @var string
12+
*/
13+
private $value;
14+
15+
/**
16+
* Internal constructor for an error value.
17+
*
18+
* @param string $value
19+
*
20+
* @return void
21+
*/
22+
private function __construct($value)
23+
{
24+
$this->value = $value;
25+
}
26+
27+
/**
28+
* Create a new error value.
29+
*
30+
* @param string $value
31+
*
32+
* @return \Dotenv\Regex\Result
33+
*/
34+
public static function create($value)
35+
{
36+
return new self($value);
37+
}
38+
39+
/**
40+
* Get the success option value.
41+
*
42+
* @return \PhpOption\Option
43+
*/
44+
public function success()
45+
{
46+
return None::create();
47+
}
48+
49+
/**
50+
* Map over the success value.
51+
*
52+
* @param callable $f
53+
*
54+
* @return \Dotenv\Regex\Result
55+
*/
56+
public function mapSuccess(callable $f)
57+
{
58+
return self::create($this->value);
59+
}
60+
61+
/**
62+
* Get the error option value.
63+
*
64+
* @return \PhpOption\Option
65+
*/
66+
public function error()
67+
{
68+
return Some::create($this->value);
69+
}
70+
71+
/**
72+
* Map over the error value.
73+
*
74+
* @param callable $f
75+
*
76+
* @return \Dotenv\Regex\Result
77+
*/
78+
public function mapError(callable $f)
79+
{
80+
return self::create($f($this->value));
81+
}
82+
}

src/Regex/Regex.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
namespace Dotenv\Regex;
4+
5+
use PhpOption\Option;
6+
7+
class Regex
8+
{
9+
/**
10+
* Perform a preg replace, failing with an exception.
11+
*
12+
* @param string $pattern
13+
* @param string $repalcement
14+
* @param string $subject
15+
*
16+
* @return \Dotenv\Regex\Result
17+
*/
18+
public static function pregReplace($pattern, $replacement, $subject)
19+
{
20+
$result = (string) @preg_replace($pattern, $replacement, $subject);
21+
22+
if (($e = preg_last_error()) !== PREG_NO_ERROR) {
23+
return Error::create(self::lookupError($e));
24+
}
25+
26+
return Success::create($result);
27+
}
28+
29+
/**
30+
* Lookup the preg error code.
31+
*
32+
* @param int $code
33+
*
34+
* @return string
35+
*/
36+
private static function lookupError($code)
37+
{
38+
return Option::fromValue(get_defined_constants(true))
39+
->filter(function (array $consts) {
40+
return isset($consts['pcre']) && defined('ARRAY_FILTER_USE_KEY');
41+
})
42+
->map(function (array $consts) {
43+
return array_filter($consts['pcre'], function ($msg) {
44+
return substr($msg, -6) === '_ERROR';
45+
}, ARRAY_FILTER_USE_KEY);
46+
})
47+
->flatMap(function (array $errors) use ($code) {
48+
return Option::fromValue(
49+
array_search($code, $errors, true)
50+
);
51+
})
52+
->getOrElse('PREG_ERROR');
53+
}
54+
}

src/Regex/Result.php

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
namespace Dotenv\Regex;
4+
5+
abstract class Result
6+
{
7+
/**
8+
* Get the success option value.
9+
*
10+
* @return \PhpOption\Option
11+
*/
12+
public abstract function success();
13+
14+
/**
15+
* Get the error value, if possible.
16+
*
17+
* @return string
18+
*/
19+
public function getSuccess()
20+
{
21+
return $this->success()->get();
22+
}
23+
24+
/**
25+
* Map over the success value.
26+
*
27+
* @param callable $f
28+
*
29+
* @return \Dotenv\Regex\Result
30+
*/
31+
public abstract function mapSuccess(callable $f);
32+
33+
/**
34+
* Get the error option value.
35+
*
36+
* @return \PhpOption\Option
37+
*/
38+
public abstract function error();
39+
40+
/**
41+
* Get the error value, if possible.
42+
*
43+
* @return string
44+
*/
45+
public function getError()
46+
{
47+
return $this->error()->get();
48+
}
49+
50+
/**
51+
* Map over the error value.
52+
*
53+
* @param callable $f
54+
*
55+
* @return \Dotenv\Regex\Result
56+
*/
57+
public abstract function mapError(callable $f);
58+
}

src/Regex/Success.php

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
namespace Dotenv\Regex;
4+
5+
use PhpOption\None;
6+
use PhpOption\Some;
7+
8+
class Success extends Result
9+
{
10+
/**
11+
* @var string
12+
*/
13+
private $value;
14+
15+
/**
16+
* Internal constructor for a success value.
17+
*
18+
* @param string $value
19+
*
20+
* @return void
21+
*/
22+
private function __construct($value)
23+
{
24+
$this->value = $value;
25+
}
26+
27+
/**
28+
* Create a new success value.
29+
*
30+
* @param string $value
31+
*
32+
* @return \Dotenv\Regex\Result
33+
*/
34+
public static function create($value)
35+
{
36+
return new self($value);
37+
}
38+
39+
/**
40+
* Get the success option value.
41+
*
42+
* @return \PhpOption\Option
43+
*/
44+
public function success()
45+
{
46+
return Some::create($this->value);
47+
}
48+
49+
/**
50+
* Map over the success value.
51+
*
52+
* @param callable $f
53+
*
54+
* @return \Dotenv\Regex\Result
55+
*/
56+
public function mapSuccess(callable $f)
57+
{
58+
return self::create($f($this->value));
59+
}
60+
61+
/**
62+
* Get the error option value.
63+
*
64+
* @return \PhpOption\Option
65+
*/
66+
public function error()
67+
{
68+
return None::create();
69+
}
70+
71+
/**
72+
* Map over the error value.
73+
*
74+
* @param callable $f
75+
*
76+
* @return \Dotenv\Regex\Result
77+
*/
78+
public function mapError(callable $f)
79+
{
80+
return self::create($this->value);
81+
}
82+
}

tests/Dotenv/ParserTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public function testParseInvalidName()
5454

5555
/**
5656
* @expectedException \Dotenv\Exception\InvalidFileException
57-
* @expectedExceptionMessage Failed to parse dotenv file due to a quote parsing error (PREG_BACKTRACK_LIMIT_ERROR). Failed at ["iiiiviiiixiiiiviiii\n"].
57+
* @expectedExceptionMessage Failed to parse dotenv file due to a quote parsing error (PREG_
5858
*/
5959
public function testParserFailsWithException()
6060
{

0 commit comments

Comments
 (0)