Primo Committ

This commit is contained in:
paoloar77
2024-05-07 12:17:25 +02:00
commit e73d0e5113
7204 changed files with 884387 additions and 0 deletions

30
vendor/vlucas/phpdotenv/LICENSE vendored Normal file
View File

@@ -0,0 +1,30 @@
BSD 3-Clause License
Copyright (c) 2014, Graham Campbell.
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:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. 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.
3. Neither the name of the copyright holder 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.

51
vendor/vlucas/phpdotenv/composer.json vendored Normal file
View File

@@ -0,0 +1,51 @@
{
"name": "vlucas/phpdotenv",
"description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
"keywords": ["env", "dotenv", "environment"],
"license": "BSD-3-Clause",
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
},
{
"name": "Vance Lucas",
"email": "vance@vancelucas.com",
"homepage": "https://github.com/vlucas"
}
],
"require": {
"php": "^5.5.9 || ^7.0 || ^8.0",
"phpoption/phpoption": "^1.7.3",
"symfony/polyfill-ctype": "^1.17"
},
"require-dev": {
"ext-filter": "*",
"ext-pcre": "*",
"bamarni/composer-bin-plugin": "^1.4.1",
"phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.21"
},
"autoload": {
"psr-4": {
"Dotenv\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Dotenv\\Tests\\": "tests/Dotenv/"
}
},
"suggest": {
"ext-filter": "Required to use the boolean validator.",
"ext-pcre": "Required to use most of the library."
},
"config": {
"preferred-install": "dist"
},
"extra": {
"branch-alias": {
"dev-master": "4.2-dev"
}
}
}

200
vendor/vlucas/phpdotenv/src/Dotenv.php vendored Normal file
View File

@@ -0,0 +1,200 @@
<?php
namespace Dotenv;
use Dotenv\Exception\InvalidPathException;
use Dotenv\Loader\Loader;
use Dotenv\Loader\LoaderInterface;
use Dotenv\Repository\Adapter\ArrayAdapter;
use Dotenv\Repository\RepositoryBuilder;
use Dotenv\Repository\RepositoryInterface;
use Dotenv\Store\FileStore;
use Dotenv\Store\StoreBuilder;
use Dotenv\Store\StringStore;
class Dotenv
{
/**
* The loader instance.
*
* @var \Dotenv\Loader\LoaderInterface
*/
protected $loader;
/**
* The repository instance.
*
* @var \Dotenv\Repository\RepositoryInterface
*/
protected $repository;
/**
* The store instance.
*
* @var \Dotenv\Store\StoreInterface
*/
protected $store;
/**
* Create a new dotenv instance.
*
* @param \Dotenv\Loader\LoaderInterface $loader
* @param \Dotenv\Repository\RepositoryInterface $repository
* @param \Dotenv\Store\StoreInterface|string[] $store
*
* @return void
*/
public function __construct(LoaderInterface $loader, RepositoryInterface $repository, $store)
{
$this->loader = $loader;
$this->repository = $repository;
$this->store = is_array($store) ? new FileStore($store, true) : $store;
}
/**
* Create a new dotenv instance.
*
* @param \Dotenv\Repository\RepositoryInterface $repository
* @param string|string[] $paths
* @param string|string[]|null $names
* @param bool $shortCircuit
*
* @return \Dotenv\Dotenv
*/
public static function create(RepositoryInterface $repository, $paths, $names = null, $shortCircuit = true)
{
$builder = StoreBuilder::create()->withPaths($paths)->withNames($names);
if ($shortCircuit) {
$builder = $builder->shortCircuit();
}
return new self(new Loader(), $repository, $builder->make());
}
/**
* Create a new mutable dotenv instance with default repository.
*
* @param string|string[] $paths
* @param string|string[]|null $names
* @param bool $shortCircuit
*
* @return \Dotenv\Dotenv
*/
public static function createMutable($paths, $names = null, $shortCircuit = true)
{
$repository = RepositoryBuilder::create()->make();
return self::create($repository, $paths, $names, $shortCircuit);
}
/**
* Create a new immutable dotenv instance with default repository.
*
* @param string|string[] $paths
* @param string|string[]|null $names
* @param bool $shortCircuit
*
* @return \Dotenv\Dotenv
*/
public static function createImmutable($paths, $names = null, $shortCircuit = true)
{
$repository = RepositoryBuilder::create()->immutable()->make();
return self::create($repository, $paths, $names, $shortCircuit);
}
/**
* Create a new dotenv instance with an array backed repository.
*
* @param string|string[] $paths
* @param string|string[]|null $names
* @param bool $shortCircuit
*
* @return \Dotenv\Dotenv
*/
public static function createArrayBacked($paths, $names = null, $shortCircuit = true)
{
$adapter = new ArrayAdapter();
$repository = RepositoryBuilder::create()->withReaders([$adapter])->withWriters([$adapter])->make();
return self::create($repository, $paths, $names, $shortCircuit);
}
/**
* Parse the given content and resolve nested variables.
*
* This method behaves just like load(), only without mutating your actual
* environment. We do this by using an array backed repository.
*
* @param string $content
*
* @throws \Dotenv\Exception\InvalidFileException
*
* @return array<string,string|null>
*/
public static function parse($content)
{
$adapter = new ArrayAdapter();
$repository = RepositoryBuilder::create()->withReaders([$adapter])->withWriters([$adapter])->make();
$phpdotenv = new self(new Loader(), $repository, new StringStore($content));
return $phpdotenv->load();
}
/**
* Read and load environment file(s).
*
* @throws \Dotenv\Exception\InvalidPathException|\Dotenv\Exception\InvalidFileException
*
* @return array<string,string|null>
*/
public function load()
{
return $this->loader->load($this->repository, $this->store->read());
}
/**
* Read and load environment file(s), silently failing if no files can be read.
*
* @throws \Dotenv\Exception\InvalidFileException
*
* @return array<string,string|null>
*/
public function safeLoad()
{
try {
return $this->load();
} catch (InvalidPathException $e) {
// suppressing exception
return [];
}
}
/**
* Required ensures that the specified variables exist, and returns a new validator object.
*
* @param string|string[] $variables
*
* @return \Dotenv\Validator
*/
public function required($variables)
{
return new Validator($this->repository, (array) $variables);
}
/**
* 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($this->repository, (array) $variables, false);
}
}

View File

@@ -0,0 +1,8 @@
<?php
namespace Dotenv\Exception;
interface ExceptionInterface
{
//
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Dotenv\Exception;
use InvalidArgumentException;
class InvalidFileException extends InvalidArgumentException implements ExceptionInterface
{
//
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Dotenv\Exception;
use InvalidArgumentException;
class InvalidPathException extends InvalidArgumentException implements ExceptionInterface
{
//
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Dotenv\Exception;
use RuntimeException;
class ValidationException extends RuntimeException implements ExceptionInterface
{
//
}

View File

@@ -0,0 +1,135 @@
<?php
namespace Dotenv\Loader;
class Lines
{
/**
* Process the array of lines of environment variables.
*
* This will produce an array of entries, one per variable.
*
* @param string[] $lines
*
* @return string[]
*/
public static function process(array $lines)
{
$output = [];
$multiline = false;
$multilineBuffer = [];
foreach ($lines as $line) {
list($multiline, $line, $multilineBuffer) = self::multilineProcess($multiline, $line, $multilineBuffer);
if (!$multiline && !self::isCommentOrWhitespace($line)) {
$output[] = $line;
}
}
return $output;
}
/**
* Used to make all multiline variable process.
*
* @param bool $multiline
* @param string $line
* @param string[] $buffer
*
* @return array{bool,string,string[]}
*/
private static function multilineProcess($multiline, $line, array $buffer)
{
// check if $line can be multiline variable
if ($started = self::looksLikeMultilineStart($line)) {
$multiline = true;
}
if ($multiline) {
array_push($buffer, $line);
if (self::looksLikeMultilineStop($line, $started)) {
$multiline = false;
$line = implode("\n", $buffer);
$buffer = [];
}
}
return [$multiline, $line, $buffer];
}
/**
* Determine if the given line can be the start of a multiline variable.
*
* @param string $line
*
* @return bool
*/
private static function looksLikeMultilineStart($line)
{
if (strpos($line, '="') === false) {
return false;
}
return self::looksLikeMultilineStop($line, true) === false;
}
/**
* Determine if the given line can be the start of a multiline variable.
*
* @param string $line
* @param bool $started
*
* @return bool
*/
private static function looksLikeMultilineStop($line, $started)
{
if ($line === '"') {
return true;
}
$seen = $started ? 0 : 1;
foreach (self::getCharPairs(str_replace('\\\\', '', $line)) as $pair) {
if ($pair[0] !== '\\' && $pair[1] === '"') {
$seen++;
}
}
return $seen > 1;
}
/**
* Get all pairs of adjacent characters within the line.
*
* @param string $line
*
* @return array{array{string,string|null}}
*/
private static function getCharPairs($line)
{
$chars = str_split($line);
/** @var array{array{string,string|null}} */
return array_map(null, $chars, array_slice($chars, 1));
}
/**
* Determine if the line in the file is a comment or whitespace.
*
* @param string $line
*
* @return bool
*/
private static function isCommentOrWhitespace($line)
{
if (trim($line) === '') {
return true;
}
$line = ltrim($line);
return isset($line[0]) && $line[0] === '#';
}
}

