vendor/php-http/discovery/src/ClassDiscovery.php line 188

Open in your IDE?
  1. <?php
  2. namespace Http\Discovery;
  3. use Http\Discovery\Exception\ClassInstantiationFailedException;
  4. use Http\Discovery\Exception\DiscoveryFailedException;
  5. use Http\Discovery\Exception\NoCandidateFoundException;
  6. use Http\Discovery\Exception\StrategyUnavailableException;
  7. use Http\Discovery\Strategy\DiscoveryStrategy;
  8. /**
  9.  * Registry that based find results on class existence.
  10.  *
  11.  * @author David de Boer <david@ddeboer.nl>
  12.  * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
  13.  * @author Tobias Nyholm <tobias.nyholm@gmail.com>
  14.  */
  15. abstract class ClassDiscovery
  16. {
  17.     /**
  18.      * A list of strategies to find classes.
  19.      *
  20.      * @var DiscoveryStrategy[]
  21.      */
  22.     private static $strategies = [
  23.         Strategy\GeneratedDiscoveryStrategy::class,
  24.         Strategy\CommonClassesStrategy::class,
  25.         Strategy\CommonPsr17ClassesStrategy::class,
  26.         Strategy\PuliBetaStrategy::class,
  27.     ];
  28.     private static $deprecatedStrategies = [
  29.         Strategy\PuliBetaStrategy::class => true,
  30.     ];
  31.     /**
  32.      * Discovery cache to make the second time we use discovery faster.
  33.      *
  34.      * @var array
  35.      */
  36.     private static $cache = [];
  37.     /**
  38.      * Finds a class.
  39.      *
  40.      * @param string $type
  41.      *
  42.      * @return string|\Closure
  43.      *
  44.      * @throws DiscoveryFailedException
  45.      */
  46.     protected static function findOneByType($type)
  47.     {
  48.         // Look in the cache
  49.         if (null !== ($class self::getFromCache($type))) {
  50.             return $class;
  51.         }
  52.         static $skipStrategy;
  53.         $skipStrategy ?? $skipStrategy self::safeClassExists(Strategy\GeneratedDiscoveryStrategy::class) ? false Strategy\GeneratedDiscoveryStrategy::class;
  54.         $exceptions = [];
  55.         foreach (self::$strategies as $strategy) {
  56.             if ($skipStrategy === $strategy) {
  57.                 continue;
  58.             }
  59.             try {
  60.                 $candidates $strategy::getCandidates($type);
  61.             } catch (StrategyUnavailableException $e) {
  62.                 if (!isset(self::$deprecatedStrategies[$strategy])) {
  63.                     $exceptions[] = $e;
  64.                 }
  65.                 continue;
  66.             }
  67.             foreach ($candidates as $candidate) {
  68.                 if (isset($candidate['condition'])) {
  69.                     if (!self::evaluateCondition($candidate['condition'])) {
  70.                         continue;
  71.                     }
  72.                 }
  73.                 // save the result for later use
  74.                 self::storeInCache($type$candidate);
  75.                 return $candidate['class'];
  76.             }
  77.             $exceptions[] = new NoCandidateFoundException($strategy$candidates);
  78.         }
  79.         throw DiscoveryFailedException::create($exceptions);
  80.     }
  81.     /**
  82.      * Get a value from cache.
  83.      *
  84.      * @param string $type
  85.      *
  86.      * @return string|null
  87.      */
  88.     private static function getFromCache($type)
  89.     {
  90.         if (!isset(self::$cache[$type])) {
  91.             return;
  92.         }
  93.         $candidate self::$cache[$type];
  94.         if (isset($candidate['condition'])) {
  95.             if (!self::evaluateCondition($candidate['condition'])) {
  96.                 return;
  97.             }
  98.         }
  99.         return $candidate['class'];
  100.     }
  101.     /**
  102.      * Store a value in cache.
  103.      *
  104.      * @param string $type
  105.      * @param string $class
  106.      */
  107.     private static function storeInCache($type$class)
  108.     {
  109.         self::$cache[$type] = $class;
  110.     }
  111.     /**
  112.      * Set new strategies and clear the cache.
  113.      *
  114.      * @param string[] $strategies list of fully qualified class names that implement DiscoveryStrategy
  115.      */
  116.     public static function setStrategies(array $strategies)
  117.     {
  118.         self::$strategies $strategies;
  119.         self::clearCache();
  120.     }
  121.     /**
  122.      * Returns the currently configured discovery strategies as fully qualified class names.
  123.      *
  124.      * @return string[]
  125.      */
  126.     public static function getStrategies(): iterable
  127.     {
  128.         return self::$strategies;
  129.     }
  130.     /**
  131.      * Append a strategy at the end of the strategy queue.
  132.      *
  133.      * @param string $strategy Fully qualified class name of a DiscoveryStrategy
  134.      */
  135.     public static function appendStrategy($strategy)
  136.     {
  137.         self::$strategies[] = $strategy;
  138.         self::clearCache();
  139.     }
  140.     /**
  141.      * Prepend a strategy at the beginning of the strategy queue.
  142.      *
  143.      * @param string $strategy Fully qualified class name to a DiscoveryStrategy
  144.      */
  145.     public static function prependStrategy($strategy)
  146.     {
  147.         array_unshift(self::$strategies$strategy);
  148.         self::clearCache();
  149.     }
  150.     public static function clearCache()
  151.     {
  152.         self::$cache = [];
  153.     }
  154.     /**
  155.      * Evaluates conditions to boolean.
  156.      *
  157.      * @return bool
  158.      */
  159.     protected static function evaluateCondition($condition)
  160.     {
  161.         if (is_string($condition)) {
  162.             // Should be extended for functions, extensions???
  163.             return self::safeClassExists($condition);
  164.         }
  165.         if (is_callable($condition)) {
  166.             return (bool) $condition();
  167.         }
  168.         if (is_bool($condition)) {
  169.             return $condition;
  170.         }
  171.         if (is_array($condition)) {
  172.             foreach ($condition as $c) {
  173.                 if (false === static::evaluateCondition($c)) {
  174.                     // Immediately stop execution if the condition is false
  175.                     return false;
  176.                 }
  177.             }
  178.             return true;
  179.         }
  180.         return false;
  181.     }
  182.     /**
  183.      * Get an instance of the $class.
  184.      *
  185.      * @param string|\Closure $class a FQCN of a class or a closure that instantiate the class
  186.      *
  187.      * @return object
  188.      *
  189.      * @throws ClassInstantiationFailedException
  190.      */
  191.     protected static function instantiateClass($class)
  192.     {
  193.         try {
  194.             if (is_string($class)) {
  195.                 return new $class();
  196.             }
  197.             if (is_callable($class)) {
  198.                 return $class();
  199.             }
  200.         } catch (\Exception $e) {
  201.             throw new ClassInstantiationFailedException('Unexpected exception when instantiating class.'0$e);
  202.         }
  203.         throw new ClassInstantiationFailedException('Could not instantiate class because parameter is neither a callable nor a string');
  204.     }
  205.     /**
  206.      * We need a "safe" version of PHP's "class_exists" because Magento has a bug
  207.      * (or they call it a "feature"). Magento is throwing an exception if you do class_exists()
  208.      * on a class that ends with "Factory" and if that file does not exits.
  209.      *
  210.      * This function catches all potential exceptions and makes sure to always return a boolean.
  211.      *
  212.      * @param string $class
  213.      *
  214.      * @return bool
  215.      */
  216.     public static function safeClassExists($class)
  217.     {
  218.         try {
  219.             return class_exists($class) || interface_exists($class);
  220.         } catch (\Exception $e) {
  221.             return false;
  222.         }
  223.     }
  224. }