.doctrine-project.json000064400000001504151742737360011007 0ustar00{ "active": true, "name": "Common", "slug": "common", "docsSlug": "doctrine-common", "versions": [ { "name": "3.1", "branchName": "3.1.x", "slug": "3.1", "upcoming": true }, { "name": "3.0", "branchName": "3.0.x", "slug": "3.0", "current": true }, { "name": "2.13", "branchName": "2.13.x", "slug": "2.13", "maintained": false }, { "name": "2.12", "branchName": "2.12.x", "slug": "2.12", "maintained": false }, { "name": "2.11", "branchName": "2.11", "slug": "2.11", "maintained": false } ] } LICENSE000064400000002051151742737360005566 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.md000064400000000727151742737360006050 0ustar00# Doctrine Common [![Build Status](https://github.com/doctrine/common/workflows/Continuous%20Integration/badge.svg)](https://github.com/doctrine/common/actions) The Doctrine Common project is a library that provides extensions to core PHP functionality. ## More resources: * [Website](https://www.doctrine-project.org/) * [Documentation](https://www.doctrine-project.org/projects/doctrine-common/en/latest/) * [Downloads](https://github.com/doctrine/common/releases) UPGRADE_TO_2_1000064400000004121151742737360006636 0ustar00This document details all the possible changes that you should investigate when updating your project from Doctrine Common 2.0.x to 2.1 ## AnnotationReader changes The annotation reader was heavily refactored between 2.0 and 2.1-RC1. In theory the operation of the new reader should be backwards compatible, but it has to be setup differently to work that way: $reader = new \Doctrine\Common\Annotations\AnnotationReader(); $reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\'); // new code necessary starting here $reader->setIgnoreNotImportedAnnotations(true); $reader->setEnableParsePhpImports(false); $reader = new \Doctrine\Common\Annotations\CachedReader( new \Doctrine\Common\Annotations\IndexedReader($reader), new ArrayCache() ); ## Annotation Base class or @Annotation Beginning after 2.1-RC2 you have to either extend ``Doctrine\Common\Annotations\Annotation`` or add @Annotation to your annotations class-level docblock, otherwise the class will simply be ignored. ## Removed methods on AnnotationReader * AnnotationReader::setAutoloadAnnotations() * AnnotationReader::getAutoloadAnnotations() * AnnotationReader::isAutoloadAnnotations() ## AnnotationRegistry Autoloading through the PHP autoloader is removed from the 2.1 AnnotationReader. Instead you have to use the global AnnotationRegistry for loading purposes: \Doctrine\Common\Annotations\AnnotationRegistry::registerFile($fileWithAnnotations); \Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespace($namespace, $dirs = null); \Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespaces($namespaces); \Doctrine\Common\Annotations\AnnotationRegistry::registerLoader($callable); The $callable for registering a loader accepts a class as first and only parameter and must try to silently autoload it. On success true has to be returned. The registerAutoloadNamespace function registers a PSR-0 compatible silent autoloader for all classes with the given namespace in the given directories. If null is passed as directory the include path will be used. UPGRADE_TO_2_2000064400000004573151742737360006652 0ustar00This document details all the possible changes that you should investigate when updating your project from Doctrine Common 2.1 to 2.2: ## Annotation Changes - AnnotationReader::setIgnoreNotImportedAnnotations has been removed, you need to add ignore annotation names which are supposed to be ignored via AnnotationReader::addGlobalIgnoredName - AnnotationReader::setAutoloadAnnotations was deprecated by the AnnotationRegistry in 2.1 and has been removed in 2.2 - AnnotationReader::setEnableParsePhpImports was added to ease transition to the new annotation mechanism in 2.1 and is removed in 2.2 - AnnotationReader::isParsePhpImportsEnabled is removed (see above) - AnnotationReader::setDefaultAnnotationNamespace was deprecated in favor of explicit configuration in 2.1 and will be removed in 2.2 (for isolated projects where you have full-control over _all_ available annotations, we offer a dedicated reader class ``SimpleAnnotationReader``) - AnnotationReader::setAnnotationCreationFunction was deprecated in 2.1 and will be removed in 2.2. We only offer two creation mechanisms which cannot be changed anymore to allow the same reader instance to work with all annotations regardless of which library they are coming from. - AnnotationReader::setAnnotationNamespaceAlias was deprecated in 2.1 and will be removed in 2.2 (see setDefaultAnnotationNamespace) - If you use a class as annotation which has not the @Annotation marker in it's class block, we will now throw an exception instead of silently ignoring it. You can however still achieve the previous behavior using the @IgnoreAnnotation, or AnnotationReader::addGlobalIgnoredName (the exception message will contain detailed instructions when you run into this problem). ## Cache Changes - Renamed old AbstractCache to CacheProvider - Dropped the support to the following functions of all cache providers: - CacheProvider::deleteByWildcard - CacheProvider::deleteByRegEx - CacheProvider::deleteByPrefix - CacheProvider::deleteBySuffix - CacheProvider::deleteAll will not remove ALL entries, it will only mark them as invalid - CacheProvider::flushAll will remove ALL entries, namespaced or not - Added support to MemcachedCache - Added support to WincacheCache ## ClassLoader Changes - ClassLoader::fileExistsInIncludePath() no longer exists. Use the native stream_resolve_include_path() PHP functioncomposer.json000064400000002722151742737360007310 0ustar00{ "name": "doctrine/common", "type": "library", "description": "PHP Doctrine Common project is a library that provides additional functionality that other Doctrine projects depend on such as better reflection support, proxies and much more.", "keywords": [ "php", "common", "doctrine" ], "homepage": "https://www.doctrine-project.org/projects/common.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/persistence": "^2.0" }, "require-dev": { "phpstan/phpstan": "^0.12", "phpstan/phpstan-phpunit": "^0.12", "phpunit/phpunit": "^7.5.20 || ^8.0 || ^9.0", "doctrine/coding-standard": "^6.0 || ^8.0", "squizlabs/php_codesniffer": "^3.0", "symfony/phpunit-bridge": "^4.0.5" }, "autoload": { "psr-4": { "Doctrine\\Common\\": "lib/Doctrine/Common" } }, "autoload-dev": { "psr-4": { "Doctrine\\Tests\\": "tests/Doctrine/Tests" } } } docs/en/index.rst000064400000000214151742737360007753 0ustar00Common Documentation ==================== Welcome to the Doctrine Common Library documentation. .. toctree:: :depth: 2 :glob: * docs/en/reference/class-loading.rst000064400000022451151742737360013331 0ustar00Class Loading ============= Class loading is an essential part of any PHP application that makes heavy use of classes and interfaces. Unfortunately, a lot of people and projects spend a lot of time and effort on custom and specialized class loading strategies. It can quickly become a pain to understand what is going on when using multiple libraries and/or frameworks, each with its own way to do class loading. Class loading should be simple and it is an ideal candidate for convention over configuration. Overview -------- The Doctrine Common ClassLoader implements a simple and efficient approach to class loading that is easy to understand and use. The implementation is based on the widely used and accepted convention of mapping namespace and class names to a directory structure. This approach is used for example by Symfony2, the Zend Framework and of course, Doctrine. For example, the following class: .. code-block:: php register(); $dbalLoader->register(); $ormLoader->register(); Do not be afraid of using multiple class loaders. Due to the efficient class loading design you will not incur much overhead from using many class loaders. Take a look at the implementation of ``ClassLoader#loadClass`` to see how simple and efficient the class loading is. The iteration over the installed class loaders happens in C (with the exception of using ``ClassLoader::classExists``). A ClassLoader can be used in the following other variations, however, these are rarely used/needed: - If only the second argument is not supplied, the class loader will be responsible for the namespace prefix given in the first argument and it will rely on the PHP include_path. - If only the first argument is not supplied, the class loader will be responsible for *all* classes and it will try to look up *all* classes starting at the directory given as the second argument. - If both arguments are not supplied, the class loader will be responsible for *all* classes and it will rely on the PHP include_path. File Extension -------------- By default, a ClassLoader uses the ``.php`` file extension for all class files. You can change this behavior, for example to use a ClassLoader to load classes from a library that uses the ".class.php" convention (but it must nevertheless adhere to the directory structure convention!): .. code-block:: php setFileExtension('.class.php'); $customLoader->register(); Namespace Separator ------------------- By default, a ClassLoader uses the ``\`` namespace separator. You can change this behavior, for example to use a ClassLoader to load legacy Zend Framework classes that still use the underscore "_" separator: .. code-block:: php setNamespaceSeparator('_'); $zend1Loader->register(); Failing Silently and class_exists ---------------------------------- A lot of class/autoloaders these days try to fail silently when a class file is not found. For the most part this is necessary in order to support using ``class_exists('ClassName', true)`` which is supposed to return a boolean value but triggers autoloading. This is a bad thing as it basically forces class loaders to fail silently, which in turn requires costly file_exists or fopen calls for each class being loaded, even though in at least 99% of the cases this is not necessary (compare the number of class_exists(..., true) invocations to the total number of classes being loaded in a request). The Doctrine Common ClassLoader does not fail silently, by design. It therefore does not need any costly checks for file existence. A ClassLoader is always responsible for all classes with a certain namespace prefix and if a class is requested to be loaded and can not be found this is considered to be a fatal error. This also means that using class_exists(..., true) to check for class existence when using a Doctrine Common ClassLoader is not possible but this is not a bad thing. What class\_exists(..., true) actually means is two things: 1) Check whether the class is already defined/exists (i.e. class_exists(..., false)) and if not 2) check whether a class file can be loaded for that class. In the Doctrine Common ClassLoader the two responsibilities of loading a class and checking for its existence are separated, which can be observed by the existence of the two methods ``loadClass`` and ``canLoadClass``. Thereby ``loadClass`` does not invoke ``canLoadClass`` internally, by design. However, you are free to use it yourself to check whether a class can be loaded and the following code snippet is thus equivalent to class\_exists(..., true): .. code-block:: php canLoadClass('Foo')) { // ... } The only problem with this is that it is inconvenient as you need to have a reference to the class loaders around (and there are often multiple class loaders in use). Therefore, a simpler alternative exists for the cases in which you really want to ask all installed class loaders whether they can load the class: ``ClassLoader::classExists($className)``: .. code-block:: php ClassLoader is an autoloader for class files that can be * installed on the SPL autoload stack. It is a class loader that either loads only classes * of a specific namespace or all namespaces and it is suitable for working together * with other autoloaders in the SPL autoload stack. * * If no include path is configured through the constructor or {@link setIncludePath}, a ClassLoader * relies on the PHP include_path. * * @deprecated The ClassLoader is deprecated and will be removed in version 4.0 of doctrine/common. */ class ClassLoader { /** * PHP file extension. * * @var string */ protected $fileExtension = '.php'; /** * Current namespace. * * @var string|null */ protected $namespace; /** * Current include path. * * @var string|null */ protected $includePath; /** * PHP namespace separator. * * @var string */ protected $namespaceSeparator = '\\'; /** * Creates a new ClassLoader that loads classes of the * specified namespace from the specified include path. * * If no include path is given, the ClassLoader relies on the PHP include_path. * If neither a namespace nor an include path is given, the ClassLoader will * be responsible for loading all classes, thereby relying on the PHP include_path. * * @param string|null $ns The namespace of the classes to load. * @param string|null $includePath The base include path to use. */ public function __construct($ns = null, $includePath = null) { $this->namespace = $ns; $this->includePath = $includePath; } /** * Sets the namespace separator used by classes in the namespace of this ClassLoader. * * @param string $sep The separator to use. * * @return void */ public function setNamespaceSeparator($sep) { $this->namespaceSeparator = $sep; } /** * Gets the namespace separator used by classes in the namespace of this ClassLoader. * * @return string */ public function getNamespaceSeparator() { return $this->namespaceSeparator; } /** * Sets the base include path for all class files in the namespace of this ClassLoader. * * @param string|null $includePath * * @return void */ public function setIncludePath($includePath) { $this->includePath = $includePath; } /** * Gets the base include path for all class files in the namespace of this ClassLoader. * * @return string|null */ public function getIncludePath() { return $this->includePath; } /** * Sets the file extension of class files in the namespace of this ClassLoader. * * @param string $fileExtension * * @return void */ public function setFileExtension($fileExtension) { $this->fileExtension = $fileExtension; } /** * Gets the file extension of class files in the namespace of this ClassLoader. * * @return string */ public function getFileExtension() { return $this->fileExtension; } /** * Registers this ClassLoader on the SPL autoload stack. * * @return void */ public function register() { spl_autoload_register([$this, 'loadClass']); } /** * Removes this ClassLoader from the SPL autoload stack. * * @return void */ public function unregister() { spl_autoload_unregister([$this, 'loadClass']); } /** * Loads the given class or interface. * * @param string $className The name of the class to load. * * @return bool TRUE if the class has been successfully loaded, FALSE otherwise. */ public function loadClass($className) { if (self::typeExists($className)) { return true; } if (! $this->canLoadClass($className)) { return false; } require($this->includePath !== null ? $this->includePath . DIRECTORY_SEPARATOR : '') . str_replace($this->namespaceSeparator, DIRECTORY_SEPARATOR, $className) . $this->fileExtension; return self::typeExists($className); } /** * Asks this ClassLoader whether it can potentially load the class (file) with * the given name. * * @param string $className The fully-qualified name of the class. * * @return bool TRUE if this ClassLoader can load the class, FALSE otherwise. */ public function canLoadClass($className) { if ($this->namespace !== null && strpos($className, $this->namespace . $this->namespaceSeparator) !== 0) { return false; } $file = str_replace($this->namespaceSeparator, DIRECTORY_SEPARATOR, $className) . $this->fileExtension; if ($this->includePath !== null) { return is_file($this->includePath . DIRECTORY_SEPARATOR . $file); } return stream_resolve_include_path($file) !== false; } /** * Checks whether a class with a given name exists. A class "exists" if it is either * already defined in the current request or if there is an autoloader on the SPL * autoload stack that is a) responsible for the class in question and b) is able to * load a class file in which the class definition resides. * * If the class is not already defined, each autoloader in the SPL autoload stack * is asked whether it is able to tell if the class exists. If the autoloader is * a ClassLoader, {@link canLoadClass} is used, otherwise the autoload * function of the autoloader is invoked and expected to return a value that * evaluates to TRUE if the class (file) exists. As soon as one autoloader reports * that the class exists, TRUE is returned. * * Note that, depending on what kinds of autoloaders are installed on the SPL * autoload stack, the class (file) might already be loaded as a result of checking * for its existence. This is not the case with a ClassLoader, who separates * these responsibilities. * * @param string $className The fully-qualified name of the class. * * @return bool TRUE if the class exists as per the definition given above, FALSE otherwise. */ public static function classExists($className) { return self::typeExists($className, true); } /** * Gets the ClassLoader from the SPL autoload stack that is responsible * for (and is able to load) the class with the given name. * * @param string $className The name of the class. * * @return ClassLoader|null The ClassLoader for the class or NULL if no such ClassLoader exists. */ public static function getClassLoader($className) { foreach (spl_autoload_functions() as $loader) { if (! is_array($loader)) { continue; } $classLoader = reset($loader); if ($classLoader instanceof ClassLoader && $classLoader->canLoadClass($className)) { return $classLoader; } } return null; } /** * Checks whether a given type exists * * @param string $type * @param bool $autoload * * @return bool */ private static function typeExists($type, $autoload = false) { return class_exists($type, $autoload) || interface_exists($type, $autoload) || trait_exists($type, $autoload); } } lib/Doctrine/Common/CommonException.php000064400000000424151742737360014150 0ustar00proxyGenerator = $proxyGenerator; $this->metadataFactory = $metadataFactory; $this->autoGenerate = (int) $autoGenerate; if (! in_array($this->autoGenerate, self::AUTOGENERATE_MODES, true)) { throw InvalidArgumentException::invalidAutoGenerateMode($autoGenerate); } } /** * Gets a reference proxy instance for the entity of the given type and identified by * the given identifier. * * @param string $className * @param array $identifier * * @return Proxy * * @throws OutOfBoundsException */ public function getProxy($className, array $identifier) { $definition = $this->definitions[$className] ?? $this->getProxyDefinition($className); $fqcn = $definition->proxyClassName; $proxy = new $fqcn($definition->initializer, $definition->cloner); foreach ($definition->identifierFields as $idField) { if (! isset($identifier[$idField])) { throw OutOfBoundsException::missingPrimaryKeyValue($className, $idField); } $definition->reflectionFields[$idField]->setValue($proxy, $identifier[$idField]); } return $proxy; } /** * Generates proxy classes for all given classes. * * @param ClassMetadata[] $classes The classes (ClassMetadata instances) * for which to generate proxies. * @param string $proxyDir The target directory of the proxy classes. If not specified, the * directory configured on the Configuration of the EntityManager used * by this factory is used. * * @return int Number of generated proxies. */ public function generateProxyClasses(array $classes, $proxyDir = null) { $generated = 0; foreach ($classes as $class) { if ($this->skipClass($class)) { continue; } $proxyFileName = $this->proxyGenerator->getProxyFileName($class->getName(), $proxyDir); $this->proxyGenerator->generateProxyClass($class, $proxyFileName); $generated += 1; } return $generated; } /** * Reset initialization/cloning logic for an un-initialized proxy * * @return Proxy * * @throws InvalidArgumentException */ public function resetUninitializedProxy(Proxy $proxy) { if ($proxy->__isInitialized()) { throw InvalidArgumentException::unitializedProxyExpected($proxy); } $className = ClassUtils::getClass($proxy); $definition = $this->definitions[$className] ?? $this->getProxyDefinition($className); $proxy->__setInitializer($definition->initializer); $proxy->__setCloner($definition->cloner); return $proxy; } /** * Get a proxy definition for the given class name. * * @param string $className * * @return ProxyDefinition */ private function getProxyDefinition($className) { $classMetadata = $this->metadataFactory->getMetadataFor($className); $className = $classMetadata->getName(); // aliases and case sensitivity $this->definitions[$className] = $this->createProxyDefinition($className); $proxyClassName = $this->definitions[$className]->proxyClassName; if (! class_exists($proxyClassName, false)) { $fileName = $this->proxyGenerator->getProxyFileName($className); switch ($this->autoGenerate) { case self::AUTOGENERATE_NEVER: require $fileName; break; case self::AUTOGENERATE_FILE_NOT_EXISTS: if (! file_exists($fileName)) { $this->proxyGenerator->generateProxyClass($classMetadata, $fileName); } require $fileName; break; case self::AUTOGENERATE_ALWAYS: $this->proxyGenerator->generateProxyClass($classMetadata, $fileName); require $fileName; break; case self::AUTOGENERATE_EVAL: $this->proxyGenerator->generateProxyClass($classMetadata, false); break; } } return $this->definitions[$className]; } /** * Determine if this class should be skipped during proxy generation. * * @return bool */ abstract protected function skipClass(ClassMetadata $metadata); /** * @param string $className * * @return ProxyDefinition */ abstract protected function createProxyDefinition($className); } interface_exists(ClassMetadata::class); interface_exists(ClassMetadataFactory::class); lib/Doctrine/Common/Proxy/Autoloader.php000064400000005543151742737360014270 0ustar00 Keys are the property names, and values are the default values * for those properties. */ public function __getLazyProperties(); } lib/Doctrine/Common/Proxy/ProxyDefinition.php000064400000002160151742737360015313 0ustar00 */ public $identifierFields; /** @var ReflectionProperty[] */ public $reflectionFields; /** @var callable */ public $initializer; /** @var callable */ public $cloner; /** * @param string $proxyClassName * @param array $identifierFields * @param array $reflectionFields * @param callable $initializer * @param callable $cloner */ public function __construct($proxyClassName, array $identifierFields, array $reflectionFields, $initializer, $cloner) { $this->proxyClassName = $proxyClassName; $this->identifierFields = $identifierFields; $this->reflectionFields = $reflectionFields; $this->initializer = $initializer; $this->cloner = $cloner; } } lib/Doctrine/Common/Proxy/ProxyGenerator.php000064400000103674151742737360015165 0ustar00%s;\s*})i'; /** * The namespace that contains all proxy classes. * * @var string */ private $proxyNamespace; /** * The directory that contains all proxy classes. * * @var string */ private $proxyDirectory; /** * Map of callables used to fill in placeholders set in the template. * * @var string[]|callable[] */ protected $placeholders = [ 'baseProxyInterface' => Proxy::class, 'additionalProperties' => '', ]; /** * Template used as a blueprint to generate proxies. * * @var string */ protected $proxyClassTemplate = '; /** * DO NOT EDIT THIS FILE - IT WAS CREATED BY DOCTRINE\'S PROXY GENERATOR */ class extends \ implements \ { /** * @var \Closure the callback responsible for loading properties in the proxy object. This callback is called with * three parameters, being respectively the proxy object to be initialized, the method that triggered the * initialization process and an array of ordered parameters that were passed to that method. * * @see \Doctrine\Common\Proxy\Proxy::__setInitializer */ public $__initializer__; /** * @var \Closure the callback responsible of loading properties that need to be copied in the cloned object * * @see \Doctrine\Common\Proxy\Proxy::__setCloner */ public $__cloner__; /** * @var boolean flag indicating if this object was already initialized * * @see \Doctrine\Persistence\Proxy::__isInitialized */ public $__isInitialized__ = false; /** * @var array properties to be lazy loaded, indexed by property name */ public static $lazyPropertiesNames = ; /** * @var array default values of properties to be lazy loaded, with keys being the property names * * @see \Doctrine\Common\Proxy\Proxy::__getLazyProperties */ public static $lazyPropertiesDefaults = ; /** * Forces initialization of the proxy */ public function __load() { $this->__initializer__ && $this->__initializer__->__invoke($this, \'__load\', []); } /** * {@inheritDoc} * @internal generated method: use only when explicitly handling proxy specific loading logic */ public function __isInitialized() { return $this->__isInitialized__; } /** * {@inheritDoc} * @internal generated method: use only when explicitly handling proxy specific loading logic */ public function __setInitialized($initialized) { $this->__isInitialized__ = $initialized; } /** * {@inheritDoc} * @internal generated method: use only when explicitly handling proxy specific loading logic */ public function __setInitializer(\Closure $initializer = null) { $this->__initializer__ = $initializer; } /** * {@inheritDoc} * @internal generated method: use only when explicitly handling proxy specific loading logic */ public function __getInitializer() { return $this->__initializer__; } /** * {@inheritDoc} * @internal generated method: use only when explicitly handling proxy specific loading logic */ public function __setCloner(\Closure $cloner = null) { $this->__cloner__ = $cloner; } /** * {@inheritDoc} * @internal generated method: use only when explicitly handling proxy specific cloning logic */ public function __getCloner() { return $this->__cloner__; } /** * {@inheritDoc} * @internal generated method: use only when explicitly handling proxy specific loading logic * @deprecated no longer in use - generated code now relies on internal components rather than generated public API * @static */ public function __getLazyProperties() { return self::$lazyPropertiesDefaults; } } '; /** * Initializes a new instance of the ProxyFactory class that is * connected to the given EntityManager. * * @param string $proxyDirectory The directory to use for the proxy classes. It must exist. * @param string $proxyNamespace The namespace to use for the proxy classes. * * @throws InvalidArgumentException */ public function __construct($proxyDirectory, $proxyNamespace) { if (! $proxyDirectory) { throw InvalidArgumentException::proxyDirectoryRequired(); } if (! $proxyNamespace) { throw InvalidArgumentException::proxyNamespaceRequired(); } $this->proxyDirectory = $proxyDirectory; $this->proxyNamespace = $proxyNamespace; } /** * Sets a placeholder to be replaced in the template. * * @param string $name * @param string|callable $placeholder * * @throws InvalidArgumentException */ public function setPlaceholder($name, $placeholder) { if (! is_string($placeholder) && ! is_callable($placeholder)) { throw InvalidArgumentException::invalidPlaceholder($name); } $this->placeholders[$name] = $placeholder; } /** * Sets the base template used to create proxy classes. * * @param string $proxyClassTemplate */ public function setProxyClassTemplate($proxyClassTemplate) { $this->proxyClassTemplate = (string) $proxyClassTemplate; } /** * Generates a proxy class file. * * @param ClassMetadata $class Metadata for the original class. * @param string|bool $fileName Filename (full path) for the generated class. If none is given, eval() is used. * * @throws InvalidArgumentException * @throws UnexpectedValueException */ public function generateProxyClass(ClassMetadata $class, $fileName = false) { $this->verifyClassCanBeProxied($class); preg_match_all('(<([a-zA-Z]+)>)', $this->proxyClassTemplate, $placeholderMatches); $placeholderMatches = array_combine($placeholderMatches[0], $placeholderMatches[1]); $placeholders = []; foreach ($placeholderMatches as $placeholder => $name) { $placeholders[$placeholder] = $this->placeholders[$name] ?? [$this, 'generate' . $name]; } foreach ($placeholders as & $placeholder) { if (! is_callable($placeholder)) { continue; } $placeholder = call_user_func($placeholder, $class); } $proxyCode = strtr($this->proxyClassTemplate, $placeholders); if (! $fileName) { $proxyClassName = $this->generateNamespace($class) . '\\' . $this->generateProxyShortClassName($class); if (! class_exists($proxyClassName)) { eval(substr($proxyCode, 5)); } return; } $parentDirectory = dirname($fileName); if (! is_dir($parentDirectory) && (@mkdir($parentDirectory, 0775, true) === false)) { throw UnexpectedValueException::proxyDirectoryNotWritable($this->proxyDirectory); } if (! is_writable($parentDirectory)) { throw UnexpectedValueException::proxyDirectoryNotWritable($this->proxyDirectory); } $tmpFileName = $fileName . '.' . uniqid('', true); file_put_contents($tmpFileName, $proxyCode); @chmod($tmpFileName, 0664); rename($tmpFileName, $fileName); } /** * @throws InvalidArgumentException */ private function verifyClassCanBeProxied(ClassMetadata $class) { if ($class->getReflectionClass()->isFinal()) { throw InvalidArgumentException::classMustNotBeFinal($class->getName()); } if ($class->getReflectionClass()->isAbstract()) { throw InvalidArgumentException::classMustNotBeAbstract($class->getName()); } } /** * Generates the proxy short class name to be used in the template. * * @return string */ private function generateProxyShortClassName(ClassMetadata $class) { $proxyClassName = ClassUtils::generateProxyClassName($class->getName(), $this->proxyNamespace); $parts = explode('\\', strrev($proxyClassName), 2); return strrev($parts[0]); } /** * Generates the proxy namespace. * * @return string */ private function generateNamespace(ClassMetadata $class) { $proxyClassName = ClassUtils::generateProxyClassName($class->getName(), $this->proxyNamespace); $parts = explode('\\', strrev($proxyClassName), 2); return strrev($parts[1]); } /** * Generates the original class name. * * @return string */ private function generateClassName(ClassMetadata $class) { return ltrim($class->getName(), '\\'); } /** * Generates the array representation of lazy loaded public properties and their default values. * * @return string */ private function generateLazyPropertiesNames(ClassMetadata $class) { $lazyPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class); $values = []; foreach ($lazyPublicProperties as $name) { $values[$name] = null; } return var_export($values, true); } /** * Generates the array representation of lazy loaded public properties names. * * @return string */ private function generateLazyPropertiesDefaults(ClassMetadata $class) { return var_export($this->getLazyLoadedPublicProperties($class), true); } /** * Generates the constructor code (un-setting public lazy loaded properties, setting identifier field values). * * @return string */ private function generateConstructorImpl(ClassMetadata $class) { $constructorImpl = <<<'EOT' public function __construct(?\Closure $initializer = null, ?\Closure $cloner = null) { EOT; $toUnset = array_map(static function (string $name): string { return '$this->' . $name; }, $this->getLazyLoadedPublicPropertiesNames($class)); return $constructorImpl . ($toUnset === [] ? '' : ' unset(' . implode(', ', $toUnset) . ");\n") . <<<'EOT' $this->__initializer__ = $initializer; $this->__cloner__ = $cloner; } EOT; } /** * Generates the magic getter invoked when lazy loaded public properties are requested. * * @return string */ private function generateMagicGet(ClassMetadata $class) { $lazyPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class); $reflectionClass = $class->getReflectionClass(); $hasParentGet = false; $returnReference = ''; $inheritDoc = ''; $name = '$name'; $parametersString = '$name'; $returnTypeHint = null; if ($reflectionClass->hasMethod('__get')) { $hasParentGet = true; $inheritDoc = '{@inheritDoc}'; $methodReflection = $reflectionClass->getMethod('__get'); if ($methodReflection->returnsReference()) { $returnReference = '& '; } $methodParameters = $methodReflection->getParameters(); $name = '$' . $methodParameters[0]->getName(); $parametersString = $this->buildParametersString($methodReflection->getParameters(), ['name']); $returnTypeHint = $this->getMethodReturnType($methodReflection); } if (empty($lazyPublicProperties) && ! $hasParentGet) { return ''; } $magicGet = <<__initializer__ && $this->__initializer__->__invoke($this, '__get', [$name]); EOT; if ($returnTypeHint === ': void') { $magicGet .= "\n return;"; } else { $magicGet .= "\n return \$this->\$name;"; } $magicGet .= <<<'EOT' } EOT; } if ($hasParentGet) { $magicGet .= <<<'EOT' $this->__initializer__ && $this->__initializer__->__invoke($this, '__get', [$name]); EOT; if ($returnTypeHint === ': void') { $magicGet .= <<<'EOT' parent::__get($name); return; EOT; } else { $magicGet .= <<<'EOT' return parent::__get($name); EOT; } } else { $magicGet .= sprintf(<<getLazyLoadedPublicPropertiesNames($class); $hasParentSet = $class->getReflectionClass()->hasMethod('__set'); $parametersString = '$name, $value'; $returnTypeHint = null; if ($hasParentSet) { $methodReflection = $class->getReflectionClass()->getMethod('__set'); $parametersString = $this->buildParametersString($methodReflection->getParameters(), ['name', 'value']); $returnTypeHint = $this->getMethodReturnType($methodReflection); } if (empty($lazyPublicProperties) && ! $hasParentSet) { return ''; } $inheritDoc = $hasParentSet ? '{@inheritDoc}' : ''; $magicSet = sprintf(<<<'EOT' /** * %s * @param string $name * @param mixed $value */ public function __set(%s)%s { EOT , $inheritDoc, $parametersString, $returnTypeHint); if (! empty($lazyPublicProperties)) { $magicSet .= <<<'EOT' if (\array_key_exists($name, self::$lazyPropertiesNames)) { $this->__initializer__ && $this->__initializer__->__invoke($this, '__set', [$name, $value]); $this->$name = $value; return; } EOT; } if ($hasParentSet) { $magicSet .= <<<'EOT' $this->__initializer__ && $this->__initializer__->__invoke($this, '__set', [$name, $value]); return parent::__set($name, $value); EOT; } else { $magicSet .= ' $this->$name = $value;'; } return $magicSet . "\n }"; } /** * Generates the magic issetter invoked when lazy loaded public properties are checked against isset(). * * @return string */ private function generateMagicIsset(ClassMetadata $class) { $lazyPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class); $hasParentIsset = $class->getReflectionClass()->hasMethod('__isset'); $parametersString = '$name'; $returnTypeHint = null; if ($hasParentIsset) { $methodReflection = $class->getReflectionClass()->getMethod('__isset'); $parametersString = $this->buildParametersString($methodReflection->getParameters(), ['name']); $returnTypeHint = $this->getMethodReturnType($methodReflection); } if (empty($lazyPublicProperties) && ! $hasParentIsset) { return ''; } $inheritDoc = $hasParentIsset ? '{@inheritDoc}' : ''; $magicIsset = <<__initializer__ && $this->__initializer__->__invoke($this, '__isset', [$name]); return isset($this->$name); } EOT; } if ($hasParentIsset) { $magicIsset .= <<<'EOT' $this->__initializer__ && $this->__initializer__->__invoke($this, '__isset', [$name]); return parent::__isset($name); EOT; } else { $magicIsset .= ' return false;'; } return $magicIsset . "\n }"; } /** * Generates implementation for the `__sleep` method of proxies. * * @return string */ private function generateSleepImpl(ClassMetadata $class) { $reflectionClass = $class->getReflectionClass(); $hasParentSleep = $reflectionClass->hasMethod('__sleep'); $inheritDoc = $hasParentSleep ? '{@inheritDoc}' : ''; $returnTypeHint = $hasParentSleep ? $this->getMethodReturnType($reflectionClass->getMethod('__sleep')) : ''; $sleepImpl = <<__isInitialized__) { $properties = array_diff($properties, array_keys(self::$lazyPropertiesNames)); } return $properties; } EOT; } $allProperties = ['__isInitialized__']; foreach ($class->getReflectionClass()->getProperties() as $prop) { assert($prop instanceof ReflectionProperty); if ($prop->isStatic()) { continue; } $allProperties[] = $prop->isPrivate() ? "\0" . $prop->getDeclaringClass()->getName() . "\0" . $prop->getName() : $prop->getName(); } $lazyPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class); $protectedProperties = array_diff($allProperties, $lazyPublicProperties); foreach ($allProperties as &$property) { $property = var_export($property, true); } foreach ($protectedProperties as &$property) { $property = var_export($property, true); } $allProperties = implode(', ', $allProperties); $protectedProperties = implode(', ', $protectedProperties); return $sleepImpl . <<__isInitialized__) { return [$allProperties]; } return [$protectedProperties]; } EOT; } /** * Generates implementation for the `__wakeup` method of proxies. * * @return string */ private function generateWakeupImpl(ClassMetadata $class) { $reflectionClass = $class->getReflectionClass(); $hasParentWakeup = $reflectionClass->hasMethod('__wakeup'); $unsetPublicProperties = []; foreach ($this->getLazyLoadedPublicPropertiesNames($class) as $lazyPublicProperty) { $unsetPublicProperties[] = '$this->' . $lazyPublicProperty; } $shortName = $this->generateProxyShortClassName($class); $inheritDoc = $hasParentWakeup ? '{@inheritDoc}' : ''; $returnTypeHint = $hasParentWakeup ? $this->getMethodReturnType($reflectionClass->getMethod('__wakeup')) : ''; $wakeupImpl = <<__isInitialized__) { \$this->__initializer__ = function ($shortName \$proxy) { \$proxy->__setInitializer(null); \$proxy->__setCloner(null); \$existingProperties = get_object_vars(\$proxy); foreach (\$proxy::\$lazyPropertiesDefaults as \$property => \$defaultValue) { if ( ! array_key_exists(\$property, \$existingProperties)) { \$proxy->\$property = \$defaultValue; } } }; EOT; if (! empty($unsetPublicProperties)) { $wakeupImpl .= "\n unset(" . implode(', ', $unsetPublicProperties) . ');'; } $wakeupImpl .= "\n }"; if ($hasParentWakeup) { $wakeupImpl .= "\n parent::__wakeup();"; } $wakeupImpl .= "\n }"; return $wakeupImpl; } /** * Generates implementation for the `__clone` method of proxies. * * @return string */ private function generateCloneImpl(ClassMetadata $class) { $hasParentClone = $class->getReflectionClass()->hasMethod('__clone'); $inheritDoc = $hasParentClone ? '{@inheritDoc}' : ''; $callParentClone = $hasParentClone ? "\n parent::__clone();\n" : ''; return <<__cloner__ && \$this->__cloner__->__invoke(\$this, '__clone', []); $callParentClone } EOT; } /** * Generates decorated methods by picking those available in the parent class. * * @return string */ private function generateMethods(ClassMetadata $class) { $methods = ''; $methodNames = []; $reflectionMethods = $class->getReflectionClass()->getMethods(ReflectionMethod::IS_PUBLIC); $skippedMethods = [ '__sleep' => true, '__clone' => true, '__wakeup' => true, '__get' => true, '__set' => true, '__isset' => true, ]; foreach ($reflectionMethods as $method) { $name = $method->getName(); if ( $method->isConstructor() || isset($skippedMethods[strtolower($name)]) || isset($methodNames[$name]) || $method->isFinal() || $method->isStatic() || ( ! $method->isPublic()) ) { continue; } $methodNames[$name] = true; $methods .= "\n /**\n" . " * {@inheritDoc}\n" . " */\n" . ' public function '; if ($method->returnsReference()) { $methods .= '&'; } $methods .= $name . '(' . $this->buildParametersString($method->getParameters()) . ')'; $methods .= $this->getMethodReturnType($method); $methods .= "\n" . ' {' . "\n"; if ($this->isShortIdentifierGetter($method, $class)) { $identifier = lcfirst(substr($name, 3)); $fieldType = $class->getTypeOfField($identifier); $cast = in_array($fieldType, ['integer', 'smallint']) ? '(int) ' : ''; $methods .= ' if ($this->__isInitialized__ === false) {' . "\n"; $methods .= ' '; $methods .= $this->shouldProxiedMethodReturn($method) ? 'return ' : ''; $methods .= $cast . ' parent::' . $method->getName() . "();\n"; $methods .= ' }' . "\n\n"; } $invokeParamsString = implode(', ', $this->getParameterNamesForInvoke($method->getParameters())); $callParamsString = implode(', ', $this->getParameterNamesForParentCall($method->getParameters())); $methods .= "\n \$this->__initializer__ " . '&& $this->__initializer__->__invoke($this, ' . var_export($name, true) . ', [' . $invokeParamsString . ']);' . "\n\n " . ($this->shouldProxiedMethodReturn($method) ? 'return ' : '') . 'parent::' . $name . '(' . $callParamsString . ');' . "\n" . ' }' . "\n"; } return $methods; } /** * Generates the Proxy file name. * * @param string $className * @param string $baseDirectory Optional base directory for proxy file name generation. * If not specified, the directory configured on the Configuration of the * EntityManager will be used by this factory. * * @return string */ public function getProxyFileName($className, $baseDirectory = null) { $baseDirectory = $baseDirectory ?: $this->proxyDirectory; return rtrim($baseDirectory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . Proxy::MARKER . str_replace('\\', '', $className) . '.php'; } /** * Checks if the method is a short identifier getter. * * What does this mean? For proxy objects the identifier is already known, * however accessing the getter for this identifier usually triggers the * lazy loading, leading to a query that may not be necessary if only the * ID is interesting for the userland code (for example in views that * generate links to the entity, but do not display anything else). * * @param ReflectionMethod $method * * @return bool */ private function isShortIdentifierGetter($method, ClassMetadata $class) { $identifier = lcfirst(substr($method->getName(), 3)); $startLine = $method->getStartLine(); $endLine = $method->getEndLine(); $cheapCheck = $method->getNumberOfParameters() === 0 && substr($method->getName(), 0, 3) === 'get' && in_array($identifier, $class->getIdentifier(), true) && $class->hasField($identifier) && ($endLine - $startLine <= 4); if ($cheapCheck) { $code = file($method->getFileName()); $code = trim(implode(' ', array_slice($code, $startLine - 1, $endLine - $startLine + 1))); $pattern = sprintf(self::PATTERN_MATCH_ID_METHOD, $method->getName(), $identifier); if (preg_match($pattern, $code)) { return true; } } return false; } /** * Generates the list of public properties to be lazy loaded. * * @return array */ private function getLazyLoadedPublicPropertiesNames(ClassMetadata $class): array { $properties = []; foreach ($class->getReflectionClass()->getProperties(ReflectionProperty::IS_PUBLIC) as $property) { $name = $property->getName(); if ((! $class->hasField($name) && ! $class->hasAssociation($name)) || $class->isIdentifier($name)) { continue; } $properties[] = $name; } return $properties; } /** * Generates the list of default values of public properties. * * @return mixed[] */ private function getLazyLoadedPublicProperties(ClassMetadata $class) { $defaultProperties = $class->getReflectionClass()->getDefaultProperties(); $lazyLoadedPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class); $defaultValues = []; foreach ($class->getReflectionClass()->getProperties(ReflectionProperty::IS_PUBLIC) as $property) { $name = $property->getName(); if (! in_array($name, $lazyLoadedPublicProperties, true)) { continue; } if (array_key_exists($name, $defaultProperties)) { $defaultValues[$name] = $defaultProperties[$name]; } elseif (method_exists($property, 'getType')) { $propertyType = $property->getType(); if ($propertyType !== null && $propertyType->allowsNull()) { $defaultValues[$name] = null; } } } return $defaultValues; } /** * @param ReflectionParameter[] $parameters * @param string[] $renameParameters * * @return string */ private function buildParametersString(array $parameters, array $renameParameters = []) { $parameterDefinitions = []; $i = -1; foreach ($parameters as $param) { assert($param instanceof ReflectionParameter); $i++; $parameterDefinition = ''; $parameterType = $this->getParameterType($param); if ($parameterType !== null) { $parameterDefinition .= $parameterType . ' '; } if ($param->isPassedByReference()) { $parameterDefinition .= '&'; } if ($param->isVariadic()) { $parameterDefinition .= '...'; } $parameterDefinition .= '$' . ($renameParameters ? $renameParameters[$i] : $param->getName()); if ($param->isDefaultValueAvailable()) { $parameterDefinition .= ' = ' . var_export($param->getDefaultValue(), true); } $parameterDefinitions[] = $parameterDefinition; } return implode(', ', $parameterDefinitions); } /** * @return string|null */ private function getParameterType(ReflectionParameter $parameter) { if (! $parameter->hasType()) { return null; } return $this->formatType($parameter->getType(), $parameter->getDeclaringFunction(), $parameter); } /** * @param ReflectionParameter[] $parameters * * @return string[] */ private function getParameterNamesForInvoke(array $parameters) { return array_map( static function (ReflectionParameter $parameter) { return '$' . $parameter->getName(); }, $parameters ); } /** * @param ReflectionParameter[] $parameters * * @return string[] */ private function getParameterNamesForParentCall(array $parameters) { return array_map( static function (ReflectionParameter $parameter) { $name = ''; if ($parameter->isVariadic()) { $name .= '...'; } $name .= '$' . $parameter->getName(); return $name; }, $parameters ); } /** * @return string */ private function getMethodReturnType(ReflectionMethod $method) { if (! $method->hasReturnType()) { return ''; } return ': ' . $this->formatType($method->getReturnType(), $method); } /** * @return bool */ private function shouldProxiedMethodReturn(ReflectionMethod $method) { if (! $method->hasReturnType()) { return true; } return strtolower($this->formatType($method->getReturnType(), $method)) !== 'void'; } /** * @return string */ private function formatType( ReflectionNamedType $type, ReflectionMethod $method, ?ReflectionParameter $parameter = null ) { $name = $type->getName(); $nameLower = strtolower($name); if ($nameLower === 'self') { $name = $method->getDeclaringClass()->getName(); } if ($nameLower === 'parent') { $name = $method->getDeclaringClass()->getParentClass()->getName(); } if (! $type->isBuiltin() && ! class_exists($name) && ! interface_exists($name)) { if ($parameter !== null) { throw UnexpectedValueException::invalidParameterTypeHint( $method->getDeclaringClass()->getName(), $method->getName(), $parameter->getName() ); } throw UnexpectedValueException::invalidReturnTypeHint( $method->getDeclaringClass()->getName(), $method->getName() ); } if (! $type->isBuiltin()) { $name = '\\' . $name; } if ( $type->allowsNull() && ($parameter === null || ! $parameter->isDefaultValueAvailable() || $parameter->getDefaultValue() !== null) ) { $name = '?' . $name; } return $name; } } interface_exists(ClassMetadata::class); lib/Doctrine/Common/Util/ClassUtils.php000064400000004263151742737360014051 0ustar00toArray(); } if (! $maxDepth) { return is_object($var) ? get_class($var) : (is_array($var) ? 'Array(' . count($var) . ')' : $var); } if (is_array($var)) { $return = []; foreach ($var as $k => $v) { $return[$k] = self::export($v, $maxDepth - 1); } return $return; } if (! $isObj) { return $var; } $return = new stdClass(); if ($var instanceof DateTimeInterface) { $return->__CLASS__ = get_class($var); $return->date = $var->format('c'); $return->timezone = $var->getTimezone()->getName(); return $return; } $return->__CLASS__ = ClassUtils::getClass($var); if ($var instanceof Proxy) { $return->__IS_PROXY__ = true; $return->__PROXY_INITIALIZED__ = $var->__isInitialized(); } if ($var instanceof ArrayObject || $var instanceof ArrayIterator) { $return->__STORAGE__ = self::export($var->getArrayCopy(), $maxDepth - 1); } return self::fillReturnWithClassAttributes($var, $return, $maxDepth); } /** * Fill the $return variable with class attributes * Based on obj2array function from {@see https://secure.php.net/manual/en/function.get-object-vars.php#47075} * * @param object $var * @param int $maxDepth * * @return mixed */ private static function fillReturnWithClassAttributes($var, stdClass $return, $maxDepth) { $clone = (array) $var; foreach (array_keys($clone) as $key) { $aux = explode("\0", $key); $name = end($aux); if ($aux[0] === '') { $name .= ':' . ($aux[1] === '*' ? 'protected' : $aux[1] . ':private'); } $return->$name = self::export($clone[$key], $maxDepth - 1); } return $return; } /** * Returns a string representation of an object. * * @param object $obj * * @return string */ public static function toString($obj) { return method_exists($obj, '__toString') ? (string) $obj : get_class($obj) . '@' . spl_object_hash($obj); } } phpstan.neon.dist000064400000005642151742737360010072 0ustar00parameters: phpVersion: 70100 level: 3 paths: - lib - tests excludes_analyse: - lib/vendor/doctrine-build-common - tests/Doctrine/Tests/Common/Proxy/InvalidReturnTypeClass.php - tests/Doctrine/Tests/Common/Proxy/InvalidTypeHintClass.php - tests/Doctrine/Tests/Common/Proxy/SerializedClass.php - tests/Doctrine/Tests/Common/Proxy/VariadicTypeHintClass.php - tests/Doctrine/Tests/Common/Proxy/ProxyLogicTypedPropertiesTest.php - tests/Doctrine/Tests/Common/Proxy/LazyLoadableObjectWithTypedProperties.php - tests/Doctrine/Tests/Common/Proxy/generated ignoreErrors: - '#Access to an undefined property Doctrine\\Common\\Proxy\\Proxy::\$publicField#' - message: '#^Result of method Doctrine\\Tests\\Common\\Proxy\\LazyLoadableObjectWithVoid::(adding|incrementing)AndReturningVoid\(\) \(void\) is used\.$#' path: 'tests/Doctrine/Tests/Common/Proxy/ProxyLogicVoidReturnTypeTest.php' - message: '#^Property Doctrine\\Tests\\Common\\Proxy\\ProxyLogicTest::\$initializerCallbackMock \(callable\(\): mixed&PHPUnit\\Framework\\MockObject\\MockObject\) does not accept PHPUnit\\Framework\\MockObject\\MockObject&stdClass\.$#' path: 'tests/Doctrine/Tests/Common/Proxy/ProxyLogicTest.php' - message: '#^Access to an undefined property Doctrine\\Common\\Proxy\\Proxy&Doctrine\\Tests\\Common\\Proxy\\LazyLoadableObject::\$non_existing_property\.$#' path: 'tests/Doctrine/Tests/Common/Proxy/ProxyLogicTest.php' - message: '#^Instantiated class Doctrine\\Tests\\Common\\ProxyProxy\\__CG__\\Doctrine\\Tests\\Common\\Proxy\\.* not found.$#' path: 'tests/Doctrine/Tests/Common/Proxy/ProxyLogicTest.php' - message: '#^Instantiated class Doctrine\\Tests\\Common\\ProxyProxy\\__CG__\\Doctrine\\Tests\\Common\\Proxy\\.* not found.$#' path: 'tests/Doctrine/Tests/Common/Proxy/ProxyLogicVoidReturnTypeTest.php' - message: '#^Property Doctrine\\Tests\\Common\\Proxy\\ProxyLogicVoidReturnTypeTest::\$initializerCallbackMock \(callable\(\): mixed&PHPUnit\\Framework\\MockObject\\MockObject\) does not accept PHPUnit\\Framework\\MockObject\\MockObject&stdClass\.$#' path: 'tests/Doctrine/Tests/Common/Proxy/ProxyLogicVoidReturnTypeTest.php' - message: '#^Method Doctrine\\Tests\\Common\\Proxy\\MagicIssetClassWithInteger::__isset\(\) should return bool but returns int\.$#' path: 'tests/Doctrine/Tests/Common/Proxy/MagicIssetClassWithInteger.php' - message: '#^Access to an undefined property Doctrine\\Tests\\Common\\Proxy\\MagicGetByRefClass\:\:\$nonExisting\.$#' path: 'tests/Doctrine/Tests/Common/Proxy/ProxyMagicMethodsTest.php' includes: - vendor/phpstan/phpstan-phpunit/extension.neon - vendor/phpstan/phpstan-phpunit/rules.neon