diff --git a/src/Loader.php b/src/Loader.php index ef0a2a15..42f661b8 100644 --- a/src/Loader.php +++ b/src/Loader.php @@ -11,6 +11,8 @@ * It's responsible for loading variables by reading a file from disk and: * - stripping comments beginning with a `#`, * - parsing lines that look shell variable setters, e.g `export key = value`, `key="value"`. + * - multiline variable look always start with a " and end with it, e.g: `key="value + * value"` */ class Loader { @@ -84,8 +86,15 @@ public function load() $filePath = $this->filePath; $lines = $this->readLinesFromFile($filePath); + + // multiline + $multiline = false; + $multilineBuffer = []; + foreach ($lines as $line) { - if (!$this->isComment($line) && $this->looksLikeSetter($line)) { + list($multiline, $line, $multilineBuffer) = $this->multilineProcess($multiline, $line, $multilineBuffer); + + if (!$multiline && !$this->isComment($line) && $this->looksLikeSetter($line)) { $this->setEnvironmentVariable($line); } } @@ -167,6 +176,34 @@ protected function readLinesFromFile($filePath) return $lines; } + /** + * Used to make all multiline variable process. + * + * @param bool $multiline + * @param string $line + * @param array $buffer + * + * @return array + */ + protected function multilineProcess($multiline, $line, $buffer) + { + // check if $line can be multiline variable + if ($this->looksLikeMultilineStart($line)) { + $multiline = true; + } + if ($multiline) { + array_push($buffer, $line); + + if ($this->looksLikeMultilineStop($line)) { + $multiline = false; + $line = implode("\n", $buffer); + $buffer = []; + } + } + + return [$multiline, $line, $buffer]; + } + /** * Determine if the line in the file is a comment, e.g. begins with a #. * @@ -193,6 +230,30 @@ protected function looksLikeSetter($line) return strpos($line, '=') !== false; } + /** + * Determine if the given line can be the start of a multiline variable. + * + * @param string $line + * + * @return bool + */ + protected function looksLikeMultilineStart($line) + { + return strpos($line, '="') !== false && substr_count($line, '"') === 1; + } + + /** + * Determine if the given line can be the start of a multiline variable. + * + * @param string $line + * + * @return bool + */ + protected function looksLikeMultilineStop($line) + { + return strpos($line, '"') !== false && substr_count($line, '="') === 0; + } + /** * Split the compound string into parts. * diff --git a/tests/Dotenv/DotenvTest.php b/tests/Dotenv/DotenvTest.php index 38d9c37f..398cb0db 100644 --- a/tests/Dotenv/DotenvTest.php +++ b/tests/Dotenv/DotenvTest.php @@ -263,6 +263,9 @@ public function testDotenvAssertions() $this->assertEmpty(getenv('ASSERTVAR3')); $this->assertSame('0', getenv('ASSERTVAR4')); $this->assertSame('#foo', getenv('ASSERTVAR5')); + $this->assertSame("val1\nval2", getenv('ASSERTVAR6')); + $this->assertSame("val3", getenv('ASSERTVAR7')); + $this->assertSame("val3", getenv('ASSERTVAR8')); $dotenv->required([ 'ASSERTVAR1', @@ -270,19 +273,28 @@ public function testDotenvAssertions() 'ASSERTVAR3', 'ASSERTVAR4', 'ASSERTVAR5', + 'ASSERTVAR6', + 'ASSERTVAR7', + 'ASSERTVAR8', + 'ASSERTVAR9', ]); $dotenv->required([ 'ASSERTVAR1', 'ASSERTVAR4', 'ASSERTVAR5', + 'ASSERTVAR6', + 'ASSERTVAR7', + 'ASSERTVAR8', ])->notEmpty(); $dotenv->required([ 'ASSERTVAR1', 'ASSERTVAR4', 'ASSERTVAR5', - ])->notEmpty()->allowedValues(['0', 'val1', '#foo']); + 'ASSERTVAR7', + 'ASSERTVAR8', + ])->notEmpty()->allowedValues(['0', 'val1', 'val3', '#foo']); $this->assertTrue(true); // anything wrong an an exception will be thrown } diff --git a/tests/fixtures/env/assertions.env b/tests/fixtures/env/assertions.env index 6361d178..565c1c6c 100644 --- a/tests/fixtures/env/assertions.env +++ b/tests/fixtures/env/assertions.env @@ -2,4 +2,12 @@ ASSERTVAR1="val1" ASSERTVAR2="" ASSERTVAR3=" " ASSERTVAR4="0" # empty looking value -ASSERTVAR5=#foo \ No newline at end of file +ASSERTVAR5=#foo +ASSERTVAR6="val1 +val2" +ASSERTVAR7=" +val3" # +ASSERTVAR8="val3 +" +ASSERTVAR9=" +"