[Dotenv] added the component
This commit is contained in:
parent
e66e6afcc1
commit
5a6be8ae9c
@ -40,6 +40,7 @@
|
||||
"symfony/debug-bundle": "self.version",
|
||||
"symfony/doctrine-bridge": "self.version",
|
||||
"symfony/dom-crawler": "self.version",
|
||||
"symfony/dotenv": "self.version",
|
||||
"symfony/event-dispatcher": "self.version",
|
||||
"symfony/expression-language": "self.version",
|
||||
"symfony/filesystem": "self.version",
|
||||
|
3
src/Symfony/Component/Dotenv/.gitignore
vendored
Normal file
3
src/Symfony/Component/Dotenv/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
vendor/
|
||||
composer.lock
|
||||
phpunit.xml
|
7
src/Symfony/Component/Dotenv/CHANGELOG.md
Normal file
7
src/Symfony/Component/Dotenv/CHANGELOG.md
Normal file
@ -0,0 +1,7 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
3.3.0
|
||||
-----
|
||||
|
||||
* added the component
|
338
src/Symfony/Component/Dotenv/Dotenv.php
Normal file
338
src/Symfony/Component/Dotenv/Dotenv.php
Normal file
@ -0,0 +1,338 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Dotenv;
|
||||
|
||||
use Symfony\Component\Dotenv\Exception\FormatException;
|
||||
use Symfony\Component\Dotenv\Exception\FormatExceptionContext;
|
||||
use Symfony\Component\Dotenv\Exception\PathException;
|
||||
use Symfony\Component\Process\Exception\ExceptionInterface as ProcessException;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
/**
|
||||
* Manages .env files.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
final class Dotenv
|
||||
{
|
||||
const VARNAME_REGEX = '(?i:[A-Z][A-Z0-9_]*+)';
|
||||
const STATE_VARNAME = 0;
|
||||
const STATE_VALUE = 1;
|
||||
|
||||
private $path;
|
||||
private $cursor;
|
||||
private $lineno;
|
||||
private $data;
|
||||
private $end;
|
||||
private $state;
|
||||
private $values;
|
||||
|
||||
/**
|
||||
* Loads one or several .env files.
|
||||
*
|
||||
* @param ...string A list of files to load
|
||||
*
|
||||
* @throws FormatException when a file has a syntax error
|
||||
* @throws PathException when a file does not exist or is not readable
|
||||
*/
|
||||
public function load(/*...$paths*/)
|
||||
{
|
||||
// func_get_args() to be replaced by a variadic argument for Symfony 4.0
|
||||
foreach (func_get_args() as $path) {
|
||||
if (!is_readable($path)) {
|
||||
throw new PathException($path);
|
||||
}
|
||||
|
||||
$this->populate($this->parse(file_get_contents($path), $path));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets values as environment variables (via putenv, $_ENV, and $_SERVER).
|
||||
*
|
||||
* Note that existing environment variables are never overridden.
|
||||
*
|
||||
* @param array An array of env variables
|
||||
*/
|
||||
public function populate($values)
|
||||
{
|
||||
foreach ($values as $name => $value) {
|
||||
if (isset($_ENV[$name]) || false !== getenv($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
putenv("$name=$value");
|
||||
$_ENV[$name] = $value;
|
||||
$_SERVER[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the contents of an .env file.
|
||||
*
|
||||
* @param string $data The data to be parsed
|
||||
* @param string $path The original file name where data where stored (used for more meaningful error messages)
|
||||
*
|
||||
* @return array An array of env variables
|
||||
*
|
||||
* @throws FormatException when a file has a syntax error
|
||||
*/
|
||||
public function parse($data, $path = '.env')
|
||||
{
|
||||
$this->path = $path;
|
||||
$this->data = str_replace(array("\r\n", "\r"), "\n", $data);
|
||||
$this->lineno = 1;
|
||||
$this->cursor = 0;
|
||||
$this->end = strlen($this->data);
|
||||
$this->state = self::STATE_VARNAME;
|
||||
$this->values = array();
|
||||
$name = $value = '';
|
||||
|
||||
$this->skipEmptyLines();
|
||||
|
||||
while ($this->cursor < $this->end) {
|
||||
switch ($this->state) {
|
||||
case self::STATE_VARNAME:
|
||||
$name = $this->lexVarname();
|
||||
$this->state = self::STATE_VALUE;
|
||||
break;
|
||||
|
||||
case self::STATE_VALUE:
|
||||
$this->values[$name] = $this->lexValue();
|
||||
$this->state = self::STATE_VARNAME;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (self::STATE_VALUE === $this->state) {
|
||||
$this->values[$name] = '';
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->values;
|
||||
} finally {
|
||||
$this->values = array();
|
||||
}
|
||||
}
|
||||
|
||||
private function lexVarname()
|
||||
{
|
||||
// var name + optional export
|
||||
if (!preg_match('/(export[ \t]++)?('.self::VARNAME_REGEX.')/A', $this->data, $matches, 0, $this->cursor)) {
|
||||
throw $this->createFormatException('Invalid character in variable name');
|
||||
}
|
||||
$this->moveCursor($matches[0]);
|
||||
|
||||
if ($this->cursor === $this->end || "\n" === $this->data[$this->cursor] || '#' === $this->data[$this->cursor]) {
|
||||
if ($matches[1]) {
|
||||
throw $this->createFormatException('Unable to unset an environment variable');
|
||||
}
|
||||
|
||||
throw $this->createFormatException('Missing = in the environment variable declaration');
|
||||
}
|
||||
|
||||
if (' ' === $this->data[$this->cursor] || "\t" === $this->data[$this->cursor]) {
|
||||
throw $this->createFormatException('Whitespace are not supported after the variable name');
|
||||
}
|
||||
|
||||
if ('=' !== $this->data[$this->cursor]) {
|
||||
throw $this->createFormatException('Missing = in the environment variable declaration');
|
||||
}
|
||||
++$this->cursor;
|
||||
|
||||
return $matches[2];
|
||||
}
|
||||
|
||||
private function lexValue()
|
||||
{
|
||||
if (preg_match('/[ \t]*+(?:#.*)?$/Am', $this->data, $matches, null, $this->cursor)) {
|
||||
$this->moveCursor($matches[0]);
|
||||
$this->skipEmptyLines();
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
if (' ' === $this->data[$this->cursor] || "\t" === $this->data[$this->cursor]) {
|
||||
throw $this->createFormatException('Whitespace are not supported before the value');
|
||||
}
|
||||
|
||||
$value = '';
|
||||
$singleQuoted = false;
|
||||
$notQuoted = false;
|
||||
if ("'" === $this->data[$this->cursor]) {
|
||||
$singleQuoted = true;
|
||||
++$this->cursor;
|
||||
while ("\n" !== $this->data[$this->cursor]) {
|
||||
if ("'" === $this->data[$this->cursor]) {
|
||||
if ($this->cursor + 1 === $this->end) {
|
||||
break;
|
||||
}
|
||||
if ("'" !== $this->data[$this->cursor + 1]) {
|
||||
break;
|
||||
}
|
||||
|
||||
++$this->cursor;
|
||||
}
|
||||
$value .= $this->data[$this->cursor];
|
||||
++$this->cursor;
|
||||
|
||||
if ($this->cursor === $this->end) {
|
||||
throw $this->createFormatException('Missing quote to end the value');
|
||||
}
|
||||
}
|
||||
if ("\n" === $this->data[$this->cursor]) {
|
||||
throw $this->createFormatException('Missing quote to end the value');
|
||||
}
|
||||
++$this->cursor;
|
||||
} elseif ('"' === $this->data[$this->cursor]) {
|
||||
++$this->cursor;
|
||||
while ('"' !== $this->data[$this->cursor] || ('\\' === $this->data[$this->cursor - 1] && '\\' !== $this->data[$this->cursor - 2])) {
|
||||
$value .= $this->data[$this->cursor];
|
||||
++$this->cursor;
|
||||
|
||||
if ($this->cursor === $this->end) {
|
||||
throw $this->createFormatException('Missing quote to end the value');
|
||||
}
|
||||
}
|
||||
if ("\n" === $this->data[$this->cursor]) {
|
||||
throw $this->createFormatException('Missing quote to end the value');
|
||||
}
|
||||
++$this->cursor;
|
||||
$value = str_replace(array('\\\\', '\\"', '\r', '\n'), array('\\', '"', "\r", "\n"), $value);
|
||||
} else {
|
||||
$notQuoted = true;
|
||||
$prevChr = $this->data[$this->cursor - 1];
|
||||
while ($this->cursor < $this->end && "\n" !== $this->data[$this->cursor] && !((' ' === $prevChr || "\t" === $prevChr) && '#' === $this->data[$this->cursor])) {
|
||||
$value .= $prevChr = $this->data[$this->cursor];
|
||||
++$this->cursor;
|
||||
}
|
||||
$value = rtrim($value);
|
||||
}
|
||||
|
||||
$this->skipEmptyLines();
|
||||
|
||||
$currentValue = $value;
|
||||
if (!$singleQuoted) {
|
||||
$value = $this->resolveVariables($value);
|
||||
$value = $this->resolveCommands($value);
|
||||
}
|
||||
|
||||
if ($notQuoted && $currentValue == $value && preg_match('/\s+/', $value)) {
|
||||
throw $this->createFormatException('A value containing spaces must be surrounded by quotes');
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
private function skipWhitespace()
|
||||
{
|
||||
$this->cursor += strspn($this->data, " \t", $this->cursor);
|
||||
}
|
||||
|
||||
private function skipEmptyLines()
|
||||
{
|
||||
if (preg_match('/(?:\s*+(?:#[^\n]*+)?+)++/A', $this->data, $match, null, $this->cursor)) {
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private function resolveCommands($value)
|
||||
{
|
||||
if (false === strpos($value, '$')) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$regex = '/
|
||||
(\\\\)? # escaped with a backslash?
|
||||
\$
|
||||
(?<cmd>
|
||||
\( # require opening parenthesis
|
||||
([^()]|\g<cmd>)+ # allow any number of non-parens, or balanced parens (by nesting the <cmd> expression recursively)
|
||||
\) # require closing paren
|
||||
)
|
||||
/x';
|
||||
|
||||
return preg_replace_callback($regex, function ($matches) {
|
||||
if ('\\' === $matches[1]) {
|
||||
return substr($matches[0], 1);
|
||||
}
|
||||
|
||||
if ('\\' === DIRECTORY_SEPARATOR) {
|
||||
throw new \LogicException('Resolving commands is not supported on Windows.');
|
||||
}
|
||||
|
||||
if (!class_exists(Process::class)) {
|
||||
throw new \LogicException('Resolving commands requires the Symfony Process component.');
|
||||
}
|
||||
|
||||
$process = new Process('echo '.$matches[0]);
|
||||
$process->inheritEnvironmentVariables(true);
|
||||
$process->setEnv($this->values);
|
||||
try {
|
||||
$process->mustRun();
|
||||
} catch (ProcessException $e) {
|
||||
throw $this->createFormatException(sprintf('Issue expanding a command (%s)', $process->getErrorOutput()));
|
||||
}
|
||||
|
||||
return preg_replace('/[\r\n]+$/', '', $process->getOutput());
|
||||
}, $value);
|
||||
}
|
||||
|
||||
private function resolveVariables($value)
|
||||
{
|
||||
if (false === strpos($value, '$')) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$regex = '/
|
||||
(\\\\)? # escaped with a backslash?
|
||||
\$
|
||||
(?!\() # no opening parenthesis
|
||||
(\{)? # optional brace
|
||||
('.self::VARNAME_REGEX.') # var name
|
||||
(\})? # optional closing brace
|
||||
/x';
|
||||
|
||||
$value = preg_replace_callback($regex, function ($matches) {
|
||||
if ('\\' === $matches[1]) {
|
||||
return substr($matches[0], 1);
|
||||
}
|
||||
|
||||
if ('{' === $matches[2] && !isset($matches[4])) {
|
||||
throw $this->createFormatException('Unclosed braces on variable expansion');
|
||||
}
|
||||
|
||||
$name = $matches[3];
|
||||
$value = isset($this->values[$name]) ? $this->values[$name] : (isset($_ENV[$name]) ? isset($_ENV[$name]) : (string) getenv($name));
|
||||
|
||||
if (!$matches[2] && isset($matches[4])) {
|
||||
$value .= '}';
|
||||
}
|
||||
|
||||
return $value;
|
||||
}, $value);
|
||||
|
||||
// unescape $
|
||||
return str_replace('\\$', '$', $value);
|
||||
}
|
||||
|
||||
private function moveCursor($text)
|
||||
{
|
||||
$this->cursor += strlen($text);
|
||||
$this->lineno += substr_count($text, "\n");
|
||||
}
|
||||
|
||||
private function createFormatException($message)
|
||||
{
|
||||
return new FormatException($message, new FormatExceptionContext($this->data, $this->path, $this->lineno, $this->cursor));
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Dotenv\Exception;
|
||||
|
||||
/**
|
||||
* Interface for exceptions.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface ExceptionInterface
|
||||
{
|
||||
}
|
34
src/Symfony/Component/Dotenv/Exception/FormatException.php
Normal file
34
src/Symfony/Component/Dotenv/Exception/FormatException.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Dotenv\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when a file has a syntax error.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
final class FormatException extends \LogicException implements ExceptionInterface
|
||||
{
|
||||
private $context;
|
||||
|
||||
public function __construct($message, FormatExceptionContext $context, $code = 0, \Exception $previous = null)
|
||||
{
|
||||
$this->context = $context;
|
||||
|
||||
parent::__construct(sprintf("%s in \"%s\" at line %d.\n%s", $message, $context->getPath(), $context->getLineno(), $context->getDetails()), $code, $previous);
|
||||
}
|
||||
|
||||
public function getContext()
|
||||
{
|
||||
return $this->context;
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Dotenv\Exception;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
final class FormatExceptionContext
|
||||
{
|
||||
private $data;
|
||||
private $path;
|
||||
private $lineno;
|
||||
private $cursor;
|
||||
|
||||
public function __construct($data, $path, $lineno, $cursor)
|
||||
{
|
||||
$this->data = $data;
|
||||
$this->path = $path;
|
||||
$this->lineno = $lineno;
|
||||
$this->cursor = $cursor;
|
||||
}
|
||||
|
||||
public function getPath()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
public function getLineno()
|
||||
{
|
||||
return $this->lineno;
|
||||
}
|
||||
|
||||
public function getDetails()
|
||||
{
|
||||
$before = str_replace("\n", '\n', substr($this->data, max(0, $this->cursor - 20), min(20, $this->cursor)));
|
||||
$after = str_replace("\n", '\n', substr($this->data, $this->cursor, 20));
|
||||
|
||||
return '...'.$before.$after."...\n".str_repeat(' ', strlen($before) + 2).'^ line '.$this->lineno.' offset '.$this->cursor;
|
||||
}
|
||||
}
|
25
src/Symfony/Component/Dotenv/Exception/PathException.php
Normal file
25
src/Symfony/Component/Dotenv/Exception/PathException.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Dotenv\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when a file does not exist or is not readable.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
final class PathException extends \RuntimeException implements ExceptionInterface
|
||||
{
|
||||
public function __construct($path, $code = 0, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct(sprintf('Unable to read the "%s" environment file.', $path), $code, $previous);
|
||||
}
|
||||
}
|
19
src/Symfony/Component/Dotenv/LICENSE
Normal file
19
src/Symfony/Component/Dotenv/LICENSE
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2016-2017 Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
14
src/Symfony/Component/Dotenv/README.md
Normal file
14
src/Symfony/Component/Dotenv/README.md
Normal file
@ -0,0 +1,14 @@
|
||||
Dotenv Component
|
||||
================
|
||||
|
||||
Symfony Dotenv parses `.env` files to make environment variables stored in them
|
||||
accessible via `getenv()`, `$_ENV`, or `$_SERVER`.
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
* [Documentation](https://symfony.com/doc/current/components/dotenv/index.html)
|
||||
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
|
||||
* [Report issues](https://github.com/symfony/symfony/issues) and
|
||||
[send Pull Requests](https://github.com/symfony/symfony/pulls)
|
||||
in the [main Symfony repository](https://github.com/symfony/symfony)
|
147
src/Symfony/Component/Dotenv/Tests/DotenvTest.php
Normal file
147
src/Symfony/Component/Dotenv/Tests/DotenvTest.php
Normal file
@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Dotenv\Tests;
|
||||
|
||||
use Symfony\Component\Dotenv\Dotenv;
|
||||
use Symfony\Component\Dotenv\Exception\FormatException;
|
||||
|
||||
class DotenvTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider getEnvDataWithFormatErrors
|
||||
*/
|
||||
public function testParseWithFormatError($data, $error)
|
||||
{
|
||||
$dotenv = new Dotenv();
|
||||
|
||||
try {
|
||||
$dotenv->parse($data);
|
||||
$this->fail('Should throw a FormatException');
|
||||
} catch (FormatException $e) {
|
||||
$this->assertStringMatchesFormat($error, $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function getEnvDataWithFormatErrors()
|
||||
{
|
||||
$tests = array(
|
||||
array('FOO=BAR BAZ', "A value containing spaces must be surrounded by quotes in \".env\" at line 1.\n...FOO=BAR BAZ...\n ^ line 1 offset 11"),
|
||||
array('FOO BAR=BAR', "Whitespace are not supported after the variable name in \".env\" at line 1.\n...FOO BAR=BAR...\n ^ line 1 offset 3"),
|
||||
array('FOO', "Missing = in the environment variable declaration in \".env\" at line 1.\n...FOO...\n ^ line 1 offset 3"),
|
||||
array('FOO="foo', "Missing quote to end the value in \".env\" at line 1.\n...FOO=\"foo...\n ^ line 1 offset 8"),
|
||||
array('FOO=\'foo', "Missing quote to end the value in \".env\" at line 1.\n...FOO='foo...\n ^ line 1 offset 8"),
|
||||
array('export FOO', "Unable to unset an environment variable in \".env\" at line 1.\n...export FOO...\n ^ line 1 offset 10"),
|
||||
array('FOO=${FOO', "Unclosed braces on variable expansion in \".env\" at line 1.\n...FOO=\${FOO...\n ^ line 1 offset 9"),
|
||||
);
|
||||
|
||||
if ('\\' !== DIRECTORY_SEPARATOR) {
|
||||
$tests[] = array('FOO=$((1dd2))', "Issue expanding a command (%s\n) in \".env\" at line 1.\n...FOO=$((1dd2))...\n ^ line 1 offset 13");
|
||||
}
|
||||
|
||||
return $tests;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getEnvData
|
||||
*/
|
||||
public function testParse($data, $expected)
|
||||
{
|
||||
$dotenv = new Dotenv();
|
||||
$this->assertSame($expected, $dotenv->parse($data));
|
||||
}
|
||||
|
||||
public function getEnvData()
|
||||
{
|
||||
putenv('LOCAL=local');
|
||||
|
||||
$tests = array(
|
||||
// spaces
|
||||
array('FOO=bar', array('FOO' => 'bar')),
|
||||
array(' FOO=bar ', array('FOO' => 'bar')),
|
||||
array('FOO=', array('FOO' => '')),
|
||||
array("FOO=\n\n\nBAR=bar", array('FOO' => '', 'BAR' => 'bar')),
|
||||
array('FOO= ', array('FOO' => '')),
|
||||
array("FOO=\nBAR=bar", array('FOO' => '', 'BAR' => 'bar')),
|
||||
|
||||
// newlines
|
||||
array("\n\nFOO=bar\r\n\n", array('FOO' => 'bar')),
|
||||
array("FOO=bar\r\nBAR=foo", array('FOO' => 'bar', 'BAR' => 'foo')),
|
||||
array("FOO=bar\rBAR=foo", array('FOO' => 'bar', 'BAR' => 'foo')),
|
||||
array("FOO=bar\nBAR=foo", array('FOO' => 'bar', 'BAR' => 'foo')),
|
||||
|
||||
// quotes
|
||||
array("FOO=\"bar\"\n", array('FOO' => 'bar')),
|
||||
array("FOO=\"bar'foo\"\n", array('FOO' => 'bar\'foo')),
|
||||
array("FOO='bar'\n", array('FOO' => 'bar')),
|
||||
array("FOO='bar\"foo'\n", array('FOO' => 'bar"foo')),
|
||||
array("FOO=\"bar\\\"foo\"\n", array('FOO' => 'bar"foo')),
|
||||
array("FOO='bar''foo'\n", array('FOO' => 'bar\'foo')),
|
||||
array('FOO="bar\nfoo"', array('FOO' => "bar\nfoo")),
|
||||
array('FOO="bar\rfoo"', array('FOO' => "bar\rfoo")),
|
||||
array('FOO=\'bar\nfoo\'', array('FOO' => 'bar\nfoo')),
|
||||
array('FOO=\'bar\rfoo\'', array('FOO' => 'bar\rfoo')),
|
||||
array('FOO=" FOO "', array('FOO' => ' FOO ')),
|
||||
array('FOO=" "', array('FOO' => ' ')),
|
||||
array('PATH="c:\\\\"', array('PATH' => 'c:\\')),
|
||||
array("FOO=\"bar\nfoo\"", array('FOO' => "bar\nfoo")),
|
||||
|
||||
// concatenated values
|
||||
|
||||
// comments
|
||||
array("#FOO=bar\nBAR=foo", array('BAR' => 'foo')),
|
||||
array("#FOO=bar # Comment\nBAR=foo", array('BAR' => 'foo')),
|
||||
array("FOO='bar foo' # Comment", array('FOO' => 'bar foo')),
|
||||
array("FOO='bar#foo' # Comment", array('FOO' => 'bar#foo')),
|
||||
array("# Comment\r\nFOO=bar\n# Comment\nBAR=foo", array('FOO' => 'bar', 'BAR' => 'foo')),
|
||||
array("FOO=bar # Another comment\nBAR=foo", array('FOO' => 'bar', 'BAR' => 'foo')),
|
||||
array("FOO=\n\n# comment\nBAR=bar", array('FOO' => '', 'BAR' => 'bar')),
|
||||
array('FOO=NOT#COMMENT', array('FOO' => 'NOT#COMMENT')),
|
||||
array('FOO= # Comment', array('FOO' => '')),
|
||||
|
||||
// edge cases (no conversions, only strings as values)
|
||||
array('FOO=0', array('FOO' => '0')),
|
||||
array('FOO=false', array('FOO' => 'false')),
|
||||
array('FOO=null', array('FOO' => 'null')),
|
||||
|
||||
// export
|
||||
array('export FOO=bar', array('FOO' => 'bar')),
|
||||
array(' export FOO=bar', array('FOO' => 'bar')),
|
||||
|
||||
// variable expansion
|
||||
array("FOO=BAR\nBAR=\$FOO", array('FOO' => 'BAR', 'BAR' => 'BAR')),
|
||||
array("FOO=BAR\nBAR=\"\$FOO\"", array('FOO' => 'BAR', 'BAR' => 'BAR')),
|
||||
array("FOO=BAR\nBAR='\$FOO'", array('FOO' => 'BAR', 'BAR' => '$FOO')),
|
||||
array("FOO_BAR9=BAR\nBAR=\$FOO_BAR9", array('FOO_BAR9' => 'BAR', 'BAR' => 'BAR')),
|
||||
array("FOO=BAR\nBAR=\${FOO}Z", array('FOO' => 'BAR', 'BAR' => 'BARZ')),
|
||||
array("FOO=BAR\nBAR=\$FOO}", array('FOO' => 'BAR', 'BAR' => 'BAR}')),
|
||||
array("FOO=BAR\nBAR=\\\$FOO", array('FOO' => 'BAR', 'BAR' => '$FOO')),
|
||||
array('FOO=" \\$ "', array('FOO' => ' $ ')),
|
||||
array('FOO=" $ "', array('FOO' => ' $ ')),
|
||||
array('BAR=$LOCAL', array('BAR' => 'local')),
|
||||
array('FOO=$NOTDEFINED', array('FOO' => '')),
|
||||
);
|
||||
|
||||
if ('\\' !== DIRECTORY_SEPARATOR) {
|
||||
$tests = array_merge($tests, array(
|
||||
// command expansion
|
||||
array('FOO=$(echo foo)', array('FOO' => 'foo')),
|
||||
array('FOO=$((1+2))', array('FOO' => '3')),
|
||||
array('FOO=FOO$((1+2))BAR', array('FOO' => 'FOO3BAR')),
|
||||
array('FOO=$(echo "$(echo "$(echo "$(echo foo)")")")', array('FOO' => 'foo')),
|
||||
array("FOO=$(echo \"Quotes won't be a problem\")", array('FOO' => 'Quotes won\'t be a problem')),
|
||||
array("FOO=bar\nBAR=$(echo \"FOO is \$FOO\")", array('FOO' => 'bar', 'BAR' => 'FOO is bar')),
|
||||
));
|
||||
}
|
||||
|
||||
return $tests;
|
||||
}
|
||||
}
|
36
src/Symfony/Component/Dotenv/composer.json
Normal file
36
src/Symfony/Component/Dotenv/composer.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "symfony/dotenv",
|
||||
"type": "library",
|
||||
"description": "Registers environment variables from a .env file",
|
||||
"keywords": ["environment", "env", "dotenv"],
|
||||
"homepage": "https://symfony.com",
|
||||
"license" : "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.5.9"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/process": "^3.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Component\\Dotenv\\": "" },
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.3-dev"
|
||||
}
|
||||
}
|
||||
}
|
28
src/Symfony/Component/Dotenv/phpunit.xml.dist
Normal file
28
src/Symfony/Component/Dotenv/phpunit.xml.dist
Normal file
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
|
||||
backupGlobals="false"
|
||||
colors="true"
|
||||
bootstrap="vendor/autoload.php"
|
||||
>
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Symfony Dotenv Component Test Suite">
|
||||
<directory>./Tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory>./</directory>
|
||||
<exclude>
|
||||
<directory>./Tests</directory>
|
||||
<directory>./vendor</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
Reference in New Issue
Block a user