vendor/doctrine/persistence/lib/Doctrine/Persistence/Mapping/Driver/AnnotationDriver.php line 177

Open in your IDE?
  1. <?php
  2. namespace Doctrine\Persistence\Mapping\Driver;
  3. use Doctrine\Common\Annotations\Reader;
  4. use Doctrine\Persistence\Mapping\MappingException;
  5. use FilesystemIterator;
  6. use RecursiveDirectoryIterator;
  7. use RecursiveIteratorIterator;
  8. use RecursiveRegexIterator;
  9. use ReflectionClass;
  10. use RegexIterator;
  11. use function array_merge;
  12. use function array_unique;
  13. use function assert;
  14. use function get_class;
  15. use function get_declared_classes;
  16. use function in_array;
  17. use function is_dir;
  18. use function preg_match;
  19. use function preg_quote;
  20. use function realpath;
  21. use function str_replace;
  22. use function strpos;
  23. /**
  24.  * The AnnotationDriver reads the mapping metadata from docblock annotations.
  25.  */
  26. abstract class AnnotationDriver implements MappingDriver
  27. {
  28.     /**
  29.      * The annotation reader.
  30.      *
  31.      * @var Reader
  32.      */
  33.     protected $reader;
  34.     /**
  35.      * The paths where to look for mapping files.
  36.      *
  37.      * @var string[]
  38.      */
  39.     protected $paths = [];
  40.     /**
  41.      * The paths excluded from path where to look for mapping files.
  42.      *
  43.      * @var string[]
  44.      */
  45.     protected $excludePaths = [];
  46.     /**
  47.      * The file extension of mapping documents.
  48.      *
  49.      * @var string
  50.      */
  51.     protected $fileExtension '.php';
  52.     /**
  53.      * Cache for AnnotationDriver#getAllClassNames().
  54.      *
  55.      * @var string[]|null
  56.      * @psalm-var list<class-string>|null
  57.      */
  58.     protected $classNames;
  59.     /**
  60.      * Name of the entity annotations as keys.
  61.      *
  62.      * @var array<class-string, bool|int>
  63.      */
  64.     protected $entityAnnotationClasses = [];
  65.     /**
  66.      * Initializes a new AnnotationDriver that uses the given AnnotationReader for reading
  67.      * docblock annotations.
  68.      *
  69.      * @param Reader               $reader The AnnotationReader to use, duck-typed.
  70.      * @param string|string[]|null $paths  One or multiple paths where mapping classes can be found.
  71.      */
  72.     public function __construct($reader$paths null)
  73.     {
  74.         $this->reader $reader;
  75.         if (! $paths) {
  76.             return;
  77.         }
  78.         $this->addPaths((array) $paths);
  79.     }
  80.     /**
  81.      * Appends lookup paths to metadata driver.
  82.      *
  83.      * @param string[] $paths
  84.      *
  85.      * @return void
  86.      */
  87.     public function addPaths(array $paths)
  88.     {
  89.         $this->paths array_unique(array_merge($this->paths$paths));
  90.     }
  91.     /**
  92.      * Retrieves the defined metadata lookup paths.
  93.      *
  94.      * @return string[]
  95.      */
  96.     public function getPaths()
  97.     {
  98.         return $this->paths;
  99.     }
  100.     /**
  101.      * Append exclude lookup paths to metadata driver.
  102.      *
  103.      * @param string[] $paths
  104.      *
  105.      * @return void
  106.      */
  107.     public function addExcludePaths(array $paths)
  108.     {
  109.         $this->excludePaths array_unique(array_merge($this->excludePaths$paths));
  110.     }
  111.     /**
  112.      * Retrieve the defined metadata lookup exclude paths.
  113.      *
  114.      * @return string[]
  115.      */
  116.     public function getExcludePaths()
  117.     {
  118.         return $this->excludePaths;
  119.     }
  120.     /**
  121.      * Retrieve the current annotation reader
  122.      *
  123.      * @return Reader
  124.      */
  125.     public function getReader()
  126.     {
  127.         return $this->reader;
  128.     }
  129.     /**
  130.      * Gets the file extension used to look for mapping files under.
  131.      *
  132.      * @return string
  133.      */
  134.     public function getFileExtension()
  135.     {
  136.         return $this->fileExtension;
  137.     }
  138.     /**
  139.      * Sets the file extension used to look for mapping files under.
  140.      *
  141.      * @param string $fileExtension The file extension to set.
  142.      *
  143.      * @return void
  144.      */
  145.     public function setFileExtension($fileExtension)
  146.     {
  147.         $this->fileExtension $fileExtension;
  148.     }
  149.     /**
  150.      * Returns whether the class with the specified name is transient. Only non-transient
  151.      * classes, that is entities and mapped superclasses, should have their metadata loaded.
  152.      *
  153.      * A class is non-transient if it is annotated with an annotation
  154.      * from the {@see AnnotationDriver::entityAnnotationClasses}.
  155.      *
  156.      * {@inheritDoc}
  157.      */
  158.     public function isTransient($className)
  159.     {
  160.         $classAnnotations $this->reader->getClassAnnotations(new ReflectionClass($className));
  161.         foreach ($classAnnotations as $annot) {
  162.             if (isset($this->entityAnnotationClasses[get_class($annot)])) {
  163.                 return false;
  164.             }
  165.         }
  166.         return true;
  167.     }
  168.     /**
  169.      * {@inheritDoc}
  170.      */
  171.     public function getAllClassNames()
  172.     {
  173.         if ($this->classNames !== null) {
  174.             return $this->classNames;
  175.         }
  176.         if (! $this->paths) {
  177.             throw MappingException::pathRequired();
  178.         }
  179.         $classes       = [];
  180.         $includedFiles = [];
  181.         foreach ($this->paths as $path) {
  182.             if (! is_dir($path)) {
  183.                 throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
  184.             }
  185.             $iterator = new RegexIterator(
  186.                 new RecursiveIteratorIterator(
  187.                     new RecursiveDirectoryIterator($pathFilesystemIterator::SKIP_DOTS),
  188.                     RecursiveIteratorIterator::LEAVES_ONLY
  189.                 ),
  190.                 '/^.+' preg_quote($this->fileExtension) . '$/i',
  191.                 RecursiveRegexIterator::GET_MATCH
  192.             );
  193.             foreach ($iterator as $file) {
  194.                 $sourceFile $file[0];
  195.                 if (! preg_match('(^phar:)i'$sourceFile)) {
  196.                     $sourceFile realpath($sourceFile);
  197.                 }
  198.                 foreach ($this->excludePaths as $excludePath) {
  199.                     $realExcludePath realpath($excludePath);
  200.                     assert($realExcludePath !== false);
  201.                     $exclude str_replace('\\''/'$realExcludePath);
  202.                     $current str_replace('\\''/'$sourceFile);
  203.                     if (strpos($current$exclude) !== false) {
  204.                         continue 2;
  205.                     }
  206.                 }
  207.                 require_once $sourceFile;
  208.                 $includedFiles[] = $sourceFile;
  209.             }
  210.         }
  211.         $declared get_declared_classes();
  212.         foreach ($declared as $className) {
  213.             $rc         = new ReflectionClass($className);
  214.             $sourceFile $rc->getFileName();
  215.             if (! in_array($sourceFile$includedFiles) || $this->isTransient($className)) {
  216.                 continue;
  217.             }
  218.             $classes[] = $className;
  219.         }
  220.         $this->classNames $classes;
  221.         return $classes;
  222.     }
  223. }