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

Skip to content

Commit d33ec7f

Browse files
committed
Merge pull request vlucas#37 from brightmachine/bugs/special-chars-36
Allow special characters including $ signs
2 parents e783b68 + 4cbefdf commit d33ec7f

7 files changed

Lines changed: 80 additions & 18 deletions

File tree

README.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,14 @@ $s3_bucket = $request->server->get('S3_BUCKET');
100100

101101
### Nesting Variables
102102

103-
It's possible to nest an environment variable within another, useful to cut down on repetition:
103+
It's possible to nest an environment variable within another, useful to cut down on repetition.
104+
105+
This is done by wrapping an existing environment variable in `{$…}` e.g.
104106

105107
```shell
106108
BASE_DIR=/var/webroot/project-root
107-
CACHE_DIR=$BASE_DIR/cache
108-
LOG_DIR=$BASE_DIR/logs
109+
CACHE_DIR={$BASE_DIR}/cache
110+
TMP_DIR={$BASE_DIR}/tmp
109111
```
110112

111113
### Immutability
@@ -160,6 +162,16 @@ Again, if the environment variable wasn't in this list, you'd get a similar Exce
160162
Required environment variable missing or value not allowed: 'SESSION_STORE'
161163
```
162164

165+
### Comments
166+
167+
You can comment your `.env` file using the `#` character. E.g.
168+
169+
```shell
170+
# this is a comment
171+
VAR="value" # comment
172+
VAR=value # comment
173+
```
174+
163175
Usage Notes
164176
-----------
165177

src/Dotenv.php

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ public static function load($path, $file = '.env')
4040

