.doctrine-project.json000064400000002740151742734500011003 0ustar00{ "active": true, "name": "Persistence", "slug": "persistence", "docsSlug": "doctrine-persistence", "versions": [ { "name": "3.0", "branchName": "3.0.x", "slug": "latest", "upcoming": true }, { "name": "2.2", "branchName": "2.2.x", "slug": "2.2", "upcoming": true }, { "name": "2.1", "branchName": "2.1.x", "slug": "2.1", "current": true, "aliases": [ "current", "stable" ] }, { "name": "2.0", "branchName": "2.0.x", "slug": "2.0", "maintained": false }, { "name": "1.4", "branchName": "1.4.x", "slug": "1.4", "maintained": false }, { "name": "1.3", "branchName": "1.3.x", "slug": "1.3", "maintained": false }, { "name": "1.2", "branchName": "1.2.x", "slug": "1.2", "maintained": false }, { "name": "1.1", "branchName": "1.1.x", "slug": "1.1", "maintained": false }, { "name": "1.0", "branchName": "1.0.x", "slug": "1.0", "maintained": false } ] } .github/workflows/coding-standards.yml000064400000001567151742734500014131 0ustar00 name: "Coding Standards" on: pull_request: branches: - "*.x" push: branches: - "*.x" env: COMPOSER_ROOT_VERSION: "2.1" jobs: coding-standards: name: "Coding Standards" runs-on: "ubuntu-20.04" strategy: matrix: php-version: - "7.4" steps: - name: "Checkout" uses: "actions/checkout@v2" - name: "Install PHP" uses: "shivammathur/setup-php@v2" with: coverage: "none" php-version: "${{ matrix.php-version }}" tools: "cs2pr" - name: "Install dependencies with Composer" uses: "ramsey/composer-install@v1" with: dependency-versions: "${{ matrix.deps }}" # https://github.com/doctrine/.github/issues/3 - name: "Run PHP_CodeSniffer" run: "vendor/bin/phpcs -q --no-colors --report=checkstyle | cs2pr" .github/workflows/continuous-integration.yml000064400000002430151742734500015422 0ustar00 name: "Continuous Integration" on: pull_request: null env: COMPOSER_ROOT_VERSION: "2.1" jobs: phpunit: name: "PHPUnit" runs-on: "ubuntu-20.04" strategy: matrix: php-version: - "7.1" - "7.2" - "7.3" - "7.4" - "8.0" deps: - "highest" include: - deps: "lowest" php-version: "7.1" steps: - name: "Checkout" uses: "actions/checkout@v2" with: fetch-depth: 2 - name: "Install PHP with pcov" uses: "shivammathur/setup-php@v2" if: "${{ matrix.php-version != '7.1' }}" with: php-version: "${{ matrix.php-version }}" coverage: "pcov" - name: "Install PHP with xdebug" uses: "shivammathur/setup-php@v2" if: "${{ matrix.php-version == '7.1' }}" with: php-version: "${{ matrix.php-version }}" coverage: "xdebug" - name: "Install dependencies with Composer" uses: "ramsey/composer-install@v1" with: dependency-versions: "${{ matrix.deps }}" - name: "Run PHPUnit" run: "vendor/bin/phpunit --coverage-clover=coverage.xml" - name: "Upload to Codecov" uses: "codecov/codecov-action@v1" .github/workflows/release-on-milestone-closed.yml000064400000003703151742734500016175 0ustar00name: "Automatic Releases" on: milestone: types: - "closed" jobs: release: name: "Git tag, release & create merge-up PR" runs-on: "ubuntu-20.04" steps: - name: "Checkout" uses: "actions/checkout@v2" - name: "Release" uses: "laminas/automatic-releases@v1" with: command-name: "laminas:automatic-releases:release" env: "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }} "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} - name: "Create Merge-Up Pull Request" uses: "laminas/automatic-releases@v1" with: command-name: "laminas:automatic-releases:create-merge-up-pull-request" env: "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }} "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} - name: "Create and/or Switch to new Release Branch" uses: "laminas/automatic-releases@v1" with: command-name: "laminas:automatic-releases:switch-default-branch-to-next-minor" env: "GITHUB_TOKEN": ${{ secrets.ORGANIZATION_ADMIN_TOKEN }} "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} - name: "Create new milestones" uses: "laminas/automatic-releases@v1" with: command-name: "laminas:automatic-releases:create-milestones" env: "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }} "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} .github/workflows/static-analysis.yml000064400000003064151742734500014007 0ustar00 name: "Static Analysis" on: pull_request: branches: - "*.x" push: branches: - "*.x" env: COMPOSER_ROOT_VERSION: "2.1" jobs: static-analysis-phpstan: name: "Static Analysis with PHPStan" runs-on: "ubuntu-20.04" strategy: matrix: php-version: - "7.4" steps: - name: "Checkout code" uses: "actions/checkout@v2" - name: "Install PHP" uses: "shivammathur/setup-php@v2" with: coverage: "none" php-version: "${{ matrix.php-version }}" tools: "cs2pr" - name: "Install dependencies with Composer" uses: "ramsey/composer-install@v1" with: dependency-versions: "${{ matrix.deps }}" - name: "Run a static analysis with phpstan/phpstan" run: "vendor/bin/phpstan analyse --error-format=checkstyle | cs2pr" static-analysis-psalm: name: "Static Analysis with Psalm" runs-on: "ubuntu-20.04" strategy: matrix: php-version: - "7.4" steps: - name: "Checkout code" uses: "actions/checkout@v2" - name: "Install PHP" uses: "shivammathur/setup-php@v2" with: coverage: "none" php-version: "${{ matrix.php-version }}" - name: "Install dependencies with Composer" uses: "ramsey/composer-install@v1" with: dependency-versions: "highest" - name: "Run a static analysis with vimeo/psalm" run: "vendor/bin/psalm --show-info=false --stats --output-format=github --threads=$(nproc)" LICENSE000064400000002051151742734500005557 0ustar00Copyright (c) 2006-2015 Doctrine Project Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. README.md000064400000001166151742734500006037 0ustar00# Doctrine Persistence [![Build Status](https://travis-ci.org/doctrine/persistence.svg)](https://travis-ci.org/doctrine/persistence) [![Code Coverage](https://codecov.io/gh/doctrine/persistence/branch/2.1.x/graph/badge.svg)](https://codecov.io/gh/doctrine/persistence/branch/2.1.x) The Doctrine Persistence project is a library that provides common abstractions for object mapper persistence. ## More resources: * [Website](https://www.doctrine-project.org/) * [Documentation](https://www.doctrine-project.org/projects/doctrine-persistence/en/latest/index.html) * [Downloads](https://github.com/doctrine/persistence/releases) UPGRADE-1.2.md000064400000000577151742734500006474 0ustar00UPGRADE FROM 1.x to 1.2 ======================= * Deprecated `ObjectManager::merge()`. Please handle merge operations in your application instead. * Deprecated `ObjectManager::detach()`. Please use `ObjectManager::clear()` instead. * Deprecated `PersistentObject` class. Please implement this functionality directly in your application if you want ActiveRecord style functionality. UPGRADE-2.2.md000064400000001214151742734500006462 0ustar00UPGRADE FROM 2.1 to 2.2 ======================= * Deprecated using doctrine/cache for metadata caching. The `setCacheDriver` and `getCacheDriver` methods in `Doctrine\Persistence\Mapping\AbstractMetadata` have been deprecated. Please use `getCache` and `setCache` with a PSR-6 implementation instead. Note that even after switching to PSR-6, `getCacheDriver` will return a cache instance that wraps the PSR-6 cache. Note that if you use a custom implementation of doctrine/cache, the library may not be able to provide a forward compatibility layer. The cache implementation MUST extend the `Doctrine\Common\Cache\CacheProvider` class. composer.json000064400000003531151742734500007300 0ustar00{ "name": "doctrine/persistence", "type": "library", "description": "The Doctrine Persistence project is a set of shared interfaces and functionality that the different Doctrine object mappers share.", "keywords": [ "persistence", "object", "mapper", "orm", "odm" ], "homepage": "https://doctrine-project.org/projects/persistence.html", "license": "MIT", "authors": [ {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, {"name": "Roman Borschel", "email": "roman@code-factory.org"}, {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, {"name": "Jonathan Wage", "email": "jonwage@gmail.com"}, {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}, {"name": "Marco Pivetta", "email": "ocramius@gmail.com"} ], "require": { "php": "^7.1 || ^8.0", "doctrine/annotations": "^1.0", "doctrine/cache": "^1.11 || ^2.0", "doctrine/collections": "^1.0", "doctrine/event-manager": "^1.0", "psr/cache": "^1.0|^2.0|^3.0" }, "require-dev": { "composer/package-versions-deprecated": "^1.11", "phpstan/phpstan": "0.12.84", "doctrine/coding-standard": "^6.0 || ^9.0", "doctrine/common": "^3.0", "phpunit/phpunit": "^7.5.20 || ^8.0 || ^9.0", "symfony/cache": "^4.4|^5.0", "vimeo/psalm": "4.7.0" }, "conflict": { "doctrine/common": "<2.10@dev" }, "autoload": { "psr-4": { "Doctrine\\Common\\": "lib/Doctrine/Common", "Doctrine\\Persistence\\": "lib/Doctrine/Persistence" } }, "autoload-dev": { "psr-4": { "Doctrine\\Tests\\": "tests/Doctrine/Tests", "Doctrine\\Tests_PHP74\\": "tests/Doctrine/Tests_PHP74" } } } lib/Doctrine/Common/Persistence/PersistentObject.php000064400000017107151742734500016613 0ustar00getId(); // method exists through __call */ abstract class PersistentObject implements ObjectManagerAware { /** @var ObjectManager|null */ private static $objectManager = null; /** @var ClassMetadata|null */ private $cm = null; /** * Sets the object manager responsible for all persistent object base classes. * * @return void */ public static function setObjectManager(?ObjectManager $objectManager = null) { self::$objectManager = $objectManager; } /** * @return ObjectManager|null */ public static function getObjectManager() { return self::$objectManager; } /** * Injects the Doctrine Object Manager. * * @return void * * @throws RuntimeException */ public function injectObjectManager(ObjectManager $objectManager, ClassMetadata $classMetadata) { if ($objectManager !== self::$objectManager) { throw new RuntimeException('Trying to use PersistentObject with different ObjectManager instances. ' . 'Was PersistentObject::setObjectManager() called?'); } $this->cm = $classMetadata; } /** * Sets a persistent fields value. * * @param string $field * @param mixed[] $args * * @return void * * @throws BadMethodCallException When no persistent field exists by that name. * @throws InvalidArgumentException When the wrong target object type is passed to an association. */ private function set($field, $args) { if ($this->cm->hasField($field) && ! $this->cm->isIdentifier($field)) { $this->$field = $args[0]; } elseif ($this->cm->hasAssociation($field) && $this->cm->isSingleValuedAssociation($field)) { $targetClass = $this->cm->getAssociationTargetClass($field); if (! ($args[0] instanceof $targetClass) && $args[0] !== null) { throw new InvalidArgumentException("Expected persistent object of type '" . $targetClass . "'"); } $this->$field = $args[0]; $this->completeOwningSide($field, $targetClass, $args[0]); } else { throw new BadMethodCallException("no field with name '" . $field . "' exists on '" . $this->cm->getName() . "'"); } } /** * Gets a persistent field value. * * @param string $field * * @return mixed * * @throws BadMethodCallException When no persistent field exists by that name. */ private function get($field) { if ($this->cm->hasField($field) || $this->cm->hasAssociation($field)) { return $this->$field; } throw new BadMethodCallException("no field with name '" . $field . "' exists on '" . $this->cm->getName() . "'"); } /** * If this is an inverse side association, completes the owning side. * * @param string $field * @param string $targetClass * @param object $targetObject * * @return void */ private function completeOwningSide($field, $targetClass, $targetObject) { // add this object on the owning side as well, for obvious infinite recursion // reasons this is only done when called on the inverse side. if (! $this->cm->isAssociationInverseSide($field)) { return; } $mappedByField = $this->cm->getAssociationMappedByTargetField($field); $targetMetadata = self::$objectManager->getClassMetadata($targetClass); $setter = ($targetMetadata->isCollectionValuedAssociation($mappedByField) ? 'add' : 'set') . $mappedByField; $targetObject->$setter($this); } /** * Adds an object to a collection. * * @param string $field * @param mixed[] $args * * @return void * * @throws BadMethodCallException * @throws InvalidArgumentException */ private function add($field, $args) { if (! $this->cm->hasAssociation($field) || ! $this->cm->isCollectionValuedAssociation($field)) { throw new BadMethodCallException('There is no method add' . $field . '() on ' . $this->cm->getName()); } $targetClass = $this->cm->getAssociationTargetClass($field); if (! ($args[0] instanceof $targetClass)) { throw new InvalidArgumentException("Expected persistent object of type '" . $targetClass . "'"); } if (! ($this->$field instanceof Collection)) { $this->$field = new ArrayCollection($this->$field ?: []); } $this->$field->add($args[0]); $this->completeOwningSide($field, $targetClass, $args[0]); } /** * Initializes Doctrine Metadata for this class. * * @return void * * @throws RuntimeException */ private function initializeDoctrine() { if ($this->cm !== null) { return; } if (! self::$objectManager) { throw new RuntimeException('No runtime object manager set. Call PersistentObject#setObjectManager().'); } $this->cm = self::$objectManager->getClassMetadata(static::class); } /** * Magic methods. * * @param string $method * @param mixed[] $args * * @return mixed * * @throws BadMethodCallException */ public function __call($method, $args) { $this->initializeDoctrine(); $command = substr($method, 0, 3); $field = lcfirst(substr($method, 3)); if ($command === 'set') { $this->set($field, $args); } elseif ($command === 'get') { return $this->get($field); } elseif ($command === 'add') { $this->add($field, $args); } else { throw new BadMethodCallException('There is no method ' . $method . ' on ' . $this->cm->getName()); } } } lib/Doctrine/Persistence/AbstractManagerRegistry.php000064400000014526151742734500016665 0ustar00name = $name; $this->connections = $connections; $this->managers = $managers; $this->defaultConnection = $defaultConnection; $this->defaultManager = $defaultManager; $this->proxyInterfaceName = $proxyInterfaceName; } /** * Fetches/creates the given services. * * A service in this context is connection or a manager instance. * * @param string $name The name of the service. * * @return ObjectManager The instance of the given service. */ abstract protected function getService($name); /** * Resets the given services. * * A service in this context is connection or a manager instance. * * @param string $name The name of the service. * * @return void */ abstract protected function resetService($name); /** * Gets the name of the registry. * * @return string */ public function getName() { return $this->name; } /** * {@inheritdoc} */ public function getConnection($name = null) { if ($name === null) { $name = $this->defaultConnection; } if (! isset($this->connections[$name])) { throw new InvalidArgumentException(sprintf('Doctrine %s Connection named "%s" does not exist.', $this->name, $name)); } return $this->getService($this->connections[$name]); } /** * {@inheritdoc} */ public function getConnectionNames() { return $this->connections; } /** * {@inheritdoc} */ public function getConnections() { $connections = []; foreach ($this->connections as $name => $id) { $connections[$name] = $this->getService($id); } return $connections; } /** * {@inheritdoc} */ public function getDefaultConnectionName() { return $this->defaultConnection; } /** * {@inheritdoc} */ public function getDefaultManagerName() { return $this->defaultManager; } /** * {@inheritdoc} * * @throws InvalidArgumentException */ public function getManager($name = null) { if ($name === null) { $name = $this->defaultManager; } if (! isset($this->managers[$name])) { throw new InvalidArgumentException(sprintf('Doctrine %s Manager named "%s" does not exist.', $this->name, $name)); } return $this->getService($this->managers[$name]); } /** * {@inheritdoc} */ public function getManagerForClass($class) { $className = $this->getRealClassName($class); $proxyClass = new ReflectionClass($className); if ($proxyClass->implementsInterface($this->proxyInterfaceName)) { $parentClass = $proxyClass->getParentClass(); if (! $parentClass) { return null; } $className = $parentClass->getName(); } foreach ($this->managers as $id) { $manager = $this->getService($id); if (! $manager->getMetadataFactory()->isTransient($className)) { return $manager; } } return null; } /** * {@inheritdoc} */ public function getManagerNames() { return $this->managers; } /** * {@inheritdoc} */ public function getManagers() { $dms = []; foreach ($this->managers as $name => $id) { $dms[$name] = $this->getService($id); } return $dms; } /** * {@inheritdoc} */ public function getRepository($persistentObject, $persistentManagerName = null) { return $this ->selectManager($persistentObject, $persistentManagerName) ->getRepository($persistentObject); } /** * {@inheritdoc} */ public function resetManager($name = null) { if ($name === null) { $name = $this->defaultManager; } if (! isset($this->managers[$name])) { throw new InvalidArgumentException(sprintf('Doctrine %s Manager named "%s" does not exist.', $this->name, $name)); } // force the creation of a new document manager // if the current one is closed $this->resetService($this->managers[$name]); return $this->getManager($name); } private function selectManager(string $persistentObjectName, ?string $persistentManagerName = null): ObjectManager { if ($persistentManagerName !== null) { return $this->getManager($persistentManagerName); } return $this->getManagerForClass($persistentObjectName) ?? $this->getManager(); } /** * @psalm-return class-string */ private function getRealClassName(string $classNameOrAlias): string { // Check for namespace alias if (strpos($classNameOrAlias, ':') !== false) { [$namespaceAlias, $simpleClassName] = explode(':', $classNameOrAlias, 2); /** @psalm-var class-string */ return $this->getAliasNamespace($namespaceAlias) . '\\' . $simpleClassName; } /** @psalm-var class-string */ return $classNameOrAlias; } } lib/Doctrine/Persistence/ConnectionRegistry.php000064400000001562151742734500015722 0ustar00object = $object; $this->objectManager = $objectManager; } /** * Retrieves the associated entity. * * @deprecated * * @return object */ public function getEntity() { return $this->object; } /** * Retrieves the associated object. * * @return object */ public function getObject() { return $this->object; } /** * Retrieves the associated ObjectManager. * * @return ObjectManager */ public function getObjectManager() { return $this->objectManager; } } lib/Doctrine/Persistence/Event/LoadClassMetadataEventArgs.php000064400000001727151742734500020303 0ustar00classMetadata = $classMetadata; $this->objectManager = $objectManager; } /** * Retrieves the associated ClassMetadata. * * @return ClassMetadata */ public function getClassMetadata() { return $this->classMetadata; } /** * Retrieves the associated ObjectManager. * * @return ObjectManager */ public function getObjectManager() { return $this->objectManager; } } lib/Doctrine/Persistence/Event/ManagerEventArgs.php000064400000001117151742734500016340 0ustar00objectManager = $objectManager; } /** * Retrieves the associated ObjectManager. * * @return ObjectManager */ public function getObjectManager() { return $this->objectManager; } } lib/Doctrine/Persistence/Event/OnClearEventArgs.php000064400000002361151742734500016313 0ustar00objectManager = $objectManager; $this->entityClass = $entityClass; } /** * Retrieves the associated ObjectManager. * * @return ObjectManager */ public function getObjectManager() { return $this->objectManager; } /** * Returns the name of the entity class that is cleared, or null if all are cleared. * * @return string|null */ public function getEntityClass() { return $this->entityClass; } /** * Returns whether this event clears all entities. * * @return bool */ public function clearsAllEntities() { return $this->entityClass === null; } } lib/Doctrine/Persistence/Event/PreUpdateEventArgs.php000064400000004621151742734500016662 0ustar00entityChangeSet = &$changeSet; } /** * Retrieves the entity changeset. * * @return mixed[][] */ public function getEntityChangeSet() { return $this->entityChangeSet; } /** * Checks if field has a changeset. * * @param string $field * * @return bool */ public function hasChangedField($field) { return isset($this->entityChangeSet[$field]); } /** * Gets the old value of the changeset of the changed field. * * @param string $field * * @return mixed */ public function getOldValue($field) { $this->assertValidField($field); return $this->entityChangeSet[$field][0]; } /** * Gets the new value of the changeset of the changed field. * * @param string $field * * @return mixed */ public function getNewValue($field) { $this->assertValidField($field); return $this->entityChangeSet[$field][1]; } /** * Sets the new value of this field. * * @param string $field * @param mixed $value * * @return void */ public function setNewValue($field, $value) { $this->assertValidField($field); $this->entityChangeSet[$field][1] = $value; } /** * Asserts the field exists in changeset. * * @param string $field * * @return void * * @throws InvalidArgumentException */ private function assertValidField($field) { if (! isset($this->entityChangeSet[$field])) { throw new InvalidArgumentException(sprintf( 'Field "%s" is not a valid field of the entity "%s" in PreUpdateEventArgs.', $field, get_class($this->getObject()) )); } } } lib/Doctrine/Persistence/ManagerRegistry.php000064400000005253151742734500015176 0ustar00 $persistentObject * * @return ObjectRepository * @psalm-return ObjectRepository * * @template T */ public function getRepository($persistentObject, $persistentManagerName = null); /** * Gets the object manager associated with a given class. * * @param string $class A persistent object class name. * * @return ObjectManager|null */ public function getManagerForClass($class); } lib/Doctrine/Persistence/Mapping/AbstractClassMetadataFactory.php000064400000037064151742734500021215 0ustar00 */ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory { /** * Salt used by specific Object Manager implementation. * * @var string */ protected $cacheSalt = '__CLASSMETADATA__'; /** @var Cache|null */ private $cacheDriver; /** @var CacheItemPoolInterface|null */ private $cache; /** * @var ClassMetadata[] * @psalm-var CMTemplate[] */ private $loadedMetadata = []; /** @var bool */ protected $initialized = false; /** @var ReflectionService|null */ private $reflectionService = null; /** @var ProxyClassNameResolver|null */ private $proxyClassNameResolver = null; /** * Sets the cache driver used by the factory to cache ClassMetadata instances. * * @deprecated setCacheDriver was deprecated in doctrine/persistence 2.2 and will be removed in 3.0. Use setCache instead * * @return void */ public function setCacheDriver(?Cache $cacheDriver = null) { @trigger_error(sprintf('%s is deprecated. Use setCache() with a PSR-6 cache instead.', __METHOD__), E_USER_DEPRECATED); $this->cacheDriver = $cacheDriver; if ($cacheDriver === null) { $this->cache = null; return; } if (! $cacheDriver instanceof CacheProvider) { throw new BadMethodCallException('Cannot convert cache to PSR-6 cache'); } $this->cache = CacheAdapter::wrap($cacheDriver); } /** * Gets the cache driver used by the factory to cache ClassMetadata instances. * * @deprecated getCacheDriver was deprecated in doctrine/persistence 2.2 and will be removed in 3.0. * * @return Cache|null */ public function getCacheDriver() { @trigger_error(sprintf('%s is deprecated. Use getCache() instead.', __METHOD__), E_USER_DEPRECATED); return $this->cacheDriver; } public function setCache(CacheItemPoolInterface $cache): void { $this->cache = $cache; $this->cacheDriver = DoctrineProvider::wrap($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(); /** * Gets the fully qualified class-name from the namespace alias. * * @param string $namespaceAlias * @param string $simpleClassName * * @return string * @psalm-return class-string */ abstract protected function getFqcnFromAlias($namespaceAlias, $simpleClassName); /** * 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); /** * {@inheritDoc} * * @throws ReflectionException * @throws MappingException */ public function getMetadataFor($className) { if (isset($this->loadedMetadata[$className])) { return $this->loadedMetadata[$className]; } // Check for namespace alias if (strpos($className, ':') !== false) { [$namespaceAlias, $simpleClassName] = explode(':', $className, 2); $realClassName = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); } else { /** @psalm-var class-string $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]; } $loadingException = null; try { if ($this->cache) { $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) { 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($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. * * {@inheritDoc} * * @return void */ public function setMetadataFor($className, $class) { $this->loadedMetadata[$className] = $class; } /** * Gets an array of parent classes for the given entity class. * * @param string $name * @psalm-param class-string $name * * @return string[] * @psalm-return class-string[] */ protected function getParentClasses($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\Common\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 string[] */ protected function loadMetadata($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 * * @param string $className * * @return ClassMetadata|null * @psalm-return CMTemplate|null */ protected function onNotFoundMetadata($className) { return null; } /** * Actually loads the metadata from the underlying metadata. * * @param ClassMetadata $class * @param ClassMetadata|null $parent * @param bool $rootEntityFound * @param string[] $nonSuperclassParents All parent class names * that are not marked as mapped superclasses. * @psalm-param CMTemplate $class * @psalm-param CMTemplate|null $parent * * @return void */ abstract protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents); /** * Creates a new ClassMetadata instance for the given class name. * * @param string $className * @psalm-param class-string $className * * @return ClassMetadata * @psalm-return CMTemplate * * @template T of object */ abstract protected function newClassMetadataInstance($className); /** * {@inheritDoc} * * @psalm-param class-string|string $class */ public function isTransient($class) { if (! $this->initialized) { $this->initialize(); } // Check for namespace alias if (strpos($class, ':') !== false) { [$namespaceAlias, $simpleClassName] = explode(':', $class, 2); $class = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); } /** @psalm-var class-string $class */ return $this->getDriver()->isTransient($class); } /** * 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); } }; } } lib/Doctrine/Persistence/Mapping/ClassMetadata.php000064400000007361151742734500016176 0ustar00 */ public function getName(); /** * Gets the mapped identifier field name. * * The returned structure is an array of the identifier field names. * * @return mixed[] */ 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. * * @param string $fieldName * * @return bool */ public function isIdentifier($fieldName); /** * Checks if the given field is a mapped property for this class. * * @param string $fieldName * * @return bool */ public function hasField($fieldName); /** * Checks if the given field is a mapped association for this class. * * @param string $fieldName * * @return bool */ public function hasAssociation($fieldName); /** * Checks if the given field is a mapped single valued association for this class. * * @param string $fieldName * * @return bool */ public function isSingleValuedAssociation($fieldName); /** * Checks if the given field is a mapped collection valued association for this class. * * @param string $fieldName * * @return bool */ public function isCollectionValuedAssociation($fieldName); /** * A numerically indexed list of field names of this persistent class. * * This array includes identifier fields if present on this class. * * @return string[] */ public function getFieldNames(); /** * Returns an array of identifier field names numerically indexed. * * @return string[] */ 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 string[] */ 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. * * @param string $fieldName * * @return string */ public function getTypeOfField($fieldName); /** * Returns the target class name of the given association. * * @param string $assocName * * @return string * @psalm-return class-string */ public function getAssociationTargetClass($assocName); /** * Checks if the association is the inverse side of a bidirectional association. * * @param string $assocName * * @return bool */ public function isAssociationInverseSide($assocName); /** * Returns the target field of the owning side of the association. * * @param string $assocName * * @return string */ public function getAssociationMappedByTargetField($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. * * @param object $object * * @return mixed[] */ public function getIdentifierValues($object); } lib/Doctrine/Persistence/Mapping/ClassMetadataFactory.php000064400000003140151742734500017515 0ustar00 */ protected $entityAnnotationClasses = []; /** * Initializes a new AnnotationDriver that uses the given AnnotationReader for reading * docblock annotations. * * @param Reader $reader The AnnotationReader to use, duck-typed. * @param string|string[]|null $paths One or multiple paths where mapping classes can be found. */ public function __construct($reader, $paths = null) { $this->reader = $reader; if (! $paths) { return; } $this->addPaths((array) $paths); } /** * Appends lookup paths to metadata driver. * * @param string[] $paths * * @return void */ public function addPaths(array $paths) { $this->paths = array_unique(array_merge($this->paths, $paths)); } /** * Retrieves the defined metadata lookup paths. * * @return string[] */ 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 string[] */ public function getExcludePaths() { return $this->excludePaths; } /** * Retrieve the current annotation reader * * @return Reader */ public function getReader() { return $this->reader; } /** * 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. * * @param string $fileExtension The file extension to set. * * @return void */ public function setFileExtension($fileExtension) { $this->fileExtension = $fileExtension; } /** * 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. * * A class is non-transient if it is annotated with an annotation * from the {@see AnnotationDriver::entityAnnotationClasses}. * * {@inheritDoc} */ public function isTransient($className) { $classAnnotations = $this->reader->getClassAnnotations(new ReflectionClass($className)); foreach ($classAnnotations as $annot) { if (isset($this->entityAnnotationClasses[get_class($annot)])) { return false; } } return true; } /** * {@inheritDoc} */ public function getAllClassNames() { if ($this->classNames !== null) { return $this->classNames; } if (! $this->paths) { throw MappingException::pathRequired(); } $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)) { $sourceFile = realpath($sourceFile); } foreach ($this->excludePaths as $excludePath) { $exclude = str_replace('\\', '/', realpath($excludePath)); $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) || $this->isTransient($className)) { continue; } $classes[] = $className; } $this->classNames = $classes; return $classes; } } lib/Doctrine/Persistence/Mapping/Driver/DefaultFileLocator.php000064400000010303151742734500020421 0ustar00addPaths((array) $paths); $this->fileExtension = $fileExtension; } /** * Appends lookup paths to metadata driver. * * @param string[] $paths * * @return void */ public function addPaths(array $paths) { $this->paths = array_unique(array_merge($this->paths, $paths)); } /** * Retrieves the defined metadata lookup paths. * * @return string[] */ 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($fileExtension) { $this->fileExtension = $fileExtension; } /** * {@inheritDoc} */ public function findMappingFile($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($globalBasename) { $classes = []; if ($this->paths) { 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! $classes[] = str_replace('.', '\\', $fileName); } } } return $classes; } /** * {@inheritDoc} */ public function fileExists($className) { $fileName = str_replace('\\', '.', $className) . $this->fileExtension; // Check whether file exists foreach ((array) $this->paths as $path) { if (is_file($path . DIRECTORY_SEPARATOR . $fileName)) { return true; } } return false; } } lib/Doctrine/Persistence/Mapping/Driver/FileDriver.php000064400000012576151742734500016762 0ustar00locator = $locator; } else { $this->locator = new DefaultFileLocator((array) $locator, $fileExtension); } } /** * Sets the global basename. * * @param string $file * * @return void */ public function setGlobalBasename($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. * * @param string $className * * @return ClassMetadata The element of schema meta data. * * @throws MappingException */ public function getElement($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($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 (array) $this->locator->getAllClassNames($this->globalBasename); } return array_unique(array_merge( array_keys($this->classCache), (array) $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 ClassMetadata[] * @psalm-return array */ abstract protected function loadMappingFile($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 === null) { 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; } } lib/Doctrine/Persistence/Mapping/Driver/FileLocator.php000064400000002356151742734500017125 0ustar00defaultDriver; } /** * Set the default driver. * * @return void */ public function setDefaultDriver(MappingDriver $driver) { $this->defaultDriver = $driver; } /** * Adds a nested driver. * * @param string $namespace * * @return void */ public function addDriver(MappingDriver $nestedDriver, $namespace) { $this->drivers[$namespace] = $nestedDriver; } /** * Gets the array of nested drivers. * * @return MappingDriver[] $drivers */ public function getDrivers() { return $this->drivers; } /** * {@inheritDoc} */ public function loadMetadataForClass($className, ClassMetadata $metadata) { foreach ($this->drivers as $namespace => $driver) { assert($driver instanceof MappingDriver); 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) { assert($driver instanceof MappingDriver); $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($className) { foreach ($this->drivers as $namespace => $driver) { assert($driver instanceof MappingDriver); if (strpos($className, $namespace) === 0) { return $driver->isTransient($className); } } if ($this->defaultDriver !== null) { return $this->defaultDriver->isTransient($className); } return true; } } lib/Doctrine/Persistence/Mapping/Driver/PHPDriver.php000064400000001611151742734500016516 0ustar00metadata = $metadata; $this->loadMappingFile($this->locator->findMappingFile($className)); } /** * {@inheritDoc} */ protected function loadMappingFile($file) { $metadata = $this->metadata; include $file; return [$metadata->getName() => $metadata]; } } lib/Doctrine/Persistence/Mapping/Driver/StaticPHPDriver.php000064400000006072151742734500017674 0ustar00addPaths((array) $paths); } /** * Adds paths. * * @param string[] $paths * * @return void */ public function addPaths(array $paths) { $this->paths = array_unique(array_merge($this->paths, $paths)); } /** * {@inheritdoc} */ public function loadMetadataForClass($className, ClassMetadata $metadata) { $className::loadMetadata($metadata); } /** * {@inheritDoc} * * @todo Same code exists in AnnotationDriver, 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::pathRequired(); } $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) || $this->isTransient($className)) { continue; } $classes[] = $className; } $this->classNames = $classes; return $classes; } /** * {@inheritdoc} */ public function isTransient($className) { return ! method_exists($className, 'loadMetadata'); } } lib/Doctrine/Persistence/Mapping/Driver/SymfonyFileLocator.php000064400000014715151742734500020514 0ustar00addNamespacePrefixes($prefixes); $this->fileExtension = $fileExtension; if (empty($nsSeparator)) { throw new InvalidArgumentException('Namespace separator should not be empty'); } $this->nsSeparator = (string) $nsSeparator; } /** * Adds Namespace Prefixes. * * @param string[] $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($fileExtension) { $this->fileExtension = $fileExtension; } /** * {@inheritDoc} */ public function fileExists($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($globalBasename = null) { $classes = []; if ($this->paths) { foreach ((array) $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(realpath($file->getPath()), strlen(realpath($path))), $this->nsSeparator, '\\' ); $classes[] = $this->prefixes[$path] . str_replace(DIRECTORY_SEPARATOR, '\\', $nsSuffix) . '\\' . str_replace($this->nsSeparator, '\\', $fileName); } else { $classes[] = str_replace($this->nsSeparator, '\\', $fileName); } } } } return $classes; } /** * {@inheritDoc} */ public function findMappingFile($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; } } throw MappingException::mappingFileNotFound($className, substr($className, (int) strrpos($className, '\\') + 1) . $this->fileExtension); } } lib/Doctrine/Persistence/Mapping/MappingException.php000064400000004304151742734500016734 0ustar00>|class-string $className * * @psalm-return class-string * * @template T of object */ public function resolveClassName(string $className): string; } lib/Doctrine/Persistence/Mapping/ReflectionService.php000064400000003357151742734500017104 0ustar00supportsTypedPropertiesWorkaround = version_compare((string) phpversion(), '7.4.0') >= 0; } /** * {@inheritDoc} */ public function getParentClasses($class) { if (! class_exists($class)) { throw MappingException::nonExistingClass($class); } return class_parents($class); } /** * {@inheritDoc} */ public function getClassShortName($class) { $reflectionClass = new ReflectionClass($class); return $reflectionClass->getShortName(); } /** * {@inheritDoc} */ public function getClassNamespace($class) { $reflectionClass = new ReflectionClass($class); return $reflectionClass->getNamespaceName(); } /** * @param string $class * @psalm-param class-string $class * * @return ReflectionClass */ public function getClass($class) { return new ReflectionClass($class); } /** * {@inheritDoc} */ public function getAccessibleProperty($class, $property) { $reflectionProperty = new ReflectionProperty($class, $property); if ($reflectionProperty->isPublic()) { $reflectionProperty = new RuntimePublicReflectionProperty($class, $property); } elseif ($this->supportsTypedPropertiesWorkaround && ! array_key_exists($property, $this->getClass($class)->getDefaultProperties())) { $reflectionProperty = new TypedNoDefaultReflectionProperty($class, $property); } $reflectionProperty->setAccessible(true); return $reflectionProperty; } /** * {@inheritDoc} */ public function hasPublicMethod($class, $method) { try { $reflectionMethod = new ReflectionMethod($class, $method); } catch (ReflectionException $e) { return false; } return $reflectionMethod->isPublic(); } } lib/Doctrine/Persistence/Mapping/StaticReflectionService.php000064400000002521151742734500020244 0ustar00find($id). * * @param string $className The class name of the object to find. * @param mixed $id The identity of the object to find. * @psalm-param class-string $className * * @return object|null The found object. * @psalm-return T|null * * @template T */ public function find($className, $id); /** * Tells the ObjectManager to make an instance managed and persistent. * * The object will be entered into the database as a result of the flush operation. * * NOTE: The persist operation always considers objects that are not yet known to * this ObjectManager as NEW. Do not pass detached objects to the persist operation. * * @param object $object The instance to make managed and persistent. * * @return void */ public function persist($object); /** * Removes an object instance. * * A removed object will be removed from the database as a result of the flush operation. * * @param object $object The object instance to remove. * * @return void */ public function remove($object); /** * Merges the state of a detached object into the persistence context * of this ObjectManager and returns the managed copy of the object. * The object passed to merge will not become associated/managed with this ObjectManager. * * @deprecated Merge operation is deprecated and will be removed in Persistence 2.0. * Merging should be part of the business domain of an application rather than * a generic operation of ObjectManager. * * @param object $object * * @return object */ public function merge($object); /** * Clears the ObjectManager. All objects that are currently managed * by this ObjectManager become detached. * * @param string|null $objectName if given, only objects of this type will get detached. * * @return void */ public function clear($objectName = null); /** * Detaches an object from the ObjectManager, causing a managed object to * become detached. Unflushed changes made to the object if any * (including removal of the object), will not be synchronized to the database. * Objects which previously referenced the detached object will continue to * reference it. * * @deprecated Detach operation is deprecated and will be removed in Persistence 2.0. Please use * {@see ObjectManager::clear()} instead. * * @param object $object The object to detach. * * @return void */ public function detach($object); /** * Refreshes the persistent state of an object from the database, * overriding any local changes that have not yet been persisted. * * @param object $object The object to refresh. * * @return void */ public function refresh($object); /** * Flushes all changes to objects that have been queued up to now to the database. * This effectively synchronizes the in-memory state of managed objects with the * database. * * @return void */ public function flush(); /** * Gets the repository for a class. * * @param string $className * @psalm-param class-string $className * * @return ObjectRepository * @psalm-return ObjectRepository * * @template T */ public function getRepository($className); /** * Returns the ClassMetadata descriptor for a class. * * The class name must be the fully-qualified class name without a leading backslash * (as it is returned by get_class($obj)). * * @param string $className * @psalm-param class-string $className * * @return ClassMetadata * @psalm-return ClassMetadata * * @template T of object */ public function getClassMetadata($className); /** * Gets the metadata factory used to gather the metadata of classes. * * @return ClassMetadataFactory */ public function getMetadataFactory(); /** * Helper method to initialize a lazy loading proxy or persistent collection. * * This method is a no-op for other objects. * * @param object $obj * * @return void */ public function initializeObject($obj); /** * Checks if the object is part of the current UnitOfWork and therefore managed. * * @param object $object * * @return bool */ public function contains($object); } lib/Doctrine/Persistence/ObjectManagerAware.php000064400000001775151742734500015561 0ustar00wrapped->find($className, $id); } /** * {@inheritdoc} */ public function persist($object) { $this->wrapped->persist($object); } /** * {@inheritdoc} */ public function remove($object) { $this->wrapped->remove($object); } /** * {@inheritdoc} */ public function merge($object) { return $this->wrapped->merge($object); } /** * {@inheritdoc} */ public function clear($objectName = null) { $this->wrapped->clear($objectName); } /** * {@inheritdoc} */ public function detach($object) { $this->wrapped->detach($object); } /** * {@inheritdoc} */ public function refresh($object) { $this->wrapped->refresh($object); } /** * {@inheritdoc} */ public function flush() { $this->wrapped->flush(); } /** * {@inheritdoc} */ public function getRepository($className) { return $this->wrapped->getRepository($className); } /** * {@inheritdoc} */ public function getClassMetadata($className) { return $this->wrapped->getClassMetadata($className); } /** * {@inheritdoc} */ public function getMetadataFactory() { return $this->wrapped->getMetadataFactory(); } /** * {@inheritdoc} */ public function initializeObject($obj) { $this->wrapped->initializeObject($obj); } /** * {@inheritdoc} */ public function contains($object) { return $this->wrapped->contains($object); } } lib/Doctrine/Persistence/ObjectRepository.php000064400000003257151742734500015403 0ustar00 The objects. * @psalm-return T[] */ public function findAll(); /** * Finds objects by a set of criteria. * * Optionally sorting and limiting details can be passed. An implementation may throw * an UnexpectedValueException if certain values of the sorting or limiting details are * not supported. * * @param mixed[] $criteria * @param string[]|null $orderBy * @param int|null $limit * @param int|null $offset * @psalm-param array $orderBy * * @return object[] The objects. * @psalm-return T[] * * @throws UnexpectedValueException */ public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null); /** * Finds a single object by a set of criteria. * * @param mixed[] $criteria The criteria. * * @return object|null The object. * @psalm-return T|null */ public function findOneBy(array $criteria); /** * Returns the class name of the object managed by the repository. * * @return string */ public function getClassName(); } lib/Doctrine/Persistence/PropertyChangedListener.php000064400000001253151742734500016673 0ustar00getName(); if ($object instanceof Proxy && ! $object->__isInitialized()) { $originalInitializer = $object->__getInitializer(); $object->__setInitializer(null); $val = $object->$name ?? null; $object->__setInitializer($originalInitializer); return $val; } return isset($object->$name) ? parent::getValue($object) : null; } /** * {@inheritDoc} * * Avoids triggering lazy loading via `__set` if the provided object * is a {@see \Doctrine\Common\Proxy\Proxy}. * * @link https://bugs.php.net/bug.php?id=63463 */ public function setValue($object, $value = null) { if (! ($object instanceof Proxy && ! $object->__isInitialized())) { parent::setValue($object, $value); return; } $originalInitializer = $object->__getInitializer(); $object->__setInitializer(null); parent::setValue($object, $value); $object->__setInitializer($originalInitializer); } } lib/Doctrine/Persistence/Reflection/TypedNoDefaultReflectionProperty.php000064400000003073151742734500022632 0ustar00isInitialized($object) ? parent::getValue($object) : null; } /** * {@inheritDoc} * * Works around the problem with setting typed no default properties to * NULL which is not supported, instead unset() to uninitialize. * * @link https://github.com/doctrine/orm/issues/7999 */ public function setValue($object, $value = null) { if ($value === null && $this->hasType() && ! $this->getType()->allowsNull()) { $propertyName = $this->getName(); $unsetter = function () use ($propertyName): void { unset($this->$propertyName); }; $unsetter = $unsetter->bindTo($object, $this->getDeclaringClass()->getName()); assert($unsetter instanceof Closure); $unsetter(); return; } parent::setValue($object, $value); } } phpstan-baseline.neon000064400000001550151742734500010673 0ustar00parameters: ignoreErrors: - "#^Call to an undefined method Doctrine\\\\Tests\\\\Persistence\\\\TestObject\\:\\:.+\\(\\)\\.$#" - "#^Instantiated class Doctrine\\\\Common\\\\Cache\\\\ArrayCache not found\\.$#" - message: "#^Parameter \\#3 \\$nsSeparator of class Doctrine\\\\Persistence\\\\Mapping\\\\Driver\\\\SymfonyFileLocator constructor expects string, null given\\.$#" count: 1 path: tests/Doctrine/Tests/Persistence/Mapping/SymfonyFileLocatorTest.php - # On purpose to make a test fail. message: "#^Parameter \\#1 \\$class of method Doctrine\\\\Persistence\\\\Mapping\\\\RuntimeReflectionService\\:\\:getParentClasses\\(\\) expects class-string, string given.$#" count: 1 path: tests/Doctrine/Tests/Persistence/Mapping/RuntimeReflectionServiceTest.php