From 4cf156d909954fe8e0cde52378625dc1f3bc93ba Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Tue, 29 Jan 2019 10:57:28 +0000 Subject: [PATCH] Parsing fixes --- src/Parser.php | 84 +++++++++++++++++++++-------- tests/Dotenv/DotenvTest.php | 8 +++ tests/fixtures/env/quoted.env | 4 ++ tests/fixtures/env/specialchars.env | 3 ++ 4 files changed, 76 insertions(+), 23 deletions(-) diff --git a/src/Parser.php b/src/Parser.php index 963e4d38..67a5c4d5 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -7,11 +7,10 @@ class Parser { const INITIAL_STATE = 0; - const UNQUOTED_STATE = 1; - const QUOTED_STATE = 2; - const ESCAPE_STATE = 3; - const WHITESPACE_STATE = 4; - const COMMENT_STATE = 5; + const QUOTED_STATE = 1; + const ESCAPE_STATE = 2; + const WHITESPACE_STATE = 3; + const COMMENT_STATE = 4; /** * Parse the given variable name. @@ -29,31 +28,45 @@ public static function parseName($name) * Parse the given variable value. * * @param string $value - * + * * @throws \Dotenv\Exception\InvalidFileException * * @return string */ public static function parseValue($value) + { + if ($value === '') { + return ''; + } elseif ($value[0] === '"' || $value[0] === '\'') { + return Parser::parseQuotedValue($value); + } else { + return Parser::parseUnquotedValue($value); + } + } + + /** + * Parse the given quoted value. + * + * @param string $value + * + * @throws \Dotenv\Exception\InvalidFileException + * + * @return string + */ + public static function parseQuotedValue($value) { $data = array_reduce(str_split($value), function ($data, $char) use ($value) { switch ($data[1]) { case Parser::INITIAL_STATE: - if ($char === '"') { + if ($char === '"' || $char === '\'') { return array($data[0], Parser::QUOTED_STATE); } else { - return array($data[0].$char, Parser::UNQUOTED_STATE); - } - case Parser::UNQUOTED_STATE: - if ($char === '#') { - return array($data[0], Parser::COMMENT_STATE); - } elseif (ctype_space($char)) { - return array($data[0], Parser::WHITESPACE_STATE); - } else { - return array($data[0].$char, Parser::UNQUOTED_STATE); + throw new InvalidFileException( + 'Expected the value to start with a quote.' + ); } case Parser::QUOTED_STATE: - if ($char === '"') { + if ($char === $value[0]) { return array($data[0], Parser::WHITESPACE_STATE); } elseif ($char === '\\') { return array($data[0], Parser::ESCAPE_STATE); @@ -61,7 +74,7 @@ public static function parseValue($value) return array($data[0].$char, Parser::QUOTED_STATE); } case Parser::ESCAPE_STATE: - if ($char === '"' || $char === '\\') { + if ($char === $value[0] || $char === '\\') { return array($data[0].$char, Parser::QUOTED_STATE); } else { return array($data[0].'\\'.$char, Parser::QUOTED_STATE); @@ -70,11 +83,9 @@ public static function parseValue($value) if ($char === '#') { return array($data[0], Parser::COMMENT_STATE); } elseif (!ctype_space($char)) { - if ($data[0] !== '' && $data[0][0] === '#') { - return array('', Parser::COMMENT_STATE); - } else { - throw new InvalidFileException('Dotenv values containing spaces must be surrounded by quotes.'); - } + throw new InvalidFileException( + 'Dotenv values containing spaces must be surrounded by quotes.' + ); } else { return array($data[0], Parser::WHITESPACE_STATE); } @@ -85,4 +96,31 @@ public static function parseValue($value) return trim($data[0]); } + + /** + * Parse the given unquoted value. + * + * @param string $value + * + * @throws \Dotenv\Exception\InvalidFileException + * + * @return string + */ + public static function parseUnquotedValue($value) + { + $parts = explode(' #', $value, 2); + $value = trim($parts[0]); + + // Unquoted values cannot contain whitespace + if (preg_match('/\s+/', $value) > 0) { + // Check if value is a comment (usually triggered when empty value with comment) + if (preg_match('/^#/', $value) > 0) { + $value = ''; + } else { + throw new InvalidFileException('Dotenv values containing spaces must be surrounded by quotes.'); + } + } + + return trim($value); + } } diff --git a/tests/Dotenv/DotenvTest.php b/tests/Dotenv/DotenvTest.php index 0273f336..8458b0f7 100644 --- a/tests/Dotenv/DotenvTest.php +++ b/tests/Dotenv/DotenvTest.php @@ -64,9 +64,14 @@ public function testQuotedDotenvLoadsEnvironmentVars() $this->assertSame('with spaces', getenv('QSPACED')); $this->assertEmpty(getenv('QNULL')); $this->assertSame('pgsql:host=localhost;dbname=test', getenv('QEQUALS')); + $this->assertSame('test some escaped characters like a quote (") or maybe a backslash (\\)', getenv('QESCAPED')); $this->assertSame('iiiiviiiixiiiiviiii\\n', getenv('QSLASH1')); $this->assertSame('iiiiviiiixiiiiviiii\\n', getenv('QSLASH2')); + + $this->assertSame('test some escaped characters like a quote (\') or maybe a backslash (\\)', getenv('SQESCAPED')); + $this->assertSame('iiiiviiiixiiiiviiii\\n', getenv('SQSLASH1')); + $this->assertSame('iiiiviiiixiiiiviiii\\n', getenv('SQSLASH2')); } public function testLargeDotenvLoadsEnvironmentVars() @@ -261,6 +266,9 @@ public function testDotenvAllowsSpecialCharacters() $this->assertSame('jdgEB4{QgEC]HL))&GcXxokB+wqoN+j>xkV7K?m$r', getenv('SPVAR3')); $this->assertSame('22222:22#2^{', getenv('SPVAR4')); $this->assertSame('test some escaped characters like a quote " or maybe a backslash \\', getenv('SPVAR5')); + $this->assertSame('secret!@#', getenv('SPVAR6')); + $this->assertSame('secret!@#', getenv('SPVAR7')); + $this->assertSame('secret!@#', getenv('SPVAR8')); } public function testDotenvAssertions() diff --git a/tests/fixtures/env/quoted.env b/tests/fixtures/env/quoted.env index cd8ab578..91a1c8ee 100644 --- a/tests/fixtures/env/quoted.env +++ b/tests/fixtures/env/quoted.env @@ -9,3 +9,7 @@ QWHITESPACE = "no space" QESCAPED="test some escaped characters like a quote (\") or maybe a backslash (\\)" QSLASH1="iiiiviiiixiiiiviiii\n" QSLASH2="iiiiviiiixiiiiviiii\\n" + +SQESCAPED='test some escaped characters like a quote (\') or maybe a backslash (\\)' +SQSLASH1='iiiiviiiixiiiiviiii\n' +SQSLASH2='iiiiviiiixiiiiviiii\\n' diff --git a/tests/fixtures/env/specialchars.env b/tests/fixtures/env/specialchars.env index f595f52a..8852d538 100644 --- a/tests/fixtures/env/specialchars.env +++ b/tests/fixtures/env/specialchars.env @@ -3,3 +3,6 @@ SPVAR2="?BUty3koaV3%GA*hMAwH}B" SPVAR3="jdgEB4{QgEC]HL))&GcXxokB+wqoN+j>xkV7K?m$r" SPVAR4="22222:22#2^{" SPVAR5="test some escaped characters like a quote \" or maybe a backslash \\" # not escaped +SPVAR6=secret!@# +SPVAR7='secret!@#' +SPVAR8="secret!@#"