updated backend

This commit is contained in:
Torsten 2020-01-17 10:58:01 +01:00
parent 1fea0f4fe7
commit 11521c2339
3459 changed files with 72735 additions and 168535 deletions

View file

@ -1,32 +0,0 @@
The BSD 3-Clause License
http://opensource.org/licenses/BSD-3-Clause
Copyright (c) 2013, Vance Lucas
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Vance Lucas nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -4,17 +4,24 @@
"keywords": ["env", "dotenv", "environment"],
"license" : "BSD-3-Clause",
"authors" : [
{
"name": "Graham Campbell",
"email": "graham@alt-three.com",
"homepage": "https://gjcampbell.co.uk/"
},
{
"name": "Vance Lucas",
"email": "vance@vancelucas.com",
"homepage": "http://www.vancelucas.com"
"homepage": "https://vancelucas.com/"
}
],
"require": {
"php": ">=5.3.9"
"php": "^5.4 || ^7.0",
"phpoption/phpoption": "^1.5",
"symfony/polyfill-ctype": "^1.9"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5.0"
"phpunit/phpunit": "^4.8.35 || ^5.0 || ^6.0 || ^7.0"
},
"autoload": {
"psr-4": {
@ -23,7 +30,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
"dev-master": "3.6-dev"
}
}
}

View file

