summaryrefslogtreecommitdiff
path: root/vendor/vlucas/phpdotenv/src/Parser
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/vlucas/phpdotenv/src/Parser')
-rw-r--r--vendor/vlucas/phpdotenv/src/Parser/Entry.php59
-rw-r--r--vendor/vlucas/phpdotenv/src/Parser/EntryParser.php300
-rw-r--r--vendor/vlucas/phpdotenv/src/Parser/Lexer.php58
-rw-r--r--vendor/vlucas/phpdotenv/src/Parser/Lines.php127
-rw-r--r--vendor/vlucas/phpdotenv/src/Parser/Parser.php53
-rw-r--r--vendor/vlucas/phpdotenv/src/Parser/ParserInterface.php19
-rw-r--r--vendor/vlucas/phpdotenv/src/Parser/Value.php88
7 files changed, 704 insertions, 0 deletions
diff --git a/vendor/vlucas/phpdotenv/src/Parser/Entry.php b/vendor/vlucas/phpdotenv/src/Parser/Entry.php
new file mode 100644
index 0000000..716f422
--- /dev/null
+++ b/vendor/vlucas/phpdotenv/src/Parser/Entry.php
@@ -0,0 +1,59 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Dotenv\Parser;
+
+use PhpOption\Option;
+
+final class Entry
+{
+ /**
+ * The entry name.
+ *
+ * @var string
+ */
+ private $name;
+
+ /**
+ * The entry value.
+ *
+ * @var \Dotenv\Parser\Value|null
+ */
+ private $value;
+
+ /**
+ * Create a new entry instance.
+ *
+ * @param string $name
+ * @param \Dotenv\Parser\Value|null $value
+ *
+ * @return void
+ */
+ public function __construct(string $name, ?Value $value = null)
+ {
+ $this->name = $name;
+ $this->value = $value;
+ }
+
+ /**
+ * Get the entry name.
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Get the entry value.
+ *
+ * @return \PhpOption\Option<\Dotenv\Parser\Value>
+ */
+ public function getValue()
+ {
+ /** @var \PhpOption\Option<\Dotenv\Parser\Value> */
+ return Option::fromValue($this->value);
+ }
+}
diff --git a/vendor/vlucas/phpdotenv/src/Parser/EntryParser.php b/vendor/vlucas/phpdotenv/src/Parser/EntryParser.php
new file mode 100644
index 0000000..e286840
--- /dev/null
+++ b/vendor/vlucas/phpdotenv/src/Parser/EntryParser.php
@@ -0,0 +1,300 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Dotenv\Parser;
+
+use Dotenv\Util\Regex;
+use Dotenv\Util\Str;
+use GrahamCampbell\ResultType\Error;
+use GrahamCampbell\ResultType\Result;
+use GrahamCampbell\ResultType\Success;
+
+final class EntryParser
+{
+ private const INITIAL_STATE = 0;
+ private const UNQUOTED_STATE = 1;
+ private const SINGLE_QUOTED_STATE = 2;
+ private const DOUBLE_QUOTED_STATE = 3;
+ private const ESCAPE_SEQUENCE_STATE = 4;
+ private const WHITESPACE_STATE = 5;
+ private const COMMENT_STATE = 6;
+ private const REJECT_STATES = [self::SINGLE_QUOTED_STATE, self::DOUBLE_QUOTED_STATE, self::ESCAPE_SEQUENCE_STATE];
+
+ /**
+ * This class is a singleton.
+ *
+ * @codeCoverageIgnore
+ *
+ * @return void
+ */
+ private function __construct()
+ {
+ //
+ }
+
+ /**
+ * Parse a raw entry into a proper entry.
+ *
+ * That is, turn a raw environment variable entry into a name and possibly
+ * a value. We wrap the answer in a result type.
+ *
+ * @param string $entry
+ *
+ * @return \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Entry,string>
+ */
+ public static function parse(string $entry)
+ {
+ return self::splitStringIntoParts($entry)->flatMap(static function (array $parts) {
+ [$name, $value] = $parts;
+
+ return self::parseName($name)->flatMap(static function (string $name) use ($value) {
+ /** @var Result<Value|null,string> */
+ $parsedValue = $value === null ? Success::create(null) : self::parseValue($value);
+
+ return $parsedValue->map(static function (?Value $value) use ($name) {
+ return new Entry($name, $value);
+ });
+ });
+ });
+ }
+
+ /**
+ * Split the compound string into parts.
+ *
+ * @param string $line
+ *
+ * @return \GrahamCampbell\ResultType\Result<array{string,string|null},string>
+ */
+ private static function splitStringIntoParts(string $line)
+ {
+ /** @var array{string,string|null} */
+ $result = Str::pos($line, '=')->map(static function () use ($line) {
+ return \array_map('trim', \explode('=', $line, 2));
+ })->getOrElse([$line, null]);
+
+ if ($result[0] === '') {
+ /** @var \GrahamCampbell\ResultType\Result<array{string,string|null},string> */
+ return Error::create(self::getErrorMessage('an unexpected equals', $line));
+ }
+
+ /** @var \GrahamCampbell\ResultType\Result<array{string,string|null},string> */
+ return Success::create($result);
+ }
+
+ /**
+ * Parse the given variable name.
+ *
+ * That is, strip the optional quotes and leading "export" from the
+ * variable name. We wrap the answer in a result type.
+ *
+ * @param string $name
+ *
+ * @return \GrahamCampbell\ResultType\Result<string,string>
+ */
+ private static function parseName(string $name)
+ {
+ if (Str::len($name) > 8 && Str::substr($name, 0, 6) === 'export' && \ctype_space(Str::substr($name, 6, 1))) {
+ $name = \ltrim(Str::substr($name, 6));
+ }
+
+ if (self::isQuotedName($name)) {
+ $name = Str::substr($name, 1, -1);
+ }
+
+ if (!self::isValidName($name)) {
+ /** @var \GrahamCampbell\ResultType\Result<string,string> */
+ return Error::create(self::getErrorMessage('an invalid name', $name));
+ }
+
+ /** @var \GrahamCampbell\ResultType\Result<string,string> */
+ return Success::create($name);
+ }
+
+ /**
+ * Is the given variable name quoted?
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ private static function isQuotedName(string $name)
+ {
+ if (Str::len($name) < 3) {
+ return false;
+ }
+
+ $first = Str::substr($name, 0, 1);
+ $last = Str::substr($name, -1, 1);
+
+ return ($first === '"' && $last === '"') || ($first === '\'' && $last === '\'');
+ }
+
+ /**
+ * Is the given variable name valid?
+ *
+ * @param string $name
+ *
+ * @return bool
+ */
+ private static function isValidName(string $name)
+ {
+ return Regex::matches('~(*UTF8)\A[\p{Ll}\p{Lu}\p{M}\p{N}_.]+\z~', $name)->success()->getOrElse(false);
+ }
+
+ /**
+ * Parse the given variable value.
+ *
+ * This has the effect of stripping quotes and comments, dealing with
+ * special characters, and locating nested variables, but not resolving
+ * them. Formally, we run a finite state automaton with an output tape: a
+ * transducer. We wrap the answer in a result type.
+ *
+ * @param string $value
+ *
+ * @return \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Value,string>
+ */
+ private static function parseValue(string $value)
+ {
+ if (\trim($value) === '') {
+ /** @var \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Value,string> */
+ return Success::create(Value::blank());
+ }
+
+ return \array_reduce(\iterator_to_array(Lexer::lex($value)), static function (Result $data, string $token) {
+ return $data->flatMap(static function (array $data) use ($token) {
+ return self::processToken($data[1], $token)->map(static function (array $val) use ($data) {
+ return [$data[0]->append($val[0], $val[1]), $val[2]];
+ });
+ });
+ }, Success::create([Value::blank(), self::INITIAL_STATE]))->flatMap(static function (array $result) {
+ /** @psalm-suppress DocblockTypeContradiction */
+ if (in_array($result[1], self::REJECT_STATES, true)) {
+ /** @var \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Value,string> */
+ return Error::create('a missing closing quote');
+ }
+
+ /** @var \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Value,string> */
+ return Success::create($result[0]);
+ })->mapError(static function (string $err) use ($value) {
+ return self::getErrorMessage($err, $value);
+ });
+ }
+
+ /**
+ * Process the given token.
+ *
+ * @param int $state
+ * @param string $token
+ *
+ * @return \GrahamCampbell\ResultType\Result<array{string,bool,int},string>
+ */
+ private static function processToken(int $state, string $token)
+ {
+ switch ($state) {
+ case self::INITIAL_STATE:
+ if ($token === '\'') {
+ /** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
+ return Success::create(['', false, self::SINGLE_QUOTED_STATE]);
+ } elseif ($token === '"') {
+ /** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
+ return Success::create(['', false, self::DOUBLE_QUOTED_STATE]);
+ } elseif ($token === '#') {
+ /** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
+ return Success::create(['', false, self::COMMENT_STATE]);
+ } elseif ($token === '$') {
+ /** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
+ return Success::create([$token, true, self::UNQUOTED_STATE]);
+ } else {
+ /** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
+ return Success::create([$token, false, self::UNQUOTED_STATE]);
+ }
+ case self::UNQUOTED_STATE:
+ if ($token === '#') {
+ /** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
+ return Success::create(['', false, self::COMMENT_STATE]);
+ } elseif (\ctype_space($token)) {
+ /** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
+ return Success::create(['', false, self::WHITESPACE_STATE]);
+ } elseif ($token === '$') {
+ /** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
+ return Success::create([$token, true, self::UNQUOTED_STATE]);
+ } else {
+ /** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
+ return Success::create([$token, false, self::UNQUOTED_STATE]);
+ }
+ case self::SINGLE_QUOTED_STATE:
+ if ($token === '\'') {
+ /** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
+ return Success::create(['', false, self::WHITESPACE_STATE]);
+ } else {
+ /** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
+ return Success::create([$token, false, self::SINGLE_QUOTED_STATE]);
+ }
+ case self::DOUBLE_QUOTED_STATE:
+ if ($token === '"') {
+ /** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
+ return Success::create(['', false, self::WHITESPACE_STATE]);
+ } elseif ($token === '\\') {
+ /** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
+ return Success::create(['', false, self::ESCAPE_SEQUENCE_STATE]);
+ } elseif ($token === '$') {
+ /** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
+ return Success::create([$token, true, self::DOUBLE_QUOTED_STATE]);
+ } else {
+ /** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
+ return Success::create([$token, false, self::DOUBLE_QUOTED_STATE]);
+ }
+ case self::ESCAPE_SEQUENCE_STATE:
+ if ($token === '"' || $token === '\\') {
+ /** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
+ return Success::create([$token, false, self::DOUBLE_QUOTED_STATE]);
+ } elseif ($token === '$') {
+ /** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
+ return Success::create([$token, false, self::DOUBLE_QUOTED_STATE]);
+ } else {
+ $first = Str::substr($token, 0, 1);
+ if (\in_array($first, ['f', 'n', 'r', 't', 'v'], true)) {
+ /** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
+ return Success::create([\stripcslashes('\\'.$first).Str::substr($token, 1), false, self::DOUBLE_QUOTED_STATE]);
+ } else {
+ /** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
+ return Error::create('an unexpected escape sequence');
+ }
+ }
+ case self::WHITESPACE_STATE:
+ if ($token === '#') {
+ /** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
+ return Success::create(['', false, self::COMMENT_STATE]);
+ } elseif (!\ctype_space($token)) {
+ /** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
+ return Error::create('unexpected whitespace');
+ } else {
+ /** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
+ return Success::create(['', false, self::WHITESPACE_STATE]);
+ }
+ case self::COMMENT_STATE:
+ /** @var \GrahamCampbell\ResultType\Result<array{string,bool,int},string> */
+ return Success::create(['', false, self::COMMENT_STATE]);
+ default:
+ throw new \Error('Parser entered invalid state.');
+ }
+ }
+
+ /**
+ * Generate a friendly error message.
+ *
+ * @param string $cause
+ * @param string $subject
+ *
+ * @return string
+ */
+ private static function getErrorMessage(string $cause, string $subject)
+ {
+ return \sprintf(
+ 'Encountered %s at [%s].',
+ $cause,
+ \strtok($subject, "\n")
+ );
+ }
+}
diff --git a/vendor/vlucas/phpdotenv/src/Parser/Lexer.php b/vendor/vlucas/phpdotenv/src/Parser/Lexer.php
new file mode 100644
index 0000000..981af24
--- /dev/null
+++ b/vendor/vlucas/phpdotenv/src/Parser/Lexer.php
@@ -0,0 +1,58 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Dotenv\Parser;
+
+final class Lexer
+{
+ /**
+ * The regex for each type of token.
+ */
+ private const PATTERNS = [
+ '[\r\n]{1,1000}', '[^\S\r\n]{1,1000}', '\\\\', '\'', '"', '\\#', '\\$', '([^(\s\\\\\'"\\#\\$)]|\\(|\\)){1,1000}',
+ ];
+
+ /**
+ * This class is a singleton.
+ *
+ * @codeCoverageIgnore
+ *
+ * @return void
+ */
+ private function __construct()
+ {
+ //
+ }
+
+ /**
+ * Convert content into a token stream.
+ *
+ * Multibyte string processing is not needed here, and nether is error
+ * handling, for performance reasons.
+ *
+ * @param string $content
+ *
+ * @return \Generator<string>
+ */
+ public static function lex(string $content)
+ {
+ static $regex;
+
+ if ($regex === null) {
+ $regex = '(('.\implode(')|(', self::PATTERNS).'))A';
+ }
+
+ $offset = 0;
+
+ while (isset($content[$offset])) {
+ if (!\preg_match($regex, $content, $matches, 0, $offset)) {
+ throw new \Error(\sprintf('Lexer encountered unexpected character [%s].', $content[$offset]));
+ }
+
+ $offset += \strlen($matches[0]);
+
+ yield $matches[0];
+ }
+ }
+}
diff --git a/vendor/vlucas/phpdotenv/src/Parser/Lines.php b/vendor/vlucas/phpdotenv/src/Parser/Lines.php
new file mode 100644
index 0000000..6497993
--- /dev/null
+++ b/vendor/vlucas/phpdotenv/src/Parser/Lines.php
@@ -0,0 +1,127 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Dotenv\Parser;
+
+use Dotenv\Util\Regex;
+use Dotenv\Util\Str;
+
+final class Lines
+{
+ /**
+ * This class is a singleton.
+ *
+ * @codeCoverageIgnore
+ *
+ * @return void
+ */
+ private function __construct()
+ {
+ //
+ }
+
+ /**
+ * Process the array of lines of environment variables.
+ *
+ * This will produce an array of raw entries, one per variable.
+ *
+ * @param string[] $lines
+ *
+ * @return string[]
+ */
+ public static function process(array $lines)
+ {
+ $output = [];
+ $multiline = false;
+ $multilineBuffer = [];
+
+ foreach ($lines as $line) {
+ [$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(bool $multiline, string $line, array $buffer)
+ {
+ $startsOnCurrentLine = $multiline ? false : self::looksLikeMultilineStart($line);
+
+ // check if $line can be multiline variable
+ if ($startsOnCurrentLine) {
+ $multiline = true;
+ }
+
+ if ($multiline) {
+ \array_push($buffer, $line);
+
+ if (self::looksLikeMultilineStop($line, $startsOnCurrentLine)) {
+ $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(string $line)
+ {
+ return Str::pos($line, '="')->map(static function () use ($line) {
+ return self::looksLikeMultilineStop($line, true) === false;
+ })->getOrElse(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(string $line, bool $started)
+ {
+ if ($line === '"') {
+ return true;
+ }
+
+ return Regex::occurrences('/(?=([^\\\\]"))/', \str_replace('\\\\', '', $line))->map(static function (int $count) use ($started) {
+ return $started ? $count > 1 : $count >= 1;
+ })->success()->getOrElse(false);
+ }
+
+ /**
+ * Determine if the line in the file is a comment or whitespace.
+ *
+ * @param string $line
+ *
+ * @return bool
+ */
+ private static function isCommentOrWhitespace(string $line)
+ {
+ $line = \trim($line);
+
+ return $line === '' || (isset($line[0]) && $line[0] === '#');
+ }
+}
diff --git a/vendor/vlucas/phpdotenv/src/Parser/Parser.php b/vendor/vlucas/phpdotenv/src/Parser/Parser.php
new file mode 100644
index 0000000..2d30dfd
--- /dev/null
+++ b/vendor/vlucas/phpdotenv/src/Parser/Parser.php
@@ -0,0 +1,53 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Dotenv\Parser;
+
+use Dotenv\Exception\InvalidFileException;
+use Dotenv\Util\Regex;
+use GrahamCampbell\ResultType\Result;
+use GrahamCampbell\ResultType\Success;
+
+final class Parser implements ParserInterface
+{
+ /**
+ * Parse content into an entry array.
+ *
+ * @param string $content
+ *
+ * @throws \Dotenv\Exception\InvalidFileException
+ *
+ * @return \Dotenv\Parser\Entry[]
+ */
+ public function parse(string $content)
+ {
+ return Regex::split("/(\r\n|\n|\r)/", $content)->mapError(static function () {
+ return 'Could not split into separate lines.';
+ })->flatMap(static function (array $lines) {
+ return self::process(Lines::process($lines));
+ })->mapError(static function (string $error) {
+ throw new InvalidFileException(\sprintf('Failed to parse dotenv file. %s', $error));
+ })->success()->get();
+ }
+
+ /**
+ * Convert the raw entries into proper entries.
+ *
+ * @param string[] $entries
+ *
+ * @return \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Entry[],string>
+ */
+ private static function process(array $entries)
+ {
+ /** @var \GrahamCampbell\ResultType\Result<\Dotenv\Parser\Entry[],string> */
+ return \array_reduce($entries, static function (Result $result, string $raw) {
+ return $result->flatMap(static function (array $entries) use ($raw) {
+ return EntryParser::parse($raw)->map(static function (Entry $entry) use ($entries) {
+ /** @var \Dotenv\Parser\Entry[] */
+ return \array_merge($entries, [$entry]);
+ });
+ });
+ }, Success::create([]));
+ }
+}
diff --git a/vendor/vlucas/phpdotenv/src/Parser/ParserInterface.php b/vendor/vlucas/phpdotenv/src/Parser/ParserInterface.php
new file mode 100644
index 0000000..17cc42a
--- /dev/null
+++ b/vendor/vlucas/phpdotenv/src/Parser/ParserInterface.php
@@ -0,0 +1,19 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Dotenv\Parser;
+
+interface ParserInterface
+{
+ /**
+ * Parse content into an entry array.
+ *
+ * @param string $content
+ *
+ * @throws \Dotenv\Exception\InvalidFileException
+ *
+ * @return \Dotenv\Parser\Entry[]
+ */
+ public function parse(string $content);
+}
diff --git a/vendor/vlucas/phpdotenv/src/Parser/Value.php b/vendor/vlucas/phpdotenv/src/Parser/Value.php
new file mode 100644
index 0000000..9e495a1
--- /dev/null
+++ b/vendor/vlucas/phpdotenv/src/Parser/Value.php
@@ -0,0 +1,88 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Dotenv\Parser;
+
+use Dotenv\Util\Str;
+
+final 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(string $chars, array $vars)
+ {
+ $this->chars = $chars;
+ $this->vars = $vars;
+ }
+
+ /**
+ * Create an empty value instance.
+ *
+ * @return \Dotenv\Parser\Value
+ */
+ public static function blank()
+ {
+ return new self('', []);
+ }
+
+ /**
+ * Create a new value instance, appending the characters.
+ *
+ * @param string $chars
+ * @param bool $var
+ *
+ * @return \Dotenv\Parser\Value
+ */
+ public function append(string $chars, bool $var)
+ {
+ return new self(
+ $this->chars.$chars,
+ $var ? \array_merge($this->vars, [Str::len($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;
+ }
+}