diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index beba1265..be305133 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,7 +11,7 @@ jobs: strategy: matrix: - php: ['7.1', '7.2', '7.3', '7.4', '8.0', '8.1'] + php: ['7.1', '7.2', '7.3', '7.4', '8.0'] steps: - name: Checkout Code diff --git a/src/Parser/EntryParser.php b/src/Parser/EntryParser.php index 64df9a02..5cfa3eef 100644 --- a/src/Parser/EntryParser.php +++ b/src/Parser/EntryParser.php @@ -19,6 +19,7 @@ final class EntryParser private const ESCAPE_SEQUENCE_STATE = 4; private const WHITESPACE_STATE = 5; private const COMMENT_STATE = 6; + private const REJECT_STATES = [self::SINGLE_QUOTED_STATE, self::DOUBLE_QUOTED_STATE, self::ESCAPE_SEQUENCE_STATE]; /** * This class is a singleton. @@ -156,16 +157,20 @@ private static function parseValue(string $value) return Success::create(Value::blank()); } - return \array_reduce(\iterator_to_array(Lexer::lex($value)), static function (Result $data, string $token) use ($value) { - return $data->flatMap(static function (array $data) use ($token, $value) { - return self::processToken($data[1], $token)->mapError(static function (string $err) use ($value) { - return self::getErrorMessage($err, $value); - })->map(static function (array $val) use ($data) { + return \array_reduce(\iterator_to_array(Lexer::lex($value)), static function (Result $data, string $token) { + return $data->flatMap(static function (array $data) use ($token) { + return self::processToken($data[1], $token)->map(static function (array $val) use ($data) { return [$data[0]->append($val[0], $val[1]), $val[2]]; }); }); - }, Success::create([Value::blank(), self::INITIAL_STATE]))->map(static function (array $data) { - return $data[0]; + }, Success::create([Value::blank(), self::INITIAL_STATE]))->flatMap(static function (array $result) { + if (in_array($result[1], self::REJECT_STATES, true)) { + return Error::create('a missing closing quote'); + } + + return Success::create($result[0]); + })->mapError(static function (string $err) use ($value) { + return self::getErrorMessage($err, $value); }); } diff --git a/tests/Dotenv/Parser/EntryParserTest.php b/tests/Dotenv/Parser/EntryParserTest.php index d3082e44..e61be25e 100644 --- a/tests/Dotenv/Parser/EntryParserTest.php +++ b/tests/Dotenv/Parser/EntryParserTest.php @@ -157,6 +157,30 @@ public function testParserEscapingSingle() $this->checkPositiveResult($result, 'FOO_BAD', 'iiiiviiiixiiiiviiii\\a'); } + public function testParserMissingClosingSingleQuote() + { + $result = EntryParser::parse('TEST=\'erert'); + $this->checkErrorResult($result, 'Encountered a missing closing quote at [\'erert].'); + } + + public function testParserMissingClosingDoubleQuote() + { + $result = EntryParser::parse('TEST="erert'); + $this->checkErrorResult($result, 'Encountered a missing closing quote at ["erert].'); + } + + public function testParserMissingClosingQuotes() + { + $result = EntryParser::parse("TEST=\"erert\nTEST='erert\n"); + $this->checkErrorResult($result, 'Encountered a missing closing quote at ["erert].'); + } + + public function testParserClosingQuoteWithEscape() + { + $result = EntryParser::parse('TEST="\\'); + $this->checkErrorResult($result, 'Encountered a missing closing quote at ["\\].'); + } + /** * @param \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Entry,string> $result * @param string $name diff --git a/tests/Dotenv/Parser/LinesTest.php b/tests/Dotenv/Parser/LinesTest.php index 57886760..3f0502f5 100644 --- a/tests/Dotenv/Parser/LinesTest.php +++ b/tests/Dotenv/Parser/LinesTest.php @@ -62,4 +62,17 @@ public function testProcessClosingSlash() self::assertSame($expected, $lines); } + + public function testProcessBadQuotes() + { + $lines = [ + "TEST=\"erert\nTEST='erert\n", + ]; + + $expected = [ + "TEST=\"erert\nTEST='erert\n", + ]; + + self::assertSame($expected, $lines); + } }