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 */ private static function processChar($state, $char) { switch ($state) { case self::INITIAL_STATE: if ($char === '\'') { /** @var \Dotenv\Result\Result */ return Success::create(['', false, self::SINGLE_QUOTED_STATE]); } elseif ($char === '"') { /** @var \Dotenv\Result\Result */ return Success::create(['', false, self::DOUBLE_QUOTED_STATE]); } elseif ($char === '#') { /** @var \Dotenv\Result\Result */ return Success::create(['', false, self::COMMENT_STATE]); } elseif ($char === '$') { /** @var \Dotenv\Result\Result */ return Success::create([$char, true, self::UNQUOTED_STATE]); } else { /** @var \Dotenv\Result\Result */ return Success::create([$char, false, self::UNQUOTED_STATE]); } case self::UNQUOTED_STATE: if ($char === '#') { /** @var \Dotenv\Result\Result */ return Success::create(['', false, self::COMMENT_STATE]); } elseif (ctype_space($char)) { /** @var \Dotenv\Result\Result */ return Success::create(['', false, self::WHITESPACE_STATE]); } elseif ($char === '$') { /** @var \Dotenv\Result\Result */ return Success::create([$char, true, self::UNQUOTED_STATE]); } else { /** @var \Dotenv\Result\Result */ return Success::create([$char, false, self::UNQUOTED_STATE]); } case self::SINGLE_QUOTED_STATE: if ($char === '\'') { /** @var \Dotenv\Result\Result */ return Success::create(['', false, self::WHITESPACE_STATE]); } else { /** @var \Dotenv\Result\Result */ return Success::create([$char, false, self::SINGLE_QUOTED_STATE]); } case self::DOUBLE_QUOTED_STATE: if ($char === '"') { /** @var \Dotenv\Result\Result */ return Success::create(['', false, self::WHITESPACE_STATE]); } elseif ($char === '\\') { /** @var \Dotenv\Result\Result */ return Success::create(['', false, self::ESCAPE_SEQUENCE_STATE]); } elseif ($char === '$') { /** @var \Dotenv\Result\Result */ return Success::create([$char, true, self::DOUBLE_QUOTED_STATE]); } else { /** @var \Dotenv\Result\Result */ return Success::create([$char, false, self::DOUBLE_QUOTED_STATE]); } case self::ESCAPE_SEQUENCE_STATE: if ($char === '"' || $char === '\\') { /** @var \Dotenv\Result\Result */ return Success::create([$char, false, self::DOUBLE_QUOTED_STATE]); } elseif ($char === '$') { /** @var \Dotenv\Result\Result */ return Success::create([$char, false, self::DOUBLE_QUOTED_STATE]); } elseif (in_array($char, ['f', 'n', 'r', 't', 'v'], true)) { /** @var \Dotenv\Result\Result */ return Success::create([stripcslashes('\\'.$char), false, self::DOUBLE_QUOTED_STATE]); } else { /** @var \Dotenv\Result\Result */ return Error::create('an unexpected escape sequence'); } case self::WHITESPACE_STATE: if ($char === '#') { /** @var \Dotenv\Result\Result */ return Success::create(['', false, self::COMMENT_STATE]); } elseif (!ctype_space($char)) { /** @var \Dotenv\Result\Result */ return Error::create('unexpected whitespace'); } else { /** @var \Dotenv\Result\Result */ return Success::create(['', false, self::WHITESPACE_STATE]); } case self::COMMENT_STATE: /** @var \Dotenv\Result\Result */ 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") ); } }