mirror of
				https://github.com/billz/raspap-webgui.git
				synced 2025-03-01 10:31:47 +00:00 
			
		
		
		
	Update dependencies + cp vendor/*/src src/
This commit is contained in:
		| @@ -13,12 +13,16 @@ | ||||
|         } | ||||
|     ], | ||||
|     "require": { | ||||
|         "php": "^7.0" | ||||
|         "php": "^8.2", | ||||
|         "vlucas/phpdotenv": "^5.6", | ||||
|         "phpoption/phpoption": "^1.9", | ||||
|         "ext-mbstring": "*" | ||||
|     }, | ||||
|     "require-dev": { | ||||
|         "php-parallel-lint/php-parallel-lint": "^1.2.0", | ||||
|         "phpcompatibility/php-compatibility": "^9.3.5", | ||||
|         "squizlabs/php_codesniffer": "^3.5.5" | ||||
|         "squizlabs/php_codesniffer": "^3.9.0", | ||||
|         "ext-simplexml": "*" | ||||
|     }, | ||||
|     "scripts": { | ||||
|         "lint": "parallel-lint . --exclude vendor", | ||||
|   | ||||
							
								
								
									
										267
									
								
								src/Dotenv/Dotenv.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								src/Dotenv/Dotenv.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,267 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv; | ||||
|  | ||||
| use Dotenv\Exception\InvalidPathException; | ||||
| use Dotenv\Loader\Loader; | ||||
| use Dotenv\Loader\LoaderInterface; | ||||
| use Dotenv\Parser\Parser; | ||||
| use Dotenv\Parser\ParserInterface; | ||||
| use Dotenv\Repository\Adapter\ArrayAdapter; | ||||
| use Dotenv\Repository\Adapter\PutenvAdapter; | ||||
| use Dotenv\Repository\RepositoryBuilder; | ||||
| use Dotenv\Repository\RepositoryInterface; | ||||
| use Dotenv\Store\StoreBuilder; | ||||
| use Dotenv\Store\StoreInterface; | ||||
| use Dotenv\Store\StringStore; | ||||
|  | ||||
| class Dotenv | ||||
| { | ||||
|     /** | ||||
|      * The store instance. | ||||
|      * | ||||
|      * @var \Dotenv\Store\StoreInterface | ||||
|      */ | ||||
|     private $store; | ||||
|  | ||||
|     /** | ||||
|      * The parser instance. | ||||
|      * | ||||
|      * @var \Dotenv\Parser\ParserInterface | ||||
|      */ | ||||
|     private $parser; | ||||
|  | ||||
|     /** | ||||
|      * The loader instance. | ||||
|      * | ||||
|      * @var \Dotenv\Loader\LoaderInterface | ||||
|      */ | ||||
|     private $loader; | ||||
|  | ||||
|     /** | ||||
|      * The repository instance. | ||||
|      * | ||||
|      * @var \Dotenv\Repository\RepositoryInterface | ||||
|      */ | ||||
|     private $repository; | ||||
|  | ||||
|     /** | ||||
|      * Create a new dotenv instance. | ||||
|      * | ||||
|      * @param \Dotenv\Store\StoreInterface           $store | ||||
|      * @param \Dotenv\Parser\ParserInterface         $parser | ||||
|      * @param \Dotenv\Loader\LoaderInterface         $loader | ||||
|      * @param \Dotenv\Repository\RepositoryInterface $repository | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct( | ||||
|         StoreInterface $store, | ||||
|         ParserInterface $parser, | ||||
|         LoaderInterface $loader, | ||||
|         RepositoryInterface $repository | ||||
|     ) { | ||||
|         $this->store = $store; | ||||
|         $this->parser = $parser; | ||||
|         $this->loader = $loader; | ||||
|         $this->repository = $repository; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a new dotenv instance. | ||||
|      * | ||||
|      * @param \Dotenv\Repository\RepositoryInterface $repository | ||||
|      * @param string|string[]                        $paths | ||||
|      * @param string|string[]|null                   $names | ||||
|      * @param bool                                   $shortCircuit | ||||
|      * @param string|null                            $fileEncoding | ||||
|      * | ||||
|      * @return \Dotenv\Dotenv | ||||
|      */ | ||||
|     public static function create(RepositoryInterface $repository, $paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null) | ||||
|     { | ||||
|         $builder = $names === null ? StoreBuilder::createWithDefaultName() : StoreBuilder::createWithNoNames(); | ||||
|  | ||||
|         foreach ((array) $paths as $path) { | ||||
|             $builder = $builder->addPath($path); | ||||
|         } | ||||
|  | ||||
|         foreach ((array) $names as $name) { | ||||
|             $builder = $builder->addName($name); | ||||
|         } | ||||
|  | ||||
|         if ($shortCircuit) { | ||||
|             $builder = $builder->shortCircuit(); | ||||
|         } | ||||
|  | ||||
|         return new self($builder->fileEncoding($fileEncoding)->make(), new Parser(), new Loader(), $repository); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a new mutable dotenv instance with default repository. | ||||
|      * | ||||
|      * @param string|string[]      $paths | ||||
|      * @param string|string[]|null $names | ||||
|      * @param bool                 $shortCircuit | ||||
|      * @param string|null          $fileEncoding | ||||
|      * | ||||
|      * @return \Dotenv\Dotenv | ||||
|      */ | ||||
|     public static function createMutable($paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null) | ||||
|     { | ||||
|         $repository = RepositoryBuilder::createWithDefaultAdapters()->make(); | ||||
|  | ||||
|         return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a new mutable dotenv instance with default repository with the putenv adapter. | ||||
|      * | ||||
|      * @param string|string[]      $paths | ||||
|      * @param string|string[]|null $names | ||||
|      * @param bool                 $shortCircuit | ||||
|      * @param string|null          $fileEncoding | ||||
|      * | ||||
|      * @return \Dotenv\Dotenv | ||||
|      */ | ||||
|     public static function createUnsafeMutable($paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null) | ||||
|     { | ||||
|         $repository = RepositoryBuilder::createWithDefaultAdapters() | ||||
|             ->addAdapter(PutenvAdapter::class) | ||||
|             ->make(); | ||||
|  | ||||
|         return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a new immutable dotenv instance with default repository. | ||||
|      * | ||||
|      * @param string|string[]      $paths | ||||
|      * @param string|string[]|null $names | ||||
|      * @param bool                 $shortCircuit | ||||
|      * @param string|null          $fileEncoding | ||||
|      * | ||||
|      * @return \Dotenv\Dotenv | ||||
|      */ | ||||
|     public static function createImmutable($paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null) | ||||
|     { | ||||
|         $repository = RepositoryBuilder::createWithDefaultAdapters()->immutable()->make(); | ||||
|  | ||||
|         return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a new immutable dotenv instance with default repository with the putenv adapter. | ||||
|      * | ||||
|      * @param string|string[]      $paths | ||||
|      * @param string|string[]|null $names | ||||
|      * @param bool                 $shortCircuit | ||||
|      * @param string|null          $fileEncoding | ||||
|      * | ||||
|      * @return \Dotenv\Dotenv | ||||
|      */ | ||||
|     public static function createUnsafeImmutable($paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null) | ||||
|     { | ||||
|         $repository = RepositoryBuilder::createWithDefaultAdapters() | ||||
|             ->addAdapter(PutenvAdapter::class) | ||||
|             ->immutable() | ||||
|             ->make(); | ||||
|  | ||||
|         return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a new dotenv instance with an array backed repository. | ||||
|      * | ||||
|      * @param string|string[]      $paths | ||||
|      * @param string|string[]|null $names | ||||
|      * @param bool                 $shortCircuit | ||||
|      * @param string|null          $fileEncoding | ||||
|      * | ||||
|      * @return \Dotenv\Dotenv | ||||
|      */ | ||||
|     public static function createArrayBacked($paths, $names = null, bool $shortCircuit = true, string $fileEncoding = null) | ||||
|     { | ||||
|         $repository = RepositoryBuilder::createWithNoAdapters()->addAdapter(ArrayAdapter::class)->make(); | ||||
|  | ||||
|         return self::create($repository, $paths, $names, $shortCircuit, $fileEncoding); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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(string $content) | ||||
|     { | ||||
|         $repository = RepositoryBuilder::createWithNoAdapters()->addAdapter(ArrayAdapter::class)->make(); | ||||
|  | ||||
|         $phpdotenv = new self(new StringStore($content), new Parser(), new Loader(), $repository); | ||||
|  | ||||
|         return $phpdotenv->load(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Read and load environment file(s). | ||||
|      * | ||||
|      * @throws \Dotenv\Exception\InvalidPathException|\Dotenv\Exception\InvalidEncodingException|\Dotenv\Exception\InvalidFileException | ||||
|      * | ||||
|      * @return array<string,string|null> | ||||
|      */ | ||||
|     public function load() | ||||
|     { | ||||
|         $entries = $this->parser->parse($this->store->read()); | ||||
|  | ||||
|         return $this->loader->load($this->repository, $entries); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Read and load environment file(s), silently failing if no files can be read. | ||||
|      * | ||||
|      * @throws \Dotenv\Exception\InvalidEncodingException|\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))->required(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										12
									
								
								src/Dotenv/Exception/ExceptionInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/Dotenv/Exception/ExceptionInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Exception; | ||||
|  | ||||
| use Throwable; | ||||
|  | ||||
| interface ExceptionInterface extends Throwable | ||||
| { | ||||
|     // | ||||
| } | ||||
							
								
								
									
										12
									
								
								src/Dotenv/Exception/InvalidEncodingException.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/Dotenv/Exception/InvalidEncodingException.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Exception; | ||||
|  | ||||
| use InvalidArgumentException; | ||||
|  | ||||
| final class InvalidEncodingException extends InvalidArgumentException implements ExceptionInterface | ||||
| { | ||||
|     // | ||||
| } | ||||
							
								
								
									
										12
									
								
								src/Dotenv/Exception/InvalidFileException.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/Dotenv/Exception/InvalidFileException.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Exception; | ||||
|  | ||||
| use InvalidArgumentException; | ||||
|  | ||||
| final class InvalidFileException extends InvalidArgumentException implements ExceptionInterface | ||||
| { | ||||
|     // | ||||
| } | ||||
							
								
								
									
										12
									
								
								src/Dotenv/Exception/InvalidPathException.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/Dotenv/Exception/InvalidPathException.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Exception; | ||||
|  | ||||
| use InvalidArgumentException; | ||||
|  | ||||
| final class InvalidPathException extends InvalidArgumentException implements ExceptionInterface | ||||
| { | ||||
|     // | ||||
| } | ||||
							
								
								
									
										12
									
								
								src/Dotenv/Exception/ValidationException.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/Dotenv/Exception/ValidationException.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Exception; | ||||
|  | ||||
| use RuntimeException; | ||||
|  | ||||
| final class ValidationException extends RuntimeException implements ExceptionInterface | ||||
| { | ||||
|     // | ||||
| } | ||||
							
								
								
									
										47
									
								
								src/Dotenv/Loader/Loader.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/Dotenv/Loader/Loader.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Loader; | ||||
|  | ||||
| use Dotenv\Parser\Entry; | ||||
| use Dotenv\Parser\Value; | ||||
| use Dotenv\Repository\RepositoryInterface; | ||||
|  | ||||
| final class Loader implements LoaderInterface | ||||
| { | ||||
|     /** | ||||
|      * Load the given entries into the repository. | ||||
|      * | ||||
|      * We'll substitute any nested variables, and send each variable to the | ||||
|      * repository, with the effect of actually mutating the environment. | ||||
|      * | ||||
|      * @param \Dotenv\Repository\RepositoryInterface $repository | ||||
|      * @param \Dotenv\Parser\Entry[]                 $entries | ||||
|      * | ||||
|      * @return array<string,string|null> | ||||
|      */ | ||||
|     public function load(RepositoryInterface $repository, array $entries) | ||||
|     { | ||||
|         return \array_reduce($entries, static function (array $vars, Entry $entry) use ($repository) { | ||||
|             $name = $entry->getName(); | ||||
|  | ||||
|             $value = $entry->getValue()->map(static function (Value $value) use ($repository) { | ||||
|                 return Resolver::resolve($repository, $value); | ||||
|             }); | ||||
|  | ||||
|             if ($value->isDefined()) { | ||||
|                 $inner = $value->get(); | ||||
|                 if ($repository->set($name, $inner)) { | ||||
|                     return \array_merge($vars, [$name => $inner]); | ||||
|                 } | ||||
|             } else { | ||||
|                 if ($repository->clear($name)) { | ||||
|                     return \array_merge($vars, [$name => null]); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return $vars; | ||||
|         }, []); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										20
									
								
								src/Dotenv/Loader/LoaderInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/Dotenv/Loader/LoaderInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Loader; | ||||
|  | ||||
| use Dotenv\Repository\RepositoryInterface; | ||||
|  | ||||
| interface LoaderInterface | ||||
| { | ||||
|     /** | ||||
|      * Load the given entries into the repository. | ||||
|      * | ||||
|      * @param \Dotenv\Repository\RepositoryInterface $repository | ||||
|      * @param \Dotenv\Parser\Entry[]                 $entries | ||||
|      * | ||||
|      * @return array<string,string|null> | ||||
|      */ | ||||
|     public function load(RepositoryInterface $repository, array $entries); | ||||
| } | ||||
							
								
								
									
										65
									
								
								src/Dotenv/Loader/Resolver.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/Dotenv/Loader/Resolver.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Loader; | ||||
|  | ||||
| use Dotenv\Parser\Value; | ||||
| use Dotenv\Repository\RepositoryInterface; | ||||
| use Dotenv\Util\Regex; | ||||
| use Dotenv\Util\Str; | ||||
| use PhpOption\Option; | ||||
|  | ||||
| final class Resolver | ||||
| { | ||||
|     /** | ||||
|      * This class is a singleton. | ||||
|      * | ||||
|      * @codeCoverageIgnore | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     private function __construct() | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Resolve the nested variables in the given value. | ||||
|      * | ||||
|      * Replaces ${varname} patterns in the allowed positions in the variable | ||||
|      * value by an existing environment variable. | ||||
|      * | ||||
|      * @param \Dotenv\Repository\RepositoryInterface $repository | ||||
|      * @param \Dotenv\Parser\Value                   $value | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public static function resolve(RepositoryInterface $repository, Value $value) | ||||
|     { | ||||
|         return \array_reduce($value->getVars(), static function (string $s, int $i) use ($repository) { | ||||
|             return Str::substr($s, 0, $i).self::resolveVariable($repository, Str::substr($s, $i)); | ||||
|         }, $value->getChars()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Resolve a single nested variable. | ||||
|      * | ||||
|      * @param \Dotenv\Repository\RepositoryInterface $repository | ||||
|      * @param string                                 $str | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     private static function resolveVariable(RepositoryInterface $repository, string $str) | ||||
|     { | ||||
|         return Regex::replaceCallback( | ||||
|             '/\A\${([a-zA-Z0-9_.]+)}/', | ||||
|             static function (array $matches) use ($repository) { | ||||
|                 return Option::fromValue($repository->get($matches[1])) | ||||
|                     ->getOrElse($matches[0]); | ||||
|             }, | ||||
|             $str, | ||||
|             1 | ||||
|         )->success()->getOrElse($str); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										59
									
								
								src/Dotenv/Parser/Entry.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/Dotenv/Parser/Entry.php
									
									
									
									
									
										Normal file
									
								
							| @@ -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); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										300
									
								
								src/Dotenv/Parser/EntryParser.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										300
									
								
								src/Dotenv/Parser/EntryParser.php
									
									
									
									
									
										Normal file
									
								
							| @@ -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") | ||||
|         ); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										58
									
								
								src/Dotenv/Parser/Lexer.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								src/Dotenv/Parser/Lexer.php
									
									
									
									
									
										Normal file
									
								
							| @@ -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]; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										127
									
								
								src/Dotenv/Parser/Lines.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								src/Dotenv/Parser/Lines.php
									
									
									
									
									
										Normal file
									
								
							| @@ -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] === '#'); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										53
									
								
								src/Dotenv/Parser/Parser.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/Dotenv/Parser/Parser.php
									
									
									
									
									
										Normal file
									
								
							| @@ -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([])); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										19
									
								
								src/Dotenv/Parser/ParserInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/Dotenv/Parser/ParserInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -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); | ||||
| } | ||||
							
								
								
									
										88
									
								
								src/Dotenv/Parser/Value.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								src/Dotenv/Parser/Value.php
									
									
									
									
									
										Normal file
									
								
							| @@ -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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										15
									
								
								src/Dotenv/Repository/Adapter/AdapterInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/Dotenv/Repository/Adapter/AdapterInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Repository\Adapter; | ||||
|  | ||||
| interface AdapterInterface extends ReaderInterface, WriterInterface | ||||
| { | ||||
|     /** | ||||
|      * Create a new instance of the adapter, if it is available. | ||||
|      * | ||||
|      * @return \PhpOption\Option<\Dotenv\Repository\Adapter\AdapterInterface> | ||||
|      */ | ||||
|     public static function create(); | ||||
| } | ||||
							
								
								
									
										89
									
								
								src/Dotenv/Repository/Adapter/ApacheAdapter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/Dotenv/Repository/Adapter/ApacheAdapter.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Repository\Adapter; | ||||
|  | ||||
| use PhpOption\None; | ||||
| use PhpOption\Option; | ||||
| use PhpOption\Some; | ||||
|  | ||||
| final class ApacheAdapter implements AdapterInterface | ||||
| { | ||||
|     /** | ||||
|      * Create a new apache adapter instance. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     private function __construct() | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a new instance of the adapter, if it is available. | ||||
|      * | ||||
|      * @return \PhpOption\Option<\Dotenv\Repository\Adapter\AdapterInterface> | ||||
|      */ | ||||
|     public static function create() | ||||
|     { | ||||
|         if (self::isSupported()) { | ||||
|             /** @var \PhpOption\Option<AdapterInterface> */ | ||||
|             return Some::create(new self()); | ||||
|         } | ||||
|  | ||||
|         return None::create(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Determines if the adapter is supported. | ||||
|      * | ||||
|      * This happens if PHP is running as an Apache module. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     private static function isSupported() | ||||
|     { | ||||
|         return \function_exists('apache_getenv') && \function_exists('apache_setenv'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Read an environment variable, if it exists. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * | ||||
|      * @return \PhpOption\Option<string> | ||||
|      */ | ||||
|     public function read(string $name) | ||||
|     { | ||||
|         /** @var \PhpOption\Option<string> */ | ||||
|         return Option::fromValue(apache_getenv($name))->filter(static function ($value) { | ||||
|             return \is_string($value) && $value !== ''; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Write to an environment variable, if possible. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * @param string           $value | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function write(string $name, string $value) | ||||
|     { | ||||
|         return apache_setenv($name, $value); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Delete an environment variable, if possible. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function delete(string $name) | ||||
|     { | ||||
|         return apache_setenv($name, ''); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										80
									
								
								src/Dotenv/Repository/Adapter/ArrayAdapter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								src/Dotenv/Repository/Adapter/ArrayAdapter.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Repository\Adapter; | ||||
|  | ||||
| use PhpOption\Option; | ||||
| use PhpOption\Some; | ||||
|  | ||||
| final class ArrayAdapter implements AdapterInterface | ||||
| { | ||||
|     /** | ||||
|      * The variables and their values. | ||||
|      * | ||||
|      * @var array<string,string> | ||||
|      */ | ||||
|     private $variables; | ||||
|  | ||||
|     /** | ||||
|      * Create a new array adapter instance. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     private function __construct() | ||||
|     { | ||||
|         $this->variables = []; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a new instance of the adapter, if it is available. | ||||
|      * | ||||
|      * @return \PhpOption\Option<\Dotenv\Repository\Adapter\AdapterInterface> | ||||
|      */ | ||||
|     public static function create() | ||||
|     { | ||||
|         /** @var \PhpOption\Option<AdapterInterface> */ | ||||
|         return Some::create(new self()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Read an environment variable, if it exists. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * | ||||
|      * @return \PhpOption\Option<string> | ||||
|      */ | ||||
|     public function read(string $name) | ||||
|     { | ||||
|         return Option::fromArraysValue($this->variables, $name); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Write to an environment variable, if possible. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * @param string           $value | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function write(string $name, string $value) | ||||
|     { | ||||
|         $this->variables[$name] = $value; | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Delete an environment variable, if possible. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function delete(string $name) | ||||
|     { | ||||
|         unset($this->variables[$name]); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										89
									
								
								src/Dotenv/Repository/Adapter/EnvConstAdapter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/Dotenv/Repository/Adapter/EnvConstAdapter.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Repository\Adapter; | ||||
|  | ||||
| use PhpOption\Option; | ||||
| use PhpOption\Some; | ||||
|  | ||||
| final class EnvConstAdapter implements AdapterInterface | ||||
| { | ||||
|     /** | ||||
|      * Create a new env const adapter instance. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     private function __construct() | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a new instance of the adapter, if it is available. | ||||
|      * | ||||
|      * @return \PhpOption\Option<\Dotenv\Repository\Adapter\AdapterInterface> | ||||
|      */ | ||||
|     public static function create() | ||||
|     { | ||||
|         /** @var \PhpOption\Option<AdapterInterface> */ | ||||
|         return Some::create(new self()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Read an environment variable, if it exists. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * | ||||
|      * @return \PhpOption\Option<string> | ||||
|      */ | ||||
|     public function read(string $name) | ||||
|     { | ||||
|         /** @var \PhpOption\Option<string> */ | ||||
|         return Option::fromArraysValue($_ENV, $name) | ||||
|             ->filter(static function ($value) { | ||||
|                 return \is_scalar($value); | ||||
|             }) | ||||
|             ->map(static function ($value) { | ||||
|                 if ($value === false) { | ||||
|                     return 'false'; | ||||
|                 } | ||||
|  | ||||
|                 if ($value === true) { | ||||
|                     return 'true'; | ||||
|                 } | ||||
|  | ||||
|                 /** @psalm-suppress PossiblyInvalidCast */ | ||||
|                 return (string) $value; | ||||
|             }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Write to an environment variable, if possible. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * @param string           $value | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function write(string $name, string $value) | ||||
|     { | ||||
|         $_ENV[$name] = $value; | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Delete an environment variable, if possible. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function delete(string $name) | ||||
|     { | ||||
|         unset($_ENV[$name]); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										85
									
								
								src/Dotenv/Repository/Adapter/GuardedWriter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/Dotenv/Repository/Adapter/GuardedWriter.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,85 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Repository\Adapter; | ||||
|  | ||||
| final class GuardedWriter implements WriterInterface | ||||
| { | ||||
|     /** | ||||
|      * The inner writer to use. | ||||
|      * | ||||
|      * @var \Dotenv\Repository\Adapter\WriterInterface | ||||
|      */ | ||||
|     private $writer; | ||||
|  | ||||
|     /** | ||||
|      * The variable name allow list. | ||||
|      * | ||||
|      * @var string[] | ||||
|      */ | ||||
|     private $allowList; | ||||
|  | ||||
|     /** | ||||
|      * Create a new guarded writer instance. | ||||
|      * | ||||
|      * @param \Dotenv\Repository\Adapter\WriterInterface $writer | ||||
|      * @param string[]                                   $allowList | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct(WriterInterface $writer, array $allowList) | ||||
|     { | ||||
|         $this->writer = $writer; | ||||
|         $this->allowList = $allowList; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Write to an environment variable, if possible. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * @param string           $value | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function write(string $name, string $value) | ||||
|     { | ||||
|         // Don't set non-allowed variables | ||||
|         if (!$this->isAllowed($name)) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Set the value on the inner writer | ||||
|         return $this->writer->write($name, $value); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Delete an environment variable, if possible. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function delete(string $name) | ||||
|     { | ||||
|         // Don't clear non-allowed variables | ||||
|         if (!$this->isAllowed($name)) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Set the value on the inner writer | ||||
|         return $this->writer->delete($name); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Determine if the given variable is allowed. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function isAllowed(string $name) | ||||
|     { | ||||
|         return \in_array($name, $this->allowList, true); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										110
									
								
								src/Dotenv/Repository/Adapter/ImmutableWriter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								src/Dotenv/Repository/Adapter/ImmutableWriter.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Repository\Adapter; | ||||
|  | ||||
| final class ImmutableWriter implements WriterInterface | ||||
| { | ||||
|     /** | ||||
|      * The inner writer to use. | ||||
|      * | ||||
|      * @var \Dotenv\Repository\Adapter\WriterInterface | ||||
|      */ | ||||
|     private $writer; | ||||
|  | ||||
|     /** | ||||
|      * The inner reader to use. | ||||
|      * | ||||
|      * @var \Dotenv\Repository\Adapter\ReaderInterface | ||||
|      */ | ||||
|     private $reader; | ||||
|  | ||||
|     /** | ||||
|      * The record of loaded variables. | ||||
|      * | ||||
|      * @var array<string,string> | ||||
|      */ | ||||
|     private $loaded; | ||||
|  | ||||
|     /** | ||||
|      * Create a new immutable writer instance. | ||||
|      * | ||||
|      * @param \Dotenv\Repository\Adapter\WriterInterface $writer | ||||
|      * @param \Dotenv\Repository\Adapter\ReaderInterface $reader | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct(WriterInterface $writer, ReaderInterface $reader) | ||||
|     { | ||||
|         $this->writer = $writer; | ||||
|         $this->reader = $reader; | ||||
|         $this->loaded = []; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Write to an environment variable, if possible. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * @param string           $value | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function write(string $name, string $value) | ||||
|     { | ||||
|         // Don't overwrite existing environment variables | ||||
|         // Ruby's dotenv does this with `ENV[key] ||= value` | ||||
|         if ($this->isExternallyDefined($name)) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Set the value on the inner writer | ||||
|         if (!$this->writer->write($name, $value)) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Record that we have loaded the variable | ||||
|         $this->loaded[$name] = ''; | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Delete an environment variable, if possible. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function delete(string $name) | ||||
|     { | ||||
|         // Don't clear existing environment variables | ||||
|         if ($this->isExternallyDefined($name)) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Clear the value on the inner writer | ||||
|         if (!$this->writer->delete($name)) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Leave the variable as fair game | ||||
|         unset($this->loaded[$name]); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Determine if the given variable is externally defined. | ||||
|      * | ||||
|      * That is, is it an "existing" variable. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function isExternallyDefined(string $name) | ||||
|     { | ||||
|         return $this->reader->read($name)->isDefined() && !isset($this->loaded[$name]); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										48
									
								
								src/Dotenv/Repository/Adapter/MultiReader.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/Dotenv/Repository/Adapter/MultiReader.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Repository\Adapter; | ||||
|  | ||||
| use PhpOption\None; | ||||
|  | ||||
| final class MultiReader implements ReaderInterface | ||||
| { | ||||
|     /** | ||||
|      * The set of readers to use. | ||||
|      * | ||||
|      * @var \Dotenv\Repository\Adapter\ReaderInterface[] | ||||
|      */ | ||||
|     private $readers; | ||||
|  | ||||
|     /** | ||||
|      * Create a new multi-reader instance. | ||||
|      * | ||||
|      * @param \Dotenv\Repository\Adapter\ReaderInterface[] $readers | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct(array $readers) | ||||
|     { | ||||
|         $this->readers = $readers; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Read an environment variable, if it exists. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * | ||||
|      * @return \PhpOption\Option<string> | ||||
|      */ | ||||
|     public function read(string $name) | ||||
|     { | ||||
|         foreach ($this->readers as $reader) { | ||||
|             $result = $reader->read($name); | ||||
|             if ($result->isDefined()) { | ||||
|                 return $result; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return None::create(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										64
									
								
								src/Dotenv/Repository/Adapter/MultiWriter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/Dotenv/Repository/Adapter/MultiWriter.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Repository\Adapter; | ||||
|  | ||||
| final class MultiWriter implements WriterInterface | ||||
| { | ||||
|     /** | ||||
|      * The set of writers to use. | ||||
|      * | ||||
|      * @var \Dotenv\Repository\Adapter\WriterInterface[] | ||||
|      */ | ||||
|     private $writers; | ||||
|  | ||||
|     /** | ||||
|      * Create a new multi-writer instance. | ||||
|      * | ||||
|      * @param \Dotenv\Repository\Adapter\WriterInterface[] $writers | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct(array $writers) | ||||
|     { | ||||
|         $this->writers = $writers; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Write to an environment variable, if possible. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * @param string           $value | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function write(string $name, string $value) | ||||
|     { | ||||
|         foreach ($this->writers as $writers) { | ||||
|             if (!$writers->write($name, $value)) { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Delete an environment variable, if possible. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function delete(string $name) | ||||
|     { | ||||
|         foreach ($this->writers as $writers) { | ||||
|             if (!$writers->delete($name)) { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										91
									
								
								src/Dotenv/Repository/Adapter/PutenvAdapter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								src/Dotenv/Repository/Adapter/PutenvAdapter.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Repository\Adapter; | ||||
|  | ||||
| use PhpOption\None; | ||||
| use PhpOption\Option; | ||||
| use PhpOption\Some; | ||||
|  | ||||
| final class PutenvAdapter implements AdapterInterface | ||||
| { | ||||
|     /** | ||||
|      * Create a new putenv adapter instance. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     private function __construct() | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a new instance of the adapter, if it is available. | ||||
|      * | ||||
|      * @return \PhpOption\Option<\Dotenv\Repository\Adapter\AdapterInterface> | ||||
|      */ | ||||
|     public static function create() | ||||
|     { | ||||
|         if (self::isSupported()) { | ||||
|             /** @var \PhpOption\Option<AdapterInterface> */ | ||||
|             return Some::create(new self()); | ||||
|         } | ||||
|  | ||||
|         return None::create(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Determines if the adapter is supported. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     private static function isSupported() | ||||
|     { | ||||
|         return \function_exists('getenv') && \function_exists('putenv'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Read an environment variable, if it exists. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * | ||||
|      * @return \PhpOption\Option<string> | ||||
|      */ | ||||
|     public function read(string $name) | ||||
|     { | ||||
|         /** @var \PhpOption\Option<string> */ | ||||
|         return Option::fromValue(\getenv($name), false)->filter(static function ($value) { | ||||
|             return \is_string($value); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Write to an environment variable, if possible. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * @param string           $value | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function write(string $name, string $value) | ||||
|     { | ||||
|         \putenv("$name=$value"); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Delete an environment variable, if possible. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function delete(string $name) | ||||
|     { | ||||
|         \putenv($name); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										17
									
								
								src/Dotenv/Repository/Adapter/ReaderInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/Dotenv/Repository/Adapter/ReaderInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Repository\Adapter; | ||||
|  | ||||
| interface ReaderInterface | ||||
| { | ||||
|     /** | ||||
|      * Read an environment variable, if it exists. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * | ||||
|      * @return \PhpOption\Option<string> | ||||
|      */ | ||||
|     public function read(string $name); | ||||
| } | ||||
							
								
								
									
										104
									
								
								src/Dotenv/Repository/Adapter/ReplacingWriter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								src/Dotenv/Repository/Adapter/ReplacingWriter.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Repository\Adapter; | ||||
|  | ||||
| final class ReplacingWriter implements WriterInterface | ||||
| { | ||||
|     /** | ||||
|      * The inner writer to use. | ||||
|      * | ||||
|      * @var \Dotenv\Repository\Adapter\WriterInterface | ||||
|      */ | ||||
|     private $writer; | ||||
|  | ||||
|     /** | ||||
|      * The inner reader to use. | ||||
|      * | ||||
|      * @var \Dotenv\Repository\Adapter\ReaderInterface | ||||
|      */ | ||||
|     private $reader; | ||||
|  | ||||
|     /** | ||||
|      * The record of seen variables. | ||||
|      * | ||||
|      * @var array<string,string> | ||||
|      */ | ||||
|     private $seen; | ||||
|  | ||||
|     /** | ||||
|      * Create a new replacement writer instance. | ||||
|      * | ||||
|      * @param \Dotenv\Repository\Adapter\WriterInterface $writer | ||||
|      * @param \Dotenv\Repository\Adapter\ReaderInterface $reader | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct(WriterInterface $writer, ReaderInterface $reader) | ||||
|     { | ||||
|         $this->writer = $writer; | ||||
|         $this->reader = $reader; | ||||
|         $this->seen = []; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Write to an environment variable, if possible. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * @param string           $value | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function write(string $name, string $value) | ||||
|     { | ||||
|         if ($this->exists($name)) { | ||||
|             return $this->writer->write($name, $value); | ||||
|         } | ||||
|  | ||||
|         // succeed if nothing to do | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Delete an environment variable, if possible. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function delete(string $name) | ||||
|     { | ||||
|         if ($this->exists($name)) { | ||||
|             return $this->writer->delete($name); | ||||
|         } | ||||
|  | ||||
|         // succeed if nothing to do | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Does the given environment variable exist. | ||||
|      * | ||||
|      * Returns true if it currently exists, or existed at any point in the past | ||||
|      * that we are aware of. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     private function exists(string $name) | ||||
|     { | ||||
|         if (isset($this->seen[$name])) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         if ($this->reader->read($name)->isDefined()) { | ||||
|             $this->seen[$name] = ''; | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										89
									
								
								src/Dotenv/Repository/Adapter/ServerConstAdapter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/Dotenv/Repository/Adapter/ServerConstAdapter.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Repository\Adapter; | ||||
|  | ||||
| use PhpOption\Option; | ||||
| use PhpOption\Some; | ||||
|  | ||||
| final class ServerConstAdapter implements AdapterInterface | ||||
| { | ||||
|     /** | ||||
|      * Create a new server const adapter instance. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     private function __construct() | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a new instance of the adapter, if it is available. | ||||
|      * | ||||
|      * @return \PhpOption\Option<\Dotenv\Repository\Adapter\AdapterInterface> | ||||
|      */ | ||||
|     public static function create() | ||||
|     { | ||||
|         /** @var \PhpOption\Option<AdapterInterface> */ | ||||
|         return Some::create(new self()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Read an environment variable, if it exists. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * | ||||
|      * @return \PhpOption\Option<string> | ||||
|      */ | ||||
|     public function read(string $name) | ||||
|     { | ||||
|         /** @var \PhpOption\Option<string> */ | ||||
|         return Option::fromArraysValue($_SERVER, $name) | ||||
|             ->filter(static function ($value) { | ||||
|                 return \is_scalar($value); | ||||
|             }) | ||||
|             ->map(static function ($value) { | ||||
|                 if ($value === false) { | ||||
|                     return 'false'; | ||||
|                 } | ||||
|  | ||||
|                 if ($value === true) { | ||||
|                     return 'true'; | ||||
|                 } | ||||
|  | ||||
|                 /** @psalm-suppress PossiblyInvalidCast */ | ||||
|                 return (string) $value; | ||||
|             }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Write to an environment variable, if possible. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * @param string           $value | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function write(string $name, string $value) | ||||
|     { | ||||
|         $_SERVER[$name] = $value; | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Delete an environment variable, if possible. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function delete(string $name) | ||||
|     { | ||||
|         unset($_SERVER[$name]); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										27
									
								
								src/Dotenv/Repository/Adapter/WriterInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/Dotenv/Repository/Adapter/WriterInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Repository\Adapter; | ||||
|  | ||||
| interface WriterInterface | ||||
| { | ||||
|     /** | ||||
|      * Write to an environment variable, if possible. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * @param string           $value | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function write(string $name, string $value); | ||||
|  | ||||
|     /** | ||||
|      * Delete an environment variable, if possible. | ||||
|      * | ||||
|      * @param non-empty-string $name | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function delete(string $name); | ||||
| } | ||||
							
								
								
									
										107
									
								
								src/Dotenv/Repository/AdapterRepository.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								src/Dotenv/Repository/AdapterRepository.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Repository; | ||||
|  | ||||
| use Dotenv\Repository\Adapter\ReaderInterface; | ||||
| use Dotenv\Repository\Adapter\WriterInterface; | ||||
| use InvalidArgumentException; | ||||
|  | ||||
| final class AdapterRepository implements RepositoryInterface | ||||
| { | ||||
|     /** | ||||
|      * The reader to use. | ||||
|      * | ||||
|      * @var \Dotenv\Repository\Adapter\ReaderInterface | ||||
|      */ | ||||
|     private $reader; | ||||
|  | ||||
|     /** | ||||
|      * The writer to use. | ||||
|      * | ||||
|      * @var \Dotenv\Repository\Adapter\WriterInterface | ||||
|      */ | ||||
|     private $writer; | ||||
|  | ||||
|     /** | ||||
|      * Create a new adapter repository instance. | ||||
|      * | ||||
|      * @param \Dotenv\Repository\Adapter\ReaderInterface $reader | ||||
|      * @param \Dotenv\Repository\Adapter\WriterInterface $writer | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct(ReaderInterface $reader, WriterInterface $writer) | ||||
|     { | ||||
|         $this->reader = $reader; | ||||
|         $this->writer = $writer; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Determine if the given environment variable is defined. | ||||
|      * | ||||
|      * @param string $name | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function has(string $name) | ||||
|     { | ||||
|         return '' !== $name && $this->reader->read($name)->isDefined(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get an environment variable. | ||||
|      * | ||||
|      * @param string $name | ||||
|      * | ||||
|      * @throws \InvalidArgumentException | ||||
|      * | ||||
|      * @return string|null | ||||
|      */ | ||||
|     public function get(string $name) | ||||
|     { | ||||
|         if ('' === $name) { | ||||
|             throw new InvalidArgumentException('Expected name to be a non-empty string.'); | ||||
|         } | ||||
|  | ||||
|         return $this->reader->read($name)->getOrElse(null); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set an environment variable. | ||||
|      * | ||||
|      * @param string $name | ||||
|      * @param string $value | ||||
|      * | ||||
|      * @throws \InvalidArgumentException | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function set(string $name, string $value) | ||||
|     { | ||||
|         if ('' === $name) { | ||||
|             throw new InvalidArgumentException('Expected name to be a non-empty string.'); | ||||
|         } | ||||
|  | ||||
|         return $this->writer->write($name, $value); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Clear an environment variable. | ||||
|      * | ||||
|      * @param string $name | ||||
|      * | ||||
|      * @throws \InvalidArgumentException | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function clear(string $name) | ||||
|     { | ||||
|         if ('' === $name) { | ||||
|             throw new InvalidArgumentException('Expected name to be a non-empty string.'); | ||||
|         } | ||||
|  | ||||
|         return $this->writer->delete($name); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										272
									
								
								src/Dotenv/Repository/RepositoryBuilder.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										272
									
								
								src/Dotenv/Repository/RepositoryBuilder.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,272 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Repository; | ||||
|  | ||||
| use Dotenv\Repository\Adapter\AdapterInterface; | ||||
| use Dotenv\Repository\Adapter\EnvConstAdapter; | ||||
| use Dotenv\Repository\Adapter\GuardedWriter; | ||||
| use Dotenv\Repository\Adapter\ImmutableWriter; | ||||
| use Dotenv\Repository\Adapter\MultiReader; | ||||
| use Dotenv\Repository\Adapter\MultiWriter; | ||||
| use Dotenv\Repository\Adapter\ReaderInterface; | ||||
| use Dotenv\Repository\Adapter\ServerConstAdapter; | ||||
| use Dotenv\Repository\Adapter\WriterInterface; | ||||
| use InvalidArgumentException; | ||||
| use PhpOption\Some; | ||||
| use ReflectionClass; | ||||
|  | ||||
| final class RepositoryBuilder | ||||
| { | ||||
|     /** | ||||
|      * The set of default adapters. | ||||
|      */ | ||||
|     private const DEFAULT_ADAPTERS = [ | ||||
|         ServerConstAdapter::class, | ||||
|         EnvConstAdapter::class, | ||||
|     ]; | ||||
|  | ||||
|     /** | ||||
|      * The set of readers to use. | ||||
|      * | ||||
|      * @var \Dotenv\Repository\Adapter\ReaderInterface[] | ||||
|      */ | ||||
|     private $readers; | ||||
|  | ||||
|     /** | ||||
|      * The set of writers to use. | ||||
|      * | ||||
|      * @var \Dotenv\Repository\Adapter\WriterInterface[] | ||||
|      */ | ||||
|     private $writers; | ||||
|  | ||||
|     /** | ||||
|      * Are we immutable? | ||||
|      * | ||||
|      * @var bool | ||||
|      */ | ||||
|     private $immutable; | ||||
|  | ||||
|     /** | ||||
|      * The variable name allow list. | ||||
|      * | ||||
|      * @var string[]|null | ||||
|      */ | ||||
|     private $allowList; | ||||
|  | ||||
|     /** | ||||
|      * Create a new repository builder instance. | ||||
|      * | ||||
|      * @param \Dotenv\Repository\Adapter\ReaderInterface[] $readers | ||||
|      * @param \Dotenv\Repository\Adapter\WriterInterface[] $writers | ||||
|      * @param bool                                         $immutable | ||||
|      * @param string[]|null                                $allowList | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     private function __construct(array $readers = [], array $writers = [], bool $immutable = false, array $allowList = null) | ||||
|     { | ||||
|         $this->readers = $readers; | ||||
|         $this->writers = $writers; | ||||
|         $this->immutable = $immutable; | ||||
|         $this->allowList = $allowList; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a new repository builder instance with no adapters added. | ||||
|      * | ||||
|      * @return \Dotenv\Repository\RepositoryBuilder | ||||
|      */ | ||||
|     public static function createWithNoAdapters() | ||||
|     { | ||||
|         return new self(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a new repository builder instance with the default adapters added. | ||||
|      * | ||||
|      * @return \Dotenv\Repository\RepositoryBuilder | ||||
|      */ | ||||
|     public static function createWithDefaultAdapters() | ||||
|     { | ||||
|         $adapters = \iterator_to_array(self::defaultAdapters()); | ||||
|  | ||||
|         return new self($adapters, $adapters); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return the array of default adapters. | ||||
|      * | ||||
|      * @return \Generator<\Dotenv\Repository\Adapter\AdapterInterface> | ||||
|      */ | ||||
|     private static function defaultAdapters() | ||||
|     { | ||||
|         foreach (self::DEFAULT_ADAPTERS as $adapter) { | ||||
|             $instance = $adapter::create(); | ||||
|             if ($instance->isDefined()) { | ||||
|                 yield $instance->get(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Determine if the given name if of an adapterclass. | ||||
|      * | ||||
|      * @param string $name | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     private static function isAnAdapterClass(string $name) | ||||
|     { | ||||
|         if (!\class_exists($name)) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return (new ReflectionClass($name))->implementsInterface(AdapterInterface::class); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a repository builder with the given reader added. | ||||
|      * | ||||
|      * Accepts either a reader instance, or a class-string for an adapter. If | ||||
|      * the adapter is not supported, then we silently skip adding it. | ||||
|      * | ||||
|      * @param \Dotenv\Repository\Adapter\ReaderInterface|string $reader | ||||
|      * | ||||
|      * @throws \InvalidArgumentException | ||||
|      * | ||||
|      * @return \Dotenv\Repository\RepositoryBuilder | ||||
|      */ | ||||
|     public function addReader($reader) | ||||
|     { | ||||
|         if (!(\is_string($reader) && self::isAnAdapterClass($reader)) && !($reader instanceof ReaderInterface)) { | ||||
|             throw new InvalidArgumentException( | ||||
|                 \sprintf( | ||||
|                     'Expected either an instance of %s or a class-string implementing %s', | ||||
|                     ReaderInterface::class, | ||||
|                     AdapterInterface::class | ||||
|                 ) | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         $optional = Some::create($reader)->flatMap(static function ($reader) { | ||||
|             return \is_string($reader) ? $reader::create() : Some::create($reader); | ||||
|         }); | ||||
|  | ||||
|         $readers = \array_merge($this->readers, \iterator_to_array($optional)); | ||||
|  | ||||
|         return new self($readers, $this->writers, $this->immutable, $this->allowList); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a repository builder with the given writer added. | ||||
|      * | ||||
|      * Accepts either a writer instance, or a class-string for an adapter. If | ||||
|      * the adapter is not supported, then we silently skip adding it. | ||||
|      * | ||||
|      * @param \Dotenv\Repository\Adapter\WriterInterface|string $writer | ||||
|      * | ||||
|      * @throws \InvalidArgumentException | ||||
|      * | ||||
|      * @return \Dotenv\Repository\RepositoryBuilder | ||||
|      */ | ||||
|     public function addWriter($writer) | ||||
|     { | ||||
|         if (!(\is_string($writer) && self::isAnAdapterClass($writer)) && !($writer instanceof WriterInterface)) { | ||||
|             throw new InvalidArgumentException( | ||||
|                 \sprintf( | ||||
|                     'Expected either an instance of %s or a class-string implementing %s', | ||||
|                     WriterInterface::class, | ||||
|                     AdapterInterface::class | ||||
|                 ) | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         $optional = Some::create($writer)->flatMap(static function ($writer) { | ||||
|             return \is_string($writer) ? $writer::create() : Some::create($writer); | ||||
|         }); | ||||
|  | ||||
|         $writers = \array_merge($this->writers, \iterator_to_array($optional)); | ||||
|  | ||||
|         return new self($this->readers, $writers, $this->immutable, $this->allowList); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a repository builder with the given adapter added. | ||||
|      * | ||||
|      * Accepts either an adapter instance, or a class-string for an adapter. If | ||||
|      * the adapter is not supported, then we silently skip adding it. We will | ||||
|      * add the adapter as both a reader and a writer. | ||||
|      * | ||||
|      * @param \Dotenv\Repository\Adapter\WriterInterface|string $adapter | ||||
|      * | ||||
|      * @throws \InvalidArgumentException | ||||
|      * | ||||
|      * @return \Dotenv\Repository\RepositoryBuilder | ||||
|      */ | ||||
|     public function addAdapter($adapter) | ||||
|     { | ||||
|         if (!(\is_string($adapter) && self::isAnAdapterClass($adapter)) && !($adapter instanceof AdapterInterface)) { | ||||
|             throw new InvalidArgumentException( | ||||
|                 \sprintf( | ||||
|                     'Expected either an instance of %s or a class-string implementing %s', | ||||
|                     WriterInterface::class, | ||||
|                     AdapterInterface::class | ||||
|                 ) | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         $optional = Some::create($adapter)->flatMap(static function ($adapter) { | ||||
|             return \is_string($adapter) ? $adapter::create() : Some::create($adapter); | ||||
|         }); | ||||
|  | ||||
|         $readers = \array_merge($this->readers, \iterator_to_array($optional)); | ||||
|         $writers = \array_merge($this->writers, \iterator_to_array($optional)); | ||||
|  | ||||
|         return new self($readers, $writers, $this->immutable, $this->allowList); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a repository builder with mutability enabled. | ||||
|      * | ||||
|      * @return \Dotenv\Repository\RepositoryBuilder | ||||
|      */ | ||||
|     public function immutable() | ||||
|     { | ||||
|         return new self($this->readers, $this->writers, true, $this->allowList); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a repository builder with the given allow list. | ||||
|      * | ||||
|      * @param string[]|null $allowList | ||||
|      * | ||||
|      * @return \Dotenv\Repository\RepositoryBuilder | ||||
|      */ | ||||
|     public function allowList(array $allowList = null) | ||||
|     { | ||||
|         return new self($this->readers, $this->writers, $this->immutable, $allowList); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a new repository instance. | ||||
|      * | ||||
|      * @return \Dotenv\Repository\RepositoryInterface | ||||
|      */ | ||||
|     public function make() | ||||
|     { | ||||
|         $reader = new MultiReader($this->readers); | ||||
|         $writer = new MultiWriter($this->writers); | ||||
|  | ||||
|         if ($this->immutable) { | ||||
|             $writer = new ImmutableWriter($writer, $reader); | ||||
|         } | ||||
|  | ||||
|         if ($this->allowList !== null) { | ||||
|             $writer = new GuardedWriter($writer, $this->allowList); | ||||
|         } | ||||
|  | ||||
|         return new AdapterRepository($reader, $writer); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										51
									
								
								src/Dotenv/Repository/RepositoryInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/Dotenv/Repository/RepositoryInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Repository; | ||||
|  | ||||
| interface RepositoryInterface | ||||
| { | ||||
|     /** | ||||
|      * Determine if the given environment variable is defined. | ||||
|      * | ||||
|      * @param string $name | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function has(string $name); | ||||
|  | ||||
|     /** | ||||
|      * Get an environment variable. | ||||
|      * | ||||
|      * @param string $name | ||||
|      * | ||||
|      * @throws \InvalidArgumentException | ||||
|      * | ||||
|      * @return string|null | ||||
|      */ | ||||
|     public function get(string $name); | ||||
|  | ||||
|     /** | ||||
|      * Set an environment variable. | ||||
|      * | ||||
|      * @param string $name | ||||
|      * @param string $value | ||||
|      * | ||||
|      * @throws \InvalidArgumentException | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function set(string $name, string $value); | ||||
|  | ||||
|     /** | ||||
|      * Clear an environment variable. | ||||
|      * | ||||
|      * @param string $name | ||||
|      * | ||||
|      * @throws \InvalidArgumentException | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function clear(string $name); | ||||
| } | ||||
							
								
								
									
										44
									
								
								src/Dotenv/Store/File/Paths.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/Dotenv/Store/File/Paths.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Store\File; | ||||
|  | ||||
| /** | ||||
|  * @internal | ||||
|  */ | ||||
| final class Paths | ||||
| { | ||||
|     /** | ||||
|      * This class is a singleton. | ||||
|      * | ||||
|      * @codeCoverageIgnore | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     private function __construct() | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										81
									
								
								src/Dotenv/Store/File/Reader.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								src/Dotenv/Store/File/Reader.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Store\File; | ||||
|  | ||||
| use Dotenv\Exception\InvalidEncodingException; | ||||
| use Dotenv\Util\Str; | ||||
| use PhpOption\Option; | ||||
|  | ||||
| /** | ||||
|  * @internal | ||||
|  */ | ||||
| final class Reader | ||||
| { | ||||
|     /** | ||||
|      * This class is a singleton. | ||||
|      * | ||||
|      * @codeCoverageIgnore | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     private function __construct() | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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 | ||||
|      * @param string|null $fileEncoding | ||||
|      * | ||||
|      * @throws \Dotenv\Exception\InvalidEncodingException | ||||
|      * | ||||
|      * @return array<string,string> | ||||
|      */ | ||||
|     public static function read(array $filePaths, bool $shortCircuit = true, string $fileEncoding = null) | ||||
|     { | ||||
|         $output = []; | ||||
|  | ||||
|         foreach ($filePaths as $filePath) { | ||||
|             $content = self::readFromFile($filePath, $fileEncoding); | ||||
|             if ($content->isDefined()) { | ||||
|                 $output[$filePath] = $content->get(); | ||||
|                 if ($shortCircuit) { | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $output; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Read the given file. | ||||
|      * | ||||
|      * @param string      $path | ||||
|      * @param string|null $encoding | ||||
|      * | ||||
|      * @throws \Dotenv\Exception\InvalidEncodingException | ||||
|      * | ||||
|      * @return \PhpOption\Option<string> | ||||
|      */ | ||||
|     private static function readFromFile(string $path, string $encoding = null) | ||||
|     { | ||||
|         /** @var Option<string> */ | ||||
|         $content = Option::fromValue(@\file_get_contents($path), false); | ||||
|  | ||||
|         return $content->flatMap(static function (string $content) use ($encoding) { | ||||
|             return Str::utf8($content, $encoding)->mapError(static function (string $error) { | ||||
|                 throw new InvalidEncodingException($error); | ||||
|             })->success(); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										72
									
								
								src/Dotenv/Store/FileStore.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/Dotenv/Store/FileStore.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Store; | ||||
|  | ||||
| use Dotenv\Exception\InvalidPathException; | ||||
| use Dotenv\Store\File\Reader; | ||||
|  | ||||
| final class FileStore implements StoreInterface | ||||
| { | ||||
|     /** | ||||
|      * The file paths. | ||||
|      * | ||||
|      * @var string[] | ||||
|      */ | ||||
|     private $filePaths; | ||||
|  | ||||
|     /** | ||||
|      * Should file loading short circuit? | ||||
|      * | ||||
|      * @var bool | ||||
|      */ | ||||
|     private $shortCircuit; | ||||
|  | ||||
|     /** | ||||
|      * The file encoding. | ||||
|      * | ||||
|      * @var string|null | ||||
|      */ | ||||
|     private $fileEncoding; | ||||
|  | ||||
|     /** | ||||
|      * Create a new file store instance. | ||||
|      * | ||||
|      * @param string[]    $filePaths | ||||
|      * @param bool        $shortCircuit | ||||
|      * @param string|null $fileEncoding | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct(array $filePaths, bool $shortCircuit, string $fileEncoding = null) | ||||
|     { | ||||
|         $this->filePaths = $filePaths; | ||||
|         $this->shortCircuit = $shortCircuit; | ||||
|         $this->fileEncoding = $fileEncoding; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Read the content of the environment file(s). | ||||
|      * | ||||
|      * @throws \Dotenv\Exception\InvalidEncodingException|\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, $this->fileEncoding); | ||||
|  | ||||
|         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)) | ||||
|         ); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										141
									
								
								src/Dotenv/Store/StoreBuilder.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								src/Dotenv/Store/StoreBuilder.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,141 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Store; | ||||
|  | ||||
| use Dotenv\Store\File\Paths; | ||||
|  | ||||
| final class StoreBuilder | ||||
| { | ||||
|     /** | ||||
|      * The of default name. | ||||
|      */ | ||||
|     private const DEFAULT_NAME = '.env'; | ||||
|  | ||||
|     /** | ||||
|      * The paths to search within. | ||||
|      * | ||||
|      * @var string[] | ||||
|      */ | ||||
|     private $paths; | ||||
|  | ||||
|     /** | ||||
|      * The file names to search for. | ||||
|      * | ||||
|      * @var string[] | ||||
|      */ | ||||
|     private $names; | ||||
|  | ||||
|     /** | ||||
|      * Should file loading short circuit? | ||||
|      * | ||||
|      * @var bool | ||||
|      */ | ||||
|     private $shortCircuit; | ||||
|  | ||||
|     /** | ||||
|      * The file encoding. | ||||
|      * | ||||
|      * @var string|null | ||||
|      */ | ||||
|     private $fileEncoding; | ||||
|  | ||||
|     /** | ||||
|      * Create a new store builder instance. | ||||
|      * | ||||
|      * @param string[]    $paths | ||||
|      * @param string[]    $names | ||||
|      * @param bool        $shortCircuit | ||||
|      * @param string|null $fileEncoding | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     private function __construct(array $paths = [], array $names = [], bool $shortCircuit = false, string $fileEncoding = null) | ||||
|     { | ||||
|         $this->paths = $paths; | ||||
|         $this->names = $names; | ||||
|         $this->shortCircuit = $shortCircuit; | ||||
|         $this->fileEncoding = $fileEncoding; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a new store builder instance with no names. | ||||
|      * | ||||
|      * @return \Dotenv\Store\StoreBuilder | ||||
|      */ | ||||
|     public static function createWithNoNames() | ||||
|     { | ||||
|         return new self(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a new store builder instance with the default name. | ||||
|      * | ||||
|      * @return \Dotenv\Store\StoreBuilder | ||||
|      */ | ||||
|     public static function createWithDefaultName() | ||||
|     { | ||||
|         return new self([], [self::DEFAULT_NAME]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a store builder with the given path added. | ||||
|      * | ||||
|      * @param string $path | ||||
|      * | ||||
|      * @return \Dotenv\Store\StoreBuilder | ||||
|      */ | ||||
|     public function addPath(string $path) | ||||
|     { | ||||
|         return new self(\array_merge($this->paths, [$path]), $this->names, $this->shortCircuit, $this->fileEncoding); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a store builder with the given name added. | ||||
|      * | ||||
|      * @param string $name | ||||
|      * | ||||
|      * @return \Dotenv\Store\StoreBuilder | ||||
|      */ | ||||
|     public function addName(string $name) | ||||
|     { | ||||
|         return new self($this->paths, \array_merge($this->names, [$name]), $this->shortCircuit, $this->fileEncoding); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a store builder with short circuit mode enabled. | ||||
|      * | ||||
|      * @return \Dotenv\Store\StoreBuilder | ||||
|      */ | ||||
|     public function shortCircuit() | ||||
|     { | ||||
|         return new self($this->paths, $this->names, true, $this->fileEncoding); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a store builder with the specified file encoding. | ||||
|      * | ||||
|      * @param string|null $fileEncoding | ||||
|      * | ||||
|      * @return \Dotenv\Store\StoreBuilder | ||||
|      */ | ||||
|     public function fileEncoding(string $fileEncoding = null) | ||||
|     { | ||||
|         return new self($this->paths, $this->names, $this->shortCircuit, $fileEncoding); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a new store instance. | ||||
|      * | ||||
|      * @return \Dotenv\Store\StoreInterface | ||||
|      */ | ||||
|     public function make() | ||||
|     { | ||||
|         return new FileStore( | ||||
|             Paths::filePaths($this->paths, $this->names), | ||||
|             $this->shortCircuit, | ||||
|             $this->fileEncoding | ||||
|         ); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										17
									
								
								src/Dotenv/Store/StoreInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/Dotenv/Store/StoreInterface.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Store; | ||||
|  | ||||
| interface StoreInterface | ||||
| { | ||||
|     /** | ||||
|      * Read the content of the environment file(s). | ||||
|      * | ||||
|      * @throws \Dotenv\Exception\InvalidEncodingException|\Dotenv\Exception\InvalidPathException | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function read(); | ||||
| } | ||||
							
								
								
									
										37
									
								
								src/Dotenv/Store/StringStore.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/Dotenv/Store/StringStore.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| 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(string $content) | ||||
|     { | ||||
|         $this->content = $content; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Read the content of the environment file(s). | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function read() | ||||
|     { | ||||
|         return $this->content; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										112
									
								
								src/Dotenv/Util/Regex.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								src/Dotenv/Util/Regex.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,112 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Util; | ||||
|  | ||||
| use GrahamCampbell\ResultType\Error; | ||||
| use GrahamCampbell\ResultType\Success; | ||||
|  | ||||
| /** | ||||
|  * @internal | ||||
|  */ | ||||
| final class Regex | ||||
| { | ||||
|     /** | ||||
|      * This class is a singleton. | ||||
|      * | ||||
|      * @codeCoverageIgnore | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     private function __construct() | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Perform a preg match, wrapping up the result. | ||||
|      * | ||||
|      * @param string $pattern | ||||
|      * @param string $subject | ||||
|      * | ||||
|      * @return \GrahamCampbell\ResultType\Result<bool,string> | ||||
|      */ | ||||
|     public static function matches(string $pattern, string $subject) | ||||
|     { | ||||
|         return self::pregAndWrap(static function (string $subject) use ($pattern) { | ||||
|             return @\preg_match($pattern, $subject) === 1; | ||||
|         }, $subject); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Perform a preg match all, wrapping up the result. | ||||
|      * | ||||
|      * @param string $pattern | ||||
|      * @param string $subject | ||||
|      * | ||||
|      * @return \GrahamCampbell\ResultType\Result<int,string> | ||||
|      */ | ||||
|     public static function occurrences(string $pattern, string $subject) | ||||
|     { | ||||
|         return self::pregAndWrap(static function (string $subject) use ($pattern) { | ||||
|             return (int) @\preg_match_all($pattern, $subject); | ||||
|         }, $subject); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Perform a preg replace callback, wrapping up the result. | ||||
|      * | ||||
|      * @param string   $pattern | ||||
|      * @param callable $callback | ||||
|      * @param string   $subject | ||||
|      * @param int|null $limit | ||||
|      * | ||||
|      * @return \GrahamCampbell\ResultType\Result<string,string> | ||||
|      */ | ||||
|     public static function replaceCallback(string $pattern, callable $callback, string $subject, int $limit = null) | ||||
|     { | ||||
|         return self::pregAndWrap(static function (string $subject) use ($pattern, $callback, $limit) { | ||||
|             return (string) @\preg_replace_callback($pattern, $callback, $subject, $limit ?? -1); | ||||
|         }, $subject); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Perform a preg split, wrapping up the result. | ||||
|      * | ||||
|      * @param string $pattern | ||||
|      * @param string $subject | ||||
|      * | ||||
|      * @return \GrahamCampbell\ResultType\Result<string[],string> | ||||
|      */ | ||||
|     public static function split(string $pattern, string $subject) | ||||
|     { | ||||
|         return self::pregAndWrap(static function (string $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 \GrahamCampbell\ResultType\Result<V,string> | ||||
|      */ | ||||
|     private static function pregAndWrap(callable $operation, string $subject) | ||||
|     { | ||||
|         $result = $operation($subject); | ||||
|  | ||||
|         if (\preg_last_error() !== \PREG_NO_ERROR) { | ||||
|             /** @var \GrahamCampbell\ResultType\Result<V,string> */ | ||||
|             return Error::create(\preg_last_error_msg()); | ||||
|         } | ||||
|  | ||||
|         /** @var \GrahamCampbell\ResultType\Result<V,string> */ | ||||
|         return Success::create($result); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										98
									
								
								src/Dotenv/Util/Str.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								src/Dotenv/Util/Str.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,98 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv\Util; | ||||
|  | ||||
| use GrahamCampbell\ResultType\Error; | ||||
| use GrahamCampbell\ResultType\Success; | ||||
| use PhpOption\Option; | ||||
|  | ||||
| /** | ||||
|  * @internal | ||||
|  */ | ||||
| final class Str | ||||
| { | ||||
|     /** | ||||
|      * This class is a singleton. | ||||
|      * | ||||
|      * @codeCoverageIgnore | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     private function __construct() | ||||
|     { | ||||
|         // | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Convert a string to UTF-8 from the given encoding. | ||||
|      * | ||||
|      * @param string      $input | ||||
|      * @param string|null $encoding | ||||
|      * | ||||
|      * @return \GrahamCampbell\ResultType\Result<string,string> | ||||
|      */ | ||||
|     public static function utf8(string $input, string $encoding = null) | ||||
|     { | ||||
|         if ($encoding !== null && !\in_array($encoding, \mb_list_encodings(), true)) { | ||||
|             /** @var \GrahamCampbell\ResultType\Result<string,string> */ | ||||
|             return Error::create( | ||||
|                 \sprintf('Illegal character encoding [%s] specified.', $encoding) | ||||
|             ); | ||||
|         } | ||||
|         $converted = $encoding === null ? | ||||
|             @\mb_convert_encoding($input, 'UTF-8') : | ||||
|             @\mb_convert_encoding($input, 'UTF-8', $encoding); | ||||
|         /** | ||||
|          * this is for support UTF-8 with BOM encoding | ||||
|          * @see https://en.wikipedia.org/wiki/Byte_order_mark | ||||
|          * @see https://github.com/vlucas/phpdotenv/issues/500 | ||||
|          */ | ||||
|         if (\substr($converted, 0, 3) == "\xEF\xBB\xBF") { | ||||
|             $converted = \substr($converted, 3); | ||||
|         } | ||||
|         /** @var \GrahamCampbell\ResultType\Result<string,string> */ | ||||
|         return Success::create($converted); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Search for a given substring of the input. | ||||
|      * | ||||
|      * @param string $haystack | ||||
|      * @param string $needle | ||||
|      * | ||||
|      * @return \PhpOption\Option<int> | ||||
|      */ | ||||
|     public static function pos(string $haystack, string $needle) | ||||
|     { | ||||
|         /** @var \PhpOption\Option<int> */ | ||||
|         return Option::fromValue(\mb_strpos($haystack, $needle, 0, 'UTF-8'), false); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Grab the specified substring of the input. | ||||
|      * | ||||
|      * @param string   $input | ||||
|      * @param int      $start | ||||
|      * @param int|null $length | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public static function substr(string $input, int $start, int $length = null) | ||||
|     { | ||||
|         return \mb_substr($input, $start, $length, 'UTF-8'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Compute the length of the given string. | ||||
|      * | ||||
|      * @param string $input | ||||
|      * | ||||
|      * @return int | ||||
|      */ | ||||
|     public static function len(string $input) | ||||
|     { | ||||
|         return \mb_strlen($input, 'UTF-8'); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										209
									
								
								src/Dotenv/Validator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								src/Dotenv/Validator.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,209 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Dotenv; | ||||
|  | ||||
| use Dotenv\Exception\ValidationException; | ||||
| use Dotenv\Repository\RepositoryInterface; | ||||
| use Dotenv\Util\Regex; | ||||
| use Dotenv\Util\Str; | ||||
|  | ||||
| class Validator | ||||
| { | ||||
|     /** | ||||
|      * The environment repository instance. | ||||
|      * | ||||
|      * @var \Dotenv\Repository\RepositoryInterface | ||||
|      */ | ||||
|     private $repository; | ||||
|  | ||||
|     /** | ||||
|      * The variables to validate. | ||||
|      * | ||||
|      * @var string[] | ||||
|      */ | ||||
|     private $variables; | ||||
|  | ||||
|     /** | ||||
|      * Create a new validator instance. | ||||
|      * | ||||
|      * @param \Dotenv\Repository\RepositoryInterface $repository | ||||
|      * @param string[]                               $variables | ||||
|      * | ||||
|      * @throws \Dotenv\Exception\ValidationException | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct(RepositoryInterface $repository, array $variables) | ||||
|     { | ||||
|         $this->repository = $repository; | ||||
|         $this->variables = $variables; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Assert that each variable is present. | ||||
|      * | ||||
|      * @throws \Dotenv\Exception\ValidationException | ||||
|      * | ||||
|      * @return \Dotenv\Validator | ||||
|      */ | ||||
|     public function required() | ||||
|     { | ||||
|         return $this->assert( | ||||
|             static function (?string $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->assertNullable( | ||||
|             static function (string $value) { | ||||
|                 return Str::len(\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->assertNullable( | ||||
|             static function (string $value) { | ||||
|                 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->assertNullable( | ||||
|             static function (string $value) { | ||||
|                 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->assertNullable( | ||||
|             static function (string $value) use ($choices) { | ||||
|                 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(string $regex) | ||||
|     { | ||||
|         return $this->assertNullable( | ||||
|             static function (string $value) use ($regex) { | ||||
|                 return Regex::matches($regex, $value)->success()->getOrElse(false); | ||||
|             }, | ||||
|             \sprintf('does not match "%s"', $regex) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Assert that the callback returns true for each variable. | ||||
|      * | ||||
|      * @param callable(?string):bool $callback | ||||
|      * @param string                 $message | ||||
|      * | ||||
|      * @throws \Dotenv\Exception\ValidationException | ||||
|      * | ||||
|      * @return \Dotenv\Validator | ||||
|      */ | ||||
|     public function assert(callable $callback, string $message) | ||||
|     { | ||||
|         $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; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Assert that the callback returns true for each variable. | ||||
|      * | ||||
|      * Skip checking null variable values. | ||||
|      * | ||||
|      * @param callable(string):bool $callback | ||||
|      * @param string                $message | ||||
|      * | ||||
|      * @throws \Dotenv\Exception\ValidationException | ||||
|      * | ||||
|      * @return \Dotenv\Validator | ||||
|      */ | ||||
|     public function assertNullable(callable $callback, string $message) | ||||
|     { | ||||
|         return $this->assert( | ||||
|             static function (?string $value) use ($callback) { | ||||
|                 if ($value === null) { | ||||
|                     return true; | ||||
|                 } | ||||
|  | ||||
|                 return $callback($value); | ||||
|             }, | ||||
|             $message | ||||
|         ); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										121
									
								
								src/GrahamCampbell/ResultType/Error.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								src/GrahamCampbell/ResultType/Error.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,121 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * This file is part of Result Type. | ||||
|  * | ||||
|  * (c) Graham Campbell <hello@gjcampbell.co.uk> | ||||
|  * | ||||
|  * For the full copyright and license information, please view the LICENSE | ||||
|  * file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace GrahamCampbell\ResultType; | ||||
|  | ||||
| use PhpOption\None; | ||||
| use PhpOption\Some; | ||||
|  | ||||
| /** | ||||
|  * @template T | ||||
|  * @template E | ||||
|  * | ||||
|  * @extends \GrahamCampbell\ResultType\Result<T,E> | ||||
|  */ | ||||
| final 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 \GrahamCampbell\ResultType\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 \GrahamCampbell\ResultType\Result<S,E> | ||||
|      */ | ||||
|     public function map(callable $f) | ||||
|     { | ||||
|         return self::create($this->value); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Flat map over the success value. | ||||
|      * | ||||
|      * @template S | ||||
|      * @template F | ||||
|      * | ||||
|      * @param callable(T):\GrahamCampbell\ResultType\Result<S,F> $f | ||||
|      * | ||||
|      * @return \GrahamCampbell\ResultType\Result<S,F> | ||||
|      */ | ||||
|     public function flatMap(callable $f) | ||||
|     { | ||||
|         /** @var \GrahamCampbell\ResultType\Result<S,F> */ | ||||
|         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 \GrahamCampbell\ResultType\Result<T,F> | ||||
|      */ | ||||
|     public function mapError(callable $f) | ||||
|     { | ||||
|         return self::create($f($this->value)); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										69
									
								
								src/GrahamCampbell/ResultType/Result.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/GrahamCampbell/ResultType/Result.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * This file is part of Result Type. | ||||
|  * | ||||
|  * (c) Graham Campbell <hello@gjcampbell.co.uk> | ||||
|  * | ||||
|  * For the full copyright and license information, please view the LICENSE | ||||
|  * file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace GrahamCampbell\ResultType; | ||||
|  | ||||
| /** | ||||
|  * @template T | ||||
|  * @template E | ||||
|  */ | ||||
| abstract class Result | ||||
| { | ||||
|     /** | ||||
|      * Get the success option value. | ||||
|      * | ||||
|      * @return \PhpOption\Option<T> | ||||
|      */ | ||||
|     abstract public function success(); | ||||
|  | ||||
|     /** | ||||
|      * Map over the success value. | ||||
|      * | ||||
|      * @template S | ||||
|      * | ||||
|      * @param callable(T):S $f | ||||
|      * | ||||
|      * @return \GrahamCampbell\ResultType\Result<S,E> | ||||
|      */ | ||||
|     abstract public function map(callable $f); | ||||
|  | ||||
|     /** | ||||
|      * Flat map over the success value. | ||||
|      * | ||||
|      * @template S | ||||
|      * @template F | ||||
|      * | ||||
|      * @param callable(T):\GrahamCampbell\ResultType\Result<S,F> $f | ||||
|      * | ||||
|      * @return \GrahamCampbell\ResultType\Result<S,F> | ||||
|      */ | ||||
|     abstract public function flatMap(callable $f); | ||||
|  | ||||
|     /** | ||||
|      * Get the error option value. | ||||
|      * | ||||
|      * @return \PhpOption\Option<E> | ||||
|      */ | ||||
|     abstract public function error(); | ||||
|  | ||||
|     /** | ||||
|      * Map over the error value. | ||||
|      * | ||||
|      * @template F | ||||
|      * | ||||
|      * @param callable(E):F $f | ||||
|      * | ||||
|      * @return \GrahamCampbell\ResultType\Result<T,F> | ||||
|      */ | ||||
|     abstract public function mapError(callable $f); | ||||
| } | ||||
							
								
								
									
										120
									
								
								src/GrahamCampbell/ResultType/Success.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								src/GrahamCampbell/ResultType/Success.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| /* | ||||
|  * This file is part of Result Type. | ||||
|  * | ||||
|  * (c) Graham Campbell <hello@gjcampbell.co.uk> | ||||
|  * | ||||
|  * For the full copyright and license information, please view the LICENSE | ||||
|  * file that was distributed with this source code. | ||||
|  */ | ||||
|  | ||||
| namespace GrahamCampbell\ResultType; | ||||
|  | ||||
| use PhpOption\None; | ||||
| use PhpOption\Some; | ||||
|  | ||||
| /** | ||||
|  * @template T | ||||
|  * @template E | ||||
|  * | ||||
|  * @extends \GrahamCampbell\ResultType\Result<T,E> | ||||
|  */ | ||||
| final 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 \GrahamCampbell\ResultType\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 \GrahamCampbell\ResultType\Result<S,E> | ||||
|      */ | ||||
|     public function map(callable $f) | ||||
|     { | ||||
|         return self::create($f($this->value)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Flat map over the success value. | ||||
|      * | ||||
|      * @template S | ||||
|      * @template F | ||||
|      * | ||||
|      * @param callable(T):\GrahamCampbell\ResultType\Result<S,F> $f | ||||
|      * | ||||
|      * @return \GrahamCampbell\ResultType\Result<S,F> | ||||
|      */ | ||||
|     public function flatMap(callable $f) | ||||
|     { | ||||
|         return $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 \GrahamCampbell\ResultType\Result<T,F> | ||||
|      */ | ||||
|     public function mapError(callable $f) | ||||
|     { | ||||
|         return self::create($this->value); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										175
									
								
								src/PhpOption/LazyOption.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								src/PhpOption/LazyOption.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,175 @@ | ||||
| <?php | ||||
|  | ||||
| /* | ||||
|  * Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com> | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| namespace PhpOption; | ||||
|  | ||||
| use Traversable; | ||||
|  | ||||
| /** | ||||
|  * @template T | ||||
|  * | ||||
|  * @extends Option<T> | ||||
|  */ | ||||
| final class LazyOption extends Option | ||||
| { | ||||
|     /** @var callable(mixed...):(Option<T>) */ | ||||
|     private $callback; | ||||
|  | ||||
|     /** @var array<int, mixed> */ | ||||
|     private $arguments; | ||||
|  | ||||
|     /** @var Option<T>|null */ | ||||
|     private $option; | ||||
|  | ||||
|     /** | ||||
|      * @template S | ||||
|      * @param callable(mixed...):(Option<S>) $callback | ||||
|      * @param array<int, mixed>              $arguments | ||||
|      * | ||||
|      * @return LazyOption<S> | ||||
|      */ | ||||
|     public static function create($callback, array $arguments = []): self | ||||
|     { | ||||
|         return new self($callback, $arguments); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param callable(mixed...):(Option<T>) $callback | ||||
|      * @param array<int, mixed>              $arguments | ||||
|      */ | ||||
|     public function __construct($callback, array $arguments = []) | ||||
|     { | ||||
|         if (!is_callable($callback)) { | ||||
|             throw new \InvalidArgumentException('Invalid callback given'); | ||||
|         } | ||||
|  | ||||
|         $this->callback = $callback; | ||||
|         $this->arguments = $arguments; | ||||
|     } | ||||
|  | ||||
|     public function isDefined(): bool | ||||
|     { | ||||
|         return $this->option()->isDefined(); | ||||
|     } | ||||
|  | ||||
|     public function isEmpty(): bool | ||||
|     { | ||||
|         return $this->option()->isEmpty(); | ||||
|     } | ||||
|  | ||||
|     public function get() | ||||
|     { | ||||
|         return $this->option()->get(); | ||||
|     } | ||||
|  | ||||
|     public function getOrElse($default) | ||||
|     { | ||||
|         return $this->option()->getOrElse($default); | ||||
|     } | ||||
|  | ||||
|     public function getOrCall($callable) | ||||
|     { | ||||
|         return $this->option()->getOrCall($callable); | ||||
|     } | ||||
|  | ||||
|     public function getOrThrow(\Exception $ex) | ||||
|     { | ||||
|         return $this->option()->getOrThrow($ex); | ||||
|     } | ||||
|  | ||||
|     public function orElse(Option $else) | ||||
|     { | ||||
|         return $this->option()->orElse($else); | ||||
|     } | ||||
|  | ||||
|     public function ifDefined($callable) | ||||
|     { | ||||
|         $this->option()->forAll($callable); | ||||
|     } | ||||
|  | ||||
|     public function forAll($callable) | ||||
|     { | ||||
|         return $this->option()->forAll($callable); | ||||
|     } | ||||
|  | ||||
|     public function map($callable) | ||||
|     { | ||||
|         return $this->option()->map($callable); | ||||
|     } | ||||
|  | ||||
|     public function flatMap($callable) | ||||
|     { | ||||
|         return $this->option()->flatMap($callable); | ||||
|     } | ||||
|  | ||||
|     public function filter($callable) | ||||
|     { | ||||
|         return $this->option()->filter($callable); | ||||
|     } | ||||
|  | ||||
|     public function filterNot($callable) | ||||
|     { | ||||
|         return $this->option()->filterNot($callable); | ||||
|     } | ||||
|  | ||||
|     public function select($value) | ||||
|     { | ||||
|         return $this->option()->select($value); | ||||
|     } | ||||
|  | ||||
|     public function reject($value) | ||||
|     { | ||||
|         return $this->option()->reject($value); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Traversable<T> | ||||
|      */ | ||||
|     public function getIterator(): Traversable | ||||
|     { | ||||
|         return $this->option()->getIterator(); | ||||
|     } | ||||
|  | ||||
|     public function foldLeft($initialValue, $callable) | ||||
|     { | ||||
|         return $this->option()->foldLeft($initialValue, $callable); | ||||
|     } | ||||
|  | ||||
|     public function foldRight($initialValue, $callable) | ||||
|     { | ||||
|         return $this->option()->foldRight($initialValue, $callable); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return Option<T> | ||||
|      */ | ||||
|     private function option(): Option | ||||
|     { | ||||
|         if (null === $this->option) { | ||||
|             /** @var mixed */ | ||||
|             $option = call_user_func_array($this->callback, $this->arguments); | ||||
|             if ($option instanceof Option) { | ||||
|                 $this->option = $option; | ||||
|             } else { | ||||
|                 throw new \RuntimeException(sprintf('Expected instance of %s', Option::class)); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $this->option; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										136
									
								
								src/PhpOption/None.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								src/PhpOption/None.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,136 @@ | ||||
| <?php | ||||
|  | ||||
| /* | ||||
|  * Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com> | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| namespace PhpOption; | ||||
|  | ||||
| use EmptyIterator; | ||||
|  | ||||
| /** | ||||
|  * @extends Option<mixed> | ||||
|  */ | ||||
| final class None extends Option | ||||
| { | ||||
|     /** @var None|null */ | ||||
|     private static $instance; | ||||
|  | ||||
|     /** | ||||
|      * @return None | ||||
|      */ | ||||
|     public static function create(): self | ||||
|     { | ||||
|         if (null === self::$instance) { | ||||
|             self::$instance = new self(); | ||||
|         } | ||||
|  | ||||
|         return self::$instance; | ||||
|     } | ||||
|  | ||||
|     public function get() | ||||
|     { | ||||
|         throw new \RuntimeException('None has no value.'); | ||||
|     } | ||||
|  | ||||
|     public function getOrCall($callable) | ||||
|     { | ||||
|         return $callable(); | ||||
|     } | ||||
|  | ||||
|     public function getOrElse($default) | ||||
|     { | ||||
|         return $default; | ||||
|     } | ||||
|  | ||||
|     public function getOrThrow(\Exception $ex) | ||||
|     { | ||||
|         throw $ex; | ||||
|     } | ||||
|  | ||||
|     public function isEmpty(): bool | ||||
|     { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public function isDefined(): bool | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     public function orElse(Option $else) | ||||
|     { | ||||
|         return $else; | ||||
|     } | ||||
|  | ||||
|     public function ifDefined($callable) | ||||
|     { | ||||
|         // Just do nothing in that case. | ||||
|     } | ||||
|  | ||||
|     public function forAll($callable) | ||||
|     { | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function map($callable) | ||||
|     { | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function flatMap($callable) | ||||
|     { | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function filter($callable) | ||||
|     { | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function filterNot($callable) | ||||
|     { | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function select($value) | ||||
|     { | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function reject($value) | ||||
|     { | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function getIterator(): EmptyIterator | ||||
|     { | ||||
|         return new EmptyIterator(); | ||||
|     } | ||||
|  | ||||
|     public function foldLeft($initialValue, $callable) | ||||
|     { | ||||
|         return $initialValue; | ||||
|     } | ||||
|  | ||||
|     public function foldRight($initialValue, $callable) | ||||
|     { | ||||
|         return $initialValue; | ||||
|     } | ||||
|  | ||||
|     private function __construct() | ||||
|     { | ||||
|     } | ||||
| } | ||||
							
								
								
									
										434
									
								
								src/PhpOption/Option.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										434
									
								
								src/PhpOption/Option.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,434 @@ | ||||
| <?php | ||||
|  | ||||
| /* | ||||
|  * Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com> | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| namespace PhpOption; | ||||
|  | ||||
| use ArrayAccess; | ||||
| use IteratorAggregate; | ||||
|  | ||||
| /** | ||||
|  * @template T | ||||
|  * | ||||
|  * @implements IteratorAggregate<T> | ||||
|  */ | ||||
| abstract class Option implements IteratorAggregate | ||||
| { | ||||
|     /** | ||||
|      * Creates an option given a return value. | ||||
|      * | ||||
|      * This is intended for consuming existing APIs and allows you to easily | ||||
|      * convert them to an option. By default, we treat ``null`` as the None | ||||
|      * case, and everything else as Some. | ||||
|      * | ||||
|      * @template S | ||||
|      * | ||||
|      * @param S $value     The actual return value. | ||||
|      * @param S $noneValue The value which should be considered "None"; null by | ||||
|      *                     default. | ||||
|      * | ||||
|      * @return Option<S> | ||||
|      */ | ||||
|     public static function fromValue($value, $noneValue = null) | ||||
|     { | ||||
|         if ($value === $noneValue) { | ||||
|             return None::create(); | ||||
|         } | ||||
|  | ||||
|         return new Some($value); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates an option from an array's value. | ||||
|      * | ||||
|      * If the key does not exist in the array, the array is not actually an | ||||
|      * array, or the array's value at the given key is null, None is returned. | ||||
|      * Otherwise, Some is returned wrapping the value at the given key. | ||||
|      * | ||||
|      * @template S | ||||
|      * | ||||
|      * @param array<string|int,S>|ArrayAccess<string|int,S>|null $array A potential array or \ArrayAccess value. | ||||
|      * @param string                                             $key   The key to check. | ||||
|      * | ||||
|      * @return Option<S> | ||||
|      */ | ||||
|     public static function fromArraysValue($array, $key) | ||||
|     { | ||||
|         if (!(is_array($array) || $array instanceof ArrayAccess) || !isset($array[$key])) { | ||||
|             return None::create(); | ||||
|         } | ||||
|  | ||||
|         return new Some($array[$key]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a lazy-option with the given callback. | ||||
|      * | ||||
|      * This is also a helper constructor for lazy-consuming existing APIs where | ||||
|      * the return value is not yet an option. By default, we treat ``null`` as | ||||
|      * None case, and everything else as Some. | ||||
|      * | ||||
|      * @template S | ||||
|      * | ||||
|      * @param callable $callback  The callback to evaluate. | ||||
|      * @param array    $arguments The arguments for the callback. | ||||
|      * @param S        $noneValue The value which should be considered "None"; | ||||
|     *                             null by default. | ||||
|      * | ||||
|      * @return LazyOption<S> | ||||
|      */ | ||||
|     public static function fromReturn($callback, array $arguments = [], $noneValue = null) | ||||
|     { | ||||
|         return new LazyOption(static function () use ($callback, $arguments, $noneValue) { | ||||
|             /** @var mixed */ | ||||
|             $return = call_user_func_array($callback, $arguments); | ||||
|  | ||||
|             if ($return === $noneValue) { | ||||
|                 return None::create(); | ||||
|             } | ||||
|  | ||||
|             return new Some($return); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Option factory, which creates new option based on passed value. | ||||
|      * | ||||
|      * If value is already an option, it simply returns. If value is callable, | ||||
|      * LazyOption with passed callback created and returned. If Option | ||||
|      * returned from callback, it returns directly. On other case value passed | ||||
|      * to Option::fromValue() method. | ||||
|      * | ||||
|      * @template S | ||||
|      * | ||||
|      * @param Option<S>|callable|S $value | ||||
|      * @param S                    $noneValue Used when $value is mixed or | ||||
|      *                                        callable, for None-check. | ||||
|      * | ||||
|      * @return Option<S>|LazyOption<S> | ||||
|      */ | ||||
|     public static function ensure($value, $noneValue = null) | ||||
|     { | ||||
|         if ($value instanceof self) { | ||||
|             return $value; | ||||
|         } elseif (is_callable($value)) { | ||||
|             return new LazyOption(static function () use ($value, $noneValue) { | ||||
|                 /** @var mixed */ | ||||
|                 $return = $value(); | ||||
|  | ||||
|                 if ($return instanceof self) { | ||||
|                     return $return; | ||||
|                 } else { | ||||
|                     return self::fromValue($return, $noneValue); | ||||
|                 } | ||||
|             }); | ||||
|         } else { | ||||
|             return self::fromValue($value, $noneValue); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Lift a function so that it accepts Option as parameters. | ||||
|      * | ||||
|      * We return a new closure that wraps the original callback. If any of the | ||||
|      * parameters passed to the lifted function is empty, the function will | ||||
|      * return a value of None. Otherwise, we will pass all parameters to the | ||||
|      * original callback and return the value inside a new Option, unless an | ||||
|      * Option is returned from the function, in which case, we use that. | ||||
|      * | ||||
|      * @template S | ||||
|      * | ||||
|      * @param callable $callback | ||||
|      * @param mixed    $noneValue | ||||
|      * | ||||
|      * @return callable | ||||
|      */ | ||||
|     public static function lift($callback, $noneValue = null) | ||||
|     { | ||||
|         return static function () use ($callback, $noneValue) { | ||||
|             /** @var array<int, mixed> */ | ||||
|             $args = func_get_args(); | ||||
|  | ||||
|             $reduced_args = array_reduce( | ||||
|                 $args, | ||||
|                 /** @param bool $status */ | ||||
|                 static function ($status, self $o) { | ||||
|                     return $o->isEmpty() ? true : $status; | ||||
|                 }, | ||||
|                 false | ||||
|             ); | ||||
|             // if at least one parameter is empty, return None | ||||
|             if ($reduced_args) { | ||||
|                 return None::create(); | ||||
|             } | ||||
|  | ||||
|             $args = array_map( | ||||
|                 /** @return T */ | ||||
|                 static function (self $o) { | ||||
|                     // it is safe to do so because the fold above checked | ||||
|                     // that all arguments are of type Some | ||||
|                     /** @var T */ | ||||
|                     return $o->get(); | ||||
|                 }, | ||||
|                 $args | ||||
|             ); | ||||
|  | ||||
|             return self::ensure(call_user_func_array($callback, $args), $noneValue); | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the value if available, or throws an exception otherwise. | ||||
|      * | ||||
|      * @throws \RuntimeException If value is not available. | ||||
|      * | ||||
|      * @return T | ||||
|      */ | ||||
|     abstract public function get(); | ||||
|  | ||||
|     /** | ||||
|      * Returns the value if available, or the default value if not. | ||||
|      * | ||||
|      * @template S | ||||
|      * | ||||
|      * @param S $default | ||||
|      * | ||||
|      * @return T|S | ||||
|      */ | ||||
|     abstract public function getOrElse($default); | ||||
|  | ||||
|     /** | ||||
|      * Returns the value if available, or the results of the callable. | ||||
|      * | ||||
|      * This is preferable over ``getOrElse`` if the computation of the default | ||||
|      * value is expensive. | ||||
|      * | ||||
|      * @template S | ||||
|      * | ||||
|      * @param callable():S $callable | ||||
|      * | ||||
|      * @return T|S | ||||
|      */ | ||||
|     abstract public function getOrCall($callable); | ||||
|  | ||||
|     /** | ||||
|      * Returns the value if available, or throws the passed exception. | ||||
|      * | ||||
|      * @param \Exception $ex | ||||
|      * | ||||
|      * @return T | ||||
|      */ | ||||
|     abstract public function getOrThrow(\Exception $ex); | ||||
|  | ||||
|     /** | ||||
|      * Returns true if no value is available, false otherwise. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     abstract public function isEmpty(); | ||||
|  | ||||
|     /** | ||||
|      * Returns true if a value is available, false otherwise. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     abstract public function isDefined(); | ||||
|  | ||||
|     /** | ||||
|      * Returns this option if non-empty, or the passed option otherwise. | ||||
|      * | ||||
|      * This can be used to try multiple alternatives, and is especially useful | ||||
|      * with lazy evaluating options: | ||||
|      * | ||||
|      * ```php | ||||
|      *     $repo->findSomething() | ||||
|      *         ->orElse(new LazyOption(array($repo, 'findSomethingElse'))) | ||||
|      *         ->orElse(new LazyOption(array($repo, 'createSomething'))); | ||||
|      * ``` | ||||
|      * | ||||
|      * @param Option<T> $else | ||||
|      * | ||||
|      * @return Option<T> | ||||
|      */ | ||||
|     abstract public function orElse(self $else); | ||||
|  | ||||
|     /** | ||||
|      * This is similar to map() below except that the return value has no meaning; | ||||
|      * the passed callable is simply executed if the option is non-empty, and | ||||
|      * ignored if the option is empty. | ||||
|      * | ||||
|      * In all cases, the return value of the callable is discarded. | ||||
|      * | ||||
|      * ```php | ||||
|      *     $comment->getMaybeFile()->ifDefined(function($file) { | ||||
|      *         // Do something with $file here. | ||||
|      *     }); | ||||
|      * ``` | ||||
|      * | ||||
|      * If you're looking for something like ``ifEmpty``, you can use ``getOrCall`` | ||||
|      * and ``getOrElse`` in these cases. | ||||
|      * | ||||
|      * @deprecated Use forAll() instead. | ||||
|      * | ||||
|      * @param callable(T):mixed $callable | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     abstract public function ifDefined($callable); | ||||
|  | ||||
|     /** | ||||
|      * This is similar to map() except that the return value of the callable has no meaning. | ||||
|      * | ||||
|      * The passed callable is simply executed if the option is non-empty, and ignored if the | ||||
|      * option is empty. This method is preferred for callables with side-effects, while map() | ||||
|      * is intended for callables without side-effects. | ||||
|      * | ||||
|      * @param callable(T):mixed $callable | ||||
|      * | ||||
|      * @return Option<T> | ||||
|      */ | ||||
|     abstract public function forAll($callable); | ||||
|  | ||||
|     /** | ||||
|      * Applies the callable to the value of the option if it is non-empty, | ||||
|      * and returns the return value of the callable wrapped in Some(). | ||||
|      * | ||||
|      * If the option is empty, then the callable is not applied. | ||||
|      * | ||||
|      * ```php | ||||
|      *     (new Some("foo"))->map('strtoupper')->get(); // "FOO" | ||||
|      * ``` | ||||
|      * | ||||
|      * @template S | ||||
|      * | ||||
|      * @param callable(T):S $callable | ||||
|      * | ||||
|      * @return Option<S> | ||||
|      */ | ||||
|     abstract public function map($callable); | ||||
|  | ||||
|     /** | ||||
|      * Applies the callable to the value of the option if it is non-empty, and | ||||
|      * returns the return value of the callable directly. | ||||
|      * | ||||
|      * In contrast to ``map``, the return value of the callable is expected to | ||||
|      * be an Option itself; it is not automatically wrapped in Some(). | ||||
|      * | ||||
|      * @template S | ||||
|      * | ||||
|      * @param callable(T):Option<S> $callable must return an Option | ||||
|      * | ||||
|      * @return Option<S> | ||||
|      */ | ||||
|     abstract public function flatMap($callable); | ||||
|  | ||||
|     /** | ||||
|      * If the option is empty, it is returned immediately without applying the callable. | ||||
|      * | ||||
|      * If the option is non-empty, the callable is applied, and if it returns true, | ||||
|      * the option itself is returned; otherwise, None is returned. | ||||
|      * | ||||
|      * @param callable(T):bool $callable | ||||
|      * | ||||
|      * @return Option<T> | ||||
|      */ | ||||
|     abstract public function filter($callable); | ||||
|  | ||||
|     /** | ||||
|      * If the option is empty, it is returned immediately without applying the callable. | ||||
|      * | ||||
|      * If the option is non-empty, the callable is applied, and if it returns false, | ||||
|      * the option itself is returned; otherwise, None is returned. | ||||
|      * | ||||
|      * @param callable(T):bool $callable | ||||
|      * | ||||
|      * @return Option<T> | ||||
|      */ | ||||
|     abstract public function filterNot($callable); | ||||
|  | ||||
|     /** | ||||
|      * If the option is empty, it is returned immediately. | ||||
|      * | ||||
|      * If the option is non-empty, and its value does not equal the passed value | ||||
|      * (via a shallow comparison ===), then None is returned. Otherwise, the | ||||
|      * Option is returned. | ||||
|      * | ||||
|      * In other words, this will filter all but the passed value. | ||||
|      * | ||||
|      * @param T $value | ||||
|      * | ||||
|      * @return Option<T> | ||||
|      */ | ||||
|     abstract public function select($value); | ||||
|  | ||||
|     /** | ||||
|      * If the option is empty, it is returned immediately. | ||||
|      * | ||||
|      * If the option is non-empty, and its value does equal the passed value (via | ||||
|      * a shallow comparison ===), then None is returned; otherwise, the Option is | ||||
|      * returned. | ||||
|      * | ||||
|      * In other words, this will let all values through except the passed value. | ||||
|      * | ||||
|      * @param T $value | ||||
|      * | ||||
|      * @return Option<T> | ||||
|      */ | ||||
|     abstract public function reject($value); | ||||
|  | ||||
|     /** | ||||
|      * Binary operator for the initial value and the option's value. | ||||
|      * | ||||
|      * If empty, the initial value is returned. If non-empty, the callable | ||||
|      * receives the initial value and the option's value as arguments. | ||||
|      * | ||||
|      * ```php | ||||
|      * | ||||
|      *     $some = new Some(5); | ||||
|      *     $none = None::create(); | ||||
|      *     $result = $some->foldLeft(1, function($a, $b) { return $a + $b; }); // int(6) | ||||
|      *     $result = $none->foldLeft(1, function($a, $b) { return $a + $b; }); // int(1) | ||||
|      * | ||||
|      *     // This can be used instead of something like the following: | ||||
|      *     $option = Option::fromValue($integerOrNull); | ||||
|      *     $result = 1; | ||||
|      *     if ( ! $option->isEmpty()) { | ||||
|      *         $result += $option->get(); | ||||
|      *     } | ||||
|      * ``` | ||||
|      * | ||||
|      * @template S | ||||
|      * | ||||
|      * @param S                $initialValue | ||||
|      * @param callable(S, T):S $callable | ||||
|      * | ||||
|      * @return S | ||||
|      */ | ||||
|     abstract public function foldLeft($initialValue, $callable); | ||||
|  | ||||
|     /** | ||||
|      * foldLeft() but with reversed arguments for the callable. | ||||
|      * | ||||
|      * @template S | ||||
|      * | ||||
|      * @param S                $initialValue | ||||
|      * @param callable(T, S):S $callable | ||||
|      * | ||||
|      * @return S | ||||
|      */ | ||||
|     abstract public function foldRight($initialValue, $callable); | ||||
| } | ||||
							
								
								
									
										169
									
								
								src/PhpOption/Some.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								src/PhpOption/Some.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,169 @@ | ||||
| <?php | ||||
|  | ||||
| /* | ||||
|  * Copyright 2012 Johannes M. Schmitt <schmittjoh@gmail.com> | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| namespace PhpOption; | ||||
|  | ||||
| use ArrayIterator; | ||||
|  | ||||
| /** | ||||
|  * @template T | ||||
|  * | ||||
|  * @extends Option<T> | ||||
|  */ | ||||
| final class Some extends Option | ||||
| { | ||||
|     /** @var T */ | ||||
|     private $value; | ||||
|  | ||||
|     /** | ||||
|      * @param T $value | ||||
|      */ | ||||
|     public function __construct($value) | ||||
|     { | ||||
|         $this->value = $value; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @template U | ||||
|      * | ||||
|      * @param U $value | ||||
|      * | ||||
|      * @return Some<U> | ||||
|      */ | ||||
|     public static function create($value): self | ||||
|     { | ||||
|         return new self($value); | ||||
|     } | ||||
|  | ||||
|     public function isDefined(): bool | ||||
|     { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public function isEmpty(): bool | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     public function get() | ||||
|     { | ||||
|         return $this->value; | ||||
|     } | ||||
|  | ||||
|     public function getOrElse($default) | ||||
|     { | ||||
|         return $this->value; | ||||
|     } | ||||
|  | ||||
|     public function getOrCall($callable) | ||||
|     { | ||||
|         return $this->value; | ||||
|     } | ||||
|  | ||||
|     public function getOrThrow(\Exception $ex) | ||||
|     { | ||||
|         return $this->value; | ||||
|     } | ||||
|  | ||||
|     public function orElse(Option $else) | ||||
|     { | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function ifDefined($callable) | ||||
|     { | ||||
|         $this->forAll($callable); | ||||
|     } | ||||
|  | ||||
|     public function forAll($callable) | ||||
|     { | ||||
|         $callable($this->value); | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     public function map($callable) | ||||
|     { | ||||
|         return new self($callable($this->value)); | ||||
|     } | ||||
|  | ||||
|     public function flatMap($callable) | ||||
|     { | ||||
|         /** @var mixed */ | ||||
|         $rs = $callable($this->value); | ||||
|         if (!$rs instanceof Option) { | ||||
|             throw new \RuntimeException('Callables passed to flatMap() must return an Option. Maybe you should use map() instead?'); | ||||
|         } | ||||
|  | ||||
|         return $rs; | ||||
|     } | ||||
|  | ||||
|     public function filter($callable) | ||||
|     { | ||||
|         if (true === $callable($this->value)) { | ||||
|             return $this; | ||||
|         } | ||||
|  | ||||
|         return None::create(); | ||||
|     } | ||||
|  | ||||
|     public function filterNot($callable) | ||||
|     { | ||||
|         if (false === $callable($this->value)) { | ||||
|             return $this; | ||||
|         } | ||||
|  | ||||
|         return None::create(); | ||||
|     } | ||||
|  | ||||
|     public function select($value) | ||||
|     { | ||||
|         if ($this->value === $value) { | ||||
|             return $this; | ||||
|         } | ||||
|  | ||||
|         return None::create(); | ||||
|     } | ||||
|  | ||||
|     public function reject($value) | ||||
|     { | ||||
|         if ($this->value === $value) { | ||||
|             return None::create(); | ||||
|         } | ||||
|  | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return ArrayIterator<int, T> | ||||
|      */ | ||||
|     public function getIterator(): ArrayIterator | ||||
|     { | ||||
|         return new ArrayIterator([$this->value]); | ||||
|     } | ||||
|  | ||||
|     public function foldLeft($initialValue, $callable) | ||||
|     { | ||||
|         return $callable($initialValue, $this->value); | ||||
|     } | ||||
|  | ||||
|     public function foldRight($initialValue, $callable) | ||||
|     { | ||||
|         return $callable($this->value, $initialValue); | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user