123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- <?php
- namespace Dotenv;
- use Dotenv\Exception\InvalidFileException;
- use Dotenv\Regex\Regex;
- class Parser
- {
- const INITIAL_STATE = 0;
- const UNQUOTED_STATE = 1;
- const QUOTED_STATE = 2;
- const ESCAPE_STATE = 3;
- const WHITESPACE_STATE = 4;
- const COMMENT_STATE = 5;
- /**
- * Parse the given environment variable entry into a name and value.
- *
- * @param string $entry
- *
- * @throws \Dotenv\Exception\InvalidFileException
- *
- * @return array
- */
- 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
- */
- 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 string|null
- */
- private static function parseValue($value)
- {
- if ($value === null || trim($value) === '') {
- return $value;
- }
- $result = array_reduce(str_split($value), function ($data, $char) use ($value) {
- switch ($data[1]) {
- case self::INITIAL_STATE:
- if ($char === '"' || $char === '\'') {
- return [$data[0], self::QUOTED_STATE];
- } elseif ($char === '#') {
- return [$data[0], self::COMMENT_STATE];
- } else {
- return [$data[0].$char, self::UNQUOTED_STATE];
- }
- case self::UNQUOTED_STATE:
- if ($char === '#') {
- return [$data[0], self::COMMENT_STATE];
- } elseif (ctype_space($char)) {
- return [$data[0], self::WHITESPACE_STATE];
- } else {
- return [$data[0].$char, self::UNQUOTED_STATE];
- }
- case self::QUOTED_STATE:
- if ($char === $value[0]) {
- return [$data[0], self::WHITESPACE_STATE];
- } elseif ($char === '\\') {
- return [$data[0], self::ESCAPE_STATE];
- } else {
- return [$data[0].$char, self::QUOTED_STATE];
- }
- case self::ESCAPE_STATE:
- if ($char === $value[0] || $char === '\\') {
- return [$data[0].$char, self::QUOTED_STATE];
- } elseif (in_array($char, ['f', 'n', 'r', 't', 'v'], true)) {
- return [$data[0].stripcslashes('\\'.$char), self::QUOTED_STATE];
- } else {
- throw new InvalidFileException(
- self::getErrorMessage('an unexpected escape sequence', $value)
- );
- }
- case self::WHITESPACE_STATE:
- if ($char === '#') {
- return [$data[0], self::COMMENT_STATE];
- } elseif (!ctype_space($char)) {
- throw new InvalidFileException(
- self::getErrorMessage('unexpected whitespace', $value)
- );
- } else {
- return [$data[0], self::WHITESPACE_STATE];
- }
- case self::COMMENT_STATE:
- return [$data[0], self::COMMENT_STATE];
- }
- }, ['', self::INITIAL_STATE]);
- if ($result[1] === self::QUOTED_STATE || $result[1] === self::ESCAPE_STATE) {
- throw new InvalidFileException(
- self::getErrorMessage('a missing closing quote', $value)
- );
- }
- return $result[0];
- }
- /**
- * 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")
- );
- }
- }
|