mirror of
https://github.com/doctrine/persistence.git
synced 2026-03-23 22:42:11 +01:00
Merge origin/2.2.x into 3.0.x (using imerge)
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -7,4 +7,5 @@
|
||||
/phpunit.xml.dist export-ignore
|
||||
/phpcs.xml.dist export-ignore
|
||||
/phpstan.neon export-ignore
|
||||
/psalm.xml export-ignore
|
||||
/composer.lock export-ignore
|
||||
|
||||
13
.github/workflows/coding-standards.yml
vendored
13
.github/workflows/coding-standards.yml
vendored
@@ -5,11 +5,9 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "*.x"
|
||||
- "master"
|
||||
push:
|
||||
branches:
|
||||
- "*.x"
|
||||
- "master"
|
||||
|
||||
env:
|
||||
COMPOSER_ROOT_VERSION: "2.1"
|
||||
@@ -35,15 +33,10 @@ jobs:
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
tools: "cs2pr"
|
||||
|
||||
- name: "Cache dependencies installed with Composer"
|
||||
uses: "actions/cache@v2"
|
||||
with:
|
||||
path: "~/.composer/cache"
|
||||
key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
|
||||
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
|
||||
|
||||
- name: "Install dependencies with Composer"
|
||||
run: "composer install --no-interaction --no-progress --no-suggest"
|
||||
uses: "ramsey/composer-install@v1"
|
||||
with:
|
||||
dependency-versions: "${{ matrix.deps }}"
|
||||
|
||||
# https://github.com/doctrine/.github/issues/3
|
||||
- name: "Run PHP_CodeSniffer"
|
||||
|
||||
30
.github/workflows/continuous-integration.yml
vendored
30
.github/workflows/continuous-integration.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
- "7.4"
|
||||
- "8.0"
|
||||
deps:
|
||||
- "normal"
|
||||
- "highest"
|
||||
include:
|
||||
- deps: "low"
|
||||
php-version: "7.2"
|
||||
@@ -37,31 +37,11 @@ jobs:
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
coverage: "pcov"
|
||||
|
||||
- name: "Cache dependencies installed with composer"
|
||||
uses: "actions/cache@v2"
|
||||
|
||||
- name: "Update dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v1"
|
||||
with:
|
||||
path: "~/.composer/cache"
|
||||
key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
|
||||
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
|
||||
|
||||
# Remove this block when
|
||||
# https://github.com/felixfbecker/php-language-server-protocol/pull/15 is
|
||||
# merged and released
|
||||
- name: "Remove dependency on vimeo/psalm for PHP8"
|
||||
run: "composer remove --dev --no-update vimeo/psalm"
|
||||
if: "${{ matrix.php-version == '8.0' }}"
|
||||
|
||||
- name: "Downgrade Composer"
|
||||
run: "composer self-update --1"
|
||||
if: "${{ matrix.php-version == '7.1' }}"
|
||||
|
||||
- name: "Update dependencies with composer"
|
||||
run: "composer update --no-interaction --no-progress --no-suggest"
|
||||
if: "${{ matrix.deps == 'normal' }}"
|
||||
|
||||
- name: "Install lowest possible dependencies with composer"
|
||||
run: "composer update --no-interaction --no-progress --no-suggest --prefer-dist --prefer-lowest"
|
||||
if: "${{ matrix.deps == 'low' }}"
|
||||
dependency-versions: "${{ matrix.deps }}"
|
||||
|
||||
- name: "Run PHPUnit"
|
||||
run: "vendor/bin/phpunit --coverage-clover=coverage.xml"
|
||||
|
||||
32
.github/workflows/static-analysis.yml
vendored
32
.github/workflows/static-analysis.yml
vendored
@@ -5,11 +5,9 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "*.x"
|
||||
- "master"
|
||||
push:
|
||||
branches:
|
||||
- "*.x"
|
||||
- "master"
|
||||
env:
|
||||
COMPOSER_ROOT_VERSION: "2.1"
|
||||
|
||||
@@ -34,15 +32,10 @@ jobs:
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
tools: "cs2pr"
|
||||
|
||||
- name: "Cache dependencies installed with composer"
|
||||
uses: "actions/cache@v2"
|
||||
- name: "Install dependencies with Composer"
|
||||
uses: "ramsey/composer-install@v1"
|
||||
with:
|
||||
path: "~/.composer/cache"
|
||||
key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}"
|
||||
restore-keys: "php-${{ matrix.php-version }}-composer-locked-"
|
||||
|
||||
- name: "Install dependencies with composer"
|
||||
run: "composer install --no-interaction --no-progress --no-suggest"
|
||||
dependency-versions: "${{ matrix.deps }}"
|
||||
|
||||
- name: "Run a static analysis with phpstan/phpstan"
|
||||
run: "vendor/bin/phpstan analyse --error-format=checkstyle | cs2pr"
|
||||
@@ -57,10 +50,19 @@ jobs:
|
||||
- "7.4"
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: "Checkout code"
|
||||
uses: "actions/checkout@v2"
|
||||
|
||||
- name: Psalm
|
||||
uses: docker://vimeo/psalm-github-actions
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
composer_require_dev: true
|
||||
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)"
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@
|
||||
/phpcs.xml
|
||||
/.phpcs-cache
|
||||
/.phpunit.result.cache
|
||||
/composer.lock
|
||||
|
||||
11
UPGRADE-2.2.md
Normal file
11
UPGRADE-2.2.md
Normal file
@@ -0,0 +1,11 @@
|
||||
UPGRADE 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.
|
||||
@@ -22,19 +22,21 @@
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0",
|
||||
"doctrine/annotations": "^1.7.0",
|
||||
"doctrine/cache": "^1.0",
|
||||
"doctrine/cache": "^1.11 || ^2.0",
|
||||
"doctrine/collections": "^1.0",
|
||||
"doctrine/event-manager": "^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",
|
||||
"phpstan/phpstan": "0.12.84",
|
||||
"phpstan/phpstan-phpunit": "^0.12",
|
||||
"phpstan/phpstan-strict-rules": "^0.12",
|
||||
"doctrine/coding-standard": "^6.0 || ^8.0",
|
||||
"doctrine/coding-standard": "^6.0 || ^9.0",
|
||||
"doctrine/common": "^3.0",
|
||||
"phpunit/phpunit": "^8.0 || ^9.0",
|
||||
"vimeo/psalm": "^3.11"
|
||||
"symfony/cache": "^4.4|^5.0",
|
||||
"vimeo/psalm": "4.7.0"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/common": "<2.10@dev"
|
||||
|
||||
4677
composer.lock
generated
4677
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -31,12 +31,16 @@ abstract class AbstractManagerRegistry implements ManagerRegistry
|
||||
/** @var string */
|
||||
private $defaultManager;
|
||||
|
||||
/** @var string */
|
||||
/**
|
||||
* @var string
|
||||
* @psalm-var class-string
|
||||
*/
|
||||
private $proxyInterfaceName;
|
||||
|
||||
/**
|
||||
* @param array<string, string> $connections
|
||||
* @param array<string, string> $managers
|
||||
* @psalm-param class-string $proxyInterfaceName
|
||||
*/
|
||||
public function __construct(
|
||||
string $name,
|
||||
@@ -166,14 +170,9 @@ abstract class AbstractManagerRegistry implements ManagerRegistry
|
||||
*/
|
||||
public function getManagerForClass(string $class)
|
||||
{
|
||||
// Check for namespace alias
|
||||
if (strpos($class, ':') !== false) {
|
||||
[$namespaceAlias, $simpleClassName] = explode(':', $class, 2);
|
||||
$className = $this->getRealClassName($class);
|
||||
|
||||
$class = $this->getAliasNamespace($namespaceAlias) . '\\' . $simpleClassName;
|
||||
}
|
||||
|
||||
$proxyClass = new ReflectionClass($class);
|
||||
$proxyClass = new ReflectionClass($className);
|
||||
|
||||
if ($proxyClass->implementsInterface($this->proxyInterfaceName)) {
|
||||
$parentClass = $proxyClass->getParentClass();
|
||||
@@ -182,13 +181,13 @@ abstract class AbstractManagerRegistry implements ManagerRegistry
|
||||
return null;
|
||||
}
|
||||
|
||||
$class = $parentClass->getName();
|
||||
$className = $parentClass->getName();
|
||||
}
|
||||
|
||||
foreach ($this->managers as $id) {
|
||||
$manager = $this->getService($id);
|
||||
|
||||
if (! $manager->getMetadataFactory()->isTransient($class)) {
|
||||
if (! $manager->getMetadataFactory()->isTransient($className)) {
|
||||
return $manager;
|
||||
}
|
||||
}
|
||||
@@ -264,4 +263,21 @@ abstract class AbstractManagerRegistry implements ManagerRegistry
|
||||
|
||||
return $this->getManagerForClass($persistentObject) ?? $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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,12 +13,15 @@ use Doctrine\Persistence\ObjectManager;
|
||||
*/
|
||||
class LoadClassMetadataEventArgs extends EventArgs
|
||||
{
|
||||
/** @var ClassMetadata */
|
||||
/** @psalm-var ClassMetadata<object> */
|
||||
private $classMetadata;
|
||||
|
||||
/** @var ObjectManager */
|
||||
private $objectManager;
|
||||
|
||||
/**
|
||||
* @psalm-param ClassMetadata<object> $classMetadata
|
||||
*/
|
||||
public function __construct(ClassMetadata $classMetadata, ObjectManager $objectManager)
|
||||
{
|
||||
$this->classMetadata = $classMetadata;
|
||||
@@ -28,7 +31,7 @@ class LoadClassMetadataEventArgs extends EventArgs
|
||||
/**
|
||||
* Retrieves the associated ClassMetadata.
|
||||
*
|
||||
* @return ClassMetadata
|
||||
* @psalm-return ClassMetadata<object>
|
||||
*/
|
||||
public function getClassMetadata()
|
||||
{
|
||||
|
||||
@@ -58,7 +58,7 @@ interface ManagerRegistry extends ConnectionRegistry
|
||||
*
|
||||
* @param string $alias The alias.
|
||||
*
|
||||
* @phpstan-return class-string The full namespace.
|
||||
* @phpstan-return string The full namespace.
|
||||
*/
|
||||
public function getAliasNamespace(string $alias);
|
||||
|
||||
@@ -74,12 +74,12 @@ interface ManagerRegistry extends ConnectionRegistry
|
||||
*
|
||||
* @param string $persistentObject The name of the persistent object.
|
||||
* @param string $persistentManagerName The object manager name (null for the default one).
|
||||
* @psalm-param class-string<T> $persistentObject
|
||||
*
|
||||
* @return ObjectRepository
|
||||
* @psalm-return ObjectRepository<T>
|
||||
*
|
||||
* @template T of object
|
||||
* @psalm-param class-string<T> $persistentObject
|
||||
* @psalm-return ObjectRepository<T>
|
||||
*/
|
||||
public function getRepository(
|
||||
string $persistentObject,
|
||||
|
||||
@@ -4,17 +4,32 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Persistence\Mapping;
|
||||
|
||||
use BadMethodCallException;
|
||||
use Doctrine\Common\Cache\Cache;
|
||||
use Doctrine\Common\Cache\CacheProvider;
|
||||
use Doctrine\Common\Cache\Psr6\CacheAdapter;
|
||||
use Doctrine\Common\Cache\Psr6\DoctrineProvider;
|
||||
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
|
||||
use Doctrine\Persistence\Proxy;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use ReflectionException;
|
||||
|
||||
use function array_combine;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function array_reverse;
|
||||
use function array_unshift;
|
||||
use function assert;
|
||||
use function explode;
|
||||
use function is_array;
|
||||
use function sprintf;
|
||||
use function str_replace;
|
||||
use function strpos;
|
||||
use function strrpos;
|
||||
use function substr;
|
||||
use function trigger_error;
|
||||
|
||||
use const E_USER_DEPRECATED;
|
||||
|
||||
/**
|
||||
* The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
|
||||
@@ -22,6 +37,9 @@ use function substr;
|
||||
* to a relational database.
|
||||
*
|
||||
* This class was abstracted from the ORM ClassMetadataFactory.
|
||||
*
|
||||
* @template CMTemplate of ClassMetadata
|
||||
* @template-implements ClassMetadataFactory<CMTemplate>
|
||||
*/
|
||||
abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
|
||||
{
|
||||
@@ -30,12 +48,18 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $cacheSalt = '$CLASSMETADATA';
|
||||
protected $cacheSalt = '__CLASSMETADATA__';
|
||||
|
||||
/** @var Cache|null */
|
||||
private $cacheDriver;
|
||||
|
||||
/** @var array<string, ClassMetadata> */
|
||||
/** @var CacheItemPoolInterface|null */
|
||||
private $cache;
|
||||
|
||||
/**
|
||||
* @var array<string, ClassMetadata>
|
||||
* @psalm-var CMTemplate[]
|
||||
*/
|
||||
private $loadedMetadata = [];
|
||||
|
||||
/** @var bool */
|
||||
@@ -44,30 +68,65 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
|
||||
/** @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()
|
||||
{
|
||||
@@ -75,10 +134,7 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the factory to load the metadata of all classes known to the underlying
|
||||
* mapping driver.
|
||||
*
|
||||
* @return array<int, ClassMetadata> The ClassMetadata instances of all mapped classes.
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getAllMetadata()
|
||||
{
|
||||
@@ -95,6 +151,11 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
|
||||
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.
|
||||
@@ -107,7 +168,6 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
|
||||
* Gets the fully qualified class-name from the namespace alias.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @psalm-return class-string
|
||||
*/
|
||||
abstract protected function getFqcnFromAlias(
|
||||
@@ -125,6 +185,8 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
|
||||
/**
|
||||
* Wakes up reflection after ClassMetadata gets unserialized from cache.
|
||||
*
|
||||
* @psalm-param CMTemplate $class
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract protected function wakeupReflection(
|
||||
@@ -135,6 +197,8 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
|
||||
/**
|
||||
* Initializes Reflection after ClassMetadata was constructed.
|
||||
*
|
||||
* @psalm-param CMTemplate $class
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract protected function initializeReflection(
|
||||
@@ -147,16 +211,14 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
|
||||
*
|
||||
* This method should return false for mapped superclasses or embedded classes.
|
||||
*
|
||||
* @psalm-param CMTemplate $class
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract protected function isEntity(ClassMetadata $class);
|
||||
|
||||
/**
|
||||
* Gets the class metadata descriptor for a class.
|
||||
*
|
||||
* @param string $className The name of the class.
|
||||
*
|
||||
* @return ClassMetadata
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @throws ReflectionException
|
||||
* @throws MappingException
|
||||
@@ -173,6 +235,7 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
|
||||
|
||||
$realClassName = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName);
|
||||
} else {
|
||||
/** @psalm-var class-string $className */
|
||||
$realClassName = $this->getRealClass($className);
|
||||
}
|
||||
|
||||
@@ -184,20 +247,31 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
|
||||
$loadingException = null;
|
||||
|
||||
try {
|
||||
if ($this->cacheDriver !== null) {
|
||||
$cached = $this->cacheDriver->fetch($realClassName . $this->cacheSalt);
|
||||
|
||||
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 {
|
||||
foreach ($this->loadMetadata($realClassName) as $loadedClassName) {
|
||||
$this->cacheDriver->save(
|
||||
$loadedClassName . $this->cacheSalt,
|
||||
$this->loadedMetadata[$loadedClassName]
|
||||
);
|
||||
$loadedMetadata = $this->loadMetadata($realClassName);
|
||||
$classNames = array_combine(
|
||||
array_map([$this, 'getCacheKey'], $loadedMetadata),
|
||||
$loadedMetadata
|
||||
);
|
||||
assert(is_array($classNames));
|
||||
|
||||
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);
|
||||
@@ -221,9 +295,7 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the factory has the metadata for a class loaded already.
|
||||
*
|
||||
* @return bool TRUE if the metadata of the class in question is already loaded, FALSE otherwise.
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function hasMetadataFor(string $className)
|
||||
{
|
||||
@@ -235,6 +307,8 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
|
||||
*
|
||||
* NOTE: This is only useful in very special cases, like when generating proxy classes.
|
||||
*
|
||||
* @psalm-param CMTemplate $class
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setMetadataFor(string $className, ClassMetadata $class)
|
||||
@@ -245,9 +319,10 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
|
||||
/**
|
||||
* Gets an array of parent classes for the given entity class.
|
||||
*
|
||||
* @return array<int, string>
|
||||
*
|
||||
* @psalm-param class-string $name
|
||||
*
|
||||
* @return string[]
|
||||
* @psalm-return class-string[]
|
||||
*/
|
||||
protected function getParentClasses(string $name)
|
||||
{
|
||||
@@ -279,10 +354,9 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
|
||||
* 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<int, string>
|
||||
*
|
||||
* @psalm-param class-string $name
|
||||
*/
|
||||
protected function loadMetadata(string $name)
|
||||
{
|
||||
@@ -343,6 +417,7 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
|
||||
* Override this method to implement a fallback strategy for failed metadata loading
|
||||
*
|
||||
* @return ClassMetadata|null
|
||||
* @psalm-return CMTemplate|null
|
||||
*/
|
||||
protected function onNotFoundMetadata(string $className)
|
||||
{
|
||||
@@ -352,7 +427,10 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
|
||||
/**
|
||||
* Actually loads the metadata from the underlying metadata.
|
||||
*
|
||||
* @param array<int, string> $nonSuperclassParents All parent class names that are not marked as mapped superclasses.
|
||||
* @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
|
||||
*/
|
||||
@@ -366,12 +444,19 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
|
||||
/**
|
||||
* Creates a new ClassMetadata instance for the given class name.
|
||||
*
|
||||
* @return ClassMetadata
|
||||
* @psalm-param class-string<T> $className
|
||||
*
|
||||
* @return ClassMetadata<T>
|
||||
* @psalm-return CMTemplate
|
||||
*
|
||||
* @template T of object
|
||||
*/
|
||||
abstract protected function newClassMetadataInstance(string $className);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-param class-string|string $class
|
||||
*/
|
||||
public function isTransient(string $class)
|
||||
{
|
||||
@@ -386,6 +471,7 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
|
||||
$class = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName);
|
||||
}
|
||||
|
||||
/** @psalm-var class-string $class */
|
||||
return $this->getDriver()->isTransient($class);
|
||||
}
|
||||
|
||||
@@ -413,20 +499,53 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory
|
||||
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
|
||||
* @psalm-return class-string
|
||||
* @psalm-param class-string<Proxy<T>>|class-string<T> $class
|
||||
*
|
||||
* @psalm-return class-string<T>
|
||||
*
|
||||
* @template T of object
|
||||
*/
|
||||
private function getRealClass(string $class): string
|
||||
{
|
||||
$pos = strrpos($class, '\\' . Proxy::MARKER . '\\');
|
||||
|
||||
if ($pos === false) {
|
||||
return $class;
|
||||
if ($this->proxyClassNameResolver === null) {
|
||||
$this->createDefaultProxyClassNameResolver();
|
||||
}
|
||||
|
||||
return substr($class, $pos + Proxy::MARKER_LENGTH + 2);
|
||||
assert($this->proxyClassNameResolver !== null);
|
||||
|
||||
return $this->proxyClassNameResolver->resolveClassName($class);
|
||||
}
|
||||
|
||||
private function createDefaultProxyClassNameResolver(): void
|
||||
{
|
||||
$this->proxyClassNameResolver = new class implements ProxyClassNameResolver {
|
||||
/**
|
||||
* @psalm-param class-string<Proxy<T>>|class-string<T> $className
|
||||
*
|
||||
* @psalm-return class-string<T>
|
||||
*
|
||||
* @template T of object
|
||||
*/
|
||||
public function resolveClassName(string $className): string
|
||||
{
|
||||
$pos = strrpos($className, '\\' . Proxy::MARKER . '\\');
|
||||
|
||||
if ($pos === false) {
|
||||
/** @psalm-var class-string<T> */
|
||||
return $className;
|
||||
}
|
||||
|
||||
/** @psalm-var class-string<T> */
|
||||
return substr($className, $pos + Proxy::MARKER_LENGTH + 2);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ use ReflectionClass;
|
||||
|
||||
/**
|
||||
* Contract for a Doctrine persistence layer ClassMetadata class to implement.
|
||||
*
|
||||
* @template-covariant T of object
|
||||
*/
|
||||
interface ClassMetadata
|
||||
{
|
||||
@@ -15,6 +17,7 @@ interface ClassMetadata
|
||||
* Gets the fully-qualified class name of this persistent class.
|
||||
*
|
||||
* @return string
|
||||
* @psalm-return class-string<T>
|
||||
*/
|
||||
public function getName();
|
||||
|
||||
@@ -30,7 +33,7 @@ interface ClassMetadata
|
||||
/**
|
||||
* Gets the ReflectionClass instance for this mapped class.
|
||||
*
|
||||
* @return ReflectionClass<object>
|
||||
* @return ReflectionClass<T>
|
||||
*/
|
||||
public function getReflectionClass();
|
||||
|
||||
@@ -108,6 +111,7 @@ interface ClassMetadata
|
||||
* Returns the target class name of the given association.
|
||||
*
|
||||
* @return string
|
||||
* @psalm-return class-string
|
||||
*/
|
||||
public function getAssociationTargetClass(string $assocName);
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace Doctrine\Persistence\Mapping;
|
||||
|
||||
/**
|
||||
* Contract for a Doctrine persistence layer ClassMetadata class to implement.
|
||||
*
|
||||
* @template T of ClassMetadata
|
||||
*/
|
||||
interface ClassMetadataFactory
|
||||
{
|
||||
@@ -13,7 +15,8 @@ interface ClassMetadataFactory
|
||||
* Forces the factory to load the metadata of all classes known to the underlying
|
||||
* mapping driver.
|
||||
*
|
||||
* @return array<int, ClassMetadata> The ClassMetadata instances of all mapped classes.
|
||||
* @return ClassMetadata[] The ClassMetadata instances of all mapped classes.
|
||||
* @psalm-return T[]
|
||||
*/
|
||||
public function getAllMetadata();
|
||||
|
||||
@@ -23,6 +26,7 @@ interface ClassMetadataFactory
|
||||
* @param string $className The name of the class.
|
||||
*
|
||||
* @return ClassMetadata
|
||||
* @psalm-return T
|
||||
*/
|
||||
public function getMetadataFor(string $className);
|
||||
|
||||
@@ -36,6 +40,8 @@ interface ClassMetadataFactory
|
||||
/**
|
||||
* Sets the metadata descriptor for a specific class.
|
||||
*
|
||||
* @psalm-param T $class
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setMetadataFor(string $className, ClassMetadata $class);
|
||||
@@ -44,6 +50,8 @@ interface ClassMetadataFactory
|
||||
* 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);
|
||||
|
||||
@@ -71,7 +71,7 @@ abstract class AnnotationDriver implements MappingDriver
|
||||
/**
|
||||
* Name of the entity annotations as keys.
|
||||
*
|
||||
* @var array<string, int>
|
||||
* @var array<class-string, bool|int>
|
||||
*/
|
||||
protected $entityAnnotationClasses = [];
|
||||
|
||||
@@ -122,7 +122,7 @@ abstract class AnnotationDriver implements MappingDriver
|
||||
/**
|
||||
* Append exclude lookup paths to metadata driver.
|
||||
*
|
||||
* @param array<int, string> $paths
|
||||
* @param string[] $paths
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@@ -181,6 +181,8 @@ abstract class AnnotationDriver implements MappingDriver
|
||||
*
|
||||
* A class is non-transient if it is annotated with an annotation
|
||||
* from the {@see AnnotationDriver::entityAnnotationClasses}.
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isTransient(string $className)
|
||||
{
|
||||
|
||||
@@ -26,7 +26,7 @@ abstract class FileDriver implements MappingDriver
|
||||
/** @var FileLocator */
|
||||
protected $locator;
|
||||
|
||||
/** @var ClassMetadata[]|null */
|
||||
/** @psalm-var ClassMetadata<object>[]|null */
|
||||
protected $classCache;
|
||||
|
||||
/** @var string */
|
||||
@@ -72,7 +72,7 @@ abstract class FileDriver implements MappingDriver
|
||||
* 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.
|
||||
*
|
||||
* @return ClassMetadata The element of schema meta data.
|
||||
* @psalm-return ClassMetadata<object> The element of schema meta data.
|
||||
*
|
||||
* @throws MappingException
|
||||
*/
|
||||
@@ -129,7 +129,7 @@ abstract class FileDriver implements MappingDriver
|
||||
return $this->locator->getAllClassNames($this->globalBasename);
|
||||
}
|
||||
|
||||
/** @var array<string, ClassMetadata> $classCache */
|
||||
/** @var array<string, ClassMetadata<object>> $classCache */
|
||||
$classCache = $this->classCache;
|
||||
|
||||
/** @var array<int, string> $keys */
|
||||
@@ -148,6 +148,7 @@ abstract class FileDriver implements MappingDriver
|
||||
* @param string $file The mapping file to load.
|
||||
*
|
||||
* @return ClassMetadata[]
|
||||
* @psalm-return array<class-string, ClassMetadata<object>>
|
||||
*/
|
||||
abstract protected function loadMappingFile(string $file);
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ interface MappingDriver
|
||||
/**
|
||||
* Loads the metadata for the specified class into the provided container.
|
||||
*
|
||||
* @param ClassMetadata<object> $metadata
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function loadMetadataForClass(string $className, ClassMetadata $metadata);
|
||||
@@ -29,9 +31,9 @@ interface MappingDriver
|
||||
* 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.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @psalm-param class-string $className
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isTransient(string $className);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use Doctrine\Persistence\Mapping\ClassMetadata;
|
||||
*/
|
||||
class PHPDriver extends FileDriver
|
||||
{
|
||||
/** @var ClassMetadata */
|
||||
/** @var ClassMetadata<object> */
|
||||
protected $metadata;
|
||||
|
||||
/**
|
||||
|
||||
@@ -52,7 +52,7 @@ class MappingException extends Exception
|
||||
return new self(sprintf(
|
||||
'File mapping drivers must have a valid directory path, ' .
|
||||
'however the given path %s seems to be incorrect!',
|
||||
$path
|
||||
(string) $path
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
19
lib/Doctrine/Persistence/Mapping/ProxyClassNameResolver.php
Normal file
19
lib/Doctrine/Persistence/Mapping/ProxyClassNameResolver.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Persistence\Mapping;
|
||||
|
||||
use Doctrine\Persistence\Proxy;
|
||||
|
||||
interface ProxyClassNameResolver
|
||||
{
|
||||
/**
|
||||
* @psalm-param class-string<Proxy<T>>|class-string<T> $className
|
||||
*
|
||||
* @psalm-return class-string<T>
|
||||
*
|
||||
* @template T of object
|
||||
*/
|
||||
public function resolveClassName(string $className): string;
|
||||
}
|
||||
@@ -18,56 +18,57 @@ interface ReflectionService
|
||||
/**
|
||||
* Returns an array of the parent classes (not interfaces) for the given class.
|
||||
*
|
||||
* @psalm-param class-string $class
|
||||
*
|
||||
* @return string[]
|
||||
* @psalm-return class-string[]
|
||||
*
|
||||
* @throws MappingException
|
||||
*
|
||||
* @psalm-param class-string $class
|
||||
* @psalm-return class-string[]
|
||||
*/
|
||||
public function getParentClasses(string $class);
|
||||
|
||||
/**
|
||||
* Returns the shortname of a class.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @psalm-param class-string $class
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getClassShortName(string $class);
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*
|
||||
* @psalm-param class-string $class
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getClassNamespace(string $class);
|
||||
|
||||
/**
|
||||
* Returns a reflection class instance or null.
|
||||
*
|
||||
* @psalm-param class-string<T> $class
|
||||
*
|
||||
* @return ReflectionClass<T>|null
|
||||
*
|
||||
* @template T of object
|
||||
* @psalm-param class-string<T> $class
|
||||
*/
|
||||
public function getClass(string $class);
|
||||
|
||||
/**
|
||||
* Returns an accessible property (setAccessible(true)) or null.
|
||||
*
|
||||
* @return ReflectionProperty|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.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @psalm-param class-string $class
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPublicMethod(string $class, string $method);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ use ReflectionMethod;
|
||||
use ReflectionProperty;
|
||||
|
||||
use function array_key_exists;
|
||||
use function assert;
|
||||
use function class_exists;
|
||||
use function class_parents;
|
||||
use function phpversion;
|
||||
@@ -39,7 +40,11 @@ class RuntimeReflectionService implements ReflectionService
|
||||
throw MappingException::nonExistingClass($class);
|
||||
}
|
||||
|
||||
return class_parents($class);
|
||||
$parents = class_parents($class);
|
||||
|
||||
assert($parents !== false);
|
||||
|
||||
return $parents;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,7 +68,11 @@ class RuntimeReflectionService implements ReflectionService
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @psalm-param class-string<T> $class
|
||||
*
|
||||
* @return ReflectionClass<T>
|
||||
*
|
||||
* @template T of object
|
||||
*/
|
||||
public function getClass(string $class)
|
||||
{
|
||||
|
||||
@@ -4,8 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Persistence\Mapping;
|
||||
|
||||
use function assert;
|
||||
use function is_int;
|
||||
use function strpos;
|
||||
use function strrev;
|
||||
use function strrpos;
|
||||
@@ -29,11 +27,10 @@ class StaticReflectionService implements ReflectionService
|
||||
*/
|
||||
public function getClassShortName(string $className)
|
||||
{
|
||||
if (strpos($className, '\\') !== false) {
|
||||
$pos = strrpos($className, '\\');
|
||||
assert(is_int($pos));
|
||||
$nsSeparatorLastPosition = strrpos($className, '\\');
|
||||
|
||||
$className = substr($className, $pos + 1);
|
||||
if ($nsSeparatorLastPosition !== false) {
|
||||
$className = substr($className, $nsSeparatorLastPosition + 1);
|
||||
}
|
||||
|
||||
return $className;
|
||||
@@ -47,10 +44,7 @@ class StaticReflectionService implements ReflectionService
|
||||
$namespace = '';
|
||||
|
||||
if (strpos($className, '\\') !== false) {
|
||||
$pos = strpos(strrev($className), '\\');
|
||||
assert(is_int($pos));
|
||||
|
||||
$namespace = strrev(substr(strrev($className), $pos + 1));
|
||||
$namespace = strrev(substr(strrev($className), (int) strpos(strrev($className), '\\') + 1));
|
||||
}
|
||||
|
||||
return $namespace;
|
||||
|
||||
@@ -19,12 +19,12 @@ interface ObjectManager
|
||||
*
|
||||
* @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<T> $className
|
||||
*
|
||||
* @return object|null The found object.
|
||||
* @psalm-return T|null
|
||||
*
|
||||
* @template T of object
|
||||
* @psalm-param class-string<T> $className
|
||||
* @psalm-return T|null
|
||||
*/
|
||||
public function find(string $className, $id): ?object;
|
||||
|
||||
@@ -100,9 +100,11 @@ interface ObjectManager
|
||||
/**
|
||||
* Gets the repository for a class.
|
||||
*
|
||||
* @template T of object
|
||||
* @psalm-param class-string<T> $className
|
||||
*
|
||||
* @psalm-return ObjectRepository<T>
|
||||
*
|
||||
* @template T of object
|
||||
*/
|
||||
public function getRepository(string $className): ObjectRepository;
|
||||
|
||||
@@ -111,11 +113,19 @@ interface ObjectManager
|
||||
*
|
||||
* The class name must be the fully-qualified class name without a leading backslash
|
||||
* (as it is returned by get_class($obj)).
|
||||
*
|
||||
* @psalm-param class-string<T> $className
|
||||
*
|
||||
* @psalm-return ClassMetadata<T>
|
||||
*
|
||||
* @template T of object
|
||||
*/
|
||||
public function getClassMetadata(string $className): ClassMetadata;
|
||||
|
||||
/**
|
||||
* Gets the metadata factory used to gather the metadata of classes.
|
||||
*
|
||||
* @psalm-return ClassMetadataFactory<ClassMetadata<object>>
|
||||
*/
|
||||
public function getMetadataFactory(): ClassMetadataFactory;
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ interface ObjectManagerAware
|
||||
{
|
||||
/**
|
||||
* Injects responsible ObjectManager and the ClassMetadata into this persistent object.
|
||||
*
|
||||
* @psalm-param ClassMetadata<object> $classMetadata
|
||||
*/
|
||||
public function injectObjectManager(
|
||||
ObjectManager $objectManager,
|
||||
|
||||
@@ -68,6 +68,9 @@ abstract class ObjectManagerDecorator implements ObjectManager
|
||||
return $this->wrapped->getClassMetadata($className);
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-return ClassMetadataFactory<ClassMetadata<object>>
|
||||
*/
|
||||
public function getMetadataFactory(): ClassMetadataFactory
|
||||
{
|
||||
return $this->wrapped->getMetadataFactory();
|
||||
|
||||
@@ -19,7 +19,6 @@ interface ObjectRepository
|
||||
* @param mixed $id The identifier.
|
||||
*
|
||||
* @return object|null The object.
|
||||
*
|
||||
* @psalm-return T|null
|
||||
*/
|
||||
public function find($id): ?object;
|
||||
@@ -28,7 +27,6 @@ interface ObjectRepository
|
||||
* Finds all objects in the repository.
|
||||
*
|
||||
* @return array<int, object> The objects.
|
||||
*
|
||||
* @psalm-return T[]
|
||||
*/
|
||||
public function findAll(): array;
|
||||
@@ -42,12 +40,12 @@ interface ObjectRepository
|
||||
*
|
||||
* @param array<string, mixed> $criteria
|
||||
* @param array<string, string> $orderBy
|
||||
* @psalm-param array<string, 'asc'|'desc'|'ASC'|'DESC'> $orderBy
|
||||
*
|
||||
* @return array<int, object> The objects.
|
||||
* @psalm-return T[]
|
||||
*
|
||||
* @throws UnexpectedValueException
|
||||
*
|
||||
* @psalm-return T[]
|
||||
*/
|
||||
public function findBy(
|
||||
array $criteria,
|
||||
@@ -62,7 +60,6 @@ interface ObjectRepository
|
||||
* @param array<string, mixed> $criteria The criteria.
|
||||
*
|
||||
* @return object|null The object.
|
||||
*
|
||||
* @psalm-return T|null
|
||||
*/
|
||||
public function findOneBy(array $criteria): ?object;
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace Doctrine\Persistence;
|
||||
|
||||
/**
|
||||
* Interface for proxy classes.
|
||||
*
|
||||
* @template T of object
|
||||
*/
|
||||
interface Proxy
|
||||
{
|
||||
|
||||
@@ -4,8 +4,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Persistence\Reflection;
|
||||
|
||||
use Closure;
|
||||
use ReflectionProperty;
|
||||
|
||||
use function assert;
|
||||
|
||||
/**
|
||||
* PHP Typed No Default Reflection Property - special override for typed properties without a default value.
|
||||
*/
|
||||
@@ -42,6 +45,7 @@ class TypedNoDefaultReflectionProperty extends ReflectionProperty
|
||||
unset($this->$propertyName);
|
||||
};
|
||||
$unsetter = $unsetter->bindTo($object, $this->getDeclaringClass()->getName());
|
||||
|
||||
$unsetter();
|
||||
|
||||
return;
|
||||
|
||||
@@ -48,12 +48,4 @@
|
||||
<rule ref="SlevomatCodingStandard.Classes.SuperfluousExceptionNaming.SuperfluousSuffix">
|
||||
<exclude-pattern>lib/Doctrine/Persistence/Mapping/MappingException.php</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="SlevomatCodingStandard.Classes.UnusedPrivateElements.UnusedProperty">
|
||||
<exclude-pattern>RuntimeReflectionServiceTest</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
<rule ref="SlevomatCodingStandard.Classes.UnusedPrivateElements.WriteOnlyProperty">
|
||||
<exclude-pattern>tests/Doctrine/Tests_PHP74/Persistence/Reflection/TypedNoDefaultReflectionPropertyTest.php</exclude-pattern>
|
||||
</rule>
|
||||
</ruleset>
|
||||
|
||||
27
phpstan.neon
27
phpstan.neon
@@ -20,29 +20,16 @@ parameters:
|
||||
-
|
||||
message: '#Parameter \#1 \$class of method Doctrine\\Persistence\\Mapping\\RuntimeReflectionService\:\:getParentClasses\(\) expects class\-string, string given\.#'
|
||||
path: 'tests/Doctrine/Tests/Persistence/Mapping/RuntimeReflectionServiceTest.php'
|
||||
-
|
||||
message: '#Parameter \#1 \$className of method Doctrine\\Persistence\\Mapping\\Driver\\MappingDriverChain\:\:isTransient\(\) expects class\-string, string given\.#'
|
||||
path: 'tests/Doctrine/Tests/Persistence/Mapping/DriverChainTest.php'
|
||||
-
|
||||
message: '#Parameter \#1 \$className of method Doctrine\\Persistence\\Mapping\\Driver\\FileDriver\:\:isTransient\(\) expects class\-string, string given\.#'
|
||||
path: 'tests/Doctrine/Tests/Persistence/Mapping/FileDriverTest.php'
|
||||
-
|
||||
message: '#Method Doctrine\\Tests\\Persistence\\Mapping\\TestClassMetadataFactory\:\:getFqcnFromAlias\(\) should return class\-string but returns string\.#'
|
||||
path: 'tests/Doctrine/Tests/Persistence/Mapping/TestClassMetadataFactory.php'
|
||||
|
||||
- "#^Instantiated class Doctrine\\\\Common\\\\Cache\\\\ArrayCache not found\\.$#"
|
||||
|
||||
-
|
||||
message: '#Method Doctrine\\Tests\\Persistence\\TestManagerRegistry\:\:getAliasNamespace\(\) should return class\-string but returns string\.#'
|
||||
path: 'tests/Doctrine/Tests/Persistence/ManagerRegistryTest.php'
|
||||
-
|
||||
message: '#Method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory\:\:getRealClass\(\) should return class\-string but returns string\.#'
|
||||
path: 'lib/Doctrine/Persistence/Mapping/AbstractClassMetadataFactory.php'
|
||||
-
|
||||
message: '#Parameter \#1 \$class of method Doctrine\\Persistence\\Mapping\\AbstractClassMetadataFactory\:\:getRealClass\(\) expects class\-string, string given\.#'
|
||||
path: 'lib/Doctrine/Persistence/Mapping/AbstractClassMetadataFactory.php'
|
||||
-
|
||||
message: '#Parameter \#1 \$className of method Doctrine\\Persistence\\Mapping\\Driver\\MappingDriver\:\:isTransient\(\) expects class\-string, string given\.#'
|
||||
path: 'lib/Doctrine/Persistence/Mapping/AbstractClassMetadataFactory.php'
|
||||
-
|
||||
message: '#Parameter \#1 \$argument of class ReflectionClass constructor expects class\-string\<T of object\>\|T of object, string given\.#'
|
||||
message: '#Method Doctrine\\Persistence\\AbstractManagerRegistry\:\:getRealClassName\(\) should return class\-string but returns string\.#'
|
||||
path: 'lib/Doctrine/Persistence/AbstractManagerRegistry.php'
|
||||
|
||||
|
||||
-
|
||||
message: '#Call to static method PHPUnit\\Framework\\Assert\:\:assertSame\(\) with Symfony\\Component\\Cache\\Adapter\\ArrayAdapter and null will always evaluate to false\.#'
|
||||
path: 'tests/Doctrine/Tests/Persistence/Mapping/ClassMetadataFactoryTest.php'
|
||||
|
||||
42
psalm.xml
42
psalm.xml
@@ -1,16 +1,17 @@
|
||||
<?xml version="1.0"?>
|
||||
<psalm
|
||||
totallyTyped="false"
|
||||
errorLevel="4"
|
||||
resolveFromConfigFile="true"
|
||||
errorLevel="3"
|
||||
findUnusedPsalmSuppress="true"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="https://getpsalm.org/schema/config"
|
||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||
>
|
||||
<projectFiles>
|
||||
<directory name="lib/Doctrine/Persistence" />
|
||||
<directory name="tests/Doctrine" />
|
||||
<ignoreFiles>
|
||||
<directory name="vendor" />
|
||||
<file name="tests/Doctrine/Tests/Persistence/Mapping/_files/TestEntity.php" />
|
||||
</ignoreFiles>
|
||||
</projectFiles>
|
||||
<issueHandlers>
|
||||
@@ -20,5 +21,40 @@
|
||||
<file name="lib/Doctrine/Persistence/Reflection/TypedNoDefaultReflectionProperty.php"/>
|
||||
</errorLevel>
|
||||
</RedundantCondition>
|
||||
<InvalidNullableReturnType>
|
||||
<errorLevel type="suppress">
|
||||
<!-- see https://github.com/vimeo/psalm/issues/5193 -->
|
||||
<file name="lib/Doctrine/Persistence/Mapping/AbstractClassMetadataFactory.php"/>
|
||||
</errorLevel>
|
||||
</InvalidNullableReturnType>
|
||||
<NullableReturnStatement>
|
||||
<errorLevel type="suppress">
|
||||
<!-- see https://github.com/vimeo/psalm/issues/5193 -->
|
||||
<file name="lib/Doctrine/Persistence/Mapping/AbstractClassMetadataFactory.php"/>
|
||||
</errorLevel>
|
||||
</NullableReturnStatement>
|
||||
<NullArgument>
|
||||
<errorLevel type="suppress">
|
||||
<file name="tests/Doctrine/Tests/Persistence/Mapping/SymfonyFileLocatorTest.php"/>
|
||||
</errorLevel>
|
||||
</NullArgument>
|
||||
<UndefinedClass>
|
||||
<errorLevel type="suppress">
|
||||
<!-- This test references ArrayCache which has been removed from Doctrine Cache -->
|
||||
<file name="tests/Doctrine/Tests/Persistence/Mapping/ClassMetadataFactoryTest.php"/>
|
||||
</errorLevel>
|
||||
</UndefinedClass>
|
||||
<ArgumentTypeCoercion>
|
||||
<errorLevel type="suppress">
|
||||
<!-- On purpose to use a non existing class for tests -->
|
||||
<file name="tests/Doctrine/Tests/Persistence/Mapping/RuntimeReflectionServiceTest.php"/>
|
||||
</errorLevel>
|
||||
</ArgumentTypeCoercion>
|
||||
<MoreSpecificReturnType>
|
||||
<errorLevel type="suppress">
|
||||
<!-- FileDriver::loadMappingFile() in tests could have a more specific return, but is not needed -->
|
||||
<file name="tests/Doctrine/Tests/Persistence/Mapping/FileDriverTest.php"/>
|
||||
</errorLevel>
|
||||
</MoreSpecificReturnType>
|
||||
</issueHandlers>
|
||||
</psalm>
|
||||
|
||||
@@ -205,6 +205,8 @@ class TestManagerRegistry extends AbstractManagerRegistry
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @psalm-param class-string $proxyInterfaceName
|
||||
*/
|
||||
public function __construct(
|
||||
string $name,
|
||||
|
||||
@@ -88,8 +88,8 @@ class AnnotationDriverTest extends TestCase
|
||||
|
||||
class SimpleAnnotationDriver extends AnnotationDriver
|
||||
{
|
||||
/** @var array<string, int> */
|
||||
protected $entityAnnotationClasses = [Entity::class => 1];
|
||||
/** @var array<class-string, bool|int> */
|
||||
protected $entityAnnotationClasses = [Entity::class => true];
|
||||
|
||||
public function loadMetadataForClass(string $className, ClassMetadata $metadata): void
|
||||
{
|
||||
|
||||
@@ -6,41 +6,63 @@ namespace Doctrine\Tests\Persistence\Mapping;
|
||||
|
||||
use Doctrine\Common\Cache\ArrayCache;
|
||||
use Doctrine\Common\Cache\Cache;
|
||||
use Doctrine\Common\Cache\Psr6\DoctrineProvider;
|
||||
use Doctrine\Persistence\Mapping\AbstractClassMetadataFactory;
|
||||
use Doctrine\Persistence\Mapping\ClassMetadata;
|
||||
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
|
||||
use Doctrine\Persistence\Mapping\MappingException;
|
||||
use Doctrine\Tests\DoctrineTestCase;
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use ReflectionMethod;
|
||||
use stdClass;
|
||||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||
|
||||
use function assert;
|
||||
use function class_exists;
|
||||
|
||||
/**
|
||||
* @covers \Doctrine\Persistence\Mapping\AbstractClassMetadataFactory
|
||||
*/
|
||||
class ClassMetadataFactoryTest extends DoctrineTestCase
|
||||
{
|
||||
/** @var TestClassMetadataFactory */
|
||||
/** @var TestClassMetadataFactory<ClassMetadata<object>> */
|
||||
private $cmf;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$driver = $this->createMock(MappingDriver::class);
|
||||
|
||||
/** @psalm-var ClassMetadata<object> */
|
||||
$metadata = $this->createMock(ClassMetadata::class);
|
||||
$this->cmf = new TestClassMetadataFactory($driver, $metadata);
|
||||
}
|
||||
|
||||
public function testGetCacheDriver(): void
|
||||
public function testSetGetCacheDriver(): void
|
||||
{
|
||||
self::assertNull($this->cmf->getCacheDriver());
|
||||
self::assertNull(self::getCache($this->cmf));
|
||||
|
||||
$cache = new ArrayCache();
|
||||
|
||||
$cache = $this->getArrayCache();
|
||||
$this->cmf->setCacheDriver($cache);
|
||||
|
||||
$cacheDriver = $this->cmf->getCacheDriver();
|
||||
assert($cacheDriver instanceof ArrayCache);
|
||||
self::assertSame($cache, $this->cmf->getCacheDriver());
|
||||
self::assertInstanceOf(CacheItemPoolInterface::class, self::getCache($this->cmf));
|
||||
|
||||
self::assertSame($cache, $cacheDriver);
|
||||
$this->cmf->setCacheDriver(null);
|
||||
self::assertNull($this->cmf->getCacheDriver());
|
||||
self::assertNull(self::getCache($this->cmf));
|
||||
}
|
||||
|
||||
public function testSetGetCache(): void
|
||||
{
|
||||
self::assertNull(self::getCache($this->cmf));
|
||||
self::assertNull($this->cmf->getCacheDriver());
|
||||
|
||||
$cache = new ArrayAdapter();
|
||||
$this->cmf->setCache($cache);
|
||||
self::assertSame($cache, self::getCache($this->cmf));
|
||||
self::assertInstanceOf(DoctrineProvider::class, $this->cmf->getCacheDriver());
|
||||
}
|
||||
|
||||
public function testGetMetadataFor(): void
|
||||
@@ -67,22 +89,26 @@ class ClassMetadataFactoryTest extends DoctrineTestCase
|
||||
public function testGetCachedMetadata(): void
|
||||
{
|
||||
$metadata = $this->createMock(ClassMetadata::class);
|
||||
$cache = new ArrayCache();
|
||||
$cache->save(ChildEntity::class . '$CLASSMETADATA', $metadata);
|
||||
$cache = new ArrayAdapter();
|
||||
$item = $cache->getItem($this->cmf->getCacheKey(ChildEntity::class));
|
||||
$item->set($metadata);
|
||||
$cache->save($item);
|
||||
|
||||
$this->cmf->setCacheDriver($cache);
|
||||
$this->cmf->setCache($cache);
|
||||
|
||||
self::assertSame($metadata, $this->cmf->getMetadataFor(ChildEntity::class));
|
||||
self::assertEquals($metadata, $this->cmf->getMetadataFor(ChildEntity::class));
|
||||
}
|
||||
|
||||
public function testCacheGetMetadataFor(): void
|
||||
{
|
||||
$cache = new ArrayCache();
|
||||
$this->cmf->setCacheDriver($cache);
|
||||
$cache = new ArrayAdapter();
|
||||
$this->cmf->setCache($cache);
|
||||
|
||||
$loadedMetadata = $this->cmf->getMetadataFor(ChildEntity::class);
|
||||
|
||||
self::assertSame($loadedMetadata, $cache->fetch(ChildEntity::class . '$CLASSMETADATA'));
|
||||
$item = $cache->getItem($this->cmf->getCacheKey(ChildEntity::class));
|
||||
self::assertTrue($item->isHit());
|
||||
self::assertEquals($loadedMetadata, $item->get());
|
||||
}
|
||||
|
||||
public function testGetAliasedMetadata(): void
|
||||
@@ -122,9 +148,7 @@ class ClassMetadataFactoryTest extends DoctrineTestCase
|
||||
return $classMetadata;
|
||||
};
|
||||
|
||||
$fooClassMetadata = $this->cmf->getMetadataFor('Foo');
|
||||
|
||||
self::assertSame($classMetadata, $fooClassMetadata);
|
||||
self::assertSame($classMetadata, $this->cmf->getMetadataFor('Foo'));
|
||||
}
|
||||
|
||||
public function testWillFailOnFallbackFailureWithNotLoadedMetadata(): void
|
||||
@@ -144,33 +168,103 @@ class ClassMetadataFactoryTest extends DoctrineTestCase
|
||||
*/
|
||||
public function testWillIgnoreCacheEntriesThatAreNotMetadataInstances(): void
|
||||
{
|
||||
$cacheDriver = $this->createMock(Cache::class);
|
||||
$key = $this->cmf->getCacheKey(RootEntity::class);
|
||||
|
||||
$this->cmf->setCacheDriver($cacheDriver);
|
||||
$metadata = $this->cmf->metadata;
|
||||
|
||||
$cacheDriver->expects(self::once())->method('fetch')->with('Foo$CLASSMETADATA')->willReturn(new stdClass());
|
||||
$item = $this->createMock(CacheItemInterface::class);
|
||||
|
||||
$metadata = $this->createMock(ClassMetadata::class);
|
||||
$item
|
||||
->method('getKey')
|
||||
->willReturn($key);
|
||||
$item
|
||||
->method('get')
|
||||
->willReturn(new stdClass());
|
||||
$item
|
||||
->expects(self::once())
|
||||
->method('set')
|
||||
->with($metadata);
|
||||
|
||||
$fallbackCallback = new class ($metadata) {
|
||||
/** @var ClassMetadata */
|
||||
private $metadata;
|
||||
$cacheDriver = $this->createMock(CacheItemPoolInterface::class);
|
||||
$cacheDriver
|
||||
->method('getItem')
|
||||
->with($key)
|
||||
->willReturn($item);
|
||||
$cacheDriver
|
||||
->expects(self::once())
|
||||
->method('getItems')
|
||||
->with([$key])
|
||||
->willReturn([$item]);
|
||||
$cacheDriver
|
||||
->expects(self::once())
|
||||
->method('saveDeferred')
|
||||
->with($item);
|
||||
$cacheDriver
|
||||
->expects(self::once())
|
||||
->method('commit');
|
||||
|
||||
public function __construct(ClassMetadata $metadata)
|
||||
{
|
||||
$this->metadata = $metadata;
|
||||
}
|
||||
$this->cmf->setCache($cacheDriver);
|
||||
|
||||
public function __invoke(): ClassMetadata
|
||||
{
|
||||
return $this->metadata;
|
||||
}
|
||||
self::assertSame($metadata, $this->cmf->getMetadataFor(RootEntity::class));
|
||||
}
|
||||
|
||||
public function testWillNotCacheFallbackMetadata(): void
|
||||
{
|
||||
$key = $this->cmf->getCacheKey('Foo');
|
||||
|
||||
$metadata = $this->cmf->metadata;
|
||||
|
||||
$item = $this->createMock(CacheItemInterface::class);
|
||||
|
||||
$item
|
||||
->method('get')
|
||||
->willReturn(null);
|
||||
$item
|
||||
->expects(self::never())
|
||||
->method('set');
|
||||
|
||||
$cacheDriver = $this->createMock(CacheItemPoolInterface::class);
|
||||
$cacheDriver
|
||||
->expects(self::once())
|
||||
->method('getItem')
|
||||
->with($key)
|
||||
->willReturn($item);
|
||||
$cacheDriver
|
||||
->expects(self::never())
|
||||
->method('saveDeferred');
|
||||
$cacheDriver
|
||||
->expects(self::never())
|
||||
->method('commit');
|
||||
|
||||
$this->cmf->setCache($cacheDriver);
|
||||
|
||||
$this->cmf->fallbackCallback = static function () use ($metadata): ClassMetadata {
|
||||
return $metadata;
|
||||
};
|
||||
|
||||
$this->cmf->fallbackCallback = $fallbackCallback;
|
||||
|
||||
self::assertSame($metadata, $this->cmf->getMetadataFor('Foo'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-param AbstractClassMetadataFactory<ClassMetadata<object>> $classMetadataFactory
|
||||
*/
|
||||
private static function getCache(AbstractClassMetadataFactory $classMetadataFactory): ?CacheItemPoolInterface
|
||||
{
|
||||
$method = new ReflectionMethod($classMetadataFactory, 'getCache');
|
||||
$method->setAccessible(true);
|
||||
|
||||
return $method->invoke($classMetadataFactory);
|
||||
}
|
||||
|
||||
private function getArrayCache(): Cache
|
||||
{
|
||||
$cache = class_exists(DoctrineProvider::class)
|
||||
? DoctrineProvider::wrap(new ArrayAdapter())
|
||||
: new ArrayCache();
|
||||
assert($cache instanceof Cache);
|
||||
|
||||
return $cache;
|
||||
}
|
||||
}
|
||||
|
||||
class RootEntity
|
||||
|
||||
@@ -9,6 +9,9 @@ use Doctrine\Persistence\Mapping\Driver\MappingDriver;
|
||||
use Doctrine\Persistence\Mapping\Driver\MappingDriverChain;
|
||||
use Doctrine\Persistence\Mapping\MappingException;
|
||||
use Doctrine\Tests\DoctrineTestCase;
|
||||
use Doctrine\Tests\Persistence\Mapping\Fixtures\Manager\Manager;
|
||||
use Doctrine\Tests\Persistence\Mapping\Fixtures\Model;
|
||||
use stdClass;
|
||||
|
||||
class DriverChainTest extends DoctrineTestCase
|
||||
{
|
||||
@@ -26,13 +29,13 @@ class DriverChainTest extends DoctrineTestCase
|
||||
->method('isTransient');
|
||||
|
||||
$driver2 = $this->createMock(MappingDriver::class);
|
||||
$driver2->expects(self::at(0))
|
||||
$driver2->expects(self::once())
|
||||
->method('loadMetadataForClass')
|
||||
->with(self::equalTo($className), self::equalTo($classMetadata));
|
||||
$driver2->expects(self::at(1))
|
||||
$driver2->expects(self::once())
|
||||
->method('isTransient')
|
||||
->with(self::equalTo($className))
|
||||
->will(self::returnValue(true));
|
||||
->willReturn(true);
|
||||
|
||||
$chain->addDriver($driver1, 'Doctrine\Tests\Models\Company');
|
||||
$chain->addDriver($driver2, 'Doctrine\Tests\Persistence\Mapping');
|
||||
@@ -86,7 +89,7 @@ class DriverChainTest extends DoctrineTestCase
|
||||
$chain = new MappingDriverChain();
|
||||
$chain->addDriver($driver1, 'Doctrine\Tests\Models\CMS');
|
||||
|
||||
self::assertTrue($chain->isTransient('stdClass'), 'stdClass isTransient');
|
||||
self::assertTrue($chain->isTransient(stdClass::class), 'stdClass isTransient');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,8 +99,8 @@ class DriverChainTest extends DoctrineTestCase
|
||||
{
|
||||
$companyDriver = $this->createMock(MappingDriver::class);
|
||||
$defaultDriver = $this->createMock(MappingDriver::class);
|
||||
$entityClassName = DriverChainEntity::class;
|
||||
$managerClassName = 'Doctrine\Tests\Models\Company\CompanyManager';
|
||||
$entityClassName = Model::class;
|
||||
$managerClassName = Manager::class;
|
||||
$chain = new MappingDriverChain();
|
||||
|
||||
$companyDriver->expects(self::never())
|
||||
@@ -117,7 +120,7 @@ class DriverChainTest extends DoctrineTestCase
|
||||
self::assertNull($chain->getDefaultDriver());
|
||||
|
||||
$chain->setDefaultDriver($defaultDriver);
|
||||
$chain->addDriver($companyDriver, 'Doctrine\Tests\Models\Company');
|
||||
$chain->addDriver($companyDriver, 'Doctrine\Tests\Persistence\Mapping\Fixtures\Manager');
|
||||
|
||||
$driver = $chain->getDefaultDriver();
|
||||
|
||||
|
||||
@@ -8,7 +8,12 @@ use Doctrine\Persistence\Mapping\ClassMetadata;
|
||||
use Doctrine\Persistence\Mapping\Driver\FileDriver;
|
||||
use Doctrine\Persistence\Mapping\Driver\FileLocator;
|
||||
use Doctrine\Tests\DoctrineTestCase;
|
||||
use Doctrine\Tests\Persistence\Mapping\Fixtures\AnotherGlobalClass;
|
||||
use Doctrine\Tests\Persistence\Mapping\Fixtures\GlobalClass;
|
||||
use Doctrine\Tests\Persistence\Mapping\Fixtures\NotLoadedClass;
|
||||
use Doctrine\Tests\Persistence\Mapping\Fixtures\TestClassMetadata;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use stdClass;
|
||||
|
||||
use function strpos;
|
||||
|
||||
@@ -31,9 +36,9 @@ class FileDriverTest extends DoctrineTestCase
|
||||
|
||||
$driver->setGlobalBasename('global');
|
||||
|
||||
$element = $driver->getElement('stdGlobal');
|
||||
$element = $driver->getElement(GlobalClass::class);
|
||||
|
||||
self::assertSame($driver->stdGlobal, $element);
|
||||
self::assertSame(GlobalClass::class, $element->getName());
|
||||
}
|
||||
|
||||
public function testGetElementFromFile(): void
|
||||
@@ -41,12 +46,12 @@ class FileDriverTest extends DoctrineTestCase
|
||||
$locator = $this->newLocator();
|
||||
$locator->expects(self::once())
|
||||
->method('findMappingFile')
|
||||
->with(self::equalTo('stdClass'))
|
||||
->with(self::equalTo(stdClass::class))
|
||||
->will(self::returnValue(__DIR__ . '/_files/stdClass.yml'));
|
||||
|
||||
$driver = $this->createTestFileDriver($locator);
|
||||
|
||||
self::assertSame($driver->stdClass, $driver->getElement('stdClass'));
|
||||
self::assertSame(stdClass::class, $driver->getElement(stdClass::class)->getName());
|
||||
}
|
||||
|
||||
public function testGetElementUpdatesClassCache(): void
|
||||
@@ -56,29 +61,32 @@ class FileDriverTest extends DoctrineTestCase
|
||||
// findMappingFile should only be called once
|
||||
$locator->expects(self::once())
|
||||
->method('findMappingFile')
|
||||
->with(self::equalTo('stdClass'))
|
||||
->with(self::equalTo(stdClass::class))
|
||||
->will(self::returnValue(__DIR__ . '/_files/stdClass.yml'));
|
||||
|
||||
$driver = $this->createTestFileDriver($locator);
|
||||
|
||||
// not cached
|
||||
self::assertSame($driver->stdClass, $driver->getElement('stdClass'));
|
||||
self::assertSame(stdClass::class, $driver->getElement(stdClass::class)->getName());
|
||||
|
||||
// cached call
|
||||
self::assertSame($driver->stdClass, $driver->getElement('stdClass'));
|
||||
self::assertSame(stdClass::class, $driver->getElement(stdClass::class)->getName());
|
||||
}
|
||||
|
||||
public function testGetAllClassNamesGlobalBasename(): void
|
||||
{
|
||||
$locator = $this->newLocator();
|
||||
$locator->expects(self::any())->method('getAllClassNames')->with('global')->will(self::returnValue(['stdGlobal', 'stdGlobal2']));
|
||||
$locator->expects(self::any())->method('getAllClassNames')->with('global')->will(self::returnValue([
|
||||
GlobalClass::class,
|
||||
AnotherGlobalClass::class,
|
||||
]));
|
||||
|
||||
$driver = $this->createTestFileDriver($locator);
|
||||
$driver->setGlobalBasename('global');
|
||||
|
||||
$classNames = $driver->getAllClassNames();
|
||||
|
||||
self::assertSame(['stdGlobal', 'stdGlobal2'], $classNames);
|
||||
self::assertSame([GlobalClass::class, AnotherGlobalClass::class], $classNames);
|
||||
}
|
||||
|
||||
public function testGetAllClassNamesFromMappingFile(): void
|
||||
@@ -86,14 +94,13 @@ class FileDriverTest extends DoctrineTestCase
|
||||
$locator = $this->newLocator();
|
||||
$locator->expects(self::any())
|
||||
->method('getAllClassNames')
|
||||
->with(self::equalTo(''))
|
||||
->will(self::returnValue(['stdClass']));
|
||||
|
||||
$driver = $this->createTestFileDriver($locator);
|
||||
->with(self::equalTo(null))
|
||||
->will(self::returnValue([stdClass::class]));
|
||||
$driver = new TestFileDriver($locator);
|
||||
|
||||
$classNames = $driver->getAllClassNames();
|
||||
|
||||
self::assertSame(['stdClass'], $classNames);
|
||||
self::assertSame([stdClass::class], $classNames);
|
||||
}
|
||||
|
||||
public function testGetAllClassNamesBothSources(): void
|
||||
@@ -102,14 +109,13 @@ class FileDriverTest extends DoctrineTestCase
|
||||
$locator->expects(self::any())
|
||||
->method('getAllClassNames')
|
||||
->with(self::equalTo('global'))
|
||||
->will(self::returnValue(['stdClass']));
|
||||
|
||||
$driver = $this->createTestFileDriver($locator);
|
||||
->will(self::returnValue([stdClass::class]));
|
||||
$driver = new TestFileDriver($locator);
|
||||
$driver->setGlobalBasename('global');
|
||||
|
||||
$classNames = $driver->getAllClassNames();
|
||||
|
||||
self::assertSame(['stdGlobal', 'stdGlobal2', 'stdClass'], $classNames);
|
||||
self::assertSame([GlobalClass::class, AnotherGlobalClass::class, stdClass::class], $classNames);
|
||||
}
|
||||
|
||||
public function testGetAllClassNamesBothSourcesNoDupes(): void
|
||||
@@ -118,20 +124,18 @@ class FileDriverTest extends DoctrineTestCase
|
||||
$locator->expects(self::once())
|
||||
->method('getAllClassNames')
|
||||
->with(self::equalTo('global'))
|
||||
->willReturn(['stdClass']);
|
||||
|
||||
$driver = $this->createTestFileDriver($locator);
|
||||
$driver->setGlobalBasename('global');
|
||||
|
||||
->willReturn([stdClass::class]);
|
||||
$locator->expects(self::once())
|
||||
->method('findMappingFile')
|
||||
->with('stdClass')
|
||||
->willReturn('');
|
||||
->with(self::equalTo(stdClass::class))
|
||||
->will(self::returnValue(__DIR__ . '/_files/stdClass.yml'));
|
||||
$driver = new TestFileDriver($locator);
|
||||
$driver->setGlobalBasename('global');
|
||||
|
||||
$driver->getElement('stdClass');
|
||||
$driver->getElement(stdClass::class);
|
||||
$classNames = $driver->getAllClassNames();
|
||||
|
||||
self::assertSame(['stdGlobal', 'stdGlobal2', 'stdClass'], $classNames);
|
||||
self::assertSame([GlobalClass::class, AnotherGlobalClass::class, stdClass::class], $classNames);
|
||||
}
|
||||
|
||||
public function testIsNotTransient(): void
|
||||
@@ -139,15 +143,15 @@ class FileDriverTest extends DoctrineTestCase
|
||||
$locator = $this->newLocator();
|
||||
$locator->expects(self::once())
|
||||
->method('fileExists')
|
||||
->with(self::equalTo('stdClass'))
|
||||
->with(self::equalTo(stdClass::class))
|
||||
->will(self::returnValue(true));
|
||||
|
||||
$driver = $this->createTestFileDriver($locator);
|
||||
$driver->setGlobalBasename('global');
|
||||
|
||||
self::assertFalse($driver->isTransient('stdClass'));
|
||||
self::assertFalse($driver->isTransient('stdGlobal'));
|
||||
self::assertFalse($driver->isTransient('stdGlobal2'));
|
||||
self::assertFalse($driver->isTransient(stdClass::class));
|
||||
self::assertFalse($driver->isTransient(GlobalClass::class));
|
||||
self::assertFalse($driver->isTransient(AnotherGlobalClass::class));
|
||||
}
|
||||
|
||||
public function testIsTransient(): void
|
||||
@@ -155,19 +159,19 @@ class FileDriverTest extends DoctrineTestCase
|
||||
$locator = $this->newLocator();
|
||||
$locator->expects(self::once())
|
||||
->method('fileExists')
|
||||
->with(self::equalTo('stdClass2'))
|
||||
->with(self::equalTo(NotLoadedClass::class))
|
||||
->will(self::returnValue(false));
|
||||
|
||||
$driver = $this->createTestFileDriver($locator);
|
||||
|
||||
self::assertTrue($driver->isTransient('stdClass2'));
|
||||
self::assertTrue($driver->isTransient(NotLoadedClass::class));
|
||||
}
|
||||
|
||||
public function testNonLocatorFallback(): void
|
||||
{
|
||||
$driver = $this->createTestFileDriver(__DIR__ . '/_files', '.yml');
|
||||
self::assertTrue($driver->isTransient('stdClass2'));
|
||||
self::assertFalse($driver->isTransient('stdClass'));
|
||||
$driver = new TestFileDriver(__DIR__ . '/_files', '.yml');
|
||||
self::assertTrue($driver->isTransient(NotLoadedClass::class));
|
||||
self::assertFalse($driver->isTransient(stdClass::class));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,13 +203,13 @@ class FileDriverTest extends DoctrineTestCase
|
||||
|
||||
class TestFileDriver extends FileDriver
|
||||
{
|
||||
/** @var ClassMetadata */
|
||||
/** @var ClassMetadata<object> */
|
||||
public $stdGlobal;
|
||||
|
||||
/** @var ClassMetadata */
|
||||
/** @var ClassMetadata<object> */
|
||||
public $stdGlobal2;
|
||||
|
||||
/** @var ClassMetadata */
|
||||
/** @var ClassMetadata<object> */
|
||||
public $stdClass;
|
||||
|
||||
/**
|
||||
@@ -215,14 +219,17 @@ class TestFileDriver extends FileDriver
|
||||
{
|
||||
if (strpos($file, 'global.yml') !== false) {
|
||||
return [
|
||||
'stdGlobal' => $this->stdGlobal,
|
||||
'stdGlobal2' => $this->stdGlobal2,
|
||||
GlobalClass::class => new TestClassMetadata(GlobalClass::class),
|
||||
AnotherGlobalClass::class => new TestClassMetadata(AnotherGlobalClass::class),
|
||||
];
|
||||
}
|
||||
|
||||
return ['stdClass' => $this->stdClass];
|
||||
return [stdClass::class => new TestClassMetadata(stdClass::class)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMetadata<object> $metadata
|
||||
*/
|
||||
public function loadMetadataForClass(string $className, ClassMetadata $metadata): void
|
||||
{
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Persistence\Mapping\Fixtures;
|
||||
|
||||
final class AnotherGlobalClass
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Persistence\Mapping\Fixtures;
|
||||
|
||||
final class GlobalClass
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Persistence\Mapping\Fixtures\Manager;
|
||||
|
||||
final class Manager
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Persistence\Mapping\Fixtures;
|
||||
|
||||
final class Model
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Persistence\Mapping\Fixtures;
|
||||
|
||||
final class NotLoadedClass
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Tests\Persistence\Mapping\Fixtures;
|
||||
|
||||
use Doctrine\Persistence\Mapping\ClassMetadata;
|
||||
use LogicException;
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
* @template T of object
|
||||
* @template-implements ClassMetadata<T>
|
||||
*/
|
||||
final class TestClassMetadata implements ClassMetadata
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @psalm-var class-string<T>
|
||||
*/
|
||||
private $className;
|
||||
|
||||
/**
|
||||
* @psalm-param class-string<T> $className
|
||||
*/
|
||||
public function __construct(string $className)
|
||||
{
|
||||
$this->className = $className;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->className;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getIdentifier(): array
|
||||
{
|
||||
return ['id'];
|
||||
}
|
||||
|
||||
public function getReflectionClass(): ReflectionClass
|
||||
{
|
||||
return new ReflectionClass($this->getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isIdentifier(string $fieldName): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function hasField(string $fieldName): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function hasAssociation(string $fieldName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isSingleValuedAssociation(string $fieldName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isCollectionValuedAssociation(string $fieldName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getFieldNames(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getIdentifierFieldNames(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getAssociationNames(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getTypeOfField(string $fieldName)
|
||||
{
|
||||
throw new LogicException('Not implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getAssociationTargetClass(string $assocName)
|
||||
{
|
||||
throw new LogicException('Not implemented');
|
||||
}
|
||||
|
||||
public function isAssociationInverseSide(string $assocName): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getAssociationMappedByTargetField(string $assocName)
|
||||
{
|
||||
throw new LogicException('Not implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getIdentifierValues(object $object): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,9 @@ class StaticPHPDriverTest extends DoctrineTestCase
|
||||
|
||||
class TestEntity
|
||||
{
|
||||
/**
|
||||
* @psalm-param ClassMetadata<object> $metadata
|
||||
*/
|
||||
public static function loadMetadata(ClassMetadata $metadata): void
|
||||
{
|
||||
$metadata->getFieldNames();
|
||||
|
||||
@@ -9,17 +9,27 @@ use Doctrine\Persistence\Mapping\ClassMetadata;
|
||||
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
|
||||
use Doctrine\Persistence\Mapping\ReflectionService;
|
||||
|
||||
/**
|
||||
* @template CMTemplate of ClassMetadata
|
||||
* @template-extends AbstractClassMetadataFactory<CMTemplate>
|
||||
*/
|
||||
class TestClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
{
|
||||
/** @var MappingDriver */
|
||||
public $driver;
|
||||
|
||||
/** @var ClassMetadata */
|
||||
/**
|
||||
* @var ClassMetadata
|
||||
* @psalm-var CMTemplate
|
||||
*/
|
||||
public $metadata;
|
||||
|
||||
/** @var callable|null */
|
||||
public $fallbackCallback;
|
||||
|
||||
/**
|
||||
* @psalm-param CMTemplate $metadata
|
||||
*/
|
||||
public function __construct(MappingDriver $driver, ClassMetadata $metadata)
|
||||
{
|
||||
$this->driver = $driver;
|
||||
@@ -39,6 +49,7 @@ class TestClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
|
||||
protected function getFqcnFromAlias(string $namespaceAlias, string $simpleClassName): string
|
||||
{
|
||||
/** @psalm-var class-string */
|
||||
return __NAMESPACE__ . '\\' . $simpleClassName;
|
||||
}
|
||||
|
||||
@@ -84,4 +95,9 @@ class TestClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
|
||||
return $class !== $name;
|
||||
}
|
||||
|
||||
public function getCacheKey(string $realClassName): string
|
||||
{
|
||||
return parent::getCacheKey($realClassName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ObjectManagerDecoratorTest extends TestCase
|
||||
{
|
||||
/** @var ObjectManager|MockObject */
|
||||
/** @var MockObject&ObjectManager */
|
||||
private $wrapped;
|
||||
|
||||
/** @var NullObjectManagerDecorator */
|
||||
@@ -182,7 +182,7 @@ class ObjectManagerDecoratorTest extends TestCase
|
||||
class NullObjectManagerDecorator extends ObjectManagerDecorator
|
||||
{
|
||||
/**
|
||||
* @param ObjectManager|MockObject $wrapped
|
||||
* @psalm-param ObjectManager&MockObject $wrapped
|
||||
*/
|
||||
public function __construct(ObjectManager $wrapped)
|
||||
{
|
||||
|
||||
@@ -143,7 +143,6 @@ class RuntimePublicReflectionPropertyTestProxyMock implements Proxy
|
||||
*
|
||||
* @return mixed[] Keys are the property names, and values are the default
|
||||
* values for those properties.
|
||||
*
|
||||
* @phpstan-return array<string, mixed>
|
||||
*/
|
||||
public function __getLazyProperties()
|
||||
|
||||
Reference in New Issue
Block a user