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

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