diff options
Diffstat (limited to 'vendor/php-http/discovery/src/ClassDiscovery.php')
-rw-r--r-- | vendor/php-http/discovery/src/ClassDiscovery.php | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/vendor/php-http/discovery/src/ClassDiscovery.php b/vendor/php-http/discovery/src/ClassDiscovery.php new file mode 100644 index 000000000..5ea469797 --- /dev/null +++ b/vendor/php-http/discovery/src/ClassDiscovery.php @@ -0,0 +1,255 @@ +<?php + +namespace Http\Discovery; + +use Http\Discovery\Exception\ClassInstantiationFailedException; +use Http\Discovery\Exception\DiscoveryFailedException; +use Http\Discovery\Exception\NoCandidateFoundException; +use Http\Discovery\Exception\StrategyUnavailableException; +use Http\Discovery\Strategy\DiscoveryStrategy; + +/** + * Registry that based find results on class existence. + * + * @author David de Boer <[email protected]> + * @author Márk Sági-Kazár <[email protected]> + * @author Tobias Nyholm <[email protected]> + */ +abstract class ClassDiscovery +{ + /** + * A list of strategies to find classes. + * + * @var DiscoveryStrategy[] + */ + private static $strategies = [ + Strategy\GeneratedDiscoveryStrategy::class, + Strategy\CommonClassesStrategy::class, + Strategy\CommonPsr17ClassesStrategy::class, + Strategy\PuliBetaStrategy::class, + ]; + + private static $deprecatedStrategies = [ + Strategy\PuliBetaStrategy::class => true, + ]; + + /** + * Discovery cache to make the second time we use discovery faster. + * + * @var array + */ + private static $cache = []; + + /** + * Finds a class. + * + * @param string $type + * + * @return string|\Closure + * + * @throws DiscoveryFailedException + */ + protected static function findOneByType($type) + { + // Look in the cache + if (null !== ($class = self::getFromCache($type))) { + return $class; + } + + static $skipStrategy; + $skipStrategy ?? $skipStrategy = self::safeClassExists(Strategy\GeneratedDiscoveryStrategy::class) ? false : Strategy\GeneratedDiscoveryStrategy::class; + + $exceptions = []; + foreach (self::$strategies as $strategy) { + if ($skipStrategy === $strategy) { + continue; + } + + try { + $candidates = $strategy::getCandidates($type); + } catch (StrategyUnavailableException $e) { + if (!isset(self::$deprecatedStrategies[$strategy])) { + $exceptions[] = $e; + } + + continue; + } + + foreach ($candidates as $candidate) { + if (isset($candidate['condition'])) { + if (!self::evaluateCondition($candidate['condition'])) { + continue; + } + } + + // save the result for later use + self::storeInCache($type, $candidate); + + return $candidate['class']; + } + + $exceptions[] = new NoCandidateFoundException($strategy, $candidates); + } + + throw DiscoveryFailedException::create($exceptions); + } + + /** + * Get a value from cache. + * + * @param string $type + * + * @return string|null + */ + private static function getFromCache($type) + { + if (!isset(self::$cache[$type])) { + return; + } + + $candidate = self::$cache[$type]; + if (isset($candidate['condition'])) { + if (!self::evaluateCondition($candidate['condition'])) { + return; + } + } + + return $candidate['class']; + } + + /** + * Store a value in cache. + * + * @param string $type + * @param string $class + */ + private static function storeInCache($type, $class) + { + self::$cache[$type] = $class; + } + + /** + * Set new strategies and clear the cache. + * + * @param string[] $strategies list of fully qualified class names that implement DiscoveryStrategy + */ + public static function setStrategies(array $strategies) + { + self::$strategies = $strategies; + self::clearCache(); + } + + /** + * Returns the currently configured discovery strategies as fully qualified class names. + * + * @return string[] + */ + public static function getStrategies(): iterable + { + return self::$strategies; + } + + /** + * Append a strategy at the end of the strategy queue. + * + * @param string $strategy Fully qualified class name of a DiscoveryStrategy + */ + public static function appendStrategy($strategy) + { + self::$strategies[] = $strategy; + self::clearCache(); + } + + /** + * Prepend a strategy at the beginning of the strategy queue. + * + * @param string $strategy Fully qualified class name to a DiscoveryStrategy + */ + public static function prependStrategy($strategy) + { + array_unshift(self::$strategies, $strategy); + self::clearCache(); + } + + public static function clearCache() + { + self::$cache = []; + } + + /** + * Evaluates conditions to boolean. + * + * @return bool + */ + protected static function evaluateCondition($condition) + { + if (is_string($condition)) { + // Should be extended for functions, extensions??? + return self::safeClassExists($condition); + } + if (is_callable($condition)) { + return (bool) $condition(); + } + if (is_bool($condition)) { + return $condition; + } + if (is_array($condition)) { + foreach ($condition as $c) { + if (false === static::evaluateCondition($c)) { + // Immediately stop execution if the condition is false + return false; + } + } + + return true; + } + + return false; + } + + /** + * Get an instance of the $class. + * + * @param string|\Closure $class a FQCN of a class or a closure that instantiate the class + * + * @return object + * + * @throws ClassInstantiationFailedException + */ + protected static function instantiateClass($class) + { + try { + if (is_string($class)) { + return new $class(); + } + + if (is_callable($class)) { + return $class(); + } + } catch (\Exception $e) { + throw new ClassInstantiationFailedException('Unexpected exception when instantiating class.', 0, $e); + } + + throw new ClassInstantiationFailedException('Could not instantiate class because parameter is neither a callable nor a string'); + } + + /** + * We need a "safe" version of PHP's "class_exists" because Magento has a bug + * (or they call it a "feature"). Magento is throwing an exception if you do class_exists() + * on a class that ends with "Factory" and if that file does not exits. + * + * This function catches all potential exceptions and makes sure to always return a boolean. + * + * @param string $class + * + * @return bool + */ + public static function safeClassExists($class) + { + try { + return class_exists($class) || interface_exists($class); + } catch (\Exception $e) { + return false; + } + } +} |