From bf6655a534a6775d30cafa67bd801276bda1d98d Mon Sep 17 00:00:00 2001 From: polo Date: Tue, 13 Aug 2024 23:45:21 +0200 Subject: =?UTF-8?q?VERSION=200.2=20doctrine=20ORM=20et=20entit=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Mapping/AbstractClassMetadataFactory.php | 499 +++++++++++++++++++++ .../src/Persistence/Mapping/ClassMetadata.php | 141 ++++++ .../Persistence/Mapping/ClassMetadataFactory.php | 61 +++ .../Mapping/Driver/ColocatedMappingDriver.php | 212 +++++++++ .../Mapping/Driver/DefaultFileLocator.php | 175 ++++++++ .../src/Persistence/Mapping/Driver/FileDriver.php | 213 +++++++++ .../src/Persistence/Mapping/Driver/FileLocator.php | 52 +++ .../Persistence/Mapping/Driver/MappingDriver.php | 43 ++ .../Mapping/Driver/MappingDriverChain.php | 142 ++++++ .../src/Persistence/Mapping/Driver/PHPDriver.php | 49 ++ .../Persistence/Mapping/Driver/StaticPHPDriver.php | 132 ++++++ .../Mapping/Driver/SymfonyFileLocator.php | 265 +++++++++++ .../src/Persistence/Mapping/MappingException.php | 88 ++++ .../Persistence/Mapping/ProxyClassNameResolver.php | 19 + .../src/Persistence/Mapping/ReflectionService.php | 75 ++++ .../Mapping/RuntimeReflectionService.php | 111 +++++ .../Mapping/StaticReflectionService.php | 78 ++++ 17 files changed, 2355 insertions(+) create mode 100644 vendor/doctrine/persistence/src/Persistence/Mapping/AbstractClassMetadataFactory.php create mode 100644 vendor/doctrine/persistence/src/Persistence/Mapping/ClassMetadata.php create mode 100644 vendor/doctrine/persistence/src/Persistence/Mapping/ClassMetadataFactory.php create mode 100644 vendor/doctrine/persistence/src/Persistence/Mapping/Driver/ColocatedMappingDriver.php create mode 100644 vendor/doctrine/persistence/src/Persistence/Mapping/Driver/DefaultFileLocator.php create mode 100644 vendor/doctrine/persistence/src/Persistence/Mapping/Driver/FileDriver.php create mode 100644 vendor/doctrine/persistence/src/Persistence/Mapping/Driver/FileLocator.php create mode 100644 vendor/doctrine/persistence/src/Persistence/Mapping/Driver/MappingDriver.php create mode 100644 vendor/doctrine/persistence/src/Persistence/Mapping/Driver/MappingDriverChain.php create mode 100644 vendor/doctrine/persistence/src/Persistence/Mapping/Driver/PHPDriver.php create mode 100644 vendor/doctrine/persistence/src/Persistence/Mapping/Driver/StaticPHPDriver.php create mode 100644 vendor/doctrine/persistence/src/Persistence/Mapping/Driver/SymfonyFileLocator.php create mode 100644 vendor/doctrine/persistence/src/Persistence/Mapping/MappingException.php create mode 100644 vendor/doctrine/persistence/src/Persistence/Mapping/ProxyClassNameResolver.php create mode 100644 vendor/doctrine/persistence/src/Persistence/Mapping/ReflectionService.php create mode 100644 vendor/doctrine/persistence/src/Persistence/Mapping/RuntimeReflectionService.php create mode 100644 vendor/doctrine/persistence/src/Persistence/Mapping/StaticReflectionService.php (limited to 'vendor/doctrine/persistence/src/Persistence/Mapping') diff --git a/vendor/doctrine/persistence/src/Persistence/Mapping/AbstractClassMetadataFactory.php b/vendor/doctrine/persistence/src/Persistence/Mapping/AbstractClassMetadataFactory.php new file mode 100644 index 0000000..e8f6aca --- /dev/null +++ b/vendor/doctrine/persistence/src/Persistence/Mapping/AbstractClassMetadataFactory.php @@ -0,0 +1,499 @@ + + */ +abstract class AbstractClassMetadataFactory implements ClassMetadataFactory +{ + /** + * Salt used by specific Object Manager implementation. + * + * @var string + */ + protected $cacheSalt = '__CLASSMETADATA__'; + + /** @var CacheItemPoolInterface|null */ + private $cache; + + /** + * @var array + * @psalm-var CMTemplate[] + */ + private $loadedMetadata = []; + + /** @var bool */ + protected $initialized = false; + + /** @var ReflectionService|null */ + private $reflectionService = null; + + /** @var ProxyClassNameResolver|null */ + private $proxyClassNameResolver = null; + + public function setCache(CacheItemPoolInterface $cache): void + { + $this->cache = $cache; + } + + final protected function getCache(): ?CacheItemPoolInterface + { + return $this->cache; + } + + /** + * Returns an array of all the loaded metadata currently in memory. + * + * @return ClassMetadata[] + * @psalm-return CMTemplate[] + */ + public function getLoadedMetadata() + { + return $this->loadedMetadata; + } + + /** + * {@inheritDoc} + */ + public function getAllMetadata() + { + if (! $this->initialized) { + $this->initialize(); + } + + $driver = $this->getDriver(); + $metadata = []; + foreach ($driver->getAllClassNames() as $className) { + $metadata[] = $this->getMetadataFor($className); + } + + return $metadata; + } + + public function setProxyClassNameResolver(ProxyClassNameResolver $resolver): void + { + $this->proxyClassNameResolver = $resolver; + } + + /** + * Lazy initialization of this stuff, especially the metadata driver, + * since these are not needed at all when a metadata cache is active. + * + * @return void + */ + abstract protected function initialize(); + + /** + * Returns the mapping driver implementation. + * + * @return MappingDriver + */ + abstract protected function getDriver(); + + /** + * Wakes up reflection after ClassMetadata gets unserialized from cache. + * + * @psalm-param CMTemplate $class + * + * @return void + */ + abstract protected function wakeupReflection( + ClassMetadata $class, + ReflectionService $reflService + ); + + /** + * Initializes Reflection after ClassMetadata was constructed. + * + * @psalm-param CMTemplate $class + * + * @return void + */ + abstract protected function initializeReflection( + ClassMetadata $class, + ReflectionService $reflService + ); + + /** + * Checks whether the class metadata is an entity. + * + * This method should return false for mapped superclasses or embedded classes. + * + * @psalm-param CMTemplate $class + * + * @return bool + */ + abstract protected function isEntity(ClassMetadata $class); + + /** + * Removes the prepended backslash of a class string to conform with how php outputs class names + * + * @psalm-param class-string $className + * + * @psalm-return class-string + */ + private function normalizeClassName(string $className): string + { + return ltrim($className, '\\'); + } + + /** + * {@inheritDoc} + * + * @throws ReflectionException + * @throws MappingException + */ + public function getMetadataFor(string $className) + { + $className = $this->normalizeClassName($className); + + if (isset($this->loadedMetadata[$className])) { + return $this->loadedMetadata[$className]; + } + + if (class_exists($className, false) && (new ReflectionClass($className))->isAnonymous()) { + throw MappingException::classIsAnonymous($className); + } + + if (! class_exists($className, false) && strpos($className, ':') !== false) { + throw MappingException::nonExistingClass($className); + } + + $realClassName = $this->getRealClass($className); + + if (isset($this->loadedMetadata[$realClassName])) { + // We do not have the alias name in the map, include it + return $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; + } + + try { + if ($this->cache !== null) { + $cached = $this->cache->getItem($this->getCacheKey($realClassName))->get(); + if ($cached instanceof ClassMetadata) { + /** @psalm-var CMTemplate $cached */ + $this->loadedMetadata[$realClassName] = $cached; + + $this->wakeupReflection($cached, $this->getReflectionService()); + } else { + $loadedMetadata = $this->loadMetadata($realClassName); + $classNames = array_combine( + array_map([$this, 'getCacheKey'], $loadedMetadata), + $loadedMetadata + ); + + foreach ($this->cache->getItems(array_keys($classNames)) as $item) { + if (! isset($classNames[$item->getKey()])) { + continue; + } + + $item->set($this->loadedMetadata[$classNames[$item->getKey()]]); + $this->cache->saveDeferred($item); + } + + $this->cache->commit(); + } + } else { + $this->loadMetadata($realClassName); + } + } catch (MappingException $loadingException) { + $fallbackMetadataResponse = $this->onNotFoundMetadata($realClassName); + + if ($fallbackMetadataResponse === null) { + throw $loadingException; + } + + $this->loadedMetadata[$realClassName] = $fallbackMetadataResponse; + } + + if ($className !== $realClassName) { + // We do not have the alias name in the map, include it + $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; + } + + return $this->loadedMetadata[$className]; + } + + /** + * {@inheritDoc} + */ + public function hasMetadataFor(string $className) + { + $className = $this->normalizeClassName($className); + + return isset($this->loadedMetadata[$className]); + } + + /** + * Sets the metadata descriptor for a specific class. + * + * NOTE: This is only useful in very special cases, like when generating proxy classes. + * + * @psalm-param class-string $className + * @psalm-param CMTemplate $class + * + * @return void + */ + public function setMetadataFor(string $className, ClassMetadata $class) + { + $this->loadedMetadata[$this->normalizeClassName($className)] = $class; + } + + /** + * Gets an array of parent classes for the given entity class. + * + * @psalm-param class-string $name + * + * @return string[] + * @psalm-return list + */ + protected function getParentClasses(string $name) + { + // Collect parent classes, ignoring transient (not-mapped) classes. + $parentClasses = []; + + foreach (array_reverse($this->getReflectionService()->getParentClasses($name)) as $parentClass) { + if ($this->getDriver()->isTransient($parentClass)) { + continue; + } + + $parentClasses[] = $parentClass; + } + + return $parentClasses; + } + + /** + * Loads the metadata of the class in question and all it's ancestors whose metadata + * is still not loaded. + * + * Important: The class $name does not necessarily exist at this point here. + * Scenarios in a code-generation setup might have access to XML/YAML + * Mapping files without the actual PHP code existing here. That is why the + * {@see \Doctrine\Persistence\Mapping\ReflectionService} interface + * should be used for reflection. + * + * @param string $name The name of the class for which the metadata should get loaded. + * @psalm-param class-string $name + * + * @return array + * @psalm-return list + */ + protected function loadMetadata(string $name) + { + if (! $this->initialized) { + $this->initialize(); + } + + $loaded = []; + + $parentClasses = $this->getParentClasses($name); + $parentClasses[] = $name; + + // Move down the hierarchy of parent classes, starting from the topmost class + $parent = null; + $rootEntityFound = false; + $visited = []; + $reflService = $this->getReflectionService(); + + foreach ($parentClasses as $className) { + if (isset($this->loadedMetadata[$className])) { + $parent = $this->loadedMetadata[$className]; + + if ($this->isEntity($parent)) { + $rootEntityFound = true; + + array_unshift($visited, $className); + } + + continue; + } + + $class = $this->newClassMetadataInstance($className); + $this->initializeReflection($class, $reflService); + + $this->doLoadMetadata($class, $parent, $rootEntityFound, $visited); + + $this->loadedMetadata[$className] = $class; + + $parent = $class; + + if ($this->isEntity($class)) { + $rootEntityFound = true; + + array_unshift($visited, $className); + } + + $this->wakeupReflection($class, $reflService); + + $loaded[] = $className; + } + + return $loaded; + } + + /** + * Provides a fallback hook for loading metadata when loading failed due to reflection/mapping exceptions + * + * Override this method to implement a fallback strategy for failed metadata loading + * + * @return ClassMetadata|null + * @psalm-return CMTemplate|null + */ + protected function onNotFoundMetadata(string $className) + { + return null; + } + + /** + * Actually loads the metadata from the underlying metadata. + * + * @param bool $rootEntityFound True when there is another entity (non-mapped superclass) class above the current class in the PHP class hierarchy. + * @param list $nonSuperclassParents All parent class names that are not marked as mapped superclasses, with the direct parent class being the first and the root entity class the last element. + * @psalm-param CMTemplate $class + * @psalm-param CMTemplate|null $parent + * + * @return void + */ + abstract protected function doLoadMetadata( + ClassMetadata $class, + ?ClassMetadata $parent, + bool $rootEntityFound, + array $nonSuperclassParents + ); + + /** + * Creates a new ClassMetadata instance for the given class name. + * + * @psalm-param class-string $className + * + * @return ClassMetadata + * @psalm-return CMTemplate + * + * @template T of object + */ + abstract protected function newClassMetadataInstance(string $className); + + /** + * {@inheritDoc} + */ + public function isTransient(string $className) + { + if (! $this->initialized) { + $this->initialize(); + } + + if (class_exists($className, false) && (new ReflectionClass($className))->isAnonymous()) { + return false; + } + + if (! class_exists($className, false) && strpos($className, ':') !== false) { + throw MappingException::nonExistingClass($className); + } + + /** @psalm-var class-string $className */ + return $this->getDriver()->isTransient($className); + } + + /** + * Sets the reflectionService. + * + * @return void + */ + public function setReflectionService(ReflectionService $reflectionService) + { + $this->reflectionService = $reflectionService; + } + + /** + * Gets the reflection service associated with this metadata factory. + * + * @return ReflectionService + */ + public function getReflectionService() + { + if ($this->reflectionService === null) { + $this->reflectionService = new RuntimeReflectionService(); + } + + return $this->reflectionService; + } + + protected function getCacheKey(string $realClassName): string + { + return str_replace('\\', '__', $realClassName) . $this->cacheSalt; + } + + /** + * Gets the real class name of a class name that could be a proxy. + * + * @psalm-param class-string>|class-string $class + * + * @psalm-return class-string + * + * @template T of object + */ + private function getRealClass(string $class): string + { + if ($this->proxyClassNameResolver === null) { + $this->createDefaultProxyClassNameResolver(); + } + + assert($this->proxyClassNameResolver !== null); + + return $this->proxyClassNameResolver->resolveClassName($class); + } + + private function createDefaultProxyClassNameResolver(): void + { + $this->proxyClassNameResolver = new class implements ProxyClassNameResolver { + /** + * @psalm-param class-string>|class-string $className + * + * @psalm-return class-string + * + * @template T of object + */ + public function resolveClassName(string $className): string + { + $pos = strrpos($className, '\\' . Proxy::MARKER . '\\'); + + if ($pos === false) { + /** @psalm-var class-string */ + return $className; + } + + /** @psalm-var class-string */ + return substr($className, $pos + Proxy::MARKER_LENGTH + 2); + } + }; + } +} diff --git a/vendor/doctrine/persistence/src/Persistence/Mapping/ClassMetadata.php b/vendor/doctrine/persistence/src/Persistence/Mapping/ClassMetadata.php new file mode 100644 index 0000000..f407ba3 --- /dev/null +++ b/vendor/doctrine/persistence/src/Persistence/Mapping/ClassMetadata.php @@ -0,0 +1,141 @@ + + */ + public function getName(); + + /** + * Gets the mapped identifier field name. + * + * The returned structure is an array of the identifier field names. + * + * @return array + * @psalm-return list + */ + public function getIdentifier(); + + /** + * Gets the ReflectionClass instance for this mapped class. + * + * @return ReflectionClass + */ + public function getReflectionClass(); + + /** + * Checks if the given field name is a mapped identifier for this class. + * + * @return bool + */ + public function isIdentifier(string $fieldName); + + /** + * Checks if the given field is a mapped property for this class. + * + * @return bool + */ + public function hasField(string $fieldName); + + /** + * Checks if the given field is a mapped association for this class. + * + * @return bool + */ + public function hasAssociation(string $fieldName); + + /** + * Checks if the given field is a mapped single valued association for this class. + * + * @return bool + */ + public function isSingleValuedAssociation(string $fieldName); + + /** + * Checks if the given field is a mapped collection valued association for this class. + * + * @return bool + */ + public function isCollectionValuedAssociation(string $fieldName); + + /** + * A numerically indexed list of field names of this persistent class. + * + * This array includes identifier fields if present on this class. + * + * @return array + */ + public function getFieldNames(); + + /** + * Returns an array of identifier field names numerically indexed. + * + * @return array + */ + public function getIdentifierFieldNames(); + + /** + * Returns a numerically indexed list of association names of this persistent class. + * + * This array includes identifier associations if present on this class. + * + * @return array + */ + public function getAssociationNames(); + + /** + * Returns a type name of this field. + * + * This type names can be implementation specific but should at least include the php types: + * integer, string, boolean, float/double, datetime. + * + * @return string|null + */ + public function getTypeOfField(string $fieldName); + + /** + * Returns the target class name of the given association. + * + * @return string|null + * @psalm-return class-string|null + */ + public function getAssociationTargetClass(string $assocName); + + /** + * Checks if the association is the inverse side of a bidirectional association. + * + * @return bool + */ + public function isAssociationInverseSide(string $assocName); + + /** + * Returns the target field of the owning side of the association. + * + * @return string + */ + public function getAssociationMappedByTargetField(string $assocName); + + /** + * Returns the identifier of this object as an array with field name as key. + * + * Has to return an empty array if no identifier isset. + * + * @return array + */ + public function getIdentifierValues(object $object); +} diff --git a/vendor/doctrine/persistence/src/Persistence/Mapping/ClassMetadataFactory.php b/vendor/doctrine/persistence/src/Persistence/Mapping/ClassMetadataFactory.php new file mode 100644 index 0000000..28b8303 --- /dev/null +++ b/vendor/doctrine/persistence/src/Persistence/Mapping/ClassMetadataFactory.php @@ -0,0 +1,61 @@ + + */ + public function getAllMetadata(); + + /** + * Gets the class metadata descriptor for a class. + * + * @param class-string $className The name of the class. + * + * @return ClassMetadata + * @psalm-return T + */ + public function getMetadataFor(string $className); + + /** + * Checks whether the factory has the metadata for a class loaded already. + * + * @param class-string $className + * + * @return bool TRUE if the metadata of the class in question is already loaded, FALSE otherwise. + */ + public function hasMetadataFor(string $className); + + /** + * Sets the metadata descriptor for a specific class. + * + * @param class-string $className + * @psalm-param T $class + * + * @return void + */ + public function setMetadataFor(string $className, ClassMetadata $class); + + /** + * Returns whether the class with the specified name should have its metadata loaded. + * This is only the case if it is either mapped directly or as a MappedSuperclass. + * + * @psalm-param class-string $className + * + * @return bool + */ + public function isTransient(string $className); +} diff --git a/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/ColocatedMappingDriver.php b/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/ColocatedMappingDriver.php new file mode 100644 index 0000000..e85ba70 --- /dev/null +++ b/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/ColocatedMappingDriver.php @@ -0,0 +1,212 @@ + + */ + protected $paths = []; + + /** + * The paths excluded from path where to look for mapping files. + * + * @var array + */ + protected $excludePaths = []; + + /** + * The file extension of mapping documents. + * + * @var string + */ + protected $fileExtension = '.php'; + + /** + * Cache for getAllClassNames(). + * + * @var array|null + * @psalm-var list|null + */ + protected $classNames; + + /** + * Appends lookup paths to metadata driver. + * + * @param array $paths + * + * @return void + */ + public function addPaths(array $paths) + { + $this->paths = array_unique(array_merge($this->paths, $paths)); + } + + /** + * Retrieves the defined metadata lookup paths. + * + * @return array + */ + public function getPaths() + { + return $this->paths; + } + + /** + * Append exclude lookup paths to metadata driver. + * + * @param string[] $paths + * + * @return void + */ + public function addExcludePaths(array $paths) + { + $this->excludePaths = array_unique(array_merge($this->excludePaths, $paths)); + } + + /** + * Retrieve the defined metadata lookup exclude paths. + * + * @return array + */ + public function getExcludePaths() + { + return $this->excludePaths; + } + + /** + * Gets the file extension used to look for mapping files under. + * + * @return string + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Sets the file extension used to look for mapping files under. + * + * @return void + */ + public function setFileExtension(string $fileExtension) + { + $this->fileExtension = $fileExtension; + } + + /** + * {@inheritDoc} + * + * Returns whether the class with the specified name is transient. Only non-transient + * classes, that is entities and mapped superclasses, should have their metadata loaded. + * + * @psalm-param class-string $className + * + * @return bool + */ + abstract public function isTransient(string $className); + + /** + * Gets the names of all mapped classes known to this driver. + * + * @return string[] The names of all mapped classes known to this driver. + * @psalm-return list + */ + public function getAllClassNames() + { + if ($this->classNames !== null) { + return $this->classNames; + } + + if ($this->paths === []) { + throw MappingException::pathRequiredForDriver(static::class); + } + + $classes = []; + $includedFiles = []; + + foreach ($this->paths as $path) { + if (! is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new RegexIterator( + new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS), + RecursiveIteratorIterator::LEAVES_ONLY + ), + '/^.+' . preg_quote($this->fileExtension) . '$/i', + RecursiveRegexIterator::GET_MATCH + ); + + foreach ($iterator as $file) { + $sourceFile = $file[0]; + + if (preg_match('(^phar:)i', $sourceFile) === 0) { + $sourceFile = realpath($sourceFile); + } + + foreach ($this->excludePaths as $excludePath) { + $realExcludePath = realpath($excludePath); + assert($realExcludePath !== false); + $exclude = str_replace('\\', '/', $realExcludePath); + $current = str_replace('\\', '/', $sourceFile); + + if (strpos($current, $exclude) !== false) { + continue 2; + } + } + + require_once $sourceFile; + + $includedFiles[] = $sourceFile; + } + } + + $declared = get_declared_classes(); + + foreach ($declared as $className) { + $rc = new ReflectionClass($className); + + $sourceFile = $rc->getFileName(); + + if (! in_array($sourceFile, $includedFiles, true) || $this->isTransient($className)) { + continue; + } + + $classes[] = $className; + } + + $this->classNames = $classes; + + return $classes; + } +} diff --git a/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/DefaultFileLocator.php b/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/DefaultFileLocator.php new file mode 100644 index 0000000..9b00e74 --- /dev/null +++ b/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/DefaultFileLocator.php @@ -0,0 +1,175 @@ + + */ + protected $paths = []; + + /** + * The file extension of mapping documents. + * + * @var string|null + */ + protected $fileExtension; + + /** + * Initializes a new FileDriver that looks in the given path(s) for mapping + * documents and operates in the specified operating mode. + * + * @param string|array $paths One or multiple paths where mapping documents + * can be found. + * @param string|null $fileExtension The file extension of mapping documents, + * usually prefixed with a dot. + */ + public function __construct($paths, ?string $fileExtension = null) + { + $this->addPaths((array) $paths); + $this->fileExtension = $fileExtension; + } + + /** + * Appends lookup paths to metadata driver. + * + * @param array $paths + * + * @return void + */ + public function addPaths(array $paths) + { + $this->paths = array_unique(array_merge($this->paths, $paths)); + } + + /** + * Retrieves the defined metadata lookup paths. + * + * @return array + */ + public function getPaths() + { + return $this->paths; + } + + /** + * Gets the file extension used to look for mapping files under. + * + * @return string|null + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Sets the file extension used to look for mapping files under. + * + * @param string|null $fileExtension The file extension to set. + * + * @return void + */ + public function setFileExtension(?string $fileExtension) + { + $this->fileExtension = $fileExtension; + } + + /** + * {@inheritDoc} + */ + public function findMappingFile(string $className) + { + $fileName = str_replace('\\', '.', $className) . $this->fileExtension; + + // Check whether file exists + foreach ($this->paths as $path) { + if (is_file($path . DIRECTORY_SEPARATOR . $fileName)) { + return $path . DIRECTORY_SEPARATOR . $fileName; + } + } + + throw MappingException::mappingFileNotFound($className, $fileName); + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames(string $globalBasename) + { + if ($this->paths === []) { + return []; + } + + $classes = []; + + foreach ($this->paths as $path) { + if (! is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($path), + RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($iterator as $file) { + $fileName = $file->getBasename($this->fileExtension); + + if ($fileName === $file->getBasename() || $fileName === $globalBasename) { + continue; + } + + // NOTE: All files found here means classes are not transient! + + assert(is_string($fileName)); + /** @psalm-var class-string */ + $class = str_replace('.', '\\', $fileName); + $classes[] = $class; + } + } + + return $classes; + } + + /** + * {@inheritDoc} + */ + public function fileExists(string $className) + { + $fileName = str_replace('\\', '.', $className) . $this->fileExtension; + + // Check whether file exists + foreach ($this->paths as $path) { + if (is_file($path . DIRECTORY_SEPARATOR . $fileName)) { + return true; + } + } + + return false; + } +} diff --git a/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/FileDriver.php b/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/FileDriver.php new file mode 100644 index 0000000..c116233 --- /dev/null +++ b/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/FileDriver.php @@ -0,0 +1,213 @@ +|null + */ + protected $classCache; + + /** @var string */ + protected $globalBasename = ''; + + /** + * Initializes a new FileDriver that looks in the given path(s) for mapping + * documents and operates in the specified operating mode. + * + * @param string|array|FileLocator $locator A FileLocator or one/multiple paths + * where mapping documents can be found. + */ + public function __construct($locator, ?string $fileExtension = null) + { + if ($locator instanceof FileLocator) { + $this->locator = $locator; + } else { + $this->locator = new DefaultFileLocator((array) $locator, $fileExtension); + } + } + + /** + * Sets the global basename. + * + * @return void + */ + public function setGlobalBasename(string $file) + { + $this->globalBasename = $file; + } + + /** + * Retrieves the global basename. + * + * @return string|null + */ + public function getGlobalBasename() + { + return $this->globalBasename; + } + + /** + * Gets the element of schema meta data for the class from the mapping file. + * This will lazily load the mapping file if it is not loaded yet. + * + * @psalm-param class-string $className + * + * @return T The element of schema meta data. + * + * @throws MappingException + */ + public function getElement(string $className) + { + if ($this->classCache === null) { + $this->initialize(); + } + + if (isset($this->classCache[$className])) { + return $this->classCache[$className]; + } + + $result = $this->loadMappingFile($this->locator->findMappingFile($className)); + + if (! isset($result[$className])) { + throw MappingException::invalidMappingFile( + $className, + str_replace('\\', '.', $className) . $this->locator->getFileExtension() + ); + } + + $this->classCache[$className] = $result[$className]; + + return $result[$className]; + } + + /** + * {@inheritDoc} + */ + public function isTransient(string $className) + { + if ($this->classCache === null) { + $this->initialize(); + } + + if (isset($this->classCache[$className])) { + return false; + } + + return ! $this->locator->fileExists($className); + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames() + { + if ($this->classCache === null) { + $this->initialize(); + } + + if ($this->classCache === []) { + return $this->locator->getAllClassNames($this->globalBasename); + } + + /** @psalm-var array> $classCache */ + $classCache = $this->classCache; + + /** @var list $keys */ + $keys = array_keys($classCache); + + return array_values(array_unique(array_merge( + $keys, + $this->locator->getAllClassNames($this->globalBasename) + ))); + } + + /** + * Loads a mapping file with the given name and returns a map + * from class/entity names to their corresponding file driver elements. + * + * @param string $file The mapping file to load. + * + * @return mixed[] + * @psalm-return array + */ + abstract protected function loadMappingFile(string $file); + + /** + * Initializes the class cache from all the global files. + * + * Using this feature adds a substantial performance hit to file drivers as + * more metadata has to be loaded into memory than might actually be + * necessary. This may not be relevant to scenarios where caching of + * metadata is in place, however hits very hard in scenarios where no + * caching is used. + * + * @return void + */ + protected function initialize() + { + $this->classCache = []; + if ($this->globalBasename === '') { + return; + } + + foreach ($this->locator->getPaths() as $path) { + $file = $path . '/' . $this->globalBasename . $this->locator->getFileExtension(); + if (! is_file($file)) { + continue; + } + + $this->classCache = array_merge( + $this->classCache, + $this->loadMappingFile($file) + ); + } + } + + /** + * Retrieves the locator used to discover mapping files by className. + * + * @return FileLocator + */ + public function getLocator() + { + return $this->locator; + } + + /** + * Sets the locator used to discover mapping files by className. + * + * @return void + */ + public function setLocator(FileLocator $locator) + { + $this->locator = $locator; + } +} diff --git a/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/FileLocator.php b/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/FileLocator.php new file mode 100644 index 0000000..e57d539 --- /dev/null +++ b/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/FileLocator.php @@ -0,0 +1,52 @@ + + * @psalm-return list + */ + public function getAllClassNames(string $globalBasename); + + /** + * Checks if a file can be found for this class name. + * + * @return bool + */ + public function fileExists(string $className); + + /** + * Gets all the paths that this file locator looks for mapping files. + * + * @return array + */ + public function getPaths(); + + /** + * Gets the file extension that mapping files are suffixed with. + * + * @return string|null + */ + public function getFileExtension(); +} diff --git a/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/MappingDriver.php b/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/MappingDriver.php new file mode 100644 index 0000000..9b6f0c8 --- /dev/null +++ b/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/MappingDriver.php @@ -0,0 +1,43 @@ + $className + * @psalm-param ClassMetadata $metadata + * + * @return void + * + * @template T of object + */ + public function loadMetadataForClass(string $className, ClassMetadata $metadata); + + /** + * Gets the names of all mapped classes known to this driver. + * + * @return array The names of all mapped classes known to this driver. + * @psalm-return list + */ + public function getAllClassNames(); + + /** + * Returns whether the class with the specified name should have its metadata loaded. + * This is only the case if it is either mapped as an Entity or a MappedSuperclass. + * + * @psalm-param class-string $className + * + * @return bool + */ + public function isTransient(string $className); +} diff --git a/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/MappingDriverChain.php b/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/MappingDriverChain.php new file mode 100644 index 0000000..8563dd2 --- /dev/null +++ b/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/MappingDriverChain.php @@ -0,0 +1,142 @@ + */ + private $drivers = []; + + /** + * Gets the default driver. + * + * @return MappingDriver|null + */ + public function getDefaultDriver() + { + return $this->defaultDriver; + } + + /** + * Set the default driver. + * + * @return void + */ + public function setDefaultDriver(MappingDriver $driver) + { + $this->defaultDriver = $driver; + } + + /** + * Adds a nested driver. + * + * @return void + */ + public function addDriver(MappingDriver $nestedDriver, string $namespace) + { + $this->drivers[$namespace] = $nestedDriver; + } + + /** + * Gets the array of nested drivers. + * + * @return array $drivers + */ + public function getDrivers() + { + return $this->drivers; + } + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass(string $className, ClassMetadata $metadata) + { + foreach ($this->drivers as $namespace => $driver) { + if (strpos($className, $namespace) === 0) { + $driver->loadMetadataForClass($className, $metadata); + + return; + } + } + + if ($this->defaultDriver !== null) { + $this->defaultDriver->loadMetadataForClass($className, $metadata); + + return; + } + + throw MappingException::classNotFoundInNamespaces($className, array_keys($this->drivers)); + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames() + { + $classNames = []; + $driverClasses = []; + + foreach ($this->drivers as $namespace => $driver) { + $oid = spl_object_hash($driver); + + if (! isset($driverClasses[$oid])) { + $driverClasses[$oid] = $driver->getAllClassNames(); + } + + foreach ($driverClasses[$oid] as $className) { + if (strpos($className, $namespace) !== 0) { + continue; + } + + $classNames[$className] = true; + } + } + + if ($this->defaultDriver !== null) { + foreach ($this->defaultDriver->getAllClassNames() as $className) { + $classNames[$className] = true; + } + } + + return array_keys($classNames); + } + + /** + * {@inheritDoc} + */ + public function isTransient(string $className) + { + foreach ($this->drivers as $namespace => $driver) { + if (strpos($className, $namespace) === 0) { + return $driver->isTransient($className); + } + } + + if ($this->defaultDriver !== null) { + return $this->defaultDriver->isTransient($className); + } + + return true; + } +} diff --git a/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/PHPDriver.php b/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/PHPDriver.php new file mode 100644 index 0000000..1c1ab9c --- /dev/null +++ b/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/PHPDriver.php @@ -0,0 +1,49 @@ +> + */ +class PHPDriver extends FileDriver +{ + /** + * @var ClassMetadata + * @psalm-var ClassMetadata + */ + protected $metadata; + + /** @param string|array|FileLocator $locator */ + public function __construct($locator) + { + parent::__construct($locator, '.php'); + } + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass(string $className, ClassMetadata $metadata) + { + $this->metadata = $metadata; + + $this->loadMappingFile($this->locator->findMappingFile($className)); + } + + /** + * {@inheritDoc} + */ + protected function loadMappingFile(string $file) + { + $metadata = $this->metadata; + include $file; + + return [$metadata->getName() => $metadata]; + } +} diff --git a/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/StaticPHPDriver.php b/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/StaticPHPDriver.php new file mode 100644 index 0000000..bbcff75 --- /dev/null +++ b/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/StaticPHPDriver.php @@ -0,0 +1,132 @@ + + */ + private $paths = []; + + /** + * Map of all class names. + * + * @var array + * @psalm-var list + */ + private $classNames; + + /** @param array|string $paths */ + public function __construct($paths) + { + $this->addPaths((array) $paths); + } + + /** + * @param array $paths + * + * @return void + */ + public function addPaths(array $paths) + { + $this->paths = array_unique(array_merge($this->paths, $paths)); + } + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass(string $className, ClassMetadata $metadata) + { + $className::loadMetadata($metadata); + } + + /** + * {@inheritDoc} + * + * @todo Same code exists in ColocatedMappingDriver, should we re-use it + * somehow or not worry about it? + */ + public function getAllClassNames() + { + if ($this->classNames !== null) { + return $this->classNames; + } + + if ($this->paths === []) { + throw MappingException::pathRequiredForDriver(static::class); + } + + $classes = []; + $includedFiles = []; + + foreach ($this->paths as $path) { + if (! is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($path), + RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($iterator as $file) { + if ($file->getBasename('.php') === $file->getBasename()) { + continue; + } + + $sourceFile = realpath($file->getPathName()); + require_once $sourceFile; + $includedFiles[] = $sourceFile; + } + } + + $declared = get_declared_classes(); + + foreach ($declared as $className) { + $rc = new ReflectionClass($className); + + $sourceFile = $rc->getFileName(); + + if (! in_array($sourceFile, $includedFiles, true) || $this->isTransient($className)) { + continue; + } + + $classes[] = $className; + } + + $this->classNames = $classes; + + return $classes; + } + + /** + * {@inheritDoc} + */ + public function isTransient(string $className) + { + return ! method_exists($className, 'loadMetadata'); + } +} diff --git a/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/SymfonyFileLocator.php b/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/SymfonyFileLocator.php new file mode 100644 index 0000000..428d5fb --- /dev/null +++ b/vendor/doctrine/persistence/src/Persistence/Mapping/Driver/SymfonyFileLocator.php @@ -0,0 +1,265 @@ + + */ + protected $paths = []; + + /** + * A map of mapping directory path to namespace prefix used to expand class shortnames. + * + * @var array + */ + protected $prefixes = []; + + /** + * File extension that is searched for. + * + * @var string|null + */ + protected $fileExtension; + + /** + * Represents PHP namespace delimiters when looking for files + * + * @var string + */ + private $nsSeparator; + + /** + * @param array $prefixes + * @param string $nsSeparator String which would be used when converting FQCN + * to filename and vice versa. Should not be empty + */ + public function __construct( + array $prefixes, + string $fileExtension = '', + string $nsSeparator = '.' + ) { + $this->addNamespacePrefixes($prefixes); + $this->fileExtension = $fileExtension; + + if ($nsSeparator === '') { + throw new InvalidArgumentException('Namespace separator should not be empty'); + } + + $this->nsSeparator = $nsSeparator; + } + + /** + * Adds Namespace Prefixes. + * + * @param array $prefixes + * + * @return void + */ + public function addNamespacePrefixes(array $prefixes) + { + $this->prefixes = array_merge($this->prefixes, $prefixes); + $this->paths = array_merge($this->paths, array_keys($prefixes)); + } + + /** + * Gets Namespace Prefixes. + * + * @return string[] + */ + public function getNamespacePrefixes() + { + return $this->prefixes; + } + + /** + * {@inheritDoc} + */ + public function getPaths() + { + return $this->paths; + } + + /** + * {@inheritDoc} + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Sets the file extension used to look for mapping files under. + * + * @param string $fileExtension The file extension to set. + * + * @return void + */ + public function setFileExtension(string $fileExtension) + { + $this->fileExtension = $fileExtension; + } + + /** + * {@inheritDoc} + */ + public function fileExists(string $className) + { + $defaultFileName = str_replace('\\', $this->nsSeparator, $className) . $this->fileExtension; + foreach ($this->paths as $path) { + if (! isset($this->prefixes[$path])) { + // global namespace class + if (is_file($path . DIRECTORY_SEPARATOR . $defaultFileName)) { + return true; + } + + continue; + } + + $prefix = $this->prefixes[$path]; + + if (strpos($className, $prefix . '\\') !== 0) { + continue; + } + + $filename = $path . '/' . strtr(substr($className, strlen($prefix) + 1), '\\', $this->nsSeparator) . $this->fileExtension; + + if (is_file($filename)) { + return true; + } + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames(?string $globalBasename = null) + { + if ($this->paths === []) { + return []; + } + + $classes = []; + + foreach ($this->paths as $path) { + if (! is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($path), + RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($iterator as $file) { + $fileName = $file->getBasename($this->fileExtension); + + if ($fileName === $file->getBasename() || $fileName === $globalBasename) { + continue; + } + + // NOTE: All files found here means classes are not transient! + if (isset($this->prefixes[$path])) { + // Calculate namespace suffix for given prefix as a relative path from basepath to file path + $nsSuffix = strtr( + substr($this->realpath($file->getPath()), strlen($this->realpath($path))), + $this->nsSeparator, + '\\' + ); + + /** @psalm-var class-string */ + $class = $this->prefixes[$path] . str_replace(DIRECTORY_SEPARATOR, '\\', $nsSuffix) . '\\' . str_replace($this->nsSeparator, '\\', $fileName); + } else { + /** @psalm-var class-string */ + $class = str_replace($this->nsSeparator, '\\', $fileName); + } + + $classes[] = $class; + } + } + + return $classes; + } + + /** + * {@inheritDoc} + */ + public function findMappingFile(string $className) + { + $defaultFileName = str_replace('\\', $this->nsSeparator, $className) . $this->fileExtension; + foreach ($this->paths as $path) { + if (! isset($this->prefixes[$path])) { + if (is_file($path . DIRECTORY_SEPARATOR . $defaultFileName)) { + return $path . DIRECTORY_SEPARATOR . $defaultFileName; + } + + continue; + } + + $prefix = $this->prefixes[$path]; + + if (strpos($className, $prefix . '\\') !== 0) { + continue; + } + + $filename = $path . '/' . strtr(substr($className, strlen($prefix) + 1), '\\', $this->nsSeparator) . $this->fileExtension; + if (is_file($filename)) { + return $filename; + } + } + + $pos = strrpos($className, '\\'); + assert(is_int($pos)); + + throw MappingException::mappingFileNotFound( + $className, + substr($className, $pos + 1) . $this->fileExtension + ); + } + + private function realpath(string $path): string + { + $realpath = realpath($path); + + if ($realpath === false) { + throw new RuntimeException(sprintf('Could not get realpath for %s', $path)); + } + + return $realpath; + } +} diff --git a/vendor/doctrine/persistence/src/Persistence/Mapping/MappingException.php b/vendor/doctrine/persistence/src/Persistence/Mapping/MappingException.php new file mode 100644 index 0000000..f0bc4eb --- /dev/null +++ b/vendor/doctrine/persistence/src/Persistence/Mapping/MappingException.php @@ -0,0 +1,88 @@ + $namespaces + * + * @return self + */ + public static function classNotFoundInNamespaces( + string $className, + array $namespaces + ) { + return new self(sprintf( + "The class '%s' was not found in the chain configured namespaces %s", + $className, + implode(', ', $namespaces) + )); + } + + /** @param class-string $driverClassName */ + public static function pathRequiredForDriver(string $driverClassName): self + { + return new self(sprintf( + 'Specifying the paths to your entities is required when using %s to retrieve all class names.', + $driverClassName + )); + } + + /** @return self */ + public static function fileMappingDriversRequireConfiguredDirectoryPath( + ?string $path = null + ) { + if ($path !== null) { + $path = '[' . $path . ']'; + } + + return new self(sprintf( + 'File mapping drivers must have a valid directory path, ' . + 'however the given path %s seems to be incorrect!', + (string) $path + )); + } + + /** @return self */ + public static function mappingFileNotFound(string $entityName, string $fileName) + { + return new self(sprintf( + "No mapping file found named '%s' for class '%s'.", + $fileName, + $entityName + )); + } + + /** @return self */ + public static function invalidMappingFile(string $entityName, string $fileName) + { + return new self(sprintf( + "Invalid mapping file '%s' for class '%s'.", + $fileName, + $entityName + )); + } + + /** @return self */ + public static function nonExistingClass(string $className) + { + return new self(sprintf("Class '%s' does not exist", $className)); + } + + /** @param class-string $className */ + public static function classIsAnonymous(string $className): self + { + return new self(sprintf('Class "%s" is anonymous', $className)); + } +} diff --git a/vendor/doctrine/persistence/src/Persistence/Mapping/ProxyClassNameResolver.php b/vendor/doctrine/persistence/src/Persistence/Mapping/ProxyClassNameResolver.php new file mode 100644 index 0000000..2801d90 --- /dev/null +++ b/vendor/doctrine/persistence/src/Persistence/Mapping/ProxyClassNameResolver.php @@ -0,0 +1,19 @@ +>|class-string $className + * + * @psalm-return class-string + * + * @template T of object + */ + public function resolveClassName(string $className): string; +} diff --git a/vendor/doctrine/persistence/src/Persistence/Mapping/ReflectionService.php b/vendor/doctrine/persistence/src/Persistence/Mapping/ReflectionService.php new file mode 100644 index 0000000..9484e1f --- /dev/null +++ b/vendor/doctrine/persistence/src/Persistence/Mapping/ReflectionService.php @@ -0,0 +1,75 @@ + $class + * + * @return ReflectionClass|null + * @psalm-return ReflectionClass|null + * + * @template T of object + */ + public function getClass(string $class); + + /** + * Returns an accessible property (setAccessible(true)) or null. + * + * @psalm-param class-string $class + * + * @return ReflectionProperty|null + */ + public function getAccessibleProperty(string $class, string $property); + + /** + * Checks if the class have a public method with the given name. + * + * @psalm-param class-string $class + * + * @return bool + */ + public function hasPublicMethod(string $class, string $method); +} diff --git a/vendor/doctrine/persistence/src/Persistence/Mapping/RuntimeReflectionService.php b/vendor/doctrine/persistence/src/Persistence/Mapping/RuntimeReflectionService.php new file mode 100644 index 0000000..399e057 --- /dev/null +++ b/vendor/doctrine/persistence/src/Persistence/Mapping/RuntimeReflectionService.php @@ -0,0 +1,111 @@ +supportsTypedPropertiesWorkaround = version_compare(phpversion(), '7.4.0') >= 0; + } + + /** + * {@inheritDoc} + */ + public function getParentClasses(string $class) + { + if (! class_exists($class)) { + throw MappingException::nonExistingClass($class); + } + + $parents = class_parents($class); + + assert($parents !== false); + + return $parents; + } + + /** + * {@inheritDoc} + */ + public function getClassShortName(string $class) + { + $reflectionClass = new ReflectionClass($class); + + return $reflectionClass->getShortName(); + } + + /** + * {@inheritDoc} + */ + public function getClassNamespace(string $class) + { + $reflectionClass = new ReflectionClass($class); + + return $reflectionClass->getNamespaceName(); + } + + /** + * @psalm-param class-string $class + * + * @return ReflectionClass + * @psalm-return ReflectionClass + * + * @template T of object + */ + public function getClass(string $class) + { + return new ReflectionClass($class); + } + + /** + * {@inheritDoc} + */ + public function getAccessibleProperty(string $class, string $property) + { + $reflectionProperty = new RuntimeReflectionProperty($class, $property); + + if ($this->supportsTypedPropertiesWorkaround && ! array_key_exists($property, $this->getClass($class)->getDefaultProperties())) { + $reflectionProperty = new TypedNoDefaultReflectionProperty($class, $property); + } + + $reflectionProperty->setAccessible(true); + + return $reflectionProperty; + } + + /** + * {@inheritDoc} + */ + public function hasPublicMethod(string $class, string $method) + { + try { + $reflectionMethod = new ReflectionMethod($class, $method); + } catch (ReflectionException $e) { + return false; + } + + return $reflectionMethod->isPublic(); + } +} diff --git a/vendor/doctrine/persistence/src/Persistence/Mapping/StaticReflectionService.php b/vendor/doctrine/persistence/src/Persistence/Mapping/StaticReflectionService.php new file mode 100644 index 0000000..c9f2147 --- /dev/null +++ b/vendor/doctrine/persistence/src/Persistence/Mapping/StaticReflectionService.php @@ -0,0 +1,78 @@ +