Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 90d87c5

Browse files
committed
[Dotenv] parse concatenated variable values
1 parent 8b6cb57 commit 90d87c5

File tree

2 files changed

+91
-47
lines changed

2 files changed

+91
-47
lines changed

src/Symfony/Component/Dotenv/Dotenv.php

Lines changed: 87 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -168,69 +168,110 @@ private function lexValue()
168168
throw $this->createFormatException('Whitespace are not supported before the value');
169169
}
170170

171-
$value = '';
172-
$singleQuoted = false;
173-
$notQuoted = false;
174-
if ("'" === $this->data[$this->cursor]) {
175-
$singleQuoted = true;
176-
++$this->cursor;
177-
while ("\n" !== $this->data[$this->cursor]) {
178-
if ("'" === $this->data[$this->cursor]) {
179-
if ($this->cursor + 1 === $this->end) {
180-
break;
181-
}
182-
if ("'" !== $this->data[$this->cursor + 1]) {
171+
$v = '';
172+
173+
do {
174+
if ("'" === $this->data[$this->cursor]) {
175+
$value = '';
176+
++$this->cursor;
177+
178+
while ("\n" !== $this->data[$this->cursor]) {
179+
if ("'" === $this->data[$this->cursor]) {
183180
break;
184181
}
185-
182+
$value .= $this->data[$this->cursor];
186183
++$this->cursor;
187-
}
188-
$value .= $this->data[$this->cursor];
189-
++$this->cursor;
190184

191-
if ($this->cursor === $this->end) {
185+
if ($this->cursor === $this->end) {
186+
throw $this->createFormatException('Missing quote to end the value');
187+
}
188+
}
189+
if ("\n" === $this->data[$this->cursor]) {
192190
throw $this->createFormatException('Missing quote to end the value');
193191
}
194-
}
195-
if ("\n" === $this->data[$this->cursor]) {
196-
throw $this->createFormatException('Missing quote to end the value');
197-
}
198-
++$this->cursor;
199-
} elseif ('"' === $this->data[$this->cursor]) {
200-
++$this->cursor;
201-
while ('"' !== $this->data[$this->cursor] || ('\\' === $this->data[$this->cursor - 1] && '\\' !== $this->data[$this->cursor - 2])) {
202-
$value .= $this->data[$this->cursor];
203192
++$this->cursor;
193+
$v .= $value;
194+
} elseif ('"' === $this->data[$this->cursor]) {
195+
$value = '';
196+
++$this->cursor;
197+
198+
while ('"' !== $this->data[$this->cursor] || ('\\' === $this->data[$this->cursor - 1] && '\\' !== $this->data[$this->cursor - 2])) {
199+
$value .= $this->data[$this->cursor];
200+
++$this->cursor;
204201

205-
if ($this->cursor === $this->end) {
202+
if ($this->cursor === $this->end) {
203+
throw $this->createFormatException('Missing quote to end the value');
204+
}
205+
}
206+
if ("\n" === $this->data[$this->cursor]) {
206207
throw $this->createFormatException('Missing quote to end the value');
207208
}
208-
}
209-
if ("\n" === $this->data[$this->cursor]) {
210-
throw $this->createFormatException('Missing quote to end the value');
211-
}
212-
++$this->cursor;
213-
$value = str_replace(array('\\\\', '\\"', '\r', '\n'), array('\\', '"', "\r", "\n"), $value);
214-
} else {
215-
$notQuoted = true;
216-
$prevChr = $this->data[$this->cursor - 1];
217-
while ($this->cursor < $this->end && "\n" !== $this->data[$this->cursor] && !((' ' === $prevChr || "\t" === $prevChr) && '#' === $this->data[$this->cursor])) {
218-
$value .= $prevChr = $this->data[$this->cursor];
219209
++$this->cursor;
210+
$value = str_replace(array('\\\\', '\\"', '\r', '\n'), array('\\', '"', "\r", "\n"), $value);
211+
$resolvedValue = $value;
212+
$resolvedValue = $this->resolveVariables($resolvedValue);
213+
$resolvedValue = $this->resolveCommands($resolvedValue);
214+
$v .= $resolvedValue;
215+
} else {
216+
$value = '';
217+
$prevChr = $this->data[$this->cursor - 1];
218+
while ($this->cursor < $this->end && !in_array($this->data[$this->cursor], array("\n", '"', "'"), true) && !((' ' === $prevChr || "\t" === $prevChr) && '#' === $this->data[$this->cursor])) {
219+
if ('\\' === $this->data[$this->cursor] && isset($this->data[$this->cursor + 1]) && ('"' === $this->data[$this->cursor + 1] || "'" === $this->data[$this->cursor + 1])) {
220+
++$this->cursor;
221+
}
222+
223+
$value .= $prevChr = $this->data[$this->cursor];
224+
225+
if ('$' === $this->data[$this->cursor] && isset($this->data[$this->cursor + 1]) && '(' === $this->data[$this->cursor + 1]) {
226+
++$this->cursor;
227+
$value .= '('.$this->lexNestedExpression().')';
228+
}
229+
230+
++$this->cursor;
231+
}
232+
$value = rtrim($value);
233+
$resolvedValue = $value;
234+
$resolvedValue = $this->resolveVariables($resolvedValue);
235+
$resolvedValue = $this->resolveCommands($resolvedValue);
236+
237+
if ($resolvedValue === $value && preg_match('/\s+/', $value)) {
238+
throw $this->createFormatException('A value containing spaces must be surrounded by quotes');
239+
}
240+
241+
$v .= $resolvedValue;
242+
243+
if ($this->cursor < $this->end && '#' === $this->data[$this->cursor]) {
244+
break;
245+
}
220246
}
221-
$value = rtrim($value);
222-
}
247+
} while ($this->cursor < $this->end && "\n" !== $this->data[$this->cursor]);
223248

224249
$this->skipEmptyLines();
225250

226-
$currentValue = $value;
227-
if (!$singleQuoted) {
228-
$value = $this->resolveVariables($value);
229-
$value = $this->resolveCommands($value);
251+
return $v;
252+
}
253+
254+
private function lexNestedExpression()
255+
{
256+
++$this->cursor;
257+
$value = '';
258+
259+
while ("\n" !== $this->data[$this->cursor] && ')' !== $this->data[$this->cursor]) {
260+
$value .= $this->data[$this->cursor];
261+
262+
if ('(' === $this->data[$this->cursor]) {
263+
$value .= $this->lexNestedExpression() . ')';
264+
}
265+
266+
++$this->cursor;
267+
268+
if ($this->cursor === $this->end) {
269+
throw $this->createFormatException('Missing closing parenthesis.');
270+
}
230271
}
231272

232-
if ($notQuoted && $currentValue == $value && preg_match('/\s+/', $value)) {
233-
throw $this->createFormatException('A value containing spaces must be surrounded by quotes');
273+
if ("\n" === $this->data[$this->cursor]) {
274+
throw $this->createFormatException('Missing closing parenthesis.');
234275
}
235276

236277
return $value;

src/Symfony/Component/Dotenv/Tests/DotenvTest.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ public function getEnvData()
8585
array("FOO='bar'\n", array('FOO' => 'bar')),
8686
array("FOO='bar\"foo'\n", array('FOO' => 'bar"foo')),
8787
array("FOO=\"bar\\\"foo\"\n", array('FOO' => 'bar"foo')),
88-
array("FOO='bar''foo'\n", array('FOO' => 'bar\'foo')),
8988
array('FOO="bar\nfoo"', array('FOO' => "bar\nfoo")),
9089
array('FOO="bar\rfoo"', array('FOO' => "bar\rfoo")),
9190
array('FOO=\'bar\nfoo\'', array('FOO' => 'bar\nfoo')),
@@ -96,6 +95,10 @@ public function getEnvData()
9695
array("FOO=\"bar\nfoo\"", array('FOO' => "bar\nfoo")),
9796

9897
// concatenated values
98+
array("FOO='bar''foo'\n", array('FOO' => 'barfoo')),
99+
array("FOO='bar '' baz'", array('FOO' => 'bar baz')),
100+
array("FOO=bar\nBAR='baz'\"\$FOO\"", array('FOO' => 'bar', 'BAR' => 'bazbar')),
101+
array("FOO='bar '\\'' baz'", array('FOO' => "bar ' baz")),
99102

100103
// comments
101104
array("#FOO=bar\nBAR=foo", array('BAR' => 'foo')),

0 commit comments

Comments
 (0)