????PK!-persistence/composer.jsonnu[{ "name": "doctrine/persistence", "type": "library", "description": "The Doctrine Persistence project is a set of shared interfaces and functionality that the different Doctrine object mappers share.", "keywords": [ "persistence", "object", "mapper", "orm", "odm" ], "homepage": "https://www.doctrine-project.org/projects/persistence.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.2 || ^8.0", "doctrine/event-manager": "^1 || ^2", "psr/cache": "^1.0 || ^2.0 || ^3.0" }, "require-dev": { "composer/package-versions-deprecated": "^1.11", "phpstan/phpstan": "1.9.4", "phpstan/phpstan-phpunit": "^1", "phpstan/phpstan-strict-rules": "^1.1", "doctrine/coding-standard": "^11", "doctrine/common": "^3.0", "phpunit/phpunit": "^8.5 || ^9.5", "symfony/cache": "^4.4 || ^5.4 || ^6.0", "vimeo/psalm": "4.30.0 || 5.3.0" }, "conflict": { "doctrine/common": "<2.10" }, "autoload": { "psr-4": { "Doctrine\\Persistence\\": "src/Persistence" } }, "autoload-dev": { "psr-4": { "Doctrine\\Tests\\": "tests", "Doctrine\\Tests_PHP74\\": "tests_php74", "Doctrine\\Tests_PHP81\\": "tests_php81" } }, "config": { "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true, "composer/package-versions-deprecated": true } } } PK!nm``persistence/CONTRIBUTING.mdnu[# Circular dependency This package has a development dependency on `doctrine/common`, which has a regular dependency on this package (`^2.0` at the time of writing). To be able to use Composer, one has to let it understand that this is version 2 (even when developing on 3.0.x), as follows: ```shell COMPOSER_ROOT_VERSION=2.0 composer update -v ``` PK!9))persistence/LICENSEnu[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. PK!l9b&persistence/UPGRADE.mdnu[Note about upgrading: Doctrine uses static and runtime mechanisms to raise awareness about deprecated code. - Use of `@deprecated` docblock that is detected by IDEs (like PHPStorm) or Static Analysis tools (like Psalm, phpstan) - Use of our low-overhead runtime deprecation API, details: https://github.com/doctrine/deprecations/ # Upgrade to 3.3 ## Added method `ObjectManager::isUninitializedObject()` Classes implementing `Doctrine\Persistence\ObjectManager` should implement the new method. This method will be added to the interface in 4.0. # Upgrade to 3.1 ## Deprecated `RuntimePublicReflectionProperty` Use `RuntimeReflectionProperty` instead. # Upgrade to 3.0 ## Removed `OnClearEventArgs::clearsAllEntities()` and `OnClearEventArgs::getEntityClass()` These methods only make sense when partially clearing the object manager, which is no longer possible. The second argument of the constructor of `OnClearEventArgs` is removed as well. ## BC Break: removed `ObjectManagerAware` Implement active record style functionality directly in your application, by using a `postLoad` event. ## BC Break: removed `AnnotationDriver` Use `ColocatedMappingDriver` instead. ## BC Break: Removed `MappingException::pathRequired()` Use `MappingException::pathRequiredForDriver()` instead. ## BC Break: removed `LifecycleEventArgs::getEntity()` Use `LifecycleEventArgs::getObject()` instead. ## BC Break: removed support for short namespace aliases - `AbstractClassMetadataFactory::getFqcnFromAlias()` is removed. - `ClassMetadataFactory` methods now require their `$className` argument to be an actual FQCN. ## BC Break: removed `ObjectManager::merge()` `ObjectManagerDecorator::merge()` is removed without replacement. ## BC Break: removed support for `doctrine/cache` Removed support for using doctrine/cache for metadata caching. The `setCacheDriver` and `getCacheDriver` methods have been removed from `Doctrine\Persistence\Mapping\AbstractMetadata`. Please use `getCache` and `setCache` with a PSR-6 implementation instead. ## BC Break: changed signatures `$objectName` has been dropped from the signature of `ObjectManager::clear()`. ```diff - public function clear($objectName = null) + public function clear(): void ``` Also, native parameter type declarations have been added on all public APIs. Native return type declarations have not been added so that it is possible to implement types compatible with both 2.x and 3.x. ## BC Break: Removed `PersistentObject` Please implement this functionality directly in your application if you want ActiveRecord style functionality. # Upgrade to 2.5 ## Deprecated `OnClearEventArgs::clearsAllEntities()` and `OnClearEventArgs::getEntityClass()` These methods only make sense when partially clearing the object manager, which is deprecated. Passing a second argument to the constructor of `OnClearEventArgs` is deprecated as well. ## Deprecated `ObjectManagerAware` Along with deprecating `PersistentObject`, deprecating `ObjectManagerAware` means deprecating support for active record, which already came with a word of warning. Please implement this directly in your application with a `postLoad` event if you need active record style functionality. ## Deprecated `MappingException::pathRequired()` `MappingException::pathRequiredForDriver()` should be used instead. # Upgrade to 2.4 ## Deprecated `AnnotationDriver` Since attributes were introduced in PHP 8.0, annotations are deprecated. `AnnotationDriver` is an abstract class that is used when implementing concrete annotation drivers in dependent packages. It is deprecated in favor of using `ColocatedMappingDriver` to implement both annotation and attribute based drivers. This will involve implementing `isTransient()` as well as `__construct()` and `getReader()` to retain backward compatibility. # Upgrade to 2.3 ## Deprecated using short namespace alias syntax in favor of `::class` syntax. Before: ```php $objectManager->find('MyPackage:MyClass', $id); $objectManager->createQuery('SELECT u FROM MyPackage:MyClass'); ``` After: ```php $objectManager->find(MyClass::class, $id); $objectManager->createQuery('SELECT u FROM '. MyClass::class); ``` # Upgrade to 2.2 ## Deprecated `doctrine/cache` usage for metadata caching The `setCacheDriver` and `getCacheDriver` methods in `Doctrine\Persistence\Mapping\AbstractMetadata` have been deprecated. Please use `getCache` and `setCache` with a PSR-6 implementation instead. Note that even after switching to PSR-6, `getCacheDriver` will return a cache instance that wraps the PSR-6 cache. Note that if you use a custom implementation of doctrine/cache, the library may not be able to provide a forward compatibility layer. The cache implementation MUST extend the `Doctrine\Common\Cache\CacheProvider` class. # Upgrade to 1.2 ## Deprecated `ObjectManager::merge()` and `ObjectManager::detach()` Please handle merge operations in your application, and use `ObjectManager::clear()` instead. ## Deprecated `PersistentObject` Please implement this functionality directly in your application if you want ActiveRecord style functionality. PK!af9uhhpersistence/README.mdnu[# Doctrine Persistence [![GitHub Actions][GA 3.3 image]][GA 3.3] [![Code Coverage][Coverage 3.3 image]][CodeCov 3.3] The Doctrine Persistence project is a library that provides common abstractions for object mapper persistence. ## More resources: * [Website](https://www.doctrine-project.org/) * [Documentation](https://www.doctrine-project.org/projects/doctrine-persistence/en/latest/index.html) * [Downloads](https://github.com/doctrine/persistence/releases) [Coverage 3.3 image]: https://codecov.io/gh/doctrine/persistence/branch/3.3.x/graph/badge.svg [CodeCov 3.3]: https://codecov.io/gh/doctrine/persistence/branch/3.3.x [GA 3.3 image]: https://github.com/doctrine/persistence/workflows/Continuous%20Integration/badge.svg?branch=3.3.x [GA 3.3]: https://github.com/doctrine/persistence/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A3.3.x PK!6gA227persistence/src/Persistence/AbstractManagerRegistry.phpnu[ */ private $connections; /** @var array */ private $managers; /** @var string */ private $defaultConnection; /** @var string */ private $defaultManager; /** * @var string * @psalm-var class-string */ private $proxyInterfaceName; /** * @param array $connections * @param array $managers * @psalm-param class-string $proxyInterfaceName */ public function __construct( string $name, array $connections, array $managers, string $defaultConnection, string $defaultManager, string $proxyInterfaceName ) { $this->name = $name; $this->connections = $connections; $this->managers = $managers; $this->defaultConnection = $defaultConnection; $this->defaultManager = $defaultManager; $this->proxyInterfaceName = $proxyInterfaceName; } /** * Fetches/creates the given services. * * A service in this context is connection or a manager instance. * * @param string $name The name of the service. * * @return ObjectManager The instance of the given service. */ abstract protected function getService(string $name); /** * Resets the given services. * * A service in this context is connection or a manager instance. * * @param string $name The name of the service. * * @return void */ abstract protected function resetService(string $name); /** * Gets the name of the registry. * * @return string */ public function getName() { return $this->name; } /** * {@inheritdoc} */ public function getConnection(?string $name = null) { if ($name === null) { $name = $this->defaultConnection; } if (! isset($this->connections[$name])) { throw new InvalidArgumentException( sprintf('Doctrine %s Connection named "%s" does not exist.', $this->name, $name) ); } return $this->getService($this->connections[$name]); } /** * {@inheritdoc} */ public function getConnectionNames() { return $this->connections; } /** * {@inheritdoc} */ public function getConnections() { $connections = []; foreach ($this->connections as $name => $id) { $connections[$name] = $this->getService($id); } return $connections; } /** * {@inheritdoc} */ public function getDefaultConnectionName() { return $this->defaultConnection; } /** * {@inheritdoc} */ public function getDefaultManagerName() { return $this->defaultManager; } /** * {@inheritdoc} * * @throws InvalidArgumentException */ public function getManager(?string $name = null) { if ($name === null) { $name = $this->defaultManager; } if (! isset($this->managers[$name])) { throw new InvalidArgumentException( sprintf('Doctrine %s Manager named "%s" does not exist.', $this->name, $name) ); } return $this->getService($this->managers[$name]); } /** * {@inheritDoc} */ public function getManagerForClass(string $class) { $proxyClass = new ReflectionClass($class); if ($proxyClass->isAnonymous()) { return null; } if ($proxyClass->implementsInterface($this->proxyInterfaceName)) { $parentClass = $proxyClass->getParentClass(); if ($parentClass === false) { return null; } $class = $parentClass->getName(); } foreach ($this->managers as $id) { $manager = $this->getService($id); if (! $manager->getMetadataFactory()->isTransient($class)) { return $manager; } } return null; } /** * {@inheritdoc} */ public function getManagerNames() { return $this->managers; } /** * {@inheritdoc} */ public function getManagers() { $managers = []; foreach ($this->managers as $name => $id) { $manager = $this->getService($id); $managers[$name] = $manager; } return $managers; } /** * {@inheritdoc} */ public function getRepository( string $persistentObject, ?string $persistentManagerName = null ) { return $this ->selectManager($persistentObject, $persistentManagerName) ->getRepository($persistentObject); } /** * {@inheritdoc} */ public function resetManager(?string $name = null) { if ($name === null) { $name = $this->defaultManager; } if (! isset($this->managers[$name])) { throw new InvalidArgumentException(sprintf('Doctrine %s Manager named "%s" does not exist.', $this->name, $name)); } // force the creation of a new document manager // if the current one is closed $this->resetService($this->managers[$name]); return $this->getManager($name); } /** @psalm-param class-string $persistentObject */ private function selectManager( string $persistentObject, ?string $persistentManagerName = null ): ObjectManager { if ($persistentManagerName !== null) { return $this->getManager($persistentManagerName); } return $this->getManagerForClass($persistentObject) ?? $this->getManager(); } } PK!W_mm-persistence/src/Persistence/ObjectManager.phpnu[find($id). * * @param string $className The class name of the object to find. * @param mixed $id The identity of the object to find. * @psalm-param class-string $className * * @return object|null The found object. * @psalm-return T|null * * @template T of object */ public function find(string $className, $id); /** * Tells the ObjectManager to make an instance managed and persistent. * * The object will be entered into the database as a result of the flush operation. * * NOTE: The persist operation always considers objects that are not yet known to * this ObjectManager as NEW. Do not pass detached objects to the persist operation. * * @param object $object The instance to make managed and persistent. * * @return void */ public function persist(object $object); /** * Removes an object instance. * * A removed object will be removed from the database as a result of the flush operation. * * @param object $object The object instance to remove. * * @return void */ public function remove(object $object); /** * Clears the ObjectManager. All objects that are currently managed * by this ObjectManager become detached. * * @return void */ public function clear(); /** * Detaches an object from the ObjectManager, causing a managed object to * become detached. Unflushed changes made to the object if any * (including removal of the object), will not be synchronized to the database. * Objects which previously referenced the detached object will continue to * reference it. * * @param object $object The object to detach. * * @return void */ public function detach(object $object); /** * Refreshes the persistent state of an object from the database, * overriding any local changes that have not yet been persisted. * * @param object $object The object to refresh. * * @return void */ public function refresh(object $object); /** * Flushes all changes to objects that have been queued up to now to the database. * This effectively synchronizes the in-memory state of managed objects with the * database. * * @return void */ public function flush(); /** * Gets the repository for a class. * * @psalm-param class-string $className * * @psalm-return ObjectRepository * * @template T of object */ public function getRepository(string $className); /** * Returns the ClassMetadata descriptor for a class. * * The class name must be the fully-qualified class name without a leading backslash * (as it is returned by get_class($obj)). * * @psalm-param class-string $className * * @psalm-return ClassMetadata * * @template T of object */ public function getClassMetadata(string $className); /** * Gets the metadata factory used to gather the metadata of classes. * * @psalm-return ClassMetadataFactory> */ public function getMetadataFactory(); /** * Helper method to initialize a lazy loading proxy or persistent collection. * * This method is a no-op for other objects. * * @return void */ public function initializeObject(object $obj); /** * Checks if the object is part of the current UnitOfWork and therefore managed. * * @return bool */ public function contains(object $object); } PK!  0persistence/src/Persistence/ObjectRepository.phpnu[ The objects. * @psalm-return T[] */ public function findAll(); /** * Finds objects by a set of criteria. * * Optionally sorting and limiting details can be passed. An implementation may throw * an UnexpectedValueException if certain values of the sorting or limiting details are * not supported. * * @param array $criteria * @param array|null $orderBy * @psalm-param array|null $orderBy * * @return array The objects. * @psalm-return T[] * * @throws UnexpectedValueException */ public function findBy( array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null ); /** * Finds a single object by a set of criteria. * * @param array $criteria The criteria. * * @return object|null The object. * @psalm-return T|null */ public function findOneBy(array $criteria); /** * Returns the class name of the object managed by the repository. * * @psalm-return class-string */ public function getClassName(); } PK!c?persistence/src/Persistence/Mapping/StaticReflectionService.phpnu[persistence/src/Persistence/Mapping/ProxyClassNameResolver.phpnu[>|class-string $className * * @psalm-return class-string * * @template T of object */ public function resolveClassName(string $className): string; } PK!Ұpp<persistence/src/Persistence/Mapping/ClassMetadataFactory.phpnu[ */ public function getAllMetadata(); /** * Gets the class metadata descriptor for a class. * * @param class-string $className The name of the class. * * @return ClassMetadata * @psalm-return T */ public function getMetadataFor(string $className); /** * Checks whether the factory has the metadata for a class loaded already. * * @param class-string $className * * @return bool TRUE if the metadata of the class in question is already loaded, FALSE otherwise. */ public function hasMetadataFor(string $className); /** * Sets the metadata descriptor for a specific class. * * @param class-string $className * @psalm-param T $class * * @return void */ public function setMetadataFor(string $className, ClassMetadata $class); /** * Returns whether the class with the specified name should have its metadata loaded. * This is only the case if it is either mapped directly or as a MappedSuperclass. * * @psalm-param class-string $className * * @return bool */ public function isTransient(string $className); } PK!=AJI I @persistence/src/Persistence/Mapping/RuntimeReflectionService.phpnu[supportsTypedPropertiesWorkaround = version_compare(phpversion(), '7.4.0') >= 0; } /** * {@inheritDoc} */ public function getParentClasses(string $class) { if (! class_exists($class)) { throw MappingException::nonExistingClass($class); } $parents = class_parents($class); assert($parents !== false); return $parents; } /** * {@inheritDoc} */ public function getClassShortName(string $class) { $reflectionClass = new ReflectionClass($class); return $reflectionClass->getShortName(); } /** * {@inheritDoc} */ public function getClassNamespace(string $class) { $reflectionClass = new ReflectionClass($class); return $reflectionClass->getNamespaceName(); } /** * @psalm-param class-string $class * * @return ReflectionClass * @psalm-return ReflectionClass * * @template T of object */ public function getClass(string $class) { return new ReflectionClass($class); } /** * {@inheritDoc} */ public function getAccessibleProperty(string $class, string $property) { $reflectionProperty = new RuntimeReflectionProperty($class, $property); if ($this->supportsTypedPropertiesWorkaround && ! array_key_exists($property, $this->getClass($class)->getDefaultProperties())) { $reflectionProperty = new TypedNoDefaultReflectionProperty($class, $property); } $reflectionProperty->setAccessible(true); return $reflectionProperty; } /** * {@inheritDoc} */ public function hasPublicMethod(string $class, string $method) { try { $reflectionMethod = new ReflectionMethod($class, $method); } catch (ReflectionException $e) { return false; } return $reflectionMethod->isPublic(); } } PK!*99Dpersistence/src/Persistence/Mapping/AbstractClassMetadataFactory.phpnu[ */ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory { /** * Salt used by specific Object Manager implementation. * * @var string */ protected $cacheSalt = '__CLASSMETADATA__'; /** @var CacheItemPoolInterface|null */ private $cache; /** * @var array * @psalm-var CMTemplate[] */ private $loadedMetadata = []; /** @var bool */ protected $initialized = false; /** @var ReflectionService|null */ private $reflectionService = null; /** @var ProxyClassNameResolver|null */ private $proxyClassNameResolver = null; public function setCache(CacheItemPoolInterface $cache): void { $this->cache = $cache; } final protected function getCache(): ?CacheItemPoolInterface { return $this->cache; } /** * Returns an array of all the loaded metadata currently in memory. * * @return ClassMetadata[] * @psalm-return CMTemplate[] */ public function getLoadedMetadata() { return $this->loadedMetadata; } /** * {@inheritDoc} */ public function getAllMetadata() { if (! $this->initialized) { $this->initialize(); } $driver = $this->getDriver(); $metadata = []; foreach ($driver->getAllClassNames() as $className) { $metadata[] = $this->getMetadataFor($className); } return $metadata; } public function setProxyClassNameResolver(ProxyClassNameResolver $resolver): void { $this->proxyClassNameResolver = $resolver; } /** * Lazy initialization of this stuff, especially the metadata driver, * since these are not needed at all when a metadata cache is active. * * @return void */ abstract protected function initialize(); /** * Returns the mapping driver implementation. * * @return MappingDriver */ abstract protected function getDriver(); /** * Wakes up reflection after ClassMetadata gets unserialized from cache. * * @psalm-param CMTemplate $class * * @return void */ abstract protected function wakeupReflection( ClassMetadata $class, ReflectionService $reflService ); /** * Initializes Reflection after ClassMetadata was constructed. * * @psalm-param CMTemplate $class * * @return void */ abstract protected function initializeReflection( ClassMetadata $class, ReflectionService $reflService ); /** * Checks whether the class metadata is an entity. * * This method should return false for mapped superclasses or embedded classes. * * @psalm-param CMTemplate $class * * @return bool */ abstract protected function isEntity(ClassMetadata $class); /** * Removes the prepended backslash of a class string to conform with how php outputs class names * * @psalm-param class-string $className * * @psalm-return class-string */ private function normalizeClassName(string $className): string { return ltrim($className, '\\'); } /** * {@inheritDoc} * * @throws ReflectionException * @throws MappingException */ public function getMetadataFor(string $className) { $className = $this->normalizeClassName($className); if (isset($this->loadedMetadata[$className])) { return $this->loadedMetadata[$className]; } if (class_exists($className, false) && (new ReflectionClass($className))->isAnonymous()) { throw MappingException::classIsAnonymous($className); } if (! class_exists($className, false) && strpos($className, ':') !== false) { throw MappingException::nonExistingClass($className); } $realClassName = $this->getRealClass($className); if (isset($this->loadedMetadata[$realClassName])) { // We do not have the alias name in the map, include it return $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; } try { if ($this->cache !== null) { $cached = $this->cache->getItem($this->getCacheKey($realClassName))->get(); if ($cached instanceof ClassMetadata) { /** @psalm-var CMTemplate $cached */ $this->loadedMetadata[$realClassName] = $cached; $this->wakeupReflection($cached, $this->getReflectionService()); } else { $loadedMetadata = $this->loadMetadata($realClassName); $classNames = array_combine( array_map([$this, 'getCacheKey'], $loadedMetadata), $loadedMetadata ); foreach ($this->cache->getItems(array_keys($classNames)) as $item) { if (! isset($classNames[$item->getKey()])) { continue; } $item->set($this->loadedMetadata[$classNames[$item->getKey()]]); $this->cache->saveDeferred($item); } $this->cache->commit(); } } else { $this->loadMetadata($realClassName); } } catch (MappingException $loadingException) { $fallbackMetadataResponse = $this->onNotFoundMetadata($realClassName); if ($fallbackMetadataResponse === null) { throw $loadingException; } $this->loadedMetadata[$realClassName] = $fallbackMetadataResponse; } if ($className !== $realClassName) { // We do not have the alias name in the map, include it $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; } return $this->loadedMetadata[$className]; } /** * {@inheritDoc} */ public function hasMetadataFor(string $className) { $className = $this->normalizeClassName($className); return isset($this->loadedMetadata[$className]); } /** * Sets the metadata descriptor for a specific class. * * NOTE: This is only useful in very special cases, like when generating proxy classes. * * @psalm-param class-string $className * @psalm-param CMTemplate $class * * @return void */ public function setMetadataFor(string $className, ClassMetadata $class) { $this->loadedMetadata[$this->normalizeClassName($className)] = $class; } /** * Gets an array of parent classes for the given entity class. * * @psalm-param class-string $name * * @return string[] * @psalm-return list */ protected function getParentClasses(string $name) { // Collect parent classes, ignoring transient (not-mapped) classes. $parentClasses = []; foreach (array_reverse($this->getReflectionService()->getParentClasses($name)) as $parentClass) { if ($this->getDriver()->isTransient($parentClass)) { continue; } $parentClasses[] = $parentClass; } return $parentClasses; } /** * Loads the metadata of the class in question and all it's ancestors whose metadata * is still not loaded. * * Important: The class $name does not necessarily exist at this point here. * Scenarios in a code-generation setup might have access to XML/YAML * Mapping files without the actual PHP code existing here. That is why the * {@see \Doctrine\Persistence\Mapping\ReflectionService} interface * should be used for reflection. * * @param string $name The name of the class for which the metadata should get loaded. * @psalm-param class-string $name * * @return array * @psalm-return list */ protected function loadMetadata(string $name) { if (! $this->initialized) { $this->initialize(); } $loaded = []; $parentClasses = $this->getParentClasses($name); $parentClasses[] = $name; // Move down the hierarchy of parent classes, starting from the topmost class $parent = null; $rootEntityFound = false; $visited = []; $reflService = $this->getReflectionService(); foreach ($parentClasses as $className) { if (isset($this->loadedMetadata[$className])) { $parent = $this->loadedMetadata[$className]; if ($this->isEntity($parent)) { $rootEntityFound = true; array_unshift($visited, $className); } continue; } $class = $this->newClassMetadataInstance($className); $this->initializeReflection($class, $reflService); $this->doLoadMetadata($class, $parent, $rootEntityFound, $visited); $this->loadedMetadata[$className] = $class; $parent = $class; if ($this->isEntity($class)) { $rootEntityFound = true; array_unshift($visited, $className); } $this->wakeupReflection($class, $reflService); $loaded[] = $className; } return $loaded; } /** * Provides a fallback hook for loading metadata when loading failed due to reflection/mapping exceptions * * Override this method to implement a fallback strategy for failed metadata loading * * @return ClassMetadata|null * @psalm-return CMTemplate|null */ protected function onNotFoundMetadata(string $className) { return null; } /** * Actually loads the metadata from the underlying metadata. * * @param bool $rootEntityFound True when there is another entity (non-mapped superclass) class above the current class in the PHP class hierarchy. * @param list $nonSuperclassParents All parent class names that are not marked as mapped superclasses, with the direct parent class being the first and the root entity class the last element. * @psalm-param CMTemplate $class * @psalm-param CMTemplate|null $parent * * @return void */ abstract protected function doLoadMetadata( ClassMetadata $class, ?ClassMetadata $parent, bool $rootEntityFound, array $nonSuperclassParents ); /** * Creates a new ClassMetadata instance for the given class name. * * @psalm-param class-string $className * * @return ClassMetadata * @psalm-return CMTemplate * * @template T of object */ abstract protected function newClassMetadataInstance(string $className); /** * {@inheritDoc} */ public function isTransient(string $className) { if (! $this->initialized) { $this->initialize(); } if (class_exists($className, false) && (new ReflectionClass($className))->isAnonymous()) { return false; } if (! class_exists($className, false) && strpos($className, ':') !== false) { throw MappingException::nonExistingClass($className); } /** @psalm-var class-string $className */ return $this->getDriver()->isTransient($className); } /** * Sets the reflectionService. * * @return void */ public function setReflectionService(ReflectionService $reflectionService) { $this->reflectionService = $reflectionService; } /** * Gets the reflection service associated with this metadata factory. * * @return ReflectionService */ public function getReflectionService() { if ($this->reflectionService === null) { $this->reflectionService = new RuntimeReflectionService(); } return $this->reflectionService; } protected function getCacheKey(string $realClassName): string { return str_replace('\\', '__', $realClassName) . $this->cacheSalt; } /** * Gets the real class name of a class name that could be a proxy. * * @psalm-param class-string>|class-string $class * * @psalm-return class-string * * @template T of object */ private function getRealClass(string $class): string { if ($this->proxyClassNameResolver === null) { $this->createDefaultProxyClassNameResolver(); } assert($this->proxyClassNameResolver !== null); return $this->proxyClassNameResolver->resolveClassName($class); } private function createDefaultProxyClassNameResolver(): void { $this->proxyClassNameResolver = new class implements ProxyClassNameResolver { /** * @psalm-param class-string>|class-string $className * * @psalm-return class-string * * @template T of object */ public function resolveClassName(string $className): string { $pos = strrpos($className, '\\' . Proxy::MARKER . '\\'); if ($pos === false) { /** @psalm-var class-string */ return $className; } /** @psalm-var class-string */ return substr($className, $pos + Proxy::MARKER_LENGTH + 2); } }; } } PK!d9persistence/src/Persistence/Mapping/ReflectionService.phpnu[ $class * * @return ReflectionClass|null * @psalm-return ReflectionClass|null * * @template T of object */ public function getClass(string $class); /** * Returns an accessible property (setAccessible(true)) or null. * * @psalm-param class-string $class * * @return ReflectionProperty|null */ public function getAccessibleProperty(string $class, string $property); /** * Checks if the class have a public method with the given name. * * @psalm-param class-string $class * * @return bool */ public function hasPublicMethod(string $class, string $method); } PK!FAz9persistence/src/Persistence/Mapping/Driver/FileDriver.phpnu[>|null */ protected $classCache; /** @var string */ protected $globalBasename = ''; /** * Initializes a new FileDriver that looks in the given path(s) for mapping * documents and operates in the specified operating mode. * * @param string|array|FileLocator $locator A FileLocator or one/multiple paths * where mapping documents can be found. */ public function __construct($locator, ?string $fileExtension = null) { if ($locator instanceof FileLocator) { $this->locator = $locator; } else { $this->locator = new DefaultFileLocator((array) $locator, $fileExtension); } } /** * Sets the global basename. * * @return void */ public function setGlobalBasename(string $file) { $this->globalBasename = $file; } /** * Retrieves the global basename. * * @return string|null */ public function getGlobalBasename() { return $this->globalBasename; } /** * Gets the element of schema meta data for the class from the mapping file. * This will lazily load the mapping file if it is not loaded yet. * * @psalm-param class-string $className * * @return ClassMetadata The element of schema meta data. * @psalm-return ClassMetadata * * @throws MappingException */ public function getElement(string $className) { if ($this->classCache === null) { $this->initialize(); } if (isset($this->classCache[$className])) { return $this->classCache[$className]; } $result = $this->loadMappingFile($this->locator->findMappingFile($className)); if (! isset($result[$className])) { throw MappingException::invalidMappingFile( $className, str_replace('\\', '.', $className) . $this->locator->getFileExtension() ); } $this->classCache[$className] = $result[$className]; return $result[$className]; } /** * {@inheritDoc} */ public function isTransient(string $className) { if ($this->classCache === null) { $this->initialize(); } if (isset($this->classCache[$className])) { return false; } return ! $this->locator->fileExists($className); } /** * {@inheritDoc} */ public function getAllClassNames() { if ($this->classCache === null) { $this->initialize(); } if ($this->classCache === []) { return $this->locator->getAllClassNames($this->globalBasename); } /** @psalm-var array> $classCache */ $classCache = $this->classCache; /** @var list $keys */ $keys = array_keys($classCache); return array_values(array_unique(array_merge( $keys, $this->locator->getAllClassNames($this->globalBasename) ))); } /** * Loads a mapping file with the given name and returns a map * from class/entity names to their corresponding file driver elements. * * @param string $file The mapping file to load. * * @return ClassMetadata[] * @psalm-return array> */ abstract protected function loadMappingFile(string $file); /** * Initializes the class cache from all the global files. * * Using this feature adds a substantial performance hit to file drivers as * more metadata has to be loaded into memory than might actually be * necessary. This may not be relevant to scenarios where caching of * metadata is in place, however hits very hard in scenarios where no * caching is used. * * @return void */ protected function initialize() { $this->classCache = []; if ($this->globalBasename === null) { return; } foreach ($this->locator->getPaths() as $path) { $file = $path . '/' . $this->globalBasename . $this->locator->getFileExtension(); if (! is_file($file)) { continue; } $this->classCache = array_merge( $this->classCache, $this->loadMappingFile($file) ); } } /** * Retrieves the locator used to discover mapping files by className. * * @return FileLocator */ public function getLocator() { return $this->locator; } /** * Sets the locator used to discover mapping files by className. * * @return void */ public function setLocator(FileLocator $locator) { $this->locator = $locator; } } PK!Epersistence/src/Persistence/Mapping/Driver/ColocatedMappingDriver.phpnu[ */ protected $paths = []; /** * The paths excluded from path where to look for mapping files. * * @var array */ protected $excludePaths = []; /** * The file extension of mapping documents. * * @var string */ protected $fileExtension = '.php'; /** * Cache for getAllClassNames(). * * @var array|null * @psalm-var list|null */ protected $classNames; /** * Appends lookup paths to metadata driver. * * @param array $paths * * @return void */ public function addPaths(array $paths) { $this->paths = array_unique(array_merge($this->paths, $paths)); } /** * Retrieves the defined metadata lookup paths. * * @return array */ public function getPaths() { return $this->paths; } /** * Append exclude lookup paths to metadata driver. * * @param string[] $paths * * @return void */ public function addExcludePaths(array $paths) { $this->excludePaths = array_unique(array_merge($this->excludePaths, $paths)); } /** * Retrieve the defined metadata lookup exclude paths. * * @return array */ public function getExcludePaths() { return $this->excludePaths; } /** * Gets the file extension used to look for mapping files under. * * @return string */ public function getFileExtension() { return $this->fileExtension; } /** * Sets the file extension used to look for mapping files under. * * @return void */ public function setFileExtension(string $fileExtension) { $this->fileExtension = $fileExtension; } /** * {@inheritDoc} * * Returns whether the class with the specified name is transient. Only non-transient * classes, that is entities and mapped superclasses, should have their metadata loaded. * * @psalm-param class-string $className * * @return bool */ abstract public function isTransient(string $className); /** * Gets the names of all mapped classes known to this driver. * * @return string[] The names of all mapped classes known to this driver. * @psalm-return list */ public function getAllClassNames() { if ($this->classNames !== null) { return $this->classNames; } if ($this->paths === []) { throw MappingException::pathRequiredForDriver(static::class); } $classes = []; $includedFiles = []; foreach ($this->paths as $path) { if (! is_dir($path)) { throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); } $iterator = new RegexIterator( new RecursiveIteratorIterator( new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::LEAVES_ONLY ), '/^.+' . preg_quote($this->fileExtension) . '$/i', RecursiveRegexIterator::GET_MATCH ); foreach ($iterator as $file) { $sourceFile = $file[0]; if (preg_match('(^phar:)i', $sourceFile) === 0) { $sourceFile = realpath($sourceFile); } foreach ($this->excludePaths as $excludePath) { $realExcludePath = realpath($excludePath); assert($realExcludePath !== false); $exclude = str_replace('\\', '/', $realExcludePath); $current = str_replace('\\', '/', $sourceFile); if (strpos($current, $exclude) !== false) { continue 2; } } require_once $sourceFile; $includedFiles[] = $sourceFile; } } $declared = get_declared_classes(); foreach ($declared as $className) { $rc = new ReflectionClass($className); $sourceFile = $rc->getFileName(); if (! in_array($sourceFile, $includedFiles, true) || $this->isTransient($className)) { continue; } $classes[] = $className; } $this->classNames = $classes; return $classes; } } PK!Θ/ Apersistence/src/Persistence/Mapping/Driver/MappingDriverChain.phpnu[ */ private $drivers = []; /** * Gets the default driver. * * @return MappingDriver|null */ public function getDefaultDriver() { return $this->defaultDriver; } /** * Set the default driver. * * @return void */ public function setDefaultDriver(MappingDriver $driver) { $this->defaultDriver = $driver; } /** * Adds a nested driver. * * @return void */ public function addDriver(MappingDriver $nestedDriver, string $namespace) { $this->drivers[$namespace] = $nestedDriver; } /** * Gets the array of nested drivers. * * @return array $drivers */ public function getDrivers() { return $this->drivers; } /** * {@inheritDoc} */ public function loadMetadataForClass(string $className, ClassMetadata $metadata) { foreach ($this->drivers as $namespace => $driver) { if (strpos($className, $namespace) === 0) { $driver->loadMetadataForClass($className, $metadata); return; } } if ($this->defaultDriver !== null) { $this->defaultDriver->loadMetadataForClass($className, $metadata); return; } throw MappingException::classNotFoundInNamespaces($className, array_keys($this->drivers)); } /** * {@inheritDoc} */ public function getAllClassNames() { $classNames = []; $driverClasses = []; foreach ($this->drivers as $namespace => $driver) { $oid = spl_object_hash($driver); if (! isset($driverClasses[$oid])) { $driverClasses[$oid] = $driver->getAllClassNames(); } foreach ($driverClasses[$oid] as $className) { if (strpos($className, $namespace) !== 0) { continue; } $classNames[$className] = true; } } if ($this->defaultDriver !== null) { foreach ($this->defaultDriver->getAllClassNames() as $className) { $classNames[$className] = true; } } return array_keys($classNames); } /** * {@inheritDoc} */ public function isTransient(string $className) { foreach ($this->drivers as $namespace => $driver) { if (strpos($className, $namespace) === 0) { return $driver->isTransient($className); } } if ($this->defaultDriver !== null) { return $this->defaultDriver->isTransient($className); } return true; } } PK!KKApersistence/src/Persistence/Mapping/Driver/SymfonyFileLocator.phpnu[ */ protected $paths = []; /** * A map of mapping directory path to namespace prefix used to expand class shortnames. * * @var array */ protected $prefixes = []; /** * File extension that is searched for. * * @var string|null */ protected $fileExtension; /** * Represents PHP namespace delimiters when looking for files * * @var string */ private $nsSeparator; /** * @param array $prefixes * @param string $nsSeparator String which would be used when converting FQCN * to filename and vice versa. Should not be empty */ public function __construct( array $prefixes, string $fileExtension = '', string $nsSeparator = '.' ) { $this->addNamespacePrefixes($prefixes); $this->fileExtension = $fileExtension; if ($nsSeparator === '') { throw new InvalidArgumentException('Namespace separator should not be empty'); } $this->nsSeparator = $nsSeparator; } /** * Adds Namespace Prefixes. * * @param array $prefixes * * @return void */ public function addNamespacePrefixes(array $prefixes) { $this->prefixes = array_merge($this->prefixes, $prefixes); $this->paths = array_merge($this->paths, array_keys($prefixes)); } /** * Gets Namespace Prefixes. * * @return string[] */ public function getNamespacePrefixes() { return $this->prefixes; } /** * {@inheritDoc} */ public function getPaths() { return $this->paths; } /** * {@inheritDoc} */ public function getFileExtension() { return $this->fileExtension; } /** * Sets the file extension used to look for mapping files under. * * @param string $fileExtension The file extension to set. * * @return void */ public function setFileExtension(string $fileExtension) { $this->fileExtension = $fileExtension; } /** * {@inheritDoc} */ public function fileExists(string $className) { $defaultFileName = str_replace('\\', $this->nsSeparator, $className) . $this->fileExtension; foreach ($this->paths as $path) { if (! isset($this->prefixes[$path])) { // global namespace class if (is_file($path . DIRECTORY_SEPARATOR . $defaultFileName)) { return true; } continue; } $prefix = $this->prefixes[$path]; if (strpos($className, $prefix . '\\') !== 0) { continue; } $filename = $path . '/' . strtr(substr($className, strlen($prefix) + 1), '\\', $this->nsSeparator) . $this->fileExtension; if (is_file($filename)) { return true; } } return false; } /** * {@inheritDoc} */ public function getAllClassNames(?string $globalBasename = null) { if ($this->paths === []) { return []; } $classes = []; foreach ($this->paths as $path) { if (! is_dir($path)) { throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); } $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::LEAVES_ONLY ); foreach ($iterator as $file) { $fileName = $file->getBasename($this->fileExtension); if ($fileName === $file->getBasename() || $fileName === $globalBasename) { continue; } // NOTE: All files found here means classes are not transient! if (isset($this->prefixes[$path])) { // Calculate namespace suffix for given prefix as a relative path from basepath to file path $nsSuffix = strtr( substr($this->realpath($file->getPath()), strlen($this->realpath($path))), $this->nsSeparator, '\\' ); /** @psalm-var class-string */ $class = $this->prefixes[$path] . str_replace(DIRECTORY_SEPARATOR, '\\', $nsSuffix) . '\\' . str_replace($this->nsSeparator, '\\', $fileName); } else { /** @psalm-var class-string */ $class = str_replace($this->nsSeparator, '\\', $fileName); } $classes[] = $class; } } return $classes; } /** * {@inheritDoc} */ public function findMappingFile(string $className) { $defaultFileName = str_replace('\\', $this->nsSeparator, $className) . $this->fileExtension; foreach ($this->paths as $path) { if (! isset($this->prefixes[$path])) { if (is_file($path . DIRECTORY_SEPARATOR . $defaultFileName)) { return $path . DIRECTORY_SEPARATOR . $defaultFileName; } continue; } $prefix = $this->prefixes[$path]; if (strpos($className, $prefix . '\\') !== 0) { continue; } $filename = $path . '/' . strtr(substr($className, strlen($prefix) + 1), '\\', $this->nsSeparator) . $this->fileExtension; if (is_file($filename)) { return $filename; } } $pos = strrpos($className, '\\'); assert(is_int($pos)); throw MappingException::mappingFileNotFound( $className, substr($className, $pos + 1) . $this->fileExtension ); } private function realpath(string $path): string { $realpath = realpath($path); if ($realpath === false) { throw new RuntimeException(sprintf('Could not get realpath for %s', $path)); } return $realpath; } } PK!%%Apersistence/src/Persistence/Mapping/Driver/DefaultFileLocator.phpnu[ */ protected $paths = []; /** * The file extension of mapping documents. * * @var string|null */ protected $fileExtension; /** * Initializes a new FileDriver that looks in the given path(s) for mapping * documents and operates in the specified operating mode. * * @param string|array $paths One or multiple paths where mapping documents * can be found. * @param string|null $fileExtension The file extension of mapping documents, * usually prefixed with a dot. */ public function __construct($paths, ?string $fileExtension = null) { $this->addPaths((array) $paths); $this->fileExtension = $fileExtension; } /** * Appends lookup paths to metadata driver. * * @param array $paths * * @return void */ public function addPaths(array $paths) { $this->paths = array_unique(array_merge($this->paths, $paths)); } /** * Retrieves the defined metadata lookup paths. * * @return array */ public function getPaths() { return $this->paths; } /** * Gets the file extension used to look for mapping files under. * * @return string|null */ public function getFileExtension() { return $this->fileExtension; } /** * Sets the file extension used to look for mapping files under. * * @param string|null $fileExtension The file extension to set. * * @return void */ public function setFileExtension(?string $fileExtension) { $this->fileExtension = $fileExtension; } /** * {@inheritDoc} */ public function findMappingFile(string $className) { $fileName = str_replace('\\', '.', $className) . $this->fileExtension; // Check whether file exists foreach ($this->paths as $path) { if (is_file($path . DIRECTORY_SEPARATOR . $fileName)) { return $path . DIRECTORY_SEPARATOR . $fileName; } } throw MappingException::mappingFileNotFound($className, $fileName); } /** * {@inheritDoc} */ public function getAllClassNames(string $globalBasename) { if ($this->paths === []) { return []; } $classes = []; foreach ($this->paths as $path) { if (! is_dir($path)) { throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); } $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::LEAVES_ONLY ); foreach ($iterator as $file) { $fileName = $file->getBasename($this->fileExtension); if ($fileName === $file->getBasename() || $fileName === $globalBasename) { continue; } // NOTE: All files found here means classes are not transient! assert(is_string($fileName)); /** @psalm-var class-string */ $class = str_replace('.', '\\', $fileName); $classes[] = $class; } } return $classes; } /** * {@inheritDoc} */ public function fileExists(string $className) { $fileName = str_replace('\\', '.', $className) . $this->fileExtension; // Check whether file exists foreach ($this->paths as $path) { if (is_file($path . DIRECTORY_SEPARATOR . $fileName)) { return true; } } return false; } } PK!"8persistence/src/Persistence/Mapping/Driver/PHPDriver.phpnu[ */ protected $metadata; /** @param string|array|FileLocator $locator */ public function __construct($locator) { parent::__construct($locator, '.php'); } /** * {@inheritDoc} */ public function loadMetadataForClass(string $className, ClassMetadata $metadata) { $this->metadata = $metadata; $this->loadMappingFile($this->locator->findMappingFile($className)); } /** * {@inheritDoc} */ protected function loadMappingFile(string $file) { $metadata = $this->metadata; include $file; return [$metadata->getName() => $metadata]; } } PK!Y:persistence/src/Persistence/Mapping/Driver/FileLocator.phpnu[ * @psalm-return list */ public function getAllClassNames(string $globalBasename); /** * Checks if a file can be found for this class name. * * @return bool */ public function fileExists(string $className); /** * Gets all the paths that this file locator looks for mapping files. * * @return array */ public function getPaths(); /** * Gets the file extension that mapping files are suffixed with. * * @return string|null */ public function getFileExtension(); } PK!s ~hh<persistence/src/Persistence/Mapping/Driver/MappingDriver.phpnu[ $className * @psalm-param ClassMetadata $metadata * * @return void * * @template T of object */ public function loadMetadataForClass(string $className, ClassMetadata $metadata); /** * Gets the names of all mapped classes known to this driver. * * @return array The names of all mapped classes known to this driver. * @psalm-return list */ public function getAllClassNames(); /** * Returns whether the class with the specified name should have its metadata loaded. * This is only the case if it is either mapped as an Entity or a MappedSuperclass. * * @psalm-param class-string $className * * @return bool */ public function isTransient(string $className); } PK!4! >persistence/src/Persistence/Mapping/Driver/StaticPHPDriver.phpnu[ */ private $paths = []; /** * Map of all class names. * * @var array * @psalm-var list */ private $classNames; /** @param array|string $paths */ public function __construct($paths) { $this->addPaths((array) $paths); } /** * @param array $paths * * @return void */ public function addPaths(array $paths) { $this->paths = array_unique(array_merge($this->paths, $paths)); } /** * {@inheritdoc} */ public function loadMetadataForClass(string $className, ClassMetadata $metadata) { $className::loadMetadata($metadata); } /** * {@inheritDoc} * * @todo Same code exists in ColocatedMappingDriver, should we re-use it * somehow or not worry about it? */ public function getAllClassNames() { if ($this->classNames !== null) { return $this->classNames; } if ($this->paths === []) { throw MappingException::pathRequiredForDriver(static::class); } $classes = []; $includedFiles = []; foreach ($this->paths as $path) { if (! is_dir($path)) { throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); } $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::LEAVES_ONLY ); foreach ($iterator as $file) { if ($file->getBasename('.php') === $file->getBasename()) { continue; } $sourceFile = realpath($file->getPathName()); require_once $sourceFile; $includedFiles[] = $sourceFile; } } $declared = get_declared_classes(); foreach ($declared as $className) { $rc = new ReflectionClass($className); $sourceFile = $rc->getFileName(); if (! in_array($sourceFile, $includedFiles, true) || $this->isTransient($className)) { continue; } $classes[] = $className; } $this->classNames = $classes; return $classes; } /** * {@inheritdoc} */ public function isTransient(string $className) { return ! method_exists($className, 'loadMetadata'); } } PK![555persistence/src/Persistence/Mapping/ClassMetadata.phpnu[ */ public function getName(); /** * Gets the mapped identifier field name. * * The returned structure is an array of the identifier field names. * * @return array * @psalm-return list */ public function getIdentifier(); /** * Gets the ReflectionClass instance for this mapped class. * * @return ReflectionClass */ public function getReflectionClass(); /** * Checks if the given field name is a mapped identifier for this class. * * @return bool */ public function isIdentifier(string $fieldName); /** * Checks if the given field is a mapped property for this class. * * @return bool */ public function hasField(string $fieldName); /** * Checks if the given field is a mapped association for this class. * * @return bool */ public function hasAssociation(string $fieldName); /** * Checks if the given field is a mapped single valued association for this class. * * @return bool */ public function isSingleValuedAssociation(string $fieldName); /** * Checks if the given field is a mapped collection valued association for this class. * * @return bool */ public function isCollectionValuedAssociation(string $fieldName); /** * A numerically indexed list of field names of this persistent class. * * This array includes identifier fields if present on this class. * * @return array */ public function getFieldNames(); /** * Returns an array of identifier field names numerically indexed. * * @return array */ public function getIdentifierFieldNames(); /** * Returns a numerically indexed list of association names of this persistent class. * * This array includes identifier associations if present on this class. * * @return array */ public function getAssociationNames(); /** * Returns a type name of this field. * * This type names can be implementation specific but should at least include the php types: * integer, string, boolean, float/double, datetime. * * @return string|null */ public function getTypeOfField(string $fieldName); /** * Returns the target class name of the given association. * * @return string|null * @psalm-return class-string|null */ public function getAssociationTargetClass(string $assocName); /** * Checks if the association is the inverse side of a bidirectional association. * * @return bool */ public function isAssociationInverseSide(string $assocName); /** * Returns the target field of the owning side of the association. * * @return string */ public function getAssociationMappedByTargetField(string $assocName); /** * Returns the identifier of this object as an array with field name as key. * * Has to return an empty array if no identifier isset. * * @return array */ public function getIdentifierValues(object $object); } PK!̟' ' 8persistence/src/Persistence/Mapping/MappingException.phpnu[ $namespaces * * @return self */ public static function classNotFoundInNamespaces( string $className, array $namespaces ) { return new self(sprintf( "The class '%s' was not found in the chain configured namespaces %s", $className, implode(', ', $namespaces) )); } /** @param class-string $driverClassName */ public static function pathRequiredForDriver(string $driverClassName): self { return new self(sprintf( 'Specifying the paths to your entities is required when using %s to retrieve all class names.', $driverClassName )); } /** @return self */ public static function fileMappingDriversRequireConfiguredDirectoryPath( ?string $path = null ) { if ($path !== null) { $path = '[' . $path . ']'; } return new self(sprintf( 'File mapping drivers must have a valid directory path, ' . 'however the given path %s seems to be incorrect!', (string) $path )); } /** @return self */ public static function mappingFileNotFound(string $entityName, string $fileName) { return new self(sprintf( "No mapping file found named '%s' for class '%s'.", $fileName, $entityName )); } /** @return self */ public static function invalidMappingFile(string $entityName, string $fileName) { return new self(sprintf( "Invalid mapping file '%s' for class '%s'.", $fileName, $entityName )); } /** @return self */ public static function nonExistingClass(string $className) { return new self(sprintf("Class '%s' does not exist", $className)); } /** @param class-string $className */ public static function classIsAnonymous(string $className): self { return new self(sprintf('Class "%s" is anonymous', $className)); } } PK!΍LL6persistence/src/Persistence/ObjectManagerDecorator.phpnu[wrapped->find($className, $id); } public function persist(object $object) { $this->wrapped->persist($object); } public function remove(object $object) { $this->wrapped->remove($object); } public function clear(): void { $this->wrapped->clear(); } public function detach(object $object) { $this->wrapped->detach($object); } public function refresh(object $object) { $this->wrapped->refresh($object); } public function flush() { $this->wrapped->flush(); } /** * {@inheritdoc} */ public function getRepository(string $className) { return $this->wrapped->getRepository($className); } /** * {@inheritdoc} */ public function getClassMetadata(string $className) { return $this->wrapped->getClassMetadata($className); } /** @psalm-return ClassMetadataFactory> */ public function getMetadataFactory() { return $this->wrapped->getMetadataFactory(); } public function initializeObject(object $obj) { $this->wrapped->initializeObject($obj); } /** * {@inheritdoc} */ public function contains(object $object) { return $this->wrapped->contains($object); } } PK!#4a /persistence/src/Persistence/ManagerRegistry.phpnu[ An array of ObjectManager instances */ public function getManagers(); /** * Resets a named object manager. * * This method is useful when an object manager has been closed * because of a rollbacked transaction AND when you think that * it makes sense to get a new one to replace the closed one. * * Be warned that you will get a brand new object manager as * the existing one is not useable anymore. This means that any * other object with a dependency on this object manager will * hold an obsolete reference. You can inject the registry instead * to avoid this problem. * * @param string|null $name The object manager name (null for the default one). * * @return ObjectManager */ public function resetManager(?string $name = null); /** * Gets all object manager names and associated service IDs. A service ID * is a string that allows to obtain an object manager, typically from a * PSR-11 container. * * @return array An array with object manager names as keys, * and service IDs as values. */ public function getManagerNames(); /** * Gets the ObjectRepository for a persistent object. * * @param string $persistentObject The name of the persistent object. * @param string|null $persistentManagerName The object manager name (null for the default one). * @psalm-param class-string $persistentObject * * @return ObjectRepository * @psalm-return ObjectRepository * * @template T of object */ public function getRepository( string $persistentObject, ?string $persistentManagerName = null ); /** * Gets the object manager associated with a given class. * * @param class-string $class A persistent object class name. * * @return ObjectManager|null */ public function getManagerForClass(string $class); } PK!ؘk+%persistence/src/Persistence/Proxy.phpnu[ An array of Connection instances. */ public function getConnections(); /** * Gets all connection names. * * @return array An array of connection names. */ public function getConnectionNames(); } PK!zXpersistence/src/Persistence/Reflection/TypedNoDefaultRuntimePublicReflectionProperty.phpnu[getName()] ?? null : parent::getValue(); } /** * {@inheritDoc} * * Avoids triggering lazy loading via `__set` if the provided object * is a {@see \Doctrine\Common\Proxy\Proxy}. * * @link https://bugs.php.net/bug.php?id=63463 * * @param object|null $object * @param mixed $value * * @return void */ #[ReturnTypeWillChange] public function setValue($object, $value = null) { if (! ($object instanceof Proxy && ! $object->__isInitialized())) { parent::setValue($object, $value); return; } $originalInitializer = $object->__getInitializer(); $object->__setInitializer(null); parent::setValue($object, $value); $object->__setInitializer($originalInitializer); } } PK!7Dpersistence/src/Persistence/Reflection/RuntimeReflectionProperty.phpnu[key = $this->isPrivate() ? "\0" . ltrim($class, '\\') . "\0" . $name : ($this->isProtected() ? "\0*\0" . $name : $name); } /** * {@inheritDoc} * * @return mixed */ #[ReturnTypeWillChange] public function getValue($object = null) { if ($object === null) { return parent::getValue($object); } return ((array) $object)[$this->key] ?? null; } /** * {@inheritDoc} * * @param object|null $object * @param mixed $value * * @return void */ #[ReturnTypeWillChange] public function setValue($object, $value = null) { if (! ($object instanceof Proxy && ! $object->__isInitialized())) { parent::setValue($object, $value); return; } if ($object instanceof CommonProxy) { $originalInitializer = $object->__getInitializer(); $object->__setInitializer(null); parent::setValue($object, $value); $object->__setInitializer($originalInitializer); return; } if (! method_exists($object, '__setInitialized')) { return; } $object->__setInitialized(true); parent::setValue($object, $value); $object->__setInitialized(false); } } PK!oy((Opersistence/src/Persistence/Reflection/TypedNoDefaultReflectionPropertyBase.phpnu[isInitialized($object) ? parent::getValue($object) : null; } /** * {@inheritDoc} * * Works around the problem with setting typed no default properties to * NULL which is not supported, instead unset() to uninitialize. * * @link https://github.com/doctrine/orm/issues/7999 * * @param object|null $object * * @return void */ #[ReturnTypeWillChange] public function setValue($object, $value = null) { if ($value === null && $this->hasType() && ! $this->getType()->allowsNull()) { $propertyName = $this->getName(); $unsetter = function () use ($propertyName): void { unset($this->$propertyName); }; $unsetter = $unsetter->bindTo($object, $this->getDeclaringClass()->getName()); assert($unsetter instanceof Closure); $unsetter(); return; } parent::setValue($object, $value); } } PK!_00Apersistence/src/Persistence/Reflection/EnumReflectionProperty.phpnu[ */ private $enumType; /** @param class-string $enumType */ public function __construct(ReflectionProperty $originalReflectionProperty, string $enumType) { $this->originalReflectionProperty = $originalReflectionProperty; $this->enumType = $enumType; } /** * {@inheritDoc} * * @psalm-external-mutation-free */ public function getDeclaringClass(): ReflectionClass { return $this->originalReflectionProperty->getDeclaringClass(); } /** * {@inheritDoc} * * @psalm-external-mutation-free */ public function getName(): string { return $this->originalReflectionProperty->getName(); } /** * {@inheritDoc} * * @psalm-external-mutation-free */ public function getType(): ?ReflectionType { return $this->originalReflectionProperty->getType(); } /** * {@inheritDoc} */ public function getAttributes(?string $name = null, int $flags = 0): array { return $this->originalReflectionProperty->getAttributes($name, $flags); } /** * {@inheritDoc} * * Converts enum instance to its value. * * @param object|null $object * * @return int|string|int[]|string[]|null */ #[ReturnTypeWillChange] public function getValue($object = null) { if ($object === null) { return null; } $enum = $this->originalReflectionProperty->getValue($object); if ($enum === null) { return null; } return $this->fromEnum($enum); } /** * Converts enum value to enum instance. * * @param object $object * @param mixed $value */ public function setValue($object, $value = null): void { if ($value !== null) { $value = $this->toEnum($value); } $this->originalReflectionProperty->setValue($object, $value); } /** * @param BackedEnum|BackedEnum[] $enum * * @return ($enum is BackedEnum ? (string|int) : (string[]|int[])) */ private function fromEnum($enum) { if (is_array($enum)) { return array_map(static function (BackedEnum $enum) { return $enum->value; }, $enum); } return $enum->value; } /** * @param int|string|int[]|string[]|BackedEnum|BackedEnum[] $value * * @return ($value is int|string|BackedEnum ? BackedEnum : BackedEnum[]) */ private function toEnum($value) { if ($value instanceof BackedEnum) { return $value; } if (is_array($value)) { $v = reset($value); if ($v instanceof BackedEnum) { return $value; } return array_map([$this->enumType, 'from'], $value); } return $this->enumType::from($value); } /** * {@inheritDoc} * * @psalm-external-mutation-free */ public function getModifiers(): int { return $this->originalReflectionProperty->getModifiers(); } /** * {@inheritDoc} * * @psalm-external-mutation-free */ public function getDocComment(): string|false { return $this->originalReflectionProperty->getDocComment(); } } PK!ڬ]]@persistence/src/Persistence/Event/LoadClassMetadataEventArgs.phpnu[ * @template-covariant TObjectManager of ObjectManager */ class LoadClassMetadataEventArgs extends EventArgs { /** * @var ClassMetadata * @psalm-var TClassMetadata */ private $classMetadata; /** * @var ObjectManager * @psalm-var TObjectManager */ private $objectManager; /** * @psalm-param TClassMetadata $classMetadata * @psalm-param TObjectManager $objectManager */ public function __construct(ClassMetadata $classMetadata, ObjectManager $objectManager) { $this->classMetadata = $classMetadata; $this->objectManager = $objectManager; } /** * Retrieves the associated ClassMetadata. * * @return ClassMetadata * @psalm-return TClassMetadata */ public function getClassMetadata() { return $this->classMetadata; } /** * Retrieves the associated ObjectManager. * * @return TObjectManager */ public function getObjectManager() { return $this->objectManager; } } PK!4캙**6persistence/src/Persistence/Event/ManagerEventArgs.phpnu[objectManager = $objectManager; } /** * Retrieves the associated ObjectManager. * * @return ObjectManager * @psalm-return TObjectManager */ public function getObjectManager() { return $this->objectManager; } } PK!O tt6persistence/src/Persistence/Event/OnClearEventArgs.phpnu[objectManager = $objectManager; } /** * Retrieves the associated ObjectManager. * * @return ObjectManager * @psalm-return TObjectManager */ public function getObjectManager() { return $this->objectManager; } } PK!haNj 8persistence/src/Persistence/Event/PreUpdateEventArgs.phpnu[ */ class PreUpdateEventArgs extends LifecycleEventArgs { /** @var array> */ private $entityChangeSet; /** * @param array> $changeSet * @psalm-param TObjectManager $objectManager */ public function __construct(object $entity, ObjectManager $objectManager, array &$changeSet) { parent::__construct($entity, $objectManager); $this->entityChangeSet = &$changeSet; } /** * Retrieves the entity changeset. * * @return array> */ public function getEntityChangeSet() { return $this->entityChangeSet; } /** * Checks if field has a changeset. * * @return bool */ public function hasChangedField(string $field) { return isset($this->entityChangeSet[$field]); } /** * Gets the old value of the changeset of the changed field. * * @return mixed */ public function getOldValue(string $field) { $this->assertValidField($field); return $this->entityChangeSet[$field][0]; } /** * Gets the new value of the changeset of the changed field. * * @return mixed */ public function getNewValue(string $field) { $this->assertValidField($field); return $this->entityChangeSet[$field][1]; } /** * Sets the new value of this field. * * @param mixed $value * * @return void */ public function setNewValue(string $field, $value) { $this->assertValidField($field); $this->entityChangeSet[$field][1] = $value; } /** * Asserts the field exists in changeset. * * @return void * * @throws InvalidArgumentException */ private function assertValidField(string $field) { if (! isset($this->entityChangeSet[$field])) { throw new InvalidArgumentException(sprintf( 'Field "%s" is not a valid field of the entity "%s" in PreUpdateEventArgs.', $field, get_class($this->getObject()) )); } } } PK!'8^^8persistence/src/Persistence/Event/LifecycleEventArgs.phpnu[object = $object; $this->objectManager = $objectManager; } /** * Retrieves the associated object. * * @return object */ public function getObject() { return $this->object; } /** * Retrieves the associated ObjectManager. * * @return ObjectManager * @psalm-return TObjectManager */ public function getObjectManager() { return $this->objectManager; } } PK![7persistence/src/Persistence/PropertyChangedListener.phpnu[ PK!Sddevent-manager/UPGRADE.mdnu[# Upgrade to 2.0 ## Made the `$event` parameter of `EventManager::getListeners()` mandatory When calling `EventManager::getListeners()` you need to specify the event that you want to fetch the listeners for. Call `getAllListeners()` instead if you want to access the listeners of all events. # Upgrade to 1.2 ## Deprecated calling `EventManager::getListeners()` without an event name When calling `EventManager::getListeners()` without an event name, all listeners were returned, keyed by event name. A new method `getAllListeners()` has been added to provide this functionality. It should be used instead. PK!gHHevent-manager/README.mdnu[# Doctrine Event Manager [![Build Status](https://github.com/doctrine/event-manager/workflows/Continuous%20Integration/badge.svg)](https://github.com/doctrine/event-manager/actions) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/doctrine/event-manager/badges/quality-score.png?b=1.2.x)](https://scrutinizer-ci.com/g/doctrine/event-manager/?branch=1.2.x) [![Code Coverage](https://scrutinizer-ci.com/g/doctrine/event-manager/badges/coverage.png?b=1.2.x)](https://scrutinizer-ci.com/g/doctrine/event-manager/?branch=1.2.x) The Doctrine Event Manager is a library that provides a simple event system. ## More resources: * [Website](https://www.doctrine-project.org/) * [Documentation](https://www.doctrine-project.org/projects/doctrine-event-manager/en/latest/) * [Downloads](https://github.com/doctrine/event-manager/releases) PK! 88event-manager/phpstan.neon.distnu[parameters: level: 9 paths: - src/ - tests/ PK!ievent-manager/src/EventArgs.phpnu[ => * * @var array */ private array $listeners = []; /** * Dispatches an event to all registered listeners. * * @param string $eventName The name of the event to dispatch. The name of the event is * the name of the method that is invoked on listeners. * @param EventArgs|null $eventArgs The event arguments to pass to the event handlers/listeners. * If not supplied, the single empty EventArgs instance is used. */ public function dispatchEvent(string $eventName, EventArgs|null $eventArgs = null): void { if (! isset($this->listeners[$eventName])) { return; } $eventArgs ??= EventArgs::getEmptyInstance(); foreach ($this->listeners[$eventName] as $listener) { $listener->$eventName($eventArgs); } } /** * Gets the listeners of a specific event. * * @param string $event The name of the event. * * @return object[] */ public function getListeners(string $event): array { return $this->listeners[$event] ?? []; } /** * Gets all listeners keyed by event name. * * @return array The event listeners for the specified event, or all event listeners. */ public function getAllListeners(): array { return $this->listeners; } /** * Checks whether an event has any registered listeners. */ public function hasListeners(string $event): bool { return ! empty($this->listeners[$event]); } /** * Adds an event listener that listens on the specified events. * * @param string|string[] $events The event(s) to listen on. * @param object $listener The listener object. */ public function addEventListener(string|array $events, object $listener): void { // Picks the hash code related to that listener $hash = spl_object_hash($listener); foreach ((array) $events as $event) { // Overrides listener if a previous one was associated already // Prevents duplicate listeners on same event (same instance only) $this->listeners[$event][$hash] = $listener; } } /** * Removes an event listener from the specified events. * * @param string|string[] $events */ public function removeEventListener(string|array $events, object $listener): void { // Picks the hash code related to that listener $hash = spl_object_hash($listener); foreach ((array) $events as $event) { unset($this->listeners[$event][$hash]); } } /** * Adds an EventSubscriber. * * The subscriber is asked for all the events it is interested in and added * as a listener for these events. */ public function addEventSubscriber(EventSubscriber $subscriber): void { $this->addEventListener($subscriber->getSubscribedEvents(), $subscriber); } /** * Removes an EventSubscriber. * * The subscriber is asked for all the events it is interested in and removed * as a listener for these events. */ public function removeEventSubscriber(EventSubscriber $subscriber): void { $this->removeEventListener($subscriber->getSubscribedEvents(), $subscriber); } } PK!aS%event-manager/src/EventSubscriber.phpnu[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. PK!Tz common/src/Util/ClassUtils.phpnu[>|class-string $className * * @return string * @psalm-return class-string * * @template T of object */ public static function getRealClass($className) { $pos = strrpos($className, '\\' . Proxy::MARKER . '\\'); if ($pos === false) { /** @psalm-var class-string */ return $className; } return substr($className, $pos + Proxy::MARKER_LENGTH + 2); } /** * Gets the real class name of an object (even if its a proxy). * * @param object $object * @psalm-param Proxy|T $object * * @return string * @psalm-return class-string * * @template T of object */ public static function getClass($object) { return self::getRealClass(get_class($object)); } /** * Gets the real parent class name of a class or object. * * @param string $className * @psalm-param class-string $className * * @return string * @psalm-return class-string */ public static function getParentClass($className) { return get_parent_class(self::getRealClass($className)); } /** * Creates a new reflection class. * * @param string $className * @psalm-param class-string $className * * @return ReflectionClass */ public static function newReflectionClass($className) { return new ReflectionClass(self::getRealClass($className)); } /** * Creates a new reflection object. * * @param object $object * * @return ReflectionClass */ public static function newReflectionObject($object) { return self::newReflectionClass(self::getClass($object)); } /** * Given a class name and a proxy namespace returns the proxy name. * * @param string $className * @param string $proxyNamespace * @psalm-param class-string $className * * @return string * @psalm-return class-string */ public static function generateProxyClassName($className, $proxyNamespace) { return rtrim($proxyNamespace, '\\') . '\\' . Proxy::MARKER . '\\' . ltrim($className, '\\'); } } PK!Iiicommon/src/Util/Debug.phpnu[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); } } PK!Lcommon/src/CommonException.phpnu[proxyGenerator = $proxyGenerator; $this->metadataFactory = $metadataFactory; $this->autoGenerate = (int) $autoGenerate; if (! in_array($this->autoGenerate, self::AUTOGENERATE_MODES, true)) { throw InvalidArgumentException::invalidAutoGenerateMode($autoGenerate); } } /** * Gets a reference proxy instance for the entity of the given type and identified by * the given identifier. * * @param string $className * @param array $identifier * * @return Proxy * * @throws OutOfBoundsException */ public function getProxy($className, array $identifier) { $definition = $this->definitions[$className] ?? $this->getProxyDefinition($className); $fqcn = $definition->proxyClassName; $proxy = new $fqcn($definition->initializer, $definition->cloner); foreach ($definition->identifierFields as $idField) { if (! isset($identifier[$idField])) { throw OutOfBoundsException::missingPrimaryKeyValue($className, $idField); } $definition->reflectionFields[$idField]->setValue($proxy, $identifier[$idField]); } return $proxy; } /** * Generates proxy classes for all given classes. * * @param ClassMetadata[] $classes The classes (ClassMetadata instances) * for which to generate proxies. * @param string $proxyDir The target directory of the proxy classes. If not specified, the * directory configured on the Configuration of the EntityManager used * by this factory is used. * * @return int Number of generated proxies. */ public function generateProxyClasses(array $classes, $proxyDir = null) { $generated = 0; foreach ($classes as $class) { if ($this->skipClass($class)) { continue; } $proxyFileName = $this->proxyGenerator->getProxyFileName($class->getName(), $proxyDir); $this->proxyGenerator->generateProxyClass($class, $proxyFileName); $generated += 1; } return $generated; } /** * Reset initialization/cloning logic for an un-initialized proxy * * @return Proxy * * @throws InvalidArgumentException */ public function resetUninitializedProxy(Proxy $proxy) { if ($proxy->__isInitialized()) { throw InvalidArgumentException::unitializedProxyExpected($proxy); } $className = ClassUtils::getClass($proxy); $definition = $this->definitions[$className] ?? $this->getProxyDefinition($className); $proxy->__setInitializer($definition->initializer); $proxy->__setCloner($definition->cloner); return $proxy; } /** * Get a proxy definition for the given class name. * * @param string $className * @psalm-param class-string $className * * @return ProxyDefinition */ private function getProxyDefinition($className) { $classMetadata = $this->metadataFactory->getMetadataFor($className); $className = $classMetadata->getName(); // aliases and case sensitivity $this->definitions[$className] = $this->createProxyDefinition($className); $proxyClassName = $this->definitions[$className]->proxyClassName; if (! class_exists($proxyClassName, false)) { $fileName = $this->proxyGenerator->getProxyFileName($className); switch ($this->autoGenerate) { case self::AUTOGENERATE_NEVER: require $fileName; break; case self::AUTOGENERATE_FILE_NOT_EXISTS: if (! file_exists($fileName)) { $this->proxyGenerator->generateProxyClass($classMetadata, $fileName); } require $fileName; break; case self::AUTOGENERATE_ALWAYS: $this->proxyGenerator->generateProxyClass($classMetadata, $fileName); require $fileName; break; case self::AUTOGENERATE_EVAL: $this->proxyGenerator->generateProxyClass($classMetadata, false); break; case self::AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED: if (! file_exists($fileName) || filemtime($fileName) < filemtime($classMetadata->getReflectionClass()->getFileName())) { $this->proxyGenerator->generateProxyClass($classMetadata, $fileName); } require $fileName; break; } } return $this->definitions[$className]; } /** * Determine if this class should be skipped during proxy generation. * * @return bool */ abstract protected function skipClass(ClassMetadata $metadata); /** * @param string $className * @psalm-param class-string $className * * @return ProxyDefinition */ abstract protected function createProxyDefinition($className); } PK!B6 common/src/Proxy/Autoloader.phpnu[\\?[a-z_\x7f-\xff][\w\x7f-\xff]*(?:\\[a-z_\x7f-\xff][\w\x7f-\xff]*)*) (?(?&type)\s*&\s*(?&type)) (?(?:(?:\(\s*(?&intersection_type)\s*\))|(?&type))(?:\s*\|\s*(?:(?:\(\s*(?&intersection_type)\s*\))|(?&type)))+) )(?:public\s+)?(?:function\s+%s\s*\(\)\s*)\s*(?::\s*(?:(?&union_type)|(?&intersection_type)|(?:\??(?&type)))\s*)?{\s*return\s*\$this->%s;\s*})i EOT; /** * 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(): void { $this->__initializer__ && $this->__initializer__->__invoke($this, \'__load\', []); } /** * {@inheritDoc} * @internal generated method: use only when explicitly handling proxy specific loading logic */ public function __isInitialized(): bool { return $this->__isInitialized__; } /** * {@inheritDoc} * @internal generated method: use only when explicitly handling proxy specific loading logic */ public function __setInitialized($initialized): void { $this->__isInitialized__ = $initialized; } /** * {@inheritDoc} * @internal generated method: use only when explicitly handling proxy specific loading logic */ public function __setInitializer(?\Closure $initializer = null): void { $this->__initializer__ = $initializer; } /** * {@inheritDoc} * @internal generated method: use only when explicitly handling proxy specific loading logic */ public function __getInitializer(): ?\Closure { return $this->__initializer__; } /** * {@inheritDoc} * @internal generated method: use only when explicitly handling proxy specific loading logic */ public function __setCloner(?\Closure $cloner = null): void { $this->__cloner__ = $cloner; } /** * {@inheritDoc} * @internal generated method: use only when explicitly handling proxy specific cloning logic */ public function __getCloner(): ?\Closure { 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(): array { 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 . '.' . bin2hex(random_bytes(12)); 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()); } if (PHP_VERSION_ID >= 80200 && $class->getReflectionClass()->isReadOnly()) { throw InvalidArgumentException::classMustNotBeReadOnly($class->getName()); } } /** * Generates the proxy short class name to be used in the template. * * @return string */ private function generateProxyShortClassName(ClassMetadata $class) { $proxyClassName = ClassUtils::generateProxyClassName($class->getName(), $this->proxyNamespace); $parts = explode('\\', strrev($proxyClassName), 2); return strrev($parts[0]); } /** * Generates the proxy namespace. * * @return string */ private function generateNamespace(ClassMetadata $class) { $proxyClassName = ClassUtils::generateProxyClassName($class->getName(), $this->proxyNamespace); $parts = explode('\\', strrev($proxyClassName), 2); return strrev($parts[1]); } /** * Enums must have a use statement when used as public property defaults. */ public function generateEnumUseStatements(ClassMetadata $class): string { if (PHP_VERSION_ID < 80100) { return "\n"; } $defaultProperties = $class->getReflectionClass()->getDefaultProperties(); $lazyLoadedPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class); $enumClasses = []; foreach ($class->getReflectionClass()->getProperties(ReflectionProperty::IS_PUBLIC) as $property) { $name = $property->getName(); if (! in_array($name, $lazyLoadedPublicProperties, true)) { continue; } if (array_key_exists($name, $defaultProperties) && $defaultProperties[$name] instanceof BackedEnum) { $enumClassNameParts = explode('\\', get_class($defaultProperties[$name])); $enumClasses[] = $enumClassNameParts[0]; } } return implode( "\n", array_map( static function ($className) { return 'use ' . $className . ';'; }, array_unique($enumClasses) ) ) . "\n"; } /** * Generates the original class name. * * @return string */ private function generateClassName(ClassMetadata $class) { return ltrim($class->getName(), '\\'); } /** * Generates the array representation of lazy loaded public properties and their default values. * * @return string */ private function generateLazyPropertiesNames(ClassMetadata $class) { $lazyPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class); $values = []; foreach ($lazyPublicProperties as $name) { $values[$name] = null; } return var_export($values, true); } /** * Generates the array representation of lazy loaded public properties names. * * @return string */ private function generateLazyPropertiesDefaults(ClassMetadata $class) { return var_export($this->getLazyLoadedPublicProperties($class), true); } /** * Generates the constructor code (un-setting public lazy loaded properties, setting identifier field values). * * @return string */ private function generateConstructorImpl(ClassMetadata $class) { $constructorImpl = <<<'EOT' public function __construct(?\Closure $initializer = null, ?\Closure $cloner = null) { EOT; $toUnset = array_map(static function (string $name): string { return '$this->' . $name; }, $this->getLazyLoadedPublicPropertiesNames($class)); return $constructorImpl . ($toUnset === [] ? '' : ' unset(' . implode(', ', $toUnset) . ");\n") . <<<'EOT' $this->__initializer__ = $initializer; $this->__cloner__ = $cloner; } EOT; } /** * Generates the magic getter invoked when lazy loaded public properties are requested. * * @return string */ private function generateMagicGet(ClassMetadata $class) { $lazyPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class); $reflectionClass = $class->getReflectionClass(); $hasParentGet = false; $returnReference = ''; $inheritDoc = ''; $name = '$name'; $parametersString = '$name'; $returnTypeHint = null; if ($reflectionClass->hasMethod('__get')) { $hasParentGet = true; $inheritDoc = '{@inheritDoc}'; $methodReflection = $reflectionClass->getMethod('__get'); if ($methodReflection->returnsReference()) { $returnReference = '& '; } $methodParameters = $methodReflection->getParameters(); $name = '$' . $methodParameters[0]->getName(); $parametersString = $this->buildParametersString($methodReflection->getParameters(), ['name']); $returnTypeHint = $this->getMethodReturnType($methodReflection); } if (empty($lazyPublicProperties) && ! $hasParentGet) { return ''; } $magicGet = <<__initializer__ && $this->__initializer__->__invoke($this, '__get', [$name]); EOT; if ($returnTypeHint === ': void') { $magicGet .= "\n return;"; } else { $magicGet .= "\n return \$this->\$name;"; } $magicGet .= <<<'EOT' } EOT; } if ($hasParentGet) { $magicGet .= <<<'EOT' $this->__initializer__ && $this->__initializer__->__invoke($this, '__get', [$name]); EOT; if ($returnTypeHint === ': void') { $magicGet .= <<<'EOT' parent::__get($name); return; EOT; } elseif ($returnTypeHint === ': never') { $magicGet .= <<<'EOT' parent::__get($name); EOT; } else { $magicGet .= <<<'EOT' return parent::__get($name); EOT; } } else { $magicGet .= sprintf(<<getLazyLoadedPublicPropertiesNames($class); $reflectionClass = $class->getReflectionClass(); $hasParentSet = false; $inheritDoc = ''; $parametersString = '$name, $value'; $returnTypeHint = null; if ($reflectionClass->hasMethod('__set')) { $hasParentSet = true; $inheritDoc = '{@inheritDoc}'; $methodReflection = $reflectionClass->getMethod('__set'); $parametersString = $this->buildParametersString($methodReflection->getParameters(), ['name', 'value']); $returnTypeHint = $this->getMethodReturnType($methodReflection); } if (empty($lazyPublicProperties) && ! $hasParentSet) { return ''; } $magicSet = <<__initializer__ && $this->__initializer__->__invoke($this, '__set', [$name, $value]); $this->$name = $value; return; } EOT; } if ($hasParentSet) { $magicSet .= <<<'EOT' $this->__initializer__ && $this->__initializer__->__invoke($this, '__set', [$name, $value]); EOT; if ($returnTypeHint === ': void') { $magicSet .= <<<'EOT' parent::__set($name, $value); return; EOT; } elseif ($returnTypeHint === ': never') { $magicSet .= <<<'EOT' parent::__set($name, $value); EOT; } else { $magicSet .= <<<'EOT' return parent::__set($name, $value); EOT; } } else { $magicSet .= ' $this->$name = $value;'; } return $magicSet . "\n }"; } /** * Generates the magic issetter invoked when lazy loaded public properties are checked against isset(). * * @return string */ private function generateMagicIsset(ClassMetadata $class) { $lazyPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class); $hasParentIsset = $class->getReflectionClass()->hasMethod('__isset'); $parametersString = '$name'; $returnTypeHint = null; if ($hasParentIsset) { $methodReflection = $class->getReflectionClass()->getMethod('__isset'); $parametersString = $this->buildParametersString($methodReflection->getParameters(), ['name']); $returnTypeHint = $this->getMethodReturnType($methodReflection); } if (empty($lazyPublicProperties) && ! $hasParentIsset) { return ''; } $inheritDoc = $hasParentIsset ? '{@inheritDoc}' : ''; $magicIsset = <<__initializer__ && $this->__initializer__->__invoke($this, '__isset', [$name]); return isset($this->$name); } EOT; } if ($hasParentIsset) { $magicIsset .= <<<'EOT' $this->__initializer__ && $this->__initializer__->__invoke($this, '__isset', [$name]); return parent::__isset($name); EOT; } else { $magicIsset .= ' return false;'; } return $magicIsset . "\n }"; } /** * Generates implementation for the `__sleep` method of proxies. * * @return string */ private function generateSleepImpl(ClassMetadata $class) { $reflectionClass = $class->getReflectionClass(); $hasParentSleep = $reflectionClass->hasMethod('__sleep'); $inheritDoc = $hasParentSleep ? '{@inheritDoc}' : ''; $returnTypeHint = $hasParentSleep ? $this->getMethodReturnType($reflectionClass->getMethod('__sleep')) : ''; $sleepImpl = <<__isInitialized__) { $properties = array_diff($properties, array_keys(self::$lazyPropertiesNames)); } return $properties; } EOT; } $allProperties = ['__isInitialized__']; foreach ($class->getReflectionClass()->getProperties() as $prop) { assert($prop instanceof ReflectionProperty); if ($prop->isStatic()) { continue; } $allProperties[] = $prop->isPrivate() ? "\0" . $prop->getDeclaringClass()->getName() . "\0" . $prop->getName() : $prop->getName(); } $lazyPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class); $protectedProperties = array_diff($allProperties, $lazyPublicProperties); foreach ($allProperties as &$property) { $property = var_export($property, true); } foreach ($protectedProperties as &$property) { $property = var_export($property, true); } $allProperties = implode(', ', $allProperties); $protectedProperties = implode(', ', $protectedProperties); return $sleepImpl . <<__isInitialized__) { return [$allProperties]; } return [$protectedProperties]; } EOT; } /** * Generates implementation for the `__wakeup` method of proxies. * * @return string */ private function generateWakeupImpl(ClassMetadata $class) { $reflectionClass = $class->getReflectionClass(); $hasParentWakeup = $reflectionClass->hasMethod('__wakeup'); $unsetPublicProperties = []; foreach ($this->getLazyLoadedPublicPropertiesNames($class) as $lazyPublicProperty) { $unsetPublicProperties[] = '$this->' . $lazyPublicProperty; } $shortName = $this->generateProxyShortClassName($class); $inheritDoc = $hasParentWakeup ? '{@inheritDoc}' : ''; $returnTypeHint = $hasParentWakeup ? $this->getMethodReturnType($reflectionClass->getMethod('__wakeup')) : ''; $wakeupImpl = <<__isInitialized__) { \$this->__initializer__ = function ($shortName \$proxy) { \$proxy->__setInitializer(null); \$proxy->__setCloner(null); \$existingProperties = get_object_vars(\$proxy); foreach (\$proxy::\$lazyPropertiesDefaults as \$property => \$defaultValue) { if ( ! array_key_exists(\$property, \$existingProperties)) { \$proxy->\$property = \$defaultValue; } } }; EOT; if (! empty($unsetPublicProperties)) { $wakeupImpl .= "\n unset(" . implode(', ', $unsetPublicProperties) . ');'; } $wakeupImpl .= "\n }"; if ($hasParentWakeup) { $wakeupImpl .= "\n parent::__wakeup();"; } $wakeupImpl .= "\n }"; return $wakeupImpl; } /** * Generates implementation for the `__clone` method of proxies. * * @return string */ private function generateCloneImpl(ClassMetadata $class) { $reflectionClass = $class->getReflectionClass(); $hasParentClone = $reflectionClass->hasMethod('__clone'); $returnTypeHint = $hasParentClone ? $this->getMethodReturnType($reflectionClass->getMethod('__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'], true) ? '(int) ' : ''; $methods .= ' if ($this->__isInitialized__ === false) {' . "\n"; $methods .= ' '; $methods .= $this->shouldProxiedMethodReturn($method) ? 'return ' : ''; $methods .= $cast . ' parent::' . $method->getName() . "();\n"; $methods .= ' }' . "\n\n"; } $invokeParamsString = implode(', ', $this->getParameterNamesForInvoke($method->getParameters())); $callParamsString = implode(', ', $this->getParameterNamesForParentCall($method->getParameters())); $methods .= "\n \$this->__initializer__ " . '&& $this->__initializer__->__invoke($this, ' . var_export($name, true) . ', [' . $invokeParamsString . ']);' . "\n\n " . ($this->shouldProxiedMethodReturn($method) ? 'return ' : '') . 'parent::' . $name . '(' . $callParamsString . ');' . "\n" . ' }' . "\n"; } return $methods; } /** * Generates the Proxy file name. * * @param string $className * @param string $baseDirectory Optional base directory for proxy file name generation. * If not specified, the directory configured on the Configuration of the * EntityManager will be used by this factory. * @psalm-param class-string $className * * @return string */ public function getProxyFileName($className, $baseDirectory = null) { $baseDirectory = $baseDirectory ?: $this->proxyDirectory; return rtrim($baseDirectory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . Proxy::MARKER . str_replace('\\', '', $className) . '.php'; } /** * Checks if the method is a short identifier getter. * * What does this mean? For proxy objects the identifier is already known, * however accessing the getter for this identifier usually triggers the * lazy loading, leading to a query that may not be necessary if only the * ID is interesting for the userland code (for example in views that * generate links to the entity, but do not display anything else). * * @param ReflectionMethod $method * * @return bool */ private function isShortIdentifierGetter($method, ClassMetadata $class) { $identifier = lcfirst(substr($method->getName(), 3)); $startLine = $method->getStartLine(); $endLine = $method->getEndLine(); $cheapCheck = $method->getNumberOfParameters() === 0 && substr($method->getName(), 0, 3) === 'get' && in_array($identifier, $class->getIdentifier(), true) && $class->hasField($identifier) && ($endLine - $startLine <= 4); if ($cheapCheck) { $code = file($method->getFileName()); $code = trim(implode(' ', array_slice($code, $startLine - 1, $endLine - $startLine + 1))); $pattern = sprintf(self::PATTERN_MATCH_ID_METHOD, $method->getName(), $identifier); if (preg_match($pattern, $code)) { return true; } } return false; } /** * Generates the list of public properties to be lazy loaded. * * @return array */ private function getLazyLoadedPublicPropertiesNames(ClassMetadata $class): array { $properties = []; foreach ($class->getReflectionClass()->getProperties(ReflectionProperty::IS_PUBLIC) as $property) { $name = $property->getName(); if ((! $class->hasField($name) && ! $class->hasAssociation($name)) || $class->isIdentifier($name)) { continue; } $properties[] = $name; } return $properties; } /** * Generates the list of default values of public properties. * * @return mixed[] */ private function getLazyLoadedPublicProperties(ClassMetadata $class) { $defaultProperties = $class->getReflectionClass()->getDefaultProperties(); $lazyLoadedPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class); $defaultValues = []; foreach ($class->getReflectionClass()->getProperties(ReflectionProperty::IS_PUBLIC) as $property) { $name = $property->getName(); if (! in_array($name, $lazyLoadedPublicProperties, true)) { continue; } if (array_key_exists($name, $defaultProperties)) { $defaultValues[$name] = $defaultProperties[$name]; } elseif (method_exists($property, 'getType')) { $propertyType = $property->getType(); if ($propertyType !== null && $propertyType->allowsNull()) { $defaultValues[$name] = null; } } } return $defaultValues; } /** * @param ReflectionParameter[] $parameters * @param string[] $renameParameters * * @return string */ private function buildParametersString(array $parameters, array $renameParameters = []) { $parameterDefinitions = []; $i = -1; foreach ($parameters as $param) { assert($param instanceof ReflectionParameter); $i++; $parameterDefinition = ''; $parameterType = $this->getParameterType($param); if ($parameterType !== null) { $parameterDefinition .= $parameterType . ' '; } if ($param->isPassedByReference()) { $parameterDefinition .= '&'; } if ($param->isVariadic()) { $parameterDefinition .= '...'; } $parameterDefinition .= '$' . ($renameParameters ? $renameParameters[$i] : $param->getName()); $parameterDefinition .= $this->getParameterDefaultValue($param); $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); } /** @return string */ private function getParameterDefaultValue(ReflectionParameter $parameter) { if (! $parameter->isDefaultValueAvailable()) { return ''; } if (PHP_VERSION_ID < 80100 || is_scalar($parameter->getDefaultValue())) { return ' = ' . var_export($parameter->getDefaultValue(), true); } $value = rtrim(substr(explode('$' . $parameter->getName() . ' = ', (string) $parameter, 2)[1], 0, -2)); if (strpos($value, '\\') !== false || strpos($value, '::') !== false) { $value = preg_split("/('(?:[^'\\\\]*+(?:\\\\.)*+)*+')/", $value, -1, PREG_SPLIT_DELIM_CAPTURE); foreach ($value as $i => $part) { if ($i % 2 === 0) { $value[$i] = preg_replace('/(?getName(); }, $parameters ); } /** * @param ReflectionParameter[] $parameters * * @return string[] */ private function getParameterNamesForParentCall(array $parameters) { return array_map( static function (ReflectionParameter $parameter) { $name = ''; if ($parameter->isVariadic()) { $name .= '...'; } $name .= '$' . $parameter->getName(); return $name; }, $parameters ); } /** @return string */ private function getMethodReturnType(ReflectionMethod $method) { if (! $method->hasReturnType()) { return ''; } return ': ' . $this->formatType($method->getReturnType(), $method); } /** @return bool */ private function shouldProxiedMethodReturn(ReflectionMethod $method) { if (! $method->hasReturnType()) { return true; } return ! in_array( strtolower($this->formatType($method->getReturnType(), $method)), ['void', 'never'], true ); } /** @return string */ private function formatType( ReflectionType $type, ReflectionMethod $method, ?ReflectionParameter $parameter = null ) { if ($type instanceof ReflectionUnionType) { return implode('|', array_map( function (ReflectionType $unionedType) use ($method, $parameter) { if ($unionedType instanceof ReflectionIntersectionType) { return '(' . $this->formatType($unionedType, $method, $parameter) . ')'; } return $this->formatType($unionedType, $method, $parameter); }, $type->getTypes() )); } if ($type instanceof ReflectionIntersectionType) { return implode('&', array_map( function (ReflectionType $intersectedType) use ($method, $parameter) { return $this->formatType($intersectedType, $method, $parameter); }, $type->getTypes() )); } assert($type instanceof ReflectionNamedType); $name = $type->getName(); $nameLower = strtolower($name); if ($nameLower === 'static') { $name = 'static'; } if ($nameLower === 'self') { $name = $method->getDeclaringClass()->getName(); } if ($nameLower === 'parent') { $name = $method->getDeclaringClass()->getParentClass()->getName(); } if (! $type->isBuiltin() && ! class_exists($name) && ! interface_exists($name) && $name !== 'static') { if ($parameter !== null) { throw UnexpectedValueException::invalidParameterTypeHint( $method->getDeclaringClass()->getName(), $method->getName(), $parameter->getName() ); } throw UnexpectedValueException::invalidReturnTypeHint( $method->getDeclaringClass()->getName(), $method->getName() ); } if (! $type->isBuiltin() && $name !== 'static') { $name = '\\' . $name; } if ( $type->allowsNull() && ! in_array($name, ['mixed', 'null'], true) && ($parameter === null || ! $parameter->isDefaultValueAvailable() || $parameter->getDefaultValue() !== null) ) { $name = '?' . $name; } return $name; } } PK!)common/src/Proxy/Proxy.phpnu[ */ interface Proxy extends BaseProxy { /** * Marks the proxy as initialized or not. * * @param bool $initialized * * @return void */ public function __setInitialized($initialized); /** * Sets the initializer callback to be used when initializing the proxy. That * initializer should accept 3 parameters: $proxy, $method and $params. Those * are respectively the proxy object that is being initialized, the method name * that triggered initialization and the parameters passed to that method. * * @return void */ public function __setInitializer(?Closure $initializer = null); /** * Retrieves the initializer callback used to initialize the proxy. * * @see __setInitializer * * @return Closure|null */ public function __getInitializer(); /** * Sets the callback to be used when cloning the proxy. That initializer should accept * a single parameter, which is the cloned proxy instance itself. * * @return void */ public function __setCloner(?Closure $cloner = null); /** * Retrieves the callback to be used when cloning the proxy. * * @see __setCloner * * @return Closure|null */ public function __getCloner(); /** * Retrieves the list of lazy loaded properties for a given proxy * * @return array Keys are the property names, and values are the default values * for those properties. */ public function __getLazyProperties(); } PK!(jpp$common/src/Proxy/ProxyDefinition.phpnu[ */ 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; } } PK!* common/src/Comparable.phpnu[ClassLoader is an autoloader for class files that can be * installed on the SPL autoload stack. It is a class loader that either loads only classes * of a specific namespace or all namespaces and it is suitable for working together * with other autoloaders in the SPL autoload stack. * * If no include path is configured through the constructor or {@link setIncludePath}, a ClassLoader * relies on the PHP include_path. * * @deprecated The ClassLoader is deprecated and will be removed in version 4.0 of doctrine/common. */ class ClassLoader { /** * PHP file extension. * * @var string */ protected $fileExtension = '.php'; /** * Current namespace. * * @var string|null */ protected $namespace; /** * Current include path. * * @var string|null */ protected $includePath; /** * PHP namespace separator. * * @var string */ protected $namespaceSeparator = '\\'; /** * Creates a new ClassLoader that loads classes of the * specified namespace from the specified include path. * * If no include path is given, the ClassLoader relies on the PHP include_path. * If neither a namespace nor an include path is given, the ClassLoader will * be responsible for loading all classes, thereby relying on the PHP include_path. * * @param string|null $ns The namespace of the classes to load. * @param string|null $includePath The base include path to use. */ public function __construct($ns = null, $includePath = null) { $this->namespace = $ns; $this->includePath = $includePath; } /** * Sets the namespace separator used by classes in the namespace of this ClassLoader. * * @param string $sep The separator to use. * * @return void */ public function setNamespaceSeparator($sep) { $this->namespaceSeparator = $sep; } /** * Gets the namespace separator used by classes in the namespace of this ClassLoader. * * @return string */ public function getNamespaceSeparator() { return $this->namespaceSeparator; } /** * Sets the base include path for all class files in the namespace of this ClassLoader. * * @param string|null $includePath * * @return void */ public function setIncludePath($includePath) { $this->includePath = $includePath; } /** * Gets the base include path for all class files in the namespace of this ClassLoader. * * @return string|null */ public function getIncludePath() { return $this->includePath; } /** * Sets the file extension of class files in the namespace of this ClassLoader. * * @param string $fileExtension * * @return void */ public function setFileExtension($fileExtension) { $this->fileExtension = $fileExtension; } /** * Gets the file extension of class files in the namespace of this ClassLoader. * * @return string */ public function getFileExtension() { return $this->fileExtension; } /** * Registers this ClassLoader on the SPL autoload stack. * * @return void */ public function register() { spl_autoload_register([$this, 'loadClass']); } /** * Removes this ClassLoader from the SPL autoload stack. * * @return void */ public function unregister() { spl_autoload_unregister([$this, 'loadClass']); } /** * Loads the given class or interface. * * @param string $className The name of the class to load. * @psalm-param class-string $className * * @return bool TRUE if the class has been successfully loaded, FALSE otherwise. */ public function loadClass($className) { if (self::typeExists($className)) { return true; } if (! $this->canLoadClass($className)) { return false; } require($this->includePath !== null ? $this->includePath . DIRECTORY_SEPARATOR : '') . str_replace($this->namespaceSeparator, DIRECTORY_SEPARATOR, $className) . $this->fileExtension; return self::typeExists($className); } /** * Asks this ClassLoader whether it can potentially load the class (file) with * the given name. * * @param string $className The fully-qualified name of the class. * @psalm-param class-string $className * * @return bool TRUE if this ClassLoader can load the class, FALSE otherwise. */ public function canLoadClass($className) { if ($this->namespace !== null && strpos($className, $this->namespace . $this->namespaceSeparator) !== 0) { return false; } $file = str_replace($this->namespaceSeparator, DIRECTORY_SEPARATOR, $className) . $this->fileExtension; if ($this->includePath !== null) { return is_file($this->includePath . DIRECTORY_SEPARATOR . $file); } return stream_resolve_include_path($file) !== false; } /** * Checks whether a class with a given name exists. A class "exists" if it is either * already defined in the current request or if there is an autoloader on the SPL * autoload stack that is a) responsible for the class in question and b) is able to * load a class file in which the class definition resides. * * If the class is not already defined, each autoloader in the SPL autoload stack * is asked whether it is able to tell if the class exists. If the autoloader is * a ClassLoader, {@link canLoadClass} is used, otherwise the autoload * function of the autoloader is invoked and expected to return a value that * evaluates to TRUE if the class (file) exists. As soon as one autoloader reports * that the class exists, TRUE is returned. * * Note that, depending on what kinds of autoloaders are installed on the SPL * autoload stack, the class (file) might already be loaded as a result of checking * for its existence. This is not the case with a ClassLoader, who separates * these responsibilities. * * @param string $className The fully-qualified name of the class. * @psalm-param class-string $className * * @return bool TRUE if the class exists as per the definition given above, FALSE otherwise. */ public static function classExists($className) { return self::typeExists($className, true); } /** * Gets the ClassLoader from the SPL autoload stack that is responsible * for (and is able to load) the class with the given name. * * @param string $className The name of the class. * @psalm-param class-string $className * * @return ClassLoader|null The ClassLoader for the class or NULL if no such ClassLoader exists. */ public static function getClassLoader($className) { foreach (spl_autoload_functions() as $loader) { if (! is_array($loader)) { continue; } $classLoader = reset($loader); if ($classLoader instanceof ClassLoader && $classLoader->canLoadClass($className)) { return $classLoader; } } return null; } /** * Checks whether a given type exists * * @param string $type * @param bool $autoload * * @return bool */ private static function typeExists($type, $autoload = false) { return class_exists($type, $autoload) || interface_exists($type, $autoload) || trait_exists($type, $autoload); } } PK!$%$%*common/docs/en/reference/class-loading.rstnu[Class Loading ============= Class loading is an essential part of any PHP application that makes heavy use of classes and interfaces. Unfortunately, a lot of people and projects spend a lot of time and effort on custom and specialized class loading strategies. It can quickly become a pain to understand what is going on when using multiple libraries and/or frameworks, each with its own way to do class loading. Class loading should be simple and it is an ideal candidate for convention over configuration. Overview -------- The Doctrine Common ClassLoader implements a simple and efficient approach to class loading that is easy to understand and use. The implementation is based on the widely used and accepted convention of mapping namespace and class names to a directory structure. This approach is used for example by Symfony2, the Zend Framework and of course, Doctrine. For example, the following class: .. code-block:: php register(); $dbalLoader->register(); $ormLoader->register(); Do not be afraid of using multiple class loaders. Due to the efficient class loading design you will not incur much overhead from using many class loaders. Take a look at the implementation of ``ClassLoader#loadClass`` to see how simple and efficient the class loading is. The iteration over the installed class loaders happens in C (with the exception of using ``ClassLoader::classExists``). A ClassLoader can be used in the following other variations, however, these are rarely used/needed: - If only the second argument is not supplied, the class loader will be responsible for the namespace prefix given in the first argument and it will rely on the PHP include_path. - If only the first argument is not supplied, the class loader will be responsible for *all* classes and it will try to look up *all* classes starting at the directory given as the second argument. - If both arguments are not supplied, the class loader will be responsible for *all* classes and it will rely on the PHP include_path. File Extension -------------- By default, a ClassLoader uses the ``.php`` file extension for all class files. You can change this behavior, for example to use a ClassLoader to load classes from a library that uses the ".class.php" convention (but it must nevertheless adhere to the directory structure convention!): .. code-block:: php setFileExtension('.class.php'); $customLoader->register(); Namespace Separator ------------------- By default, a ClassLoader uses the ``\`` namespace separator. You can change this behavior, for example to use a ClassLoader to load legacy Zend Framework classes that still use the underscore "_" separator: .. code-block:: php setNamespaceSeparator('_'); $zend1Loader->register(); Failing Silently and class_exists ---------------------------------- A lot of class/autoloaders these days try to fail silently when a class file is not found. For the most part this is necessary in order to support using ``class_exists('ClassName', true)`` which is supposed to return a boolean value but triggers autoloading. This is a bad thing as it basically forces class loaders to fail silently, which in turn requires costly file_exists or fopen calls for each class being loaded, even though in at least 99% of the cases this is not necessary (compare the number of class_exists(..., true) invocations to the total number of classes being loaded in a request). The Doctrine Common ClassLoader does not fail silently, by design. It therefore does not need any costly checks for file existence. A ClassLoader is always responsible for all classes with a certain namespace prefix and if a class is requested to be loaded and can not be found this is considered to be a fatal error. This also means that using class_exists(..., true) to check for class existence when using a Doctrine Common ClassLoader is not possible but this is not a bad thing. What class\_exists(..., true) actually means is two things: 1) Check whether the class is already defined/exists (i.e. class_exists(..., false)) and if not 2) check whether a class file can be loaded for that class. In the Doctrine Common ClassLoader the two responsibilities of loading a class and checking for its existence are separated, which can be observed by the existence of the two methods ``loadClass`` and ``canLoadClass``. Thereby ``loadClass`` does not invoke ``canLoadClass`` internally, by design. However, you are free to use it yourself to check whether a class can be loaded and the following code snippet is thus equivalent to class\_exists(..., true): .. code-block:: php canLoadClass('Foo')) { // ... } The only problem with this is that it is inconvenient as you need to have a reference to the class loaders around (and there are often multiple class loaders in use). Therefore, a simpler alternative exists for the cases in which you really want to ask all installed class loaders whether they can load the class: ``ClassLoader::classExists($className)``: .. code-block:: php wordInflector = $wordInflector; } public function inflect(string $word): string { return $this->cache[$word] ?? $this->cache[$word] = $this->wordInflector->inflect($word); } } PK!h%AS:inflector/lib/Doctrine/Inflector/Rules/Transformations.phpnu[transformations = $transformations; } public function inflect(string $word): string { foreach ($this->transformations as $transformation) { if ($transformation->getPattern()->matches($word)) { return $transformation->inflect($word); } } return $word; } } PK!J>inflector/lib/Doctrine/Inflector/Rules/Turkish/Inflectible.phpnu[Yεgg>inflector/lib/Doctrine/Inflector/Rules/Turkish/Uninflected.phpnu[getFlippedSubstitutions() ); } public static function getPluralRuleset(): Ruleset { return new Ruleset( new Transformations(...Inflectible::getPlural()), new Patterns(...Uninflected::getPlural()), new Substitutions(...Inflectible::getIrregular()) ); } } PK!{<Finflector/lib/Doctrine/Inflector/Rules/NorwegianBokmal/Inflectible.phpnu[getFlippedSubstitutions() ); } public static function getPluralRuleset(): Ruleset { return new Ruleset( new Transformations(...Inflectible::getPlural()), new Patterns(...Uninflected::getPlural()), new Substitutions(...Inflectible::getIrregular()) ); } } PK!͕99>inflector/lib/Doctrine/Inflector/Rules/Spanish/Inflectible.phpnu[inflector/lib/Doctrine/Inflector/Rules/Spanish/Uninflected.phpnu[getFlippedSubstitutions() ); } public static function getPluralRuleset(): Ruleset { return new Ruleset( new Transformations(...Inflectible::getPlural()), new Patterns(...Uninflected::getPlural()), new Substitutions(...Inflectible::getIrregular()) ); } } PK!a_2inflector/lib/Doctrine/Inflector/Rules/Pattern.phpnu[pattern = $pattern; if (isset($this->pattern[0]) && $this->pattern[0] === '/') { $this->regex = $this->pattern; } else { $this->regex = '/' . $this->pattern . '/i'; } } public function getPattern(): string { return $this->pattern; } public function getRegex(): string { return $this->regex; } public function matches(string $word): bool { return preg_match($this->getRegex(), $word) === 1; } } PK!@YoAinflector/lib/Doctrine/Inflector/Rules/Portuguese/Inflectible.phpnu[getFlippedSubstitutions() ); } public static function getPluralRuleset(): Ruleset { return new Ruleset( new Transformations(...Inflectible::getPlural()), new Patterns(...Uninflected::getPlural()), new Substitutions(...Inflectible::getIrregular()) ); } } PK!'9inflector/lib/Doctrine/Inflector/Rules/Transformation.phpnu[pattern = $pattern; $this->replacement = $replacement; } public function getPattern(): Pattern { return $this->pattern; } public function getReplacement(): string { return $this->replacement; } public function inflect(string $word): string { return (string) preg_replace($this->pattern->getRegex(), $this->replacement, $word); } } PK!fP  2inflector/lib/Doctrine/Inflector/Rules/Ruleset.phpnu[regular = $regular; $this->uninflected = $uninflected; $this->irregular = $irregular; } public function getRegular(): Transformations { return $this->regular; } public function getUninflected(): Patterns { return $this->uninflected; } public function getIrregular(): Substitutions { return $this->irregular; } } PK!7inflector/lib/Doctrine/Inflector/Rules/Substitution.phpnu[from = $from; $this->to = $to; } public function getFrom(): Word { return $this->from; } public function getTo(): Word { return $this->to; } } PK!m\\8inflector/lib/Doctrine/Inflector/Rules/Substitutions.phpnu[substitutions[$substitution->getFrom()->getWord()] = $substitution; } } public function getFlippedSubstitutions(): Substitutions { $substitutions = []; foreach ($this->substitutions as $substitution) { $substitutions[] = new Substitution( $substitution->getTo(), $substitution->getFrom() ); } return new Substitutions(...$substitutions); } public function inflect(string $word): string { $lowerWord = strtolower($word); if (isset($this->substitutions[$lowerWord])) { $firstLetterUppercase = $lowerWord[0] !== $word[0]; $toWord = $this->substitutions[$lowerWord]->getTo()->getWord(); if ($firstLetterUppercase) { return strtoupper($toWord[0]) . substr($toWord, 1); } return $toWord; } return $word; } } PK!fy_._.>inflector/lib/Doctrine/Inflector/Rules/English/Inflectible.phpnu[inflector/lib/Doctrine/Inflector/Rules/English/Uninflected.phpnu[getFlippedSubstitutions() ); } public static function getPluralRuleset(): Ruleset { return new Ruleset( new Transformations(...Inflectible::getPlural()), new Patterns(...Uninflected::getPlural()), new Substitutions(...Inflectible::getIrregular()) ); } } PK!v掸3inflector/lib/Doctrine/Inflector/Rules/Patterns.phpnu[patterns = $patterns; $patterns = array_map(static function (Pattern $pattern): string { return $pattern->getPattern(); }, $this->patterns); $this->regex = '/^(?:' . implode('|', $patterns) . ')$/i'; } public function matches(string $word): bool { return preg_match($this->regex, $word, $regs) === 1; } } PK!KHW&&/inflector/lib/Doctrine/Inflector/Rules/Word.phpnu[word = $word; } public function getWord(): string { return $this->word; } } PK!yXVV=inflector/lib/Doctrine/Inflector/Rules/French/Inflectible.phpnu[getFlippedSubstitutions() ); } public static function getPluralRuleset(): Ruleset { return new Ruleset( new Transformations(...Inflectible::getPlural()), new Patterns(...Uninflected::getPlural()), new Substitutions(...Inflectible::getIrregular()) ); } } PK!##l2inflector/lib/Doctrine/Inflector/WordInflector.phpnu[ 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'Ae', 'Æ' => 'Ae', 'Å' => 'Aa', 'æ' => 'a', 'Ç' => 'C', 'È' => 'E', 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E', 'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I', 'Ñ' => 'N', 'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ö' => 'Oe', 'Ù' => 'U', 'Ú' => 'U', 'Û' => 'U', 'Ü' => 'Ue', 'Ý' => 'Y', 'ß' => 'ss', 'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ä' => 'ae', 'å' => 'aa', 'ç' => 'c', 'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e', 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i', 'ñ' => 'n', 'ò' => 'o', 'ó' => 'o', 'ô' => 'o', 'õ' => 'o', 'ö' => 'oe', 'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ü' => 'ue', 'ý' => 'y', 'ÿ' => 'y', 'Ā' => 'A', 'ā' => 'a', 'Ă' => 'A', 'ă' => 'a', 'Ą' => 'A', 'ą' => 'a', 'Ć' => 'C', 'ć' => 'c', 'Ĉ' => 'C', 'ĉ' => 'c', 'Ċ' => 'C', 'ċ' => 'c', 'Č' => 'C', 'č' => 'c', 'Ď' => 'D', 'ď' => 'd', 'Đ' => 'D', 'đ' => 'd', 'Ē' => 'E', 'ē' => 'e', 'Ĕ' => 'E', 'ĕ' => 'e', 'Ė' => 'E', 'ė' => 'e', 'Ę' => 'E', 'ę' => 'e', 'Ě' => 'E', 'ě' => 'e', 'Ĝ' => 'G', 'ĝ' => 'g', 'Ğ' => 'G', 'ğ' => 'g', 'Ġ' => 'G', 'ġ' => 'g', 'Ģ' => 'G', 'ģ' => 'g', 'Ĥ' => 'H', 'ĥ' => 'h', 'Ħ' => 'H', 'ħ' => 'h', 'Ĩ' => 'I', 'ĩ' => 'i', 'Ī' => 'I', 'ī' => 'i', 'Ĭ' => 'I', 'ĭ' => 'i', 'Į' => 'I', 'į' => 'i', 'İ' => 'I', 'ı' => 'i', 'IJ' => 'IJ', 'ij' => 'ij', 'Ĵ' => 'J', 'ĵ' => 'j', 'Ķ' => 'K', 'ķ' => 'k', 'ĸ' => 'k', 'Ĺ' => 'L', 'ĺ' => 'l', 'Ļ' => 'L', 'ļ' => 'l', 'Ľ' => 'L', 'ľ' => 'l', 'Ŀ' => 'L', 'ŀ' => 'l', 'Ł' => 'L', 'ł' => 'l', 'Ń' => 'N', 'ń' => 'n', 'Ņ' => 'N', 'ņ' => 'n', 'Ň' => 'N', 'ň' => 'n', 'ʼn' => 'N', 'Ŋ' => 'n', 'ŋ' => 'N', 'Ō' => 'O', 'ō' => 'o', 'Ŏ' => 'O', 'ŏ' => 'o', 'Ő' => 'O', 'ő' => 'o', 'Œ' => 'OE', 'œ' => 'oe', 'Ø' => 'O', 'ø' => 'o', 'Ŕ' => 'R', 'ŕ' => 'r', 'Ŗ' => 'R', 'ŗ' => 'r', 'Ř' => 'R', 'ř' => 'r', 'Ś' => 'S', 'ś' => 's', 'Ŝ' => 'S', 'ŝ' => 's', 'Ş' => 'S', 'ş' => 's', 'Š' => 'S', 'š' => 's', 'Ţ' => 'T', 'ţ' => 't', 'Ť' => 'T', 'ť' => 't', 'Ŧ' => 'T', 'ŧ' => 't', 'Ũ' => 'U', 'ũ' => 'u', 'Ū' => 'U', 'ū' => 'u', 'Ŭ' => 'U', 'ŭ' => 'u', 'Ů' => 'U', 'ů' => 'u', 'Ű' => 'U', 'ű' => 'u', 'Ų' => 'U', 'ų' => 'u', 'Ŵ' => 'W', 'ŵ' => 'w', 'Ŷ' => 'Y', 'ŷ' => 'y', 'Ÿ' => 'Y', 'Ź' => 'Z', 'ź' => 'z', 'Ż' => 'Z', 'ż' => 'z', 'Ž' => 'Z', 'ž' => 'z', 'ſ' => 's', '€' => 'E', '£' => '', ]; /** @var WordInflector */ private $singularizer; /** @var WordInflector */ private $pluralizer; public function __construct(WordInflector $singularizer, WordInflector $pluralizer) { $this->singularizer = $singularizer; $this->pluralizer = $pluralizer; } /** * Converts a word into the format for a Doctrine table name. Converts 'ModelName' to 'model_name'. */ public function tableize(string $word): string { $tableized = preg_replace('~(?<=\\w)([A-Z])~u', '_$1', $word); if ($tableized === null) { throw new RuntimeException(sprintf( 'preg_replace returned null for value "%s"', $word )); } return mb_strtolower($tableized); } /** * Converts a word into the format for a Doctrine class name. Converts 'table_name' to 'TableName'. */ public function classify(string $word): string { return str_replace([' ', '_', '-'], '', ucwords($word, ' _-')); } /** * Camelizes a word. This uses the classify() method and turns the first character to lowercase. */ public function camelize(string $word): string { return lcfirst($this->classify($word)); } /** * Uppercases words with configurable delimiters between words. * * Takes a string and capitalizes all of the words, like PHP's built-in * ucwords function. This extends that behavior, however, by allowing the * word delimiters to be configured, rather than only separating on * whitespace. * * Here is an example: * * capitalize($string); * // Top-O-The-Morning To All_of_you! * * echo $inflector->capitalize($string, '-_ '); * // Top-O-The-Morning To All_Of_You! * ?> * * * @param string $string The string to operate on. * @param string $delimiters A list of word separators. * * @return string The string with all delimiter-separated words capitalized. */ public function capitalize(string $string, string $delimiters = " \n\t\r\0\x0B-"): string { return ucwords($string, $delimiters); } /** * Checks if the given string seems like it has utf8 characters in it. * * @param string $string The string to check for utf8 characters in. */ public function seemsUtf8(string $string): bool { for ($i = 0; $i < strlen($string); $i++) { if (ord($string[$i]) < 0x80) { continue; // 0bbbbbbb } if ((ord($string[$i]) & 0xE0) === 0xC0) { $n = 1; // 110bbbbb } elseif ((ord($string[$i]) & 0xF0) === 0xE0) { $n = 2; // 1110bbbb } elseif ((ord($string[$i]) & 0xF8) === 0xF0) { $n = 3; // 11110bbb } elseif ((ord($string[$i]) & 0xFC) === 0xF8) { $n = 4; // 111110bb } elseif ((ord($string[$i]) & 0xFE) === 0xFC) { $n = 5; // 1111110b } else { return false; // Does not match any model } for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ? if (++$i === strlen($string) || ((ord($string[$i]) & 0xC0) !== 0x80)) { return false; } } } return true; } /** * Remove any illegal characters, accents, etc. * * @param string $string String to unaccent * * @return string Unaccented string */ public function unaccent(string $string): string { if (preg_match('/[\x80-\xff]/', $string) === false) { return $string; } if ($this->seemsUtf8($string)) { $string = strtr($string, self::ACCENTED_CHARACTERS); } else { $characters = []; // Assume ISO-8859-1 if not UTF-8 $characters['in'] = chr(128) . chr(131) . chr(138) . chr(142) . chr(154) . chr(158) . chr(159) . chr(162) . chr(165) . chr(181) . chr(192) . chr(193) . chr(194) . chr(195) . chr(196) . chr(197) . chr(199) . chr(200) . chr(201) . chr(202) . chr(203) . chr(204) . chr(205) . chr(206) . chr(207) . chr(209) . chr(210) . chr(211) . chr(212) . chr(213) . chr(214) . chr(216) . chr(217) . chr(218) . chr(219) . chr(220) . chr(221) . chr(224) . chr(225) . chr(226) . chr(227) . chr(228) . chr(229) . chr(231) . chr(232) . chr(233) . chr(234) . chr(235) . chr(236) . chr(237) . chr(238) . chr(239) . chr(241) . chr(242) . chr(243) . chr(244) . chr(245) . chr(246) . chr(248) . chr(249) . chr(250) . chr(251) . chr(252) . chr(253) . chr(255); $characters['out'] = 'EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy'; $string = strtr($string, $characters['in'], $characters['out']); $doubleChars = []; $doubleChars['in'] = [ chr(140), chr(156), chr(198), chr(208), chr(222), chr(223), chr(230), chr(240), chr(254), ]; $doubleChars['out'] = ['OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th']; $string = str_replace($doubleChars['in'], $doubleChars['out'], $string); } return $string; } /** * Convert any passed string to a url friendly string. * Converts 'My first blog post' to 'my-first-blog-post' * * @param string $string String to urlize. * * @return string Urlized string. */ public function urlize(string $string): string { // Remove all non url friendly characters with the unaccent function $unaccented = $this->unaccent($string); if (function_exists('mb_strtolower')) { $lowered = mb_strtolower($unaccented); } else { $lowered = strtolower($unaccented); } $replacements = [ '/\W/' => ' ', '/([A-Z]+)([A-Z][a-z])/' => '\1_\2', '/([a-z\d])([A-Z])/' => '\1_\2', '/[^A-Z^a-z^0-9^\/]+/' => '-', ]; $urlized = $lowered; foreach ($replacements as $pattern => $replacement) { $replaced = preg_replace($pattern, $replacement, $urlized); if ($replaced === null) { throw new RuntimeException(sprintf( 'preg_replace returned null for value "%s"', $urlized )); } $urlized = $replaced; } return trim($urlized, '-'); } /** * Returns a word in singular form. * * @param string $word The word in plural form. * * @return string The word in singular form. */ public function singularize(string $word): string { return $this->singularizer->inflect($word); } /** * Returns a word in plural form. * * @param string $word The word in singular form. * * @return string The word in plural form. */ public function pluralize(string $word): string { return $this->pluralizer->inflect($word); } } PK!x%%=inflector/lib/Doctrine/Inflector/LanguageInflectorFactory.phpnu[rulesets = array_merge([$ruleset], $rulesets); } public function inflect(string $word): string { if ($word === '') { return ''; } foreach ($this->rulesets as $ruleset) { if ($ruleset->getUninflected()->matches($word)) { return $word; } $inflected = $ruleset->getIrregular()->inflect($word); if ($inflected !== $word) { return $inflected; } $inflected = $ruleset->getRegular()->inflect($word); if ($inflected !== $word) { return $inflected; } } return $word; } } PK!Kڮ-inflector/lib/Doctrine/Inflector/Language.phpnu[singularRulesets[] = $this->getSingularRuleset(); $this->pluralRulesets[] = $this->getPluralRuleset(); } final public function build(): Inflector { return new Inflector( new CachedWordInflector(new RulesetInflector( ...$this->singularRulesets )), new CachedWordInflector(new RulesetInflector( ...$this->pluralRulesets )) ); } final public function withSingularRules(?Ruleset $singularRules, bool $reset = false): LanguageInflectorFactory { if ($reset) { $this->singularRulesets = []; } if ($singularRules instanceof Ruleset) { array_unshift($this->singularRulesets, $singularRules); } return $this; } final public function withPluralRules(?Ruleset $pluralRules, bool $reset = false): LanguageInflectorFactory { if ($reset) { $this->pluralRulesets = []; } if ($pluralRules instanceof Ruleset) { array_unshift($this->pluralRulesets, $pluralRules); } return $this; } abstract protected function getSingularRuleset(): Ruleset; abstract protected function getPluralRuleset(): Ruleset; } PK!dFinflector/composer.jsonnu[{ "name": "doctrine/inflector", "type": "library", "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", "keywords": ["php", "strings", "words", "manipulation", "inflector", "inflection", "uppercase", "lowercase", "singular", "plural"], "homepage": "https://www.doctrine-project.org/projects/inflector.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"} ], "require": { "php": "^7.2 || ^8.0" }, "require-dev": { "doctrine/coding-standard": "^11.0", "phpstan/phpstan": "^1.8", "phpstan/phpstan-phpunit": "^1.1", "phpstan/phpstan-strict-rules": "^1.3", "phpunit/phpunit": "^8.5 || ^9.5", "vimeo/psalm": "^4.25 || ^5.4" }, "autoload": { "psr-4": { "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" } }, "autoload-dev": { "psr-4": { "Doctrine\\Tests\\Inflector\\": "tests/Doctrine/Tests/Inflector" } }, "config": { "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true } } } PK!9))inflector/LICENSEnu[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. PK!w)Q  inflector/README.mdnu[# Doctrine Inflector Doctrine Inflector is a small library that can perform string manipulations with regard to uppercase/lowercase and singular/plural forms of words. [![Build Status](https://github.com/doctrine/inflector/workflows/Continuous%20Integration/badge.svg)](https://github.com/doctrine/inflector/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A4.0.x) [![Code Coverage](https://codecov.io/gh/doctrine/inflector/branch/2.0.x/graph/badge.svg)](https://codecov.io/gh/doctrine/inflector/branch/2.0.x) PK!W||inflector/docs/en/index.rstnu[Introduction ============ The Doctrine Inflector has methods for inflecting text. The features include pluralization, singularization, converting between camelCase and under_score and capitalizing words. Installation ============ You can install the Inflector with composer: .. code-block:: console $ composer require doctrine/inflector Usage ===== Using the inflector is easy, you can create a new ``Doctrine\Inflector\Inflector`` instance by using the ``Doctrine\Inflector\InflectorFactory`` class: .. code-block:: php use Doctrine\Inflector\InflectorFactory; $inflector = InflectorFactory::create()->build(); By default it will create an English inflector. If you want to use another language, just pass the language you want to create an inflector for to the ``createForLanguage()`` method: .. code-block:: php use Doctrine\Inflector\InflectorFactory; use Doctrine\Inflector\Language; $inflector = InflectorFactory::createForLanguage(Language::SPANISH)->build(); The supported languages are as follows: - ``Language::ENGLISH`` - ``Language::FRENCH`` - ``Language::NORWEGIAN_BOKMAL`` - ``Language::PORTUGUESE`` - ``Language::SPANISH`` - ``Language::TURKISH`` If you want to manually construct the inflector instead of using a factory, you can do so like this: .. code-block:: php use Doctrine\Inflector\CachedWordInflector; use Doctrine\Inflector\RulesetInflector; use Doctrine\Inflector\Rules\English; $inflector = new Inflector( new CachedWordInflector(new RulesetInflector( English\Rules::getSingularRuleset() )), new CachedWordInflector(new RulesetInflector( English\Rules::getPluralRuleset() )) ); Adding Languages ---------------- If you are interested in adding support for your language, take a look at the other languages defined in the ``Doctrine\Inflector\Rules`` namespace and the tests located in ``Doctrine\Tests\Inflector\Rules``. You can copy one of the languages and update the rules for your language. Once you have done this, send a pull request to the ``doctrine/inflector`` repository with the additions. Custom Setup ============ If you want to setup custom singular and plural rules, you can configure these in the factory: .. code-block:: php use Doctrine\Inflector\InflectorFactory; use Doctrine\Inflector\Rules\Pattern; use Doctrine\Inflector\Rules\Patterns; use Doctrine\Inflector\Rules\Ruleset; use Doctrine\Inflector\Rules\Substitution; use Doctrine\Inflector\Rules\Substitutions; use Doctrine\Inflector\Rules\Transformation; use Doctrine\Inflector\Rules\Transformations; use Doctrine\Inflector\Rules\Word; $inflector = InflectorFactory::create() ->withSingularRules( new Ruleset( new Transformations( new Transformation(new Pattern('/^(bil)er$/i'), '\1'), new Transformation(new Pattern('/^(inflec|contribu)tors$/i'), '\1ta') ), new Patterns(new Pattern('singulars')), new Substitutions(new Substitution(new Word('spins'), new Word('spinor'))) ) ) ->withPluralRules( new Ruleset( new Transformations( new Transformation(new Pattern('^(bil)er$'), '\1'), new Transformation(new Pattern('^(inflec|contribu)tors$'), '\1ta') ), new Patterns(new Pattern('noflect'), new Pattern('abtuse')), new Substitutions( new Substitution(new Word('amaze'), new Word('amazable')), new Substitution(new Word('phone'), new Word('phonezes')) ) ) ) ->build(); No operation inflector ---------------------- The ``Doctrine\Inflector\NoopWordInflector`` may be used to configure an inflector that doesn't perform any operation for pluralization and/or singularization. If will simply return the input as output. This is an implementation of the `Null Object design pattern `_. .. code-block:: php use Doctrine\Inflector\Inflector; use Doctrine\Inflector\NoopWordInflector; $inflector = new Inflector(new NoopWordInflector(), new NoopWordInflector()); Tableize ======== Converts ``ModelName`` to ``model_name``: .. code-block:: php echo $inflector->tableize('ModelName'); // model_name Classify ======== Converts ``model_name`` to ``ModelName``: .. code-block:: php echo $inflector->classify('model_name'); // ModelName Camelize ======== This method uses `Classify`_ and then converts the first character to lowercase: .. code-block:: php echo $inflector->camelize('model_name'); // modelName Capitalize ========== Takes a string and capitalizes all of the words, like PHP's built-in ``ucwords`` function. This extends that behavior, however, by allowing the word delimiters to be configured, rather than only separating on whitespace. Here is an example: .. code-block:: php $string = 'top-o-the-morning to all_of_you!'; echo $inflector->capitalize($string); // Top-O-The-Morning To All_of_you! echo $inflector->capitalize($string, '-_ '); // Top-O-The-Morning To All_Of_You! Pluralize ========= Returns a word in plural form. .. code-block:: php echo $inflector->pluralize('browser'); // browsers Singularize =========== Returns a word in singular form. .. code-block:: php echo $inflector->singularize('browsers'); // browser Urlize ====== Generate a URL friendly string from a string of text: .. code-block:: php echo $inflector->urlize('My first blog post'); // my-first-blog-post Unaccent ======== You can unaccent a string of text using the ``unaccent()`` method: .. code-block:: php echo $inflector->unaccent('año'); // ano Legacy API ========== The API present in Inflector 1.x is still available, but will be deprecated in a future release and dropped for 3.0. Support for languages other than English is available in the 2.0 API only. Acknowledgements ================ The language rules in this library have been adapted from several different sources, including but not limited to: - `Ruby On Rails Inflector `_ - `ICanBoogie Inflector `_ - `CakePHP Inflector `_ PK!&Pannotations/lib/Doctrine/Common/Annotations/ImplicitlyIgnoredAnnotationNames.phpnu[ true, 'Attribute' => true, 'Attributes' => true, /* Can we enable this? 'Enum' => true, */ 'Required' => true, 'Target' => true, 'NamedArgumentConstructor' => true, ]; private const WidelyUsedNonStandard = [ 'fix' => true, 'fixme' => true, 'override' => true, ]; private const PhpDocumentor1 = [ 'abstract' => true, 'access' => true, 'code' => true, 'deprec' => true, 'endcode' => true, 'exception' => true, 'final' => true, 'ingroup' => true, 'inheritdoc' => true, 'inheritDoc' => true, 'magic' => true, 'name' => true, 'private' => true, 'static' => true, 'staticvar' => true, 'staticVar' => true, 'toc' => true, 'tutorial' => true, 'throw' => true, ]; private const PhpDocumentor2 = [ 'api' => true, 'author' => true, 'category' => true, 'copyright' => true, 'deprecated' => true, 'example' => true, 'filesource' => true, 'global' => true, 'ignore' => true, /* Can we enable this? 'index' => true, */ 'internal' => true, 'license' => true, 'link' => true, 'method' => true, 'package' => true, 'param' => true, 'property' => true, 'property-read' => true, 'property-write' => true, 'return' => true, 'see' => true, 'since' => true, 'source' => true, 'subpackage' => true, 'throws' => true, 'todo' => true, 'TODO' => true, 'usedby' => true, 'uses' => true, 'var' => true, 'version' => true, ]; private const PHPUnit = [ 'author' => true, 'after' => true, 'afterClass' => true, 'backupGlobals' => true, 'backupStaticAttributes' => true, 'before' => true, 'beforeClass' => true, 'codeCoverageIgnore' => true, 'codeCoverageIgnoreStart' => true, 'codeCoverageIgnoreEnd' => true, 'covers' => true, 'coversDefaultClass' => true, 'coversNothing' => true, 'dataProvider' => true, 'depends' => true, 'doesNotPerformAssertions' => true, 'expectedException' => true, 'expectedExceptionCode' => true, 'expectedExceptionMessage' => true, 'expectedExceptionMessageRegExp' => true, 'group' => true, 'large' => true, 'medium' => true, 'preserveGlobalState' => true, 'requires' => true, 'runTestsInSeparateProcesses' => true, 'runInSeparateProcess' => true, 'small' => true, 'test' => true, 'testdox' => true, 'testWith' => true, 'ticket' => true, 'uses' => true, ]; private const PhpCheckStyle = ['SuppressWarnings' => true]; private const PhpStorm = ['noinspection' => true]; private const PEAR = ['package_version' => true]; private const PlainUML = [ 'startuml' => true, 'enduml' => true, ]; private const Symfony = ['experimental' => true]; private const PhpCodeSniffer = [ 'codingStandardsIgnoreStart' => true, 'codingStandardsIgnoreEnd' => true, ]; private const SlevomatCodingStandard = ['phpcsSuppress' => true]; private const Phan = ['suppress' => true]; private const Rector = ['noRector' => true]; private const StaticAnalysis = [ // PHPStan, Psalm 'extends' => true, 'implements' => true, 'readonly' => true, 'template' => true, 'use' => true, // Psalm 'pure' => true, 'immutable' => true, ]; public const LIST = self::Reserved + self::WidelyUsedNonStandard + self::PhpDocumentor1 + self::PhpDocumentor2 + self::PHPUnit + self::PhpCheckStyle + self::PhpStorm + self::PEAR + self::PlainUML + self::Symfony + self::SlevomatCodingStandard + self::PhpCodeSniffer + self::Phan + self::Rector + self::StaticAnalysis; private function __construct() { } } PK!U,,@annotations/lib/Doctrine/Common/Annotations/AnnotationReader.phpnu[ */ private static $globalImports = [ 'ignoreannotation' => Annotation\IgnoreAnnotation::class, ]; /** * A list with annotations that are not causing exceptions when not resolved to an annotation class. * * The names are case sensitive. * * @var array */ private static $globalIgnoredNames = ImplicitlyIgnoredAnnotationNames::LIST; /** * A list with annotations that are not causing exceptions when not resolved to an annotation class. * * The names are case sensitive. * * @var array */ private static $globalIgnoredNamespaces = []; /** * Add a new annotation to the globally ignored annotation names with regard to exception handling. * * @param string $name */ public static function addGlobalIgnoredName($name) { self::$globalIgnoredNames[$name] = true; } /** * Add a new annotation to the globally ignored annotation namespaces with regard to exception handling. * * @param string $namespace */ public static function addGlobalIgnoredNamespace($namespace) { self::$globalIgnoredNamespaces[$namespace] = true; } /** * Annotations parser. * * @var DocParser */ private $parser; /** * Annotations parser used to collect parsing metadata. * * @var DocParser */ private $preParser; /** * PHP parser used to collect imports. * * @var PhpParser */ private $phpParser; /** * In-memory cache mechanism to store imported annotations per class. * * @psalm-var array<'class'|'function', array>> */ private $imports = []; /** * In-memory cache mechanism to store ignored annotations per class. * * @psalm-var array<'class'|'function', array>> */ private $ignoredAnnotationNames = []; /** * Initializes a new AnnotationReader. * * @throws AnnotationException */ public function __construct(?DocParser $parser = null) { if ( extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.save_comments') === '0' || ini_get('opcache.save_comments') === '0') ) { throw AnnotationException::optimizerPlusSaveComments(); } if (extension_loaded('Zend OPcache') && ini_get('opcache.save_comments') === 0) { throw AnnotationException::optimizerPlusSaveComments(); } // Make sure that the IgnoreAnnotation annotation is loaded class_exists(IgnoreAnnotation::class); $this->parser = $parser ?: new DocParser(); $this->preParser = new DocParser(); $this->preParser->setImports(self::$globalImports); $this->preParser->setIgnoreNotImportedAnnotations(true); $this->preParser->setIgnoredAnnotationNames(self::$globalIgnoredNames); $this->phpParser = new PhpParser(); } /** * {@inheritDoc} */ public function getClassAnnotations(ReflectionClass $class) { $this->parser->setTarget(Target::TARGET_CLASS); $this->parser->setImports($this->getImports($class)); $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces); return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName()); } /** * {@inheritDoc} */ public function getClassAnnotation(ReflectionClass $class, $annotationName) { $annotations = $this->getClassAnnotations($class); foreach ($annotations as $annotation) { if ($annotation instanceof $annotationName) { return $annotation; } } return null; } /** * {@inheritDoc} */ public function getPropertyAnnotations(ReflectionProperty $property) { $class = $property->getDeclaringClass(); $context = 'property ' . $class->getName() . '::$' . $property->getName(); $this->parser->setTarget(Target::TARGET_PROPERTY); $this->parser->setImports($this->getPropertyImports($property)); $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces); return $this->parser->parse($property->getDocComment(), $context); } /** * {@inheritDoc} */ public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) { $annotations = $this->getPropertyAnnotations($property); foreach ($annotations as $annotation) { if ($annotation instanceof $annotationName) { return $annotation; } } return null; } /** * {@inheritDoc} */ public function getMethodAnnotations(ReflectionMethod $method) { $class = $method->getDeclaringClass(); $context = 'method ' . $class->getName() . '::' . $method->getName() . '()'; $this->parser->setTarget(Target::TARGET_METHOD); $this->parser->setImports($this->getMethodImports($method)); $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces); return $this->parser->parse($method->getDocComment(), $context); } /** * {@inheritDoc} */ public function getMethodAnnotation(ReflectionMethod $method, $annotationName) { $annotations = $this->getMethodAnnotations($method); foreach ($annotations as $annotation) { if ($annotation instanceof $annotationName) { return $annotation; } } return null; } /** * Gets the annotations applied to a function. * * @phpstan-return list An array of Annotations. */ public function getFunctionAnnotations(ReflectionFunction $function): array { $context = 'function ' . $function->getName(); $this->parser->setTarget(Target::TARGET_FUNCTION); $this->parser->setImports($this->getImports($function)); $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($function)); $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces); return $this->parser->parse($function->getDocComment(), $context); } /** * Gets a function annotation. * * @return object|null The Annotation or NULL, if the requested annotation does not exist. */ public function getFunctionAnnotation(ReflectionFunction $function, string $annotationName) { $annotations = $this->getFunctionAnnotations($function); foreach ($annotations as $annotation) { if ($annotation instanceof $annotationName) { return $annotation; } } return null; } /** * Returns the ignored annotations for the given class or function. * * @param ReflectionClass|ReflectionFunction $reflection * * @return array */ private function getIgnoredAnnotationNames($reflection): array { $type = $reflection instanceof ReflectionClass ? 'class' : 'function'; $name = $reflection->getName(); if (isset($this->ignoredAnnotationNames[$type][$name])) { return $this->ignoredAnnotationNames[$type][$name]; } $this->collectParsingMetadata($reflection); return $this->ignoredAnnotationNames[$type][$name]; } /** * Retrieves imports for a class or a function. * * @param ReflectionClass|ReflectionFunction $reflection * * @return array */ private function getImports($reflection): array { $type = $reflection instanceof ReflectionClass ? 'class' : 'function'; $name = $reflection->getName(); if (isset($this->imports[$type][$name])) { return $this->imports[$type][$name]; } $this->collectParsingMetadata($reflection); return $this->imports[$type][$name]; } /** * Retrieves imports for methods. * * @return array */ private function getMethodImports(ReflectionMethod $method) { $class = $method->getDeclaringClass(); $classImports = $this->getImports($class); $traitImports = []; foreach ($class->getTraits() as $trait) { if ( ! $trait->hasMethod($method->getName()) || $trait->getFileName() !== $method->getFileName() ) { continue; } $traitImports = array_merge($traitImports, $this->phpParser->parseUseStatements($trait)); } return array_merge($classImports, $traitImports); } /** * Retrieves imports for properties. * * @return array */ private function getPropertyImports(ReflectionProperty $property) { $class = $property->getDeclaringClass(); $classImports = $this->getImports($class); $traitImports = []; foreach ($class->getTraits() as $trait) { if (! $trait->hasProperty($property->getName())) { continue; } $traitImports = array_merge($traitImports, $this->phpParser->parseUseStatements($trait)); } return array_merge($classImports, $traitImports); } /** * Collects parsing metadata for a given class or function. * * @param ReflectionClass|ReflectionFunction $reflection */ private function collectParsingMetadata($reflection): void { $type = $reflection instanceof ReflectionClass ? 'class' : 'function'; $name = $reflection->getName(); $ignoredAnnotationNames = self::$globalIgnoredNames; $annotations = $this->preParser->parse($reflection->getDocComment(), $type . ' ' . $name); foreach ($annotations as $annotation) { if (! ($annotation instanceof IgnoreAnnotation)) { continue; } foreach ($annotation->names as $annot) { $ignoredAnnotationNames[$annot] = true; } } $this->imports[$type][$name] = array_merge( self::$globalImports, $this->phpParser->parseUseStatements($reflection), [ '__NAMESPACE__' => $reflection->getNamespaceName(), 'self' => $name, ] ); $this->ignoredAnnotationNames[$type][$name] = $ignoredAnnotationNames; } } PK!l'=annotations/lib/Doctrine/Common/Annotations/IndexedReader.phpnu[delegate = $reader; } /** * {@inheritDoc} */ public function getClassAnnotations(ReflectionClass $class) { $annotations = []; foreach ($this->delegate->getClassAnnotations($class) as $annot) { $annotations[get_class($annot)] = $annot; } return $annotations; } /** * {@inheritDoc} */ public function getClassAnnotation(ReflectionClass $class, $annotationName) { return $this->delegate->getClassAnnotation($class, $annotationName); } /** * {@inheritDoc} */ public function getMethodAnnotations(ReflectionMethod $method) { $annotations = []; foreach ($this->delegate->getMethodAnnotations($method) as $annot) { $annotations[get_class($annot)] = $annot; } return $annotations; } /** * {@inheritDoc} */ public function getMethodAnnotation(ReflectionMethod $method, $annotationName) { return $this->delegate->getMethodAnnotation($method, $annotationName); } /** * {@inheritDoc} */ public function getPropertyAnnotations(ReflectionProperty $property) { $annotations = []; foreach ($this->delegate->getPropertyAnnotations($property) as $annot) { $annotations[get_class($annot)] = $annot; } return $annotations; } /** * {@inheritDoc} */ public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) { return $this->delegate->getPropertyAnnotation($property, $annotationName); } /** * Proxies all methods to the delegate. * * @param string $method * @param mixed[] $args * * @return mixed */ public function __call($method, $args) { return call_user_func_array([$this->delegate, $method], $args); } } PK!ٷ"\;annotations/lib/Doctrine/Common/Annotations/TokenParser.phpnu[ */ private $tokens; /** * The number of tokens. * * @var int */ private $numTokens; /** * The current array pointer. * * @var int */ private $pointer = 0; /** @param string $contents */ public function __construct($contents) { $this->tokens = token_get_all($contents); // The PHP parser sets internal compiler globals for certain things. Annoyingly, the last docblock comment it // saw gets stored in doc_comment. When it comes to compile the next thing to be include()d this stored // doc_comment becomes owned by the first thing the compiler sees in the file that it considers might have a // docblock. If the first thing in the file is a class without a doc block this would cause calls to // getDocBlock() on said class to return our long lost doc_comment. Argh. // To workaround, cause the parser to parse an empty docblock. Sure getDocBlock() will return this, but at least // it's harmless to us. token_get_all("numTokens = count($this->tokens); } /** * Gets the next non whitespace and non comment token. * * @param bool $docCommentIsComment If TRUE then a doc comment is considered a comment and skipped. * If FALSE then only whitespace and normal comments are skipped. * * @return mixed[]|string|null The token if exists, null otherwise. */ public function next($docCommentIsComment = true) { for ($i = $this->pointer; $i < $this->numTokens; $i++) { $this->pointer++; if ( $this->tokens[$i][0] === T_WHITESPACE || $this->tokens[$i][0] === T_COMMENT || ($docCommentIsComment && $this->tokens[$i][0] === T_DOC_COMMENT) ) { continue; } return $this->tokens[$i]; } return null; } /** * Parses a single use statement. * * @return array A list with all found class names for a use statement. */ public function parseUseStatement() { $groupRoot = ''; $class = ''; $alias = ''; $statements = []; $explicitAlias = false; while (($token = $this->next())) { if (! $explicitAlias && $token[0] === T_STRING) { $class .= $token[1]; $alias = $token[1]; } elseif ($explicitAlias && $token[0] === T_STRING) { $alias = $token[1]; } elseif ( PHP_VERSION_ID >= 80000 && ($token[0] === T_NAME_QUALIFIED || $token[0] === T_NAME_FULLY_QUALIFIED) ) { $class .= $token[1]; $classSplit = explode('\\', $token[1]); $alias = $classSplit[count($classSplit) - 1]; } elseif ($token[0] === T_NS_SEPARATOR) { $class .= '\\'; $alias = ''; } elseif ($token[0] === T_AS) { $explicitAlias = true; $alias = ''; } elseif ($token === ',') { $statements[strtolower($alias)] = $groupRoot . $class; $class = ''; $alias = ''; $explicitAlias = false; } elseif ($token === ';') { $statements[strtolower($alias)] = $groupRoot . $class; break; } elseif ($token === '{') { $groupRoot = $class; $class = ''; } elseif ($token === '}') { continue; } else { break; } } return $statements; } /** * Gets all use statements. * * @param string $namespaceName The namespace name of the reflected class. * * @return array A list with all found use statements. */ public function parseUseStatements($namespaceName) { $statements = []; while (($token = $this->next())) { if ($token[0] === T_USE) { $statements = array_merge($statements, $this->parseUseStatement()); continue; } if ($token[0] !== T_NAMESPACE || $this->parseNamespace() !== $namespaceName) { continue; } // Get fresh array for new namespace. This is to prevent the parser to collect the use statements // for a previous namespace with the same name. This is the case if a namespace is defined twice // or if a namespace with the same name is commented out. $statements = []; } return $statements; } /** * Gets the namespace. * * @return string The found namespace. */ public function parseNamespace() { $name = ''; while ( ($token = $this->next()) && ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR || ( PHP_VERSION_ID >= 80000 && ($token[0] === T_NAME_QUALIFIED || $token[0] === T_NAME_FULLY_QUALIFIED) )) ) { $name .= $token[1]; } return $name; } /** * Gets the class name. * * @return string The found class name. */ public function parseClass() { // Namespaces and class names are tokenized the same: T_STRINGs // separated by T_NS_SEPARATOR so we can use one function to provide // both. return $this->parseNamespace(); } } PK!u8annotations/lib/Doctrine/Common/Annotations/DocLexer.phpnu[ */ final class DocLexer extends AbstractLexer { public const T_NONE = 1; public const T_INTEGER = 2; public const T_STRING = 3; public const T_FLOAT = 4; // All tokens that are also identifiers should be >= 100 public const T_IDENTIFIER = 100; public const T_AT = 101; public const T_CLOSE_CURLY_BRACES = 102; public const T_CLOSE_PARENTHESIS = 103; public const T_COMMA = 104; public const T_EQUALS = 105; public const T_FALSE = 106; public const T_NAMESPACE_SEPARATOR = 107; public const T_OPEN_CURLY_BRACES = 108; public const T_OPEN_PARENTHESIS = 109; public const T_TRUE = 110; public const T_NULL = 111; public const T_COLON = 112; public const T_MINUS = 113; /** @var array */ protected $noCase = [ '@' => self::T_AT, ',' => self::T_COMMA, '(' => self::T_OPEN_PARENTHESIS, ')' => self::T_CLOSE_PARENTHESIS, '{' => self::T_OPEN_CURLY_BRACES, '}' => self::T_CLOSE_CURLY_BRACES, '=' => self::T_EQUALS, ':' => self::T_COLON, '-' => self::T_MINUS, '\\' => self::T_NAMESPACE_SEPARATOR, ]; /** @var array */ protected $withCase = [ 'true' => self::T_TRUE, 'false' => self::T_FALSE, 'null' => self::T_NULL, ]; /** * Whether the next token starts immediately, or if there were * non-captured symbols before that */ public function nextTokenIsAdjacent(): bool { return $this->token === null || ($this->lookahead !== null && ($this->lookahead['position'] - $this->token['position']) === strlen($this->token['value'])); } /** * {@inheritdoc} */ protected function getCatchablePatterns() { return [ '[a-z_\\\][a-z0-9_\:\\\]*[a-z_][a-z0-9_]*', '(?:[+-]?[0-9]+(?:[\.][0-9]+)*)(?:[eE][+-]?[0-9]+)?', '"(?:""|[^"])*+"', ]; } /** * {@inheritdoc} */ protected function getNonCatchablePatterns() { return ['\s+', '\*+', '(.)']; } /** * {@inheritdoc} */ protected function getType(&$value) { $type = self::T_NONE; if ($value[0] === '"') { $value = str_replace('""', '"', substr($value, 1, strlen($value) - 2)); return self::T_STRING; } if (isset($this->noCase[$value])) { return $this->noCase[$value]; } if ($value[0] === '_' || $value[0] === '\\' || ctype_alpha($value[0])) { return self::T_IDENTIFIER; } $lowerValue = strtolower($value); if (isset($this->withCase[$lowerValue])) { return $this->withCase[$lowerValue]; } // Checking numeric value if (is_numeric($value)) { return strpos($value, '.') !== false || stripos($value, 'e') !== false ? self::T_FLOAT : self::T_INTEGER; } return $type; } /** @return array{value: int|string, type:self::T_*|null, position:int} */ public function peek(): ?array { $token = parent::peek(); if ($token === null) { return null; } return (array) $token; } } PK!sfk!!?annotations/lib/Doctrine/Common/Annotations/FileCacheReader.phpnu[> */ private $loadedAnnotations = []; /** @var array */ private $classNameHashes = []; /** @var int */ private $umask; /** * @param string $cacheDir * @param bool $debug * @param int $umask * * @throws InvalidArgumentException */ public function __construct(Reader $reader, $cacheDir, $debug = false, $umask = 0002) { if (! is_int($umask)) { throw new InvalidArgumentException(sprintf( 'The parameter umask must be an integer, was: %s', gettype($umask) )); } $this->reader = $reader; $this->umask = $umask; if (! is_dir($cacheDir) && ! @mkdir($cacheDir, 0777 & (~$this->umask), true)) { throw new InvalidArgumentException(sprintf( 'The directory "%s" does not exist and could not be created.', $cacheDir )); } $this->dir = rtrim($cacheDir, '\\/'); $this->debug = $debug; } /** * {@inheritDoc} */ public function getClassAnnotations(ReflectionClass $class) { if (! isset($this->classNameHashes[$class->name])) { $this->classNameHashes[$class->name] = sha1($class->name); } $key = $this->classNameHashes[$class->name]; if (isset($this->loadedAnnotations[$key])) { return $this->loadedAnnotations[$key]; } $path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php'; if (! is_file($path)) { $annot = $this->reader->getClassAnnotations($class); $this->saveCacheFile($path, $annot); return $this->loadedAnnotations[$key] = $annot; } $filename = $class->getFilename(); if ( $this->debug && $filename !== false && filemtime($path) < filemtime($filename) ) { @unlink($path); $annot = $this->reader->getClassAnnotations($class); $this->saveCacheFile($path, $annot); return $this->loadedAnnotations[$key] = $annot; } return $this->loadedAnnotations[$key] = include $path; } /** * {@inheritDoc} */ public function getPropertyAnnotations(ReflectionProperty $property) { $class = $property->getDeclaringClass(); if (! isset($this->classNameHashes[$class->name])) { $this->classNameHashes[$class->name] = sha1($class->name); } $key = $this->classNameHashes[$class->name] . '$' . $property->getName(); if (isset($this->loadedAnnotations[$key])) { return $this->loadedAnnotations[$key]; } $path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php'; if (! is_file($path)) { $annot = $this->reader->getPropertyAnnotations($property); $this->saveCacheFile($path, $annot); return $this->loadedAnnotations[$key] = $annot; } $filename = $class->getFilename(); if ( $this->debug && $filename !== false && filemtime($path) < filemtime($filename) ) { @unlink($path); $annot = $this->reader->getPropertyAnnotations($property); $this->saveCacheFile($path, $annot); return $this->loadedAnnotations[$key] = $annot; } return $this->loadedAnnotations[$key] = include $path; } /** * {@inheritDoc} */ public function getMethodAnnotations(ReflectionMethod $method) { $class = $method->getDeclaringClass(); if (! isset($this->classNameHashes[$class->name])) { $this->classNameHashes[$class->name] = sha1($class->name); } $key = $this->classNameHashes[$class->name] . '#' . $method->getName(); if (isset($this->loadedAnnotations[$key])) { return $this->loadedAnnotations[$key]; } $path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php'; if (! is_file($path)) { $annot = $this->reader->getMethodAnnotations($method); $this->saveCacheFile($path, $annot); return $this->loadedAnnotations[$key] = $annot; } $filename = $class->getFilename(); if ( $this->debug && $filename !== false && filemtime($path) < filemtime($filename) ) { @unlink($path); $annot = $this->reader->getMethodAnnotations($method); $this->saveCacheFile($path, $annot); return $this->loadedAnnotations[$key] = $annot; } return $this->loadedAnnotations[$key] = include $path; } /** * Saves the cache file. * * @param string $path * @param mixed $data * * @return void */ private function saveCacheFile($path, $data) { if (! is_writable($this->dir)) { throw new InvalidArgumentException(sprintf( <<<'EXCEPTION' The directory "%s" is not writable. Both the webserver and the console user need access. You can manage access rights for multiple users with "chmod +a". If your system does not support this, check out the acl package., EXCEPTION , $this->dir )); } $tempfile = tempnam($this->dir, uniqid('', true)); if ($tempfile === false) { throw new RuntimeException(sprintf('Unable to create tempfile in directory: %s', $this->dir)); } @chmod($tempfile, 0666 & (~$this->umask)); $written = file_put_contents( $tempfile, 'umask)); if (rename($tempfile, $path) === false) { @unlink($tempfile); throw new RuntimeException(sprintf('Unable to rename %s to %s', $tempfile, $path)); } } /** * {@inheritDoc} */ public function getClassAnnotation(ReflectionClass $class, $annotationName) { $annotations = $this->getClassAnnotations($class); foreach ($annotations as $annotation) { if ($annotation instanceof $annotationName) { return $annotation; } } return null; } /** * {@inheritDoc} */ public function getMethodAnnotation(ReflectionMethod $method, $annotationName) { $annotations = $this->getMethodAnnotations($method); foreach ($annotations as $annotation) { if ($annotation instanceof $annotationName) { return $annotation; } } return null; } /** * {@inheritDoc} */ public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) { $annotations = $this->getPropertyAnnotations($property); foreach ($annotations as $annotation) { if ($annotation instanceof $annotationName) { return $annotation; } } return null; } /** * Clears loaded annotations. * * @return void */ public function clearLoadedAnnotations() { $this->loadedAnnotations = []; } } PK!χQQBannotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.phpnu[|null $dirs */ public static function registerAutoloadNamespace(string $namespace, $dirs = null): void { self::$autoloadNamespaces[$namespace] = $dirs; } /** * Registers multiple namespaces. * * Loading of this namespaces will be done with a PSR-0 namespace loading algorithm. * * @deprecated This method is deprecated and will be removed in * doctrine/annotations 2.0. Annotations will be autoloaded in 2.0. * * @param string[][]|string[]|null[] $namespaces indexed by namespace name */ public static function registerAutoloadNamespaces(array $namespaces): void { self::$autoloadNamespaces = array_merge(self::$autoloadNamespaces, $namespaces); } /** * Registers an autoloading callable for annotations, much like spl_autoload_register(). * * NOTE: These class loaders HAVE to be silent when a class was not found! * IMPORTANT: Loaders have to return true if they loaded a class that could contain the searched annotation class. * * @deprecated This method is deprecated and will be removed in * doctrine/annotations 2.0. Annotations will be autoloaded in 2.0. */ public static function registerLoader(callable $callable): void { // Reset our static cache now that we have a new loader to work with self::$failedToAutoload = []; self::$loaders[] = $callable; } /** * Registers an autoloading callable for annotations, if it is not already registered * * @deprecated This method is deprecated and will be removed in * doctrine/annotations 2.0. Annotations will be autoloaded in 2.0. */ public static function registerUniqueLoader(callable $callable): void { if (in_array($callable, self::$loaders, true)) { return; } self::registerLoader($callable); } /** * Autoloads an annotation class silently. */ public static function loadAnnotationClass(string $class): bool { if (class_exists($class, false)) { return true; } if (array_key_exists($class, self::$failedToAutoload)) { return false; } foreach (self::$autoloadNamespaces as $namespace => $dirs) { if (strpos($class, $namespace) !== 0) { continue; } $file = str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php'; if ($dirs === null) { $path = stream_resolve_include_path($file); if ($path) { require $path; return true; } } else { foreach ((array) $dirs as $dir) { if (is_file($dir . DIRECTORY_SEPARATOR . $file)) { require $dir . DIRECTORY_SEPARATOR . $file; return true; } } } } foreach (self::$loaders as $loader) { if ($loader($class) === true) { return true; } } if ( self::$loaders === [] && self::$autoloadNamespaces === [] && self::$registerFileUsed === false && class_exists($class) ) { return true; } self::$failedToAutoload[$class] = null; return false; } } PK!t4ZZRannotations/lib/Doctrine/Common/Annotations/NamedArgumentConstructorAnnotation.phpnu[ $data Key-value for properties to be defined in this class. */ final public function __construct(array $data) { foreach ($data as $key => $value) { $this->$key = $value; } } /** * Error handler for unknown property accessor in Annotation class. * * @param string $name Unknown property name. * * @throws BadMethodCallException */ public function __get($name) { throw new BadMethodCallException( sprintf("Unknown property '%s' on annotation '%s'.", $name, static::class) ); } /** * Error handler for unknown property mutator in Annotation class. * * @param string $name Unknown property name. * @param mixed $value Property value. * * @throws BadMethodCallException */ public function __set($name, $value) { throw new BadMethodCallException( sprintf("Unknown property '%s' on annotation '%s'.", $name, static::class) ); } } PK!v* 9annotations/lib/Doctrine/Common/Annotations/PhpParser.phpnu[ReflectionClass object. * * @return array A list with use statements in the form (Alias => FQN). */ public function parseClass(ReflectionClass $class) { return $this->parseUseStatements($class); } /** * Parse a class or function for use statements. * * @param ReflectionClass|ReflectionFunction $reflection * * @psalm-return array a list with use statements in the form (Alias => FQN). */ public function parseUseStatements($reflection): array { if (method_exists($reflection, 'getUseStatements')) { return $reflection->getUseStatements(); } $filename = $reflection->getFileName(); if ($filename === false) { return []; } $content = $this->getFileContent($filename, $reflection->getStartLine()); if ($content === null) { return []; } $namespace = preg_quote($reflection->getNamespaceName()); $content = preg_replace('/^.*?(\bnamespace\s+' . $namespace . '\s*[;{].*)$/s', '\\1', $content); $tokenizer = new TokenParser('parseUseStatements($reflection->getNamespaceName()); } /** * Gets the content of the file right up to the given line number. * * @param string $filename The name of the file to load. * @param int $lineNumber The number of lines to read from file. * * @return string|null The content of the file or null if the file does not exist. */ private function getFileContent($filename, $lineNumber) { if (! is_file($filename)) { return null; } $content = ''; $lineCnt = 0; $file = new SplFileObject($filename); while (! $file->eof()) { if ($lineCnt++ === $lineNumber) { break; } $content .= $file->fgets(); } return $content; } } PK!,RR<annotations/lib/Doctrine/Common/Annotations/CachedReader.phpnu[> */ private $loadedAnnotations = []; /** @var int[] */ private $loadedFilemtimes = []; /** @param bool $debug */ public function __construct(Reader $reader, Cache $cache, $debug = false) { $this->delegate = $reader; $this->cache = $cache; $this->debug = (bool) $debug; } /** * {@inheritDoc} */ public function getClassAnnotations(ReflectionClass $class) { $cacheKey = $class->getName(); if (isset($this->loadedAnnotations[$cacheKey])) { return $this->loadedAnnotations[$cacheKey]; } $annots = $this->fetchFromCache($cacheKey, $class); if ($annots === false) { $annots = $this->delegate->getClassAnnotations($class); $this->saveToCache($cacheKey, $annots); } return $this->loadedAnnotations[$cacheKey] = $annots; } /** * {@inheritDoc} */ public function getClassAnnotation(ReflectionClass $class, $annotationName) { foreach ($this->getClassAnnotations($class) as $annot) { if ($annot instanceof $annotationName) { return $annot; } } return null; } /** * {@inheritDoc} */ public function getPropertyAnnotations(ReflectionProperty $property) { $class = $property->getDeclaringClass(); $cacheKey = $class->getName() . '$' . $property->getName(); if (isset($this->loadedAnnotations[$cacheKey])) { return $this->loadedAnnotations[$cacheKey]; } $annots = $this->fetchFromCache($cacheKey, $class); if ($annots === false) { $annots = $this->delegate->getPropertyAnnotations($property); $this->saveToCache($cacheKey, $annots); } return $this->loadedAnnotations[$cacheKey] = $annots; } /** * {@inheritDoc} */ public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) { foreach ($this->getPropertyAnnotations($property) as $annot) { if ($annot instanceof $annotationName) { return $annot; } } return null; } /** * {@inheritDoc} */ public function getMethodAnnotations(ReflectionMethod $method) { $class = $method->getDeclaringClass(); $cacheKey = $class->getName() . '#' . $method->getName(); if (isset($this->loadedAnnotations[$cacheKey])) { return $this->loadedAnnotations[$cacheKey]; } $annots = $this->fetchFromCache($cacheKey, $class); if ($annots === false) { $annots = $this->delegate->getMethodAnnotations($method); $this->saveToCache($cacheKey, $annots); } return $this->loadedAnnotations[$cacheKey] = $annots; } /** * {@inheritDoc} */ public function getMethodAnnotation(ReflectionMethod $method, $annotationName) { foreach ($this->getMethodAnnotations($method) as $annot) { if ($annot instanceof $annotationName) { return $annot; } } return null; } /** * Clears loaded annotations. * * @return void */ public function clearLoadedAnnotations() { $this->loadedAnnotations = []; $this->loadedFilemtimes = []; } /** * Fetches a value from the cache. * * @param string $cacheKey The cache key. * * @return mixed The cached value or false when the value is not in cache. */ private function fetchFromCache($cacheKey, ReflectionClass $class) { $data = $this->cache->fetch($cacheKey); if ($data !== false) { if (! $this->debug || $this->isCacheFresh($cacheKey, $class)) { return $data; } } return false; } /** * Saves a value to the cache. * * @param string $cacheKey The cache key. * @param mixed $value The value. * * @return void */ private function saveToCache($cacheKey, $value) { $this->cache->save($cacheKey, $value); if (! $this->debug) { return; } $this->cache->save('[C]' . $cacheKey, time()); } /** * Checks if the cache is fresh. * * @param string $cacheKey * * @return bool */ private function isCacheFresh($cacheKey, ReflectionClass $class) { $lastModification = $this->getLastModification($class); if ($lastModification === 0) { return true; } return $this->cache->fetch('[C]' . $cacheKey) >= $lastModification; } /** * Returns the time the class was last modified, testing traits and parents */ private function getLastModification(ReflectionClass $class): int { $filename = $class->getFileName(); if (isset($this->loadedFilemtimes[$filename])) { return $this->loadedFilemtimes[$filename]; } $parent = $class->getParentClass(); $lastModification = max(array_merge( [$filename ? filemtime($filename) : 0], array_map(function (ReflectionClass $reflectionTrait): int { return $this->getTraitLastModificationTime($reflectionTrait); }, $class->getTraits()), array_map(function (ReflectionClass $class): int { return $this->getLastModification($class); }, $class->getInterfaces()), $parent ? [$this->getLastModification($parent)] : [] )); assert($lastModification !== false); return $this->loadedFilemtimes[$filename] = $lastModification; } private function getTraitLastModificationTime(ReflectionClass $reflectionTrait): int { $fileName = $reflectionTrait->getFileName(); if (isset($this->loadedFilemtimes[$fileName])) { return $this->loadedFilemtimes[$fileName]; } $lastModificationTime = max(array_merge( [$fileName ? filemtime($fileName) : 0], array_map(function (ReflectionClass $reflectionTrait): int { return $this->getTraitLastModificationTime($reflectionTrait); }, $reflectionTrait->getTraits()) )); assert($lastModificationTime !== false); return $this->loadedFilemtimes[$fileName] = $lastModificationTime; } } PK!(@v9annotations/lib/Doctrine/Common/Annotations/DocParser.phpnu[ */ private static $classIdentifiers = [ DocLexer::T_IDENTIFIER, DocLexer::T_TRUE, DocLexer::T_FALSE, DocLexer::T_NULL, ]; /** * The lexer. * * @var DocLexer */ private $lexer; /** * Current target context. * * @var int */ private $target; /** * Doc parser used to collect annotation target. * * @var DocParser */ private static $metadataParser; /** * Flag to control if the current annotation is nested or not. * * @var bool */ private $isNestedAnnotation = false; /** * Hashmap containing all use-statements that are to be used when parsing * the given doc block. * * @var array */ private $imports = []; /** * This hashmap is used internally to cache results of class_exists() * look-ups. * * @var array */ private $classExists = []; /** * Whether annotations that have not been imported should be ignored. * * @var bool */ private $ignoreNotImportedAnnotations = false; /** * An array of default namespaces if operating in simple mode. * * @var string[] */ private $namespaces = []; /** * A list with annotations that are not causing exceptions when not resolved to an annotation class. * * The names must be the raw names as used in the class, not the fully qualified * * @var bool[] indexed by annotation name */ private $ignoredAnnotationNames = []; /** * A list with annotations in namespaced format * that are not causing exceptions when not resolved to an annotation class. * * @var bool[] indexed by namespace name */ private $ignoredAnnotationNamespaces = []; /** @var string */ private $context = ''; /** * Hash-map for caching annotation metadata. * * @var array */ private static $annotationMetadata = [ Annotation\Target::class => [ 'is_annotation' => true, 'has_constructor' => true, 'has_named_argument_constructor' => false, 'properties' => [], 'targets_literal' => 'ANNOTATION_CLASS', 'targets' => Target::TARGET_CLASS, 'default_property' => 'value', 'attribute_types' => [ 'value' => [ 'required' => false, 'type' => 'array', 'array_type' => 'string', 'value' => 'array', ], ], ], Annotation\Attribute::class => [ 'is_annotation' => true, 'has_constructor' => false, 'has_named_argument_constructor' => false, 'targets_literal' => 'ANNOTATION_ANNOTATION', 'targets' => Target::TARGET_ANNOTATION, 'default_property' => 'name', 'properties' => [ 'name' => 'name', 'type' => 'type', 'required' => 'required', ], 'attribute_types' => [ 'value' => [ 'required' => true, 'type' => 'string', 'value' => 'string', ], 'type' => [ 'required' => true, 'type' => 'string', 'value' => 'string', ], 'required' => [ 'required' => false, 'type' => 'boolean', 'value' => 'boolean', ], ], ], Annotation\Attributes::class => [ 'is_annotation' => true, 'has_constructor' => false, 'has_named_argument_constructor' => false, 'targets_literal' => 'ANNOTATION_CLASS', 'targets' => Target::TARGET_CLASS, 'default_property' => 'value', 'properties' => ['value' => 'value'], 'attribute_types' => [ 'value' => [ 'type' => 'array', 'required' => true, 'array_type' => Annotation\Attribute::class, 'value' => 'array<' . Annotation\Attribute::class . '>', ], ], ], Annotation\Enum::class => [ 'is_annotation' => true, 'has_constructor' => true, 'has_named_argument_constructor' => false, 'targets_literal' => 'ANNOTATION_PROPERTY', 'targets' => Target::TARGET_PROPERTY, 'default_property' => 'value', 'properties' => ['value' => 'value'], 'attribute_types' => [ 'value' => [ 'type' => 'array', 'required' => true, ], 'literal' => [ 'type' => 'array', 'required' => false, ], ], ], Annotation\NamedArgumentConstructor::class => [ 'is_annotation' => true, 'has_constructor' => false, 'has_named_argument_constructor' => false, 'targets_literal' => 'ANNOTATION_CLASS', 'targets' => Target::TARGET_CLASS, 'default_property' => null, 'properties' => [], 'attribute_types' => [], ], ]; /** * Hash-map for handle types declaration. * * @var array */ private static $typeMap = [ 'float' => 'double', 'bool' => 'boolean', // allow uppercase Boolean in honor of George Boole 'Boolean' => 'boolean', 'int' => 'integer', ]; /** * Constructs a new DocParser. */ public function __construct() { $this->lexer = new DocLexer(); } /** * Sets the annotation names that are ignored during the parsing process. * * The names are supposed to be the raw names as used in the class, not the * fully qualified class names. * * @param bool[] $names indexed by annotation name * * @return void */ public function setIgnoredAnnotationNames(array $names) { $this->ignoredAnnotationNames = $names; } /** * Sets the annotation namespaces that are ignored during the parsing process. * * @param bool[] $ignoredAnnotationNamespaces indexed by annotation namespace name * * @return void */ public function setIgnoredAnnotationNamespaces($ignoredAnnotationNamespaces) { $this->ignoredAnnotationNamespaces = $ignoredAnnotationNamespaces; } /** * Sets ignore on not-imported annotations. * * @param bool $bool * * @return void */ public function setIgnoreNotImportedAnnotations($bool) { $this->ignoreNotImportedAnnotations = (bool) $bool; } /** * Sets the default namespaces. * * @param string $namespace * * @return void * * @throws RuntimeException */ public function addNamespace($namespace) { if ($this->imports) { throw new RuntimeException('You must either use addNamespace(), or setImports(), but not both.'); } $this->namespaces[] = $namespace; } /** * Sets the imports. * * @param array $imports * * @return void * * @throws RuntimeException */ public function setImports(array $imports) { if ($this->namespaces) { throw new RuntimeException('You must either use addNamespace(), or setImports(), but not both.'); } $this->imports = $imports; } /** * Sets current target context as bitmask. * * @param int $target * * @return void */ public function setTarget($target) { $this->target = $target; } /** * Parses the given docblock string for annotations. * * @param string $input The docblock string to parse. * @param string $context The parsing context. * * @phpstan-return list Array of annotations. If no annotations are found, an empty array is returned. * * @throws AnnotationException * @throws ReflectionException */ public function parse($input, $context = '') { $pos = $this->findInitialTokenPosition($input); if ($pos === null) { return []; } $this->context = $context; $this->lexer->setInput(trim(substr($input, $pos), '* /')); $this->lexer->moveNext(); return $this->Annotations(); } /** * Finds the first valid annotation * * @param string $input The docblock string to parse */ private function findInitialTokenPosition($input): ?int { $pos = 0; // search for first valid annotation while (($pos = strpos($input, '@', $pos)) !== false) { $preceding = substr($input, $pos - 1, 1); // if the @ is preceded by a space, a tab or * it is valid if ($pos === 0 || $preceding === ' ' || $preceding === '*' || $preceding === "\t") { return $pos; } $pos++; } return null; } /** * Attempts to match the given token with the current lookahead token. * If they match, updates the lookahead token; otherwise raises a syntax error. * * @param int $token Type of token. * * @return bool True if tokens match; false otherwise. * * @throws AnnotationException */ private function match(int $token): bool { if (! $this->lexer->isNextToken($token)) { throw $this->syntaxError($this->lexer->getLiteral($token)); } return $this->lexer->moveNext(); } /** * Attempts to match the current lookahead token with any of the given tokens. * * If any of them matches, this method updates the lookahead token; otherwise * a syntax error is raised. * * @phpstan-param list $tokens * * @throws AnnotationException */ private function matchAny(array $tokens): bool { if (! $this->lexer->isNextTokenAny($tokens)) { throw $this->syntaxError(implode(' or ', array_map([$this->lexer, 'getLiteral'], $tokens))); } return $this->lexer->moveNext(); } /** * Generates a new syntax error. * * @param string $expected Expected string. * @param mixed[]|null $token Optional token. */ private function syntaxError(string $expected, ?array $token = null): AnnotationException { if ($token === null) { $token = $this->lexer->lookahead; } $message = sprintf('Expected %s, got ', $expected); $message .= $this->lexer->lookahead === null ? 'end of string' : sprintf("'%s' at position %s", $token['value'], $token['position']); if (strlen($this->context)) { $message .= ' in ' . $this->context; } $message .= '.'; return AnnotationException::syntaxError($message); } /** * Attempts to check if a class exists or not. This never goes through the PHP autoloading mechanism * but uses the {@link AnnotationRegistry} to load classes. * * @param class-string $fqcn */ private function classExists(string $fqcn): bool { if (isset($this->classExists[$fqcn])) { return $this->classExists[$fqcn]; } // first check if the class already exists, maybe loaded through another AnnotationReader if (class_exists($fqcn, false)) { return $this->classExists[$fqcn] = true; } // final check, does this class exist? return $this->classExists[$fqcn] = AnnotationRegistry::loadAnnotationClass($fqcn); } /** * Collects parsing metadata for a given annotation class * * @param class-string $name The annotation name * * @throws AnnotationException * @throws ReflectionException */ private function collectAnnotationMetadata(string $name): void { if (self::$metadataParser === null) { self::$metadataParser = new self(); self::$metadataParser->setIgnoreNotImportedAnnotations(true); self::$metadataParser->setIgnoredAnnotationNames($this->ignoredAnnotationNames); self::$metadataParser->setImports([ 'enum' => Enum::class, 'target' => Target::class, 'attribute' => Attribute::class, 'attributes' => Attributes::class, 'namedargumentconstructor' => NamedArgumentConstructor::class, ]); // Make sure that annotations from metadata are loaded class_exists(Enum::class); class_exists(Target::class); class_exists(Attribute::class); class_exists(Attributes::class); class_exists(NamedArgumentConstructor::class); } $class = new ReflectionClass($name); $docComment = $class->getDocComment(); // Sets default values for annotation metadata $constructor = $class->getConstructor(); $metadata = [ 'default_property' => null, 'has_constructor' => $constructor !== null && $constructor->getNumberOfParameters() > 0, 'constructor_args' => [], 'properties' => [], 'property_types' => [], 'attribute_types' => [], 'targets_literal' => null, 'targets' => Target::TARGET_ALL, 'is_annotation' => strpos($docComment, '@Annotation') !== false, ]; $metadata['has_named_argument_constructor'] = $metadata['has_constructor'] && $class->implementsInterface(NamedArgumentConstructorAnnotation::class); // verify that the class is really meant to be an annotation if ($metadata['is_annotation']) { self::$metadataParser->setTarget(Target::TARGET_CLASS); foreach (self::$metadataParser->parse($docComment, 'class @' . $name) as $annotation) { if ($annotation instanceof Target) { $metadata['targets'] = $annotation->targets; $metadata['targets_literal'] = $annotation->literal; continue; } if ($annotation instanceof NamedArgumentConstructor) { $metadata['has_named_argument_constructor'] = $metadata['has_constructor']; if ($metadata['has_named_argument_constructor']) { // choose the first argument as the default property $metadata['default_property'] = $constructor->getParameters()[0]->getName(); } } if (! ($annotation instanceof Attributes)) { continue; } foreach ($annotation->value as $attribute) { $this->collectAttributeTypeMetadata($metadata, $attribute); } } // if not has a constructor will inject values into public properties if ($metadata['has_constructor'] === false) { // collect all public properties foreach ($class->getProperties(ReflectionProperty::IS_PUBLIC) as $property) { $metadata['properties'][$property->name] = $property->name; $propertyComment = $property->getDocComment(); if ($propertyComment === false) { continue; } $attribute = new Attribute(); $attribute->required = (strpos($propertyComment, '@Required') !== false); $attribute->name = $property->name; $attribute->type = (strpos($propertyComment, '@var') !== false && preg_match('/@var\s+([^\s]+)/', $propertyComment, $matches)) ? $matches[1] : 'mixed'; $this->collectAttributeTypeMetadata($metadata, $attribute); // checks if the property has @Enum if (strpos($propertyComment, '@Enum') === false) { continue; } $context = 'property ' . $class->name . '::$' . $property->name; self::$metadataParser->setTarget(Target::TARGET_PROPERTY); foreach (self::$metadataParser->parse($propertyComment, $context) as $annotation) { if (! $annotation instanceof Enum) { continue; } $metadata['enum'][$property->name]['value'] = $annotation->value; $metadata['enum'][$property->name]['literal'] = (! empty($annotation->literal)) ? $annotation->literal : $annotation->value; } } // choose the first property as default property $metadata['default_property'] = reset($metadata['properties']); } elseif ($metadata['has_named_argument_constructor']) { foreach ($constructor->getParameters() as $parameter) { if ($parameter->isVariadic()) { break; } $metadata['constructor_args'][$parameter->getName()] = [ 'position' => $parameter->getPosition(), 'default' => $parameter->isOptional() ? $parameter->getDefaultValue() : null, ]; } } } self::$annotationMetadata[$name] = $metadata; } /** * Collects parsing metadata for a given attribute. * * @param mixed[] $metadata */ private function collectAttributeTypeMetadata(array &$metadata, Attribute $attribute): void { // handle internal type declaration $type = self::$typeMap[$attribute->type] ?? $attribute->type; // handle the case if the property type is mixed if ($type === 'mixed') { return; } // Evaluate type $pos = strpos($type, '<'); if ($pos !== false) { // Checks if the property has array $arrayType = substr($type, $pos + 1, -1); $type = 'array'; if (isset(self::$typeMap[$arrayType])) { $arrayType = self::$typeMap[$arrayType]; } $metadata['attribute_types'][$attribute->name]['array_type'] = $arrayType; } else { // Checks if the property has type[] $pos = strrpos($type, '['); if ($pos !== false) { $arrayType = substr($type, 0, $pos); $type = 'array'; if (isset(self::$typeMap[$arrayType])) { $arrayType = self::$typeMap[$arrayType]; } $metadata['attribute_types'][$attribute->name]['array_type'] = $arrayType; } } $metadata['attribute_types'][$attribute->name]['type'] = $type; $metadata['attribute_types'][$attribute->name]['value'] = $attribute->type; $metadata['attribute_types'][$attribute->name]['required'] = $attribute->required; } /** * Annotations ::= Annotation {[ "*" ]* [Annotation]}* * * @phpstan-return list * * @throws AnnotationException * @throws ReflectionException */ private function Annotations(): array { $annotations = []; while ($this->lexer->lookahead !== null) { if ($this->lexer->lookahead['type'] !== DocLexer::T_AT) { $this->lexer->moveNext(); continue; } // make sure the @ is preceded by non-catchable pattern if ( $this->lexer->token !== null && $this->lexer->lookahead['position'] === $this->lexer->token['position'] + strlen( $this->lexer->token['value'] ) ) { $this->lexer->moveNext(); continue; } // make sure the @ is followed by either a namespace separator, or // an identifier token $peek = $this->lexer->glimpse(); if ( ($peek === null) || ($peek['type'] !== DocLexer::T_NAMESPACE_SEPARATOR && ! in_array( $peek['type'], self::$classIdentifiers, true )) || $peek['position'] !== $this->lexer->lookahead['position'] + 1 ) { $this->lexer->moveNext(); continue; } $this->isNestedAnnotation = false; $annot = $this->Annotation(); if ($annot === false) { continue; } $annotations[] = $annot; } return $annotations; } /** * Annotation ::= "@" AnnotationName MethodCall * AnnotationName ::= QualifiedName | SimpleName * QualifiedName ::= NameSpacePart "\" {NameSpacePart "\"}* SimpleName * NameSpacePart ::= identifier | null | false | true * SimpleName ::= identifier | null | false | true * * @return object|false False if it is not a valid annotation. * * @throws AnnotationException * @throws ReflectionException */ private function Annotation() { $this->match(DocLexer::T_AT); // check if we have an annotation $name = $this->Identifier(); if ( $this->lexer->isNextToken(DocLexer::T_MINUS) && $this->lexer->nextTokenIsAdjacent() ) { // Annotations with dashes, such as "@foo-" or "@foo-bar", are to be discarded return false; } // only process names which are not fully qualified, yet // fully qualified names must start with a \ $originalName = $name; if ($name[0] !== '\\') { $pos = strpos($name, '\\'); $alias = ($pos === false) ? $name : substr($name, 0, $pos); $found = false; $loweredAlias = strtolower($alias); if ($this->namespaces) { foreach ($this->namespaces as $namespace) { if ($this->classExists($namespace . '\\' . $name)) { $name = $namespace . '\\' . $name; $found = true; break; } } } elseif (isset($this->imports[$loweredAlias])) { $namespace = ltrim($this->imports[$loweredAlias], '\\'); $name = ($pos !== false) ? $namespace . substr($name, $pos) : $namespace; $found = $this->classExists($name); } elseif ( ! isset($this->ignoredAnnotationNames[$name]) && isset($this->imports['__NAMESPACE__']) && $this->classExists($this->imports['__NAMESPACE__'] . '\\' . $name) ) { $name = $this->imports['__NAMESPACE__'] . '\\' . $name; $found = true; } elseif (! isset($this->ignoredAnnotationNames[$name]) && $this->classExists($name)) { $found = true; } if (! $found) { if ($this->isIgnoredAnnotation($name)) { return false; } throw AnnotationException::semanticalError(sprintf( <<<'EXCEPTION' The annotation "@%s" in %s was never imported. Did you maybe forget to add a "use" statement for this annotation? EXCEPTION , $name, $this->context )); } } $name = ltrim($name, '\\'); if (! $this->classExists($name)) { throw AnnotationException::semanticalError(sprintf( 'The annotation "@%s" in %s does not exist, or could not be auto-loaded.', $name, $this->context )); } // at this point, $name contains the fully qualified class name of the // annotation, and it is also guaranteed that this class exists, and // that it is loaded // collects the metadata annotation only if there is not yet if (! isset(self::$annotationMetadata[$name])) { $this->collectAnnotationMetadata($name); } // verify that the class is really meant to be an annotation and not just any ordinary class if (self::$annotationMetadata[$name]['is_annotation'] === false) { if ($this->isIgnoredAnnotation($originalName) || $this->isIgnoredAnnotation($name)) { return false; } throw AnnotationException::semanticalError(sprintf( <<<'EXCEPTION' The class "%s" is not annotated with @Annotation. Are you sure this class can be used as annotation? If so, then you need to add @Annotation to the _class_ doc comment of "%s". If it is indeed no annotation, then you need to add @IgnoreAnnotation("%s") to the _class_ doc comment of %s. EXCEPTION , $name, $name, $originalName, $this->context )); } //if target is nested annotation $target = $this->isNestedAnnotation ? Target::TARGET_ANNOTATION : $this->target; // Next will be nested $this->isNestedAnnotation = true; //if annotation does not support current target if ((self::$annotationMetadata[$name]['targets'] & $target) === 0 && $target) { throw AnnotationException::semanticalError( sprintf( <<<'EXCEPTION' Annotation @%s is not allowed to be declared on %s. You may only use this annotation on these code elements: %s. EXCEPTION , $originalName, $this->context, self::$annotationMetadata[$name]['targets_literal'] ) ); } $arguments = $this->MethodCall(); $values = $this->resolvePositionalValues($arguments, $name); if (isset(self::$annotationMetadata[$name]['enum'])) { // checks all declared attributes foreach (self::$annotationMetadata[$name]['enum'] as $property => $enum) { // checks if the attribute is a valid enumerator if (isset($values[$property]) && ! in_array($values[$property], $enum['value'])) { throw AnnotationException::enumeratorError( $property, $name, $this->context, $enum['literal'], $values[$property] ); } } } // checks all declared attributes foreach (self::$annotationMetadata[$name]['attribute_types'] as $property => $type) { if ( $property === self::$annotationMetadata[$name]['default_property'] && ! isset($values[$property]) && isset($values['value']) ) { $property = 'value'; } // handle a not given attribute or null value if (! isset($values[$property])) { if ($type['required']) { throw AnnotationException::requiredError( $property, $originalName, $this->context, 'a(n) ' . $type['value'] ); } continue; } if ($type['type'] === 'array') { // handle the case of a single value if (! is_array($values[$property])) { $values[$property] = [$values[$property]]; } // checks if the attribute has array type declaration, such as "array" if (isset($type['array_type'])) { foreach ($values[$property] as $item) { if (gettype($item) !== $type['array_type'] && ! $item instanceof $type['array_type']) { throw AnnotationException::attributeTypeError( $property, $originalName, $this->context, 'either a(n) ' . $type['array_type'] . ', or an array of ' . $type['array_type'] . 's', $item ); } } } } elseif (gettype($values[$property]) !== $type['type'] && ! $values[$property] instanceof $type['type']) { throw AnnotationException::attributeTypeError( $property, $originalName, $this->context, 'a(n) ' . $type['value'], $values[$property] ); } } if (self::$annotationMetadata[$name]['has_named_argument_constructor']) { if (PHP_VERSION_ID >= 80000) { foreach ($values as $property => $value) { if (! isset(self::$annotationMetadata[$name]['constructor_args'][$property])) { throw AnnotationException::creationError(sprintf( <<<'EXCEPTION' The annotation @%s declared on %s does not have a property named "%s" that can be set through its named arguments constructor. Available named arguments: %s EXCEPTION , $originalName, $this->context, $property, implode(', ', array_keys(self::$annotationMetadata[$name]['constructor_args'])) )); } } return $this->instantiateAnnotiation($originalName, $this->context, $name, $values); } $positionalValues = []; foreach (self::$annotationMetadata[$name]['constructor_args'] as $property => $parameter) { $positionalValues[$parameter['position']] = $parameter['default']; } foreach ($values as $property => $value) { if (! isset(self::$annotationMetadata[$name]['constructor_args'][$property])) { throw AnnotationException::creationError(sprintf( <<<'EXCEPTION' The annotation @%s declared on %s does not have a property named "%s" that can be set through its named arguments constructor. Available named arguments: %s EXCEPTION , $originalName, $this->context, $property, implode(', ', array_keys(self::$annotationMetadata[$name]['constructor_args'])) )); } $positionalValues[self::$annotationMetadata[$name]['constructor_args'][$property]['position']] = $value; } return $this->instantiateAnnotiation($originalName, $this->context, $name, $positionalValues); } // check if the annotation expects values via the constructor, // or directly injected into public properties if (self::$annotationMetadata[$name]['has_constructor'] === true) { return $this->instantiateAnnotiation($originalName, $this->context, $name, [$values]); } $instance = $this->instantiateAnnotiation($originalName, $this->context, $name, []); foreach ($values as $property => $value) { if (! isset(self::$annotationMetadata[$name]['properties'][$property])) { if ($property !== 'value') { throw AnnotationException::creationError(sprintf( <<<'EXCEPTION' The annotation @%s declared on %s does not have a property named "%s". Available properties: %s EXCEPTION , $originalName, $this->context, $property, implode(', ', self::$annotationMetadata[$name]['properties']) )); } // handle the case if the property has no annotations $property = self::$annotationMetadata[$name]['default_property']; if (! $property) { throw AnnotationException::creationError(sprintf( 'The annotation @%s declared on %s does not accept any values, but got %s.', $originalName, $this->context, json_encode($values) )); } } $instance->{$property} = $value; } return $instance; } /** * MethodCall ::= ["(" [Values] ")"] * * @return mixed[] * * @throws AnnotationException * @throws ReflectionException */ private function MethodCall(): array { $values = []; if (! $this->lexer->isNextToken(DocLexer::T_OPEN_PARENTHESIS)) { return $values; } $this->match(DocLexer::T_OPEN_PARENTHESIS); if (! $this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) { $values = $this->Values(); } $this->match(DocLexer::T_CLOSE_PARENTHESIS); return $values; } /** * Values ::= Array | Value {"," Value}* [","] * * @return mixed[] * * @throws AnnotationException * @throws ReflectionException */ private function Values(): array { $values = [$this->Value()]; while ($this->lexer->isNextToken(DocLexer::T_COMMA)) { $this->match(DocLexer::T_COMMA); if ($this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) { break; } $token = $this->lexer->lookahead; $value = $this->Value(); $values[] = $value; } $namedArguments = []; $positionalArguments = []; foreach ($values as $k => $value) { if (is_object($value) && $value instanceof stdClass) { $namedArguments[$value->name] = $value->value; } else { $positionalArguments[$k] = $value; } } return ['named_arguments' => $namedArguments, 'positional_arguments' => $positionalArguments]; } /** * Constant ::= integer | string | float | boolean * * @return mixed * * @throws AnnotationException */ private function Constant() { $identifier = $this->Identifier(); if (! defined($identifier) && strpos($identifier, '::') !== false && $identifier[0] !== '\\') { [$className, $const] = explode('::', $identifier); $pos = strpos($className, '\\'); $alias = ($pos === false) ? $className : substr($className, 0, $pos); $found = false; $loweredAlias = strtolower($alias); switch (true) { case ! empty($this->namespaces): foreach ($this->namespaces as $ns) { if (class_exists($ns . '\\' . $className) || interface_exists($ns . '\\' . $className)) { $className = $ns . '\\' . $className; $found = true; break; } } break; case isset($this->imports[$loweredAlias]): $found = true; $className = ($pos !== false) ? $this->imports[$loweredAlias] . substr($className, $pos) : $this->imports[$loweredAlias]; break; default: if (isset($this->imports['__NAMESPACE__'])) { $ns = $this->imports['__NAMESPACE__']; if (class_exists($ns . '\\' . $className) || interface_exists($ns . '\\' . $className)) { $className = $ns . '\\' . $className; $found = true; } } break; } if ($found) { $identifier = $className . '::' . $const; } } /** * Checks if identifier ends with ::class and remove the leading backslash if it exists. */ if ( $this->identifierEndsWithClassConstant($identifier) && ! $this->identifierStartsWithBackslash($identifier) ) { return substr($identifier, 0, $this->getClassConstantPositionInIdentifier($identifier)); } if ($this->identifierEndsWithClassConstant($identifier) && $this->identifierStartsWithBackslash($identifier)) { return substr($identifier, 1, $this->getClassConstantPositionInIdentifier($identifier) - 1); } if (! defined($identifier)) { throw AnnotationException::semanticalErrorConstants($identifier, $this->context); } return constant($identifier); } private function identifierStartsWithBackslash(string $identifier): bool { return $identifier[0] === '\\'; } private function identifierEndsWithClassConstant(string $identifier): bool { return $this->getClassConstantPositionInIdentifier($identifier) === strlen($identifier) - strlen('::class'); } /** @return int|false */ private function getClassConstantPositionInIdentifier(string $identifier) { return stripos($identifier, '::class'); } /** * Identifier ::= string * * @throws AnnotationException */ private function Identifier(): string { // check if we have an annotation if (! $this->lexer->isNextTokenAny(self::$classIdentifiers)) { throw $this->syntaxError('namespace separator or identifier'); } $this->lexer->moveNext(); $className = $this->lexer->token['value']; while ( $this->lexer->lookahead !== null && $this->lexer->lookahead['position'] === ($this->lexer->token['position'] + strlen($this->lexer->token['value'])) && $this->lexer->isNextToken(DocLexer::T_NAMESPACE_SEPARATOR) ) { $this->match(DocLexer::T_NAMESPACE_SEPARATOR); $this->matchAny(self::$classIdentifiers); $className .= '\\' . $this->lexer->token['value']; } return $className; } /** * Value ::= PlainValue | FieldAssignment * * @return mixed * * @throws AnnotationException * @throws ReflectionException */ private function Value() { $peek = $this->lexer->glimpse(); if ($peek['type'] === DocLexer::T_EQUALS) { return $this->FieldAssignment(); } return $this->PlainValue(); } /** * PlainValue ::= integer | string | float | boolean | Array | Annotation * * @return mixed * * @throws AnnotationException * @throws ReflectionException */ private function PlainValue() { if ($this->lexer->isNextToken(DocLexer::T_OPEN_CURLY_BRACES)) { return $this->Arrayx(); } if ($this->lexer->isNextToken(DocLexer::T_AT)) { return $this->Annotation(); } if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) { return $this->Constant(); } switch ($this->lexer->lookahead['type']) { case DocLexer::T_STRING: $this->match(DocLexer::T_STRING); return $this->lexer->token['value']; case DocLexer::T_INTEGER: $this->match(DocLexer::T_INTEGER); return (int) $this->lexer->token['value']; case DocLexer::T_FLOAT: $this->match(DocLexer::T_FLOAT); return (float) $this->lexer->token['value']; case DocLexer::T_TRUE: $this->match(DocLexer::T_TRUE); return true; case DocLexer::T_FALSE: $this->match(DocLexer::T_FALSE); return false; case DocLexer::T_NULL: $this->match(DocLexer::T_NULL); return null; default: throw $this->syntaxError('PlainValue'); } } /** * FieldAssignment ::= FieldName "=" PlainValue * FieldName ::= identifier * * @throws AnnotationException * @throws ReflectionException */ private function FieldAssignment(): stdClass { $this->match(DocLexer::T_IDENTIFIER); $fieldName = $this->lexer->token['value']; $this->match(DocLexer::T_EQUALS); $item = new stdClass(); $item->name = $fieldName; $item->value = $this->PlainValue(); return $item; } /** * Array ::= "{" ArrayEntry {"," ArrayEntry}* [","] "}" * * @return mixed[] * * @throws AnnotationException * @throws ReflectionException */ private function Arrayx(): array { $array = $values = []; $this->match(DocLexer::T_OPEN_CURLY_BRACES); // If the array is empty, stop parsing and return. if ($this->lexer->isNextToken(DocLexer::T_CLOSE_CURLY_BRACES)) { $this->match(DocLexer::T_CLOSE_CURLY_BRACES); return $array; } $values[] = $this->ArrayEntry(); while ($this->lexer->isNextToken(DocLexer::T_COMMA)) { $this->match(DocLexer::T_COMMA); // optional trailing comma if ($this->lexer->isNextToken(DocLexer::T_CLOSE_CURLY_BRACES)) { break; } $values[] = $this->ArrayEntry(); } $this->match(DocLexer::T_CLOSE_CURLY_BRACES); foreach ($values as $value) { [$key, $val] = $value; if ($key !== null) { $array[$key] = $val; } else { $array[] = $val; } } return $array; } /** * ArrayEntry ::= Value | KeyValuePair * KeyValuePair ::= Key ("=" | ":") PlainValue | Constant * Key ::= string | integer | Constant * * @phpstan-return array{mixed, mixed} * * @throws AnnotationException * @throws ReflectionException */ private function ArrayEntry(): array { $peek = $this->lexer->glimpse(); if ( $peek['type'] === DocLexer::T_EQUALS || $peek['type'] === DocLexer::T_COLON ) { if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) { $key = $this->Constant(); } else { $this->matchAny([DocLexer::T_INTEGER, DocLexer::T_STRING]); $key = $this->lexer->token['value']; } $this->matchAny([DocLexer::T_EQUALS, DocLexer::T_COLON]); return [$key, $this->PlainValue()]; } return [null, $this->Value()]; } /** * Checks whether the given $name matches any ignored annotation name or namespace */ private function isIgnoredAnnotation(string $name): bool { if ($this->ignoreNotImportedAnnotations || isset($this->ignoredAnnotationNames[$name])) { return true; } foreach (array_keys($this->ignoredAnnotationNamespaces) as $ignoredAnnotationNamespace) { $ignoredAnnotationNamespace = rtrim($ignoredAnnotationNamespace, '\\') . '\\'; if (stripos(rtrim($name, '\\') . '\\', $ignoredAnnotationNamespace) === 0) { return true; } } return false; } /** * Resolve positional arguments (without name) to named ones * * @param array $arguments * * @return array */ private function resolvePositionalValues(array $arguments, string $name): array { $positionalArguments = $arguments['positional_arguments'] ?? []; $values = $arguments['named_arguments'] ?? []; if ( self::$annotationMetadata[$name]['has_named_argument_constructor'] && self::$annotationMetadata[$name]['default_property'] !== null ) { // We must ensure that we don't have positional arguments after named ones $positions = array_keys($positionalArguments); $lastPosition = null; foreach ($positions as $position) { if ( ($lastPosition === null && $position !== 0) || ($lastPosition !== null && $position !== $lastPosition + 1) ) { throw $this->syntaxError('Positional arguments after named arguments is not allowed'); } $lastPosition = $position; } foreach (self::$annotationMetadata[$name]['constructor_args'] as $property => $parameter) { $position = $parameter['position']; if (isset($values[$property]) || ! isset($positionalArguments[$position])) { continue; } $values[$property] = $positionalArguments[$position]; } } else { if (count($positionalArguments) > 0 && ! isset($values['value'])) { if (count($positionalArguments) === 1) { $value = array_pop($positionalArguments); } else { $value = array_values($positionalArguments); } $values['value'] = $value; } } return $values; } /** * Try to instantiate the annotation and catch and process any exceptions related to failure * * @param class-string $name * @param array $arguments * * @return object * * @throws AnnotationException */ private function instantiateAnnotiation(string $originalName, string $context, string $name, array $arguments) { try { return new $name(...$arguments); } catch (Throwable $exception) { throw AnnotationException::creationError( sprintf( 'An error occurred while instantiating the annotation @%s declared on %s: "%s".', $originalName, $context, $exception->getMessage() ), $exception ); } } } PK!z jXggDannotations/lib/Doctrine/Common/Annotations/Annotation/Attribute.phpnu[ */ public $value; } PK!GOSannotations/lib/Doctrine/Common/Annotations/Annotation/NamedArgumentConstructor.phpnu[ */ private static $map = [ 'ALL' => self::TARGET_ALL, 'CLASS' => self::TARGET_CLASS, 'METHOD' => self::TARGET_METHOD, 'PROPERTY' => self::TARGET_PROPERTY, 'FUNCTION' => self::TARGET_FUNCTION, 'ANNOTATION' => self::TARGET_ANNOTATION, ]; /** @phpstan-var list */ public $value; /** * Targets as bitmask. * * @var int */ public $targets; /** * Literal target declaration. * * @var string */ public $literal; /** * @phpstan-param array{value?: string|list} $values * * @throws InvalidArgumentException */ public function __construct(array $values) { if (! isset($values['value'])) { $values['value'] = null; } if (is_string($values['value'])) { $values['value'] = [$values['value']]; } if (! is_array($values['value'])) { throw new InvalidArgumentException( sprintf( '@Target expects either a string value, or an array of strings, "%s" given.', is_object($values['value']) ? get_class($values['value']) : gettype($values['value']) ) ); } $bitmask = 0; foreach ($values['value'] as $literal) { if (! isset(self::$map[$literal])) { throw new InvalidArgumentException( sprintf( 'Invalid Target "%s". Available targets: [%s]', $literal, implode(', ', array_keys(self::$map)) ) ); } $bitmask |= self::$map[$literal]; } $this->targets = $bitmask; $this->value = $values['value']; $this->literal = implode(', ', $this->value); } } PK!H?annotations/lib/Doctrine/Common/Annotations/Annotation/Enum.phpnu[ */ public $value; /** * Literal target declaration. * * @var mixed[] */ public $literal; /** * @phpstan-param array{literal?: mixed[], value: list} $values * * @throws InvalidArgumentException */ public function __construct(array $values) { if (! isset($values['literal'])) { $values['literal'] = []; } foreach ($values['value'] as $var) { if (! is_scalar($var)) { throw new InvalidArgumentException(sprintf( '@Enum supports only scalar values "%s" given.', is_object($var) ? get_class($var) : gettype($var) )); } } foreach ($values['literal'] as $key => $var) { if (! in_array($key, $values['value'])) { throw new InvalidArgumentException(sprintf( 'Undefined enumerator value "%s" for literal "%s".', $key, $var )); } } $this->value = $values['value']; $this->literal = $values['literal']; } } PK! /Kannotations/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.phpnu[ */ public $names; /** * @phpstan-param array{value: string|list} $values * * @throws RuntimeException */ public function __construct(array $values) { if (is_string($values['value'])) { $values['value'] = [$values['value']]; } if (! is_array($values['value'])) { throw new RuntimeException(sprintf( '@IgnoreAnnotation expects either a string name, or an array of strings, but got %s.', json_encode($values['value']) )); } $this->names = $values['value']; } } PK!Ec33Cannotations/lib/Doctrine/Common/Annotations/AnnotationException.phpnu[ $available * * @return AnnotationException */ public static function enumeratorError($attributeName, $annotationName, $context, $available, $given) { return new self(sprintf( '[Enum Error] Attribute "%s" of @%s declared on %s accepts only [%s], but got %s.', $attributeName, $annotationName, $context, implode(', ', $available), is_object($given) ? get_class($given) : $given )); } /** @return AnnotationException */ public static function optimizerPlusSaveComments() { return new self( 'You have to enable opcache.save_comments=1 or zend_optimizerplus.save_comments=1.' ); } /** @return AnnotationException */ public static function optimizerPlusLoadComments() { return new self( 'You have to enable opcache.load_comments=1 or zend_optimizerplus.load_comments=1.' ); } } PK!m8 Fannotations/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.phpnu[parser = new DocParser(); $this->parser->setIgnoreNotImportedAnnotations(true); } /** * Adds a namespace in which we will look for annotations. * * @param string $namespace * * @return void */ public function addNamespace($namespace) { $this->parser->addNamespace($namespace); } /** * {@inheritDoc} */ public function getClassAnnotations(ReflectionClass $class) { return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName()); } /** * {@inheritDoc} */ public function getMethodAnnotations(ReflectionMethod $method) { return $this->parser->parse( $method->getDocComment(), 'method ' . $method->getDeclaringClass()->name . '::' . $method->getName() . '()' ); } /** * {@inheritDoc} */ public function getPropertyAnnotations(ReflectionProperty $property) { return $this->parser->parse( $property->getDocComment(), 'property ' . $property->getDeclaringClass()->name . '::$' . $property->getName() ); } /** * {@inheritDoc} */ public function getClassAnnotation(ReflectionClass $class, $annotationName) { foreach ($this->getClassAnnotations($class) as $annot) { if ($annot instanceof $annotationName) { return $annot; } } return null; } /** * {@inheritDoc} */ public function getMethodAnnotation(ReflectionMethod $method, $annotationName) { foreach ($this->getMethodAnnotations($method) as $annot) { if ($annot instanceof $annotationName) { return $annot; } } return null; } /** * {@inheritDoc} */ public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) { foreach ($this->getPropertyAnnotations($property) as $annot) { if ($annot instanceof $annotationName) { return $annot; } } return null; } } PK!$Vi 6annotations/lib/Doctrine/Common/Annotations/Reader.phpnu[ An array of Annotations. */ public function getClassAnnotations(ReflectionClass $class); /** * Gets a class annotation. * * @param ReflectionClass $class The ReflectionClass of the class from which * the class annotations should be read. * @param class-string $annotationName The name of the annotation. * * @return T|null The Annotation or NULL, if the requested annotation does not exist. * * @template T */ public function getClassAnnotation(ReflectionClass $class, $annotationName); /** * Gets the annotations applied to a method. * * @param ReflectionMethod $method The ReflectionMethod of the method from which * the annotations should be read. * * @return array An array of Annotations. */ public function getMethodAnnotations(ReflectionMethod $method); /** * Gets a method annotation. * * @param ReflectionMethod $method The ReflectionMethod to read the annotations from. * @param class-string $annotationName The name of the annotation. * * @return T|null The Annotation or NULL, if the requested annotation does not exist. * * @template T */ public function getMethodAnnotation(ReflectionMethod $method, $annotationName); /** * Gets the annotations applied to a property. * * @param ReflectionProperty $property The ReflectionProperty of the property * from which the annotations should be read. * * @return array An array of Annotations. */ public function getPropertyAnnotations(ReflectionProperty $property); /** * Gets a property annotation. * * @param ReflectionProperty $property The ReflectionProperty to read the annotations from. * @param class-string $annotationName The name of the annotation. * * @return T|null The Annotation or NULL, if the requested annotation does not exist. * * @template T */ public function getPropertyAnnotation(ReflectionProperty $property, $annotationName); } PK!UU?annotations/lib/Doctrine/Common/Annotations/PsrCachedReader.phpnu[> */ private $loadedAnnotations = []; /** @var int[] */ private $loadedFilemtimes = []; public function __construct(Reader $reader, CacheItemPoolInterface $cache, bool $debug = false) { $this->delegate = $reader; $this->cache = $cache; $this->debug = (bool) $debug; } /** * {@inheritDoc} */ public function getClassAnnotations(ReflectionClass $class) { $cacheKey = $class->getName(); if (isset($this->loadedAnnotations[$cacheKey])) { return $this->loadedAnnotations[$cacheKey]; } $annots = $this->fetchFromCache($cacheKey, $class, 'getClassAnnotations', $class); return $this->loadedAnnotations[$cacheKey] = $annots; } /** * {@inheritDoc} */ public function getClassAnnotation(ReflectionClass $class, $annotationName) { foreach ($this->getClassAnnotations($class) as $annot) { if ($annot instanceof $annotationName) { return $annot; } } return null; } /** * {@inheritDoc} */ public function getPropertyAnnotations(ReflectionProperty $property) { $class = $property->getDeclaringClass(); $cacheKey = $class->getName() . '$' . $property->getName(); if (isset($this->loadedAnnotations[$cacheKey])) { return $this->loadedAnnotations[$cacheKey]; } $annots = $this->fetchFromCache($cacheKey, $class, 'getPropertyAnnotations', $property); return $this->loadedAnnotations[$cacheKey] = $annots; } /** * {@inheritDoc} */ public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) { foreach ($this->getPropertyAnnotations($property) as $annot) { if ($annot instanceof $annotationName) { return $annot; } } return null; } /** * {@inheritDoc} */ public function getMethodAnnotations(ReflectionMethod $method) { $class = $method->getDeclaringClass(); $cacheKey = $class->getName() . '#' . $method->getName(); if (isset($this->loadedAnnotations[$cacheKey])) { return $this->loadedAnnotations[$cacheKey]; } $annots = $this->fetchFromCache($cacheKey, $class, 'getMethodAnnotations', $method); return $this->loadedAnnotations[$cacheKey] = $annots; } /** * {@inheritDoc} */ public function getMethodAnnotation(ReflectionMethod $method, $annotationName) { foreach ($this->getMethodAnnotations($method) as $annot) { if ($annot instanceof $annotationName) { return $annot; } } return null; } public function clearLoadedAnnotations(): void { $this->loadedAnnotations = []; $this->loadedFilemtimes = []; } /** @return mixed[] */ private function fetchFromCache( string $cacheKey, ReflectionClass $class, string $method, Reflector $reflector ): array { $cacheKey = rawurlencode($cacheKey); $item = $this->cache->getItem($cacheKey); if (($this->debug && ! $this->refresh($cacheKey, $class)) || ! $item->isHit()) { $this->cache->save($item->set($this->delegate->{$method}($reflector))); } return $item->get(); } /** * Used in debug mode to check if the cache is fresh. * * @return bool Returns true if the cache was fresh, or false if the class * being read was modified since writing to the cache. */ private function refresh(string $cacheKey, ReflectionClass $class): bool { $lastModification = $this->getLastModification($class); if ($lastModification === 0) { return true; } $item = $this->cache->getItem('[C]' . $cacheKey); if ($item->isHit() && $item->get() >= $lastModification) { return true; } $this->cache->save($item->set(time())); return false; } /** * Returns the time the class was last modified, testing traits and parents */ private function getLastModification(ReflectionClass $class): int { $filename = $class->getFileName(); if (isset($this->loadedFilemtimes[$filename])) { return $this->loadedFilemtimes[$filename]; } $parent = $class->getParentClass(); $lastModification = max(array_merge( [$filename ? filemtime($filename) : 0], array_map(function (ReflectionClass $reflectionTrait): int { return $this->getTraitLastModificationTime($reflectionTrait); }, $class->getTraits()), array_map(function (ReflectionClass $class): int { return $this->getLastModification($class); }, $class->getInterfaces()), $parent ? [$this->getLastModification($parent)] : [] )); assert($lastModification !== false); return $this->loadedFilemtimes[$filename] = $lastModification; } private function getTraitLastModificationTime(ReflectionClass $reflectionTrait): int { $fileName = $reflectionTrait->getFileName(); if (isset($this->loadedFilemtimes[$fileName])) { return $this->loadedFilemtimes[$fileName]; } $lastModificationTime = max(array_merge( [$fileName ? filemtime($fileName) : 0], array_map(function (ReflectionClass $reflectionTrait): int { return $this->getTraitLastModificationTime($reflectionTrait); }, $reflectionTrait->getTraits()) )); assert($lastModificationTime !== false); return $this->loadedFilemtimes[$fileName] = $lastModificationTime; } } PK!^zzannotations/composer.jsonnu[{ "name": "doctrine/annotations", "description": "Docblock Annotations Parser", "license": "MIT", "type": "library", "keywords": [ "annotations", "docblock", "parser" ], "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" } ], "homepage": "https://www.doctrine-project.org/projects/annotations.html", "require": { "php": "^7.1 || ^8.0", "ext-tokenizer": "*", "doctrine/lexer": "^1 || ^2", "psr/cache": "^1 || ^2 || ^3" }, "require-dev": { "doctrine/cache": "^1.11 || ^2.0", "doctrine/coding-standard": "^9 || ^10", "phpstan/phpstan": "~1.4.10 || ^1.8.0", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", "symfony/cache": "^4.4 || ^5.4 || ^6", "vimeo/psalm": "^4.10" }, "suggest": { "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" }, "autoload": { "psr-4": { "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" } }, "autoload-dev": { "psr-4": { "Doctrine\\Performance\\Common\\Annotations\\": "tests/Doctrine/Performance/Common/Annotations", "Doctrine\\Tests\\Common\\Annotations\\": "tests/Doctrine/Tests/Common/Annotations" }, "files": [ "tests/Doctrine/Tests/Common/Annotations/Fixtures/functions.php", "tests/Doctrine/Tests/Common/Annotations/Fixtures/SingleClassLOC1000.php" ] }, "config": { "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true }, "sort-packages": true } } PK!``))annotations/LICENSEnu[Copyright (c) 2006-2013 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. PK! C,annotations/psalm.xmlnu[ PK!5annotations/README.mdnu[⚠️ PHP 8 introduced [attributes](https://www.php.net/manual/en/language.attributes.overview.php), which are a native replacement for annotations. As such, this library is considered feature complete, and should receive exclusively bugfixes and security fixes. # Doctrine Annotations [![Build Status](https://github.com/doctrine/annotations/workflows/Continuous%20Integration/badge.svg?label=build)](https://github.com/doctrine/persistence/actions) [![Dependency Status](https://www.versioneye.com/package/php--doctrine--annotations/badge.png)](https://www.versioneye.com/package/php--doctrine--annotations) [![Reference Status](https://www.versioneye.com/php/doctrine:annotations/reference_badge.svg)](https://www.versioneye.com/php/doctrine:annotations/references) [![Total Downloads](https://poser.pugx.org/doctrine/annotations/downloads.png)](https://packagist.org/packages/doctrine/annotations) [![Latest Stable Version](https://img.shields.io/packagist/v/doctrine/annotations.svg?label=stable)](https://packagist.org/packages/doctrine/annotations) Docblock Annotations Parser library (extracted from [Doctrine Common](https://github.com/doctrine/common)). ## Documentation See the [doctrine-project website](https://www.doctrine-project.org/projects/doctrine-annotations/en/latest/index.html). ## Contributing When making a pull request, make sure your changes follow the [Coding Standard Guidelines](https://www.doctrine-project.org/projects/doctrine-coding-standard/en/current/reference/index.html#introduction). PK!.HۯX&X&#annotations/docs/en/annotations.rstnu[Handling Annotations ==================== There are several different approaches to handling annotations in PHP. Doctrine Annotations maps docblock annotations to PHP classes. Because not all docblock annotations are used for metadata purposes a filter is applied to ignore or skip classes that are not Doctrine annotations. Take a look at the following code snippet: .. code-block:: php namespace MyProject\Entities; use Doctrine\ORM\Mapping AS ORM; use Symfony\Component\Validator\Constraints AS Assert; /** * @author Benjamin Eberlei * @ORM\Entity * @MyProject\Annotations\Foobarable */ class User { /** * @ORM\Id @ORM\Column @ORM\GeneratedValue * @dummy * @var int */ private $id; /** * @ORM\Column(type="string") * @Assert\NotEmpty * @Assert\Email * @var string */ private $email; } In this snippet you can see a variety of different docblock annotations: - Documentation annotations such as ``@var`` and ``@author``. These annotations are ignored and never considered for throwing an exception due to wrongly used annotations. - Annotations imported through use statements. The statement ``use Doctrine\ORM\Mapping AS ORM`` makes all classes under that namespace available as ``@ORM\ClassName``. Same goes for the import of ``@Assert``. - The ``@dummy`` annotation. It is not a documentation annotation and not ignored. For Doctrine Annotations it is not entirely clear how to handle this annotation. Depending on the configuration an exception (unknown annotation) will be thrown when parsing this annotation. - The fully qualified annotation ``@MyProject\Annotations\Foobarable``. This is transformed directly into the given class name. How are these annotations loaded? From looking at the code you could guess that the ORM Mapping, Assert Validation and the fully qualified annotation can just be loaded using the defined PHP autoloaders. This is not the case however: For error handling reasons every check for class existence inside the ``AnnotationReader`` sets the second parameter $autoload of ``class_exists($name, $autoload)`` to false. To work flawlessly the ``AnnotationReader`` requires silent autoloaders which many autoloaders are not. Silent autoloading is NOT part of the `PSR-0 specification `_ for autoloading. This is why Doctrine Annotations uses its own autoloading mechanism through a global registry. If you are wondering about the annotation registry being global, there is no other way to solve the architectural problems of autoloading annotation classes in a straightforward fashion. Additionally if you think about PHP autoloading then you recognize it is a global as well. To anticipate the configuration section, making the above PHP class work with Doctrine Annotations requires this setup: .. code-block:: php use Doctrine\Common\Annotations\AnnotationReader; use Doctrine\Common\Annotations\AnnotationRegistry; AnnotationRegistry::registerFile("/path/to/doctrine/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php"); AnnotationRegistry::registerAutoloadNamespace("Symfony\Component\Validator\Constraint", "/path/to/symfony/src"); AnnotationRegistry::registerAutoloadNamespace("MyProject\Annotations", "/path/to/myproject/src"); $reader = new AnnotationReader(); AnnotationReader::addGlobalIgnoredName('dummy'); The second block with the annotation registry calls registers all the three different annotation namespaces that are used. Doctrine Annotations saves all its annotations in a single file, that is why ``AnnotationRegistry#registerFile`` is used in contrast to ``AnnotationRegistry#registerAutoloadNamespace`` which creates a PSR-0 compatible loading mechanism for class to file names. In the third block, we create the actual ``AnnotationReader`` instance. Note that we also add ``dummy`` to the global list of ignored annotations for which we do not throw exceptions. Setting this is necessary in our example case, otherwise ``@dummy`` would trigger an exception to be thrown during the parsing of the docblock of ``MyProject\Entities\User#id``. Setup and Configuration ----------------------- To use the annotations library is simple, you just need to create a new ``AnnotationReader`` instance: .. code-block:: php $reader = new \Doctrine\Common\Annotations\AnnotationReader(); This creates a simple annotation reader with no caching other than in memory (in php arrays). Since parsing docblocks can be expensive you should cache this process by using a caching reader. To cache annotations, you can create a ``Doctrine\Common\Annotations\PsrCachedReader``. This reader decorates the original reader and stores all annotations in a PSR-6 cache: .. code-block:: php use Doctrine\Common\Annotations\AnnotationReader; use Doctrine\Common\Annotations\PsrCachedReader; $cache = ... // instantiate a PSR-6 Cache pool $reader = new PsrCachedReader( new AnnotationReader(), $cache, $debug = true ); The ``debug`` flag is used here as well to invalidate the cache files when the PHP class with annotations changed and should be used during development. .. warning :: The ``AnnotationReader`` works and caches under the assumption that all annotations of a doc-block are processed at once. That means that annotation classes that do not exist and aren't loaded and cannot be autoloaded (using the AnnotationRegistry) would never be visible and not accessible if a cache is used unless the cache is cleared and the annotations requested again, this time with all annotations defined. By default the annotation reader returns a list of annotations with numeric indexes. If you want your annotations to be indexed by their class name you can wrap the reader in an ``IndexedReader``: .. code-block:: php use Doctrine\Common\Annotations\AnnotationReader; use Doctrine\Common\Annotations\IndexedReader; $reader = new IndexedReader(new AnnotationReader()); .. warning:: You should never wrap the indexed reader inside a cached reader, only the other way around. This way you can re-use the cache with indexed or numeric keys, otherwise your code may experience failures due to caching in a numerical or indexed format. Registering Annotations ~~~~~~~~~~~~~~~~~~~~~~~ As explained in the introduction, Doctrine Annotations uses its own autoloading mechanism to determine if a given annotation has a corresponding PHP class that can be autoloaded. For annotation autoloading you have to configure the ``Doctrine\Common\Annotations\AnnotationRegistry``. There are three different mechanisms to configure annotation autoloading: - Calling ``AnnotationRegistry#registerFile($file)`` to register a file that contains one or more annotation classes. - Calling ``AnnotationRegistry#registerNamespace($namespace, $dirs = null)`` to register that the given namespace contains annotations and that their base directory is located at the given $dirs or in the include path if ``NULL`` is passed. The given directories should *NOT* be the directory where classes of the namespace are in, but the base directory of the root namespace. The AnnotationRegistry uses a namespace to directory separator approach to resolve the correct path. - Calling ``AnnotationRegistry#registerLoader($callable)`` to register an autoloader callback. The callback accepts the class as first and only parameter and has to return ``true`` if the corresponding file was found and included. .. note:: Loaders have to fail silently, if a class is not found even if it matches for example the namespace prefix of that loader. Never is a loader to throw a warning or exception if the loading failed otherwise parsing doc block annotations will become a huge pain. A sample loader callback could look like: .. code-block:: php use Doctrine\Common\Annotations\AnnotationRegistry; use Symfony\Component\ClassLoader\UniversalClassLoader; AnnotationRegistry::registerLoader(function($class) { $file = str_replace("\\", DIRECTORY_SEPARATOR, $class) . ".php"; if (file_exists("/my/base/path/" . $file)) { // file_exists() makes sure that the loader fails silently require "/my/base/path/" . $file; } }); $loader = new UniversalClassLoader(); AnnotationRegistry::registerLoader(array($loader, "loadClass")); Ignoring missing exceptions ~~~~~~~~~~~~~~~~~~~~~~~~~~~ By default an exception is thrown from the ``AnnotationReader`` if an annotation was found that: - is not part of the list of ignored "documentation annotations"; - was not imported through a use statement; - is not a fully qualified class that exists. You can disable this behavior for specific names if your docblocks do not follow strict requirements: .. code-block:: php $reader = new \Doctrine\Common\Annotations\AnnotationReader(); AnnotationReader::addGlobalIgnoredName('foo'); PHP Imports ~~~~~~~~~~~ By default the annotation reader parses the use-statement of a php file to gain access to the import rules and register them for the annotation processing. Only if you are using PHP Imports can you validate the correct usage of annotations and throw exceptions if you misspelled an annotation. This mechanism is enabled by default. To ease the upgrade path, we still allow you to disable this mechanism. Note however that we will remove this in future versions: .. code-block:: php $reader = new \Doctrine\Common\Annotations\AnnotationReader(); $reader->setEnabledPhpImports(false); PK! \AAannotations/docs/en/sidebar.rstnu[.. toctree:: :depth: 3 index annotations custom PK!CB''annotations/docs/en/custom.rstnu[Custom Annotation Classes ========================= If you want to define your own annotations, you just have to group them in a namespace and register this namespace in the ``AnnotationRegistry``. Annotation classes have to contain a class-level docblock with the text ``@Annotation``: .. code-block:: php namespace MyCompany\Annotations; /** @Annotation */ class Bar { // some code } Inject annotation values ------------------------ The annotation parser checks if the annotation constructor has arguments, if so then it will pass the value array, otherwise it will try to inject values into public properties directly: .. code-block:: php namespace MyCompany\Annotations; /** * @Annotation * * Some Annotation using a constructor */ class Bar { private $foo; public function __construct(array $values) { $this->foo = $values['foo']; } } /** * @Annotation * * Some Annotation without a constructor */ class Foo { public $bar; } Optional: Constructors with Named Parameters -------------------------------------------- Starting with Annotations v1.11 a new annotation instantiation strategy is available that aims at compatibility of Annotation classes with the PHP 8 attribute feature. You need to declare a constructor with regular parameter names that match the named arguments in the annotation syntax. To enable this feature, you can tag your annotation class with ``@NamedArgumentConstructor`` (available from v1.12) or implement the ``Doctrine\Common\Annotations\NamedArgumentConstructorAnnotation`` interface (available from v1.11 and deprecated as of v1.12). When using the ``@NamedArgumentConstructor`` tag, the first argument of the constructor is considered as the default one. Usage with the ``@NamedArgumentConstructor`` tag .. code-block:: php namespace MyCompany\Annotations; /** * @Annotation * @NamedArgumentConstructor */ class Bar implements NamedArgumentConstructorAnnotation { private $foo; public function __construct(string $foo) { $this->foo = $foo; } } /** Usable with @Bar(foo="baz") */ /** Usable with @Bar("baz") */ In combination with PHP 8's constructor property promotion feature you can simplify this to: .. code-block:: php namespace MyCompany\Annotations; /** * @Annotation * @NamedArgumentConstructor */ class Bar implements NamedArgumentConstructorAnnotation { public function __construct(private string $foo) {} } Usage with the ``Doctrine\Common\Annotations\NamedArgumentConstructorAnnotation`` interface (v1.11, deprecated as of v1.12): .. code-block:: php namespace MyCompany\Annotations; use Doctrine\Common\Annotations\NamedArgumentConstructorAnnotation; /** @Annotation */ class Bar implements NamedArgumentConstructorAnnotation { private $foo; public function __construct(private string $foo) {} } /** Usable with @Bar(foo="baz") */ Annotation Target ----------------- ``@Target`` indicates the kinds of class elements to which an annotation type is applicable. Then you could define one or more targets: - ``CLASS`` Allowed in class docblocks - ``PROPERTY`` Allowed in property docblocks - ``METHOD`` Allowed in the method docblocks - ``FUNCTION`` Allowed in function dockblocks - ``ALL`` Allowed in class, property, method and function docblocks - ``ANNOTATION`` Allowed inside other annotations If the annotations is not allowed in the current context, an ``AnnotationException`` is thrown. .. code-block:: php namespace MyCompany\Annotations; /** * @Annotation * @Target({"METHOD","PROPERTY"}) */ class Bar { // some code } /** * @Annotation * @Target("CLASS") */ class Foo { // some code } Attribute types --------------- The annotation parser checks the given parameters using the phpdoc annotation ``@var``, The data type could be validated using the ``@var`` annotation on the annotation properties or using the ``@Attributes`` and ``@Attribute`` annotations. If the data type does not match you get an ``AnnotationException`` .. code-block:: php namespace MyCompany\Annotations; /** * @Annotation * @Target({"METHOD","PROPERTY"}) */ class Bar { /** @var mixed */ public $mixed; /** @var boolean */ public $boolean; /** @var bool */ public $bool; /** @var float */ public $float; /** @var string */ public $string; /** @var integer */ public $integer; /** @var array */ public $array; /** @var SomeAnnotationClass */ public $annotation; /** @var array */ public $arrayOfIntegers; /** @var array */ public $arrayOfAnnotations; } /** * @Annotation * @Target({"METHOD","PROPERTY"}) * @Attributes({ * @Attribute("stringProperty", type = "string"), * @Attribute("annotProperty", type = "SomeAnnotationClass"), * }) */ class Foo { public function __construct(array $values) { $this->stringProperty = $values['stringProperty']; $this->annotProperty = $values['annotProperty']; } // some code } Annotation Required ------------------- ``@Required`` indicates that the field must be specified when the annotation is used. If it is not used you get an ``AnnotationException`` stating that this value can not be null. Declaring a required field: .. code-block:: php /** * @Annotation * @Target("ALL") */ class Foo { /** @Required */ public $requiredField; } Usage: .. code-block:: php /** @Foo(requiredField="value") */ public $direction; // Valid /** @Foo */ public $direction; // Required field missing, throws an AnnotationException Enumerated values ----------------- - An annotation property marked with ``@Enum`` is a field that accepts a fixed set of scalar values. - You should use ``@Enum`` fields any time you need to represent fixed values. - The annotation parser checks the given value and throws an ``AnnotationException`` if the value does not match. Declaring an enumerated property: .. code-block:: php /** * @Annotation * @Target("ALL") */ class Direction { /** * @Enum({"NORTH", "SOUTH", "EAST", "WEST"}) */ public $value; } Annotation usage: .. code-block:: php /** @Direction("NORTH") */ public $direction; // Valid value /** @Direction("NORTHEAST") */ public $direction; // Invalid value, throws an AnnotationException Constants --------- The use of constants and class constants is available on the annotations parser. The following usages are allowed: .. code-block:: php namespace MyCompany\Entity; use MyCompany\Annotations\Foo; use MyCompany\Annotations\Bar; use MyCompany\Entity\SomeClass; /** * @Foo(PHP_EOL) * @Bar(Bar::FOO) * @Foo({SomeClass::FOO, SomeClass::BAR}) * @Bar({SomeClass::FOO_KEY = SomeClass::BAR_VALUE}) */ class User { } Be careful with constants and the cache ! .. note:: The cached reader will not re-evaluate each time an annotation is loaded from cache. When a constant is changed the cache must be cleaned. Usage ----- Using the library API is simple. Using the annotations described in the previous section, you can now annotate other classes with your annotations: .. code-block:: php namespace MyCompany\Entity; use MyCompany\Annotations\Foo; use MyCompany\Annotations\Bar; /** * @Foo(bar="foo") * @Bar(foo="bar") */ class User { } Now we can write a script to get the annotations above: .. code-block:: php $reflClass = new ReflectionClass('MyCompany\Entity\User'); $classAnnotations = $reader->getClassAnnotations($reflClass); foreach ($classAnnotations AS $annot) { if ($annot instanceof \MyCompany\Annotations\Foo) { echo $annot->bar; // prints "foo"; } else if ($annot instanceof \MyCompany\Annotations\Bar) { echo $annot->foo; // prints "bar"; } } You have a complete API for retrieving annotation class instances from a class, property or method docblock: Reader API ~~~~~~~~~~ Access all annotations of a class ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: php public function getClassAnnotations(\ReflectionClass $class); Access one annotation of a class ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: php public function getClassAnnotation(\ReflectionClass $class, $annotationName); Access all annotations of a method ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: php public function getMethodAnnotations(\ReflectionMethod $method); Access one annotation of a method ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: php public function getMethodAnnotation(\ReflectionMethod $method, $annotationName); Access all annotations of a property ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: php public function getPropertyAnnotations(\ReflectionProperty $property); Access one annotation of a property ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: php public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName); Access all annotations of a function ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: php public function getFunctionAnnotations(\ReflectionFunction $property); Access one annotation of a function ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: php public function getFunctionAnnotation(\ReflectionFunction $property, $annotationName); PK!' annotations/docs/en/index.rstnu[Deprecation notice ================== PHP 8 introduced `attributes `_, which are a native replacement for annotations. As such, this library is considered feature complete, and should receive exclusively bugfixes and security fixes. Introduction ============ Doctrine Annotations allows to implement custom annotation functionality for PHP classes and functions. .. code-block:: php class Foo { /** * @MyAnnotation(myProperty="value") */ private $bar; } Annotations aren't implemented in PHP itself which is why this component offers a way to use the PHP doc-blocks as a place for the well known annotation syntax using the ``@`` char. Annotations in Doctrine are used for the ORM configuration to build the class mapping, but it can be used in other projects for other purposes too. Installation ============ You can install the Annotation component with composer: .. code-block::   $ composer require doctrine/annotations Create an annotation class ========================== An annotation class is a representation of the later used annotation configuration in classes. The annotation class of the previous example looks like this: .. code-block:: php /** * @Annotation */ final class MyAnnotation { public $myProperty; } The annotation class is declared as an annotation by ``@Annotation``. :ref:`Read more about custom annotations. ` Reading annotations =================== The access to the annotations happens by reflection of the class or function containing them. There are multiple reader-classes implementing the ``Doctrine\Common\Annotations\Reader`` interface, that can access the annotations of a class. A common one is ``Doctrine\Common\Annotations\AnnotationReader``: .. code-block:: php use Doctrine\Common\Annotations\AnnotationReader; use Doctrine\Common\Annotations\AnnotationRegistry; // Deprecated and will be removed in 2.0 but currently needed AnnotationRegistry::registerLoader('class_exists'); $reflectionClass = new ReflectionClass(Foo::class); $property = $reflectionClass->getProperty('bar'); $reader = new AnnotationReader(); $myAnnotation = $reader->getPropertyAnnotation( $property, MyAnnotation::class ); echo $myAnnotation->myProperty; // result: "value" Note that ``AnnotationRegistry::registerLoader('class_exists')`` only works if you already have an autoloader configured (i.e. composer autoloader). Otherwise, :ref:`please take a look to the other annotation autoload mechanisms `. A reader has multiple methods to access the annotations of a class or function. :ref:`Read more about handling annotations. ` IDE Support ----------- Some IDEs already provide support for annotations: - Eclipse via the `Symfony2 Plugin `_ - PhpStorm via the `PHP Annotations Plugin `_ or the `Symfony Plugin `_ .. _Read more about handling annotations.: annotations .. _Read more about custom annotations.: custom PK!=2lexer/composer.jsonnu[{ "name": "doctrine/lexer", "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", "license": "MIT", "type": "library", "keywords": [ "php", "parser", "lexer", "annotations", "docblock" ], "authors": [ { "name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com" }, { "name": "Roman Borschel", "email": "roman@code-factory.org" }, { "name": "Johannes Schmitt", "email": "schmittjoh@gmail.com" } ], "homepage": "https://www.doctrine-project.org/projects/lexer.html", "require": { "php": "^7.1 || ^8.0", "doctrine/deprecations": "^1.0" }, "require-dev": { "doctrine/coding-standard": "^9 || ^12", "phpstan/phpstan": "^1.3", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", "psalm/plugin-phpunit": "^0.18.3", "vimeo/psalm": "^4.11 || ^5.21" }, "autoload": { "psr-4": { "Doctrine\\Common\\Lexer\\": "src" } }, "autoload-dev": { "psr-4": { "Doctrine\\Tests\\Common\\Lexer\\": "tests" } }, "config": { "allow-plugins": { "composer/package-versions-deprecated": true, "dealerdirect/phpcodesniffer-composer-installer": true }, "sort-packages": true } } PK!`XQ)) lexer/LICENSEnu[Copyright (c) 2006-2018 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. PK!oKwwlexer/UPGRADE.mdnu[Note about upgrading: Doctrine uses static and runtime mechanisms to raise awareness about deprecated code. - Use of `@deprecated` docblock that is detected by IDEs (like PHPStorm) or Static Analysis tools (like Psalm, phpstan) - Use of our low-overhead runtime deprecation API, details: https://github.com/doctrine/deprecations/ # Upgrade to 2.0.0 `AbstractLexer::glimpse()` and `AbstractLexer::peek()` now return instances of `Doctrine\Common\Lexer\Token`, which is an array-like class Using it as an array is deprecated in favor of using properties of that class. Using `count()` on it is deprecated with no replacement. PK!6qoolexer/README.mdnu[# Doctrine Lexer [![Build Status](https://github.com/doctrine/lexer/workflows/Continuous%20Integration/badge.svg)](https://github.com/doctrine/lexer/actions) Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers. This lexer is used in Doctrine Annotations and in Doctrine ORM (DQL). https://www.doctrine-project.org/projects/lexer.html PK!pSSlexer/src/AbstractLexer.phpnu[> */ private $tokens = []; /** * Current lexer position in input string. * * @var int */ private $position = 0; /** * Current peek of current lexer position. * * @var int */ private $peek = 0; /** * The next token in the input. * * @var Token|null */ public $lookahead; /** * The last matched/seen token. * * @var Token|null */ public $token; /** * Composed regex for input parsing. * * @var non-empty-string|null */ private $regex; /** * Sets the input data to be tokenized. * * The Lexer is immediately reset and the new input tokenized. * Any unprocessed tokens from any previous input are lost. * * @param string $input The input to be tokenized. * * @return void */ public function setInput($input) { $this->input = $input; $this->tokens = []; $this->reset(); $this->scan($input); } /** * Resets the lexer. * * @return void */ public function reset() { $this->lookahead = null; $this->token = null; $this->peek = 0; $this->position = 0; } /** * Resets the peek pointer to 0. * * @return void */ public function resetPeek() { $this->peek = 0; } /** * Resets the lexer position on the input to the given position. * * @param int $position Position to place the lexical scanner. * * @return void */ public function resetPosition($position = 0) { $this->position = $position; } /** * Retrieve the original lexer's input until a given position. * * @param int $position * * @return string */ public function getInputUntilPosition($position) { return substr($this->input, 0, $position); } /** * Checks whether a given token matches the current lookahead. * * @param T $type * * @return bool * * @psalm-assert-if-true !=null $this->lookahead */ public function isNextToken($type) { return $this->lookahead !== null && $this->lookahead->isA($type); } /** * Checks whether any of the given tokens matches the current lookahead. * * @param list $types * * @return bool * * @psalm-assert-if-true !=null $this->lookahead */ public function isNextTokenAny(array $types) { return $this->lookahead !== null && $this->lookahead->isA(...$types); } /** * Moves to the next token in the input string. * * @return bool * * @psalm-assert-if-true !null $this->lookahead */ public function moveNext() { $this->peek = 0; $this->token = $this->lookahead; $this->lookahead = isset($this->tokens[$this->position]) ? $this->tokens[$this->position++] : null; return $this->lookahead !== null; } /** * Tells the lexer to skip input tokens until it sees a token with the given value. * * @param T $type The token type to skip until. * * @return void */ public function skipUntil($type) { while ($this->lookahead !== null && ! $this->lookahead->isA($type)) { $this->moveNext(); } } /** * Checks if given value is identical to the given token. * * @param string $value * @param int|string $token * * @return bool */ public function isA($value, $token) { return $this->getType($value) === $token; } /** * Moves the lookahead token forward. * * @return Token|null The next token or NULL if there are no more tokens ahead. */ public function peek() { if (isset($this->tokens[$this->position + $this->peek])) { return $this->tokens[$this->position + $this->peek++]; } return null; } /** * Peeks at the next token, returns it and immediately resets the peek. * * @return Token|null The next token or NULL if there are no more tokens ahead. */ public function glimpse() { $peek = $this->peek(); $this->peek = 0; return $peek; } /** * Scans the input string for tokens. * * @param string $input A query string. * * @return void */ protected function scan($input) { if (! isset($this->regex)) { $this->regex = sprintf( '/(%s)|%s/%s', implode(')|(', $this->getCatchablePatterns()), implode('|', $this->getNonCatchablePatterns()), $this->getModifiers() ); } $flags = PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE; $matches = preg_split($this->regex, $input, -1, $flags); if ($matches === false) { // Work around https://bugs.php.net/78122 $matches = [[$input, 0]]; } foreach ($matches as $match) { // Must remain before 'value' assignment since it can change content $firstMatch = $match[0]; $type = $this->getType($firstMatch); $this->tokens[] = new Token( $firstMatch, $type, $match[1] ); } } /** * Gets the literal for a given token. * * @param T $token * * @return int|string */ public function getLiteral($token) { if ($token instanceof UnitEnum) { return get_class($token) . '::' . $token->name; } $className = static::class; $reflClass = new ReflectionClass($className); $constants = $reflClass->getConstants(); foreach ($constants as $name => $value) { if ($value === $token) { return $className . '::' . $name; } } return $token; } /** * Regex modifiers * * @return string */ protected function getModifiers() { return 'iu'; } /** * Lexical catchable patterns. * * @return string[] */ abstract protected function getCatchablePatterns(); /** * Lexical non-catchable patterns. * * @return string[] */ abstract protected function getNonCatchablePatterns(); /** * Retrieve token type. Also processes the token value if necessary. * * @param string $value * * @return T|null * * @param-out V $value */ abstract protected function getType(&$value); } PK!+{ lexer/src/Token.phpnu[ */ final class Token implements ArrayAccess { /** * The string value of the token in the input string * * @readonly * @var V */ public $value; /** * The type of the token (identifier, numeric, string, input parameter, none) * * @readonly * @var T|null */ public $type; /** * The position of the token in the input string * * @readonly * @var int */ public $position; /** * @param V $value * @param T|null $type */ public function __construct($value, $type, int $position) { $this->value = $value; $this->type = $type; $this->position = $position; } /** @param T ...$types */ public function isA(...$types): bool { return in_array($this->type, $types, true); } /** * @deprecated Use the value, type or position property instead * {@inheritDoc} */ public function offsetExists($offset): bool { Deprecation::trigger( 'doctrine/lexer', 'https://github.com/doctrine/lexer/pull/79', 'Accessing %s properties via ArrayAccess is deprecated, use the value, type or position property instead', self::class ); return in_array($offset, ['value', 'type', 'position'], true); } /** * @deprecated Use the value, type or position property instead * {@inheritDoc} * * @param O $offset * * @return mixed * @psalm-return ( * O is 'value' * ? V * : ( * O is 'type' * ? T|null * : ( * O is 'position' * ? int * : mixed * ) * ) * ) * * @template O of array-key */ #[ReturnTypeWillChange] public function offsetGet($offset) { Deprecation::trigger( 'doctrine/lexer', 'https://github.com/doctrine/lexer/pull/79', 'Accessing %s properties via ArrayAccess is deprecated, use the value, type or position property instead', self::class ); return $this->$offset; } /** * @deprecated no replacement planned * {@inheritDoc} */ public function offsetSet($offset, $value): void { Deprecation::trigger( 'doctrine/lexer', 'https://github.com/doctrine/lexer/pull/79', 'Setting %s properties via ArrayAccess is deprecated', self::class ); $this->$offset = $value; } /** * @deprecated no replacement planned * {@inheritDoc} */ public function offsetUnset($offset): void { Deprecation::trigger( 'doctrine/lexer', 'https://github.com/doctrine/lexer/pull/79', 'Setting %s properties via ArrayAccess is deprecated', self::class ); $this->$offset = null; } } PK!dbal/composer.jsonnu[{ "name": "doctrine/dbal", "type": "library", "description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.", "keywords": [ "abstraction", "database", "dbal", "db2", "mariadb", "mssql", "mysql", "pgsql", "postgresql", "oci8", "oracle", "pdo", "queryobject", "sasql", "sql", "sqlite", "sqlserver", "sqlsrv" ], "homepage": "https://www.doctrine-project.org/projects/dbal.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"} ], "require": { "php": "^7.4 || ^8.0", "composer-runtime-api": "^2", "doctrine/cache": "^1.11|^2.0", "doctrine/deprecations": "^0.5.3|^1", "doctrine/event-manager": "^1|^2", "psr/cache": "^1|^2|^3", "psr/log": "^1|^2|^3" }, "require-dev": { "doctrine/coding-standard": "12.0.0", "fig/log-test": "^1", "jetbrains/phpstorm-stubs": "2023.1", "phpstan/phpstan": "1.10.58", "phpstan/phpstan-strict-rules": "^1.5", "phpunit/phpunit": "9.6.16", "psalm/plugin-phpunit": "0.18.4", "slevomat/coding-standard": "8.13.1", "squizlabs/php_codesniffer": "3.9.0", "symfony/cache": "^5.4|^6.0|^7.0", "symfony/console": "^4.4|^5.4|^6.0|^7.0", "vimeo/psalm": "4.30.0" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." }, "bin": ["bin/doctrine-dbal"], "config": { "sort-packages": true, "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true, "composer/package-versions-deprecated": true } }, "autoload": { "psr-4": { "Doctrine\\DBAL\\": "src" } }, "autoload-dev": { "psr-4": { "Doctrine\\DBAL\\Tests\\": "tests" } } } PK!%Smnndbal/CONTRIBUTING.mdnu[This repository has [guidelines specific to testing][testing guidelines], and Doctrine has [general contributing guidelines][contributor workflow], make sure you follow both. [contributor workflow]: https://www.doctrine-project.org/contribute/index.html [testing guidelines]: https://www.doctrine-project.org/projects/doctrine-dbal/en/stable/reference/testing.html PK!`XQ)) dbal/LICENSEnu[Copyright (c) 2006-2018 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. PK!?Ћndbal/bin/doctrine-dbal.phpnu[conflictResolutionMode = $conflictResolutionMode; } public function getConflictResolutionMode(): int { return $this->conflictResolutionMode; } } PK!j3dbal/src/Query/ForUpdate/ConflictResolutionMode.phpnu['; public const LT = '<'; public const LTE = '<='; public const GT = '>'; public const GTE = '>='; /** * The DBAL Connection. */ private Connection $connection; /** * Initializes a new ExpressionBuilder. * * @param Connection $connection The DBAL Connection. */ public function __construct(Connection $connection) { $this->connection = $connection; } /** * Creates a conjunction of the given expressions. * * @param string|CompositeExpression $expression * @param string|CompositeExpression ...$expressions */ public function and($expression, ...$expressions): CompositeExpression { return CompositeExpression::and($expression, ...$expressions); } /** * Creates a disjunction of the given expressions. * * @param string|CompositeExpression $expression * @param string|CompositeExpression ...$expressions */ public function or($expression, ...$expressions): CompositeExpression { return CompositeExpression::or($expression, ...$expressions); } /** * @deprecated Use `and()` instead. * * @param mixed $x Optional clause. Defaults = null, but requires * at least one defined when converting to string. * * @return CompositeExpression */ public function andX($x = null) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/3851', 'ExpressionBuilder::andX() is deprecated, use ExpressionBuilder::and() instead.', ); return new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); } /** * @deprecated Use `or()` instead. * * @param mixed $x Optional clause. Defaults = null, but requires * at least one defined when converting to string. * * @return CompositeExpression */ public function orX($x = null) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/3851', 'ExpressionBuilder::orX() is deprecated, use ExpressionBuilder::or() instead.', ); return new CompositeExpression(CompositeExpression::TYPE_OR, func_get_args()); } /** * Creates a comparison expression. * * @param mixed $x The left expression. * @param string $operator One of the ExpressionBuilder::* constants. * @param mixed $y The right expression. * * @return string */ public function comparison($x, $operator, $y) { return $x . ' ' . $operator . ' ' . $y; } /** * Creates an equality comparison expression with the given arguments. * * First argument is considered the left expression and the second is the right expression. * When converted to string, it will generated a = . Example: * * [php] * // u.id = ? * $expr->eq('u.id', '?'); * * @param mixed $x The left expression. * @param mixed $y The right expression. * * @return string */ public function eq($x, $y) { return $this->comparison($x, self::EQ, $y); } /** * Creates a non equality comparison expression with the given arguments. * First argument is considered the left expression and the second is the right expression. * When converted to string, it will generated a <> . Example: * * [php] * // u.id <> 1 * $q->where($q->expr()->neq('u.id', '1')); * * @param mixed $x The left expression. * @param mixed $y The right expression. * * @return string */ public function neq($x, $y) { return $this->comparison($x, self::NEQ, $y); } /** * Creates a lower-than comparison expression with the given arguments. * First argument is considered the left expression and the second is the right expression. * When converted to string, it will generated a < . Example: * * [php] * // u.id < ? * $q->where($q->expr()->lt('u.id', '?')); * * @param mixed $x The left expression. * @param mixed $y The right expression. * * @return string */ public function lt($x, $y) { return $this->comparison($x, self::LT, $y); } /** * Creates a lower-than-equal comparison expression with the given arguments. * First argument is considered the left expression and the second is the right expression. * When converted to string, it will generated a <= . Example: * * [php] * // u.id <= ? * $q->where($q->expr()->lte('u.id', '?')); * * @param mixed $x The left expression. * @param mixed $y The right expression. * * @return string */ public function lte($x, $y) { return $this->comparison($x, self::LTE, $y); } /** * Creates a greater-than comparison expression with the given arguments. * First argument is considered the left expression and the second is the right expression. * When converted to string, it will generated a > . Example: * * [php] * // u.id > ? * $q->where($q->expr()->gt('u.id', '?')); * * @param mixed $x The left expression. * @param mixed $y The right expression. * * @return string */ public function gt($x, $y) { return $this->comparison($x, self::GT, $y); } /** * Creates a greater-than-equal comparison expression with the given arguments. * First argument is considered the left expression and the second is the right expression. * When converted to string, it will generated a >= . Example: * * [php] * // u.id >= ? * $q->where($q->expr()->gte('u.id', '?')); * * @param mixed $x The left expression. * @param mixed $y The right expression. * * @return string */ public function gte($x, $y) { return $this->comparison($x, self::GTE, $y); } /** * Creates an IS NULL expression with the given arguments. * * @param string $x The expression to be restricted by IS NULL. * * @return string */ public function isNull($x) { return $x . ' IS NULL'; } /** * Creates an IS NOT NULL expression with the given arguments. * * @param string $x The expression to be restricted by IS NOT NULL. * * @return string */ public function isNotNull($x) { return $x . ' IS NOT NULL'; } /** * Creates a LIKE() comparison expression with the given arguments. * * @param string $x The expression to be inspected by the LIKE comparison * @param mixed $y The pattern to compare against * * @return string */ public function like($x, $y/*, ?string $escapeChar = null */) { return $this->comparison($x, 'LIKE', $y) . (func_num_args() >= 3 ? sprintf(' ESCAPE %s', func_get_arg(2)) : ''); } /** * Creates a NOT LIKE() comparison expression with the given arguments. * * @param string $x The expression to be inspected by the NOT LIKE comparison * @param mixed $y The pattern to compare against * * @return string */ public function notLike($x, $y/*, ?string $escapeChar = null */) { return $this->comparison($x, 'NOT LIKE', $y) . (func_num_args() >= 3 ? sprintf(' ESCAPE %s', func_get_arg(2)) : ''); } /** * Creates an IN () comparison expression with the given arguments. * * @param string $x The SQL expression to be matched against the set. * @param string|string[] $y The SQL expression or an array of SQL expressions representing the set. * * @return string */ public function in($x, $y) { return $this->comparison($x, 'IN', '(' . implode(', ', (array) $y) . ')'); } /** * Creates a NOT IN () comparison expression with the given arguments. * * @param string $x The SQL expression to be matched against the set. * @param string|string[] $y The SQL expression or an array of SQL expressions representing the set. * * @return string */ public function notIn($x, $y) { return $this->comparison($x, 'NOT IN', '(' . implode(', ', (array) $y) . ')'); } /** * Builds an SQL literal from a given input parameter. * * The usage of this method is discouraged. Use prepared statements * or {@see AbstractPlatform::quoteStringLiteral()} instead. * * @param mixed $input The parameter to be quoted. * @param int|null $type The type of the parameter. * * @return string */ public function literal($input, $type = null) { return $this->connection->quote($input, $type); } } PK!V..1dbal/src/Query/Expression/CompositeExpression.phpnu[type = $type; $this->addMultiple($parts); Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/3864', 'Do not use CompositeExpression constructor directly, use static and() and or() factory methods.', ); } /** * @param self|string $part * @param self|string ...$parts */ public static function and($part, ...$parts): self { return new self(self::TYPE_AND, array_merge([$part], $parts)); } /** * @param self|string $part * @param self|string ...$parts */ public static function or($part, ...$parts): self { return new self(self::TYPE_OR, array_merge([$part], $parts)); } /** * Adds multiple parts to composite expression. * * @deprecated This class will be made immutable. Use with() instead. * * @param self[]|string[] $parts * * @return CompositeExpression */ public function addMultiple(array $parts = []) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3844', 'CompositeExpression::addMultiple() is deprecated, use CompositeExpression::with() instead.', ); foreach ($parts as $part) { $this->add($part); } return $this; } /** * Adds an expression to composite expression. * * @deprecated This class will be made immutable. Use with() instead. * * @param mixed $part * * @return CompositeExpression */ public function add($part) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3844', 'CompositeExpression::add() is deprecated, use CompositeExpression::with() instead.', ); if ($part === null) { return $this; } if ($part instanceof self && count($part) === 0) { return $this; } $this->parts[] = $part; return $this; } /** * Returns a new CompositeExpression with the given parts added. * * @param self|string $part * @param self|string ...$parts */ public function with($part, ...$parts): self { $that = clone $this; $that->parts = array_merge($that->parts, [$part], $parts); return $that; } /** * Retrieves the amount of expressions on composite expression. * * @return int * @psalm-return int<0, max> */ #[ReturnTypeWillChange] public function count() { return count($this->parts); } /** * Retrieves the string representation of this composite expression. * * @return string */ public function __toString() { if ($this->count() === 1) { return (string) $this->parts[0]; } return '(' . implode(') ' . $this->type . ' (', $this->parts) . ')'; } /** * Returns the type of this composite expression (AND/OR). * * @return string */ public function getType() { return $this->type; } } PK!Pdbal/src/Query/QueryBuilder.phpnu[ [], 'distinct' => false, 'from' => [], 'join' => [], 'set' => [], 'where' => null, 'groupBy' => [], 'having' => null, 'orderBy' => [], 'values' => [], 'for_update' => null, ]; /** * The array of SQL parts collected. * * @var mixed[] */ private array $sqlParts = self::SQL_PARTS_DEFAULTS; /** * The complete SQL string for this query. */ private ?string $sql = null; /** * The query parameters. * * @var list|array */ private $params = []; /** * The parameter type map of this query. * * @var array|array */ private array $paramTypes = []; /** * The type of query this is. Can be select, update or delete. * * @psalm-var self::SELECT|self::DELETE|self::UPDATE|self::INSERT */ private int $type = self::SELECT; /** * The state of the query object. Can be dirty or clean. * * @psalm-var self::STATE_* */ private int $state = self::STATE_CLEAN; /** * The index of the first result to retrieve. */ private int $firstResult = 0; /** * The maximum number of results to retrieve or NULL to retrieve all results. */ private ?int $maxResults = null; /** * The counter of bound parameters used with {@see bindValue). */ private int $boundCounter = 0; /** * The query cache profile used for caching results. */ private ?QueryCacheProfile $resultCacheProfile = null; /** * Initializes a new QueryBuilder. * * @param Connection $connection The DBAL Connection. */ public function __construct(Connection $connection) { $this->connection = $connection; } /** * Gets an ExpressionBuilder used for object-oriented construction of query expressions. * This producer method is intended for convenient inline usage. Example: * * * $qb = $conn->createQueryBuilder() * ->select('u') * ->from('users', 'u') * ->where($qb->expr()->eq('u.id', 1)); * * * For more complex expression construction, consider storing the expression * builder object in a local variable. * * @return ExpressionBuilder */ public function expr() { return $this->connection->getExpressionBuilder(); } /** * Gets the type of the currently built query. * * @deprecated If necessary, track the type of the query being built outside of the builder. * * @return int */ public function getType() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5551', 'Relying on the type of the query being built is deprecated.' . ' If necessary, track the type of the query being built outside of the builder.', ); return $this->type; } /** * Gets the associated DBAL Connection for this query builder. * * @deprecated Use the connection used to instantiate the builder instead. * * @return Connection */ public function getConnection() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5780', '%s is deprecated. Use the connection used to instantiate the builder instead.', __METHOD__, ); return $this->connection; } /** * Gets the state of this query builder instance. * * @deprecated The builder state is an internal concern. * * @return int Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN. * @psalm-return self::STATE_* */ public function getState() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5551', 'Relying on the query builder state is deprecated as it is an internal concern.', ); return $this->state; } /** * Prepares and executes an SQL query and returns the first row of the result * as an associative array. * * @return array|false False is returned if no rows are found. * * @throws Exception */ public function fetchAssociative() { return $this->executeQuery()->fetchAssociative(); } /** * Prepares and executes an SQL query and returns the first row of the result * as a numerically indexed array. * * @return array|false False is returned if no rows are found. * * @throws Exception */ public function fetchNumeric() { return $this->executeQuery()->fetchNumeric(); } /** * Prepares and executes an SQL query and returns the value of a single column * of the first row of the result. * * @return mixed|false False is returned if no rows are found. * * @throws Exception */ public function fetchOne() { return $this->executeQuery()->fetchOne(); } /** * Prepares and executes an SQL query and returns the result as an array of numeric arrays. * * @return array> * * @throws Exception */ public function fetchAllNumeric(): array { return $this->executeQuery()->fetchAllNumeric(); } /** * Prepares and executes an SQL query and returns the result as an array of associative arrays. * * @return array> * * @throws Exception */ public function fetchAllAssociative(): array { return $this->executeQuery()->fetchAllAssociative(); } /** * Prepares and executes an SQL query and returns the result as an associative array with the keys * mapped to the first column and the values mapped to the second column. * * @return array * * @throws Exception */ public function fetchAllKeyValue(): array { return $this->executeQuery()->fetchAllKeyValue(); } /** * Prepares and executes an SQL query and returns the result as an associative array with the keys mapped * to the first column and the values being an associative array representing the rest of the columns * and their values. * * @return array> * * @throws Exception */ public function fetchAllAssociativeIndexed(): array { return $this->executeQuery()->fetchAllAssociativeIndexed(); } /** * Prepares and executes an SQL query and returns the result as an array of the first column values. * * @return array * * @throws Exception */ public function fetchFirstColumn(): array { return $this->executeQuery()->fetchFirstColumn(); } /** * Executes an SQL query (SELECT) and returns a Result. * * @throws Exception */ public function executeQuery(): Result { return $this->connection->executeQuery( $this->getSQL(), $this->params, $this->paramTypes, $this->resultCacheProfile, ); } /** * Executes an SQL statement and returns the number of affected rows. * * Should be used for INSERT, UPDATE and DELETE * * @return int The number of affected rows. * * @throws Exception */ public function executeStatement(): int { return $this->connection->executeStatement($this->getSQL(), $this->params, $this->paramTypes); } /** * Executes this query using the bound parameters and their types. * * @deprecated Use {@see executeQuery()} or {@see executeStatement()} instead. * * @return Result|int|string * * @throws Exception */ public function execute() { if ($this->type === self::SELECT) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4578', 'QueryBuilder::execute() is deprecated, use QueryBuilder::executeQuery() for SQL queries instead.', ); return $this->executeQuery(); } Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4578', 'QueryBuilder::execute() is deprecated, use QueryBuilder::executeStatement() for SQL statements instead.', ); return $this->connection->executeStatement($this->getSQL(), $this->params, $this->paramTypes); } /** * Gets the complete SQL string formed by the current specifications of this QueryBuilder. * * * $qb = $em->createQueryBuilder() * ->select('u') * ->from('User', 'u') * echo $qb->getSQL(); // SELECT u FROM User u * * * @return string The SQL query string. */ public function getSQL() { if ($this->sql !== null && $this->state === self::STATE_CLEAN) { return $this->sql; } switch ($this->type) { case self::INSERT: $sql = $this->getSQLForInsert(); break; case self::DELETE: $sql = $this->getSQLForDelete(); break; case self::UPDATE: $sql = $this->getSQLForUpdate(); break; case self::SELECT: $sql = $this->getSQLForSelect(); break; } $this->state = self::STATE_CLEAN; $this->sql = $sql; return $sql; } /** * Sets a query parameter for the query being constructed. * * * $qb = $conn->createQueryBuilder() * ->select('u') * ->from('users', 'u') * ->where('u.id = :user_id') * ->setParameter('user_id', 1); * * * @param int|string $key Parameter position or name * @param mixed $value Parameter value * @param int|string|Type|null $type Parameter type * * @return $this This QueryBuilder instance. */ public function setParameter($key, $value, $type = ParameterType::STRING) { if ($type !== null) { $this->paramTypes[$key] = $type; } else { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5550', 'Using NULL as prepared statement parameter type is deprecated.' . 'Omit or use ParameterType::STRING instead', ); } $this->params[$key] = $value; return $this; } /** * Sets a collection of query parameters for the query being constructed. * * * $qb = $conn->createQueryBuilder() * ->select('u') * ->from('users', 'u') * ->where('u.id = :user_id1 OR u.id = :user_id2') * ->setParameters(array( * 'user_id1' => 1, * 'user_id2' => 2 * )); * * * @param list|array $params Parameters to set * @param array|array $types Parameter types * * @return $this This QueryBuilder instance. */ public function setParameters(array $params, array $types = []) { $this->paramTypes = $types; $this->params = $params; return $this; } /** * Gets all defined query parameters for the query being constructed indexed by parameter index or name. * * @return list|array The currently defined query parameters */ public function getParameters() { return $this->params; } /** * Gets a (previously set) query parameter of the query being constructed. * * @param mixed $key The key (index or name) of the bound parameter. * * @return mixed The value of the bound parameter. */ public function getParameter($key) { return $this->params[$key] ?? null; } /** * Gets all defined query parameter types for the query being constructed indexed by parameter index or name. * * @return array|array The currently defined * query parameter types */ public function getParameterTypes() { return $this->paramTypes; } /** * Gets a (previously set) query parameter type of the query being constructed. * * @param int|string $key The key of the bound parameter type * * @return int|string|Type The value of the bound parameter type */ public function getParameterType($key) { return $this->paramTypes[$key] ?? ParameterType::STRING; } /** * Sets the position of the first result to retrieve (the "offset"). * * @param int $firstResult The first result to return. * * @return $this This QueryBuilder instance. */ public function setFirstResult($firstResult) { $this->state = self::STATE_DIRTY; $this->firstResult = $firstResult; return $this; } /** * Gets the position of the first result the query object was set to retrieve (the "offset"). * * @return int The position of the first result. */ public function getFirstResult() { return $this->firstResult; } /** * Sets the maximum number of results to retrieve (the "limit"). * * @param int|null $maxResults The maximum number of results to retrieve or NULL to retrieve all results. * * @return $this This QueryBuilder instance. */ public function setMaxResults($maxResults) { $this->state = self::STATE_DIRTY; $this->maxResults = $maxResults; return $this; } /** * Gets the maximum number of results the query object was set to retrieve (the "limit"). * Returns NULL if all results will be returned. * * @return int|null The maximum number of results. */ public function getMaxResults() { return $this->maxResults; } /** * Locks the queried rows for a subsequent update. * * @return $this */ public function forUpdate(int $conflictResolutionMode = ConflictResolutionMode::ORDINARY): self { $this->state = self::STATE_DIRTY; $this->sqlParts['for_update'] = new ForUpdate($conflictResolutionMode); return $this; } /** * Either appends to or replaces a single, generic query part. * * The available parts are: 'select', 'from', 'set', 'where', * 'groupBy', 'having' and 'orderBy'. * * @param string $sqlPartName * @param mixed $sqlPart * @param bool $append * * @return $this This QueryBuilder instance. */ public function add($sqlPartName, $sqlPart, $append = false) { $isArray = is_array($sqlPart); $isMultiple = is_array($this->sqlParts[$sqlPartName]); if ($isMultiple && ! $isArray) { $sqlPart = [$sqlPart]; } $this->state = self::STATE_DIRTY; if ($append) { if ( $sqlPartName === 'orderBy' || $sqlPartName === 'groupBy' || $sqlPartName === 'select' || $sqlPartName === 'set' ) { foreach ($sqlPart as $part) { $this->sqlParts[$sqlPartName][] = $part; } } elseif ($isArray && is_array($sqlPart[key($sqlPart)])) { $key = key($sqlPart); $this->sqlParts[$sqlPartName][$key][] = $sqlPart[$key]; } elseif ($isMultiple) { $this->sqlParts[$sqlPartName][] = $sqlPart; } else { $this->sqlParts[$sqlPartName] = $sqlPart; } return $this; } $this->sqlParts[$sqlPartName] = $sqlPart; return $this; } /** * Specifies an item that is to be returned in the query result. * Replaces any previously specified selections, if any. * * USING AN ARRAY ARGUMENT IS DEPRECATED. Pass each value as an individual argument. * * * $qb = $conn->createQueryBuilder() * ->select('u.id', 'p.id') * ->from('users', 'u') * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id'); * * * @param string|string[]|null $select The selection expression. USING AN ARRAY OR NULL IS DEPRECATED. * Pass each value as an individual argument. * * @return $this This QueryBuilder instance. */ public function select($select = null/*, string ...$selects*/) { $this->type = self::SELECT; if ($select === null) { return $this; } if (is_array($select)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3837', 'Passing an array for the first argument to QueryBuilder::select() is deprecated, ' . 'pass each value as an individual variadic argument instead.', ); } $selects = is_array($select) ? $select : func_get_args(); return $this->add('select', $selects); } /** * Adds or removes DISTINCT to/from the query. * * * $qb = $conn->createQueryBuilder() * ->select('u.id') * ->distinct() * ->from('users', 'u') * * * @return $this This QueryBuilder instance. */ public function distinct(/* bool $distinct = true */): self { $this->sqlParts['distinct'] = func_num_args() < 1 || func_get_arg(0); $this->state = self::STATE_DIRTY; return $this; } /** * Adds an item that is to be returned in the query result. * * USING AN ARRAY ARGUMENT IS DEPRECATED. Pass each value as an individual argument. * * * $qb = $conn->createQueryBuilder() * ->select('u.id') * ->addSelect('p.id') * ->from('users', 'u') * ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id'); * * * @param string|string[]|null $select The selection expression. USING AN ARRAY OR NULL IS DEPRECATED. * Pass each value as an individual argument. * * @return $this This QueryBuilder instance. */ public function addSelect($select = null/*, string ...$selects*/) { $this->type = self::SELECT; if ($select === null) { return $this; } if (is_array($select)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3837', 'Passing an array for the first argument to QueryBuilder::addSelect() is deprecated, ' . 'pass each value as an individual variadic argument instead.', ); } $selects = is_array($select) ? $select : func_get_args(); return $this->add('select', $selects, true); } /** * Turns the query being built into a bulk delete query that ranges over * a certain table. * * * $qb = $conn->createQueryBuilder() * ->delete('users', 'u') * ->where('u.id = :user_id') * ->setParameter(':user_id', 1); * * * @param string $delete The table whose rows are subject to the deletion. * @param string $alias The table alias used in the constructed query. * * @return $this This QueryBuilder instance. */ public function delete($delete = null, $alias = null) { $this->type = self::DELETE; if ($delete === null) { return $this; } return $this->add('from', [ 'table' => $delete, 'alias' => $alias, ]); } /** * Turns the query being built into a bulk update query that ranges over * a certain table * * * $qb = $conn->createQueryBuilder() * ->update('counters', 'c') * ->set('c.value', 'c.value + 1') * ->where('c.id = ?'); * * * @param string $update The table whose rows are subject to the update. * @param string $alias The table alias used in the constructed query. * * @return $this This QueryBuilder instance. */ public function update($update = null, $alias = null) { $this->type = self::UPDATE; if ($update === null) { return $this; } return $this->add('from', [ 'table' => $update, 'alias' => $alias, ]); } /** * Turns the query being built into an insert query that inserts into * a certain table * * * $qb = $conn->createQueryBuilder() * ->insert('users') * ->values( * array( * 'name' => '?', * 'password' => '?' * ) * ); * * * @param string $insert The table into which the rows should be inserted. * * @return $this This QueryBuilder instance. */ public function insert($insert = null) { $this->type = self::INSERT; if ($insert === null) { return $this; } return $this->add('from', ['table' => $insert]); } /** * Creates and adds a query root corresponding to the table identified by the * given alias, forming a cartesian product with any existing query roots. * * * $qb = $conn->createQueryBuilder() * ->select('u.id') * ->from('users', 'u') * * * @param string $from The table. * @param string|null $alias The alias of the table. * * @return $this This QueryBuilder instance. */ public function from($from, $alias = null) { return $this->add('from', [ 'table' => $from, 'alias' => $alias, ], true); } /** * Creates and adds a join to the query. * * * $qb = $conn->createQueryBuilder() * ->select('u.name') * ->from('users', 'u') * ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1'); * * * @param string $fromAlias The alias that points to a from clause. * @param string $join The table name to join. * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * * @return $this This QueryBuilder instance. */ public function join($fromAlias, $join, $alias, $condition = null) { return $this->innerJoin($fromAlias, $join, $alias, $condition); } /** * Creates and adds a join to the query. * * * $qb = $conn->createQueryBuilder() * ->select('u.name') * ->from('users', 'u') * ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); * * * @param string $fromAlias The alias that points to a from clause. * @param string $join The table name to join. * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * * @return $this This QueryBuilder instance. */ public function innerJoin($fromAlias, $join, $alias, $condition = null) { return $this->add('join', [ $fromAlias => [ 'joinType' => 'inner', 'joinTable' => $join, 'joinAlias' => $alias, 'joinCondition' => $condition, ], ], true); } /** * Creates and adds a left join to the query. * * * $qb = $conn->createQueryBuilder() * ->select('u.name') * ->from('users', 'u') * ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); * * * @param string $fromAlias The alias that points to a from clause. * @param string $join The table name to join. * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * * @return $this This QueryBuilder instance. */ public function leftJoin($fromAlias, $join, $alias, $condition = null) { return $this->add('join', [ $fromAlias => [ 'joinType' => 'left', 'joinTable' => $join, 'joinAlias' => $alias, 'joinCondition' => $condition, ], ], true); } /** * Creates and adds a right join to the query. * * * $qb = $conn->createQueryBuilder() * ->select('u.name') * ->from('users', 'u') * ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1'); * * * @param string $fromAlias The alias that points to a from clause. * @param string $join The table name to join. * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * * @return $this This QueryBuilder instance. */ public function rightJoin($fromAlias, $join, $alias, $condition = null) { return $this->add('join', [ $fromAlias => [ 'joinType' => 'right', 'joinTable' => $join, 'joinAlias' => $alias, 'joinCondition' => $condition, ], ], true); } /** * Sets a new value for a column in a bulk update query. * * * $qb = $conn->createQueryBuilder() * ->update('counters', 'c') * ->set('c.value', 'c.value + 1') * ->where('c.id = ?'); * * * @param string $key The column to set. * @param string $value The value, expression, placeholder, etc. * * @return $this This QueryBuilder instance. */ public function set($key, $value) { return $this->add('set', $key . ' = ' . $value, true); } /** * Specifies one or more restrictions to the query result. * Replaces any previously specified restrictions, if any. * * * $qb = $conn->createQueryBuilder() * ->select('c.value') * ->from('counters', 'c') * ->where('c.id = ?'); * * // You can optionally programmatically build and/or expressions * $qb = $conn->createQueryBuilder(); * * $or = $qb->expr()->orx(); * $or->add($qb->expr()->eq('c.id', 1)); * $or->add($qb->expr()->eq('c.id', 2)); * * $qb->update('counters', 'c') * ->set('c.value', 'c.value + 1') * ->where($or); * * * @param mixed $predicates The restriction predicates. * * @return $this This QueryBuilder instance. */ public function where($predicates) { if (! (func_num_args() === 1 && $predicates instanceof CompositeExpression)) { $predicates = CompositeExpression::and(...func_get_args()); } return $this->add('where', $predicates); } /** * Adds one or more restrictions to the query results, forming a logical * conjunction with any previously specified restrictions. * * * $qb = $conn->createQueryBuilder() * ->select('u') * ->from('users', 'u') * ->where('u.username LIKE ?') * ->andWhere('u.is_active = 1'); * * * @see where() * * @param mixed $where The query restrictions. * * @return $this This QueryBuilder instance. */ public function andWhere($where) { $args = func_get_args(); $where = $this->getQueryPart('where'); if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_AND) { $where = $where->with(...$args); } else { array_unshift($args, $where); $where = CompositeExpression::and(...$args); } return $this->add('where', $where, true); } /** * Adds one or more restrictions to the query results, forming a logical * disjunction with any previously specified restrictions. * * * $qb = $em->createQueryBuilder() * ->select('u.name') * ->from('users', 'u') * ->where('u.id = 1') * ->orWhere('u.id = 2'); * * * @see where() * * @param mixed $where The WHERE statement. * * @return $this This QueryBuilder instance. */ public function orWhere($where) { $args = func_get_args(); $where = $this->getQueryPart('where'); if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_OR) { $where = $where->with(...$args); } else { array_unshift($args, $where); $where = CompositeExpression::or(...$args); } return $this->add('where', $where, true); } /** * Specifies a grouping over the results of the query. * Replaces any previously specified groupings, if any. * * USING AN ARRAY ARGUMENT IS DEPRECATED. Pass each value as an individual argument. * * * $qb = $conn->createQueryBuilder() * ->select('u.name') * ->from('users', 'u') * ->groupBy('u.id'); * * * @param string|string[] $groupBy The grouping expression. USING AN ARRAY IS DEPRECATED. * Pass each value as an individual argument. * * @return $this This QueryBuilder instance. */ public function groupBy($groupBy/*, string ...$groupBys*/) { if (is_array($groupBy) && count($groupBy) === 0) { return $this; } if (is_array($groupBy)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3837', 'Passing an array for the first argument to QueryBuilder::groupBy() is deprecated, ' . 'pass each value as an individual variadic argument instead.', ); } $groupBy = is_array($groupBy) ? $groupBy : func_get_args(); return $this->add('groupBy', $groupBy, false); } /** * Adds a grouping expression to the query. * * USING AN ARRAY ARGUMENT IS DEPRECATED. Pass each value as an individual argument. * * * $qb = $conn->createQueryBuilder() * ->select('u.name') * ->from('users', 'u') * ->groupBy('u.lastLogin') * ->addGroupBy('u.createdAt'); * * * @param string|string[] $groupBy The grouping expression. USING AN ARRAY IS DEPRECATED. * Pass each value as an individual argument. * * @return $this This QueryBuilder instance. */ public function addGroupBy($groupBy/*, string ...$groupBys*/) { if (is_array($groupBy) && count($groupBy) === 0) { return $this; } if (is_array($groupBy)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3837', 'Passing an array for the first argument to QueryBuilder::addGroupBy() is deprecated, ' . 'pass each value as an individual variadic argument instead.', ); } $groupBy = is_array($groupBy) ? $groupBy : func_get_args(); return $this->add('groupBy', $groupBy, true); } /** * Sets a value for a column in an insert query. * * * $qb = $conn->createQueryBuilder() * ->insert('users') * ->values( * array( * 'name' => '?' * ) * ) * ->setValue('password', '?'); * * * @param string $column The column into which the value should be inserted. * @param string $value The value that should be inserted into the column. * * @return $this This QueryBuilder instance. */ public function setValue($column, $value) { $this->sqlParts['values'][$column] = $value; return $this; } /** * Specifies values for an insert query indexed by column names. * Replaces any previous values, if any. * * * $qb = $conn->createQueryBuilder() * ->insert('users') * ->values( * array( * 'name' => '?', * 'password' => '?' * ) * ); * * * @param mixed[] $values The values to specify for the insert query indexed by column names. * * @return $this This QueryBuilder instance. */ public function values(array $values) { return $this->add('values', $values); } /** * Specifies a restriction over the groups of the query. * Replaces any previous having restrictions, if any. * * @param mixed $having The restriction over the groups. * * @return $this This QueryBuilder instance. */ public function having($having) { if (! (func_num_args() === 1 && $having instanceof CompositeExpression)) { $having = CompositeExpression::and(...func_get_args()); } return $this->add('having', $having); } /** * Adds a restriction over the groups of the query, forming a logical * conjunction with any existing having restrictions. * * @param mixed $having The restriction to append. * * @return $this This QueryBuilder instance. */ public function andHaving($having) { $args = func_get_args(); $having = $this->getQueryPart('having'); if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_AND) { $having = $having->with(...$args); } else { array_unshift($args, $having); $having = CompositeExpression::and(...$args); } return $this->add('having', $having); } /** * Adds a restriction over the groups of the query, forming a logical * disjunction with any existing having restrictions. * * @param mixed $having The restriction to add. * * @return $this This QueryBuilder instance. */ public function orHaving($having) { $args = func_get_args(); $having = $this->getQueryPart('having'); if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_OR) { $having = $having->with(...$args); } else { array_unshift($args, $having); $having = CompositeExpression::or(...$args); } return $this->add('having', $having); } /** * Specifies an ordering for the query results. * Replaces any previously specified orderings, if any. * * @param string $sort The ordering expression. * @param string $order The ordering direction. * * @return $this This QueryBuilder instance. */ public function orderBy($sort, $order = null) { return $this->add('orderBy', $sort . ' ' . ($order ?? 'ASC'), false); } /** * Adds an ordering to the query results. * * @param string $sort The ordering expression. * @param string $order The ordering direction. * * @return $this This QueryBuilder instance. */ public function addOrderBy($sort, $order = null) { return $this->add('orderBy', $sort . ' ' . ($order ?? 'ASC'), true); } /** * Gets a query part by its name. * * @deprecated The query parts are implementation details and should not be relied upon. * * @param string $queryPartName * * @return mixed */ public function getQueryPart($queryPartName) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6179', 'Getting query parts is deprecated as they are implementation details.', ); return $this->sqlParts[$queryPartName]; } /** * Gets all query parts. * * @deprecated The query parts are implementation details and should not be relied upon. * * @return mixed[] */ public function getQueryParts() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6179', 'Getting query parts is deprecated as they are implementation details.', ); return $this->sqlParts; } /** * Resets SQL parts. * * @deprecated Use the dedicated reset*() methods instead. * * @param string[]|null $queryPartNames * * @return $this This QueryBuilder instance. */ public function resetQueryParts($queryPartNames = null) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6193', '%s() is deprecated, instead use dedicated reset methods for the parts that shall be reset.', __METHOD__, ); $queryPartNames ??= array_keys($this->sqlParts); foreach ($queryPartNames as $queryPartName) { $this->sqlParts[$queryPartName] = self::SQL_PARTS_DEFAULTS[$queryPartName]; } $this->state = self::STATE_DIRTY; return $this; } /** * Resets a single SQL part. * * @deprecated Use the dedicated reset*() methods instead. * * @param string $queryPartName * * @return $this This QueryBuilder instance. */ public function resetQueryPart($queryPartName) { if ($queryPartName === 'distinct') { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6193', 'Calling %s() with "distinct" is deprecated, call distinct(false) instead.', __METHOD__, ); return $this->distinct(false); } $newMethodName = 'reset' . ucfirst($queryPartName); if (array_key_exists($queryPartName, self::SQL_PARTS_DEFAULTS) && method_exists($this, $newMethodName)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6193', 'Calling %s() with "%s" is deprecated, call %s() instead.', __METHOD__, $queryPartName, $newMethodName, ); return $this->$newMethodName(); } Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6193', 'Calling %s() with "%s" is deprecated without replacement.', __METHOD__, $queryPartName, $newMethodName, ); $this->sqlParts[$queryPartName] = self::SQL_PARTS_DEFAULTS[$queryPartName]; $this->state = self::STATE_DIRTY; return $this; } /** * Resets the WHERE conditions for the query. * * @return $this This QueryBuilder instance. */ public function resetWhere(): self { $this->sqlParts['where'] = self::SQL_PARTS_DEFAULTS['where']; $this->state = self::STATE_DIRTY; return $this; } /** * Resets the grouping for the query. * * @return $this This QueryBuilder instance. */ public function resetGroupBy(): self { $this->sqlParts['groupBy'] = self::SQL_PARTS_DEFAULTS['groupBy']; $this->state = self::STATE_DIRTY; return $this; } /** * Resets the HAVING conditions for the query. * * @return $this This QueryBuilder instance. */ public function resetHaving(): self { $this->sqlParts['having'] = self::SQL_PARTS_DEFAULTS['having']; $this->state = self::STATE_DIRTY; return $this; } /** * Resets the ordering for the query. * * @return $this This QueryBuilder instance. */ public function resetOrderBy(): self { $this->sqlParts['orderBy'] = self::SQL_PARTS_DEFAULTS['orderBy']; $this->state = self::STATE_DIRTY; return $this; } /** @throws Exception */ private function getSQLForSelect(): string { return $this->connection->getDatabasePlatform() ->createSelectSQLBuilder() ->buildSQL( new SelectQuery( $this->sqlParts['distinct'], $this->sqlParts['select'], $this->getFromClauses(), $this->sqlParts['where'], $this->sqlParts['groupBy'], $this->sqlParts['having'], $this->sqlParts['orderBy'], new Limit($this->maxResults, $this->firstResult), $this->sqlParts['for_update'], ), ); } /** * @return string[] * * @throws QueryException */ private function getFromClauses(): array { $fromClauses = []; $knownAliases = []; // Loop through all FROM clauses foreach ($this->sqlParts['from'] as $from) { if ($from['alias'] === null) { $tableSql = $from['table']; $tableReference = $from['table']; } else { $tableSql = $from['table'] . ' ' . $from['alias']; $tableReference = $from['alias']; } $knownAliases[$tableReference] = true; $fromClauses[$tableReference] = $tableSql . $this->getSQLForJoins($tableReference, $knownAliases); } $this->verifyAllAliasesAreKnown($knownAliases); return $fromClauses; } /** * @param array $knownAliases * * @throws QueryException */ private function verifyAllAliasesAreKnown(array $knownAliases): void { foreach ($this->sqlParts['join'] as $fromAlias => $joins) { if (! isset($knownAliases[$fromAlias])) { throw QueryException::unknownAlias($fromAlias, array_keys($knownAliases)); } } } /** * Converts this instance into an INSERT string in SQL. */ private function getSQLForInsert(): string { return 'INSERT INTO ' . $this->sqlParts['from']['table'] . ' (' . implode(', ', array_keys($this->sqlParts['values'])) . ')' . ' VALUES(' . implode(', ', $this->sqlParts['values']) . ')'; } /** * Converts this instance into an UPDATE string in SQL. */ private function getSQLForUpdate(): string { $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : ''); return 'UPDATE ' . $table . ' SET ' . implode(', ', $this->sqlParts['set']) . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : ''); } /** * Converts this instance into a DELETE string in SQL. */ private function getSQLForDelete(): string { $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : ''); return 'DELETE FROM ' . $table . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : ''); } /** * Gets a string representation of this QueryBuilder which corresponds to * the final SQL query being constructed. * * @return string The string representation of this QueryBuilder. */ public function __toString() { return $this->getSQL(); } /** * Creates a new named parameter and bind the value $value to it. * * This method provides a shortcut for {@see Statement::bindValue()} * when using prepared statements. * * The parameter $value specifies the value that you want to bind. If * $placeholder is not provided createNamedParameter() will automatically * create a placeholder for you. An automatic placeholder will be of the * name ':dcValue1', ':dcValue2' etc. * * Example: * * $value = 2; * $q->eq( 'id', $q->createNamedParameter( $value ) ); * $stmt = $q->executeQuery(); // executed with 'id = 2' * * * @link http://www.zetacomponents.org * * @param mixed $value * @param int|string|Type|null $type * @param string $placeHolder The name to bind with. The string must start with a colon ':'. * * @return string the placeholder name used. */ public function createNamedParameter($value, $type = ParameterType::STRING, $placeHolder = null) { if ($placeHolder === null) { $this->boundCounter++; $placeHolder = ':dcValue' . $this->boundCounter; } $this->setParameter(substr($placeHolder, 1), $value, $type); return $placeHolder; } /** * Creates a new positional parameter and bind the given value to it. * * Attention: If you are using positional parameters with the query builder you have * to be very careful to bind all parameters in the order they appear in the SQL * statement , otherwise they get bound in the wrong order which can lead to serious * bugs in your code. * * Example: * * $qb = $conn->createQueryBuilder(); * $qb->select('u.*') * ->from('users', 'u') * ->where('u.username = ' . $qb->createPositionalParameter('Foo', ParameterType::STRING)) * ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', ParameterType::STRING)) * * * @param mixed $value * @param int|string|Type|null $type * * @return string */ public function createPositionalParameter($value, $type = ParameterType::STRING) { $this->setParameter($this->boundCounter, $value, $type); $this->boundCounter++; return '?'; } /** * @param string $fromAlias * @param array $knownAliases * * @throws QueryException */ private function getSQLForJoins($fromAlias, array &$knownAliases): string { $sql = ''; if (isset($this->sqlParts['join'][$fromAlias])) { foreach ($this->sqlParts['join'][$fromAlias] as $join) { if (array_key_exists($join['joinAlias'], $knownAliases)) { throw QueryException::nonUniqueAlias((string) $join['joinAlias'], array_keys($knownAliases)); } $sql .= ' ' . strtoupper($join['joinType']) . ' JOIN ' . $join['joinTable'] . ' ' . $join['joinAlias']; if ($join['joinCondition'] !== null) { $sql .= ' ON ' . $join['joinCondition']; } $knownAliases[$join['joinAlias']] = true; } foreach ($this->sqlParts['join'][$fromAlias] as $join) { $sql .= $this->getSQLForJoins($join['joinAlias'], $knownAliases); } } return $sql; } /** * Deep clone of all expression objects in the SQL parts. * * @return void */ public function __clone() { foreach ($this->sqlParts as $part => $elements) { if (is_array($this->sqlParts[$part])) { foreach ($this->sqlParts[$part] as $idx => $element) { if (! is_object($element)) { continue; } $this->sqlParts[$part][$idx] = clone $element; } } elseif (is_object($elements)) { $this->sqlParts[$part] = clone $elements; } } foreach ($this->params as $name => $param) { if (! is_object($param)) { continue; } $this->params[$name] = clone $param; } } /** * Enables caching of the results of this query, for given amount of seconds * and optionally specified which key to use for the cache entry. * * @return $this */ public function enableResultCache(QueryCacheProfile $cacheProfile): self { $this->resultCacheProfile = $cacheProfile; return $this; } /** * Disables caching of the results of this query. * * @return $this */ public function disableResultCache(): self { $this->resultCacheProfile = null; return $this; } } PK!ki1!dbal/src/Query/QueryException.phpnu[maxResults = $maxResults; $this->firstResult = $firstResult; } public function isDefined(): bool { return $this->maxResults !== null || $this->firstResult !== 0; } public function getMaxResults(): ?int { return $this->maxResults; } public function getFirstResult(): int { return $this->firstResult; } } PK!%"LFFdbal/src/Query/SelectQuery.phpnu[distinct = $distinct; $this->columns = $columns; $this->from = $from; $this->where = $where; $this->groupBy = $groupBy; $this->having = $having; $this->orderBy = $orderBy; $this->limit = $limit; $this->forUpdate = $forUpdate; } public function isDistinct(): bool { return $this->distinct; } /** @return string[] */ public function getColumns(): array { return $this->columns; } /** @return string[] */ public function getFrom(): array { return $this->from; } public function getWhere(): ?string { return $this->where; } /** @return string[] */ public function getGroupBy(): array { return $this->groupBy; } public function getHaving(): ?string { return $this->having; } /** @return string[] */ public function getOrderBy(): array { return $this->orderBy; } public function getLimit(): Limit { return $this->limit; } public function getForUpdate(): ?ForUpdate { return $this->forUpdate; } } PK!84dbal/src/ParameterType.phpnu[getMessage(); } else { $message = 'An exception occurred in the driver: ' . $driverException->getMessage(); } parent::__construct($message, $driverException->getCode(), $driverException); $this->query = $query; } /** * {@inheritDoc} */ public function getSQLState() { $previous = $this->getPrevious(); assert($previous instanceof TheDriverException); return $previous->getSQLState(); } public function getQuery(): ?Query { return $this->query; } } PK!E;I2dbal/src/Exception/NonUniqueFieldNameException.phpnu[ $params All connection parameters. * @psalm-param Params $params All connection parameters. * * @return DriverConnection The database connection. * * @throws Exception */ public function connect( #[SensitiveParameter] array $params ); /** * Gets the DatabasePlatform instance that provides all the metadata about * the platform this driver connects to. * * @return AbstractPlatform The database platform. */ public function getDatabasePlatform(); /** * Gets the SchemaManager that can be used to inspect and change the underlying * database schema of the platform this driver connects to. * * @deprecated Use {@link AbstractPlatform::createSchemaManager()} instead. * * @return AbstractSchemaManager */ public function getSchemaManager(Connection $conn, AbstractPlatform $platform); /** * Gets the ExceptionConverter that can be used to convert driver-level exceptions into DBAL exceptions. */ public function getExceptionConverter(): ExceptionConverter; } PK!D*dbal/src/Exception.phpnu[ */ private array $params; /** * The types of the parameters bound to the query. * * @var array */ private array $types; /** * @param array $params * @param array $types * * @psalm-suppress ImpurePropertyAssignment */ public function __construct(string $sql, array $params, array $types) { $this->sql = $sql; $this->params = $params; $this->types = $types; } public function getSQL(): string { return $this->sql; } /** @return array */ public function getParams(): array { return $this->params; } /** @return array */ public function getTypes(): array { return $this->types; } } PK!#9++5dbal/src/Connections/PrimaryReadReplicaConnection.phpnu[executeQuery("DELETE FROM table"); * * Be aware that Connection#executeQuery is a method specifically for READ * operations only. * * Use Connection#executeStatement for any SQL statement that changes/updates * state in the database (UPDATE, INSERT, DELETE or DDL statements). * * This connection is limited to replica operations using the * Connection#executeQuery operation only, because it wouldn't be compatible * with the ORM or SchemaManager code otherwise. Both use all the other * operations in a context where writes could happen to a replica, which makes * this restricted approach necessary. * * You can manually connect to the primary at any time by calling: * * $conn->ensureConnectedToPrimary(); * * Instantiation through the DriverManager looks like: * * @psalm-import-type Params from DriverManager * @example * * $conn = DriverManager::getConnection(array( * 'wrapperClass' => 'Doctrine\DBAL\Connections\PrimaryReadReplicaConnection', * 'driver' => 'pdo_mysql', * 'primary' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''), * 'replica' => array( * array('user' => 'replica1', 'password' => '', 'host' => '', 'dbname' => ''), * array('user' => 'replica2', 'password' => '', 'host' => '', 'dbname' => ''), * ) * )); * * You can also pass 'driverOptions' and any other documented option to each of this drivers * to pass additional information. */ class PrimaryReadReplicaConnection extends Connection { /** * Primary and Replica connection (one of the randomly picked replicas). * * @var DriverConnection[]|null[] */ protected $connections = ['primary' => null, 'replica' => null]; /** * You can keep the replica connection and then switch back to it * during the request if you know what you are doing. * * @var bool */ protected $keepReplica = false; /** * Creates Primary Replica Connection. * * @internal The connection can be only instantiated by the driver manager. * * @param array $params * @psalm-param Params $params * * @throws Exception * @throws InvalidArgumentException */ public function __construct( array $params, Driver $driver, ?Configuration $config = null, ?EventManager $eventManager = null ) { if (! isset($params['replica'], $params['primary'])) { throw new InvalidArgumentException('primary or replica configuration missing'); } if (count($params['replica']) === 0) { throw new InvalidArgumentException('You have to configure at least one replica.'); } if (isset($params['driver'])) { $params['primary']['driver'] = $params['driver']; foreach ($params['replica'] as $replicaKey => $replica) { $params['replica'][$replicaKey]['driver'] = $params['driver']; } } $this->keepReplica = (bool) ($params['keepReplica'] ?? false); parent::__construct($params, $driver, $config, $eventManager); } /** * Checks if the connection is currently towards the primary or not. */ public function isConnectedToPrimary(): bool { return $this->_conn !== null && $this->_conn === $this->connections['primary']; } /** * @param string|null $connectionName * * @return bool */ public function connect($connectionName = null) { if ($connectionName !== null) { throw new InvalidArgumentException( 'Passing a connection name as first argument is not supported anymore.' . ' Use ensureConnectedToPrimary()/ensureConnectedToReplica() instead.', ); } return $this->performConnect(); } protected function performConnect(?string $connectionName = null): bool { $requestedConnectionChange = ($connectionName !== null); $connectionName = $connectionName ?? 'replica'; if ($connectionName !== 'replica' && $connectionName !== 'primary') { throw new InvalidArgumentException('Invalid option to connect(), only primary or replica allowed.'); } // If we have a connection open, and this is not an explicit connection // change request, then abort right here, because we are already done. // This prevents writes to the replica in case of "keepReplica" option enabled. if ($this->_conn !== null && ! $requestedConnectionChange) { return false; } $forcePrimaryAsReplica = false; if ($this->getTransactionNestingLevel() > 0) { $connectionName = 'primary'; $forcePrimaryAsReplica = true; } if (isset($this->connections[$connectionName])) { $this->_conn = $this->connections[$connectionName]; if ($forcePrimaryAsReplica && ! $this->keepReplica) { $this->connections['replica'] = $this->_conn; } return false; } if ($connectionName === 'primary') { $this->connections['primary'] = $this->_conn = $this->connectTo($connectionName); // Set replica connection to primary to avoid invalid reads if (! $this->keepReplica) { $this->connections['replica'] = $this->connections['primary']; } } else { $this->connections['replica'] = $this->_conn = $this->connectTo($connectionName); } if ($this->_eventManager->hasListeners(Events::postConnect)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5784', 'Subscribing to %s events is deprecated. Implement a middleware instead.', Events::postConnect, ); $eventArgs = new ConnectionEventArgs($this); $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); } return true; } /** * Connects to the primary node of the database cluster. * * All following statements after this will be executed against the primary node. */ public function ensureConnectedToPrimary(): bool { return $this->performConnect('primary'); } /** * Connects to a replica node of the database cluster. * * All following statements after this will be executed against the replica node, * unless the keepReplica option is set to false and a primary connection * was already opened. */ public function ensureConnectedToReplica(): bool { return $this->performConnect('replica'); } /** * Connects to a specific connection. * * @param string $connectionName * * @return DriverConnection * * @throws Exception */ protected function connectTo($connectionName) { $params = $this->getParams(); $connectionParams = $this->chooseConnectionConfiguration($connectionName, $params); try { return $this->_driver->connect($connectionParams); } catch (DriverException $e) { throw $this->convertException($e); } } /** * @param string $connectionName * @param mixed[] $params * * @return mixed */ protected function chooseConnectionConfiguration( $connectionName, #[SensitiveParameter] $params ) { if ($connectionName === 'primary') { return $params['primary']; } $config = $params['replica'][array_rand($params['replica'])]; if (! isset($config['charset']) && isset($params['primary']['charset'])) { $config['charset'] = $params['primary']['charset']; } return $config; } /** * {@inheritDoc} */ public function executeStatement($sql, array $params = [], array $types = []) { $this->ensureConnectedToPrimary(); return parent::executeStatement($sql, $params, $types); } /** * {@inheritDoc} */ public function beginTransaction() { $this->ensureConnectedToPrimary(); return parent::beginTransaction(); } /** * {@inheritDoc} */ public function commit() { $this->ensureConnectedToPrimary(); return parent::commit(); } /** * {@inheritDoc} */ public function rollBack() { $this->ensureConnectedToPrimary(); return parent::rollBack(); } /** * {@inheritDoc} */ public function close() { unset($this->connections['primary'], $this->connections['replica']); parent::close(); $this->_conn = null; $this->connections = ['primary' => null, 'replica' => null]; } /** * {@inheritDoc} */ public function createSavepoint($savepoint) { $this->ensureConnectedToPrimary(); parent::createSavepoint($savepoint); } /** * {@inheritDoc} */ public function releaseSavepoint($savepoint) { $this->ensureConnectedToPrimary(); parent::releaseSavepoint($savepoint); } /** * {@inheritDoc} */ public function rollbackSavepoint($savepoint) { $this->ensureConnectedToPrimary(); parent::rollbackSavepoint($savepoint); } public function prepare(string $sql): Statement { $this->ensureConnectedToPrimary(); return parent::prepare($sql); } } PK!Pu_$$dbal/src/DriverManager.phpnu[, * driverClass?: class-string, * driverOptions?: array, * host?: string, * password?: string, * path?: string, * persistent?: bool, * platform?: Platforms\AbstractPlatform, * port?: int, * serverVersion?: string, * url?: string, * user?: string, * unix_socket?: string, * } * @psalm-type Params = array{ * application_name?: string, * charset?: string, * dbname?: string, * defaultTableOptions?: array, * default_dbname?: string, * driver?: key-of, * driverClass?: class-string, * driverOptions?: array, * host?: string, * keepSlave?: bool, * keepReplica?: bool, * master?: OverrideParams, * memory?: bool, * password?: string, * path?: string, * persistent?: bool, * platform?: Platforms\AbstractPlatform, * port?: int, * primary?: OverrideParams, * replica?: array, * serverVersion?: string, * sharding?: array, * slaves?: array, * url?: string, * user?: string, * wrapperClass?: class-string, * unix_socket?: string, * } */ final class DriverManager { /** * List of supported drivers and their mappings to the driver classes. * * To add your own driver use the 'driverClass' parameter to {@see DriverManager::getConnection()}. */ private const DRIVER_MAP = [ 'pdo_mysql' => PDO\MySQL\Driver::class, 'pdo_sqlite' => PDO\SQLite\Driver::class, 'pdo_pgsql' => PDO\PgSQL\Driver::class, 'pdo_oci' => PDO\OCI\Driver::class, 'oci8' => OCI8\Driver::class, 'ibm_db2' => IBMDB2\Driver::class, 'pdo_sqlsrv' => PDO\SQLSrv\Driver::class, 'mysqli' => Mysqli\Driver::class, 'pgsql' => PgSQL\Driver::class, 'sqlsrv' => SQLSrv\Driver::class, 'sqlite3' => SQLite3\Driver::class, ]; /** * List of URL schemes from a database URL and their mappings to driver. * * @deprecated Use actual driver names instead. * * @var array * @psalm-var array> */ private static array $driverSchemeAliases = [ 'db2' => 'ibm_db2', 'mssql' => 'pdo_sqlsrv', 'mysql' => 'pdo_mysql', 'mysql2' => 'pdo_mysql', // Amazon RDS, for some weird reason 'postgres' => 'pdo_pgsql', 'postgresql' => 'pdo_pgsql', 'pgsql' => 'pdo_pgsql', 'sqlite' => 'pdo_sqlite', 'sqlite3' => 'pdo_sqlite', ]; /** * Private constructor. This class cannot be instantiated. * * @codeCoverageIgnore */ private function __construct() { } /** * Creates a connection object based on the specified parameters. * This method returns a Doctrine\DBAL\Connection which wraps the underlying * driver connection. * * $params must contain at least one of the following. * * Either 'driver' with one of the array keys of {@see DRIVER_MAP}, * OR 'driverClass' that contains the full class name (with namespace) of the * driver class to instantiate. * * Other (optional) parameters: * * user (string): * The username to use when connecting. * * password (string): * The password to use when connecting. * * driverOptions (array): * Any additional driver-specific options for the driver. These are just passed * through to the driver. * * wrapperClass: * You may specify a custom wrapper class through the 'wrapperClass' * parameter but this class MUST inherit from Doctrine\DBAL\Connection. * * driverClass: * The driver class to use. * * @param Configuration|null $config The configuration to use. * @param EventManager|null $eventManager The event manager to use. * @psalm-param Params $params * * @psalm-return ($params is array{wrapperClass: class-string} ? T : Connection) * * @throws Exception * * @template T of Connection */ public static function getConnection( #[SensitiveParameter] array $params, ?Configuration $config = null, ?EventManager $eventManager = null ): Connection { // create default config and event manager, if not set $config ??= new Configuration(); $eventManager ??= new EventManager(); $params = self::parseDatabaseUrl($params); // URL support for PrimaryReplicaConnection if (isset($params['primary'])) { $params['primary'] = self::parseDatabaseUrl($params['primary']); } if (isset($params['replica'])) { foreach ($params['replica'] as $key => $replicaParams) { $params['replica'][$key] = self::parseDatabaseUrl($replicaParams); } } $driver = self::createDriver($params['driver'] ?? null, $params['driverClass'] ?? null); foreach ($config->getMiddlewares() as $middleware) { $driver = $middleware->wrap($driver); } $wrapperClass = $params['wrapperClass'] ?? Connection::class; if (! is_a($wrapperClass, Connection::class, true)) { throw Exception::invalidWrapperClass($wrapperClass); } return new $wrapperClass($params, $driver, $config, $eventManager); } /** * Returns the list of supported drivers. * * @return string[] * @psalm-return list> */ public static function getAvailableDrivers(): array { return array_keys(self::DRIVER_MAP); } /** * @throws Exception * * @psalm-assert key-of|null $driver * @psalm-assert class-string|null $driverClass */ private static function createDriver(?string $driver, ?string $driverClass): Driver { if ($driverClass === null) { if ($driver === null) { throw Exception::driverRequired(); } if (! isset(self::DRIVER_MAP[$driver])) { throw Exception::unknownDriver($driver, array_keys(self::DRIVER_MAP)); } $driverClass = self::DRIVER_MAP[$driver]; } elseif (! is_a($driverClass, Driver::class, true)) { throw Exception::invalidDriverClass($driverClass); } return new $driverClass(); } /** * Extracts parts from a database URL, if present, and returns an * updated list of parameters. * * @param mixed[] $params The list of parameters. * @psalm-param Params $params * * @return mixed[] A modified list of parameters with info from a database * URL extracted into indidivual parameter parts. * @psalm-return Params * * @throws Exception */ private static function parseDatabaseUrl( #[SensitiveParameter] array $params ): array { if (! isset($params['url'])) { return $params; } Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5843', 'The "url" connection parameter is deprecated. Please use %s to parse a database url before calling %s.', DsnParser::class, self::class, ); $parser = new DsnParser(self::$driverSchemeAliases); try { $parsedParams = $parser->parse($params['url']); } catch (MalformedDsnException $e) { throw new Exception('Malformed parameter "url".', 0, $e); } if (isset($parsedParams['driver'])) { // The requested driver from the URL scheme takes precedence // over the default custom driver from the connection parameters (if any). unset($params['driverClass']); } $params = array_merge($params, $parsedParams); // If a schemeless connection URL is given, we require a default driver or default custom driver // as connection parameter. if (! isset($params['driverClass']) && ! isset($params['driver'])) { throw Exception::driverRequired($params['url']); } return $params; } } PK!KZ[[dbal/src/Cache/ArrayResult.phpnu[> */ private array $data; private int $columnCount = 0; private int $num = 0; /** @param list> $data */ public function __construct(array $data) { $this->data = $data; if (count($data) === 0) { return; } $this->columnCount = count($data[0]); } /** * {@inheritDoc} */ public function fetchNumeric() { $row = $this->fetch(); if ($row === false) { return false; } return array_values($row); } /** * {@inheritDoc} */ public function fetchAssociative() { return $this->fetch(); } /** * {@inheritDoc} */ public function fetchOne() { $row = $this->fetch(); if ($row === false) { return false; } return reset($row); } /** * {@inheritDoc} */ public function fetchAllNumeric(): array { return FetchUtils::fetchAllNumeric($this); } /** * {@inheritDoc} */ public function fetchAllAssociative(): array { return FetchUtils::fetchAllAssociative($this); } /** * {@inheritDoc} */ public function fetchFirstColumn(): array { return FetchUtils::fetchFirstColumn($this); } public function rowCount(): int { return count($this->data); } public function columnCount(): int { return $this->columnCount; } public function free(): void { $this->data = []; } /** @return array|false */ private function fetch() { if (! isset($this->data[$this->num])) { return false; } return $this->data[$this->num++]; } } PK!q}qq$dbal/src/Cache/QueryCacheProfile.phpnu[lifetime = $lifetime; $this->cacheKey = $cacheKey; if ($resultCache instanceof CacheItemPoolInterface) { $this->resultCache = $resultCache; } elseif ($resultCache instanceof Cache) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4620', 'Passing an instance of %s to %s as $resultCache is deprecated. Pass an instance of %s instead.', Cache::class, __METHOD__, CacheItemPoolInterface::class, ); $this->resultCache = CacheAdapter::wrap($resultCache); } elseif ($resultCache !== null) { throw new TypeError(sprintf( '$resultCache: Expected either null or an instance of %s or %s, got %s.', CacheItemPoolInterface::class, Cache::class, get_class($resultCache), )); } } public function getResultCache(): ?CacheItemPoolInterface { return $this->resultCache; } /** * @deprecated Use {@see getResultCache()} instead. * * @return Cache|null */ public function getResultCacheDriver() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4620', '%s is deprecated, call getResultCache() instead.', __METHOD__, ); return $this->resultCache !== null ? DoctrineProvider::wrap($this->resultCache) : null; } /** @return int */ public function getLifetime() { return $this->lifetime; } /** * @return string * * @throws CacheException */ public function getCacheKey() { if ($this->cacheKey === null) { throw CacheException::noCacheKey(); } return $this->cacheKey; } /** * Generates the real cache key from query, params, types and connection parameters. * * @param string $sql * @param list|array $params * @param array|array $types * @param array $connectionParams * * @return array{string, string} */ public function generateCacheKeys($sql, $params, $types, array $connectionParams = []) { if (isset($connectionParams['password'])) { unset($connectionParams['password']); } $realCacheKey = 'query=' . $sql . '¶ms=' . serialize($params) . '&types=' . serialize($types) . '&connectionParams=' . hash('sha256', serialize($connectionParams)); // should the key be automatically generated using the inputs or is the cache key set? $cacheKey = $this->cacheKey ?? sha1($realCacheKey); return [$cacheKey, $realCacheKey]; } public function setResultCache(CacheItemPoolInterface $cache): QueryCacheProfile { return new QueryCacheProfile($this->lifetime, $this->cacheKey, $cache); } /** * @deprecated Use {@see setResultCache()} instead. * * @return QueryCacheProfile */ public function setResultCacheDriver(Cache $cache) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4620', '%s is deprecated, call setResultCache() instead.', __METHOD__, ); return new QueryCacheProfile($this->lifetime, $this->cacheKey, CacheAdapter::wrap($cache)); } /** * @param string|null $cacheKey * * @return QueryCacheProfile */ public function setCacheKey($cacheKey) { return new QueryCacheProfile($this->lifetime, $cacheKey, $this->resultCache); } /** * @param int $lifetime * * @return QueryCacheProfile */ public function setLifetime($lifetime) { return new QueryCacheProfile($lifetime, $this->cacheKey, $this->resultCache); } } PK!wu!dbal/src/Cache/CacheException.phpnu[logger = $logger; } public function wrap(DriverInterface $driver): DriverInterface { return new Driver($driver, $this->logger); } } PK!yUUdbal/src/Logging/Driver.phpnu[logger = $logger; } /** * {@inheritDoc} */ public function connect( #[SensitiveParameter] array $params ) { $this->logger->info('Connecting with parameters {params}', ['params' => $this->maskPassword($params)]); return new Connection( parent::connect($params), $this->logger, ); } /** * @param array $params Connection parameters * * @return array */ private function maskPassword( #[SensitiveParameter] array $params ): array { if (isset($params['password'])) { $params['password'] = ''; } if (isset($params['url'])) { $params['url'] = ''; } return $params; } } PK!i}Udbal/src/Logging/Connection.phpnu[logger = $logger; } public function __destruct() { $this->logger->info('Disconnecting'); } public function prepare(string $sql): DriverStatement { return new Statement( parent::prepare($sql), $this->logger, $sql, ); } public function query(string $sql): Result { $this->logger->debug('Executing query: {sql}', ['sql' => $sql]); return parent::query($sql); } public function exec(string $sql): int { $this->logger->debug('Executing statement: {sql}', ['sql' => $sql]); return parent::exec($sql); } /** * {@inheritDoc} */ public function beginTransaction() { $this->logger->debug('Beginning transaction'); return parent::beginTransaction(); } /** * {@inheritDoc} */ public function commit() { $this->logger->debug('Committing transaction'); return parent::commit(); } /** * {@inheritDoc} */ public function rollBack() { $this->logger->debug('Rolling back transaction'); return parent::rollBack(); } } PK!;dbal/src/Logging/DebugStack.phpnu[> */ public $queries = []; /** * If Debug Stack is enabled (log queries) or not. * * @var bool */ public $enabled = true; /** @var float|null */ public $start = null; /** @var int */ public $currentQuery = 0; public function __construct() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4967', 'DebugStack is deprecated.', ); } /** * {@inheritDoc} */ public function startQuery($sql, ?array $params = null, ?array $types = null) { if (! $this->enabled) { return; } $this->start = microtime(true); $this->queries[++$this->currentQuery] = [ 'sql' => $sql, 'params' => $params, 'types' => $types, 'executionMS' => 0, ]; } /** * {@inheritDoc} */ public function stopQuery() { if (! $this->enabled) { return; } $this->queries[$this->currentQuery]['executionMS'] = microtime(true) - $this->start; } } PK!"f dbal/src/Logging/Statement.phpnu[|array */ private array $params = []; /** @var array|array */ private array $types = []; /** @internal This statement can be only instantiated by its connection. */ public function __construct(StatementInterface $statement, LoggerInterface $logger, string $sql) { parent::__construct($statement); $this->logger = $logger; $this->sql = $sql; } /** * {@inheritDoc} * * @deprecated Use {@see bindValue()} instead. */ public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5563', '%s is deprecated. Use bindValue() instead.', __METHOD__, ); if (func_num_args() < 3) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5558', 'Not passing $type to Statement::bindParam() is deprecated.' . ' Pass the type corresponding to the parameter being bound.', ); } $this->params[$param] = &$variable; $this->types[$param] = $type; return parent::bindParam($param, $variable, $type, ...array_slice(func_get_args(), 3)); } /** * {@inheritDoc} */ public function bindValue($param, $value, $type = ParameterType::STRING) { if (func_num_args() < 3) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5558', 'Not passing $type to Statement::bindValue() is deprecated.' . ' Pass the type corresponding to the parameter being bound.', ); } $this->params[$param] = $value; $this->types[$param] = $type; return parent::bindValue($param, $value, $type); } /** * {@inheritDoc} */ public function execute($params = null): ResultInterface { $this->logger->debug('Executing statement: {sql} (parameters: {params}, types: {types})', [ 'sql' => $this->sql, 'params' => $params ?? $this->params, 'types' => $this->types, ]); return parent::execute($params); } } PK! dbal/src/Logging/LoggerChain.phpnu[ */ private iterable $loggers; /** @param iterable $loggers */ public function __construct(iterable $loggers = []) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4967', 'LoggerChain is deprecated', ); $this->loggers = $loggers; } /** * {@inheritDoc} */ public function startQuery($sql, ?array $params = null, ?array $types = null) { foreach ($this->loggers as $logger) { $logger->startQuery($sql, $params, $types); } } /** * {@inheritDoc} */ public function stopQuery() { foreach ($this->loggers as $logger) { $logger->stopQuery(); } } } PK!DL!dbal/src/Logging/SQLLogger.phpnu[|array|null $params Statement parameters * @param array|array|null $types Parameter types * * @return void */ public function startQuery($sql, ?array $params = null, ?array $types = null); /** * Marks the last started query as stopped. This can be used for timing of queries. * * @return void */ public function stopQuery(); } PK!8NӮdbal/src/Configuration.phpnu[schemaAssetsFilter = static function (): bool { return true; }; } /** * Sets the SQL logger to use. Defaults to NULL which means SQL logging is disabled. * * @deprecated Use {@see setMiddlewares()} and {@see \Doctrine\DBAL\Logging\Middleware} instead. */ public function setSQLLogger(?SQLLogger $logger = null): void { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4967', '%s is deprecated, use setMiddlewares() and Logging\\Middleware instead.', __METHOD__, ); $this->sqlLogger = $logger; } /** * Gets the SQL logger that is used. * * @deprecated */ public function getSQLLogger(): ?SQLLogger { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4967', '%s is deprecated.', __METHOD__, ); return $this->sqlLogger; } /** * Gets the cache driver implementation that is used for query result caching. */ public function getResultCache(): ?CacheItemPoolInterface { return $this->resultCache; } /** * Gets the cache driver implementation that is used for query result caching. * * @deprecated Use {@see getResultCache()} instead. */ public function getResultCacheImpl(): ?Cache { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4620', '%s is deprecated, call getResultCache() instead.', __METHOD__, ); return $this->resultCacheImpl; } /** * Sets the cache driver implementation that is used for query result caching. */ public function setResultCache(CacheItemPoolInterface $cache): void { $this->resultCacheImpl = DoctrineProvider::wrap($cache); $this->resultCache = $cache; } /** * Sets the cache driver implementation that is used for query result caching. * * @deprecated Use {@see setResultCache()} instead. */ public function setResultCacheImpl(Cache $cacheImpl): void { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4620', '%s is deprecated, call setResultCache() instead.', __METHOD__, ); $this->resultCacheImpl = $cacheImpl; $this->resultCache = CacheAdapter::wrap($cacheImpl); } /** * Sets the callable to use to filter schema assets. */ public function setSchemaAssetsFilter(?callable $callable = null): void { if (func_num_args() < 1) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5483', 'Not passing an argument to %s is deprecated.', __METHOD__, ); } elseif ($callable === null) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5483', 'Using NULL as a schema asset filter is deprecated.' . ' Use a callable that always returns true instead.', ); } $this->schemaAssetsFilter = $callable; } /** * Returns the callable to use to filter schema assets. */ public function getSchemaAssetsFilter(): ?callable { return $this->schemaAssetsFilter; } /** * Sets the default auto-commit mode for connections. * * If a connection is in auto-commit mode, then all its SQL statements will be executed and committed as individual * transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by a call to either * the method commit or the method rollback. By default, new connections are in auto-commit mode. * * @see getAutoCommit * * @param bool $autoCommit True to enable auto-commit mode; false to disable it */ public function setAutoCommit(bool $autoCommit): void { $this->autoCommit = $autoCommit; } /** * Returns the default auto-commit mode for connections. * * @see setAutoCommit * * @return bool True if auto-commit mode is enabled by default for connections, false otherwise. */ public function getAutoCommit(): bool { return $this->autoCommit; } /** * @param Middleware[] $middlewares * * @return $this */ public function setMiddlewares(array $middlewares): self { $this->middlewares = $middlewares; return $this; } /** @return Middleware[] */ public function getMiddlewares(): array { return $this->middlewares; } public function getSchemaManagerFactory(): ?SchemaManagerFactory { return $this->schemaManagerFactory; } /** @return $this */ public function setSchemaManagerFactory(SchemaManagerFactory $schemaManagerFactory): self { $this->schemaManagerFactory = $schemaManagerFactory; return $this; } public function getDisableTypeComments(): bool { return $this->disableTypeComments; } /** @return $this */ public function setDisableTypeComments(bool $disableTypeComments): self { $this->disableTypeComments = $disableTypeComments; return $this; } } PK!Y.dbal/src/ArrayParameterType.phpnu[ * @psalm-var Params */ private array $params; /** * The database platform object used by the connection or NULL before it's initialized. */ private ?AbstractPlatform $platform = null; private ?ExceptionConverter $exceptionConverter = null; private ?Parser $parser = null; /** * The schema manager. * * @deprecated Use {@see createSchemaManager()} instead. * * @var AbstractSchemaManager|null */ protected $_schemaManager; /** * The used DBAL driver. * * @var Driver */ protected $_driver; /** * Flag that indicates whether the current transaction is marked for rollback only. */ private bool $isRollbackOnly = false; private SchemaManagerFactory $schemaManagerFactory; /** * Initializes a new instance of the Connection class. * * @internal The connection can be only instantiated by the driver manager. * * @param array $params The connection parameters. * @param Driver $driver The driver to use. * @param Configuration|null $config The configuration, optional. * @param EventManager|null $eventManager The event manager, optional. * @psalm-param Params $params * * @throws Exception */ public function __construct( #[SensitiveParameter] array $params, Driver $driver, ?Configuration $config = null, ?EventManager $eventManager = null ) { $this->_driver = $driver; $this->params = $params; // Create default config and event manager if none given $config ??= new Configuration(); $eventManager ??= new EventManager(); $this->_config = $config; $this->_eventManager = $eventManager; if (isset($params['platform'])) { if (! $params['platform'] instanceof Platforms\AbstractPlatform) { throw Exception::invalidPlatformType($params['platform']); } Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5699', 'The "platform" connection parameter is deprecated.' . ' Use a driver middleware that would instantiate the platform instead.', ); $this->platform = $params['platform']; $this->platform->setEventManager($this->_eventManager); $this->platform->setDisableTypeComments($config->getDisableTypeComments()); } $this->_expr = $this->createExpressionBuilder(); $this->autoCommit = $config->getAutoCommit(); $schemaManagerFactory = $config->getSchemaManagerFactory(); if ($schemaManagerFactory === null) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5812', 'Not configuring a schema manager factory is deprecated.' . ' Use %s which is going to be the default in DBAL 4.', DefaultSchemaManagerFactory::class, ); $schemaManagerFactory = new LegacySchemaManagerFactory(); } $this->schemaManagerFactory = $schemaManagerFactory; } /** * Gets the parameters used during instantiation. * * @internal * * @return array * @psalm-return Params */ public function getParams() { return $this->params; } /** * Gets the name of the currently selected database. * * @return string|null The name of the database or NULL if a database is not selected. * The platforms which don't support the concept of a database (e.g. embedded databases) * must always return a string as an indicator of an implicitly selected database. * * @throws Exception */ public function getDatabase() { $platform = $this->getDatabasePlatform(); $query = $platform->getDummySelectSQL($platform->getCurrentDatabaseExpression()); $database = $this->fetchOne($query); assert(is_string($database) || $database === null); return $database; } /** * Gets the DBAL driver instance. * * @return Driver */ public function getDriver() { return $this->_driver; } /** * Gets the Configuration used by the Connection. * * @return Configuration */ public function getConfiguration() { return $this->_config; } /** * Gets the EventManager used by the Connection. * * @deprecated * * @return EventManager */ public function getEventManager() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5784', '%s is deprecated.', __METHOD__, ); return $this->_eventManager; } /** * Gets the DatabasePlatform for the connection. * * @return AbstractPlatform * * @throws Exception */ public function getDatabasePlatform() { if ($this->platform === null) { $this->platform = $this->detectDatabasePlatform(); $this->platform->setEventManager($this->_eventManager); $this->platform->setDisableTypeComments($this->_config->getDisableTypeComments()); } return $this->platform; } /** * Creates an expression builder for the connection. */ public function createExpressionBuilder(): ExpressionBuilder { return new ExpressionBuilder($this); } /** * Gets the ExpressionBuilder for the connection. * * @deprecated Use {@see createExpressionBuilder()} instead. * * @return ExpressionBuilder */ public function getExpressionBuilder() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4515', 'Connection::getExpressionBuilder() is deprecated,' . ' use Connection::createExpressionBuilder() instead.', ); return $this->_expr; } /** * Establishes the connection with the database. * * @internal This method will be made protected in DBAL 4.0. * * @return bool TRUE if the connection was successfully established, FALSE if * the connection is already open. * * @throws Exception * * @psalm-assert !null $this->_conn */ public function connect() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4966', 'Public access to Connection::connect() is deprecated.', ); if ($this->_conn !== null) { return false; } try { $this->_conn = $this->_driver->connect($this->params); } catch (Driver\Exception $e) { throw $this->convertException($e); } if ($this->autoCommit === false) { $this->beginTransaction(); } if ($this->_eventManager->hasListeners(Events::postConnect)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5784', 'Subscribing to %s events is deprecated. Implement a middleware instead.', Events::postConnect, ); $eventArgs = new Event\ConnectionEventArgs($this); $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs); } return true; } /** * Detects and sets the database platform. * * Evaluates custom platform class and version in order to set the correct platform. * * @throws Exception If an invalid platform was specified for this connection. */ private function detectDatabasePlatform(): AbstractPlatform { $version = $this->getDatabasePlatformVersion(); if ($version !== null) { assert($this->_driver instanceof VersionAwarePlatformDriver); return $this->_driver->createDatabasePlatformForVersion($version); } return $this->_driver->getDatabasePlatform(); } /** * Returns the version of the related platform if applicable. * * Returns null if either the driver is not capable to create version * specific platform instances, no explicit server version was specified * or the underlying driver connection cannot determine the platform * version without having to query it (performance reasons). * * @return string|null * * @throws Throwable */ private function getDatabasePlatformVersion() { // Driver does not support version specific platforms. if (! $this->_driver instanceof VersionAwarePlatformDriver) { return null; } // Explicit platform version requested (supersedes auto-detection). if (isset($this->params['serverVersion'])) { return $this->params['serverVersion']; } if (isset($this->params['primary']) && isset($this->params['primary']['serverVersion'])) { return $this->params['primary']['serverVersion']; } // If not connected, we need to connect now to determine the platform version. if ($this->_conn === null) { try { $this->connect(); } catch (Exception $originalException) { if (! isset($this->params['dbname'])) { throw $originalException; } Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5707', 'Relying on a fallback connection used to determine the database platform while connecting' . ' to a non-existing database is deprecated. Either use an existing database name in' . ' connection parameters or omit the database name if the platform' . ' and the server configuration allow that.', ); // The database to connect to might not yet exist. // Retry detection without database name connection parameter. $params = $this->params; unset($this->params['dbname']); try { $this->connect(); } catch (Exception $fallbackException) { // Either the platform does not support database-less connections // or something else went wrong. throw $originalException; } finally { $this->params = $params; } $serverVersion = $this->getServerVersion(); // Close "temporary" connection to allow connecting to the real database again. $this->close(); return $serverVersion; } } return $this->getServerVersion(); } /** * Returns the database server version if the underlying driver supports it. * * @return string|null * * @throws Exception */ private function getServerVersion() { $connection = $this->getWrappedConnection(); // Automatic platform version detection. if ($connection instanceof ServerInfoAwareConnection) { try { return $connection->getServerVersion(); } catch (Driver\Exception $e) { throw $this->convertException($e); } } Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4750', 'Not implementing the ServerInfoAwareConnection interface in %s is deprecated', get_class($connection), ); // Unable to detect platform version. return null; } /** * Returns the current auto-commit mode for this connection. * * @see setAutoCommit * * @return bool True if auto-commit mode is currently enabled for this connection, false otherwise. */ public function isAutoCommit() { return $this->autoCommit === true; } /** * Sets auto-commit mode for this connection. * * If a connection is in auto-commit mode, then all its SQL statements will be executed and committed as individual * transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by a call to either * the method commit or the method rollback. By default, new connections are in auto-commit mode. * * NOTE: If this method is called during a transaction and the auto-commit mode is changed, the transaction is * committed. If this method is called and the auto-commit mode is not changed, the call is a no-op. * * @see isAutoCommit * * @param bool $autoCommit True to enable auto-commit mode; false to disable it. * * @return void */ public function setAutoCommit($autoCommit) { $autoCommit = (bool) $autoCommit; // Mode not changed, no-op. if ($autoCommit === $this->autoCommit) { return; } $this->autoCommit = $autoCommit; // Commit all currently active transactions if any when switching auto-commit mode. if ($this->_conn === null || $this->transactionNestingLevel === 0) { return; } $this->commitAll(); } /** * Prepares and executes an SQL query and returns the first row of the result * as an associative array. * * @param string $query SQL query * @param list|array $params Query parameters * @param array|array $types Parameter types * * @return array|false False is returned if no rows are found. * * @throws Exception */ public function fetchAssociative(string $query, array $params = [], array $types = []) { return $this->executeQuery($query, $params, $types)->fetchAssociative(); } /** * Prepares and executes an SQL query and returns the first row of the result * as a numerically indexed array. * * @param string $query SQL query * @param list|array $params Query parameters * @param array|array $types Parameter types * * @return list|false False is returned if no rows are found. * * @throws Exception */ public function fetchNumeric(string $query, array $params = [], array $types = []) { return $this->executeQuery($query, $params, $types)->fetchNumeric(); } /** * Prepares and executes an SQL query and returns the value of a single column * of the first row of the result. * * @param string $query SQL query * @param list|array $params Query parameters * @param array|array $types Parameter types * * @return mixed|false False is returned if no rows are found. * * @throws Exception */ public function fetchOne(string $query, array $params = [], array $types = []) { return $this->executeQuery($query, $params, $types)->fetchOne(); } /** * Whether an actual connection to the database is established. * * @return bool */ public function isConnected() { return $this->_conn !== null; } /** * Checks whether a transaction is currently active. * * @return bool TRUE if a transaction is currently active, FALSE otherwise. */ public function isTransactionActive() { return $this->transactionNestingLevel > 0; } /** * Adds condition based on the criteria to the query components * * @param array $criteria Map of key columns to their values * @param string[] $columns Column names * @param mixed[] $values Column values * @param string[] $conditions Key conditions * * @throws Exception */ private function addCriteriaCondition( array $criteria, array &$columns, array &$values, array &$conditions ): void { $platform = $this->getDatabasePlatform(); foreach ($criteria as $columnName => $value) { if ($value === null) { $conditions[] = $platform->getIsNullExpression($columnName); continue; } $columns[] = $columnName; $values[] = $value; $conditions[] = $columnName . ' = ?'; } } /** * Executes an SQL DELETE statement on a table. * * Table expression and columns are not escaped and are not safe for user-input. * * @param string $table Table name * @param array $criteria Deletion criteria * @param array|array $types Parameter types * * @return int|string The number of affected rows. * * @throws Exception */ public function delete($table, array $criteria, array $types = []) { if (count($criteria) === 0) { throw InvalidArgumentException::fromEmptyCriteria(); } $columns = $values = $conditions = []; $this->addCriteriaCondition($criteria, $columns, $values, $conditions); return $this->executeStatement( 'DELETE FROM ' . $table . ' WHERE ' . implode(' AND ', $conditions), $values, is_string(key($types)) ? $this->extractTypeValues($columns, $types) : $types, ); } /** * Closes the connection. * * @return void */ public function close() { $this->_conn = null; $this->transactionNestingLevel = 0; } /** * Sets the transaction isolation level. * * @param TransactionIsolationLevel::* $level The level to set. * * @return int|string * * @throws Exception */ public function setTransactionIsolation($level) { $this->transactionIsolationLevel = $level; return $this->executeStatement($this->getDatabasePlatform()->getSetTransactionIsolationSQL($level)); } /** * Gets the currently active transaction isolation level. * * @return TransactionIsolationLevel::* The current transaction isolation level. * * @throws Exception */ public function getTransactionIsolation() { return $this->transactionIsolationLevel ??= $this->getDatabasePlatform()->getDefaultTransactionIsolationLevel(); } /** * Executes an SQL UPDATE statement on a table. * * Table expression and columns are not escaped and are not safe for user-input. * * @param string $table Table name * @param array $data Column-value pairs * @param array $criteria Update criteria * @param array|array $types Parameter types * * @return int|string The number of affected rows. * * @throws Exception */ public function update($table, array $data, array $criteria, array $types = []) { $columns = $values = $conditions = $set = []; foreach ($data as $columnName => $value) { $columns[] = $columnName; $values[] = $value; $set[] = $columnName . ' = ?'; } $this->addCriteriaCondition($criteria, $columns, $values, $conditions); if (is_string(key($types))) { $types = $this->extractTypeValues($columns, $types); } $sql = 'UPDATE ' . $table . ' SET ' . implode(', ', $set) . ' WHERE ' . implode(' AND ', $conditions); return $this->executeStatement($sql, $values, $types); } /** * Inserts a table row with specified data. * * Table expression and columns are not escaped and are not safe for user-input. * * @param string $table Table name * @param array $data Column-value pairs * @param array|array $types Parameter types * * @return int|string The number of affected rows. * * @throws Exception */ public function insert($table, array $data, array $types = []) { if (count($data) === 0) { return $this->executeStatement('INSERT INTO ' . $table . ' () VALUES ()'); } $columns = []; $values = []; $set = []; foreach ($data as $columnName => $value) { $columns[] = $columnName; $values[] = $value; $set[] = '?'; } return $this->executeStatement( 'INSERT INTO ' . $table . ' (' . implode(', ', $columns) . ')' . ' VALUES (' . implode(', ', $set) . ')', $values, is_string(key($types)) ? $this->extractTypeValues($columns, $types) : $types, ); } /** * Extract ordered type list from an ordered column list and type map. * * @param array $columnList * @param array|array $types * * @return array|array */ private function extractTypeValues(array $columnList, array $types): array { $typeValues = []; foreach ($columnList as $columnName) { $typeValues[] = $types[$columnName] ?? ParameterType::STRING; } return $typeValues; } /** * Quotes a string so it can be safely used as a table or column name, even if * it is a reserved name. * * Delimiting style depends on the underlying database platform that is being used. * * NOTE: Just because you CAN use quoted identifiers does not mean * you SHOULD use them. In general, they end up causing way more * problems than they solve. * * @param string $str The name to be quoted. * * @return string The quoted name. */ public function quoteIdentifier($str) { return $this->getDatabasePlatform()->quoteIdentifier($str); } /** * The usage of this method is discouraged. Use prepared statements * or {@see AbstractPlatform::quoteStringLiteral()} instead. * * @param mixed $value * @param int|string|Type|null $type * * @return mixed */ public function quote($value, $type = ParameterType::STRING) { $connection = $this->getWrappedConnection(); [$value, $bindingType] = $this->getBindingInfo($value, $type); return $connection->quote($value, $bindingType); } /** * Prepares and executes an SQL query and returns the result as an array of numeric arrays. * * @param string $query SQL query * @param list|array $params Query parameters * @param array|array $types Parameter types * * @return list> * * @throws Exception */ public function fetchAllNumeric(string $query, array $params = [], array $types = []): array { return $this->executeQuery($query, $params, $types)->fetchAllNumeric(); } /** * Prepares and executes an SQL query and returns the result as an array of associative arrays. * * @param string $query SQL query * @param list|array $params Query parameters * @param array|array $types Parameter types * * @return list> * * @throws Exception */ public function fetchAllAssociative(string $query, array $params = [], array $types = []): array { return $this->executeQuery($query, $params, $types)->fetchAllAssociative(); } /** * Prepares and executes an SQL query and returns the result as an associative array with the keys * mapped to the first column and the values mapped to the second column. * * @param string $query SQL query * @param list|array $params Query parameters * @param array|array $types Parameter types * * @return array * * @throws Exception */ public function fetchAllKeyValue(string $query, array $params = [], array $types = []): array { return $this->executeQuery($query, $params, $types)->fetchAllKeyValue(); } /** * Prepares and executes an SQL query and returns the result as an associative array with the keys mapped * to the first column and the values being an associative array representing the rest of the columns * and their values. * * @param string $query SQL query * @param list|array $params Query parameters * @param array|array $types Parameter types * * @return array> * * @throws Exception */ public function fetchAllAssociativeIndexed(string $query, array $params = [], array $types = []): array { return $this->executeQuery($query, $params, $types)->fetchAllAssociativeIndexed(); } /** * Prepares and executes an SQL query and returns the result as an array of the first column values. * * @param string $query SQL query * @param list|array $params Query parameters * @param array|array $types Parameter types * * @return list * * @throws Exception */ public function fetchFirstColumn(string $query, array $params = [], array $types = []): array { return $this->executeQuery($query, $params, $types)->fetchFirstColumn(); } /** * Prepares and executes an SQL query and returns the result as an iterator over rows represented as numeric arrays. * * @param string $query SQL query * @param list|array $params Query parameters * @param array|array $types Parameter types * * @return Traversable> * * @throws Exception */ public function iterateNumeric(string $query, array $params = [], array $types = []): Traversable { return $this->executeQuery($query, $params, $types)->iterateNumeric(); } /** * Prepares and executes an SQL query and returns the result as an iterator over rows represented * as associative arrays. * * @param string $query SQL query * @param list|array $params Query parameters * @param array|array $types Parameter types * * @return Traversable> * * @throws Exception */ public function iterateAssociative(string $query, array $params = [], array $types = []): Traversable { return $this->executeQuery($query, $params, $types)->iterateAssociative(); } /** * Prepares and executes an SQL query and returns the result as an iterator with the keys * mapped to the first column and the values mapped to the second column. * * @param string $query SQL query * @param list|array $params Query parameters * @param array|array $types Parameter types * * @return Traversable * * @throws Exception */ public function iterateKeyValue(string $query, array $params = [], array $types = []): Traversable { return $this->executeQuery($query, $params, $types)->iterateKeyValue(); } /** * Prepares and executes an SQL query and returns the result as an iterator with the keys mapped * to the first column and the values being an associative array representing the rest of the columns * and their values. * * @param string $query SQL query * @param list|array $params Query parameters * @param array|array $types Parameter types * * @return Traversable> * * @throws Exception */ public function iterateAssociativeIndexed(string $query, array $params = [], array $types = []): Traversable { return $this->executeQuery($query, $params, $types)->iterateAssociativeIndexed(); } /** * Prepares and executes an SQL query and returns the result as an iterator over the first column values. * * @param string $query SQL query * @param list|array $params Query parameters * @param array|array $types Parameter types * * @return Traversable * * @throws Exception */ public function iterateColumn(string $query, array $params = [], array $types = []): Traversable { return $this->executeQuery($query, $params, $types)->iterateColumn(); } /** * Prepares an SQL statement. * * @param string $sql The SQL statement to prepare. * * @throws Exception */ public function prepare(string $sql): Statement { $connection = $this->getWrappedConnection(); try { $statement = $connection->prepare($sql); } catch (Driver\Exception $e) { throw $this->convertExceptionDuringQuery($e, $sql); } return new Statement($this, $statement, $sql); } /** * Executes an, optionally parameterized, SQL query. * * If the query is parametrized, a prepared statement is used. * If an SQLLogger is configured, the execution is logged. * * @param string $sql SQL query * @param list|array $params Query parameters * @param array|array $types Parameter types * * @throws Exception */ public function executeQuery( string $sql, array $params = [], $types = [], ?QueryCacheProfile $qcp = null ): Result { if ($qcp !== null) { return $this->executeCacheQuery($sql, $params, $types, $qcp); } $connection = $this->getWrappedConnection(); $logger = $this->_config->getSQLLogger(); if ($logger !== null) { $logger->startQuery($sql, $params, $types); } try { if (count($params) > 0) { if ($this->needsArrayParameterConversion($params, $types)) { [$sql, $params, $types] = $this->expandArrayParameters($sql, $params, $types); } $stmt = $connection->prepare($sql); $this->bindParameters($stmt, $params, $types); $result = $stmt->execute(); } else { $result = $connection->query($sql); } return new Result($result, $this); } catch (Driver\Exception $e) { throw $this->convertExceptionDuringQuery($e, $sql, $params, $types); } finally { if ($logger !== null) { $logger->stopQuery(); } } } /** * Executes a caching query. * * @param string $sql SQL query * @param list|array $params Query parameters * @param array|array $types Parameter types * * @throws CacheException * @throws Exception */ public function executeCacheQuery($sql, $params, $types, QueryCacheProfile $qcp): Result { $resultCache = $qcp->getResultCache() ?? $this->_config->getResultCache(); if ($resultCache === null) { throw CacheException::noResultDriverConfigured(); } $connectionParams = $this->params; unset($connectionParams['platform'], $connectionParams['password'], $connectionParams['url']); [$cacheKey, $realKey] = $qcp->generateCacheKeys($sql, $params, $types, $connectionParams); $item = $resultCache->getItem($cacheKey); if ($item->isHit()) { $value = $item->get(); if (! is_array($value)) { $value = []; } if (isset($value[$realKey])) { return new Result(new ArrayResult($value[$realKey]), $this); } } else { $value = []; } $data = $this->fetchAllAssociative($sql, $params, $types); $value[$realKey] = $data; $item->set($value); $lifetime = $qcp->getLifetime(); if ($lifetime > 0) { $item->expiresAfter($lifetime); } $resultCache->save($item); return new Result(new ArrayResult($data), $this); } /** * Executes an SQL statement with the given parameters and returns the number of affected rows. * * Could be used for: * - DML statements: INSERT, UPDATE, DELETE, etc. * - DDL statements: CREATE, DROP, ALTER, etc. * - DCL statements: GRANT, REVOKE, etc. * - Session control statements: ALTER SESSION, SET, DECLARE, etc. * - Other statements that don't yield a row set. * * This method supports PDO binding types as well as DBAL mapping types. * * @param string $sql SQL statement * @param list|array $params Statement parameters * @param array|array $types Parameter types * * @return int|string The number of affected rows. * * @throws Exception */ public function executeStatement($sql, array $params = [], array $types = []) { $connection = $this->getWrappedConnection(); $logger = $this->_config->getSQLLogger(); if ($logger !== null) { $logger->startQuery($sql, $params, $types); } try { if (count($params) > 0) { if ($this->needsArrayParameterConversion($params, $types)) { [$sql, $params, $types] = $this->expandArrayParameters($sql, $params, $types); } $stmt = $connection->prepare($sql); $this->bindParameters($stmt, $params, $types); return $stmt->execute() ->rowCount(); } return $connection->exec($sql); } catch (Driver\Exception $e) { throw $this->convertExceptionDuringQuery($e, $sql, $params, $types); } finally { if ($logger !== null) { $logger->stopQuery(); } } } /** * Returns the current transaction nesting level. * * @return int The nesting level. A value of 0 means there's no active transaction. */ public function getTransactionNestingLevel() { return $this->transactionNestingLevel; } /** * Returns the ID of the last inserted row, or the last value from a sequence object, * depending on the underlying driver. * * Note: This method may not return a meaningful or consistent result across different drivers, * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY * columns or sequences. * * @param string|null $name Name of the sequence object from which the ID should be returned. * * @return string|int|false A string representation of the last inserted ID. * * @throws Exception */ public function lastInsertId($name = null) { if ($name !== null) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4687', 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', ); } try { return $this->getWrappedConnection()->lastInsertId($name); } catch (Driver\Exception $e) { throw $this->convertException($e); } } /** * Executes a function in a transaction. * * The function gets passed this Connection instance as an (optional) parameter. * * If an exception occurs during execution of the function or transaction commit, * the transaction is rolled back and the exception re-thrown. * * @param Closure(self):T $func The function to execute transactionally. * * @return T The value returned by $func * * @throws Throwable * * @template T */ public function transactional(Closure $func) { $this->beginTransaction(); try { $res = $func($this); $this->commit(); return $res; } catch (Throwable $e) { $this->rollBack(); throw $e; } } /** * Sets if nested transactions should use savepoints. * * @param bool $nestTransactionsWithSavepoints * * @return void * * @throws Exception */ public function setNestTransactionsWithSavepoints($nestTransactionsWithSavepoints) { if (! $nestTransactionsWithSavepoints) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5383', <<<'DEPRECATION' Nesting transactions without enabling savepoints is deprecated. Call %s::setNestTransactionsWithSavepoints(true) to enable savepoints. DEPRECATION, self::class, ); } if ($this->transactionNestingLevel > 0) { throw ConnectionException::mayNotAlterNestedTransactionWithSavepointsInTransaction(); } $this->nestTransactionsWithSavepoints = (bool) $nestTransactionsWithSavepoints; } /** * Gets if nested transactions should use savepoints. * * @return bool */ public function getNestTransactionsWithSavepoints() { return $this->nestTransactionsWithSavepoints; } /** * Returns the savepoint name to use for nested transactions. * * @return string */ protected function _getNestedTransactionSavePointName() { return 'DOCTRINE_' . $this->transactionNestingLevel; } /** * @return bool * * @throws Exception */ public function beginTransaction() { $connection = $this->getWrappedConnection(); ++$this->transactionNestingLevel; $logger = $this->_config->getSQLLogger(); if ($this->transactionNestingLevel === 1) { if ($logger !== null) { $logger->startQuery('"START TRANSACTION"'); } $connection->beginTransaction(); if ($logger !== null) { $logger->stopQuery(); } } elseif ($this->nestTransactionsWithSavepoints) { if ($logger !== null) { $logger->startQuery('"SAVEPOINT"'); } $this->createSavepoint($this->_getNestedTransactionSavePointName()); if ($logger !== null) { $logger->stopQuery(); } } else { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5383', <<<'DEPRECATION' Nesting transactions without enabling savepoints is deprecated. Call %s::setNestTransactionsWithSavepoints(true) to enable savepoints. DEPRECATION, self::class, ); } $eventManager = $this->getEventManager(); if ($eventManager->hasListeners(Events::onTransactionBegin)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5784', 'Subscribing to %s events is deprecated.', Events::onTransactionBegin, ); $eventManager->dispatchEvent(Events::onTransactionBegin, new TransactionBeginEventArgs($this)); } return true; } /** * @return bool * * @throws Exception */ public function commit() { if ($this->transactionNestingLevel === 0) { throw ConnectionException::noActiveTransaction(); } if ($this->isRollbackOnly) { throw ConnectionException::commitFailedRollbackOnly(); } $result = true; $connection = $this->getWrappedConnection(); if ($this->transactionNestingLevel === 1) { $result = $this->doCommit($connection); } elseif ($this->nestTransactionsWithSavepoints) { $this->releaseSavepoint($this->_getNestedTransactionSavePointName()); } --$this->transactionNestingLevel; $eventManager = $this->getEventManager(); if ($eventManager->hasListeners(Events::onTransactionCommit)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5784', 'Subscribing to %s events is deprecated.', Events::onTransactionCommit, ); $eventManager->dispatchEvent(Events::onTransactionCommit, new TransactionCommitEventArgs($this)); } if ($this->autoCommit !== false || $this->transactionNestingLevel !== 0) { return $result; } $this->beginTransaction(); return $result; } /** * @return bool * * @throws DriverException */ private function doCommit(DriverConnection $connection) { $logger = $this->_config->getSQLLogger(); if ($logger !== null) { $logger->startQuery('"COMMIT"'); } $result = $connection->commit(); if ($logger !== null) { $logger->stopQuery(); } return $result; } /** * Commits all current nesting transactions. * * @throws Exception */ private function commitAll(): void { while ($this->transactionNestingLevel !== 0) { if ($this->autoCommit === false && $this->transactionNestingLevel === 1) { // When in no auto-commit mode, the last nesting commit immediately starts a new transaction. // Therefore we need to do the final commit here and then leave to avoid an infinite loop. $this->commit(); return; } $this->commit(); } } /** * Cancels any database changes done during the current transaction. * * @return bool * * @throws Exception */ public function rollBack() { if ($this->transactionNestingLevel === 0) { throw ConnectionException::noActiveTransaction(); } $connection = $this->getWrappedConnection(); $logger = $this->_config->getSQLLogger(); if ($this->transactionNestingLevel === 1) { if ($logger !== null) { $logger->startQuery('"ROLLBACK"'); } $this->transactionNestingLevel = 0; $connection->rollBack(); $this->isRollbackOnly = false; if ($logger !== null) { $logger->stopQuery(); } if ($this->autoCommit === false) { $this->beginTransaction(); } } elseif ($this->nestTransactionsWithSavepoints) { if ($logger !== null) { $logger->startQuery('"ROLLBACK TO SAVEPOINT"'); } $this->rollbackSavepoint($this->_getNestedTransactionSavePointName()); --$this->transactionNestingLevel; if ($logger !== null) { $logger->stopQuery(); } } else { $this->isRollbackOnly = true; --$this->transactionNestingLevel; } $eventManager = $this->getEventManager(); if ($eventManager->hasListeners(Events::onTransactionRollBack)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5784', 'Subscribing to %s events is deprecated.', Events::onTransactionRollBack, ); $eventManager->dispatchEvent(Events::onTransactionRollBack, new TransactionRollBackEventArgs($this)); } return true; } /** * Creates a new savepoint. * * @param string $savepoint The name of the savepoint to create. * * @return void * * @throws Exception */ public function createSavepoint($savepoint) { $platform = $this->getDatabasePlatform(); if (! $platform->supportsSavepoints()) { throw ConnectionException::savepointsNotSupported(); } $this->executeStatement($platform->createSavePoint($savepoint)); } /** * Releases the given savepoint. * * @param string $savepoint The name of the savepoint to release. * * @return void * * @throws Exception */ public function releaseSavepoint($savepoint) { $logger = $this->_config->getSQLLogger(); $platform = $this->getDatabasePlatform(); if (! $platform->supportsSavepoints()) { throw ConnectionException::savepointsNotSupported(); } if (! $platform->supportsReleaseSavepoints()) { if ($logger !== null) { $logger->stopQuery(); } return; } if ($logger !== null) { $logger->startQuery('"RELEASE SAVEPOINT"'); } $this->executeStatement($platform->releaseSavePoint($savepoint)); if ($logger === null) { return; } $logger->stopQuery(); } /** * Rolls back to the given savepoint. * * @param string $savepoint The name of the savepoint to rollback to. * * @return void * * @throws Exception */ public function rollbackSavepoint($savepoint) { $platform = $this->getDatabasePlatform(); if (! $platform->supportsSavepoints()) { throw ConnectionException::savepointsNotSupported(); } $this->executeStatement($platform->rollbackSavePoint($savepoint)); } /** * Gets the wrapped driver connection. * * @deprecated Use {@link getNativeConnection()} to access the native connection. * * @return DriverConnection * * @throws Exception */ public function getWrappedConnection() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4966', 'Connection::getWrappedConnection() is deprecated.' . ' Use Connection::getNativeConnection() to access the native connection.', ); $this->connect(); return $this->_conn; } /** @return resource|object */ public function getNativeConnection() { $this->connect(); if (! method_exists($this->_conn, 'getNativeConnection')) { throw new LogicException(sprintf( 'The driver connection %s does not support accessing the native connection.', get_class($this->_conn), )); } return $this->_conn->getNativeConnection(); } /** * Creates a SchemaManager that can be used to inspect or change the * database schema through the connection. * * @throws Exception */ public function createSchemaManager(): AbstractSchemaManager { return $this->schemaManagerFactory->createSchemaManager($this); } /** * Gets the SchemaManager that can be used to inspect or change the * database schema through the connection. * * @deprecated Use {@see createSchemaManager()} instead. * * @return AbstractSchemaManager * * @throws Exception */ public function getSchemaManager() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4515', 'Connection::getSchemaManager() is deprecated, use Connection::createSchemaManager() instead.', ); return $this->_schemaManager ??= $this->createSchemaManager(); } /** * Marks the current transaction so that the only possible * outcome for the transaction to be rolled back. * * @return void * * @throws ConnectionException If no transaction is active. */ public function setRollbackOnly() { if ($this->transactionNestingLevel === 0) { throw ConnectionException::noActiveTransaction(); } $this->isRollbackOnly = true; } /** * Checks whether the current transaction is marked for rollback only. * * @return bool * * @throws ConnectionException If no transaction is active. */ public function isRollbackOnly() { if ($this->transactionNestingLevel === 0) { throw ConnectionException::noActiveTransaction(); } return $this->isRollbackOnly; } /** * Converts a given value to its database representation according to the conversion * rules of a specific DBAL mapping type. * * @param mixed $value The value to convert. * @param string $type The name of the DBAL mapping type. * * @return mixed The converted value. * * @throws Exception */ public function convertToDatabaseValue($value, $type) { return Type::getType($type)->convertToDatabaseValue($value, $this->getDatabasePlatform()); } /** * Converts a given value to its PHP representation according to the conversion * rules of a specific DBAL mapping type. * * @param mixed $value The value to convert. * @param string $type The name of the DBAL mapping type. * * @return mixed The converted type. * * @throws Exception */ public function convertToPHPValue($value, $type) { return Type::getType($type)->convertToPHPValue($value, $this->getDatabasePlatform()); } /** * Binds a set of parameters, some or all of which are typed with a PDO binding type * or DBAL mapping type, to a given statement. * * @param DriverStatement $stmt Prepared statement * @param list|array $params Statement parameters * @param array|array $types Parameter types * * @throws Exception */ private function bindParameters(DriverStatement $stmt, array $params, array $types): void { // Check whether parameters are positional or named. Mixing is not allowed. if (is_int(key($params))) { $bindIndex = 1; foreach ($params as $key => $value) { if (isset($types[$key])) { $type = $types[$key]; [$value, $bindingType] = $this->getBindingInfo($value, $type); } else { if (array_key_exists($key, $types)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5550', 'Using NULL as prepared statement parameter type is deprecated.' . 'Omit or use ParameterType::STRING instead', ); } $bindingType = ParameterType::STRING; } $stmt->bindValue($bindIndex, $value, $bindingType); ++$bindIndex; } } else { // Named parameters foreach ($params as $name => $value) { if (isset($types[$name])) { $type = $types[$name]; [$value, $bindingType] = $this->getBindingInfo($value, $type); } else { if (array_key_exists($name, $types)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5550', 'Using NULL as prepared statement parameter type is deprecated.' . 'Omit or use ParameterType::STRING instead', ); } $bindingType = ParameterType::STRING; } $stmt->bindValue($name, $value, $bindingType); } } } /** * Gets the binding type of a given type. * * @param mixed $value The value to bind. * @param int|string|Type|null $type The type to bind (PDO or DBAL). * * @return array{mixed, int} [0] => the (escaped) value, [1] => the binding type. * * @throws Exception */ private function getBindingInfo($value, $type): array { if (is_string($type)) { $type = Type::getType($type); } if ($type instanceof Type) { $value = $type->convertToDatabaseValue($value, $this->getDatabasePlatform()); $bindingType = $type->getBindingType(); } else { $bindingType = $type ?? ParameterType::STRING; } return [$value, $bindingType]; } /** * Creates a new instance of a SQL query builder. * * @return QueryBuilder */ public function createQueryBuilder() { return new Query\QueryBuilder($this); } /** * @internal * * @param list|array $params * @param array|array $types */ final public function convertExceptionDuringQuery( Driver\Exception $e, string $sql, array $params = [], array $types = [] ): DriverException { return $this->handleDriverException($e, new Query($sql, $params, $types)); } /** @internal */ final public function convertException(Driver\Exception $e): DriverException { return $this->handleDriverException($e, null); } /** * @param array|array $params * @param array|array $types * * @return array{string, list, array} */ private function expandArrayParameters(string $sql, array $params, array $types): array { $this->parser ??= $this->getDatabasePlatform()->createSQLParser(); $visitor = new ExpandArrayParameters($params, $types); $this->parser->parse($sql, $visitor); return [ $visitor->getSQL(), $visitor->getParameters(), $visitor->getTypes(), ]; } /** * @param array|array $params * @param array|array $types */ private function needsArrayParameterConversion(array $params, array $types): bool { if (is_string(key($params))) { return true; } foreach ($types as $type) { if ( $type === ArrayParameterType::INTEGER || $type === ArrayParameterType::STRING || $type === ArrayParameterType::ASCII || $type === ArrayParameterType::BINARY ) { return true; } } return false; } private function handleDriverException( Driver\Exception $driverException, ?Query $query ): DriverException { $this->exceptionConverter ??= $this->_driver->getExceptionConverter(); $exception = $this->exceptionConverter->convert($driverException, $query); if ($exception instanceof ConnectionLost) { $this->close(); } return $exception; } /** * BC layer for a wide-spread use-case of old DBAL APIs * * @deprecated Use {@see executeStatement()} instead * * @param array $params The query parameters * @param array $types The parameter types */ public function executeUpdate(string $sql, array $params = [], array $types = []): int { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4163', '%s is deprecated, please use executeStatement() instead.', __METHOD__, ); return $this->executeStatement($sql, $params, $types); } /** * BC layer for a wide-spread use-case of old DBAL APIs * * @deprecated Use {@see executeQuery()} instead */ public function query(string $sql): Result { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4163', '%s is deprecated, please use executeQuery() instead.', __METHOD__, ); return $this->executeQuery($sql); } /** * BC layer for a wide-spread use-case of old DBAL APIs * * @deprecated please use {@see executeStatement()} instead */ public function exec(string $sql): int { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4163', '%s is deprecated, please use executeStatement() instead.', __METHOD__, ); return $this->executeStatement($sql); } } PK!TMMdbal/src/Events.phpnu[ */ private static array $platforms = [ DB2Platform::class => 0, OraclePlatform::class => Connection::PORTABILITY_EMPTY_TO_NULL, PostgreSQLPlatform::class => 0, SqlitePlatform::class => 0, SQLServerPlatform::class => 0, ]; public function __invoke(AbstractPlatform $platform, int $flags): int { foreach (self::$platforms as $class => $mask) { if ($platform instanceof $class) { $flags &= ~$mask; break; } } return $flags; } } PK!Ė#dbal/src/Portability/Middleware.phpnu[mode = $mode; $this->case = $case; } public function wrap(DriverInterface $driver): DriverInterface { if ($this->mode !== 0) { return new Driver($driver, $this->mode, $this->case); } return $driver; } } PK!?? dbal/src/Portability/Driver.phpnu[mode = $mode; $this->case = $case; } /** * {@inheritDoc} */ public function connect( #[SensitiveParameter] array $params ) { $connection = parent::connect($params); $portability = (new OptimizeFlags())( $this->getDatabasePlatform(), $this->mode, ); $case = null; if ($this->case !== 0 && ($portability & Connection::PORTABILITY_FIX_CASE) !== 0) { $nativeConnection = null; if (method_exists($connection, 'getNativeConnection')) { try { $nativeConnection = $connection->getNativeConnection(); } catch (LogicException $e) { } } if ($nativeConnection instanceof PDO) { $portability &= ~Connection::PORTABILITY_FIX_CASE; $nativeConnection->setAttribute( PDO::ATTR_CASE, $this->case === ColumnCase::LOWER ? PDO::CASE_LOWER : PDO::CASE_UPPER, ); } else { $case = $this->case === ColumnCase::LOWER ? Converter::CASE_LOWER : Converter::CASE_UPPER; } } $convertEmptyStringToNull = ($portability & Connection::PORTABILITY_EMPTY_TO_NULL) !== 0; $rightTrimString = ($portability & Connection::PORTABILITY_RTRIM) !== 0; if (! $convertEmptyStringToNull && ! $rightTrimString && $case === null) { return $connection; } return new Connection( $connection, new Converter($convertEmptyStringToNull, $rightTrimString, $case), ); } } PK!#dbal/src/Portability/Connection.phpnu[converter = $converter; } public function prepare(string $sql): DriverStatement { return new Statement( parent::prepare($sql), $this->converter, ); } public function query(string $sql): DriverResult { return new Result( parent::query($sql), $this->converter, ); } } PK!"!!"dbal/src/Portability/Converter.phpnu[createConvertValue($convertEmptyStringToNull, $rightTrimString); $convertNumeric = $this->createConvertRow($convertValue, null); $convertAssociative = $this->createConvertRow($convertValue, $case); $this->convertNumeric = $this->createConvert($convertNumeric, [self::class, 'id']); $this->convertAssociative = $this->createConvert($convertAssociative, [self::class, 'id']); $this->convertOne = $this->createConvert($convertValue, [self::class, 'id']); $this->convertAllNumeric = $this->createConvertAll($convertNumeric, [self::class, 'id']); $this->convertAllAssociative = $this->createConvertAll($convertAssociative, [self::class, 'id']); $this->convertFirstColumn = $this->createConvertAll($convertValue, [self::class, 'id']); } /** * @param array|false $row * * @return list|false */ public function convertNumeric($row) { return ($this->convertNumeric)($row); } /** * @param array|false $row * * @return array|false */ public function convertAssociative($row) { return ($this->convertAssociative)($row); } /** * @param mixed|false $value * * @return mixed|false */ public function convertOne($value) { return ($this->convertOne)($value); } /** * @param list> $data * * @return list> */ public function convertAllNumeric(array $data): array { return ($this->convertAllNumeric)($data); } /** * @param list> $data * * @return list> */ public function convertAllAssociative(array $data): array { return ($this->convertAllAssociative)($data); } /** * @param list $data * * @return list */ public function convertFirstColumn(array $data): array { return ($this->convertFirstColumn)($data); } /** * @param T $value * * @return T * * @template T */ private static function id($value) { return $value; } /** * @param T $value * * @return T|null * * @template T */ private static function convertEmptyStringToNull($value) { if ($value === '') { return null; } return $value; } /** * @param T $value * * @return T|string * @psalm-return (T is string ? string : T) * * @template T */ private static function rightTrimString($value) { if (! is_string($value)) { return $value; } return rtrim($value); } /** * Creates a function that will convert each individual value retrieved from the database * * @param bool $convertEmptyStringToNull Whether each empty string should be converted to NULL * @param bool $rightTrimString Whether each string should right-trimmed * * @return callable|null The resulting function or NULL if no conversion is needed */ private function createConvertValue(bool $convertEmptyStringToNull, bool $rightTrimString): ?callable { $functions = []; if ($convertEmptyStringToNull) { $functions[] = [self::class, 'convertEmptyStringToNull']; } if ($rightTrimString) { $functions[] = [self::class, 'rightTrimString']; } return $this->compose(...$functions); } /** * Creates a function that will convert each array-row retrieved from the database * * @param callable|null $function The function that will convert each value * @param self::CASE_LOWER|self::CASE_UPPER|null $case Column name case * * @return callable|null The resulting function or NULL if no conversion is needed */ private function createConvertRow(?callable $function, ?int $case): ?callable { $functions = []; if ($function !== null) { $functions[] = $this->createMapper($function); } if ($case !== null) { $functions[] = static function (array $row) use ($case): array { return array_change_key_case($row, $case); }; } return $this->compose(...$functions); } /** * Creates a function that will be applied to the return value of Statement::fetch*() * or an identity function if no conversion is needed * * @param callable|null $function The function that will convert each tow * @param callable $id Identity function */ private function createConvert(?callable $function, callable $id): callable { if ($function === null) { return $id; } return /** * @param T $value * * @psalm-return (T is false ? false : T) * * @template T */ static function ($value) use ($function) { if ($value === false) { return false; } return $function($value); }; } /** * Creates a function that will be applied to the return value of Statement::fetchAll*() * or an identity function if no transformation is required * * @param callable|null $function The function that will transform each value * @param callable $id Identity function */ private function createConvertAll(?callable $function, callable $id): callable { if ($function === null) { return $id; } return $this->createMapper($function); } /** * Creates a function that maps each value of the array using the given function * * @param callable $function The function that maps each value of the array */ private function createMapper(callable $function): callable { return static function (array $array) use ($function): array { return array_map($function, $array); }; } /** * Creates a composition of the given set of functions * * @param callable(T):T ...$functions The functions to compose * * @return callable(T):T|null * * @template T */ private function compose(callable ...$functions): ?callable { return array_reduce($functions, static function (?callable $carry, callable $item): callable { if ($carry === null) { return $item; } return /** * @param T $value * * @return T * * @template T */ static function ($value) use ($carry, $item) { return $item($carry($value)); }; }); } } PK!"r;;"dbal/src/Portability/Statement.phpnu[Statement and applies portability measures. */ public function __construct(DriverStatement $stmt, Converter $converter) { parent::__construct($stmt); $this->converter = $converter; } /** * {@inheritDoc} */ public function execute($params = null): ResultInterface { return new Result( parent::execute($params), $this->converter, ); } } PK!t[Qdbal/src/Portability/Result.phpnu[converter = $converter; } /** * {@inheritDoc} */ public function fetchNumeric() { return $this->converter->convertNumeric( parent::fetchNumeric(), ); } /** * {@inheritDoc} */ public function fetchAssociative() { return $this->converter->convertAssociative( parent::fetchAssociative(), ); } /** * {@inheritDoc} */ public function fetchOne() { return $this->converter->convertOne( parent::fetchOne(), ); } /** * {@inheritDoc} */ public function fetchAllNumeric(): array { return $this->converter->convertAllNumeric( parent::fetchAllNumeric(), ); } /** * {@inheritDoc} */ public function fetchAllAssociative(): array { return $this->converter->convertAllAssociative( parent::fetchAllAssociative(), ); } /** * {@inheritDoc} */ public function fetchFirstColumn(): array { return $this->converter->convertFirstColumn( parent::fetchFirstColumn(), ); } } PK!dbal/src/LockMode.phpnu[Statement for the given SQL and Connection. * * @internal The statement can be only instantiated by {@see Connection}. * * @param Connection $conn The connection for handling statement errors. * @param Driver\Statement $statement The underlying driver-level statement. * @param string $sql The SQL of the statement. * * @throws Exception */ public function __construct(Connection $conn, Driver\Statement $statement, string $sql) { $this->conn = $conn; $this->stmt = $statement; $this->sql = $sql; $this->platform = $conn->getDatabasePlatform(); } /** * Binds a parameter value to the statement. * * The value can optionally be bound with a DBAL mapping type. * If bound with a DBAL mapping type, the binding type is derived from the mapping * type and the value undergoes the conversion routines of the mapping type before * being bound. * * @param string|int $param The name or position of the parameter. * @param mixed $value The value of the parameter. * @param mixed $type Either a PDO binding type or a DBAL mapping type name or instance. * * @return bool TRUE on success, FALSE on failure. * * @throws Exception */ public function bindValue($param, $value, $type = ParameterType::STRING) { $this->params[$param] = $value; $this->types[$param] = $type; $bindingType = ParameterType::STRING; if ($type !== null) { if (is_string($type)) { $type = Type::getType($type); } $bindingType = $type; if ($type instanceof Type) { $value = $type->convertToDatabaseValue($value, $this->platform); $bindingType = $type->getBindingType(); } } try { return $this->stmt->bindValue($param, $value, $bindingType); } catch (Driver\Exception $e) { throw $this->conn->convertException($e); } } /** * Binds a parameter to a value by reference. * * Binding a parameter by reference does not support DBAL mapping types. * * @deprecated Use {@see bindValue()} instead. * * @param string|int $param The name or position of the parameter. * @param mixed $variable The reference to the variable to bind. * @param int $type The binding type. * @param int|null $length Must be specified when using an OUT bind * so that PHP allocates enough memory to hold the returned value. * * @return bool TRUE on success, FALSE on failure. * * @throws Exception */ public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5563', '%s is deprecated. Use bindValue() instead.', __METHOD__, ); $this->params[$param] = $variable; $this->types[$param] = $type; try { if (func_num_args() > 3) { return $this->stmt->bindParam($param, $variable, $type, $length); } return $this->stmt->bindParam($param, $variable, $type); } catch (Driver\Exception $e) { throw $this->conn->convertException($e); } } /** * Executes the statement with the currently bound parameters. * * @deprecated Statement::execute() is deprecated, use Statement::executeQuery() or executeStatement() instead * * @param mixed[]|null $params * * @throws Exception */ public function execute($params = null): Result { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4580', '%s() is deprecated, use Statement::executeQuery() or Statement::executeStatement() instead', __METHOD__, ); if ($params !== null) { $this->params = $params; } $logger = $this->conn->getConfiguration()->getSQLLogger(); if ($logger !== null) { $logger->startQuery($this->sql, $this->params, $this->types); } try { return new Result( $this->stmt->execute($params), $this->conn, ); } catch (Driver\Exception $ex) { throw $this->conn->convertExceptionDuringQuery($ex, $this->sql, $this->params, $this->types); } finally { if ($logger !== null) { $logger->stopQuery(); } } } /** * Executes the statement with the currently bound parameters and return result. * * @param mixed[] $params * * @throws Exception */ public function executeQuery(array $params = []): Result { if (func_num_args() > 0) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5556', 'Passing $params to Statement::executeQuery() is deprecated. Bind parameters using' . ' Statement::bindParam() or Statement::bindValue() instead.', ); } if ($params === []) { $params = null; // Workaround as long execute() exists and used internally. } return $this->execute($params); } /** * Executes the statement with the currently bound parameters and return affected rows. * * @param mixed[] $params * * @throws Exception */ public function executeStatement(array $params = []): int { if (func_num_args() > 0) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5556', 'Passing $params to Statement::executeStatement() is deprecated. Bind parameters using' . ' Statement::bindParam() or Statement::bindValue() instead.', ); } if ($params === []) { $params = null; // Workaround as long execute() exists and used internally. } return $this->execute($params)->rowCount(); } /** * Gets the wrapped driver statement. * * @return Driver\Statement */ public function getWrappedStatement() { return $this->stmt; } } PK!$YY&dbal/src/TransactionIsolationLevel.phpnu[connectionProvider = $connectionProvider; } /** @return void */ protected function configure() { $this ->setName('dbal:run-sql') ->setDescription('Executes arbitrary SQL directly from the command line.') ->setDefinition([ new InputOption('connection', null, InputOption::VALUE_REQUIRED, 'The named database connection'), new InputArgument('sql', InputArgument::REQUIRED, 'The SQL statement to execute.'), new InputOption('depth', null, InputOption::VALUE_REQUIRED, 'Dumping depth of result set (deprecated).'), new InputOption('force-fetch', null, InputOption::VALUE_NONE, 'Forces fetching the result.'), ]) ->setHelp(<<<'EOT' The %command.name% command executes the given SQL query and outputs the results: php %command.full_name% "SELECT * FROM users" EOT); } /** @throws Exception */ private function doExecute(InputInterface $input, OutputInterface $output): int { $conn = $this->getConnection($input); $io = new SymfonyStyle($input, $output); $sql = $input->getArgument('sql'); if ($sql === null) { throw new RuntimeException("Argument 'SQL' is required in order to execute this command correctly."); } assert(is_string($sql)); if ($input->getOption('depth') !== null) { $io->warning('Parameter "depth" is deprecated and has no effect anymore.'); } $forceFetch = $input->getOption('force-fetch'); assert(is_bool($forceFetch)); if (stripos($sql, 'select') === 0 || $forceFetch) { $this->runQuery($io, $conn, $sql); } else { $this->runStatement($io, $conn, $sql); } return 0; } private function getConnection(InputInterface $input): Connection { $connectionName = $input->getOption('connection'); assert(is_string($connectionName) || $connectionName === null); if ($connectionName !== null) { return $this->connectionProvider->getConnection($connectionName); } return $this->connectionProvider->getDefaultConnection(); } /** @throws Exception */ private function runQuery(SymfonyStyle $io, Connection $conn, string $sql): void { $resultSet = $conn->fetchAllAssociative($sql); if ($resultSet === []) { $io->success('The query yielded an empty result set.'); return; } $io->table(array_keys($resultSet[0]), $resultSet); } /** @throws Exception */ private function runStatement(SymfonyStyle $io, Connection $conn, string $sql): void { $io->success(sprintf('%d rows affected.', $conn->executeStatement($sql))); } } PK!6hh7dbal/src/Tools/Console/Command/CommandCompatibility.phpnu[hasReturnType()) { /** @internal */ trait CommandCompatibility { protected function execute(InputInterface $input, OutputInterface $output): int { return $this->doExecute($input, $output); } } } else { /** @internal */ trait CommandCompatibility { /** * {@inheritDoc} * * @return int */ protected function execute(InputInterface $input, OutputInterface $output) { return $this->doExecute($input, $output); } } } PK!,QGG7dbal/src/Tools/Console/Command/ReservedWordsCommand.phpnu[ */ private array $keywordLists; private ConnectionProvider $connectionProvider; public function __construct(ConnectionProvider $connectionProvider) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5431', 'ReservedWordsCommand is deprecated. Use database documentation instead.', ); parent::__construct(); $this->connectionProvider = $connectionProvider; $this->keywordLists = [ 'db2' => new DB2Keywords(), 'mariadb102' => new MariaDb102Keywords(), 'mysql' => new MySQLKeywords(), 'mysql57' => new MySQL57Keywords(), 'mysql80' => new MySQL80Keywords(), 'oracle' => new OracleKeywords(), 'pgsql' => new PostgreSQL94Keywords(), 'pgsql100' => new PostgreSQL100Keywords(), 'sqlite' => new SQLiteKeywords(), 'sqlserver' => new SQLServer2012Keywords(), ]; } /** * Add or replace a keyword list. */ public function setKeywordList(string $name, KeywordList $keywordList): void { $this->keywordLists[$name] = $keywordList; } /** * If you want to add or replace a keywords list use this command. * * @param string $name * @param class-string $class * * @return void */ public function setKeywordListClass($name, $class) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4510', 'ReservedWordsCommand::setKeywordListClass() is deprecated,' . ' use ReservedWordsCommand::setKeywordList() instead.', ); $this->keywordLists[$name] = new $class(); } /** @return void */ protected function configure() { $this ->setName('dbal:reserved-words') ->setDescription('Checks if the current database contains identifiers that are reserved.') ->setDefinition([ new InputOption('connection', null, InputOption::VALUE_REQUIRED, 'The named database connection'), new InputOption( 'list', 'l', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Keyword-List name.', ), ]) ->setHelp(<<<'EOT' Checks if the current database contains tables and columns with names that are identifiers in this dialect or in other SQL dialects. By default all supported platform keywords are checked: %command.full_name% If you want to check against specific dialects you can pass them to the command: %command.full_name% -l mysql -l pgsql The following keyword lists are currently shipped with Doctrine: * db2 * mariadb102 * mysql * mysql57 * mysql80 * oracle * pgsql * pgsql100 * sqlite * sqlserver EOT); } /** @throws Exception */ private function doExecute(InputInterface $input, OutputInterface $output): int { $output->writeln( 'The dbal:reserved-words command is deprecated.' . ' Use the documentation on the used database platform(s) instead.', ); $output->writeln(''); $conn = $this->getConnection($input); $keywordLists = $input->getOption('list'); if (is_string($keywordLists)) { $keywordLists = [$keywordLists]; } elseif (! is_array($keywordLists)) { $keywordLists = []; } if (count($keywordLists) === 0) { $keywordLists = array_keys($this->keywordLists); } $keywords = []; foreach ($keywordLists as $keywordList) { if (! isset($this->keywordLists[$keywordList])) { throw new InvalidArgumentException( "There exists no keyword list with name '" . $keywordList . "'. " . 'Known lists: ' . implode(', ', array_keys($this->keywordLists)), ); } $keywords[] = $this->keywordLists[$keywordList]; } $output->write( 'Checking keyword violations for ' . implode(', ', $keywordLists) . '...', true, ); $schema = $conn->getSchemaManager()->introspectSchema(); $visitor = new ReservedKeywordsValidator($keywords); $schema->visit($visitor); $violations = $visitor->getViolations(); if (count($violations) !== 0) { $output->write( 'There are ' . count($violations) . ' reserved keyword violations' . ' in your database schema:', true, ); foreach ($violations as $violation) { $output->write(' - ' . $violation, true); } return 1; } $output->write('No reserved keywords violations have been found!', true); return 0; } private function getConnection(InputInterface $input): Connection { $connectionName = $input->getOption('connection'); assert(is_string($connectionName) || $connectionName === null); if ($connectionName !== null) { return $this->connectionProvider->getConnection($connectionName); } return $this->connectionProvider->getDefaultConnection(); } } PK! z-dbal/src/Tools/Console/ConnectionNotFound.phpnu[connection = $connection; $this->defaultConnectionName = $defaultConnectionName; } public function getDefaultConnection(): Connection { return $this->connection; } public function getConnection(string $name): Connection { if ($name !== $this->defaultConnectionName) { throw new ConnectionNotFound(sprintf('Connection with name "%s" does not exist.', $name)); } return $this->connection; } } PK!2ޒ (dbal/src/Tools/Console/ConsoleRunner.phpnu[setCatchExceptions(true); self::addCommands($cli, $connectionProvider); $cli->addCommands($commands); $cli->run(); } /** @return void */ public static function addCommands(Application $cli, ConnectionProvider $connectionProvider) { $cli->addCommands([ new RunSqlCommand($connectionProvider), new ReservedWordsCommand($connectionProvider), ]); } /** * Prints the instructions to create a configuration file * * @deprecated This method will be removed without replacement. * * @return void */ public static function printCliConfigTemplate() { echo <<<'HELP' You are missing a "cli-config.php" or "config/cli-config.php" file in your project, which is required to get the Doctrine-DBAL Console working. You can use the following sample as a template: MMdbal/src/Tools/DsnParser.phpnu[> */ private array $schemeMapping; /** @param array> $schemeMapping An array used to map DSN schemes to DBAL drivers */ public function __construct(array $schemeMapping = []) { $this->schemeMapping = $schemeMapping; } /** * @psalm-return Params * * @throws MalformedDsnException */ public function parse( #[SensitiveParameter] string $dsn ): array { // (pdo-)?sqlite3?:///... => (pdo-)?sqlite3?://localhost/... or else the URL will be invalid $url = preg_replace('#^((?:pdo-)?sqlite3?):///#', '$1://localhost/', $dsn); assert($url !== null); $url = parse_url($url); if ($url === false) { throw MalformedDsnException::new(); } foreach ($url as $param => $value) { if (! is_string($value)) { continue; } $url[$param] = rawurldecode($value); } $params = []; if (isset($url['scheme'])) { $params['driver'] = $this->parseDatabaseUrlScheme($url['scheme']); } if (isset($url['host'])) { $params['host'] = $url['host']; } if (isset($url['port'])) { $params['port'] = $url['port']; } if (isset($url['user'])) { $params['user'] = $url['user']; } if (isset($url['pass'])) { $params['password'] = $url['pass']; } if (isset($params['driver']) && is_a($params['driver'], Driver::class, true)) { $params['driverClass'] = $params['driver']; unset($params['driver']); } $params = $this->parseDatabaseUrlPath($url, $params); $params = $this->parseDatabaseUrlQuery($url, $params); return $params; } /** * Parses the given connection URL and resolves the given connection parameters. * * Assumes that the connection URL scheme is already parsed and resolved into the given connection parameters * via {@see parseDatabaseUrlScheme}. * * @see parseDatabaseUrlScheme * * @param mixed[] $url The URL parts to evaluate. * @param mixed[] $params The connection parameters to resolve. * * @return mixed[] The resolved connection parameters. */ private function parseDatabaseUrlPath(array $url, array $params): array { if (! isset($url['path'])) { return $params; } $url['path'] = $this->normalizeDatabaseUrlPath($url['path']); // If we do not have a known DBAL driver, we do not know any connection URL path semantics to evaluate // and therefore treat the path as a regular DBAL connection URL path. if (! isset($params['driver'])) { return $this->parseRegularDatabaseUrlPath($url, $params); } if (strpos($params['driver'], 'sqlite') !== false) { return $this->parseSqliteDatabaseUrlPath($url, $params); } return $this->parseRegularDatabaseUrlPath($url, $params); } /** * Normalizes the given connection URL path. * * @return string The normalized connection URL path */ private function normalizeDatabaseUrlPath(string $urlPath): string { // Trim leading slash from URL path. return substr($urlPath, 1); } /** * Parses the query part of the given connection URL and resolves the given connection parameters. * * @param mixed[] $url The connection URL parts to evaluate. * @param mixed[] $params The connection parameters to resolve. * * @return mixed[] The resolved connection parameters. */ private function parseDatabaseUrlQuery(array $url, array $params): array { if (! isset($url['query'])) { return $params; } $query = []; parse_str($url['query'], $query); // simply ingest query as extra params, e.g. charset or sslmode return array_merge($params, $query); // parse_str wipes existing array elements } /** * Parses the given regular connection URL and resolves the given connection parameters. * * Assumes that the "path" URL part is already normalized via {@see normalizeDatabaseUrlPath}. * * @see normalizeDatabaseUrlPath * * @param mixed[] $url The regular connection URL parts to evaluate. * @param mixed[] $params The connection parameters to resolve. * * @return mixed[] The resolved connection parameters. */ private function parseRegularDatabaseUrlPath(array $url, array $params): array { $params['dbname'] = $url['path']; return $params; } /** * Parses the given SQLite connection URL and resolves the given connection parameters. * * Assumes that the "path" URL part is already normalized via {@see normalizeDatabaseUrlPath}. * * @see normalizeDatabaseUrlPath * * @param mixed[] $url The SQLite connection URL parts to evaluate. * @param mixed[] $params The connection parameters to resolve. * * @return mixed[] The resolved connection parameters. */ private function parseSqliteDatabaseUrlPath(array $url, array $params): array { if ($url['path'] === ':memory:') { $params['memory'] = true; return $params; } $params['path'] = $url['path']; // pdo_sqlite driver uses 'path' instead of 'dbname' key return $params; } /** * Parses the scheme part from given connection URL and resolves the given connection parameters. * * @return string The resolved driver. */ private function parseDatabaseUrlScheme(string $scheme): string { // URL schemes must not contain underscores, but dashes are ok $driver = str_replace('-', '_', $scheme); // If the driver is an alias (e.g. "postgres"), map it to the actual name ("pdo-pgsql"). // Otherwise, let checkParams decide later if the driver exists. return $this->schemeMapping[$driver] ?? $driver; } } PK!!2dbal/src/Schema/Exception/SequenceDoesNotExist.phpnu[getName(), implode(', ', $foreignKey->getColumns()), $foreignKey->getForeignTableName(), implode(', ', $foreignKey->getForeignColumns()), ), ); } } PK!YÞ^0dbal/src/Schema/Exception/TableAlreadyExists.phpnu[ Identifier) * * @var Identifier[] */ protected $_columns = []; /** @var bool */ protected $_isUnique = false; /** @var bool */ protected $_isPrimary = false; /** * Platform specific flags for indexes. * array($flagName => true) * * @var true[] */ protected $_flags = []; /** * Platform specific options * * @todo $_flags should eventually be refactored into options * @var mixed[] */ private array $options = []; /** * @param string $name * @param string[] $columns * @param bool $isUnique * @param bool $isPrimary * @param string[] $flags * @param mixed[] $options */ public function __construct( $name, array $columns, $isUnique = false, $isPrimary = false, array $flags = [], array $options = [] ) { $isUnique = $isUnique || $isPrimary; $this->_setName($name); $this->_isUnique = $isUnique; $this->_isPrimary = $isPrimary; $this->options = $options; foreach ($columns as $column) { $this->_addColumn($column); } foreach ($flags as $flag) { $this->addFlag($flag); } } /** @throws InvalidArgumentException */ protected function _addColumn(string $column): void { $this->_columns[$column] = new Identifier($column); } /** * {@inheritDoc} */ public function getColumns() { return array_keys($this->_columns); } /** * {@inheritDoc} */ public function getQuotedColumns(AbstractPlatform $platform) { $subParts = $platform->supportsColumnLengthIndexes() && $this->hasOption('lengths') ? $this->getOption('lengths') : []; $columns = []; foreach ($this->_columns as $column) { $length = array_shift($subParts); $quotedColumn = $column->getQuotedName($platform); if ($length !== null) { $quotedColumn .= '(' . $length . ')'; } $columns[] = $quotedColumn; } return $columns; } /** @return string[] */ public function getUnquotedColumns() { return array_map([$this, 'trimQuotes'], $this->getColumns()); } /** * Is the index neither unique nor primary key? * * @return bool */ public function isSimpleIndex() { return ! $this->_isPrimary && ! $this->_isUnique; } /** @return bool */ public function isUnique() { return $this->_isUnique; } /** @return bool */ public function isPrimary() { return $this->_isPrimary; } /** * @param string $name * @param int $pos * * @return bool */ public function hasColumnAtPosition($name, $pos = 0) { $name = $this->trimQuotes(strtolower($name)); $indexColumns = array_map('strtolower', $this->getUnquotedColumns()); return array_search($name, $indexColumns, true) === $pos; } /** * Checks if this index exactly spans the given column names in the correct order. * * @param string[] $columnNames * * @return bool */ public function spansColumns(array $columnNames) { $columns = $this->getColumns(); $numberOfColumns = count($columns); $sameColumns = true; for ($i = 0; $i < $numberOfColumns; $i++) { if ( isset($columnNames[$i]) && $this->trimQuotes(strtolower($columns[$i])) === $this->trimQuotes(strtolower($columnNames[$i])) ) { continue; } $sameColumns = false; } return $sameColumns; } /** * Keeping misspelled function name for backwards compatibility * * @deprecated Use {@see isFulfilledBy()} instead. * * @return bool */ public function isFullfilledBy(Index $other) { return $this->isFulfilledBy($other); } /** * Checks if the other index already fulfills all the indexing and constraint needs of the current one. */ public function isFulfilledBy(Index $other): bool { // allow the other index to be equally large only. It being larger is an option // but it creates a problem with scenarios of the kind PRIMARY KEY(foo,bar) UNIQUE(foo) if (count($other->getColumns()) !== count($this->getColumns())) { return false; } // Check if columns are the same, and even in the same order $sameColumns = $this->spansColumns($other->getColumns()); if ($sameColumns) { if (! $this->samePartialIndex($other)) { return false; } if (! $this->hasSameColumnLengths($other)) { return false; } if (! $this->isUnique() && ! $this->isPrimary()) { // this is a special case: If the current key is neither primary or unique, any unique or // primary key will always have the same effect for the index and there cannot be any constraint // overlaps. This means a primary or unique index can always fulfill the requirements of just an // index that has no constraints. return true; } if ($other->isPrimary() !== $this->isPrimary()) { return false; } return $other->isUnique() === $this->isUnique(); } return false; } /** * Detects if the other index is a non-unique, non primary index that can be overwritten by this one. * * @return bool */ public function overrules(Index $other) { if ($other->isPrimary()) { return false; } if ($this->isSimpleIndex() && $other->isUnique()) { return false; } return $this->spansColumns($other->getColumns()) && ($this->isPrimary() || $this->isUnique()) && $this->samePartialIndex($other); } /** * Returns platform specific flags for indexes. * * @return string[] */ public function getFlags() { return array_keys($this->_flags); } /** * Adds Flag for an index that translates to platform specific handling. * * @param string $flag * * @return Index * * @example $index->addFlag('CLUSTERED') */ public function addFlag($flag) { $this->_flags[strtolower($flag)] = true; return $this; } /** * Does this index have a specific flag? * * @param string $flag * * @return bool */ public function hasFlag($flag) { return isset($this->_flags[strtolower($flag)]); } /** * Removes a flag. * * @param string $flag * * @return void */ public function removeFlag($flag) { unset($this->_flags[strtolower($flag)]); } /** * @param string $name * * @return bool */ public function hasOption($name) { return isset($this->options[strtolower($name)]); } /** * @param string $name * * @return mixed */ public function getOption($name) { return $this->options[strtolower($name)]; } /** @return mixed[] */ public function getOptions() { return $this->options; } /** * Return whether the two indexes have the same partial index */ private function samePartialIndex(Index $other): bool { if ( $this->hasOption('where') && $other->hasOption('where') && $this->getOption('where') === $other->getOption('where') ) { return true; } return ! $this->hasOption('where') && ! $other->hasOption('where'); } /** * Returns whether the index has the same column lengths as the other */ private function hasSameColumnLengths(self $other): bool { $filter = static function (?int $length): bool { return $length !== null; }; return array_filter($this->options['lengths'] ?? [], $filter) === array_filter($other->options['lengths'] ?? [], $filter); } } PK!sdbal/src/Schema/ColumnDiff.phpnu[oldColumnName = $oldColumnName; $this->column = $column; $this->changedProperties = $changedProperties; $this->fromColumn = $fromColumn; } public function getOldColumn(): ?Column { return $this->fromColumn; } public function getNewColumn(): Column { return $this->column; } public function hasTypeChanged(): bool { return $this->hasChanged('type'); } public function hasLengthChanged(): bool { return $this->hasChanged('length'); } public function hasPrecisionChanged(): bool { return $this->hasChanged('precision'); } public function hasScaleChanged(): bool { return $this->hasChanged('scale'); } public function hasUnsignedChanged(): bool { return $this->hasChanged('unsigned'); } public function hasFixedChanged(): bool { return $this->hasChanged('fixed'); } public function hasNotNullChanged(): bool { return $this->hasChanged('notnull'); } public function hasDefaultChanged(): bool { return $this->hasChanged('default'); } public function hasAutoIncrementChanged(): bool { return $this->hasChanged('autoincrement'); } public function hasCommentChanged(): bool { return $this->hasChanged('comment'); } /** * @deprecated Use {@see hasTypeChanged()}, {@see hasLengthChanged()}, {@see hasPrecisionChanged()}, * {@see hasScaleChanged()}, {@see hasUnsignedChanged()}, {@see hasFixedChanged()}, {@see hasNotNullChanged()}, * {@see hasDefaultChanged()}, {@see hasAutoIncrementChanged()} or {@see hasCommentChanged()} instead. * * @param string $propertyName * * @return bool */ public function hasChanged($propertyName) { return in_array($propertyName, $this->changedProperties, true); } /** * @deprecated Use {@see $fromColumn} instead. * * @return Identifier */ public function getOldColumnName() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5622', '%s is deprecated. Use $fromColumn instead.', __METHOD__, ); if ($this->fromColumn !== null) { $name = $this->fromColumn->getName(); $quote = $this->fromColumn->isQuoted(); } else { $name = $this->oldColumnName; $quote = false; } return new Identifier($name, $quote); } } PK!3MMY--dbal/src/Schema/Constraint.phpnu[_setName($name); $this->setType($type); $this->setOptions($options); } /** * @param mixed[] $options * * @return Column * * @throws SchemaException */ public function setOptions(array $options) { foreach ($options as $name => $value) { $method = 'set' . $name; if (! method_exists($this, $method)) { throw UnknownColumnOption::new($name); } $this->$method($value); } return $this; } /** @return Column */ public function setType(Type $type) { $this->_type = $type; return $this; } /** * @param int|null $length * * @return Column */ public function setLength($length) { if ($length !== null) { $this->_length = (int) $length; } else { $this->_length = null; } return $this; } /** * @param int $precision * * @return Column */ public function setPrecision($precision) { if (! is_numeric($precision)) { $precision = 10; // defaults to 10 when no valid precision is given. } $this->_precision = (int) $precision; return $this; } /** * @param int $scale * * @return Column */ public function setScale($scale) { if (! is_numeric($scale)) { $scale = 0; } $this->_scale = (int) $scale; return $this; } /** * @param bool $unsigned * * @return Column */ public function setUnsigned($unsigned) { $this->_unsigned = (bool) $unsigned; return $this; } /** * @param bool $fixed * * @return Column */ public function setFixed($fixed) { $this->_fixed = (bool) $fixed; return $this; } /** * @param bool $notnull * * @return Column */ public function setNotnull($notnull) { $this->_notnull = (bool) $notnull; return $this; } /** * @param mixed $default * * @return Column */ public function setDefault($default) { $this->_default = $default; return $this; } /** * @param mixed[] $platformOptions * * @return Column */ public function setPlatformOptions(array $platformOptions) { $this->_platformOptions = $platformOptions; return $this; } /** * @param string $name * @param mixed $value * * @return Column */ public function setPlatformOption($name, $value) { $this->_platformOptions[$name] = $value; return $this; } /** * @param string|null $value * * @return Column */ public function setColumnDefinition($value) { $this->_columnDefinition = $value; return $this; } /** @return Type */ public function getType() { return $this->_type; } /** @return int|null */ public function getLength() { return $this->_length; } /** @return int */ public function getPrecision() { return $this->_precision; } /** @return int */ public function getScale() { return $this->_scale; } /** @return bool */ public function getUnsigned() { return $this->_unsigned; } /** @return bool */ public function getFixed() { return $this->_fixed; } /** @return bool */ public function getNotnull() { return $this->_notnull; } /** @return mixed */ public function getDefault() { return $this->_default; } /** @return mixed[] */ public function getPlatformOptions() { return $this->_platformOptions; } /** * @param string $name * * @return bool */ public function hasPlatformOption($name) { return isset($this->_platformOptions[$name]); } /** * @param string $name * * @return mixed */ public function getPlatformOption($name) { return $this->_platformOptions[$name]; } /** @return string|null */ public function getColumnDefinition() { return $this->_columnDefinition; } /** @return bool */ public function getAutoincrement() { return $this->_autoincrement; } /** * @param bool $flag * * @return Column */ public function setAutoincrement($flag) { $this->_autoincrement = $flag; return $this; } /** * @param string|null $comment * * @return Column */ public function setComment($comment) { $this->_comment = $comment; return $this; } /** @return string|null */ public function getComment() { return $this->_comment; } /** * @deprecated Use {@link setPlatformOption()} instead * * @param string $name * @param mixed $value * * @return Column */ public function setCustomSchemaOption($name, $value) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5476', 'Column::setCustomSchemaOption() is deprecated. Use setPlatformOption() instead.', ); $this->_customSchemaOptions[$name] = $value; return $this; } /** * @deprecated Use {@link hasPlatformOption()} instead * * @param string $name * * @return bool */ public function hasCustomSchemaOption($name) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5476', 'Column::hasCustomSchemaOption() is deprecated. Use hasPlatformOption() instead.', ); return isset($this->_customSchemaOptions[$name]); } /** * @deprecated Use {@link getPlatformOption()} instead * * @param string $name * * @return mixed */ public function getCustomSchemaOption($name) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5476', 'Column::getCustomSchemaOption() is deprecated. Use getPlatformOption() instead.', ); return $this->_customSchemaOptions[$name]; } /** * @deprecated Use {@link setPlatformOptions()} instead * * @param mixed[] $customSchemaOptions * * @return Column */ public function setCustomSchemaOptions(array $customSchemaOptions) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5476', 'Column::setCustomSchemaOptions() is deprecated. Use setPlatformOptions() instead.', ); $this->_customSchemaOptions = $customSchemaOptions; return $this; } /** * @deprecated Use {@link getPlatformOptions()} instead * * @return mixed[] */ public function getCustomSchemaOptions() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5476', 'Column::getCustomSchemaOptions() is deprecated. Use getPlatformOptions() instead.', ); return $this->_customSchemaOptions; } /** @return mixed[] */ public function toArray() { return array_merge([ 'name' => $this->_name, 'type' => $this->_type, 'default' => $this->_default, 'notnull' => $this->_notnull, 'length' => $this->_length, 'precision' => $this->_precision, 'scale' => $this->_scale, 'fixed' => $this->_fixed, 'unsigned' => $this->_unsigned, 'autoincrement' => $this->_autoincrement, 'columnDefinition' => $this->_columnDefinition, 'comment' => $this->_comment, ], $this->_platformOptions, $this->_customSchemaOptions); } } PK!b */ class MySQLSchemaManager extends AbstractSchemaManager { /** @see https://mariadb.com/kb/en/library/string-literals/#escape-sequences */ private const MARIADB_ESCAPE_SEQUENCES = [ '\\0' => "\0", "\\'" => "'", '\\"' => '"', '\\b' => "\b", '\\n' => "\n", '\\r' => "\r", '\\t' => "\t", '\\Z' => "\x1a", '\\\\' => '\\', '\\%' => '%', '\\_' => '_', // Internally, MariaDB escapes single quotes using the standard syntax "''" => "'", ]; /** * {@inheritDoc} */ public function listTableNames() { return $this->doListTableNames(); } /** * {@inheritDoc} */ public function listTables() { return $this->doListTables(); } /** * {@inheritDoc} * * @deprecated Use {@see introspectTable()} instead. */ public function listTableDetails($name) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5595', '%s is deprecated. Use introspectTable() instead.', __METHOD__, ); return $this->doListTableDetails($name); } /** * {@inheritDoc} */ public function listTableColumns($table, $database = null) { return $this->doListTableColumns($table, $database); } /** * {@inheritDoc} */ public function listTableIndexes($table) { return $this->doListTableIndexes($table); } /** * {@inheritDoc} */ public function listTableForeignKeys($table, $database = null) { return $this->doListTableForeignKeys($table, $database); } /** * {@inheritDoc} */ protected function _getPortableViewDefinition($view) { return new View($view['TABLE_NAME'], $view['VIEW_DEFINITION']); } /** * {@inheritDoc} */ protected function _getPortableTableDefinition($table) { return array_shift($table); } /** * {@inheritDoc} */ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) { foreach ($tableIndexes as $k => $v) { $v = array_change_key_case($v, CASE_LOWER); if ($v['key_name'] === 'PRIMARY') { $v['primary'] = true; } else { $v['primary'] = false; } if (strpos($v['index_type'], 'FULLTEXT') !== false) { $v['flags'] = ['FULLTEXT']; } elseif (strpos($v['index_type'], 'SPATIAL') !== false) { $v['flags'] = ['SPATIAL']; } // Ignore prohibited prefix `length` for spatial index if (strpos($v['index_type'], 'SPATIAL') === false) { $v['length'] = isset($v['sub_part']) ? (int) $v['sub_part'] : null; } $tableIndexes[$k] = $v; } return parent::_getPortableTableIndexesList($tableIndexes, $tableName); } /** * {@inheritDoc} */ protected function _getPortableDatabaseDefinition($database) { return $database['Database']; } /** * {@inheritDoc} */ protected function _getPortableTableColumnDefinition($tableColumn) { $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); $dbType = strtolower($tableColumn['type']); $dbType = strtok($dbType, '(), '); assert(is_string($dbType)); $length = $tableColumn['length'] ?? strtok('(), '); $fixed = null; if (! isset($tableColumn['name'])) { $tableColumn['name'] = ''; } $scale = null; $precision = null; $type = $origType = $this->_platform->getDoctrineTypeMapping($dbType); // In cases where not connected to a database DESCRIBE $table does not return 'Comment' if (isset($tableColumn['comment'])) { $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); } switch ($dbType) { case 'char': case 'binary': $fixed = true; break; case 'float': case 'double': case 'real': case 'numeric': case 'decimal': if ( preg_match( '([A-Za-z]+\(([0-9]+),([0-9]+)\))', $tableColumn['type'], $match, ) === 1 ) { $precision = $match[1]; $scale = $match[2]; $length = null; } break; case 'tinytext': $length = AbstractMySQLPlatform::LENGTH_LIMIT_TINYTEXT; break; case 'text': $length = AbstractMySQLPlatform::LENGTH_LIMIT_TEXT; break; case 'mediumtext': $length = AbstractMySQLPlatform::LENGTH_LIMIT_MEDIUMTEXT; break; case 'tinyblob': $length = AbstractMySQLPlatform::LENGTH_LIMIT_TINYBLOB; break; case 'blob': $length = AbstractMySQLPlatform::LENGTH_LIMIT_BLOB; break; case 'mediumblob': $length = AbstractMySQLPlatform::LENGTH_LIMIT_MEDIUMBLOB; break; case 'tinyint': case 'smallint': case 'mediumint': case 'int': case 'integer': case 'bigint': case 'year': $length = null; break; } if ($this->_platform instanceof MariaDb1027Platform) { $columnDefault = $this->getMariaDb1027ColumnDefault($this->_platform, $tableColumn['default']); } else { $columnDefault = $tableColumn['default']; } $options = [ 'length' => $length !== null ? (int) $length : null, 'unsigned' => strpos($tableColumn['type'], 'unsigned') !== false, 'fixed' => (bool) $fixed, 'default' => $columnDefault, 'notnull' => $tableColumn['null'] !== 'YES', 'scale' => null, 'precision' => null, 'autoincrement' => strpos($tableColumn['extra'], 'auto_increment') !== false, 'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== '' ? $tableColumn['comment'] : null, ]; if ($scale !== null && $precision !== null) { $options['scale'] = (int) $scale; $options['precision'] = (int) $precision; } $column = new Column($tableColumn['field'], Type::getType($type), $options); if (isset($tableColumn['characterset'])) { $column->setPlatformOption('charset', $tableColumn['characterset']); } if (isset($tableColumn['collation'])) { $column->setPlatformOption('collation', $tableColumn['collation']); } if (isset($tableColumn['declarationMismatch'])) { $column->setPlatformOption('declarationMismatch', $tableColumn['declarationMismatch']); } // Check underlying database type where doctrine type is inferred from DC2Type comment // and set a flag if it is not as expected. if ($type === 'json' && $origType !== $type && $this->expectedDbType($type, $options) !== $dbType) { $column->setPlatformOption('declarationMismatch', true); } return $column; } /** * Returns the database data type for a given doctrine type and column * * Note that for data types that depend on length where length is not part of the column definition * and therefore the $tableColumn['length'] will not be set, for example TEXT (which could be LONGTEXT, * MEDIUMTEXT) or BLOB (LONGBLOB or TINYBLOB), the expectedDbType cannot be inferred exactly, merely * the default type. * * This method is intended to be used to determine underlying database type where doctrine type is * inferred from a DC2Type comment. * * @param mixed[] $tableColumn */ private function expectedDbType(string $type, array $tableColumn): string { $_type = Type::getType($type); $expectedDbType = strtolower($_type->getSQLDeclaration($tableColumn, $this->_platform)); $expectedDbType = strtok($expectedDbType, '(), '); return $expectedDbType === false ? '' : $expectedDbType; } /** * Return Doctrine/Mysql-compatible column default values for MariaDB 10.2.7+ servers. * * - Since MariaDb 10.2.7 column defaults stored in information_schema are now quoted * to distinguish them from expressions (see MDEV-10134). * - CURRENT_TIMESTAMP, CURRENT_TIME, CURRENT_DATE are stored in information_schema * as current_timestamp(), currdate(), currtime() * - Quoted 'NULL' is not enforced by Maria, it is technically possible to have * null in some circumstances (see https://jira.mariadb.org/browse/MDEV-14053) * - \' is always stored as '' in information_schema (normalized) * * @link https://mariadb.com/kb/en/library/information-schema-columns-table/ * @link https://jira.mariadb.org/browse/MDEV-13132 * * @param string|null $columnDefault default value as stored in information_schema for MariaDB >= 10.2.7 */ private function getMariaDb1027ColumnDefault(MariaDb1027Platform $platform, ?string $columnDefault): ?string { if ($columnDefault === 'NULL' || $columnDefault === null) { return null; } if (preg_match('/^\'(.*)\'$/', $columnDefault, $matches) === 1) { return strtr($matches[1], self::MARIADB_ESCAPE_SEQUENCES); } switch ($columnDefault) { case 'current_timestamp()': return $platform->getCurrentTimestampSQL(); case 'curdate()': return $platform->getCurrentDateSQL(); case 'curtime()': return $platform->getCurrentTimeSQL(); } return $columnDefault; } /** * {@inheritDoc} */ protected function _getPortableTableForeignKeysList($tableForeignKeys) { $list = []; foreach ($tableForeignKeys as $value) { $value = array_change_key_case($value, CASE_LOWER); if (! isset($list[$value['constraint_name']])) { if (! isset($value['delete_rule']) || $value['delete_rule'] === 'RESTRICT') { $value['delete_rule'] = null; } if (! isset($value['update_rule']) || $value['update_rule'] === 'RESTRICT') { $value['update_rule'] = null; } $list[$value['constraint_name']] = [ 'name' => $value['constraint_name'], 'local' => [], 'foreign' => [], 'foreignTable' => $value['referenced_table_name'], 'onDelete' => $value['delete_rule'], 'onUpdate' => $value['update_rule'], ]; } $list[$value['constraint_name']]['local'][] = $value['column_name']; $list[$value['constraint_name']]['foreign'][] = $value['referenced_column_name']; } return parent::_getPortableTableForeignKeysList($list); } /** * {@inheritDoc} */ protected function _getPortableTableForeignKeyDefinition($tableForeignKey): ForeignKeyConstraint { return new ForeignKeyConstraint( $tableForeignKey['local'], $tableForeignKey['foreignTable'], $tableForeignKey['foreign'], $tableForeignKey['name'], [ 'onDelete' => $tableForeignKey['onDelete'], 'onUpdate' => $tableForeignKey['onUpdate'], ], ); } public function createComparator(): Comparator { return new MySQL\Comparator( $this->_platform, new CachingCollationMetadataProvider( new ConnectionCollationMetadataProvider($this->_conn), ), ); } protected function selectTableNames(string $databaseName): Result { $sql = <<<'SQL' SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_NAME SQL; return $this->_conn->executeQuery($sql, [$databaseName]); } protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result { // @todo 4.0 - call getColumnTypeSQLSnippet() instead [$columnTypeSQL, $joinCheckConstraintSQL] = $this->_platform->getColumnTypeSQLSnippets('c', $databaseName); $sql = 'SELECT'; if ($tableName === null) { $sql .= ' c.TABLE_NAME,'; } $sql .= <<_conn->executeQuery($sql, $params); } protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result { $sql = 'SELECT'; if ($tableName === null) { $sql .= ' TABLE_NAME,'; } $sql .= <<<'SQL' NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, COLUMN_NAME AS Column_Name, SUB_PART AS Sub_Part, INDEX_TYPE AS Index_Type FROM information_schema.STATISTICS SQL; $conditions = ['TABLE_SCHEMA = ?']; $params = [$databaseName]; if ($tableName !== null) { $conditions[] = 'TABLE_NAME = ?'; $params[] = $tableName; } $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY SEQ_IN_INDEX'; return $this->_conn->executeQuery($sql, $params); } protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result { $sql = 'SELECT DISTINCT'; if ($tableName === null) { $sql .= ' k.TABLE_NAME,'; } $sql .= <<<'SQL' k.CONSTRAINT_NAME, k.COLUMN_NAME, k.REFERENCED_TABLE_NAME, k.REFERENCED_COLUMN_NAME, k.ORDINAL_POSITION /*!50116, c.UPDATE_RULE, c.DELETE_RULE */ FROM information_schema.key_column_usage k /*!50116 INNER JOIN information_schema.referential_constraints c ON c.CONSTRAINT_NAME = k.CONSTRAINT_NAME AND c.TABLE_NAME = k.TABLE_NAME */ SQL; $conditions = ['k.TABLE_SCHEMA = ?']; $params = [$databaseName]; if ($tableName !== null) { $conditions[] = 'k.TABLE_NAME = ?'; $params[] = $tableName; } $conditions[] = 'k.REFERENCED_COLUMN_NAME IS NOT NULL'; $sql .= ' WHERE ' . implode(' AND ', $conditions) // The schema name is passed multiple times in the WHERE clause instead of using a JOIN condition // in order to avoid performance issues on MySQL older than 8.0 and the corresponding MariaDB versions // caused by https://bugs.mysql.com/bug.php?id=81347. // Use a string literal for the database name since the internal PDO SQL parser // cannot recognize parameter placeholders inside conditional comments . ' /*!50116 AND c.CONSTRAINT_SCHEMA = ' . $this->_conn->quote($databaseName) . ' */' . ' ORDER BY k.ORDINAL_POSITION'; return $this->_conn->executeQuery($sql, $params); } /** * {@inheritDoc} */ protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array { $sql = <<<'SQL' SELECT t.TABLE_NAME, t.ENGINE, t.AUTO_INCREMENT, t.TABLE_COMMENT, t.CREATE_OPTIONS, t.TABLE_COLLATION, ccsa.CHARACTER_SET_NAME FROM information_schema.TABLES t INNER JOIN information_schema.COLLATION_CHARACTER_SET_APPLICABILITY ccsa ON ccsa.COLLATION_NAME = t.TABLE_COLLATION SQL; $conditions = ['t.TABLE_SCHEMA = ?']; $params = [$databaseName]; if ($tableName !== null) { $conditions[] = 't.TABLE_NAME = ?'; $params[] = $tableName; } $conditions[] = "t.TABLE_TYPE = 'BASE TABLE'"; $sql .= ' WHERE ' . implode(' AND ', $conditions); /** @var array> $metadata */ $metadata = $this->_conn->executeQuery($sql, $params) ->fetchAllAssociativeIndexed(); $tableOptions = []; foreach ($metadata as $table => $data) { $data = array_change_key_case($data, CASE_LOWER); $tableOptions[$table] = [ 'engine' => $data['engine'], 'collation' => $data['table_collation'], 'charset' => $data['character_set_name'], 'autoincrement' => $data['auto_increment'], 'comment' => $data['table_comment'], 'create_options' => $this->parseCreateOptions($data['create_options']), ]; } return $tableOptions; } /** @return string[]|true[] */ private function parseCreateOptions(?string $string): array { $options = []; if ($string === null || $string === '') { return $options; } foreach (explode(' ', $string) as $pair) { $parts = explode('=', $pair, 2); $options[$parts[0]] = $parts[1] ?? true; } return $options; } } PK!m#dbal/src/Schema/SchemaException.phpnu[ $createdSchemas * @param array $droppedSchemas * @param array $createdSequences * @param array $alteredSequences * @param array $droppedSequences */ public function __construct( $newTables = [], $changedTables = [], $removedTables = [], ?Schema $fromSchema = null, $createdSchemas = [], $droppedSchemas = [], $createdSequences = [], $alteredSequences = [], $droppedSequences = [] ) { $this->newTables = $newTables; $this->changedTables = array_filter($changedTables, static function (TableDiff $diff): bool { return ! $diff->isEmpty(); }); $this->removedTables = $removedTables; $this->fromSchema = $fromSchema; $this->newNamespaces = $createdSchemas; $this->removedNamespaces = $droppedSchemas; $this->newSequences = $createdSequences; $this->changedSequences = $alteredSequences; $this->removedSequences = $droppedSequences; } /** @return array */ public function getCreatedSchemas(): array { return $this->newNamespaces; } /** @return array */ public function getDroppedSchemas(): array { return $this->removedNamespaces; } /** @return array */ public function getCreatedTables(): array { return $this->newTables; } /** @return array */ public function getAlteredTables(): array { return $this->changedTables; } /** @return array
*/ public function getDroppedTables(): array { return $this->removedTables; } /** @return array */ public function getCreatedSequences(): array { return $this->newSequences; } /** @return array */ public function getAlteredSequences(): array { return $this->changedSequences; } /** @return array */ public function getDroppedSequences(): array { return $this->removedSequences; } /** * Returns whether the diff is empty (contains no changes). */ public function isEmpty(): bool { return count($this->newNamespaces) === 0 && count($this->removedNamespaces) === 0 && count($this->newTables) === 0 && count($this->changedTables) === 0 && count($this->removedTables) === 0 && count($this->newSequences) === 0 && count($this->changedSequences) === 0 && count($this->removedSequences) === 0; } /** * The to save sql mode ensures that the following things don't happen: * * 1. Tables are deleted * 2. Sequences are deleted * 3. Foreign Keys which reference tables that would otherwise be deleted. * * This way it is ensured that assets are deleted which might not be relevant to the metadata schema at all. * * @deprecated * * @return list */ public function toSaveSql(AbstractPlatform $platform) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5766', '%s is deprecated.', __METHOD__, ); return $this->_toSql($platform, true); } /** * @deprecated Use {@link AbstractPlatform::getAlterSchemaSQL()} instead. * * @return list */ public function toSql(AbstractPlatform $platform) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5766', '%s is deprecated. Use AbstractPlatform::getAlterSchemaSQL() instead.', __METHOD__, ); return $this->_toSql($platform, false); } /** * @param bool $saveMode * * @return list */ protected function _toSql(AbstractPlatform $platform, $saveMode = false) { $sql = []; if ($platform->supportsSchemas()) { foreach ($this->getCreatedSchemas() as $schema) { $sql[] = $platform->getCreateSchemaSQL($schema); } } if ($platform->supportsForeignKeyConstraints() && $saveMode === false) { foreach ($this->orphanedForeignKeys as $orphanedForeignKey) { $sql[] = $platform->getDropForeignKeySQL($orphanedForeignKey, $orphanedForeignKey->getLocalTable()); } } if ($platform->supportsSequences() === true) { foreach ($this->getAlteredSequences() as $sequence) { $sql[] = $platform->getAlterSequenceSQL($sequence); } if ($saveMode === false) { foreach ($this->getDroppedSequences() as $sequence) { $sql[] = $platform->getDropSequenceSQL($sequence); } } foreach ($this->getCreatedSequences() as $sequence) { $sql[] = $platform->getCreateSequenceSQL($sequence); } } $sql = array_merge($sql, $platform->getCreateTablesSQL($this->getCreatedTables())); if ($saveMode === false) { $sql = array_merge($sql, $platform->getDropTablesSQL($this->getDroppedTables())); } foreach ($this->getAlteredTables() as $tableDiff) { $sql = array_merge($sql, $platform->getAlterTableSQL($tableDiff)); } return $sql; } } PK!1}}(dbal/src/Schema/SchemaManagerFactory.phpnu[ */ class SQLServerSchemaManager extends AbstractSchemaManager { private ?string $databaseCollation = null; /** * {@inheritDoc} */ public function listTableNames() { return $this->doListTableNames(); } /** * {@inheritDoc} */ public function listTables() { return $this->doListTables(); } /** * {@inheritDoc} * * @deprecated Use {@see introspectTable()} instead. */ public function listTableDetails($name) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5595', '%s is deprecated. Use introspectTable() instead.', __METHOD__, ); return $this->doListTableDetails($name); } /** * {@inheritDoc} */ public function listTableColumns($table, $database = null) { return $this->doListTableColumns($table, $database); } /** * {@inheritDoc} */ public function listTableIndexes($table) { return $this->doListTableIndexes($table); } /** * {@inheritDoc} */ public function listTableForeignKeys($table, $database = null) { return $this->doListTableForeignKeys($table, $database); } /** * {@inheritDoc} */ public function listSchemaNames(): array { return $this->_conn->fetchFirstColumn( <<<'SQL' SELECT name FROM sys.schemas WHERE name NOT IN('guest', 'INFORMATION_SCHEMA', 'sys') SQL, ); } /** * {@inheritDoc} */ protected function _getPortableSequenceDefinition($sequence) { return new Sequence($sequence['name'], (int) $sequence['increment'], (int) $sequence['start_value']); } /** * {@inheritDoc} */ protected function _getPortableTableColumnDefinition($tableColumn) { $dbType = strtok($tableColumn['type'], '(), '); assert(is_string($dbType)); $fixed = null; $length = (int) $tableColumn['length']; $default = $tableColumn['default']; if (! isset($tableColumn['name'])) { $tableColumn['name'] = ''; } if ($default !== null) { $default = $this->parseDefaultExpression($default); } switch ($dbType) { case 'nchar': case 'ntext': // Unicode data requires 2 bytes per character $length /= 2; break; case 'nvarchar': if ($length === -1) { break; } // Unicode data requires 2 bytes per character $length /= 2; break; case 'varchar': // TEXT type is returned as VARCHAR(MAX) with a length of -1 if ($length === -1) { $dbType = 'text'; } break; case 'varbinary': if ($length === -1) { $dbType = 'blob'; } break; } if ($dbType === 'char' || $dbType === 'nchar' || $dbType === 'binary') { $fixed = true; } $type = $this->_platform->getDoctrineTypeMapping($dbType); $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); $options = [ 'unsigned' => false, 'fixed' => (bool) $fixed, 'default' => $default, 'notnull' => (bool) $tableColumn['notnull'], 'scale' => $tableColumn['scale'], 'precision' => $tableColumn['precision'], 'autoincrement' => (bool) $tableColumn['autoincrement'], 'comment' => $tableColumn['comment'] !== '' ? $tableColumn['comment'] : null, ]; if ($length !== 0 && ($type === 'text' || $type === 'string' || $type === 'binary')) { $options['length'] = $length; } $column = new Column($tableColumn['name'], Type::getType($type), $options); if (isset($tableColumn['collation']) && $tableColumn['collation'] !== 'NULL') { $column->setPlatformOption('collation', $tableColumn['collation']); } return $column; } private function parseDefaultExpression(string $value): ?string { while (preg_match('/^\((.*)\)$/s', $value, $matches)) { $value = $matches[1]; } if ($value === 'NULL') { return null; } if (preg_match('/^\'(.*)\'$/s', $value, $matches) === 1) { $value = str_replace("''", "'", $matches[1]); } if ($value === 'getdate()') { return $this->_platform->getCurrentTimestampSQL(); } return $value; } /** * {@inheritDoc} */ protected function _getPortableTableForeignKeysList($tableForeignKeys) { $foreignKeys = []; foreach ($tableForeignKeys as $tableForeignKey) { $name = $tableForeignKey['ForeignKey']; if (! isset($foreignKeys[$name])) { $foreignKeys[$name] = [ 'local_columns' => [$tableForeignKey['ColumnName']], 'foreign_table' => $tableForeignKey['ReferenceTableName'], 'foreign_columns' => [$tableForeignKey['ReferenceColumnName']], 'name' => $name, 'options' => [ 'onUpdate' => str_replace('_', ' ', $tableForeignKey['update_referential_action_desc']), 'onDelete' => str_replace('_', ' ', $tableForeignKey['delete_referential_action_desc']), ], ]; } else { $foreignKeys[$name]['local_columns'][] = $tableForeignKey['ColumnName']; $foreignKeys[$name]['foreign_columns'][] = $tableForeignKey['ReferenceColumnName']; } } return parent::_getPortableTableForeignKeysList($foreignKeys); } /** * {@inheritDoc} */ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) { foreach ($tableIndexes as &$tableIndex) { $tableIndex['non_unique'] = (bool) $tableIndex['non_unique']; $tableIndex['primary'] = (bool) $tableIndex['primary']; $tableIndex['flags'] = $tableIndex['flags'] ? [$tableIndex['flags']] : null; } return parent::_getPortableTableIndexesList($tableIndexes, $tableName); } /** * {@inheritDoc} */ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) { return new ForeignKeyConstraint( $tableForeignKey['local_columns'], $tableForeignKey['foreign_table'], $tableForeignKey['foreign_columns'], $tableForeignKey['name'], $tableForeignKey['options'], ); } /** * {@inheritDoc} */ protected function _getPortableTableDefinition($table) { if ($table['schema_name'] !== 'dbo') { return $table['schema_name'] . '.' . $table['table_name']; } return $table['table_name']; } /** * {@inheritDoc} */ protected function _getPortableDatabaseDefinition($database) { return $database['name']; } /** * {@inheritDoc} * * @deprecated Use {@see listSchemaNames()} instead. */ protected function getPortableNamespaceDefinition(array $namespace) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4503', 'SQLServerSchemaManager::getPortableNamespaceDefinition() is deprecated,' . ' use SQLServerSchemaManager::listSchemaNames() instead.', ); return $namespace['name']; } /** * {@inheritDoc} */ protected function _getPortableViewDefinition($view) { // @todo return new View($view['name'], $view['definition']); } /** * {@inheritDoc} */ public function alterTable(TableDiff $tableDiff) { $droppedColumns = $tableDiff->getDroppedColumns(); if (count($droppedColumns) > 0) { $tableName = ($tableDiff->getOldTable() ?? $tableDiff->getName($this->_platform))->getName(); foreach ($droppedColumns as $col) { foreach ($this->getColumnConstraints($tableName, $col->getName()) as $constraint) { $this->_conn->executeStatement( sprintf( 'ALTER TABLE %s DROP CONSTRAINT %s', $tableName, $constraint, ), ); } } } parent::alterTable($tableDiff); } /** * Returns the names of the constraints for a given column. * * @return iterable * * @throws Exception */ private function getColumnConstraints(string $table, string $column): iterable { return $this->_conn->iterateColumn( <<<'SQL' SELECT o.name FROM sys.objects o INNER JOIN sys.objects t ON t.object_id = o.parent_object_id AND t.type = 'U' INNER JOIN sys.default_constraints dc ON dc.object_id = o.object_id INNER JOIN sys.columns c ON c.column_id = dc.parent_column_id AND c.object_id = t.object_id WHERE t.name = ? AND c.name = ? SQL , [$table, $column], ); } /** @throws Exception */ public function createComparator(): Comparator { return new SQLServer\Comparator($this->_platform, $this->getDatabaseCollation()); } /** @throws Exception */ private function getDatabaseCollation(): string { if ($this->databaseCollation === null) { $databaseCollation = $this->_conn->fetchOne( 'SELECT collation_name FROM sys.databases WHERE name = ' . $this->_platform->getCurrentDatabaseExpression(), ); // a database is always selected, even if omitted in the connection parameters assert(is_string($databaseCollation)); $this->databaseCollation = $databaseCollation; } return $this->databaseCollation; } protected function selectTableNames(string $databaseName): Result { // The "sysdiagrams" table must be ignored as it's internal SQL Server table for Database Diagrams $sql = <<<'SQL' SELECT name AS table_name, SCHEMA_NAME(schema_id) AS schema_name FROM sys.objects WHERE type = 'U' AND name != 'sysdiagrams' ORDER BY name SQL; return $this->_conn->executeQuery($sql); } protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result { $sql = 'SELECT'; if ($tableName === null) { $sql .= ' obj.name AS table_name, scm.name AS schema_name,'; } $sql .= <<<'SQL' col.name, type.name AS type, col.max_length AS length, ~col.is_nullable AS notnull, def.definition AS [default], col.scale, col.precision, col.is_identity AS autoincrement, col.collation_name AS collation, -- CAST avoids driver error for sql_variant type CAST(prop.value AS NVARCHAR(MAX)) AS comment FROM sys.columns AS col JOIN sys.types AS type ON col.user_type_id = type.user_type_id JOIN sys.objects AS obj ON col.object_id = obj.object_id JOIN sys.schemas AS scm ON obj.schema_id = scm.schema_id LEFT JOIN sys.default_constraints def ON col.default_object_id = def.object_id AND col.object_id = def.parent_object_id LEFT JOIN sys.extended_properties AS prop ON obj.object_id = prop.major_id AND col.column_id = prop.minor_id AND prop.name = 'MS_Description' SQL; // The "sysdiagrams" table must be ignored as it's internal SQL Server table for Database Diagrams $conditions = ["obj.type = 'U'", "obj.name != 'sysdiagrams'"]; $params = []; if ($tableName !== null) { $conditions[] = $this->getTableWhereClause($tableName, 'scm.name', 'obj.name'); } $sql .= ' WHERE ' . implode(' AND ', $conditions); return $this->_conn->executeQuery($sql, $params); } protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result { $sql = 'SELECT'; if ($tableName === null) { $sql .= ' tbl.name AS table_name, scm.name AS schema_name,'; } $sql .= <<<'SQL' idx.name AS key_name, col.name AS column_name, ~idx.is_unique AS non_unique, idx.is_primary_key AS [primary], CASE idx.type WHEN '1' THEN 'clustered' WHEN '2' THEN 'nonclustered' ELSE NULL END AS flags FROM sys.tables AS tbl JOIN sys.schemas AS scm ON tbl.schema_id = scm.schema_id JOIN sys.indexes AS idx ON tbl.object_id = idx.object_id JOIN sys.index_columns AS idxcol ON idx.object_id = idxcol.object_id AND idx.index_id = idxcol.index_id JOIN sys.columns AS col ON idxcol.object_id = col.object_id AND idxcol.column_id = col.column_id SQL; $conditions = []; $params = []; if ($tableName !== null) { $conditions[] = $this->getTableWhereClause($tableName, 'scm.name', 'tbl.name'); $sql .= ' WHERE ' . implode(' AND ', $conditions); } $sql .= ' ORDER BY idx.index_id, idxcol.key_ordinal'; return $this->_conn->executeQuery($sql, $params); } protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result { $sql = 'SELECT'; if ($tableName === null) { $sql .= ' OBJECT_NAME (f.parent_object_id) AS table_name, SCHEMA_NAME(f.schema_id) AS schema_name,'; } $sql .= <<<'SQL' f.name AS ForeignKey, SCHEMA_NAME (f.SCHEMA_ID) AS SchemaName, OBJECT_NAME (f.parent_object_id) AS TableName, COL_NAME (fc.parent_object_id,fc.parent_column_id) AS ColumnName, SCHEMA_NAME (o.SCHEMA_ID) ReferenceSchemaName, OBJECT_NAME (f.referenced_object_id) AS ReferenceTableName, COL_NAME(fc.referenced_object_id,fc.referenced_column_id) AS ReferenceColumnName, f.delete_referential_action_desc, f.update_referential_action_desc FROM sys.foreign_keys AS f INNER JOIN sys.foreign_key_columns AS fc INNER JOIN sys.objects AS o ON o.OBJECT_ID = fc.referenced_object_id ON f.OBJECT_ID = fc.constraint_object_id SQL; $conditions = []; $params = []; if ($tableName !== null) { $conditions[] = $this->getTableWhereClause( $tableName, 'SCHEMA_NAME(f.schema_id)', 'OBJECT_NAME(f.parent_object_id)', ); $sql .= ' WHERE ' . implode(' AND ', $conditions); } $sql .= ' ORDER BY fc.constraint_column_id'; return $this->_conn->executeQuery($sql, $params); } /** * {@inheritDoc} */ protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array { $sql = <<<'SQL' SELECT tbl.name, p.value AS [table_comment] FROM sys.tables AS tbl INNER JOIN sys.extended_properties AS p ON p.major_id=tbl.object_id AND p.minor_id=0 AND p.class=1 SQL; $conditions = ["SCHEMA_NAME(tbl.schema_id) = N'dbo'", "p.name = N'MS_Description'"]; $params = []; if ($tableName !== null) { $conditions[] = "tbl.name = N'" . $tableName . "'"; } $sql .= ' WHERE ' . implode(' AND ', $conditions); /** @var array> $metadata */ $metadata = $this->_conn->executeQuery($sql, $params) ->fetchAllAssociativeIndexed(); $tableOptions = []; foreach ($metadata as $table => $data) { $data = array_change_key_case($data, CASE_LOWER); $tableOptions[$table] = [ 'comment' => $data['table_comment'], ]; } return $tableOptions; } /** * Returns the where clause to filter schema and table name in a query. * * @param string $table The full qualified name of the table. * @param string $schemaColumn The name of the column to compare the schema to in the where clause. * @param string $tableColumn The name of the column to compare the table to in the where clause. */ private function getTableWhereClause($table, $schemaColumn, $tableColumn): string { if (strpos($table, '.') !== false) { [$schema, $table] = explode('.', $table); $schema = $this->_platform->quoteStringLiteral($schema); $table = $this->_platform->quoteStringLiteral($table); } else { $schema = 'SCHEMA_NAME()'; $table = $this->_platform->quoteStringLiteral($table); } return sprintf('(%s = %s AND %s = %s)', $tableColumn, $table, $schemaColumn, $schema); } } PK! 1@1@'dbal/src/Schema/OracleSchemaManager.phpnu[ */ class OracleSchemaManager extends AbstractSchemaManager { /** * {@inheritDoc} */ public function listTableNames() { return $this->doListTableNames(); } /** * {@inheritDoc} */ public function listTables() { return $this->doListTables(); } /** * {@inheritDoc} * * @deprecated Use {@see introspectTable()} instead. */ public function listTableDetails($name) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5595', '%s is deprecated. Use introspectTable() instead.', __METHOD__, ); return $this->doListTableDetails($name); } /** * {@inheritDoc} */ public function listTableColumns($table, $database = null) { return $this->doListTableColumns($table, $database); } /** * {@inheritDoc} */ public function listTableIndexes($table) { return $this->doListTableIndexes($table); } /** * {@inheritDoc} */ public function listTableForeignKeys($table, $database = null) { return $this->doListTableForeignKeys($table, $database); } /** * {@inheritDoc} */ protected function _getPortableViewDefinition($view) { $view = array_change_key_case($view, CASE_LOWER); return new View($this->getQuotedIdentifierName($view['view_name']), $view['text']); } /** * {@inheritDoc} */ protected function _getPortableTableDefinition($table) { $table = array_change_key_case($table, CASE_LOWER); return $this->getQuotedIdentifierName($table['table_name']); } /** * {@inheritDoc} * * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html */ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) { $indexBuffer = []; foreach ($tableIndexes as $tableIndex) { $tableIndex = array_change_key_case($tableIndex, CASE_LOWER); $keyName = strtolower($tableIndex['name']); $buffer = []; if ($tableIndex['is_primary'] === 'P') { $keyName = 'primary'; $buffer['primary'] = true; $buffer['non_unique'] = false; } else { $buffer['primary'] = false; $buffer['non_unique'] = ! $tableIndex['is_unique']; } $buffer['key_name'] = $keyName; $buffer['column_name'] = $this->getQuotedIdentifierName($tableIndex['column_name']); $indexBuffer[] = $buffer; } return parent::_getPortableTableIndexesList($indexBuffer, $tableName); } /** * {@inheritDoc} */ protected function _getPortableTableColumnDefinition($tableColumn) { $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); $dbType = strtolower($tableColumn['data_type']); if (strpos($dbType, 'timestamp(') === 0) { if (strpos($dbType, 'with time zone') !== false) { $dbType = 'timestamptz'; } else { $dbType = 'timestamp'; } } $unsigned = $fixed = $precision = $scale = $length = null; if (! isset($tableColumn['column_name'])) { $tableColumn['column_name'] = ''; } // Default values returned from database sometimes have trailing spaces. if (is_string($tableColumn['data_default'])) { $tableColumn['data_default'] = trim($tableColumn['data_default']); } if ($tableColumn['data_default'] === '' || $tableColumn['data_default'] === 'NULL') { $tableColumn['data_default'] = null; } if ($tableColumn['data_default'] !== null) { // Default values returned from database are represented as literal expressions if (preg_match('/^\'(.*)\'$/s', $tableColumn['data_default'], $matches) === 1) { $tableColumn['data_default'] = str_replace("''", "'", $matches[1]); } } if ($tableColumn['data_precision'] !== null) { $precision = (int) $tableColumn['data_precision']; } if ($tableColumn['data_scale'] !== null) { $scale = (int) $tableColumn['data_scale']; } $type = $this->_platform->getDoctrineTypeMapping($dbType); $type = $this->extractDoctrineTypeFromComment($tableColumn['comments'], $type); $tableColumn['comments'] = $this->removeDoctrineTypeFromComment($tableColumn['comments'], $type); switch ($dbType) { case 'number': if ($precision === 20 && $scale === 0) { $type = 'bigint'; } elseif ($precision === 5 && $scale === 0) { $type = 'smallint'; } elseif ($precision === 1 && $scale === 0) { $type = 'boolean'; } elseif ($scale > 0) { $type = 'decimal'; } break; case 'varchar': case 'varchar2': case 'nvarchar2': $length = $tableColumn['char_length']; $fixed = false; break; case 'raw': $length = $tableColumn['data_length']; $fixed = true; break; case 'char': case 'nchar': $length = $tableColumn['char_length']; $fixed = true; break; } $options = [ 'notnull' => $tableColumn['nullable'] === 'N', 'fixed' => (bool) $fixed, 'unsigned' => (bool) $unsigned, 'default' => $tableColumn['data_default'], 'length' => $length, 'precision' => $precision, 'scale' => $scale, 'comment' => isset($tableColumn['comments']) && $tableColumn['comments'] !== '' ? $tableColumn['comments'] : null, ]; return new Column($this->getQuotedIdentifierName($tableColumn['column_name']), Type::getType($type), $options); } /** * {@inheritDoc} */ protected function _getPortableTableForeignKeysList($tableForeignKeys) { $list = []; foreach ($tableForeignKeys as $value) { $value = array_change_key_case($value, CASE_LOWER); if (! isset($list[$value['constraint_name']])) { if ($value['delete_rule'] === 'NO ACTION') { $value['delete_rule'] = null; } $list[$value['constraint_name']] = [ 'name' => $this->getQuotedIdentifierName($value['constraint_name']), 'local' => [], 'foreign' => [], 'foreignTable' => $value['references_table'], 'onDelete' => $value['delete_rule'], ]; } $localColumn = $this->getQuotedIdentifierName($value['local_column']); $foreignColumn = $this->getQuotedIdentifierName($value['foreign_column']); $list[$value['constraint_name']]['local'][$value['position']] = $localColumn; $list[$value['constraint_name']]['foreign'][$value['position']] = $foreignColumn; } return parent::_getPortableTableForeignKeysList($list); } /** * {@inheritDoc} */ protected function _getPortableTableForeignKeyDefinition($tableForeignKey): ForeignKeyConstraint { return new ForeignKeyConstraint( array_values($tableForeignKey['local']), $this->getQuotedIdentifierName($tableForeignKey['foreignTable']), array_values($tableForeignKey['foreign']), $this->getQuotedIdentifierName($tableForeignKey['name']), ['onDelete' => $tableForeignKey['onDelete']], ); } /** * {@inheritDoc} */ protected function _getPortableSequenceDefinition($sequence) { $sequence = array_change_key_case($sequence, CASE_LOWER); return new Sequence( $this->getQuotedIdentifierName($sequence['sequence_name']), (int) $sequence['increment_by'], (int) $sequence['min_value'], ); } /** * {@inheritDoc} */ protected function _getPortableDatabaseDefinition($database) { $database = array_change_key_case($database, CASE_LOWER); return $database['username']; } /** * {@inheritDoc} */ public function createDatabase($database) { $statement = $this->_platform->getCreateDatabaseSQL($database); $params = $this->_conn->getParams(); if (isset($params['password'])) { $statement .= ' IDENTIFIED BY ' . $params['password']; } $this->_conn->executeStatement($statement); $statement = 'GRANT DBA TO ' . $database; $this->_conn->executeStatement($statement); } /** * @internal The method should be only used from within the OracleSchemaManager class hierarchy. * * @param string $table * * @return bool * * @throws Exception */ public function dropAutoincrement($table) { $sql = $this->_platform->getDropAutoincrementSql($table); foreach ($sql as $query) { $this->_conn->executeStatement($query); } return true; } /** * {@inheritDoc} */ public function dropTable($name) { $this->tryMethod('dropAutoincrement', $name); parent::dropTable($name); } /** * Returns the quoted representation of the given identifier name. * * Quotes non-uppercase identifiers explicitly to preserve case * and thus make references to the particular identifier work. * * @param string $identifier The identifier to quote. */ private function getQuotedIdentifierName($identifier): string { if (preg_match('/[a-z]/', $identifier) === 1) { return $this->_platform->quoteIdentifier($identifier); } return $identifier; } protected function selectTableNames(string $databaseName): Result { $sql = <<<'SQL' SELECT TABLE_NAME FROM ALL_TABLES WHERE OWNER = :OWNER ORDER BY TABLE_NAME SQL; return $this->_conn->executeQuery($sql, ['OWNER' => $databaseName]); } protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result { $sql = 'SELECT'; if ($tableName === null) { $sql .= ' C.TABLE_NAME,'; } $sql .= <<<'SQL' C.COLUMN_NAME, C.DATA_TYPE, C.DATA_DEFAULT, C.DATA_PRECISION, C.DATA_SCALE, C.CHAR_LENGTH, C.DATA_LENGTH, C.NULLABLE, D.COMMENTS FROM ALL_TAB_COLUMNS C INNER JOIN ALL_TABLES T ON T.OWNER = C.OWNER AND T.TABLE_NAME = C.TABLE_NAME LEFT JOIN ALL_COL_COMMENTS D ON D.OWNER = C.OWNER AND D.TABLE_NAME = C.TABLE_NAME AND D.COLUMN_NAME = C.COLUMN_NAME SQL; $conditions = ['C.OWNER = :OWNER']; $params = ['OWNER' => $databaseName]; if ($tableName !== null) { $conditions[] = 'C.TABLE_NAME = :TABLE_NAME'; $params['TABLE_NAME'] = $tableName; } $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY C.COLUMN_ID'; return $this->_conn->executeQuery($sql, $params); } protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result { $sql = 'SELECT'; if ($tableName === null) { $sql .= ' IND_COL.TABLE_NAME,'; } $sql .= <<<'SQL' IND_COL.INDEX_NAME AS NAME, IND.INDEX_TYPE AS TYPE, DECODE(IND.UNIQUENESS, 'NONUNIQUE', 0, 'UNIQUE', 1) AS IS_UNIQUE, IND_COL.COLUMN_NAME, IND_COL.COLUMN_POSITION AS COLUMN_POS, CON.CONSTRAINT_TYPE AS IS_PRIMARY FROM ALL_IND_COLUMNS IND_COL LEFT JOIN ALL_INDEXES IND ON IND.OWNER = IND_COL.INDEX_OWNER AND IND.INDEX_NAME = IND_COL.INDEX_NAME LEFT JOIN ALL_CONSTRAINTS CON ON CON.OWNER = IND_COL.INDEX_OWNER AND CON.INDEX_NAME = IND_COL.INDEX_NAME SQL; $conditions = ['IND_COL.INDEX_OWNER = :OWNER']; $params = ['OWNER' => $databaseName]; if ($tableName !== null) { $conditions[] = 'IND_COL.TABLE_NAME = :TABLE_NAME'; $params['TABLE_NAME'] = $tableName; } $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY IND_COL.TABLE_NAME, IND_COL.INDEX_NAME' . ', IND_COL.COLUMN_POSITION'; return $this->_conn->executeQuery($sql, $params); } protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result { $sql = 'SELECT'; if ($tableName === null) { $sql .= ' COLS.TABLE_NAME,'; } $sql .= <<<'SQL' ALC.CONSTRAINT_NAME, ALC.DELETE_RULE, COLS.COLUMN_NAME LOCAL_COLUMN, COLS.POSITION, R_COLS.TABLE_NAME REFERENCES_TABLE, R_COLS.COLUMN_NAME FOREIGN_COLUMN FROM ALL_CONS_COLUMNS COLS LEFT JOIN ALL_CONSTRAINTS ALC ON ALC.OWNER = COLS.OWNER AND ALC.CONSTRAINT_NAME = COLS.CONSTRAINT_NAME LEFT JOIN ALL_CONS_COLUMNS R_COLS ON R_COLS.OWNER = ALC.R_OWNER AND R_COLS.CONSTRAINT_NAME = ALC.R_CONSTRAINT_NAME AND R_COLS.POSITION = COLS.POSITION SQL; $conditions = ["ALC.CONSTRAINT_TYPE = 'R'", 'COLS.OWNER = :OWNER']; $params = ['OWNER' => $databaseName]; if ($tableName !== null) { $conditions[] = 'COLS.TABLE_NAME = :TABLE_NAME'; $params['TABLE_NAME'] = $tableName; } $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY COLS.TABLE_NAME, COLS.CONSTRAINT_NAME' . ', COLS.POSITION'; return $this->_conn->executeQuery($sql, $params); } /** * {@inheritDoc} */ protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array { $sql = 'SELECT TABLE_NAME, COMMENTS'; $conditions = ['OWNER = :OWNER']; $params = ['OWNER' => $databaseName]; if ($tableName !== null) { $conditions[] = 'TABLE_NAME = :TABLE_NAME'; $params['TABLE_NAME'] = $tableName; } $sql .= ' FROM ALL_TAB_COMMENTS WHERE ' . implode(' AND ', $conditions); /** @var array> $metadata */ $metadata = $this->_conn->executeQuery($sql, $params) ->fetchAllAssociativeIndexed(); $tableOptions = []; foreach ($metadata as $table => $data) { $data = array_change_key_case($data, CASE_LOWER); $tableOptions[$table] = [ 'comment' => $data['comments'], ]; } return $tableOptions; } protected function normalizeName(string $name): string { $identifier = new Identifier($name); return $identifier->isQuoted() ? $identifier->getName() : strtoupper($name); } } PK!.dbal/src/Schema/LegacySchemaManagerFactory.phpnu[getDriver()->getSchemaManager( $connection, $connection->getDatabasePlatform(), ); } } PK!f22dbal/src/Schema/Schema.phpnu[_schemaConfig = $schemaConfig; $this->_setName($schemaConfig->getName() ?? 'public'); foreach ($namespaces as $namespace) { $this->createNamespace($namespace); } foreach ($tables as $table) { $this->_addTable($table); } foreach ($sequences as $sequence) { $this->_addSequence($sequence); } } /** * @deprecated * * @return bool */ public function hasExplicitForeignKeyIndexes() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4822', 'Schema::hasExplicitForeignKeyIndexes() is deprecated.', ); return $this->_schemaConfig->hasExplicitForeignKeyIndexes(); } /** * @return void * * @throws SchemaException */ protected function _addTable(Table $table) { $namespaceName = $table->getNamespaceName(); $tableName = $this->normalizeName($table); if (isset($this->_tables[$tableName])) { throw SchemaException::tableAlreadyExists($tableName); } if ( $namespaceName !== null && ! $table->isInDefaultNamespace($this->getName()) && ! $this->hasNamespace($namespaceName) ) { $this->createNamespace($namespaceName); } $this->_tables[$tableName] = $table; $table->setSchemaConfig($this->_schemaConfig); } /** * @return void * * @throws SchemaException */ protected function _addSequence(Sequence $sequence) { $namespaceName = $sequence->getNamespaceName(); $seqName = $this->normalizeName($sequence); if (isset($this->_sequences[$seqName])) { throw SchemaException::sequenceAlreadyExists($seqName); } if ( $namespaceName !== null && ! $sequence->isInDefaultNamespace($this->getName()) && ! $this->hasNamespace($namespaceName) ) { $this->createNamespace($namespaceName); } $this->_sequences[$seqName] = $sequence; } /** * Returns the namespaces of this schema. * * @return string[] A list of namespace names. */ public function getNamespaces() { return $this->namespaces; } /** * Gets all tables of this schema. * * @return Table[] */ public function getTables() { return $this->_tables; } /** * @param string $name * * @return Table * * @throws SchemaException */ public function getTable($name) { $name = $this->getFullQualifiedAssetName($name); if (! isset($this->_tables[$name])) { throw SchemaException::tableDoesNotExist($name); } return $this->_tables[$name]; } /** @param string $name */ private function getFullQualifiedAssetName($name): string { $name = $this->getUnquotedAssetName($name); if (strpos($name, '.') === false) { $name = $this->getName() . '.' . $name; } return strtolower($name); } private function normalizeName(AbstractAsset $asset): string { return $asset->getFullQualifiedName($this->getName()); } /** * Returns the unquoted representation of a given asset name. * * @param string $assetName Quoted or unquoted representation of an asset name. */ private function getUnquotedAssetName($assetName): string { if ($this->isIdentifierQuoted($assetName)) { return $this->trimQuotes($assetName); } return $assetName; } /** * Does this schema have a namespace with the given name? * * @param string $name * * @return bool */ public function hasNamespace($name) { $name = strtolower($this->getUnquotedAssetName($name)); return isset($this->namespaces[$name]); } /** * Does this schema have a table with the given name? * * @param string $name * * @return bool */ public function hasTable($name) { $name = $this->getFullQualifiedAssetName($name); return isset($this->_tables[$name]); } /** * Gets all table names, prefixed with a schema name, even the default one if present. * * @deprecated Use {@see getTables()} and {@see Table::getName()} instead. * * @return string[] */ public function getTableNames() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4800', 'Schema::getTableNames() is deprecated.' . ' Use Schema::getTables() and Table::getName() instead.', __METHOD__, ); return array_keys($this->_tables); } /** * @param string $name * * @return bool */ public function hasSequence($name) { $name = $this->getFullQualifiedAssetName($name); return isset($this->_sequences[$name]); } /** * @param string $name * * @return Sequence * * @throws SchemaException */ public function getSequence($name) { $name = $this->getFullQualifiedAssetName($name); if (! $this->hasSequence($name)) { throw SchemaException::sequenceDoesNotExist($name); } return $this->_sequences[$name]; } /** @return Sequence[] */ public function getSequences() { return $this->_sequences; } /** * Creates a new namespace. * * @param string $name The name of the namespace to create. * * @return Schema This schema instance. * * @throws SchemaException */ public function createNamespace($name) { $unquotedName = strtolower($this->getUnquotedAssetName($name)); if (isset($this->namespaces[$unquotedName])) { throw SchemaException::namespaceAlreadyExists($unquotedName); } $this->namespaces[$unquotedName] = $name; return $this; } /** * Creates a new table. * * @param string $name * * @return Table * * @throws SchemaException */ public function createTable($name) { $table = new Table($name); $this->_addTable($table); foreach ($this->_schemaConfig->getDefaultTableOptions() as $option => $value) { $table->addOption($option, $value); } return $table; } /** * Renames a table. * * @param string $oldName * @param string $newName * * @return Schema * * @throws SchemaException */ public function renameTable($oldName, $newName) { $table = $this->getTable($oldName); $table->_setName($newName); $this->dropTable($oldName); $this->_addTable($table); return $this; } /** * Drops a table from the schema. * * @param string $name * * @return Schema * * @throws SchemaException */ public function dropTable($name) { $name = $this->getFullQualifiedAssetName($name); $this->getTable($name); unset($this->_tables[$name]); return $this; } /** * Creates a new sequence. * * @param string $name * @param int $allocationSize * @param int $initialValue * * @return Sequence * * @throws SchemaException */ public function createSequence($name, $allocationSize = 1, $initialValue = 1) { $seq = new Sequence($name, $allocationSize, $initialValue); $this->_addSequence($seq); return $seq; } /** * @param string $name * * @return Schema */ public function dropSequence($name) { $name = $this->getFullQualifiedAssetName($name); unset($this->_sequences[$name]); return $this; } /** * Returns an array of necessary SQL queries to create the schema on the given platform. * * @return list * * @throws Exception */ public function toSql(AbstractPlatform $platform) { $builder = new CreateSchemaObjectsSQLBuilder($platform); return $builder->buildSQL($this); } /** * Return an array of necessary SQL queries to drop the schema on the given platform. * * @return list * * @throws Exception */ public function toDropSql(AbstractPlatform $platform) { $builder = new DropSchemaObjectsSQLBuilder($platform); return $builder->buildSQL($this); } /** * @deprecated * * @return string[] * * @throws SchemaException */ public function getMigrateToSql(Schema $toSchema, AbstractPlatform $platform) { $schemaDiff = (new Comparator())->compareSchemas($this, $toSchema); return $schemaDiff->toSql($platform); } /** * @deprecated * * @return string[] * * @throws SchemaException */ public function getMigrateFromSql(Schema $fromSchema, AbstractPlatform $platform) { $schemaDiff = (new Comparator())->compareSchemas($fromSchema, $this); return $schemaDiff->toSql($platform); } /** * @deprecated * * @return void */ public function visit(Visitor $visitor) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5435', 'Schema::visit() is deprecated.', ); $visitor->acceptSchema($this); if ($visitor instanceof NamespaceVisitor) { foreach ($this->namespaces as $namespace) { $visitor->acceptNamespace($namespace); } } foreach ($this->_tables as $table) { $table->visit($visitor); } foreach ($this->_sequences as $sequence) { $sequence->visit($visitor); } } /** * Cloning a Schema triggers a deep clone of all related assets. * * @return void */ public function __clone() { foreach ($this->_tables as $k => $table) { $this->_tables[$k] = clone $table; } foreach ($this->_sequences as $k => $sequence) { $this->_sequences[$k] = clone $sequence; } } } PK!&2&2$dbal/src/Schema/DB2SchemaManager.phpnu[ */ class DB2SchemaManager extends AbstractSchemaManager { /** * {@inheritDoc} */ public function listTableNames() { return $this->doListTableNames(); } /** * {@inheritDoc} */ public function listTables() { return $this->doListTables(); } /** * {@inheritDoc} * * @deprecated Use {@see introspectTable()} instead. */ public function listTableDetails($name) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5595', '%s is deprecated. Use introspectTable() instead.', __METHOD__, ); return $this->doListTableDetails($name); } /** * {@inheritDoc} */ public function listTableColumns($table, $database = null) { return $this->doListTableColumns($table, $database); } /** * {@inheritDoc} */ public function listTableIndexes($table) { return $this->doListTableIndexes($table); } /** * {@inheritDoc} */ public function listTableForeignKeys($table, $database = null) { return $this->doListTableForeignKeys($table, $database); } /** * {@inheritDoc} * * @throws Exception */ protected function _getPortableTableColumnDefinition($tableColumn) { $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); $length = null; $fixed = null; $scale = false; $precision = false; $default = null; if ($tableColumn['default'] !== null && $tableColumn['default'] !== 'NULL') { $default = $tableColumn['default']; if (preg_match('/^\'(.*)\'$/s', $default, $matches) === 1) { $default = str_replace("''", "'", $matches[1]); } } $type = $this->_platform->getDoctrineTypeMapping($tableColumn['typename']); if (isset($tableColumn['comment'])) { $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); } switch (strtolower($tableColumn['typename'])) { case 'varchar': if ($tableColumn['codepage'] === 0) { $type = Types::BINARY; } $length = $tableColumn['length']; $fixed = false; break; case 'character': if ($tableColumn['codepage'] === 0) { $type = Types::BINARY; } $length = $tableColumn['length']; $fixed = true; break; case 'clob': $length = $tableColumn['length']; break; case 'decimal': case 'double': case 'real': $scale = $tableColumn['scale']; $precision = $tableColumn['length']; break; } $options = [ 'length' => $length, 'fixed' => (bool) $fixed, 'default' => $default, 'autoincrement' => (bool) $tableColumn['autoincrement'], 'notnull' => $tableColumn['nulls'] === 'N', 'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== '' ? $tableColumn['comment'] : null, ]; if ($scale !== null && $precision !== null) { $options['scale'] = $scale; $options['precision'] = $precision; } return new Column($tableColumn['colname'], Type::getType($type), $options); } /** * {@inheritDoc} */ protected function _getPortableTableDefinition($table) { $table = array_change_key_case($table, CASE_LOWER); return $table['name']; } /** * {@inheritDoc} */ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) { foreach ($tableIndexes as &$tableIndexRow) { $tableIndexRow = array_change_key_case($tableIndexRow, CASE_LOWER); $tableIndexRow['primary'] = (bool) $tableIndexRow['primary']; } return parent::_getPortableTableIndexesList($tableIndexes, $tableName); } /** * {@inheritDoc} */ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) { return new ForeignKeyConstraint( $tableForeignKey['local_columns'], $tableForeignKey['foreign_table'], $tableForeignKey['foreign_columns'], $tableForeignKey['name'], $tableForeignKey['options'], ); } /** * {@inheritDoc} */ protected function _getPortableTableForeignKeysList($tableForeignKeys) { $foreignKeys = []; foreach ($tableForeignKeys as $tableForeignKey) { $tableForeignKey = array_change_key_case($tableForeignKey, CASE_LOWER); if (! isset($foreignKeys[$tableForeignKey['index_name']])) { $foreignKeys[$tableForeignKey['index_name']] = [ 'local_columns' => [$tableForeignKey['local_column']], 'foreign_table' => $tableForeignKey['foreign_table'], 'foreign_columns' => [$tableForeignKey['foreign_column']], 'name' => $tableForeignKey['index_name'], 'options' => [ 'onUpdate' => $tableForeignKey['on_update'], 'onDelete' => $tableForeignKey['on_delete'], ], ]; } else { $foreignKeys[$tableForeignKey['index_name']]['local_columns'][] = $tableForeignKey['local_column']; $foreignKeys[$tableForeignKey['index_name']]['foreign_columns'][] = $tableForeignKey['foreign_column']; } } return parent::_getPortableTableForeignKeysList($foreignKeys); } /** * @param string $def * * @return string|null */ protected function _getPortableForeignKeyRuleDef($def) { if ($def === 'C') { return 'CASCADE'; } if ($def === 'N') { return 'SET NULL'; } return null; } /** * {@inheritDoc} */ protected function _getPortableViewDefinition($view) { $view = array_change_key_case($view, CASE_LOWER); $sql = ''; $pos = strpos($view['text'], ' AS '); if ($pos !== false) { $sql = substr($view['text'], $pos + 4); } return new View($view['name'], $sql); } protected function normalizeName(string $name): string { $identifier = new Identifier($name); return $identifier->isQuoted() ? $identifier->getName() : strtoupper($name); } protected function selectTableNames(string $databaseName): Result { $sql = <<<'SQL' SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = 'T' AND CREATOR = ? SQL; return $this->_conn->executeQuery($sql, [$databaseName]); } protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result { $sql = 'SELECT'; if ($tableName === null) { $sql .= ' C.TABNAME AS NAME,'; } $sql .= <<<'SQL' C.COLNAME, C.TYPENAME, C.CODEPAGE, C.NULLS, C.LENGTH, C.SCALE, C.REMARKS AS COMMENT, CASE WHEN C.GENERATED = 'D' THEN 1 ELSE 0 END AS AUTOINCREMENT, C.DEFAULT FROM SYSCAT.COLUMNS C JOIN SYSCAT.TABLES AS T ON T.TABSCHEMA = C.TABSCHEMA AND T.TABNAME = C.TABNAME SQL; $conditions = ['C.TABSCHEMA = ?', "T.TYPE = 'T'"]; $params = [$databaseName]; if ($tableName !== null) { $conditions[] = 'C.TABNAME = ?'; $params[] = $tableName; } $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY C.TABNAME, C.COLNO'; return $this->_conn->executeQuery($sql, $params); } protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result { $sql = 'SELECT'; if ($tableName === null) { $sql .= ' IDX.TABNAME AS NAME,'; } $sql .= <<<'SQL' IDX.INDNAME AS KEY_NAME, IDXCOL.COLNAME AS COLUMN_NAME, CASE WHEN IDX.UNIQUERULE = 'P' THEN 1 ELSE 0 END AS PRIMARY, CASE WHEN IDX.UNIQUERULE = 'D' THEN 1 ELSE 0 END AS NON_UNIQUE FROM SYSCAT.INDEXES AS IDX JOIN SYSCAT.TABLES AS T ON IDX.TABSCHEMA = T.TABSCHEMA AND IDX.TABNAME = T.TABNAME JOIN SYSCAT.INDEXCOLUSE AS IDXCOL ON IDX.INDSCHEMA = IDXCOL.INDSCHEMA AND IDX.INDNAME = IDXCOL.INDNAME SQL; $conditions = ['IDX.TABSCHEMA = ?', "T.TYPE = 'T'"]; $params = [$databaseName]; if ($tableName !== null) { $conditions[] = 'IDX.TABNAME = ?'; $params[] = $tableName; } $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY IDX.INDNAME, IDXCOL.COLSEQ'; return $this->_conn->executeQuery($sql, $params); } protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result { $sql = 'SELECT'; if ($tableName === null) { $sql .= ' R.TABNAME AS NAME,'; } $sql .= <<<'SQL' FKCOL.COLNAME AS LOCAL_COLUMN, R.REFTABNAME AS FOREIGN_TABLE, PKCOL.COLNAME AS FOREIGN_COLUMN, R.CONSTNAME AS INDEX_NAME, CASE WHEN R.UPDATERULE = 'R' THEN 'RESTRICT' END AS ON_UPDATE, CASE WHEN R.DELETERULE = 'C' THEN 'CASCADE' WHEN R.DELETERULE = 'N' THEN 'SET NULL' WHEN R.DELETERULE = 'R' THEN 'RESTRICT' END AS ON_DELETE FROM SYSCAT.REFERENCES AS R JOIN SYSCAT.TABLES AS T ON T.TABSCHEMA = R.TABSCHEMA AND T.TABNAME = R.TABNAME JOIN SYSCAT.KEYCOLUSE AS FKCOL ON FKCOL.CONSTNAME = R.CONSTNAME AND FKCOL.TABSCHEMA = R.TABSCHEMA AND FKCOL.TABNAME = R.TABNAME JOIN SYSCAT.KEYCOLUSE AS PKCOL ON PKCOL.CONSTNAME = R.REFKEYNAME AND PKCOL.TABSCHEMA = R.REFTABSCHEMA AND PKCOL.TABNAME = R.REFTABNAME AND PKCOL.COLSEQ = FKCOL.COLSEQ SQL; $conditions = ['R.TABSCHEMA = ?', "T.TYPE = 'T'"]; $params = [$databaseName]; if ($tableName !== null) { $conditions[] = 'R.TABNAME = ?'; $params[] = $tableName; } $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY R.CONSTNAME, FKCOL.COLSEQ'; return $this->_conn->executeQuery($sql, $params); } /** * {@inheritDoc} */ protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array { $sql = 'SELECT NAME, REMARKS'; $conditions = []; $params = []; if ($tableName !== null) { $conditions[] = 'NAME = ?'; $params[] = $tableName; } $sql .= ' FROM SYSIBM.SYSTABLES'; if ($conditions !== []) { $sql .= ' WHERE ' . implode(' AND ', $conditions); } /** @var array> $metadata */ $metadata = $this->_conn->executeQuery($sql, $params) ->fetchAllAssociativeIndexed(); $tableOptions = []; foreach ($metadata as $table => $data) { $data = array_change_key_case($data, CASE_LOWER); $tableOptions[$table] = ['comment' => $data['remarks']]; } return $tableOptions; } } PK!F $dbal/src/Schema/UniqueConstraint.phpnu[ Identifier) * * @var Identifier[] */ protected $columns = []; /** * Platform specific flags. * array($flagName => true) * * @var true[] */ protected $flags = []; /** * Platform specific options. * * @var mixed[] */ private array $options; /** * @param string[] $columns * @param string[] $flags * @param mixed[] $options */ public function __construct(string $name, array $columns, array $flags = [], array $options = []) { $this->_setName($name); $this->options = $options; foreach ($columns as $column) { $this->addColumn($column); } foreach ($flags as $flag) { $this->addFlag($flag); } } /** * {@inheritDoc} */ public function getColumns() { return array_keys($this->columns); } /** * {@inheritDoc} */ public function getQuotedColumns(AbstractPlatform $platform) { $columns = []; foreach ($this->columns as $column) { $columns[] = $column->getQuotedName($platform); } return $columns; } /** @return string[] */ public function getUnquotedColumns(): array { return array_map([$this, 'trimQuotes'], $this->getColumns()); } /** * Returns platform specific flags for unique constraint. * * @return string[] */ public function getFlags(): array { return array_keys($this->flags); } /** * Adds flag for a unique constraint that translates to platform specific handling. * * @return $this * * @example $uniqueConstraint->addFlag('CLUSTERED') */ public function addFlag(string $flag): UniqueConstraint { $this->flags[strtolower($flag)] = true; return $this; } /** * Does this unique constraint have a specific flag? */ public function hasFlag(string $flag): bool { return isset($this->flags[strtolower($flag)]); } /** * Removes a flag. */ public function removeFlag(string $flag): void { unset($this->flags[strtolower($flag)]); } /** * Does this unique constraint have a specific option? */ public function hasOption(string $name): bool { return isset($this->options[strtolower($name)]); } /** @return mixed */ public function getOption(string $name) { return $this->options[strtolower($name)]; } /** @return mixed[] */ public function getOptions(): array { return $this->options; } /** * Adds a new column to the unique constraint. */ protected function addColumn(string $column): void { $this->columns[$column] = new Identifier($column); } } PK!c0oodbal/src/Schema/Table.phpnu[ [], ]; /** @var SchemaConfig|null */ protected $_schemaConfig; /** @var Index[] */ private array $implicitIndexes = []; /** * @param Column[] $columns * @param Index[] $indexes * @param UniqueConstraint[] $uniqueConstraints * @param ForeignKeyConstraint[] $fkConstraints * @param mixed[] $options * * @throws SchemaException * @throws Exception */ public function __construct( string $name, array $columns = [], array $indexes = [], array $uniqueConstraints = [], array $fkConstraints = [], array $options = [] ) { if ($name === '') { throw InvalidTableName::new($name); } $this->_setName($name); foreach ($columns as $column) { $this->_addColumn($column); } foreach ($indexes as $idx) { $this->_addIndex($idx); } foreach ($uniqueConstraints as $uniqueConstraint) { $this->_addUniqueConstraint($uniqueConstraint); } foreach ($fkConstraints as $constraint) { $this->_addForeignKeyConstraint($constraint); } $this->_options = array_merge($this->_options, $options); } /** @return void */ public function setSchemaConfig(SchemaConfig $schemaConfig) { $this->_schemaConfig = $schemaConfig; } /** @return int */ protected function _getMaxIdentifierLength() { if ($this->_schemaConfig instanceof SchemaConfig) { return $this->_schemaConfig->getMaxIdentifierLength(); } return 63; } /** * Sets the Primary Key. * * @param string[] $columnNames * @param string|false $indexName * * @return self * * @throws SchemaException */ public function setPrimaryKey(array $columnNames, $indexName = false) { if ($indexName === false) { $indexName = 'primary'; } $this->_addIndex($this->_createIndex($columnNames, $indexName, true, true)); foreach ($columnNames as $columnName) { $column = $this->getColumn($columnName); $column->setNotnull(true); } return $this; } /** * @param string[] $columnNames * @param string[] $flags * @param mixed[] $options * * @return self * * @throws SchemaException */ public function addIndex(array $columnNames, ?string $indexName = null, array $flags = [], array $options = []) { $indexName ??= $this->_generateIdentifierName( array_merge([$this->getName()], $columnNames), 'idx', $this->_getMaxIdentifierLength(), ); return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options)); } /** * @param string[] $columnNames * @param string[] $flags * @param mixed[] $options * * @return self */ public function addUniqueConstraint( array $columnNames, ?string $indexName = null, array $flags = [], array $options = [] ): Table { $indexName ??= $this->_generateIdentifierName( array_merge([$this->getName()], $columnNames), 'uniq', $this->_getMaxIdentifierLength(), ); return $this->_addUniqueConstraint($this->_createUniqueConstraint($columnNames, $indexName, $flags, $options)); } /** * Drops the primary key from this table. * * @return void * * @throws SchemaException */ public function dropPrimaryKey() { if ($this->_primaryKeyName === null) { return; } $this->dropIndex($this->_primaryKeyName); $this->_primaryKeyName = null; } /** * Drops an index from this table. * * @param string $name The index name. * * @return void * * @throws SchemaException If the index does not exist. */ public function dropIndex($name) { $name = $this->normalizeIdentifier($name); if (! $this->hasIndex($name)) { throw SchemaException::indexDoesNotExist($name, $this->_name); } unset($this->_indexes[$name]); } /** * @param string[] $columnNames * @param string|null $indexName * @param mixed[] $options * * @return self * * @throws SchemaException */ public function addUniqueIndex(array $columnNames, $indexName = null, array $options = []) { $indexName ??= $this->_generateIdentifierName( array_merge([$this->getName()], $columnNames), 'uniq', $this->_getMaxIdentifierLength(), ); return $this->_addIndex($this->_createIndex($columnNames, $indexName, true, false, [], $options)); } /** * Renames an index. * * @param string $oldName The name of the index to rename from. * @param string|null $newName The name of the index to rename to. * If null is given, the index name will be auto-generated. * * @return self This table instance. * * @throws SchemaException If no index exists for the given current name * or if an index with the given new name already exists on this table. */ public function renameIndex($oldName, $newName = null) { $oldName = $this->normalizeIdentifier($oldName); $normalizedNewName = $this->normalizeIdentifier($newName); if ($oldName === $normalizedNewName) { return $this; } if (! $this->hasIndex($oldName)) { throw SchemaException::indexDoesNotExist($oldName, $this->_name); } if ($this->hasIndex($normalizedNewName)) { throw SchemaException::indexAlreadyExists($normalizedNewName, $this->_name); } $oldIndex = $this->_indexes[$oldName]; if ($oldIndex->isPrimary()) { $this->dropPrimaryKey(); return $this->setPrimaryKey($oldIndex->getColumns(), $newName ?? false); } unset($this->_indexes[$oldName]); if ($oldIndex->isUnique()) { return $this->addUniqueIndex($oldIndex->getColumns(), $newName, $oldIndex->getOptions()); } return $this->addIndex($oldIndex->getColumns(), $newName, $oldIndex->getFlags(), $oldIndex->getOptions()); } /** * Checks if an index begins in the order of the given columns. * * @param string[] $columnNames * * @return bool */ public function columnsAreIndexed(array $columnNames) { foreach ($this->getIndexes() as $index) { if ($index->spansColumns($columnNames)) { return true; } } return false; } /** * @param string[] $columnNames * @param string $indexName * @param bool $isUnique * @param bool $isPrimary * @param string[] $flags * @param mixed[] $options * * @throws SchemaException */ private function _createIndex( array $columnNames, $indexName, $isUnique, $isPrimary, array $flags = [], array $options = [] ): Index { if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName)) === 1) { throw SchemaException::indexNameInvalid($indexName); } foreach ($columnNames as $columnName) { if (! $this->hasColumn($columnName)) { throw SchemaException::columnDoesNotExist($columnName, $this->_name); } } return new Index($indexName, $columnNames, $isUnique, $isPrimary, $flags, $options); } /** * @param string $name * @param string $typeName * @param mixed[] $options * * @return Column * * @throws SchemaException */ public function addColumn($name, $typeName, array $options = []) { $column = new Column($name, Type::getType($typeName), $options); $this->_addColumn($column); return $column; } /** * Change Column Details. * * @deprecated Use {@link modifyColumn()} instead. * * @param string $name * @param mixed[] $options * * @return self * * @throws SchemaException */ public function changeColumn($name, array $options) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5747', '%s is deprecated. Use modifyColumn() instead.', __METHOD__, ); return $this->modifyColumn($name, $options); } /** * @param string $name * @param mixed[] $options * * @return self * * @throws SchemaException */ public function modifyColumn($name, array $options) { $column = $this->getColumn($name); $column->setOptions($options); return $this; } /** * Drops a Column from the Table. * * @param string $name * * @return self */ public function dropColumn($name) { $name = $this->normalizeIdentifier($name); unset($this->_columns[$name]); return $this; } /** * Adds a foreign key constraint. * * Name is inferred from the local columns. * * @param Table|string $foreignTable Table schema instance or table name * @param string[] $localColumnNames * @param string[] $foreignColumnNames * @param mixed[] $options * @param string|null $name * * @return self * * @throws SchemaException */ public function addForeignKeyConstraint( $foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [], $name = null ) { $name ??= $this->_generateIdentifierName( array_merge([$this->getName()], $localColumnNames), 'fk', $this->_getMaxIdentifierLength(), ); if ($foreignTable instanceof Table) { foreach ($foreignColumnNames as $columnName) { if (! $foreignTable->hasColumn($columnName)) { throw SchemaException::columnDoesNotExist($columnName, $foreignTable->getName()); } } } foreach ($localColumnNames as $columnName) { if (! $this->hasColumn($columnName)) { throw SchemaException::columnDoesNotExist($columnName, $this->_name); } } $constraint = new ForeignKeyConstraint( $localColumnNames, $foreignTable, $foreignColumnNames, $name, $options, ); return $this->_addForeignKeyConstraint($constraint); } /** * @param string $name * @param mixed $value * * @return self */ public function addOption($name, $value) { $this->_options[$name] = $value; return $this; } /** * @return void * * @throws SchemaException */ protected function _addColumn(Column $column) { $columnName = $column->getName(); $columnName = $this->normalizeIdentifier($columnName); if (isset($this->_columns[$columnName])) { throw SchemaException::columnAlreadyExists($this->getName(), $columnName); } $this->_columns[$columnName] = $column; } /** * Adds an index to the table. * * @return self * * @throws SchemaException */ protected function _addIndex(Index $indexCandidate) { $indexName = $indexCandidate->getName(); $indexName = $this->normalizeIdentifier($indexName); $replacedImplicitIndexes = []; foreach ($this->implicitIndexes as $name => $implicitIndex) { if (! $implicitIndex->isFulfilledBy($indexCandidate) || ! isset($this->_indexes[$name])) { continue; } $replacedImplicitIndexes[] = $name; } if ( (isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) || ($this->_primaryKeyName !== null && $indexCandidate->isPrimary()) ) { throw SchemaException::indexAlreadyExists($indexName, $this->_name); } foreach ($replacedImplicitIndexes as $name) { unset($this->_indexes[$name], $this->implicitIndexes[$name]); } if ($indexCandidate->isPrimary()) { $this->_primaryKeyName = $indexName; } $this->_indexes[$indexName] = $indexCandidate; return $this; } /** @return self */ protected function _addUniqueConstraint(UniqueConstraint $constraint): Table { $mergedNames = array_merge([$this->getName()], $constraint->getColumns()); $name = strlen($constraint->getName()) > 0 ? $constraint->getName() : $this->_generateIdentifierName($mergedNames, 'fk', $this->_getMaxIdentifierLength()); $name = $this->normalizeIdentifier($name); $this->uniqueConstraints[$name] = $constraint; // If there is already an index that fulfills this requirements drop the request. In the case of __construct // calling this method during hydration from schema-details all the explicitly added indexes lead to duplicates. // This creates computation overhead in this case, however no duplicate indexes are ever added (column based). $indexName = $this->_generateIdentifierName($mergedNames, 'idx', $this->_getMaxIdentifierLength()); $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, true, false); foreach ($this->_indexes as $existingIndex) { if ($indexCandidate->isFulfilledBy($existingIndex)) { return $this; } } $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate; return $this; } /** @return self */ protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint) { $constraint->setLocalTable($this); if (strlen($constraint->getName()) > 0) { $name = $constraint->getName(); } else { $name = $this->_generateIdentifierName( array_merge([$this->getName()], $constraint->getLocalColumns()), 'fk', $this->_getMaxIdentifierLength(), ); } $name = $this->normalizeIdentifier($name); $this->_fkConstraints[$name] = $constraint; /* Add an implicit index (defined by the DBAL) on the foreign key columns. If there is already a user-defined index that fulfills these requirements drop the request. In the case of __construct() calling this method during hydration from schema-details, all the explicitly added indexes lead to duplicates. This creates computation overhead in this case, however no duplicate indexes are ever added (based on columns). */ $indexName = $this->_generateIdentifierName( array_merge([$this->getName()], $constraint->getColumns()), 'idx', $this->_getMaxIdentifierLength(), ); $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false); foreach ($this->_indexes as $existingIndex) { if ($indexCandidate->isFulfilledBy($existingIndex)) { return $this; } } $this->_addIndex($indexCandidate); $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate; return $this; } /** * Returns whether this table has a foreign key constraint with the given name. * * @param string $name * * @return bool */ public function hasForeignKey($name) { $name = $this->normalizeIdentifier($name); return isset($this->_fkConstraints[$name]); } /** * Returns the foreign key constraint with the given name. * * @param string $name The constraint name. * * @return ForeignKeyConstraint * * @throws SchemaException If the foreign key does not exist. */ public function getForeignKey($name) { $name = $this->normalizeIdentifier($name); if (! $this->hasForeignKey($name)) { throw SchemaException::foreignKeyDoesNotExist($name, $this->_name); } return $this->_fkConstraints[$name]; } /** * Removes the foreign key constraint with the given name. * * @param string $name The constraint name. * * @return void * * @throws SchemaException */ public function removeForeignKey($name) { $name = $this->normalizeIdentifier($name); if (! $this->hasForeignKey($name)) { throw SchemaException::foreignKeyDoesNotExist($name, $this->_name); } unset($this->_fkConstraints[$name]); } /** * Returns whether this table has a unique constraint with the given name. */ public function hasUniqueConstraint(string $name): bool { $name = $this->normalizeIdentifier($name); return isset($this->uniqueConstraints[$name]); } /** * Returns the unique constraint with the given name. * * @throws SchemaException If the unique constraint does not exist. */ public function getUniqueConstraint(string $name): UniqueConstraint { $name = $this->normalizeIdentifier($name); if (! $this->hasUniqueConstraint($name)) { throw SchemaException::uniqueConstraintDoesNotExist($name, $this->_name); } return $this->uniqueConstraints[$name]; } /** * Removes the unique constraint with the given name. * * @throws SchemaException If the unique constraint does not exist. */ public function removeUniqueConstraint(string $name): void { $name = $this->normalizeIdentifier($name); if (! $this->hasUniqueConstraint($name)) { throw SchemaException::uniqueConstraintDoesNotExist($name, $this->_name); } unset($this->uniqueConstraints[$name]); } /** * Returns ordered list of columns (primary keys are first, then foreign keys, then the rest) * * @return Column[] */ public function getColumns() { $primaryKeyColumns = $this->getPrimaryKey() !== null ? $this->getPrimaryKeyColumns() : []; $foreignKeyColumns = $this->getForeignKeyColumns(); $remainderColumns = $this->filterColumns( array_merge(array_keys($primaryKeyColumns), array_keys($foreignKeyColumns)), true, ); return array_merge($primaryKeyColumns, $foreignKeyColumns, $remainderColumns); } /** * Returns the foreign key columns * * @deprecated Use {@see getForeignKey()} and {@see ForeignKeyConstraint::getLocalColumns()} instead. * * @return Column[] */ public function getForeignKeyColumns() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5731', '%s is deprecated. Use getForeignKey() and ForeignKeyConstraint::getLocalColumns() instead.', __METHOD__, ); $foreignKeyColumns = []; foreach ($this->getForeignKeys() as $foreignKey) { $foreignKeyColumns = array_merge($foreignKeyColumns, $foreignKey->getLocalColumns()); } return $this->filterColumns($foreignKeyColumns); } /** * Returns only columns that have specified names * * @param string[] $columnNames * * @return Column[] */ private function filterColumns(array $columnNames, bool $reverse = false): array { return array_filter($this->_columns, static function (string $columnName) use ($columnNames, $reverse): bool { return in_array($columnName, $columnNames, true) !== $reverse; }, ARRAY_FILTER_USE_KEY); } /** * Returns whether this table has a Column with the given name. * * @param string $name The column name. * * @return bool */ public function hasColumn($name) { $name = $this->normalizeIdentifier($name); return isset($this->_columns[$name]); } /** * Returns the Column with the given name. * * @param string $name The column name. * * @return Column * * @throws SchemaException If the column does not exist. */ public function getColumn($name) { $name = $this->normalizeIdentifier($name); if (! $this->hasColumn($name)) { throw SchemaException::columnDoesNotExist($name, $this->_name); } return $this->_columns[$name]; } /** * Returns the primary key. * * @return Index|null The primary key, or null if this Table has no primary key. */ public function getPrimaryKey() { if ($this->_primaryKeyName !== null) { return $this->getIndex($this->_primaryKeyName); } return null; } /** * Returns the primary key columns. * * @deprecated Use {@see getPrimaryKey()} and {@see Index::getColumns()} instead. * * @return Column[] * * @throws Exception */ public function getPrimaryKeyColumns() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5731', '%s is deprecated. Use getPrimaryKey() and Index::getColumns() instead.', __METHOD__, ); $primaryKey = $this->getPrimaryKey(); if ($primaryKey === null) { throw new Exception('Table ' . $this->getName() . ' has no primary key.'); } return $this->filterColumns($primaryKey->getColumns()); } /** * Returns whether this table has a primary key. * * @deprecated Use {@see getPrimaryKey()} instead. * * @return bool */ public function hasPrimaryKey() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5731', '%s is deprecated. Use getPrimaryKey() instead.', __METHOD__, ); return $this->_primaryKeyName !== null && $this->hasIndex($this->_primaryKeyName); } /** * Returns whether this table has an Index with the given name. * * @param string $name The index name. * * @return bool */ public function hasIndex($name) { $name = $this->normalizeIdentifier($name); return isset($this->_indexes[$name]); } /** * Returns the Index with the given name. * * @param string $name The index name. * * @return Index * * @throws SchemaException If the index does not exist. */ public function getIndex($name) { $name = $this->normalizeIdentifier($name); if (! $this->hasIndex($name)) { throw SchemaException::indexDoesNotExist($name, $this->_name); } return $this->_indexes[$name]; } /** @return Index[] */ public function getIndexes() { return $this->_indexes; } /** * Returns the unique constraints. * * @return UniqueConstraint[] */ public function getUniqueConstraints(): array { return $this->uniqueConstraints; } /** * Returns the foreign key constraints. * * @return ForeignKeyConstraint[] */ public function getForeignKeys() { return $this->_fkConstraints; } /** * @param string $name * * @return bool */ public function hasOption($name) { return isset($this->_options[$name]); } /** * @param string $name * * @return mixed */ public function getOption($name) { return $this->_options[$name]; } /** @return mixed[] */ public function getOptions() { return $this->_options; } /** * @deprecated * * @return void * * @throws SchemaException */ public function visit(Visitor $visitor) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5435', 'Table::visit() is deprecated.', ); $visitor->acceptTable($this); foreach ($this->getColumns() as $column) { $visitor->acceptColumn($this, $column); } foreach ($this->getIndexes() as $index) { $visitor->acceptIndex($this, $index); } foreach ($this->getForeignKeys() as $constraint) { $visitor->acceptForeignKey($this, $constraint); } } /** * Clone of a Table triggers a deep clone of all affected assets. * * @return void */ public function __clone() { foreach ($this->_columns as $k => $column) { $this->_columns[$k] = clone $column; } foreach ($this->_indexes as $k => $index) { $this->_indexes[$k] = clone $index; } foreach ($this->_fkConstraints as $k => $fk) { $this->_fkConstraints[$k] = clone $fk; $this->_fkConstraints[$k]->setLocalTable($this); } } /** * @param string[] $columnNames * @param string[] $flags * @param mixed[] $options * * @throws SchemaException */ private function _createUniqueConstraint( array $columnNames, string $indexName, array $flags = [], array $options = [] ): UniqueConstraint { if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName)) === 1) { throw SchemaException::indexNameInvalid($indexName); } foreach ($columnNames as $columnName) { if (! $this->hasColumn($columnName)) { throw SchemaException::columnDoesNotExist($columnName, $this->_name); } } return new UniqueConstraint($indexName, $columnNames, $flags, $options); } /** * Normalizes a given identifier. * * Trims quotes and lowercases the given identifier. * * @return string The normalized identifier. */ private function normalizeIdentifier(?string $identifier): string { if ($identifier === null) { return ''; } return $this->trimQuotes(strtolower($identifier)); } public function setComment(?string $comment): self { // For keeping backward compatibility with MySQL in previous releases, table comments are stored as options. $this->addOption('comment', $comment); return $this; } public function getComment(): ?string { return $this->_options['comment'] ?? null; } } PK! ?a7BB/dbal/src/Schema/DefaultSchemaManagerFactory.phpnu[getDatabasePlatform()->createSchemaManager($connection); } } PK!Xtdbal/src/Schema/View.phpnu[_setName($name); $this->sql = $sql; } /** @return string */ public function getSql() { return $this->sql; } } PK!2,,(dbal/src/Schema/ForeignKeyConstraint.phpnu[ Identifier) * * @var Identifier[] */ protected $_localColumnNames; /** * Table or asset identifier instance of the referenced table name the foreign key constraint is associated with. * * @var Table|Identifier */ protected $_foreignTableName; /** * Asset identifier instances of the referenced table column names the foreign key constraint is associated with. * array($columnName => Identifier) * * @var Identifier[] */ protected $_foreignColumnNames; /** * Options associated with the foreign key constraint. * * @var mixed[] */ protected $_options; /** * Initializes the foreign key constraint. * * @param string[] $localColumnNames Names of the referencing table columns. * @param Table|string $foreignTableName Referenced table. * @param string[] $foreignColumnNames Names of the referenced table columns. * @param string|null $name Name of the foreign key constraint. * @param mixed[] $options Options associated with the foreign key constraint. */ public function __construct( array $localColumnNames, $foreignTableName, array $foreignColumnNames, $name = null, array $options = [] ) { if ($name !== null) { $this->_setName($name); } $this->_localColumnNames = $this->createIdentifierMap($localColumnNames); if ($foreignTableName instanceof Table) { $this->_foreignTableName = $foreignTableName; } else { $this->_foreignTableName = new Identifier($foreignTableName); } $this->_foreignColumnNames = $this->createIdentifierMap($foreignColumnNames); $this->_options = $options; } /** * @param string[] $names * * @return Identifier[] */ private function createIdentifierMap(array $names): array { $identifiers = []; foreach ($names as $name) { $identifiers[$name] = new Identifier($name); } return $identifiers; } /** * Returns the name of the referencing table * the foreign key constraint is associated with. * * @deprecated Use the table that contains the foreign key as part of its {@see Table::$_fkConstraints} instead. * * @return string */ public function getLocalTableName() { return $this->_localTable->getName(); } /** * Sets the Table instance of the referencing table * the foreign key constraint is associated with. * * @deprecated Use the table that contains the foreign key as part of its {@see Table::$_fkConstraints} instead. * * @param Table $table Instance of the referencing table. * * @return void */ public function setLocalTable(Table $table) { $this->_localTable = $table; } /** * @deprecated Use the table that contains the foreign key as part of its {@see Table::$_fkConstraints} instead. * * @return Table */ public function getLocalTable() { return $this->_localTable; } /** * Returns the names of the referencing table columns * the foreign key constraint is associated with. * * @return string[] */ public function getLocalColumns() { return array_keys($this->_localColumnNames); } /** * Returns the quoted representation of the referencing table column names * the foreign key constraint is associated with. * * But only if they were defined with one or the referencing table column name * is a keyword reserved by the platform. * Otherwise the plain unquoted value as inserted is returned. * * @param AbstractPlatform $platform The platform to use for quotation. * * @return string[] */ public function getQuotedLocalColumns(AbstractPlatform $platform) { $columns = []; foreach ($this->_localColumnNames as $column) { $columns[] = $column->getQuotedName($platform); } return $columns; } /** * Returns unquoted representation of local table column names for comparison with other FK * * @return string[] */ public function getUnquotedLocalColumns() { return array_map([$this, 'trimQuotes'], $this->getLocalColumns()); } /** * Returns unquoted representation of foreign table column names for comparison with other FK * * @return string[] */ public function getUnquotedForeignColumns() { return array_map([$this, 'trimQuotes'], $this->getForeignColumns()); } /** * {@inheritDoc} * * @deprecated Use {@see getLocalColumns()} instead. * * @see getLocalColumns */ public function getColumns() { return $this->getLocalColumns(); } /** * Returns the quoted representation of the referencing table column names * the foreign key constraint is associated with. * * But only if they were defined with one or the referencing table column name * is a keyword reserved by the platform. * Otherwise the plain unquoted value as inserted is returned. * * @deprecated Use {@see getQuotedLocalColumns()} instead. * * @see getQuotedLocalColumns * * @param AbstractPlatform $platform The platform to use for quotation. * * @return string[] */ public function getQuotedColumns(AbstractPlatform $platform) { return $this->getQuotedLocalColumns($platform); } /** * Returns the name of the referenced table * the foreign key constraint is associated with. * * @return string */ public function getForeignTableName() { return $this->_foreignTableName->getName(); } /** * Returns the non-schema qualified foreign table name. * * @return string */ public function getUnqualifiedForeignTableName() { $name = $this->_foreignTableName->getName(); $position = strrpos($name, '.'); if ($position !== false) { $name = substr($name, $position + 1); } return strtolower($name); } /** * Returns the quoted representation of the referenced table name * the foreign key constraint is associated with. * * But only if it was defined with one or the referenced table name * is a keyword reserved by the platform. * Otherwise the plain unquoted value as inserted is returned. * * @param AbstractPlatform $platform The platform to use for quotation. * * @return string */ public function getQuotedForeignTableName(AbstractPlatform $platform) { return $this->_foreignTableName->getQuotedName($platform); } /** * Returns the names of the referenced table columns * the foreign key constraint is associated with. * * @return string[] */ public function getForeignColumns() { return array_keys($this->_foreignColumnNames); } /** * Returns the quoted representation of the referenced table column names * the foreign key constraint is associated with. * * But only if they were defined with one or the referenced table column name * is a keyword reserved by the platform. * Otherwise the plain unquoted value as inserted is returned. * * @param AbstractPlatform $platform The platform to use for quotation. * * @return string[] */ public function getQuotedForeignColumns(AbstractPlatform $platform) { $columns = []; foreach ($this->_foreignColumnNames as $column) { $columns[] = $column->getQuotedName($platform); } return $columns; } /** * Returns whether or not a given option * is associated with the foreign key constraint. * * @param string $name Name of the option to check. * * @return bool */ public function hasOption($name) { return isset($this->_options[$name]); } /** * Returns an option associated with the foreign key constraint. * * @param string $name Name of the option the foreign key constraint is associated with. * * @return mixed */ public function getOption($name) { return $this->_options[$name]; } /** * Returns the options associated with the foreign key constraint. * * @return mixed[] */ public function getOptions() { return $this->_options; } /** * Returns the referential action for UPDATE operations * on the referenced table the foreign key constraint is associated with. * * @return string|null */ public function onUpdate() { return $this->onEvent('onUpdate'); } /** * Returns the referential action for DELETE operations * on the referenced table the foreign key constraint is associated with. * * @return string|null */ public function onDelete() { return $this->onEvent('onDelete'); } /** * Returns the referential action for a given database operation * on the referenced table the foreign key constraint is associated with. * * @param string $event Name of the database operation/event to return the referential action for. */ private function onEvent($event): ?string { if (isset($this->_options[$event])) { $onEvent = strtoupper($this->_options[$event]); if ($onEvent !== 'NO ACTION' && $onEvent !== 'RESTRICT') { return $onEvent; } } return null; } /** * Checks whether this foreign key constraint intersects the given index columns. * * Returns `true` if at least one of this foreign key's local columns * matches one of the given index's columns, `false` otherwise. * * @param Index $index The index to be checked against. * * @return bool */ public function intersectsIndexColumns(Index $index) { foreach ($index->getColumns() as $indexColumn) { foreach ($this->_localColumnNames as $localColumn) { if (strtolower($indexColumn) === strtolower($localColumn->getName())) { return true; } } } return false; } } PK!d dbal/src/Schema/SchemaConfig.phpnu[hasExplicitForeignKeyIndexes; } /** * @deprecated * * @param bool $flag * * @return void */ public function setExplicitForeignKeyIndexes($flag) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4822', 'SchemaConfig::setExplicitForeignKeyIndexes() is deprecated.', ); $this->hasExplicitForeignKeyIndexes = (bool) $flag; } /** * @param int $length * * @return void */ public function setMaxIdentifierLength($length) { $this->maxIdentifierLength = (int) $length; } /** @return int */ public function getMaxIdentifierLength() { return $this->maxIdentifierLength; } /** * Gets the default namespace of schema objects. * * @return string|null */ public function getName() { return $this->name; } /** * Sets the default namespace name of schema objects. * * @param string $name The value to set. * * @return void */ public function setName($name) { $this->name = $name; } /** * Gets the default options that are passed to Table instances created with * Schema#createTable(). * * @return mixed[] */ public function getDefaultTableOptions() { return $this->defaultTableOptions; } /** * @param mixed[] $defaultTableOptions * * @return void */ public function setDefaultTableOptions(array $defaultTableOptions) { $this->defaultTableOptions = $defaultTableOptions; } } PK!槾!dbal/src/Schema/AbstractAsset.phpnu[ Table($tableName)); if you want to rename the table, you have to make sure */ abstract class AbstractAsset { /** @var string */ protected $_name = ''; /** * Namespace of the asset. If none isset the default namespace is assumed. * * @var string|null */ protected $_namespace; /** @var bool */ protected $_quoted = false; /** * Sets the name of this asset. * * @param string $name * * @return void */ protected function _setName($name) { if ($this->isIdentifierQuoted($name)) { $this->_quoted = true; $name = $this->trimQuotes($name); } if (strpos($name, '.') !== false) { $parts = explode('.', $name); $this->_namespace = $parts[0]; $name = $parts[1]; } $this->_name = $name; } /** * Is this asset in the default namespace? * * @param string $defaultNamespaceName * * @return bool */ public function isInDefaultNamespace($defaultNamespaceName) { return $this->_namespace === $defaultNamespaceName || $this->_namespace === null; } /** * Gets the namespace name of this asset. * * If NULL is returned this means the default namespace is used. * * @return string|null */ public function getNamespaceName() { return $this->_namespace; } /** * The shortest name is stripped of the default namespace. All other * namespaced elements are returned as full-qualified names. * * @param string|null $defaultNamespaceName * * @return string */ public function getShortestName($defaultNamespaceName) { $shortestName = $this->getName(); if ($this->_namespace === $defaultNamespaceName) { $shortestName = $this->_name; } return strtolower($shortestName); } /** * The normalized name is full-qualified and lower-cased. Lower-casing is * actually wrong, but we have to do it to keep our sanity. If you are * using database objects that only differentiate in the casing (FOO vs * Foo) then you will NOT be able to use Doctrine Schema abstraction. * * Every non-namespaced element is prefixed with the default namespace * name which is passed as argument to this method. * * @deprecated Use {@see getNamespaceName()} and {@see getName()} instead. * * @param string $defaultNamespaceName * * @return string */ public function getFullQualifiedName($defaultNamespaceName) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4814', 'AbstractAsset::getFullQualifiedName() is deprecated.' . ' Use AbstractAsset::getNamespaceName() and ::getName() instead.', ); $name = $this->getName(); if ($this->_namespace === null) { $name = $defaultNamespaceName . '.' . $name; } return strtolower($name); } /** * Checks if this asset's name is quoted. * * @return bool */ public function isQuoted() { return $this->_quoted; } /** * Checks if this identifier is quoted. * * @param string $identifier * * @return bool */ protected function isIdentifierQuoted($identifier) { return isset($identifier[0]) && ($identifier[0] === '`' || $identifier[0] === '"' || $identifier[0] === '['); } /** * Trim quotes from the identifier. * * @param string $identifier * * @return string */ protected function trimQuotes($identifier) { return str_replace(['`', '"', '[', ']'], '', $identifier); } /** * Returns the name of this schema asset. * * @return string */ public function getName() { if ($this->_namespace !== null) { return $this->_namespace . '.' . $this->_name; } return $this->_name; } /** * Gets the quoted representation of this asset but only if it was defined with one. Otherwise * return the plain unquoted value as inserted. * * @return string */ public function getQuotedName(AbstractPlatform $platform) { $keywords = $platform->getReservedKeywordsList(); $parts = explode('.', $this->getName()); foreach ($parts as $k => $v) { $parts[$k] = $this->_quoted || $keywords->isKeyword($v) ? $platform->quoteIdentifier($v) : $v; } return implode('.', $parts); } /** * Generates an identifier from a list of column names obeying a certain string length. * * This is especially important for Oracle, since it does not allow identifiers larger than 30 chars, * however building idents automatically for foreign keys, composite keys or such can easily create * very long names. * * @param string[] $columnNames * @param string $prefix * @param int $maxSize * * @return string */ protected function _generateIdentifierName($columnNames, $prefix = '', $maxSize = 30) { $hash = implode('', array_map(static function ($column): string { return dechex(crc32($column)); }, $columnNames)); return strtoupper(substr($prefix . '_' . $hash, 0, $maxSize)); } } PK!J1dbal/src/Schema/Identifier.phpnu[_setName($identifier); if (! $quote || $this->_quoted) { return; } $this->_setName('"' . $this->getName() . '"'); } } PK! x 2dbal/src/Schema/Visitor/DropSchemaSqlCollector.phpnu[platform = $platform; $this->initializeQueries(); } /** * {@inheritDoc} */ public function acceptTable(Table $table) { $this->tables->attach($table); } /** * {@inheritDoc} */ public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) { if (strlen($fkConstraint->getName()) === 0) { throw SchemaException::namedForeignKeyRequired($localTable, $fkConstraint); } $this->constraints->attach($fkConstraint, $localTable); } /** * {@inheritDoc} */ public function acceptSequence(Sequence $sequence) { $this->sequences->attach($sequence); } /** @return void */ public function clearQueries() { $this->initializeQueries(); } /** @return string[] */ public function getQueries() { $sql = []; foreach ($this->constraints as $fkConstraint) { assert($fkConstraint instanceof ForeignKeyConstraint); $localTable = $this->constraints[$fkConstraint]; $sql[] = $this->platform->getDropForeignKeySQL( $fkConstraint->getQuotedName($this->platform), $localTable->getQuotedName($this->platform), ); } foreach ($this->sequences as $sequence) { assert($sequence instanceof Sequence); $sql[] = $this->platform->getDropSequenceSQL($sequence->getQuotedName($this->platform)); } foreach ($this->tables as $table) { assert($table instanceof Table); $sql[] = $this->platform->getDropTableSQL($table->getQuotedName($this->platform)); } return $sql; } private function initializeQueries(): void { $this->constraints = new SplObjectStorage(); $this->sequences = new SplObjectStorage(); $this->tables = new SplObjectStorage(); } } PK!\Ӽ 4dbal/src/Schema/Visitor/CreateSchemaSqlCollector.phpnu[platform = $platform; } /** * {@inheritDoc} */ public function acceptNamespace($namespaceName) { if (! $this->platform->supportsSchemas()) { return; } $this->createNamespaceQueries[] = $this->platform->getCreateSchemaSQL($namespaceName); } /** * {@inheritDoc} */ public function acceptTable(Table $table) { $this->createTableQueries = array_merge($this->createTableQueries, $this->platform->getCreateTableSQL($table)); } /** * {@inheritDoc} */ public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) { if (! $this->platform->supportsForeignKeyConstraints()) { return; } $this->createFkConstraintQueries[] = $this->platform->getCreateForeignKeySQL($fkConstraint, $localTable); } /** * {@inheritDoc} */ public function acceptSequence(Sequence $sequence) { $this->createSequenceQueries[] = $this->platform->getCreateSequenceSQL($sequence); } /** @return void */ public function resetQueries() { $this->createNamespaceQueries = []; $this->createTableQueries = []; $this->createSequenceQueries = []; $this->createFkConstraintQueries = []; } /** * Gets all queries collected so far. * * @return string[] */ public function getQueries() { return array_merge( $this->createNamespaceQueries, $this->createSequenceQueries, $this->createTableQueries, $this->createFkConstraintQueries, ); } } PK!J+dbal/src/Schema/Visitor/AbstractVisitor.phpnu[output .= $this->createNodeRelation( $fkConstraint->getLocalTableName() . ':col' . current($fkConstraint->getLocalColumns()) . ':se', $fkConstraint->getForeignTableName() . ':col' . current($fkConstraint->getForeignColumns()) . ':se', [ 'dir' => 'back', 'arrowtail' => 'dot', 'arrowhead' => 'normal', ], ); } /** * {@inheritDoc} */ public function acceptSchema(Schema $schema) { $this->output = 'digraph "' . $schema->getName() . '" {' . "\n"; $this->output .= 'splines = true;' . "\n"; $this->output .= 'overlap = false;' . "\n"; $this->output .= 'outputorder=edgesfirst;' . "\n"; $this->output .= 'mindist = 0.6;' . "\n"; $this->output .= 'sep = .2;' . "\n"; } /** * {@inheritDoc} */ public function acceptTable(Table $table) { $this->output .= $this->createNode( $table->getName(), [ 'label' => $this->createTableLabel($table), 'shape' => 'plaintext', ], ); } private function createTableLabel(Table $table): string { // Start the table $label = '<
'; // The title $label .= ''; // The attributes block foreach ($table->getColumns() as $column) { $columnLabel = $column->getName(); $label .= '' . '' . '' . ''; } // End the table $label .= '
' . '' . $table->getName() . '
' . '' . $columnLabel . '' . '' . '' . strtolower($column->getType()->getName()) . '' . ''; $primaryKey = $table->getPrimaryKey(); if ($primaryKey !== null && in_array($column->getName(), $primaryKey->getColumns(), true)) { $label .= "\xe2\x9c\xb7"; } $label .= '
>'; return $label; } /** * @param string $name * @param string[] $options */ private function createNode($name, $options): string { $node = $name . ' ['; foreach ($options as $key => $value) { $node .= $key . '=' . $value . ' '; } $node .= "]\n"; return $node; } /** * @param string $node1 * @param string $node2 * @param string[] $options */ private function createNodeRelation($node1, $node2, $options): string { $relation = $node1 . ' -> ' . $node2 . ' ['; foreach ($options as $key => $value) { $relation .= $key . '=' . $value . ' '; } $relation .= "]\n"; return $relation; } /** * Get Graphviz Output * * @return string */ public function getOutput() { return $this->output . '}'; } /** * Writes dot language output to a file. This should usually be a *.dot file. * * You have to convert the output into a viewable format. For example use "neato" on linux systems * and execute: * * neato -Tpng -o er.png er.dot * * @param string $filename * * @return void */ public function write($filename) { file_put_contents($filename, $this->getOutput()); } } PK!?  #dbal/src/Schema/Visitor/Visitor.phpnu[schema = $schema; } /** * {@inheritDoc} */ public function acceptTable(Table $table) { if ($this->schema === null) { return; } if ($table->isInDefaultNamespace($this->schema->getName())) { return; } $this->schema->dropTable($table->getName()); } /** * {@inheritDoc} */ public function acceptSequence(Sequence $sequence) { if ($this->schema === null) { return; } if ($sequence->isInDefaultNamespace($this->schema->getName())) { return; } $this->schema->dropSequence($sequence->getName()); } /** * {@inheritDoc} */ public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) { if ($this->schema === null) { return; } // The table may already be deleted in a previous // RemoveNamespacedAssets#acceptTable call. Removing Foreign keys that // point to nowhere. if (! $this->schema->hasTable($fkConstraint->getForeignTableName())) { $localTable->removeForeignKey($fkConstraint->getName()); return; } $foreignTable = $this->schema->getTable($fkConstraint->getForeignTableName()); if ($foreignTable->isInDefaultNamespace($this->schema->getName())) { return; } $localTable->removeForeignKey($fkConstraint->getName()); } } PK!ڟePq&q&dbal/src/Schema/TableDiff.phpnu[ $addedColumns * @param array $modifiedColumns * @param array $droppedColumns * @param array $addedIndexes * @param array $changedIndexes * @param array $removedIndexes * @param list $addedForeignKeys * @param list $changedForeignKeys * @param list $removedForeignKeys * @param array $renamedColumns * @param array $renamedIndexes */ public function __construct( $tableName, $addedColumns = [], $modifiedColumns = [], $droppedColumns = [], $addedIndexes = [], $changedIndexes = [], $removedIndexes = [], ?Table $fromTable = null, $addedForeignKeys = [], $changedForeignKeys = [], $removedForeignKeys = [], $renamedColumns = [], $renamedIndexes = [] ) { $this->name = $tableName; $this->addedColumns = $addedColumns; $this->changedColumns = $modifiedColumns; $this->renamedColumns = $renamedColumns; $this->removedColumns = $droppedColumns; $this->addedIndexes = $addedIndexes; $this->changedIndexes = $changedIndexes; $this->renamedIndexes = $renamedIndexes; $this->removedIndexes = $removedIndexes; $this->addedForeignKeys = $addedForeignKeys; $this->changedForeignKeys = $changedForeignKeys; $this->removedForeignKeys = $removedForeignKeys; if ($fromTable === null) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5678', 'Not passing the $fromTable to %s is deprecated.', __METHOD__, ); } $this->fromTable = $fromTable; } /** * @deprecated Use {@see getOldTable()} instead. * * @param AbstractPlatform $platform The platform to use for retrieving this table diff's name. * * @return Identifier */ public function getName(AbstractPlatform $platform) { return new Identifier( $this->fromTable instanceof Table ? $this->fromTable->getQuotedName($platform) : $this->name, ); } /** * @deprecated Rename tables via {@link AbstractSchemaManager::renameTable()} instead. * * @return Identifier|false */ public function getNewName() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5663', '%s is deprecated. Rename tables via AbstractSchemaManager::renameTable() instead.', __METHOD__, ); if ($this->newName === false) { return false; } return new Identifier($this->newName); } public function getOldTable(): ?Table { return $this->fromTable; } /** @return list */ public function getAddedColumns(): array { return array_values($this->addedColumns); } /** @return list */ public function getModifiedColumns(): array { return array_values($this->changedColumns); } /** @return list */ public function getDroppedColumns(): array { return array_values($this->removedColumns); } /** @return array */ public function getRenamedColumns(): array { return $this->renamedColumns; } /** @return list */ public function getAddedIndexes(): array { return array_values($this->addedIndexes); } /** * @internal This method exists only for compatibility with the current implementation of schema managers * that modify the diff while processing it. */ public function unsetAddedIndex(Index $index): void { $this->addedIndexes = array_filter( $this->addedIndexes, static function (Index $addedIndex) use ($index): bool { return $addedIndex !== $index; }, ); } /** @return array */ public function getModifiedIndexes(): array { return array_values($this->changedIndexes); } /** @return list */ public function getDroppedIndexes(): array { return array_values($this->removedIndexes); } /** * @internal This method exists only for compatibility with the current implementation of schema managers * that modify the diff while processing it. */ public function unsetDroppedIndex(Index $index): void { $this->removedIndexes = array_filter( $this->removedIndexes, static function (Index $removedIndex) use ($index): bool { return $removedIndex !== $index; }, ); } /** @return array */ public function getRenamedIndexes(): array { return $this->renamedIndexes; } /** @return list */ public function getAddedForeignKeys(): array { return $this->addedForeignKeys; } /** @return list */ public function getModifiedForeignKeys(): array { return $this->changedForeignKeys; } /** @return list */ public function getDroppedForeignKeys(): array { return $this->removedForeignKeys; } /** * @internal This method exists only for compatibility with the current implementation of the schema comparator. * * @param ForeignKeyConstraint|string $foreignKey */ public function unsetDroppedForeignKey($foreignKey): void { $this->removedForeignKeys = array_filter( $this->removedForeignKeys, static function ($removedForeignKey) use ($foreignKey): bool { return $removedForeignKey !== $foreignKey; }, ); } /** * Returns whether the diff is empty (contains no changes). */ public function isEmpty(): bool { return count($this->addedColumns) === 0 && count($this->changedColumns) === 0 && count($this->removedColumns) === 0 && count($this->renamedColumns) === 0 && count($this->addedIndexes) === 0 && count($this->changedIndexes) === 0 && count($this->removedIndexes) === 0 && count($this->renamedIndexes) === 0 && count($this->addedForeignKeys) === 0 && count($this->changedForeignKeys) === 0 && count($this->removedForeignKeys) === 0; } } PK!Ԏ I2^2^dbal/src/Schema/Comparator.phpnu[platform = $platform; } /** @param list $args */ public function __call(string $method, array $args): SchemaDiff { if ($method !== 'compareSchemas') { throw new BadMethodCallException(sprintf('Unknown method "%s"', $method)); } return $this->doCompareSchemas(...$args); } /** @param list $args */ public static function __callStatic(string $method, array $args): SchemaDiff { if ($method !== 'compareSchemas') { throw new BadMethodCallException(sprintf('Unknown method "%s"', $method)); } Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4707', 'Calling %s::%s() statically is deprecated.', self::class, $method, ); $comparator = new self(); return $comparator->doCompareSchemas(...$args); } /** * Returns a SchemaDiff object containing the differences between the schemas $fromSchema and $toSchema. * * This method should be called non-statically since it will be declared as non-static in the next major release. * * @return SchemaDiff * * @throws SchemaException */ private function doCompareSchemas( Schema $fromSchema, Schema $toSchema ) { $createdSchemas = []; $droppedSchemas = []; $createdTables = []; $alteredTables = []; $droppedTables = []; $createdSequences = []; $alteredSequences = []; $droppedSequences = []; $orphanedForeignKeys = []; $foreignKeysToTable = []; foreach ($toSchema->getNamespaces() as $namespace) { if ($fromSchema->hasNamespace($namespace)) { continue; } $createdSchemas[$namespace] = $namespace; } foreach ($fromSchema->getNamespaces() as $namespace) { if ($toSchema->hasNamespace($namespace)) { continue; } $droppedSchemas[$namespace] = $namespace; } foreach ($toSchema->getTables() as $table) { $tableName = $table->getShortestName($toSchema->getName()); if (! $fromSchema->hasTable($tableName)) { $createdTables[$tableName] = $toSchema->getTable($tableName); } else { $tableDifferences = $this->diffTable( $fromSchema->getTable($tableName), $toSchema->getTable($tableName), ); if ($tableDifferences !== false) { $alteredTables[$tableName] = $tableDifferences; } } } /* Check if there are tables removed */ foreach ($fromSchema->getTables() as $table) { $tableName = $table->getShortestName($fromSchema->getName()); $table = $fromSchema->getTable($tableName); if (! $toSchema->hasTable($tableName)) { $droppedTables[$tableName] = $table; } // also remember all foreign keys that point to a specific table foreach ($table->getForeignKeys() as $foreignKey) { $foreignTable = strtolower($foreignKey->getForeignTableName()); if (! isset($foreignKeysToTable[$foreignTable])) { $foreignKeysToTable[$foreignTable] = []; } $foreignKeysToTable[$foreignTable][] = $foreignKey; } } foreach ($droppedTables as $tableName => $table) { if (! isset($foreignKeysToTable[$tableName])) { continue; } foreach ($foreignKeysToTable[$tableName] as $foreignKey) { if (isset($droppedTables[strtolower($foreignKey->getLocalTableName())])) { continue; } $orphanedForeignKeys[] = $foreignKey; } // deleting duplicated foreign keys present on both on the orphanedForeignKey // and the removedForeignKeys from changedTables foreach ($foreignKeysToTable[$tableName] as $foreignKey) { // strtolower the table name to make if compatible with getShortestName $localTableName = strtolower($foreignKey->getLocalTableName()); if (! isset($alteredTables[$localTableName])) { continue; } foreach ($alteredTables[$localTableName]->getDroppedForeignKeys() as $droppedForeignKey) { assert($droppedForeignKey instanceof ForeignKeyConstraint); // We check if the key is from the removed table if not we skip. if ($tableName !== strtolower($droppedForeignKey->getForeignTableName())) { continue; } $alteredTables[$localTableName]->unsetDroppedForeignKey($droppedForeignKey); } } } foreach ($toSchema->getSequences() as $sequence) { $sequenceName = $sequence->getShortestName($toSchema->getName()); if (! $fromSchema->hasSequence($sequenceName)) { if (! $this->isAutoIncrementSequenceInSchema($fromSchema, $sequence)) { $createdSequences[] = $sequence; } } else { if ($this->diffSequence($sequence, $fromSchema->getSequence($sequenceName))) { $alteredSequences[] = $toSchema->getSequence($sequenceName); } } } foreach ($fromSchema->getSequences() as $sequence) { if ($this->isAutoIncrementSequenceInSchema($toSchema, $sequence)) { continue; } $sequenceName = $sequence->getShortestName($fromSchema->getName()); if ($toSchema->hasSequence($sequenceName)) { continue; } $droppedSequences[] = $sequence; } $diff = new SchemaDiff( $createdTables, $alteredTables, $droppedTables, $fromSchema, $createdSchemas, $droppedSchemas, $createdSequences, $alteredSequences, $droppedSequences, ); $diff->orphanedForeignKeys = $orphanedForeignKeys; return $diff; } /** * @deprecated Use non-static call to {@see compareSchemas()} instead. * * @return SchemaDiff * * @throws SchemaException */ public function compare(Schema $fromSchema, Schema $toSchema) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4707', 'Method compare() is deprecated. Use a non-static call to compareSchemas() instead.', ); return $this->compareSchemas($fromSchema, $toSchema); } /** * @param Schema $schema * @param Sequence $sequence */ private function isAutoIncrementSequenceInSchema($schema, $sequence): bool { foreach ($schema->getTables() as $table) { if ($sequence->isAutoIncrementsFor($table)) { return true; } } return false; } /** @return bool */ public function diffSequence(Sequence $sequence1, Sequence $sequence2) { if ($sequence1->getAllocationSize() !== $sequence2->getAllocationSize()) { return true; } return $sequence1->getInitialValue() !== $sequence2->getInitialValue(); } /** * Returns the difference between the tables $fromTable and $toTable. * * If there are no differences this method returns the boolean false. * * @deprecated Use {@see compareTables()} and, optionally, {@see TableDiff::isEmpty()} instead. * * @return TableDiff|false * * @throws Exception */ public function diffTable(Table $fromTable, Table $toTable) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5770', '%s is deprecated. Use compareTables() instead.', __METHOD__, ); $diff = $this->compareTables($fromTable, $toTable); if ($diff->isEmpty()) { return false; } return $diff; } /** * Compares the tables and returns the difference between them. * * @throws Exception */ public function compareTables(Table $fromTable, Table $toTable): TableDiff { $addedColumns = []; $modifiedColumns = []; $droppedColumns = []; $addedIndexes = []; $modifiedIndexes = []; $droppedIndexes = []; $addedForeignKeys = []; $modifiedForeignKeys = []; $droppedForeignKeys = []; $fromTableColumns = $fromTable->getColumns(); $toTableColumns = $toTable->getColumns(); /* See if all the columns in "from" table exist in "to" table */ foreach ($toTableColumns as $columnName => $column) { if ($fromTable->hasColumn($columnName)) { continue; } $addedColumns[$columnName] = $column; } /* See if there are any removed columns in "to" table */ foreach ($fromTableColumns as $columnName => $column) { // See if column is removed in "to" table. if (! $toTable->hasColumn($columnName)) { $droppedColumns[$columnName] = $column; continue; } $toColumn = $toTable->getColumn($columnName); // See if column has changed properties in "to" table. $changedProperties = $this->diffColumn($column, $toColumn); if ($this->platform !== null) { if ($this->columnsEqual($column, $toColumn)) { continue; } } elseif (count($changedProperties) === 0) { continue; } $modifiedColumns[$column->getName()] = new ColumnDiff( $column->getName(), $toColumn, $changedProperties, $column, ); } $renamedColumns = $this->detectRenamedColumns($addedColumns, $droppedColumns); $fromTableIndexes = $fromTable->getIndexes(); $toTableIndexes = $toTable->getIndexes(); /* See if all the indexes in "from" table exist in "to" table */ foreach ($toTableIndexes as $indexName => $index) { if (($index->isPrimary() && $fromTable->getPrimaryKey() !== null) || $fromTable->hasIndex($indexName)) { continue; } $addedIndexes[$indexName] = $index; } /* See if there are any removed indexes in "to" table */ foreach ($fromTableIndexes as $indexName => $index) { // See if index is removed in "to" table. if ( ($index->isPrimary() && $toTable->getPrimaryKey() === null) || ! $index->isPrimary() && ! $toTable->hasIndex($indexName) ) { $droppedIndexes[$indexName] = $index; continue; } // See if index has changed in "to" table. $toTableIndex = $index->isPrimary() ? $toTable->getPrimaryKey() : $toTable->getIndex($indexName); assert($toTableIndex instanceof Index); if (! $this->diffIndex($index, $toTableIndex)) { continue; } $modifiedIndexes[$indexName] = $toTableIndex; } $renamedIndexes = $this->detectRenamedIndexes($addedIndexes, $droppedIndexes); $fromForeignKeys = $fromTable->getForeignKeys(); $toForeignKeys = $toTable->getForeignKeys(); foreach ($fromForeignKeys as $fromKey => $fromConstraint) { foreach ($toForeignKeys as $toKey => $toConstraint) { if ($this->diffForeignKey($fromConstraint, $toConstraint) === false) { unset($fromForeignKeys[$fromKey], $toForeignKeys[$toKey]); } else { if (strtolower($fromConstraint->getName()) === strtolower($toConstraint->getName())) { $modifiedForeignKeys[] = $toConstraint; unset($fromForeignKeys[$fromKey], $toForeignKeys[$toKey]); } } } } foreach ($fromForeignKeys as $fromConstraint) { $droppedForeignKeys[] = $fromConstraint; } foreach ($toForeignKeys as $toConstraint) { $addedForeignKeys[] = $toConstraint; } return new TableDiff( $toTable->getName(), $addedColumns, $modifiedColumns, $droppedColumns, $addedIndexes, $modifiedIndexes, $droppedIndexes, $fromTable, $addedForeignKeys, $modifiedForeignKeys, $droppedForeignKeys, $renamedColumns, $renamedIndexes, ); } /** * Try to find columns that only changed their name, rename operations maybe cheaper than add/drop * however ambiguities between different possibilities should not lead to renaming at all. * * @param array $addedColumns * @param array $removedColumns * * @return array * * @throws Exception */ private function detectRenamedColumns(array &$addedColumns, array &$removedColumns): array { $candidatesByName = []; foreach ($addedColumns as $addedColumnName => $addedColumn) { foreach ($removedColumns as $removedColumn) { if (! $this->columnsEqual($addedColumn, $removedColumn)) { continue; } $candidatesByName[$addedColumn->getName()][] = [$removedColumn, $addedColumn, $addedColumnName]; } } $renamedColumns = []; foreach ($candidatesByName as $candidates) { if (count($candidates) !== 1) { continue; } [$removedColumn, $addedColumn] = $candidates[0]; $removedColumnName = $removedColumn->getName(); $addedColumnName = strtolower($addedColumn->getName()); if (isset($renamedColumns[$removedColumnName])) { continue; } $renamedColumns[$removedColumnName] = $addedColumn; unset( $addedColumns[$addedColumnName], $removedColumns[strtolower($removedColumnName)], ); } return $renamedColumns; } /** * Try to find indexes that only changed their name, rename operations maybe cheaper than add/drop * however ambiguities between different possibilities should not lead to renaming at all. * * @param array $addedIndexes * @param array $removedIndexes * * @return array */ private function detectRenamedIndexes(array &$addedIndexes, array &$removedIndexes): array { $candidatesByName = []; // Gather possible rename candidates by comparing each added and removed index based on semantics. foreach ($addedIndexes as $addedIndexName => $addedIndex) { foreach ($removedIndexes as $removedIndex) { if ($this->diffIndex($addedIndex, $removedIndex)) { continue; } $candidatesByName[$addedIndex->getName()][] = [$removedIndex, $addedIndex, $addedIndexName]; } } $renamedIndexes = []; foreach ($candidatesByName as $candidates) { // If the current rename candidate contains exactly one semantically equal index, // we can safely rename it. // Otherwise, it is unclear if a rename action is really intended, // therefore we let those ambiguous indexes be added/dropped. if (count($candidates) !== 1) { continue; } [$removedIndex, $addedIndex] = $candidates[0]; $removedIndexName = strtolower($removedIndex->getName()); $addedIndexName = strtolower($addedIndex->getName()); if (isset($renamedIndexes[$removedIndexName])) { continue; } $renamedIndexes[$removedIndexName] = $addedIndex; unset( $addedIndexes[$addedIndexName], $removedIndexes[$removedIndexName], ); } return $renamedIndexes; } /** * @internal The method should be only used from within the {@see Comparator} class hierarchy. * * @return bool */ public function diffForeignKey(ForeignKeyConstraint $key1, ForeignKeyConstraint $key2) { if ( array_map('strtolower', $key1->getUnquotedLocalColumns()) !== array_map('strtolower', $key2->getUnquotedLocalColumns()) ) { return true; } if ( array_map('strtolower', $key1->getUnquotedForeignColumns()) !== array_map('strtolower', $key2->getUnquotedForeignColumns()) ) { return true; } if ($key1->getUnqualifiedForeignTableName() !== $key2->getUnqualifiedForeignTableName()) { return true; } if ($key1->onUpdate() !== $key2->onUpdate()) { return true; } return $key1->onDelete() !== $key2->onDelete(); } /** * Compares the definitions of the given columns * * @internal The method should be only used from within the {@see Comparator} class hierarchy. * * @throws Exception */ public function columnsEqual(Column $column1, Column $column2): bool { if ($this->platform === null) { return $this->diffColumn($column1, $column2) === []; } return $this->platform->columnsEqual($column1, $column2); } /** * Returns the difference between the columns * * If there are differences this method returns the changed properties as a * string array, otherwise an empty array gets returned. * * @deprecated Use {@see columnsEqual()} instead. * * @return string[] */ public function diffColumn(Column $column1, Column $column2) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5650', '%s is deprecated. Use diffTable() instead.', __METHOD__, ); $properties1 = $column1->toArray(); $properties2 = $column2->toArray(); $changedProperties = []; if (get_class($properties1['type']) !== get_class($properties2['type'])) { $changedProperties[] = 'type'; } foreach (['notnull', 'unsigned', 'autoincrement'] as $property) { if ($properties1[$property] === $properties2[$property]) { continue; } $changedProperties[] = $property; } // Null values need to be checked additionally as they tell whether to create or drop a default value. // null != 0, null != false, null != '' etc. This affects platform's table alteration SQL generation. if ( ($properties1['default'] === null) !== ($properties2['default'] === null) || $properties1['default'] != $properties2['default'] ) { $changedProperties[] = 'default'; } if ( ($properties1['type'] instanceof Types\StringType && ! $properties1['type'] instanceof Types\GuidType) || $properties1['type'] instanceof Types\BinaryType ) { // check if value of length is set at all, default value assumed otherwise. $length1 = $properties1['length'] ?? 255; $length2 = $properties2['length'] ?? 255; if ($length1 !== $length2) { $changedProperties[] = 'length'; } if ($properties1['fixed'] !== $properties2['fixed']) { $changedProperties[] = 'fixed'; } } elseif ($properties1['type'] instanceof Types\DecimalType) { if (($properties1['precision'] ?? 10) !== ($properties2['precision'] ?? 10)) { $changedProperties[] = 'precision'; } if ($properties1['scale'] !== $properties2['scale']) { $changedProperties[] = 'scale'; } } // A null value and an empty string are actually equal for a comment so they should not trigger a change. if ( $properties1['comment'] !== $properties2['comment'] && ! ($properties1['comment'] === null && $properties2['comment'] === '') && ! ($properties2['comment'] === null && $properties1['comment'] === '') ) { $changedProperties[] = 'comment'; } $customOptions1 = $column1->getCustomSchemaOptions(); $customOptions2 = $column2->getCustomSchemaOptions(); foreach (array_merge(array_keys($customOptions1), array_keys($customOptions2)) as $key) { if (! array_key_exists($key, $properties1) || ! array_key_exists($key, $properties2)) { $changedProperties[] = $key; } elseif ($properties1[$key] !== $properties2[$key]) { $changedProperties[] = $key; } } $platformOptions1 = $column1->getPlatformOptions(); $platformOptions2 = $column2->getPlatformOptions(); foreach (array_keys(array_intersect_key($platformOptions1, $platformOptions2)) as $key) { if ($properties1[$key] === $properties2[$key]) { continue; } $changedProperties[] = $key; } return array_unique($changedProperties); } /** * Finds the difference between the indexes $index1 and $index2. * * Compares $index1 with $index2 and returns true if there are any * differences or false in case there are no differences. * * @internal The method should be only used from within the {@see Comparator} class hierarchy. * * @return bool */ public function diffIndex(Index $index1, Index $index2) { return ! ($index1->isFulfilledBy($index2) && $index2->isFulfilledBy($index1)); } } PK!1x)dbal/src/Schema/AbstractSchemaManager.phpnu[_conn = $connection; $this->_platform = $platform; } /** * Returns the associated platform. * * @deprecated Use {@link Connection::getDatabasePlatform()} instead. * * @return T */ public function getDatabasePlatform() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5387', 'AbstractSchemaManager::getDatabasePlatform() is deprecated.' . ' Use Connection::getDatabasePlatform() instead.', ); return $this->_platform; } /** * Tries any method on the schema manager. Normally a method throws an * exception when your DBMS doesn't support it or if an error occurs. * This method allows you to try and method on your SchemaManager * instance and will return false if it does not work or is not supported. * * * $result = $sm->tryMethod('dropView', 'view_name'); * * * @deprecated * * @return mixed */ public function tryMethod() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4897', 'AbstractSchemaManager::tryMethod() is deprecated.', ); $args = func_get_args(); $method = $args[0]; unset($args[0]); $args = array_values($args); $callback = [$this, $method]; assert(is_callable($callback)); try { return call_user_func_array($callback, $args); } catch (Throwable $e) { return false; } } /** * Lists the available databases for this connection. * * @return string[] * * @throws Exception */ public function listDatabases() { $sql = $this->_platform->getListDatabasesSQL(); $databases = $this->_conn->fetchAllAssociative($sql); return $this->_getPortableDatabasesList($databases); } /** * Returns a list of all namespaces in the current database. * * @deprecated Use {@see listSchemaNames()} instead. * * @return string[] * * @throws Exception */ public function listNamespaceNames() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4503', 'AbstractSchemaManager::listNamespaceNames() is deprecated,' . ' use AbstractSchemaManager::listSchemaNames() instead.', ); $sql = $this->_platform->getListNamespacesSQL(); $namespaces = $this->_conn->fetchAllAssociative($sql); return $this->getPortableNamespacesList($namespaces); } /** * Returns a list of the names of all schemata in the current database. * * @return list * * @throws Exception */ public function listSchemaNames(): array { throw Exception::notSupported(__METHOD__); } /** * Lists the available sequences for this connection. * * @param string|null $database * * @return Sequence[] * * @throws Exception */ public function listSequences($database = null) { if ($database === null) { $database = $this->getDatabase(__METHOD__); } else { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5284', 'Passing $database to AbstractSchemaManager::listSequences() is deprecated.', ); } $sql = $this->_platform->getListSequencesSQL($database); $sequences = $this->_conn->fetchAllAssociative($sql); return $this->filterAssetNames($this->_getPortableSequencesList($sequences)); } /** * Lists the columns for a given table. * * In contrast to other libraries and to the old version of Doctrine, * this column definition does try to contain the 'primary' column for * the reason that it is not portable across different RDBMS. Use * {@see listTableIndexes($tableName)} to retrieve the primary key * of a table. Where a RDBMS specifies more details, these are held * in the platformDetails array. * * @param string $table The name of the table. * @param string|null $database * * @return Column[] * * @throws Exception */ public function listTableColumns($table, $database = null) { if ($database === null) { $database = $this->getDatabase(__METHOD__); } else { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5284', 'Passing $database to AbstractSchemaManager::listTableColumns() is deprecated.', ); } $sql = $this->_platform->getListTableColumnsSQL($table, $database); $tableColumns = $this->_conn->fetchAllAssociative($sql); return $this->_getPortableTableColumnList($table, $database, $tableColumns); } /** * @param string $table * @param string|null $database * * @return Column[] * * @throws Exception */ protected function doListTableColumns($table, $database = null): array { if ($database === null) { $database = $this->getDatabase(__METHOD__); } else { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5284', 'Passing $database to AbstractSchemaManager::doListTableColumns() is deprecated.', ); } return $this->_getPortableTableColumnList( $table, $database, $this->selectTableColumns($database, $this->normalizeName($table)) ->fetchAllAssociative(), ); } /** * Lists the indexes for a given table returning an array of Index instances. * * Keys of the portable indexes list are all lower-cased. * * @param string $table The name of the table. * * @return Index[] * * @throws Exception */ public function listTableIndexes($table) { $sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase()); $tableIndexes = $this->_conn->fetchAllAssociative($sql); return $this->_getPortableTableIndexesList($tableIndexes, $table); } /** * @param string $table * * @return Index[] * * @throws Exception */ protected function doListTableIndexes($table): array { $database = $this->getDatabase(__METHOD__); $table = $this->normalizeName($table); return $this->_getPortableTableIndexesList( $this->selectIndexColumns( $database, $table, )->fetchAllAssociative(), $table, ); } /** * Returns true if all the given tables exist. * * The usage of a string $tableNames is deprecated. Pass a one-element array instead. * * @param string|string[] $names * * @return bool * * @throws Exception */ public function tablesExist($names) { if (is_string($names)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3580', 'The usage of a string $tableNames in AbstractSchemaManager::tablesExist() is deprecated. ' . 'Pass a one-element array instead.', ); } $names = array_map('strtolower', (array) $names); return count($names) === count(array_intersect($names, array_map('strtolower', $this->listTableNames()))); } /** * Returns a list of all tables in the current database. * * @return string[] * * @throws Exception */ public function listTableNames() { $sql = $this->_platform->getListTablesSQL(); $tables = $this->_conn->fetchAllAssociative($sql); $tableNames = $this->_getPortableTablesList($tables); return $this->filterAssetNames($tableNames); } /** * @return list * * @throws Exception */ protected function doListTableNames(): array { $database = $this->getDatabase(__METHOD__); return $this->filterAssetNames( $this->_getPortableTablesList( $this->selectTableNames($database) ->fetchAllAssociative(), ), ); } /** * Filters asset names if they are configured to return only a subset of all * the found elements. * * @param mixed[] $assetNames * * @return mixed[] */ protected function filterAssetNames($assetNames) { $filter = $this->_conn->getConfiguration()->getSchemaAssetsFilter(); if ($filter === null) { return $assetNames; } return array_values(array_filter($assetNames, $filter)); } /** * Lists the tables for this connection. * * @return list * * @throws Exception */ public function listTables() { $tableNames = $this->listTableNames(); $tables = []; foreach ($tableNames as $tableName) { $tables[] = $this->introspectTable($tableName); } return $tables; } /** * @return list
* * @throws Exception */ protected function doListTables(): array { $database = $this->getDatabase(__METHOD__); $tableColumnsByTable = $this->fetchTableColumnsByTable($database); $indexColumnsByTable = $this->fetchIndexColumnsByTable($database); $foreignKeyColumnsByTable = $this->fetchForeignKeyColumnsByTable($database); $tableOptionsByTable = $this->fetchTableOptionsByTable($database); $filter = $this->_conn->getConfiguration()->getSchemaAssetsFilter(); $tables = []; foreach ($tableColumnsByTable as $tableName => $tableColumns) { if ($filter !== null && ! $filter($tableName)) { continue; } $tables[] = new Table( $tableName, $this->_getPortableTableColumnList($tableName, $database, $tableColumns), $this->_getPortableTableIndexesList($indexColumnsByTable[$tableName] ?? [], $tableName), [], $this->_getPortableTableForeignKeysList($foreignKeyColumnsByTable[$tableName] ?? []), $tableOptionsByTable[$tableName] ?? [], ); } return $tables; } /** * @deprecated Use {@see introspectTable()} instead. * * @param string $name * * @return Table * * @throws Exception */ public function listTableDetails($name) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5595', '%s is deprecated. Use introspectTable() instead.', __METHOD__, ); $columns = $this->listTableColumns($name); $foreignKeys = []; if ($this->_platform->supportsForeignKeyConstraints()) { $foreignKeys = $this->listTableForeignKeys($name); } $indexes = $this->listTableIndexes($name); return new Table($name, $columns, $indexes, [], $foreignKeys); } /** * @param string $name * * @throws Exception */ protected function doListTableDetails($name): Table { $database = $this->getDatabase(__METHOD__); $normalizedName = $this->normalizeName($name); $tableOptionsByTable = $this->fetchTableOptionsByTable($database, $normalizedName); if ($this->_platform->supportsForeignKeyConstraints()) { $foreignKeys = $this->listTableForeignKeys($name); } else { $foreignKeys = []; } return new Table( $name, $this->listTableColumns($name, $database), $this->listTableIndexes($name), [], $foreignKeys, $tableOptionsByTable[$normalizedName] ?? [], ); } /** * An extension point for those platforms where case sensitivity of the object name depends on whether it's quoted. * * Such platforms should convert a possibly quoted name into a value of the corresponding case. */ protected function normalizeName(string $name): string { $identifier = new Identifier($name); return $identifier->getName(); } /** * Selects names of tables in the specified database. * * @throws Exception * * @abstract */ protected function selectTableNames(string $databaseName): Result { throw Exception::notSupported(__METHOD__); } /** * Selects definitions of table columns in the specified database. If the table name is specified, narrows down * the selection to this table. * * @throws Exception * * @abstract */ protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result { throw Exception::notSupported(__METHOD__); } /** * Selects definitions of index columns in the specified database. If the table name is specified, narrows down * the selection to this table. * * @throws Exception */ protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result { throw Exception::notSupported(__METHOD__); } /** * Selects definitions of foreign key columns in the specified database. If the table name is specified, * narrows down the selection to this table. * * @throws Exception */ protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result { throw Exception::notSupported(__METHOD__); } /** * Fetches definitions of table columns in the specified database and returns them grouped by table name. * * @return array>> * * @throws Exception */ protected function fetchTableColumnsByTable(string $databaseName): array { return $this->fetchAllAssociativeGrouped($this->selectTableColumns($databaseName)); } /** * Fetches definitions of index columns in the specified database and returns them grouped by table name. * * @return array>> * * @throws Exception */ protected function fetchIndexColumnsByTable(string $databaseName): array { return $this->fetchAllAssociativeGrouped($this->selectIndexColumns($databaseName)); } /** * Fetches definitions of foreign key columns in the specified database and returns them grouped by table name. * * @return array>> * * @throws Exception */ protected function fetchForeignKeyColumnsByTable(string $databaseName): array { if (! $this->_platform->supportsForeignKeyConstraints()) { return []; } return $this->fetchAllAssociativeGrouped( $this->selectForeignKeyColumns($databaseName), ); } /** * Fetches table options for the tables in the specified database and returns them grouped by table name. * If the table name is specified, narrows down the selection to this table. * * @return array> * * @throws Exception */ protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array { throw Exception::notSupported(__METHOD__); } /** * Introspects the table with the given name. * * @throws Exception */ public function introspectTable(string $name): Table { $table = $this->listTableDetails($name); if ($table->getColumns() === []) { throw SchemaException::tableDoesNotExist($name); } return $table; } /** * Lists the views this connection has. * * @return View[] * * @throws Exception */ public function listViews() { $database = $this->_conn->getDatabase(); $sql = $this->_platform->getListViewsSQL($database); $views = $this->_conn->fetchAllAssociative($sql); return $this->_getPortableViewsList($views); } /** * Lists the foreign keys for the given table. * * @param string $table The name of the table. * @param string|null $database * * @return ForeignKeyConstraint[] * * @throws Exception */ public function listTableForeignKeys($table, $database = null) { if ($database === null) { $database = $this->getDatabase(__METHOD__); } else { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5284', 'Passing $database to AbstractSchemaManager::listTableForeignKeys() is deprecated.', ); } $sql = $this->_platform->getListTableForeignKeysSQL($table, $database); $tableForeignKeys = $this->_conn->fetchAllAssociative($sql); return $this->_getPortableTableForeignKeysList($tableForeignKeys); } /** * @param string $table * @param string|null $database * * @return ForeignKeyConstraint[] * * @throws Exception */ protected function doListTableForeignKeys($table, $database = null): array { if ($database === null) { $database = $this->getDatabase(__METHOD__); } else { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5284', 'Passing $database to AbstractSchemaManager::listTableForeignKeys() is deprecated.', ); } return $this->_getPortableTableForeignKeysList( $this->selectForeignKeyColumns( $database, $this->normalizeName($table), )->fetchAllAssociative(), ); } /* drop*() Methods */ /** * Drops a database. * * NOTE: You can not drop the database this SchemaManager is currently connected to. * * @param string $database The name of the database to drop. * * @return void * * @throws Exception */ public function dropDatabase($database) { $this->_conn->executeStatement( $this->_platform->getDropDatabaseSQL($database), ); } /** * Drops a schema. * * @throws Exception */ public function dropSchema(string $schemaName): void { $this->_conn->executeStatement( $this->_platform->getDropSchemaSQL($schemaName), ); } /** * Drops the given table. * * @param string $name The name of the table to drop. * * @return void * * @throws Exception */ public function dropTable($name) { $this->_conn->executeStatement( $this->_platform->getDropTableSQL($name), ); } /** * Drops the index from the given table. * * @param Index|string $index The name of the index. * @param Table|string $table The name of the table. * * @return void * * @throws Exception */ public function dropIndex($index, $table) { if ($index instanceof Index) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $index as an Index object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); $index = $index->getQuotedName($this->_platform); } if ($table instanceof Table) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $table as an Table object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); $table = $table->getQuotedName($this->_platform); } $this->_conn->executeStatement( $this->_platform->getDropIndexSQL($index, $table), ); } /** * Drops the constraint from the given table. * * @deprecated Use {@see dropIndex()}, {@see dropForeignKey()} or {@see dropUniqueConstraint()} instead. * * @param Table|string $table The name of the table. * * @return void * * @throws Exception */ public function dropConstraint(Constraint $constraint, $table) { if ($table instanceof Table) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); $table = $table->getQuotedName($this->_platform); } $this->_conn->executeStatement($this->_platform->getDropConstraintSQL( $constraint->getQuotedName($this->_platform), $table, )); } /** * Drops a foreign key from a table. * * @param ForeignKeyConstraint|string $foreignKey The name of the foreign key. * @param Table|string $table The name of the table with the foreign key. * * @return void * * @throws Exception */ public function dropForeignKey($foreignKey, $table) { if ($foreignKey instanceof ForeignKeyConstraint) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $foreignKey as a ForeignKeyConstraint object to %s is deprecated.' . ' Pass it as a quoted name instead.', __METHOD__, ); $foreignKey = $foreignKey->getQuotedName($this->_platform); } if ($table instanceof Table) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); $table = $table->getQuotedName($this->_platform); } $this->_conn->executeStatement( $this->_platform->getDropForeignKeySQL($foreignKey, $table), ); } /** * Drops a sequence with a given name. * * @param string $name The name of the sequence to drop. * * @return void * * @throws Exception */ public function dropSequence($name) { $this->_conn->executeStatement( $this->_platform->getDropSequenceSQL($name), ); } /** * Drops the unique constraint from the given table. * * @throws Exception */ public function dropUniqueConstraint(string $name, string $tableName): void { $this->_conn->executeStatement( $this->_platform->getDropUniqueConstraintSQL($name, $tableName), ); } /** * Drops a view. * * @param string $name The name of the view. * * @return void * * @throws Exception */ public function dropView($name) { $this->_conn->executeStatement( $this->_platform->getDropViewSQL($name), ); } /* create*() Methods */ /** @throws Exception */ public function createSchemaObjects(Schema $schema): void { $this->_execSql($schema->toSql($this->_platform)); } /** * Creates a new database. * * @param string $database The name of the database to create. * * @return void * * @throws Exception */ public function createDatabase($database) { $this->_conn->executeStatement( $this->_platform->getCreateDatabaseSQL($database), ); } /** * Creates a new table. * * @return void * * @throws Exception */ public function createTable(Table $table) { $createFlags = AbstractPlatform::CREATE_INDEXES | AbstractPlatform::CREATE_FOREIGNKEYS; $this->_execSql($this->_platform->getCreateTableSQL($table, $createFlags)); } /** * Creates a new sequence. * * @param Sequence $sequence * * @return void * * @throws Exception */ public function createSequence($sequence) { $this->_conn->executeStatement( $this->_platform->getCreateSequenceSQL($sequence), ); } /** * Creates a constraint on a table. * * @deprecated Use {@see createIndex()}, {@see createForeignKey()} or {@see createUniqueConstraint()} instead. * * @param Table|string $table * * @return void * * @throws Exception */ public function createConstraint(Constraint $constraint, $table) { $this->_conn->executeStatement( $this->_platform->getCreateConstraintSQL($constraint, $table), ); } /** * Creates a new index on a table. * * @param Table|string $table The name of the table on which the index is to be created. * * @return void * * @throws Exception */ public function createIndex(Index $index, $table) { $this->_conn->executeStatement( $this->_platform->getCreateIndexSQL($index, $table), ); } /** * Creates a new foreign key. * * @param ForeignKeyConstraint $foreignKey The ForeignKey instance. * @param Table|string $table The name of the table on which the foreign key is to be created. * * @return void * * @throws Exception */ public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) { $this->_conn->executeStatement( $this->_platform->getCreateForeignKeySQL($foreignKey, $table), ); } /** * Creates a unique constraint on a table. * * @throws Exception */ public function createUniqueConstraint(UniqueConstraint $uniqueConstraint, string $tableName): void { $this->_conn->executeStatement( $this->_platform->getCreateUniqueConstraintSQL($uniqueConstraint, $tableName), ); } /** * Creates a new view. * * @return void * * @throws Exception */ public function createView(View $view) { $this->_conn->executeStatement( $this->_platform->getCreateViewSQL( $view->getQuotedName($this->_platform), $view->getSql(), ), ); } /* dropAndCreate*() Methods */ /** @throws Exception */ public function dropSchemaObjects(Schema $schema): void { $this->_execSql($schema->toDropSql($this->_platform)); } /** * Drops and creates a constraint. * * @deprecated Use {@see dropIndex()} and {@see createIndex()}, * {@see dropForeignKey()} and {@see createForeignKey()} * or {@see dropUniqueConstraint()} and {@see createUniqueConstraint()} instead. * * @see dropConstraint() * @see createConstraint() * * @param Table|string $table * * @return void * * @throws Exception */ public function dropAndCreateConstraint(Constraint $constraint, $table) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4897', 'AbstractSchemaManager::dropAndCreateConstraint() is deprecated.' . ' Use AbstractSchemaManager::dropIndex() and AbstractSchemaManager::createIndex(),' . ' AbstractSchemaManager::dropForeignKey() and AbstractSchemaManager::createForeignKey()' . ' or AbstractSchemaManager::dropUniqueConstraint()' . ' and AbstractSchemaManager::createUniqueConstraint() instead.', ); $this->tryMethod('dropConstraint', $constraint, $table); $this->createConstraint($constraint, $table); } /** * Drops and creates a new index on a table. * * @deprecated Use {@see dropIndex()} and {@see createIndex()} instead. * * @param Table|string $table The name of the table on which the index is to be created. * * @return void * * @throws Exception */ public function dropAndCreateIndex(Index $index, $table) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4897', 'AbstractSchemaManager::dropAndCreateIndex() is deprecated.' . ' Use AbstractSchemaManager::dropIndex() and AbstractSchemaManager::createIndex() instead.', ); $this->tryMethod('dropIndex', $index->getQuotedName($this->_platform), $table); $this->createIndex($index, $table); } /** * Drops and creates a new foreign key. * * @deprecated Use {@see dropForeignKey()} and {@see createForeignKey()} instead. * * @param ForeignKeyConstraint $foreignKey An associative array that defines properties * of the foreign key to be created. * @param Table|string $table The name of the table on which the foreign key is to be created. * * @return void * * @throws Exception */ public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4897', 'AbstractSchemaManager::dropAndCreateForeignKey() is deprecated.' . ' Use AbstractSchemaManager::dropForeignKey() and AbstractSchemaManager::createForeignKey() instead.', ); $this->tryMethod('dropForeignKey', $foreignKey, $table); $this->createForeignKey($foreignKey, $table); } /** * Drops and create a new sequence. * * @deprecated Use {@see dropSequence()} and {@see createSequence()} instead. * * @return void * * @throws Exception */ public function dropAndCreateSequence(Sequence $sequence) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4897', 'AbstractSchemaManager::dropAndCreateSequence() is deprecated.' . ' Use AbstractSchemaManager::dropSequence() and AbstractSchemaManager::createSequence() instead.', ); $this->tryMethod('dropSequence', $sequence->getQuotedName($this->_platform)); $this->createSequence($sequence); } /** * Drops and creates a new table. * * @deprecated Use {@see dropTable()} and {@see createTable()} instead. * * @return void * * @throws Exception */ public function dropAndCreateTable(Table $table) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4897', 'AbstractSchemaManager::dropAndCreateTable() is deprecated.' . ' Use AbstractSchemaManager::dropTable() and AbstractSchemaManager::createTable() instead.', ); $this->tryMethod('dropTable', $table->getQuotedName($this->_platform)); $this->createTable($table); } /** * Drops and creates a new database. * * @deprecated Use {@see dropDatabase()} and {@see createDatabase()} instead. * * @param string $database The name of the database to create. * * @return void * * @throws Exception */ public function dropAndCreateDatabase($database) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4897', 'AbstractSchemaManager::dropAndCreateDatabase() is deprecated.' . ' Use AbstractSchemaManager::dropDatabase() and AbstractSchemaManager::createDatabase() instead.', ); $this->tryMethod('dropDatabase', $database); $this->createDatabase($database); } /** * Drops and creates a new view. * * @deprecated Use {@see dropView()} and {@see createView()} instead. * * @return void * * @throws Exception */ public function dropAndCreateView(View $view) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4897', 'AbstractSchemaManager::dropAndCreateView() is deprecated.' . ' Use AbstractSchemaManager::dropView() and AbstractSchemaManager::createView() instead.', ); $this->tryMethod('dropView', $view->getQuotedName($this->_platform)); $this->createView($view); } /** * Alters an existing schema. * * @throws Exception */ public function alterSchema(SchemaDiff $schemaDiff): void { $this->_execSql($this->_platform->getAlterSchemaSQL($schemaDiff)); } /** * Migrates an existing schema to a new schema. * * @throws Exception */ public function migrateSchema(Schema $toSchema): void { $schemaDiff = $this->createComparator() ->compareSchemas($this->introspectSchema(), $toSchema); $this->alterSchema($schemaDiff); } /* alterTable() Methods */ /** * Alters an existing tables schema. * * @return void * * @throws Exception */ public function alterTable(TableDiff $tableDiff) { $this->_execSql($this->_platform->getAlterTableSQL($tableDiff)); } /** * Renames a given table to another name. * * @param string $name The current name of the table. * @param string $newName The new name of the table. * * @return void * * @throws Exception */ public function renameTable($name, $newName) { $this->_execSql($this->_platform->getRenameTableSQL($name, $newName)); } /** * Methods for filtering return values of list*() methods to convert * the native DBMS data definition to a portable Doctrine definition */ /** * @param mixed[] $databases * * @return string[] */ protected function _getPortableDatabasesList($databases) { $list = []; foreach ($databases as $value) { $list[] = $this->_getPortableDatabaseDefinition($value); } return $list; } /** * Converts a list of namespace names from the native DBMS data definition to a portable Doctrine definition. * * @deprecated Use {@see listSchemaNames()} instead. * * @param array> $namespaces The list of namespace names * in the native DBMS data definition. * * @return string[] */ protected function getPortableNamespacesList(array $namespaces) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4503', 'AbstractSchemaManager::getPortableNamespacesList() is deprecated,' . ' use AbstractSchemaManager::listSchemaNames() instead.', ); $namespacesList = []; foreach ($namespaces as $namespace) { $namespacesList[] = $this->getPortableNamespaceDefinition($namespace); } return $namespacesList; } /** * @param mixed $database * * @return mixed */ protected function _getPortableDatabaseDefinition($database) { return $database; } /** * Converts a namespace definition from the native DBMS data definition to a portable Doctrine definition. * * @deprecated Use {@see listSchemaNames()} instead. * * @param array $namespace The native DBMS namespace definition. * * @return mixed */ protected function getPortableNamespaceDefinition(array $namespace) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4503', 'AbstractSchemaManager::getPortableNamespaceDefinition() is deprecated,' . ' use AbstractSchemaManager::listSchemaNames() instead.', ); return $namespace; } /** * @param mixed[][] $sequences * * @return Sequence[] * * @throws Exception */ protected function _getPortableSequencesList($sequences) { $list = []; foreach ($sequences as $value) { $list[] = $this->_getPortableSequenceDefinition($value); } return $list; } /** * @param mixed[] $sequence * * @return Sequence * * @throws Exception */ protected function _getPortableSequenceDefinition($sequence) { throw Exception::notSupported('Sequences'); } /** * Independent of the database the keys of the column list result are lowercased. * * The name of the created column instance however is kept in its case. * * @param string $table The name of the table. * @param string $database * @param mixed[][] $tableColumns * * @return Column[] * * @throws Exception */ protected function _getPortableTableColumnList($table, $database, $tableColumns) { $eventManager = $this->_platform->getEventManager(); $list = []; foreach ($tableColumns as $tableColumn) { $column = null; $defaultPrevented = false; if ($eventManager !== null && $eventManager->hasListeners(Events::onSchemaColumnDefinition)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5784', 'Subscribing to %s events is deprecated. Use a custom schema manager instead.', Events::onSchemaColumnDefinition, ); $eventArgs = new SchemaColumnDefinitionEventArgs($tableColumn, $table, $database, $this->_conn); $eventManager->dispatchEvent(Events::onSchemaColumnDefinition, $eventArgs); $defaultPrevented = $eventArgs->isDefaultPrevented(); $column = $eventArgs->getColumn(); } if (! $defaultPrevented) { $column = $this->_getPortableTableColumnDefinition($tableColumn); } if ($column === null) { continue; } $name = strtolower($column->getQuotedName($this->_platform)); $list[$name] = $column; } return $list; } /** * Gets Table Column Definition. * * @param mixed[] $tableColumn * * @return Column * * @throws Exception */ abstract protected function _getPortableTableColumnDefinition($tableColumn); /** * Aggregates and groups the index results according to the required data result. * * @param mixed[][] $tableIndexes * @param string|null $tableName * * @return Index[] * * @throws Exception */ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) { $result = []; foreach ($tableIndexes as $tableIndex) { $indexName = $keyName = $tableIndex['key_name']; if ($tableIndex['primary']) { $keyName = 'primary'; } $keyName = strtolower($keyName); if (! isset($result[$keyName])) { $options = [ 'lengths' => [], ]; if (isset($tableIndex['where'])) { $options['where'] = $tableIndex['where']; } $result[$keyName] = [ 'name' => $indexName, 'columns' => [], 'unique' => ! $tableIndex['non_unique'], 'primary' => $tableIndex['primary'], 'flags' => $tableIndex['flags'] ?? [], 'options' => $options, ]; } $result[$keyName]['columns'][] = $tableIndex['column_name']; $result[$keyName]['options']['lengths'][] = $tableIndex['length'] ?? null; } $eventManager = $this->_platform->getEventManager(); $indexes = []; foreach ($result as $indexKey => $data) { $index = null; $defaultPrevented = false; if ($eventManager !== null && $eventManager->hasListeners(Events::onSchemaIndexDefinition)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5784', 'Subscribing to %s events is deprecated. Use a custom schema manager instead.', Events::onSchemaColumnDefinition, ); $eventArgs = new SchemaIndexDefinitionEventArgs($data, $tableName, $this->_conn); $eventManager->dispatchEvent(Events::onSchemaIndexDefinition, $eventArgs); $defaultPrevented = $eventArgs->isDefaultPrevented(); $index = $eventArgs->getIndex(); } if (! $defaultPrevented) { $index = new Index( $data['name'], $data['columns'], $data['unique'], $data['primary'], $data['flags'], $data['options'], ); } if ($index === null) { continue; } $indexes[$indexKey] = $index; } return $indexes; } /** * @param mixed[][] $tables * * @return string[] */ protected function _getPortableTablesList($tables) { $list = []; foreach ($tables as $value) { $list[] = $this->_getPortableTableDefinition($value); } return $list; } /** * @param mixed $table * * @return string */ protected function _getPortableTableDefinition($table) { return $table; } /** * @param mixed[][] $views * * @return View[] */ protected function _getPortableViewsList($views) { $list = []; foreach ($views as $value) { $view = $this->_getPortableViewDefinition($value); if ($view === false) { continue; } $viewName = strtolower($view->getQuotedName($this->_platform)); $list[$viewName] = $view; } return $list; } /** * @param mixed[] $view * * @return View|false */ protected function _getPortableViewDefinition($view) { return false; } /** * @param mixed[][] $tableForeignKeys * * @return ForeignKeyConstraint[] */ protected function _getPortableTableForeignKeysList($tableForeignKeys) { $list = []; foreach ($tableForeignKeys as $value) { $list[] = $this->_getPortableTableForeignKeyDefinition($value); } return $list; } /** * @param mixed $tableForeignKey * * @return ForeignKeyConstraint * * @abstract */ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) { return $tableForeignKey; } /** * @internal * * @param string[]|string $sql * * @return void * * @throws Exception */ protected function _execSql($sql) { foreach ((array) $sql as $query) { $this->_conn->executeStatement($query); } } /** * Creates a schema instance for the current database. * * @deprecated Use {@link introspectSchema()} instead. * * @return Schema * * @throws Exception */ public function createSchema() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5613', '%s is deprecated. Use introspectSchema() instead.', __METHOD__, ); $schemaNames = []; if ($this->_platform->supportsSchemas()) { $schemaNames = $this->listNamespaceNames(); } $sequences = []; if ($this->_platform->supportsSequences()) { $sequences = $this->listSequences(); } $tables = $this->listTables(); return new Schema($tables, $sequences, $this->createSchemaConfig(), $schemaNames); } /** * Returns a {@see Schema} instance representing the current database schema. * * @throws Exception */ public function introspectSchema(): Schema { return $this->createSchema(); } /** * Creates the configuration for this schema. * * @return SchemaConfig * * @throws Exception */ public function createSchemaConfig() { $schemaConfig = new SchemaConfig(); $schemaConfig->setMaxIdentifierLength($this->_platform->getMaxIdentifierLength()); $searchPaths = $this->getSchemaSearchPaths(); if (isset($searchPaths[0])) { $schemaConfig->setName($searchPaths[0]); } $params = $this->_conn->getParams(); if (! isset($params['defaultTableOptions'])) { $params['defaultTableOptions'] = []; } if (! isset($params['defaultTableOptions']['charset']) && isset($params['charset'])) { $params['defaultTableOptions']['charset'] = $params['charset']; } $schemaConfig->setDefaultTableOptions($params['defaultTableOptions']); return $schemaConfig; } /** * The search path for namespaces in the currently connected database. * * The first entry is usually the default namespace in the Schema. All * further namespaces contain tables/sequences which can also be addressed * with a short, not full-qualified name. * * For databases that don't support subschema/namespaces this method * returns the name of the currently connected database. * * @deprecated * * @return string[] * * @throws Exception */ public function getSchemaSearchPaths() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4821', 'AbstractSchemaManager::getSchemaSearchPaths() is deprecated.', ); $database = $this->_conn->getDatabase(); if ($database !== null) { return [$database]; } return []; } /** * Given a table comment this method tries to extract a typehint for Doctrine Type, or returns * the type given as default. * * @internal This method should be only used from within the AbstractSchemaManager class hierarchy. * * @param string|null $comment * @param string $currentType * * @return string */ public function extractDoctrineTypeFromComment($comment, $currentType) { if ($comment !== null && preg_match('(\(DC2Type:(((?!\)).)+)\))', $comment, $match) === 1) { return $match[1]; } return $currentType; } /** * @internal This method should be only used from within the AbstractSchemaManager class hierarchy. * * @param string|null $comment * @param string|null $type * * @return string|null */ public function removeDoctrineTypeFromComment($comment, $type) { if ($comment === null) { return null; } return str_replace('(DC2Type:' . $type . ')', '', $comment); } /** @throws Exception */ private function getDatabase(string $methodName): string { $database = $this->_conn->getDatabase(); if ($database === null) { throw DatabaseRequired::new($methodName); } return $database; } public function createComparator(): Comparator { return new Comparator($this->_platform); } /** * @return array>> * * @throws Exception */ private function fetchAllAssociativeGrouped(Result $result): array { $data = []; foreach ($result->fetchAllAssociative() as $row) { $tableName = $this->_getPortableTableDefinition($row); $data[$tableName][] = $row; } return $data; } } PK!x w\\+dbal/src/Schema/PostgreSQLSchemaManager.phpnu[ */ class PostgreSQLSchemaManager extends AbstractSchemaManager { /** @var string[]|null */ private ?array $existingSchemaPaths = null; /** * {@inheritDoc} */ public function listTableNames() { return $this->doListTableNames(); } /** * {@inheritDoc} */ public function listTables() { return $this->doListTables(); } /** * {@inheritDoc} * * @deprecated Use {@see introspectTable()} instead. */ public function listTableDetails($name) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5595', '%s is deprecated. Use introspectTable() instead.', __METHOD__, ); return $this->doListTableDetails($name); } /** * {@inheritDoc} */ public function listTableColumns($table, $database = null) { return $this->doListTableColumns($table, $database); } /** * {@inheritDoc} */ public function listTableIndexes($table) { return $this->doListTableIndexes($table); } /** * {@inheritDoc} */ public function listTableForeignKeys($table, $database = null) { return $this->doListTableForeignKeys($table, $database); } /** * Gets all the existing schema names. * * @deprecated Use {@see listSchemaNames()} instead. * * @return string[] * * @throws Exception */ public function getSchemaNames() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4503', 'PostgreSQLSchemaManager::getSchemaNames() is deprecated,' . ' use PostgreSQLSchemaManager::listSchemaNames() instead.', ); return $this->listNamespaceNames(); } /** * {@inheritDoc} */ public function listSchemaNames(): array { return $this->_conn->fetchFirstColumn( <<<'SQL' SELECT schema_name FROM information_schema.schemata WHERE schema_name NOT LIKE 'pg\_%' AND schema_name != 'information_schema' SQL, ); } /** * {@inheritDoc} * * @deprecated */ public function getSchemaSearchPaths() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4821', 'PostgreSQLSchemaManager::getSchemaSearchPaths() is deprecated.', ); $params = $this->_conn->getParams(); $searchPaths = $this->_conn->fetchOne('SHOW search_path'); assert($searchPaths !== false); $schema = explode(',', $searchPaths); if (isset($params['user'])) { $schema = str_replace('"$user"', $params['user'], $schema); } return array_map('trim', $schema); } /** * Gets names of all existing schemas in the current users search path. * * This is a PostgreSQL only function. * * @internal The method should be only used from within the PostgreSQLSchemaManager class hierarchy. * * @return string[] * * @throws Exception */ public function getExistingSchemaSearchPaths() { if ($this->existingSchemaPaths === null) { $this->determineExistingSchemaSearchPaths(); } assert($this->existingSchemaPaths !== null); return $this->existingSchemaPaths; } /** * Returns the name of the current schema. * * @return string|null * * @throws Exception */ protected function getCurrentSchema() { $schemas = $this->getExistingSchemaSearchPaths(); return array_shift($schemas); } /** * Sets or resets the order of the existing schemas in the current search path of the user. * * This is a PostgreSQL only function. * * @internal The method should be only used from within the PostgreSQLSchemaManager class hierarchy. * * @return void * * @throws Exception */ public function determineExistingSchemaSearchPaths() { $names = $this->listSchemaNames(); $paths = $this->getSchemaSearchPaths(); $this->existingSchemaPaths = array_filter($paths, static function ($v) use ($names): bool { return in_array($v, $names, true); }); } /** * {@inheritDoc} */ protected function _getPortableTableForeignKeyDefinition($tableForeignKey) { $onUpdate = null; $onDelete = null; if ( preg_match( '(ON UPDATE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', $tableForeignKey['condef'], $match, ) === 1 ) { $onUpdate = $match[1]; } if ( preg_match( '(ON DELETE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', $tableForeignKey['condef'], $match, ) === 1 ) { $onDelete = $match[1]; } $result = preg_match('/FOREIGN KEY \((.+)\) REFERENCES (.+)\((.+)\)/', $tableForeignKey['condef'], $values); assert($result === 1); // PostgreSQL returns identifiers that are keywords with quotes, we need them later, don't get // the idea to trim them here. $localColumns = array_map('trim', explode(',', $values[1])); $foreignColumns = array_map('trim', explode(',', $values[3])); $foreignTable = $values[2]; return new ForeignKeyConstraint( $localColumns, $foreignTable, $foreignColumns, $tableForeignKey['conname'], ['onUpdate' => $onUpdate, 'onDelete' => $onDelete], ); } /** * {@inheritDoc} */ protected function _getPortableViewDefinition($view) { return new View($view['schemaname'] . '.' . $view['viewname'], $view['definition']); } /** * {@inheritDoc} */ protected function _getPortableTableDefinition($table) { $currentSchema = $this->getCurrentSchema(); if ($table['schema_name'] === $currentSchema) { return $table['table_name']; } return $table['schema_name'] . '.' . $table['table_name']; } /** * {@inheritDoc} * * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html */ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) { $buffer = []; foreach ($tableIndexes as $row) { $colNumbers = array_map('intval', explode(' ', $row['indkey'])); $columnNameSql = sprintf( 'SELECT attnum, attname FROM pg_attribute WHERE attrelid=%d AND attnum IN (%s) ORDER BY attnum ASC', $row['indrelid'], implode(' ,', $colNumbers), ); $indexColumns = $this->_conn->fetchAllAssociative($columnNameSql); // required for getting the order of the columns right. foreach ($colNumbers as $colNum) { foreach ($indexColumns as $colRow) { if ($colNum !== $colRow['attnum']) { continue; } $buffer[] = [ 'key_name' => $row['relname'], 'column_name' => trim($colRow['attname']), 'non_unique' => ! $row['indisunique'], 'primary' => $row['indisprimary'], 'where' => $row['where'], ]; } } } return parent::_getPortableTableIndexesList($buffer, $tableName); } /** * {@inheritDoc} */ protected function _getPortableDatabaseDefinition($database) { return $database['datname']; } /** * {@inheritDoc} * * @deprecated Use {@see listSchemaNames()} instead. */ protected function getPortableNamespaceDefinition(array $namespace) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4503', 'PostgreSQLSchemaManager::getPortableNamespaceDefinition() is deprecated,' . ' use PostgreSQLSchemaManager::listSchemaNames() instead.', ); return $namespace['nspname']; } /** * {@inheritDoc} */ protected function _getPortableSequenceDefinition($sequence) { if ($sequence['schemaname'] !== 'public') { $sequenceName = $sequence['schemaname'] . '.' . $sequence['relname']; } else { $sequenceName = $sequence['relname']; } return new Sequence($sequenceName, (int) $sequence['increment_by'], (int) $sequence['min_value']); } /** * {@inheritDoc} */ protected function _getPortableTableColumnDefinition($tableColumn) { $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); if (strtolower($tableColumn['type']) === 'varchar' || strtolower($tableColumn['type']) === 'bpchar') { // get length from varchar definition $length = preg_replace('~.*\(([0-9]*)\).*~', '$1', $tableColumn['complete_type']); $tableColumn['length'] = $length; } $matches = []; $autoincrement = false; if ( $tableColumn['default'] !== null && preg_match("/^nextval\('(.*)'(::.*)?\)$/", $tableColumn['default'], $matches) === 1 ) { $tableColumn['sequence'] = $matches[1]; $tableColumn['default'] = null; $autoincrement = true; } if ($tableColumn['default'] !== null) { if (preg_match("/^['(](.*)[')]::/", $tableColumn['default'], $matches) === 1) { $tableColumn['default'] = $matches[1]; } elseif (preg_match('/^NULL::/', $tableColumn['default']) === 1) { $tableColumn['default'] = null; } } $length = $tableColumn['length'] ?? null; if ($length === '-1' && isset($tableColumn['atttypmod'])) { $length = $tableColumn['atttypmod'] - 4; } if ((int) $length <= 0) { $length = null; } $fixed = null; if (! isset($tableColumn['name'])) { $tableColumn['name'] = ''; } $precision = null; $scale = null; $jsonb = null; $dbType = strtolower($tableColumn['type']); if ( $tableColumn['domain_type'] !== null && $tableColumn['domain_type'] !== '' && ! $this->_platform->hasDoctrineTypeMappingFor($tableColumn['type']) ) { $dbType = strtolower($tableColumn['domain_type']); $tableColumn['complete_type'] = $tableColumn['domain_complete_type']; } $type = $this->_platform->getDoctrineTypeMapping($dbType); $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); switch ($dbType) { case 'smallint': case 'int2': $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); $length = null; break; case 'int': case 'int4': case 'integer': $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); $length = null; break; case 'bigint': case 'int8': $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); $length = null; break; case 'bool': case 'boolean': if ($tableColumn['default'] === 'true') { $tableColumn['default'] = true; } if ($tableColumn['default'] === 'false') { $tableColumn['default'] = false; } $length = null; break; case 'json': case 'text': case '_varchar': case 'varchar': $tableColumn['default'] = $this->parseDefaultExpression($tableColumn['default']); $fixed = false; break; case 'interval': $fixed = false; break; case 'char': case 'bpchar': $fixed = true; break; case 'float': case 'float4': case 'float8': case 'double': case 'double precision': case 'real': case 'decimal': case 'money': case 'numeric': $tableColumn['default'] = $this->fixVersion94NegativeNumericDefaultValue($tableColumn['default']); if ( preg_match( '([A-Za-z]+\(([0-9]+),([0-9]+)\))', $tableColumn['complete_type'], $match, ) === 1 ) { $precision = $match[1]; $scale = $match[2]; $length = null; } break; case 'year': $length = null; break; // PostgreSQL 9.4+ only case 'jsonb': $jsonb = true; break; } if ( $tableColumn['default'] !== null && preg_match( "('([^']+)'::)", $tableColumn['default'], $match, ) === 1 ) { $tableColumn['default'] = $match[1]; } $options = [ 'length' => $length, 'notnull' => (bool) $tableColumn['isnotnull'], 'default' => $tableColumn['default'], 'precision' => $precision, 'scale' => $scale, 'fixed' => $fixed, 'autoincrement' => $autoincrement, 'comment' => isset($tableColumn['comment']) && $tableColumn['comment'] !== '' ? $tableColumn['comment'] : null, ]; $column = new Column($tableColumn['field'], Type::getType($type), $options); if (! empty($tableColumn['collation'])) { $column->setPlatformOption('collation', $tableColumn['collation']); } if ($column->getType()->getName() === Types::JSON) { if (! $column->getType() instanceof JsonType) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5049', <<<'DEPRECATION' %s not extending %s while being named %s is deprecated, and will lead to jsonb never to being used in 4.0., DEPRECATION, get_class($column->getType()), JsonType::class, Types::JSON, ); } $column->setPlatformOption('jsonb', $jsonb); } return $column; } /** * PostgreSQL 9.4 puts parentheses around negative numeric default values that need to be stripped eventually. * * @param mixed $defaultValue * * @return mixed */ private function fixVersion94NegativeNumericDefaultValue($defaultValue) { if ($defaultValue !== null && strpos($defaultValue, '(') === 0) { return trim($defaultValue, '()'); } return $defaultValue; } /** * Parses a default value expression as given by PostgreSQL */ private function parseDefaultExpression(?string $default): ?string { if ($default === null) { return $default; } return str_replace("''", "'", $default); } protected function selectTableNames(string $databaseName): Result { $sql = <<<'SQL' SELECT quote_ident(table_name) AS table_name, table_schema AS schema_name FROM information_schema.tables WHERE table_catalog = ? AND table_schema NOT LIKE 'pg\_%' AND table_schema != 'information_schema' AND table_name != 'geometry_columns' AND table_name != 'spatial_ref_sys' AND table_type = 'BASE TABLE' SQL; return $this->_conn->executeQuery($sql, [$databaseName]); } protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result { $sql = 'SELECT'; if ($tableName === null) { $sql .= ' c.relname AS table_name, n.nspname AS schema_name,'; } $sql .= <<<'SQL' a.attnum, quote_ident(a.attname) AS field, t.typname AS type, format_type(a.atttypid, a.atttypmod) AS complete_type, (SELECT tc.collcollate FROM pg_catalog.pg_collation tc WHERE tc.oid = a.attcollation) AS collation, (SELECT t1.typname FROM pg_catalog.pg_type t1 WHERE t1.oid = t.typbasetype) AS domain_type, (SELECT format_type(t2.typbasetype, t2.typtypmod) FROM pg_catalog.pg_type t2 WHERE t2.typtype = 'd' AND t2.oid = a.atttypid) AS domain_complete_type, a.attnotnull AS isnotnull, (SELECT 't' FROM pg_index WHERE c.oid = pg_index.indrelid AND pg_index.indkey[0] = a.attnum AND pg_index.indisprimary = 't' ) AS pri, (SELECT pg_get_expr(adbin, adrelid) FROM pg_attrdef WHERE c.oid = pg_attrdef.adrelid AND pg_attrdef.adnum=a.attnum ) AS default, (SELECT pg_description.description FROM pg_description WHERE pg_description.objoid = c.oid AND a.attnum = pg_description.objsubid ) AS comment FROM pg_attribute a INNER JOIN pg_class c ON c.oid = a.attrelid INNER JOIN pg_type t ON t.oid = a.atttypid INNER JOIN pg_namespace n ON n.oid = c.relnamespace LEFT JOIN pg_depend d ON d.objid = c.oid AND d.deptype = 'e' AND d.classid = (SELECT oid FROM pg_class WHERE relname = 'pg_class') SQL; $conditions = array_merge([ 'a.attnum > 0', "c.relkind = 'r'", 'd.refobjid IS NULL', ], $this->buildQueryConditions($tableName)); $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY a.attnum'; return $this->_conn->executeQuery($sql); } protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result { $sql = 'SELECT'; if ($tableName === null) { $sql .= ' tc.relname AS table_name, tn.nspname AS schema_name,'; } $sql .= <<<'SQL' quote_ident(ic.relname) AS relname, i.indisunique, i.indisprimary, i.indkey, i.indrelid, pg_get_expr(indpred, indrelid) AS "where" FROM pg_index i JOIN pg_class AS tc ON tc.oid = i.indrelid JOIN pg_namespace tn ON tn.oid = tc.relnamespace JOIN pg_class AS ic ON ic.oid = i.indexrelid WHERE ic.oid IN ( SELECT indexrelid FROM pg_index i, pg_class c, pg_namespace n SQL; $conditions = array_merge([ 'c.oid = i.indrelid', 'c.relnamespace = n.oid', ], $this->buildQueryConditions($tableName)); $sql .= ' WHERE ' . implode(' AND ', $conditions) . ')'; return $this->_conn->executeQuery($sql); } protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result { $sql = 'SELECT'; if ($tableName === null) { $sql .= ' tc.relname AS table_name, tn.nspname AS schema_name,'; } $sql .= <<<'SQL' quote_ident(r.conname) as conname, pg_get_constraintdef(r.oid, true) as condef FROM pg_constraint r JOIN pg_class AS tc ON tc.oid = r.conrelid JOIN pg_namespace tn ON tn.oid = tc.relnamespace WHERE r.conrelid IN ( SELECT c.oid FROM pg_class c, pg_namespace n SQL; $conditions = array_merge(['n.oid = c.relnamespace'], $this->buildQueryConditions($tableName)); $sql .= ' WHERE ' . implode(' AND ', $conditions) . ") AND r.contype = 'f'"; return $this->_conn->executeQuery($sql); } /** * {@inheritDoc} */ protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array { $sql = <<<'SQL' SELECT c.relname, CASE c.relpersistence WHEN 'u' THEN true ELSE false END as unlogged, obj_description(c.oid, 'pg_class') AS comment FROM pg_class c INNER JOIN pg_namespace n ON n.oid = c.relnamespace SQL; $conditions = array_merge(["c.relkind = 'r'"], $this->buildQueryConditions($tableName)); $sql .= ' WHERE ' . implode(' AND ', $conditions); return $this->_conn->fetchAllAssociativeIndexed($sql); } /** * @param string|null $tableName * * @return list */ private function buildQueryConditions($tableName): array { $conditions = []; if ($tableName !== null) { if (strpos($tableName, '.') !== false) { [$schemaName, $tableName] = explode('.', $tableName); $conditions[] = 'n.nspname = ' . $this->_platform->quoteStringLiteral($schemaName); } else { $conditions[] = 'n.nspname = ANY(current_schemas(false))'; } $identifier = new Identifier($tableName); $conditions[] = 'c.relname = ' . $this->_platform->quoteStringLiteral($identifier->getName()); } $conditions[] = "n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')"; return $conditions; } } PK!ӨYY'dbal/src/Schema/SqliteSchemaManager.phpnu[ */ class SqliteSchemaManager extends AbstractSchemaManager { /** * {@inheritDoc} */ public function listTableNames() { return $this->doListTableNames(); } /** * {@inheritDoc} */ public function listTables() { return $this->doListTables(); } /** * {@inheritDoc} * * @deprecated Use {@see introspectTable()} instead. */ public function listTableDetails($name) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5595', '%s is deprecated. Use introspectTable() instead.', __METHOD__, ); return $this->doListTableDetails($name); } /** * {@inheritDoc} */ public function listTableColumns($table, $database = null) { return $this->doListTableColumns($table, $database); } /** * {@inheritDoc} */ public function listTableIndexes($table) { return $this->doListTableIndexes($table); } /** * {@inheritDoc} */ protected function fetchForeignKeyColumnsByTable(string $databaseName): array { $columnsByTable = parent::fetchForeignKeyColumnsByTable($databaseName); if (count($columnsByTable) > 0) { foreach ($columnsByTable as $table => $columns) { $columnsByTable[$table] = $this->addDetailsToTableForeignKeyColumns($table, $columns); } } return $columnsByTable; } /** * {@inheritDoc} * * @deprecated Delete the database file using the filesystem. */ public function dropDatabase($database) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4963', 'SqliteSchemaManager::dropDatabase() is deprecated. Delete the database file using the filesystem.', ); if (! file_exists($database)) { return; } unlink($database); } /** * {@inheritDoc} * * @deprecated The engine will create the database file automatically. */ public function createDatabase($database) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4963', 'SqliteSchemaManager::createDatabase() is deprecated.' . ' The engine will create the database file automatically.', ); $params = $this->_conn->getParams(); $params['path'] = $database; unset($params['memory']); $conn = DriverManager::getConnection($params); $conn->connect(); $conn->close(); } /** * {@inheritDoc} */ public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) { if (! $table instanceof Table) { $table = $this->listTableDetails($table); } $this->alterTable(new TableDiff($table->getName(), [], [], [], [], [], [], $table, [$foreignKey])); } /** * {@inheritDoc} * * @deprecated Use {@see dropForeignKey()} and {@see createForeignKey()} instead. */ public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4897', 'SqliteSchemaManager::dropAndCreateForeignKey() is deprecated.' . ' Use SqliteSchemaManager::dropForeignKey() and SqliteSchemaManager::createForeignKey() instead.', ); if (! $table instanceof Table) { $table = $this->listTableDetails($table); } $this->alterTable(new TableDiff($table->getName(), [], [], [], [], [], [], $table, [], [$foreignKey])); } /** * {@inheritDoc} */ public function dropForeignKey($foreignKey, $table) { if (! $table instanceof Table) { $table = $this->listTableDetails($table); } $this->alterTable(new TableDiff($table->getName(), [], [], [], [], [], [], $table, [], [], [$foreignKey])); } /** * {@inheritDoc} */ public function listTableForeignKeys($table, $database = null) { $table = $this->normalizeName($table); $columns = $this->selectForeignKeyColumns('', $table) ->fetchAllAssociative(); if (count($columns) > 0) { $columns = $this->addDetailsToTableForeignKeyColumns($table, $columns); } return $this->_getPortableTableForeignKeysList($columns); } /** * {@inheritDoc} */ protected function _getPortableTableDefinition($table) { return $table['table_name']; } /** * {@inheritDoc} * * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html */ protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) { $indexBuffer = []; // fetch primary $indexArray = $this->_conn->fetchAllAssociative('SELECT * FROM PRAGMA_TABLE_INFO (?)', [$tableName]); usort( $indexArray, /** * @param array $a * @param array $b */ static function (array $a, array $b): int { if ($a['pk'] === $b['pk']) { return $a['cid'] - $b['cid']; } return $a['pk'] - $b['pk']; }, ); foreach ($indexArray as $indexColumnRow) { if ($indexColumnRow['pk'] === 0 || $indexColumnRow['pk'] === '0') { continue; } $indexBuffer[] = [ 'key_name' => 'primary', 'primary' => true, 'non_unique' => false, 'column_name' => $indexColumnRow['name'], ]; } // fetch regular indexes foreach ($tableIndexes as $tableIndex) { // Ignore indexes with reserved names, e.g. autoindexes if (strpos($tableIndex['name'], 'sqlite_') === 0) { continue; } $keyName = $tableIndex['name']; $idx = []; $idx['key_name'] = $keyName; $idx['primary'] = false; $idx['non_unique'] = ! $tableIndex['unique']; $indexArray = $this->_conn->fetchAllAssociative('SELECT * FROM PRAGMA_INDEX_INFO (?)', [$keyName]); foreach ($indexArray as $indexColumnRow) { $idx['column_name'] = $indexColumnRow['name']; $indexBuffer[] = $idx; } } return parent::_getPortableTableIndexesList($indexBuffer, $tableName); } /** * {@inheritDoc} */ protected function _getPortableTableColumnList($table, $database, $tableColumns) { $list = parent::_getPortableTableColumnList($table, $database, $tableColumns); // find column with autoincrement $autoincrementColumn = null; $autoincrementCount = 0; foreach ($tableColumns as $tableColumn) { if ($tableColumn['pk'] === 0 || $tableColumn['pk'] === '0') { continue; } $autoincrementCount++; if ($autoincrementColumn !== null || strtolower($tableColumn['type']) !== 'integer') { continue; } $autoincrementColumn = $tableColumn['name']; } if ($autoincrementCount === 1 && $autoincrementColumn !== null) { foreach ($list as $column) { if ($autoincrementColumn !== $column->getName()) { continue; } $column->setAutoincrement(true); } } // inspect column collation and comments $createSql = $this->getCreateTableSQL($table); foreach ($list as $columnName => $column) { $type = $column->getType(); if ($type instanceof StringType || $type instanceof TextType) { $column->setPlatformOption( 'collation', $this->parseColumnCollationFromSQL($columnName, $createSql) ?? 'BINARY', ); } $comment = $this->parseColumnCommentFromSQL($columnName, $createSql); if ($comment === null) { continue; } $type = $this->extractDoctrineTypeFromComment($comment, ''); if ($type !== '') { $column->setType(Type::getType($type)); $comment = $this->removeDoctrineTypeFromComment($comment, $type); } $column->setComment($comment); } return $list; } /** * {@inheritDoc} */ protected function _getPortableTableColumnDefinition($tableColumn) { $parts = explode('(', $tableColumn['type']); $tableColumn['type'] = trim($parts[0]); if (isset($parts[1])) { $length = trim($parts[1], ')'); $tableColumn['length'] = $length; } $dbType = strtolower($tableColumn['type']); $length = $tableColumn['length'] ?? null; $unsigned = false; if (strpos($dbType, ' unsigned') !== false) { $dbType = str_replace(' unsigned', '', $dbType); $unsigned = true; } $fixed = false; $type = $this->_platform->getDoctrineTypeMapping($dbType); $default = $tableColumn['dflt_value']; if ($default === 'NULL') { $default = null; } if ($default !== null) { // SQLite returns the default value as a literal expression, so we need to parse it if (preg_match('/^\'(.*)\'$/s', $default, $matches) === 1) { $default = str_replace("''", "'", $matches[1]); } } $notnull = (bool) $tableColumn['notnull']; if (! isset($tableColumn['name'])) { $tableColumn['name'] = ''; } $precision = null; $scale = null; switch ($dbType) { case 'char': $fixed = true; break; case 'float': case 'double': case 'real': case 'decimal': case 'numeric': if (isset($tableColumn['length'])) { if (strpos($tableColumn['length'], ',') === false) { $tableColumn['length'] .= ',0'; } [$precision, $scale] = array_map('trim', explode(',', $tableColumn['length'])); } $length = null; break; } $options = [ 'length' => $length, 'unsigned' => $unsigned, 'fixed' => $fixed, 'notnull' => $notnull, 'default' => $default, 'precision' => $precision, 'scale' => $scale, ]; return new Column($tableColumn['name'], Type::getType($type), $options); } /** * {@inheritDoc} */ protected function _getPortableViewDefinition($view) { return new View($view['name'], $view['sql']); } /** * {@inheritDoc} */ protected function _getPortableTableForeignKeysList($tableForeignKeys) { $list = []; foreach ($tableForeignKeys as $value) { $value = array_change_key_case($value, CASE_LOWER); $id = $value['id']; if (! isset($list[$id])) { if (! isset($value['on_delete']) || $value['on_delete'] === 'RESTRICT') { $value['on_delete'] = null; } if (! isset($value['on_update']) || $value['on_update'] === 'RESTRICT') { $value['on_update'] = null; } $list[$id] = [ 'name' => $value['constraint_name'], 'local' => [], 'foreign' => [], 'foreignTable' => $value['table'], 'onDelete' => $value['on_delete'], 'onUpdate' => $value['on_update'], 'deferrable' => $value['deferrable'], 'deferred' => $value['deferred'], ]; } $list[$id]['local'][] = $value['from']; if ($value['to'] === null) { // Inferring a shorthand form for the foreign key constraint, where the "to" field is empty. // @see https://www.sqlite.org/foreignkeys.html#fk_indexes. $foreignTableIndexes = $this->_getPortableTableIndexesList([], $value['table']); if (! isset($foreignTableIndexes['primary'])) { continue; } $list[$id]['foreign'] = [...$list[$id]['foreign'], ...$foreignTableIndexes['primary']->getColumns()]; continue; } $list[$id]['foreign'][] = $value['to']; } return parent::_getPortableTableForeignKeysList($list); } /** * {@inheritDoc} */ protected function _getPortableTableForeignKeyDefinition($tableForeignKey): ForeignKeyConstraint { return new ForeignKeyConstraint( $tableForeignKey['local'], $tableForeignKey['foreignTable'], $tableForeignKey['foreign'], $tableForeignKey['name'], [ 'onDelete' => $tableForeignKey['onDelete'], 'onUpdate' => $tableForeignKey['onUpdate'], 'deferrable' => $tableForeignKey['deferrable'], 'deferred' => $tableForeignKey['deferred'], ], ); } private function parseColumnCollationFromSQL(string $column, string $sql): ?string { $pattern = '{(?:\W' . preg_quote($column) . '\W|\W' . preg_quote($this->_platform->quoteSingleIdentifier($column)) . '\W)[^,(]+(?:\([^()]+\)[^,]*)?(?:(?:DEFAULT|CHECK)\s*(?:\(.*?\))?[^,]*)*COLLATE\s+["\']?([^\s,"\')]+)}is'; if (preg_match($pattern, $sql, $match) !== 1) { return null; } return $match[1]; } private function parseTableCommentFromSQL(string $table, string $sql): ?string { $pattern = '/\s* # Allow whitespace characters at start of line CREATE\sTABLE # Match "CREATE TABLE" (?:\W"' . preg_quote($this->_platform->quoteSingleIdentifier($table), '/') . '"\W|\W' . preg_quote($table, '/') . '\W) # Match table name (quoted and unquoted) ( # Start capture (?:\s*--[^\n]*\n?)+ # Capture anything that starts with whitespaces followed by -- until the end of the line(s) )/ix'; if (preg_match($pattern, $sql, $match) !== 1) { return null; } $comment = preg_replace('{^\s*--}m', '', rtrim($match[1], "\n")); return $comment === '' ? null : $comment; } private function parseColumnCommentFromSQL(string $column, string $sql): ?string { $pattern = '{[\s(,](?:\W' . preg_quote($this->_platform->quoteSingleIdentifier($column)) . '\W|\W' . preg_quote($column) . '\W)(?:\([^)]*?\)|[^,(])*?,?((?:(?!\n))(?:\s*--[^\n]*\n?)+)}i'; if (preg_match($pattern, $sql, $match) !== 1) { return null; } $comment = preg_replace('{^\s*--}m', '', rtrim($match[1], "\n")); return $comment === '' ? null : $comment; } /** @throws Exception */ private function getCreateTableSQL(string $table): string { $sql = $this->_conn->fetchOne( <<<'SQL' SELECT sql FROM ( SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master ) WHERE type = 'table' AND name = ? SQL , [$table], ); if ($sql !== false) { return $sql; } return ''; } /** * @param list> $columns * * @return list> * * @throws Exception */ private function addDetailsToTableForeignKeyColumns(string $table, array $columns): array { $foreignKeyDetails = $this->getForeignKeyDetails($table); $foreignKeyCount = count($foreignKeyDetails); foreach ($columns as $i => $column) { // SQLite identifies foreign keys in reverse order of appearance in SQL $columns[$i] = array_merge($column, $foreignKeyDetails[$foreignKeyCount - $column['id'] - 1]); } return $columns; } /** * @param string $table * * @return list> * * @throws Exception */ private function getForeignKeyDetails($table) { $createSql = $this->getCreateTableSQL($table); if ( preg_match_all( '# (?:CONSTRAINT\s+(\S+)\s+)? (?:FOREIGN\s+KEY[^)]+\)\s*)? REFERENCES\s+\S+\s*(?:\([^)]+\))? (?: [^,]*? (NOT\s+DEFERRABLE|DEFERRABLE) (?:\s+INITIALLY\s+(DEFERRED|IMMEDIATE))? )?#isx', $createSql, $match, ) === 0 ) { return []; } $names = $match[1]; $deferrable = $match[2]; $deferred = $match[3]; $details = []; for ($i = 0, $count = count($match[0]); $i < $count; $i++) { $details[] = [ 'constraint_name' => isset($names[$i]) && $names[$i] !== '' ? $names[$i] : null, 'deferrable' => isset($deferrable[$i]) && strcasecmp($deferrable[$i], 'deferrable') === 0, 'deferred' => isset($deferred[$i]) && strcasecmp($deferred[$i], 'deferred') === 0, ]; } return $details; } public function createComparator(): Comparator { return new SQLite\Comparator($this->_platform); } /** * {@inheritDoc} * * @deprecated */ public function getSchemaSearchPaths() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4821', 'SqliteSchemaManager::getSchemaSearchPaths() is deprecated.', ); // SQLite does not support schemas or databases return []; } protected function selectTableNames(string $databaseName): Result { $sql = <<<'SQL' SELECT name AS table_name FROM sqlite_master WHERE type = 'table' AND name != 'sqlite_sequence' AND name != 'geometry_columns' AND name != 'spatial_ref_sys' UNION ALL SELECT name FROM sqlite_temp_master WHERE type = 'table' ORDER BY name SQL; return $this->_conn->executeQuery($sql); } protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result { $sql = <<<'SQL' SELECT t.name AS table_name, c.* FROM sqlite_master t JOIN pragma_table_info(t.name) c SQL; $conditions = [ "t.type = 'table'", "t.name NOT IN ('geometry_columns', 'spatial_ref_sys', 'sqlite_sequence')", ]; $params = []; if ($tableName !== null) { $conditions[] = 't.name = ?'; $params[] = str_replace('.', '__', $tableName); } $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY t.name, c.cid'; return $this->_conn->executeQuery($sql, $params); } protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result { $sql = <<<'SQL' SELECT t.name AS table_name, i.* FROM sqlite_master t JOIN pragma_index_list(t.name) i SQL; $conditions = [ "t.type = 'table'", "t.name NOT IN ('geometry_columns', 'spatial_ref_sys', 'sqlite_sequence')", ]; $params = []; if ($tableName !== null) { $conditions[] = 't.name = ?'; $params[] = str_replace('.', '__', $tableName); } $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY t.name, i.seq'; return $this->_conn->executeQuery($sql, $params); } protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result { $sql = <<<'SQL' SELECT t.name AS table_name, p.* FROM sqlite_master t JOIN pragma_foreign_key_list(t.name) p ON p."seq" != '-1' SQL; $conditions = [ "t.type = 'table'", "t.name NOT IN ('geometry_columns', 'spatial_ref_sys', 'sqlite_sequence')", ]; $params = []; if ($tableName !== null) { $conditions[] = 't.name = ?'; $params[] = str_replace('.', '__', $tableName); } $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY t.name, p.id DESC, p.seq'; return $this->_conn->executeQuery($sql, $params); } /** * {@inheritDoc} */ protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array { if ($tableName === null) { $tables = $this->listTableNames(); } else { $tables = [$tableName]; } $tableOptions = []; foreach ($tables as $table) { $comment = $this->parseTableCommentFromSQL($table, $this->getCreateTableSQL($table)); if ($comment === null) { continue; } $tableOptions[$table]['comment'] = $comment; } return $tableOptions; } } PK!u! ! dbal/src/Schema/Sequence.phpnu[_setName($name); $this->setAllocationSize($allocationSize); $this->setInitialValue($initialValue); $this->cache = $cache; } /** @return int */ public function getAllocationSize() { return $this->allocationSize; } /** @return int */ public function getInitialValue() { return $this->initialValue; } /** @return int|null */ public function getCache() { return $this->cache; } /** * @param int $allocationSize * * @return Sequence */ public function setAllocationSize($allocationSize) { if ($allocationSize > 0) { $this->allocationSize = $allocationSize; } else { $this->allocationSize = 1; } return $this; } /** * @param int $initialValue * * @return Sequence */ public function setInitialValue($initialValue) { if ($initialValue > 0) { $this->initialValue = $initialValue; } else { $this->initialValue = 1; } return $this; } /** * @param int $cache * * @return Sequence */ public function setCache($cache) { $this->cache = $cache; return $this; } /** * Checks if this sequence is an autoincrement sequence for a given table. * * This is used inside the comparator to not report sequences as missing, * when the "from" schema implicitly creates the sequences. * * @return bool */ public function isAutoIncrementsFor(Table $table) { $primaryKey = $table->getPrimaryKey(); if ($primaryKey === null) { return false; } $pkColumns = $primaryKey->getColumns(); if (count($pkColumns) !== 1) { return false; } $column = $table->getColumn($pkColumns[0]); if (! $column->getAutoincrement()) { return false; } $sequenceName = $this->getShortestName($table->getNamespaceName()); $tableName = $table->getShortestName($table->getNamespaceName()); $tableSequenceName = sprintf('%s_%s_seq', $tableName, $column->getShortestName($table->getNamespaceName())); return $tableSequenceName === $sequenceName; } /** * @deprecated * * @return void */ public function visit(Visitor $visitor) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5435', 'Sequence::visit() is deprecated.', ); $visitor->acceptSequence($this); } } PK!\Vʈ'dbal/src/VersionAwarePlatformDriver.phpnu[column = $column; $this->tableDiff = $tableDiff; $this->platform = $platform; } /** @return Column */ public function getColumn() { return $this->column; } /** @return TableDiff */ public function getTableDiff() { return $this->tableDiff; } /** @return AbstractPlatform */ public function getPlatform() { return $this->platform; } /** * Passing multiple SQL statements as an array is deprecated. Pass each statement as an individual argument instead. * * @param string|string[] $sql * * @return SchemaAlterTableRemoveColumnEventArgs */ public function addSql($sql) { $this->sql = array_merge($this->sql, is_array($sql) ? $sql : func_get_args()); return $this; } /** @return string[] */ public function getSql() { return $this->sql; } } PK!B򿂘/dbal/src/Event/TransactionRollBackEventArgs.phpnu[columnDiff = $columnDiff; $this->tableDiff = $tableDiff; $this->platform = $platform; } /** @return ColumnDiff */ public function getColumnDiff() { return $this->columnDiff; } /** @return TableDiff */ public function getTableDiff() { return $this->tableDiff; } /** @return AbstractPlatform */ public function getPlatform() { return $this->platform; } /** * Passing multiple SQL statements as an array is deprecated. Pass each statement as an individual argument instead. * * @param string|string[] $sql * * @return SchemaAlterTableChangeColumnEventArgs */ public function addSql($sql) { $this->sql = array_merge($this->sql, is_array($sql) ? $sql : func_get_args()); return $this; } /** @return string[] */ public function getSql() { return $this->sql; } } PK!I6ޖ-dbal/src/Event/TransactionCommitEventArgs.phpnu[connection = $connection; } public function getConnection(): Connection { return $this->connection; } } PK!3  2dbal/src/Event/SchemaColumnDefinitionEventArgs.phpnu[tableColumn = $tableColumn; $this->table = $table; $this->database = $database; $this->connection = $connection; } /** * Allows to clear the column which means the column will be excluded from * tables column list. * * @return SchemaColumnDefinitionEventArgs */ public function setColumn(?Column $column = null) { $this->column = $column; return $this; } /** @return Column|null */ public function getColumn() { return $this->column; } /** @return mixed[] */ public function getTableColumn() { return $this->tableColumn; } /** @return string */ public function getTable() { return $this->table; } /** @return string */ public function getDatabase() { return $this->database; } /** @return Connection */ public function getConnection() { return $this->connection; } } PK!5td;;3dbal/src/Event/SchemaCreateTableColumnEventArgs.phpnu[column = $column; $this->table = $table; $this->platform = $platform; } /** @return Column */ public function getColumn() { return $this->column; } /** @return Table */ public function getTable() { return $this->table; } /** @return AbstractPlatform */ public function getPlatform() { return $this->platform; } /** * Passing multiple SQL statements as an array is deprecated. Pass each statement as an individual argument instead. * * @param string|string[] $sql * * @return SchemaCreateTableColumnEventArgs */ public function addSql($sql) { $this->sql = array_merge($this->sql, is_array($sql) ? $sql : func_get_args()); return $this; } /** @return string[] */ public function getSql() { return $this->sql; } } PK!8dbal/src/Event/SchemaAlterTableRenameColumnEventArgs.phpnu[oldColumnName = $oldColumnName; $this->column = $column; $this->tableDiff = $tableDiff; $this->platform = $platform; } /** @return string */ public function getOldColumnName() { return $this->oldColumnName; } /** @return Column */ public function getColumn() { return $this->column; } /** @return TableDiff */ public function getTableDiff() { return $this->tableDiff; } /** @return AbstractPlatform */ public function getPlatform() { return $this->platform; } /** * Passing multiple SQL statements as an array is deprecated. Pass each statement as an individual argument instead. * * @param string|string[] $sql * * @return SchemaAlterTableRenameColumnEventArgs */ public function addSql($sql) { $this->sql = array_merge($this->sql, is_array($sql) ? $sql : func_get_args()); return $this; } /** @return string[] */ public function getSql() { return $this->sql; } } PK!ww,dbal/src/Event/SchemaAlterTableEventArgs.phpnu[tableDiff = $tableDiff; $this->platform = $platform; } /** @return TableDiff */ public function getTableDiff() { return $this->tableDiff; } /** @return AbstractPlatform */ public function getPlatform() { return $this->platform; } /** * Passing multiple SQL statements as an array is deprecated. Pass each statement as an individual argument instead. * * @param string|string[] $sql * * @return SchemaAlterTableEventArgs */ public function addSql($sql) { $this->sql = array_merge($this->sql, is_array($sql) ? $sql : func_get_args()); return $this; } /** @return string[] */ public function getSql() { return $this->sql; } } PK!'&dbal/src/Event/ConnectionEventArgs.phpnu[connection = $connection; } /** @return Connection */ public function getConnection() { return $this->connection; } } PK!.x#5dbal/src/Event/SchemaAlterTableAddColumnEventArgs.phpnu[column = $column; $this->tableDiff = $tableDiff; $this->platform = $platform; } /** @return Column */ public function getColumn() { return $this->column; } /** @return TableDiff */ public function getTableDiff() { return $this->tableDiff; } /** @return AbstractPlatform */ public function getPlatform() { return $this->platform; } /** * Passing multiple SQL statements as an array is deprecated. Pass each statement as an individual argument instead. * * @param string|string[] $sql * * @return SchemaAlterTableAddColumnEventArgs */ public function addSql($sql) { if (is_array($sql)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3580', 'Passing multiple SQL statements as an array to SchemaAlterTableAddColumnEventaArrgs::addSql() ' . 'is deprecated. Pass each statement as an individual argument instead.', ); } $this->sql = array_merge($this->sql, is_array($sql) ? $sql : func_get_args()); return $this; } /** @return string[] */ public function getSql() { return $this->sql; } } PK!"dbal/src/Event/SchemaEventArgs.phpnu[preventDefault = true; return $this; } /** @return bool */ public function isDefaultPrevented() { return $this->preventDefault; } } PK!|,dbal/src/Event/TransactionBeginEventArgs.phpnu[tableIndex = $tableIndex; $this->table = $table; $this->connection = $connection; } /** * Allows to clear the index which means the index will be excluded from tables index list. * * @return SchemaIndexDefinitionEventArgs */ public function setIndex(?Index $index = null) { $this->index = $index; return $this; } /** @return Index|null */ public function getIndex() { return $this->index; } /** @return mixed[] */ public function getTableIndex() { return $this->tableIndex; } /** @return string */ public function getTable() { return $this->table; } /** @return Connection */ public function getConnection() { return $this->connection; } } PK!h9!LL-dbal/src/Event/SchemaCreateTableEventArgs.phpnu[table = $table; $this->columns = $columns; $this->options = $options; $this->platform = $platform; } /** @return Table */ public function getTable() { return $this->table; } /** @return mixed[][] */ public function getColumns() { return $this->columns; } /** @return mixed[] */ public function getOptions() { return $this->options; } /** @return AbstractPlatform */ public function getPlatform() { return $this->platform; } /** * Passing multiple SQL statements as an array is deprecated. Pass each statement as an individual argument instead. * * @param string|string[] $sql * * @return SchemaCreateTableEventArgs */ public function addSql($sql) { $this->sql = array_merge($this->sql, is_array($sql) ? $sql : func_get_args()); return $this; } /** @return string[] */ public function getSql() { return $this->sql; } } PK!=Ӻ.dbal/src/Event/Listeners/SQLiteSessionInit.phpnu[getConnection()->executeStatement('PRAGMA foreign_keys=ON'); } /** * {@inheritDoc} */ public function getSubscribedEvents() { return [Events::postConnect]; } } PK!*nn+dbal/src/Event/Listeners/SQLSessionInit.phpnu[sql = $sql; } /** * @return void * * @throws Exception */ public function postConnect(ConnectionEventArgs $args) { $args->getConnection()->executeStatement($this->sql); } /** * {@inheritDoc} */ public function getSubscribedEvents() { return [Events::postConnect]; } } PK!3 .dbal/src/Event/Listeners/OracleSessionInit.phpnu[ 'HH24:MI:SS', 'NLS_DATE_FORMAT' => 'YYYY-MM-DD HH24:MI:SS', 'NLS_TIMESTAMP_FORMAT' => 'YYYY-MM-DD HH24:MI:SS', 'NLS_TIMESTAMP_TZ_FORMAT' => 'YYYY-MM-DD HH24:MI:SS TZH:TZM', 'NLS_NUMERIC_CHARACTERS' => '.,', ]; /** @param string[] $oracleSessionVars */ public function __construct(array $oracleSessionVars = []) { $this->_defaultSessionVars = array_merge($this->_defaultSessionVars, $oracleSessionVars); } /** * @return void * * @throws Exception */ public function postConnect(ConnectionEventArgs $args) { if (count($this->_defaultSessionVars) === 0) { return; } $vars = []; foreach (array_change_key_case($this->_defaultSessionVars, CASE_UPPER) as $option => $value) { if ($option === 'CURRENT_SCHEMA') { $vars[] = $option . ' = ' . $value; } else { $vars[] = $option . " = '" . $value . "'"; } } $sql = 'ALTER SESSION SET ' . implode(' ', $vars); $args->getConnection()->executeStatement($sql); } /** * {@inheritDoc} */ public function getSubscribedEvents() { return [Events::postConnect]; } } PK!v+i+dbal/src/Event/SchemaDropTableEventArgs.phpnu[table = $table; $this->platform = $platform; } /** @return string|Table */ public function getTable() { return $this->table; } /** @return AbstractPlatform */ public function getPlatform() { return $this->platform; } /** * @param string $sql * * @return SchemaDropTableEventArgs */ public function setSql($sql) { $this->sql = $sql; return $this; } /** @return string|null */ public function getSql() { return $this->sql; } } PK!dbal/src/Types/FloatType.phpnu[getFloatDeclarationSQL($column); } /** * {@inheritDoc} * * @param T $value * * @return (T is null ? null : float) * * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { return $value === null ? null : (float) $value; } } PK!=66dbal/src/Types/ArrayType.phpnu[getClobTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { // @todo 3.0 - $value === null check to save real NULL in database return serialize($value); } /** * {@inheritDoc} */ public function convertToPHPValue($value, AbstractPlatform $platform) { if ($value === null) { return null; } $value = is_resource($value) ? stream_get_contents($value) : $value; set_error_handler(function (int $code, string $message): bool { if ($code === E_DEPRECATED || $code === E_USER_DEPRECATED) { return false; } throw ConversionException::conversionFailedUnserialization($this->getName(), $message); }); try { return unserialize($value); } finally { restore_error_handler(); } } /** * {@inheritDoc} */ public function getName() { return Types::ARRAY; } /** * {@inheritDoc} * * @deprecated */ public function requiresSQLCommentHint(AbstractPlatform $platform) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5509', '%s is deprecated.', __METHOD__, ); return true; } } PK!K!dbal/src/Types/DateTimeTzType.phpnu[getDateTimeTzTypeDeclarationSQL($column); } /** * {@inheritDoc} * * @param T $value * * @return (T is null ? null : string) * * @template T */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { if ($value === null) { return $value; } if ($value instanceof DateTimeImmutable) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6017', 'Passing an instance of %s is deprecated, use %s::%s() instead.', get_class($value), DateTimeTzImmutableType::class, __FUNCTION__, ); } if ($value instanceof DateTimeInterface) { return $value->format($platform->getDateTimeTzFormatString()); } throw ConversionException::conversionFailedInvalidType( $value, $this->getName(), ['null', DateTime::class], ); } /** * {@inheritDoc} * * @param T $value * * @return (T is null ? null : DateTimeInterface) * * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { if ($value instanceof DateTimeImmutable) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6017', 'Passing an instance of %s is deprecated, use %s::%s() instead.', get_class($value), DateTimeTzImmutableType::class, __FUNCTION__, ); } if ($value === null || $value instanceof DateTimeInterface) { return $value; } $dateTime = DateTime::createFromFormat($platform->getDateTimeTzFormatString(), $value); if ($dateTime !== false) { return $dateTime; } throw ConversionException::conversionFailedFormat( $value, $this->getName(), $platform->getDateTimeTzFormatString(), ); } } PK!ã(dbal/src/Types/PhpIntegerMappingType.phpnu[getClobTypeDeclarationSQL($column); } /** * {@inheritDoc} * * @param mixed $value * * @return string */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { return serialize($value); } /** * {@inheritDoc} */ public function convertToPHPValue($value, AbstractPlatform $platform) { if ($value === null) { return null; } $value = is_resource($value) ? stream_get_contents($value) : $value; set_error_handler(function (int $code, string $message): bool { throw ConversionException::conversionFailedUnserialization($this->getName(), $message); }); try { return unserialize($value); } finally { restore_error_handler(); } } /** * {@inheritDoc} */ public function getName() { return Types::OBJECT; } /** * {@inheritDoc} * * @deprecated */ public function requiresSQLCommentHint(AbstractPlatform $platform) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5509', '%s is deprecated.', __METHOD__, ); return true; } } PK!Aoo&dbal/src/Types/ConversionException.phpnu[ 32 ? substr($value, 0, 20) . '...' : $value; return new self('Could not convert database value "' . $value . '" to Doctrine Type ' . $toType, 0, $previous); } /** * Thrown when a Database to Doctrine Type Conversion fails and we can make a statement * about the expected format. * * @param mixed $value * @param string $toType * @param string $expectedFormat * * @return ConversionException */ public static function conversionFailedFormat($value, $toType, $expectedFormat, ?Throwable $previous = null) { $value = strlen($value) > 32 ? substr($value, 0, 20) . '...' : $value; return new self( 'Could not convert database value "' . $value . '" to Doctrine Type ' . $toType . '. Expected format: ' . $expectedFormat, 0, $previous, ); } /** * Thrown when the PHP value passed to the converter was not of the expected type. * * @param mixed $value * @param string $toType * @param string[] $possibleTypes * * @return ConversionException */ public static function conversionFailedInvalidType( $value, $toType, array $possibleTypes, ?Throwable $previous = null ) { if (is_scalar($value) || $value === null) { return new self(sprintf( 'Could not convert PHP value %s to type %s. Expected one of the following types: %s', var_export($value, true), $toType, implode(', ', $possibleTypes), ), 0, $previous); } return new self(sprintf( 'Could not convert PHP value of type %s to type %s. Expected one of the following types: %s', is_object($value) ? get_class($value) : gettype($value), $toType, implode(', ', $possibleTypes), ), 0, $previous); } /** * @param mixed $value * @param string $format * @param string $error * * @return ConversionException */ public static function conversionFailedSerialization($value, $format, $error /*, ?Throwable $previous = null */) { $actualType = is_object($value) ? get_class($value) : gettype($value); return new self(sprintf( "Could not convert PHP type '%s' to '%s', as an '%s' error was triggered by the serialization", $actualType, $format, $error, ), 0, func_num_args() >= 4 ? func_get_arg(3) : null); } public static function conversionFailedUnserialization(string $format, string $error): self { return new self(sprintf( "Could not convert database value to '%s' as an error was triggered by the unserialization: '%s'", $format, $error, )); } } PK!tidbal/src/Types/SmallIntType.phpnu[getSmallIntTypeDeclarationSQL($column); } /** * {@inheritDoc} * * @param T $value * * @return (T is null ? null : int) * * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { return $value === null ? null : (int) $value; } /** * {@inheritDoc} */ public function getBindingType() { return ParameterType::INTEGER; } } PK!@sdbal/src/Types/JsonType.phpnu[getJsonTypeDeclarationSQL($column); } /** * {@inheritDoc} * * @param T $value * * @return (T is null ? null : string) * * @template T */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { if ($value === null) { return null; } try { return json_encode($value, JSON_THROW_ON_ERROR | JSON_PRESERVE_ZERO_FRACTION); } catch (JsonException $e) { throw ConversionException::conversionFailedSerialization($value, 'json', $e->getMessage(), $e); } } /** * {@inheritDoc} */ public function convertToPHPValue($value, AbstractPlatform $platform) { if ($value === null || $value === '') { return null; } if (is_resource($value)) { $value = stream_get_contents($value); } try { return json_decode($value, true, 512, JSON_THROW_ON_ERROR); } catch (JsonException $e) { throw ConversionException::conversionFailed($value, $this->getName(), $e); } } /** * {@inheritDoc} */ public function getName() { return Types::JSON; } /** * {@inheritDoc} * * @deprecated */ public function requiresSQLCommentHint(AbstractPlatform $platform) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5509', '%s is deprecated.', __METHOD__, ); return ! $platform->hasNativeJsonType(); } } PK!;dbal/src/Types/GuidType.phpnu[getGuidTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function getName() { return Types::GUID; } /** * {@inheritDoc} * * @deprecated */ public function requiresSQLCommentHint(AbstractPlatform $platform) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5509', '%s is deprecated.', __METHOD__, ); return ! $platform->hasNativeGuidType(); } } PK!Qߦ dbal/src/Types/DateTimeType.phpnu[getDateTimeTypeDeclarationSQL($column); } /** * {@inheritDoc} * * @param T $value * * @return (T is null ? null : string) * * @template T */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { if ($value === null) { return $value; } if ($value instanceof DateTimeImmutable) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6017', 'Passing an instance of %s is deprecated, use %s::%s() instead.', get_class($value), DateTimeImmutableType::class, __FUNCTION__, ); } if ($value instanceof DateTimeInterface) { return $value->format($platform->getDateTimeFormatString()); } throw ConversionException::conversionFailedInvalidType( $value, $this->getName(), ['null', DateTime::class, DateTimeImmutable::class], ); } /** * {@inheritDoc} * * @param T $value * * @return (T is null ? null : DateTimeInterface) * * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { if ($value instanceof DateTimeImmutable) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6017', 'Passing an instance of %s is deprecated, use %s::%s() instead.', get_class($value), DateTimeImmutableType::class, __FUNCTION__, ); } if ($value === null || $value instanceof DateTimeInterface) { return $value; } $dateTime = DateTime::createFromFormat($platform->getDateTimeFormatString(), $value); if ($dateTime !== false) { return $dateTime; } try { return new DateTime($value); } catch (Exception $e) { throw ConversionException::conversionFailedFormat( $value, $this->getName(), $platform->getDateTimeFormatString(), $e, ); } } } PK!eGdbal/src/Types/IntegerType.phpnu[getIntegerTypeDeclarationSQL($column); } /** * {@inheritDoc} * * @param T $value * * @return (T is null ? null : int) * * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { return $value === null ? null : (int) $value; } /** * {@inheritDoc} */ public function getBindingType() { return ParameterType::INTEGER; } } PK!YXs"s"dbal/src/Types/Type.phpnu[ ArrayType::class, Types::ASCII_STRING => AsciiStringType::class, Types::BIGINT => BigIntType::class, Types::BINARY => BinaryType::class, Types::BLOB => BlobType::class, Types::BOOLEAN => BooleanType::class, Types::DATE_MUTABLE => DateType::class, Types::DATE_IMMUTABLE => DateImmutableType::class, Types::DATEINTERVAL => DateIntervalType::class, Types::DATETIME_MUTABLE => DateTimeType::class, Types::DATETIME_IMMUTABLE => DateTimeImmutableType::class, Types::DATETIMETZ_MUTABLE => DateTimeTzType::class, Types::DATETIMETZ_IMMUTABLE => DateTimeTzImmutableType::class, Types::DECIMAL => DecimalType::class, Types::FLOAT => FloatType::class, Types::GUID => GuidType::class, Types::INTEGER => IntegerType::class, Types::JSON => JsonType::class, Types::OBJECT => ObjectType::class, Types::SIMPLE_ARRAY => SimpleArrayType::class, Types::SMALLINT => SmallIntType::class, Types::STRING => StringType::class, Types::TEXT => TextType::class, Types::TIME_MUTABLE => TimeType::class, Types::TIME_IMMUTABLE => TimeImmutableType::class, ]; private static ?TypeRegistry $typeRegistry = null; /** @internal Do not instantiate directly - use {@see Type::addType()} method instead. */ final public function __construct() { } /** * Converts a value from its PHP representation to its database representation * of this type. * * @param mixed $value The value to convert. * @param AbstractPlatform $platform The currently used database platform. * * @return mixed The database representation of the value. * * @throws ConversionException */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { return $value; } /** * Converts a value from its database representation to its PHP representation * of this type. * * @param mixed $value The value to convert. * @param AbstractPlatform $platform The currently used database platform. * * @return mixed The PHP representation of the value. * * @throws ConversionException */ public function convertToPHPValue($value, AbstractPlatform $platform) { return $value; } /** * Gets the SQL declaration snippet for a column of this type. * * @param mixed[] $column The column definition * @param AbstractPlatform $platform The currently used database platform. * * @return string */ abstract public function getSQLDeclaration(array $column, AbstractPlatform $platform); /** * Gets the name of this type. * * @deprecated this method will be removed in Doctrine DBAL 4.0, * use {@see TypeRegistry::lookupName()} instead. * * @return string */ abstract public function getName(); final public static function getTypeRegistry(): TypeRegistry { return self::$typeRegistry ??= self::createTypeRegistry(); } private static function createTypeRegistry(): TypeRegistry { $instances = []; foreach (self::BUILTIN_TYPES_MAP as $name => $class) { $instances[$name] = new $class(); } return new TypeRegistry($instances); } /** * Factory method to create type instances. * Type instances are implemented as flyweights. * * @param string $name The name of the type (as returned by getName()). * * @return Type * * @throws Exception */ public static function getType($name) { return self::getTypeRegistry()->get($name); } /** * Finds a name for the given type. * * @throws Exception */ public static function lookupName(self $type): string { return self::getTypeRegistry()->lookupName($type); } /** * Adds a custom type to the type map. * * @param string $name The name of the type. This should correspond to what getName() returns. * @param class-string $className The class name of the custom type. * * @return void * * @throws Exception */ public static function addType($name, $className) { self::getTypeRegistry()->register($name, new $className()); } /** * Checks if exists support for a type. * * @param string $name The name of the type. * * @return bool TRUE if type is supported; FALSE otherwise. */ public static function hasType($name) { return self::getTypeRegistry()->has($name); } /** * Overrides an already defined type to use a different implementation. * * @param string $name * @param class-string $className * * @return void * * @throws Exception */ public static function overrideType($name, $className) { self::getTypeRegistry()->override($name, new $className()); } /** * Gets the (preferred) binding type for values of this type that * can be used when binding parameters to prepared statements. * * This method should return one of the {@see ParameterType} constants. * * @return int */ public function getBindingType() { return ParameterType::STRING; } /** * Gets the types array map which holds all registered types and the corresponding * type class * * @return array */ public static function getTypesMap() { return array_map( static function (Type $type): string { return get_class($type); }, self::getTypeRegistry()->getMap(), ); } /** * Does working with this column require SQL conversion functions? * * This is a metadata function that is required for example in the ORM. * Usage of {@see convertToDatabaseValueSQL} and * {@see convertToPHPValueSQL} works for any type and mostly * does nothing. This method can additionally be used for optimization purposes. * * @deprecated Consumers should call {@see convertToDatabaseValueSQL} and {@see convertToPHPValueSQL} * regardless of the type. * * @return bool */ public function canRequireSQLConversion() { return false; } /** * Modifies the SQL expression (identifier, parameter) to convert to a database value. * * @param string $sqlExpr * * @return string */ public function convertToDatabaseValueSQL($sqlExpr, AbstractPlatform $platform) { return $sqlExpr; } /** * Modifies the SQL expression (identifier, parameter) to convert to a PHP value. * * @param string $sqlExpr * @param AbstractPlatform $platform * * @return string */ public function convertToPHPValueSQL($sqlExpr, $platform) { return $sqlExpr; } /** * Gets an array of database types that map to this Doctrine type. * * @return string[] */ public function getMappedDatabaseTypes(AbstractPlatform $platform) { return []; } /** * If this Doctrine Type maps to an already mapped database type, * reverse schema engineering can't tell them apart. You need to mark * one of those types as commented, which will have Doctrine use an SQL * comment to typehint the actual Doctrine Type. * * @deprecated * * @return bool */ public function requiresSQLCommentHint(AbstractPlatform $platform) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5509', '%s is deprecated.', __METHOD__, ); return false; } } PK!ѝr\(("dbal/src/Types/SimpleArrayType.phpnu[getClobTypeDeclarationSQL($column); } /** * {@inheritDoc} * * @param mixed $value * * @return string|null */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { if (! is_array($value) || count($value) === 0) { return null; } return implode(',', $value); } /** * {@inheritDoc} * * @param mixed $value * * @return list */ public function convertToPHPValue($value, AbstractPlatform $platform) { if ($value === null) { return []; } $value = is_resource($value) ? stream_get_contents($value) : $value; return explode(',', $value); } /** * {@inheritDoc} */ public function getName() { return Types::SIMPLE_ARRAY; } /** * {@inheritDoc} * * @deprecated */ public function requiresSQLCommentHint(AbstractPlatform $platform) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5509', '%s is deprecated.', __METHOD__, ); return true; } } PK!!*dbal/src/Types/DateTimeTzImmutableType.phpnu[format($platform->getDateTimeTzFormatString()); } throw ConversionException::conversionFailedInvalidType( $value, $this->getName(), ['null', DateTimeImmutable::class], ); } /** * {@inheritDoc} * * @param T $value * * @return (T is null ? null : DateTimeImmutable) * * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { if ($value === null || $value instanceof DateTimeImmutable) { return $value; } $dateTime = DateTimeImmutable::createFromFormat($platform->getDateTimeTzFormatString(), $value); if ($dateTime !== false) { return $dateTime; } throw ConversionException::conversionFailedFormat( $value, $this->getName(), $platform->getDateTimeTzFormatString(), ); } /** * {@inheritDoc} * * @deprecated */ public function requiresSQLCommentHint(AbstractPlatform $platform) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5509', '%s is deprecated.', __METHOD__, ); return true; } } PK!gfIH$dbal/src/Types/DateImmutableType.phpnu[format($platform->getDateFormatString()); } throw ConversionException::conversionFailedInvalidType( $value, $this->getName(), ['null', DateTimeImmutable::class], ); } /** * {@inheritDoc} * * @param T $value * * @return (T is null ? null : DateTimeImmutable) * * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { if ($value === null || $value instanceof DateTimeImmutable) { return $value; } $dateTime = DateTimeImmutable::createFromFormat('!' . $platform->getDateFormatString(), $value); if ($dateTime === false) { throw ConversionException::conversionFailedFormat( $value, $this->getName(), $platform->getDateFormatString(), ); } return $dateTime; } /** * {@inheritDoc} * * @deprecated */ public function requiresSQLCommentHint(AbstractPlatform $platform) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5509', '%s is deprecated.', __METHOD__, ); return true; } } PK!^odbal/src/Types/TextType.phpnu[getClobTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function convertToPHPValue($value, AbstractPlatform $platform) { return is_resource($value) ? stream_get_contents($value) : $value; } /** * {@inheritDoc} */ public function getName() { return Types::TEXT; } } PK!y y dbal/src/Types/TimeType.phpnu[getTimeTypeDeclarationSQL($column); } /** * {@inheritDoc} * * @param T $value * * @return (T is null ? null : string) * * @template T */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { if ($value === null) { return $value; } if ($value instanceof DateTimeImmutable) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6017', 'Passing an instance of %s is deprecated, use %s::%s() instead.', get_class($value), TimeImmutableType::class, __FUNCTION__, ); } if ($value instanceof DateTimeInterface) { return $value->format($platform->getTimeFormatString()); } throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', DateTime::class]); } /** * {@inheritDoc} * * @param T $value * * @return (T is null ? null : DateTimeInterface) * * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { if ($value instanceof DateTimeImmutable) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6017', 'Passing an instance of %s is deprecated, use %s::%s() instead.', get_class($value), TimeImmutableType::class, __FUNCTION__, ); } if ($value === null || $value instanceof DateTimeInterface) { return $value; } $dateTime = DateTime::createFromFormat('!' . $platform->getTimeFormatString(), $value); if ($dateTime !== false) { return $dateTime; } throw ConversionException::conversionFailedFormat( $value, $this->getName(), $platform->getTimeFormatString(), ); } } PK!|CCdbal/src/Types/BinaryType.phpnu[getBinaryTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function convertToPHPValue($value, AbstractPlatform $platform) { if ($value === null) { return null; } if (is_string($value)) { $fp = fopen('php://temp', 'rb+'); assert(is_resource($fp)); fwrite($fp, $value); fseek($fp, 0); $value = $fp; } if (! is_resource($value)) { throw ConversionException::conversionFailed($value, Types::BINARY); } return $value; } /** * {@inheritDoc} */ public function getName() { return Types::BINARY; } /** * {@inheritDoc} */ public function getBindingType() { return ParameterType::BINARY; } } PK!| +dbal/src/Types/VarDateTimeImmutableType.phpnu[format($platform->getDateTimeFormatString()); } throw ConversionException::conversionFailedInvalidType( $value, $this->getName(), ['null', DateTimeImmutable::class], ); } /** * {@inheritDoc} * * @param T $value * * @return (T is null ? null : DateTimeImmutable) * * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { if ($value === null || $value instanceof DateTimeImmutable) { return $value; } try { $dateTime = new DateTimeImmutable($value); } catch (Exception $e) { throw ConversionException::conversionFailed($value, $this->getName(), $e); } return $dateTime; } /** * {@inheritDoc} * * @deprecated */ public function requiresSQLCommentHint(AbstractPlatform $platform) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5509', '%s is deprecated.', __METHOD__, ); return true; } } PK!s>n #dbal/src/Types/DateIntervalType.phpnu[getStringTypeDeclarationSQL($column); } /** * {@inheritDoc} * * @param T $value * * @return (T is null ? null : string) * * @template T */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { if ($value === null) { return null; } if ($value instanceof DateInterval) { return $value->format(self::FORMAT); } throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', DateInterval::class]); } /** * {@inheritDoc} * * @param T $value * * @return (T is null ? null : DateInterval) * * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { if ($value === null || $value instanceof DateInterval) { return $value; } $negative = false; if (isset($value[0]) && ($value[0] === '+' || $value[0] === '-')) { $negative = $value[0] === '-'; $value = substr($value, 1); } try { $interval = new DateInterval($value); if ($negative) { $interval->invert = 1; } return $interval; } catch (Throwable $exception) { throw ConversionException::conversionFailedFormat($value, $this->getName(), self::FORMAT, $exception); } } /** * {@inheritDoc} * * @deprecated */ public function requiresSQLCommentHint(AbstractPlatform $platform) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5509', '%s is deprecated.', __METHOD__, ); return true; } } PK!2jdbal/src/Types/BooleanType.phpnu[getBooleanTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { return $platform->convertBooleansToDatabaseValue($value); } /** * {@inheritDoc} * * @param T $value * * @return (T is null ? null : bool) * * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { return $platform->convertFromBoolean($value); } /** * {@inheritDoc} */ public function getName() { return Types::BOOLEAN; } /** * {@inheritDoc} */ public function getBindingType() { return ParameterType::BOOLEAN; } /** * @deprecated * * @return bool */ public function requiresSQLCommentHint(AbstractPlatform $platform) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5509', '%s is deprecated.', __METHOD__, ); // We require a commented boolean type in order to distinguish between // boolean and smallint as both (have to) map to the same native type. return $platform instanceof DB2Platform; } } PK!"t dbal/src/Types/DateType.phpnu[getDateTypeDeclarationSQL($column); } /** * {@inheritDoc} * * @psalm-param T $value * * @return (T is null ? null : string) * * @template T */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { if ($value === null) { return $value; } if ($value instanceof DateTimeInterface) { if ($value instanceof DateTimeImmutable) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6017', 'Passing an instance of %s is deprecated, use %s::%s() instead.', get_class($value), DateImmutableType::class, __FUNCTION__, ); } return $value->format($platform->getDateFormatString()); } throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', DateTime::class]); } /** * {@inheritDoc} * * @param T $value * * @return (T is null ? null : DateTimeInterface) * * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { if ($value instanceof DateTimeImmutable) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6017', 'Passing an instance of %s is deprecated, use %s::%s() instead.', get_class($value), DateImmutableType::class, __FUNCTION__, ); } if ($value === null || $value instanceof DateTimeInterface) { return $value; } $dateTime = DateTime::createFromFormat('!' . $platform->getDateFormatString(), $value); if ($dateTime !== false) { return $dateTime; } throw ConversionException::conversionFailedFormat( $value, $this->getName(), $platform->getDateFormatString(), ); } } PK!=۪(dbal/src/Types/DateTimeImmutableType.phpnu[format($platform->getDateTimeFormatString()); } throw ConversionException::conversionFailedInvalidType( $value, $this->getName(), ['null', DateTimeImmutable::class], ); } /** * {@inheritDoc} * * @param T $value * * @return (T is null ? null : DateTimeImmutable) * * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { if ($value === null || $value instanceof DateTimeImmutable) { return $value; } $dateTime = DateTimeImmutable::createFromFormat($platform->getDateTimeFormatString(), $value); if ($dateTime !== false) { return $dateTime; } try { return new DateTimeImmutable($value); } catch (Exception $e) { throw ConversionException::conversionFailedFormat( $value, $this->getName(), $platform->getDateTimeFormatString(), $e, ); } } /** * {@inheritDoc} * * @deprecated */ public function requiresSQLCommentHint(AbstractPlatform $platform) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5509', '%s is deprecated.', __METHOD__, ); return true; } } PK!!$dbal/src/Types/TimeImmutableType.phpnu[format($platform->getTimeFormatString()); } throw ConversionException::conversionFailedInvalidType( $value, $this->getName(), ['null', DateTimeImmutable::class], ); } /** * {@inheritDoc} * * @param T $value * * @return (T is null ? null : DateTimeImmutable) * * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { if ($value === null || $value instanceof DateTimeImmutable) { return $value; } $dateTime = DateTimeImmutable::createFromFormat('!' . $platform->getTimeFormatString(), $value); if ($dateTime !== false) { return $dateTime; } throw ConversionException::conversionFailedFormat( $value, $this->getName(), $platform->getTimeFormatString(), ); } /** * {@inheritDoc} * * @deprecated */ public function requiresSQLCommentHint(AbstractPlatform $platform) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5509', '%s is deprecated.', __METHOD__, ); return true; } } PK!getAsciiStringTypeDeclarationSQL($column); } public function getBindingType(): int { return ParameterType::ASCII; } public function getName(): string { return Types::ASCII_STRING; } } PK!|f%%dbal/src/Types/DecimalType.phpnu[getDecimalTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function convertToPHPValue($value, AbstractPlatform $platform) { // Some drivers starting from PHP 8.1 can represent decimals as float/int // See also: https://github.com/doctrine/dbal/pull/4818 if ((PHP_VERSION_ID >= 80100 || $platform instanceof SqlitePlatform) && (is_float($value) || is_int($value))) { return (string) $value; } return $value; } } PK!ݶΦ55dbal/src/Types/BlobType.phpnu[getBlobTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function convertToPHPValue($value, AbstractPlatform $platform) { if ($value === null) { return null; } if (is_string($value)) { $fp = fopen('php://temp', 'rb+'); assert(is_resource($fp)); fwrite($fp, $value); fseek($fp, 0); $value = $fp; } if (! is_resource($value)) { throw ConversionException::conversionFailed($value, Types::BLOB); } return $value; } /** * {@inheritDoc} */ public function getName() { return Types::BLOB; } /** * {@inheritDoc} */ public function getBindingType() { return ParameterType::LARGE_OBJECT; } } PK!ܠm! dbal/src/Types/TypeRegistry.phpnu[ Map of type names and their corresponding flyweight objects. */ private array $instances; /** @var array */ private array $instancesReverseIndex; /** @param array $instances */ public function __construct(array $instances = []) { $this->instances = []; $this->instancesReverseIndex = []; foreach ($instances as $name => $type) { $this->register($name, $type); } } /** * Finds a type by the given name. * * @throws Exception */ public function get(string $name): Type { $type = $this->instances[$name] ?? null; if ($type === null) { throw Exception::unknownColumnType($name); } return $type; } /** * Finds a name for the given type. * * @throws Exception */ public function lookupName(Type $type): string { $name = $this->findTypeName($type); if ($name === null) { throw Exception::typeNotRegistered($type); } return $name; } /** * Checks if there is a type of the given name. */ public function has(string $name): bool { return isset($this->instances[$name]); } /** * Registers a custom type to the type map. * * @throws Exception */ public function register(string $name, Type $type): void { if (isset($this->instances[$name])) { throw Exception::typeExists($name); } if ($this->findTypeName($type) !== null) { throw Exception::typeAlreadyRegistered($type); } $this->instances[$name] = $type; $this->instancesReverseIndex[spl_object_id($type)] = $name; } /** * Overrides an already defined type to use a different implementation. * * @throws Exception */ public function override(string $name, Type $type): void { $origType = $this->instances[$name] ?? null; if ($origType === null) { throw Exception::typeNotFound($name); } if (($this->findTypeName($type) ?? $name) !== $name) { throw Exception::typeAlreadyRegistered($type); } unset($this->instancesReverseIndex[spl_object_id($origType)]); $this->instances[$name] = $type; $this->instancesReverseIndex[spl_object_id($type)] = $name; } /** * Gets the map of all registered types and their corresponding type instances. * * @internal * * @return array */ public function getMap(): array { return $this->instances; } private function findTypeName(Type $type): ?string { return $this->instancesReverseIndex[spl_object_id($type)] ?? null; } } PK!#sdbal/src/Types/StringType.phpnu[getStringTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function getName() { return Types::STRING; } } PK!'33"dbal/src/Types/VarDateTimeType.phpnu[ 0 it is necessary to use this type. */ class VarDateTimeType extends DateTimeType { /** * {@inheritDoc} * * @param T $value * * @return (T is null ? null : DateTimeInterface) * * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { if ($value === null || $value instanceof DateTime) { return $value; } try { $dateTime = new DateTime($value); } catch (Exception $e) { throw ConversionException::conversionFailed($value, $this->getName(), $e); } return $dateTime; } } PK!z 00dbal/src/Types/Types.phpnu[getBigIntTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function getBindingType() { return ParameterType::STRING; } /** * {@inheritDoc} * * @param T $value * * @return (T is null ? null : string) * * @template T */ public function convertToPHPValue($value, AbstractPlatform $platform) { return $value === null ? null : (string) $value; } } PK!" "dbal/src/ExpandArrayParameters.phpnu[|array */ private array $originalParameters; /** @var array|array */ private array $originalTypes; private int $originalParameterIndex = 0; /** @var list */ private array $convertedSQL = []; /** @var list */ private array $convertedParameters = []; /** @var array */ private array $convertedTypes = []; /** * @param array|array $parameters * @param array|array $types */ public function __construct(array $parameters, array $types) { $this->originalParameters = $parameters; $this->originalTypes = $types; } public function acceptPositionalParameter(string $sql): void { $index = $this->originalParameterIndex; if (! array_key_exists($index, $this->originalParameters)) { throw MissingPositionalParameter::new($index); } $this->acceptParameter($index, $this->originalParameters[$index]); $this->originalParameterIndex++; } public function acceptNamedParameter(string $sql): void { $name = substr($sql, 1); if (! array_key_exists($name, $this->originalParameters)) { throw MissingNamedParameter::new($name); } $this->acceptParameter($name, $this->originalParameters[$name]); } public function acceptOther(string $sql): void { $this->convertedSQL[] = $sql; } public function getSQL(): string { return implode('', $this->convertedSQL); } /** @return list */ public function getParameters(): array { return $this->convertedParameters; } /** * @param int|string $key * @param mixed $value */ private function acceptParameter($key, $value): void { if (! isset($this->originalTypes[$key])) { $this->convertedSQL[] = '?'; $this->convertedParameters[] = $value; return; } $type = $this->originalTypes[$key]; if ( $type !== ArrayParameterType::INTEGER && $type !== ArrayParameterType::STRING && $type !== ArrayParameterType::ASCII && $type !== ArrayParameterType::BINARY ) { $this->appendTypedParameter([$value], $type); return; } if (count($value) === 0) { $this->convertedSQL[] = 'NULL'; return; } $this->appendTypedParameter($value, ArrayParameterType::toElementParameterType($type)); } /** @return array */ public function getTypes(): array { return $this->convertedTypes; } /** * @param list $values * @param Type|int|string|null $type */ private function appendTypedParameter(array $values, $type): void { $this->convertedSQL[] = implode(', ', array_fill(0, count($values), '?')); $index = count($this->convertedParameters); foreach ($values as $value) { $this->convertedParameters[] = $value; $this->convertedTypes[$index] = $type; $index++; } } } PK!L94dbal/src/Driver/IBMDB2/Exception/ConnectionError.phpnu[dbal/src/Driver/IBMDB2/Exception/CannotCreateTemporaryFile.phpnu[toString(); $username = $params['user'] ?? ''; $password = $params['password'] ?? ''; $driverOptions = $params['driverOptions'] ?? []; if (! empty($params['persistent'])) { $connection = db2_pconnect($dataSourceName, $username, $password, $driverOptions); } else { $connection = db2_connect($dataSourceName, $username, $password, $driverOptions); } if ($connection === false) { throw ConnectionFailed::new(); } return new Connection($connection); } } PK!3Oj %dbal/src/Driver/IBMDB2/Connection.phpnu[connection = $connection; } /** * {@inheritDoc} */ public function getServerVersion() { $serverInfo = db2_server_info($this->connection); assert($serverInfo instanceof stdClass); return $serverInfo->DBMS_VER; } public function prepare(string $sql): DriverStatement { $stmt = @db2_prepare($this->connection, $sql); if ($stmt === false) { throw PrepareFailed::new(error_get_last()); } return new Statement($stmt); } public function query(string $sql): ResultInterface { return $this->prepare($sql)->execute(); } /** * {@inheritDoc} */ public function quote($value, $type = ParameterType::STRING) { $value = db2_escape_string($value); if ($type === ParameterType::INTEGER) { return $value; } return "'" . $value . "'"; } public function exec(string $sql): int { $stmt = @db2_exec($this->connection, $sql); if ($stmt === false) { throw StatementError::new(); } return db2_num_rows($stmt); } /** * {@inheritDoc} */ public function lastInsertId($name = null) { if ($name !== null) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4687', 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', ); } return db2_last_insert_id($this->connection) ?? false; } public function beginTransaction(): bool { return db2_autocommit($this->connection, DB2_AUTOCOMMIT_OFF); } public function commit(): bool { if (! db2_commit($this->connection)) { throw ConnectionError::new($this->connection); } return db2_autocommit($this->connection, DB2_AUTOCOMMIT_ON); } public function rollBack(): bool { if (! db2_rollback($this->connection)) { throw ConnectionError::new($this->connection); } return db2_autocommit($this->connection, DB2_AUTOCOMMIT_ON); } /** @return resource */ public function getNativeConnection() { return $this->connection; } } PK!b'')dbal/src/Driver/IBMDB2/DataSourceName.phpnu[string = $string; } public function toString(): string { return $this->string; } /** * Creates the object from an array representation * * @param array $params */ public static function fromArray( #[SensitiveParameter] array $params ): self { $chunks = []; foreach ($params as $key => $value) { $chunks[] = sprintf('%s=%s', $key, $value); } return new self(implode(';', $chunks)); } /** * Creates the object from the given DBAL connection parameters. * * @param array $params */ public static function fromConnectionParameters( #[SensitiveParameter] array $params ): self { if (isset($params['dbname']) && strpos($params['dbname'], '=') !== false) { return new self($params['dbname']); } $dsnParams = []; foreach ( [ 'host' => 'HOSTNAME', 'port' => 'PORT', 'protocol' => 'PROTOCOL', 'dbname' => 'DATABASE', 'user' => 'UID', 'password' => 'PWD', ] as $dbalParam => $dsnParam ) { if (! isset($params[$dbalParam])) { continue; } $dsnParams[$dsnParam] = $params[$dbalParam]; } return self::fromArray($dsnParams); } } PK!~c$dbal/src/Driver/IBMDB2/Statement.phpnu[ */ private array $lobs = []; /** * @internal The statement can be only instantiated by its driver connection. * * @param resource $stmt */ public function __construct($stmt) { $this->stmt = $stmt; } /** * {@inheritDoc} */ public function bindValue($param, $value, $type = ParameterType::STRING): bool { assert(is_int($param)); if (func_num_args() < 3) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5558', 'Not passing $type to Statement::bindValue() is deprecated.' . ' Pass the type corresponding to the parameter being bound.', ); } return $this->bindParam($param, $value, $type); } /** * {@inheritDoc} * * @deprecated Use {@see bindValue()} instead. */ public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5563', '%s is deprecated. Use bindValue() instead.', __METHOD__, ); assert(is_int($param)); if (func_num_args() < 3) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5558', 'Not passing $type to Statement::bindParam() is deprecated.' . ' Pass the type corresponding to the parameter being bound.', ); } switch ($type) { case ParameterType::INTEGER: $this->bind($param, $variable, DB2_PARAM_IN, DB2_LONG); break; case ParameterType::LARGE_OBJECT: $this->lobs[$param] = &$variable; break; default: $this->bind($param, $variable, DB2_PARAM_IN, DB2_CHAR); break; } return true; } /** * @param int $position Parameter position * @param mixed $variable * * @throws Exception */ private function bind($position, &$variable, int $parameterType, int $dataType): void { $this->parameters[$position] =& $variable; if (! db2_bind_param($this->stmt, $position, '', $parameterType, $dataType)) { throw StatementError::new($this->stmt); } } /** * {@inheritDoc} */ public function execute($params = null): ResultInterface { if ($params !== null) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5556', 'Passing $params to Statement::execute() is deprecated. Bind parameters using' . ' Statement::bindParam() or Statement::bindValue() instead.', ); } $handles = $this->bindLobs(); $result = @db2_execute($this->stmt, $params ?? $this->parameters); foreach ($handles as $handle) { fclose($handle); } $this->lobs = []; if ($result === false) { throw StatementError::new($this->stmt); } return new Result($this->stmt); } /** * @return list * * @throws Exception */ private function bindLobs(): array { $handles = []; foreach ($this->lobs as $param => $value) { if (is_resource($value)) { $handle = $handles[] = $this->createTemporaryFile(); $path = stream_get_meta_data($handle)['uri']; $this->copyStreamToStream($value, $handle); $this->bind($param, $path, DB2_PARAM_FILE, DB2_BINARY); } else { $this->bind($param, $value, DB2_PARAM_IN, DB2_CHAR); } unset($value); } return $handles; } /** * @return resource * * @throws Exception */ private function createTemporaryFile() { $handle = @tmpfile(); if ($handle === false) { throw CannotCreateTemporaryFile::new(error_get_last()); } return $handle; } /** * @param resource $source * @param resource $target * * @throws Exception */ private function copyStreamToStream($source, $target): void { if (@stream_copy_to_stream($source, $target) === false) { throw CannotCopyStreamToStream::new(error_get_last()); } } } PK!z$ !dbal/src/Driver/IBMDB2/Result.phpnu[statement = $statement; } /** * {@inheritDoc} */ public function fetchNumeric() { $row = @db2_fetch_array($this->statement); if ($row === false && db2_stmt_error($this->statement) !== '02000') { throw StatementError::new($this->statement); } return $row; } /** * {@inheritDoc} */ public function fetchAssociative() { $row = @db2_fetch_assoc($this->statement); if ($row === false && db2_stmt_error($this->statement) !== '02000') { throw StatementError::new($this->statement); } return $row; } /** * {@inheritDoc} */ public function fetchOne() { return FetchUtils::fetchOne($this); } /** * {@inheritDoc} */ public function fetchAllNumeric(): array { return FetchUtils::fetchAllNumeric($this); } /** * {@inheritDoc} */ public function fetchAllAssociative(): array { return FetchUtils::fetchAllAssociative($this); } /** * {@inheritDoc} */ public function fetchFirstColumn(): array { return FetchUtils::fetchFirstColumn($this); } public function rowCount(): int { return @db2_num_rows($this->statement); } public function columnCount(): int { $count = db2_num_fields($this->statement); if ($count !== false) { return $count; } return 0; } public function free(): void { db2_free_result($this->statement); } } PK!=2dbal/src/Driver/Exception/UnknownParameterType.phpnu[sqlState = $sqlState; } /** * {@inheritDoc} */ public function getSQLState() { return $this->sqlState; } } PK!*dbal/src/Driver/SQLSrv/Exception/Error.phpnu[connection = $connection; } /** * {@inheritDoc} */ public function getServerVersion() { $serverInfo = sqlsrv_server_info($this->connection); return $serverInfo['SQLServerVersion']; } public function prepare(string $sql): DriverStatement { return new Statement($this->connection, $sql); } public function query(string $sql): ResultInterface { return $this->prepare($sql)->execute(); } /** * {@inheritDoc} */ public function quote($value, $type = ParameterType::STRING) { if (is_int($value)) { return $value; } if (is_float($value)) { return sprintf('%F', $value); } return "'" . str_replace("'", "''", $value) . "'"; } public function exec(string $sql): int { $stmt = sqlsrv_query($this->connection, $sql); if ($stmt === false) { throw Error::new(); } $rowsAffected = sqlsrv_rows_affected($stmt); if ($rowsAffected === false) { throw Error::new(); } return $rowsAffected; } /** * {@inheritDoc} */ public function lastInsertId($name = null) { if ($name !== null) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4687', 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', ); $result = $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?') ->execute([$name]); } else { $result = $this->query('SELECT @@IDENTITY'); } return $result->fetchOne(); } public function beginTransaction(): bool { if (! sqlsrv_begin_transaction($this->connection)) { throw Error::new(); } return true; } public function commit(): bool { if (! sqlsrv_commit($this->connection)) { throw Error::new(); } return true; } public function rollBack(): bool { if (! sqlsrv_rollback($this->connection)) { throw Error::new(); } return true; } /** @return resource */ public function getNativeConnection() { return $this->connection; } } PK!W HH$dbal/src/Driver/SQLSrv/Statement.phpnu[ */ private array $variables = []; /** * Bound parameter types. * * @var array */ private array $types = []; /** * Append to any INSERT query to retrieve the last insert id. */ private const LAST_INSERT_ID_SQL = ';SELECT SCOPE_IDENTITY() AS LastInsertId;'; /** * @internal The statement can be only instantiated by its driver connection. * * @param resource $conn * @param string $sql */ public function __construct($conn, $sql) { $this->conn = $conn; $this->sql = $sql; if (stripos($sql, 'INSERT INTO ') !== 0) { return; } $this->sql .= self::LAST_INSERT_ID_SQL; } /** * {@inheritDoc} */ public function bindValue($param, $value, $type = ParameterType::STRING): bool { assert(is_int($param)); if (func_num_args() < 3) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5558', 'Not passing $type to Statement::bindValue() is deprecated.' . ' Pass the type corresponding to the parameter being bound.', ); } $this->variables[$param] = $value; $this->types[$param] = $type; return true; } /** * {@inheritDoc} * * @deprecated Use {@see bindValue()} instead. */ public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5563', '%s is deprecated. Use bindValue() instead.', __METHOD__, ); assert(is_int($param)); if (func_num_args() < 3) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5558', 'Not passing $type to Statement::bindParam() is deprecated.' . ' Pass the type corresponding to the parameter being bound.', ); } $this->variables[$param] =& $variable; $this->types[$param] = $type; // unset the statement resource if it exists as the new one will need to be bound to the new variable $this->stmt = null; return true; } /** * {@inheritDoc} */ public function execute($params = null): ResultInterface { if ($params !== null) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5556', 'Passing $params to Statement::execute() is deprecated. Bind parameters using' . ' Statement::bindParam() or Statement::bindValue() instead.', ); foreach ($params as $key => $val) { if (is_int($key)) { $this->bindValue($key + 1, $val, ParameterType::STRING); } else { $this->bindValue($key, $val, ParameterType::STRING); } } } $this->stmt ??= $this->prepare(); if (! sqlsrv_execute($this->stmt)) { throw Error::new(); } return new Result($this->stmt); } /** * Prepares SQL Server statement resource * * @return resource * * @throws Exception */ private function prepare() { $params = []; foreach ($this->variables as $column => &$variable) { switch ($this->types[$column]) { case ParameterType::LARGE_OBJECT: $params[$column - 1] = [ &$variable, SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY), SQLSRV_SQLTYPE_VARBINARY('max'), ]; break; case ParameterType::BINARY: $params[$column - 1] = [ &$variable, SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY), ]; break; case ParameterType::ASCII: $params[$column - 1] = [ &$variable, SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR), ]; break; default: $params[$column - 1] =& $variable; break; } } $stmt = sqlsrv_prepare($this->conn, $this->sql, $params); if ($stmt === false) { throw Error::new(); } return $stmt; } } PK!^GZ^ !dbal/src/Driver/SQLSrv/Result.phpnu[statement = $stmt; } /** * {@inheritDoc} */ public function fetchNumeric() { return $this->fetch(SQLSRV_FETCH_NUMERIC); } /** * {@inheritDoc} */ public function fetchAssociative() { return $this->fetch(SQLSRV_FETCH_ASSOC); } /** * {@inheritDoc} */ public function fetchOne() { return FetchUtils::fetchOne($this); } /** * {@inheritDoc} */ public function fetchAllNumeric(): array { return FetchUtils::fetchAllNumeric($this); } /** * {@inheritDoc} */ public function fetchAllAssociative(): array { return FetchUtils::fetchAllAssociative($this); } /** * {@inheritDoc} */ public function fetchFirstColumn(): array { return FetchUtils::fetchFirstColumn($this); } public function rowCount(): int { $count = sqlsrv_rows_affected($this->statement); if ($count !== false) { return $count; } return 0; } public function columnCount(): int { $count = sqlsrv_num_fields($this->statement); if ($count !== false) { return $count; } return 0; } public function free(): void { // emulate it by fetching and discarding rows, similarly to what PDO does in this case // @link http://php.net/manual/en/pdostatement.closecursor.php // @link https://github.com/php/php-src/blob/php-7.0.11/ext/pdo/pdo_stmt.c#L2075 // deliberately do not consider multiple result sets, since doctrine/dbal doesn't support them while (sqlsrv_fetch($this->statement)) { } } /** @return mixed|false */ private function fetch(int $fetchType) { return sqlsrv_fetch_array($this->statement, $fetchType) ?? false; } } PK!1}$dbal/src/Driver/PDO/PDOException.phpnu[message, 0, $previous); $exception->errorInfo = $previous->errorInfo; $exception->code = $previous->code; $exception->sqlState = $previous->errorInfo[0] ?? null; return $exception; } public function getSQLState(): ?string { return $this->sqlState; } } PK!]TSii!dbal/src/Driver/PDO/Exception.phpnu[errorInfo !== null) { [$sqlState, $code] = $exception->errorInfo; $code ??= 0; } else { $code = $exception->getCode(); $sqlState = null; } return new self($exception->getMessage(), $sqlState, $code, $exception); } } PK!qc{{(dbal/src/Driver/PDO/ParameterTypeMap.phpnu[ PDO::PARAM_NULL, ParameterType::INTEGER => PDO::PARAM_INT, ParameterType::STRING => PDO::PARAM_STR, ParameterType::ASCII => PDO::PARAM_STR, ParameterType::BINARY => PDO::PARAM_LOB, ParameterType::LARGE_OBJECT => PDO::PARAM_LOB, ParameterType::BOOLEAN => PDO::PARAM_BOOL, ]; /** * Converts DBAL parameter type to PDO parameter type * * @psalm-return PDO::PARAM_* * * @throws UnknownParameterType * * @psalm-assert ParameterType::* $type */ public static function convertParamType(int $type): int { if (! isset(self::PARAM_TYPE_MAP[$type])) { throw UnknownParameterType::new($type); } return self::PARAM_TYPE_MAP[$type]; } private function __construct() { } private function __clone() { } } PK!HA``%dbal/src/Driver/PDO/SQLite/Driver.phpnu[constructPdoDsn(array_intersect_key($params, ['path' => true, 'memory' => true])), $params['user'] ?? '', $params['password'] ?? '', $driverOptions, ); } catch (PDOException $exception) { throw Exception::new($exception); } UserDefinedFunctions::register( [$pdo, 'sqliteCreateFunction'], $userDefinedFunctions, ); return new Connection($pdo); } /** * Constructs the Sqlite PDO DSN. * * @param array $params */ private function constructPdoDsn(array $params): string { $dsn = 'sqlite:'; if (isset($params['path'])) { $dsn .= $params['path']; } elseif (isset($params['memory'])) { $dsn .= ':memory:'; } return $dsn; } } PK!iF %dbal/src/Driver/PDO/SQLSrv/Driver.phpnu[ $value) { if (is_int($option)) { $driverOptions[$option] = $value; } else { $dsnOptions[$option] = $value; } } } if (! empty($params['persistent'])) { $driverOptions[PDO::ATTR_PERSISTENT] = true; } $safeParams = $params; unset($safeParams['password'], $safeParams['url']); try { $pdo = new PDO( $this->constructDsn($safeParams, $dsnOptions), $params['user'] ?? '', $params['password'] ?? '', $driverOptions, ); } catch (\PDOException $exception) { throw PDOException::new($exception); } return new Connection(new PDOConnection($pdo)); } /** * Constructs the Sqlsrv PDO DSN. * * @param mixed[] $params * @param string[] $connectionOptions * * @throws Exception */ private function constructDsn(array $params, array $connectionOptions): string { $dsn = 'sqlsrv:server='; if (isset($params['host'])) { $dsn .= $params['host']; if (isset($params['port'])) { $dsn .= ',' . $params['port']; } } elseif (isset($params['port'])) { throw PortWithoutHost::new(); } if (isset($params['dbname'])) { $connectionOptions['Database'] = $params['dbname']; } if (isset($params['MultipleActiveResultSets'])) { $connectionOptions['MultipleActiveResultSets'] = $params['MultipleActiveResultSets'] ? 'true' : 'false'; } return $dsn . $this->getConnectionOptionsDsn($connectionOptions); } /** * Converts a connection options array to the DSN * * @param string[] $connectionOptions */ private function getConnectionOptionsDsn(array $connectionOptions): string { $connectionOptionsDsn = ''; foreach ($connectionOptions as $paramName => $paramValue) { $connectionOptionsDsn .= sprintf(';%s=%s', $paramName, $paramValue); } return $connectionOptionsDsn; } } PK!#kk)dbal/src/Driver/PDO/SQLSrv/Connection.phpnu[connection = $connection; } public function prepare(string $sql): StatementInterface { return new Statement( $this->connection->prepare($sql), ); } /** * {@inheritDoc} */ public function lastInsertId($name = null) { if ($name === null) { return parent::lastInsertId($name); } Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4687', 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', ); $statement = $this->prepare( 'SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?', ); $statement->bindValue(1, $name); return $statement->execute() ->fetchOne(); } public function getNativeConnection(): PDO { return $this->connection->getNativeConnection(); } /** @deprecated Call {@see getNativeConnection()} instead. */ public function getWrappedConnection(): PDO { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5037', '%s is deprecated, call getNativeConnection() instead.', __METHOD__, ); return $this->connection->getWrappedConnection(); } } PK!4D (dbal/src/Driver/PDO/SQLSrv/Statement.phpnu[statement = $statement; } /** * {@inheritDoc} * * @deprecated Use {@see bindValue()} instead. * * @param string|int $param * @param mixed $variable * @param int $type * @param int|null $length * @param mixed $driverOptions The usage of the argument is deprecated. * * @throws UnknownParameterType * * @psalm-assert ParameterType::* $type */ public function bindParam( $param, &$variable, $type = ParameterType::STRING, $length = null, $driverOptions = null ): bool { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5563', '%s is deprecated. Use bindValue() instead.', __METHOD__, ); if (func_num_args() < 3) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5558', 'Not passing $type to Statement::bindParam() is deprecated.' . ' Pass the type corresponding to the parameter being bound.', ); } if (func_num_args() > 4) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4533', 'The $driverOptions argument of Statement::bindParam() is deprecated.', ); } switch ($type) { case ParameterType::LARGE_OBJECT: case ParameterType::BINARY: $driverOptions ??= PDO::SQLSRV_ENCODING_BINARY; break; case ParameterType::ASCII: $type = ParameterType::STRING; $length = 0; $driverOptions = PDO::SQLSRV_ENCODING_SYSTEM; break; } return $this->statement->bindParam($param, $variable, $type, $length ?? 0, $driverOptions); } /** * @throws UnknownParameterType * * {@inheritDoc} * * @psalm-assert ParameterType::* $type */ public function bindValue($param, $value, $type = ParameterType::STRING): bool { if (func_num_args() < 3) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5558', 'Not passing $type to Statement::bindValue() is deprecated.' . ' Pass the type corresponding to the parameter being bound.', ); } return $this->bindParam($param, $value, $type); } } PK!X"""dbal/src/Driver/PDO/Connection.phpnu[setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->connection = $connection; } public function exec(string $sql): int { try { $result = $this->connection->exec($sql); assert($result !== false); return $result; } catch (PDOException $exception) { throw Exception::new($exception); } } /** * {@inheritDoc} */ public function getServerVersion() { return $this->connection->getAttribute(PDO::ATTR_SERVER_VERSION); } /** * {@inheritDoc} * * @return Statement */ public function prepare(string $sql): StatementInterface { try { $stmt = $this->connection->prepare($sql); assert($stmt instanceof PDOStatement); return new Statement($stmt); } catch (PDOException $exception) { throw Exception::new($exception); } } public function query(string $sql): ResultInterface { try { $stmt = $this->connection->query($sql); assert($stmt instanceof PDOStatement); return new Result($stmt); } catch (PDOException $exception) { throw Exception::new($exception); } } /** * {@inheritDoc} * * @throws UnknownParameterType * * @psalm-assert ParameterType::* $type */ public function quote($value, $type = ParameterType::STRING) { return $this->connection->quote($value, ParameterTypeMap::convertParamType($type)); } /** * {@inheritDoc} */ public function lastInsertId($name = null) { try { if ($name === null) { return $this->connection->lastInsertId(); } Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4687', 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', ); return $this->connection->lastInsertId($name); } catch (PDOException $exception) { throw Exception::new($exception); } } public function beginTransaction(): bool { try { return $this->connection->beginTransaction(); } catch (PDOException $exception) { throw DriverPDOException::new($exception); } } public function commit(): bool { try { return $this->connection->commit(); } catch (PDOException $exception) { throw DriverPDOException::new($exception); } } public function rollBack(): bool { try { return $this->connection->rollBack(); } catch (PDOException $exception) { throw DriverPDOException::new($exception); } } public function getNativeConnection(): PDO { return $this->connection; } /** @deprecated Call {@see getNativeConnection()} instead. */ public function getWrappedConnection(): PDO { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5037', '%s is deprecated, call getNativeConnection() instead.', __METHOD__, ); return $this->getNativeConnection(); } } PK!x4mii$dbal/src/Driver/PDO/PgSQL/Driver.phpnu[constructPdoDsn($safeParams), $params['user'] ?? '', $params['password'] ?? '', $driverOptions, ); } catch (PDOException $exception) { throw Exception::new($exception); } if ( ! isset($driverOptions[PDO::PGSQL_ATTR_DISABLE_PREPARES]) || $driverOptions[PDO::PGSQL_ATTR_DISABLE_PREPARES] === true ) { $pdo->setAttribute(PDO::PGSQL_ATTR_DISABLE_PREPARES, true); } $connection = new Connection($pdo); /* defining client_encoding via SET NAMES to avoid inconsistent DSN support * - passing client_encoding via the 'options' param breaks pgbouncer support */ if (isset($params['charset'])) { $connection->exec('SET NAMES \'' . $params['charset'] . '\''); } return $connection; } /** * Constructs the Postgres PDO DSN. * * @param array $params */ private function constructPdoDsn(array $params): string { $dsn = 'pgsql:'; if (isset($params['host']) && $params['host'] !== '') { $dsn .= 'host=' . $params['host'] . ';'; } if (isset($params['port']) && $params['port'] !== '') { $dsn .= 'port=' . $params['port'] . ';'; } if (isset($params['dbname'])) { $dsn .= 'dbname=' . $params['dbname'] . ';'; } elseif (isset($params['default_dbname'])) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5705', 'The "default_dbname" connection parameter is deprecated. Use "dbname" instead.', ); $dsn .= 'dbname=' . $params['default_dbname'] . ';'; } else { if (isset($params['user']) && $params['user'] !== 'postgres') { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5705', 'Relying on the DBAL connecting to the "postgres" database by default is deprecated.' . ' Unless you want to have the server determine the default database for the connection,' . ' specify the database name explicitly.', ); } // Used for temporary connections to allow operations like dropping the database currently connected to. $dsn .= 'dbname=postgres;'; } if (isset($params['sslmode'])) { $dsn .= 'sslmode=' . $params['sslmode'] . ';'; } if (isset($params['sslrootcert'])) { $dsn .= 'sslrootcert=' . $params['sslrootcert'] . ';'; } if (isset($params['sslcert'])) { $dsn .= 'sslcert=' . $params['sslcert'] . ';'; } if (isset($params['sslkey'])) { $dsn .= 'sslkey=' . $params['sslkey'] . ';'; } if (isset($params['sslcrl'])) { $dsn .= 'sslcrl=' . $params['sslcrl'] . ';'; } if (isset($params['application_name'])) { $dsn .= 'application_name=' . $params['application_name'] . ';'; } if (isset($params['gssencmode'])) { $dsn .= 'gssencmode=' . $params['gssencmode'] . ';'; } return $dsn; } } PK!<<$dbal/src/Driver/PDO/MySQL/Driver.phpnu[constructPdoDsn($safeParams), $params['user'] ?? '', $params['password'] ?? '', $driverOptions, ); } catch (PDOException $exception) { throw Exception::new($exception); } return new Connection($pdo); } /** * Constructs the MySQL PDO DSN. * * @param mixed[] $params */ private function constructPdoDsn(array $params): string { $dsn = 'mysql:'; if (isset($params['host']) && $params['host'] !== '') { $dsn .= 'host=' . $params['host'] . ';'; } if (isset($params['port'])) { $dsn .= 'port=' . $params['port'] . ';'; } if (isset($params['dbname'])) { $dsn .= 'dbname=' . $params['dbname'] . ';'; } if (isset($params['unix_socket'])) { $dsn .= 'unix_socket=' . $params['unix_socket'] . ';'; } if (isset($params['charset'])) { $dsn .= 'charset=' . $params['charset'] . ';'; } return $dsn; } } PK!&ɞ!dbal/src/Driver/PDO/Statement.phpnu[stmt = $stmt; } /** * {@inheritDoc} * * @throws UnknownParameterType * * @psalm-assert ParameterType::* $type */ public function bindValue($param, $value, $type = ParameterType::STRING) { if (func_num_args() < 3) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5558', 'Not passing $type to Statement::bindValue() is deprecated.' . ' Pass the type corresponding to the parameter being bound.', ); } $pdoType = ParameterTypeMap::convertParamType($type); try { return $this->stmt->bindValue($param, $value, $pdoType); } catch (PDOException $exception) { throw Exception::new($exception); } } /** * {@inheritDoc} * * @deprecated Use {@see bindValue()} instead. * * @param mixed $param * @param mixed $variable * @param int $type * @param int|null $length * @param mixed $driverOptions The usage of the argument is deprecated. * * @throws UnknownParameterType * * @psalm-assert ParameterType::* $type */ public function bindParam( $param, &$variable, $type = ParameterType::STRING, $length = null, $driverOptions = null ): bool { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5563', '%s is deprecated. Use bindValue() instead.', __METHOD__, ); if (func_num_args() < 3) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5558', 'Not passing $type to Statement::bindParam() is deprecated.' . ' Pass the type corresponding to the parameter being bound.', ); } if (func_num_args() > 4) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4533', 'The $driverOptions argument of Statement::bindParam() is deprecated.', ); } $pdoType = ParameterTypeMap::convertParamType($type); try { return $this->stmt->bindParam( $param, $variable, $pdoType, $length ?? 0, ...array_slice(func_get_args(), 4), ); } catch (PDOException $exception) { throw Exception::new($exception); } } /** * {@inheritDoc} */ public function execute($params = null): ResultInterface { if ($params !== null) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5556', 'Passing $params to Statement::execute() is deprecated. Bind parameters using' . ' Statement::bindParam() or Statement::bindValue() instead.', ); } try { $this->stmt->execute($params); } catch (PDOException $exception) { throw Exception::new($exception); } return new Result($this->stmt); } } PK!G"dbal/src/Driver/PDO/OCI/Driver.phpnu[constructPdoDsn($params), $params['user'] ?? '', $params['password'] ?? '', $driverOptions, ); } catch (PDOException $exception) { throw Exception::new($exception); } return new Connection($pdo); } /** * Constructs the Oracle PDO DSN. * * @param mixed[] $params */ private function constructPdoDsn(array $params): string { $dsn = 'oci:dbname=' . $this->getEasyConnectString($params); if (isset($params['charset'])) { $dsn .= ';charset=' . $params['charset']; } return $dsn; } } PK!ti dbal/src/Driver/PDO/Result.phpnu[statement = $statement; } /** * {@inheritDoc} */ public function fetchNumeric() { return $this->fetch(PDO::FETCH_NUM); } /** * {@inheritDoc} */ public function fetchAssociative() { return $this->fetch(PDO::FETCH_ASSOC); } /** * {@inheritDoc} */ public function fetchOne() { return $this->fetch(PDO::FETCH_COLUMN); } /** * {@inheritDoc} */ public function fetchAllNumeric(): array { return $this->fetchAll(PDO::FETCH_NUM); } /** * {@inheritDoc} */ public function fetchAllAssociative(): array { return $this->fetchAll(PDO::FETCH_ASSOC); } /** * {@inheritDoc} */ public function fetchFirstColumn(): array { return $this->fetchAll(PDO::FETCH_COLUMN); } public function rowCount(): int { try { return $this->statement->rowCount(); } catch (PDOException $exception) { throw Exception::new($exception); } } public function columnCount(): int { try { return $this->statement->columnCount(); } catch (PDOException $exception) { throw Exception::new($exception); } } public function free(): void { $this->statement->closeCursor(); } /** * @psalm-param PDO::FETCH_* $mode * * @return mixed * * @throws Exception */ private function fetch(int $mode) { try { return $this->statement->fetch($mode); } catch (PDOException $exception) { throw Exception::new($exception); } } /** * @psalm-param PDO::FETCH_* $mode * * @return list * * @throws Exception */ private function fetchAll(int $mode): array { try { return $this->statement->fetchAll($mode); } catch (PDOException $exception) { throw Exception::new($exception); } } } PK!֞#dbal/src/Driver/Connection.phpnu[>(dbal/src/Driver/AbstractOracleDriver.phpnu[ $params The connection parameters to return the Easy Connect String for. * * @return string */ protected function getEasyConnectString(array $params) { return (string) EasyConnectString::fromConnectionParameters($params); } } PK!X"dbal/src/Driver/SQLite3/Driver.phpnu[enableExceptions(true); UserDefinedFunctions::register([$connection, 'createFunction']); return new Connection($connection); } } PK!*q@gg%dbal/src/Driver/SQLite3/Exception.phpnu[getMessage(), null, (int) $exception->getCode(), $exception); } } PK! &dbal/src/Driver/SQLite3/Connection.phpnu[connection = $connection; } public function prepare(string $sql): Statement { try { $statement = $this->connection->prepare($sql); } catch (\Exception $e) { throw Exception::new($e); } assert($statement !== false); return new Statement($this->connection, $statement); } public function query(string $sql): Result { try { $result = $this->connection->query($sql); } catch (\Exception $e) { throw Exception::new($e); } assert($result !== false); return new Result($result, $this->connection->changes()); } /** @inheritDoc */ public function quote($value, $type = ParameterType::STRING): string { return sprintf('\'%s\'', SQLite3::escapeString($value)); } public function exec(string $sql): int { try { $this->connection->exec($sql); } catch (\Exception $e) { throw Exception::new($e); } return $this->connection->changes(); } /** @inheritDoc */ public function lastInsertId($name = null): int { return $this->connection->lastInsertRowID(); } public function beginTransaction(): bool { try { return $this->connection->exec('BEGIN'); } catch (\Exception $e) { return false; } } public function commit(): bool { try { return $this->connection->exec('COMMIT'); } catch (\Exception $e) { return false; } } public function rollBack(): bool { try { return $this->connection->exec('ROLLBACK'); } catch (\Exception $e) { return false; } } public function getNativeConnection(): SQLite3 { return $this->connection; } public function getServerVersion(): string { return SQLite3::version()['versionString']; } } PK!#[%dbal/src/Driver/SQLite3/Statement.phpnu[ SQLITE3_NULL, ParameterType::INTEGER => SQLITE3_INTEGER, ParameterType::STRING => SQLITE3_TEXT, ParameterType::ASCII => SQLITE3_TEXT, ParameterType::BINARY => SQLITE3_BLOB, ParameterType::LARGE_OBJECT => SQLITE3_BLOB, ParameterType::BOOLEAN => SQLITE3_INTEGER, ]; private SQLite3 $connection; private SQLite3Stmt $statement; /** @internal The statement can be only instantiated by its driver connection. */ public function __construct(SQLite3 $connection, SQLite3Stmt $statement) { $this->connection = $connection; $this->statement = $statement; } /** * @throws UnknownParameterType * * {@inheritDoc} * * @psalm-assert ParameterType::* $type */ public function bindValue($param, $value, $type = ParameterType::STRING): bool { if (func_num_args() < 3) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5558', 'Not passing $type to Statement::bindValue() is deprecated.' . ' Pass the type corresponding to the parameter being bound.', ); } return $this->statement->bindValue($param, $value, $this->convertParamType($type)); } /** * @throws UnknownParameterType * * {@inheritDoc} * * @psalm-assert ParameterType::* $type */ public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5563', '%s is deprecated. Use bindValue() instead.', __METHOD__, ); if (func_num_args() < 3) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5558', 'Not passing $type to Statement::bindParam() is deprecated.' . ' Pass the type corresponding to the parameter being bound.', ); } return $this->statement->bindParam($param, $variable, $this->convertParamType($type)); } /** @inheritDoc */ public function execute($params = null): Result { if ($params !== null) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5556', 'Passing $params to Statement::execute() is deprecated. Bind parameters using' . ' Statement::bindParam() or Statement::bindValue() instead.', ); foreach ($params as $param => $value) { if (is_int($param)) { $this->bindValue($param + 1, $value, ParameterType::STRING); } else { $this->bindValue($param, $value, ParameterType::STRING); } } } try { $result = $this->statement->execute(); } catch (\Exception $e) { throw Exception::new($e); } assert($result !== false); return new Result($result, $this->connection->changes()); } /** * @psalm-return value-of * * @psalm-assert ParameterType::* $type */ private function convertParamType(int $type): int { if (! isset(self::PARAM_TYPE_MAP[$type])) { throw UnknownParameterType::new($type); } return self::PARAM_TYPE_MAP[$type]; } } PK!}}"dbal/src/Driver/SQLite3/Result.phpnu[result = $result; $this->changes = $changes; } /** @inheritDoc */ public function fetchNumeric() { if ($this->result === null) { return false; } return $this->result->fetchArray(SQLITE3_NUM); } /** @inheritDoc */ public function fetchAssociative() { if ($this->result === null) { return false; } return $this->result->fetchArray(SQLITE3_ASSOC); } /** @inheritDoc */ public function fetchOne() { return FetchUtils::fetchOne($this); } /** @inheritDoc */ public function fetchAllNumeric(): array { return FetchUtils::fetchAllNumeric($this); } /** @inheritDoc */ public function fetchAllAssociative(): array { return FetchUtils::fetchAllAssociative($this); } /** @inheritDoc */ public function fetchFirstColumn(): array { return FetchUtils::fetchFirstColumn($this); } public function rowCount(): int { return $this->changes; } public function columnCount(): int { if ($this->result === null) { return 0; } return $this->result->numColumns(); } public function free(): void { if ($this->result === null) { return; } $this->result->finalize(); $this->result = null; } } PK!"y4dbal/src/Driver/PgSQL/Exception/UnknownParameter.phpnu[constructConnectionString($params), PGSQL_CONNECT_FORCE_NEW); } catch (ErrorException $e) { throw new Exception($e->getMessage(), '08006', 0, $e); } finally { restore_error_handler(); } if ($connection === false) { throw new Exception('Unable to connect to Postgres server.'); } $driverConnection = new Connection($connection); if (isset($params['application_name'])) { $driverConnection->exec('SET application_name = ' . $driverConnection->quote($params['application_name'])); } return $driverConnection; } /** * Constructs the Postgres connection string * * @param array $params */ private function constructConnectionString( #[SensitiveParameter] array $params ): string { $components = array_filter( [ 'host' => $params['host'] ?? null, 'port' => $params['port'] ?? null, 'dbname' => $params['dbname'] ?? 'postgres', 'user' => $params['user'] ?? null, 'password' => $params['password'] ?? null, 'sslmode' => $params['sslmode'] ?? null, 'gssencmode' => $params['gssencmode'] ?? null, ], static fn ($value) => $value !== '' && $value !== null, ); return implode(' ', array_map( static fn ($value, string $key) => sprintf("%s='%s'", $key, addslashes($value)), array_values($components), array_keys($components), )); } } PK!^ '#dbal/src/Driver/PgSQL/Exception.phpnu[connection = $connection; $this->parser = new Parser(false); } public function __destruct() { if (! isset($this->connection)) { return; } @pg_close($this->connection); } public function prepare(string $sql): Statement { $visitor = new ConvertParameters(); $this->parser->parse($sql, $visitor); $statementName = uniqid('dbal', true); if (@pg_send_prepare($this->connection, $statementName, $visitor->getSQL()) !== true) { throw new Exception(pg_last_error($this->connection)); } $result = @pg_get_result($this->connection); assert($result !== false); if ((bool) pg_result_error($result)) { throw Exception::fromResult($result); } return new Statement($this->connection, $statementName, $visitor->getParameterMap()); } public function query(string $sql): Result { if (@pg_send_query($this->connection, $sql) !== true) { throw new Exception(pg_last_error($this->connection)); } $result = @pg_get_result($this->connection); assert($result !== false); if ((bool) pg_result_error($result)) { throw Exception::fromResult($result); } return new Result($result); } /** {@inheritDoc} */ public function quote($value, $type = ParameterType::STRING) { if ($type === ParameterType::BINARY || $type === ParameterType::LARGE_OBJECT) { return sprintf("'%s'", pg_escape_bytea($this->connection, $value)); } return pg_escape_literal($this->connection, $value); } public function exec(string $sql): int { return $this->query($sql)->rowCount(); } /** {@inheritDoc} */ public function lastInsertId($name = null) { if ($name !== null) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4687', 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', ); return $this->query(sprintf('SELECT CURRVAL(%s)', $this->quote($name)))->fetchOne(); } return $this->query('SELECT LASTVAL()')->fetchOne(); } /** @return true */ public function beginTransaction(): bool { $this->exec('BEGIN'); return true; } /** @return true */ public function commit(): bool { $this->exec('COMMIT'); return true; } /** @return true */ public function rollBack(): bool { $this->exec('ROLLBACK'); return true; } public function getServerVersion(): string { return (string) pg_version($this->connection)['server']; } /** @return PgSqlConnection|resource */ public function getNativeConnection() { return $this->connection; } } PK!#dbal/src/Driver/PgSQL/Statement.phpnu[ */ private array $parameterMap; /** @var array */ private array $parameters = []; /** @psalm-var array */ private array $parameterTypes = []; /** * @param PgSqlConnection|resource $connection * @param array $parameterMap */ public function __construct($connection, string $name, array $parameterMap) { if (! is_resource($connection) && ! $connection instanceof PgSqlConnection) { throw new TypeError(sprintf( 'Expected connection to be a resource or an instance of %s, got %s.', PgSqlConnection::class, is_object($connection) ? get_class($connection) : gettype($connection), )); } $this->connection = $connection; $this->name = $name; $this->parameterMap = $parameterMap; } public function __destruct() { if (! isset($this->connection)) { return; } @pg_query( $this->connection, 'DEALLOCATE ' . pg_escape_identifier($this->connection, $this->name), ); } /** {@inheritDoc} */ public function bindValue($param, $value, $type = ParameterType::STRING): bool { if (! isset($this->parameterMap[$param])) { throw UnknownParameter::new((string) $param); } $this->parameters[$this->parameterMap[$param]] = $value; $this->parameterTypes[$this->parameterMap[$param]] = $type; return true; } /** {@inheritDoc} */ public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5563', '%s is deprecated. Use bindValue() instead.', __METHOD__, ); if (func_num_args() < 3) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5558', 'Not passing $type to Statement::bindParam() is deprecated.' . ' Pass the type corresponding to the parameter being bound.', ); } if (func_num_args() > 4) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4533', 'The $driverOptions argument of Statement::bindParam() is deprecated.', ); } if (! isset($this->parameterMap[$param])) { throw UnknownParameter::new((string) $param); } $this->parameters[$this->parameterMap[$param]] = &$variable; $this->parameterTypes[$this->parameterMap[$param]] = $type; return true; } /** {@inheritDoc} */ public function execute($params = null): Result { if ($params !== null) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5556', 'Passing $params to Statement::execute() is deprecated. Bind parameters using' . ' Statement::bindParam() or Statement::bindValue() instead.', ); foreach ($params as $param => $value) { if (is_int($param)) { $this->bindValue($param + 1, $value, ParameterType::STRING); } else { $this->bindValue($param, $value, ParameterType::STRING); } } } ksort($this->parameters); $escapedParameters = []; foreach ($this->parameters as $parameter => $value) { switch ($this->parameterTypes[$parameter]) { case ParameterType::BINARY: case ParameterType::LARGE_OBJECT: $escapedParameters[] = $value === null ? null : pg_escape_bytea( $this->connection, is_resource($value) ? stream_get_contents($value) : $value, ); break; default: $escapedParameters[] = $value; } } if (@pg_send_execute($this->connection, $this->name, $escapedParameters) !== true) { throw new Exception(pg_last_error($this->connection)); } $result = @pg_get_result($this->connection); assert($result !== false); if ((bool) pg_result_error($result)) { throw Exception::fromResult($result); } return new Result($result); } } PK!V:+dbal/src/Driver/PgSQL/ConvertParameters.phpnu[ */ private array $buffer = []; /** @var array */ private array $parameterMap = []; public function acceptPositionalParameter(string $sql): void { $position = count($this->parameterMap) + 1; $this->parameterMap[$position] = $position; $this->buffer[] = '$' . $position; } public function acceptNamedParameter(string $sql): void { $position = count($this->parameterMap) + 1; $this->parameterMap[$sql] = $position; $this->buffer[] = '$' . $position; } public function acceptOther(string $sql): void { $this->buffer[] = $sql; } public function getSQL(): string { return implode('', $this->buffer); } /** @return array */ public function getParameterMap(): array { return $this->parameterMap; } } PK!4 dbal/src/Driver/PgSQL/Result.phpnu[result = $result; } public function __destruct() { if (! isset($this->result)) { return; } $this->free(); } /** {@inheritDoc} */ public function fetchNumeric() { if ($this->result === null) { return false; } $row = pg_fetch_row($this->result); if ($row === false) { return false; } return $this->mapNumericRow($row, $this->fetchNumericColumnTypes()); } /** {@inheritDoc} */ public function fetchAssociative() { if ($this->result === null) { return false; } $row = pg_fetch_assoc($this->result); if ($row === false) { return false; } return $this->mapAssociativeRow($row, $this->fetchAssociativeColumnTypes()); } /** {@inheritDoc} */ public function fetchOne() { return FetchUtils::fetchOne($this); } /** {@inheritDoc} */ public function fetchAllNumeric(): array { if ($this->result === null) { return []; } $resultSet = pg_fetch_all($this->result, PGSQL_NUM); // On PHP 7.4, pg_fetch_all() might return false for empty result sets. if ($resultSet === false) { return []; } $types = $this->fetchNumericColumnTypes(); return array_map( fn (array $row) => $this->mapNumericRow($row, $types), $resultSet, ); } /** {@inheritDoc} */ public function fetchAllAssociative(): array { if ($this->result === null) { return []; } $resultSet = pg_fetch_all($this->result, PGSQL_ASSOC); // On PHP 7.4, pg_fetch_all() might return false for empty result sets. if ($resultSet === false) { return []; } $types = $this->fetchAssociativeColumnTypes(); return array_map( fn (array $row) => $this->mapAssociativeRow($row, $types), $resultSet, ); } /** {@inheritDoc} */ public function fetchFirstColumn(): array { if ($this->result === null) { return []; } $postgresType = pg_field_type($this->result, 0); return array_map( fn ($value) => $this->mapType($postgresType, $value), pg_fetch_all_columns($this->result), ); } public function rowCount(): int { if ($this->result === null) { return 0; } return pg_affected_rows($this->result); } public function columnCount(): int { if ($this->result === null) { return 0; } return pg_num_fields($this->result); } public function free(): void { if ($this->result === null) { return; } pg_free_result($this->result); $this->result = null; } /** @return array */ private function fetchNumericColumnTypes(): array { assert($this->result !== null); $types = []; $numFields = pg_num_fields($this->result); for ($i = 0; $i < $numFields; ++$i) { $types[$i] = pg_field_type($this->result, $i); } return $types; } /** @return array */ private function fetchAssociativeColumnTypes(): array { assert($this->result !== null); $types = []; $numFields = pg_num_fields($this->result); for ($i = 0; $i < $numFields; ++$i) { $types[pg_field_name($this->result, $i)] = pg_field_type($this->result, $i); } return $types; } /** * @param list $row * @param array $types * * @return list */ private function mapNumericRow(array $row, array $types): array { assert($this->result !== null); return array_map( fn ($value, $field) => $this->mapType($types[$field], $value), $row, array_keys($row), ); } /** * @param array $row * @param array $types * * @return array */ private function mapAssociativeRow(array $row, array $types): array { assert($this->result !== null); $mappedRow = []; foreach ($row as $field => $value) { $mappedRow[$field] = $this->mapType($types[$field], $value); } return $mappedRow; } /** @return string|int|float|bool|null */ private function mapType(string $postgresType, ?string $value) { if ($value === null) { return null; } switch ($postgresType) { case 'bool': switch ($value) { case 't': return true; case 'f': return false; } throw UnexpectedValue::new($value, $postgresType); case 'bytea': return hex2bin(substr($value, 2)); case 'float4': case 'float8': return (float) $value; case 'int2': case 'int4': return (int) $value; case 'int8': return PHP_INT_SIZE >= 8 ? (int) $value : $value; } return $value; } } PK!5dbal/src/Driver/Statement.phpnu[execute() is called. * * As mentioned above, the named parameters are not natively supported by the mysqli driver, use executeQuery(), * fetchAll(), fetchArray(), fetchColumn(), fetchAssoc() methods to have the named parameter emulated by doctrine. * * Most parameters are input parameters, that is, parameters that are * used in a read-only fashion to build up the query. Some drivers support the invocation * of stored procedures that return data as output parameters, and some also as input/output * parameters that both send in data and are updated to receive it. * * @deprecated Use {@see bindValue()} instead. * * @param string|int $param Parameter identifier. For a prepared statement using named placeholders, * this will be a parameter name of the form :name. For a prepared statement using * question mark placeholders, this will be the 1-indexed position of the parameter. * @param mixed $variable Name of the PHP variable to bind to the SQL statement parameter. * @param int $type Explicit data type for the parameter using the {@see ParameterType} * constants. * @param int|null $length You must specify maxlength when using an OUT bind * so that PHP allocates enough memory to hold the returned value. * * @return bool TRUE on success or FALSE on failure. * * @throws Exception */ public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null); /** * Executes a prepared statement * * If the prepared statement included parameter markers, you must either: * call {@see bindParam()} to bind PHP variables to the parameter markers: * bound variables pass their value as input and receive the output value, * if any, of their associated parameter markers or pass an array of input-only * parameter values. * * @param mixed[]|null $params A numeric array of values with as many elements as there are * bound parameters in the SQL statement being executed. * * @throws Exception */ public function execute($params = null): Result; } PK!+ :dbal/src/Driver/AbstractOracleDriver/EasyConnectString.phpnu[string = $string; } public function __toString(): string { return $this->string; } /** * Creates the object from an array representation * * @param mixed[] $params */ public static function fromArray(array $params): self { return new self(self::renderParams($params)); } /** * Creates the object from the given DBAL connection parameters. * * @param mixed[] $params */ public static function fromConnectionParameters(array $params): self { if (isset($params['connectstring'])) { return new self($params['connectstring']); } if (! isset($params['host'])) { return new self($params['dbname'] ?? ''); } $connectData = []; if (isset($params['servicename']) || isset($params['dbname'])) { $serviceKey = 'SID'; if (isset($params['service'])) { $serviceKey = 'SERVICE_NAME'; } $serviceName = $params['servicename'] ?? $params['dbname']; $connectData[$serviceKey] = $serviceName; } if (isset($params['instancename'])) { $connectData['INSTANCE_NAME'] = $params['instancename']; } if (! empty($params['pooled'])) { $connectData['SERVER'] = 'POOLED'; } return self::fromArray([ 'DESCRIPTION' => [ 'ADDRESS' => [ 'PROTOCOL' => 'TCP', 'HOST' => $params['host'], 'PORT' => $params['port'] ?? 1521, ], 'CONNECT_DATA' => $connectData, ], ]); } /** @param mixed[] $params */ private static function renderParams(array $params): string { $chunks = []; foreach ($params as $key => $value) { $string = self::renderValue($value); if ($string === '') { continue; } $chunks[] = sprintf('(%s=%s)', $key, $string); } return implode('', $chunks); } /** @param mixed $value */ private static function renderValue($value): string { if (is_array($value)) { return self::renderParams($value); } return (string) $value; } } PK!D3||7dbal/src/Driver/OCI8/Exception/SequenceDoesNotExist.phpnu[getEasyConnectString($params); $persistent = ! empty($params['persistent']); $exclusive = ! empty($params['driverOptions']['exclusive']); if ($persistent && $exclusive) { throw InvalidConfiguration::forPersistentAndExclusive(); } if ($persistent) { $connection = @oci_pconnect($username, $password, $connectionString, $charset, $sessionMode); } elseif ($exclusive) { $connection = @oci_new_connect($username, $password, $connectionString, $charset, $sessionMode); } else { $connection = @oci_connect($username, $password, $connectionString, $charset, $sessionMode); } if ($connection === false) { throw ConnectionFailed::new(); } return new Connection($connection); } } PK!7DSS#dbal/src/Driver/OCI8/Connection.phpnu[connection = $connection; $this->parser = new Parser(false); $this->executionMode = new ExecutionMode(); } public function getServerVersion(): string { $version = oci_server_version($this->connection); if ($version === false) { throw Error::new($this->connection); } $result = preg_match('/\s+(\d+\.\d+\.\d+\.\d+\.\d+)\s+/', $version, $matches); assert($result === 1); return $matches[1]; } /** @throws Parser\Exception */ public function prepare(string $sql): DriverStatement { $visitor = new ConvertPositionalToNamedPlaceholders(); $this->parser->parse($sql, $visitor); $statement = oci_parse($this->connection, $visitor->getSQL()); assert(is_resource($statement)); return new Statement($this->connection, $statement, $visitor->getParameterMap(), $this->executionMode); } /** * @throws Exception * @throws Parser\Exception */ public function query(string $sql): ResultInterface { return $this->prepare($sql)->execute(); } /** * {@inheritDoc} */ public function quote($value, $type = ParameterType::STRING) { if (is_int($value) || is_float($value)) { return $value; } $value = str_replace("'", "''", $value); return "'" . addcslashes($value, "\000\n\r\\\032") . "'"; } /** * @throws Exception * @throws Parser\Exception */ public function exec(string $sql): int { return $this->prepare($sql)->execute()->rowCount(); } /** * {@inheritDoc} * * @param string|null $name * * @return int|false * * @throws Parser\Exception */ public function lastInsertId($name = null) { if ($name === null) { return false; } Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4687', 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', ); $result = $this->query('SELECT ' . $name . '.CURRVAL FROM DUAL')->fetchOne(); if ($result === false) { throw SequenceDoesNotExist::new(); } return (int) $result; } public function beginTransaction(): bool { $this->executionMode->disableAutoCommit(); return true; } public function commit(): bool { if (! oci_commit($this->connection)) { throw Error::new($this->connection); } $this->executionMode->enableAutoCommit(); return true; } public function rollBack(): bool { if (! oci_rollback($this->connection)) { throw Error::new($this->connection); } $this->executionMode->enableAutoCommit(); return true; } /** @return resource */ public function getNativeConnection() { return $this->connection; } } PK! 80"dbal/src/Driver/OCI8/Statement.phpnu[ */ private array $parameterMap; private ExecutionMode $executionMode; /** * @internal The statement can be only instantiated by its driver connection. * * @param resource $connection * @param resource $statement * @param array $parameterMap */ public function __construct($connection, $statement, array $parameterMap, ExecutionMode $executionMode) { $this->connection = $connection; $this->statement = $statement; $this->parameterMap = $parameterMap; $this->executionMode = $executionMode; } /** * {@inheritDoc} */ public function bindValue($param, $value, $type = ParameterType::STRING): bool { if (func_num_args() < 3) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5558', 'Not passing $type to Statement::bindValue() is deprecated.' . ' Pass the type corresponding to the parameter being bound.', ); } return $this->bindParam($param, $value, $type); } /** * {@inheritDoc} * * @deprecated Use {@see bindValue()} instead. */ public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5563', '%s is deprecated. Use bindValue() instead.', __METHOD__, ); if (func_num_args() < 3) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5558', 'Not passing $type to Statement::bindParam() is deprecated.' . ' Pass the type corresponding to the parameter being bound.', ); } if (is_int($param)) { if (! isset($this->parameterMap[$param])) { throw UnknownParameterIndex::new($param); } $param = $this->parameterMap[$param]; } if ($type === ParameterType::LARGE_OBJECT) { if ($variable !== null) { $lob = oci_new_descriptor($this->connection, OCI_D_LOB); $lob->writeTemporary($variable, OCI_TEMP_BLOB); $variable =& $lob; } else { $type = ParameterType::STRING; } } return oci_bind_by_name( $this->statement, $param, $variable, $length ?? -1, $this->convertParameterType($type), ); } /** * Converts DBAL parameter type to oci8 parameter type */ private function convertParameterType(int $type): int { switch ($type) { case ParameterType::BINARY: return OCI_B_BIN; case ParameterType::LARGE_OBJECT: return OCI_B_BLOB; default: return SQLT_CHR; } } /** * {@inheritDoc} */ public function execute($params = null): ResultInterface { if ($params !== null) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5556', 'Passing $params to Statement::execute() is deprecated. Bind parameters using' . ' Statement::bindParam() or Statement::bindValue() instead.', ); foreach ($params as $key => $val) { if (is_int($key)) { $this->bindValue($key + 1, $val, ParameterType::STRING); } else { $this->bindValue($key, $val, ParameterType::STRING); } } } if ($this->executionMode->isAutoCommitEnabled()) { $mode = OCI_COMMIT_ON_SUCCESS; } else { $mode = OCI_NO_AUTO_COMMIT; } $ret = @oci_execute($this->statement, $mode); if (! $ret) { throw Error::new($this->statement); } return new Result($this->statement); } } PK!||&dbal/src/Driver/OCI8/ExecutionMode.phpnu[isAutoCommitEnabled = true; } public function disableAutoCommit(): void { $this->isAutoCommitEnabled = false; } public function isAutoCommitEnabled(): bool { return $this->isAutoCommitEnabled; } } PK!N- dbal/src/Driver/OCI8/Result.phpnu[statement = $statement; } /** * {@inheritDoc} */ public function fetchNumeric() { return $this->fetch(OCI_NUM); } /** * {@inheritDoc} */ public function fetchAssociative() { return $this->fetch(OCI_ASSOC); } /** * {@inheritDoc} */ public function fetchOne() { return FetchUtils::fetchOne($this); } /** * {@inheritDoc} */ public function fetchAllNumeric(): array { return $this->fetchAll(OCI_NUM, OCI_FETCHSTATEMENT_BY_ROW); } /** * {@inheritDoc} */ public function fetchAllAssociative(): array { return $this->fetchAll(OCI_ASSOC, OCI_FETCHSTATEMENT_BY_ROW); } /** * {@inheritDoc} */ public function fetchFirstColumn(): array { return $this->fetchAll(OCI_NUM, OCI_FETCHSTATEMENT_BY_COLUMN)[0]; } public function rowCount(): int { $count = oci_num_rows($this->statement); if ($count !== false) { return $count; } return 0; } public function columnCount(): int { $count = oci_num_fields($this->statement); if ($count !== false) { return $count; } return 0; } public function free(): void { oci_cancel($this->statement); } /** * @return mixed|false * * @throws Exception */ private function fetch(int $mode) { $result = oci_fetch_array($this->statement, $mode | OCI_RETURN_NULLS | OCI_RETURN_LOBS); if ($result === false && oci_error($this->statement) !== false) { throw Error::new($this->statement); } return $result; } /** @return array */ private function fetchAll(int $mode, int $fetchStructure): array { oci_fetch_all( $this->statement, $result, 0, -1, $mode | OCI_RETURN_NULLS | $fetchStructure | OCI_RETURN_LOBS, ); return $result; } } PK!ҽw""=dbal/src/Driver/OCI8/ConvertPositionalToNamedPlaceholders.phpnu[). * * Oracle does not support positional parameters, hence this method converts all * positional parameters into artificially named parameters. * * @internal This class is not covered by the backward compatibility promise */ final class ConvertPositionalToNamedPlaceholders implements Visitor { /** @var list */ private array $buffer = []; /** @var array */ private array $parameterMap = []; public function acceptOther(string $sql): void { $this->buffer[] = $sql; } public function acceptPositionalParameter(string $sql): void { $position = count($this->parameterMap) + 1; $param = ':param' . $position; $this->parameterMap[$position] = $param; $this->buffer[] = $param; } public function acceptNamedParameter(string $sql): void { $this->buffer[] = $sql; } public function getSQL(): string { return implode('', $this->buffer); } /** @return array */ public function getParameterMap(): array { return $this->parameterMap; } } PK!@,5dbal/src/Driver/OCI8/Middleware/InitializeSession.phpnu[exec( 'ALTER SESSION SET' . " NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'" . " NLS_TIME_FORMAT = 'HH24:MI:SS'" . " NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'" . " NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS'" . " NLS_TIMESTAMP_TZ_FORMAT = 'YYYY-MM-DD HH24:MI:SS TZH:TZM'" . " NLS_NUMERIC_CHARACTERS = '.,'", ); return $connection; } }; } } PK!R'eS+dbal/src/Driver/AbstractSQLServerDriver.phpnu[exec('PRAGMA foreign_keys=ON'); return $connection; } }; } } PK!<8""'dbal/src/Driver/AbstractMySQLDriver.phpnu[getMariaDbMysqlVersionNumber($version); if (version_compare($mariaDbVersion, '10.6.0', '>=')) { return new MariaDb1060Platform(); } if (version_compare($mariaDbVersion, '10.5.2', '>=')) { return new MariaDb1052Platform(); } if (version_compare($mariaDbVersion, '10.4.3', '>=')) { return new MariaDb1043Platform(); } Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6110', 'Support for MariaDB < 10.4 is deprecated and will be removed in DBAL 4.' . ' Consider upgrading to a more recent version of MariaDB.', ); if (version_compare($mariaDbVersion, '10.2.7', '>=')) { return new MariaDb1027Platform(); } } else { $oracleMysqlVersion = $this->getOracleMysqlVersionNumber($version); if (version_compare($oracleMysqlVersion, '8', '>=')) { if (! version_compare($version, '8.0.0', '>=')) { Deprecation::trigger( 'doctrine/orm', 'https://github.com/doctrine/dbal/pull/5779', 'Version detection logic for MySQL will change in DBAL 4. ' . 'Please specify the version as the server reports it, e.g. "8.0.31" instead of "8".', ); } return new MySQL80Platform(); } if (version_compare($oracleMysqlVersion, '5.7.9', '>=')) { if (! version_compare($version, '5.7.9', '>=')) { Deprecation::trigger( 'doctrine/orm', 'https://github.com/doctrine/dbal/pull/5779', 'Version detection logic for MySQL will change in DBAL 4. ' . 'Please specify the version as the server reports it, e.g. "5.7.40" instead of "5.7".', ); } return new MySQL57Platform(); } Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5072', 'MySQL 5.6 support is deprecated and will be removed in DBAL 4.' . ' Consider upgrading to MySQL 5.7 or later.', ); } return $this->getDatabasePlatform(); } /** * Get a normalized 'version number' from the server string * returned by Oracle MySQL servers. * * @param string $versionString Version string returned by the driver, i.e. '5.7.10' * * @throws Exception */ private function getOracleMysqlVersionNumber(string $versionString): string { if ( preg_match( '/^(?P\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $versionString, $versionParts, ) === 0 ) { throw Exception::invalidPlatformVersionSpecified( $versionString, '..', ); } $majorVersion = $versionParts['major']; $minorVersion = $versionParts['minor'] ?? 0; $patchVersion = $versionParts['patch'] ?? null; if ($majorVersion === '5' && $minorVersion === '7') { $patchVersion ??= '9'; } return $majorVersion . '.' . $minorVersion . '.' . $patchVersion; } /** * Detect MariaDB server version, including hack for some mariadb distributions * that starts with the prefix '5.5.5-' * * @param string $versionString Version string as returned by mariadb server, i.e. '5.5.5-Mariadb-10.0.8-xenial' * * @throws Exception */ private function getMariaDbMysqlVersionNumber(string $versionString): string { if (stripos($versionString, 'MariaDB') === 0) { Deprecation::trigger( 'doctrine/orm', 'https://github.com/doctrine/dbal/pull/5779', 'Version detection logic for MySQL will change in DBAL 4. ' . 'Please specify the version as the server reports it, ' . 'e.g. "10.9.3-MariaDB" instead of "mariadb-10.9".', ); } if ( preg_match( '/^(?:5\.5\.5-)?(mariadb-)?(?P\d+)\.(?P\d+)\.(?P\d+)/i', $versionString, $versionParts, ) === 0 ) { throw Exception::invalidPlatformVersionSpecified( $versionString, '^(?:5\.5\.5-)?(mariadb-)?..', ); } return $versionParts['major'] . '.' . $versionParts['minor'] . '.' . $versionParts['patch']; } /** * {@inheritDoc} * * @return AbstractMySQLPlatform */ public function getDatabasePlatform() { return new MySQLPlatform(); } /** * {@inheritDoc} * * @deprecated Use {@link AbstractMySQLPlatform::createSchemaManager()} instead. * * @return MySQLSchemaManager */ public function getSchemaManager(Connection $conn, AbstractPlatform $platform) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5458', 'AbstractMySQLDriver::getSchemaManager() is deprecated.' . ' Use MySQLPlatform::createSchemaManager() instead.', ); assert($platform instanceof AbstractMySQLPlatform); return new MySQLSchemaManager($conn, $platform); } public function getExceptionConverter(): ExceptionConverter { return new MySQL\ExceptionConverter(); } } PK!} %dbal/src/Driver/AbstractDB2Driver.phpnu[getVersionNumber($version), '11.1', '>=')) { return new DB2111Platform(); } Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5156', 'IBM DB2 < 11.1 support is deprecated and will be removed in DBAL 4.' . ' Consider upgrading to IBM DB2 11.1 or later.', ); return $this->getDatabasePlatform(); } /** * Detects IBM DB2 server version * * @param string $versionString Version string as returned by IBM DB2 server, i.e. 'DB2/LINUXX8664 11.5.8.0' * * @throws DBALException */ private function getVersionNumber(string $versionString): string { if ( preg_match( '/^(?:[^\s]+\s)?(?P\d+)\.(?P\d+)\.(?P\d+)/i', $versionString, $versionParts, ) === 0 ) { throw DBALException::invalidPlatformVersionSpecified( $versionString, '^(?:[^\s]+\s)?..', ); } return $versionParts['major'] . '.' . $versionParts['minor'] . '.' . $versionParts['patch']; } } PK!` ,dbal/src/Driver/AbstractPostgreSQLDriver.phpnu[\d+)(?:\.(?P\d+)(?:\.(?P\d+))?)?/', $version, $versionParts) === 0) { throw Exception::invalidPlatformVersionSpecified( $version, '..', ); } $majorVersion = $versionParts['major']; $minorVersion = $versionParts['minor'] ?? 0; $patchVersion = $versionParts['patch'] ?? 0; $version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion; if (version_compare($version, '10.0', '>=')) { return new PostgreSQL100Platform(); } Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5060', 'PostgreSQL 9 support is deprecated and will be removed in DBAL 4.' . ' Consider upgrading to Postgres 10 or later.', ); return new PostgreSQL94Platform(); } /** * {@inheritDoc} */ public function getDatabasePlatform() { return new PostgreSQL94Platform(); } /** * {@inheritDoc} * * @deprecated Use {@link PostgreSQLPlatform::createSchemaManager()} instead. */ public function getSchemaManager(Connection $conn, AbstractPlatform $platform) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5458', 'AbstractPostgreSQLDriver::getSchemaManager() is deprecated.' . ' Use PostgreSQLPlatform::createSchemaManager() instead.', ); assert($platform instanceof PostgreSQLPlatform); return new PostgreSQLSchemaManager($conn, $platform); } public function getExceptionConverter(): ExceptionConverter { return new PostgreSQL\ExceptionConverter(); } } PK!O dbal/src/Driver/Result.phpnu[|false * * @throws Exception */ public function fetchNumeric(); /** * Returns the next row of the result as an associative array or FALSE if there are no more rows. * * @return array|false * * @throws Exception */ public function fetchAssociative(); /** * Returns the first value of the next row of the result or FALSE if there are no more rows. * * @return mixed|false * * @throws Exception */ public function fetchOne(); /** * Returns an array containing all of the result rows represented as numeric arrays. * * @return list> * * @throws Exception */ public function fetchAllNumeric(): array; /** * Returns an array containing all of the result rows represented as associative arrays. * * @return list> * * @throws Exception */ public function fetchAllAssociative(): array; /** * Returns an array containing the values of the first column of the result. * * @return list * * @throws Exception */ public function fetchFirstColumn(): array; /** * Returns the number of rows affected by the DELETE, INSERT, or UPDATE statement that produced the result. * * If the statement executed a SELECT query or a similar platform-specific SQL (e.g. DESCRIBE, SHOW, etc.), * some database drivers may return the number of rows returned by that query. However, this behaviour * is not guaranteed for all drivers and should not be relied on in portable applications. * * @return int The number of rows. * * @throws Exception */ public function rowCount(): int; /** * Returns the number of columns in the result * * @return int The number of columns in the result. If the columns cannot be counted, * this method must return 0. * * @throws Exception */ public function columnCount(): int; /** * Discards the non-fetched portion of the result, enabling the originating statement to be executed again. */ public function free(): void; } PK!['7dbal/src/Driver/Middleware/AbstractResultMiddleware.phpnu[wrappedResult = $result; } /** * {@inheritDoc} */ public function fetchNumeric() { return $this->wrappedResult->fetchNumeric(); } /** * {@inheritDoc} */ public function fetchAssociative() { return $this->wrappedResult->fetchAssociative(); } /** * {@inheritDoc} */ public function fetchOne() { return $this->wrappedResult->fetchOne(); } /** * {@inheritDoc} */ public function fetchAllNumeric(): array { return $this->wrappedResult->fetchAllNumeric(); } /** * {@inheritDoc} */ public function fetchAllAssociative(): array { return $this->wrappedResult->fetchAllAssociative(); } /** * {@inheritDoc} */ public function fetchFirstColumn(): array { return $this->wrappedResult->fetchFirstColumn(); } public function rowCount(): int { return $this->wrappedResult->rowCount(); } public function columnCount(): int { return $this->wrappedResult->columnCount(); } public function free(): void { $this->wrappedResult->free(); } } PK!K:dbal/src/Driver/Middleware/AbstractStatementMiddleware.phpnu[wrappedStatement = $wrappedStatement; } /** * {@inheritDoc} */ public function bindValue($param, $value, $type = ParameterType::STRING) { if (func_num_args() < 3) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5558', 'Not passing $type to Statement::bindValue() is deprecated.' . ' Pass the type corresponding to the parameter being bound.', ); } return $this->wrappedStatement->bindValue($param, $value, $type); } /** * {@inheritDoc} * * @deprecated Use {@see bindValue()} instead. */ public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5563', '%s is deprecated. Use bindValue() instead.', __METHOD__, ); if (func_num_args() < 3) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5558', 'Not passing $type to Statement::bindParam() is deprecated.' . ' Pass the type corresponding to the parameter being bound.', ); } return $this->wrappedStatement->bindParam($param, $variable, $type, $length); } /** * {@inheritDoc} */ public function execute($params = null): Result { return $this->wrappedStatement->execute($params); } } PK!BJ7dbal/src/Driver/Middleware/AbstractDriverMiddleware.phpnu[wrappedDriver = $wrappedDriver; } /** * {@inheritDoc} */ public function connect( #[SensitiveParameter] array $params ) { return $this->wrappedDriver->connect($params); } /** * {@inheritDoc} */ public function getDatabasePlatform() { return $this->wrappedDriver->getDatabasePlatform(); } /** * {@inheritDoc} * * @deprecated Use {@link AbstractPlatform::createSchemaManager()} instead. */ public function getSchemaManager(Connection $conn, AbstractPlatform $platform) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5458', 'AbstractDriverMiddleware::getSchemaManager() is deprecated.' . ' Use AbstractPlatform::createSchemaManager() instead.', ); return $this->wrappedDriver->getSchemaManager($conn, $platform); } public function getExceptionConverter(): ExceptionConverter { return $this->wrappedDriver->getExceptionConverter(); } /** * {@inheritDoc} */ public function createDatabasePlatformForVersion($version) { if ($this->wrappedDriver instanceof VersionAwarePlatformDriver) { return $this->wrappedDriver->createDatabasePlatformForVersion($version); } return $this->wrappedDriver->getDatabasePlatform(); } } PK!} ;dbal/src/Driver/Middleware/AbstractConnectionMiddleware.phpnu[wrappedConnection = $wrappedConnection; } public function prepare(string $sql): Statement { return $this->wrappedConnection->prepare($sql); } public function query(string $sql): Result { return $this->wrappedConnection->query($sql); } /** * {@inheritDoc} */ public function quote($value, $type = ParameterType::STRING) { return $this->wrappedConnection->quote($value, $type); } public function exec(string $sql): int { return $this->wrappedConnection->exec($sql); } /** * {@inheritDoc} */ public function lastInsertId($name = null) { if ($name !== null) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4687', 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', ); } return $this->wrappedConnection->lastInsertId($name); } /** * {@inheritDoc} */ public function beginTransaction() { return $this->wrappedConnection->beginTransaction(); } /** * {@inheritDoc} */ public function commit() { return $this->wrappedConnection->commit(); } /** * {@inheritDoc} */ public function rollBack() { return $this->wrappedConnection->rollBack(); } /** * {@inheritDoc} */ public function getServerVersion() { if (! $this->wrappedConnection instanceof ServerInfoAwareConnection) { throw new LogicException('The underlying connection is not a ServerInfoAwareConnection'); } return $this->wrappedConnection->getServerVersion(); } /** @return resource|object */ public function getNativeConnection() { if (! method_exists($this->wrappedConnection, 'getNativeConnection')) { throw new LogicException(sprintf( 'The driver connection %s does not support accessing the native connection.', get_class($this->wrappedConnection), )); } return $this->wrappedConnection->getNativeConnection(); } } PK!1qqEdbal/src/Driver/AbstractSQLServerDriver/Exception/PortWithoutHost.phpnu[error, $connection->sqlstate, $connection->errno); } public static function upcast(mysqli_sql_exception $exception): self { $p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate'); $p->setAccessible(true); return new self($exception->getMessage(), $p->getValue($exception), (int) $exception->getCode(), $exception); } } PK!~ѯ>dbal/src/Driver/Mysqli/Exception/FailedReadingStreamOffset.phpnu[error, $statement->sqlstate, $statement->errno); } public static function upcast(mysqli_sql_exception $exception): self { $p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate'); $p->setAccessible(true); return new self($exception->getMessage(), $p->getValue($exception), (int) $exception->getCode(), $exception); } } PK!˩3dbal/src/Driver/Mysqli/Exception/InvalidCharset.phpnu[error), $connection->sqlstate, $connection->errno, ); } public static function upcast(mysqli_sql_exception $exception, string $charset): self { $p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate'); $p->setAccessible(true); return new self( sprintf('Failed to set charset "%s": %s', $charset, $exception->getMessage()), $p->getValue($exception), (int) $exception->getCode(), $exception, ); } } PK!`2dbal/src/Driver/Mysqli/Exception/InvalidOption.phpnu[connect_error; assert($error !== null); return new self($error, 'HY000', $connection->connect_errno); } public static function upcast(mysqli_sql_exception $exception): self { $p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate'); $p->setAccessible(true); return new self($exception->getMessage(), $p->getValue($exception), (int) $exception->getCode(), $exception); } } PK!E^Ȋ !dbal/src/Driver/Mysqli/Driver.phpnu[compilePreInitializers($params) as $initializer) { $initializer->initialize($connection); } try { $success = @$connection->real_connect( $host, $params['user'] ?? null, $params['password'] ?? null, $params['dbname'] ?? null, $params['port'] ?? null, $params['unix_socket'] ?? null, $params['driverOptions'][Connection::OPTION_FLAGS] ?? 0, ); } catch (mysqli_sql_exception $e) { throw ConnectionFailed::upcast($e); } if (! $success) { throw ConnectionFailed::new($connection); } foreach ($this->compilePostInitializers($params) as $initializer) { $initializer->initialize($connection); } return new Connection($connection); } /** * @param array $params * * @return Generator */ private function compilePreInitializers( #[SensitiveParameter] array $params ): Generator { unset($params['driverOptions'][Connection::OPTION_FLAGS]); if (isset($params['driverOptions']) && $params['driverOptions'] !== []) { yield new Options($params['driverOptions']); } if ( ! isset($params['ssl_key']) && ! isset($params['ssl_cert']) && ! isset($params['ssl_ca']) && ! isset($params['ssl_capath']) && ! isset($params['ssl_cipher']) ) { return; } yield new Secure( $params['ssl_key'] ?? '', $params['ssl_cert'] ?? '', $params['ssl_ca'] ?? '', $params['ssl_capath'] ?? '', $params['ssl_cipher'] ?? '', ); } /** * @param array $params * * @return Generator */ private function compilePostInitializers( #[SensitiveParameter] array $params ): Generator { if (! isset($params['charset'])) { return; } yield new Charset($params['charset']); } } PK!.dbal/src/Driver/Mysqli/Initializer/Options.phpnu[ */ private array $options; /** @param array $options */ public function __construct(array $options) { $this->options = $options; } public function initialize(mysqli $connection): void { foreach ($this->options as $option => $value) { if (! mysqli_options($connection, $option, $value)) { throw InvalidOption::fromOption($option, $value); } } } } PK!7vy.dbal/src/Driver/Mysqli/Initializer/Charset.phpnu[charset = $charset; } public function initialize(mysqli $connection): void { try { $success = $connection->set_charset($this->charset); } catch (mysqli_sql_exception $e) { throw InvalidCharset::upcast($e, $this->charset); } if ($success) { return; } throw InvalidCharset::fromCharset($connection, $this->charset); } } PK!G VV-dbal/src/Driver/Mysqli/Initializer/Secure.phpnu[key = $key; $this->cert = $cert; $this->ca = $ca; $this->capath = $capath; $this->cipher = $cipher; } public function initialize(mysqli $connection): void { $connection->ssl_set($this->key, $this->cert, $this->ca, $this->capath, $this->cipher); } } PK!a %dbal/src/Driver/Mysqli/Connection.phpnu[connection = $connection; } /** * Retrieves mysqli native resource handle. * * Could be used if part of your application is not using DBAL. * * @deprecated Call {@see getNativeConnection()} instead. */ public function getWrappedResourceHandle(): mysqli { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5037', '%s is deprecated, call getNativeConnection() instead.', __METHOD__, ); return $this->getNativeConnection(); } public function getServerVersion(): string { return $this->connection->get_server_info(); } public function prepare(string $sql): DriverStatement { try { $stmt = $this->connection->prepare($sql); } catch (mysqli_sql_exception $e) { throw ConnectionError::upcast($e); } if ($stmt === false) { throw ConnectionError::new($this->connection); } return new Statement($stmt); } public function query(string $sql): ResultInterface { return $this->prepare($sql)->execute(); } /** * {@inheritDoc} */ public function quote($value, $type = ParameterType::STRING) { return "'" . $this->connection->escape_string($value) . "'"; } public function exec(string $sql): int { try { $result = $this->connection->query($sql); } catch (mysqli_sql_exception $e) { throw ConnectionError::upcast($e); } if ($result === false) { throw ConnectionError::new($this->connection); } return $this->connection->affected_rows; } /** * {@inheritDoc} */ public function lastInsertId($name = null) { if ($name !== null) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4687', 'The usage of Connection::lastInsertId() with a sequence name is deprecated.', ); } return $this->connection->insert_id; } public function beginTransaction(): bool { $this->connection->begin_transaction(); return true; } public function commit(): bool { try { return $this->connection->commit(); } catch (mysqli_sql_exception $e) { return false; } } public function rollBack(): bool { try { return $this->connection->rollback(); } catch (mysqli_sql_exception $e) { return false; } } public function getNativeConnection(): mysqli { return $this->connection; } } PK!ڬ&dbal/src/Driver/Mysqli/Initializer.phpnu[ 's', ParameterType::STRING => 's', ParameterType::BINARY => 's', ParameterType::BOOLEAN => 'i', ParameterType::NULL => 's', ParameterType::INTEGER => 'i', ParameterType::LARGE_OBJECT => 'b', ]; private mysqli_stmt $stmt; /** @var mixed[] */ private array $boundValues; private string $types; /** * Contains ref values for bindValue(). * * @var mixed[] */ private array $values = []; /** @internal The statement can be only instantiated by its driver connection. */ public function __construct(mysqli_stmt $stmt) { $this->stmt = $stmt; $paramCount = $this->stmt->param_count; $this->types = str_repeat('s', $paramCount); $this->boundValues = array_fill(1, $paramCount, null); } /** * @deprecated Use {@see bindValue()} instead. * * {@inheritDoc} * * @psalm-assert ParameterType::* $type */ public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null): bool { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5563', '%s is deprecated. Use bindValue() instead.', __METHOD__, ); assert(is_int($param)); if (func_num_args() < 3) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5558', 'Not passing $type to Statement::bindParam() is deprecated.' . ' Pass the type corresponding to the parameter being bound.', ); } if (! isset(self::PARAM_TYPE_MAP[$type])) { throw UnknownParameterType::new($type); } $this->boundValues[$param] =& $variable; $this->types[$param - 1] = self::PARAM_TYPE_MAP[$type]; return true; } /** * {@inheritDoc} * * @psalm-assert ParameterType::* $type */ public function bindValue($param, $value, $type = ParameterType::STRING): bool { assert(is_int($param)); if (func_num_args() < 3) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5558', 'Not passing $type to Statement::bindValue() is deprecated.' . ' Pass the type corresponding to the parameter being bound.', ); } if (! isset(self::PARAM_TYPE_MAP[$type])) { throw UnknownParameterType::new($type); } $this->values[$param] = $value; $this->boundValues[$param] =& $this->values[$param]; $this->types[$param - 1] = self::PARAM_TYPE_MAP[$type]; return true; } /** * {@inheritDoc} */ public function execute($params = null): ResultInterface { if ($params !== null) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5556', 'Passing $params to Statement::execute() is deprecated. Bind parameters using' . ' Statement::bindParam() or Statement::bindValue() instead.', ); } if ($params !== null && count($params) > 0) { if (! $this->bindUntypedValues($params)) { throw StatementError::new($this->stmt); } } elseif (count($this->boundValues) > 0) { $this->bindTypedParameters(); } try { $result = $this->stmt->execute(); } catch (mysqli_sql_exception $e) { throw StatementError::upcast($e); } if (! $result) { throw StatementError::new($this->stmt); } return new Result($this->stmt); } /** * Binds parameters with known types previously bound to the statement * * @throws Exception */ private function bindTypedParameters(): void { $streams = $values = []; $types = $this->types; foreach ($this->boundValues as $parameter => $value) { assert(is_int($parameter)); if (! isset($types[$parameter - 1])) { $types[$parameter - 1] = self::PARAM_TYPE_MAP[ParameterType::STRING]; } if ($types[$parameter - 1] === self::PARAM_TYPE_MAP[ParameterType::LARGE_OBJECT]) { if (is_resource($value)) { if (get_resource_type($value) !== 'stream') { throw NonStreamResourceUsedAsLargeObject::new($parameter); } $streams[$parameter] = $value; $values[$parameter] = null; continue; } $types[$parameter - 1] = self::PARAM_TYPE_MAP[ParameterType::STRING]; } $values[$parameter] = $value; } if (! $this->stmt->bind_param($types, ...$values)) { throw StatementError::new($this->stmt); } $this->sendLongData($streams); } /** * Handle $this->_longData after regular query parameters have been bound * * @param array $streams * * @throws Exception */ private function sendLongData(array $streams): void { foreach ($streams as $paramNr => $stream) { while (! feof($stream)) { $chunk = fread($stream, 8192); if ($chunk === false) { throw FailedReadingStreamOffset::new($paramNr); } if (! $this->stmt->send_long_data($paramNr - 1, $chunk)) { throw StatementError::new($this->stmt); } } } } /** * Binds a array of values to bound parameters. * * @param mixed[] $values */ private function bindUntypedValues(array $values): bool { return $this->stmt->bind_param(str_repeat('s', count($values)), ...$values); } } PK!Gק!dbal/src/Driver/Mysqli/Result.phpnu[ */ private array $columnNames = []; /** @var mixed[] */ private array $boundValues = []; /** * @internal The result can be only instantiated by its driver connection or statement. * * @throws Exception */ public function __construct(mysqli_stmt $statement) { $this->statement = $statement; $meta = $statement->result_metadata(); if ($meta === false) { return; } $this->hasColumns = true; $this->columnNames = array_column($meta->fetch_fields(), 'name'); $meta->free(); // Store result of every execution which has it. Otherwise it will be impossible // to execute a new statement in case if the previous one has non-fetched rows // @link http://dev.mysql.com/doc/refman/5.7/en/commands-out-of-sync.html $this->statement->store_result(); // Bind row values _after_ storing the result. Otherwise, if mysqli is compiled with libmysql, // it will have to allocate as much memory as it may be needed for the given column type // (e.g. for a LONGBLOB column it's 4 gigabytes) // @link https://bugs.php.net/bug.php?id=51386#1270673122 // // Make sure that the values are bound after each execution. Otherwise, if free() has been // previously called on the result, the values are unbound making the statement unusable. // // It's also important that row values are bound after _each_ call to store_result(). Otherwise, // if mysqli is compiled with libmysql, subsequently fetched string values will get truncated // to the length of the ones fetched during the previous execution. $this->boundValues = array_fill(0, count($this->columnNames), null); // The following is necessary as PHP cannot handle references to properties properly $refs = &$this->boundValues; if (! $this->statement->bind_result(...$refs)) { throw StatementError::new($this->statement); } } /** * {@inheritDoc} */ public function fetchNumeric() { try { $ret = $this->statement->fetch(); } catch (mysqli_sql_exception $e) { throw StatementError::upcast($e); } if ($ret === false) { throw StatementError::new($this->statement); } if ($ret === null) { return false; } $values = []; foreach ($this->boundValues as $v) { $values[] = $v; } return $values; } /** * {@inheritDoc} */ public function fetchAssociative() { $values = $this->fetchNumeric(); if ($values === false) { return false; } return array_combine($this->columnNames, $values); } /** * {@inheritDoc} */ public function fetchOne() { return FetchUtils::fetchOne($this); } /** * {@inheritDoc} */ public function fetchAllNumeric(): array { return FetchUtils::fetchAllNumeric($this); } /** * {@inheritDoc} */ public function fetchAllAssociative(): array { return FetchUtils::fetchAllAssociative($this); } /** * {@inheritDoc} */ public function fetchFirstColumn(): array { return FetchUtils::fetchFirstColumn($this); } public function rowCount(): int { if ($this->hasColumns) { return $this->statement->num_rows; } return $this->statement->affected_rows; } public function columnCount(): int { return $this->statement->field_count; } public function free(): void { $this->statement->free_result(); } } PK!\Y-dbal/src/Driver/ServerInfoAwareConnection.phpnu[getCode()) { case -104: return new SyntaxErrorException($exception, $query); case -203: return new NonUniqueFieldNameException($exception, $query); case -204: return new TableNotFoundException($exception, $query); case -206: return new InvalidFieldNameException($exception, $query); case -407: return new NotNullConstraintViolationException($exception, $query); case -530: case -531: case -532: case -20356: return new ForeignKeyConstraintViolationException($exception, $query); case -601: return new TableExistsException($exception, $query); case -803: return new UniqueConstraintViolationException($exception, $query); case -1336: case -30082: return new ConnectionException($exception, $query); } return new DriverException($exception, $query); } } PK! 73dbal/src/Driver/API/SQLite/UserDefinedFunctions.phpnu[ ['callback' => [SqlitePlatform::class, 'udfSqrt'], 'numArgs' => 1], 'mod' => ['callback' => [SqlitePlatform::class, 'udfMod'], 'numArgs' => 2], 'locate' => ['callback' => [SqlitePlatform::class, 'udfLocate'], 'numArgs' => -1], ]; /** * @param callable(string, callable, int): bool $callback * @param array $additionalFunctions */ public static function register(callable $callback, array $additionalFunctions = []): void { $userDefinedFunctions = array_merge(self::DEFAULT_FUNCTIONS, $additionalFunctions); foreach ($userDefinedFunctions as $function => $data) { $callback($function, $data['callback'], $data['numArgs']); } } /** * User-defined function that implements MOD(). * * @param int $a * @param int $b */ public static function mod($a, $b): int { return $a % $b; } /** * User-defined function that implements LOCATE(). * * @param string $str * @param string $substr * @param int $offset */ public static function locate($str, $substr, $offset = 0): int { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5749', 'Relying on DBAL\'s emulated LOCATE() function is deprecated. ' . 'Use INSTR() or %s::getLocateExpression() instead.', AbstractPlatform::class, ); // SQL's LOCATE function works on 1-based positions, while PHP's strpos works on 0-based positions. // So we have to make them compatible if an offset is given. if ($offset > 0) { $offset -= 1; } $pos = strpos($str, $substr, $offset); if ($pos !== false) { return $pos + 1; } return 0; } } PK!f?$ $ 1dbal/src/Driver/API/SQLite/ExceptionConverter.phpnu[getMessage(), 'database is locked') !== false) { return new LockWaitTimeoutException($exception, $query); } if ( strpos($exception->getMessage(), 'must be unique') !== false || strpos($exception->getMessage(), 'is not unique') !== false || strpos($exception->getMessage(), 'are not unique') !== false || strpos($exception->getMessage(), 'UNIQUE constraint failed') !== false ) { return new UniqueConstraintViolationException($exception, $query); } if ( strpos($exception->getMessage(), 'may not be NULL') !== false || strpos($exception->getMessage(), 'NOT NULL constraint failed') !== false ) { return new NotNullConstraintViolationException($exception, $query); } if (strpos($exception->getMessage(), 'no such table:') !== false) { return new TableNotFoundException($exception, $query); } if (strpos($exception->getMessage(), 'already exists') !== false) { return new TableExistsException($exception, $query); } if (strpos($exception->getMessage(), 'has no column named') !== false) { return new InvalidFieldNameException($exception, $query); } if (strpos($exception->getMessage(), 'ambiguous column name') !== false) { return new NonUniqueFieldNameException($exception, $query); } if (strpos($exception->getMessage(), 'syntax error') !== false) { return new SyntaxErrorException($exception, $query); } if (strpos($exception->getMessage(), 'attempt to write a readonly database') !== false) { return new ReadOnlyException($exception, $query); } if (strpos($exception->getMessage(), 'unable to open database file') !== false) { return new ConnectionException($exception, $query); } if (strpos($exception->getMessage(), 'FOREIGN KEY constraint failed') !== false) { return new ForeignKeyConstraintViolationException($exception, $query); } return new DriverException($exception, $query); } } PK!ah# # 1dbal/src/Driver/API/SQLSrv/ExceptionConverter.phpnu[getCode()) { case 102: return new SyntaxErrorException($exception, $query); case 207: return new InvalidFieldNameException($exception, $query); case 208: return new TableNotFoundException($exception, $query); case 209: return new NonUniqueFieldNameException($exception, $query); case 515: return new NotNullConstraintViolationException($exception, $query); case 547: case 4712: return new ForeignKeyConstraintViolationException($exception, $query); case 2601: case 2627: return new UniqueConstraintViolationException($exception, $query); case 2714: return new TableExistsException($exception, $query); case 3701: case 15151: return new DatabaseObjectNotFoundException($exception, $query); case 11001: case 18456: return new ConnectionException($exception, $query); } return new DriverException($exception, $query); } } PK!ά``0dbal/src/Driver/API/MySQL/ExceptionConverter.phpnu[getCode()) { case 1008: return new DatabaseDoesNotExist($exception, $query); case 1213: return new DeadlockException($exception, $query); case 1205: return new LockWaitTimeoutException($exception, $query); case 1050: return new TableExistsException($exception, $query); case 1051: case 1146: return new TableNotFoundException($exception, $query); case 1216: case 1217: case 1451: case 1452: case 1701: return new ForeignKeyConstraintViolationException($exception, $query); case 1062: case 1557: case 1569: case 1586: return new UniqueConstraintViolationException($exception, $query); case 1054: case 1166: case 1611: return new InvalidFieldNameException($exception, $query); case 1052: case 1060: case 1110: return new NonUniqueFieldNameException($exception, $query); case 1064: case 1149: case 1287: case 1341: case 1342: case 1343: case 1344: case 1382: case 1479: case 1541: case 1554: case 1626: return new SyntaxErrorException($exception, $query); case 1044: case 1045: case 1046: case 1049: case 1095: case 1142: case 1143: case 1227: case 1370: case 1429: case 2002: case 2005: case 2054: return new ConnectionException($exception, $query); case 2006: return new ConnectionLost($exception, $query); case 1048: case 1121: case 1138: case 1171: case 1252: case 1263: case 1364: case 1566: return new NotNullConstraintViolationException($exception, $query); } return new DriverException($exception, $query); } } PK!-t .dbal/src/Driver/API/OCI/ExceptionConverter.phpnu[getCode()) { case 1: case 2299: case 38911: return new UniqueConstraintViolationException($exception, $query); case 904: return new InvalidFieldNameException($exception, $query); case 918: case 960: return new NonUniqueFieldNameException($exception, $query); case 923: return new SyntaxErrorException($exception, $query); case 942: return new TableNotFoundException($exception, $query); case 955: return new TableExistsException($exception, $query); case 1017: case 12545: return new ConnectionException($exception, $query); case 1400: return new NotNullConstraintViolationException($exception, $query); case 1918: return new DatabaseDoesNotExist($exception, $query); case 2289: case 2443: case 4080: return new DatabaseObjectNotFoundException($exception, $query); case 2266: case 2291: case 2292: return new ForeignKeyConstraintViolationException($exception, $query); } return new DriverException($exception, $query); } } PK!ONN*dbal/src/Driver/API/ExceptionConverter.phpnu[getSQLState()) { case '40001': case '40P01': return new DeadlockException($exception, $query); case '0A000': // Foreign key constraint violations during a TRUNCATE operation // are considered "feature not supported" in PostgreSQL. if (strpos($exception->getMessage(), 'truncate') !== false) { return new ForeignKeyConstraintViolationException($exception, $query); } break; case '23502': return new NotNullConstraintViolationException($exception, $query); case '23503': return new ForeignKeyConstraintViolationException($exception, $query); case '23505': return new UniqueConstraintViolationException($exception, $query); case '3D000': return new DatabaseDoesNotExist($exception, $query); case '3F000': return new SchemaDoesNotExist($exception, $query); case '42601': return new SyntaxErrorException($exception, $query); case '42702': return new NonUniqueFieldNameException($exception, $query); case '42703': return new InvalidFieldNameException($exception, $query); case '42P01': return new TableNotFoundException($exception, $query); case '42P07': return new TableExistsException($exception, $query); case '08006': return new ConnectionException($exception, $query); } // Prior to fixing https://bugs.php.net/bug.php?id=64705 (PHP 7.4.10), // in some cases (mainly connection errors) the PDO exception wouldn't provide a SQLSTATE via its code. // We have to match against the SQLSTATE in the error message in these cases. if ($exception->getCode() === 7 && strpos($exception->getMessage(), 'SQLSTATE[08006]') !== false) { return new ConnectionException($exception, $query); } return new DriverException($exception, $query); } } PK!yWdbal/src/Driver/FetchUtils.phpnu[fetchNumeric(); if ($row === false) { return false; } return $row[0]; } /** * @return list> * * @throws Exception */ public static function fetchAllNumeric(Result $result): array { $rows = []; while (($row = $result->fetchNumeric()) !== false) { $rows[] = $row; } return $rows; } /** * @return list> * * @throws Exception */ public static function fetchAllAssociative(Result $result): array { $rows = []; while (($row = $result->fetchAssociative()) !== false) { $rows[] = $row; } return $rows; } /** * @return list * * @throws Exception */ public static function fetchFirstColumn(Result $result): array { $rows = []; while (($row = $result->fetchOne()) !== false) { $rows[] = $row; } return $rows; } } PK!u+dbal/src/Id/TableGeneratorSchemaVisitor.phpnu[generatorTableName = $generatorTableName; } /** * {@inheritDoc} */ public function acceptSchema(Schema $schema) { $table = $schema->createTable($this->generatorTableName); $table->addColumn('sequence_name', 'string'); $table->addColumn('sequence_value', 'integer', ['default' => 1]); $table->addColumn('sequence_increment_by', 'integer', ['default' => 1]); } /** * {@inheritDoc} */ public function acceptTable(Table $table) { } /** * {@inheritDoc} */ public function acceptColumn(Table $table, Column $column) { } /** * {@inheritDoc} */ public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) { } /** * {@inheritDoc} */ public function acceptIndex(Table $table, Index $index) { } /** * {@inheritDoc} */ public function acceptSequence(Sequence $sequence) { } } PK!^dbal/src/Id/TableGenerator.phpnu[getDriver() instanceof Driver\PDO\SQLite\Driver) { throw new Exception('Cannot use TableGenerator with SQLite.'); } $this->conn = DriverManager::getConnection( $conn->getParams(), $conn->getConfiguration(), $conn->getEventManager(), ); $this->generatorTableName = $generatorTableName; } /** * Generates the next unused value for the given sequence name. * * @param string $sequence * * @return int * * @throws Exception */ public function nextValue($sequence) { if (isset($this->sequences[$sequence])) { $value = $this->sequences[$sequence]['value']; $this->sequences[$sequence]['value']++; if ($this->sequences[$sequence]['value'] >= $this->sequences[$sequence]['max']) { unset($this->sequences[$sequence]); } return $value; } $this->conn->beginTransaction(); try { $row = $this->conn->createQueryBuilder() ->select('sequence_value', 'sequence_increment_by') ->from($this->generatorTableName) ->where('sequence_name = ?') ->forUpdate() ->setParameter(1, $sequence) ->fetchAssociative(); if ($row !== false) { $row = array_change_key_case($row, CASE_LOWER); $value = $row['sequence_value']; $value++; assert(is_int($value)); if ($row['sequence_increment_by'] > 1) { $this->sequences[$sequence] = [ 'value' => $value, 'max' => $row['sequence_value'] + $row['sequence_increment_by'], ]; } $sql = 'UPDATE ' . $this->generatorTableName . ' ' . 'SET sequence_value = sequence_value + sequence_increment_by ' . 'WHERE sequence_name = ? AND sequence_value = ?'; $rows = $this->conn->executeStatement($sql, [$sequence, $row['sequence_value']]); if ($rows !== 1) { throw new Exception('Race-condition detected while updating sequence. Aborting generation'); } } else { $this->conn->insert( $this->generatorTableName, ['sequence_name' => $sequence, 'sequence_value' => 1, 'sequence_increment_by' => 1], ); $value = 1; } $this->conn->commit(); } catch (Throwable $e) { $this->conn->rollBack(); throw new Exception( 'Error occurred while generating ID with TableGenerator, aborted generation: ' . $e->getMessage(), 0, $e, ); } return $value; } } PK! 0) { $query .= sprintf(' OFFSET %d', $offset); } } elseif ($offset > 0) { // 2^64-1 is the maximum of unsigned BIGINT, the biggest limit possible $query .= sprintf(' LIMIT 18446744073709551615 OFFSET %d', $offset); } return $query; } /** * {@inheritDoc} * * @deprecated Use {@see quoteIdentifier()} to quote identifiers instead. */ public function getIdentifierQuoteCharacter() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5388', 'AbstractMySQLPlatform::getIdentifierQuoteCharacter() is deprecated. Use quoteIdentifier() instead.', ); return '`'; } /** * {@inheritDoc} */ public function getRegexpExpression() { return 'RLIKE'; } /** * {@inheritDoc} */ public function getLocateExpression($str, $substr, $startPos = false) { if ($startPos === false) { return 'LOCATE(' . $substr . ', ' . $str . ')'; } return 'LOCATE(' . $substr . ', ' . $str . ', ' . $startPos . ')'; } /** * {@inheritDoc} */ public function getConcatExpression() { return sprintf('CONCAT(%s)', implode(', ', func_get_args())); } /** * {@inheritDoc} */ protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) { $function = $operator === '+' ? 'DATE_ADD' : 'DATE_SUB'; return $function . '(' . $date . ', INTERVAL ' . $interval . ' ' . $unit . ')'; } /** * {@inheritDoc} */ public function getDateDiffExpression($date1, $date2) { return 'DATEDIFF(' . $date1 . ', ' . $date2 . ')'; } public function getCurrentDatabaseExpression(): string { return 'DATABASE()'; } /** * {@inheritDoc} */ public function getLengthExpression($column) { return 'CHAR_LENGTH(' . $column . ')'; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListDatabasesSQL() { return 'SHOW DATABASES'; } /** * @deprecated * * {@inheritDoc} */ public function getListTableConstraintsSQL($table) { return 'SHOW INDEX FROM ' . $table; } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * {@inheritDoc} * * Two approaches to listing the table indexes. The information_schema is * preferred, because it doesn't cause problems with SQL keywords such as "order" or "table". */ public function getListTableIndexesSQL($table, $database = null) { if ($database !== null) { return 'SELECT NON_UNIQUE AS Non_Unique, INDEX_NAME AS Key_name, COLUMN_NAME AS Column_Name,' . ' SUB_PART AS Sub_Part, INDEX_TYPE AS Index_Type' . ' FROM information_schema.STATISTICS WHERE TABLE_NAME = ' . $this->quoteStringLiteral($table) . ' AND TABLE_SCHEMA = ' . $this->quoteStringLiteral($database) . ' ORDER BY SEQ_IN_INDEX ASC'; } return 'SHOW INDEX FROM ' . $table; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListViewsSQL($database) { return 'SELECT * FROM information_schema.VIEWS WHERE TABLE_SCHEMA = ' . $this->quoteStringLiteral($database); } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * @param string $table * @param string|null $database * * @return string */ public function getListTableForeignKeysSQL($table, $database = null) { // The schema name is passed multiple times as a literal in the WHERE clause instead of using a JOIN condition // in order to avoid performance issues on MySQL older than 8.0 and the corresponding MariaDB versions // caused by https://bugs.mysql.com/bug.php?id=81347 return 'SELECT k.CONSTRAINT_NAME, k.COLUMN_NAME, k.REFERENCED_TABLE_NAME, ' . 'k.REFERENCED_COLUMN_NAME /*!50116 , c.UPDATE_RULE, c.DELETE_RULE */ ' . 'FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE k /*!50116 ' . 'INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS c ON ' . 'c.CONSTRAINT_NAME = k.CONSTRAINT_NAME AND ' . 'c.TABLE_NAME = k.TABLE_NAME */ ' . 'WHERE k.TABLE_NAME = ' . $this->quoteStringLiteral($table) . ' ' . 'AND k.TABLE_SCHEMA = ' . $this->getDatabaseNameSQL($database) . ' /*!50116 ' . 'AND c.CONSTRAINT_SCHEMA = ' . $this->getDatabaseNameSQL($database) . ' */' . 'ORDER BY k.ORDINAL_POSITION'; } /** * {@inheritDoc} */ protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) { if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'Relying on the default string column length on MySQL is deprecated' . ', specify the length explicitly.', ); } return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(255)') : ($length > 0 ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); } /** * {@inheritDoc} */ protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) { if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'Relying on the default binary column length on MySQL is deprecated' . ', specify the length explicitly.', ); } return $fixed ? 'BINARY(' . ($length > 0 ? $length : 255) . ')' : 'VARBINARY(' . ($length > 0 ? $length : 255) . ')'; } /** * Gets the SQL snippet used to declare a CLOB column type. * TINYTEXT : 2 ^ 8 - 1 = 255 * TEXT : 2 ^ 16 - 1 = 65535 * MEDIUMTEXT : 2 ^ 24 - 1 = 16777215 * LONGTEXT : 2 ^ 32 - 1 = 4294967295 * * {@inheritDoc} */ public function getClobTypeDeclarationSQL(array $column) { if (! empty($column['length']) && is_numeric($column['length'])) { $length = $column['length']; if ($length <= static::LENGTH_LIMIT_TINYTEXT) { return 'TINYTEXT'; } if ($length <= static::LENGTH_LIMIT_TEXT) { return 'TEXT'; } if ($length <= static::LENGTH_LIMIT_MEDIUMTEXT) { return 'MEDIUMTEXT'; } } return 'LONGTEXT'; } /** * {@inheritDoc} */ public function getDateTimeTypeDeclarationSQL(array $column) { if (isset($column['version']) && $column['version'] === true) { return 'TIMESTAMP'; } return 'DATETIME'; } /** * {@inheritDoc} */ public function getDateTypeDeclarationSQL(array $column) { return 'DATE'; } /** * {@inheritDoc} */ public function getTimeTypeDeclarationSQL(array $column) { return 'TIME'; } /** * {@inheritDoc} */ public function getBooleanTypeDeclarationSQL(array $column) { return 'TINYINT(1)'; } /** * {@inheritDoc} * * @deprecated * * MySQL prefers "autoincrement" identity columns since sequences can only * be emulated with a table. */ public function prefersIdentityColumns() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/1519', 'AbstractMySQLPlatform::prefersIdentityColumns() is deprecated.', ); return true; } /** * {@inheritDoc} * * MySQL supports this through AUTO_INCREMENT columns. */ public function supportsIdentityColumns() { return true; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsInlineColumnComments() { return true; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsColumnCollation() { return true; } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * {@inheritDoc} */ public function getListTablesSQL() { return "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"; } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * {@inheritDoc} */ public function getListTableColumnsSQL($table, $database = null) { return 'SELECT COLUMN_NAME AS Field, COLUMN_TYPE AS Type, IS_NULLABLE AS `Null`, ' . 'COLUMN_KEY AS `Key`, COLUMN_DEFAULT AS `Default`, EXTRA AS Extra, COLUMN_COMMENT AS Comment, ' . 'CHARACTER_SET_NAME AS CharacterSet, COLLATION_NAME AS Collation ' . 'FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = ' . $this->getDatabaseNameSQL($database) . ' AND TABLE_NAME = ' . $this->quoteStringLiteral($table) . ' ORDER BY ORDINAL_POSITION ASC'; } /** * @deprecated Use {@see getColumnTypeSQLSnippet()} instead. * * The SQL snippets required to elucidate a column type * * Returns an array of the form [column type SELECT snippet, additional JOIN statement snippet] * * @return array{string, string} */ public function getColumnTypeSQLSnippets(string $tableAlias = 'c'): array { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6202', 'AbstractMySQLPlatform::getColumnTypeSQLSnippets() is deprecated. ' . 'Use AbstractMySQLPlatform::getColumnTypeSQLSnippet() instead.', ); return [$this->getColumnTypeSQLSnippet(...func_get_args()), '']; } /** * The SQL snippet required to elucidate a column type * * Returns a column type SELECT snippet string */ public function getColumnTypeSQLSnippet(string $tableAlias = 'c', ?string $databaseName = null): string { return $tableAlias . '.COLUMN_TYPE'; } /** @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. */ public function getListTableMetadataSQL(string $table, ?string $database = null): string { return sprintf( <<<'SQL' SELECT t.ENGINE, t.AUTO_INCREMENT, t.TABLE_COMMENT, t.CREATE_OPTIONS, t.TABLE_COLLATION, ccsa.CHARACTER_SET_NAME FROM information_schema.TABLES t INNER JOIN information_schema.`COLLATION_CHARACTER_SET_APPLICABILITY` ccsa ON ccsa.COLLATION_NAME = t.TABLE_COLLATION WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA = %s AND TABLE_NAME = %s SQL , $this->getDatabaseNameSQL($database), $this->quoteStringLiteral($table), ); } /** * {@inheritDoc} */ public function getCreateTablesSQL(array $tables): array { $sql = []; foreach ($tables as $table) { $sql = array_merge($sql, $this->getCreateTableWithoutForeignKeysSQL($table)); } foreach ($tables as $table) { if (! $table->hasOption('engine') || $this->engineSupportsForeignKeys($table->getOption('engine'))) { foreach ($table->getForeignKeys() as $foreignKey) { $sql[] = $this->getCreateForeignKeySQL( $foreignKey, $table->getQuotedName($this), ); } } elseif (count($table->getForeignKeys()) > 0) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5414', 'Relying on the DBAL not generating DDL for foreign keys on MySQL engines' . ' other than InnoDB is deprecated.' . ' Define foreign key constraints only if they are necessary.', ); } } return $sql; } /** * {@inheritDoc} */ protected function _getCreateTableSQL($name, array $columns, array $options = []) { $queryFields = $this->getColumnDeclarationListSQL($columns); if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { foreach ($options['uniqueConstraints'] as $constraintName => $definition) { $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($constraintName, $definition); } } // add all indexes if (isset($options['indexes']) && ! empty($options['indexes'])) { foreach ($options['indexes'] as $indexName => $definition) { $queryFields .= ', ' . $this->getIndexDeclarationSQL($indexName, $definition); } } // attach all primary keys if (isset($options['primary']) && ! empty($options['primary'])) { $keyColumns = array_unique(array_values($options['primary'])); $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; } $query = 'CREATE '; if (! empty($options['temporary'])) { $query .= 'TEMPORARY '; } $query .= 'TABLE ' . $name . ' (' . $queryFields . ') '; $query .= $this->buildTableOptions($options); $query .= $this->buildPartitionOptions($options); $sql = [$query]; // Propagate foreign key constraints only for InnoDB. if (isset($options['foreignKeys'])) { if (! isset($options['engine']) || $this->engineSupportsForeignKeys($options['engine'])) { foreach ($options['foreignKeys'] as $definition) { $sql[] = $this->getCreateForeignKeySQL($definition, $name); } } elseif (count($options['foreignKeys']) > 0) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5414', 'Relying on the DBAL not generating DDL for foreign keys on MySQL engines' . ' other than InnoDB is deprecated.' . ' Define foreign key constraints only if they are necessary.', ); } } return $sql; } public function createSelectSQLBuilder(): SelectSQLBuilder { return new DefaultSelectSQLBuilder($this, 'FOR UPDATE', null); } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getDefaultValueDeclarationSQL($column) { // Unset the default value if the given column definition does not allow default values. if ($column['type'] instanceof TextType || $column['type'] instanceof BlobType) { $column['default'] = null; } return parent::getDefaultValueDeclarationSQL($column); } /** * Build SQL for table options * * @param mixed[] $options */ private function buildTableOptions(array $options): string { if (isset($options['table_options'])) { return $options['table_options']; } $tableOptions = []; // Charset if (! isset($options['charset'])) { $options['charset'] = 'utf8'; } $tableOptions[] = sprintf('DEFAULT CHARACTER SET %s', $options['charset']); if (isset($options['collate'])) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5214', 'The "collate" option is deprecated in favor of "collation" and will be removed in 4.0.', ); $options['collation'] = $options['collate']; } // Collation if (! isset($options['collation'])) { $options['collation'] = $options['charset'] . '_unicode_ci'; } $tableOptions[] = $this->getColumnCollationDeclarationSQL($options['collation']); // Engine if (! isset($options['engine'])) { $options['engine'] = 'InnoDB'; } $tableOptions[] = sprintf('ENGINE = %s', $options['engine']); // Auto increment if (isset($options['auto_increment'])) { $tableOptions[] = sprintf('AUTO_INCREMENT = %s', $options['auto_increment']); } // Comment if (isset($options['comment'])) { $tableOptions[] = sprintf('COMMENT = %s ', $this->quoteStringLiteral($options['comment'])); } // Row format if (isset($options['row_format'])) { $tableOptions[] = sprintf('ROW_FORMAT = %s', $options['row_format']); } return implode(' ', $tableOptions); } /** * Build SQL for partition options. * * @param mixed[] $options */ private function buildPartitionOptions(array $options): string { return isset($options['partition_options']) ? ' ' . $options['partition_options'] : ''; } private function engineSupportsForeignKeys(string $engine): bool { return strcasecmp(trim($engine), 'InnoDB') === 0; } /** * {@inheritDoc} */ public function getAlterTableSQL(TableDiff $diff) { $columnSql = []; $queryParts = []; $newName = $diff->getNewName(); if ($newName !== false) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5663', 'Generation of SQL that renames a table using %s is deprecated. Use getRenameTableSQL() instead.', __METHOD__, ); $queryParts[] = 'RENAME TO ' . $newName->getQuotedName($this); } foreach ($diff->getAddedColumns() as $column) { if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { continue; } $columnProperties = array_merge($column->toArray(), [ 'comment' => $this->getColumnComment($column), ]); $queryParts[] = 'ADD ' . $this->getColumnDeclarationSQL( $column->getQuotedName($this), $columnProperties, ); } foreach ($diff->getDroppedColumns() as $column) { if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { continue; } $queryParts[] = 'DROP ' . $column->getQuotedName($this); } foreach ($diff->getModifiedColumns() as $columnDiff) { if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { continue; } $newColumn = $columnDiff->getNewColumn(); $newColumnProperties = array_merge($newColumn->toArray(), [ 'comment' => $this->getColumnComment($newColumn), ]); $oldColumn = $columnDiff->getOldColumn() ?? $columnDiff->getOldColumnName(); $queryParts[] = 'CHANGE ' . $oldColumn->getQuotedName($this) . ' ' . $this->getColumnDeclarationSQL($newColumn->getQuotedName($this), $newColumnProperties); } foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { continue; } $oldColumnName = new Identifier($oldColumnName); $columnProperties = array_merge($column->toArray(), [ 'comment' => $this->getColumnComment($column), ]); $queryParts[] = 'CHANGE ' . $oldColumnName->getQuotedName($this) . ' ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnProperties); } $addedIndexes = $this->indexAssetsByLowerCaseName($diff->getAddedIndexes()); $modifiedIndexes = $this->indexAssetsByLowerCaseName($diff->getModifiedIndexes()); $diffModified = false; if (isset($addedIndexes['primary'])) { $keyColumns = array_unique(array_values($addedIndexes['primary']->getColumns())); $queryParts[] = 'ADD PRIMARY KEY (' . implode(', ', $keyColumns) . ')'; unset($addedIndexes['primary']); $diffModified = true; } elseif (isset($modifiedIndexes['primary'])) { $addedColumns = $this->indexAssetsByLowerCaseName($diff->getAddedColumns()); // Necessary in case the new primary key includes a new auto_increment column foreach ($modifiedIndexes['primary']->getColumns() as $columnName) { if (isset($addedColumns[$columnName]) && $addedColumns[$columnName]->getAutoincrement()) { $keyColumns = array_unique(array_values($modifiedIndexes['primary']->getColumns())); $queryParts[] = 'DROP PRIMARY KEY'; $queryParts[] = 'ADD PRIMARY KEY (' . implode(', ', $keyColumns) . ')'; unset($modifiedIndexes['primary']); $diffModified = true; break; } } } if ($diffModified) { $diff = new TableDiff( $diff->name, $diff->getAddedColumns(), $diff->getModifiedColumns(), $diff->getDroppedColumns(), array_values($addedIndexes), array_values($modifiedIndexes), $diff->getDroppedIndexes(), $diff->getOldTable(), $diff->getAddedForeignKeys(), $diff->getModifiedForeignKeys(), $diff->getDroppedForeignKeys(), $diff->getRenamedColumns(), $diff->getRenamedIndexes(), ); } $sql = []; $tableSql = []; if (! $this->onSchemaAlterTable($diff, $tableSql)) { if (count($queryParts) > 0) { $sql[] = 'ALTER TABLE ' . ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this) . ' ' . implode(', ', $queryParts); } $sql = array_merge( $this->getPreAlterTableIndexForeignKeySQL($diff), $sql, $this->getPostAlterTableIndexForeignKeySQL($diff), ); } return array_merge($sql, $tableSql, $columnSql); } /** * {@inheritDoc} */ protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) { $sql = []; $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); foreach ($diff->getModifiedIndexes() as $changedIndex) { $sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $changedIndex)); } foreach ($diff->getDroppedIndexes() as $droppedIndex) { $sql = array_merge($sql, $this->getPreAlterTableAlterPrimaryKeySQL($diff, $droppedIndex)); foreach ($diff->getAddedIndexes() as $addedIndex) { if ($droppedIndex->getColumns() !== $addedIndex->getColumns()) { continue; } $indexClause = 'INDEX ' . $addedIndex->getName(); if ($addedIndex->isPrimary()) { $indexClause = 'PRIMARY KEY'; } elseif ($addedIndex->isUnique()) { $indexClause = 'UNIQUE INDEX ' . $addedIndex->getName(); } $query = 'ALTER TABLE ' . $tableNameSQL . ' DROP INDEX ' . $droppedIndex->getName() . ', '; $query .= 'ADD ' . $indexClause; $query .= ' (' . $this->getIndexFieldDeclarationListSQL($addedIndex) . ')'; $sql[] = $query; $diff->unsetAddedIndex($addedIndex); $diff->unsetDroppedIndex($droppedIndex); break; } } $engine = 'INNODB'; $table = $diff->getOldTable(); if ($table !== null && $table->hasOption('engine')) { $engine = strtoupper(trim($table->getOption('engine'))); } // Suppress foreign key constraint propagation on non-supporting engines. if ($engine !== 'INNODB') { $diff->addedForeignKeys = []; $diff->changedForeignKeys = []; $diff->removedForeignKeys = []; } $sql = array_merge( $sql, $this->getPreAlterTableAlterIndexForeignKeySQL($diff), parent::getPreAlterTableIndexForeignKeySQL($diff), $this->getPreAlterTableRenameIndexForeignKeySQL($diff), ); return $sql; } /** * @return string[] * * @throws Exception */ private function getPreAlterTableAlterPrimaryKeySQL(TableDiff $diff, Index $index): array { if (! $index->isPrimary()) { return []; } $table = $diff->getOldTable(); if ($table === null) { return []; } $sql = []; $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); // Dropping primary keys requires to unset autoincrement attribute on the particular column first. foreach ($index->getColumns() as $columnName) { if (! $table->hasColumn($columnName)) { continue; } $column = $table->getColumn($columnName); if ($column->getAutoincrement() !== true) { continue; } $column->setAutoincrement(false); $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' MODIFY ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); // original autoincrement information might be needed later on by other parts of the table alteration $column->setAutoincrement(true); } return $sql; } /** * @param TableDiff $diff The table diff to gather the SQL for. * * @return string[] * * @throws Exception */ private function getPreAlterTableAlterIndexForeignKeySQL(TableDiff $diff): array { $table = $diff->getOldTable(); if ($table === null) { return []; } $primaryKey = $table->getPrimaryKey(); if ($primaryKey === null) { return []; } $primaryKeyColumns = []; foreach ($primaryKey->getColumns() as $columnName) { if (! $table->hasColumn($columnName)) { continue; } $primaryKeyColumns[] = $table->getColumn($columnName); } if (count($primaryKeyColumns) === 0) { return []; } $sql = []; $tableNameSQL = $table->getQuotedName($this); foreach ($diff->getModifiedIndexes() as $changedIndex) { // Changed primary key if (! $changedIndex->isPrimary()) { continue; } foreach ($primaryKeyColumns as $column) { // Check if an autoincrement column was dropped from the primary key. if (! $column->getAutoincrement() || in_array($column->getName(), $changedIndex->getColumns(), true)) { continue; } // The autoincrement attribute needs to be removed from the dropped column // before we can drop and recreate the primary key. $column->setAutoincrement(false); $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' MODIFY ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); // Restore the autoincrement attribute as it might be needed later on // by other parts of the table alteration. $column->setAutoincrement(true); } } return $sql; } /** * @param TableDiff $diff The table diff to gather the SQL for. * * @return string[] */ protected function getPreAlterTableRenameIndexForeignKeySQL(TableDiff $diff) { $sql = []; $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) { if (in_array($foreignKey, $diff->getModifiedForeignKeys(), true)) { continue; } $sql[] = $this->getDropForeignKeySQL($foreignKey->getQuotedName($this), $tableNameSQL); } return $sql; } /** * Returns the remaining foreign key constraints that require one of the renamed indexes. * * "Remaining" here refers to the diff between the foreign keys currently defined in the associated * table and the foreign keys to be removed. * * @param TableDiff $diff The table diff to evaluate. * * @return ForeignKeyConstraint[] */ private function getRemainingForeignKeyConstraintsRequiringRenamedIndexes(TableDiff $diff): array { if (count($diff->getRenamedIndexes()) === 0) { return []; } $table = $diff->getOldTable(); if ($table === null) { return []; } $foreignKeys = []; /** @var ForeignKeyConstraint[] $remainingForeignKeys */ $remainingForeignKeys = array_diff_key( $table->getForeignKeys(), $diff->getDroppedForeignKeys(), ); foreach ($remainingForeignKeys as $foreignKey) { foreach ($diff->getRenamedIndexes() as $index) { if ($foreignKey->intersectsIndexColumns($index)) { $foreignKeys[] = $foreignKey; break; } } } return $foreignKeys; } /** * {@inheritDoc} */ protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) { return array_merge( parent::getPostAlterTableIndexForeignKeySQL($diff), $this->getPostAlterTableRenameIndexForeignKeySQL($diff), ); } /** * @param TableDiff $diff The table diff to gather the SQL for. * * @return string[] */ protected function getPostAlterTableRenameIndexForeignKeySQL(TableDiff $diff) { $sql = []; $newName = $diff->getNewName(); if ($newName !== false) { $tableNameSQL = $newName->getQuotedName($this); } else { $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); } foreach ($this->getRemainingForeignKeyConstraintsRequiringRenamedIndexes($diff) as $foreignKey) { if (in_array($foreignKey, $diff->getModifiedForeignKeys(), true)) { continue; } $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableNameSQL); } return $sql; } /** * {@inheritDoc} */ protected function getCreateIndexSQLFlags(Index $index) { $type = ''; if ($index->isUnique()) { $type .= 'UNIQUE '; } elseif ($index->hasFlag('fulltext')) { $type .= 'FULLTEXT '; } elseif ($index->hasFlag('spatial')) { $type .= 'SPATIAL '; } return $type; } /** * {@inheritDoc} */ public function getIntegerTypeDeclarationSQL(array $column) { return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function getBigIntTypeDeclarationSQL(array $column) { return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function getSmallIntTypeDeclarationSQL(array $column) { return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function getFloatDeclarationSQL(array $column) { return 'DOUBLE PRECISION' . $this->getUnsignedDeclaration($column); } /** * {@inheritDoc} */ public function getDecimalTypeDeclarationSQL(array $column) { return parent::getDecimalTypeDeclarationSQL($column) . $this->getUnsignedDeclaration($column); } /** * Get unsigned declaration for a column. * * @param mixed[] $columnDef */ private function getUnsignedDeclaration(array $columnDef): string { return ! empty($columnDef['unsigned']) ? ' UNSIGNED' : ''; } /** * {@inheritDoc} */ protected function _getCommonIntegerTypeDeclarationSQL(array $column) { $autoinc = ''; if (! empty($column['autoincrement'])) { $autoinc = ' AUTO_INCREMENT'; } return $this->getUnsignedDeclaration($column) . $autoinc; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getColumnCharsetDeclarationSQL($charset) { return 'CHARACTER SET ' . $charset; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) { $query = ''; if ($foreignKey->hasOption('match')) { $query .= ' MATCH ' . $foreignKey->getOption('match'); } $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); return $query; } /** * {@inheritDoc} */ public function getDropIndexSQL($index, $table = null) { if ($index instanceof Index) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $index as an Index object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); $indexName = $index->getQuotedName($this); } elseif (is_string($index)) { $indexName = $index; } else { throw new InvalidArgumentException( __METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.', ); } if ($table instanceof Table) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); $table = $table->getQuotedName($this); } elseif (! is_string($table)) { throw new InvalidArgumentException( __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.', ); } if ($index instanceof Index && $index->isPrimary()) { // MySQL primary keys are always named "PRIMARY", // so we cannot use them in statements because of them being keyword. return $this->getDropPrimaryKeySQL($table); } return 'DROP INDEX ' . $indexName . ' ON ' . $table; } /** * @param string $table * * @return string */ protected function getDropPrimaryKeySQL($table) { return 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY'; } /** * The `ALTER TABLE ... DROP CONSTRAINT` syntax is only available as of MySQL 8.0.19. * * @link https://dev.mysql.com/doc/refman/8.0/en/alter-table.html */ public function getDropUniqueConstraintSQL(string $name, string $tableName): string { return $this->getDropIndexSQL($name, $tableName); } /** * {@inheritDoc} */ public function getSetTransactionIsolationSQL($level) { return 'SET SESSION TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); } /** * {@inheritDoc} */ public function getName() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4749', 'AbstractMySQLPlatform::getName() is deprecated. Identify platforms by their class.', ); return 'mysql'; } /** * {@inheritDoc} */ public function getReadLockSQL() { return 'LOCK IN SHARE MODE'; } /** * {@inheritDoc} */ protected function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ 'bigint' => Types::BIGINT, 'binary' => Types::BINARY, 'blob' => Types::BLOB, 'char' => Types::STRING, 'date' => Types::DATE_MUTABLE, 'datetime' => Types::DATETIME_MUTABLE, 'decimal' => Types::DECIMAL, 'double' => Types::FLOAT, 'float' => Types::FLOAT, 'int' => Types::INTEGER, 'integer' => Types::INTEGER, 'longblob' => Types::BLOB, 'longtext' => Types::TEXT, 'mediumblob' => Types::BLOB, 'mediumint' => Types::INTEGER, 'mediumtext' => Types::TEXT, 'numeric' => Types::DECIMAL, 'real' => Types::FLOAT, 'set' => Types::SIMPLE_ARRAY, 'smallint' => Types::SMALLINT, 'string' => Types::STRING, 'text' => Types::TEXT, 'time' => Types::TIME_MUTABLE, 'timestamp' => Types::DATETIME_MUTABLE, 'tinyblob' => Types::BLOB, 'tinyint' => Types::BOOLEAN, 'tinytext' => Types::TEXT, 'varbinary' => Types::BINARY, 'varchar' => Types::STRING, 'year' => Types::DATE_MUTABLE, ]; } /** * {@inheritDoc} * * @deprecated */ public function getVarcharMaxLength() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'AbstractMySQLPlatform::getVarcharMaxLength() is deprecated.', ); return 65535; } /** * {@inheritDoc} * * @deprecated */ public function getBinaryMaxLength() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'AbstractMySQLPlatform::getBinaryMaxLength() is deprecated.', ); return 65535; } /** * {@inheritDoc} * * @deprecated Implement {@see createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4510', 'AbstractMySQLPlatform::getReservedKeywordsClass() is deprecated,' . ' use AbstractMySQLPlatform::createReservedKeywordsList() instead.', ); return Keywords\MySQLKeywords::class; } /** * {@inheritDoc} * * MySQL commits a transaction implicitly when DROP TABLE is executed, however not * if DROP TEMPORARY TABLE is executed. */ public function getDropTemporaryTableSQL($table) { if ($table instanceof Table) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); $table = $table->getQuotedName($this); } elseif (! is_string($table)) { throw new InvalidArgumentException( __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.', ); } return 'DROP TEMPORARY TABLE ' . $table; } /** * Gets the SQL Snippet used to declare a BLOB column type. * TINYBLOB : 2 ^ 8 - 1 = 255 * BLOB : 2 ^ 16 - 1 = 65535 * MEDIUMBLOB : 2 ^ 24 - 1 = 16777215 * LONGBLOB : 2 ^ 32 - 1 = 4294967295 * * {@inheritDoc} */ public function getBlobTypeDeclarationSQL(array $column) { if (! empty($column['length']) && is_numeric($column['length'])) { $length = $column['length']; if ($length <= static::LENGTH_LIMIT_TINYBLOB) { return 'TINYBLOB'; } if ($length <= static::LENGTH_LIMIT_BLOB) { return 'BLOB'; } if ($length <= static::LENGTH_LIMIT_MEDIUMBLOB) { return 'MEDIUMBLOB'; } } return 'LONGBLOB'; } /** * {@inheritDoc} */ public function quoteStringLiteral($str) { $str = str_replace('\\', '\\\\', $str); // MySQL requires backslashes to be escaped return parent::quoteStringLiteral($str); } /** * {@inheritDoc} */ public function getDefaultTransactionIsolationLevel() { return TransactionIsolationLevel::REPEATABLE_READ; } public function supportsColumnLengthIndexes(): bool { return true; } /** @deprecated Will be removed without replacement. */ protected function getDatabaseNameSQL(?string $databaseName): string { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6215', '%s is deprecated without replacement.', __METHOD__, ); if ($databaseName !== null) { return $this->quoteStringLiteral($databaseName); } return $this->getCurrentDatabaseExpression(); } public function createSchemaManager(Connection $connection): MySQLSchemaManager { return new MySQLSchemaManager($connection, $this); } /** * @param list $assets * * @return array * * @template T of AbstractAsset */ private function indexAssetsByLowerCaseName(array $assets): array { $result = []; foreach ($assets as $asset) { $result[strtolower($asset->getName())] = $asset; } return $result; } } PK!ulBzz"dbal/src/Platforms/DB2Platform.phpnu[getCharMaxLength(); } return parent::getVarcharTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function getBlobTypeDeclarationSQL(array $column) { // todo blob(n) with $column['length']; return 'BLOB(1M)'; } /** * {@inheritDoc} */ protected function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ 'bigint' => Types::BIGINT, 'binary' => Types::BINARY, 'blob' => Types::BLOB, 'character' => Types::STRING, 'clob' => Types::TEXT, 'date' => Types::DATE_MUTABLE, 'decimal' => Types::DECIMAL, 'double' => Types::FLOAT, 'integer' => Types::INTEGER, 'real' => Types::FLOAT, 'smallint' => Types::SMALLINT, 'time' => Types::TIME_MUTABLE, 'timestamp' => Types::DATETIME_MUTABLE, 'varbinary' => Types::BINARY, 'varchar' => Types::STRING, ]; } /** * {@inheritDoc} */ public function isCommentedDoctrineType(Type $doctrineType) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5058', '%s() is deprecated and will be removed in Doctrine DBAL 4.0. Use Type::requiresSQLCommentHint() instead.', __METHOD__, ); if ($doctrineType->getName() === Types::BOOLEAN) { // We require a commented boolean type in order to distinguish between boolean and smallint // as both (have to) map to the same native type. return true; } return parent::isCommentedDoctrineType($doctrineType); } /** * {@inheritDoc} */ protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) { if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'Relying on the default string column length on IBM DB2 is deprecated' . ', specify the length explicitly.', ); } return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(254)') : ($length > 0 ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); } /** * {@inheritDoc} */ protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) { if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'Relying on the default binary column length on IBM DB2 is deprecated' . ', specify the length explicitly.', ); } return $this->getVarcharTypeDeclarationSQLSnippet($length, $fixed) . ' FOR BIT DATA'; } /** * {@inheritDoc} */ public function getClobTypeDeclarationSQL(array $column) { // todo clob(n) with $column['length']; return 'CLOB(1M)'; } /** * {@inheritDoc} */ public function getName() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4749', '%s() is deprecated. Identify platforms by their class.', __METHOD__, ); return 'db2'; } /** * {@inheritDoc} */ public function getBooleanTypeDeclarationSQL(array $column) { return 'SMALLINT'; } /** * {@inheritDoc} */ public function getIntegerTypeDeclarationSQL(array $column) { return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function getBigIntTypeDeclarationSQL(array $column) { return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function getSmallIntTypeDeclarationSQL(array $column) { return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } /** * {@inheritDoc} */ protected function _getCommonIntegerTypeDeclarationSQL(array $column) { $autoinc = ''; if (! empty($column['autoincrement'])) { $autoinc = ' GENERATED BY DEFAULT AS IDENTITY'; } return $autoinc; } /** * {@inheritDoc} */ public function getBitAndComparisonExpression($value1, $value2) { return 'BITAND(' . $value1 . ', ' . $value2 . ')'; } /** * {@inheritDoc} */ public function getBitOrComparisonExpression($value1, $value2) { return 'BITOR(' . $value1 . ', ' . $value2 . ')'; } /** * {@inheritDoc} */ protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) { switch ($unit) { case DateIntervalUnit::WEEK: $interval = $this->multiplyInterval((string) $interval, 7); $unit = DateIntervalUnit::DAY; break; case DateIntervalUnit::QUARTER: $interval = $this->multiplyInterval((string) $interval, 3); $unit = DateIntervalUnit::MONTH; break; } return $date . ' ' . $operator . ' ' . $interval . ' ' . $unit; } /** * {@inheritDoc} */ public function getDateDiffExpression($date1, $date2) { return 'DAYS(' . $date1 . ') - DAYS(' . $date2 . ')'; } /** * {@inheritDoc} */ public function getDateTimeTypeDeclarationSQL(array $column) { if (isset($column['version']) && $column['version'] === true) { return 'TIMESTAMP(0) WITH DEFAULT'; } return 'TIMESTAMP(0)'; } /** * {@inheritDoc} */ public function getDateTypeDeclarationSQL(array $column) { return 'DATE'; } /** * {@inheritDoc} */ public function getTimeTypeDeclarationSQL(array $column) { return 'TIME'; } /** * {@inheritDoc} */ public function getTruncateTableSQL($tableName, $cascade = false) { $tableIdentifier = new Identifier($tableName); return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this) . ' IMMEDIATE'; } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * This code fragment is originally from the Zend_Db_Adapter_Db2 class, but has been edited. * * @param string $table * @param string $database * * @return string */ public function getListTableColumnsSQL($table, $database = null) { $table = $this->quoteStringLiteral($table); // We do the funky subquery and join syscat.columns.default this crazy way because // as of db2 v10, the column is CLOB(64k) and the distinct operator won't allow a CLOB, // it wants shorter stuff like a varchar. return " SELECT cols.default, subq.* FROM ( SELECT DISTINCT c.tabschema, c.tabname, c.colname, c.colno, c.typename, c.codepage, c.nulls, c.length, c.scale, c.identity, tc.type AS tabconsttype, c.remarks AS comment, k.colseq, CASE WHEN c.generated = '" . self::SYSCAT_COLUMNS_GENERATED_DEFAULT . "' THEN 1 ELSE 0 END AS autoincrement FROM syscat.columns c LEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tc ON (k.tabschema = tc.tabschema AND k.tabname = tc.tabname AND tc.type = '" . self::SYSCAT_TABCONST_TYPE_PRIMARY_KEY . "')) ON (c.tabschema = k.tabschema AND c.tabname = k.tabname AND c.colname = k.colname) WHERE UPPER(c.tabname) = UPPER(" . $table . ') ORDER BY c.colno ) subq JOIN syscat.columns cols ON subq.tabschema = cols.tabschema AND subq.tabname = cols.tabname AND subq.colno = cols.colno ORDER BY subq.colno '; } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * {@inheritDoc} */ public function getListTablesSQL() { return "SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = '" . self::SYSIBM_SYSTABLES_TYPE_TABLE . "'" . ' AND CREATOR = CURRENT_USER'; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListViewsSQL($database) { return 'SELECT NAME, TEXT FROM SYSIBM.SYSVIEWS'; } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * {@inheritDoc} */ public function getListTableIndexesSQL($table, $database = null) { $table = $this->quoteStringLiteral($table); return "SELECT idx.INDNAME AS key_name, idxcol.COLNAME AS column_name, CASE WHEN idx.UNIQUERULE = '" . self::SYSCAT_INDEXES_UNIQUERULE_IMPLEMENTS_PRIMARY_KEY . "' THEN 1 ELSE 0 END AS primary, CASE WHEN idx.UNIQUERULE = '" . self::SYSCAT_INDEXES_UNIQUERULE_PERMITS_DUPLICATES . "' THEN 1 ELSE 0 END AS non_unique FROM SYSCAT.INDEXES AS idx JOIN SYSCAT.INDEXCOLUSE AS idxcol ON idx.INDSCHEMA = idxcol.INDSCHEMA AND idx.INDNAME = idxcol.INDNAME WHERE idx.TABNAME = UPPER(" . $table . ') ORDER BY idxcol.COLSEQ ASC'; } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * {@inheritDoc} */ public function getListTableForeignKeysSQL($table) { $table = $this->quoteStringLiteral($table); return "SELECT fkcol.COLNAME AS local_column, fk.REFTABNAME AS foreign_table, pkcol.COLNAME AS foreign_column, fk.CONSTNAME AS index_name, CASE WHEN fk.UPDATERULE = '" . self::SYSCAT_REFERENCES_UPDATERULE_RESTRICT . "' THEN 'RESTRICT' ELSE NULL END AS on_update, CASE WHEN fk.DELETERULE = '" . self::SYSCAT_REFERENCES_DELETERULE_CASCADE . "' THEN 'CASCADE' WHEN fk.DELETERULE = '" . self::SYSCAT_REFERENCES_DELETERULE_SET_NULL . "' THEN 'SET NULL' WHEN fk.DELETERULE = '" . self::SYSCAT_REFERENCES_DELETERULE_RESTRICT . "' THEN 'RESTRICT' ELSE NULL END AS on_delete FROM SYSCAT.REFERENCES AS fk JOIN SYSCAT.KEYCOLUSE AS fkcol ON fk.CONSTNAME = fkcol.CONSTNAME AND fk.TABSCHEMA = fkcol.TABSCHEMA AND fk.TABNAME = fkcol.TABNAME JOIN SYSCAT.KEYCOLUSE AS pkcol ON fk.REFKEYNAME = pkcol.CONSTNAME AND fk.REFTABSCHEMA = pkcol.TABSCHEMA AND fk.REFTABNAME = pkcol.TABNAME WHERE fk.TABNAME = UPPER(" . $table . ') ORDER BY fkcol.COLSEQ ASC'; } /** * {@inheritDoc} * * @deprecated */ public function supportsCreateDropDatabase() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5513', '%s() is deprecated.', __METHOD__, ); return false; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsCommentOnStatement() { return true; } /** * {@inheritDoc} */ public function getCurrentDateSQL() { return 'CURRENT DATE'; } /** * {@inheritDoc} */ public function getCurrentTimeSQL() { return 'CURRENT TIME'; } /** * {@inheritDoc} */ public function getCurrentTimestampSQL() { return 'CURRENT TIMESTAMP'; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getIndexDeclarationSQL($name, Index $index) { // Index declaration in statements like CREATE TABLE is not supported. throw Exception::notSupported(__METHOD__); } /** * {@inheritDoc} */ protected function _getCreateTableSQL($name, array $columns, array $options = []) { $indexes = []; if (isset($options['indexes'])) { $indexes = $options['indexes']; } $options['indexes'] = []; $sqls = parent::_getCreateTableSQL($name, $columns, $options); foreach ($indexes as $definition) { $sqls[] = $this->getCreateIndexSQL($definition, $name); } return $sqls; } /** * {@inheritDoc} */ public function getAlterTableSQL(TableDiff $diff) { $sql = []; $columnSql = []; $commentsSQL = []; $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); $queryParts = []; foreach ($diff->getAddedColumns() as $column) { if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { continue; } $columnDef = $column->toArray(); $queryPart = 'ADD COLUMN ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef); // Adding non-nullable columns to a table requires a default value to be specified. if ( ! empty($columnDef['notnull']) && ! isset($columnDef['default']) && empty($columnDef['autoincrement']) ) { $queryPart .= ' WITH DEFAULT'; } $queryParts[] = $queryPart; $comment = $this->getColumnComment($column); if ($comment === null || $comment === '') { continue; } $commentsSQL[] = $this->getCommentOnColumnSQL( $tableNameSQL, $column->getQuotedName($this), $comment, ); } foreach ($diff->getDroppedColumns() as $column) { if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { continue; } $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this); } foreach ($diff->getModifiedColumns() as $columnDiff) { if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { continue; } if ($columnDiff->hasCommentChanged()) { $commentsSQL[] = $this->getCommentOnColumnSQL( $tableNameSQL, $columnDiff->getNewColumn()->getQuotedName($this), $this->getColumnComment($columnDiff->getNewColumn()), ); } $this->gatherAlterColumnSQL( $tableNameSQL, $columnDiff, $sql, $queryParts, ); } foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { continue; } $oldColumnName = new Identifier($oldColumnName); $queryParts[] = 'RENAME COLUMN ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this); } $tableSql = []; if (! $this->onSchemaAlterTable($diff, $tableSql)) { if (count($queryParts) > 0) { $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . implode(' ', $queryParts); } // Some table alteration operations require a table reorganization. if (count($diff->getDroppedColumns()) > 0 || count($diff->getModifiedColumns()) > 0) { $sql[] = "CALL SYSPROC.ADMIN_CMD ('REORG TABLE " . $tableNameSQL . "')"; } $sql = array_merge($sql, $commentsSQL); $newName = $diff->getNewName(); if ($newName !== false) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5663', 'Generation of "rename table" SQL using %s() is deprecated. Use getRenameTableSQL() instead.', __METHOD__, ); $sql[] = sprintf( 'RENAME TABLE %s TO %s', $tableNameSQL, $newName->getQuotedName($this), ); } $sql = array_merge( $this->getPreAlterTableIndexForeignKeySQL($diff), $sql, $this->getPostAlterTableIndexForeignKeySQL($diff), ); } return array_merge($sql, $tableSql, $columnSql); } /** * {@inheritDoc} */ public function getRenameTableSQL(string $oldName, string $newName): array { return [ sprintf('RENAME TABLE %s TO %s', $oldName, $newName), ]; } /** * Gathers the table alteration SQL for a given column diff. * * @param string $table The table to gather the SQL for. * @param ColumnDiff $columnDiff The column diff to evaluate. * @param string[] $sql The sequence of table alteration statements to fill. * @param mixed[] $queryParts The sequence of column alteration clauses to fill. */ private function gatherAlterColumnSQL( string $table, ColumnDiff $columnDiff, array &$sql, array &$queryParts ): void { $alterColumnClauses = $this->getAlterColumnClausesSQL($columnDiff); if (empty($alterColumnClauses)) { return; } // If we have a single column alteration, we can append the clause to the main query. if (count($alterColumnClauses) === 1) { $queryParts[] = current($alterColumnClauses); return; } // We have multiple alterations for the same column, // so we need to trigger a complete ALTER TABLE statement // for each ALTER COLUMN clause. foreach ($alterColumnClauses as $alterColumnClause) { $sql[] = 'ALTER TABLE ' . $table . ' ' . $alterColumnClause; } } /** * Returns the ALTER COLUMN SQL clauses for altering a column described by the given column diff. * * @return string[] */ private function getAlterColumnClausesSQL(ColumnDiff $columnDiff): array { $newColumn = $columnDiff->getNewColumn()->toArray(); $alterClause = 'ALTER COLUMN ' . $columnDiff->getNewColumn()->getQuotedName($this); if ($newColumn['columnDefinition'] !== null) { return [$alterClause . ' ' . $newColumn['columnDefinition']]; } $clauses = []; if ( $columnDiff->hasTypeChanged() || $columnDiff->hasLengthChanged() || $columnDiff->hasPrecisionChanged() || $columnDiff->hasScaleChanged() || $columnDiff->hasFixedChanged() ) { $clauses[] = $alterClause . ' SET DATA TYPE ' . $newColumn['type']->getSQLDeclaration($newColumn, $this); } if ($columnDiff->hasNotNullChanged()) { $clauses[] = $newColumn['notnull'] ? $alterClause . ' SET NOT NULL' : $alterClause . ' DROP NOT NULL'; } if ($columnDiff->hasDefaultChanged()) { if (isset($newColumn['default'])) { $defaultClause = $this->getDefaultValueDeclarationSQL($newColumn); if ($defaultClause !== '') { $clauses[] = $alterClause . ' SET' . $defaultClause; } } else { $clauses[] = $alterClause . ' DROP DEFAULT'; } } return $clauses; } /** * {@inheritDoc} */ protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) { $sql = []; $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); foreach ($diff->getDroppedIndexes() as $droppedIndex) { foreach ($diff->getAddedIndexes() as $addedIndex) { if ($droppedIndex->getColumns() !== $addedIndex->getColumns()) { continue; } if ($droppedIndex->isPrimary()) { $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' DROP PRIMARY KEY'; } elseif ($droppedIndex->isUnique()) { $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' DROP UNIQUE ' . $droppedIndex->getQuotedName($this); } else { $sql[] = $this->getDropIndexSQL($droppedIndex, $tableNameSQL); } $sql[] = $this->getCreateIndexSQL($addedIndex, $tableNameSQL); $diff->unsetAddedIndex($addedIndex); $diff->unsetDroppedIndex($droppedIndex); break; } } return array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff)); } /** * {@inheritDoc} */ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) { if (strpos($tableName, '.') !== false) { [$schema] = explode('.', $tableName); $oldIndexName = $schema . '.' . $oldIndexName; } return ['RENAME INDEX ' . $oldIndexName . ' TO ' . $index->getQuotedName($this)]; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getDefaultValueDeclarationSQL($column) { if (! empty($column['autoincrement'])) { return ''; } if (! empty($column['version'])) { if ((string) $column['type'] !== 'DateTime') { $column['default'] = '1'; } } return parent::getDefaultValueDeclarationSQL($column); } /** * {@inheritDoc} */ public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) { return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (DEFAULT)'; } /** * {@inheritDoc} */ public function getCreateTemporaryTableSnippetSQL() { return 'DECLARE GLOBAL TEMPORARY TABLE'; } /** * {@inheritDoc} */ public function getTemporaryTableName($tableName) { return 'SESSION.' . $tableName; } /** * {@inheritDoc} */ protected function doModifyLimitQuery($query, $limit, $offset) { $where = []; if ($offset > 0) { $where[] = sprintf('db22.DC_ROWNUM >= %d', $offset + 1); } if ($limit !== null) { $where[] = sprintf('db22.DC_ROWNUM <= %d', $offset + $limit); } if (empty($where)) { return $query; } // Todo OVER() needs ORDER BY data! return sprintf( 'SELECT db22.* FROM (SELECT db21.*, ROW_NUMBER() OVER() AS DC_ROWNUM FROM (%s) db21) db22 WHERE %s', $query, implode(' AND ', $where), ); } /** * {@inheritDoc} */ public function getLocateExpression($str, $substr, $startPos = false) { if ($startPos === false) { return 'LOCATE(' . $substr . ', ' . $str . ')'; } return 'LOCATE(' . $substr . ', ' . $str . ', ' . $startPos . ')'; } /** * {@inheritDoc} */ public function getSubstringExpression($string, $start, $length = null) { if ($length === null) { return 'SUBSTR(' . $string . ', ' . $start . ')'; } return 'SUBSTR(' . $string . ', ' . $start . ', ' . $length . ')'; } /** * {@inheritDoc} */ public function getLengthExpression($column) { return 'LENGTH(' . $column . ', CODEUNITS32)'; } public function getCurrentDatabaseExpression(): string { return 'CURRENT_USER'; } /** * {@inheritDoc} */ public function supportsIdentityColumns() { return true; } /** * {@inheritDoc} * * @deprecated */ public function prefersIdentityColumns() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/1519', '%s() is deprecated.', __METHOD__, ); return true; } public function createSelectSQLBuilder(): SelectSQLBuilder { return new DefaultSelectSQLBuilder($this, 'WITH RR USE AND KEEP UPDATE LOCKS', null); } /** * {@inheritDoc} * * @deprecated This API is not portable. */ public function getForUpdateSQL() { return ' WITH RR USE AND KEEP UPDATE LOCKS'; } /** * {@inheritDoc} */ public function getDummySelectSQL() { $expression = func_num_args() > 0 ? func_get_arg(0) : '1'; return sprintf('SELECT %s FROM sysibm.sysdummy1', $expression); } /** * {@inheritDoc} * * DB2 supports savepoints, but they work semantically different than on other vendor platforms. * * TODO: We have to investigate how to get DB2 up and running with savepoints. */ public function supportsSavepoints() { return false; } /** * {@inheritDoc} * * @deprecated Implement {@see createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4510', '%s() is deprecated,' . ' use %s::createReservedKeywordsList() instead.', __METHOD__, static::class, ); return Keywords\DB2Keywords::class; } /** @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. */ public function getListTableCommentsSQL(string $table): string { return sprintf( <<<'SQL' SELECT REMARKS FROM SYSIBM.SYSTABLES WHERE NAME = UPPER( %s ) SQL , $this->quoteStringLiteral($table), ); } public function createSchemaManager(Connection $connection): DB2SchemaManager { return new DB2SchemaManager($connection, $this); } } PK!Tߨss&dbal/src/Platforms/MariaDBPlatform.phpnu[doctrineTypeMapping['json'] = Types::JSON; } } PK! +*dbal/src/Platforms/MariaDb1043Platform.phpnu[getColumnTypeSQLSnippets('c', $database); return sprintf( <<getDatabaseNameSQL($database), $this->quoteStringLiteral($table), ); } /** * Generate SQL snippets to reverse the aliasing of JSON to LONGTEXT. * * MariaDb aliases columns specified as JSON to LONGTEXT and sets a CHECK constraint to ensure the column * is valid json. This function generates the SQL snippets which reverse this aliasing i.e. report a column * as JSON where it was originally specified as such instead of LONGTEXT. * * The CHECK constraints are stored in information_schema.CHECK_CONSTRAINTS so query that table. */ public function getColumnTypeSQLSnippet(string $tableAlias = 'c', ?string $databaseName = null): string { if ($this->getJsonTypeDeclarationSQL([]) !== 'JSON') { return parent::getColumnTypeSQLSnippet($tableAlias, $databaseName); } if ($databaseName === null) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6215', 'Not passing a database name to methods "getColumnTypeSQLSnippet()", ' . '"getColumnTypeSQLSnippets()", and "getListTableColumnsSQL()" of "%s" is deprecated.', self::class, ); } $subQueryAlias = 'i_' . $tableAlias; $databaseName = $this->getDatabaseNameSQL($databaseName); // The check for `CONSTRAINT_SCHEMA = $databaseName` is mandatory here to prevent performance issues return <<getJsonTypeDeclarationSQL([]) === 'JSON' && ($column['type'] ?? null) instanceof JsonType) { unset($column['collation']); unset($column['charset']); } return parent::getColumnDeclarationSQL($name, $column); } } PK!5*dbal/src/Platforms/MariaDb1052Platform.phpnu[getQuotedName($this)]; } } PK!Έ**dbal/src/Platforms/TrimMode.phpnu[normalizeColumns($fromTable), $this->normalizeColumns($toTable), ); } /** * {@inheritDoc} */ public function diffTable(Table $fromTable, Table $toTable) { return parent::diffTable( $this->normalizeColumns($fromTable), $this->normalizeColumns($toTable), ); } private function normalizeColumns(Table $table): Table { $table = clone $table; foreach ($table->getColumns() as $column) { $options = $column->getPlatformOptions(); if (! isset($options['collation']) || strcasecmp($options['collation'], 'binary') !== 0) { continue; } unset($options['collation']); $column->setPlatformOptions($options); } return $table; } } PK!sj##'dbal/src/Platforms/AbstractPlatform.phpnu[disableTypeComments = $value; } /** * Sets the EventManager used by the Platform. * * @deprecated * * @return void */ public function setEventManager(EventManager $eventManager) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5784', '%s is deprecated.', __METHOD__, ); $this->_eventManager = $eventManager; } /** * Gets the EventManager used by the Platform. * * @deprecated * * @return EventManager|null */ public function getEventManager() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5784', '%s is deprecated.', __METHOD__, ); return $this->_eventManager; } /** * Returns the SQL snippet that declares a boolean column. * * @param mixed[] $column * * @return string */ abstract public function getBooleanTypeDeclarationSQL(array $column); /** * Returns the SQL snippet that declares a 4 byte integer column. * * @param mixed[] $column * * @return string */ abstract public function getIntegerTypeDeclarationSQL(array $column); /** * Returns the SQL snippet that declares an 8 byte integer column. * * @param mixed[] $column * * @return string */ abstract public function getBigIntTypeDeclarationSQL(array $column); /** * Returns the SQL snippet that declares a 2 byte integer column. * * @param mixed[] $column * * @return string */ abstract public function getSmallIntTypeDeclarationSQL(array $column); /** * Returns the SQL snippet that declares common properties of an integer column. * * @param mixed[] $column * * @return string */ abstract protected function _getCommonIntegerTypeDeclarationSQL(array $column); /** * Lazy load Doctrine Type Mappings. * * @return void */ abstract protected function initializeDoctrineTypeMappings(); /** * Initializes Doctrine Type Mappings with the platform defaults * and with all additional type mappings. */ private function initializeAllDoctrineTypeMappings(): void { $this->initializeDoctrineTypeMappings(); foreach (Type::getTypesMap() as $typeName => $className) { foreach (Type::getType($typeName)->getMappedDatabaseTypes($this) as $dbType) { $dbType = strtolower($dbType); $this->doctrineTypeMapping[$dbType] = $typeName; } } } /** * Returns the SQL snippet used to declare a column that can * store characters in the ASCII character set * * @param mixed[] $column */ public function getAsciiStringTypeDeclarationSQL(array $column): string { return $this->getStringTypeDeclarationSQL($column); } /** * Returns the SQL snippet used to declare a VARCHAR column type. * * @deprecated Use {@link getStringTypeDeclarationSQL()} instead. * * @param mixed[] $column * * @return string */ public function getVarcharTypeDeclarationSQL(array $column) { if (isset($column['length'])) { $lengthOmitted = false; } else { $column['length'] = $this->getVarcharDefaultLength(); $lengthOmitted = true; } $fixed = $column['fixed'] ?? false; $maxLength = $fixed ? $this->getCharMaxLength() : $this->getVarcharMaxLength(); if ($column['length'] > $maxLength) { return $this->getClobTypeDeclarationSQL($column); } return $this->getVarcharTypeDeclarationSQLSnippet($column['length'], $fixed, $lengthOmitted); } /** * Returns the SQL snippet used to declare a string column type. * * @param mixed[] $column * * @return string */ public function getStringTypeDeclarationSQL(array $column) { return $this->getVarcharTypeDeclarationSQL($column); } /** * Returns the SQL snippet used to declare a BINARY/VARBINARY column type. * * @param mixed[] $column The column definition. * * @return string */ public function getBinaryTypeDeclarationSQL(array $column) { if (isset($column['length'])) { $lengthOmitted = false; } else { $column['length'] = $this->getBinaryDefaultLength(); $lengthOmitted = true; } $fixed = $column['fixed'] ?? false; $maxLength = $this->getBinaryMaxLength(); if ($column['length'] > $maxLength) { if ($maxLength > 0) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3187', 'Binary column length %d is greater than supported by the platform (%d).' . ' Reduce the column length or use a BLOB column instead.', $column['length'], $maxLength, ); } return $this->getBlobTypeDeclarationSQL($column); } return $this->getBinaryTypeDeclarationSQLSnippet($column['length'], $fixed, $lengthOmitted); } /** * Returns the SQL snippet to declare a GUID/UUID column. * * By default this maps directly to a CHAR(36) and only maps to more * special datatypes when the underlying databases support this datatype. * * @param mixed[] $column * * @return string */ public function getGuidTypeDeclarationSQL(array $column) { $column['length'] = 36; $column['fixed'] = true; return $this->getStringTypeDeclarationSQL($column); } /** * Returns the SQL snippet to declare a JSON column. * * By default this maps directly to a CLOB and only maps to more * special datatypes when the underlying databases support this datatype. * * @param mixed[] $column * * @return string */ public function getJsonTypeDeclarationSQL(array $column) { return $this->getClobTypeDeclarationSQL($column); } /** * @param int|false $length * @param bool $fixed * * @return string * * @throws Exception If not supported on this platform. */ protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) { throw Exception::notSupported('VARCHARs not supported by Platform.'); } /** * Returns the SQL snippet used to declare a BINARY/VARBINARY column type. * * @param int|false $length The length of the column. * @param bool $fixed Whether the column length is fixed. * * @return string * * @throws Exception If not supported on this platform. */ protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) { throw Exception::notSupported('BINARY/VARBINARY column types are not supported by this platform.'); } /** * Returns the SQL snippet used to declare a CLOB column type. * * @param mixed[] $column * * @return string */ abstract public function getClobTypeDeclarationSQL(array $column); /** * Returns the SQL Snippet used to declare a BLOB column type. * * @param mixed[] $column * * @return string */ abstract public function getBlobTypeDeclarationSQL(array $column); /** * Gets the name of the platform. * * @deprecated Identify platforms by their class. * * @return string */ abstract public function getName(); /** * Registers a doctrine type to be used in conjunction with a column type of this platform. * * @param string $dbType * @param string $doctrineType * * @return void * * @throws Exception If the type is not found. */ public function registerDoctrineTypeMapping($dbType, $doctrineType) { if ($this->doctrineTypeMapping === null) { $this->initializeAllDoctrineTypeMappings(); } if (! Types\Type::hasType($doctrineType)) { throw Exception::typeNotFound($doctrineType); } $dbType = strtolower($dbType); $this->doctrineTypeMapping[$dbType] = $doctrineType; $doctrineType = Type::getType($doctrineType); if (! $doctrineType->requiresSQLCommentHint($this)) { return; } $this->markDoctrineTypeCommented($doctrineType); } /** * Gets the Doctrine type that is mapped for the given database column type. * * @param string $dbType * * @return string * * @throws Exception */ public function getDoctrineTypeMapping($dbType) { if ($this->doctrineTypeMapping === null) { $this->initializeAllDoctrineTypeMappings(); } $dbType = strtolower($dbType); if (! isset($this->doctrineTypeMapping[$dbType])) { throw new Exception( 'Unknown database type ' . $dbType . ' requested, ' . static::class . ' may not support it.', ); } return $this->doctrineTypeMapping[$dbType]; } /** * Checks if a database type is currently supported by this platform. * * @param string $dbType * * @return bool */ public function hasDoctrineTypeMappingFor($dbType) { if ($this->doctrineTypeMapping === null) { $this->initializeAllDoctrineTypeMappings(); } $dbType = strtolower($dbType); return isset($this->doctrineTypeMapping[$dbType]); } /** * Initializes the Doctrine Type comments instance variable for in_array() checks. * * @deprecated This API will be removed in Doctrine DBAL 4.0. * * @return void */ protected function initializeCommentedDoctrineTypes() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5058', '%s is deprecated and will be removed in Doctrine DBAL 4.0.', __METHOD__, ); $this->doctrineTypeComments = []; foreach (Type::getTypesMap() as $typeName => $className) { $type = Type::getType($typeName); if (! $type->requiresSQLCommentHint($this)) { continue; } $this->doctrineTypeComments[] = $typeName; } } /** * Is it necessary for the platform to add a parsable type comment to allow reverse engineering the given type? * * @deprecated Use {@link Type::requiresSQLCommentHint()} instead. * * @return bool */ public function isCommentedDoctrineType(Type $doctrineType) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5058', '%s is deprecated and will be removed in Doctrine DBAL 4.0. Use Type::requiresSQLCommentHint() instead.', __METHOD__, ); if ($this->doctrineTypeComments === null) { $this->initializeCommentedDoctrineTypes(); } return $doctrineType->requiresSQLCommentHint($this); } /** * Marks this type as to be commented in ALTER TABLE and CREATE TABLE statements. * * @param string|Type $doctrineType * * @return void */ public function markDoctrineTypeCommented($doctrineType) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5058', '%s is deprecated and will be removed in Doctrine DBAL 4.0. Use Type::requiresSQLCommentHint() instead.', __METHOD__, ); if ($this->doctrineTypeComments === null) { $this->initializeCommentedDoctrineTypes(); } assert(is_array($this->doctrineTypeComments)); $this->doctrineTypeComments[] = $doctrineType instanceof Type ? $doctrineType->getName() : $doctrineType; } /** * Gets the comment to append to a column comment that helps parsing this type in reverse engineering. * * @deprecated This method will be removed without replacement. * * @return string */ public function getDoctrineTypeComment(Type $doctrineType) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5107', '%s is deprecated and will be removed in Doctrine DBAL 4.0.', __METHOD__, ); return '(DC2Type:' . $doctrineType->getName() . ')'; } /** * Gets the comment of a passed column modified by potential doctrine type comment hints. * * @deprecated This method will be removed without replacement. * * @return string|null */ protected function getColumnComment(Column $column) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5107', '%s is deprecated and will be removed in Doctrine DBAL 4.0.', __METHOD__, ); $comment = $column->getComment(); if (! $this->disableTypeComments && $column->getType()->requiresSQLCommentHint($this)) { $comment .= $this->getDoctrineTypeComment($column->getType()); } return $comment; } /** * Gets the character used for identifier quoting. * * @deprecated Use {@see quoteIdentifier()} to quote identifiers instead. * * @return string */ public function getIdentifierQuoteCharacter() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5388', 'AbstractPlatform::getIdentifierQuoteCharacter() is deprecated. Use quoteIdentifier() instead.', ); return '"'; } /** * Gets the string portion that starts an SQL comment. * * @deprecated * * @return string */ public function getSqlCommentStartString() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getSqlCommentStartString() is deprecated.', ); return '--'; } /** * Gets the string portion that ends an SQL comment. * * @deprecated * * @return string */ public function getSqlCommentEndString() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getSqlCommentEndString() is deprecated.', ); return "\n"; } /** * Gets the maximum length of a char column. * * @deprecated */ public function getCharMaxLength(): int { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'AbstractPlatform::getCharMaxLength() is deprecated.', ); return $this->getVarcharMaxLength(); } /** * Gets the maximum length of a varchar column. * * @deprecated * * @return int */ public function getVarcharMaxLength() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'AbstractPlatform::getVarcharMaxLength() is deprecated.', ); return 4000; } /** * Gets the default length of a varchar column. * * @deprecated * * @return int */ public function getVarcharDefaultLength() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'Relying on the default varchar column length is deprecated, specify the length explicitly.', ); return 255; } /** * Gets the maximum length of a binary column. * * @deprecated * * @return int */ public function getBinaryMaxLength() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'AbstractPlatform::getBinaryMaxLength() is deprecated.', ); return 4000; } /** * Gets the default length of a binary column. * * @deprecated * * @return int */ public function getBinaryDefaultLength() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'Relying on the default binary column length is deprecated, specify the length explicitly.', ); return 255; } /** * Gets all SQL wildcard characters of the platform. * * @deprecated Use {@see AbstractPlatform::getLikeWildcardCharacters()} instead. * * @return string[] */ public function getWildcards() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getWildcards() is deprecated.' . ' Use AbstractPlatform::getLikeWildcardCharacters() instead.', ); return ['%', '_']; } /** * Returns the regular expression operator. * * @return string * * @throws Exception If not supported on this platform. */ public function getRegexpExpression() { throw Exception::notSupported(__METHOD__); } /** * Returns the SQL snippet to get the average value of a column. * * @deprecated Use AVG() in SQL instead. * * @param string $column The column to use. * * @return string Generated SQL including an AVG aggregate function. */ public function getAvgExpression($column) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getAvgExpression() is deprecated. Use AVG() in SQL instead.', ); return 'AVG(' . $column . ')'; } /** * Returns the SQL snippet to get the number of rows (without a NULL value) of a column. * * If a '*' is used instead of a column the number of selected rows is returned. * * @deprecated Use COUNT() in SQL instead. * * @param string|int $column The column to use. * * @return string Generated SQL including a COUNT aggregate function. */ public function getCountExpression($column) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getCountExpression() is deprecated. Use COUNT() in SQL instead.', ); return 'COUNT(' . $column . ')'; } /** * Returns the SQL snippet to get the highest value of a column. * * @deprecated Use MAX() in SQL instead. * * @param string $column The column to use. * * @return string Generated SQL including a MAX aggregate function. */ public function getMaxExpression($column) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getMaxExpression() is deprecated. Use MAX() in SQL instead.', ); return 'MAX(' . $column . ')'; } /** * Returns the SQL snippet to get the lowest value of a column. * * @deprecated Use MIN() in SQL instead. * * @param string $column The column to use. * * @return string Generated SQL including a MIN aggregate function. */ public function getMinExpression($column) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getMinExpression() is deprecated. Use MIN() in SQL instead.', ); return 'MIN(' . $column . ')'; } /** * Returns the SQL snippet to get the total sum of a column. * * @deprecated Use SUM() in SQL instead. * * @param string $column The column to use. * * @return string Generated SQL including a SUM aggregate function. */ public function getSumExpression($column) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getSumExpression() is deprecated. Use SUM() in SQL instead.', ); return 'SUM(' . $column . ')'; } // scalar functions /** * Returns the SQL snippet to get the md5 sum of a column. * * Note: Not SQL92, but common functionality. * * @deprecated * * @param string $column * * @return string */ public function getMd5Expression($column) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getMd5Expression() is deprecated.', ); return 'MD5(' . $column . ')'; } /** * Returns the SQL snippet to get the length of a text column in characters. * * @param string $column * * @return string */ public function getLengthExpression($column) { return 'LENGTH(' . $column . ')'; } /** * Returns the SQL snippet to get the squared value of a column. * * @deprecated Use SQRT() in SQL instead. * * @param string $column The column to use. * * @return string Generated SQL including an SQRT aggregate function. */ public function getSqrtExpression($column) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getSqrtExpression() is deprecated. Use SQRT() in SQL instead.', ); return 'SQRT(' . $column . ')'; } /** * Returns the SQL snippet to round a numeric column to the number of decimals specified. * * @deprecated Use ROUND() in SQL instead. * * @param string $column * @param string|int $decimals * * @return string */ public function getRoundExpression($column, $decimals = 0) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getRoundExpression() is deprecated. Use ROUND() in SQL instead.', ); return 'ROUND(' . $column . ', ' . $decimals . ')'; } /** * Returns the SQL snippet to get the remainder of the division operation $expression1 / $expression2. * * @param string $expression1 * @param string $expression2 * * @return string */ public function getModExpression($expression1, $expression2) { return 'MOD(' . $expression1 . ', ' . $expression2 . ')'; } /** * Returns the SQL snippet to trim a string. * * @param string $str The expression to apply the trim to. * @param int $mode The position of the trim (leading/trailing/both). * @param string|bool $char The char to trim, has to be quoted already. Defaults to space. * * @return string */ public function getTrimExpression($str, $mode = TrimMode::UNSPECIFIED, $char = false) { $expression = ''; switch ($mode) { case TrimMode::LEADING: $expression = 'LEADING '; break; case TrimMode::TRAILING: $expression = 'TRAILING '; break; case TrimMode::BOTH: $expression = 'BOTH '; break; } if ($char !== false) { $expression .= $char . ' '; } if ($mode !== TrimMode::UNSPECIFIED || $char !== false) { $expression .= 'FROM '; } return 'TRIM(' . $expression . $str . ')'; } /** * Returns the SQL snippet to trim trailing space characters from the expression. * * @deprecated Use RTRIM() in SQL instead. * * @param string $str Literal string or column name. * * @return string */ public function getRtrimExpression($str) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getRtrimExpression() is deprecated. Use RTRIM() in SQL instead.', ); return 'RTRIM(' . $str . ')'; } /** * Returns the SQL snippet to trim leading space characters from the expression. * * @deprecated Use LTRIM() in SQL instead. * * @param string $str Literal string or column name. * * @return string */ public function getLtrimExpression($str) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getLtrimExpression() is deprecated. Use LTRIM() in SQL instead.', ); return 'LTRIM(' . $str . ')'; } /** * Returns the SQL snippet to change all characters from the expression to uppercase, * according to the current character set mapping. * * @deprecated Use UPPER() in SQL instead. * * @param string $str Literal string or column name. * * @return string */ public function getUpperExpression($str) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getUpperExpression() is deprecated. Use UPPER() in SQL instead.', ); return 'UPPER(' . $str . ')'; } /** * Returns the SQL snippet to change all characters from the expression to lowercase, * according to the current character set mapping. * * @deprecated Use LOWER() in SQL instead. * * @param string $str Literal string or column name. * * @return string */ public function getLowerExpression($str) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getLowerExpression() is deprecated. Use LOWER() in SQL instead.', ); return 'LOWER(' . $str . ')'; } /** * Returns the SQL snippet to get the position of the first occurrence of substring $substr in string $str. * * @param string $str Literal string. * @param string $substr Literal string to find. * @param string|int|false $startPos Position to start at, beginning of string by default. * * @return string * * @throws Exception If not supported on this platform. */ public function getLocateExpression($str, $substr, $startPos = false) { throw Exception::notSupported(__METHOD__); } /** * Returns the SQL snippet to get the current system date. * * @deprecated Generate dates within the application. * * @return string */ public function getNowExpression() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4753', 'AbstractPlatform::getNowExpression() is deprecated. Generate dates within the application.', ); return 'NOW()'; } /** * Returns a SQL snippet to get a substring inside an SQL statement. * * Note: Not SQL92, but common functionality. * * SQLite only supports the 2 parameter variant of this function. * * @param string $string An sql string literal or column name/alias. * @param string|int $start Where to start the substring portion. * @param string|int|null $length The substring portion length. * * @return string */ public function getSubstringExpression($string, $start, $length = null) { if ($length === null) { return 'SUBSTRING(' . $string . ' FROM ' . $start . ')'; } return 'SUBSTRING(' . $string . ' FROM ' . $start . ' FOR ' . $length . ')'; } /** * Returns a SQL snippet to concatenate the given expressions. * * Accepts an arbitrary number of string parameters. Each parameter must contain an expression. * * @return string */ public function getConcatExpression() { return implode(' || ', func_get_args()); } /** * Returns the SQL for a logical not. * * Example: * * $q = new Doctrine_Query(); * $e = $q->expr; * $q->select('*')->from('table') * ->where($e->eq('id', $e->not('null')); * * * @deprecated Use NOT() in SQL instead. * * @param string $expression * * @return string The logical expression. */ public function getNotExpression($expression) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getNotExpression() is deprecated. Use NOT() in SQL instead.', ); return 'NOT(' . $expression . ')'; } /** * Returns the SQL that checks if an expression is null. * * @deprecated Use IS NULL in SQL instead. * * @param string $expression The expression that should be compared to null. * * @return string The logical expression. */ public function getIsNullExpression($expression) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getIsNullExpression() is deprecated. Use IS NULL in SQL instead.', ); return $expression . ' IS NULL'; } /** * Returns the SQL that checks if an expression is not null. * * @deprecated Use IS NOT NULL in SQL instead. * * @param string $expression The expression that should be compared to null. * * @return string The logical expression. */ public function getIsNotNullExpression($expression) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getIsNotNullExpression() is deprecated. Use IS NOT NULL in SQL instead.', ); return $expression . ' IS NOT NULL'; } /** * Returns the SQL that checks if an expression evaluates to a value between two values. * * The parameter $expression is checked if it is between $value1 and $value2. * * Note: There is a slight difference in the way BETWEEN works on some databases. * http://www.w3schools.com/sql/sql_between.asp. If you want complete database * independence you should avoid using between(). * * @deprecated Use BETWEEN in SQL instead. * * @param string $expression The value to compare to. * @param string $value1 The lower value to compare with. * @param string $value2 The higher value to compare with. * * @return string The logical expression. */ public function getBetweenExpression($expression, $value1, $value2) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getBetweenExpression() is deprecated. Use BETWEEN in SQL instead.', ); return $expression . ' BETWEEN ' . $value1 . ' AND ' . $value2; } /** * Returns the SQL to get the arccosine of a value. * * @deprecated Use ACOS() in SQL instead. * * @param string $value * * @return string */ public function getAcosExpression($value) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getAcosExpression() is deprecated. Use ACOS() in SQL instead.', ); return 'ACOS(' . $value . ')'; } /** * Returns the SQL to get the sine of a value. * * @deprecated Use SIN() in SQL instead. * * @param string $value * * @return string */ public function getSinExpression($value) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getSinExpression() is deprecated. Use SIN() in SQL instead.', ); return 'SIN(' . $value . ')'; } /** * Returns the SQL to get the PI value. * * @deprecated Use PI() in SQL instead. * * @return string */ public function getPiExpression() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getPiExpression() is deprecated. Use PI() in SQL instead.', ); return 'PI()'; } /** * Returns the SQL to get the cosine of a value. * * @deprecated Use COS() in SQL instead. * * @param string $value * * @return string */ public function getCosExpression($value) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getCosExpression() is deprecated. Use COS() in SQL instead.', ); return 'COS(' . $value . ')'; } /** * Returns the SQL to calculate the difference in days between the two passed dates. * * Computes diff = date1 - date2. * * @param string $date1 * @param string $date2 * * @return string * * @throws Exception If not supported on this platform. */ public function getDateDiffExpression($date1, $date2) { throw Exception::notSupported(__METHOD__); } /** * Returns the SQL to add the number of given seconds to a date. * * @param string $date * @param int|string $seconds * * @return string * * @throws Exception If not supported on this platform. */ public function getDateAddSecondsExpression($date, $seconds) { if (is_int($seconds)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/3498', 'Passing $seconds as an integer is deprecated. Pass it as a numeric string instead.', ); } return $this->getDateArithmeticIntervalExpression($date, '+', $seconds, DateIntervalUnit::SECOND); } /** * Returns the SQL to subtract the number of given seconds from a date. * * @param string $date * @param int|string $seconds * * @return string * * @throws Exception If not supported on this platform. */ public function getDateSubSecondsExpression($date, $seconds) { if (is_int($seconds)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/3498', 'Passing $seconds as an integer is deprecated. Pass it as a numeric string instead.', ); } return $this->getDateArithmeticIntervalExpression($date, '-', $seconds, DateIntervalUnit::SECOND); } /** * Returns the SQL to add the number of given minutes to a date. * * @param string $date * @param int|string $minutes * * @return string * * @throws Exception If not supported on this platform. */ public function getDateAddMinutesExpression($date, $minutes) { if (is_int($minutes)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/3498', 'Passing $minutes as an integer is deprecated. Pass it as a numeric string instead.', ); } return $this->getDateArithmeticIntervalExpression($date, '+', $minutes, DateIntervalUnit::MINUTE); } /** * Returns the SQL to subtract the number of given minutes from a date. * * @param string $date * @param int|string $minutes * * @return string * * @throws Exception If not supported on this platform. */ public function getDateSubMinutesExpression($date, $minutes) { if (is_int($minutes)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/3498', 'Passing $minutes as an integer is deprecated. Pass it as a numeric string instead.', ); } return $this->getDateArithmeticIntervalExpression($date, '-', $minutes, DateIntervalUnit::MINUTE); } /** * Returns the SQL to add the number of given hours to a date. * * @param string $date * @param int|string $hours * * @return string * * @throws Exception If not supported on this platform. */ public function getDateAddHourExpression($date, $hours) { if (is_int($hours)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/3498', 'Passing $hours as an integer is deprecated. Pass it as a numeric string instead.', ); } return $this->getDateArithmeticIntervalExpression($date, '+', $hours, DateIntervalUnit::HOUR); } /** * Returns the SQL to subtract the number of given hours to a date. * * @param string $date * @param int|string $hours * * @return string * * @throws Exception If not supported on this platform. */ public function getDateSubHourExpression($date, $hours) { if (is_int($hours)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/3498', 'Passing $hours as an integer is deprecated. Pass it as a numeric string instead.', ); } return $this->getDateArithmeticIntervalExpression($date, '-', $hours, DateIntervalUnit::HOUR); } /** * Returns the SQL to add the number of given days to a date. * * @param string $date * @param int|string $days * * @return string * * @throws Exception If not supported on this platform. */ public function getDateAddDaysExpression($date, $days) { if (is_int($days)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/3498', 'Passing $days as an integer is deprecated. Pass it as a numeric string instead.', ); } return $this->getDateArithmeticIntervalExpression($date, '+', $days, DateIntervalUnit::DAY); } /** * Returns the SQL to subtract the number of given days to a date. * * @param string $date * @param int|string $days * * @return string * * @throws Exception If not supported on this platform. */ public function getDateSubDaysExpression($date, $days) { if (is_int($days)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/3498', 'Passing $days as an integer is deprecated. Pass it as a numeric string instead.', ); } return $this->getDateArithmeticIntervalExpression($date, '-', $days, DateIntervalUnit::DAY); } /** * Returns the SQL to add the number of given weeks to a date. * * @param string $date * @param int|string $weeks * * @return string * * @throws Exception If not supported on this platform. */ public function getDateAddWeeksExpression($date, $weeks) { if (is_int($weeks)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/3498', 'Passing $weeks as an integer is deprecated. Pass it as a numeric string instead.', ); } return $this->getDateArithmeticIntervalExpression($date, '+', $weeks, DateIntervalUnit::WEEK); } /** * Returns the SQL to subtract the number of given weeks from a date. * * @param string $date * @param int|string $weeks * * @return string * * @throws Exception If not supported on this platform. */ public function getDateSubWeeksExpression($date, $weeks) { if (is_int($weeks)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/3498', 'Passing $weeks as an integer is deprecated. Pass it as a numeric string instead.', ); } return $this->getDateArithmeticIntervalExpression($date, '-', $weeks, DateIntervalUnit::WEEK); } /** * Returns the SQL to add the number of given months to a date. * * @param string $date * @param int|string $months * * @return string * * @throws Exception If not supported on this platform. */ public function getDateAddMonthExpression($date, $months) { if (is_int($months)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/3498', 'Passing $months as an integer is deprecated. Pass it as a numeric string instead.', ); } return $this->getDateArithmeticIntervalExpression($date, '+', $months, DateIntervalUnit::MONTH); } /** * Returns the SQL to subtract the number of given months to a date. * * @param string $date * @param int|string $months * * @return string * * @throws Exception If not supported on this platform. */ public function getDateSubMonthExpression($date, $months) { if (is_int($months)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/3498', 'Passing $months as an integer is deprecated. Pass it as a numeric string instead.', ); } return $this->getDateArithmeticIntervalExpression($date, '-', $months, DateIntervalUnit::MONTH); } /** * Returns the SQL to add the number of given quarters to a date. * * @param string $date * @param int|string $quarters * * @return string * * @throws Exception If not supported on this platform. */ public function getDateAddQuartersExpression($date, $quarters) { if (is_int($quarters)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/3498', 'Passing $quarters as an integer is deprecated. Pass it as a numeric string instead.', ); } return $this->getDateArithmeticIntervalExpression($date, '+', $quarters, DateIntervalUnit::QUARTER); } /** * Returns the SQL to subtract the number of given quarters from a date. * * @param string $date * @param int|string $quarters * * @return string * * @throws Exception If not supported on this platform. */ public function getDateSubQuartersExpression($date, $quarters) { if (is_int($quarters)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/3498', 'Passing $quarters as an integer is deprecated. Pass it as a numeric string instead.', ); } return $this->getDateArithmeticIntervalExpression($date, '-', $quarters, DateIntervalUnit::QUARTER); } /** * Returns the SQL to add the number of given years to a date. * * @param string $date * @param int|string $years * * @return string * * @throws Exception If not supported on this platform. */ public function getDateAddYearsExpression($date, $years) { if (is_int($years)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/3498', 'Passing $years as an integer is deprecated. Pass it as a numeric string instead.', ); } return $this->getDateArithmeticIntervalExpression($date, '+', $years, DateIntervalUnit::YEAR); } /** * Returns the SQL to subtract the number of given years from a date. * * @param string $date * @param int|string $years * * @return string * * @throws Exception If not supported on this platform. */ public function getDateSubYearsExpression($date, $years) { if (is_int($years)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/3498', 'Passing $years as an integer is deprecated. Pass it as a numeric string instead.', ); } return $this->getDateArithmeticIntervalExpression($date, '-', $years, DateIntervalUnit::YEAR); } /** * Returns the SQL for a date arithmetic expression. * * @param string $date The column or literal representing a date * to perform the arithmetic operation on. * @param string $operator The arithmetic operator (+ or -). * @param int|string $interval The interval that shall be calculated into the date. * @param string $unit The unit of the interval that shall be calculated into the date. * One of the {@see DateIntervalUnit} constants. * * @return string * * @throws Exception If not supported on this platform. */ protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) { throw Exception::notSupported(__METHOD__); } /** * Generates the SQL expression which represents the given date interval multiplied by a number * * @param string $interval SQL expression describing the interval value * @param int $multiplier Interval multiplier */ protected function multiplyInterval(string $interval, int $multiplier): string { return sprintf('(%s * %d)', $interval, $multiplier); } /** * Returns the SQL bit AND comparison expression. * * @param string $value1 * @param string $value2 * * @return string */ public function getBitAndComparisonExpression($value1, $value2) { return '(' . $value1 . ' & ' . $value2 . ')'; } /** * Returns the SQL bit OR comparison expression. * * @param string $value1 * @param string $value2 * * @return string */ public function getBitOrComparisonExpression($value1, $value2) { return '(' . $value1 . ' | ' . $value2 . ')'; } /** * Returns the SQL expression which represents the currently selected database. */ abstract public function getCurrentDatabaseExpression(): string; /** * Returns the FOR UPDATE expression. * * @deprecated This API is not portable. Use {@link QueryBuilder::forUpdate()}` instead. * * @return string */ public function getForUpdateSQL() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6191', '%s is deprecated as non-portable.', __METHOD__, ); return 'FOR UPDATE'; } /** * Honors that some SQL vendors such as MsSql use table hints for locking instead of the * ANSI SQL FOR UPDATE specification. * * @param string $fromClause The FROM clause to append the hint for the given lock mode to * @param int $lockMode One of the Doctrine\DBAL\LockMode::* constants * @psalm-param LockMode::* $lockMode */ public function appendLockHint(string $fromClause, int $lockMode): string { switch ($lockMode) { case LockMode::NONE: case LockMode::OPTIMISTIC: case LockMode::PESSIMISTIC_READ: case LockMode::PESSIMISTIC_WRITE: return $fromClause; default: throw InvalidLockMode::fromLockMode($lockMode); } } /** * Returns the SQL snippet to append to any SELECT statement which locks rows in shared read lock. * * This defaults to the ANSI SQL "FOR UPDATE", which is an exclusive lock (Write). Some database * vendors allow to lighten this constraint up to be a real read lock. * * @deprecated This API is not portable. * * @return string */ public function getReadLockSQL() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6191', '%s is deprecated as non-portable.', __METHOD__, ); return $this->getForUpdateSQL(); } /** * Returns the SQL snippet to append to any SELECT statement which obtains an exclusive lock on the rows. * * The semantics of this lock mode should equal the SELECT .. FOR UPDATE of the ANSI SQL standard. * * @deprecated This API is not portable. * * @return string */ public function getWriteLockSQL() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/6191', '%s is deprecated as non-portable.', __METHOD__, ); return $this->getForUpdateSQL(); } /** * Returns the SQL snippet to drop an existing table. * * @param Table|string $table * * @return string * * @throws InvalidArgumentException */ public function getDropTableSQL($table) { $tableArg = $table; if ($table instanceof Table) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); $table = $table->getQuotedName($this); } if (! is_string($table)) { throw new InvalidArgumentException( __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.', ); } if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaDropTable)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5784', 'Subscribing to %s events is deprecated.', Events::onSchemaDropTable, ); $eventArgs = new SchemaDropTableEventArgs($tableArg, $this); $this->_eventManager->dispatchEvent(Events::onSchemaDropTable, $eventArgs); if ($eventArgs->isDefaultPrevented()) { $sql = $eventArgs->getSql(); if ($sql === null) { throw new UnexpectedValueException('Default implementation of DROP TABLE was overridden with NULL'); } return $sql; } } return 'DROP TABLE ' . $table; } /** * Returns the SQL to safely drop a temporary table WITHOUT implicitly committing an open transaction. * * @param Table|string $table * * @return string */ public function getDropTemporaryTableSQL($table) { if ($table instanceof Table) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); $table = $table->getQuotedName($this); } return $this->getDropTableSQL($table); } /** * Returns the SQL to drop an index from a table. * * @param Index|string $index * @param Table|string|null $table * * @return string * * @throws InvalidArgumentException */ public function getDropIndexSQL($index, $table = null) { if ($index instanceof Index) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $index as an Index object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); $index = $index->getQuotedName($this); } elseif (! is_string($index)) { throw new InvalidArgumentException( __METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.', ); } return 'DROP INDEX ' . $index; } /** * Returns the SQL to drop a constraint. * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. * * @param Constraint|string $constraint * @param Table|string $table * * @return string */ public function getDropConstraintSQL($constraint, $table) { if ($constraint instanceof Constraint) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $constraint as a Constraint object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); } else { $constraint = new Identifier($constraint); } if ($table instanceof Table) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); } else { $table = new Identifier($table); } $constraint = $constraint->getQuotedName($this); $table = $table->getQuotedName($this); return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $constraint; } /** * Returns the SQL to drop a foreign key. * * @param ForeignKeyConstraint|string $foreignKey * @param Table|string $table * * @return string */ public function getDropForeignKeySQL($foreignKey, $table) { if ($foreignKey instanceof ForeignKeyConstraint) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $foreignKey as a ForeignKeyConstraint object to %s is deprecated.' . ' Pass it as a quoted name instead.', __METHOD__, ); } else { $foreignKey = new Identifier($foreignKey); } if ($table instanceof Table) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); } else { $table = new Identifier($table); } $foreignKey = $foreignKey->getQuotedName($this); $table = $table->getQuotedName($this); return 'ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $foreignKey; } /** * Returns the SQL to drop a unique constraint. */ public function getDropUniqueConstraintSQL(string $name, string $tableName): string { return $this->getDropConstraintSQL($name, $tableName); } /** * Returns the SQL statement(s) to create a table with the specified name, columns and constraints * on this platform. * * @param int $createFlags * @psalm-param int-mask-of $createFlags * * @return list The list of SQL statements. * * @throws Exception * @throws InvalidArgumentException */ public function getCreateTableSQL(Table $table, $createFlags = self::CREATE_INDEXES) { if (! is_int($createFlags)) { throw new InvalidArgumentException( 'Second argument of AbstractPlatform::getCreateTableSQL() has to be integer.', ); } if (($createFlags & self::CREATE_INDEXES) === 0) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5416', 'Unsetting the CREATE_INDEXES flag in AbstractPlatform::getCreateTableSQL() is deprecated.', ); } if (($createFlags & self::CREATE_FOREIGNKEYS) === 0) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5416', 'Not setting the CREATE_FOREIGNKEYS flag in AbstractPlatform::getCreateTableSQL()' . ' is deprecated. In order to build the statements that create multiple tables' . ' referencing each other via foreign keys, use AbstractPlatform::getCreateTablesSQL().', ); } return $this->buildCreateTableSQL( $table, ($createFlags & self::CREATE_INDEXES) > 0, ($createFlags & self::CREATE_FOREIGNKEYS) > 0, ); } public function createSelectSQLBuilder(): SelectSQLBuilder { return new DefaultSelectSQLBuilder($this, 'FOR UPDATE', 'SKIP LOCKED'); } /** * @internal * * @return list * * @throws Exception */ final protected function getCreateTableWithoutForeignKeysSQL(Table $table): array { return $this->buildCreateTableSQL($table, true, false); } /** * @return list * * @throws Exception */ private function buildCreateTableSQL(Table $table, bool $createIndexes, bool $createForeignKeys): array { if (count($table->getColumns()) === 0) { throw Exception::noColumnsSpecifiedForTable($table->getName()); } $tableName = $table->getQuotedName($this); $options = $table->getOptions(); $options['uniqueConstraints'] = []; $options['indexes'] = []; $options['primary'] = []; if ($createIndexes) { foreach ($table->getIndexes() as $index) { if (! $index->isPrimary()) { $options['indexes'][$index->getQuotedName($this)] = $index; continue; } $options['primary'] = $index->getQuotedColumns($this); $options['primary_index'] = $index; } foreach ($table->getUniqueConstraints() as $uniqueConstraint) { $options['uniqueConstraints'][$uniqueConstraint->getQuotedName($this)] = $uniqueConstraint; } } if ($createForeignKeys) { $options['foreignKeys'] = []; foreach ($table->getForeignKeys() as $fkConstraint) { $options['foreignKeys'][] = $fkConstraint; } } $columnSql = []; $columns = []; foreach ($table->getColumns() as $column) { if ( $this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTableColumn) ) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5784', 'Subscribing to %s events is deprecated.', Events::onSchemaCreateTableColumn, ); $eventArgs = new SchemaCreateTableColumnEventArgs($column, $table, $this); $this->_eventManager->dispatchEvent(Events::onSchemaCreateTableColumn, $eventArgs); $columnSql = array_merge($columnSql, $eventArgs->getSql()); if ($eventArgs->isDefaultPrevented()) { continue; } } $columnData = $this->columnToArray($column); if (in_array($column->getName(), $options['primary'], true)) { $columnData['primary'] = true; } $columns[$columnData['name']] = $columnData; } if ($this->_eventManager !== null && $this->_eventManager->hasListeners(Events::onSchemaCreateTable)) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5784', 'Subscribing to %s events is deprecated.', Events::onSchemaCreateTable, ); $eventArgs = new SchemaCreateTableEventArgs($table, $columns, $options, $this); $this->_eventManager->dispatchEvent(Events::onSchemaCreateTable, $eventArgs); if ($eventArgs->isDefaultPrevented()) { return array_merge($eventArgs->getSql(), $columnSql); } } $sql = $this->_getCreateTableSQL($tableName, $columns, $options); if ($this->supportsCommentOnStatement()) { if ($table->hasOption('comment')) { $sql[] = $this->getCommentOnTableSQL($tableName, $table->getOption('comment')); } foreach ($table->getColumns() as $column) { $comment = $this->getColumnComment($column); if ($comment === null || $comment === '') { continue; } $sql[] = $this->getCommentOnColumnSQL($tableName, $column->getQuotedName($this), $comment); } } return array_merge($sql, $columnSql); } /** * @param list
$tables * * @return list * * @throws Exception */ public function getCreateTablesSQL(array $tables): array { $sql = []; foreach ($tables as $table) { $sql = array_merge($sql, $this->getCreateTableWithoutForeignKeysSQL($table)); } foreach ($tables as $table) { foreach ($table->getForeignKeys() as $foreignKey) { $sql[] = $this->getCreateForeignKeySQL( $foreignKey, $table->getQuotedName($this), ); } } return $sql; } /** * @param list
$tables * * @return list */ public function getDropTablesSQL(array $tables): array { $sql = []; foreach ($tables as $table) { foreach ($table->getForeignKeys() as $foreignKey) { $sql[] = $this->getDropForeignKeySQL( $foreignKey->getQuotedName($this), $table->getQuotedName($this), ); } } foreach ($tables as $table) { $sql[] = $this->getDropTableSQL($table->getQuotedName($this)); } return $sql; } protected function getCommentOnTableSQL(string $tableName, ?string $comment): string { $tableName = new Identifier($tableName); return sprintf( 'COMMENT ON TABLE %s IS %s', $tableName->getQuotedName($this), $this->quoteStringLiteral((string) $comment), ); } /** * @param string $tableName * @param string $columnName * @param string|null $comment * * @return string */ public function getCommentOnColumnSQL($tableName, $columnName, $comment) { $tableName = new Identifier($tableName); $columnName = new Identifier($columnName); return sprintf( 'COMMENT ON COLUMN %s.%s IS %s', $tableName->getQuotedName($this), $columnName->getQuotedName($this), $this->quoteStringLiteral((string) $comment), ); } /** * Returns the SQL to create inline comment on a column. * * @param string $comment * * @return string * * @throws Exception If not supported on this platform. */ public function getInlineColumnCommentSQL($comment) { if (! $this->supportsInlineColumnComments()) { throw Exception::notSupported(__METHOD__); } return 'COMMENT ' . $this->quoteStringLiteral($comment); } /** * Returns the SQL used to create a table. * * @param string $name * @param mixed[][] $columns * @param mixed[] $options * * @return string[] */ protected function _getCreateTableSQL($name, array $columns, array $options = []) { $columnListSql = $this->getColumnDeclarationListSQL($columns); if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { foreach ($options['uniqueConstraints'] as $index => $definition) { $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($index, $definition); } } if (isset($options['primary']) && ! empty($options['primary'])) { $columnListSql .= ', PRIMARY KEY(' . implode(', ', array_unique(array_values($options['primary']))) . ')'; } if (isset($options['indexes']) && ! empty($options['indexes'])) { foreach ($options['indexes'] as $index => $definition) { $columnListSql .= ', ' . $this->getIndexDeclarationSQL($index, $definition); } } $query = 'CREATE TABLE ' . $name . ' (' . $columnListSql; $check = $this->getCheckDeclarationSQL($columns); if (! empty($check)) { $query .= ', ' . $check; } $query .= ')'; $sql = [$query]; if (isset($options['foreignKeys'])) { foreach ($options['foreignKeys'] as $definition) { $sql[] = $this->getCreateForeignKeySQL($definition, $name); } } return $sql; } /** @return string */ public function getCreateTemporaryTableSnippetSQL() { return 'CREATE TEMPORARY TABLE'; } /** * Generates SQL statements that can be used to apply the diff. * * @return list */ public function getAlterSchemaSQL(SchemaDiff $diff): array { return $diff->toSql($this); } /** * Returns the SQL to create a sequence on this platform. * * @return string * * @throws Exception If not supported on this platform. */ public function getCreateSequenceSQL(Sequence $sequence) { throw Exception::notSupported(__METHOD__); } /** * Returns the SQL to change a sequence on this platform. * * @return string * * @throws Exception If not supported on this platform. */ public function getAlterSequenceSQL(Sequence $sequence) { throw Exception::notSupported(__METHOD__); } /** * Returns the SQL snippet to drop an existing sequence. * * @param Sequence|string $sequence * * @return string * * @throws Exception If not supported on this platform. */ public function getDropSequenceSQL($sequence) { if (! $this->supportsSequences()) { throw Exception::notSupported(__METHOD__); } if ($sequence instanceof Sequence) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $sequence as a Sequence object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); $sequence = $sequence->getQuotedName($this); } return 'DROP SEQUENCE ' . $sequence; } /** * Returns the SQL to create a constraint on a table on this platform. * * @deprecated Use {@see getCreateIndexSQL()}, {@see getCreateForeignKeySQL()} * or {@see getCreateUniqueConstraintSQL()} instead. * * @param Table|string $table * * @return string * * @throws InvalidArgumentException */ public function getCreateConstraintSQL(Constraint $constraint, $table) { if ($table instanceof Table) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); $table = $table->getQuotedName($this); } $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $constraint->getQuotedName($this); $columnList = '(' . implode(', ', $constraint->getQuotedColumns($this)) . ')'; $referencesClause = ''; if ($constraint instanceof Index) { if ($constraint->isPrimary()) { $query .= ' PRIMARY KEY'; } elseif ($constraint->isUnique()) { $query .= ' UNIQUE'; } else { throw new InvalidArgumentException( 'Can only create primary or unique constraints, no common indexes with getCreateConstraintSQL().', ); } } elseif ($constraint instanceof UniqueConstraint) { $query .= ' UNIQUE'; } elseif ($constraint instanceof ForeignKeyConstraint) { $query .= ' FOREIGN KEY'; $referencesClause = ' REFERENCES ' . $constraint->getQuotedForeignTableName($this) . ' (' . implode(', ', $constraint->getQuotedForeignColumns($this)) . ')'; } $query .= ' ' . $columnList . $referencesClause; return $query; } /** * Returns the SQL to create an index on a table on this platform. * * @param Table|string $table The name of the table on which the index is to be created. * * @return string * * @throws InvalidArgumentException */ public function getCreateIndexSQL(Index $index, $table) { if ($table instanceof Table) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); $table = $table->getQuotedName($this); } $name = $index->getQuotedName($this); $columns = $index->getColumns(); if (count($columns) === 0) { throw new InvalidArgumentException(sprintf( 'Incomplete or invalid index definition %s on table %s', $name, $table, )); } if ($index->isPrimary()) { return $this->getCreatePrimaryKeySQL($index, $table); } $query = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table; $query .= ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')' . $this->getPartialIndexSQL($index); return $query; } /** * Adds condition for partial index. * * @return string */ protected function getPartialIndexSQL(Index $index) { if ($this->supportsPartialIndexes() && $index->hasOption('where')) { return ' WHERE ' . $index->getOption('where'); } return ''; } /** * Adds additional flags for index generation. * * @return string */ protected function getCreateIndexSQLFlags(Index $index) { return $index->isUnique() ? 'UNIQUE ' : ''; } /** * Returns the SQL to create an unnamed primary key constraint. * * @param Table|string $table * * @return string */ public function getCreatePrimaryKeySQL(Index $index, $table) { if ($table instanceof Table) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); $table = $table->getQuotedName($this); } return 'ALTER TABLE ' . $table . ' ADD PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index) . ')'; } /** * Returns the SQL to create a named schema. * * @param string $schemaName * * @return string * * @throws Exception If not supported on this platform. */ public function getCreateSchemaSQL($schemaName) { if (! $this->supportsSchemas()) { throw Exception::notSupported(__METHOD__); } return 'CREATE SCHEMA ' . $schemaName; } /** * Returns the SQL to create a unique constraint on a table on this platform. */ public function getCreateUniqueConstraintSQL(UniqueConstraint $constraint, string $tableName): string { return $this->getCreateConstraintSQL($constraint, $tableName); } /** * Returns the SQL snippet to drop a schema. * * @throws Exception If not supported on this platform. */ public function getDropSchemaSQL(string $schemaName): string { if (! $this->supportsSchemas()) { throw Exception::notSupported(__METHOD__); } return 'DROP SCHEMA ' . $schemaName; } /** * Quotes a string so that it can be safely used as a table or column name, * even if it is a reserved word of the platform. This also detects identifier * chains separated by dot and quotes them independently. * * NOTE: Just because you CAN use quoted identifiers doesn't mean * you SHOULD use them. In general, they end up causing way more * problems than they solve. * * @param string $str The identifier name to be quoted. * * @return string The quoted identifier string. */ public function quoteIdentifier($str) { if (strpos($str, '.') !== false) { $parts = array_map([$this, 'quoteSingleIdentifier'], explode('.', $str)); return implode('.', $parts); } return $this->quoteSingleIdentifier($str); } /** * Quotes a single identifier (no dot chain separation). * * @param string $str The identifier name to be quoted. * * @return string The quoted identifier string. */ public function quoteSingleIdentifier($str) { $c = $this->getIdentifierQuoteCharacter(); return $c . str_replace($c, $c . $c, $str) . $c; } /** * Returns the SQL to create a new foreign key. * * @param ForeignKeyConstraint $foreignKey The foreign key constraint. * @param Table|string $table The name of the table on which the foreign key is to be created. * * @return string */ public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) { if ($table instanceof Table) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); $table = $table->getQuotedName($this); } return 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSQL($foreignKey); } /** * Gets the SQL statements for altering an existing table. * * This method returns an array of SQL statements, since some platforms need several statements. * * @return list * * @throws Exception If not supported on this platform. */ public function getAlterTableSQL(TableDiff $diff) { throw Exception::notSupported(__METHOD__); } /** @return list */ public function getRenameTableSQL(string $oldName, string $newName): array { return [ sprintf('ALTER TABLE %s RENAME TO %s', $oldName, $newName), ]; } /** * @param mixed[] $columnSql * * @return bool */ protected function onSchemaAlterTableAddColumn(Column $column, TableDiff $diff, &$columnSql) { if ($this->_eventManager === null) { return false; } if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableAddColumn)) { return false; } Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5784', 'Subscribing to %s events is deprecated.', Events::onSchemaAlterTableAddColumn, ); $eventArgs = new SchemaAlterTableAddColumnEventArgs($column, $diff, $this); $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableAddColumn, $eventArgs); $columnSql = array_merge($columnSql, $eventArgs->getSql()); return $eventArgs->isDefaultPrevented(); } /** * @param string[] $columnSql * * @return bool */ protected function onSchemaAlterTableRemoveColumn(Column $column, TableDiff $diff, &$columnSql) { if ($this->_eventManager === null) { return false; } if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRemoveColumn)) { return false; } Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5784', 'Subscribing to %s events is deprecated.', Events::onSchemaAlterTableRemoveColumn, ); $eventArgs = new SchemaAlterTableRemoveColumnEventArgs($column, $diff, $this); $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRemoveColumn, $eventArgs); $columnSql = array_merge($columnSql, $eventArgs->getSql()); return $eventArgs->isDefaultPrevented(); } /** * @param string[] $columnSql * * @return bool */ protected function onSchemaAlterTableChangeColumn(ColumnDiff $columnDiff, TableDiff $diff, &$columnSql) { if ($this->_eventManager === null) { return false; } if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableChangeColumn)) { return false; } Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5784', 'Subscribing to %s events is deprecated.', Events::onSchemaAlterTableChangeColumn, ); $eventArgs = new SchemaAlterTableChangeColumnEventArgs($columnDiff, $diff, $this); $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableChangeColumn, $eventArgs); $columnSql = array_merge($columnSql, $eventArgs->getSql()); return $eventArgs->isDefaultPrevented(); } /** * @param string $oldColumnName * @param string[] $columnSql * * @return bool */ protected function onSchemaAlterTableRenameColumn($oldColumnName, Column $column, TableDiff $diff, &$columnSql) { if ($this->_eventManager === null) { return false; } if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTableRenameColumn)) { return false; } Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5784', 'Subscribing to %s events is deprecated.', Events::onSchemaAlterTableRenameColumn, ); $eventArgs = new SchemaAlterTableRenameColumnEventArgs($oldColumnName, $column, $diff, $this); $this->_eventManager->dispatchEvent(Events::onSchemaAlterTableRenameColumn, $eventArgs); $columnSql = array_merge($columnSql, $eventArgs->getSql()); return $eventArgs->isDefaultPrevented(); } /** * @param string[] $sql * * @return bool */ protected function onSchemaAlterTable(TableDiff $diff, &$sql) { if ($this->_eventManager === null) { return false; } if (! $this->_eventManager->hasListeners(Events::onSchemaAlterTable)) { return false; } Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/5784', 'Subscribing to %s events is deprecated.', Events::onSchemaAlterTable, ); $eventArgs = new SchemaAlterTableEventArgs($diff, $this); $this->_eventManager->dispatchEvent(Events::onSchemaAlterTable, $eventArgs); $sql = array_merge($sql, $eventArgs->getSql()); return $eventArgs->isDefaultPrevented(); } /** @return string[] */ protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) { $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); $sql = []; if ($this->supportsForeignKeyConstraints()) { foreach ($diff->getDroppedForeignKeys() as $foreignKey) { if ($foreignKey instanceof ForeignKeyConstraint) { $foreignKey = $foreignKey->getQuotedName($this); } $sql[] = $this->getDropForeignKeySQL($foreignKey, $tableNameSQL); } foreach ($diff->getModifiedForeignKeys() as $foreignKey) { $sql[] = $this->getDropForeignKeySQL($foreignKey->getQuotedName($this), $tableNameSQL); } } foreach ($diff->getDroppedIndexes() as $index) { $sql[] = $this->getDropIndexSQL($index->getQuotedName($this), $tableNameSQL); } foreach ($diff->getModifiedIndexes() as $index) { $sql[] = $this->getDropIndexSQL($index->getQuotedName($this), $tableNameSQL); } return $sql; } /** @return string[] */ protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) { $sql = []; $newName = $diff->getNewName(); if ($newName !== false) { $tableNameSQL = $newName->getQuotedName($this); } else { $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); } if ($this->supportsForeignKeyConstraints()) { foreach ($diff->getAddedForeignKeys() as $foreignKey) { $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableNameSQL); } foreach ($diff->getModifiedForeignKeys() as $foreignKey) { $sql[] = $this->getCreateForeignKeySQL($foreignKey, $tableNameSQL); } } foreach ($diff->getAddedIndexes() as $index) { $sql[] = $this->getCreateIndexSQL($index, $tableNameSQL); } foreach ($diff->getModifiedIndexes() as $index) { $sql[] = $this->getCreateIndexSQL($index, $tableNameSQL); } foreach ($diff->getRenamedIndexes() as $oldIndexName => $index) { $oldIndexName = new Identifier($oldIndexName); $sql = array_merge( $sql, $this->getRenameIndexSQL($oldIndexName->getQuotedName($this), $index, $tableNameSQL), ); } return $sql; } /** * Returns the SQL for renaming an index on a table. * * @param string $oldIndexName The name of the index to rename from. * @param Index $index The definition of the index to rename to. * @param string $tableName The table to rename the given index on. * * @return string[] The sequence of SQL statements for renaming the given index. */ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) { return [ $this->getDropIndexSQL($oldIndexName, $tableName), $this->getCreateIndexSQL($index, $tableName), ]; } /** * Gets declaration of a number of columns in bulk. * * @param mixed[][] $columns A multidimensional associative array. * The first dimension determines the column name, while the second * dimension is keyed with the name of the properties * of the column being declared as array indexes. Currently, the types * of supported column properties are as follows: * * length * Integer value that determines the maximum length of the text * column. If this argument is missing the column should be * declared to have the longest length allowed by the DBMS. * * default * Text value to be used as default for this column. * * notnull * Boolean flag that indicates whether this column is constrained * to not be set to null. * charset * Text value with the default CHARACTER SET for this column. * collation * Text value with the default COLLATION for this column. * unique * unique constraint * * @return string */ public function getColumnDeclarationListSQL(array $columns) { $declarations = []; foreach ($columns as $name => $column) { $declarations[] = $this->getColumnDeclarationSQL($name, $column); } return implode(', ', $declarations); } /** * Obtains DBMS specific SQL code portion needed to declare a generic type * column to be used in statements like CREATE TABLE. * * @param string $name The name the column to be declared. * @param mixed[] $column An associative array with the name of the properties * of the column being declared as array indexes. Currently, the types * of supported column properties are as follows: * * length * Integer value that determines the maximum length of the text * column. If this argument is missing the column should be * declared to have the longest length allowed by the DBMS. * * default * Text value to be used as default for this column. * * notnull * Boolean flag that indicates whether this column is constrained * to not be set to null. * charset * Text value with the default CHARACTER SET for this column. * collation * Text value with the default COLLATION for this column. * unique * unique constraint * check * column check constraint * columnDefinition * a string that defines the complete column * * @return string DBMS specific SQL code portion that should be used to declare the column. * * @throws Exception */ public function getColumnDeclarationSQL($name, array $column) { if (isset($column['columnDefinition'])) { $declaration = $this->getCustomTypeDeclarationSQL($column); } else { $default = $this->getDefaultValueDeclarationSQL($column); $charset = ! empty($column['charset']) ? ' ' . $this->getColumnCharsetDeclarationSQL($column['charset']) : ''; $collation = ! empty($column['collation']) ? ' ' . $this->getColumnCollationDeclarationSQL($column['collation']) : ''; $notnull = ! empty($column['notnull']) ? ' NOT NULL' : ''; if (! empty($column['unique'])) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5656', 'The usage of the "unique" column property is deprecated. Use unique constraints instead.', ); $unique = ' ' . $this->getUniqueFieldDeclarationSQL(); } else { $unique = ''; } if (! empty($column['check'])) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5656', 'The usage of the "check" column property is deprecated.', ); $check = ' ' . $column['check']; } else { $check = ''; } $typeDecl = $column['type']->getSQLDeclaration($column, $this); $declaration = $typeDecl . $charset . $default . $notnull . $unique . $check . $collation; if ($this->supportsInlineColumnComments() && isset($column['comment']) && $column['comment'] !== '') { $declaration .= ' ' . $this->getInlineColumnCommentSQL($column['comment']); } } return $name . ' ' . $declaration; } /** * Returns the SQL snippet that declares a floating point column of arbitrary precision. * * @param mixed[] $column * * @return string */ public function getDecimalTypeDeclarationSQL(array $column) { if (empty($column['precision'])) { if (! isset($column['precision'])) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5637', 'Relying on the default decimal column precision is deprecated' . ', specify the precision explicitly.', ); } $precision = 10; } else { $precision = $column['precision']; } if (empty($column['scale'])) { if (! isset($column['scale'])) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5637', 'Relying on the default decimal column scale is deprecated' . ', specify the scale explicitly.', ); } $scale = 0; } else { $scale = $column['scale']; } return 'NUMERIC(' . $precision . ', ' . $scale . ')'; } /** * Obtains DBMS specific SQL code portion needed to set a default value * declaration to be used in statements like CREATE TABLE. * * @param mixed[] $column The column definition array. * * @return string DBMS specific SQL code portion needed to set a default value. */ public function getDefaultValueDeclarationSQL($column) { if (! isset($column['default'])) { return empty($column['notnull']) ? ' DEFAULT NULL' : ''; } $default = $column['default']; if (! isset($column['type'])) { return " DEFAULT '" . $default . "'"; } $type = $column['type']; if ($type instanceof Types\PhpIntegerMappingType) { return ' DEFAULT ' . $default; } if ($type instanceof Types\PhpDateTimeMappingType && $default === $this->getCurrentTimestampSQL()) { return ' DEFAULT ' . $this->getCurrentTimestampSQL(); } if ($type instanceof Types\TimeType && $default === $this->getCurrentTimeSQL()) { return ' DEFAULT ' . $this->getCurrentTimeSQL(); } if ($type instanceof Types\DateType && $default === $this->getCurrentDateSQL()) { return ' DEFAULT ' . $this->getCurrentDateSQL(); } if ($type instanceof Types\BooleanType) { return ' DEFAULT ' . $this->convertBooleans($default); } return ' DEFAULT ' . $this->quoteStringLiteral($default); } /** * Obtains DBMS specific SQL code portion needed to set a CHECK constraint * declaration to be used in statements like CREATE TABLE. * * @param string[]|mixed[][] $definition The check definition. * * @return string DBMS specific SQL code portion needed to set a CHECK constraint. */ public function getCheckDeclarationSQL(array $definition) { $constraints = []; foreach ($definition as $column => $def) { if (is_string($def)) { $constraints[] = 'CHECK (' . $def . ')'; } else { if (isset($def['min'])) { $constraints[] = 'CHECK (' . $column . ' >= ' . $def['min'] . ')'; } if (isset($def['max'])) { $constraints[] = 'CHECK (' . $column . ' <= ' . $def['max'] . ')'; } } } return implode(', ', $constraints); } /** * Obtains DBMS specific SQL code portion needed to set a unique * constraint declaration to be used in statements like CREATE TABLE. * * @param string $name The name of the unique constraint. * @param UniqueConstraint $constraint The unique constraint definition. * * @return string DBMS specific SQL code portion needed to set a constraint. * * @throws InvalidArgumentException */ public function getUniqueConstraintDeclarationSQL($name, UniqueConstraint $constraint) { $columns = $constraint->getQuotedColumns($this); $name = new Identifier($name); if (count($columns) === 0) { throw new InvalidArgumentException("Incomplete definition. 'columns' required."); } $constraintFlags = array_merge(['UNIQUE'], array_map('strtoupper', $constraint->getFlags())); $constraintName = $name->getQuotedName($this); $columnListNames = $this->getColumnsFieldDeclarationListSQL($columns); return sprintf('CONSTRAINT %s %s (%s)', $constraintName, implode(' ', $constraintFlags), $columnListNames); } /** * Obtains DBMS specific SQL code portion needed to set an index * declaration to be used in statements like CREATE TABLE. * * @param string $name The name of the index. * @param Index $index The index definition. * * @return string DBMS specific SQL code portion needed to set an index. * * @throws InvalidArgumentException */ public function getIndexDeclarationSQL($name, Index $index) { $columns = $index->getColumns(); $name = new Identifier($name); if (count($columns) === 0) { throw new InvalidArgumentException("Incomplete definition. 'columns' required."); } return $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name->getQuotedName($this) . ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')' . $this->getPartialIndexSQL($index); } /** * Obtains SQL code portion needed to create a custom column, * e.g. when a column has the "columnDefinition" keyword. * Only "AUTOINCREMENT" and "PRIMARY KEY" are added if appropriate. * * @deprecated * * @param mixed[] $column * * @return string */ public function getCustomTypeDeclarationSQL(array $column) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5527', '%s is deprecated.', __METHOD__, ); return $column['columnDefinition']; } /** * Obtains DBMS specific SQL code portion needed to set an index * declaration to be used in statements like CREATE TABLE. * * @deprecated */ public function getIndexFieldDeclarationListSQL(Index $index): string { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5527', '%s is deprecated.', __METHOD__, ); return implode(', ', $index->getQuotedColumns($this)); } /** * Obtains DBMS specific SQL code portion needed to set an index * declaration to be used in statements like CREATE TABLE. * * @deprecated * * @param mixed[] $columns */ public function getColumnsFieldDeclarationListSQL(array $columns): string { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5527', '%s is deprecated.', __METHOD__, ); $ret = []; foreach ($columns as $column => $definition) { if (is_array($definition)) { $ret[] = $column; } else { $ret[] = $definition; } } return implode(', ', $ret); } /** * Returns the required SQL string that fits between CREATE ... TABLE * to create the table as a temporary table. * * Should be overridden in driver classes to return the correct string for the * specific database type. * * The default is to return the string "TEMPORARY" - this will result in a * SQL error for any database that does not support temporary tables, or that * requires a different SQL command from "CREATE TEMPORARY TABLE". * * @deprecated * * @return string The string required to be placed between "CREATE" and "TABLE" * to generate a temporary table, if possible. */ public function getTemporaryTableSQL() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getTemporaryTableSQL() is deprecated.', ); return 'TEMPORARY'; } /** * Some vendors require temporary table names to be qualified specially. * * @param string $tableName * * @return string */ public function getTemporaryTableName($tableName) { return $tableName; } /** * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint * of a column declaration to be used in statements like CREATE TABLE. * * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint * of a column declaration. */ public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey) { $sql = $this->getForeignKeyBaseDeclarationSQL($foreignKey); $sql .= $this->getAdvancedForeignKeyOptionsSQL($foreignKey); return $sql; } /** * Returns the FOREIGN KEY query section dealing with non-standard options * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... * * @param ForeignKeyConstraint $foreignKey The foreign key definition. * * @return string */ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) { $query = ''; if ($foreignKey->hasOption('onUpdate')) { $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onUpdate')); } if ($foreignKey->hasOption('onDelete')) { $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete')); } return $query; } /** * Returns the given referential action in uppercase if valid, otherwise throws an exception. * * @param string $action The foreign key referential action. * * @return string * * @throws InvalidArgumentException If unknown referential action given. */ public function getForeignKeyReferentialActionSQL($action) { $upper = strtoupper($action); switch ($upper) { case 'CASCADE': case 'SET NULL': case 'NO ACTION': case 'RESTRICT': case 'SET DEFAULT': return $upper; default: throw new InvalidArgumentException('Invalid foreign key action: ' . $upper); } } /** * Obtains DBMS specific SQL code portion needed to set the FOREIGN KEY constraint * of a column declaration to be used in statements like CREATE TABLE. * * @return string * * @throws InvalidArgumentException */ public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey) { $sql = ''; if (strlen($foreignKey->getName()) > 0) { $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' '; } $sql .= 'FOREIGN KEY ('; if (count($foreignKey->getLocalColumns()) === 0) { throw new InvalidArgumentException("Incomplete definition. 'local' required."); } if (count($foreignKey->getForeignColumns()) === 0) { throw new InvalidArgumentException("Incomplete definition. 'foreign' required."); } if (strlen($foreignKey->getForeignTableName()) === 0) { throw new InvalidArgumentException("Incomplete definition. 'foreignTable' required."); } return $sql . implode(', ', $foreignKey->getQuotedLocalColumns($this)) . ') REFERENCES ' . $foreignKey->getQuotedForeignTableName($this) . ' (' . implode(', ', $foreignKey->getQuotedForeignColumns($this)) . ')'; } /** * Obtains DBMS specific SQL code portion needed to set the UNIQUE constraint * of a column declaration to be used in statements like CREATE TABLE. * * @deprecated Use UNIQUE in SQL instead. * * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint * of a column declaration. */ public function getUniqueFieldDeclarationSQL() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getUniqueFieldDeclarationSQL() is deprecated. Use UNIQUE in SQL instead.', ); return 'UNIQUE'; } /** * Obtains DBMS specific SQL code portion needed to set the CHARACTER SET * of a column declaration to be used in statements like CREATE TABLE. * * @param string $charset The name of the charset. * * @return string DBMS specific SQL code portion needed to set the CHARACTER SET * of a column declaration. */ public function getColumnCharsetDeclarationSQL($charset) { return ''; } /** * Obtains DBMS specific SQL code portion needed to set the COLLATION * of a column declaration to be used in statements like CREATE TABLE. * * @param string $collation The name of the collation. * * @return string DBMS specific SQL code portion needed to set the COLLATION * of a column declaration. */ public function getColumnCollationDeclarationSQL($collation) { return $this->supportsColumnCollation() ? 'COLLATE ' . $this->quoteSingleIdentifier($collation) : ''; } /** * Whether the platform prefers identity columns (eg. autoincrement) for ID generation. * Subclasses should override this method to return TRUE if they prefer identity columns. * * @deprecated * * @return bool */ public function prefersIdentityColumns() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/1519', 'AbstractPlatform::prefersIdentityColumns() is deprecated.', ); return false; } /** * Some platforms need the boolean values to be converted. * * The default conversion in this implementation converts to integers (false => 0, true => 1). * * Note: if the input is not a boolean the original input might be returned. * * There are two contexts when converting booleans: Literals and Prepared Statements. * This method should handle the literal case * * @param mixed $item A boolean or an array of them. * * @return mixed A boolean database value or an array of them. */ public function convertBooleans($item) { if (is_array($item)) { foreach ($item as $k => $value) { if (! is_bool($value)) { continue; } $item[$k] = (int) $value; } } elseif (is_bool($item)) { $item = (int) $item; } return $item; } /** * Some platforms have boolean literals that needs to be correctly converted * * The default conversion tries to convert value into bool "(bool)$item" * * @param T $item * * @return (T is null ? null : bool) * * @template T */ public function convertFromBoolean($item) { return $item === null ? null : (bool) $item; } /** * This method should handle the prepared statements case. When there is no * distinction, it's OK to use the same method. * * Note: if the input is not a boolean the original input might be returned. * * @param mixed $item A boolean or an array of them. * * @return mixed A boolean database value or an array of them. */ public function convertBooleansToDatabaseValue($item) { return $this->convertBooleans($item); } /** * Returns the SQL specific for the platform to get the current date. * * @return string */ public function getCurrentDateSQL() { return 'CURRENT_DATE'; } /** * Returns the SQL specific for the platform to get the current time. * * @return string */ public function getCurrentTimeSQL() { return 'CURRENT_TIME'; } /** * Returns the SQL specific for the platform to get the current timestamp * * @return string */ public function getCurrentTimestampSQL() { return 'CURRENT_TIMESTAMP'; } /** * Returns the SQL for a given transaction isolation level Connection constant. * * @param int $level * * @return string * * @throws InvalidArgumentException */ protected function _getTransactionIsolationLevelSQL($level) { switch ($level) { case TransactionIsolationLevel::READ_UNCOMMITTED: return 'READ UNCOMMITTED'; case TransactionIsolationLevel::READ_COMMITTED: return 'READ COMMITTED'; case TransactionIsolationLevel::REPEATABLE_READ: return 'REPEATABLE READ'; case TransactionIsolationLevel::SERIALIZABLE: return 'SERIALIZABLE'; default: throw new InvalidArgumentException('Invalid isolation level:' . $level); } } /** * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. * * @return string * * @throws Exception If not supported on this platform. */ public function getListDatabasesSQL() { throw Exception::notSupported(__METHOD__); } /** * Returns the SQL statement for retrieving the namespaces defined in the database. * * @deprecated Use {@see AbstractSchemaManager::listSchemaNames()} instead. * * @return string * * @throws Exception If not supported on this platform. */ public function getListNamespacesSQL() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4503', 'AbstractPlatform::getListNamespacesSQL() is deprecated,' . ' use AbstractSchemaManager::listSchemaNames() instead.', ); throw Exception::notSupported(__METHOD__); } /** * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. * * @param string $database * * @return string * * @throws Exception If not supported on this platform. */ public function getListSequencesSQL($database) { throw Exception::notSupported(__METHOD__); } /** * @deprecated * * @param string $table * * @return string * * @throws Exception If not supported on this platform. */ public function getListTableConstraintsSQL($table) { throw Exception::notSupported(__METHOD__); } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * @param string $table * @param string $database * * @return string * * @throws Exception If not supported on this platform. */ public function getListTableColumnsSQL($table, $database = null) { throw Exception::notSupported(__METHOD__); } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * @return string * * @throws Exception If not supported on this platform. */ public function getListTablesSQL() { throw Exception::notSupported(__METHOD__); } /** * @deprecated * * @return string * * @throws Exception If not supported on this platform. */ public function getListUsersSQL() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::getListUsersSQL() is deprecated.', ); throw Exception::notSupported(__METHOD__); } /** * Returns the SQL to list all views of a database or user. * * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. * * @param string $database * * @return string * * @throws Exception If not supported on this platform. */ public function getListViewsSQL($database) { throw Exception::notSupported(__METHOD__); } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * Returns the list of indexes for the current database. * * The current database parameter is optional but will always be passed * when using the SchemaManager API and is the database the given table is in. * * Attention: Some platforms only support currentDatabase when they * are connected with that database. Cross-database information schema * requests may be impossible. * * @param string $table * @param string $database * * @return string * * @throws Exception If not supported on this platform. */ public function getListTableIndexesSQL($table, $database = null) { throw Exception::notSupported(__METHOD__); } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * @param string $table * * @return string * * @throws Exception If not supported on this platform. */ public function getListTableForeignKeysSQL($table) { throw Exception::notSupported(__METHOD__); } /** * @param string $name * @param string $sql * * @return string */ public function getCreateViewSQL($name, $sql) { return 'CREATE VIEW ' . $name . ' AS ' . $sql; } /** * @param string $name * * @return string */ public function getDropViewSQL($name) { return 'DROP VIEW ' . $name; } /** * @param string $sequence * * @return string * * @throws Exception If not supported on this platform. */ public function getSequenceNextValSQL($sequence) { throw Exception::notSupported(__METHOD__); } /** * Returns the SQL to create a new database. * * @param string $name The name of the database that should be created. * * @return string * * @throws Exception If not supported on this platform. */ public function getCreateDatabaseSQL($name) { if (! $this->supportsCreateDropDatabase()) { throw Exception::notSupported(__METHOD__); } return 'CREATE DATABASE ' . $name; } /** * Returns the SQL snippet to drop an existing database. * * @param string $name The name of the database that should be dropped. * * @return string */ public function getDropDatabaseSQL($name) { if (! $this->supportsCreateDropDatabase()) { throw Exception::notSupported(__METHOD__); } return 'DROP DATABASE ' . $name; } /** * Returns the SQL to set the transaction isolation level. * * @param int $level * * @return string * * @throws Exception If not supported on this platform. */ public function getSetTransactionIsolationSQL($level) { throw Exception::notSupported(__METHOD__); } /** * Obtains DBMS specific SQL to be used to create datetime columns in * statements like CREATE TABLE. * * @param mixed[] $column * * @return string * * @throws Exception If not supported on this platform. */ public function getDateTimeTypeDeclarationSQL(array $column) { throw Exception::notSupported(__METHOD__); } /** * Obtains DBMS specific SQL to be used to create datetime with timezone offset columns. * * @param mixed[] $column * * @return string */ public function getDateTimeTzTypeDeclarationSQL(array $column) { return $this->getDateTimeTypeDeclarationSQL($column); } /** * Obtains DBMS specific SQL to be used to create date columns in statements * like CREATE TABLE. * * @param mixed[] $column * * @return string * * @throws Exception If not supported on this platform. */ public function getDateTypeDeclarationSQL(array $column) { throw Exception::notSupported(__METHOD__); } /** * Obtains DBMS specific SQL to be used to create time columns in statements * like CREATE TABLE. * * @param mixed[] $column * * @return string * * @throws Exception If not supported on this platform. */ public function getTimeTypeDeclarationSQL(array $column) { throw Exception::notSupported(__METHOD__); } /** * @param mixed[] $column * * @return string */ public function getFloatDeclarationSQL(array $column) { return 'DOUBLE PRECISION'; } /** * Gets the default transaction isolation level of the platform. * * @see TransactionIsolationLevel * * @return TransactionIsolationLevel::* The default isolation level. */ public function getDefaultTransactionIsolationLevel() { return TransactionIsolationLevel::READ_COMMITTED; } /* supports*() methods */ /** * Whether the platform supports sequences. * * @return bool */ public function supportsSequences() { return false; } /** * Whether the platform supports identity columns. * * Identity columns are columns that receive an auto-generated value from the * database on insert of a row. * * @return bool */ public function supportsIdentityColumns() { return false; } /** * Whether the platform emulates identity columns through sequences. * * Some platforms that do not support identity columns natively * but support sequences can emulate identity columns by using * sequences. * * @deprecated * * @return bool */ public function usesSequenceEmulatedIdentityColumns() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5513', '%s is deprecated.', __METHOD__, ); return false; } /** * Returns the name of the sequence for a particular identity column in a particular table. * * @deprecated * * @see usesSequenceEmulatedIdentityColumns * * @param string $tableName The name of the table to return the sequence name for. * @param string $columnName The name of the identity column in the table to return the sequence name for. * * @return string * * @throws Exception If not supported on this platform. */ public function getIdentitySequenceName($tableName, $columnName) { throw Exception::notSupported(__METHOD__); } /** * Whether the platform supports indexes. * * @deprecated * * @return bool */ public function supportsIndexes() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::supportsIndexes() is deprecated.', ); return true; } /** * Whether the platform supports partial indexes. * * @return bool */ public function supportsPartialIndexes() { return false; } /** * Whether the platform supports indexes with column length definitions. */ public function supportsColumnLengthIndexes(): bool { return false; } /** * Whether the platform supports altering tables. * * @deprecated All platforms must implement altering tables. * * @return bool */ public function supportsAlterTable() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::supportsAlterTable() is deprecated. All platforms must implement altering tables.', ); return true; } /** * Whether the platform supports transactions. * * @deprecated * * @return bool */ public function supportsTransactions() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::supportsTransactions() is deprecated.', ); return true; } /** * Whether the platform supports savepoints. * * @return bool */ public function supportsSavepoints() { return true; } /** * Whether the platform supports releasing savepoints. * * @return bool */ public function supportsReleaseSavepoints() { return $this->supportsSavepoints(); } /** * Whether the platform supports primary key constraints. * * @deprecated * * @return bool */ public function supportsPrimaryConstraints() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::supportsPrimaryConstraints() is deprecated.', ); return true; } /** * Whether the platform supports foreign key constraints. * * @deprecated All platforms should support foreign key constraints. * * @return bool */ public function supportsForeignKeyConstraints() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5409', 'AbstractPlatform::supportsForeignKeyConstraints() is deprecated.', ); return true; } /** * Whether the platform supports database schemas. * * @return bool */ public function supportsSchemas() { return false; } /** * Whether this platform can emulate schemas. * * @deprecated * * Platforms that either support or emulate schemas don't automatically * filter a schema for the namespaced elements in {@see AbstractManager::introspectSchema()}. * * @return bool */ public function canEmulateSchemas() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4805', 'AbstractPlatform::canEmulateSchemas() is deprecated.', ); return false; } /** * Returns the default schema name. * * @deprecated * * @return string * * @throws Exception If not supported on this platform. */ public function getDefaultSchemaName() { throw Exception::notSupported(__METHOD__); } /** * Whether this platform supports create database. * * Some databases don't allow to create and drop databases at all or only with certain tools. * * @deprecated * * @return bool */ public function supportsCreateDropDatabase() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5513', '%s is deprecated.', __METHOD__, ); return true; } /** * Whether the platform supports getting the affected rows of a recent update/delete type query. * * @deprecated * * @return bool */ public function supportsGettingAffectedRows() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::supportsGettingAffectedRows() is deprecated.', ); return true; } /** * Whether this platform support to add inline column comments as postfix. * * @return bool */ public function supportsInlineColumnComments() { return false; } /** * Whether this platform support the proprietary syntax "COMMENT ON asset". * * @return bool */ public function supportsCommentOnStatement() { return false; } /** * Does this platform have native guid type. * * @deprecated * * @return bool */ public function hasNativeGuidType() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5509', '%s is deprecated.', __METHOD__, ); return false; } /** * Does this platform have native JSON type. * * @deprecated * * @return bool */ public function hasNativeJsonType() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5509', '%s is deprecated.', __METHOD__, ); return false; } /** * Whether this platform supports views. * * @deprecated All platforms must implement support for views. * * @return bool */ public function supportsViews() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::supportsViews() is deprecated. All platforms must implement support for views.', ); return true; } /** * Does this platform support column collation? * * @return bool */ public function supportsColumnCollation() { return false; } /** * Gets the format string, as accepted by the date() function, that describes * the format of a stored datetime value of this platform. * * @return string The format string. */ public function getDateTimeFormatString() { return 'Y-m-d H:i:s'; } /** * Gets the format string, as accepted by the date() function, that describes * the format of a stored datetime with timezone value of this platform. * * @return string The format string. */ public function getDateTimeTzFormatString() { return 'Y-m-d H:i:s'; } /** * Gets the format string, as accepted by the date() function, that describes * the format of a stored date value of this platform. * * @return string The format string. */ public function getDateFormatString() { return 'Y-m-d'; } /** * Gets the format string, as accepted by the date() function, that describes * the format of a stored time value of this platform. * * @return string The format string. */ public function getTimeFormatString() { return 'H:i:s'; } /** * Adds an driver-specific LIMIT clause to the query. * * @param string $query * @param int|null $limit * @param int $offset * * @throws Exception */ final public function modifyLimitQuery($query, $limit, $offset = 0): string { if ($offset < 0) { throw new Exception(sprintf( 'Offset must be a positive integer or zero, %d given', $offset, )); } if ($offset > 0 && ! $this->supportsLimitOffset()) { throw new Exception(sprintf( 'Platform %s does not support offset values in limit queries.', $this->getName(), )); } if ($limit !== null) { $limit = (int) $limit; } return $this->doModifyLimitQuery($query, $limit, (int) $offset); } /** * Adds an platform-specific LIMIT clause to the query. * * @param string $query * @param int|null $limit * @param int $offset * * @return string */ protected function doModifyLimitQuery($query, $limit, $offset) { if ($limit !== null) { $query .= sprintf(' LIMIT %d', $limit); } if ($offset > 0) { $query .= sprintf(' OFFSET %d', $offset); } return $query; } /** * Whether the database platform support offsets in modify limit clauses. * * @deprecated All platforms must implement support for offsets in modify limit clauses. * * @return bool */ public function supportsLimitOffset() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4724', 'AbstractPlatform::supportsViews() is deprecated.' . ' All platforms must implement support for offsets in modify limit clauses.', ); return true; } /** * Maximum length of any given database identifier, like tables or column names. * * @return int */ public function getMaxIdentifierLength() { return 63; } /** * Returns the insert SQL for an empty insert statement. * * @param string $quotedTableName * @param string $quotedIdentifierColumnName * * @return string */ public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) { return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (null)'; } /** * Generates a Truncate Table SQL statement for a given table. * * Cascade is not supported on many platforms but would optionally cascade the truncate by * following the foreign keys. * * @param string $tableName * @param bool $cascade * * @return string */ public function getTruncateTableSQL($tableName, $cascade = false) { $tableIdentifier = new Identifier($tableName); return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this); } /** * This is for test reasons, many vendors have special requirements for dummy statements. * * @return string */ public function getDummySelectSQL() { $expression = func_num_args() > 0 ? func_get_arg(0) : '1'; return sprintf('SELECT %s', $expression); } /** * Returns the SQL to create a new savepoint. * * @param string $savepoint * * @return string */ public function createSavePoint($savepoint) { return 'SAVEPOINT ' . $savepoint; } /** * Returns the SQL to release a savepoint. * * @param string $savepoint * * @return string */ public function releaseSavePoint($savepoint) { return 'RELEASE SAVEPOINT ' . $savepoint; } /** * Returns the SQL to rollback a savepoint. * * @param string $savepoint * * @return string */ public function rollbackSavePoint($savepoint) { return 'ROLLBACK TO SAVEPOINT ' . $savepoint; } /** * Returns the keyword list instance of this platform. * * @throws Exception If no keyword list is specified. */ final public function getReservedKeywordsList(): KeywordList { // Store the instance so it doesn't need to be generated on every request. return $this->_keywords ??= $this->createReservedKeywordsList(); } /** * Creates an instance of the reserved keyword list of this platform. * * This method will become @abstract in DBAL 4.0.0. * * @throws Exception */ protected function createReservedKeywordsList(): KeywordList { $class = $this->getReservedKeywordsClass(); $keywords = new $class(); if (! $keywords instanceof KeywordList) { throw Exception::notSupported(__METHOD__); } return $keywords; } /** * Returns the class name of the reserved keywords list. * * @deprecated Implement {@see createReservedKeywordsList()} instead. * * @return string * @psalm-return class-string * * @throws Exception If not supported on this platform. */ protected function getReservedKeywordsClass() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4510', 'AbstractPlatform::getReservedKeywordsClass() is deprecated,' . ' use AbstractPlatform::createReservedKeywordsList() instead.', ); throw Exception::notSupported(__METHOD__); } /** * Quotes a literal string. * This method is NOT meant to fix SQL injections! * It is only meant to escape this platform's string literal * quote character inside the given literal string. * * @param string $str The literal string to be quoted. * * @return string The quoted literal string. */ public function quoteStringLiteral($str) { $c = $this->getStringLiteralQuoteCharacter(); return $c . str_replace($c, $c . $c, $str) . $c; } /** * Gets the character used for string literal quoting. * * @deprecated Use {@see quoteStringLiteral()} to quote string literals instead. * * @return string */ public function getStringLiteralQuoteCharacter() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5388', 'AbstractPlatform::getStringLiteralQuoteCharacter() is deprecated.' . ' Use quoteStringLiteral() instead.', ); return "'"; } /** * Escapes metacharacters in a string intended to be used with a LIKE * operator. * * @param string $inputString a literal, unquoted string * @param string $escapeChar should be reused by the caller in the LIKE * expression. */ final public function escapeStringForLike(string $inputString, string $escapeChar): string { return preg_replace( '~([' . preg_quote($this->getLikeWildcardCharacters() . $escapeChar, '~') . '])~u', addcslashes($escapeChar, '\\') . '$1', $inputString, ); } /** * @return array An associative array with the name of the properties * of the column being declared as array indexes. */ private function columnToArray(Column $column): array { $name = $column->getQuotedName($this); return array_merge($column->toArray(), [ 'name' => $name, 'version' => $column->hasPlatformOption('version') ? $column->getPlatformOption('version') : false, 'comment' => $this->getColumnComment($column), ]); } /** @internal */ public function createSQLParser(): Parser { return new Parser(false); } protected function getLikeWildcardCharacters(): string { return '%_'; } /** * Compares the definitions of the given columns in the context of this platform. * * @throws Exception */ public function columnsEqual(Column $column1, Column $column2): bool { $column1Array = $this->columnToArray($column1); $column2Array = $this->columnToArray($column2); // ignore explicit columnDefinition since it's not set on the Column generated by the SchemaManager unset($column1Array['columnDefinition']); unset($column2Array['columnDefinition']); if ( $this->getColumnDeclarationSQL('', $column1Array) !== $this->getColumnDeclarationSQL('', $column2Array) ) { return false; } if (! $this->columnDeclarationsMatch($column1, $column2)) { return false; } // If the platform supports inline comments, all comparison is already done above if ($this->supportsInlineColumnComments()) { return true; } if ($column1->getComment() !== $column2->getComment()) { return false; } return $column1->getType() === $column2->getType(); } /** * Whether the database data type matches that expected for the doctrine type for the given colunms. */ private function columnDeclarationsMatch(Column $column1, Column $column2): bool { return ! ( $column1->hasPlatformOption('declarationMismatch') || $column2->hasPlatformOption('declarationMismatch') ); } /** * Creates the schema manager that can be used to inspect and change the underlying * database schema according to the dialect of the platform. * * @throws Exception * * @abstract */ public function createSchemaManager(Connection $connection): AbstractSchemaManager { throw Exception::notSupported(__METHOD__); } } PK!O++%dbal/src/Platforms/OraclePlatform.phpnu[multiplyInterval((string) $interval, 3); break; case DateIntervalUnit::YEAR: $interval = $this->multiplyInterval((string) $interval, 12); break; } return 'ADD_MONTHS(' . $date . ', ' . $operator . $interval . ')'; default: $calculationClause = ''; switch ($unit) { case DateIntervalUnit::SECOND: $calculationClause = '/24/60/60'; break; case DateIntervalUnit::MINUTE: $calculationClause = '/24/60'; break; case DateIntervalUnit::HOUR: $calculationClause = '/24'; break; case DateIntervalUnit::WEEK: $calculationClause = '*7'; break; } return '(' . $date . $operator . $interval . $calculationClause . ')'; } } /** * {@inheritDoc} */ public function getDateDiffExpression($date1, $date2) { return sprintf('TRUNC(%s) - TRUNC(%s)', $date1, $date2); } /** * {@inheritDoc} */ public function getBitAndComparisonExpression($value1, $value2) { return 'BITAND(' . $value1 . ', ' . $value2 . ')'; } public function getCurrentDatabaseExpression(): string { return "SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA')"; } /** * {@inheritDoc} */ public function getBitOrComparisonExpression($value1, $value2) { return '(' . $value1 . '-' . $this->getBitAndComparisonExpression($value1, $value2) . '+' . $value2 . ')'; } /** * {@inheritDoc} */ public function getCreatePrimaryKeySQL(Index $index, $table): string { if ($table instanceof Table) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); $table = $table->getQuotedName($this); } return 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $index->getQuotedName($this) . ' PRIMARY KEY (' . $this->getIndexFieldDeclarationListSQL($index) . ')'; } /** * {@inheritDoc} * * Need to specifiy minvalue, since start with is hidden in the system and MINVALUE <= START WITH. * Therefore we can use MINVALUE to be able to get a hint what START WITH was for later introspection * in {@see listSequences()} */ public function getCreateSequenceSQL(Sequence $sequence) { return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . ' START WITH ' . $sequence->getInitialValue() . ' MINVALUE ' . $sequence->getInitialValue() . ' INCREMENT BY ' . $sequence->getAllocationSize() . $this->getSequenceCacheSQL($sequence); } /** * {@inheritDoc} */ public function getAlterSequenceSQL(Sequence $sequence) { return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . ' INCREMENT BY ' . $sequence->getAllocationSize() . $this->getSequenceCacheSQL($sequence); } /** * Cache definition for sequences */ private function getSequenceCacheSQL(Sequence $sequence): string { if ($sequence->getCache() === 0) { return ' NOCACHE'; } if ($sequence->getCache() === 1) { return ' NOCACHE'; } if ($sequence->getCache() > 1) { return ' CACHE ' . $sequence->getCache(); } return ''; } /** * {@inheritDoc} */ public function getSequenceNextValSQL($sequence) { return 'SELECT ' . $sequence . '.nextval FROM DUAL'; } /** * {@inheritDoc} */ public function getSetTransactionIsolationSQL($level) { return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); } /** * {@inheritDoc} */ protected function _getTransactionIsolationLevelSQL($level) { switch ($level) { case TransactionIsolationLevel::READ_UNCOMMITTED: return 'READ UNCOMMITTED'; case TransactionIsolationLevel::READ_COMMITTED: return 'READ COMMITTED'; case TransactionIsolationLevel::REPEATABLE_READ: case TransactionIsolationLevel::SERIALIZABLE: return 'SERIALIZABLE'; default: return parent::_getTransactionIsolationLevelSQL($level); } } /** * {@inheritDoc} */ public function getBooleanTypeDeclarationSQL(array $column) { return 'NUMBER(1)'; } /** * {@inheritDoc} */ public function getIntegerTypeDeclarationSQL(array $column) { return 'NUMBER(10)'; } /** * {@inheritDoc} */ public function getBigIntTypeDeclarationSQL(array $column) { return 'NUMBER(20)'; } /** * {@inheritDoc} */ public function getSmallIntTypeDeclarationSQL(array $column) { return 'NUMBER(5)'; } /** * {@inheritDoc} */ public function getDateTimeTypeDeclarationSQL(array $column) { return 'TIMESTAMP(0)'; } /** * {@inheritDoc} */ public function getDateTimeTzTypeDeclarationSQL(array $column) { return 'TIMESTAMP(0) WITH TIME ZONE'; } /** * {@inheritDoc} */ public function getDateTypeDeclarationSQL(array $column) { return 'DATE'; } /** * {@inheritDoc} */ public function getTimeTypeDeclarationSQL(array $column) { return 'DATE'; } /** * {@inheritDoc} */ protected function _getCommonIntegerTypeDeclarationSQL(array $column) { return ''; } /** * {@inheritDoc} */ protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) { if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'Relying on the default string column length on Oracle is deprecated' . ', specify the length explicitly.', ); } return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(2000)') : ($length > 0 ? 'VARCHAR2(' . $length . ')' : 'VARCHAR2(4000)'); } /** * {@inheritDoc} */ protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) { if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'Relying on the default binary column length on Oracle is deprecated' . ', specify the length explicitly.', ); } return 'RAW(' . ($length > 0 ? $length : $this->getBinaryMaxLength()) . ')'; } /** * {@inheritDoc} * * @deprecated */ public function getBinaryMaxLength() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'OraclePlatform::getBinaryMaxLength() is deprecated.', ); return 2000; } /** * {@inheritDoc} */ public function getClobTypeDeclarationSQL(array $column) { return 'CLOB'; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListDatabasesSQL() { return 'SELECT username FROM all_users'; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListSequencesSQL($database) { $database = $this->normalizeIdentifier($database); $database = $this->quoteStringLiteral($database->getName()); return 'SELECT sequence_name, min_value, increment_by FROM sys.all_sequences ' . 'WHERE SEQUENCE_OWNER = ' . $database; } /** * {@inheritDoc} */ protected function _getCreateTableSQL($name, array $columns, array $options = []) { $indexes = $options['indexes'] ?? []; $options['indexes'] = []; $sql = parent::_getCreateTableSQL($name, $columns, $options); foreach ($columns as $columnName => $column) { if (isset($column['sequence'])) { $sql[] = $this->getCreateSequenceSQL($column['sequence']); } if ( ! isset($column['autoincrement']) || ! $column['autoincrement'] && (! isset($column['autoinc']) || ! $column['autoinc']) ) { continue; } $sql = array_merge($sql, $this->getCreateAutoincrementSql($columnName, $name)); } foreach ($indexes as $index) { $sql[] = $this->getCreateIndexSQL($index, $name); } return $sql; } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * {@inheritDoc} * * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaOracleReader.html */ public function getListTableIndexesSQL($table, $database = null) { $table = $this->normalizeIdentifier($table); $table = $this->quoteStringLiteral($table->getName()); return "SELECT uind_col.index_name AS name, ( SELECT uind.index_type FROM user_indexes uind WHERE uind.index_name = uind_col.index_name ) AS type, decode( ( SELECT uind.uniqueness FROM user_indexes uind WHERE uind.index_name = uind_col.index_name ), 'NONUNIQUE', 0, 'UNIQUE', 1 ) AS is_unique, uind_col.column_name AS column_name, uind_col.column_position AS column_pos, ( SELECT ucon.constraint_type FROM user_constraints ucon WHERE ucon.index_name = uind_col.index_name AND ucon.table_name = uind_col.table_name ) AS is_primary FROM user_ind_columns uind_col WHERE uind_col.table_name = " . $table . ' ORDER BY uind_col.column_position ASC'; } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * {@inheritDoc} */ public function getListTablesSQL() { return 'SELECT * FROM sys.user_tables'; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListViewsSQL($database) { return 'SELECT view_name, text FROM sys.user_views'; } /** * @internal The method should be only used from within the OraclePlatform class hierarchy. * * @param string $name * @param string $table * @param int $start * * @return string[] */ public function getCreateAutoincrementSql($name, $table, $start = 1) { $tableIdentifier = $this->normalizeIdentifier($table); $quotedTableName = $tableIdentifier->getQuotedName($this); $unquotedTableName = $tableIdentifier->getName(); $nameIdentifier = $this->normalizeIdentifier($name); $quotedName = $nameIdentifier->getQuotedName($this); $unquotedName = $nameIdentifier->getName(); $sql = []; $autoincrementIdentifierName = $this->getAutoincrementIdentifierName($tableIdentifier); $idx = new Index($autoincrementIdentifierName, [$quotedName], true, true); $sql[] = "DECLARE constraints_Count NUMBER; BEGIN SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count FROM USER_CONSTRAINTS WHERE TABLE_NAME = '" . $unquotedTableName . "' AND CONSTRAINT_TYPE = 'P'; IF constraints_Count = 0 OR constraints_Count = '' THEN EXECUTE IMMEDIATE '" . $this->getCreateConstraintSQL($idx, $quotedTableName) . "'; END IF; END;"; $sequenceName = $this->getIdentitySequenceName( $tableIdentifier->isQuoted() ? $quotedTableName : $unquotedTableName, $nameIdentifier->isQuoted() ? $quotedName : $unquotedName, ); $sequence = new Sequence($sequenceName, $start); $sql[] = $this->getCreateSequenceSQL($sequence); $sql[] = 'CREATE TRIGGER ' . $autoincrementIdentifierName . ' BEFORE INSERT ON ' . $quotedTableName . ' FOR EACH ROW DECLARE last_Sequence NUMBER; last_InsertID NUMBER; BEGIN IF (:NEW.' . $quotedName . ' IS NULL OR :NEW.' . $quotedName . ' = 0) THEN SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $quotedName . ' FROM DUAL; ELSE SELECT NVL(Last_Number, 0) INTO last_Sequence FROM User_Sequences WHERE Sequence_Name = \'' . $sequence->getName() . '\'; SELECT :NEW.' . $quotedName . ' INTO last_InsertID FROM DUAL; WHILE (last_InsertID > last_Sequence) LOOP SELECT ' . $sequenceName . '.NEXTVAL INTO last_Sequence FROM DUAL; END LOOP; SELECT ' . $sequenceName . '.NEXTVAL INTO last_Sequence FROM DUAL; END IF; END;'; return $sql; } /** * @internal The method should be only used from within the OracleSchemaManager class hierarchy. * * Returns the SQL statements to drop the autoincrement for the given table name. * * @param string $table The table name to drop the autoincrement for. * * @return string[] */ public function getDropAutoincrementSql($table) { $table = $this->normalizeIdentifier($table); $autoincrementIdentifierName = $this->getAutoincrementIdentifierName($table); $identitySequenceName = $this->getIdentitySequenceName( $table->isQuoted() ? $table->getQuotedName($this) : $table->getName(), '', ); return [ 'DROP TRIGGER ' . $autoincrementIdentifierName, $this->getDropSequenceSQL($identitySequenceName), $this->getDropConstraintSQL($autoincrementIdentifierName, $table->getQuotedName($this)), ]; } /** * Normalizes the given identifier. * * Uppercases the given identifier if it is not quoted by intention * to reflect Oracle's internal auto uppercasing strategy of unquoted identifiers. * * @param string $name The identifier to normalize. */ private function normalizeIdentifier($name): Identifier { $identifier = new Identifier($name); return $identifier->isQuoted() ? $identifier : new Identifier(strtoupper($name)); } /** * Adds suffix to identifier, * * if the new string exceeds max identifier length, * keeps $suffix, cuts from $identifier as much as the part exceeding. */ private function addSuffix(string $identifier, string $suffix): string { $maxPossibleLengthWithoutSuffix = $this->getMaxIdentifierLength() - strlen($suffix); if (strlen($identifier) > $maxPossibleLengthWithoutSuffix) { $identifier = substr($identifier, 0, $maxPossibleLengthWithoutSuffix); } return $identifier . $suffix; } /** * Returns the autoincrement primary key identifier name for the given table identifier. * * Quotes the autoincrement primary key identifier name * if the given table name is quoted by intention. */ private function getAutoincrementIdentifierName(Identifier $table): string { $identifierName = $this->addSuffix($table->getName(), '_AI_PK'); return $table->isQuoted() ? $this->quoteSingleIdentifier($identifierName) : $identifierName; } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * {@inheritDoc} */ public function getListTableForeignKeysSQL($table) { $table = $this->normalizeIdentifier($table); $table = $this->quoteStringLiteral($table->getName()); return "SELECT alc.constraint_name, alc.DELETE_RULE, cols.column_name \"local_column\", cols.position, ( SELECT r_cols.table_name FROM user_cons_columns r_cols WHERE alc.r_constraint_name = r_cols.constraint_name AND r_cols.position = cols.position ) AS \"references_table\", ( SELECT r_cols.column_name FROM user_cons_columns r_cols WHERE alc.r_constraint_name = r_cols.constraint_name AND r_cols.position = cols.position ) AS \"foreign_column\" FROM user_cons_columns cols JOIN user_constraints alc ON alc.constraint_name = cols.constraint_name AND alc.constraint_type = 'R' AND alc.table_name = " . $table . ' ORDER BY cols.constraint_name ASC, cols.position ASC'; } /** * @deprecated * * {@inheritDoc} */ public function getListTableConstraintsSQL($table) { $table = $this->normalizeIdentifier($table); $table = $this->quoteStringLiteral($table->getName()); return 'SELECT * FROM user_constraints WHERE table_name = ' . $table; } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * {@inheritDoc} */ public function getListTableColumnsSQL($table, $database = null) { $table = $this->normalizeIdentifier($table); $table = $this->quoteStringLiteral($table->getName()); $tabColumnsTableName = 'user_tab_columns'; $colCommentsTableName = 'user_col_comments'; $tabColumnsOwnerCondition = ''; $colCommentsOwnerCondition = ''; if ($database !== null && $database !== '/') { $database = $this->normalizeIdentifier($database); $database = $this->quoteStringLiteral($database->getName()); $tabColumnsTableName = 'all_tab_columns'; $colCommentsTableName = 'all_col_comments'; $tabColumnsOwnerCondition = ' AND c.owner = ' . $database; $colCommentsOwnerCondition = ' AND d.OWNER = c.OWNER'; } return sprintf( <<<'SQL' SELECT c.*, ( SELECT d.comments FROM %s d WHERE d.TABLE_NAME = c.TABLE_NAME%s AND d.COLUMN_NAME = c.COLUMN_NAME ) AS comments FROM %s c WHERE c.table_name = %s%s ORDER BY c.column_id SQL , $colCommentsTableName, $colCommentsOwnerCondition, $tabColumnsTableName, $table, $tabColumnsOwnerCondition, ); } /** * {@inheritDoc} */ public function getDropForeignKeySQL($foreignKey, $table) { if ($foreignKey instanceof ForeignKeyConstraint) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $foreignKey as a ForeignKeyConstraint object to %s is deprecated.' . ' Pass it as a quoted name instead.', __METHOD__, ); } else { $foreignKey = new Identifier($foreignKey); } if ($table instanceof Table) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); } else { $table = new Identifier($table); } $foreignKey = $foreignKey->getQuotedName($this); $table = $table->getQuotedName($this); return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) { $referentialAction = ''; if ($foreignKey->hasOption('onDelete')) { $referentialAction = $this->getForeignKeyReferentialActionSQL($foreignKey->getOption('onDelete')); } if ($referentialAction !== '') { return ' ON DELETE ' . $referentialAction; } return ''; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getForeignKeyReferentialActionSQL($action) { $action = strtoupper($action); switch ($action) { case 'RESTRICT': // RESTRICT is not supported, therefore falling back to NO ACTION. case 'NO ACTION': // NO ACTION cannot be declared explicitly, // therefore returning empty string to indicate to OMIT the referential clause. return ''; case 'CASCADE': case 'SET NULL': return $action; default: // SET DEFAULT is not supported, throw exception instead. throw new InvalidArgumentException('Invalid foreign key action: ' . $action); } } /** * {@inheritDoc} */ public function getCreateDatabaseSQL($name) { return 'CREATE USER ' . $name; } /** * {@inheritDoc} */ public function getDropDatabaseSQL($name) { return 'DROP USER ' . $name . ' CASCADE'; } /** * {@inheritDoc} */ public function getAlterTableSQL(TableDiff $diff) { $sql = []; $commentsSQL = []; $columnSql = []; $fields = []; $tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this); foreach ($diff->getAddedColumns() as $column) { if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { continue; } $fields[] = $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray()); $comment = $this->getColumnComment($column); if ($comment === null || $comment === '') { continue; } $commentsSQL[] = $this->getCommentOnColumnSQL( $tableNameSQL, $column->getQuotedName($this), $comment, ); } if (count($fields) > 0) { $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ADD (' . implode(', ', $fields) . ')'; } $fields = []; foreach ($diff->getModifiedColumns() as $columnDiff) { if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { continue; } $newColumn = $columnDiff->getNewColumn(); // Do not generate column alteration clause if type is binary and only fixed property has changed. // Oracle only supports binary type columns with variable length. // Avoids unnecessary table alteration statements. if ( $newColumn->getType() instanceof BinaryType && $columnDiff->hasFixedChanged() && count($columnDiff->changedProperties) === 1 ) { continue; } $columnHasChangedComment = $columnDiff->hasCommentChanged(); /** * Do not add query part if only comment has changed */ if (! ($columnHasChangedComment && count($columnDiff->changedProperties) === 1)) { $newColumnProperties = $newColumn->toArray(); if (! $columnDiff->hasNotNullChanged()) { unset($newColumnProperties['notnull']); } $fields[] = $newColumn->getQuotedName($this) . $this->getColumnDeclarationSQL('', $newColumnProperties); } if (! $columnHasChangedComment) { continue; } $commentsSQL[] = $this->getCommentOnColumnSQL( $tableNameSQL, $newColumn->getQuotedName($this), $this->getColumnComment($newColumn), ); } if (count($fields) > 0) { $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' MODIFY (' . implode(', ', $fields) . ')'; } foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { continue; } $oldColumnName = new Identifier($oldColumnName); $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this); } $fields = []; foreach ($diff->getDroppedColumns() as $column) { if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { continue; } $fields[] = $column->getQuotedName($this); } if (count($fields) > 0) { $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' DROP (' . implode(', ', $fields) . ')'; } $tableSql = []; if (! $this->onSchemaAlterTable($diff, $tableSql)) { $sql = array_merge($sql, $commentsSQL); $newName = $diff->getNewName(); if ($newName !== false) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5663', 'Generation of "rename table" SQL using %s is deprecated. Use getRenameTableSQL() instead.', __METHOD__, ); $sql[] = sprintf( 'ALTER TABLE %s RENAME TO %s', $tableNameSQL, $newName->getQuotedName($this), ); } $sql = array_merge( $this->getPreAlterTableIndexForeignKeySQL($diff), $sql, $this->getPostAlterTableIndexForeignKeySQL($diff), ); } return array_merge($sql, $tableSql, $columnSql); } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getColumnDeclarationSQL($name, array $column) { if (isset($column['columnDefinition'])) { $columnDef = $this->getCustomTypeDeclarationSQL($column); } else { $default = $this->getDefaultValueDeclarationSQL($column); $notnull = ''; if (isset($column['notnull'])) { $notnull = $column['notnull'] ? ' NOT NULL' : ' NULL'; } if (! empty($column['unique'])) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5656', 'The usage of the "unique" column property is deprecated. Use unique constraints instead.', ); $unique = ' ' . $this->getUniqueFieldDeclarationSQL(); } else { $unique = ''; } if (! empty($column['check'])) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5656', 'The usage of the "check" column property is deprecated.', ); $check = ' ' . $column['check']; } else { $check = ''; } $typeDecl = $column['type']->getSQLDeclaration($column, $this); $columnDef = $typeDecl . $default . $notnull . $unique . $check; } return $name . ' ' . $columnDef; } /** * {@inheritDoc} */ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) { if (strpos($tableName, '.') !== false) { [$schema] = explode('.', $tableName); $oldIndexName = $schema . '.' . $oldIndexName; } return ['ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)]; } /** * {@inheritDoc} * * @deprecated */ public function usesSequenceEmulatedIdentityColumns() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5513', '%s is deprecated.', __METHOD__, ); return true; } /** * {@inheritDoc} * * @internal The method should be only used from within the OraclePlatform class hierarchy. */ public function getIdentitySequenceName($tableName, $columnName) { $table = new Identifier($tableName); // No usage of column name to preserve BC compatibility with <2.5 $identitySequenceName = $this->addSuffix($table->getName(), '_SEQ'); if ($table->isQuoted()) { $identitySequenceName = '"' . $identitySequenceName . '"'; } $identitySequenceIdentifier = $this->normalizeIdentifier($identitySequenceName); return $identitySequenceIdentifier->getQuotedName($this); } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsCommentOnStatement() { return true; } /** * {@inheritDoc} */ public function getName() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4749', 'OraclePlatform::getName() is deprecated. Identify platforms by their class.', ); return 'oracle'; } /** * {@inheritDoc} */ protected function doModifyLimitQuery($query, $limit, $offset) { if ($limit === null && $offset <= 0) { return $query; } if (preg_match('/^\s*SELECT/i', $query) === 1) { if (preg_match('/\sFROM\s/i', $query) === 0) { $query .= ' FROM dual'; } $columns = ['a.*']; if ($offset > 0) { $columns[] = 'ROWNUM AS doctrine_rownum'; } $query = sprintf('SELECT %s FROM (%s) a', implode(', ', $columns), $query); if ($limit !== null) { $query .= sprintf(' WHERE ROWNUM <= %d', $offset + $limit); } if ($offset > 0) { $query = sprintf('SELECT * FROM (%s) WHERE doctrine_rownum >= %d', $query, $offset + 1); } } return $query; } /** * {@inheritDoc} */ public function getCreateTemporaryTableSnippetSQL() { return 'CREATE GLOBAL TEMPORARY TABLE'; } /** * {@inheritDoc} */ public function getDateTimeTzFormatString() { return 'Y-m-d H:i:sP'; } /** * {@inheritDoc} */ public function getDateFormatString() { return 'Y-m-d 00:00:00'; } /** * {@inheritDoc} */ public function getTimeFormatString() { return '1900-01-01 H:i:s'; } /** * {@inheritDoc} */ public function getMaxIdentifierLength() { return 30; } /** * {@inheritDoc} */ public function supportsSequences() { return true; } /** * {@inheritDoc} */ public function supportsReleaseSavepoints() { return false; } /** * {@inheritDoc} */ public function getTruncateTableSQL($tableName, $cascade = false) { $tableIdentifier = new Identifier($tableName); return 'TRUNCATE TABLE ' . $tableIdentifier->getQuotedName($this); } /** * {@inheritDoc} */ public function getDummySelectSQL() { $expression = func_num_args() > 0 ? func_get_arg(0) : '1'; return sprintf('SELECT %s FROM DUAL', $expression); } /** * {@inheritDoc} */ protected function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ 'binary_double' => Types::FLOAT, 'binary_float' => Types::FLOAT, 'binary_integer' => Types::BOOLEAN, 'blob' => Types::BLOB, 'char' => Types::STRING, 'clob' => Types::TEXT, 'date' => Types::DATE_MUTABLE, 'float' => Types::FLOAT, 'integer' => Types::INTEGER, 'long' => Types::STRING, 'long raw' => Types::BLOB, 'nchar' => Types::STRING, 'nclob' => Types::TEXT, 'number' => Types::INTEGER, 'nvarchar2' => Types::STRING, 'pls_integer' => Types::BOOLEAN, 'raw' => Types::BINARY, 'rowid' => Types::STRING, 'timestamp' => Types::DATETIME_MUTABLE, 'timestamptz' => Types::DATETIMETZ_MUTABLE, 'urowid' => Types::STRING, 'varchar' => Types::STRING, 'varchar2' => Types::STRING, ]; } /** * {@inheritDoc} */ public function releaseSavePoint($savepoint) { return ''; } /** * {@inheritDoc} * * @deprecated Implement {@see createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4510', 'OraclePlatform::getReservedKeywordsClass() is deprecated,' . ' use OraclePlatform::createReservedKeywordsList() instead.', ); return Keywords\OracleKeywords::class; } /** * {@inheritDoc} */ public function getBlobTypeDeclarationSQL(array $column) { return 'BLOB'; } /** @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. */ public function getListTableCommentsSQL(string $table, ?string $database = null): string { $tableCommentsName = 'user_tab_comments'; $ownerCondition = ''; if ($database !== null && $database !== '/') { $tableCommentsName = 'all_tab_comments'; $ownerCondition = ' AND owner = ' . $this->quoteStringLiteral( $this->normalizeIdentifier($database)->getName(), ); } return sprintf( <<<'SQL' SELECT comments FROM %s WHERE table_name = %s%s SQL , $tableCommentsName, $this->quoteStringLiteral($this->normalizeIdentifier($table)->getName()), $ownerCondition, ); } public function createSchemaManager(Connection $connection): OracleSchemaManager { return new OracleSchemaManager($connection, $this); } } PK!!"ZZ%dbal/src/Platforms/DB2111Platform.phpnu[ 0) { $query .= sprintf(' OFFSET %u ROWS', $offset); } if ($limit !== null) { if ($limit < 0) { throw new Exception(sprintf('Limit must be a positive integer or zero, %d given', $limit)); } $query .= sprintf(' FETCH %s %u ROWS ONLY', $offset === 0 ? 'FIRST' : 'NEXT', $limit); } return $query; } } PK!'dbal/src/Platforms/DateIntervalUnit.phpnu[platform = $platform; } public function buildSQL(SelectQuery $query): string { $parts = ['SELECT']; if ($query->isDistinct()) { $parts[] = 'DISTINCT'; } $parts[] = implode(', ', $query->getColumns()); $from = $query->getFrom(); if (count($from) > 0) { $parts[] = 'FROM ' . implode(', ', $from); } $forUpdate = $query->getForUpdate(); if ($forUpdate !== null) { $with = ['UPDLOCK', 'ROWLOCK']; if ($forUpdate->getConflictResolutionMode() === ConflictResolutionMode::SKIP_LOCKED) { $with[] = 'READPAST'; } $parts[] = 'WITH (' . implode(', ', $with) . ')'; } $where = $query->getWhere(); if ($where !== null) { $parts[] = 'WHERE ' . $where; } $groupBy = $query->getGroupBy(); if (count($groupBy) > 0) { $parts[] = 'GROUP BY ' . implode(', ', $groupBy); } $having = $query->getHaving(); if ($having !== null) { $parts[] = 'HAVING ' . $having; } $orderBy = $query->getOrderBy(); if (count($orderBy) > 0) { $parts[] = 'ORDER BY ' . implode(', ', $orderBy); } $sql = implode(' ', $parts); $limit = $query->getLimit(); if ($limit->isDefined()) { $sql = $this->platform->modifyLimitQuery($sql, $limit->getMaxResults(), $limit->getFirstResult()); } return $sql; } } PK![+dbal/src/Platforms/SQLServer/Comparator.phpnu[databaseCollation = $databaseCollation; } public function compareTables(Table $fromTable, Table $toTable): TableDiff { return parent::compareTables( $this->normalizeColumns($fromTable), $this->normalizeColumns($toTable), ); } /** * {@inheritDoc} */ public function diffTable(Table $fromTable, Table $toTable) { return parent::diffTable( $this->normalizeColumns($fromTable), $this->normalizeColumns($toTable), ); } private function normalizeColumns(Table $table): Table { $table = clone $table; foreach ($table->getColumns() as $column) { $options = $column->getPlatformOptions(); if (! isset($options['collation']) || $options['collation'] !== $this->databaseCollation) { continue; } unset($options['collation']); $column->setPlatformOptions($options); } return $table; } } PK!9Zdbal/src/Platforms/MySQL/CollationMetadataProvider/ConnectionCollationMetadataProvider.phpnu[connection = $connection; } /** @throws Exception */ public function getCollationCharset(string $collation): ?string { $charset = $this->connection->fetchOne( <<<'SQL' SELECT CHARACTER_SET_NAME FROM information_schema.COLLATIONS WHERE COLLATION_NAME = ?; SQL , [$collation], ); if ($charset !== false) { return $charset; } return null; } } PK!lWdbal/src/Platforms/MySQL/CollationMetadataProvider/CachingCollationMetadataProvider.phpnu[ */ private $cache = []; public function __construct(CollationMetadataProvider $collationMetadataProvider) { $this->collationMetadataProvider = $collationMetadataProvider; } public function getCollationCharset(string $collation): ?string { if (array_key_exists($collation, $this->cache)) { return $this->cache[$collation]; } return $this->cache[$collation] = $this->collationMetadataProvider->getCollationCharset($collation); } } PK!66dbal/src/Platforms/MySQL/CollationMetadataProvider.phpnu[collationMetadataProvider = $collationMetadataProvider; } public function compareTables(Table $fromTable, Table $toTable): TableDiff { return parent::compareTables( $this->normalizeColumns($fromTable), $this->normalizeColumns($toTable), ); } /** * {@inheritDoc} */ public function diffTable(Table $fromTable, Table $toTable) { return parent::diffTable( $this->normalizeColumns($fromTable), $this->normalizeColumns($toTable), ); } private function normalizeColumns(Table $table): Table { $tableOptions = array_intersect_key($table->getOptions(), [ 'charset' => null, 'collation' => null, ]); $table = clone $table; foreach ($table->getColumns() as $column) { $originalOptions = $column->getPlatformOptions(); $normalizedOptions = $this->normalizeOptions($originalOptions); $overrideOptions = array_diff_assoc($normalizedOptions, $tableOptions); if ($overrideOptions === $originalOptions) { continue; } $column->setPlatformOptions($overrideOptions); } return $table; } /** * @param array $options * * @return array */ private function normalizeOptions(array $options): array { if (isset($options['collation']) && ! isset($options['charset'])) { $charset = $this->collationMetadataProvider->getCollationCharset($options['collation']); if ($charset !== null) { $options['charset'] = $charset; } } return $options; } } PK!֔q&dbal/src/Platforms/MySQL80Platform.phpnu[ [ 't', 'true', 'y', 'yes', 'on', '1', ], 'false' => [ 'f', 'false', 'n', 'no', 'off', '0', ], ]; /** * PostgreSQL has different behavior with some drivers * with regard to how booleans have to be handled. * * Enables use of 'true'/'false' or otherwise 1 and 0 instead. * * @param bool $flag * * @return void */ public function setUseBooleanTrueFalseStrings($flag) { $this->useBooleanTrueFalseStrings = (bool) $flag; } /** * {@inheritDoc} */ public function getSubstringExpression($string, $start, $length = null) { if ($length === null) { return 'SUBSTRING(' . $string . ' FROM ' . $start . ')'; } return 'SUBSTRING(' . $string . ' FROM ' . $start . ' FOR ' . $length . ')'; } /** * {@inheritDoc} * * @deprecated Generate dates within the application. */ public function getNowExpression() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4753', 'PostgreSQLPlatform::getNowExpression() is deprecated. Generate dates within the application.', ); return 'LOCALTIMESTAMP(0)'; } /** * {@inheritDoc} */ public function getRegexpExpression() { return 'SIMILAR TO'; } /** * {@inheritDoc} */ public function getLocateExpression($str, $substr, $startPos = false) { if ($startPos !== false) { $str = $this->getSubstringExpression($str, $startPos); return 'CASE WHEN (POSITION(' . $substr . ' IN ' . $str . ') = 0) THEN 0' . ' ELSE (POSITION(' . $substr . ' IN ' . $str . ') + ' . $startPos . ' - 1) END'; } return 'POSITION(' . $substr . ' IN ' . $str . ')'; } /** * {@inheritDoc} */ protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) { if ($unit === DateIntervalUnit::QUARTER) { $interval = $this->multiplyInterval((string) $interval, 3); $unit = DateIntervalUnit::MONTH; } return '(' . $date . ' ' . $operator . ' (' . $interval . " || ' " . $unit . "')::interval)"; } /** * {@inheritDoc} */ public function getDateDiffExpression($date1, $date2) { return '(DATE(' . $date1 . ')-DATE(' . $date2 . '))'; } public function getCurrentDatabaseExpression(): string { return 'CURRENT_DATABASE()'; } /** * {@inheritDoc} */ public function supportsSequences() { return true; } /** * {@inheritDoc} */ public function supportsSchemas() { return true; } /** * {@inheritDoc} * * @deprecated */ public function getDefaultSchemaName() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5513', '%s is deprecated.', __METHOD__, ); return 'public'; } /** * {@inheritDoc} */ public function supportsIdentityColumns() { return true; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsPartialIndexes() { return true; } /** * {@inheritDoc} * * @deprecated */ public function usesSequenceEmulatedIdentityColumns() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5513', '%s is deprecated.', __METHOD__, ); return true; } /** * {@inheritDoc} * * @deprecated */ public function getIdentitySequenceName($tableName, $columnName) { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5513', '%s is deprecated.', __METHOD__, ); return $tableName . '_' . $columnName . '_seq'; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsCommentOnStatement() { return true; } /** * {@inheritDoc} * * @deprecated */ public function hasNativeGuidType() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5509', '%s is deprecated.', __METHOD__, ); return true; } public function createSelectSQLBuilder(): SelectSQLBuilder { return new DefaultSelectSQLBuilder($this, 'FOR UPDATE', null); } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListDatabasesSQL() { return 'SELECT datname FROM pg_database'; } /** * {@inheritDoc} * * @deprecated Use {@see PostgreSQLSchemaManager::listSchemaNames()} instead. */ public function getListNamespacesSQL() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4503', 'PostgreSQLPlatform::getListNamespacesSQL() is deprecated,' . ' use PostgreSQLSchemaManager::listSchemaNames() instead.', ); return "SELECT schema_name AS nspname FROM information_schema.schemata WHERE schema_name NOT LIKE 'pg\_%' AND schema_name != 'information_schema'"; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListSequencesSQL($database) { return 'SELECT sequence_name AS relname, sequence_schema AS schemaname, minimum_value AS min_value, increment AS increment_by FROM information_schema.sequences WHERE sequence_catalog = ' . $this->quoteStringLiteral($database) . " AND sequence_schema NOT LIKE 'pg\_%' AND sequence_schema != 'information_schema'"; } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * {@inheritDoc} */ public function getListTablesSQL() { return "SELECT quote_ident(table_name) AS table_name, table_schema AS schema_name FROM information_schema.tables WHERE table_schema NOT LIKE 'pg\_%' AND table_schema != 'information_schema' AND table_name != 'geometry_columns' AND table_name != 'spatial_ref_sys' AND table_type != 'VIEW'"; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListViewsSQL($database) { return 'SELECT quote_ident(table_name) AS viewname, table_schema AS schemaname, view_definition AS definition FROM information_schema.views WHERE view_definition IS NOT NULL'; } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * @param string $table * @param string|null $database * * @return string */ public function getListTableForeignKeysSQL($table, $database = null) { return 'SELECT quote_ident(r.conname) as conname, pg_catalog.pg_get_constraintdef(r.oid, true) as condef FROM pg_catalog.pg_constraint r WHERE r.conrelid = ( SELECT c.oid FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n WHERE ' . $this->getTableWhereClause($table) . " AND n.oid = c.relnamespace ) AND r.contype = 'f'"; } /** * @deprecated * * {@inheritDoc} */ public function getListTableConstraintsSQL($table) { $table = new Identifier($table); $table = $this->quoteStringLiteral($table->getName()); return sprintf( <<<'SQL' SELECT quote_ident(relname) as relname FROM pg_class WHERE oid IN ( SELECT indexrelid FROM pg_index, pg_class WHERE pg_class.relname = %s AND pg_class.oid = pg_index.indrelid AND (indisunique = 't' OR indisprimary = 't') ) SQL , $table, ); } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * {@inheritDoc} * * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html */ public function getListTableIndexesSQL($table, $database = null) { return 'SELECT quote_ident(relname) as relname, pg_index.indisunique, pg_index.indisprimary, pg_index.indkey, pg_index.indrelid, pg_get_expr(indpred, indrelid) AS where FROM pg_class, pg_index WHERE oid IN ( SELECT indexrelid FROM pg_index si, pg_class sc, pg_namespace sn WHERE ' . $this->getTableWhereClause($table, 'sc', 'sn') . ' AND sc.oid=si.indrelid AND sc.relnamespace = sn.oid ) AND pg_index.indexrelid = oid'; } /** * @param string $table * @param string $classAlias * @param string $namespaceAlias */ private function getTableWhereClause($table, $classAlias = 'c', $namespaceAlias = 'n'): string { $whereClause = $namespaceAlias . ".nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast') AND "; if (strpos($table, '.') !== false) { [$schema, $table] = explode('.', $table); $schema = $this->quoteStringLiteral($schema); } else { $schema = 'ANY(current_schemas(false))'; } $table = new Identifier($table); $table = $this->quoteStringLiteral($table->getName()); return $whereClause . sprintf( '%s.relname = %s AND %s.nspname = %s', $classAlias, $table, $namespaceAlias, $schema, ); } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * {@inheritDoc} */ public function getListTableColumnsSQL($table, $database = null) { return "SELECT a.attnum, quote_ident(a.attname) AS field, t.typname AS type, format_type(a.atttypid, a.atttypmod) AS complete_type, (SELECT tc.collcollate FROM pg_catalog.pg_collation tc WHERE tc.oid = a.attcollation) AS collation, (SELECT t1.typname FROM pg_catalog.pg_type t1 WHERE t1.oid = t.typbasetype) AS domain_type, (SELECT format_type(t2.typbasetype, t2.typtypmod) FROM pg_catalog.pg_type t2 WHERE t2.typtype = 'd' AND t2.oid = a.atttypid) AS domain_complete_type, a.attnotnull AS isnotnull, (SELECT 't' FROM pg_index WHERE c.oid = pg_index.indrelid AND pg_index.indkey[0] = a.attnum AND pg_index.indisprimary = 't' ) AS pri, (SELECT pg_get_expr(adbin, adrelid) FROM pg_attrdef WHERE c.oid = pg_attrdef.adrelid AND pg_attrdef.adnum=a.attnum ) AS default, (SELECT pg_description.description FROM pg_description WHERE pg_description.objoid = c.oid AND a.attnum = pg_description.objsubid ) AS comment FROM pg_attribute a, pg_class c, pg_type t, pg_namespace n WHERE " . $this->getTableWhereClause($table, 'c', 'n') . ' AND a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid AND n.oid = c.relnamespace ORDER BY a.attnum'; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) { $query = ''; if ($foreignKey->hasOption('match')) { $query .= ' MATCH ' . $foreignKey->getOption('match'); } $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey); if ($foreignKey->hasOption('deferrable') && $foreignKey->getOption('deferrable') !== false) { $query .= ' DEFERRABLE'; } else { $query .= ' NOT DEFERRABLE'; } if ( ($foreignKey->hasOption('feferred') && $foreignKey->getOption('feferred') !== false) || ($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) ) { $query .= ' INITIALLY DEFERRED'; } else { $query .= ' INITIALLY IMMEDIATE'; } return $query; } /** * {@inheritDoc} */ public function getAlterTableSQL(TableDiff $diff) { $sql = []; $commentsSQL = []; $columnSql = []; $table = $diff->getOldTable() ?? $diff->getName($this); $tableNameSQL = $table->getQuotedName($this); foreach ($diff->getAddedColumns() as $addedColumn) { if ($this->onSchemaAlterTableAddColumn($addedColumn, $diff, $columnSql)) { continue; } $query = 'ADD ' . $this->getColumnDeclarationSQL( $addedColumn->getQuotedName($this), $addedColumn->toArray(), ); $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; $comment = $this->getColumnComment($addedColumn); if ($comment === null || $comment === '') { continue; } $commentsSQL[] = $this->getCommentOnColumnSQL( $tableNameSQL, $addedColumn->getQuotedName($this), $comment, ); } foreach ($diff->getDroppedColumns() as $droppedColumn) { if ($this->onSchemaAlterTableRemoveColumn($droppedColumn, $diff, $columnSql)) { continue; } $query = 'DROP ' . $droppedColumn->getQuotedName($this); $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; } foreach ($diff->getModifiedColumns() as $columnDiff) { if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { continue; } if ($this->isUnchangedBinaryColumn($columnDiff)) { continue; } $oldColumn = $columnDiff->getOldColumn() ?? $columnDiff->getOldColumnName(); $newColumn = $columnDiff->getNewColumn(); $oldColumnName = $oldColumn->getQuotedName($this); if ( $columnDiff->hasTypeChanged() || $columnDiff->hasPrecisionChanged() || $columnDiff->hasScaleChanged() || $columnDiff->hasFixedChanged() ) { $type = $newColumn->getType(); // SERIAL/BIGSERIAL are not "real" types and we can't alter a column to that type $columnDefinition = $newColumn->toArray(); $columnDefinition['autoincrement'] = false; // here was a server version check before, but DBAL API does not support this anymore. $query = 'ALTER ' . $oldColumnName . ' TYPE ' . $type->getSQLDeclaration($columnDefinition, $this); $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; } if ($columnDiff->hasDefaultChanged()) { $defaultClause = $newColumn->getDefault() === null ? ' DROP DEFAULT' : ' SET' . $this->getDefaultValueDeclarationSQL($newColumn->toArray()); $query = 'ALTER ' . $oldColumnName . $defaultClause; $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; } if ($columnDiff->hasNotNullChanged()) { $query = 'ALTER ' . $oldColumnName . ' ' . ($newColumn->getNotnull() ? 'SET' : 'DROP') . ' NOT NULL'; $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; } if ($columnDiff->hasAutoIncrementChanged()) { if ($newColumn->getAutoincrement()) { // add autoincrement $seqName = $this->getIdentitySequenceName( $table->getName(), $oldColumnName, ); $sql[] = 'CREATE SEQUENCE ' . $seqName; $sql[] = "SELECT setval('" . $seqName . "', (SELECT MAX(" . $oldColumnName . ') FROM ' . $tableNameSQL . '))'; $query = 'ALTER ' . $oldColumnName . " SET DEFAULT nextval('" . $seqName . "')"; } else { // Drop autoincrement, but do NOT drop the sequence. It might be re-used by other tables or have $query = 'ALTER ' . $oldColumnName . ' DROP DEFAULT'; } $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; } $oldComment = $this->getOldColumnComment($columnDiff); $newComment = $this->getColumnComment($newColumn); if ( $columnDiff->hasCommentChanged() || ($columnDiff->getOldColumn() !== null && $oldComment !== $newComment) ) { $commentsSQL[] = $this->getCommentOnColumnSQL( $tableNameSQL, $newColumn->getQuotedName($this), $newComment, ); } if (! $columnDiff->hasLengthChanged()) { continue; } $query = 'ALTER ' . $oldColumnName . ' TYPE ' . $newColumn->getType()->getSQLDeclaration($newColumn->toArray(), $this); $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; } foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { continue; } $oldColumnName = new Identifier($oldColumnName); $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' RENAME COLUMN ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this); } $tableSql = []; if (! $this->onSchemaAlterTable($diff, $tableSql)) { $sql = array_merge($sql, $commentsSQL); $newName = $diff->getNewName(); if ($newName !== false) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5663', 'Generation of "rename table" SQL using %s is deprecated. Use getRenameTableSQL() instead.', __METHOD__, ); $sql[] = sprintf( 'ALTER TABLE %s RENAME TO %s', $tableNameSQL, $newName->getQuotedName($this), ); } $sql = array_merge( $this->getPreAlterTableIndexForeignKeySQL($diff), $sql, $this->getPostAlterTableIndexForeignKeySQL($diff), ); } return array_merge($sql, $tableSql, $columnSql); } /** * Checks whether a given column diff is a logically unchanged binary type column. * * Used to determine whether a column alteration for a binary type column can be skipped. * Doctrine's {@see BinaryType} and {@see BlobType} are mapped to the same database column type on this platform * as this platform does not have a native VARBINARY/BINARY column type. Therefore the comparator * might detect differences for binary type columns which do not have to be propagated * to database as there actually is no difference at database level. */ private function isUnchangedBinaryColumn(ColumnDiff $columnDiff): bool { $newColumnType = $columnDiff->getNewColumn()->getType(); if (! $newColumnType instanceof BinaryType && ! $newColumnType instanceof BlobType) { return false; } $oldColumn = $columnDiff->getOldColumn() instanceof Column ? $columnDiff->getOldColumn() : null; if ($oldColumn !== null) { $oldColumnType = $oldColumn->getType(); if (! $oldColumnType instanceof BinaryType && ! $oldColumnType instanceof BlobType) { return false; } return count(array_diff($columnDiff->changedProperties, ['type', 'length', 'fixed'])) === 0; } if ($columnDiff->hasTypeChanged()) { return false; } return count(array_diff($columnDiff->changedProperties, ['length', 'fixed'])) === 0; } /** * {@inheritDoc} */ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) { if (strpos($tableName, '.') !== false) { [$schema] = explode('.', $tableName); $oldIndexName = $schema . '.' . $oldIndexName; } return ['ALTER INDEX ' . $oldIndexName . ' RENAME TO ' . $index->getQuotedName($this)]; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getCommentOnColumnSQL($tableName, $columnName, $comment) { $tableName = new Identifier($tableName); $columnName = new Identifier($columnName); $comment = $comment === null ? 'NULL' : $this->quoteStringLiteral($comment); return sprintf( 'COMMENT ON COLUMN %s.%s IS %s', $tableName->getQuotedName($this), $columnName->getQuotedName($this), $comment, ); } /** * {@inheritDoc} */ public function getCreateSequenceSQL(Sequence $sequence) { return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . ' INCREMENT BY ' . $sequence->getAllocationSize() . ' MINVALUE ' . $sequence->getInitialValue() . ' START ' . $sequence->getInitialValue() . $this->getSequenceCacheSQL($sequence); } /** * {@inheritDoc} */ public function getAlterSequenceSQL(Sequence $sequence) { return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . ' INCREMENT BY ' . $sequence->getAllocationSize() . $this->getSequenceCacheSQL($sequence); } /** * Cache definition for sequences */ private function getSequenceCacheSQL(Sequence $sequence): string { if ($sequence->getCache() > 1) { return ' CACHE ' . $sequence->getCache(); } return ''; } /** * {@inheritDoc} */ public function getDropSequenceSQL($sequence) { return parent::getDropSequenceSQL($sequence) . ' CASCADE'; } /** * {@inheritDoc} */ public function getDropForeignKeySQL($foreignKey, $table) { return $this->getDropConstraintSQL($foreignKey, $table); } /** * {@inheritDoc} */ public function getDropIndexSQL($index, $table = null) { if ($index instanceof Index && $index->isPrimary() && $table !== null) { $constraintName = $index->getName() === 'primary' ? $this->tableName($table) . '_pkey' : $index->getName(); return $this->getDropConstraintSQL($constraintName, $table); } if ($index === '"primary"' && $table !== null) { $constraintName = $this->tableName($table) . '_pkey'; return $this->getDropConstraintSQL($constraintName, $table); } return parent::getDropIndexSQL($index, $table); } /** * @param Table|string|null $table * * @return string */ private function tableName($table) { return $table instanceof Table ? $table->getName() : (string) $table; } /** * {@inheritDoc} */ protected function _getCreateTableSQL($name, array $columns, array $options = []) { $queryFields = $this->getColumnDeclarationListSQL($columns); if (isset($options['primary']) && ! empty($options['primary'])) { $keyColumns = array_unique(array_values($options['primary'])); $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; } $unlogged = isset($options['unlogged']) && $options['unlogged'] === true ? ' UNLOGGED' : ''; $query = 'CREATE' . $unlogged . ' TABLE ' . $name . ' (' . $queryFields . ')'; $sql = [$query]; if (isset($options['indexes']) && ! empty($options['indexes'])) { foreach ($options['indexes'] as $index) { $sql[] = $this->getCreateIndexSQL($index, $name); } } if (isset($options['uniqueConstraints'])) { foreach ($options['uniqueConstraints'] as $uniqueConstraint) { $sql[] = $this->getCreateConstraintSQL($uniqueConstraint, $name); } } if (isset($options['foreignKeys'])) { foreach ($options['foreignKeys'] as $definition) { $sql[] = $this->getCreateForeignKeySQL($definition, $name); } } return $sql; } /** * Converts a single boolean value. * * First converts the value to its native PHP boolean type * and passes it to the given callback function to be reconverted * into any custom representation. * * @param mixed $value The value to convert. * @param callable $callback The callback function to use for converting the real boolean value. * * @return mixed * * @throws UnexpectedValueException */ private function convertSingleBooleanValue($value, $callback) { if ($value === null) { return $callback(null); } if (is_bool($value) || is_numeric($value)) { return $callback((bool) $value); } if (! is_string($value)) { return $callback(true); } /** * Better safe than sorry: http://php.net/in_array#106319 */ if (in_array(strtolower(trim($value)), $this->booleanLiterals['false'], true)) { return $callback(false); } if (in_array(strtolower(trim($value)), $this->booleanLiterals['true'], true)) { return $callback(true); } throw new UnexpectedValueException(sprintf("Unrecognized boolean literal '%s'", $value)); } /** * Converts one or multiple boolean values. * * First converts the value(s) to their native PHP boolean type * and passes them to the given callback function to be reconverted * into any custom representation. * * @param mixed $item The value(s) to convert. * @param callable $callback The callback function to use for converting the real boolean value(s). * * @return mixed */ private function doConvertBooleans($item, $callback) { if (is_array($item)) { foreach ($item as $key => $value) { $item[$key] = $this->convertSingleBooleanValue($value, $callback); } return $item; } return $this->convertSingleBooleanValue($item, $callback); } /** * {@inheritDoc} * * Postgres wants boolean values converted to the strings 'true'/'false'. */ public function convertBooleans($item) { if (! $this->useBooleanTrueFalseStrings) { return parent::convertBooleans($item); } return $this->doConvertBooleans( $item, /** @param mixed $value */ static function ($value) { if ($value === null) { return 'NULL'; } return $value === true ? 'true' : 'false'; }, ); } /** * {@inheritDoc} */ public function convertBooleansToDatabaseValue($item) { if (! $this->useBooleanTrueFalseStrings) { return parent::convertBooleansToDatabaseValue($item); } return $this->doConvertBooleans( $item, /** @param mixed $value */ static function ($value): ?int { return $value === null ? null : (int) $value; }, ); } /** * {@inheritDoc} * * @param T $item * * @return (T is null ? null : bool) * * @template T */ public function convertFromBoolean($item) { if ($item !== null && in_array(strtolower($item), $this->booleanLiterals['false'], true)) { return false; } return parent::convertFromBoolean($item); } /** * {@inheritDoc} */ public function getSequenceNextValSQL($sequence) { return "SELECT NEXTVAL('" . $sequence . "')"; } /** * {@inheritDoc} */ public function getSetTransactionIsolationSQL($level) { return 'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); } /** * {@inheritDoc} */ public function getBooleanTypeDeclarationSQL(array $column) { return 'BOOLEAN'; } /** * {@inheritDoc} */ public function getIntegerTypeDeclarationSQL(array $column) { if (! empty($column['autoincrement'])) { return 'SERIAL'; } return 'INT'; } /** * {@inheritDoc} */ public function getBigIntTypeDeclarationSQL(array $column) { if (! empty($column['autoincrement'])) { return 'BIGSERIAL'; } return 'BIGINT'; } /** * {@inheritDoc} */ public function getSmallIntTypeDeclarationSQL(array $column) { if (! empty($column['autoincrement'])) { return 'SMALLSERIAL'; } return 'SMALLINT'; } /** * {@inheritDoc} */ public function getGuidTypeDeclarationSQL(array $column) { return 'UUID'; } /** * {@inheritDoc} */ public function getDateTimeTypeDeclarationSQL(array $column) { return 'TIMESTAMP(0) WITHOUT TIME ZONE'; } /** * {@inheritDoc} */ public function getDateTimeTzTypeDeclarationSQL(array $column) { return 'TIMESTAMP(0) WITH TIME ZONE'; } /** * {@inheritDoc} */ public function getDateTypeDeclarationSQL(array $column) { return 'DATE'; } /** * {@inheritDoc} */ public function getTimeTypeDeclarationSQL(array $column) { return 'TIME(0) WITHOUT TIME ZONE'; } /** * {@inheritDoc} */ protected function _getCommonIntegerTypeDeclarationSQL(array $column) { return ''; } /** * {@inheritDoc} */ protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) { return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(255)') : ($length > 0 ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)'); } /** * {@inheritDoc} */ protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) { return 'BYTEA'; } /** * {@inheritDoc} */ public function getClobTypeDeclarationSQL(array $column) { return 'TEXT'; } /** * {@inheritDoc} */ public function getName() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4749', 'PostgreSQLPlatform::getName() is deprecated. Identify platforms by their class.', ); return 'postgresql'; } /** * {@inheritDoc} */ public function getDateTimeTzFormatString() { return 'Y-m-d H:i:sO'; } /** * {@inheritDoc} */ public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) { return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (DEFAULT)'; } /** * {@inheritDoc} */ public function getTruncateTableSQL($tableName, $cascade = false) { $tableIdentifier = new Identifier($tableName); $sql = 'TRUNCATE ' . $tableIdentifier->getQuotedName($this); if ($cascade) { $sql .= ' CASCADE'; } return $sql; } /** * {@inheritDoc} */ public function getReadLockSQL() { return 'FOR SHARE'; } /** * {@inheritDoc} */ protected function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ 'bigint' => Types::BIGINT, 'bigserial' => Types::BIGINT, 'bool' => Types::BOOLEAN, 'boolean' => Types::BOOLEAN, 'bpchar' => Types::STRING, 'bytea' => Types::BLOB, 'char' => Types::STRING, 'date' => Types::DATE_MUTABLE, 'datetime' => Types::DATETIME_MUTABLE, 'decimal' => Types::DECIMAL, 'double' => Types::FLOAT, 'double precision' => Types::FLOAT, 'float' => Types::FLOAT, 'float4' => Types::FLOAT, 'float8' => Types::FLOAT, 'inet' => Types::STRING, 'int' => Types::INTEGER, 'int2' => Types::SMALLINT, 'int4' => Types::INTEGER, 'int8' => Types::BIGINT, 'integer' => Types::INTEGER, 'interval' => Types::STRING, 'json' => Types::JSON, 'jsonb' => Types::JSON, 'money' => Types::DECIMAL, 'numeric' => Types::DECIMAL, 'serial' => Types::INTEGER, 'serial4' => Types::INTEGER, 'serial8' => Types::BIGINT, 'real' => Types::FLOAT, 'smallint' => Types::SMALLINT, 'text' => Types::TEXT, 'time' => Types::TIME_MUTABLE, 'timestamp' => Types::DATETIME_MUTABLE, 'timestamptz' => Types::DATETIMETZ_MUTABLE, 'timetz' => Types::TIME_MUTABLE, 'tsvector' => Types::TEXT, 'uuid' => Types::GUID, 'varchar' => Types::STRING, 'year' => Types::DATE_MUTABLE, '_varchar' => Types::STRING, ]; } /** * {@inheritDoc} * * @deprecated */ public function getVarcharMaxLength() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'PostgreSQLPlatform::getVarcharMaxLength() is deprecated.', ); return 65535; } /** * {@inheritDoc} */ public function getBinaryMaxLength() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'PostgreSQLPlatform::getBinaryMaxLength() is deprecated.', ); return 0; } /** * {@inheritDoc} * * @deprecated */ public function getBinaryDefaultLength() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'Relying on the default binary column length is deprecated, specify the length explicitly.', ); return 0; } /** * {@inheritDoc} * * @deprecated */ public function hasNativeJsonType() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5509', '%s is deprecated.', __METHOD__, ); return true; } /** * {@inheritDoc} * * @deprecated Implement {@see createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4510', 'PostgreSQLPlatform::getReservedKeywordsClass() is deprecated,' . ' use PostgreSQLPlatform::createReservedKeywordsList() instead.', ); return Keywords\PostgreSQL94Keywords::class; } /** * {@inheritDoc} */ public function getBlobTypeDeclarationSQL(array $column) { return 'BYTEA'; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getDefaultValueDeclarationSQL($column) { if (isset($column['autoincrement']) && $column['autoincrement'] === true) { return ''; } return parent::getDefaultValueDeclarationSQL($column); } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsColumnCollation() { return true; } /** * {@inheritDoc} */ public function getJsonTypeDeclarationSQL(array $column) { if (! empty($column['jsonb'])) { return 'JSONB'; } return 'JSON'; } private function getOldColumnComment(ColumnDiff $columnDiff): ?string { $oldColumn = $columnDiff->getOldColumn(); if ($oldColumn !== null) { return $this->getColumnComment($oldColumn); } return null; } /** @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. */ public function getListTableMetadataSQL(string $table, ?string $schema = null): string { if ($schema !== null) { $table = $schema . '.' . $table; } return sprintf( <<<'SQL' SELECT obj_description(%s::regclass) AS table_comment; SQL , $this->quoteStringLiteral($table), ); } public function createSchemaManager(Connection $connection): PostgreSQLSchemaManager { return new PostgreSQLSchemaManager($connection, $this); } } PK!ZByy2dbal/src/Platforms/Keywords/MariaDb102Keywords.phpnu[keywords === null) { $this->initializeKeywords(); } return isset($this->keywords[strtoupper($word)]); } /** @return void */ protected function initializeKeywords() { $this->keywords = array_flip(array_map('strtoupper', $this->getKeywords())); } /** * Returns the list of keywords. * * @return string[] */ abstract protected function getKeywords(); /** * Returns the name of this keyword list. * * @deprecated * * @return string */ abstract public function getName(); } PK!D0\\5dbal/src/Platforms/Keywords/PostgreSQL100Keywords.phpnu[ 9dbal/src/Platforms/Keywords/ReservedKeywordsValidator.phpnu[keywordLists = $keywordLists; } /** @return string[] */ public function getViolations() { return $this->violations; } /** * @param string $word * * @return string[] */ private function isReservedWord($word): array { if ($word[0] === '`') { $word = str_replace('`', '', $word); } $keywordLists = []; foreach ($this->keywordLists as $keywordList) { if (! $keywordList->isKeyword($word)) { continue; } $keywordLists[] = $keywordList->getName(); } return $keywordLists; } /** * @param string $asset * @param string[] $violatedPlatforms */ private function addViolation($asset, $violatedPlatforms): void { if (count($violatedPlatforms) === 0) { return; } $this->violations[] = $asset . ' keyword violations: ' . implode(', ', $violatedPlatforms); } /** * {@inheritDoc} */ public function acceptColumn(Table $table, Column $column) { $this->addViolation( 'Table ' . $table->getName() . ' column ' . $column->getName(), $this->isReservedWord($column->getName()), ); } /** * {@inheritDoc} */ public function acceptForeignKey(Table $localTable, ForeignKeyConstraint $fkConstraint) { } /** * {@inheritDoc} */ public function acceptIndex(Table $table, Index $index) { } /** * {@inheritDoc} */ public function acceptSchema(Schema $schema) { } /** * {@inheritDoc} */ public function acceptSequence(Sequence $sequence) { } /** * {@inheritDoc} */ public function acceptTable(Table $table) { $this->addViolation( 'Table ' . $table->getName(), $this->isReservedWord($table->getName()), ); } } PK!#  /dbal/src/Platforms/Keywords/MySQL80Keywords.phpnu[getQuotedName($this)]; } /** * {@inheritDoc} * * @deprecated Implement {@see createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4510', 'MySQL57Platform::getReservedKeywordsClass() is deprecated,' . ' use MySQL57Platform::createReservedKeywordsList() instead.', ); return Keywords\MySQL57Keywords::class; } /** * {@inheritDoc} */ protected function initializeDoctrineTypeMappings() { parent::initializeDoctrineTypeMappings(); $this->doctrineTypeMapping['json'] = Types::JSON; } } PK!;#,dbal/src/Platforms/PostgreSQL100Platform.phpnu[ 0 THEN INSTR(SUBSTR(' . $str . ', ' . $startPos . '), ' . $substr . ') + ' . $startPos . ' - 1 ELSE 0 END'; } /** * {@inheritDoc} */ protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) { switch ($unit) { case DateIntervalUnit::SECOND: case DateIntervalUnit::MINUTE: case DateIntervalUnit::HOUR: return 'DATETIME(' . $date . ",'" . $operator . $interval . ' ' . $unit . "')"; } switch ($unit) { case DateIntervalUnit::WEEK: $interval = $this->multiplyInterval((string) $interval, 7); $unit = DateIntervalUnit::DAY; break; case DateIntervalUnit::QUARTER: $interval = $this->multiplyInterval((string) $interval, 3); $unit = DateIntervalUnit::MONTH; break; } if (! is_numeric($interval)) { $interval = "' || " . $interval . " || '"; } return 'DATE(' . $date . ",'" . $operator . $interval . ' ' . $unit . "')"; } /** * {@inheritDoc} */ public function getDateDiffExpression($date1, $date2) { return sprintf("JULIANDAY(%s, 'start of day') - JULIANDAY(%s, 'start of day')", $date1, $date2); } /** * {@inheritDoc} * * The DBAL doesn't support databases on the SQLite platform. The expression here always returns a fixed string * as an indicator of an implicitly selected database. * * @link https://www.sqlite.org/lang_select.html * @see Connection::getDatabase() */ public function getCurrentDatabaseExpression(): string { return "'main'"; } /** @link https://www2.sqlite.org/cvstrac/wiki?p=UnsupportedSql */ public function createSelectSQLBuilder(): SelectSQLBuilder { return new DefaultSelectSQLBuilder($this, null, null); } /** * {@inheritDoc} */ protected function _getTransactionIsolationLevelSQL($level) { switch ($level) { case TransactionIsolationLevel::READ_UNCOMMITTED: return '0'; case TransactionIsolationLevel::READ_COMMITTED: case TransactionIsolationLevel::REPEATABLE_READ: case TransactionIsolationLevel::SERIALIZABLE: return '1'; default: return parent::_getTransactionIsolationLevelSQL($level); } } /** * {@inheritDoc} */ public function getSetTransactionIsolationSQL($level) { return 'PRAGMA read_uncommitted = ' . $this->_getTransactionIsolationLevelSQL($level); } /** * {@inheritDoc} * * @deprecated */ public function prefersIdentityColumns() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/1519', 'SqlitePlatform::prefersIdentityColumns() is deprecated.', ); return true; } /** * {@inheritDoc} */ public function getBooleanTypeDeclarationSQL(array $column) { return 'BOOLEAN'; } /** * {@inheritDoc} */ public function getIntegerTypeDeclarationSQL(array $column) { return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function getBigIntTypeDeclarationSQL(array $column) { // SQLite autoincrement is implicit for INTEGER PKs, but not for BIGINT columns if (! empty($column['autoincrement'])) { return $this->getIntegerTypeDeclarationSQL($column); } return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } /** * @deprecated Use {@see getSmallIntTypeDeclarationSQL()} instead. * * @param array $column * * @return string */ public function getTinyIntTypeDeclarationSQL(array $column) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5511', '%s is deprecated. Use getSmallIntTypeDeclarationSQL() instead.', __METHOD__, ); // SQLite autoincrement is implicit for INTEGER PKs, but not for TINYINT columns if (! empty($column['autoincrement'])) { return $this->getIntegerTypeDeclarationSQL($column); } return 'TINYINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function getSmallIntTypeDeclarationSQL(array $column) { // SQLite autoincrement is implicit for INTEGER PKs, but not for SMALLINT columns if (! empty($column['autoincrement'])) { return $this->getIntegerTypeDeclarationSQL($column); } return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } /** * @deprecated Use {@see getIntegerTypeDeclarationSQL()} instead. * * @param array $column * * @return string */ public function getMediumIntTypeDeclarationSQL(array $column) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5511', '%s is deprecated. Use getIntegerTypeDeclarationSQL() instead.', __METHOD__, ); // SQLite autoincrement is implicit for INTEGER PKs, but not for MEDIUMINT columns if (! empty($column['autoincrement'])) { return $this->getIntegerTypeDeclarationSQL($column); } return 'MEDIUMINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function getDateTimeTypeDeclarationSQL(array $column) { return 'DATETIME'; } /** * {@inheritDoc} */ public function getDateTypeDeclarationSQL(array $column) { return 'DATE'; } /** * {@inheritDoc} */ public function getTimeTypeDeclarationSQL(array $column) { return 'TIME'; } /** * {@inheritDoc} */ protected function _getCommonIntegerTypeDeclarationSQL(array $column) { // sqlite autoincrement is only possible for the primary key if (! empty($column['autoincrement'])) { return ' PRIMARY KEY AUTOINCREMENT'; } return ! empty($column['unsigned']) ? ' UNSIGNED' : ''; } /** * Disables schema emulation. * * Schema emulation is enabled by default to maintain backwards compatibility. * Disable it to opt-in to the behavior of DBAL 4. * * @deprecated Will be removed in DBAL 4.0. */ public function disableSchemaEmulation(): void { $this->schemaEmulationEnabled = false; } private function emulateSchemaNamespacing(string $tableName): string { return $this->schemaEmulationEnabled ? str_replace('.', '__', $tableName) : $tableName; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getForeignKeyDeclarationSQL(ForeignKeyConstraint $foreignKey) { return parent::getForeignKeyDeclarationSQL(new ForeignKeyConstraint( $foreignKey->getQuotedLocalColumns($this), $this->emulateSchemaNamespacing($foreignKey->getQuotedForeignTableName($this)), $foreignKey->getQuotedForeignColumns($this), $foreignKey->getName(), $foreignKey->getOptions(), )); } /** * {@inheritDoc} */ protected function _getCreateTableSQL($name, array $columns, array $options = []) { $name = $this->emulateSchemaNamespacing($name); $queryFields = $this->getColumnDeclarationListSQL($columns); if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { foreach ($options['uniqueConstraints'] as $constraintName => $definition) { $queryFields .= ', ' . $this->getUniqueConstraintDeclarationSQL($constraintName, $definition); } } $queryFields .= $this->getNonAutoincrementPrimaryKeyDefinition($columns, $options); if (isset($options['foreignKeys'])) { foreach ($options['foreignKeys'] as $foreignKey) { $queryFields .= ', ' . $this->getForeignKeyDeclarationSQL($foreignKey); } } $tableComment = ''; if (isset($options['comment'])) { $comment = trim($options['comment'], " '"); $tableComment = $this->getInlineTableCommentSQL($comment); } $query = ['CREATE TABLE ' . $name . ' ' . $tableComment . '(' . $queryFields . ')']; if (isset($options['alter']) && $options['alter'] === true) { return $query; } if (isset($options['indexes']) && ! empty($options['indexes'])) { foreach ($options['indexes'] as $indexDef) { $query[] = $this->getCreateIndexSQL($indexDef, $name); } } if (isset($options['unique']) && ! empty($options['unique'])) { foreach ($options['unique'] as $indexDef) { $query[] = $this->getCreateIndexSQL($indexDef, $name); } } return $query; } /** * Generate a PRIMARY KEY definition if no autoincrement value is used * * @param mixed[][] $columns * @param mixed[] $options */ private function getNonAutoincrementPrimaryKeyDefinition(array $columns, array $options): string { if (empty($options['primary'])) { return ''; } $keyColumns = array_unique(array_values($options['primary'])); foreach ($keyColumns as $keyColumn) { if (! empty($columns[$keyColumn]['autoincrement'])) { return ''; } } return ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; } /** * {@inheritDoc} */ protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed) { return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(255)') : ($length > 0 ? 'VARCHAR(' . $length . ')' : 'TEXT'); } /** * {@inheritDoc} */ protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed) { return 'BLOB'; } /** * {@inheritDoc} * * @deprecated */ public function getBinaryMaxLength() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'SqlitePlatform::getBinaryMaxLength() is deprecated.', ); return 0; } /** * {@inheritDoc} * * @deprecated */ public function getBinaryDefaultLength() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'Relying on the default binary column length is deprecated, specify the length explicitly.', ); return 0; } /** * {@inheritDoc} */ public function getClobTypeDeclarationSQL(array $column) { return 'CLOB'; } /** * @deprecated * * {@inheritDoc} */ public function getListTableConstraintsSQL($table) { $table = $this->emulateSchemaNamespacing($table); return sprintf( "SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name = %s AND sql NOT NULL ORDER BY name", $this->quoteStringLiteral($table), ); } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * {@inheritDoc} */ public function getListTableColumnsSQL($table, $database = null) { $table = $this->emulateSchemaNamespacing($table); return sprintf('PRAGMA table_info(%s)', $this->quoteStringLiteral($table)); } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * {@inheritDoc} */ public function getListTableIndexesSQL($table, $database = null) { $table = $this->emulateSchemaNamespacing($table); return sprintf('PRAGMA index_list(%s)', $this->quoteStringLiteral($table)); } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * {@inheritDoc} */ public function getListTablesSQL() { return 'SELECT name FROM sqlite_master' . " WHERE type = 'table'" . " AND name != 'sqlite_sequence'" . " AND name != 'geometry_columns'" . " AND name != 'spatial_ref_sys'" . ' UNION ALL SELECT name FROM sqlite_temp_master' . " WHERE type = 'table' ORDER BY name"; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListViewsSQL($database) { return "SELECT name, sql FROM sqlite_master WHERE type='view' AND sql NOT NULL"; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey) { $query = parent::getAdvancedForeignKeyOptionsSQL($foreignKey); if (! $foreignKey->hasOption('deferrable') || $foreignKey->getOption('deferrable') === false) { $query .= ' NOT'; } $query .= ' DEFERRABLE'; $query .= ' INITIALLY'; if ($foreignKey->hasOption('deferred') && $foreignKey->getOption('deferred') !== false) { $query .= ' DEFERRED'; } else { $query .= ' IMMEDIATE'; } return $query; } /** * {@inheritDoc} * * @deprecated */ public function supportsCreateDropDatabase() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5513', '%s is deprecated.', __METHOD__, ); return false; } /** * {@inheritDoc} */ public function supportsIdentityColumns() { return true; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsColumnCollation() { return true; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsInlineColumnComments() { return true; } /** * {@inheritDoc} */ public function getName() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4749', 'SqlitePlatform::getName() is deprecated. Identify platforms by their class.', ); return 'sqlite'; } /** * {@inheritDoc} */ public function getTruncateTableSQL($tableName, $cascade = false) { $tableIdentifier = new Identifier($tableName); $tableName = $this->emulateSchemaNamespacing($tableIdentifier->getQuotedName($this)); return 'DELETE FROM ' . $tableName; } /** * User-defined function for Sqlite that is used with PDO::sqliteCreateFunction(). * * @deprecated The driver will use {@see sqrt()} in the next major release. * * @param int|float $value * * @return float */ public static function udfSqrt($value) { return sqrt($value); } /** * User-defined function for Sqlite that implements MOD(a, b). * * @deprecated The driver will use {@see UserDefinedFunctions::mod()} in the next major release. * * @param int $a * @param int $b * * @return int */ public static function udfMod($a, $b) { return UserDefinedFunctions::mod($a, $b); } /** * @deprecated The driver will use {@see UserDefinedFunctions::locate()} in the next major release. * * @param string $str * @param string $substr * @param int $offset * * @return int */ public static function udfLocate($str, $substr, $offset = 0) { return UserDefinedFunctions::locate($str, $substr, $offset); } /** * {@inheritDoc} * * @deprecated This API is not portable. */ public function getForUpdateSQL() { return ''; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getInlineColumnCommentSQL($comment) { return '--' . str_replace("\n", "\n--", $comment) . "\n"; } private function getInlineTableCommentSQL(string $comment): string { return $this->getInlineColumnCommentSQL($comment); } /** * {@inheritDoc} */ protected function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ 'bigint' => Types\Types::BIGINT, 'bigserial' => Types\Types::BIGINT, 'blob' => Types\Types::BLOB, 'boolean' => Types\Types::BOOLEAN, 'char' => Types\Types::STRING, 'clob' => Types\Types::TEXT, 'date' => Types\Types::DATE_MUTABLE, 'datetime' => Types\Types::DATETIME_MUTABLE, 'decimal' => Types\Types::DECIMAL, 'double' => Types\Types::FLOAT, 'double precision' => Types\Types::FLOAT, 'float' => Types\Types::FLOAT, 'image' => Types\Types::STRING, 'int' => Types\Types::INTEGER, 'integer' => Types\Types::INTEGER, 'longtext' => Types\Types::TEXT, 'longvarchar' => Types\Types::STRING, 'mediumint' => Types\Types::INTEGER, 'mediumtext' => Types\Types::TEXT, 'ntext' => Types\Types::STRING, 'numeric' => Types\Types::DECIMAL, 'nvarchar' => Types\Types::STRING, 'real' => Types\Types::FLOAT, 'serial' => Types\Types::INTEGER, 'smallint' => Types\Types::SMALLINT, 'text' => Types\Types::TEXT, 'time' => Types\Types::TIME_MUTABLE, 'timestamp' => Types\Types::DATETIME_MUTABLE, 'tinyint' => Types\Types::BOOLEAN, 'tinytext' => Types\Types::TEXT, 'varchar' => Types\Types::STRING, 'varchar2' => Types\Types::STRING, ]; } /** * {@inheritDoc} * * @deprecated Implement {@see createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4510', 'SqlitePlatform::getReservedKeywordsClass() is deprecated,' . ' use SqlitePlatform::createReservedKeywordsList() instead.', ); return Keywords\SQLiteKeywords::class; } /** * {@inheritDoc} */ protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff) { return []; } /** * {@inheritDoc} */ protected function getPostAlterTableIndexForeignKeySQL(TableDiff $diff) { $table = $diff->getOldTable(); if (! $table instanceof Table) { throw new Exception( 'Sqlite platform requires for alter table the table diff with reference to original table schema', ); } $sql = []; $tableName = $diff->getNewName(); if ($tableName === false) { $tableName = $diff->getName($this); } foreach ($this->getIndexesInAlteredTable($diff, $table) as $index) { if ($index->isPrimary()) { continue; } $sql[] = $this->getCreateIndexSQL($index, $tableName->getQuotedName($this)); } return $sql; } /** * {@inheritDoc} */ protected function doModifyLimitQuery($query, $limit, $offset) { if ($limit === null && $offset > 0) { return sprintf('%s LIMIT -1 OFFSET %d', $query, $offset); } return parent::doModifyLimitQuery($query, $limit, $offset); } /** * {@inheritDoc} */ public function getBlobTypeDeclarationSQL(array $column) { return 'BLOB'; } /** * {@inheritDoc} */ public function getTemporaryTableName($tableName) { $tableName = $this->emulateSchemaNamespacing($tableName); return $tableName; } /** * {@inheritDoc} * * @deprecated * * Sqlite Platform emulates schema by underscoring each dot and generating tables * into the default database. * * This hack is implemented to be able to use SQLite as testdriver when * using schema supporting databases. */ public function canEmulateSchemas() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4805', 'SqlitePlatform::canEmulateSchemas() is deprecated.', ); return $this->schemaEmulationEnabled; } /** * {@inheritDoc} */ public function getCreateTablesSQL(array $tables): array { $sql = []; foreach ($tables as $table) { $sql = array_merge($sql, $this->getCreateTableSQL($table)); } return $sql; } /** * {@inheritDoc} */ public function getCreateIndexSQL(Index $index, $table) { if ($table instanceof Table) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); $table = $table->getQuotedName($this); } $name = $index->getQuotedName($this); $columns = $index->getColumns(); if (strpos($table, '.') !== false) { [$schema, $table] = explode('.', $table); $name = $schema . '.' . $name; } if (count($columns) === 0) { throw new InvalidArgumentException(sprintf( 'Incomplete or invalid index definition %s on table %s', $name, $table, )); } if ($index->isPrimary()) { return $this->getCreatePrimaryKeySQL($index, $table); } $query = 'CREATE ' . $this->getCreateIndexSQLFlags($index) . 'INDEX ' . $name . ' ON ' . $table; $query .= ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')' . $this->getPartialIndexSQL($index); return $query; } /** * {@inheritDoc} */ public function getDropTablesSQL(array $tables): array { $sql = []; foreach ($tables as $table) { $sql[] = $this->getDropTableSQL($table->getQuotedName($this)); } return $sql; } /** * {@inheritDoc} */ public function getCreatePrimaryKeySQL(Index $index, $table) { throw new Exception('Sqlite platform does not support alter primary key.'); } /** * {@inheritDoc} */ public function getCreateForeignKeySQL(ForeignKeyConstraint $foreignKey, $table) { throw new Exception('Sqlite platform does not support alter foreign key.'); } /** * {@inheritDoc} */ public function getDropForeignKeySQL($foreignKey, $table) { throw new Exception('Sqlite platform does not support alter foreign key.'); } /** * {@inheritDoc} * * @deprecated */ public function getCreateConstraintSQL(Constraint $constraint, $table) { throw new Exception('Sqlite platform does not support alter constraint.'); } /** * {@inheritDoc} * * @param int|null $createFlags * @psalm-param int-mask-of|null $createFlags */ public function getCreateTableSQL(Table $table, $createFlags = null) { $createFlags = $createFlags ?? self::CREATE_INDEXES | self::CREATE_FOREIGNKEYS; return parent::getCreateTableSQL($table, $createFlags); } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * @param string $table * @param string|null $database * * @return string */ public function getListTableForeignKeysSQL($table, $database = null) { $table = $this->emulateSchemaNamespacing($table); return sprintf('PRAGMA foreign_key_list(%s)', $this->quoteStringLiteral($table)); } /** * {@inheritDoc} */ public function getAlterTableSQL(TableDiff $diff) { $sql = $this->getSimpleAlterTableSQL($diff); if ($sql !== false) { return $sql; } $table = $diff->getOldTable(); if (! $table instanceof Table) { throw new Exception( 'Sqlite platform requires for alter table the table diff with reference to original table schema', ); } $columns = []; $oldColumnNames = []; $newColumnNames = []; $columnSql = []; foreach ($table->getColumns() as $columnName => $column) { $columnName = strtolower($columnName); $columns[$columnName] = $column; $oldColumnNames[$columnName] = $newColumnNames[$columnName] = $column->getQuotedName($this); } foreach ($diff->getDroppedColumns() as $column) { if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { continue; } $columnName = strtolower($column->getName()); if (! isset($columns[$columnName])) { continue; } unset( $columns[$columnName], $oldColumnNames[$columnName], $newColumnNames[$columnName], ); } foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) { continue; } $oldColumnName = strtolower($oldColumnName); $columns = $this->replaceColumn( $table->getName(), $columns, $oldColumnName, $column, ); if (! isset($newColumnNames[$oldColumnName])) { continue; } $newColumnNames[$oldColumnName] = $column->getQuotedName($this); } foreach ($diff->getModifiedColumns() as $columnDiff) { if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { continue; } $oldColumn = $columnDiff->getOldColumn() ?? $columnDiff->getOldColumnName(); $oldColumnName = strtolower($oldColumn->getName()); $columns = $this->replaceColumn( $table->getName(), $columns, $oldColumnName, $columnDiff->getNewColumn(), ); if (! isset($newColumnNames[$oldColumnName])) { continue; } $newColumnNames[$oldColumnName] = $columnDiff->getNewColumn()->getQuotedName($this); } foreach ($diff->getAddedColumns() as $column) { if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { continue; } $columns[strtolower($column->getName())] = $column; } $sql = []; $tableSql = []; if (! $this->onSchemaAlterTable($diff, $tableSql)) { $dataTable = new Table('__temp__' . $table->getName()); $newTable = new Table( $table->getQuotedName($this), $columns, $this->getPrimaryIndexInAlteredTable($diff, $table), [], $this->getForeignKeysInAlteredTable($diff, $table), $table->getOptions(), ); $newTable->addOption('alter', true); $sql = $this->getPreAlterTableIndexForeignKeySQL($diff); $sql[] = sprintf( 'CREATE TEMPORARY TABLE %s AS SELECT %s FROM %s', $dataTable->getQuotedName($this), implode(', ', $oldColumnNames), $table->getQuotedName($this), ); $sql[] = $this->getDropTableSQL($table); $sql = array_merge($sql, $this->getCreateTableSQL($newTable)); $sql[] = sprintf( 'INSERT INTO %s (%s) SELECT %s FROM %s', $newTable->getQuotedName($this), implode(', ', $newColumnNames), implode(', ', $oldColumnNames), $dataTable->getQuotedName($this), ); $sql[] = $this->getDropTableSQL($dataTable->getQuotedName($this)); $newName = $diff->getNewName(); if ($newName !== false) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5663', 'Generation of "rename table" SQL using %s is deprecated. Use getRenameTableSQL() instead.', __METHOD__, ); $sql[] = sprintf( 'ALTER TABLE %s RENAME TO %s', $newTable->getQuotedName($this), $newName->getQuotedName($this), ); } $sql = array_merge($sql, $this->getPostAlterTableIndexForeignKeySQL($diff)); } return array_merge($sql, $tableSql, $columnSql); } /** * Replace the column with the given name with the new column. * * @param string $tableName * @param array $columns * @param string $columnName * * @return array * * @throws Exception */ private function replaceColumn($tableName, array $columns, $columnName, Column $column): array { $keys = array_keys($columns); $index = array_search($columnName, $keys, true); if ($index === false) { throw SchemaException::columnDoesNotExist($columnName, $tableName); } $values = array_values($columns); $keys[$index] = strtolower($column->getName()); $values[$index] = $column; return array_combine($keys, $values); } /** * @return string[]|false * * @throws Exception */ private function getSimpleAlterTableSQL(TableDiff $diff) { // Suppress changes on integer type autoincrement columns. foreach ($diff->getModifiedColumns() as $columnDiff) { $oldColumn = $columnDiff->getOldColumn(); if ($oldColumn === null) { continue; } $newColumn = $columnDiff->getNewColumn(); if (! $newColumn->getAutoincrement() || ! $newColumn->getType() instanceof IntegerType) { continue; } $oldColumnName = $oldColumn->getName(); if (! $columnDiff->hasTypeChanged() && $columnDiff->hasUnsignedChanged()) { unset($diff->changedColumns[$oldColumnName]); continue; } $fromColumnType = $oldColumn->getType(); if (! ($fromColumnType instanceof Types\SmallIntType) && ! ($fromColumnType instanceof Types\BigIntType)) { continue; } unset($diff->changedColumns[$oldColumnName]); } if ( count($diff->getModifiedColumns()) > 0 || count($diff->getDroppedColumns()) > 0 || count($diff->getRenamedColumns()) > 0 || count($diff->getAddedIndexes()) > 0 || count($diff->getModifiedIndexes()) > 0 || count($diff->getDroppedIndexes()) > 0 || count($diff->getRenamedIndexes()) > 0 || count($diff->getAddedForeignKeys()) > 0 || count($diff->getModifiedForeignKeys()) > 0 || count($diff->getDroppedForeignKeys()) > 0 ) { return false; } $table = $diff->getOldTable() ?? $diff->getName($this); $sql = []; $tableSql = []; $columnSql = []; foreach ($diff->getAddedColumns() as $column) { if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { continue; } $definition = array_merge([ 'unique' => null, 'autoincrement' => null, 'default' => null, ], $column->toArray()); $type = $definition['type']; switch (true) { case isset($definition['columnDefinition']) || $definition['autoincrement'] || $definition['unique']: case $type instanceof Types\DateTimeType && $definition['default'] === $this->getCurrentTimestampSQL(): case $type instanceof Types\DateType && $definition['default'] === $this->getCurrentDateSQL(): case $type instanceof Types\TimeType && $definition['default'] === $this->getCurrentTimeSQL(): return false; } $definition['name'] = $column->getQuotedName($this); if ($type instanceof Types\StringType) { $definition['length'] ??= 255; } $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ADD COLUMN ' . $this->getColumnDeclarationSQL($definition['name'], $definition); } if (! $this->onSchemaAlterTable($diff, $tableSql)) { if ($diff->newName !== false) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5663', 'Generation of SQL that renames a table using %s is deprecated.' . ' Use getRenameTableSQL() instead.', __METHOD__, ); $newTable = new Identifier($diff->newName); $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' RENAME TO ' . $newTable->getQuotedName($this); } } return array_merge($sql, $tableSql, $columnSql); } /** @return string[] */ private function getColumnNamesInAlteredTable(TableDiff $diff, Table $fromTable): array { $columns = []; foreach ($fromTable->getColumns() as $columnName => $column) { $columns[strtolower($columnName)] = $column->getName(); } foreach ($diff->getDroppedColumns() as $column) { $columnName = strtolower($column->getName()); if (! isset($columns[$columnName])) { continue; } unset($columns[$columnName]); } foreach ($diff->getRenamedColumns() as $oldColumnName => $column) { $columnName = $column->getName(); $columns[strtolower($oldColumnName)] = $columnName; $columns[strtolower($columnName)] = $columnName; } foreach ($diff->getModifiedColumns() as $columnDiff) { $oldColumn = $columnDiff->getOldColumn() ?? $columnDiff->getOldColumnName(); $oldColumnName = $oldColumn->getName(); $newColumnName = $columnDiff->getNewColumn()->getName(); $columns[strtolower($oldColumnName)] = $newColumnName; $columns[strtolower($newColumnName)] = $newColumnName; } foreach ($diff->getAddedColumns() as $column) { $columnName = $column->getName(); $columns[strtolower($columnName)] = $columnName; } return $columns; } /** @return Index[] */ private function getIndexesInAlteredTable(TableDiff $diff, Table $fromTable): array { $indexes = $fromTable->getIndexes(); $columnNames = $this->getColumnNamesInAlteredTable($diff, $fromTable); foreach ($indexes as $key => $index) { foreach ($diff->getRenamedIndexes() as $oldIndexName => $renamedIndex) { if (strtolower($key) !== strtolower($oldIndexName)) { continue; } unset($indexes[$key]); } $changed = false; $indexColumns = []; foreach ($index->getColumns() as $columnName) { $normalizedColumnName = strtolower($columnName); if (! isset($columnNames[$normalizedColumnName])) { unset($indexes[$key]); continue 2; } $indexColumns[] = $columnNames[$normalizedColumnName]; if ($columnName === $columnNames[$normalizedColumnName]) { continue; } $changed = true; } if (! $changed) { continue; } $indexes[$key] = new Index( $index->getName(), $indexColumns, $index->isUnique(), $index->isPrimary(), $index->getFlags(), ); } foreach ($diff->getDroppedIndexes() as $index) { $indexName = strtolower($index->getName()); if (strlen($indexName) === 0 || ! isset($indexes[$indexName])) { continue; } unset($indexes[$indexName]); } foreach ( array_merge( $diff->getModifiedIndexes(), $diff->getAddedIndexes(), $diff->getRenamedIndexes(), ) as $index ) { $indexName = strtolower($index->getName()); if (strlen($indexName) > 0) { $indexes[$indexName] = $index; } else { $indexes[] = $index; } } return $indexes; } /** @return ForeignKeyConstraint[] */ private function getForeignKeysInAlteredTable(TableDiff $diff, Table $fromTable): array { $foreignKeys = $fromTable->getForeignKeys(); $columnNames = $this->getColumnNamesInAlteredTable($diff, $fromTable); foreach ($foreignKeys as $key => $constraint) { $changed = false; $localColumns = []; foreach ($constraint->getLocalColumns() as $columnName) { $normalizedColumnName = strtolower($columnName); if (! isset($columnNames[$normalizedColumnName])) { unset($foreignKeys[$key]); continue 2; } $localColumns[] = $columnNames[$normalizedColumnName]; if ($columnName === $columnNames[$normalizedColumnName]) { continue; } $changed = true; } if (! $changed) { continue; } $foreignKeys[$key] = new ForeignKeyConstraint( $localColumns, $constraint->getForeignTableName(), $constraint->getForeignColumns(), $constraint->getName(), $constraint->getOptions(), ); } foreach ($diff->getDroppedForeignKeys() as $constraint) { if (! $constraint instanceof ForeignKeyConstraint) { $constraint = new Identifier($constraint); } $constraintName = strtolower($constraint->getName()); if (strlen($constraintName) === 0 || ! isset($foreignKeys[$constraintName])) { continue; } unset($foreignKeys[$constraintName]); } foreach (array_merge($diff->getModifiedForeignKeys(), $diff->getAddedForeignKeys()) as $constraint) { $constraintName = strtolower($constraint->getName()); if (strlen($constraintName) > 0) { $foreignKeys[$constraintName] = $constraint; } else { $foreignKeys[] = $constraint; } } return $foreignKeys; } /** @return Index[] */ private function getPrimaryIndexInAlteredTable(TableDiff $diff, Table $fromTable): array { $primaryIndex = []; foreach ($this->getIndexesInAlteredTable($diff, $fromTable) as $index) { if (! $index->isPrimary()) { continue; } $primaryIndex = [$index->getName() => $index]; } return $primaryIndex; } public function createSchemaManager(Connection $connection): SqliteSchemaManager { return new SqliteSchemaManager($connection, $this); } } PK!ff*dbal/src/Platforms/MariaDb1027Platform.phpnu[getConvertExpression('date', 'GETDATE()'); } /** * {@inheritDoc} */ public function getCurrentTimeSQL() { return $this->getConvertExpression('time', 'GETDATE()'); } /** * Returns an expression that converts an expression of one data type to another. * * @param string $dataType The target native data type. Alias data types cannot be used. * @param string $expression The SQL expression to convert. */ private function getConvertExpression($dataType, $expression): string { return sprintf('CONVERT(%s, %s)', $dataType, $expression); } /** * {@inheritDoc} */ protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit) { $factorClause = ''; if ($operator === '-') { $factorClause = '-1 * '; } return 'DATEADD(' . $unit . ', ' . $factorClause . $interval . ', ' . $date . ')'; } /** * {@inheritDoc} */ public function getDateDiffExpression($date1, $date2) { return 'DATEDIFF(day, ' . $date2 . ',' . $date1 . ')'; } /** * {@inheritDoc} * * Microsoft SQL Server prefers "autoincrement" identity columns * since sequences can only be emulated with a table. * * @deprecated */ public function prefersIdentityColumns() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/1519', 'SQLServerPlatform::prefersIdentityColumns() is deprecated.', ); return true; } /** * {@inheritDoc} * * Microsoft SQL Server supports this through AUTO_INCREMENT columns. */ public function supportsIdentityColumns() { return true; } /** * {@inheritDoc} */ public function supportsReleaseSavepoints() { return false; } /** * {@inheritDoc} */ public function supportsSchemas() { return true; } /** * {@inheritDoc} * * @deprecated */ public function getDefaultSchemaName() { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5513', '%s is deprecated.', __METHOD__, ); return 'dbo'; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function supportsColumnCollation() { return true; } public function supportsSequences(): bool { return true; } public function getAlterSequenceSQL(Sequence $sequence): string { return 'ALTER SEQUENCE ' . $sequence->getQuotedName($this) . ' INCREMENT BY ' . $sequence->getAllocationSize(); } public function getCreateSequenceSQL(Sequence $sequence): string { return 'CREATE SEQUENCE ' . $sequence->getQuotedName($this) . ' START WITH ' . $sequence->getInitialValue() . ' INCREMENT BY ' . $sequence->getAllocationSize() . ' MINVALUE ' . $sequence->getInitialValue(); } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListSequencesSQL($database) { return 'SELECT seq.name, CAST( seq.increment AS VARCHAR(MAX) ) AS increment, -- CAST avoids driver error for sql_variant type CAST( seq.start_value AS VARCHAR(MAX) ) AS start_value -- CAST avoids driver error for sql_variant type FROM sys.sequences AS seq'; } /** * {@inheritDoc} */ public function getSequenceNextValSQL($sequence) { return 'SELECT NEXT VALUE FOR ' . $sequence; } /** * {@inheritDoc} * * @deprecated */ public function hasNativeGuidType() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5509', '%s is deprecated.', __METHOD__, ); return true; } /** * {@inheritDoc} */ public function getDropForeignKeySQL($foreignKey, $table) { if (! $foreignKey instanceof ForeignKeyConstraint) { $foreignKey = new Identifier($foreignKey); } if (! $table instanceof Table) { $table = new Identifier($table); } $foreignKey = $foreignKey->getQuotedName($this); $table = $table->getQuotedName($this); return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $foreignKey; } /** * {@inheritDoc} */ public function getDropIndexSQL($index, $table = null) { if ($index instanceof Index) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $index as an Index object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); $index = $index->getQuotedName($this); } elseif (! is_string($index)) { throw new InvalidArgumentException( __METHOD__ . '() expects $index parameter to be string or ' . Index::class . '.', ); } if ($table instanceof Table) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $table as an Table object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); $table = $table->getQuotedName($this); } elseif (! is_string($table)) { throw new InvalidArgumentException( __METHOD__ . '() expects $table parameter to be string or ' . Table::class . '.', ); } return 'DROP INDEX ' . $index . ' ON ' . $table; } /** * {@inheritDoc} */ protected function _getCreateTableSQL($name, array $columns, array $options = []) { $defaultConstraintsSql = []; $commentsSql = []; $tableComment = $options['comment'] ?? null; if ($tableComment !== null) { $commentsSql[] = $this->getCommentOnTableSQL($name, $tableComment); } // @todo does other code breaks because of this? // force primary keys to be not null foreach ($columns as &$column) { if (! empty($column['primary'])) { $column['notnull'] = true; } // Build default constraints SQL statements. if (isset($column['default'])) { $defaultConstraintsSql[] = 'ALTER TABLE ' . $name . ' ADD' . $this->getDefaultConstraintDeclarationSQL($name, $column); } if (empty($column['comment']) && ! is_numeric($column['comment'])) { continue; } $commentsSql[] = $this->getCreateColumnCommentSQL($name, $column['name'], $column['comment']); } $columnListSql = $this->getColumnDeclarationListSQL($columns); if (isset($options['uniqueConstraints']) && ! empty($options['uniqueConstraints'])) { foreach ($options['uniqueConstraints'] as $constraintName => $definition) { $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($constraintName, $definition); } } if (isset($options['primary']) && ! empty($options['primary'])) { $flags = ''; if (isset($options['primary_index']) && $options['primary_index']->hasFlag('nonclustered')) { $flags = ' NONCLUSTERED'; } $columnListSql .= ', PRIMARY KEY' . $flags . ' (' . implode(', ', array_unique(array_values($options['primary']))) . ')'; } $query = 'CREATE TABLE ' . $name . ' (' . $columnListSql; $check = $this->getCheckDeclarationSQL($columns); if (! empty($check)) { $query .= ', ' . $check; } $query .= ')'; $sql = [$query]; if (isset($options['indexes']) && ! empty($options['indexes'])) { foreach ($options['indexes'] as $index) { $sql[] = $this->getCreateIndexSQL($index, $name); } } if (isset($options['foreignKeys'])) { foreach ($options['foreignKeys'] as $definition) { $sql[] = $this->getCreateForeignKeySQL($definition, $name); } } return array_merge($sql, $commentsSql, $defaultConstraintsSql); } /** * {@inheritDoc} */ public function getCreatePrimaryKeySQL(Index $index, $table) { if ($table instanceof Table) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4798', 'Passing $table as a Table object to %s is deprecated. Pass it as a quoted name instead.', __METHOD__, ); $identifier = $table->getQuotedName($this); } else { $identifier = $table; } $sql = 'ALTER TABLE ' . $identifier . ' ADD PRIMARY KEY'; if ($index->hasFlag('nonclustered')) { $sql .= ' NONCLUSTERED'; } return $sql . ' (' . $this->getIndexFieldDeclarationListSQL($index) . ')'; } /** * Returns the SQL statement for creating a column comment. * * SQL Server does not support native column comments, * therefore the extended properties functionality is used * as a workaround to store them. * The property name used to store column comments is "MS_Description" * which provides compatibility with SQL Server Management Studio, * as column comments are stored in the same property there when * specifying a column's "Description" attribute. * * @param string $tableName The quoted table name to which the column belongs. * @param string $columnName The quoted column name to create the comment for. * @param string|null $comment The column's comment. * * @return string */ protected function getCreateColumnCommentSQL($tableName, $columnName, $comment) { if (strpos($tableName, '.') !== false) { [$schemaSQL, $tableSQL] = explode('.', $tableName); $schemaSQL = $this->quoteStringLiteral($schemaSQL); $tableSQL = $this->quoteStringLiteral($tableSQL); } else { $schemaSQL = "'dbo'"; $tableSQL = $this->quoteStringLiteral($tableName); } return $this->getAddExtendedPropertySQL( 'MS_Description', $comment, 'SCHEMA', $schemaSQL, 'TABLE', $tableSQL, 'COLUMN', $columnName, ); } /** * Returns the SQL snippet for declaring a default constraint. * * @internal The method should be only used from within the SQLServerPlatform class hierarchy. * * @param string $table Name of the table to return the default constraint declaration for. * @param mixed[] $column Column definition. * * @return string * * @throws InvalidArgumentException */ public function getDefaultConstraintDeclarationSQL($table, array $column) { if (! isset($column['default'])) { throw new InvalidArgumentException("Incomplete column definition. 'default' required."); } $columnName = new Identifier($column['name']); return ' CONSTRAINT ' . $this->generateDefaultConstraintName($table, $column['name']) . $this->getDefaultValueDeclarationSQL($column) . ' FOR ' . $columnName->getQuotedName($this); } /** * {@inheritDoc} */ public function getCreateIndexSQL(Index $index, $table) { $constraint = parent::getCreateIndexSQL($index, $table); if ($index->isUnique() && ! $index->isPrimary()) { $constraint = $this->_appendUniqueConstraintDefinition($constraint, $index); } return $constraint; } /** * {@inheritDoc} */ protected function getCreateIndexSQLFlags(Index $index) { $type = ''; if ($index->isUnique()) { $type .= 'UNIQUE '; } if ($index->hasFlag('clustered')) { $type .= 'CLUSTERED '; } elseif ($index->hasFlag('nonclustered')) { $type .= 'NONCLUSTERED '; } return $type; } /** * Extend unique key constraint with required filters * * @param string $sql */ private function _appendUniqueConstraintDefinition($sql, Index $index): string { $fields = []; foreach ($index->getQuotedColumns($this) as $field) { $fields[] = $field . ' IS NOT NULL'; } return $sql . ' WHERE ' . implode(' AND ', $fields); } /** * {@inheritDoc} */ public function getAlterTableSQL(TableDiff $diff) { $queryParts = []; $sql = []; $columnSql = []; $commentsSql = []; $table = $diff->getOldTable() ?? $diff->getName($this); $tableName = $table->getName(); foreach ($diff->getAddedColumns() as $column) { if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) { continue; } $columnProperties = $column->toArray(); $addColumnSql = 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnProperties); if (isset($columnProperties['default'])) { $addColumnSql .= ' CONSTRAINT ' . $this->generateDefaultConstraintName( $tableName, $column->getQuotedName($this), ) . $this->getDefaultValueDeclarationSQL($columnProperties); } $queryParts[] = $addColumnSql; $comment = $this->getColumnComment($column); if (empty($comment) && ! is_numeric($comment)) { continue; } $commentsSql[] = $this->getCreateColumnCommentSQL( $tableName, $column->getQuotedName($this), $comment, ); } foreach ($diff->getDroppedColumns() as $column) { if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) { continue; } $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this); } foreach ($diff->getModifiedColumns() as $columnDiff) { if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) { continue; } $newColumn = $columnDiff->getNewColumn(); $newComment = $this->getColumnComment($newColumn); $hasNewComment = ! empty($newComment) || is_numeric($newComment); $oldColumn = $columnDiff->getOldColumn(); if ($oldColumn instanceof Column) { $oldComment = $this->getColumnComment($oldColumn); $hasOldComment = ! empty($oldComment) || is_numeric($oldComment); if ($hasOldComment && $hasNewComment && $oldComment !== $newComment) { $commentsSql[] = $this->getAlterColumnCommentSQL( $tableName, $newColumn->getQuotedName($this), $newComment, ); } elseif ($hasOldComment && ! $hasNewComment) { $commentsSql[] = $this->getDropColumnCommentSQL( $tableName, $newColumn->getQuotedName($this), ); } elseif (! $hasOldComment && $hasNewComment) { $commentsSql[] = $this->getCreateColumnCommentSQL( $tableName, $newColumn->getQuotedName($this), $newComment, ); } } // Do not add query part if only comment has changed. if ($columnDiff->hasCommentChanged() && count($columnDiff->changedProperties) === 1) { continue; } $requireDropDefaultConstraint = $this->alterColumnRequiresDropDefaultConstraint($columnDiff); if ($requireDropDefaultConstraint) { $oldColumn = $columnDiff->getOldColumn(); if ($oldColumn !== null) { $oldColumnName = $oldColumn->getName(); } else { $oldColumnName = $columnDiff->oldColumnName; } $queryParts[] = $this->getAlterTableDropDefaultConstraintClause($tableName, $oldColumnName); } $columnProperties = $newColumn->toArray(); $queryParts[] = 'ALTER COLUMN ' . $this->getColumnDeclarationSQL($newColumn->getQuotedName($this), $columnProperties); if ( ! isset($columnProperties['default']) || (! $requireDropDefaultConstraint && ! $columnDiff->hasDefaultChanged()) ) { continue; } $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($tableName, $newColumn); } $tableNameSQL = $table->getQuotedName($this); foreach ($diff->getRenamedColumns() as $oldColumnName => $newColumn) { if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $newColumn, $diff, $columnSql)) { continue; } $oldColumnName = new Identifier($oldColumnName); $sql[] = "sp_rename '" . $tableNameSQL . '.' . $oldColumnName->getQuotedName($this) . "', '" . $newColumn->getQuotedName($this) . "', 'COLUMN'"; // Recreate default constraint with new column name if necessary (for future reference). if ($newColumn->getDefault() === null) { continue; } $queryParts[] = $this->getAlterTableDropDefaultConstraintClause( $tableName, $oldColumnName->getQuotedName($this), ); $queryParts[] = $this->getAlterTableAddDefaultConstraintClause($tableName, $newColumn); } $tableSql = []; if ($this->onSchemaAlterTable($diff, $tableSql)) { return array_merge($tableSql, $columnSql); } foreach ($queryParts as $query) { $sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . $query; } $sql = array_merge($sql, $commentsSql); $newName = $diff->getNewName(); if ($newName !== false) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5663', 'Generation of "rename table" SQL using %s is deprecated. Use getRenameTableSQL() instead.', __METHOD__, ); $sql = array_merge($sql, $this->getRenameTableSQL($tableName, $newName->getName())); } $sql = array_merge( $this->getPreAlterTableIndexForeignKeySQL($diff), $sql, $this->getPostAlterTableIndexForeignKeySQL($diff), ); return array_merge($sql, $tableSql, $columnSql); } /** * {@inheritDoc} */ public function getRenameTableSQL(string $oldName, string $newName): array { return [ sprintf('sp_rename %s, %s', $this->quoteStringLiteral($oldName), $this->quoteStringLiteral($newName)), /* Rename table's default constraints names * to match the new table name. * This is necessary to ensure that the default * constraints can be referenced in future table * alterations as the table name is encoded in * default constraints' names. */ sprintf( <<<'SQL' DECLARE @sql NVARCHAR(MAX) = N''; SELECT @sql += N'EXEC sp_rename N''' + dc.name + ''', N''' + REPLACE(dc.name, '%s', '%s') + ''', ''OBJECT'';' FROM sys.default_constraints dc JOIN sys.tables tbl ON dc.parent_object_id = tbl.object_id WHERE tbl.name = %s; EXEC sp_executesql @sql SQL, $this->generateIdentifierName($oldName), $this->generateIdentifierName($newName), $this->quoteStringLiteral($newName), ), ]; } /** * Returns the SQL clause for adding a default constraint in an ALTER TABLE statement. * * @param string $tableName The name of the table to generate the clause for. * @param Column $column The column to generate the clause for. */ private function getAlterTableAddDefaultConstraintClause($tableName, Column $column): string { $columnDef = $column->toArray(); $columnDef['name'] = $column->getQuotedName($this); return 'ADD' . $this->getDefaultConstraintDeclarationSQL($tableName, $columnDef); } /** * Returns the SQL clause for dropping an existing default constraint in an ALTER TABLE statement. * * @param string $tableName The name of the table to generate the clause for. * @param string $columnName The name of the column to generate the clause for. */ private function getAlterTableDropDefaultConstraintClause($tableName, $columnName): string { return 'DROP CONSTRAINT ' . $this->generateDefaultConstraintName($tableName, $columnName); } /** * Checks whether a column alteration requires dropping its default constraint first. * * Different to other database vendors SQL Server implements column default values * as constraints and therefore changes in a column's default value as well as changes * in a column's type require dropping the default constraint first before being to * alter the particular column to the new definition. */ private function alterColumnRequiresDropDefaultConstraint(ColumnDiff $columnDiff): bool { $oldColumn = $columnDiff->getOldColumn(); // We can only decide whether to drop an existing default constraint // if we know the original default value. if (! $oldColumn instanceof Column) { return false; } // We only need to drop an existing default constraint if we know the // column was defined with a default value before. if ($oldColumn->getDefault() === null) { return false; } // We need to drop an existing default constraint if the column was // defined with a default value before and it has changed. if ($columnDiff->hasDefaultChanged()) { return true; } // We need to drop an existing default constraint if the column was // defined with a default value before and the native column type has changed. return $columnDiff->hasTypeChanged() || $columnDiff->hasFixedChanged(); } /** * Returns the SQL statement for altering a column comment. * * SQL Server does not support native column comments, * therefore the extended properties functionality is used * as a workaround to store them. * The property name used to store column comments is "MS_Description" * which provides compatibility with SQL Server Management Studio, * as column comments are stored in the same property there when * specifying a column's "Description" attribute. * * @param string $tableName The quoted table name to which the column belongs. * @param string $columnName The quoted column name to alter the comment for. * @param string|null $comment The column's comment. * * @return string */ protected function getAlterColumnCommentSQL($tableName, $columnName, $comment) { if (strpos($tableName, '.') !== false) { [$schemaSQL, $tableSQL] = explode('.', $tableName); $schemaSQL = $this->quoteStringLiteral($schemaSQL); $tableSQL = $this->quoteStringLiteral($tableSQL); } else { $schemaSQL = "'dbo'"; $tableSQL = $this->quoteStringLiteral($tableName); } return $this->getUpdateExtendedPropertySQL( 'MS_Description', $comment, 'SCHEMA', $schemaSQL, 'TABLE', $tableSQL, 'COLUMN', $columnName, ); } /** * Returns the SQL statement for dropping a column comment. * * SQL Server does not support native column comments, * therefore the extended properties functionality is used * as a workaround to store them. * The property name used to store column comments is "MS_Description" * which provides compatibility with SQL Server Management Studio, * as column comments are stored in the same property there when * specifying a column's "Description" attribute. * * @param string $tableName The quoted table name to which the column belongs. * @param string $columnName The quoted column name to drop the comment for. * * @return string */ protected function getDropColumnCommentSQL($tableName, $columnName) { if (strpos($tableName, '.') !== false) { [$schemaSQL, $tableSQL] = explode('.', $tableName); $schemaSQL = $this->quoteStringLiteral($schemaSQL); $tableSQL = $this->quoteStringLiteral($tableSQL); } else { $schemaSQL = "'dbo'"; $tableSQL = $this->quoteStringLiteral($tableName); } return $this->getDropExtendedPropertySQL( 'MS_Description', 'SCHEMA', $schemaSQL, 'TABLE', $tableSQL, 'COLUMN', $columnName, ); } /** * {@inheritDoc} */ protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName) { return [sprintf( "EXEC sp_rename N'%s.%s', N'%s', N'INDEX'", $tableName, $oldIndexName, $index->getQuotedName($this), ), ]; } /** * Returns the SQL statement for adding an extended property to a database object. * * @internal The method should be only used from within the SQLServerPlatform class hierarchy. * * @link http://msdn.microsoft.com/en-us/library/ms180047%28v=sql.90%29.aspx * * @param string $name The name of the property to add. * @param string|null $value The value of the property to add. * @param string|null $level0Type The type of the object at level 0 the property belongs to. * @param string|null $level0Name The name of the object at level 0 the property belongs to. * @param string|null $level1Type The type of the object at level 1 the property belongs to. * @param string|null $level1Name The name of the object at level 1 the property belongs to. * @param string|null $level2Type The type of the object at level 2 the property belongs to. * @param string|null $level2Name The name of the object at level 2 the property belongs to. * * @return string */ public function getAddExtendedPropertySQL( $name, $value = null, $level0Type = null, $level0Name = null, $level1Type = null, $level1Name = null, $level2Type = null, $level2Name = null ) { return 'EXEC sp_addextendedproperty ' . 'N' . $this->quoteStringLiteral($name) . ', N' . $this->quoteStringLiteral((string) $value) . ', ' . 'N' . $this->quoteStringLiteral((string) $level0Type) . ', ' . $level0Name . ', ' . 'N' . $this->quoteStringLiteral((string) $level1Type) . ', ' . $level1Name . ', ' . 'N' . $this->quoteStringLiteral((string) $level2Type) . ', ' . $level2Name; } /** * Returns the SQL statement for dropping an extended property from a database object. * * @internal The method should be only used from within the SQLServerPlatform class hierarchy. * * @link http://technet.microsoft.com/en-gb/library/ms178595%28v=sql.90%29.aspx * * @param string $name The name of the property to drop. * @param string|null $level0Type The type of the object at level 0 the property belongs to. * @param string|null $level0Name The name of the object at level 0 the property belongs to. * @param string|null $level1Type The type of the object at level 1 the property belongs to. * @param string|null $level1Name The name of the object at level 1 the property belongs to. * @param string|null $level2Type The type of the object at level 2 the property belongs to. * @param string|null $level2Name The name of the object at level 2 the property belongs to. * * @return string */ public function getDropExtendedPropertySQL( $name, $level0Type = null, $level0Name = null, $level1Type = null, $level1Name = null, $level2Type = null, $level2Name = null ) { return 'EXEC sp_dropextendedproperty ' . 'N' . $this->quoteStringLiteral($name) . ', ' . 'N' . $this->quoteStringLiteral((string) $level0Type) . ', ' . $level0Name . ', ' . 'N' . $this->quoteStringLiteral((string) $level1Type) . ', ' . $level1Name . ', ' . 'N' . $this->quoteStringLiteral((string) $level2Type) . ', ' . $level2Name; } /** * Returns the SQL statement for updating an extended property of a database object. * * @internal The method should be only used from within the SQLServerPlatform class hierarchy. * * @link http://msdn.microsoft.com/en-us/library/ms186885%28v=sql.90%29.aspx * * @param string $name The name of the property to update. * @param string|null $value The value of the property to update. * @param string|null $level0Type The type of the object at level 0 the property belongs to. * @param string|null $level0Name The name of the object at level 0 the property belongs to. * @param string|null $level1Type The type of the object at level 1 the property belongs to. * @param string|null $level1Name The name of the object at level 1 the property belongs to. * @param string|null $level2Type The type of the object at level 2 the property belongs to. * @param string|null $level2Name The name of the object at level 2 the property belongs to. * * @return string */ public function getUpdateExtendedPropertySQL( $name, $value = null, $level0Type = null, $level0Name = null, $level1Type = null, $level1Name = null, $level2Type = null, $level2Name = null ) { return 'EXEC sp_updateextendedproperty ' . 'N' . $this->quoteStringLiteral($name) . ', N' . $this->quoteStringLiteral((string) $value) . ', ' . 'N' . $this->quoteStringLiteral((string) $level0Type) . ', ' . $level0Name . ', ' . 'N' . $this->quoteStringLiteral((string) $level1Type) . ', ' . $level1Name . ', ' . 'N' . $this->quoteStringLiteral((string) $level2Type) . ', ' . $level2Name; } /** * {@inheritDoc} */ public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName) { return 'INSERT INTO ' . $quotedTableName . ' DEFAULT VALUES'; } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * {@inheritDoc} */ public function getListTablesSQL() { // "sysdiagrams" table must be ignored as it's internal SQL Server table for Database Diagrams // Category 2 must be ignored as it is "MS SQL Server 'pseudo-system' object[s]" for replication return 'SELECT name, SCHEMA_NAME (uid) AS schema_name FROM sysobjects' . " WHERE type = 'U' AND name != 'sysdiagrams' AND category != 2 ORDER BY name"; } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * {@inheritDoc} */ public function getListTableColumnsSQL($table, $database = null) { return "SELECT col.name, type.name AS type, col.max_length AS length, ~col.is_nullable AS notnull, def.definition AS [default], col.scale, col.precision, col.is_identity AS autoincrement, col.collation_name AS collation, CAST(prop.value AS NVARCHAR(MAX)) AS comment -- CAST avoids driver error for sql_variant type FROM sys.columns AS col JOIN sys.types AS type ON col.user_type_id = type.user_type_id JOIN sys.objects AS obj ON col.object_id = obj.object_id JOIN sys.schemas AS scm ON obj.schema_id = scm.schema_id LEFT JOIN sys.default_constraints def ON col.default_object_id = def.object_id AND col.object_id = def.parent_object_id LEFT JOIN sys.extended_properties AS prop ON obj.object_id = prop.major_id AND col.column_id = prop.minor_id AND prop.name = 'MS_Description' WHERE obj.type = 'U' AND " . $this->getTableWhereClause($table, 'scm.name', 'obj.name'); } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * @param string $table * @param string|null $database * * @return string */ public function getListTableForeignKeysSQL($table, $database = null) { return 'SELECT f.name AS ForeignKey, SCHEMA_NAME (f.SCHEMA_ID) AS SchemaName, OBJECT_NAME (f.parent_object_id) AS TableName, COL_NAME (fc.parent_object_id,fc.parent_column_id) AS ColumnName, SCHEMA_NAME (o.SCHEMA_ID) ReferenceSchemaName, OBJECT_NAME (f.referenced_object_id) AS ReferenceTableName, COL_NAME(fc.referenced_object_id,fc.referenced_column_id) AS ReferenceColumnName, f.delete_referential_action_desc, f.update_referential_action_desc FROM sys.foreign_keys AS f INNER JOIN sys.foreign_key_columns AS fc INNER JOIN sys.objects AS o ON o.OBJECT_ID = fc.referenced_object_id ON f.OBJECT_ID = fc.constraint_object_id WHERE ' . $this->getTableWhereClause($table, 'SCHEMA_NAME (f.schema_id)', 'OBJECT_NAME (f.parent_object_id)') . ' ORDER BY fc.constraint_column_id'; } /** * @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. * * {@inheritDoc} */ public function getListTableIndexesSQL($table, $database = null) { return "SELECT idx.name AS key_name, col.name AS column_name, ~idx.is_unique AS non_unique, idx.is_primary_key AS [primary], CASE idx.type WHEN '1' THEN 'clustered' WHEN '2' THEN 'nonclustered' ELSE NULL END AS flags FROM sys.tables AS tbl JOIN sys.schemas AS scm ON tbl.schema_id = scm.schema_id JOIN sys.indexes AS idx ON tbl.object_id = idx.object_id JOIN sys.index_columns AS idxcol ON idx.object_id = idxcol.object_id AND idx.index_id = idxcol.index_id JOIN sys.columns AS col ON idxcol.object_id = col.object_id AND idxcol.column_id = col.column_id WHERE " . $this->getTableWhereClause($table, 'scm.name', 'tbl.name') . ' ORDER BY idx.index_id ASC, idxcol.key_ordinal ASC'; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListViewsSQL($database) { return "SELECT name, definition FROM sysobjects INNER JOIN sys.sql_modules ON sysobjects.id = sys.sql_modules.object_id WHERE type = 'V' ORDER BY name"; } /** * Returns the where clause to filter schema and table name in a query. * * @param string $table The full qualified name of the table. * @param string $schemaColumn The name of the column to compare the schema to in the where clause. * @param string $tableColumn The name of the column to compare the table to in the where clause. */ private function getTableWhereClause($table, $schemaColumn, $tableColumn): string { if (strpos($table, '.') !== false) { [$schema, $table] = explode('.', $table); $schema = $this->quoteStringLiteral($schema); $table = $this->quoteStringLiteral($table); } else { $schema = 'SCHEMA_NAME()'; $table = $this->quoteStringLiteral($table); } return sprintf('(%s = %s AND %s = %s)', $tableColumn, $table, $schemaColumn, $schema); } /** * {@inheritDoc} */ public function getLocateExpression($str, $substr, $startPos = false) { if ($startPos === false) { return 'CHARINDEX(' . $substr . ', ' . $str . ')'; } return 'CHARINDEX(' . $substr . ', ' . $str . ', ' . $startPos . ')'; } /** * {@inheritDoc} */ public function getModExpression($expression1, $expression2) { return $expression1 . ' % ' . $expression2; } /** * {@inheritDoc} */ public function getTrimExpression($str, $mode = TrimMode::UNSPECIFIED, $char = false) { if ($char === false) { switch ($mode) { case TrimMode::LEADING: $trimFn = 'LTRIM'; break; case TrimMode::TRAILING: $trimFn = 'RTRIM'; break; default: return 'LTRIM(RTRIM(' . $str . '))'; } return $trimFn . '(' . $str . ')'; } $pattern = "'%[^' + " . $char . " + ']%'"; if ($mode === TrimMode::LEADING) { return 'stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null)'; } if ($mode === TrimMode::TRAILING) { return 'reverse(stuff(reverse(' . $str . '), 1, ' . 'patindex(' . $pattern . ', reverse(' . $str . ')) - 1, null))'; } return 'reverse(stuff(reverse(stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null)), 1, ' . 'patindex(' . $pattern . ', reverse(stuff(' . $str . ', 1, patindex(' . $pattern . ', ' . $str . ') - 1, null))) - 1, null))'; } /** * {@inheritDoc} */ public function getConcatExpression() { return sprintf('CONCAT(%s)', implode(', ', func_get_args())); } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy. */ public function getListDatabasesSQL() { return 'SELECT * FROM sys.databases'; } /** * {@inheritDoc} * * @deprecated Use {@see SQLServerSchemaManager::listSchemaNames()} instead. */ public function getListNamespacesSQL() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4503', 'SQLServerPlatform::getListNamespacesSQL() is deprecated,' . ' use SQLServerSchemaManager::listSchemaNames() instead.', ); return "SELECT name FROM sys.schemas WHERE name NOT IN('guest', 'INFORMATION_SCHEMA', 'sys')"; } /** * {@inheritDoc} */ public function getSubstringExpression($string, $start, $length = null) { if ($length !== null) { return 'SUBSTRING(' . $string . ', ' . $start . ', ' . $length . ')'; } return 'SUBSTRING(' . $string . ', ' . $start . ', LEN(' . $string . ') - ' . $start . ' + 1)'; } /** * {@inheritDoc} */ public function getLengthExpression($column) { return 'LEN(' . $column . ')'; } public function getCurrentDatabaseExpression(): string { return 'DB_NAME()'; } /** * {@inheritDoc} */ public function getSetTransactionIsolationSQL($level) { return 'SET TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSQL($level); } /** * {@inheritDoc} */ public function getIntegerTypeDeclarationSQL(array $column) { return 'INT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function getBigIntTypeDeclarationSQL(array $column) { return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function getSmallIntTypeDeclarationSQL(array $column) { return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column); } /** * {@inheritDoc} */ public function getGuidTypeDeclarationSQL(array $column) { return 'UNIQUEIDENTIFIER'; } /** * {@inheritDoc} */ public function getDateTimeTzTypeDeclarationSQL(array $column) { return 'DATETIMEOFFSET(6)'; } /** * {@inheritDoc} */ public function getAsciiStringTypeDeclarationSQL(array $column): string { $length = $column['length'] ?? null; if (! isset($column['fixed'])) { return sprintf('VARCHAR(%d)', $length ?? 255); } return sprintf('CHAR(%d)', $length ?? 255); } /** * {@inheritDoc} */ protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) { if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'Relying on the default string column length on SQL Server is deprecated' . ', specify the length explicitly.', ); } return $fixed ? 'NCHAR(' . ($length > 0 ? $length : 255) . ')' : 'NVARCHAR(' . ($length > 0 ? $length : 255) . ')'; } /** * {@inheritDoc} */ protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/) { if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'Relying on the default binary column length on SQL Server is deprecated' . ', specify the length explicitly.', ); } return $fixed ? 'BINARY(' . ($length > 0 ? $length : 255) . ')' : 'VARBINARY(' . ($length > 0 ? $length : 255) . ')'; } /** * {@inheritDoc} * * @deprecated */ public function getBinaryMaxLength() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/3263', 'SQLServerPlatform::getBinaryMaxLength() is deprecated.', ); return 8000; } /** * {@inheritDoc} */ public function getClobTypeDeclarationSQL(array $column) { return 'VARCHAR(MAX)'; } /** * {@inheritDoc} */ protected function _getCommonIntegerTypeDeclarationSQL(array $column) { return ! empty($column['autoincrement']) ? ' IDENTITY' : ''; } /** * {@inheritDoc} */ public function getDateTimeTypeDeclarationSQL(array $column) { // 3 - microseconds precision length // http://msdn.microsoft.com/en-us/library/ms187819.aspx return 'DATETIME2(6)'; } /** * {@inheritDoc} */ public function getDateTypeDeclarationSQL(array $column) { return 'DATE'; } /** * {@inheritDoc} */ public function getTimeTypeDeclarationSQL(array $column) { return 'TIME(0)'; } /** * {@inheritDoc} */ public function getBooleanTypeDeclarationSQL(array $column) { return 'BIT'; } /** * {@inheritDoc} */ protected function doModifyLimitQuery($query, $limit, $offset) { if ($limit === null && $offset <= 0) { return $query; } if ($this->shouldAddOrderBy($query)) { if (preg_match('/^SELECT\s+DISTINCT/im', $query) > 0) { // SQL Server won't let us order by a non-selected column in a DISTINCT query, // so we have to do this madness. This says, order by the first column in the // result. SQL Server's docs say that a nonordered query's result order is non- // deterministic anyway, so this won't do anything that a bunch of update and // deletes to the table wouldn't do anyway. $query .= ' ORDER BY 1'; } else { // In another DBMS, we could do ORDER BY 0, but SQL Server gets angry if you // use constant expressions in the order by list. $query .= ' ORDER BY (SELECT 0)'; } } // This looks somewhat like MYSQL, but limit/offset are in inverse positions // Supposedly SQL:2008 core standard. // Per TSQL spec, FETCH NEXT n ROWS ONLY is not valid without OFFSET n ROWS. $query .= sprintf(' OFFSET %d ROWS', $offset); if ($limit !== null) { $query .= sprintf(' FETCH NEXT %d ROWS ONLY', $limit); } return $query; } /** * {@inheritDoc} */ public function convertBooleans($item) { if (is_array($item)) { foreach ($item as $key => $value) { if (! is_bool($value) && ! is_numeric($value)) { continue; } $item[$key] = (int) (bool) $value; } } elseif (is_bool($item) || is_numeric($item)) { $item = (int) (bool) $item; } return $item; } /** * {@inheritDoc} */ public function getCreateTemporaryTableSnippetSQL() { return 'CREATE TABLE'; } /** * {@inheritDoc} */ public function getTemporaryTableName($tableName) { return '#' . $tableName; } /** * {@inheritDoc} */ public function getDateTimeFormatString() { return 'Y-m-d H:i:s.u'; } /** * {@inheritDoc} */ public function getDateFormatString() { return 'Y-m-d'; } /** * {@inheritDoc} */ public function getTimeFormatString() { return 'H:i:s'; } /** * {@inheritDoc} */ public function getDateTimeTzFormatString() { return 'Y-m-d H:i:s.u P'; } /** * {@inheritDoc} */ public function getName() { return 'mssql'; } /** * {@inheritDoc} */ protected function initializeDoctrineTypeMappings() { $this->doctrineTypeMapping = [ 'bigint' => Types::BIGINT, 'binary' => Types::BINARY, 'bit' => Types::BOOLEAN, 'blob' => Types::BLOB, 'char' => Types::STRING, 'date' => Types::DATE_MUTABLE, 'datetime' => Types::DATETIME_MUTABLE, 'datetime2' => Types::DATETIME_MUTABLE, 'datetimeoffset' => Types::DATETIMETZ_MUTABLE, 'decimal' => Types::DECIMAL, 'double' => Types::FLOAT, 'double precision' => Types::FLOAT, 'float' => Types::FLOAT, 'image' => Types::BLOB, 'int' => Types::INTEGER, 'money' => Types::INTEGER, 'nchar' => Types::STRING, 'ntext' => Types::TEXT, 'numeric' => Types::DECIMAL, 'nvarchar' => Types::STRING, 'real' => Types::FLOAT, 'smalldatetime' => Types::DATETIME_MUTABLE, 'smallint' => Types::SMALLINT, 'smallmoney' => Types::INTEGER, 'text' => Types::TEXT, 'time' => Types::TIME_MUTABLE, 'tinyint' => Types::SMALLINT, 'uniqueidentifier' => Types::GUID, 'varbinary' => Types::BINARY, 'varchar' => Types::STRING, ]; } /** * {@inheritDoc} */ public function createSavePoint($savepoint) { return 'SAVE TRANSACTION ' . $savepoint; } /** * {@inheritDoc} */ public function releaseSavePoint($savepoint) { return ''; } /** * {@inheritDoc} */ public function rollbackSavePoint($savepoint) { return 'ROLLBACK TRANSACTION ' . $savepoint; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getForeignKeyReferentialActionSQL($action) { // RESTRICT is not supported, therefore falling back to NO ACTION. if (strtoupper($action) === 'RESTRICT') { return 'NO ACTION'; } return parent::getForeignKeyReferentialActionSQL($action); } public function appendLockHint(string $fromClause, int $lockMode): string { switch ($lockMode) { case LockMode::NONE: case LockMode::OPTIMISTIC: return $fromClause; case LockMode::PESSIMISTIC_READ: return $fromClause . ' WITH (HOLDLOCK, ROWLOCK)'; case LockMode::PESSIMISTIC_WRITE: return $fromClause . ' WITH (UPDLOCK, ROWLOCK)'; default: throw InvalidLockMode::fromLockMode($lockMode); } } /** * {@inheritDoc} * * @deprecated This API is not portable. */ public function getForUpdateSQL() { return ' '; } /** * {@inheritDoc} * * @deprecated Implement {@see createReservedKeywordsList()} instead. */ protected function getReservedKeywordsClass() { Deprecation::triggerIfCalledFromOutside( 'doctrine/dbal', 'https://github.com/doctrine/dbal/issues/4510', 'SQLServerPlatform::getReservedKeywordsClass() is deprecated,' . ' use SQLServerPlatform::createReservedKeywordsList() instead.', ); return Keywords\SQLServer2012Keywords::class; } /** * {@inheritDoc} */ public function quoteSingleIdentifier($str) { return '[' . str_replace(']', ']]', $str) . ']'; } /** * {@inheritDoc} */ public function getTruncateTableSQL($tableName, $cascade = false) { $tableIdentifier = new Identifier($tableName); return 'TRUNCATE TABLE ' . $tableIdentifier->getQuotedName($this); } /** * {@inheritDoc} */ public function getBlobTypeDeclarationSQL(array $column) { return 'VARBINARY(MAX)'; } /** * {@inheritDoc} * * @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy. */ public function getColumnDeclarationSQL($name, array $column) { if (isset($column['columnDefinition'])) { $columnDef = $this->getCustomTypeDeclarationSQL($column); } else { $collation = ! empty($column['collation']) ? ' ' . $this->getColumnCollationDeclarationSQL($column['collation']) : ''; $notnull = ! empty($column['notnull']) ? ' NOT NULL' : ''; if (! empty($column['unique'])) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5656', 'The usage of the "unique" column property is deprecated. Use unique constraints instead.', ); $unique = ' ' . $this->getUniqueFieldDeclarationSQL(); } else { $unique = ''; } if (! empty($column['check'])) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5656', 'The usage of the "check" column property is deprecated.', ); $check = ' ' . $column['check']; } else { $check = ''; } $typeDecl = $column['type']->getSQLDeclaration($column, $this); $columnDef = $typeDecl . $collation . $notnull . $unique . $check; } return $name . ' ' . $columnDef; } /** * {@inheritDoc} * * SQL Server does not support quoting collation identifiers. */ public function getColumnCollationDeclarationSQL($collation) { return 'COLLATE ' . $collation; } public function columnsEqual(Column $column1, Column $column2): bool { if (! parent::columnsEqual($column1, $column2)) { return false; } return $this->getDefaultValueDeclarationSQL($column1->toArray()) === $this->getDefaultValueDeclarationSQL($column2->toArray()); } protected function getLikeWildcardCharacters(): string { return parent::getLikeWildcardCharacters() . '[]^'; } /** * Returns a unique default constraint name for a table and column. * * @param string $table Name of the table to generate the unique default constraint name for. * @param string $column Name of the column in the table to generate the unique default constraint name for. */ private function generateDefaultConstraintName($table, $column): string { return 'DF_' . $this->generateIdentifierName($table) . '_' . $this->generateIdentifierName($column); } /** * Returns a hash value for a given identifier. * * @param string $identifier Identifier to generate a hash value for. */ private function generateIdentifierName($identifier): string { // Always generate name for unquoted identifiers to ensure consistency. $identifier = new Identifier($identifier); return strtoupper(dechex(crc32($identifier->getName()))); } protected function getCommentOnTableSQL(string $tableName, ?string $comment): string { return sprintf( <<<'SQL' EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N%s, @level0type=N'SCHEMA', @level0name=N'dbo', @level1type=N'TABLE', @level1name=N%s SQL , $this->quoteStringLiteral((string) $comment), $this->quoteStringLiteral($tableName), ); } /** @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. */ public function getListTableMetadataSQL(string $table): string { return sprintf( <<<'SQL' SELECT p.value AS [table_comment] FROM sys.tables AS tbl INNER JOIN sys.extended_properties AS p ON p.major_id=tbl.object_id AND p.minor_id=0 AND p.class=1 WHERE (tbl.name=N%s and SCHEMA_NAME(tbl.schema_id)=N'dbo' and p.name=N'MS_Description') SQL , $this->quoteStringLiteral($table), ); } /** @param string $query */ private function shouldAddOrderBy($query): bool { // Find the position of the last instance of ORDER BY and ensure it is not within a parenthetical statement // but can be in a newline $matches = []; $matchesCount = preg_match_all('/[\\s]+order\\s+by\\s/im', $query, $matches, PREG_OFFSET_CAPTURE); if ($matchesCount === 0) { return true; } // ORDER BY instance may be in a subquery after ORDER BY // e.g. SELECT col1 FROM test ORDER BY (SELECT col2 from test ORDER BY col2) // if in the searched query ORDER BY clause was found where // number of open parentheses after the occurrence of the clause is equal to // number of closed brackets after the occurrence of the clause, // it means that ORDER BY is included in the query being checked while ($matchesCount > 0) { $orderByPos = $matches[0][--$matchesCount][1]; $openBracketsCount = substr_count($query, '(', $orderByPos); $closedBracketsCount = substr_count($query, ')', $orderByPos); if ($openBracketsCount === $closedBracketsCount) { return false; } } return true; } public function createSchemaManager(Connection $connection): SQLServerSchemaManager { return new SQLServerSchemaManager($connection, $this); } } PK!h>">"dbal/src/Result.phpnu[result = $result; $this->connection = $connection; } /** * Returns the next row of the result as a numeric array or FALSE if there are no more rows. * * @return list|false * * @throws Exception */ public function fetchNumeric() { try { return $this->result->fetchNumeric(); } catch (DriverException $e) { throw $this->connection->convertException($e); } } /** * Returns the next row of the result as an associative array or FALSE if there are no more rows. * * @return array|false * * @throws Exception */ public function fetchAssociative() { try { return $this->result->fetchAssociative(); } catch (DriverException $e) { throw $this->connection->convertException($e); } } /** * Returns the first value of the next row of the result or FALSE if there are no more rows. * * @return mixed|false * * @throws Exception */ public function fetchOne() { try { return $this->result->fetchOne(); } catch (DriverException $e) { throw $this->connection->convertException($e); } } /** * Returns an array containing all of the result rows represented as numeric arrays. * * @return list> * * @throws Exception */ public function fetchAllNumeric(): array { try { return $this->result->fetchAllNumeric(); } catch (DriverException $e) { throw $this->connection->convertException($e); } } /** * Returns an array containing all of the result rows represented as associative arrays. * * @return list> * * @throws Exception */ public function fetchAllAssociative(): array { try { return $this->result->fetchAllAssociative(); } catch (DriverException $e) { throw $this->connection->convertException($e); } } /** * Returns an array containing the values of the first column of the result. * * @return array * * @throws Exception */ public function fetchAllKeyValue(): array { $this->ensureHasKeyValue(); $data = []; foreach ($this->fetchAllNumeric() as [$key, $value]) { $data[$key] = $value; } return $data; } /** * Returns an associative array with the keys mapped to the first column and the values being * an associative array representing the rest of the columns and their values. * * @return array> * * @throws Exception */ public function fetchAllAssociativeIndexed(): array { $data = []; foreach ($this->fetchAllAssociative() as $row) { $data[array_shift($row)] = $row; } return $data; } /** * @return list * * @throws Exception */ public function fetchFirstColumn(): array { try { return $this->result->fetchFirstColumn(); } catch (DriverException $e) { throw $this->connection->convertException($e); } } /** * @return Traversable> * * @throws Exception */ public function iterateNumeric(): Traversable { while (($row = $this->fetchNumeric()) !== false) { yield $row; } } /** * @return Traversable> * * @throws Exception */ public function iterateAssociative(): Traversable { while (($row = $this->fetchAssociative()) !== false) { yield $row; } } /** * {@inheritDoc} * * @throws Exception */ public function iterateKeyValue(): Traversable { $this->ensureHasKeyValue(); foreach ($this->iterateNumeric() as [$key, $value]) { yield $key => $value; } } /** * Returns an iterator over the result set with the keys mapped to the first column and the values being * an associative array representing the rest of the columns and their values. * * @return Traversable> * * @throws Exception */ public function iterateAssociativeIndexed(): Traversable { foreach ($this->iterateAssociative() as $row) { yield array_shift($row) => $row; } } /** * @return Traversable * * @throws Exception */ public function iterateColumn(): Traversable { while (($value = $this->fetchOne()) !== false) { yield $value; } } /** @throws Exception */ public function rowCount(): int { try { return $this->result->rowCount(); } catch (DriverException $e) { throw $this->connection->convertException($e); } } /** @throws Exception */ public function columnCount(): int { try { return $this->result->columnCount(); } catch (DriverException $e) { throw $this->connection->convertException($e); } } public function free(): void { $this->result->free(); } /** @throws Exception */ private function ensureHasKeyValue(): void { $columnCount = $this->columnCount(); if ($columnCount < 2) { throw NoKeyValue::fromColumnCount($columnCount); } } /** * BC layer for a wide-spread use-case of old DBAL APIs * * @deprecated Use {@see fetchNumeric()}, {@see fetchAssociative()} or {@see fetchOne()} instead. * * @psalm-param FetchMode::* $mode * * @return mixed * * @throws Exception */ public function fetch(int $mode = FetchMode::ASSOCIATIVE) { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4007', '%s is deprecated, please use fetchNumeric(), fetchAssociative() or fetchOne() instead.', __METHOD__, ); if (func_num_args() > 1) { throw new LogicException('Only invocations with one argument are still supported by this legacy API.'); } if ($mode === FetchMode::ASSOCIATIVE) { return $this->fetchAssociative(); } if ($mode === FetchMode::NUMERIC) { return $this->fetchNumeric(); } if ($mode === FetchMode::COLUMN) { return $this->fetchOne(); } throw new LogicException('Only fetch modes declared on Doctrine\DBAL\FetchMode are supported by legacy API.'); } /** * BC layer for a wide-spread use-case of old DBAL APIs * * @deprecated Use {@see fetchAllNumeric()}, {@see fetchAllAssociative()} or {@see fetchFirstColumn()} instead. * * @psalm-param FetchMode::* $mode * * @return list * * @throws Exception */ public function fetchAll(int $mode = FetchMode::ASSOCIATIVE): array { Deprecation::trigger( 'doctrine/dbal', 'https://github.com/doctrine/dbal/pull/4007', '%s is deprecated, please use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead.', __METHOD__, ); if (func_num_args() > 1) { throw new LogicException('Only invocations with one argument are still supported by this legacy API.'); } if ($mode === FetchMode::ASSOCIATIVE) { return $this->fetchAllAssociative(); } if ($mode === FetchMode::NUMERIC) { return $this->fetchAllNumeric(); } if ($mode === FetchMode::COLUMN) { return $this->fetchFirstColumn(); } throw new LogicException('Only fetch modes declared on Doctrine\DBAL\FetchMode are supported by legacy API.'); } } PK!SbΘ6dbal/src/SQL/Builder/CreateSchemaObjectsSQLBuilder.phpnu[platform = $platform; } /** * @return list * * @throws Exception */ public function buildSQL(Schema $schema): array { return array_merge( $this->buildNamespaceStatements($schema->getNamespaces()), $this->buildSequenceStatements($schema->getSequences()), $this->buildTableStatements($schema->getTables()), ); } /** * @param list $namespaces * * @return list * * @throws Exception */ private function buildNamespaceStatements(array $namespaces): array { $statements = []; if ($this->platform->supportsSchemas()) { foreach ($namespaces as $namespace) { $statements[] = $this->platform->getCreateSchemaSQL($namespace); } } return $statements; } /** * @param list
$tables * * @return list * * @throws Exception */ private function buildTableStatements(array $tables): array { return $this->platform->getCreateTablesSQL($tables); } /** * @param list $sequences * * @return list * * @throws Exception */ private function buildSequenceStatements(array $sequences): array { $statements = []; foreach ($sequences as $sequence) { $statements[] = $this->platform->getCreateSequenceSQL($sequence); } return $statements; } } PK!cC)dbal/src/SQL/Builder/SelectSQLBuilder.phpnu[platform = $platform; $this->forUpdateSQL = $forUpdateSQL; $this->skipLockedSQL = $skipLockedSQL; } /** @throws Exception */ public function buildSQL(SelectQuery $query): string { $parts = ['SELECT']; if ($query->isDistinct()) { $parts[] = 'DISTINCT'; } $parts[] = implode(', ', $query->getColumns()); $from = $query->getFrom(); if (count($from) > 0) { $parts[] = 'FROM ' . implode(', ', $from); } $where = $query->getWhere(); if ($where !== null) { $parts[] = 'WHERE ' . $where; } $groupBy = $query->getGroupBy(); if (count($groupBy) > 0) { $parts[] = 'GROUP BY ' . implode(', ', $groupBy); } $having = $query->getHaving(); if ($having !== null) { $parts[] = 'HAVING ' . $having; } $orderBy = $query->getOrderBy(); if (count($orderBy) > 0) { $parts[] = 'ORDER BY ' . implode(', ', $orderBy); } $sql = implode(' ', $parts); $limit = $query->getLimit(); if ($limit->isDefined()) { $sql = $this->platform->modifyLimitQuery($sql, $limit->getMaxResults(), $limit->getFirstResult()); } $forUpdate = $query->getForUpdate(); if ($forUpdate !== null) { if ($this->forUpdateSQL === null) { throw Exception::notSupported('FOR UPDATE'); } $sql .= ' ' . $this->forUpdateSQL; if ($forUpdate->getConflictResolutionMode() === ConflictResolutionMode::SKIP_LOCKED) { if ($this->skipLockedSQL === null) { throw Exception::notSupported('SKIP LOCKED'); } $sql .= ' ' . $this->skipLockedSQL; } } return $sql; } } PK!@!WW4dbal/src/SQL/Builder/DropSchemaObjectsSQLBuilder.phpnu[platform = $platform; } /** * @return list * * @throws Exception */ public function buildSQL(Schema $schema): array { return array_merge( $this->buildSequenceStatements($schema->getSequences()), $this->buildTableStatements($schema->getTables()), ); } /** * @param list
$tables * * @return list */ private function buildTableStatements(array $tables): array { return $this->platform->getDropTablesSQL($tables); } /** * @param list $sequences * * @return list * * @throws Exception */ private function buildSequenceStatements(array $sequences): array { $statements = []; foreach ($sequences as $sequence) { $statements[] = $this->platform->getDropSequenceSQL($sequence); } return $statements; } } PK!2!edMMdbal/src/SQL/Parser.phpnu[getMySQLStringLiteralPattern("'"), $this->getMySQLStringLiteralPattern('"'), ]; } else { $patterns = [ $this->getAnsiSQLStringLiteralPattern("'"), $this->getAnsiSQLStringLiteralPattern('"'), ]; } $patterns = array_merge($patterns, [ self::BACKTICK_IDENTIFIER, self::BRACKET_IDENTIFIER, self::MULTICHAR, self::ONE_LINE_COMMENT, self::MULTI_LINE_COMMENT, self::OTHER, ]); $this->sqlPattern = sprintf('(%s)', implode('|', $patterns)); } /** * Parses the given SQL statement * * @throws Exception */ public function parse(string $sql, Visitor $visitor): void { /** @var array $patterns */ $patterns = [ self::NAMED_PARAMETER => static function (string $sql) use ($visitor): void { $visitor->acceptNamedParameter($sql); }, self::POSITIONAL_PARAMETER => static function (string $sql) use ($visitor): void { $visitor->acceptPositionalParameter($sql); }, $this->sqlPattern => static function (string $sql) use ($visitor): void { $visitor->acceptOther($sql); }, self::SPECIAL => static function (string $sql) use ($visitor): void { $visitor->acceptOther($sql); }, ]; $offset = 0; while (($handler = current($patterns)) !== false) { if (preg_match('~\G' . key($patterns) . '~s', $sql, $matches, 0, $offset) === 1) { $handler($matches[0]); reset($patterns); $offset += strlen($matches[0]); } elseif (preg_last_error() !== PREG_NO_ERROR) { // @codeCoverageIgnoreStart throw RegularExpressionError::new(); // @codeCoverageIgnoreEnd } else { next($patterns); } } assert($offset === strlen($sql)); } private function getMySQLStringLiteralPattern(string $delimiter): string { return $delimiter . '((\\\\.)|(?![' . $delimiter . '\\\\]).)*' . $delimiter; } private function getAnsiSQLStringLiteralPattern(string $delimiter): string { return $delimiter . '[^' . $delimiter . ']*' . $delimiter; } } PK!D%8dbal/src/SQL/Parser/Exception/RegularExpressionError.phpnu[namespace = (string) $namespace; $this->namespaceVersion = null; } /** * Retrieves the namespace that prefixes all cache ids. * * @return string */ public function getNamespace() { return $this->namespace; } /** * {@inheritdoc} */ public function fetch($id) { return $this->doFetch($this->getNamespacedId($id)); } /** * {@inheritdoc} */ public function fetchMultiple(array $keys) { if (empty($keys)) { return []; } // note: the array_combine() is in place to keep an association between our $keys and the $namespacedKeys $namespacedKeys = array_combine($keys, array_map([$this, 'getNamespacedId'], $keys)); $items = $this->doFetchMultiple($namespacedKeys); $foundItems = []; // no internal array function supports this sort of mapping: needs to be iterative // this filters and combines keys in one pass foreach ($namespacedKeys as $requestedKey => $namespacedKey) { if (! isset($items[$namespacedKey]) && ! array_key_exists($namespacedKey, $items)) { continue; } $foundItems[$requestedKey] = $items[$namespacedKey]; } return $foundItems; } /** * {@inheritdoc} */ public function saveMultiple(array $keysAndValues, $lifetime = 0) { $namespacedKeysAndValues = []; foreach ($keysAndValues as $key => $value) { $namespacedKeysAndValues[$this->getNamespacedId($key)] = $value; } return $this->doSaveMultiple($namespacedKeysAndValues, $lifetime); } /** * {@inheritdoc} */ public function contains($id) { return $this->doContains($this->getNamespacedId($id)); } /** * {@inheritdoc} */ public function save($id, $data, $lifeTime = 0) { return $this->doSave($this->getNamespacedId($id), $data, $lifeTime); } /** * {@inheritdoc} */ public function deleteMultiple(array $keys) { return $this->doDeleteMultiple(array_map([$this, 'getNamespacedId'], $keys)); } /** * {@inheritdoc} */ public function delete($id) { return $this->doDelete($this->getNamespacedId($id)); } /** * {@inheritdoc} */ public function getStats() { return $this->doGetStats(); } /** * {@inheritDoc} */ public function flushAll() { return $this->doFlush(); } /** * {@inheritDoc} */ public function deleteAll() { $namespaceCacheKey = $this->getNamespaceCacheKey(); $namespaceVersion = $this->getNamespaceVersion() + 1; if ($this->doSave($namespaceCacheKey, $namespaceVersion)) { $this->namespaceVersion = $namespaceVersion; return true; } return false; } /** * Prefixes the passed id with the configured namespace value. * * @param string $id The id to namespace. * * @return string The namespaced id. */ private function getNamespacedId(string $id): string { $namespaceVersion = $this->getNamespaceVersion(); return sprintf('%s[%s][%s]', $this->namespace, $id, $namespaceVersion); } /** * Returns the namespace cache key. */ private function getNamespaceCacheKey(): string { return sprintf(self::DOCTRINE_NAMESPACE_CACHEKEY, $this->namespace); } /** * Returns the namespace version. */ private function getNamespaceVersion(): int { if ($this->namespaceVersion !== null) { return $this->namespaceVersion; } $namespaceCacheKey = $this->getNamespaceCacheKey(); $this->namespaceVersion = (int) $this->doFetch($namespaceCacheKey) ?: 1; return $this->namespaceVersion; } /** * Default implementation of doFetchMultiple. Each driver that supports multi-get should owerwrite it. * * @param string[] $keys Array of keys to retrieve from cache * * @return mixed[] Array of values retrieved for the given keys. */ protected function doFetchMultiple(array $keys) { $returnValues = []; foreach ($keys as $key) { $item = $this->doFetch($key); if ($item === false && ! $this->doContains($key)) { continue; } $returnValues[$key] = $item; } return $returnValues; } /** * Fetches an entry from the cache. * * @param string $id The id of the cache entry to fetch. * * @return mixed|false The cached data or FALSE, if no cache entry exists for the given id. */ abstract protected function doFetch($id); /** * Tests if an entry exists in the cache. * * @param string $id The cache id of the entry to check for. * * @return bool TRUE if a cache entry exists for the given cache id, FALSE otherwise. */ abstract protected function doContains($id); /** * Default implementation of doSaveMultiple. Each driver that supports multi-put should override it. * * @param mixed[] $keysAndValues Array of keys and values to save in cache * @param int $lifetime The lifetime. If != 0, sets a specific lifetime for these * cache entries (0 => infinite lifeTime). * * @return bool TRUE if the operation was successful, FALSE if it wasn't. */ protected function doSaveMultiple(array $keysAndValues, $lifetime = 0) { $success = true; foreach ($keysAndValues as $key => $value) { if ($this->doSave($key, $value, $lifetime)) { continue; } $success = false; } return $success; } /** * Puts data into the cache. * * @param string $id The cache id. * @param string $data The cache entry/data. * @param int $lifeTime The lifetime. If != 0, sets a specific lifetime for this * cache entry (0 => infinite lifeTime). * * @return bool TRUE if the entry was successfully stored in the cache, FALSE otherwise. */ abstract protected function doSave($id, $data, $lifeTime = 0); /** * Default implementation of doDeleteMultiple. Each driver that supports multi-delete should override it. * * @param string[] $keys Array of keys to delete from cache * * @return bool TRUE if the operation was successful, FALSE if it wasn't */ protected function doDeleteMultiple(array $keys) { $success = true; foreach ($keys as $key) { if ($this->doDelete($key)) { continue; } $success = false; } return $success; } /** * Deletes a cache entry. * * @param string $id The cache id. * * @return bool TRUE if the cache entry was successfully deleted, FALSE otherwise. */ abstract protected function doDelete($id); /** * Flushes all cache entries. * * @return bool TRUE if the cache entries were successfully flushed, FALSE otherwise. */ abstract protected function doFlush(); /** * Retrieves cached information from the data store. * * @return mixed[]|null An associative array with server's statistics if available, NULL otherwise. */ abstract protected function doGetStats(); } PK!S<^^2cache/lib/Doctrine/Common/Cache/FlushableCache.phpnu[ */ private $deferredItems = []; public static function wrap(Cache $cache): CacheItemPoolInterface { if ($cache instanceof DoctrineProvider && ! $cache->getNamespace()) { return $cache->getPool(); } if ($cache instanceof SymfonyDoctrineProvider && ! $cache->getNamespace()) { $getPool = function () { // phpcs:ignore Squiz.Scope.StaticThisUsage.Found return $this->pool; }; return $getPool->bindTo($cache, SymfonyDoctrineProvider::class)(); } return new self($cache); } private function __construct(Cache $cache) { $this->cache = $cache; } /** @internal */ public function getCache(): Cache { return $this->cache; } /** * {@inheritDoc} */ public function getItem($key): CacheItemInterface { assert(self::validKey($key)); if (isset($this->deferredItems[$key])) { $this->commit(); } $value = $this->cache->fetch($key); if (PHP_VERSION_ID >= 80000) { if ($value !== false) { return new TypedCacheItem($key, $value, true); } return new TypedCacheItem($key, null, false); } if ($value !== false) { return new CacheItem($key, $value, true); } return new CacheItem($key, null, false); } /** * {@inheritDoc} */ public function getItems(array $keys = []): array { if ($this->deferredItems) { $this->commit(); } assert(self::validKeys($keys)); $values = $this->doFetchMultiple($keys); $items = []; if (PHP_VERSION_ID >= 80000) { foreach ($keys as $key) { if (array_key_exists($key, $values)) { $items[$key] = new TypedCacheItem($key, $values[$key], true); } else { $items[$key] = new TypedCacheItem($key, null, false); } } return $items; } foreach ($keys as $key) { if (array_key_exists($key, $values)) { $items[$key] = new CacheItem($key, $values[$key], true); } else { $items[$key] = new CacheItem($key, null, false); } } return $items; } /** * {@inheritDoc} */ public function hasItem($key): bool { assert(self::validKey($key)); if (isset($this->deferredItems[$key])) { $this->commit(); } return $this->cache->contains($key); } public function clear(): bool { $this->deferredItems = []; if (! $this->cache instanceof ClearableCache) { return false; } return $this->cache->deleteAll(); } /** * {@inheritDoc} */ public function deleteItem($key): bool { assert(self::validKey($key)); unset($this->deferredItems[$key]); return $this->cache->delete($key); } /** * {@inheritDoc} */ public function deleteItems(array $keys): bool { foreach ($keys as $key) { assert(self::validKey($key)); unset($this->deferredItems[$key]); } return $this->doDeleteMultiple($keys); } public function save(CacheItemInterface $item): bool { return $this->saveDeferred($item) && $this->commit(); } public function saveDeferred(CacheItemInterface $item): bool { if (! $item instanceof CacheItem && ! $item instanceof TypedCacheItem) { return false; } $this->deferredItems[$item->getKey()] = $item; return true; } public function commit(): bool { if (! $this->deferredItems) { return true; } $now = microtime(true); $itemsCount = 0; $byLifetime = []; $expiredKeys = []; foreach ($this->deferredItems as $key => $item) { $lifetime = ($item->getExpiry() ?? $now) - $now; if ($lifetime < 0) { $expiredKeys[] = $key; continue; } ++$itemsCount; $byLifetime[(int) $lifetime][$key] = $item->get(); } $this->deferredItems = []; switch (count($expiredKeys)) { case 0: break; case 1: $this->cache->delete(current($expiredKeys)); break; default: $this->doDeleteMultiple($expiredKeys); break; } if ($itemsCount === 1) { return $this->cache->save($key, $item->get(), (int) $lifetime); } $success = true; foreach ($byLifetime as $lifetime => $values) { $success = $this->doSaveMultiple($values, $lifetime) && $success; } return $success; } public function __destruct() { $this->commit(); } /** * @param mixed $key */ private static function validKey($key): bool { if (! is_string($key)) { throw new InvalidArgument(sprintf('Cache key must be string, "%s" given.', is_object($key) ? get_class($key) : gettype($key))); } if ($key === '') { throw new InvalidArgument('Cache key length must be greater than zero.'); } if (strpbrk($key, self::RESERVED_CHARACTERS) !== false) { throw new InvalidArgument(sprintf('Cache key "%s" contains reserved characters "%s".', $key, self::RESERVED_CHARACTERS)); } return true; } /** * @param mixed[] $keys */ private static function validKeys(array $keys): bool { foreach ($keys as $key) { self::validKey($key); } return true; } /** * @param mixed[] $keys */ private function doDeleteMultiple(array $keys): bool { if ($this->cache instanceof MultiDeleteCache) { return $this->cache->deleteMultiple($keys); } $success = true; foreach ($keys as $key) { $success = $this->cache->delete($key) && $success; } return $success; } /** * @param mixed[] $keys * * @return mixed[] */ private function doFetchMultiple(array $keys): array { if ($this->cache instanceof MultiGetCache) { return $this->cache->fetchMultiple($keys); } $values = []; foreach ($keys as $key) { $value = $this->cache->fetch($key); if (! $value) { continue; } $values[$key] = $value; } return $values; } /** * @param mixed[] $keysAndValues */ private function doSaveMultiple(array $keysAndValues, int $lifetime = 0): bool { if ($this->cache instanceof MultiPutCache) { return $this->cache->saveMultiple($keysAndValues, $lifetime); } $success = true; foreach ($keysAndValues as $key => $value) { $success = $this->cache->save($key, $value, $lifetime) && $success; } return $success; } } PK! 9cache/lib/Doctrine/Common/Cache/Psr6/DoctrineProvider.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Doctrine\Common\Cache\Psr6; use Doctrine\Common\Cache\Cache; use Doctrine\Common\Cache\CacheProvider; use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\Cache\Adapter\DoctrineAdapter as SymfonyDoctrineAdapter; use Symfony\Contracts\Service\ResetInterface; use function rawurlencode; /** * This class was copied from the Symfony Framework, see the original copyright * notice above. The code is distributed subject to the license terms in * https://github.com/symfony/symfony/blob/ff0cf61278982539c49e467db9ab13cbd342f76d/LICENSE */ final class DoctrineProvider extends CacheProvider { /** @var CacheItemPoolInterface */ private $pool; public static function wrap(CacheItemPoolInterface $pool): Cache { if ($pool instanceof CacheAdapter) { return $pool->getCache(); } if ($pool instanceof SymfonyDoctrineAdapter) { $getCache = function () { // phpcs:ignore Squiz.Scope.StaticThisUsage.Found return $this->provider; }; return $getCache->bindTo($pool, SymfonyDoctrineAdapter::class)(); } return new self($pool); } private function __construct(CacheItemPoolInterface $pool) { $this->pool = $pool; } /** @internal */ public function getPool(): CacheItemPoolInterface { return $this->pool; } public function reset(): void { if ($this->pool instanceof ResetInterface) { $this->pool->reset(); } $this->setNamespace($this->getNamespace()); } /** * {@inheritdoc} */ protected function doFetch($id) { $item = $this->pool->getItem(rawurlencode($id)); return $item->isHit() ? $item->get() : false; } /** * {@inheritdoc} * * @return bool */ protected function doContains($id) { return $this->pool->hasItem(rawurlencode($id)); } /** * {@inheritdoc} * * @return bool */ protected function doSave($id, $data, $lifeTime = 0) { $item = $this->pool->getItem(rawurlencode($id)); if (0 < $lifeTime) { $item->expiresAfter($lifeTime); } return $this->pool->save($item->set($data)); } /** * {@inheritdoc} * * @return bool */ protected function doDelete($id) { return $this->pool->deleteItem(rawurlencode($id)); } /** * {@inheritdoc} * * @return bool */ protected function doFlush() { return $this->pool->clear(); } /** * {@inheritdoc} * * @return array|null */ protected function doGetStats() { return null; } } PK!P "{{7cache/lib/Doctrine/Common/Cache/Psr6/TypedCacheItem.phpnu[key; } public function get(): mixed { return $this->value; } public function isHit(): bool { return $this->isHit; } public function set(mixed $value): static { $this->value = $value; return $this; } /** * {@inheritDoc} */ public function expiresAt($expiration): static { if ($expiration === null) { $this->expiry = null; } elseif ($expiration instanceof DateTimeInterface) { $this->expiry = (float) $expiration->format('U.u'); } else { throw new TypeError(sprintf( 'Expected $expiration to be an instance of DateTimeInterface or null, got %s', get_debug_type($expiration) )); } return $this; } /** * {@inheritDoc} */ public function expiresAfter($time): static { if ($time === null) { $this->expiry = null; } elseif ($time instanceof DateInterval) { $this->expiry = microtime(true) + DateTime::createFromFormat('U', 0)->add($time)->format('U.u'); } elseif (is_int($time)) { $this->expiry = $time + microtime(true); } else { throw new TypeError(sprintf( 'Expected $time to be either an integer, an instance of DateInterval or null, got %s', get_debug_type($time) )); } return $this; } /** * @internal */ public function getExpiry(): ?float { return $this->expiry; } } PK!`4  2cache/lib/Doctrine/Common/Cache/Psr6/CacheItem.phpnu[key = $key; $this->value = $data; $this->isHit = $isHit; } public function getKey(): string { return $this->key; } /** * {@inheritDoc} * * @return mixed */ public function get() { return $this->value; } public function isHit(): bool { return $this->isHit; } /** * {@inheritDoc} */ public function set($value): self { $this->value = $value; return $this; } /** * {@inheritDoc} */ public function expiresAt($expiration): self { if ($expiration === null) { $this->expiry = null; } elseif ($expiration instanceof DateTimeInterface) { $this->expiry = (float) $expiration->format('U.u'); } else { throw new TypeError(sprintf( 'Expected $expiration to be an instance of DateTimeInterface or null, got %s', is_object($expiration) ? get_class($expiration) : gettype($expiration) )); } return $this; } /** * {@inheritDoc} */ public function expiresAfter($time): self { if ($time === null) { $this->expiry = null; } elseif ($time instanceof DateInterval) { $this->expiry = microtime(true) + DateTime::createFromFormat('U', 0)->add($time)->format('U.u'); } elseif (is_int($time)) { $this->expiry = $time + microtime(true); } else { throw new TypeError(sprintf( 'Expected $time to be either an integer, an instance of DateInterval or null, got %s', is_object($time) ? get_class($time) : gettype($time) )); } return $this; } /** * @internal */ public function getExpiry(): ?float { return $this->expiry; } } PK!>8cache/lib/Doctrine/Common/Cache/Psr6/InvalidArgument.phpnu[hits * Number of keys that have been requested and found present. * * - misses * Number of items that have been requested and not found. * * - uptime * Time that the server is running. * * - memory_usage * Memory used by this server to store items. * * - memory_available * Memory allowed to use for storage. * * @return mixed[]|null An associative array with server's statistics if available, NULL otherwise. */ public function getStats(); } PK!G52cache/lib/Doctrine/Common/Cache/ClearableCache.phpnu[ infinite lifeTime). * * @return bool TRUE if the operation was successful, FALSE if it wasn't. */ public function saveMultiple(array $keysAndValues, $lifetime = 0); } PK!i cache/UPGRADE-1.11.mdnu[# Upgrade to 1.11 doctrine/cache will no longer be maintained and all cache implementations have been marked as deprecated. These implementations will be removed in 2.0, which will only contain interfaces to provide a lightweight package for backward compatibility. There are two new classes to use in the `Doctrine\Common\Cache\Psr6` namespace: * The `CacheAdapter` class allows using any Doctrine Cache as PSR-6 cache. This is useful to provide a forward compatibility layer in libraries that accept Doctrine cache implementations and switch to PSR-6. * The `DoctrineProvider` class allows using any PSR-6 cache as Doctrine cache. This implementation is designed for libraries that leak the cache and want to switch to allowing PSR-6 implementations. This class is design to be used during the transition phase of sunsetting doctrine/cache support. A full example to setup a filesystem based PSR-6 cache with symfony/cache using the `DoctrineProvider` to convert back to Doctrine's `Cache` interface: ```php use Doctrine\Common\Cache\Psr6\DoctrineProvider; use Symfony\Component\Cache\Adapter\FilesystemAdapter; $cachePool = new FilesystemAdapter(); $cache = DoctrineProvider::wrap($cachePool); // $cache instanceof \Doctrine\Common\Cache\Cache ``` PK!jjcache/composer.jsonnu[{ "name": "doctrine/cache", "type": "library", "description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.", "keywords": [ "php", "cache", "caching", "abstraction", "redis", "memcached", "couchdb", "xcache", "apcu" ], "homepage": "https://www.doctrine-project.org/projects/cache.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"} ], "require": { "php": "~7.1 || ^8.0" }, "require-dev": { "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", "doctrine/coding-standard": "^9", "psr/cache": "^1.0 || ^2.0 || ^3.0", "cache/integration-tests": "dev-master", "symfony/cache": "^4.4 || ^5.4 || ^6", "symfony/var-exporter": "^4.4 || ^5.4 || ^6" }, "conflict": { "doctrine/common": ">2.2,<2.4" }, "autoload": { "psr-4": { "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" } }, "autoload-dev": { "psr-4": { "Doctrine\\Tests\\": "tests/Doctrine/Tests" } }, "config": { "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true } } } PK!9)) cache/LICENSEnu[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. PK!BHcache/README.mdnu[# Doctrine Cache [![Build Status](https://github.com/doctrine/cache/workflows/Continuous%20Integration/badge.svg)](https://github.com/doctrine/cache/actions) [![Code Coverage](https://codecov.io/gh/doctrine/cache/branch/1.10.x/graph/badge.svg)](https://codecov.io/gh/doctrine/cache/branch/1.10.x) [![Latest Stable Version](https://img.shields.io/packagist/v/doctrine/cache.svg?style=flat-square)](https://packagist.org/packages/doctrine/cache) [![Total Downloads](https://img.shields.io/packagist/dt/doctrine/cache.svg?style=flat-square)](https://packagist.org/packages/doctrine/cache) Cache component extracted from the Doctrine Common project. [Documentation](https://www.doctrine-project.org/projects/doctrine-cache/en/current/index.html) This library is deprecated and will no longer receive bug fixes from the Doctrine Project. Please use a different cache library, preferably PSR-6 or PSR-16 instead. PK!icache/UPGRADE-1.4.mdnu[# Upgrade to 1.4 ## Minor BC Break: `Doctrine\Common\Cache\FileCache#$extension` is now `private`. If you need to override the value of `Doctrine\Common\Cache\FileCache#$extension`, then use the second parameter of `Doctrine\Common\Cache\FileCache#__construct()` instead of overriding the property in your own implementation. ## Minor BC Break: file based caches paths changed `Doctrine\Common\Cache\FileCache`, `Doctrine\Common\Cache\PhpFileCache` and `Doctrine\Common\Cache\FilesystemCache` are using a different cache paths structure. If you rely on warmed up caches for deployments, consider that caches generated with `doctrine/cache` `<1.4` are not compatible with the new directory structure, and will be ignored. PK!:%:%6deprecations/lib/Doctrine/Deprecations/Deprecation.phpnu[|null */ private static $type; /** @var LoggerInterface|null */ private static $logger; /** @var array */ private static $ignoredPackages = []; /** @var array */ private static $triggeredDeprecations = []; /** @var array */ private static $ignoredLinks = []; /** @var bool */ private static $deduplication = true; /** * Trigger a deprecation for the given package and identfier. * * The link should point to a Github issue or Wiki entry detailing the * deprecation. It is additionally used to de-duplicate the trigger of the * same deprecation during a request. * * @param float|int|string $args */ public static function trigger(string $package, string $link, string $message, ...$args): void { $type = self::$type ?? self::getTypeFromEnv(); if ($type === self::TYPE_NONE) { return; } if (isset(self::$ignoredLinks[$link])) { return; } if (array_key_exists($link, self::$triggeredDeprecations)) { self::$triggeredDeprecations[$link]++; } else { self::$triggeredDeprecations[$link] = 1; } if (self::$deduplication === true && self::$triggeredDeprecations[$link] > 1) { return; } if (isset(self::$ignoredPackages[$package])) { return; } $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); $message = sprintf($message, ...$args); self::delegateTriggerToBackend($message, $backtrace, $link, $package); } /** * Trigger a deprecation for the given package and identifier when called from outside. * * "Outside" means we assume that $package is currently installed as a * dependency and the caller is not a file in that package. When $package * is installed as a root package then deprecations triggered from the * tests folder are also considered "outside". * * This deprecation method assumes that you are using Composer to install * the dependency and are using the default /vendor/ folder and not a * Composer plugin to change the install location. The assumption is also * that $package is the exact composer packge name. * * Compared to {@link trigger()} this method causes some overhead when * deprecation tracking is enabled even during deduplication, because it * needs to call {@link debug_backtrace()} * * @param float|int|string $args */ public static function triggerIfCalledFromOutside(string $package, string $link, string $message, ...$args): void { $type = self::$type ?? self::getTypeFromEnv(); if ($type === self::TYPE_NONE) { return; } $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); // first check that the caller is not from a tests folder, in which case we always let deprecations pass if (isset($backtrace[1]['file'], $backtrace[0]['file']) && strpos($backtrace[1]['file'], DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR) === false) { $path = DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $package) . DIRECTORY_SEPARATOR; if (strpos($backtrace[0]['file'], $path) === false) { return; } if (strpos($backtrace[1]['file'], $path) !== false) { return; } } if (isset(self::$ignoredLinks[$link])) { return; } if (array_key_exists($link, self::$triggeredDeprecations)) { self::$triggeredDeprecations[$link]++; } else { self::$triggeredDeprecations[$link] = 1; } if (self::$deduplication === true && self::$triggeredDeprecations[$link] > 1) { return; } if (isset(self::$ignoredPackages[$package])) { return; } $message = sprintf($message, ...$args); self::delegateTriggerToBackend($message, $backtrace, $link, $package); } /** * @param list $backtrace */ private static function delegateTriggerToBackend(string $message, array $backtrace, string $link, string $package): void { $type = self::$type ?? self::getTypeFromEnv(); if (($type & self::TYPE_PSR_LOGGER) > 0) { $context = [ 'file' => $backtrace[0]['file'] ?? null, 'line' => $backtrace[0]['line'] ?? null, 'package' => $package, 'link' => $link, ]; assert(self::$logger !== null); self::$logger->notice($message, $context); } if (! (($type & self::TYPE_TRIGGER_ERROR) > 0)) { return; } $message .= sprintf( ' (%s:%d called by %s:%d, %s, package %s)', self::basename($backtrace[0]['file'] ?? 'native code'), $backtrace[0]['line'] ?? 0, self::basename($backtrace[1]['file'] ?? 'native code'), $backtrace[1]['line'] ?? 0, $link, $package ); @trigger_error($message, E_USER_DEPRECATED); } /** * A non-local-aware version of PHPs basename function. */ private static function basename(string $filename): string { $pos = strrpos($filename, DIRECTORY_SEPARATOR); if ($pos === false) { return $filename; } return substr($filename, $pos + 1); } public static function enableTrackingDeprecations(): void { self::$type = self::$type ?? 0; self::$type |= self::TYPE_TRACK_DEPRECATIONS; } public static function enableWithTriggerError(): void { self::$type = self::$type ?? 0; self::$type |= self::TYPE_TRIGGER_ERROR; } public static function enableWithPsrLogger(LoggerInterface $logger): void { self::$type = self::$type ?? 0; self::$type |= self::TYPE_PSR_LOGGER; self::$logger = $logger; } public static function withoutDeduplication(): void { self::$deduplication = false; } public static function disable(): void { self::$type = self::TYPE_NONE; self::$logger = null; self::$deduplication = true; self::$ignoredLinks = []; foreach (self::$triggeredDeprecations as $link => $count) { self::$triggeredDeprecations[$link] = 0; } } public static function ignorePackage(string $packageName): void { self::$ignoredPackages[$packageName] = true; } public static function ignoreDeprecations(string ...$links): void { foreach ($links as $link) { self::$ignoredLinks[$link] = true; } } public static function getUniqueTriggeredDeprecationsCount(): int { return array_reduce(self::$triggeredDeprecations, static function (int $carry, int $count) { return $carry + $count; }, 0); } /** * Returns each triggered deprecation link identifier and the amount of occurrences. * * @return array */ public static function getTriggeredDeprecations(): array { return self::$triggeredDeprecations; } /** * @return int-mask-of */ private static function getTypeFromEnv(): int { switch ($_SERVER['DOCTRINE_DEPRECATIONS'] ?? $_ENV['DOCTRINE_DEPRECATIONS'] ?? null) { case 'trigger': self::$type = self::TYPE_TRIGGER_ERROR; break; case 'track': self::$type = self::TYPE_TRACK_DEPRECATIONS; break; default: self::$type = self::TYPE_NONE; break; } return self::$type; } } PK!/GdzEdeprecations/lib/Doctrine/Deprecations/PHPUnit/VerifyDeprecations.phpnu[ */ private $doctrineDeprecationsExpectations = []; /** @var array */ private $doctrineNoDeprecationsExpectations = []; public function expectDeprecationWithIdentifier(string $identifier): void { $this->doctrineDeprecationsExpectations[$identifier] = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0; } public function expectNoDeprecationWithIdentifier(string $identifier): void { $this->doctrineNoDeprecationsExpectations[$identifier] = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0; } /** * @before */ public function enableDeprecationTracking(): void { Deprecation::enableTrackingDeprecations(); } /** * @after */ public function verifyDeprecationsAreTriggered(): void { foreach ($this->doctrineDeprecationsExpectations as $identifier => $expectation) { $actualCount = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0; $this->assertTrue( $actualCount > $expectation, sprintf( "Expected deprecation with identifier '%s' was not triggered by code executed in test.", $identifier ) ); } foreach ($this->doctrineNoDeprecationsExpectations as $identifier => $expectation) { $actualCount = Deprecation::getTriggeredDeprecations()[$identifier] ?? 0; $this->assertTrue( $actualCount === $expectation, sprintf( "Expected deprecation with identifier '%s' was triggered by code executed in test, but expected not to.", $identifier ) ); } } } PK!,êdeprecations/composer.jsonnu[{ "name": "doctrine/deprecations", "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", "license": "MIT", "type": "library", "homepage": "https://www.doctrine-project.org/", "require": { "php": "^7.1 || ^8.0" }, "require-dev": { "doctrine/coding-standard": "^9", "phpstan/phpstan": "1.4.10 || 1.10.15", "phpstan/phpstan-phpunit": "^1.0", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", "psalm/plugin-phpunit": "0.18.4", "psr/log": "^1 || ^2 || ^3", "vimeo/psalm": "4.30.0 || 5.12.0" }, "suggest": { "psr/log": "Allows logging deprecations via PSR-3 logger implementation" }, "autoload": { "psr-4": { "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" } }, "autoload-dev": { "psr-4": { "DeprecationTests\\": "test_fixtures/src", "Doctrine\\Foo\\": "test_fixtures/vendor/doctrine/foo" } }, "config": { "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true } } } PK!"0))deprecations/LICENSEnu[Copyright (c) 2020-2021 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. PK!qU $$deprecations/README.mdnu[# Doctrine Deprecations A small (side-effect free by default) layer on top of `trigger_error(E_USER_DEPRECATED)` or PSR-3 logging. - no side-effects by default, making it a perfect fit for libraries that don't know how the error handler works they operate under - options to avoid having to rely on error handlers global state by using PSR-3 logging - deduplicate deprecation messages to avoid excessive triggering and reduce overhead We recommend to collect Deprecations using a PSR logger instead of relying on the global error handler. ## Usage from consumer perspective: Enable Doctrine deprecations to be sent to a PSR3 logger: ```php \Doctrine\Deprecations\Deprecation::enableWithPsrLogger($logger); ``` Enable Doctrine deprecations to be sent as `@trigger_error($message, E_USER_DEPRECATED)` messages by setting the `DOCTRINE_DEPRECATIONS` environment variable to `trigger`. Alternatively, call: ```php \Doctrine\Deprecations\Deprecation::enableWithTriggerError(); ``` If you only want to enable deprecation tracking, without logging or calling `trigger_error` then set the `DOCTRINE_DEPRECATIONS` environment variable to `track`. Alternatively, call: ```php \Doctrine\Deprecations\Deprecation::enableTrackingDeprecations(); ``` Tracking is enabled with all three modes and provides access to all triggered deprecations and their individual count: ```php $deprecations = \Doctrine\Deprecations\Deprecation::getTriggeredDeprecations(); foreach ($deprecations as $identifier => $count) { echo $identifier . " was triggered " . $count . " times\n"; } ``` ### Suppressing Specific Deprecations Disable triggering about specific deprecations: ```php \Doctrine\Deprecations\Deprecation::ignoreDeprecations("https://link/to/deprecations-description-identifier"); ``` Disable all deprecations from a package ```php \Doctrine\Deprecations\Deprecation::ignorePackage("doctrine/orm"); ``` ### Other Operations When used within PHPUnit or other tools that could collect multiple instances of the same deprecations the deduplication can be disabled: ```php \Doctrine\Deprecations\Deprecation::withoutDeduplication(); ``` Disable deprecation tracking again: ```php \Doctrine\Deprecations\Deprecation::disable(); ``` ## Usage from a library/producer perspective: When you want to unconditionally trigger a deprecation even when called from the library itself then the `trigger` method is the way to go: ```php \Doctrine\Deprecations\Deprecation::trigger( "doctrine/orm", "https://link/to/deprecations-description", "message" ); ``` If variable arguments are provided at the end, they are used with `sprintf` on the message. ```php \Doctrine\Deprecations\Deprecation::trigger( "doctrine/orm", "https://github.com/doctrine/orm/issue/1234", "message %s %d", "foo", 1234 ); ``` When you want to trigger a deprecation only when it is called by a function outside of the current package, but not trigger when the package itself is the cause, then use: ```php \Doctrine\Deprecations\Deprecation::triggerIfCalledFromOutside( "doctrine/orm", "https://link/to/deprecations-description", "message" ); ``` Based on the issue link each deprecation message is only triggered once per request. A limited stacktrace is included in the deprecation message to find the offending location. Note: A producer/library should never call `Deprecation::enableWith` methods and leave the decision how to handle deprecations to application and frameworks. ## Usage in PHPUnit tests There is a `VerifyDeprecations` trait that you can use to make assertions on the occurrence of deprecations within a test. ```php use Doctrine\Deprecations\PHPUnit\VerifyDeprecations; class MyTest extends TestCase { use VerifyDeprecations; public function testSomethingDeprecation() { $this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/issue/1234'); triggerTheCodeWithDeprecation(); } public function testSomethingDeprecationFixed() { $this->expectNoDeprecationWithIdentifier('https://github.com/doctrine/orm/issue/1234'); triggerTheCodeWithoutDeprecation(); } } ``` ## What is a deprecation identifier? An identifier for deprecations is just a link to any resource, most often a Github Issue or Pull Request explaining the deprecation and potentially its alternative. PK!-persistence/composer.jsonnu[PK!nm``persistence/CONTRIBUTING.mdnu[PK!9)) persistence/LICENSEnu[PK!l9b&/persistence/UPGRADE.mdnu[PK!af9uhh"persistence/README.mdnu[PK!6gA2276&persistence/src/Persistence/AbstractManagerRegistry.phpnu[PK!W_mm->persistence/src/Persistence/ObjectManager.phpnu[PK!  0Opersistence/src/Persistence/ObjectRepository.phpnu[PK!c?Wpersistence/src/Persistence/Mapping/StaticReflectionService.phpnu[PK!ww>]persistence/src/Persistence/Mapping/ProxyClassNameResolver.phpnu[PK!Ұpp<^persistence/src/Persistence/Mapping/ClassMetadataFactory.phpnu[PK!=AJI I @epersistence/src/Persistence/Mapping/RuntimeReflectionService.phpnu[PK!*99Dppersistence/src/Persistence/Mapping/AbstractClassMetadataFactory.phpnu[PK!d9persistence/src/Persistence/Mapping/ReflectionService.phpnu[PK!FAz9persistence/src/Persistence/Mapping/Driver/FileDriver.phpnu[PK!Epersistence/src/Persistence/Mapping/Driver/ColocatedMappingDriver.phpnu[PK!Θ/ Apersistence/src/Persistence/Mapping/Driver/MappingDriverChain.phpnu[PK!KKApersistence/src/Persistence/Mapping/Driver/SymfonyFileLocator.phpnu[PK!%%Apersistence/src/Persistence/Mapping/Driver/DefaultFileLocator.phpnu[PK!"8+persistence/src/Persistence/Mapping/Driver/PHPDriver.phpnu[PK!Y:persistence/src/Persistence/Mapping/Driver/FileLocator.phpnu[PK!s ~hh<%persistence/src/Persistence/Mapping/Driver/MappingDriver.phpnu[PK!4! >)persistence/src/Persistence/Mapping/Driver/StaticPHPDriver.phpnu[PK![5556persistence/src/Persistence/Mapping/ClassMetadata.phpnu[PK!̟' ' 8Epersistence/src/Persistence/Mapping/MappingException.phpnu[PK!΍LL6 Opersistence/src/Persistence/ObjectManagerDecorator.phpnu[PK!#4a /Vpersistence/src/Persistence/ManagerRegistry.phpnu[PK!ؘk+% bpersistence/src/Persistence/Proxy.phpnu[PK!uG2dpersistence/src/Persistence/ConnectionRegistry.phpnu[PK!zXipersistence/src/Persistence/Reflection/TypedNoDefaultRuntimePublicReflectionProperty.phpnu[PK!:==KIkpersistence/src/Persistence/Reflection/TypedNoDefaultReflectionProperty.phpnu[PK!L\  Jmpersistence/src/Persistence/Reflection/RuntimePublicReflectionProperty.phpnu[PK!7Dspersistence/src/Persistence/Reflection/RuntimeReflectionProperty.phpnu[PK!oy((O{persistence/src/Persistence/Reflection/TypedNoDefaultReflectionPropertyBase.phpnu[PK!_00AKpersistence/src/Persistence/Reflection/EnumReflectionProperty.phpnu[PK!ڬ]]@persistence/src/Persistence/Event/LoadClassMetadataEventArgs.phpnu[PK!4캙**6persistence/src/Persistence/Event/ManagerEventArgs.phpnu[PK!O tt6Ipersistence/src/Persistence/Event/OnClearEventArgs.phpnu[PK!haNj 8#persistence/src/Persistence/Event/PreUpdateEventArgs.phpnu[PK!'8^^8wpersistence/src/Persistence/Event/LifecycleEventArgs.phpnu[PK![7=persistence/src/Persistence/PropertyChangedListener.phpnu[PK!}5wpersistence/src/Persistence/NotifyPropertyChanged.phpnu[PK!+Gqnevent-manager/composer.jsonnu[PK!9))wevent-manager/LICENSEnu[PK!&event-manager/psalm.xmlnu[PK!Sddevent-manager/UPGRADE.mdnu[PK!gHHevent-manager/README.mdnu[PK! 88#event-manager/phpstan.neon.distnu[PK!ievent-manager/src/EventArgs.phpnu[PK!NO"event-manager/src/EventManager.phpnu[PK!aS%event-manager/src/EventSubscriber.phpnu[PK! BӶ*common/composer.jsonnu[PK!9))$common/LICENSEnu[PK!)΃PPcommon/README.mdnu[PK!7QQcommon/UPGRADE_TO_2_1nu[PK!Tz common/src/Util/ClassUtils.phpnu[PK!Iiicommon/src/Util/Debug.phpnu[PK!Lycommon/src/CommonException.phpnu[PK!mOLzz3common/src/Proxy/Exception/OutOfBoundsException.phpnu[PK!/sy7common/src/Proxy/Exception/UnexpectedValueException.phpnu[PK!~-common/src/Proxy/Exception/ProxyException.phpnu[PK!ß 7!common/src/Proxy/Exception/InvalidArgumentException.phpnu[PK!!d)-common/src/Proxy/AbstractProxyFactory.phpnu[PK!B6 /Lcommon/src/Proxy/Autoloader.phpnu[PK!L# Xcommon/src/Proxy/ProxyGenerator.phpnu[PK!)common/src/Proxy/Proxy.phpnu[PK!(jpp$Ucommon/src/Proxy/ProxyDefinition.phpnu[PK!* common/src/Comparable.phpnu[PK! a5Z"!"!/common/src/ClassLoader.phpnu[PK!$%$%*$common/docs/en/reference/class-loading.rstnu[PK!AÌJcommon/docs/en/index.rstnu[PK! { { Jcommon/UPGRADE_TO_2_2nu[PK!I6Tinflector/lib/Doctrine/Inflector/NoopWordInflector.phpnu[PK!]9@q5Uinflector/lib/Doctrine/Inflector/InflectorFactory.phpnu[PK![_i8[inflector/lib/Doctrine/Inflector/CachedWordInflector.phpnu[PK!h%AS:6^inflector/lib/Doctrine/Inflector/Rules/Transformations.phpnu[PK!J>(ainflector/lib/Doctrine/Inflector/Rules/Turkish/Inflectible.phpnu[PK! Ceinflector/lib/Doctrine/Inflector/Rules/Turkish/InflectorFactory.phpnu[PK!>Yεgg>ginflector/lib/Doctrine/Inflector/Rules/Turkish/Uninflected.phpnu[PK!ڃjj8jinflector/lib/Doctrine/Inflector/Rules/Turkish/Rules.phpnu[PK!{<Fkninflector/lib/Doctrine/Inflector/Rules/NorwegianBokmal/Inflectible.phpnu[PK!;0Krinflector/lib/Doctrine/Inflector/Rules/NorwegianBokmal/InflectorFactory.phpnu[PK!ٮdddFtinflector/lib/Doctrine/Inflector/Rules/NorwegianBokmal/Uninflected.phpnu[PK!Trr@winflector/lib/Doctrine/Inflector/Rules/NorwegianBokmal/Rules.phpnu[PK!͕99>{inflector/lib/Doctrine/Inflector/Rules/Spanish/Inflectible.phpnu[PK![CCinflector/lib/Doctrine/Inflector/Rules/Spanish/InflectorFactory.phpnu[PK!@Ogg>inflector/lib/Doctrine/Inflector/Rules/Spanish/Uninflected.phpnu[PK!j?jj8Xinflector/lib/Doctrine/Inflector/Rules/Spanish/Rules.phpnu[PK!a_2*inflector/lib/Doctrine/Inflector/Rules/Pattern.phpnu[PK!@YoAinflector/lib/Doctrine/Inflector/Rules/Portuguese/Inflectible.phpnu[PK!3F!inflector/lib/Doctrine/Inflector/Rules/Portuguese/InflectorFactory.phpnu[PK!n*Aginflector/lib/Doctrine/Inflector/Rules/Portuguese/Uninflected.phpnu[PK!!<ۘmm;inflector/lib/Doctrine/Inflector/Rules/Portuguese/Rules.phpnu[PK!'9`inflector/lib/Doctrine/Inflector/Rules/Transformation.phpnu[PK!fP  2߲inflector/lib/Doctrine/Inflector/Rules/Ruleset.phpnu[PK!7Jinflector/lib/Doctrine/Inflector/Rules/Substitution.phpnu[PK!m\\8tinflector/lib/Doctrine/Inflector/Rules/Substitutions.phpnu[PK!fy_._.>8inflector/lib/Doctrine/Inflector/Rules/English/Inflectible.phpnu[PK!Y?Cinflector/lib/Doctrine/Inflector/Rules/English/InflectorFactory.phpnu[PK!&'>Einflector/lib/Doctrine/Inflector/Rules/English/Uninflected.phpnu[PK!Djj8B inflector/lib/Doctrine/Inflector/Rules/English/Rules.phpnu[PK!v掸3 inflector/lib/Doctrine/Inflector/Rules/Patterns.phpnu[PK!KHW&&//inflector/lib/Doctrine/Inflector/Rules/Word.phpnu[PK!yXVV=inflector/lib/Doctrine/Inflector/Rules/French/Inflectible.phpnu[PK!~2Bwinflector/lib/Doctrine/Inflector/Rules/French/InflectorFactory.phpnu[PK!=inflector/lib/Doctrine/Inflector/Rules/French/Uninflected.phpnu[PK!ldii73inflector/lib/Doctrine/Inflector/Rules/French/Rules.phpnu[PK!##l2"inflector/lib/Doctrine/Inflector/WordInflector.phpnu[PK!0202."inflector/lib/Doctrine/Inflector/Inflector.phpnu[PK!x%%=Uinflector/lib/Doctrine/Inflector/LanguageInflectorFactory.phpnu[PK!edKK5Yinflector/lib/Doctrine/Inflector/RulesetInflector.phpnu[PK!Kڮ-^inflector/lib/Doctrine/Inflector/Language.phpnu[PK!;D`inflector/lib/Doctrine/Inflector/GenericLanguageInflectorFactory.phpnu[PK!dFginflector/composer.jsonnu[PK!9))%ninflector/LICENSEnu[PK!w)Q  rinflector/README.mdnu[PK!W||tinflector/docs/en/index.rstnu[PK!&Pannotations/lib/Doctrine/Common/Annotations/ImplicitlyIgnoredAnnotationNames.phpnu[PK!U,,@Ӥannotations/lib/Doctrine/Common/Annotations/AnnotationReader.phpnu[PK!l'=<annotations/lib/Doctrine/Common/Annotations/IndexedReader.phpnu[PK!ٷ"\;annotations/lib/Doctrine/Common/Annotations/TokenParser.phpnu[PK!u8annotations/lib/Doctrine/Common/Annotations/DocLexer.phpnu[PK!sfk!!?annotations/lib/Doctrine/Common/Annotations/FileCacheReader.phpnu[PK!χQQB#&annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.phpnu[PK!t4ZZR<annotations/lib/Doctrine/Common/Annotations/NamedArgumentConstructorAnnotation.phpnu[PK!D(**:>annotations/lib/Doctrine/Common/Annotations/Annotation.phpnu[PK!v* 9VDannotations/lib/Doctrine/Common/Annotations/PhpParser.phpnu[PK!,RR<pNannotations/lib/Doctrine/Common/Annotations/CachedReader.phpnu[PK!(@v9.kannotations/lib/Doctrine/Common/Annotations/DocParser.phpnu[PK!z jXggD+/annotations/lib/Doctrine/Common/Annotations/Annotation/Attribute.phpnu[PK!OC1annotations/lib/Doctrine/Common/Annotations/Annotation/Required.phpnu[PK!Վ''Ec2annotations/lib/Doctrine/Common/Annotations/Annotation/Attributes.phpnu[PK!GOS3annotations/lib/Doctrine/Common/Annotations/Annotation/NamedArgumentConstructor.phpnu[PK!ǠJ J A{5annotations/lib/Doctrine/Common/Annotations/Annotation/Target.phpnu[PK!H?6@annotations/lib/Doctrine/Common/Annotations/Annotation/Enum.phpnu[PK! /KoGannotations/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.phpnu[PK!Ec33CKannotations/lib/Doctrine/Common/Annotations/AnnotationException.phpnu[PK!m8 Fz^annotations/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.phpnu[PK!$Vi 6iannotations/lib/Doctrine/Common/Annotations/Reader.phpnu[PK!UU?sannotations/lib/Doctrine/Common/Annotations/PsrCachedReader.phpnu[PK!^zzannotations/composer.jsonnu[PK!``))hannotations/LICENSEnu[PK! C,Ԛannotations/psalm.xmlnu[PK!5annotations/README.mdnu[PK!.HۯX&X&#1annotations/docs/en/annotations.rstnu[PK! \AAannotations/docs/en/sidebar.rstnu[PK!CB''lannotations/docs/en/custom.rstnu[PK!' Hannotations/docs/en/index.rstnu[PK!=2`lexer/composer.jsonnu[PK!`XQ)) zlexer/LICENSEnu[PK!oKww lexer/UPGRADE.mdnu[PK!6qoo lexer/README.mdnu[PK!pSSElexer/src/AbstractLexer.phpnu[PK!+{ +lexer/src/Token.phpnu[PK!9dbal/composer.jsonnu[PK!%SmnnLBdbal/CONTRIBUTING.mdnu[PK!`XQ)) Cdbal/LICENSEnu[PK!?ЋncHdbal/bin/doctrine-dbal.phpnu[PK!?BBMdbal/bin/doctrine-dbalnu[PK! Zgii5Ndbal/README.mdnu[PK!0&bdbal/src/Query/ForUpdate.phpnu[PK!j3ddbal/src/Query/ForUpdate/ConflictResolutionMode.phpnu[PK! %%/fdbal/src/Query/Expression/ExpressionBuilder.phpnu[PK!V..1dbal/src/Query/Expression/CompositeExpression.phpnu[PK!P>dbal/src/Query/QueryBuilder.phpnu[PK!ki1!kdbal/src/Query/QueryException.phpnu[PK!LL_odbal/src/Query/Limit.phpnu[PK!%"LFFqdbal/src/Query/SelectQuery.phpnu[PK!84zdbal/src/ParameterType.phpnu[PK!A<3~dbal/src/Exception/ConstraintViolationException.phpnu[PK!R  &dbal/src/Exception/InvalidLockMode.phpnu[PK!ρz~~%Fdbal/src/Exception/ConnectionLost.phpnu[PK!dZP)dbal/src/Exception/RetryableException.phpnu[PK!rC=Rdbal/src/Exception/ForeignKeyConstraintViolationException.phpnu[PK!<枦&dbal/src/Exception/DriverException.phpnu[PK!E;I2dbal/src/Exception/NonUniqueFieldNameException.phpnu[PK!)dbal/src/Exception/SchemaDoesNotExist.phpnu[PK!%D4֍dbal/src/Exception/DatabaseObjectExistsException.phpnu[PK!cuZc-dbal/src/Exception/TableNotFoundException.phpnu[PK!s &,dbal/src/Exception/ServerException.phpnu[PK!fRT40Bdbal/src/Exception/InvalidFieldNameException.phpnu[PK!B=*dbal/src/Exception/ConnectionException.phpnu[PK!9dbal/src/Exception/UniqueConstraintViolationException.phpnu[PK!{D(dbal/src/Exception/DeadlockException.phpnu[PK!d2+-dbal/src/Exception/TableExistsException.phpnu[PK!acD(tdbal/src/Exception/ReadOnlyException.phpnu[PK!O[/dbal/src/Exception/InvalidArgumentException.phpnu[PK!4,dbal/src/Exception/MalformedDsnException.phpnu[PK!+ dbal/src/Exception/SyntaxErrorException.phpnu[PK!Vʴ!,dbal/src/Exception/NoKeyValue.phpnu[PK!o^+Wdbal/src/Exception/DatabaseDoesNotExist.phpnu[PK!@W.aa'<dbal/src/Exception/DatabaseRequired.phpnu[PK!Chu:dbal/src/Exception/NotNullConstraintViolationException.phpnu[PK!@~\/Ddbal/src/Exception/LockWaitTimeoutException.phpnu[PK!"q6dbal/src/Exception/DatabaseObjectNotFoundException.phpnu[PK!R||dbal/src/Driver.phpnu[PK!D*fdbal/src/Exception.phpnu[PK!̊wdbal/src/Query.phpnu[PK!#9++5`dbal/src/Connections/PrimaryReadReplicaConnection.phpnu[PK!Pu_$$adbal/src/DriverManager.phpnu[PK!KZ[[< dbal/src/Cache/ArrayResult.phpnu[PK!q}qq$ dbal/src/Cache/QueryCacheProfile.phpnu[PK!wu!5 dbal/src/Cache/CacheException.phpnu[PK! 27 dbal/src/Logging/Middleware.phpnu[PK!yUU*: dbal/src/Logging/Driver.phpnu[PK!i}U? dbal/src/Logging/Connection.phpnu[PK!;8G dbal/src/Logging/DebugStack.phpnu[PK!"f +M dbal/src/Logging/Statement.phpnu[PK! Y dbal/src/Logging/LoggerChain.phpnu[PK!DL!9] dbal/src/Logging/SQLLogger.phpnu[PK!8NӮMM?8 dbal/src/Tools/DsnParser.phpnu[PK!!2R dbal/src/Schema/Exception/SequenceDoesNotExist.phpnu[PK!V24U dbal/src/Schema/Exception/ForeignKeyDoesNotExist.phpnu[PK!<::dW dbal/src/Schema/Exception/UniqueConstraintDoesNotExist.phpnu[PK!՗0Y dbal/src/Schema/Exception/ColumnDoesNotExist.phpnu[PK!0Mʔ5\ dbal/src/Schema/Exception/NamedForeignKeyRequired.phpnu[PK!YÞ^0 ` dbal/src/Schema/Exception/TableAlreadyExists.phpnu[PK!Ns/*b dbal/src/Schema/Exception/TableDoesNotExist.phpnu[PK![3Kd dbal/src/Schema/Exception/SequenceAlreadyExists.phpnu[PK!eu.qf dbal/src/Schema/Exception/IndexNameInvalid.phpnu[PK!O/h dbal/src/Schema/Exception/IndexDoesNotExist.phpnu[PK!1j dbal/src/Schema/Exception/UnknownColumnOption.phpnu[PK!c F4l dbal/src/Schema/Exception/NamespaceAlreadyExists.phpnu[PK!E ww.n dbal/src/Schema/Exception/InvalidTableName.phpnu[PK!0p dbal/src/Schema/Exception/IndexAlreadyExists.phpnu[PK!^1s dbal/src/Schema/Exception/ColumnAlreadyExists.phpnu[PK!§""Ou dbal/src/Schema/Index.phpnu[PK!s} dbal/src/Schema/ColumnDiff.phpnu[PK!3MMY--W dbal/src/Schema/Constraint.phpnu[PK!ߎ%%ҭ dbal/src/Schema/Column.phpnu[PK!bn #p|dbal/src/Types/DateIntervalType.phpnu[PK!2jndbal/src/Types/BooleanType.phpnu[PK!"t dbal/src/Types/DateType.phpnu[PK!=۪(dbal/src/Types/DateTimeImmutableType.phpnu[PK!!$dbal/src/Types/TimeImmutableType.phpnu[PK!dbal/src/Driver/IBMDB2/Exception/CannotCreateTemporaryFile.phpnu[PK!ϖ??3gdbal/src/Driver/IBMDB2/Exception/StatementError.phpnu[PK!q2 dbal/src/Driver/IBMDB2/Exception/PrepareFailed.phpnu[PK!TT5Ndbal/src/Driver/IBMDB2/Exception/ConnectionFailed.phpnu[PK!w U//=dbal/src/Driver/IBMDB2/Exception/CannotCopyStreamToStream.phpnu[PK!E?BB!dbal/src/Driver/IBMDB2/Driver.phpnu[PK!3Oj %6dbal/src/Driver/IBMDB2/Connection.phpnu[PK!b'') dbal/src/Driver/IBMDB2/DataSourceName.phpnu[PK!~c$dbal/src/Driver/IBMDB2/Statement.phpnu[PK!z$ ! *dbal/src/Driver/IBMDB2/Result.phpnu[PK!=2[3dbal/src/Driver/Exception/UnknownParameterType.phpnu[PK!|g\5dbal/src/Driver/Middleware.phpnu[PK!R6dbal/src/Driver/Exception.phpnu[PK!Ƚ33%38dbal/src/Driver/AbstractException.phpnu[PK!*<dbal/src/Driver/SQLSrv/Exception/Error.phpnu[PK!J,  !@dbal/src/Driver/SQLSrv/Driver.phpnu[PK!!( ( %IHdbal/src/Driver/SQLSrv/Connection.phpnu[PK!W HH$Udbal/src/Driver/SQLSrv/Statement.phpnu[PK!^GZ^ !bmdbal/src/Driver/SQLSrv/Result.phpnu[PK!1}$wdbal/src/Driver/PDO/PDOException.phpnu[PK!]TSii!zdbal/src/Driver/PDO/Exception.phpnu[PK!qc{{(x}dbal/src/Driver/PDO/ParameterTypeMap.phpnu[PK!HA``%Kdbal/src/Driver/PDO/SQLite/Driver.phpnu[PK!iF %dbal/src/Driver/PDO/SQLSrv/Driver.phpnu[PK!#kk)dbal/src/Driver/PDO/SQLSrv/Connection.phpnu[PK!4D (ɞdbal/src/Driver/PDO/SQLSrv/Statement.phpnu[PK!X"""dbal/src/Driver/PDO/Connection.phpnu[PK!x4mii$ldbal/src/Driver/PDO/PgSQL/Driver.phpnu[PK!<<$)dbal/src/Driver/PDO/MySQL/Driver.phpnu[PK!&ɞ!dbal/src/Driver/PDO/Statement.phpnu[PK!G"dbal/src/Driver/PDO/OCI/Driver.phpnu[PK!ti dbal/src/Driver/PDO/Result.phpnu[PK!֞#dbal/src/Driver/Connection.phpnu[PK!(vdbal/src/Driver/AbstractSQLiteDriver.phpnu[PK!=>>(]dbal/src/Driver/AbstractOracleDriver.phpnu[PK!X" dbal/src/Driver/SQLite3/Driver.phpnu[PK!*q@gg%]dbal/src/Driver/SQLite3/Exception.phpnu[PK! &dbal/src/Driver/SQLite3/Connection.phpnu[PK!#[%dbal/src/Driver/SQLite3/Statement.phpnu[PK!}}"g+dbal/src/Driver/SQLite3/Result.phpnu[PK!"y463dbal/src/Driver/PgSQL/Exception/UnknownParameter.phpnu[PK!__35dbal/src/Driver/PgSQL/Exception/UnexpectedValue.phpnu[PK!D< 7dbal/src/Driver/PgSQL/Driver.phpnu[PK!^ '#PBdbal/src/Driver/PgSQL/Exception.phpnu[PK!l$XEdbal/src/Driver/PgSQL/Connection.phpnu[PK!#uVdbal/src/Driver/PgSQL/Statement.phpnu[PK!V:+ldbal/src/Driver/PgSQL/ConvertParameters.phpnu[PK!4 uqdbal/src/Driver/PgSQL/Result.phpnu[PK!5{dbal/src/Driver/Statement.phpnu[PK!+ :Wdbal/src/Driver/AbstractOracleDriver/EasyConnectString.phpnu[PK!D3||7ydbal/src/Driver/OCI8/Exception/SequenceDoesNotExist.phpnu[PK!kH7\dbal/src/Driver/OCI8/Exception/InvalidConfiguration.phpnu[PK!1=edbal/src/Driver/OCI8/Exception/NonTerminatedStringLiteral.phpnu[PK!%s3֭dbal/src/Driver/OCI8/Exception/ConnectionFailed.phpnu[PK!(dbal/src/Driver/OCI8/Exception/Error.phpnu[PK!_fu<8Odbal/src/Driver/OCI8/Exception/UnknownParameterIndex.phpnu[PK!Hf}dbal/src/Driver/OCI8/Driver.phpnu[PK!7DSS#ydbal/src/Driver/OCI8/Connection.phpnu[PK! 80"dbal/src/Driver/OCI8/Statement.phpnu[PK!||&+dbal/src/Driver/OCI8/ExecutionMode.phpnu[PK!N- dbal/src/Driver/OCI8/Result.phpnu[PK!ҽw""=dbal/src/Driver/OCI8/ConvertPositionalToNamedPlaceholders.phpnu[PK!@,5|dbal/src/Driver/OCI8/Middleware/InitializeSession.phpnu[PK!R'eS+dbal/src/Driver/AbstractSQLServerDriver.phpnu[PK!1C..E9dbal/src/Driver/AbstractSQLiteDriver/Middleware/EnableForeignKeys.phpnu[PK!<8""'dbal/src/Driver/AbstractMySQLDriver.phpnu[PK!} %U dbal/src/Driver/AbstractDB2Driver.phpnu[PK!` ,c,dbal/src/Driver/AbstractPostgreSQLDriver.phpnu[PK!O 7dbal/src/Driver/Result.phpnu[PK!['7Adbal/src/Driver/Middleware/AbstractResultMiddleware.phpnu[PK!K:Gdbal/src/Driver/Middleware/AbstractStatementMiddleware.phpnu[PK!BJ7Odbal/src/Driver/Middleware/AbstractDriverMiddleware.phpnu[PK!} ;Xdbal/src/Driver/Middleware/AbstractConnectionMiddleware.phpnu[PK!1qqEzcdbal/src/Driver/AbstractSQLServerDriver/Exception/PortWithoutHost.phpnu[PK!V[4`edbal/src/Driver/Mysqli/Exception/ConnectionError.phpnu[PK!~ѯ>hdbal/src/Driver/Mysqli/Exception/FailedReadingStreamOffset.phpnu[PK! 1jdbal/src/Driver/Mysqli/Exception/HostRequired.phpnu[PK!nGldbal/src/Driver/Mysqli/Exception/NonStreamResourceUsedAsLargeObject.phpnu[PK!w73odbal/src/Driver/Mysqli/Exception/StatementError.phpnu[PK!˩3rdbal/src/Driver/Mysqli/Exception/InvalidCharset.phpnu[PK!`2vdbal/src/Driver/Mysqli/Exception/InvalidOption.phpnu[PK!JQQ56ydbal/src/Driver/Mysqli/Exception/ConnectionFailed.phpnu[PK!E^Ȋ !|dbal/src/Driver/Mysqli/Driver.phpnu[PK!.ljdbal/src/Driver/Mysqli/Initializer/Options.phpnu[PK!7vy.dbal/src/Driver/Mysqli/Initializer/Charset.phpnu[PK!G VV-dbal/src/Driver/Mysqli/Initializer/Secure.phpnu[PK!a %Gdbal/src/Driver/Mysqli/Connection.phpnu[PK!ڬ&ddbal/src/Driver/Mysqli/Initializer.phpnu[PK!m[$dbal/src/Driver/Mysqli/Statement.phpnu[PK!Gק!~dbal/src/Driver/Mysqli/Result.phpnu[PK!\Y-dbal/src/Driver/ServerInfoAwareConnection.phpnu[PK!:TTT1dbal/src/Driver/API/IBMDB2/ExceptionConverter.phpnu[PK! 73dbal/src/Driver/API/SQLite/UserDefinedFunctions.phpnu[PK!f?$ $ 1%dbal/src/Driver/API/SQLite/ExceptionConverter.phpnu[PK!ah# # 1dbal/src/Driver/API/SQLSrv/ExceptionConverter.phpnu[PK!ά``0.dbal/src/Driver/API/MySQL/ExceptionConverter.phpnu[PK!-t . dbal/src/Driver/API/OCI/ExceptionConverter.phpnu[PK!ONN*dbal/src/Driver/API/ExceptionConverter.phpnu[PK!_P P 5dbal/src/Driver/API/PostgreSQL/ExceptionConverter.phpnu[PK!yWw(dbal/src/Driver/FetchUtils.phpnu[PK!u+-dbal/src/Id/TableGeneratorSchemaVisitor.phpnu[PK!^)5dbal/src/Id/TableGenerator.phpnu[PK! 9dbal/src/Platforms/Keywords/ReservedKeywordsValidator.phpnu[PK!#  /dbal/src/Platforms/Keywords/MySQL80Keywords.phpnu[PK!V%%+odbal/src/Platforms/Keywords/DB2Keywords.phpnu[PK!;3^X X &edbal/src/Platforms/MySQL57Platform.phpnu[PK!;#, dbal/src/Platforms/PostgreSQL100Platform.phpnu[PK!|+dbal/src/Platforms/PostgreSQL94Platform.phpnu[PK!.ް%rdbal/src/Platforms/SqlitePlatform.phpnu[PK!ff*Udbal/src/Platforms/MariaDb1027Platform.phpnu[PK!y$`dbal/src/Platforms/MySQLPlatform.phpnu[PK!~5(dbal/src/Platforms/SQLServerPlatform.phpnu[PK!h>">"ִdbal/src/Result.phpnu[PK!SbΘ6Wdbal/src/SQL/Builder/CreateSchemaObjectsSQLBuilder.phpnu[PK!cC)Udbal/src/SQL/Builder/SelectSQLBuilder.phpnu[PK!wc 0dbal/src/SQL/Builder/DefaultSelectSQLBuilder.phpnu[PK!@!WW4dbal/src/SQL/Builder/DropSchemaObjectsSQLBuilder.phpnu[PK!2!edMMdbal/src/SQL/Parser.phpnu[PK!D%8=dbal/src/SQL/Parser/Exception/RegularExpressionError.phpnu[PK!L*!:dbal/src/SQL/Parser/Exception.phpnu[PK!P dbal/src/SQL/Parser/Visitor.phpnu[PK! AOdbal/src/ArrayParameters/Exception/MissingPositionalParameter.phpnu[PK!<dbal/src/ArrayParameters/Exception/MissingNamedParameter.phpnu[PK!||& dbal/src/ArrayParameters/Exception.phpnu[PK!Q!!1z cache/lib/Doctrine/Common/Cache/CacheProvider.phpnu[PK!S<^^2,cache/lib/Doctrine/Common/Cache/FlushableCache.phpnu[PK!/7.cache/lib/Doctrine/Common/Cache/MultiOperationCache.phpnu[PK!vzgQQ10cache/lib/Doctrine/Common/Cache/MultiGetCache.phpnu[PK!;D"  52cache/lib/Doctrine/Common/Cache/Psr6/CacheAdapter.phpnu[PK! 9IScache/lib/Doctrine/Common/Cache/Psr6/DoctrineProvider.phpnu[PK!P "{{7h_cache/lib/Doctrine/Common/Cache/Psr6/TypedCacheItem.phpnu[PK!`4  2Jhcache/lib/Doctrine/Common/Cache/Psr6/CacheItem.phpnu[PK!>8rcache/lib/Doctrine/Common/Cache/Psr6/InvalidArgument.phpnu[PK! )+tcache/lib/Doctrine/Common/Cache/Cache.phpnu[PK!G52scache/lib/Doctrine/Common/Cache/ClearableCache.phpnu[PK!4ʁcache/lib/Doctrine/Common/Cache/MultiDeleteCache.phpnu[PK!%1cache/lib/Doctrine/Common/Cache/MultiPutCache.phpnu[PK!i !cache/UPGRADE-1.11.mdnu[PK!jjXcache/composer.jsonnu[PK!9)) cache/LICENSEnu[PK!BHkcache/README.mdnu[PK!i9cache/UPGRADE-1.4.mdnu[PK!:%:%6Sdeprecations/lib/Doctrine/Deprecations/Deprecation.phpnu[PK!/GdzEdeprecations/lib/Doctrine/Deprecations/PHPUnit/VerifyDeprecations.phpnu[PK!,êdeprecations/composer.jsonnu[PK!"0)).deprecations/LICENSEnu[PK!qU $$deprecations/README.mdnu[PK