@ -2,48 +2,78 @@
namespace Dotenv;
use Dotenv\Environment\DotenvFactory;
use Dotenv\Environment\FactoryInterface;
use Dotenv\Exception\InvalidPathException;
/**
* This is the dotenv class.
*
* It's responsible for loading a `.env` file in the given directory and
* setting the environment vars.
* setting the environment variables.
*/
class Dotenv
{
/**
* The file path.
*
* @var string
*/
protected $filePath;
/**
* The loader instance.
*
* @var \Dotenv\Loader|null
* @var \Dotenv\Loader
*/
protected $loader;
/**
* Create a new dotenv instance.
*
* @param string $path
* @param string $file
* @param \Dotenv\Loader $loader
*
* @return void
*/
public function __construct($path, $file = '.env')
public function __construct(Loader $loader)
{
$this->filePath = $this->getFilePath($path, $file);
$this->loader = new Loader($this->filePath, true);
$this->loader = $loader;
}
/**
* Create a new dotenv instance.
*
* @param string|string[] $paths
* @param string|null $file
* @param \Dotenv\Environment\FactoryInterface|null $envFactory
*
* @return \Dotenv\Dotenv
*/
public static function create($paths, $file = null, FactoryInterface $envFactory = null)
{
$loader = new Loader(
self::getFilePaths((array) $paths, $file ?: '.env'),
$envFactory ?: new DotenvFactory(),
true
);
return new self($loader);
}
/**
* Returns the full paths to the files.
*
* @param string[] $paths
* @param string $file
*
* @return string[]
*/
private static function getFilePaths(array $paths, $file)
{
return array_map(function ($path) use ($file) {
return rtrim($path, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$file;
}, $paths);
}
/**
* Load environment file in given directory.
*
* @return array
* @throws \Dotenv\Exception\InvalidPathException|\Dotenv\Exception\InvalidFileException
*
* @return array<string|null>
*/
public function load()
{
@ -51,9 +81,11 @@ class Dotenv
}
/**
* Load environment file in given directory, suppress InvalidPathException.
* Load environment file in given directory, silently failing if it doesn't exist.
*
* @return array
* @throws \Dotenv\Exception\InvalidFileException
*
* @return array<string|null>
*/
public function safeLoad()
{
@ -61,45 +93,30 @@ class Dotenv
return $this->loadData();
} catch (InvalidPathException $e) {
// suppressing exception
return array();
return [];
}
}
/**
* Load environment file in given directory.
*
* @return array
* @throws \Dotenv\Exception\InvalidPathException|\Dotenv\Exception\InvalidFileException
*
* @return array<string|null>
*/
public function overload()
{
return $this->loadData(true);
}
/**
* Returns the full path to the file.
*
* @param string $path
* @param string $file
*
* @return string
*/
protected function getFilePath($path, $file)
{
if (!is_string($file)) {
$file = '.env';
}
$filePath = rtrim($path, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$file;
return $filePath;
}
/**
* Actually load the data.
*
* @param bool $overload
*
* @return array
* @throws \Dotenv\Exception\InvalidPathException|\Dotenv\Exception\InvalidFileException
*
* @return array<string|null>
*/
protected function loadData($overload = false)
{
@ -109,22 +126,34 @@ class Dotenv
/**
* Required ensures that the specified variables exist, and returns a new validator object.
*
* @param string|string[] $variable
* @param string|string[] $variables
*
* @return \Dotenv\Validator
*/
public function required($variable)
public function required($variables)
{
return new Validator((array) $variable, $this->loader);
return new Validator((array) $variables, $this->loader);
}
/**
* Returns a new validator object that won't check if the specified variables exist.
*
* @param string|string[] $variables
*
* @return \Dotenv\Validator
*/
public function ifPresent($variables)
{
return new Validator((array) $variables, $this->loader, false);
}
/**
* Get the list of environment variables declared inside the 'env' file.
*
* @return array
* @return string[]
*/
public function getEnvironmentVariableNames()
{
return $this->loader->variableNames;
return $this->loader->getEnvironmentVariableNames();
}
}

View file

@ -1,13 +0,0 @@
<?php
namespace Dotenv\Exception;
use InvalidArgumentException;
/**
* This is the invalid callback exception class.
*/
class InvalidCallbackException extends InvalidArgumentException implements ExceptionInterface
{
//
}

View file

@ -2,269 +2,176 @@
namespace Dotenv;
use Dotenv\Exception\InvalidFileException;
use Dotenv\Environment\FactoryInterface;
use Dotenv\Exception\InvalidPathException;
use Dotenv\Regex\Regex;
use PhpOption\Option;
/**
* This is the loaded class.
* This is the loader class.
*
* 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
{
/**
* The file path.
* The file paths.
*
* @var string
* @var string[]
*/
protected $filePath;
protected $filePaths;
/**
* Are we immutable?
* The environment factory instance.
*
* @var bool
* @var \Dotenv\Environment\FactoryInterface
*/
protected $immutable;
protected $envFactory;
/**
* The environment variables instance.
*
* @var \Dotenv\Environment\VariablesInterface
*/
protected $envVariables;
/**
* The list of environment variables declared inside the 'env' file.
*
* @var array
* @var string[]
*/
public $variableNames = array();
protected $variableNames = [];
/**
* Create a new loader instance.
*
* @param string $filePath
* @param bool $immutable
* @param string[] $filePaths
* @param \Dotenv\Environment\FactoryInterface $envFactory
* @param bool $immutable
*
* @return void
*/
public function __construct($filePath, $immutable = false)
public function __construct(array $filePaths, FactoryInterface $envFactory, $immutable = false)
{
$this->filePath = $filePath;
$this->immutable = $immutable;
$this->filePaths = $filePaths;
$this->envFactory = $envFactory;
$this->setImmutable($immutable);
}
/**
* Set immutable value.
*
* @param bool $immutable
*
* @return $this
*/
public function setImmutable($immutable = false)
{
$this->immutable = $immutable;
$this->envVariables = $immutable
? $this->envFactory->createImmutable()
: $this->envFactory->create();
return $this;
}
/**
* Get immutable value.
* Load the environment file from disk.
*
* @return bool
*/
public function getImmutable()
{
return $this->immutable;
}
/**
* Load `.env` file in given directory.
* @throws \Dotenv\Exception\InvalidPathException|\Dotenv\Exception\InvalidFileException
*
* @return array
* @return array<string|null>
*/
public function load()
{
$this->ensureFileIsReadable();
$filePath = $this->filePath;
$lines = $this->readLinesFromFile($filePath);
foreach ($lines as $line) {
if (!$this->isComment($line) && $this->looksLikeSetter($line)) {
$this->setEnvironmentVariable($line);
}
}
return $lines;
return $this->loadDirect(
self::findAndRead($this->filePaths)
);
}
/**
* Ensures the given filePath is readable.
* Directly load the given string.
*
* @throws \Dotenv\Exception\InvalidPathException
*
* @return void
*/
protected function ensureFileIsReadable()
{
if (!is_readable($this->filePath) || !is_file($this->filePath)) {
throw new InvalidPathException(sprintf('Unable to read the environment file at %s.', $this->filePath));
}
}
/**
* Normalise the given environment variable.
*
* Takes value as passed in by developer and:
* - ensures we're dealing with a separate name and value, breaking apart the name string if needed,
* - cleaning the value of quotes,
* - cleaning the name of quotes,
* - resolving nested variables.
*
* @param string $name
* @param string $value
*
* @return array
*/
protected function normaliseEnvironmentVariable($name, $value)
{
list($name, $value) = $this->processFilters($name, $value);
$value = $this->resolveNestedVariables($value);
return array($name, $value);
}
/**
* Process the runtime filters.
*
* Called from `normaliseEnvironmentVariable` and the `VariableFactory`, passed as a callback in `$this->loadFromFile()`.
*
* @param string $name
* @param string $value
*
* @return array
*/
public function processFilters($name, $value)
{
list($name, $value) = $this->splitCompoundStringIntoParts($name, $value);
list($name, $value) = $this->sanitiseVariableName($name, $value);
list($name, $value) = $this->sanitiseVariableValue($name, $value);
return array($name, $value);
}
/**
* Read lines from the file, auto detecting line endings.
*
* @param string $filePath
*
* @return array
*/
protected function readLinesFromFile($filePath)
{
// Read file into an array of lines with auto-detected line endings
$autodetect = ini_get('auto_detect_line_endings');
ini_set('auto_detect_line_endings', '1');
$lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
ini_set('auto_detect_line_endings', $autodetect);
return $lines;
}
/**
* Determine if the line in the file is a comment, e.g. begins with a #.
*
* @param string $line
*
* @return bool
*/
protected function isComment($line)
{
$line = ltrim($line);
return isset($line[0]) && $line[0] === '#';
}
/**
* Determine if the given line looks like it's setting a variable.
*
* @param string $line
*
* @return bool
*/
protected function looksLikeSetter($line)
{
return strpos($line, '=') !== false;
}
/**
* Split the compound string into parts.
*
* If the `$name` contains an `=` sign, then we split it into 2 parts, a `name` & `value`
* disregarding the `$value` passed in.
*
* @param string $name
* @param string $value
*
* @return array
*/
protected function splitCompoundStringIntoParts($name, $value)
{
if (strpos($name, '=') !== false) {
list($name, $value) = array_map('trim', explode('=', $name, 2));
}
return array($name, $value);
}
/**
* Strips quotes from the environment variable value.
*
* @param string $name
* @param string $value
* @param string $content
*
* @throws \Dotenv\Exception\InvalidFileException
*
* @return array
* @return array<string|null>
*/
protected function sanitiseVariableValue($name, $value)
public function loadDirect($content)
{
$value = trim($value);
if (!$value) {
return array($name, $value);
return $this->processEntries(
Lines::process(preg_split("/(\r\n|\n|\r)/", $content))
);
}
/**
* Attempt to read the files in order.
*
* @param string[] $filePaths
*
* @throws \Dotenv\Exception\InvalidPathException
*
* @return string[]
*/
private static function findAndRead(array $filePaths)
{
if ($filePaths === []) {
throw new InvalidPathException('At least one environment file path must be provided.');
}
if ($this->beginsWithAQuote($value)) { // value starts with a quote
$quote = $value[0];
$regexPattern = sprintf(
'/^
%1$s # match a quote at the start of the value
( # capturing sub-pattern used
(?: # we do not need to capture this
[^%1$s\\\\]* # any character other than a quote or backslash
|\\\\\\\\ # or two backslashes together
|\\\\%1$s # or an escaped quote e.g \"
)* # as many characters that match the previous rules
) # end of the capturing sub-pattern
%1$s # and the closing quote
.*$ # and discard any string after the closing quote
/mx',
$quote
);
$value = preg_replace($regexPattern, '$1', $value);
$value = str_replace("\\$quote", $quote, $value);
$value = str_replace('\\\\', '\\', $value);
} else {
$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.');
}
foreach ($filePaths as $filePath) {
$lines = self::readFromFile($filePath);
if ($lines->isDefined()) {
return $lines->get();
}
}
return array($name, trim($value));
throw new InvalidPathException(
sprintf('Unable to read any of the environment file(s) at [%s].', implode(', ', $filePaths))
);
}
/**
* Read the given file.
*
* @param string $filePath
*
* @return \PhpOption\Option
*/
private static function readFromFile($filePath)
{
$content = @file_get_contents($filePath);
return Option::fromValue($content, false);
}
/**
* Process the environment variable entries.
*
* We'll fill out any nested variables, and acually set the variable using
* the underlying environment variables instance.
*
* @param string[] $entries
*
* @throws \Dotenv\Exception\InvalidFileException
*
* @return array<string|null>
*/
private function processEntries(array $entries)
{
$vars = [];
foreach ($entries as $entry) {
list($name, $value) = Parser::parse($entry);
$vars[$name] = $this->resolveNestedVariables($value);
$this->setEnvironmentVariable($name, $vars[$name]);
}
return $vars;
}
/**
@ -273,56 +180,27 @@ class Loader
* Look for ${varname} patterns in the variable value and replace with an
* existing environment variable.
*
* @param string $value
* @param string|null $value
*
* @return mixed
* @return string|null
*/
protected function resolveNestedVariables($value)
private function resolveNestedVariables($value = null)
{
if (strpos($value, '$') !== false) {
$loader = $this;
$value = preg_replace_callback(
'/\${([a-zA-Z0-9_.]+)}/',
function ($matchedPatterns) use ($loader) {
$nestedVariable = $loader->getEnvironmentVariable($matchedPatterns[1]);
if ($nestedVariable === null) {
return $matchedPatterns[0];
} else {
return $nestedVariable;
}
},
$value
);
}
return $value;
}
/**
* Strips quotes and the optional leading "export " from the environment variable name.
*
* @param string $name
* @param string $value
*
* @return array
*/
protected function sanitiseVariableName($name, $value)
{
$name = trim(str_replace(array('export ', '\'', '"'), '', $name));
return array($name, $value);
}
/**
* Determine if the given string begins with a quote.
*
* @param string $value
*
* @return bool
*/
protected function beginsWithAQuote($value)
{
return isset($value[0]) && ($value[0] === '"' || $value[0] === '\'');
return Option::fromValue($value)
->filter(function ($str) {
return strpos($str, '$') !== false;
})
->flatMap(function ($str) {
return Regex::replaceCallback(
'/\${([a-zA-Z0-9_.]+)}/',
function (array $matches) {
return Option::fromValue($this->getEnvironmentVariable($matches[1]))
->getOrElse($matches[0]);
},
$str
)->success();
})
->getOrElse($value);
}
/**
@ -334,27 +212,12 @@ class Loader
*/
public function getEnvironmentVariable($name)
{
switch (true) {
case array_key_exists($name, $_ENV):
return $_ENV[$name];
case array_key_exists($name, $_SERVER):
return $_SERVER[$name];
default:
$value = getenv($name);
return $value === false ? null : $value; // switch getenv default to null
}
return $this->envVariables->get($name);
}
/**
* Set an environment variable.
*
* This is done using:
* - putenv,
* - $_ENV,
* - $_SERVER.
*
* The environment variable value is stripped of single and double quotes.
*
* @param string $name
* @param string|null $value
*
@ -362,57 +225,31 @@ class Loader
*/
public function setEnvironmentVariable($name, $value = null)
{
list($name, $value) = $this->normaliseEnvironmentVariable($name, $value);
$this->variableNames[] = $name;
// Don't overwrite existing environment variables if we're immutable
// Ruby's dotenv does this with `ENV[key] ||= value`.
if ($this->immutable && $this->getEnvironmentVariable($name) !== null) {
return;
}
// If PHP is running as an Apache module and an existing
// Apache environment variable exists, overwrite it
if (function_exists('apache_getenv') && function_exists('apache_setenv') && apache_getenv($name)) {
apache_setenv($name, $value);
}
if (function_exists('putenv')) {
putenv("$name=$value");
}
$_ENV[$name] = $value;
$_SERVER[$name] = $value;
$this->envVariables->set($name, $value);
}
/**
* Clear an environment variable.
*
* This is not (currently) used by Dotenv but is provided as a utility
* method for 3rd party code.
*
* This is done using:
* - putenv,
* - unset($_ENV, $_SERVER).
* This method only expects names in normal form.
*
* @param string $name
*
* @see setEnvironmentVariable()
*
* @return void
*/
public function clearEnvironmentVariable($name)
{
// Don't clear anything if we're immutable.
if ($this->immutable) {
return;
}
$this->envVariables->clear($name);
}
if (function_exists('putenv')) {
putenv($name);
}
unset($_ENV[$name], $_SERVER[$name]);
/**
* Get the list of environment variables names.
*
* @return string[]
*/
public function getEnvironmentVariableNames()
{
return $this->variableNames;
}
}

View file

@ -2,8 +2,8 @@
namespace Dotenv;
use Dotenv\Exception\InvalidCallbackException;
use Dotenv\Exception\ValidationException;
use Dotenv\Regex\Regex;
/**
* This is the validator class.
@ -15,7 +15,7 @@ class Validator
/**
* The variables to validate.
*
* @var array
* @var string[]
*/
protected $variables;
@ -29,33 +29,45 @@ class Validator
/**
* Create a new validator instance.
*
* @param array $variables
* @param string[] $variables
* @param \Dotenv\Loader $loader
* @param bool $required
*
* @throws \Dotenv\Exception\ValidationException
*
* @return void
*/
public function __construct(array $variables, Loader $loader)
public function __construct(array $variables, Loader $loader, $required = true)
{
$this->variables = $variables;
$this->loader = $loader;
$this->assertCallback(
function ($value) {
return $value !== null;
},
'is missing'
);
if ($required) {
$this->assertCallback(
function ($value) {
return $value !== null;
},
'is missing'
);
}
}
/**
* Assert that each variable is not empty.
*
* @throws \Dotenv\Exception\ValidationException
*
* @return \Dotenv\Validator
*/
public function notEmpty()
{
return $this->assertCallback(
function ($value) {
if ($value === null) {
return true;
}
return strlen(trim($value)) > 0;
},
'is empty'
@ -65,12 +77,18 @@ class Validator
/**
* Assert that each specified variable is an integer.
*
* @throws \Dotenv\Exception\ValidationException
*
* @return \Dotenv\Validator
*/
public function isInteger()
{
return $this->assertCallback(
function ($value) {
if ($value === null) {
return true;
}
return ctype_digit($value);
},
'is not an integer'
@ -80,17 +98,23 @@ class Validator
/**
* Assert that each specified variable is a boolean.
*
* @throws \Dotenv\Exception\ValidationException
*
* @return \Dotenv\Validator
*/
public function isBoolean()
{
return $this->assertCallback(
function ($value) {
if ($value === null) {
return true;
}
if ($value === '') {
return false;
}
return (filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) !== NULL);
return filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) !== null;
},
'is not a boolean'
);
@ -101,15 +125,41 @@ class Validator
*
* @param string[] $choices
*
* @throws \Dotenv\Exception\ValidationException
*
* @return \Dotenv\Validator
*/
public function allowedValues(array $choices)
{
return $this->assertCallback(
function ($value) use ($choices) {
return in_array($value, $choices);
return in_array($value, $choices, true);
},
'is not an allowed value'
sprintf('is not one of [%s]', implode(', ', $choices))
);
}
/**
* Assert that each variable matches the given regular expression.
*
* @param string $regex
*
* @throws \Dotenv\Exception\ValidationException
*
* @return \Dotenv\Validator
*/
public function allowedRegexValues($regex)
{
return $this->assertCallback(
function ($value) use ($regex)
{
if ($value === null) {
return true;
}
return Regex::match($regex, $value)->success()->getOrElse(0) === 1;
},
sprintf('does not match "%s"' , $regex)
);
}
@ -119,28 +169,24 @@ class Validator
* @param callable $callback
* @param string $message
*
* @throws \Dotenv\Exception\InvalidCallbackException|\Dotenv\Exception\ValidationException
* @throws \Dotenv\Exception\ValidationException
*
* @return \Dotenv\Validator
*/
protected function assertCallback($callback, $message = 'failed callback assertion')
protected function assertCallback(callable $callback, $message = 'failed callback assertion')
{
if (!is_callable($callback)) {
throw new InvalidCallbackException('The provided callback must be callable.');
}
$failing = [];
$variablesFailingAssertion = array();
foreach ($this->variables as $variableName) {
$variableValue = $this->loader->getEnvironmentVariable($variableName);
if (call_user_func($callback, $variableValue) === false) {
$variablesFailingAssertion[] = $variableName." $message";
foreach ($this->variables as $variable) {
if ($callback($this->loader->getEnvironmentVariable($variable)) === false) {
$failing[] = sprintf('%s %s', $variable, $message);
}
}
if (count($variablesFailingAssertion) > 0) {
if (count($failing) > 0) {
throw new ValidationException(sprintf(
'One or more environment variables failed assertions: %s.',
implode(', ', $variablesFailingAssertion)
implode(', ', $failing)
));
}