From 745cf2431a71d0e6c5f08f8605839279b2f7496e Mon Sep 17 00:00:00 2001 From: Devian Date: Thu, 22 Apr 2021 17:03:46 +0800 Subject: Initiate commit --- .../common/lib/Doctrine/Common/ClassLoader.php | 289 +++++ .../common/lib/Doctrine/Common/CommonException.php | 14 + .../common/lib/Doctrine/Common/Comparable.php | 26 + .../Doctrine/Common/Proxy/AbstractProxyFactory.php | 230 ++++ .../lib/Doctrine/Common/Proxy/Autoloader.php | 100 ++ .../Proxy/Exception/InvalidArgumentException.php | 113 ++ .../Proxy/Exception/OutOfBoundsException.php | 28 + .../Common/Proxy/Exception/ProxyException.php | 12 + .../Proxy/Exception/UnexpectedValueException.php | 74 ++ .../common/lib/Doctrine/Common/Proxy/Proxy.php | 65 ++ .../lib/Doctrine/Common/Proxy/ProxyDefinition.php | 42 + .../lib/Doctrine/Common/Proxy/ProxyGenerator.php | 1194 ++++++++++++++++++++ .../common/lib/Doctrine/Common/Util/ClassUtils.php | 112 ++ .../common/lib/Doctrine/Common/Util/Debug.php | 185 +++ 14 files changed, 2484 insertions(+) create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/ClassLoader.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/CommonException.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Comparable.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Proxy/Autoloader.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/InvalidArgumentException.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/OutOfBoundsException.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/ProxyException.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/UnexpectedValueException.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Proxy/Proxy.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyDefinition.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyGenerator.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Util/ClassUtils.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Util/Debug.php (limited to 'vendor/doctrine/common/lib') 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 @@ +ClassLoader is an autoloader for class files that can be + * installed on the SPL autoload stack. It is a class loader that either loads only classes + * of a specific namespace or all namespaces and it is suitable for working together + * with other autoloaders in the SPL autoload stack. + * + * If no include path is configured through the constructor or {@link setIncludePath}, a ClassLoader + * relies on the PHP include_path. + * + * @deprecated The ClassLoader is deprecated and will be removed in version 4.0 of doctrine/common. + */ +class ClassLoader +{ + /** + * PHP file extension. + * + * @var string + */ + protected $fileExtension = '.php'; + + /** + * Current namespace. + * + * @var string|null + */ + protected $namespace; + + /** + * Current include path. + * + * @var string|null + */ + protected $includePath; + + /** + * PHP namespace separator. + * + * @var string + */ + protected $namespaceSeparator = '\\'; + + /** + * Creates a new ClassLoader that loads classes of the + * specified namespace from the specified include path. + * + * If no include path is given, the ClassLoader relies on the PHP include_path. + * If neither a namespace nor an include path is given, the ClassLoader will + * be responsible for loading all classes, thereby relying on the PHP include_path. + * + * @param string|null $ns The namespace of the classes to load. + * @param string|null $includePath The base include path to use. + */ + public function __construct($ns = null, $includePath = null) + { + $this->namespace = $ns; + $this->includePath = $includePath; + } + + /** + * Sets the namespace separator used by classes in the namespace of this ClassLoader. + * + * @param string $sep The separator to use. + * + * @return void + */ + public function setNamespaceSeparator($sep) + { + $this->namespaceSeparator = $sep; + } + + /** + * Gets the namespace separator used by classes in the namespace of this ClassLoader. + * + * @return string + */ + public function getNamespaceSeparator() + { + return $this->namespaceSeparator; + } + + /** + * Sets the base include path for all class files in the namespace of this ClassLoader. + * + * @param string|null $includePath + * + * @return void + */ + public function setIncludePath($includePath) + { + $this->includePath = $includePath; + } + + /** + * Gets the base include path for all class files in the namespace of this ClassLoader. + * + * @return string|null + */ + public function getIncludePath() + { + return $this->includePath; + } + + /** + * Sets the file extension of class files in the namespace of this ClassLoader. + * + * @param string $fileExtension + * + * @return void + */ + public function setFileExtension($fileExtension) + { + $this->fileExtension = $fileExtension; + } + + /** + * Gets the file extension of class files in the namespace of this ClassLoader. + * + * @return string + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Registers this ClassLoader on the SPL autoload stack. + * + * @return void + */ + public function register() + { + spl_autoload_register([$this, 'loadClass']); + } + + /** + * Removes this ClassLoader from the SPL autoload stack. + * + * @return void + */ + public function unregister() + { + spl_autoload_unregister([$this, 'loadClass']); + } + + /** + * Loads the given class or interface. + * + * @param string $className The name of the class to load. + * + * @return bool TRUE if the class has been successfully loaded, FALSE otherwise. + * + * @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 ClassLoader, {@link canLoadClass} is used, otherwise the autoload + * function of the autoloader is invoked and expected to return a value that + * evaluates to TRUE if the class (file) exists. As soon as one autoloader reports + * that the class exists, TRUE is returned. + * + * Note that, depending on what kinds of autoloaders are installed on the SPL + * autoload stack, the class (file) might already be loaded as a result of checking + * for its existence. This is not the case with a ClassLoader, who separates + * these responsibilities. + * + * @param string $className The fully-qualified name of the class. + * + * @return bool TRUE if the class exists as per the definition given above, FALSE otherwise. + * + * @psalm-param class-string $className + */ + public static function classExists($className) + { + return self::typeExists($className, true); + } + + /** + * Gets the ClassLoader from the SPL autoload stack that is responsible + * for (and is able to load) the class with the given name. + * + * @param string $className The name of the class. + * + * @return ClassLoader|null The ClassLoader for the class or NULL if no such ClassLoader exists. + * + * @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 @@ +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 + * + * @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 @@ + 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 @@ + */ + 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; + } +} 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 @@ +%s;\s*})i'; + + /** + * The namespace that contains all proxy classes. + * + * @var string + */ + private $proxyNamespace; + + /** + * The directory that contains all proxy classes. + * + * @var string + */ + private $proxyDirectory; + + /** + * Map of callables used to fill in placeholders set in the template. + * + * @var string[]|callable[] + */ + protected $placeholders = [ + 'baseProxyInterface' => Proxy::class, + 'additionalProperties' => '', + ]; + + /** + * Template used as a blueprint to generate proxies. + * + * @var string + */ + protected $proxyClassTemplate = '; + +/** + * DO NOT EDIT THIS FILE - IT WAS CREATED BY DOCTRINE\'S PROXY GENERATOR + */ +class extends \ implements \ +{ + /** + * @var \Closure the callback responsible for loading properties in the proxy object. This callback is called with + * three parameters, being respectively the proxy object to be initialized, the method that triggered the + * initialization process and an array of ordered parameters that were passed to that method. + * + * @see \Doctrine\Common\Proxy\Proxy::__setInitializer + */ + public $__initializer__; + + /** + * @var \Closure the callback responsible of loading properties that need to be copied in the cloned object + * + * @see \Doctrine\Common\Proxy\Proxy::__setCloner + */ + public $__cloner__; + + /** + * @var boolean flag indicating if this object was already initialized + * + * @see \Doctrine\Persistence\Proxy::__isInitialized + */ + public $__isInitialized__ = false; + + /** + * @var array properties to be lazy loaded, indexed by property name + */ + public static $lazyPropertiesNames = ; + + /** + * @var array default values of properties to be lazy loaded, with keys being the property names + * + * @see \Doctrine\Common\Proxy\Proxy::__getLazyProperties + */ + public static $lazyPropertiesDefaults = ; + + + + + + + + + + + + + + + + + + /** + * Forces initialization of the proxy + */ + public function __load() + { + $this->__initializer__ && $this->__initializer__->__invoke($this, \'__load\', []); + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __isInitialized() + { + return $this->__isInitialized__; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __setInitialized($initialized) + { + $this->__isInitialized__ = $initialized; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __setInitializer(\Closure $initializer = null) + { + $this->__initializer__ = $initializer; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __getInitializer() + { + return $this->__initializer__; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __setCloner(\Closure $cloner = null) + { + $this->__cloner__ = $cloner; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific cloning logic + */ + public function __getCloner() + { + return $this->__cloner__; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + * @deprecated no longer in use - generated code now relies on internal components rather than generated public API + * @static + */ + public function __getLazyProperties() + { + return self::$lazyPropertiesDefaults; + } + + +} +'; + + /** + * Initializes a new instance of the ProxyFactory class that is + * connected to the given EntityManager. + * + * @param string $proxyDirectory The directory to use for the proxy classes. It must exist. + * @param string $proxyNamespace The namespace to use for the proxy classes. + * + * @throws InvalidArgumentException + */ + public function __construct($proxyDirectory, $proxyNamespace) + { + if (! $proxyDirectory) { + throw InvalidArgumentException::proxyDirectoryRequired(); + } + + if (! $proxyNamespace) { + throw InvalidArgumentException::proxyNamespaceRequired(); + } + + $this->proxyDirectory = $proxyDirectory; + $this->proxyNamespace = $proxyNamespace; + } + + /** + * Sets a placeholder to be replaced in the template. + * + * @param string $name + * @param string|callable $placeholder + * + * @throws InvalidArgumentException + */ + public function setPlaceholder($name, $placeholder) + { + if (! is_string($placeholder) && ! is_callable($placeholder)) { + throw InvalidArgumentException::invalidPlaceholder($name); + } + + $this->placeholders[$name] = $placeholder; + } + + /** + * Sets the base template used to create proxy classes. + * + * @param string $proxyClassTemplate + */ + public function setProxyClassTemplate($proxyClassTemplate) + { + $this->proxyClassTemplate = (string) $proxyClassTemplate; + } + + /** + * Generates a proxy class file. + * + * @param ClassMetadata $class Metadata for the original class. + * @param string|bool $fileName Filename (full path) for the generated class. If none is given, eval() is used. + * + * @throws InvalidArgumentException + * @throws UnexpectedValueException + */ + public function generateProxyClass(ClassMetadata $class, $fileName = false) + { + $this->verifyClassCanBeProxied($class); + + preg_match_all('(<([a-zA-Z]+)>)', $this->proxyClassTemplate, $placeholderMatches); + + $placeholderMatches = array_combine($placeholderMatches[0], $placeholderMatches[1]); + $placeholders = []; + + foreach ($placeholderMatches as $placeholder => $name) { + $placeholders[$placeholder] = $this->placeholders[$name] ?? [$this, 'generate' . $name]; + } + + foreach ($placeholders as & $placeholder) { + if (! is_callable($placeholder)) { + continue; + } + + $placeholder = call_user_func($placeholder, $class); + } + + $proxyCode = strtr($this->proxyClassTemplate, $placeholders); + + if (! $fileName) { + $proxyClassName = $this->generateNamespace($class) . '\\' . $this->generateProxyShortClassName($class); + + if (! class_exists($proxyClassName)) { + eval(substr($proxyCode, 5)); + } + + return; + } + + $parentDirectory = dirname($fileName); + + if (! is_dir($parentDirectory) && (@mkdir($parentDirectory, 0775, true) === false)) { + throw UnexpectedValueException::proxyDirectoryNotWritable($this->proxyDirectory); + } + + if (! is_writable($parentDirectory)) { + throw UnexpectedValueException::proxyDirectoryNotWritable($this->proxyDirectory); + } + + $tmpFileName = $fileName . '.' . uniqid('', true); + + file_put_contents($tmpFileName, $proxyCode); + @chmod($tmpFileName, 0664); + rename($tmpFileName, $fileName); + } + + /** + * @throws InvalidArgumentException + */ + private function verifyClassCanBeProxied(ClassMetadata $class) + { + if ($class->getReflectionClass()->isFinal()) { + throw InvalidArgumentException::classMustNotBeFinal($class->getName()); + } + + if ($class->getReflectionClass()->isAbstract()) { + throw InvalidArgumentException::classMustNotBeAbstract($class->getName()); + } + } + + /** + * Generates the proxy short class name to be used in the template. + * + * @return string + */ + private function generateProxyShortClassName(ClassMetadata $class) + { + $proxyClassName = ClassUtils::generateProxyClassName($class->getName(), $this->proxyNamespace); + $parts = explode('\\', strrev($proxyClassName), 2); + + return strrev($parts[0]); + } + + /** + * Generates the proxy namespace. + * + * @return string + */ + private function generateNamespace(ClassMetadata $class) + { + $proxyClassName = ClassUtils::generateProxyClassName($class->getName(), $this->proxyNamespace); + $parts = explode('\\', strrev($proxyClassName), 2); + + return strrev($parts[1]); + } + + /** + * Generates the original class name. + * + * @return string + */ + private function generateClassName(ClassMetadata $class) + { + return ltrim($class->getName(), '\\'); + } + + /** + * Generates the array representation of lazy loaded public properties and their default values. + * + * @return string + */ + private function generateLazyPropertiesNames(ClassMetadata $class) + { + $lazyPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class); + $values = []; + + foreach ($lazyPublicProperties as $name) { + $values[$name] = null; + } + + return var_export($values, true); + } + + /** + * Generates the array representation of lazy loaded public properties names. + * + * @return string + */ + private function generateLazyPropertiesDefaults(ClassMetadata $class) + { + return var_export($this->getLazyLoadedPublicProperties($class), true); + } + + /** + * Generates the constructor code (un-setting public lazy loaded properties, setting identifier field values). + * + * @return string + */ + private function generateConstructorImpl(ClassMetadata $class) + { + $constructorImpl = <<<'EOT' + public function __construct(?\Closure $initializer = null, ?\Closure $cloner = null) + { + +EOT; + + $toUnset = array_map(static function (string $name): string { + return '$this->' . $name; + }, $this->getLazyLoadedPublicPropertiesNames($class)); + + return $constructorImpl . ($toUnset === [] ? '' : ' unset(' . implode(', ', $toUnset) . ");\n") + . <<<'EOT' + + $this->__initializer__ = $initializer; + $this->__cloner__ = $cloner; + } +EOT; + } + + /** + * Generates the magic getter invoked when lazy loaded public properties are requested. + * + * @return string + */ + private function generateMagicGet(ClassMetadata $class) + { + $lazyPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class); + $reflectionClass = $class->getReflectionClass(); + $hasParentGet = false; + $returnReference = ''; + $inheritDoc = ''; + $name = '$name'; + $parametersString = '$name'; + $returnTypeHint = null; + + if ($reflectionClass->hasMethod('__get')) { + $hasParentGet = true; + $inheritDoc = '{@inheritDoc}'; + $methodReflection = $reflectionClass->getMethod('__get'); + + if ($methodReflection->returnsReference()) { + $returnReference = '& '; + } + + $methodParameters = $methodReflection->getParameters(); + $name = '$' . $methodParameters[0]->getName(); + + $parametersString = $this->buildParametersString($methodReflection->getParameters(), ['name']); + $returnTypeHint = $this->getMethodReturnType($methodReflection); + } + + if (empty($lazyPublicProperties) && ! $hasParentGet) { + return ''; + } + + $magicGet = <<__initializer__ && $this->__initializer__->__invoke($this, '__get', [$name]); +EOT; + + if ($returnTypeHint === ': void') { + $magicGet .= "\n return;"; + } else { + $magicGet .= "\n return \$this->\$name;"; + } + + $magicGet .= <<<'EOT' + + } + + +EOT; + } + + if ($hasParentGet) { + $magicGet .= <<<'EOT' + $this->__initializer__ && $this->__initializer__->__invoke($this, '__get', [$name]); +EOT; + + if ($returnTypeHint === ': void') { + $magicGet .= <<<'EOT' + + parent::__get($name); + return; +EOT; + } else { + $magicGet .= <<<'EOT' + + return parent::__get($name); +EOT; + } + } else { + $magicGet .= sprintf(<<getLazyLoadedPublicPropertiesNames($class); + $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; + } 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. + * + * @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 + */ + 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 @@ +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); + } +} -- cgit v1.2.3