diff options
Diffstat (limited to 'vendor/doctrine/common')
25 files changed, 3024 insertions, 0 deletions
diff --git a/vendor/doctrine/common/.doctrine-project.json b/vendor/doctrine/common/.doctrine-project.json new file mode 100644 index 0000000..1f33c55 --- /dev/null +++ b/vendor/doctrine/common/.doctrine-project.json @@ -0,0 +1,38 @@ +{ + "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 + } + ] +} diff --git a/vendor/doctrine/common/LICENSE b/vendor/doctrine/common/LICENSE new file mode 100644 index 0000000..8c38cc1 --- /dev/null +++ b/vendor/doctrine/common/LICENSE @@ -0,0 +1,19 @@ +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. diff --git a/vendor/doctrine/common/README.md b/vendor/doctrine/common/README.md new file mode 100644 index 0000000..603c38f --- /dev/null +++ b/vendor/doctrine/common/README.md @@ -0,0 +1,12 @@ +# Doctrine Common + +[](https://github.com/doctrine/common/actions) +[](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/latest/) +* [Downloads](https://github.com/doctrine/common/releases) diff --git a/vendor/doctrine/common/UPGRADE_TO_2_1 b/vendor/doctrine/common/UPGRADE_TO_2_1 new file mode 100644 index 0000000..891a2e5 --- /dev/null +++ b/vendor/doctrine/common/UPGRADE_TO_2_1 @@ -0,0 +1,39 @@ +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. + diff --git a/vendor/doctrine/common/UPGRADE_TO_2_2 b/vendor/doctrine/common/UPGRADE_TO_2_2 new file mode 100644 index 0000000..1d93a13 --- /dev/null +++ b/vendor/doctrine/common/UPGRADE_TO_2_2 @@ -0,0 +1,61 @@ +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
\ No newline at end of file diff --git a/vendor/doctrine/common/composer.json b/vendor/doctrine/common/composer.json new file mode 100644 index 0000000..0f14ca3 --- /dev/null +++ b/vendor/doctrine/common/composer.json @@ -0,0 +1,43 @@ +{ + "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.5 || ^9.0", + "doctrine/coding-standard": "^6.0 || ^8.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" + } + } +} diff --git a/vendor/doctrine/common/docs/en/index.rst b/vendor/doctrine/common/docs/en/index.rst new file mode 100644 index 0000000..7550d8b --- /dev/null +++ b/vendor/doctrine/common/docs/en/index.rst @@ -0,0 +1,10 @@ +Common Documentation +==================== + +Welcome to the Doctrine Common Library documentation. + +.. toctree:: + :depth: 2 + :glob: + + * diff --git a/vendor/doctrine/common/docs/en/reference/class-loading.rst b/vendor/doctrine/common/docs/en/reference/class-loading.rst new file mode 100644 index 0000000..e193b46 --- /dev/null +++ b/vendor/doctrine/common/docs/en/reference/class-loading.rst @@ -0,0 +1,242 @@ +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 + + <?php + namespace MyProject\Shipping; + class ShippingStrategy { ... } + +resides in the following directory structure: + +:: + + src/ + /MyProject + /Shipping + ShippingStrategy.php + +Note that the name of "src" or the structure above or beside this +directory is completely arbitrary. "src" could be named "classes" +or "lib" or whatever. The only convention to adhere to is to map +namespaces to directories and classes to files named after the +class name. + +Usage +----- + +To use a Doctrine Common ClassLoader, you first need to load the +class file containing the ClassLoader. This is the only class file +that actually needs to be loaded explicitly via ``require``. All +other classes will be loaded on demand by the configured class +loaders. + +.. code-block:: php + + <?php + use Doctrine\Common\ClassLoader; + require '/path/to/Doctrine/Common/ClassLoader.php'; + $classLoader = new ClassLoader('MyProject', '/path/to/src'); + +A ``ClassLoader`` takes two constructor parameters, both optional. +In the normal case both arguments are supplied. The first argument +specifies the namespace prefix this class loader should be +responsible for and the second parameter is the path to the root +directory where the classes can be found according to the +convention mentioned previously. + +The class loader in the example above would thus be responsible for +all classes under the 'MyProject' namespace and it would look for +the class files starting at the directory '/path/to/src'. + +Also note that the prefix supplied in the first argument need not +be a root namespace but can be an arbitrarily nested namespace as +well. This allows you to even have the sources of subnamespaces +split across different directories. For example, all projects under +the Doctrine umbrella reside in the Doctrine namespace, yet the +sources for each project usually do not reside under a common root +directory. The following is an example of configuring three class +loaders, one for each used Doctrine project: + +.. code-block:: php + + <?php + use Doctrine\Common\ClassLoader; + require '/path/to/Doctrine/Common/ClassLoader.php'; + $commonLoader = new ClassLoader('Doctrine\Common', '/path/to/common/lib'); + $dbalLoader = new ClassLoader('Doctrine\DBAL', '/path/to/dbal/lib'); + $ormLoader = new ClassLoader('Doctrine\ORM', '/path/to/orm/lib'); + $commonLoader->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 + + <?php + $customLoader = new ClassLoader('CustomLib', '/path/to/custom/lib'); + $customLoader->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 + + <?php + $zend1Loader = new ClassLoader('Zend', '/path/to/zend/lib'); + $zend1Loader->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 + + <?php + // Equivalent to if ( + ('Foo', true)) if there is only 1 class loader to check + if (class_exists('Foo', false) || $classLoader->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 + + <?php + // Equivalent to if (class_exists('Foo', true)) + if (ClassLoader::classExists('Foo')) { + // ... + } + +This static method can basically be used as a drop-in replacement +for class_exists(..., true). It iterates over all installed class +loaders and asks each of them via ``canLoadClass``, returning early +(with TRUE) as soon as one class loader returns TRUE from +``canLoadClass``. If this sounds like it can potentially be rather +costly then because that is true but it is exactly the same thing +that class_exists(..., true) does under the hood, it triggers a +complete interaction of all class/auto loaders. Checking for class +existence via invoking autoloading was never a cheap thing to do +but now it is more obvious and more importantly, this check is no +longer interleaved with regular class loading, which avoids having +to check each and every class for existence prior to loading it. +The vast majority of classes to be loaded are *not* optional and a +failure to load such a class is, and should be, a fatal error. The +ClassLoader design reflects this. + +If you have code that requires the usage of class\_exists(..., +true) or ClassLoader::classExists during normal runtime of the +application (i.e. on each request) try to refactor your design to +avoid it. + +Summary +------- + +No matter which class loader you prefer to use (Doctrine classes do +not care about how they are loaded), we kindly encourage you to +adhere to the simple convention of mapping namespaces and class +names to a directory structure. + +Class loading should be simple, automated and uniform. Time is +better invested in actual application development than in designing +special directory structures, autoloaders and clever caching +strategies for class loading. + + diff --git a/vendor/doctrine/common/humbug.json.dist b/vendor/doctrine/common/humbug.json.dist new file mode 100644 index 0000000..ed6732a --- /dev/null +++ b/vendor/doctrine/common/humbug.json.dist @@ -0,0 +1,11 @@ +{ + "source": { + "directories": [ + "lib\/Doctrine" + ] + }, + "timeout": 10, + "logs": { + "text": "reports/humbuglog.txt" + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/ClassLoader.php b/vendor/doctrine/common/lib/Doctrine/Common/ClassLoader.php new file mode 100644 index 0000000..2a53bad --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/ClassLoader.php @@ -0,0 +1,289 @@ +<?php + +namespace Doctrine\Common; + +use function class_exists; +use function interface_exists; +use function is_array; +use function is_file; +use function reset; +use function spl_autoload_functions; +use function spl_autoload_register; +use function spl_autoload_unregister; +use function str_replace; +use function stream_resolve_include_path; +use function strpos; +use function trait_exists; +use function trigger_error; + +use const DIRECTORY_SEPARATOR; +use const E_USER_DEPRECATED; + +@trigger_error(ClassLoader::class . ' is deprecated.', E_USER_DEPRECATED); + +/** + * A <tt>ClassLoader</tt> 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 <code>include_path</code>. + * + * @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 <tt>ClassLoader</tt> 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. + * + * @psalm-param class-string $className + */ + 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. + * + * @psalm-param class-string $className + */ + 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 <tt>ClassLoader</tt>, {@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 <tt>ClassLoader</tt>, 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. + * + * @psalm-param class-string $className + */ + public static function classExists($className) + { + return self::typeExists($className, true); + } + + /** + * Gets the <tt>ClassLoader</tt> 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 <tt>ClassLoader</tt> for the class or NULL if no such <tt>ClassLoader</tt> exists. + * + * @psalm-param class-string $className + */ + 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); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/CommonException.php b/vendor/doctrine/common/lib/Doctrine/Common/CommonException.php new file mode 100644 index 0000000..a2aba0e --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/CommonException.php @@ -0,0 +1,14 @@ +<?php + +namespace Doctrine\Common; + +use Exception; + +/** + * Base exception class for package Doctrine\Common. + * + * @deprecated The doctrine/common package is deprecated, please use specific packages and their exceptions instead. + */ +class CommonException extends Exception +{ +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Comparable.php b/vendor/doctrine/common/lib/Doctrine/Common/Comparable.php new file mode 100644 index 0000000..f9ac4be --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Comparable.php @@ -0,0 +1,26 @@ +<?php + +namespace Doctrine\Common; + +/** + * Comparable interface that allows to compare two value objects to each other for similarity. + * + * @link www.doctrine-project.org + */ +interface Comparable +{ + /** + * Compares the current object to the passed $other. + * + * Returns 0 if they are semantically equal, 1 if the other object + * is less than the current one, or -1 if its more than the current one. + * + * This method should not check for identity using ===, only for semantical equality for example + * when two different DateTime instances point to the exact same Date + TZ. + * + * @param mixed $other + * + * @return int + */ + public function compareTo($other); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php new file mode 100644 index 0000000..a7af281 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php @@ -0,0 +1,230 @@ +<?php + +namespace Doctrine\Common\Proxy; + +use Doctrine\Common\Proxy\Exception\InvalidArgumentException; +use Doctrine\Common\Proxy\Exception\OutOfBoundsException; +use Doctrine\Common\Util\ClassUtils; +use Doctrine\Persistence\Mapping\ClassMetadata; +use Doctrine\Persistence\Mapping\ClassMetadataFactory; + +use function class_exists; +use function file_exists; +use function in_array; +use function interface_exists; + +/** + * Abstract factory for proxy objects. + */ +abstract class AbstractProxyFactory +{ + /** + * Never autogenerate a proxy and rely that it was generated by some + * process before deployment. + */ + public const AUTOGENERATE_NEVER = 0; + + /** + * Always generates a new proxy in every request. + * + * This is only sane during development. + */ + public const AUTOGENERATE_ALWAYS = 1; + + /** + * Autogenerate the proxy class when the proxy file does not exist. + * + * This strategy causes a file exists call whenever any proxy is used the + * first time in a request. + */ + public const AUTOGENERATE_FILE_NOT_EXISTS = 2; + + /** + * Generate the proxy classes using eval(). + * + * This strategy is only sane for development, and even then it gives me + * the creeps a little. + */ + public const AUTOGENERATE_EVAL = 3; + + private const AUTOGENERATE_MODES = [ + self::AUTOGENERATE_NEVER, + self::AUTOGENERATE_ALWAYS, + self::AUTOGENERATE_FILE_NOT_EXISTS, + self::AUTOGENERATE_EVAL, + ]; + + /** @var ClassMetadataFactory */ + private $metadataFactory; + + /** @var ProxyGenerator the proxy generator responsible for creating the proxy classes/files. */ + private $proxyGenerator; + + /** @var int Whether to automatically (re)generate proxy classes. */ + private $autoGenerate; + + /** @var ProxyDefinition[] */ + private $definitions = []; + + /** + * @param bool|int $autoGenerate + * + * @throws InvalidArgumentException When auto generate mode is not valid. + */ + public function __construct(ProxyGenerator $proxyGenerator, ClassMetadataFactory $metadataFactory, $autoGenerate) + { + $this->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<mixed> $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 + * + * @psalm-param class-string $className + */ + 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 + * + * @psalm-param class-string $className + */ + abstract protected function createProxyDefinition($className); +} + +interface_exists(ClassMetadata::class); +interface_exists(ClassMetadataFactory::class); diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Autoloader.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Autoloader.php new file mode 100644 index 0000000..58d221a --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Autoloader.php @@ -0,0 +1,100 @@ +<?php + +namespace Doctrine\Common\Proxy; + +use Closure; +use Doctrine\Common\Proxy\Exception\InvalidArgumentException; + +use function call_user_func; +use function file_exists; +use function is_callable; +use function ltrim; +use function spl_autoload_register; +use function str_replace; +use function strlen; +use function strpos; +use function substr; + +use const DIRECTORY_SEPARATOR; + +/** + * Special Autoloader for Proxy classes, which are not PSR-0 compliant. + * + * @internal + */ +class Autoloader +{ + /** + * Resolves proxy class name to a filename based on the following pattern. + * + * 1. Remove Proxy namespace from class name. + * 2. Remove namespace separators from remaining class name. + * 3. Return PHP filename from proxy-dir with the result from 2. + * + * @param string $proxyDir + * @param string $proxyNamespace + * @param string $className + * + * @return string + * + * @throws InvalidArgumentException + * + * @psalm-param class-string $className + */ + public static function resolveFile($proxyDir, $proxyNamespace, $className) + { + if (strpos($className, $proxyNamespace) !== 0) { + throw InvalidArgumentException::notProxyClass($className, $proxyNamespace); + } + + // remove proxy namespace from class name + $classNameRelativeToProxyNamespace = substr($className, strlen($proxyNamespace)); + + // remove namespace separators from remaining class name + $fileName = str_replace('\\', '', $classNameRelativeToProxyNamespace); + + return $proxyDir . DIRECTORY_SEPARATOR . $fileName . '.php'; + } + + /** + * Registers and returns autoloader callback for the given proxy dir and namespace. + * + * @param string $proxyDir + * @param string $proxyNamespace + * @param callable|null $notFoundCallback Invoked when the proxy file is not found. + * + * @return Closure + * + * @throws InvalidArgumentException + */ + public static function register($proxyDir, $proxyNamespace, $notFoundCallback = null) + { + $proxyNamespace = ltrim($proxyNamespace, '\\'); + + if ($notFoundCallback !== null && ! is_callable($notFoundCallback)) { + throw InvalidArgumentException::invalidClassNotFoundCallback($notFoundCallback); + } + + $autoloader = static function ($className) use ($proxyDir, $proxyNamespace, $notFoundCallback) { + if ($proxyNamespace === '') { + return; + } + + if (strpos($className, $proxyNamespace) !== 0) { + return; + } + + $file = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className); + + if ($notFoundCallback && ! file_exists($file)) { + call_user_func($notFoundCallback, $proxyDir, $proxyNamespace, $className); + } + + require $file; + }; + + spl_autoload_register($autoloader); + + return $autoloader; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/InvalidArgumentException.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..db92809 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/InvalidArgumentException.php @@ -0,0 +1,113 @@ +<?php + +namespace Doctrine\Common\Proxy\Exception; + +use Doctrine\Persistence\Proxy; +use InvalidArgumentException as BaseInvalidArgumentException; + +use function get_class; +use function gettype; +use function interface_exists; +use function is_object; +use function sprintf; + +/** + * Proxy Invalid Argument Exception. + * + * @link www.doctrine-project.org + */ +class InvalidArgumentException extends BaseInvalidArgumentException implements ProxyException +{ + /** + * @return self + */ + public static function proxyDirectoryRequired() + { + return new self('You must configure a proxy directory. See docs for details'); + } + + /** + * @param string $className + * @param string $proxyNamespace + * + * @return self + * + * @psalm-param class-string $className + */ + public static function notProxyClass($className, $proxyNamespace) + { + return new self(sprintf('The class "%s" is not part of the proxy namespace "%s"', $className, $proxyNamespace)); + } + + /** + * @param string $name + * + * @return self + */ + public static function invalidPlaceholder($name) + { + return new self(sprintf('Provided placeholder for "%s" must be either a string or a valid callable', $name)); + } + + /** + * @return self + */ + public static function proxyNamespaceRequired() + { + return new self('You must configure a proxy namespace'); + } + + /** + * @return self + */ + public static function unitializedProxyExpected(Proxy $proxy) + { + return new self(sprintf('Provided proxy of type "%s" must not be initialized.', get_class($proxy))); + } + + /** + * @param mixed $callback + * + * @return self + */ + public static function invalidClassNotFoundCallback($callback) + { + $type = is_object($callback) ? get_class($callback) : gettype($callback); + + return new self(sprintf('Invalid \$notFoundCallback given: must be a callable, "%s" given', $type)); + } + + /** + * @param string $className + * + * @return self + * + * @psalm-param class-string $className + */ + public static function classMustNotBeAbstract($className) + { + return new self(sprintf('Unable to create a proxy for an abstract class "%s".', $className)); + } + + /** + * @param string $className + * + * @return self + * + * @psalm-param class-string $className + */ + public static function classMustNotBeFinal($className) + { + return new self(sprintf('Unable to create a proxy for a final class "%s".', $className)); + } + + /** + * @param mixed $value + */ + public static function invalidAutoGenerateMode($value): self + { + return new self(sprintf('Invalid auto generate mode "%s" given.', $value)); + } +} + +interface_exists(Proxy::class); diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/OutOfBoundsException.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/OutOfBoundsException.php new file mode 100644 index 0000000..b9fde45 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/OutOfBoundsException.php @@ -0,0 +1,28 @@ +<?php + +namespace Doctrine\Common\Proxy\Exception; + +use OutOfBoundsException as BaseOutOfBoundsException; + +use function sprintf; + +/** + * Proxy Invalid Argument Exception. + * + * @link www.doctrine-project.org + */ +class OutOfBoundsException extends BaseOutOfBoundsException implements ProxyException +{ + /** + * @param string $className + * @param string $idField + * + * @return self + * + * @psalm-param class-string $className + */ + public static function missingPrimaryKeyValue($className, $idField) + { + return new self(sprintf('Missing value for primary key %s on %s', $idField, $className)); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/ProxyException.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/ProxyException.php new file mode 100644 index 0000000..f827fbd --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/ProxyException.php @@ -0,0 +1,12 @@ +<?php + +namespace Doctrine\Common\Proxy\Exception; + +/** + * Base exception interface for proxy exceptions. + * + * @link www.doctrine-project.org + */ +interface ProxyException +{ +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/UnexpectedValueException.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/UnexpectedValueException.php new file mode 100644 index 0000000..9f85a1a --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/UnexpectedValueException.php @@ -0,0 +1,74 @@ +<?php + +namespace Doctrine\Common\Proxy\Exception; + +use Throwable; +use UnexpectedValueException as BaseUnexpectedValueException; + +use function sprintf; + +/** + * Proxy Unexpected Value Exception. + * + * @link www.doctrine-project.org + */ +class UnexpectedValueException extends BaseUnexpectedValueException implements ProxyException +{ + /** + * @param string $proxyDirectory + * + * @return self + */ + public static function proxyDirectoryNotWritable($proxyDirectory) + { + return new self(sprintf('Your proxy directory "%s" must be writable', $proxyDirectory)); + } + + /** + * @param string $className + * @param string $methodName + * @param string $parameterName + * + * @return self + * + * @psalm-param class-string $className + */ + public static function invalidParameterTypeHint( + $className, + $methodName, + $parameterName, + ?Throwable $previous = null + ) { + return new self( + sprintf( + 'The type hint of parameter "%s" in method "%s" in class "%s" is invalid.', + $parameterName, + $methodName, + $className + ), + 0, + $previous + ); + } + + /** + * @param string $className + * @param string $methodName + * + * @return self + * + * @psalm-param class-string $className + */ + public static function invalidReturnTypeHint($className, $methodName, ?Throwable $previous = null) + { + return new self( + sprintf( + 'The return type of method "%s" in class "%s" is invalid.', + $methodName, + $className + ), + 0, + $previous + ); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Proxy.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Proxy.php new file mode 100644 index 0000000..93f144a --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Proxy.php @@ -0,0 +1,65 @@ +<?php + +namespace Doctrine\Common\Proxy; + +use Closure; +use Doctrine\Persistence\Proxy as BaseProxy; + +/** + * Interface for proxy classes. + */ +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<string, mixed> Keys are the property names, and values are the default values + * for those properties. + */ + public function __getLazyProperties(); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyDefinition.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyDefinition.php new file mode 100644 index 0000000..a7f3957 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyDefinition.php @@ -0,0 +1,42 @@ +<?php + +namespace Doctrine\Common\Proxy; + +use ReflectionProperty; + +/** + * Definition structure how to create a proxy. + */ +class ProxyDefinition +{ + /** @var string */ + public $proxyClassName; + + /** @var array<string> */ + public $identifierFields; + + /** @var ReflectionProperty[] */ + public $reflectionFields; + + /** @var callable */ + public $initializer; + + /** @var callable */ + public $cloner; + + /** + * @param string $proxyClassName + * @param array<string> $identifierFields + * @param array<string, ReflectionProperty> $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; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyGenerator.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyGenerator.php new file mode 100644 index 0000000..5504078 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyGenerator.php @@ -0,0 +1,1194 @@ +<?php + +namespace Doctrine\Common\Proxy; + +use Doctrine\Common\Proxy\Exception\InvalidArgumentException; +use Doctrine\Common\Proxy\Exception\UnexpectedValueException; +use Doctrine\Common\Util\ClassUtils; +use Doctrine\Persistence\Mapping\ClassMetadata; +use ReflectionMethod; +use ReflectionNamedType; +use ReflectionParameter; +use ReflectionProperty; +use ReflectionType; +use ReflectionUnionType; + +use function array_combine; +use function array_diff; +use function array_key_exists; +use function array_map; +use function array_slice; +use function assert; +use function call_user_func; +use function chmod; +use function class_exists; +use function dirname; +use function explode; +use function file; +use function file_put_contents; +use function implode; +use function in_array; +use function interface_exists; +use function is_callable; +use function is_dir; +use function is_string; +use function is_writable; +use function lcfirst; +use function ltrim; +use function method_exists; +use function mkdir; +use function preg_match; +use function preg_match_all; +use function rename; +use function rtrim; +use function sprintf; +use function str_replace; +use function strrev; +use function strtolower; +use function strtr; +use function substr; +use function trim; +use function uniqid; +use function var_export; + +use const DIRECTORY_SEPARATOR; + +/** + * This factory is used to generate proxy classes. + * It builds proxies from given parameters, a template and class metadata. + */ +class ProxyGenerator +{ + /** + * Used to match very simple id methods that don't need + * to be decorated since the identifier is known. + */ + public const PATTERN_MATCH_ID_METHOD = '((public\s+)?(function\s+%s\s*\(\)\s*)\s*(?::\s*\??\s*\\\\?[a-z_\x7f-\xff][\w\x7f-\xff]*(?:\\\\[a-z_\x7f-\xff][\w\x7f-\xff]*)*\s*)?{\s*return\s*\$this->%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 = '<?php + +namespace <namespace>; + +/** + * DO NOT EDIT THIS FILE - IT WAS CREATED BY DOCTRINE\'S PROXY GENERATOR + */ +class <proxyShortClassName> extends \<className> implements \<baseProxyInterface> +{ + /** + * @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<string, null> properties to be lazy loaded, indexed by property name + */ + public static $lazyPropertiesNames = <lazyPropertiesNames>; + + /** + * @var array<string, mixed> default values of properties to be lazy loaded, with keys being the property names + * + * @see \Doctrine\Common\Proxy\Proxy::__getLazyProperties + */ + public static $lazyPropertiesDefaults = <lazyPropertiesDefaults>; + +<additionalProperties> + +<constructorImpl> + +<magicGet> + +<magicSet> + +<magicIsset> + +<sleepImpl> + +<wakeupImpl> + +<cloneImpl> + + /** + * 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; + } + + <methods> +} +'; + + /** + * Initializes a new instance of the <tt>ProxyFactory</tt> class that is + * connected to the given <tt>EntityManager</tt>. + * + * @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 = <<<EOT + /** + * $inheritDoc + * @param string \$name + */ + public function {$returnReference}__get($parametersString)$returnTypeHint + { + +EOT; + + if (! empty($lazyPublicProperties)) { + $magicGet .= <<<'EOT' + if (\array_key_exists($name, self::$lazyPropertiesNames)) { + $this->__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(<<<EOT + trigger_error(sprintf('Undefined property: %%s::$%%s', __CLASS__, %s), E_USER_NOTICE); + +EOT + , $name); + } + + return $magicGet . "\n }"; + } + + /** + * Generates the magic setter (currently unused). + * + * @return string + */ + private function generateMagicSet(ClassMetadata $class) + { + $lazyPublicProperties = $this->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 = <<<EOT + /** + * $inheritDoc + * @param string \$name + * @param mixed \$value + */ + public function __set($parametersString)$returnTypeHint + { + +EOT; + + 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]); +EOT; + + if ($returnTypeHint === ': void') { + $magicSet .= <<<'EOT' + + parent::__set($name, $value); + return; +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 = <<<EOT + /** + * $inheritDoc + * @param string \$name + * @return boolean + */ + public function __isset($parametersString)$returnTypeHint + { + +EOT; + + if (! empty($lazyPublicProperties)) { + $magicIsset .= <<<'EOT' + if (\array_key_exists($name, self::$lazyPropertiesNames)) { + $this->__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 = <<<EOT + /** + * $inheritDoc + * @return array + */ + public function __sleep()$returnTypeHint + { + +EOT; + + if ($hasParentSleep) { + return $sleepImpl . <<<'EOT' + $properties = array_merge(['__isInitialized__'], parent::__sleep()); + + if ($this->__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 . <<<EOT + if (\$this->__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 = <<<EOT + /** + * $inheritDoc + */ + public function __wakeup()$returnTypeHint + { + if ( ! \$this->__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 <<<EOT + /** + * $inheritDoc + */ + public function __clone() + { + \$this->__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 + * + * @psalm-param class-string $className + */ + 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<int, string> + */ + 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 strtolower($this->formatType($method->getReturnType(), $method)) !== 'void'; + } + + /** + * @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() + )); + } + + 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; + } +} + +interface_exists(ClassMetadata::class); diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Util/ClassUtils.php b/vendor/doctrine/common/lib/Doctrine/Common/Util/ClassUtils.php new file mode 100644 index 0000000..d220232 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Util/ClassUtils.php @@ -0,0 +1,112 @@ +<?php + +namespace Doctrine\Common\Util; + +use Doctrine\Persistence\Proxy; +use ReflectionClass; + +use function get_class; +use function get_parent_class; +use function ltrim; +use function rtrim; +use function strrpos; +use function substr; + +/** + * Class and reflection related functionality for objects that + * might or not be proxy objects at the moment. + */ +class ClassUtils +{ + /** + * Gets the real class name of a class name that could be a proxy. + * + * @param string $className + * + * @return string + * + * @psalm-param class-string $className + * @psalm-return class-string + */ + public static function getRealClass($className) + { + $pos = strrpos($className, '\\' . Proxy::MARKER . '\\'); + + if ($pos === false) { + 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 + * + * @return string + * + * @psalm-return class-string + */ + 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 + * + * @return string + * + * @psalm-param class-string $className + * @psalm-return class-string + */ + public static function getParentClass($className) + { + return get_parent_class(self::getRealClass($className)); + } + + /** + * Creates a new reflection class. + * + * @param string $className + * + * @return ReflectionClass + * + * @psalm-param class-string $className + */ + 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 + * + * @return string + * + * @psalm-param class-string $className + * @psalm-return class-string + */ + public static function generateProxyClassName($className, $proxyNamespace) + { + return rtrim($proxyNamespace, '\\') . '\\' . Proxy::MARKER . '\\' . ltrim($className, '\\'); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Util/Debug.php b/vendor/doctrine/common/lib/Doctrine/Common/Util/Debug.php new file mode 100644 index 0000000..d21f700 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Util/Debug.php @@ -0,0 +1,185 @@ +<?php + +namespace Doctrine\Common\Util; + +use ArrayIterator; +use ArrayObject; +use DateTimeInterface; +use Doctrine\Common\Collections\Collection; +use Doctrine\Persistence\Proxy; +use stdClass; + +use function array_keys; +use function count; +use function end; +use function explode; +use function extension_loaded; +use function get_class; +use function html_entity_decode; +use function ini_get; +use function ini_set; +use function is_array; +use function is_object; +use function method_exists; +use function ob_end_clean; +use function ob_get_contents; +use function ob_start; +use function spl_object_hash; +use function strip_tags; +use function var_dump; + +/** + * Static class containing most used debug methods. + * + * @deprecated The Debug class is deprecated, please use symfony/var-dumper instead. + * + * @link www.doctrine-project.org + */ +final class Debug +{ + /** + * Private constructor (prevents instantiation). + */ + private function __construct() + { + } + + /** + * Prints a dump of the public, protected and private properties of $var. + * + * @link https://xdebug.org/ + * + * @param mixed $var The variable to dump. + * @param int $maxDepth The maximum nesting level for object properties. + * @param bool $stripTags Whether output should strip HTML tags. + * @param bool $echo Send the dumped value to the output buffer + * + * @return string + */ + public static function dump($var, $maxDepth = 2, $stripTags = true, $echo = true) + { + $html = ini_get('html_errors'); + + if ($html !== true) { + ini_set('html_errors', 'on'); + } + + if (extension_loaded('xdebug')) { + ini_set('xdebug.var_display_max_depth', $maxDepth); + } + + $var = self::export($var, $maxDepth); + + ob_start(); + var_dump($var); + + $dump = ob_get_contents(); + + ob_end_clean(); + + $dumpText = ($stripTags ? strip_tags(html_entity_decode($dump)) : $dump); + + ini_set('html_errors', $html); + + if ($echo) { + echo $dumpText; + } + + return $dumpText; + } + + /** + * @param mixed $var + * @param int $maxDepth + * + * @return mixed + */ + public static function export($var, $maxDepth) + { + $return = null; + $isObj = is_object($var); + + if ($var instanceof Collection) { + $var = $var->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); + } +} diff --git a/vendor/doctrine/common/phpstan.neon.dist b/vendor/doctrine/common/phpstan.neon.dist new file mode 100644 index 0000000..e63c669 --- /dev/null +++ b/vendor/doctrine/common/phpstan.neon.dist @@ -0,0 +1,49 @@ +parameters: + 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/LazyLoadableObjectWithTypedProperties.php + - tests/Doctrine/Tests/Common/Proxy/MagicIssetClassWithInteger.php + - tests/Doctrine/Tests/Common/Proxy/NullableNonOptionalHintClass.php + - tests/Doctrine/Tests/Common/Proxy/Php8UnionTypes.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/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 diff --git a/vendor/doctrine/common/psalm.xml b/vendor/doctrine/common/psalm.xml new file mode 100644 index 0000000..f49326f --- /dev/null +++ b/vendor/doctrine/common/psalm.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<psalm + errorLevel="8" + resolveFromConfigFile="true" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="https://getpsalm.org/schema/config" + xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" + phpVersion="8.0" +> + <projectFiles> + <directory name="lib/Doctrine/Common" /> + <ignoreFiles> + <directory name="vendor" /> + </ignoreFiles> + </projectFiles> +</psalm> |