.doctrine-project.json 0000644 00000001722 14176470036 0011002 0 ustar 00 {
"active": true,
"name": "Common",
"slug": "common",
"docsSlug": "doctrine-common",
"versions": [
{
"name": "3.2",
"branchName": "3.2.x",
"slug": "3.2",
"upcoming": true
},
{
"name": "3.1",
"branchName": "3.1.x",
"slug": "3.1",
"current": true
},
{
"name": "3.0",
"branchName": "3.0.x",
"slug": "3.0",
"maintained": false
},
{
"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
}
]
}
LICENSE 0000644 00000002051 14176470036 0005557 0 ustar 00 Copyright (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.md 0000644 00000001120 14176470036 0006025 0 ustar 00 # Doctrine Common
[![Build Status](https://github.com/doctrine/common/workflows/Continuous%20Integration/badge.svg)](https://github.com/doctrine/common/actions)
[![codecov](https://codecov.io/gh/doctrine/common/branch/3.1.x/graph/badge.svg)](https://codecov.io/gh/doctrine/common)
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/current/)
* [Downloads](https://github.com/doctrine/common/releases)
UPGRADE_TO_2_1 0000644 00000004121 14176470036 0006627 0 ustar 00 This 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_2 0000644 00000004573 14176470036 0006643 0 ustar 00 This 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 function composer.json 0000644 00000003240 14176470036 0007275 0 ustar 00 {
"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": "^1.4.1",
"phpstan/phpstan-phpunit": "^1",
"phpunit/phpunit": "^7.5.20 || ^8.5 || ^9.0",
"doctrine/coding-standard": "^9.0",
"squizlabs/php_codesniffer": "^3.0",
"symfony/phpunit-bridge": "^4.0.5",
"vimeo/psalm": "^4.4"
},
"autoload": {
"psr-4": {
"Doctrine\\Common\\": "lib/Doctrine/Common"
}
},
"autoload-dev": {
"psr-4": {
"Doctrine\\Tests\\": "tests/Doctrine/Tests"
}
},
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true,
"composer/package-versions-deprecated": true
}
}
}
docs/en/index.rst 0000644 00000000214 14176470036 0007744 0 ustar 00 Common Documentation
====================
Welcome to the Doctrine Common Library documentation.
.. toctree::
:depth: 2
:glob:
*
docs/en/reference/class-loading.rst 0000644 00000022444 14176470036 0013324 0 ustar 00 Class 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.
* @psalm-param class-string $className
*
* @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.
* @psalm-param class-string $className
*
* @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.
* @psalm-param class-string $className
*
* @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.
* @psalm-param class-string $className
*
* @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.php 0000644 00000000424 14176470036 0014141 0 ustar 00 proxyGenerator = $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
* @psalm-param class-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;
case self::AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED:
if (! file_exists($fileName) || filemtime($fileName) < filemtime($classMetadata->getReflectionClass()->getFileName())) {
$this->proxyGenerator->generateProxyClass($classMetadata, $fileName);
}
require $fileName;
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
* @psalm-param class-string $className
*
* @return ProxyDefinition
*/
abstract protected function createProxyDefinition($className);
}
lib/Doctrine/Common/Proxy/Autoloader.php 0000644 00000005617 14176470036 0014263 0 ustar 00
*/
interface Proxy extends BaseProxy
{
/**
* Marks the proxy as initialized or not.
*
* @param bool $initialized
*
* @return void
*/
public function __setInitialized($initialized);
/**
* Sets the initializer callback to be used when initializing the proxy. That
* initializer should accept 3 parameters: $proxy, $method and $params. Those
* are respectively the proxy object that is being initialized, the method name
* that triggered initialization and the parameters passed to that method.
*
* @return void
*/
public function __setInitializer(?Closure $initializer = null);
/**
* Retrieves the initializer callback used to initialize the proxy.
*
* @see __setInitializer
*
* @return Closure|null
*/
public function __getInitializer();
/**
* Sets the callback to be used when cloning the proxy. That initializer should accept
* a single parameter, which is the cloned proxy instance itself.
*
* @return void
*/
public function __setCloner(?Closure $cloner = null);
/**
* Retrieves the callback to be used when cloning the proxy.
*
* @see __setCloner
*
* @return Closure|null
*/
public function __getCloner();
/**
* Retrieves the list of lazy loaded properties for a given proxy
*
* @return array Keys are the property names, and values are the default values
* for those properties.
*/
public function __getLazyProperties();
}
lib/Doctrine/Common/Proxy/ProxyDefinition.php 0000644 00000002160 14176470036 0015304 0 ustar 00 */
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.php 0000644 00000112012 14176470036 0015140 0 ustar 00 %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]);
}
/**
* Enums must have a use statement when used as public property defaults.
*/
public function generateEnumUseStatements(ClassMetadata $class): string
{
if (PHP_VERSION_ID < 80100) {
return "\n";
}
$defaultProperties = $class->getReflectionClass()->getDefaultProperties();
$lazyLoadedPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class);
$enumClasses = [];
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) && $defaultProperties[$name] instanceof BackedEnum) {
$enumClassNameParts = explode('\\', get_class($defaultProperties[$name]));
$enumClasses[] = $enumClassNameParts[0];
}
}
return implode(
"\n",
array_map(
static function ($className) {
return 'use ' . $className . ';';
},
array_unique($enumClasses)
)
) . "\n";
}
/**
* 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;
} elseif ($returnTypeHint === ': never') {
$magicGet .= <<<'EOT'
parent::__get($name);
EOT;
} else {
$magicGet .= <<<'EOT'
return parent::__get($name);
EOT;
}
} else {
$magicGet .= sprintf(<<getLazyLoadedPublicPropertiesNames($class);
$reflectionClass = $class->getReflectionClass();
$hasParentSet = false;
$inheritDoc = '';
$parametersString = '$name, $value';
$returnTypeHint = null;
if ($reflectionClass->hasMethod('__set')) {
$hasParentSet = true;
$inheritDoc = '{@inheritDoc}';
$methodReflection = $reflectionClass->getMethod('__set');
$parametersString = $this->buildParametersString($methodReflection->getParameters(), ['name', 'value']);
$returnTypeHint = $this->getMethodReturnType($methodReflection);
}
if (empty($lazyPublicProperties) && ! $hasParentSet) {
return '';
}
$magicSet = <<__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]);
EOT;
if ($returnTypeHint === ': void') {
$magicSet .= <<<'EOT'
parent::__set($name, $value);
return;
EOT;
} elseif ($returnTypeHint === ': never') {
$magicSet .= <<<'EOT'
parent::__set($name, $value);
EOT;
} else {
$magicSet .= <<<'EOT'
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.
* @psalm-param class-string $className
*
* @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;
}
$declaringFunction = $parameter->getDeclaringFunction();
assert($declaringFunction instanceof ReflectionMethod);
return $this->formatType($parameter->getType(), $declaringFunction, $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 ! in_array(
strtolower($this->formatType($method->getReturnType(), $method)),
['void', 'never'],
true
);
}
/**
* @return string
*/
private function formatType(
ReflectionType $type,
ReflectionMethod $method,
?ReflectionParameter $parameter = null
) {
if ($type instanceof ReflectionUnionType) {
return implode('|', array_map(
function (ReflectionType $unionedType) use ($method, $parameter) {
return $this->formatType($unionedType, $method, $parameter);
},
$type->getTypes()
));
}
if ($type instanceof ReflectionIntersectionType) {
return implode('&', array_map(
function (ReflectionType $intersectedType) use ($method, $parameter) {
return $this->formatType($intersectedType, $method, $parameter);
},
$type->getTypes()
));
}
assert($type instanceof ReflectionNamedType);
$name = $type->getName();
$nameLower = strtolower($name);
if ($nameLower === 'static') {
$name = 'static';
}
if ($nameLower === 'self') {
$name = $method->getDeclaringClass()->getName();
}
if ($nameLower === 'parent') {
$name = $method->getDeclaringClass()->getParentClass()->getName();
}
if (! $type->isBuiltin() && ! class_exists($name) && ! interface_exists($name) && $name !== 'static') {
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 !== 'static') {
$name = '\\' . $name;
}
if (
$type->allowsNull()
&& ! in_array($name, ['mixed', 'null'], true)
&& ($parameter === null || ! $parameter->isDefaultValueAvailable() || $parameter->getDefaultValue() !== null)
) {
$name = '?' . $name;
}
return $name;
}
}
lib/Doctrine/Common/Util/ClassUtils.php 0000644 00000005310 14176470036 0014034 0 ustar 00 >|class-string $className
*
* @return string
* @psalm-return class-string
*
* @template T of object
*/
public static function getRealClass($className)
{
$pos = strrpos($className, '\\' . Proxy::MARKER . '\\');
if ($pos === false) {
/** @psalm-var class-string */
return $className;
}
return substr($className, $pos + Proxy::MARKER_LENGTH + 2);
}
/**
* Gets the real class name of an object (even if its a proxy).
*
* @param object $object
* @psalm-param Proxy|T $object
*
* @return string
* @psalm-return class-string
*
* @template T of object
*/
public static function getClass($object)
{
return self::getRealClass(get_class($object));
}
/**
* Gets the real parent class name of a class or object.
*
* @param string $className
* @psalm-param class-string $className
*
* @return string
* @psalm-return class-string
*/
public static function getParentClass($className)
{
return get_parent_class(self::getRealClass($className));
}
/**
* Creates a new reflection class.
*
* @param string $className
* @psalm-param class-string $className
*
* @return ReflectionClass
*/
public static function newReflectionClass($className)
{
return new ReflectionClass(self::getRealClass($className));
}
/**
* Creates a new reflection object.
*
* @param object $object
*
* @return ReflectionClass
*/
public static function newReflectionObject($object)
{
return self::newReflectionClass(self::getClass($object));
}
/**
* Given a class name and a proxy namespace returns the proxy name.
*
* @param string $className
* @param string $proxyNamespace
* @psalm-param class-string $className
*
* @return string
* @psalm-return class-string
*/
public static function generateProxyClassName($className, $proxyNamespace)
{
return rtrim($proxyNamespace, '\\') . '\\' . Proxy::MARKER . '\\' . ltrim($className, '\\');
}
}
lib/Doctrine/Common/Util/Debug.php 0000644 00000011151 14176470036 0012774 0 ustar 00 toArray();
}
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.dist 0000644 00000007246 14176470036 0010065 0 ustar 00 parameters:
phpVersion: 80100
level: 3
paths:
- lib
- tests
excludePaths:
- lib/vendor/doctrine-build-common
- tests/Doctrine/Tests/Common/Proxy/InvalidReturnTypeClass.php
- tests/Doctrine/Tests/Common/Proxy/InvalidTypeHintClass.php
- tests/Doctrine/Tests/Common/Proxy/LazyLoadableObjectWithTypedProperties.php
- tests/Doctrine/Tests/Common/Proxy/MagicIssetClassWithInteger.php
- tests/Doctrine/Tests/Common/Proxy/NullableNonOptionalHintClass.php
- tests/Doctrine/Tests/Common/Proxy/PHP81NeverType.php
- tests/Doctrine/Tests/Common/Proxy/PHP81IntersectionTypes.php
- tests/Doctrine/Tests/Common/Proxy/Php8UnionTypes.php
- tests/Doctrine/Tests/Common/Proxy/Php8StaticType.php
- tests/Doctrine/Tests/Common/Proxy/ProxyGeneratorTest.php
- tests/Doctrine/Tests/Common/Proxy/ProxyLogicTypedPropertiesTest.php
- tests/Doctrine/Tests/Common/Proxy/SerializedClass.php
- tests/Doctrine/Tests/Common/Proxy/VariadicTypeHintClass.php
- tests/Doctrine/Tests/Common/Proxy/Php71NullableDefaultedNonOptionalHintClass.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: '#.*LazyLoadableObject.*#'
paths:
- 'tests/Doctrine/Tests/Common/Proxy/ProxyLogicTest.php'
- 'tests/Doctrine/Tests/Common/Proxy/ProxyLogicVoidReturnTypeTest.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'
-
message: "#^Class Doctrine\\\\Tests\\\\Common\\\\Proxy\\\\MagicIssetClassWithInteger not found\\.$#"
count: 1
path: tests/Doctrine/Tests/Common/Proxy/ProxyMagicMethodsTest.php
includes:
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
psalm.xml 0000644 00000001401 14176470036 0006406 0 ustar 00