View File

@@ -0,0 +1,122 @@
<?php
namespace Dotenv\Loader;
use Dotenv\Regex\Regex;
use Dotenv\Repository\RepositoryInterface;
use PhpOption\Option;
class Loader implements LoaderInterface
{
/**
* The variable name whitelist.
*
* @var string[]|null
*/
protected $whitelist;
/**
* Create a new loader instance.
*
* @param string[]|null $whitelist
*
* @return void
*/
public function __construct(array $whitelist = null)
{
$this->whitelist = $whitelist;
}
/**
* Load the given environment file content into the repository.
*
* @param \Dotenv\Repository\RepositoryInterface $repository
* @param string $content
*
* @throws \Dotenv\Exception\InvalidFileException
*
* @return array<string,string|null>
*/
public function load(RepositoryInterface $repository, $content)
{
return $this->processEntries(
$repository,
Lines::process(Regex::split("/(\r\n|\n|\r)/", $content)->getSuccess())
);
}
/**
* Process the environment variable entries.
*
* We'll fill out any nested variables, and acually set the variable using
* the underlying environment variables instance.
*
* @param \Dotenv\Repository\RepositoryInterface $repository
* @param string[] $entries
*
* @throws \Dotenv\Exception\InvalidFileException
*
* @return array<string,string|null>
*/
private function processEntries(RepositoryInterface $repository, array $entries)
{
$vars = [];
foreach ($entries as $entry) {
list($name, $value) = Parser::parse($entry);
if ($this->whitelist === null || in_array($name, $this->whitelist, true)) {
$vars[$name] = self::resolveNestedVariables($repository, $value);
$repository->set($name, $vars[$name]);
}
}
return $vars;
}
/**
* Resolve the nested variables.
*
* Look for ${varname} patterns in the variable value and replace with an
* existing environment variable.
*
* @param \Dotenv\Repository\RepositoryInterface $repository
* @param \Dotenv\Loader\Value|null $value
*
* @return string|null
*/
private static function resolveNestedVariables(RepositoryInterface $repository, Value $value = null)
{
/** @var Option<Value> */
$option = Option::fromValue($value);
return $option
->map(function (Value $v) use ($repository) {
/** @var string */
return array_reduce($v->getVars(), function ($s, $i) use ($repository) {
return substr($s, 0, $i).self::resolveNestedVariable($repository, substr($s, $i));
}, $v->getChars());
})
->getOrElse(null);
}
/**
* Resolve a single nested variable.
*
* @param \Dotenv\Repository\RepositoryInterface $repository
* @param string $str
*
* @return string
*/
private static function resolveNestedVariable(RepositoryInterface $repository, $str)
{
return Regex::replaceCallback(
'/\A\${([a-zA-Z0-9_.]+)}/',
function (array $matches) use ($repository) {
return Option::fromValue($repository->get($matches[1]))
->getOrElse($matches[0]);
},
$str,
1
)->success()->getOrElse($str);
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace Dotenv\Loader;
use Dotenv\Repository\RepositoryInterface;
interface LoaderInterface
{
/**
* Load the given environment file content into the repository.
*
* @param \Dotenv\Repository\RepositoryInterface $repository
* @param string $content
*
* @throws \Dotenv\Exception\InvalidFileException
*
* @return array<string,string|null>
*/
public function load(RepositoryInterface $repository, $content);
}

View File

@@ -0,0 +1,249 @@
<?php
namespace Dotenv\Loader;
use Dotenv\Exception\InvalidFileException;
use Dotenv\Regex\Regex;
use Dotenv\Result\Error;
use Dotenv\Result\Success;
use RuntimeException;
class Parser
{
const INITIAL_STATE = 0;
const UNQUOTED_STATE = 1;
const SINGLE_QUOTED_STATE = 2;
const DOUBLE_QUOTED_STATE = 3;
const ESCAPE_SEQUENCE_STATE = 4;
const WHITESPACE_STATE = 5;
const COMMENT_STATE = 6;
/**
* Parse the given environment variable entry into a name and value.
*
* @param string $entry
*
* @throws \Dotenv\Exception\InvalidFileException
*
* @return array{string,\Dotenv\Loader\Value|null}
*/
public static function parse($entry)
{
list($name, $value) = self::splitStringIntoParts($entry);
return [self::parseName($name), self::parseValue($value)];
}
/**
* Split the compound string into parts.
*
* @param string $line
*
* @throws \Dotenv\Exception\InvalidFileException
*
* @return array{string,string|null}
*/
private static function splitStringIntoParts($line)
{
$name = $line;
$value = null;
if (strpos($line, '=') !== false) {
list($name, $value) = array_map('trim', explode('=', $line, 2));
}
if ($name === '') {
throw new InvalidFileException(
self::getErrorMessage('an unexpected equals', $line)
);
}
return [$name, $value];
}
/**
* Strips quotes and the optional leading "export " from the variable name.
*
* @param string $name
*
* @throws \Dotenv\Exception\InvalidFileException
*
* @return string
*/
private static function parseName($name)
{
$name = trim(str_replace(['export ', '\'', '"'], '', $name));
if (!self::isValidName($name)) {
throw new InvalidFileException(
self::getErrorMessage('an invalid name', $name)
);
}
return $name;
}
/**
* Is the given variable name valid?
*
* @param string $name
*
* @return bool
*/
private static function isValidName($name)
{
return Regex::match('~\A[a-zA-Z0-9_.]+\z~', $name)->success()->getOrElse(0) === 1;
}
/**
* Strips quotes and comments from the environment variable value.
*
* @param string|null $value
*
* @throws \Dotenv\Exception\InvalidFileException
*
* @return \Dotenv\Loader\Value|null
*/
private static function parseValue($value)
{
if ($value === null) {
return null;
}
if (trim($value) === '') {
return Value::blank();
}
$result = array_reduce(str_split($value), function ($data, $char) use ($value) {
return self::processChar($data[1], $char)->mapError(function ($err) use ($value) {
throw new InvalidFileException(
self::getErrorMessage($err, $value)
);
})->mapSuccess(function ($val) use ($data) {
return [$data[0]->append($val[0], $val[1]), $val[2]];
})->getSuccess();
}, [Value::blank(), self::INITIAL_STATE]);
if (in_array($result[1], [self::SINGLE_QUOTED_STATE, self::DOUBLE_QUOTED_STATE, self::ESCAPE_SEQUENCE_STATE], true)) {
throw new InvalidFileException(
self::getErrorMessage('a missing closing quote', $value)
);
}
return $result[0];
}
/**
* Process the given character.
*
* @param int $state
* @param string $char
*
* @return \Dotenv\Result\Result<array{string,bool,int},string>
*/
private static function processChar($state, $char)
{
switch ($state) {
case self::INITIAL_STATE:
if ($char === '\'') {
/** @var \Dotenv\Result\Result<array{string,bool,int},string> */
return Success::create(['', false, self::SINGLE_QUOTED_STATE]);
} elseif ($char === '"') {
/** @var \Dotenv\Result\Result<array{string,bool,int},string> */
return Success::create(['', false, self::DOUBLE_QUOTED_STATE]);
} elseif ($char === '#') {
/** @var \Dotenv\Result\Result<array{string,bool,int},string> */
return Success::create(['', false, self::COMMENT_STATE]);
} elseif ($char === '$') {
/** @var \Dotenv\Result\Result<array{string,bool,int},string> */
return Success::create([$char, true, self::UNQUOTED_STATE]);
} else {
/** @var \Dotenv\Result\Result<array{string,bool,int},string> */
return Success::create([$char, false, self::UNQUOTED_STATE]);
}
case self::UNQUOTED_STATE:
if ($char === '#') {
/** @var \Dotenv\Result\Result<array{string,bool,int},string> */
return Success::create(['', false, self::COMMENT_STATE]);
} elseif (ctype_space($char)) {
/** @var \Dotenv\Result\Result<array{string,bool,int},string> */
return Success::create(['', false, self::WHITESPACE_STATE]);
} elseif ($char === '$') {
/** @var \Dotenv\Result\Result<array{string,bool,int},string> */
return Success::create([$char, true, self::UNQUOTED_STATE]);
} else {
/** @var \Dotenv\Result\Result<array{string,bool,int},string> */
return Success::create([$char, false, self::UNQUOTED_STATE]);
}
case self::SINGLE_QUOTED_STATE:
if ($char === '\'') {
/** @var \Dotenv\Result\Result<array{string,bool,int},string> */
return Success::create(['', false, self::WHITESPACE_STATE]);
} else {
/** @var \Dotenv\Result\Result<array{string,bool,int},string> */
return Success::create([$char, false, self::SINGLE_QUOTED_STATE]);
}
case self::DOUBLE_QUOTED_STATE:
if ($char === '"') {
/** @var \Dotenv\Result\Result<array{string,bool,int},string> */
return Success::create(['', false, self::WHITESPACE_STATE]);
} elseif ($char === '\\') {
/** @var \Dotenv\Result\Result<array{string,bool,int},string> */
return Success::create(['', false, self::ESCAPE_SEQUENCE_STATE]);
} elseif ($char === '$') {
/** @var \Dotenv\Result\Result<array{string,bool,int},string> */
return Success::create([$char, true, self::DOUBLE_QUOTED_STATE]);
} else {
/** @var \Dotenv\Result\Result<array{string,bool,int},string> */
return Success::create([$char, false, self::DOUBLE_QUOTED_STATE]);
}
case self::ESCAPE_SEQUENCE_STATE:
if ($char === '"' || $char === '\\') {
/** @var \Dotenv\Result\Result<array{string,bool,int},string> */
return Success::create([$char, false, self::DOUBLE_QUOTED_STATE]);
} elseif ($char === '$') {
/** @var \Dotenv\Result\Result<array{string,bool,int},string> */
return Success::create([$char, false, self::DOUBLE_QUOTED_STATE]);
} elseif (in_array($char, ['f', 'n', 'r', 't', 'v'], true)) {
/** @var \Dotenv\Result\Result<array{string,bool,int},string> */
return Success::create([stripcslashes('\\'.$char), false, self::DOUBLE_QUOTED_STATE]);
} else {
/** @var \Dotenv\Result\Result<array{string,bool,int},string> */
return Error::create('an unexpected escape sequence');
}
case self::WHITESPACE_STATE:
if ($char === '#') {
/** @var \Dotenv\Result\Result<array{string,bool,int},string> */
return Success::create(['', false, self::COMMENT_STATE]);
} elseif (!ctype_space($char)) {
/** @var \Dotenv\Result\Result<array{string,bool,int},string> */
return Error::create('unexpected whitespace');
} else {
/** @var \Dotenv\Result\Result<array{string,bool,int},string> */
return Success::create(['', false, self::WHITESPACE_STATE]);
}
case self::COMMENT_STATE:
/** @var \Dotenv\Result\Result<array{string,bool,int},string> */
return Success::create(['', false, self::COMMENT_STATE]);
default:
throw new RuntimeException('Parser entered invalid state.');
}
}
/**
* Generate a friendly error message.
*
* @param string $cause
* @param string $subject
*
* @return string
*/
private static function getErrorMessage($cause, $subject)
{
return sprintf(
'Failed to parse dotenv file due to %s. Failed at [%s].',
$cause,
strtok($subject, "\n")
);
}
}

View File

@@ -0,0 +1,83 @@
<?php
namespace Dotenv\Loader;
class Value
{
/**
* The string representation of the parsed value.
*
* @var string
*/
private $chars;
/**
* The locations of the variables in the value.
*
* @var int[]
*/
private $vars;
/**
* Internal constructor for a value.
*
* @param string $chars
* @param int[] $vars
*
* @return void
*/
private function __construct($chars, array $vars)
{
$this->chars = $chars;
$this->vars = $vars;
}
/**
* Create an empty value instance.
*
* @return \Dotenv\Loader\Value
*/
public static function blank()
{
return new self('', []);
}
/**
* Create a new value instance, appending the character.
*
* @param string $char
* @param bool $var
*
* @return \Dotenv\Loader\Value
*/
public function append($char, $var)
{
return new self(
$this->chars.$char,
$var ? array_merge($this->vars, [strlen($this->chars)]) : $this->vars
);
}
/**
* Get the string representation of the parsed value.
*
* @return string
*/
public function getChars()
{
return $this->chars;
}
/**
* Get the locations of the variables in the value.
*
* @return int[]
*/
public function getVars()
{
$vars = $this->vars;
rsort($vars);
return $vars;
}
}

View File

@@ -0,0 +1,125 @@
<?php
namespace Dotenv\Regex;
use Dotenv\Result\Error;
use Dotenv\Result\Result;
use Dotenv\Result\Success;
class Regex
{
/**
* Perform a preg match, wrapping up the result.
*
* @param string $pattern
* @param string $subject
*
* @return \Dotenv\Result\Result<int,string>
*/
public static function match($pattern, $subject)
{
return self::pregAndWrap(function ($subject) use ($pattern) {
return (int) @preg_match($pattern, $subject);
}, $subject);
}
/**
* Perform a preg replace, wrapping up the result.
*
* @param string $pattern
* @param string $replacement
* @param string $subject
* @param int|null $limit
*
* @return \Dotenv\Result\Result<string,string>
*/
public static function replace($pattern, $replacement, $subject, $limit = null)
{
return self::pregAndWrap(function ($subject) use ($pattern, $replacement, $limit) {
return (string) @preg_replace($pattern, $replacement, $subject, $limit === null ? -1 : $limit);
}, $subject);
}
/**
* Perform a preg replace callback, wrapping up the result.
*
* @param string $pattern
* @param callable $callback
* @param string $subject
* @param int|null $limit
*
* @return \Dotenv\Result\Result<string,string>
*/
public static function replaceCallback($pattern, callable $callback, $subject, $limit = null)
{
return self::pregAndWrap(function ($subject) use ($pattern, $callback, $limit) {
return (string) @preg_replace_callback($pattern, $callback, $subject, $limit === null ? -1 : $limit);
}, $subject);
}
/**
* Perform a preg split, wrapping up the result.
*
* @param string $pattern
* @param string $subject
*
* @return \Dotenv\Result\Result<string[],string>
*/
public static function split($pattern, $subject)
{
return self::pregAndWrap(function ($subject) use ($pattern) {
/** @var string[] */
return (array) @preg_split($pattern, $subject);
}, $subject);
}
/**
* Perform a preg operation, wrapping up the result.
*
* @template V
*
* @param callable(string):V $operation
* @param string $subject
*
* @return \Dotenv\Result\Result<V,string>
*/
private static function pregAndWrap(callable $operation, $subject)
{
$result = $operation($subject);
if (($e = preg_last_error()) !== PREG_NO_ERROR) {
return Error::create(self::lookupError($e));
}
return Success::create($result);
}
/**
* Lookup the preg error code.
*
* @param int $code
*
* @return string
*/
private static function lookupError($code)
{
if (defined('PREG_JIT_STACKLIMIT_ERROR') && $code === PREG_JIT_STACKLIMIT_ERROR) {
return 'JIT stack limit exhausted';
}
switch ($code) {
case PREG_INTERNAL_ERROR:
return 'Internal error';
case PREG_BAD_UTF8_ERROR:
return 'Malformed UTF-8 characters, possibly incorrectly encoded';
case PREG_BAD_UTF8_OFFSET_ERROR:
return 'The offset did not correspond to the beginning of a valid UTF-8 code point';
case PREG_BACKTRACK_LIMIT_ERROR:
return 'Backtrack limit exhausted';
case PREG_RECURSION_LIMIT_ERROR:
return 'Recursion limit exhausted';
default:
return 'Unknown error';
}
}
}

View File

@@ -0,0 +1,180 @@
<?php
namespace Dotenv\Repository;
use Dotenv\Repository\Adapter\ArrayAdapter;
use InvalidArgumentException;
use ReturnTypeWillChange;
abstract class AbstractRepository implements RepositoryInterface
{
/**
* Are we immutable?
*
* @var bool
*/
private $immutable;
/**
* The record of loaded variables.
*
* @var \Dotenv\Repository\Adapter\ArrayAdapter
*/
private $loaded;
/**
* Create a new repository instance.
*
* @param bool $immutable
*
* @return void
*/
public function __construct($immutable)
{
$this->immutable = $immutable;
$this->loaded = new ArrayAdapter();
}
/**
* Get an environment variable.
*
* @param string $name
*
* @throws \InvalidArgumentException
*
* @return string|null
*/
public function get($name)
{
if (!is_string($name)) {
throw new InvalidArgumentException('Expected name to be a string.');
}
return $this->getInternal($name);
}
/**
* Get an environment variable.
*
* @param string $name
*
* @return string|null
*/
abstract protected function getInternal($name);
/**
* Set an environment variable.
*
* @param string $name
* @param string|null $value
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function set($name, $value = null)
{
if (!is_string($name)) {
throw new InvalidArgumentException('Expected name to be a string.');
}
// Don't overwrite existing environment variables if we're immutable
// Ruby's dotenv does this with `ENV[key] ||= value`.
if ($this->immutable && $this->get($name) !== null && $this->loaded->get($name)->isEmpty()) {
return;
}
$this->setInternal($name, $value);
$this->loaded->set($name, '');
}
/**
* Set an environment variable.
*
* @param string $name
* @param string|null $value
*
* @return void
*/
abstract protected function setInternal($name, $value = null);
/**
* Clear an environment variable.
*
* @param string $name
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function clear($name)
{
if (!is_string($name)) {
throw new InvalidArgumentException('Expected name to be a string.');
}
// Don't clear anything if we're immutable.
if ($this->immutable) {
return;
}
$this->clearInternal($name);
}
/**
* Clear an environment variable.
*
* @param string $name
*
* @return void
*/
abstract protected function clearInternal($name);
/**
* Tells whether environment variable has been defined.
*
* @param string $name
*
* @return bool
*/
public function has($name)
{
return is_string($name) && $this->get($name) !== null;
}
/**
* {@inheritdoc}
*/
#[ReturnTypeWillChange]
public function offsetExists($offset)
{
return $this->has($offset);
}
/**
* {@inheritdoc}
*/
#[ReturnTypeWillChange]
public function offsetGet($offset)
{
return $this->get($offset);
}
/**
* {@inheritdoc}
*/
#[ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
$this->set($offset, $value);
}
/**
* {@inheritdoc}
*/
#[ReturnTypeWillChange]
public function offsetUnset($offset)
{
$this->clear($offset);
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace Dotenv\Repository\Adapter;
use PhpOption\None;
class ApacheAdapter implements AvailabilityInterface, ReaderInterface, WriterInterface
{
/**
* Determines if the adapter is supported.
*
* This happens if PHP is running as an Apache module.
*
* @return bool
*/
public function isSupported()
{
return function_exists('apache_getenv') && function_exists('apache_setenv');
}
/**
* Get an environment variable, if it exists.
*
* This is intentionally not implemented, since this adapter exists only as
* a means to overwrite existing apache environment variables.
*
* @param string $name
*
* @return \PhpOption\Option<string|null>
*/
public function get($name)
{
return None::create();
}
/**
* Set an environment variable.
*
* Only if an existing apache variable exists do we overwrite it.
*
* @param string $name
* @param string|null $value
*
* @return void
*/
public function set($name, $value = null)
{
if (apache_getenv($name) !== false) {
apache_setenv($name, (string) $value);
}
}
/**
* Clear an environment variable.
*
* @param string $name
*
* @return void
*/
public function clear($name)
{
// Nothing to do here.
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace Dotenv\Repository\Adapter;
use PhpOption\None;
use PhpOption\Some;
class ArrayAdapter implements AvailabilityInterface, ReaderInterface, WriterInterface
{
/**
* The variables and their values.
*
* @var array<string,string|null>
*/
private $variables = [];
/**
* Determines if the adapter is supported.
*
* @return bool
*/
public function isSupported()
{
return true;
}
/**
* Get an environment variable, if it exists.
*
* @param string $name
*
* @return \PhpOption\Option<string|null>
*/
public function get($name)
{
if (array_key_exists($name, $this->variables)) {
return Some::create($this->variables[$name]);
}
return None::create();
}
/**
* Set an environment variable.
*
* @param string $name
* @param string|null $value
*
* @return void
*/
public function set($name, $value = null)
{
$this->variables[$name] = $value;
}
/**
* Clear an environment variable.
*
* @param string $name
*
* @return void
*/
public function clear($name)
{
unset($this->variables[$name]);
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Dotenv\Repository\Adapter;
interface AvailabilityInterface
{
/**
* Determines if the adapter is supported.
*
* @return bool
*/
public function isSupported();
}

View File

@@ -0,0 +1,60 @@
<?php
namespace Dotenv\Repository\Adapter;
use PhpOption\None;
use PhpOption\Some;
class EnvConstAdapter implements AvailabilityInterface, ReaderInterface, WriterInterface
{
/**
* Determines if the adapter is supported.
*
* @return bool
*/
public function isSupported()
{
return true;
}
/**
* Get an environment variable, if it exists.
*
* @param string $name
*
* @return \PhpOption\Option<string|null>
*/
public function get($name)
{
if (array_key_exists($name, $_ENV)) {
return Some::create($_ENV[$name]);
}
return None::create();
}
/**
* Set an environment variable.
*
* @param string $name
* @param string|null $value
*
* @return void
*/
public function set($name, $value = null)
{
$_ENV[$name] = $value;
}
/**
* Clear an environment variable.
*
* @param string $name
*
* @return void
*/
public function clear($name)
{
unset($_ENV[$name]);
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace Dotenv\Repository\Adapter;
use PhpOption\Option;
class PutenvAdapter implements AvailabilityInterface, ReaderInterface, WriterInterface
{
/**
* Determines if the adapter is supported.
*
* @return bool
*/
public function isSupported()
{
return function_exists('getenv') && function_exists('putenv');
}
/**
* Get an environment variable, if it exists.
*
* @param string $name
*
* @return \PhpOption\Option<string|null>
*/
public function get($name)
{
/** @var \PhpOption\Option<string|null> */
return Option::fromValue(getenv($name), false);
}
/**
* Set an environment variable.
*
* @param string $name
* @param string|null $value
*
* @return void
*/
public function set($name, $value = null)
{
putenv("$name=$value");
}
/**
* Clear an environment variable.
*
* @param string $name
*
* @return void
*/
public function clear($name)
{
putenv($name);
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Dotenv\Repository\Adapter;
interface ReaderInterface extends AvailabilityInterface
{
/**
* Get an environment variable, if it exists.
*
* @param string $name
*
* @return \PhpOption\Option<string|null>
*/
public function get($name);
}

View File

@@ -0,0 +1,60 @@
<?php
namespace Dotenv\Repository\Adapter;
use PhpOption\None;
use PhpOption\Some;
class ServerConstAdapter implements AvailabilityInterface, ReaderInterface, WriterInterface
{
/**
* Determines if the adapter is supported.
*
* @return bool
*/
public function isSupported()
{
return true;
}
/**
* Get an environment variable, if it exists.
*
* @param string $name
*
* @return \PhpOption\Option<string|null>
*/
public function get($name)
{
if (array_key_exists($name, $_SERVER)) {
return Some::create($_SERVER[$name]);
}
return None::create();
}
/**
* Set an environment variable.
*
* @param string $name
* @param string|null $value
*
* @return void
*/
public function set($name, $value = null)
{
$_SERVER[$name] = $value;
}
/**
* Clear an environment variable.
*
* @param string $name
*
* @return void
*/
public function clear($name)
{
unset($_SERVER[$name]);
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Dotenv\Repository\Adapter;
interface WriterInterface extends AvailabilityInterface
{
/**
* Set an environment variable.
*
* @param string $name
* @param string|null $value
*
* @return void
*/
public function set($name, $value = null);
/**
* Clear an environment variable.
*
* @param string $name
*
* @return void
*/
public function clear($name);
}

View File

@@ -0,0 +1,86 @@
<?php
namespace Dotenv\Repository;
class AdapterRepository extends AbstractRepository
{
/**
* The set of readers to use.
*
* @var \Dotenv\Repository\Adapter\ReaderInterface[]
*/
protected $readers;
/**
* The set of writers to use.
*
* @var \Dotenv\Repository\Adapter\WriterInterface[]
*/
protected $writers;
/**
* Create a new adapter repository instance.
*
* @param \Dotenv\Repository\Adapter\ReaderInterface[] $readers
* @param \Dotenv\Repository\Adapter\WriterInterface[] $writers
* @param bool $immutable
*
* @return void
*/
public function __construct(array $readers, array $writers, $immutable)
{
$this->readers = $readers;
$this->writers = $writers;
parent::__construct($immutable);
}
/**
* Get an environment variable.
*
* We do this by querying our readers sequentially.
*
* @param string $name
*
* @return string|null
*/
protected function getInternal($name)
{
foreach ($this->readers as $reader) {
$result = $reader->get($name);
if ($result->isDefined()) {
return $result->get();
}
}
return null;
}
/**
* Set an environment variable.
*
* @param string $name
* @param string|null $value
*
* @return void
*/
protected function setInternal($name, $value = null)
{
foreach ($this->writers as $writers) {
$writers->set($name, $value);
}
}
/**
* Clear an environment variable.
*
* @param string $name
*
* @return void
*/
protected function clearInternal($name)
{
foreach ($this->writers as $writers) {
$writers->clear($name);
}
}
}

View File

@@ -0,0 +1,144 @@
<?php
namespace Dotenv\Repository;
use Dotenv\Repository\Adapter\ApacheAdapter;
use Dotenv\Repository\Adapter\AvailabilityInterface;
use Dotenv\Repository\Adapter\EnvConstAdapter;
use Dotenv\Repository\Adapter\PutenvAdapter;
use Dotenv\Repository\Adapter\ServerConstAdapter;
class RepositoryBuilder
{
/**
* The set of readers to use.
*
* @var \Dotenv\Repository\Adapter\ReaderInterface[]|null
*/
private $readers;
/**
* The set of writers to use.
*
* @var \Dotenv\Repository\Adapter\WriterInterface[]|null
*/
private $writers;
/**
* Are we immutable?
*
* @var bool
*/
private $immutable;
/**
* Create a new repository builder instance.
*
* @param \Dotenv\Repository\Adapter\ReaderInterface[]|null $readers
* @param \Dotenv\Repository\Adapter\WriterInterface[]|null $writers
* @param bool $immutable
*
* @return void
*/
private function __construct(array $readers = null, array $writers = null, $immutable = false)
{
$this->readers = $readers;
$this->writers = $writers;
$this->immutable = $immutable;
}
/**
* Create a new repository builder instance.
*
* @return \Dotenv\Repository\RepositoryBuilder
*/
public static function create()
{
return new self();
}
/**
* Creates a repository builder with the given readers.
*
* @param \Dotenv\Repository\Adapter\ReaderInterface[]|null $readers
*
* @return \Dotenv\Repository\RepositoryBuilder
*/
public function withReaders(array $readers = null)
{
$readers = $readers === null ? null : self::filterByAvailability($readers);
return new self($readers, $this->writers, $this->immutable);
}
/**
* Creates a repository builder with the given writers.
*
* @param \Dotenv\Repository\Adapter\WriterInterface[]|null $writers
*
* @return \Dotenv\Repository\RepositoryBuilder
*/
public function withWriters(array $writers = null)
{
$writers = $writers === null ? null : self::filterByAvailability($writers);
return new self($this->readers, $writers, $this->immutable);
}
/**
* Creates a repository builder with mutability enabled.
*
* @return \Dotenv\Repository\RepositoryBuilder
*/
public function immutable()
{
return new self($this->readers, $this->writers, true);
}
/**
* Creates a new repository instance.
*
* @return \Dotenv\Repository\RepositoryInterface
*/
public function make()
{
if ($this->readers === null || $this->writers === null) {
$defaults = self::defaultAdapters();
}
return new AdapterRepository(
$this->readers === null ? $defaults : $this->readers,
$this->writers === null ? $defaults : $this->writers,
$this->immutable
);
}
/**
* Return the array of default adapters.
*
* @return \Dotenv\Repository\Adapter\AvailabilityInterface[]
*/
private static function defaultAdapters()
{
return self::filterByAvailability([
new ApacheAdapter(),
new EnvConstAdapter(),
new ServerConstAdapter(),
new PutenvAdapter(),
]);
}
/**
* Filter an array of adapters to only those that are supported.
*
* @param \Dotenv\Repository\Adapter\AvailabilityInterface[] $adapters
*
* @return \Dotenv\Repository\Adapter\AvailabilityInterface[]
*/
private static function filterByAvailability(array $adapters)
{
return array_filter($adapters, function (AvailabilityInterface $adapter) {
return $adapter->isSupported();
});
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace Dotenv\Repository;
use ArrayAccess;
/**
* @extends \ArrayAccess<string,string|null>
*/
interface RepositoryInterface extends ArrayAccess
{
/**
* Tells whether environment variable has been defined.
*
* @param string $name
*
* @return bool
*/
public function has($name);
/**
* Get an environment variable.
*
* @param string $name
*
* @throws \InvalidArgumentException
*
* @return string|null
*/
public function get($name);
/**
* Set an environment variable.
*
* @param string $name
* @param string|null $value
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function set($name, $value = null);
/**
* Clear an environment variable.
*
* @param string $name
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function clear($name);
}

View File

@@ -0,0 +1,94 @@
<?php
namespace Dotenv\Result;
use PhpOption\None;
use PhpOption\Some;
/**
* @template T
* @template E
* @extends \Dotenv\Result\Result<T,E>
*/
class Error extends Result
{
/**
* @var E
*/
private $value;
/**
* Internal constructor for an error value.
*
* @param E $value
*
* @return void
*/
private function __construct($value)
{
$this->value = $value;
}
/**
* Create a new error value.
*
* @template F
*
* @param F $value
*
* @return \Dotenv\Result\Result<T,F>
*/
public static function create($value)
{
return new self($value);
}
/**
* Get the success option value.
*
* @return \PhpOption\Option<T>
*/
public function success()
{
return None::create();
}
/**
* Map over the success value.
*
* @template S
*
* @param callable(T):S $f
*
* @return \Dotenv\Result\Result<S,E>
*/
public function mapSuccess(callable $f)
{
/** @var \Dotenv\Result\Result<S,E> */
return self::create($this->value);
}
/**
* Get the error option value.
*
* @return \PhpOption\Option<E>
*/
public function error()
{
return Some::create($this->value);
}
/**
* Map over the error value.
*
* @template F
*
* @param callable(E):F $f
*
* @return \Dotenv\Result\Result<T,F>
*/
public function mapError(callable $f)
{
return self::create($f($this->value));
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace Dotenv\Result;
/**
* @template T
* @template E
*/
abstract class Result
{
/**
* Get the success option value.
*
* @return \PhpOption\Option<T>
*/
abstract public function success();
/**
* Get the success value, if possible.
*
* @throws \RuntimeException
*
* @return T
*/
public function getSuccess()
{
return $this->success()->get();
}
/**
* Map over the success value.
*
* @template S
*
* @param callable(T):S $f
*
* @return \Dotenv\Result\Result<S,E>
*/
abstract public function mapSuccess(callable $f);
/**
* Get the error option value.
*
* @return \PhpOption\Option<E>
*/
abstract public function error();
/**
* Get the error value, if possible.
*
* @throws \RuntimeException
*
* @return E
*/
public function getError()
{
return $this->error()->get();
}
/**
* Map over the error value.
*
* @template F
*
* @param callable(E):F $f
*
* @return \Dotenv\Result\Result<T,F>
*/
abstract public function mapError(callable $f);
}

View File

@@ -0,0 +1,94 @@
<?php
namespace Dotenv\Result;
use PhpOption\None;
use PhpOption\Some;
/**
* @template T
* @template E
* @extends \Dotenv\Result\Result<T,E>
*/
class Success extends Result
{
/**
* @var T
*/
private $value;
/**
* Internal constructor for a success value.
*
* @param T $value
*
* @return void
*/
private function __construct($value)
{
$this->value = $value;
}
/**
* Create a new error value.
*
* @template S
*
* @param S $value
*
* @return \Dotenv\Result\Result<S,E>
*/
public static function create($value)
{
return new self($value);
}
/**
* Get the success option value.
*
* @return \PhpOption\Option<T>
*/
public function success()
{
return Some::create($this->value);
}
/**
* Map over the success value.
*
* @template S
*
* @param callable(T):S $f
*
* @return \Dotenv\Result\Result<S,E>
*/
public function mapSuccess(callable $f)
{
return self::create($f($this->value));
}
/**
* Get the error option value.
*
* @return \PhpOption\Option<E>
*/
public function error()
{
return None::create();
}
/**
* Map over the error value.
*
* @template F
*
* @param callable(E):F $f
*
* @return \Dotenv\Result\Result<T,F>
*/
public function mapError(callable $f)
{
/** @var \Dotenv\Result\Result<T,F> */
return self::create($this->value);
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Dotenv\Store\File;
class Paths
{
/**
* Returns the full paths to the files.
*
* @param string[] $paths
* @param string[] $names
*
* @return string[]
*/
public static function filePaths(array $paths, array $names)
{
$files = [];
foreach ($paths as $path) {
foreach ($names as $name) {
$files[] = rtrim($path, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$name;
}
}
return $files;
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace Dotenv\Store\File;
use PhpOption\Option;
class Reader
{
/**
* Read the file(s), and return their raw content.
*
* We provide the file path as the key, and its content as the value. If
* short circuit mode is enabled, then the returned array with have length
* at most one. File paths that couldn't be read are omitted entirely.
*
* @param string[] $filePaths
* @param bool $shortCircuit
*
* @return array<string,string>
*/
public static function read(array $filePaths, $shortCircuit = true)
{
$output = [];
foreach ($filePaths as $filePath) {
$content = self::readFromFile($filePath);
if ($content->isDefined()) {
$output[$filePath] = $content->get();
if ($shortCircuit) {
break;
}
}
}
return $output;
}
/**
* Read the given file.
*
* @param string $filePath
*
* @return \PhpOption\Option<string>
*/
private static function readFromFile($filePath)
{
$content = @file_get_contents($filePath);
/** @var \PhpOption\Option<string> */
return Option::fromValue($content, false);
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace Dotenv\Store;
use Dotenv\Exception\InvalidPathException;
use Dotenv\Store\File\Reader;
class FileStore implements StoreInterface
{
/**
* The file paths.
*
* @var string[]
*/
protected $filePaths;
/**
* Should file loading short circuit?
*
* @var bool
*/
protected $shortCircuit;
/**
* Create a new file store instance.
*
* @param string[] $filePaths
* @param bool $shortCircuit
*
* @return void
*/
public function __construct(array $filePaths, $shortCircuit)
{
$this->filePaths = $filePaths;
$this->shortCircuit = $shortCircuit;
}
/**
* Read the content of the environment file(s).
*
* @throws \Dotenv\Exception\InvalidPathException
*
* @return string
*/
public function read()
{
if ($this->filePaths === []) {
throw new InvalidPathException('At least one environment file path must be provided.');
}
$contents = Reader::read($this->filePaths, $this->shortCircuit);
if (count($contents) > 0) {
return implode("\n", $contents);
}
throw new InvalidPathException(
sprintf('Unable to read any of the environment file(s) at [%s].', implode(', ', $this->filePaths))
);
}
}

View File

@@ -0,0 +1,102 @@
<?php
namespace Dotenv\Store;
use Dotenv\Store\File\Paths;
class StoreBuilder
{
/**
* The paths to search within.
*
* @var string[]
*/
private $paths;
/**
* The file names to search for.
*
* @var string[]|null
*/
private $names;
/**
* Should file loading short circuit?
*
* @var bool
*/
protected $shortCircuit;
/**
* Create a new store builder instance.
*
* @param string[] $paths
* @param string[]|null $names
* @param bool $shortCircuit
*
* @return void
*/
private function __construct(array $paths = [], array $names = null, $shortCircuit = false)
{
$this->paths = $paths;
$this->names = $names;
$this->shortCircuit = $shortCircuit;
}
/**
* Create a new store builder instance.
*
* @return \Dotenv\Store\StoreBuilder
*/
public static function create()
{
return new self();
}
/**
* Creates a store builder with the given paths.
*
* @param string|string[] $paths
*
* @return \Dotenv\Store\StoreBuilder
*/
public function withPaths($paths)
{
return new self((array) $paths, $this->names, $this->shortCircuit);
}
/**
* Creates a store builder with the given names.
*
* @param string|string[]|null $names
*
* @return \Dotenv\Store\StoreBuilder
*/
public function withNames($names = null)
{
return new self($this->paths, $names === null ? null : (array) $names, $this->shortCircuit);
}
/**
* Creates a store builder with short circuit mode enabled.
*
* @return \Dotenv\Store\StoreBuilder
*/
public function shortCircuit()
{
return new self($this->paths, $this->names, true);
}
/**
* Creates a new store instance.
*
* @return \Dotenv\Store\StoreInterface
*/
public function make()
{
return new FileStore(
Paths::filePaths($this->paths, $this->names === null ? ['.env'] : $this->names),
$this->shortCircuit
);
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Dotenv\Store;
interface StoreInterface
{
/**
* Read the content of the environment file(s).
*
* @throws \Dotenv\Exception\InvalidPathException
*
* @return string
*/
public function read();
}

View File

@@ -0,0 +1,35 @@
<?php
namespace Dotenv\Store;
final class StringStore implements StoreInterface
{
/**
* The file content.
*
* @var string
*/
private $content;
/**
* Create a new string store instance.
*
* @param string $content
*
* @return void
*/
public function __construct($content)
{
$this->content = $content;
}
/**
* Read the content of the environment file(s).
*
* @return string
*/
public function read()
{
return $this->content;
}
}

View File

@@ -0,0 +1,193 @@
<?php
namespace Dotenv;
use Dotenv\Exception\ValidationException;
use Dotenv\Regex\Regex;
use Dotenv\Repository\RepositoryInterface;
class Validator
{
/**
* The environment repository instance.
*
* @var \Dotenv\Repository\RepositoryInterface
*/
protected $repository;
/**
* The variables to validate.
*
* @var string[]
*/
protected $variables;
/**
* Create a new validator instance.
*
* @param \Dotenv\Repository\RepositoryInterface $repository
* @param string[] $variables
* @param bool $required
*
* @throws \Dotenv\Exception\ValidationException
*
* @return void
*/
public function __construct(RepositoryInterface $repository, array $variables, $required = true)
{
$this->repository = $repository;
$this->variables = $variables;
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'
);
}
/**
* 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'
);
}
/**
* 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;
},
'is not a boolean'
);
}
/**
* Assert that each variable is amongst the given choices.
*
* @param string[] $choices
*
* @throws \Dotenv\Exception\ValidationException
*
* @return \Dotenv\Validator
*/
public function allowedValues(array $choices)
{
return $this->assertCallback(
function ($value) use ($choices) {
if ($value === null) {
return true;
}
return in_array($value, $choices, true);
},
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)
);
}
/**
* Assert that the callback returns true for each variable.
*
* @param callable $callback
* @param string $message
*
* @throws \Dotenv\Exception\ValidationException
*
* @return \Dotenv\Validator
*/
protected function assertCallback(callable $callback, $message = 'failed callback assertion')
{
$failing = [];
foreach ($this->variables as $variable) {
if ($callback($this->repository->get($variable)) === false) {
$failing[] = sprintf('%s %s', $variable, $message);
}
}
if (count($failing) > 0) {
throw new ValidationException(sprintf(
'One or more environment variables failed assertions: %s.',
implode(', ', $failing)
));
}
return $this;
}
}