4141
foreach ($lines as $line) {
4242
// Disregard comments
43-
if (strpos($line, '#') !== false) {
44-
$line = substr($line, 0, strpos($line, '#'));
43+
if (strpos(trim($line), '#') === 0) {
44+
continue;
4545
}
4646
// Only use non-empty lines that look like setters
4747
if (strpos($line, '=') !== false) {
@@ -159,7 +159,30 @@ private static function splitCompoundStringIntoParts($name, $value)
159159
*/
160160
private static function sanitiseVariableValue($value)
161161
{
162-
return trim(str_replace(array('\'', '"'), '', $value));
162+
$value = trim($value);
163+
if (!$value) return '';
164+
if (strpbrk($value[0], '"\'') !== false) { // value starts with a quote
165+
$quote = $value[0];
166+
$regexPattern = sprintf('/^
167+
%1$s # match a quote at the start of the value
168+
( # capturing sub-pattern used
169+
(?: # we do not need to capture this
170+
[^%1$s\\\\] # any character other than a quote or backslash
171+
|\\\\\\\\ # or two backslashes together
172+
|\\\\%1$s # or an escaped quote e.g \"
173+
)* # as many characters that match the previous rules
174+
) # end of the capturing sub-pattern
175+
%1$s # and the closing quote
176+
.*$ # and discard any string after the closing quote
177+
/mx', $quote);
178+
$value = preg_replace($regexPattern, '$1', $value);
179+
$value = str_replace("\\$quote", $quote, $value);
180+
$value = str_replace('\\\\', '\\', $value);
181+
} else {
182+
$parts = explode(' #', $value, 2);
183+
$value = $parts[0];
184+
}
185+
return trim($value);
163186
}
164187

165188
/**
@@ -174,7 +197,7 @@ private static function sanitiseVariableName($name)
174197
}
175198

176199
/**
177-
* Look for $varname patterns in the variable value and replace with an existing
200+
* Look for {$varname} patterns in the variable value and replace with an existing
178201
* environment variable.
179202
*
180203
* @param $value
@@ -184,9 +207,14 @@ private static function resolveNestedVariables($value)
184207
{
185208
if (strpos($value, '$') !== false) {
186209
$value = preg_replace_callback(
187-
'/\${?([a-zA-Z0-9_]+)}?/',
210+
'/{\$([a-zA-Z0-9_]+)}/',
188211
function ($matchedPatterns) {
189-
return Dotenv::findEnvironmentVariable($matchedPatterns[1]);
212+
$nestedVariable = Dotenv::findEnvironmentVariable($matchedPatterns[1]);
213+
if (is_null($nestedVariable)) {
214+
return $matchedPatterns[0];
215+
} else {
216+
return $nestedVariable;
217+
}
190218
},
191219
$value
192220
);

tests/Dotenv/Dotenv.php

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ public function testCommentedDotenvLoadsEnvironmentVars()
1717
$this->assertEquals(false, getenv('CBAR'));
1818
$this->assertEquals(false, getenv('CZOO'));
1919
$this->assertEquals('with spaces', getenv('CSPACED'));
20+
$this->assertEquals('a value with a # character', getenv('CQUOTES'));
21+
$this->assertEquals('a value with a # character & a quote " character inside quotes', getenv('CQUOTESWITHQUOTE'));
2022
$this->assertEquals('', getenv('CNULL'));
2123
}
2224

@@ -28,6 +30,7 @@ public function testQuotedDotenvLoadsEnvironmentVars()
2830
$this->assertEquals('with spaces', getenv('QSPACED'));
2931
$this->assertEquals('', getenv('QNULL'));
3032
$this->assertEquals('pgsql:host=localhost;dbname=test', getenv('QEQUALS'));
33+
$this->assertEquals("test some escaped characters like a quote (') or maybe a backslash (\\)", getenv('QESCAPED'));
3134
}
3235

3336
public function testExportedDotenvLoadsEnvironmentVars()
@@ -84,10 +87,9 @@ public function testDotenvRequiredArrayEnvironmentVars()
8487
public function testDotenvNestedEnvironmentVars()
8588
{
8689
Dotenv::load(dirname(__DIR__) . '/fixtures', 'nested.env');
87-
$this->assertEquals('foo', $_ENV['NFOO']);
88-
$this->assertEquals('bar', $_ENV['NBAR']);
89-
$this->assertEquals('foobar', $_ENV['NFOOBAR']);
90-
$this->assertEquals('foo', $_ENV['NFOOBAZ']);
90+
$this->assertEquals('Hello World!', $_ENV['NVAR3']);
91+
$this->assertEquals('${NVAR1} ${NVAR2}', $_ENV['NVAR4']); // not resolved
92+
$this->assertEquals('$NVAR1 {NVAR2}', $_ENV['NVAR5']); // not resolved
9193
}
9294

9395
public function testDotenvAllowedValues()
@@ -151,4 +153,14 @@ public function testDotenvDoesNotOverwriteEnvWhenMutable()
151153
Dotenv::load(dirname(__DIR__) . '/fixtures', 'quoted.env');
152154
$this->assertEquals('bar', getenv('QFOO'));
153155
}
156+
157+
public function testDotenvAllowsSpecialCharacters()
158+
{
159+
Dotenv::load(dirname(__DIR__) . '/fixtures', 'specialchars.env');
160+
$this->assertEquals('$a6^C7k%zs+e^.jvjXk', getenv('SPVAR1'));
161+
$this->assertEquals('?BUty3koaV3%GA*hMAwH}B', getenv('SPVAR2'));
162+
$this->assertEquals('jdgEB4{QgEC]HL))&GcXxokB+wqoN+j>xkV7K?m$r', getenv('SPVAR3'));
163+
$this->assertEquals('22222:22#2^{', getenv('SPVAR4'));
164+
$this->assertEquals("test some escaped characters like a quote \\' or maybe a backslash \\\\", getenv('SPVAR5'));
165+
}
154166
}

tests/fixtures/commented.env

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ CFOO=bar
33
#CBAR=baz
44
#CZOO=goo # a comment on a commented row
55
CSPACED=with spaces # this is a comment
6+
CQUOTES="a value with a # character" # this is a comment
7+
CQUOTESWITHQUOTE="a value with a # character & a quote \" character inside quotes" # " this is a comment
68

79
CNULL=
810

tests/fixtures/nested.env

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
NFOO='foo'
2-
NBAR='bar'
3-
NFOOBAR='$NFOO$NBAR'
4-
NFOOBAZ="$NFOO$NBAZ"
1+
NVAR1='Hello'
2+
NVAR2='World!'
3+
NVAR3='{$NVAR1} {$NVAR2}'
4+
NVAR4='${NVAR1} ${NVAR2}'
5+
NVAR5='$NVAR1 {NVAR2}'

tests/fixtures/quoted.env

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@ QSPACED='with spaces'
44
QEQUALS='pgsql:host=localhost;dbname=test'
55

66
QNULL=''
7-
QWHITESPACE = 'no space'
7+
QWHITESPACE = 'no space'
8+
9+
QESCAPED='test some escaped characters like a quote (\') or maybe a backslash (\\)'

tests/fixtures/specialchars.env

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
SPVAR1='$a6^C7k%zs+e^.jvjXk'
2+
SPVAR2='?BUty3koaV3%GA*hMAwH}B'
3+
SPVAR3='jdgEB4{QgEC]HL))&GcXxokB+wqoN+j>xkV7K?m$r'
4+
SPVAR4='22222:22#2^{'
5+
SPVAR5=test some escaped characters like a quote \' or maybe a backslash \\ # not escaped

0 commit comments

Comments
 (0)