diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 19e3da080c93b..0820ae8d918b3 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -122,7 +122,7 @@ jobs: uses: shivammathur/setup-php@v2 with: coverage: "none" - extensions: "json,couchbase,memcached,mongodb,redis,rdkafka,xsl,ldap" + extensions: "json,couchbase,memcached,mongodb-1.10.0,redis,rdkafka,xsl,ldap" ini-values: date.timezone=Europe/Paris,memory_limit=-1,default_socket_timeout=10,session.gc_probability=0,apc.enable_cli=1,zend.assertions=1 php-version: "${{ matrix.php }}" tools: pecl @@ -146,7 +146,7 @@ jobs: echo COMPOSER_ROOT_VERSION=$COMPOSER_ROOT_VERSION >> $GITHUB_ENV echo "::group::composer update" - composer require --dev --no-update mongodb/mongodb:@stable + composer require --dev --no-update mongodb/mongodb:"1.9.1@dev|^1.9.1@stable" composer update --no-progress --ansi echo "::endgroup::" diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 430d67c4c8cda..b2fabdd9c8dbb 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -140,8 +140,8 @@ jobs: run: | sed -i 's/"\*\*\/Tests\/"//' composer.json composer install -q --optimize-autoloader - SYMFONY_PATCH_TYPE_DECLARATIONS=force=1 php .github/patch-types.php - SYMFONY_PATCH_TYPE_DECLARATIONS=force=1 php .github/patch-types.php # ensure the script is idempotent + SYMFONY_PATCH_TYPE_DECLARATIONS='force=1&php=7.2' php .github/patch-types.php + SYMFONY_PATCH_TYPE_DECLARATIONS='force=1&php=7.2' php .github/patch-types.php # ensure the script is idempotent echo PHPUNIT="$PHPUNIT,legacy" >> $GITHUB_ENV - name: Run tests diff --git a/src/Symfony/Component/ErrorHandler/DebugClassLoader.php b/src/Symfony/Component/ErrorHandler/DebugClassLoader.php index 578c7840be2d4..70e7c7dca5302 100644 --- a/src/Symfony/Component/ErrorHandler/DebugClassLoader.php +++ b/src/Symfony/Component/ErrorHandler/DebugClassLoader.php @@ -20,6 +20,7 @@ use PHPUnit\Framework\MockObject\MockObject; use Prophecy\Prophecy\ProphecySubjectInterface; use ProxyManager\Proxy\ProxyInterface; +use Symfony\Component\ErrorHandler\Internal\TentativeTypes; /** * Autoloader checking if the class is really defined in the file found. @@ -32,7 +33,7 @@ * This behavior is controlled by the SYMFONY_PATCH_TYPE_DECLARATIONS env var, * which is a url-encoded array with the follow parameters: * - "force": any value enables deprecation notices - can be any of: - * - "docblock" to patch only docblock annotations + * - "phpdoc" to patch only docblock annotations * - "object" to turn union types to the "object" type when possible (not recommended) * - "1" to add all possible return types including magic methods * - "0" to add possible return types excluding magic methods @@ -57,7 +58,7 @@ class DebugClassLoader 'resource' => 'resource', 'boolean' => 'bool', 'true' => 'bool', - 'false' => 'bool', + 'false' => 'false', 'integer' => 'int', 'array' => 'array', 'bool' => 'bool', @@ -70,17 +71,14 @@ class DebugClassLoader 'self' => 'self', 'parent' => 'parent', 'mixed' => 'mixed', - ] + (\PHP_VERSION_ID >= 80000 ? [ 'static' => 'static', '$this' => 'static', - ] : [ - 'static' => 'object', - '$this' => 'object', - ]); + ]; private const BUILTIN_RETURN_TYPES = [ 'void' => true, 'array' => true, + 'false' => true, 'bool' => true, 'callable' => true, 'float' => true, @@ -90,78 +88,16 @@ class DebugClassLoader 'string' => true, 'self' => true, 'parent' => true, - ] + (\PHP_VERSION_ID >= 80000 ? [ 'mixed' => true, 'static' => true, - ] : []); + ]; private const MAGIC_METHODS = [ - '__set' => 'void', '__isset' => 'bool', - '__unset' => 'void', '__sleep' => 'array', - '__wakeup' => 'void', '__toString' => 'string', - '__clone' => 'void', '__debugInfo' => 'array', '__serialize' => 'array', - '__unserialize' => 'void', - ]; - - private const INTERNAL_TYPES = [ - 'ArrayAccess' => [ - 'offsetExists' => 'bool', - 'offsetSet' => 'void', - 'offsetUnset' => 'void', - ], - 'Countable' => [ - 'count' => 'int', - ], - 'Iterator' => [ - 'next' => 'void', - 'valid' => 'bool', - 'rewind' => 'void', - ], - 'IteratorAggregate' => [ - 'getIterator' => '\Traversable', - ], - 'OuterIterator' => [ - 'getInnerIterator' => '\Iterator', - ], - 'RecursiveIterator' => [ - 'hasChildren' => 'bool', - ], - 'SeekableIterator' => [ - 'seek' => 'void', - ], - 'Serializable' => [ - 'serialize' => 'string', - 'unserialize' => 'void', - ], - 'SessionHandlerInterface' => [ - 'open' => 'bool', - 'close' => 'bool', - 'read' => 'string', - 'write' => 'bool', - 'destroy' => 'bool', - 'gc' => 'bool', - ], - 'SessionIdInterface' => [ - 'create_sid' => 'string', - ], - 'SessionUpdateTimestampHandlerInterface' => [ - 'validateId' => 'bool', - 'updateTimestamp' => 'bool', - ], - 'Throwable' => [ - 'getMessage' => 'string', - 'getCode' => 'int', - 'getFile' => 'string', - 'getLine' => 'int', - 'getTrace' => 'array', - 'getPrevious' => '?\Throwable', - 'getTraceAsString' => 'string', - ], ]; private $classLoader; @@ -190,10 +126,14 @@ public function __construct(callable $classLoader) parse_str(getenv('SYMFONY_PATCH_TYPE_DECLARATIONS') ?: '', $this->patchTypes); $this->patchTypes += [ 'force' => null, - 'php' => null, + 'php' => \PHP_MAJOR_VERSION.'.'.\PHP_MINOR_VERSION, 'deprecations' => false, ]; + if ('phpdoc' === $this->patchTypes['force']) { + $this->patchTypes['force'] = 'docblock'; + } + if (!isset(self::$caseCheck)) { $file = is_file(__FILE__) ? __FILE__ : rtrim(realpath('.'), \DIRECTORY_SEPARATOR); $i = strrpos($file, \DIRECTORY_SEPARATOR); @@ -218,11 +158,6 @@ public function __construct(callable $classLoader) } } - /** - * Gets the wrapped class loader. - * - * @return callable - */ public function getClassLoader(): callable { return $this->classLoader; @@ -431,34 +366,28 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array $vendor = str_replace('_', '\\', substr($class, 0, $vendorLen)); } + $parent = get_parent_class($class) ?: null; + self::$returnTypes[$class] = []; + // Detect annotations on the class - if (false !== $doc = $refl->getDocComment()) { + if ($doc = $this->parsePhpDoc($refl)) { foreach (['final', 'deprecated', 'internal'] as $annotation) { - if (false !== strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$|\r?\n)#s', $doc, $notice)) { - self::${$annotation}[$class] = isset($notice[1]) ? preg_replace('#\.?\r?\n( \*)? *(?= |\r?\n|$)#', '', $notice[1]) : ''; + if (null !== $description = $doc[$annotation][0] ?? null) { + self::${$annotation}[$class] = '' !== $description ? ' '.$description.(preg_match('/[.!]$/', $description) ? '' : '.') : '.'; } } - if ($refl->isInterface() && false !== strpos($doc, 'method') && preg_match_all('#\n \* @method\s+(static\s+)?+([\w\|&\[\]\\\]+\s+)?(\w+(?:\s*\([^\)]*\))?)+(.+?([[:punct:]]\s*)?)?(?=\r?\n \*(?: @|/$|\r?\n))#', $doc, $notice, \PREG_SET_ORDER)) { - foreach ($notice as $method) { - $static = '' !== $method[1] && !empty($method[2]); - $name = $method[3]; - $description = $method[4] ?? null; - if (false === strpos($name, '(')) { - $name .= '()'; - } - if (null !== $description) { - $description = trim($description); - if (!isset($method[5])) { - $description .= '.'; - } + if ($refl->isInterface() && isset($doc['method'])) { + foreach ($doc['method'] as $name => [$static, $returnType, $signature, $description]) { + self::$method[$class][] = [$class, $static, $returnType, $name.$signature, $description]; + + if ('' !== $returnType) { + $this->setReturnType($returnType, $refl->name, $name, $refl->getFileName(), $parent); } - self::$method[$class][] = [$class, $name, $static, $description]; } } } - $parent = get_parent_class($class) ?: null; $parentAndOwnInterfaces = $this->getOwnInterfaces($class, $parent); if ($parent) { $parentAndOwnInterfaces[$parent] = $parent; @@ -468,7 +397,7 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array } if (isset(self::$final[$parent])) { - $deprecations[] = sprintf('The "%s" class is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $parent, self::$final[$parent], $className); + $deprecations[] = sprintf('The "%s" class is considered final%s It may change without further notice as of its next major version. You should not extend it from "%s".', $parent, self::$final[$parent], $className); } } @@ -481,10 +410,10 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array $type = class_exists($class, false) ? 'class' : (interface_exists($class, false) ? 'interface' : 'trait'); $verb = class_exists($use, false) || interface_exists($class, false) ? 'extends' : (interface_exists($use, false) ? 'implements' : 'uses'); - $deprecations[] = sprintf('The "%s" %s %s "%s" that is deprecated%s.', $className, $type, $verb, $use, self::$deprecated[$use]); + $deprecations[] = sprintf('The "%s" %s %s "%s" that is deprecated%s', $className, $type, $verb, $use, self::$deprecated[$use]); } if (isset(self::$internal[$use]) && strncmp($vendor, str_replace('_', '\\', $use), $vendorLen)) { - $deprecations[] = sprintf('The "%s" %s is considered internal%s. It may change without further notice. You should not use it from "%s".', $use, class_exists($use, false) ? 'class' : (interface_exists($use, false) ? 'interface' : 'trait'), self::$internal[$use], $className); + $deprecations[] = sprintf('The "%s" %s is considered internal%s It may change without further notice. You should not use it from "%s".', $use, class_exists($use, false) ? 'class' : (interface_exists($use, false) ? 'interface' : 'trait'), self::$internal[$use], $className); } if (isset(self::$method[$use])) { if ($refl->isAbstract()) { @@ -504,14 +433,13 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array } $hasCall = $refl->hasMethod('__call'); $hasStaticCall = $refl->hasMethod('__callStatic'); - foreach (self::$method[$use] as $method) { - [$interface, $name, $static, $description] = $method; + foreach (self::$method[$use] as [$interface, $static, $returnType, $name, $description]) { if ($static ? $hasStaticCall : $hasCall) { continue; } $realName = substr($name, 0, strpos($name, '(')); if (!$refl->hasMethod($realName) || !($methodRefl = $refl->getMethod($realName))->isPublic() || ($static && !$methodRefl->isStatic()) || (!$static && $methodRefl->isStatic())) { - $deprecations[] = sprintf('Class "%s" should implement method "%s::%s"%s', $className, ($static ? 'static ' : '').$interface, $name, null == $description ? '.' : ': '.$description); + $deprecations[] = sprintf('Class "%s" should implement method "%s::%s%s"%s', $className, ($static ? 'static ' : '').$interface, $name, $returnType ? ': '.$returnType : '', null === $description ? '.' : ': '.$description); } } } @@ -534,7 +462,6 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array self::$finalMethods[$class] = []; self::$internalMethods[$class] = []; self::$annotatedParameters[$class] = []; - self::$returnTypes[$class] = []; foreach ($parentAndOwnInterfaces as $use) { foreach (['finalMethods', 'internalMethods', 'annotatedParameters', 'returnTypes'] as $property) { if (isset(self::${$property}[$use])) { @@ -542,11 +469,17 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array } } - if (null !== (self::INTERNAL_TYPES[$use] ?? null)) { - foreach (self::INTERNAL_TYPES[$use] as $method => $returnType) { - if ('void' !== $returnType) { - self::$returnTypes[$class] += [$method => [$returnType, $returnType, $use, '']]; + if (null !== (TentativeTypes::RETURN_TYPES[$use] ?? null)) { + foreach (TentativeTypes::RETURN_TYPES[$use] as $method => $returnType) { + $returnType = explode('|', $returnType); + foreach ($returnType as $i => $t) { + if ('?' !== $t && !isset(self::BUILTIN_RETURN_TYPES[$t])) { + $returnType[$i] = '\\'.$t; + } } + $returnType = implode('|', $returnType); + + self::$returnTypes[$class] += [$method => [$returnType, 0 === strpos($returnType, '?') ? substr($returnType, 1).'|null' : $returnType, $use, '']]; } } } @@ -568,18 +501,18 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array if ($parent && isset(self::$finalMethods[$parent][$method->name])) { [$declaringClass, $message] = self::$finalMethods[$parent][$method->name]; - $deprecations[] = sprintf('The "%s::%s()" method is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $declaringClass, $method->name, $message, $className); + $deprecations[] = sprintf('The "%s::%s()" method is considered final%s It may change without further notice as of its next major version. You should not extend it from "%s".', $declaringClass, $method->name, $message, $className); } if (isset(self::$internalMethods[$class][$method->name])) { [$declaringClass, $message] = self::$internalMethods[$class][$method->name]; if (strncmp($ns, $declaringClass, $len)) { - $deprecations[] = sprintf('The "%s::%s()" method is considered internal%s. It may change without further notice. You should not extend it from "%s".', $declaringClass, $method->name, $message, $className); + $deprecations[] = sprintf('The "%s::%s()" method is considered internal%s It may change without further notice. You should not extend it from "%s".', $declaringClass, $method->name, $message, $className); } } // To read method annotations - $doc = $method->getDocComment(); + $doc = $this->parsePhpDoc($method); if (isset(self::$annotatedParameters[$class][$method->name])) { $definedParameters = []; @@ -588,7 +521,7 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array } foreach (self::$annotatedParameters[$class][$method->name] as $parameterName => $deprecation) { - if (!isset($definedParameters[$parameterName]) && !($doc && preg_match("/\\n\\s+\\* @param +((?(?!callable *\().*?|callable *\(.*\).*?))(?<= )\\\${$parameterName}\\b/", $doc))) { + if (!isset($definedParameters[$parameterName]) && !isset($doc['param'][$parameterName])) { $deprecations[] = sprintf($deprecation, $className); } } @@ -607,23 +540,23 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array || $method->isPrivate() || ('' === (self::$internal[$class] ?? null) && !$refl->isAbstract()) || '' === (self::$final[$class] ?? null) - || preg_match('/@(final|internal)$/m', $doc) + || '' === ($doc['final'][0] ?? null) + || '' === ($doc['internal'][0] ?? null) ; } - if (null !== ($returnType = self::$returnTypes[$class][$method->name] ?? self::MAGIC_METHODS[$method->name] ?? null) && !$method->hasReturnType() && !($doc && preg_match('/\n\s+\* @return +([^\s<(]+)/', $doc))) { - [$normalizedType, $returnType, $declaringClass, $declaringFile] = \is_string($returnType) ? [$returnType, $returnType, '', ''] : $returnType; + if (null !== ($returnType = self::$returnTypes[$class][$method->name] ?? null) && 'docblock' === $this->patchTypes['force'] && !$method->hasReturnType() && isset(TentativeTypes::RETURN_TYPES[$returnType[2]][$method->name])) { + $this->patchReturnTypeWillChange($method); + } - if ('void' === $normalizedType) { - $canAddReturnType = false; - } + if (null !== ($returnType ?? $returnType = self::MAGIC_METHODS[$method->name] ?? null) && !$method->hasReturnType() && !isset($doc['return'])) { + [$normalizedType, $returnType, $declaringClass, $declaringFile] = \is_string($returnType) ? [$returnType, $returnType, '', ''] : $returnType; if ($canAddReturnType && 'docblock' !== $this->patchTypes['force']) { $this->patchMethod($method, $returnType, $declaringFile, $normalizedType); } - - if (false === strpos($doc, '* @deprecated') && strncmp($ns, $declaringClass, $len)) { - if ($canAddReturnType && 'docblock' === $this->patchTypes['force'] && false === strpos($method->getFileName(), \DIRECTORY_SEPARATOR.'vendor'.\DIRECTORY_SEPARATOR)) { + if (!isset($doc['deprecated']) && strncmp($ns, $declaringClass, $len)) { + if ('docblock' === $this->patchTypes['force']) { $this->patchMethod($method, $returnType, $declaringFile, $normalizedType); } elseif ('' !== $declaringClass && $this->patchTypes['deprecations']) { $deprecations[] = sprintf('Method "%s::%s()" will return "%s" as of its next major version. Doing the same in %s "%s" will be required when upgrading.', $declaringClass, $method->name, $normalizedType, interface_exists($declaringClass) ? 'implementation' : 'child class', $className); @@ -637,11 +570,8 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array continue; } - $matches = []; - - if (!$method->hasReturnType() && ((false !== strpos($doc, '@return') && preg_match('/\n\s+\* @return +([^\s<(]+)/', $doc, $matches)) || 'void' !== (self::MAGIC_METHODS[$method->name] ?? 'void'))) { - $matches = $matches ?: [1 => self::MAGIC_METHODS[$method->name]]; - $this->setReturnType($matches[1], $method, $parent); + if (isset($doc['return']) || 'void' !== (self::MAGIC_METHODS[$method->name] ?? 'void')) { + $this->setReturnType($doc['return'] ?? self::MAGIC_METHODS[$method->name], $method->class, $method->name, $method->getFileName(), $parent, $method->getReturnType()); if (isset(self::$returnTypes[$class][$method->name][0]) && $canAddReturnType) { $this->fixReturnStatements($method, self::$returnTypes[$class][$method->name][0]); @@ -661,17 +591,13 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array $finalOrInternal = false; foreach (['final', 'internal'] as $annotation) { - if (false !== strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$|\r?\n)#s', $doc, $notice)) { - $message = isset($notice[1]) ? preg_replace('#\.?\r?\n( \*)? *(?= |\r?\n|$)#', '', $notice[1]) : ''; - self::${$annotation.'Methods'}[$class][$method->name] = [$class, $message]; + if (null !== $description = $doc[$annotation][0] ?? null) { + self::${$annotation.'Methods'}[$class][$method->name] = [$class, '' !== $description ? ' '.$description.(preg_match('/[[:punct:]]$/', $description) ? '' : '.') : '.']; $finalOrInternal = true; } } - if ($finalOrInternal || $method->isConstructor() || false === strpos($doc, '@param') || StatelessInvocation::class === $class) { - continue; - } - if (!preg_match_all('#\n\s+\* @param +((?(?!callable *\().*?|callable *\(.*\).*?))(?<= )\$([a-zA-Z0-9_\x7f-\xff]++)#', $doc, $matches, \PREG_SET_ORDER)) { + if ($finalOrInternal || $method->isConstructor() || !isset($doc['param']) || StatelessInvocation::class === $class) { continue; } if (!isset(self::$annotatedParameters[$class][$method->name])) { @@ -680,9 +606,8 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array $definedParameters[$parameter->name] = true; } } - foreach ($matches as [, $parameterType, $parameterName]) { + foreach ($doc['param'] as $parameterName => $parameterType) { if (!isset($definedParameters[$parameterName])) { - $parameterType = trim($parameterType); self::$annotatedParameters[$class][$method->name][$parameterName] = sprintf('The "%%s::%s()" method will require a new "%s$%s" argument in the next major version of its %s "%s", not defining it is deprecated.', $method->name, $parameterType ? $parameterType.' ' : '', $parameterName, interface_exists($className) ? 'interface' : 'parent class', $className); } } @@ -822,25 +747,32 @@ private function getOwnInterfaces(string $class, ?string $parent): array return $ownInterfaces; } - private function setReturnType(string $types, \ReflectionMethod $method, ?string $parent): void + private function setReturnType(string $types, string $class, string $method, string $filename, ?string $parent, \ReflectionType $returnType = null): void { - $nullable = false; + if ($nullable = 0 === strpos($types, 'null|')) { + $types = substr($types, 5); + } elseif ($nullable = '|null' === substr($types, -5)) { + $types = substr($types, 0, -5); + } + $arrayType = ['array' => 'array']; $typesMap = []; - foreach (explode('|', $types) as $t) { - $typesMap[$this->normalizeType($t, $method->class, $parent)] = $t; + $glue = false !== strpos($types, '&') ? '&' : '|'; + foreach (explode($glue, $types) as $t) { + $t = self::SPECIAL_RETURN_TYPES[strtolower($t)] ?? $t; + $typesMap[$this->normalizeType($t, $class, $parent, $returnType)][$t] = $t; } if (isset($typesMap['array'])) { if (isset($typesMap['Traversable']) || isset($typesMap['\Traversable'])) { - $typesMap['iterable'] = 'array' !== $typesMap['array'] ? $typesMap['array'] : 'iterable'; + $typesMap['iterable'] = $arrayType !== $typesMap['array'] ? $typesMap['array'] : ['iterable']; unset($typesMap['array'], $typesMap['Traversable'], $typesMap['\Traversable']); - } elseif ('array' !== $typesMap['array'] && isset(self::$returnTypes[$method->class][$method->name])) { + } elseif ($arrayType !== $typesMap['array'] && isset(self::$returnTypes[$class][$method]) && !$returnType) { return; } } if (isset($typesMap['array']) && isset($typesMap['iterable'])) { - if ('[]' === substr($typesMap['array'], -2)) { + if ($arrayType !== $typesMap['array']) { $typesMap['iterable'] = $typesMap['array']; } unset($typesMap['array']); @@ -854,43 +786,55 @@ private function setReturnType(string $types, \ReflectionMethod $method, ?string } } - $normalizedType = key($typesMap); - $returnType = current($typesMap); + $phpTypes = []; + $docTypes = []; foreach ($typesMap as $n => $t) { if ('null' === $n) { $nullable = true; - } elseif ('null' === $normalizedType) { - $normalizedType = $t; - $returnType = $t; - } elseif ($n !== $normalizedType || !preg_match('/^\\\\?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+$/', $n)) { - if ($iterable) { - $normalizedType = $returnType = 'iterable'; - } elseif ($object && 'object' === $this->patchTypes['force']) { - $normalizedType = $returnType = 'object'; - } else { - // ignore multi-types return declarations - return; - } + continue; + } + + $docTypes = array_merge($docTypes, $t); + + if ('mixed' === $n || 'void' === $n) { + $nullable = false; + $phpTypes = ['' => $n]; + continue; + } + + if ('resource' === $n) { + // there is no native type for "resource" + return; + } + + if (!isset($phpTypes[''])) { + $phpTypes[] = $n; } } - if ('void' === $normalizedType || (\PHP_VERSION_ID >= 80000 && 'mixed' === $normalizedType)) { - $nullable = false; - } elseif (!isset(self::BUILTIN_RETURN_TYPES[$normalizedType]) && isset(self::SPECIAL_RETURN_TYPES[$normalizedType])) { - // ignore other special return types + if (!$phpTypes) { return; } - if ($nullable) { - $normalizedType = '?'.$normalizedType; - $returnType .= '|null'; + if (1 < \count($phpTypes)) { + if ($iterable && '8.0' > $this->patchTypes['php']) { + $phpTypes = $docTypes = ['iterable']; + } elseif ($object && 'object' === $this->patchTypes['force']) { + $phpTypes = $docTypes = ['object']; + } elseif ('8.0' > $this->patchTypes['php']) { + // ignore multi-types return declarations + return; + } } - self::$returnTypes[$method->class][$method->name] = [$normalizedType, $returnType, $method->class, $method->getFileName()]; + $phpType = sprintf($nullable ? (1 < \count($phpTypes) ? '%s|null' : '?%s') : '%s', implode($glue, $phpTypes)); + $docType = sprintf($nullable ? '%s|null' : '%s', implode($glue, $docTypes)); + + self::$returnTypes[$class][$method] = [$phpType, $docType, $class, $filename]; } - private function normalizeType(string $type, string $class, ?string $parent): string + private function normalizeType(string $type, string $class, ?string $parent, ?\ReflectionType $returnType): string { if (isset(self::SPECIAL_RETURN_TYPES[$lcType = strtolower($type)])) { if ('parent' === $lcType = self::SPECIAL_RETURN_TYPES[$lcType]) { @@ -902,22 +846,54 @@ private function normalizeType(string $type, string $class, ?string $parent): st return $lcType; } - if ('[]' === substr($type, -2)) { - return 'array'; + // We could resolve "use" statements to return the FQDN + // but this would be too expensive for a runtime checker + + if ('[]' !== substr($type, -2)) { + return $type; } - if (preg_match('/^(array|iterable|callable) *[<(]/', $lcType, $m)) { - return $m[1]; + if ($returnType instanceof \ReflectionNamedType) { + $type = $returnType->getName(); + + if ('mixed' !== $type) { + return isset(self::SPECIAL_RETURN_TYPES[$type]) ? $type : '\\'.$type; + } } - // We could resolve "use" statements to return the FQDN - // but this would be too expensive for a runtime checker + return 'array'; + } + + /** + * Utility method to add #[ReturnTypeWillChange] where php triggers deprecations. + */ + private function patchReturnTypeWillChange(\ReflectionMethod $method) + { + if (\PHP_VERSION_ID >= 80000 && \count($method->getAttributes(\ReturnTypeWillChange::class))) { + return; + } + + if (!is_file($file = $method->getFileName())) { + return; + } + + $fileOffset = self::$fileOffsets[$file] ?? 0; + + $code = file($file); - return $type; + $startLine = $method->getStartLine() + $fileOffset - 2; + + if (false !== stripos($code[$startLine], 'ReturnTypeWillChange')) { + return; + } + + $code[$startLine] .= " #[\\ReturnTypeWillChange]\n"; + self::$fileOffsets[$file] = 1 + $fileOffset; + file_put_contents($file, $code); } /** - * Utility method to add @return annotations to the Symfony code-base where it triggers a self-deprecations. + * Utility method to add @return annotations to the Symfony code-base where it triggers self-deprecations. */ private function patchMethod(\ReflectionMethod $method, string $returnType, string $declaringFile, string $normalizedType) { @@ -931,18 +907,17 @@ private function patchMethod(\ReflectionMethod $method, string $returnType, stri $patchedMethods[$file][$startLine] = true; $fileOffset = self::$fileOffsets[$file] ?? 0; $startLine += $fileOffset - 2; - $nullable = '?' === $normalizedType[0] ? '?' : ''; - $normalizedType = ltrim($normalizedType, '?'); - $returnType = explode('|', $returnType); + if ($nullable = '|null' === substr($returnType, -5)) { + $returnType = substr($returnType, 0, -5); + } + $glue = false !== strpos($returnType, '&') ? '&' : '|'; + $returnType = explode($glue, $returnType); $code = file($file); foreach ($returnType as $i => $type) { if (preg_match('/((?:\[\])+)$/', $type, $m)) { $type = substr($type, 0, -\strlen($m[1])); $format = '%s'.$m[1]; - } elseif (preg_match('/^(array|iterable)<([^,>]++)>$/', $type, $m)) { - $type = $m[2]; - $format = $m[1].'<%s>'; } else { $format = null; } @@ -987,14 +962,14 @@ private function patchMethod(\ReflectionMethod $method, string $returnType, stri } $returnType[$i] = null !== $format ? sprintf($format, $alias) : $alias; - - if (!isset(self::SPECIAL_RETURN_TYPES[$normalizedType]) && !isset(self::SPECIAL_RETURN_TYPES[$returnType[$i]])) { - $normalizedType = $returnType[$i]; - } } if ('docblock' === $this->patchTypes['force'] || ('object' === $normalizedType && '7.1' === $this->patchTypes['php'])) { - $returnType = implode('|', $returnType); + $returnType = implode($glue, $returnType).($nullable ? '|null' : ''); + + if (false !== strpos($code[$startLine], '#[')) { + --$startLine; + } if ($method->getDocComment()) { $code[$startLine] = " * @return $returnType\n".$code[$startLine]; @@ -1013,7 +988,7 @@ private function patchMethod(\ReflectionMethod $method, string $returnType, stri self::$fileOffsets[$file] = $fileOffset; file_put_contents($file, $code); - $this->fixReturnStatements($method, $nullable.$normalizedType); + $this->fixReturnStatements($method, $normalizedType); } private static function getUseStatements(string $file): array @@ -1061,8 +1036,22 @@ private static function getUseStatements(string $file): array private function fixReturnStatements(\ReflectionMethod $method, string $returnType) { - if ('7.1' === $this->patchTypes['php'] && 'object' === ltrim($returnType, '?') && 'docblock' !== $this->patchTypes['force']) { - return; + if ('docblock' !== $this->patchTypes['force']) { + if ('7.1' === $this->patchTypes['php'] && 'object' === ltrim($returnType, '?')) { + return; + } + + if ('7.4' > $this->patchTypes['php'] && $method->hasReturnType()) { + return; + } + + if ('8.0' > $this->patchTypes['php'] && (false !== strpos($returnType, '|') || \in_array($returnType, ['mixed', 'static'], true))) { + return; + } + + if ('8.1' > $this->patchTypes['php'] && false !== strpos($returnType, '&')) { + return; + } } if (!is_file($file = $method->getFileName())) { @@ -1073,7 +1062,7 @@ private function fixReturnStatements(\ReflectionMethod $method, string $returnTy $i = (self::$fileOffsets[$file] ?? 0) + $method->getStartLine(); if ('?' !== $returnType && 'docblock' !== $this->patchTypes['force']) { - $fixedCode[$i - 1] = preg_replace('/\)(;?\n)/', "): $returnType\\1", $code[$i - 1]); + $fixedCode[$i - 1] = preg_replace('/\)(?::[^;\n]++)?(;?\n)/', "): $returnType\\1", $code[$i - 1]); } $end = $method->isGenerator() ? $i : $method->getEndLine(); @@ -1091,4 +1080,121 @@ private function fixReturnStatements(\ReflectionMethod $method, string $returnTy file_put_contents($file, $fixedCode); } } + + /** + * @param \ReflectionClass|\ReflectionMethod|\ReflectionProperty $reflector + */ + private function parsePhpDoc(\Reflector $reflector): array + { + if (!$doc = $reflector->getDocComment()) { + return []; + } + + $tagName = ''; + $tagContent = ''; + + $tags = []; + + foreach (explode("\n", substr($doc, 3, -2)) as $line) { + $line = ltrim($line); + $line = ltrim($line, '*'); + + if ('' === $line = trim($line)) { + if ('' !== $tagName) { + $tags[$tagName][] = $tagContent; + } + $tagName = $tagContent = ''; + continue; + } + + if ('@' === $line[0]) { + if ('' !== $tagName) { + $tags[$tagName][] = $tagContent; + $tagContent = ''; + } + + if (preg_match('{^@([-a-zA-Z0-9_:]++)(\s|$)}', $line, $m)) { + $tagName = $m[1]; + $tagContent = str_replace("\t", ' ', ltrim(substr($line, 2 + \strlen($tagName)))); + } else { + $tagName = ''; + } + } elseif ('' !== $tagName) { + $tagContent .= ' '.str_replace("\t", ' ', $line); + } + } + + if ('' !== $tagName) { + $tags[$tagName][] = $tagContent; + } + + foreach ($tags['method'] ?? [] as $i => $method) { + unset($tags['method'][$i]); + + $parts = preg_split('{(\s++|\((?:[^()]*+|(?R))*\)(?: *: *[^ ]++)?|<(?:[^<>]*+|(?R))*>|\{(?:[^{}]*+|(?R))*\})}', $method, -1, \PREG_SPLIT_DELIM_CAPTURE); + $returnType = ''; + $static = 'static' === $parts[0]; + + for ($i = $static ? 2 : 0; null !== $p = $parts[$i] ?? null; $i += 2) { + if (\in_array($p, ['', '|', '&', 'callable'], true) || \in_array(substr($returnType, -1), ['|', '&'], true)) { + $returnType .= trim($parts[$i - 1] ?? '').$p; + continue; + } + + $signature = '(' === ($parts[$i + 1][0] ?? '(') ? $parts[$i + 1] ?? '()' : null; + + if (null === $signature && '' === $returnType) { + $returnType = $p; + continue; + } + + if ($static && 2 === $i) { + $static = false; + $returnType = 'static'; + } + + if (\in_array($description = trim(implode('', \array_slice($parts, 2 + $i))), ['', '.'], true)) { + $description = null; + } elseif (!preg_match('/[.!]$/', $description)) { + $description .= '.'; + } + + $tags['method'][$p] = [$static, $returnType, $signature ?? '()', $description]; + break; + } + } + + foreach ($tags['param'] ?? [] as $i => $param) { + unset($tags['param'][$i]); + + if (\strlen($param) !== strcspn($param, '<{(')) { + $param = preg_replace('{\(([^()]*+|(?R))*\)(?: *: *[^ ]++)?|<([^<>]*+|(?R))*>|\{([^{}]*+|(?R))*\}}', '', $param); + } + + if (false === $i = strpos($param, '$')) { + continue; + } + if (false !== $j = strpos($param, ' $') ?: strpos($param, '&$')) { + $i = 1 + $j; + } + + $type = 0 === $i ? '' : rtrim(substr($param, 0, $i - 1)); + $param = substr($param, 1 + $i, (strpos($param, ' ', $i) ?: (1 + $i + \strlen($param))) - $i - 1); + + $tags['param'][$param] = $type; + } + + foreach (['var', 'return'] as $k) { + if (null === $v = $tags[$k][0] ?? null) { + continue; + } + if (\strlen($v) !== strcspn($v, '<{(')) { + $v = preg_replace('{\(([^()]*+|(?R))*\)(?: *: *[^ ]++)?|<([^<>]*+|(?R))*>|\{([^{}]*+|(?R))*\}}', '', $v); + } + + $tags[$k] = substr($v, 0, strpos($v, ' ') ?: \strlen($v)) ?: null; + } + + return $tags; + } } diff --git a/src/Symfony/Component/ErrorHandler/Internal/TentativeTypes.php b/src/Symfony/Component/ErrorHandler/Internal/TentativeTypes.php new file mode 100644 index 0000000000000..67499885ea3c0 --- /dev/null +++ b/src/Symfony/Component/ErrorHandler/Internal/TentativeTypes.php @@ -0,0 +1,1643 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ErrorHandler\Internal; + +/** + * This class has been generated by extract-tentative-return-types.php. + * + * @internal + */ +class TentativeTypes +{ + public const RETURN_TYPES = [ + 'CURLFile' => [ + 'getFilename' => 'string', + 'getMimeType' => 'string', + 'getPostFilename' => 'string', + 'setMimeType' => 'void', + 'setPostFilename' => 'void', + ], + 'DateTimeInterface' => [ + 'format' => 'string', + 'getTimezone' => 'DateTimeZone|false', + 'getOffset' => 'int', + 'getTimestamp' => 'int|false', + 'diff' => 'DateInterval', + '__wakeup' => 'void', + ], + 'DateTime' => [ + '__wakeup' => 'void', + '__set_state' => 'DateTime', + 'createFromImmutable' => 'DateTime', + 'createFromFormat' => 'DateTime|false', + 'getLastErrors' => 'array|false', + 'format' => 'string', + 'modify' => 'DateTime|false', + 'add' => 'DateTime', + 'sub' => 'DateTime', + 'getTimezone' => 'DateTimeZone|false', + 'setTimezone' => 'DateTime', + 'getOffset' => 'int', + 'setTime' => 'DateTime', + 'setDate' => 'DateTime', + 'setISODate' => 'DateTime', + 'setTimestamp' => 'DateTime', + 'getTimestamp' => 'int', + 'diff' => 'DateInterval', + ], + 'DateTimeImmutable' => [ + '__wakeup' => 'void', + '__set_state' => 'DateTimeImmutable', + 'createFromFormat' => 'DateTimeImmutable|false', + 'getLastErrors' => 'array|false', + 'format' => 'string', + 'getTimezone' => 'DateTimeZone|false', + 'getOffset' => 'int', + 'getTimestamp' => 'int', + 'diff' => 'DateInterval', + 'modify' => 'DateTimeImmutable|false', + 'add' => 'DateTimeImmutable', + 'sub' => 'DateTimeImmutable', + 'setTimezone' => 'DateTimeImmutable', + 'setTime' => 'DateTimeImmutable', + 'setDate' => 'DateTimeImmutable', + 'setISODate' => 'DateTimeImmutable', + 'setTimestamp' => 'DateTimeImmutable', + 'createFromMutable' => 'DateTimeImmutable', + ], + 'DateTimeZone' => [ + 'getName' => 'string', + 'getOffset' => 'int', + 'getTransitions' => 'array|false', + 'getLocation' => 'array|false', + 'listAbbreviations' => 'array', + 'listIdentifiers' => 'array', + '__wakeup' => 'void', + '__set_state' => 'DateTimeZone', + ], + 'DateInterval' => [ + 'createFromDateString' => 'DateInterval|false', + 'format' => 'string', + '__wakeup' => 'void', + '__set_state' => 'DateInterval', + ], + 'DatePeriod' => [ + 'getStartDate' => 'DateTimeInterface', + 'getEndDate' => '?DateTimeInterface', + 'getDateInterval' => 'DateInterval', + 'getRecurrences' => '?int', + '__wakeup' => 'void', + '__set_state' => 'DatePeriod', + ], + 'DOMNode' => [ + 'C14N' => 'string|false', + 'C14NFile' => 'int|false', + 'getLineNo' => 'int', + 'getNodePath' => '?string', + 'hasAttributes' => 'bool', + 'hasChildNodes' => 'bool', + 'isDefaultNamespace' => 'bool', + 'isSameNode' => 'bool', + 'isSupported' => 'bool', + 'lookupNamespaceURI' => '?string', + 'lookupPrefix' => '?string', + 'normalize' => 'void', + ], + 'DOMImplementation' => [ + 'getFeature' => 'never', + 'hasFeature' => 'bool', + ], + 'DOMDocumentFragment' => [ + 'appendXML' => 'bool', + ], + 'DOMNodeList' => [ + 'count' => 'int', + ], + 'DOMCharacterData' => [ + 'appendData' => 'bool', + 'insertData' => 'bool', + 'deleteData' => 'bool', + 'replaceData' => 'bool', + ], + 'DOMAttr' => [ + 'isId' => 'bool', + ], + 'DOMElement' => [ + 'getAttribute' => 'string', + 'getAttributeNS' => 'string', + 'getElementsByTagName' => 'DOMNodeList', + 'getElementsByTagNameNS' => 'DOMNodeList', + 'hasAttribute' => 'bool', + 'hasAttributeNS' => 'bool', + 'removeAttribute' => 'bool', + 'removeAttributeNS' => 'void', + 'setAttributeNS' => 'void', + 'setIdAttribute' => 'void', + 'setIdAttributeNS' => 'void', + 'setIdAttributeNode' => 'void', + ], + 'DOMDocument' => [ + 'createComment' => 'DOMComment', + 'createDocumentFragment' => 'DOMDocumentFragment', + 'createTextNode' => 'DOMText', + 'getElementById' => '?DOMElement', + 'getElementsByTagName' => 'DOMNodeList', + 'getElementsByTagNameNS' => 'DOMNodeList', + 'normalizeDocument' => 'void', + 'registerNodeClass' => 'bool', + 'save' => 'int|false', + 'saveHTML' => 'string|false', + 'saveHTMLFile' => 'int|false', + 'saveXML' => 'string|false', + 'schemaValidate' => 'bool', + 'schemaValidateSource' => 'bool', + 'relaxNGValidate' => 'bool', + 'relaxNGValidateSource' => 'bool', + 'validate' => 'bool', + 'xinclude' => 'int|false', + ], + 'DOMText' => [ + 'isWhitespaceInElementContent' => 'bool', + 'isElementContentWhitespace' => 'bool', + ], + 'DOMNamedNodeMap' => [ + 'getNamedItem' => '?DOMNode', + 'getNamedItemNS' => '?DOMNode', + 'item' => '?DOMNode', + 'count' => 'int', + ], + 'DOMXPath' => [ + 'evaluate' => 'mixed', + 'query' => 'mixed', + 'registerNamespace' => 'bool', + 'registerPhpFunctions' => 'void', + ], + 'finfo' => [ + 'file' => 'string|false', + 'buffer' => 'string|false', + ], + 'IntlPartsIterator' => [ + 'getBreakIterator' => 'IntlBreakIterator', + 'getRuleStatus' => 'int', + ], + 'IntlBreakIterator' => [ + 'createCharacterInstance' => '?IntlBreakIterator', + 'createCodePointInstance' => 'IntlCodePointBreakIterator', + 'createLineInstance' => '?IntlBreakIterator', + 'createSentenceInstance' => '?IntlBreakIterator', + 'createTitleInstance' => '?IntlBreakIterator', + 'createWordInstance' => '?IntlBreakIterator', + 'current' => 'int', + 'first' => 'int', + 'following' => 'int', + 'getErrorCode' => 'int', + 'getErrorMessage' => 'string', + 'getLocale' => 'string|false', + 'getPartsIterator' => 'IntlPartsIterator', + 'getText' => '?string', + 'isBoundary' => 'bool', + 'last' => 'int', + 'next' => 'int', + 'preceding' => 'int', + 'previous' => 'int', + 'setText' => '?bool', + ], + 'IntlRuleBasedBreakIterator' => [ + 'getBinaryRules' => 'string|false', + 'getRules' => 'string|false', + 'getRuleStatus' => 'int', + 'getRuleStatusVec' => 'array|false', + ], + 'IntlCodePointBreakIterator' => [ + 'getLastCodePoint' => 'int', + ], + 'IntlCalendar' => [ + 'createInstance' => '?IntlCalendar', + 'equals' => 'bool', + 'fieldDifference' => 'int|false', + 'add' => 'bool', + 'after' => 'bool', + 'before' => 'bool', + 'fromDateTime' => '?IntlCalendar', + 'get' => 'int|false', + 'getActualMaximum' => 'int|false', + 'getActualMinimum' => 'int|false', + 'getAvailableLocales' => 'array', + 'getDayOfWeekType' => 'int|false', + 'getErrorCode' => 'int|false', + 'getErrorMessage' => 'string|false', + 'getFirstDayOfWeek' => 'int|false', + 'getGreatestMinimum' => 'int|false', + 'getKeywordValuesForLocale' => 'IntlIterator|false', + 'getLeastMaximum' => 'int|false', + 'getLocale' => 'string|false', + 'getMaximum' => 'int|false', + 'getMinimalDaysInFirstWeek' => 'int|false', + 'getMinimum' => 'int|false', + 'getNow' => 'float', + 'getRepeatedWallTimeOption' => 'int', + 'getSkippedWallTimeOption' => 'int', + 'getTime' => 'float|false', + 'getTimeZone' => 'IntlTimeZone|false', + 'getType' => 'string', + 'getWeekendTransition' => 'int|false', + 'inDaylightTime' => 'bool', + 'isEquivalentTo' => 'bool', + 'isLenient' => 'bool', + 'isWeekend' => 'bool', + 'isSet' => 'bool', + 'setTime' => 'bool', + 'setTimeZone' => 'bool', + 'toDateTime' => 'DateTime|false', + ], + 'IntlGregorianCalendar' => [ + 'setGregorianChange' => 'bool', + 'getGregorianChange' => 'float', + 'isLeapYear' => 'bool', + ], + 'Collator' => [ + 'create' => '?Collator', + 'compare' => 'int|false', + 'sort' => 'bool', + 'sortWithSortKeys' => 'bool', + 'asort' => 'bool', + 'getAttribute' => 'int|false', + 'setAttribute' => 'bool', + 'getStrength' => 'int', + 'getLocale' => 'string|false', + 'getErrorCode' => 'int|false', + 'getErrorMessage' => 'string|false', + 'getSortKey' => 'string|false', + ], + 'IntlIterator' => [ + 'current' => 'mixed', + 'key' => 'mixed', + 'next' => 'void', + 'rewind' => 'void', + 'valid' => 'bool', + ], + 'UConverter' => [ + 'convert' => 'string|false', + 'fromUCallback' => 'string|int|array|null', + 'getAliases' => 'array|false|null', + 'getAvailable' => 'array', + 'getDestinationEncoding' => 'string|false|null', + 'getDestinationType' => 'int|false|null', + 'getErrorCode' => 'int', + 'getErrorMessage' => '?string', + 'getSourceEncoding' => 'string|false|null', + 'getSourceType' => 'int|false|null', + 'getStandards' => '?array', + 'getSubstChars' => 'string|false|null', + 'reasonText' => 'string', + 'setDestinationEncoding' => 'bool', + 'setSourceEncoding' => 'bool', + 'setSubstChars' => 'bool', + 'toUCallback' => 'string|int|array|null', + 'transcode' => 'string|false', + ], + 'IntlDateFormatter' => [ + 'create' => '?IntlDateFormatter', + 'getDateType' => 'int|false', + 'getTimeType' => 'int|false', + 'getCalendar' => 'int|false', + 'setCalendar' => 'bool', + 'getTimeZoneId' => 'string|false', + 'getCalendarObject' => 'IntlCalendar|false|null', + 'getTimeZone' => 'IntlTimeZone|false', + 'setTimeZone' => '?bool', + 'setPattern' => 'bool', + 'getPattern' => 'string|false', + 'getLocale' => 'string|false', + 'setLenient' => 'void', + 'isLenient' => 'bool', + 'format' => 'string|false', + 'formatObject' => 'string|false', + 'parse' => 'int|float|false', + 'localtime' => 'array|false', + 'getErrorCode' => 'int', + 'getErrorMessage' => 'string', + ], + 'NumberFormatter' => [ + 'create' => '?NumberFormatter', + 'format' => 'string|false', + 'parse' => 'int|float|false', + 'formatCurrency' => 'string|false', + 'parseCurrency' => 'float|false', + 'setAttribute' => 'bool', + 'getAttribute' => 'int|float|false', + 'setTextAttribute' => 'bool', + 'getTextAttribute' => 'string|false', + 'setSymbol' => 'bool', + 'getSymbol' => 'string|false', + 'setPattern' => 'bool', + 'getPattern' => 'string|false', + 'getLocale' => 'string|false', + 'getErrorCode' => 'int', + 'getErrorMessage' => 'string', + ], + 'Locale' => [ + 'getDefault' => 'string', + 'getPrimaryLanguage' => '?string', + 'getScript' => '?string', + 'getRegion' => '?string', + 'getKeywords' => 'array|false|null', + 'getDisplayScript' => 'string|false', + 'getDisplayRegion' => 'string|false', + 'getDisplayName' => 'string|false', + 'getDisplayLanguage' => 'string|false', + 'getDisplayVariant' => 'string|false', + 'composeLocale' => 'string|false', + 'parseLocale' => '?array', + 'getAllVariants' => '?array', + 'filterMatches' => '?bool', + 'lookup' => '?string', + 'canonicalize' => '?string', + 'acceptFromHttp' => 'string|false', + ], + 'MessageFormatter' => [ + 'create' => '?MessageFormatter', + 'format' => 'string|false', + 'formatMessage' => 'string|false', + 'parse' => 'array|false', + 'parseMessage' => 'array|false', + 'setPattern' => 'bool', + 'getPattern' => 'string|false', + 'getLocale' => 'string', + 'getErrorCode' => 'int', + 'getErrorMessage' => 'string', + ], + 'Normalizer' => [ + 'normalize' => 'string|false', + 'isNormalized' => 'bool', + 'getRawDecomposition' => '?string', + ], + 'ResourceBundle' => [ + 'create' => '?ResourceBundle', + 'get' => 'mixed', + 'count' => 'int', + 'getLocales' => 'array|false', + 'getErrorCode' => 'int', + 'getErrorMessage' => 'string', + ], + 'Spoofchecker' => [ + 'isSuspicious' => 'bool', + 'areConfusable' => 'bool', + 'setAllowedLocales' => 'void', + 'setChecks' => 'void', + 'setRestrictionLevel' => 'void', + ], + 'IntlTimeZone' => [ + 'countEquivalentIDs' => 'int|false', + 'createDefault' => 'IntlTimeZone', + 'createEnumeration' => 'IntlIterator|false', + 'createTimeZone' => '?IntlTimeZone', + 'createTimeZoneIDEnumeration' => 'IntlIterator|false', + 'fromDateTimeZone' => '?IntlTimeZone', + 'getCanonicalID' => 'string|false', + 'getDisplayName' => 'string|false', + 'getDSTSavings' => 'int', + 'getEquivalentID' => 'string|false', + 'getErrorCode' => 'int|false', + 'getErrorMessage' => 'string|false', + 'getGMT' => 'IntlTimeZone', + 'getID' => 'string|false', + 'getOffset' => 'bool', + 'getRawOffset' => 'int', + 'getRegion' => 'string|false', + 'getTZDataVersion' => 'string|false', + 'getUnknown' => 'IntlTimeZone', + 'getWindowsID' => 'string|false', + 'getIDForWindowsID' => 'string|false', + 'hasSameRules' => 'bool', + 'toDateTimeZone' => 'DateTimeZone|false', + 'useDaylightTime' => 'bool', + ], + 'Transliterator' => [ + 'create' => '?Transliterator', + 'createFromRules' => '?Transliterator', + 'createInverse' => '?Transliterator', + 'listIDs' => 'array|false', + 'transliterate' => 'string|false', + 'getErrorCode' => 'int|false', + 'getErrorMessage' => 'string|false', + ], + 'IntlChar' => [ + 'hasBinaryProperty' => '?bool', + 'charAge' => '?array', + 'charDigitValue' => '?int', + 'charDirection' => '?int', + 'charFromName' => '?int', + 'charMirror' => 'int|string|null', + 'charName' => '?string', + 'charType' => '?int', + 'chr' => '?string', + 'digit' => 'int|false|null', + 'enumCharNames' => '?bool', + 'enumCharTypes' => 'void', + 'foldCase' => 'int|string|null', + 'forDigit' => 'int', + 'getBidiPairedBracket' => 'int|string|null', + 'getBlockCode' => '?int', + 'getCombiningClass' => '?int', + 'getFC_NFKC_Closure' => 'string|false|null', + 'getIntPropertyMaxValue' => 'int', + 'getIntPropertyMinValue' => 'int', + 'getIntPropertyValue' => '?int', + 'getNumericValue' => '?float', + 'getPropertyEnum' => 'int', + 'getPropertyName' => 'string|false', + 'getPropertyValueEnum' => 'int', + 'getPropertyValueName' => 'string|false', + 'getUnicodeVersion' => 'array', + 'isalnum' => '?bool', + 'isalpha' => '?bool', + 'isbase' => '?bool', + 'isblank' => '?bool', + 'iscntrl' => '?bool', + 'isdefined' => '?bool', + 'isdigit' => '?bool', + 'isgraph' => '?bool', + 'isIDIgnorable' => '?bool', + 'isIDPart' => '?bool', + 'isIDStart' => '?bool', + 'isISOControl' => '?bool', + 'isJavaIDPart' => '?bool', + 'isJavaIDStart' => '?bool', + 'isJavaSpaceChar' => '?bool', + 'islower' => '?bool', + 'isMirrored' => '?bool', + 'isprint' => '?bool', + 'ispunct' => '?bool', + 'isspace' => '?bool', + 'istitle' => '?bool', + 'isUAlphabetic' => '?bool', + 'isULowercase' => '?bool', + 'isupper' => '?bool', + 'isUUppercase' => '?bool', + 'isUWhiteSpace' => '?bool', + 'isWhitespace' => '?bool', + 'isxdigit' => '?bool', + 'ord' => '?int', + 'tolower' => 'int|string|null', + 'totitle' => 'int|string|null', + 'toupper' => 'int|string|null', + ], + 'JsonSerializable' => [ + 'jsonSerialize' => 'mixed', + ], + 'mysqli' => [ + 'autocommit' => 'bool', + 'begin_transaction' => 'bool', + 'change_user' => 'bool', + 'character_set_name' => 'string', + 'commit' => 'bool', + 'connect' => 'bool', + 'dump_debug_info' => 'bool', + 'get_charset' => '?object', + 'get_client_info' => 'string', + 'get_connection_stats' => 'array', + 'get_server_info' => 'string', + 'get_warnings' => 'mysqli_warning|false', + 'kill' => 'bool', + 'multi_query' => 'bool', + 'more_results' => 'bool', + 'next_result' => 'bool', + 'ping' => 'bool', + 'poll' => 'int|false', + 'prepare' => 'mysqli_stmt|false', + 'query' => 'mysqli_result|bool', + 'real_connect' => 'bool', + 'real_escape_string' => 'string', + 'reap_async_query' => 'mysqli_result|bool', + 'escape_string' => 'string', + 'real_query' => 'bool', + 'release_savepoint' => 'bool', + 'rollback' => 'bool', + 'savepoint' => 'bool', + 'select_db' => 'bool', + 'set_charset' => 'bool', + 'options' => 'bool', + 'set_opt' => 'bool', + 'stat' => 'string|false', + 'stmt_init' => 'mysqli_stmt|false', + 'store_result' => 'mysqli_result|false', + 'thread_safe' => 'bool', + 'use_result' => 'mysqli_result|false', + 'refresh' => 'bool', + ], + 'mysqli_result' => [ + 'close' => 'void', + 'free' => 'void', + 'data_seek' => 'bool', + 'fetch_field' => 'object|false', + 'fetch_fields' => 'array', + 'fetch_field_direct' => 'object|false', + 'fetch_all' => 'array', + 'fetch_array' => 'array|null|false', + 'fetch_assoc' => 'array|null|false', + 'fetch_object' => 'object|null|false', + 'fetch_row' => 'array|null|false', + 'field_seek' => 'bool', + 'free_result' => 'void', + ], + 'mysqli_stmt' => [ + 'attr_get' => 'int', + 'attr_set' => 'bool', + 'bind_param' => 'bool', + 'bind_result' => 'bool', + 'data_seek' => 'void', + 'execute' => 'bool', + 'fetch' => '?bool', + 'get_warnings' => 'mysqli_warning|false', + 'result_metadata' => 'mysqli_result|false', + 'more_results' => 'bool', + 'next_result' => 'bool', + 'num_rows' => 'int|string', + 'send_long_data' => 'bool', + 'free_result' => 'void', + 'reset' => 'bool', + 'prepare' => 'bool', + 'store_result' => 'bool', + 'get_result' => 'mysqli_result|false', + ], + 'OCILob' => [ + 'save' => 'bool', + 'import' => 'bool', + 'savefile' => 'bool', + 'load' => 'string|false', + 'read' => 'string|false', + 'eof' => 'bool', + 'tell' => 'int|false', + 'rewind' => 'bool', + 'seek' => 'bool', + 'size' => 'int|false', + 'write' => 'int|false', + 'append' => 'bool', + 'truncate' => 'bool', + 'erase' => 'int|false', + 'flush' => 'bool', + 'setbuffering' => 'bool', + 'getbuffering' => 'bool', + 'writetofile' => 'bool', + 'export' => 'bool', + 'writetemporary' => 'bool', + 'close' => 'bool', + 'free' => 'bool', + ], + 'OCICollection' => [ + 'free' => 'bool', + 'append' => 'bool', + 'getElem' => 'string|float|null|false', + 'assign' => 'bool', + 'assignelem' => 'bool', + 'size' => 'int|false', + 'max' => 'int|false', + 'trim' => 'bool', + ], + 'PDO' => [ + 'beginTransaction' => 'bool', + 'commit' => 'bool', + 'errorCode' => '?string', + 'errorInfo' => 'array', + 'exec' => 'int|false', + 'getAttribute' => 'mixed', + 'getAvailableDrivers' => 'array', + 'inTransaction' => 'bool', + 'lastInsertId' => 'string|false', + 'prepare' => 'PDOStatement|false', + 'query' => 'PDOStatement|false', + 'quote' => 'string|false', + 'rollBack' => 'bool', + 'setAttribute' => 'bool', + ], + 'PDOStatement' => [ + 'bindColumn' => 'bool', + 'bindParam' => 'bool', + 'bindValue' => 'bool', + 'closeCursor' => 'bool', + 'columnCount' => 'int', + 'debugDumpParams' => '?bool', + 'errorCode' => '?string', + 'errorInfo' => 'array', + 'execute' => 'bool', + 'fetch' => 'mixed', + 'fetchAll' => 'array', + 'fetchColumn' => 'mixed', + 'fetchObject' => 'object|false', + 'getAttribute' => 'mixed', + 'getColumnMeta' => 'array|false', + 'nextRowset' => 'bool', + 'rowCount' => 'int', + 'setAttribute' => 'bool', + ], + 'PDO_PGSql_Ext' => [ + 'pgsqlCopyFromArray' => 'bool', + 'pgsqlCopyFromFile' => 'bool', + 'pgsqlCopyToArray' => 'array|false', + 'pgsqlCopyToFile' => 'bool', + 'pgsqlLOBCreate' => 'string|false', + 'pgsqlLOBUnlink' => 'bool', + 'pgsqlGetNotify' => 'array|false', + 'pgsqlGetPid' => 'int', + ], + 'PDO_SQLite_Ext' => [ + 'sqliteCreateFunction' => 'bool', + 'sqliteCreateAggregate' => 'bool', + 'sqliteCreateCollation' => 'bool', + ], + 'Phar' => [ + 'addEmptyDir' => 'void', + 'addFile' => 'void', + 'addFromString' => 'void', + 'buildFromDirectory' => 'array', + 'buildFromIterator' => 'array', + 'compressFiles' => 'void', + 'compress' => '?Phar', + 'decompress' => '?Phar', + 'convertToExecutable' => '?Phar', + 'convertToData' => '?PharData', + 'count' => 'int', + 'extractTo' => 'bool', + 'getAlias' => '?string', + 'getPath' => 'string', + 'getMetadata' => 'mixed', + 'getModified' => 'bool', + 'getSignature' => 'array|false', + 'getStub' => 'string', + 'getVersion' => 'string', + 'hasMetadata' => 'bool', + 'isBuffering' => 'bool', + 'isCompressed' => 'int|false', + 'isFileFormat' => 'bool', + 'isWritable' => 'bool', + 'offsetExists' => 'bool', + 'offsetGet' => 'SplFileInfo', + 'offsetSet' => 'void', + 'offsetUnset' => 'void', + 'setAlias' => 'bool', + 'setDefaultStub' => 'bool', + 'setMetadata' => 'void', + 'setSignatureAlgorithm' => 'void', + 'startBuffering' => 'void', + 'stopBuffering' => 'void', + ], + 'PharData' => [ + 'addEmptyDir' => 'void', + 'addFile' => 'void', + 'addFromString' => 'void', + 'buildFromDirectory' => 'array', + 'buildFromIterator' => 'array', + 'compressFiles' => 'void', + 'compress' => '?PharData', + 'decompress' => '?PharData', + 'convertToExecutable' => '?Phar', + 'convertToData' => '?PharData', + 'count' => 'int', + 'extractTo' => 'bool', + 'getAlias' => '?string', + 'getPath' => 'string', + 'getMetadata' => 'mixed', + 'getModified' => 'bool', + 'getSignature' => 'array|false', + 'getStub' => 'string', + 'getVersion' => 'string', + 'hasMetadata' => 'bool', + 'isBuffering' => 'bool', + 'isCompressed' => 'int|false', + 'isFileFormat' => 'bool', + 'isWritable' => 'bool', + 'offsetExists' => 'bool', + 'offsetGet' => 'SplFileInfo', + 'offsetSet' => 'void', + 'offsetUnset' => 'void', + 'setAlias' => 'bool', + 'setDefaultStub' => 'bool', + 'setMetadata' => 'void', + 'setSignatureAlgorithm' => 'void', + 'startBuffering' => 'void', + 'stopBuffering' => 'void', + ], + 'PharFileInfo' => [ + 'chmod' => 'void', + 'getCompressedSize' => 'int', + 'getCRC32' => 'int', + 'getContent' => 'string', + 'getMetadata' => 'mixed', + 'getPharFlags' => 'int', + 'hasMetadata' => 'bool', + 'isCompressed' => 'bool', + 'isCRCChecked' => 'bool', + 'setMetadata' => 'void', + ], + 'Reflection' => [ + 'getModifierNames' => 'array', + ], + 'ReflectionFunctionAbstract' => [ + 'inNamespace' => 'bool', + 'isClosure' => 'bool', + 'isDeprecated' => 'bool', + 'isInternal' => 'bool', + 'isUserDefined' => 'bool', + 'isGenerator' => 'bool', + 'isVariadic' => 'bool', + 'isStatic' => 'bool', + 'getClosureThis' => '?object', + 'getClosureScopeClass' => '?ReflectionClass', + 'getDocComment' => 'string|false', + 'getEndLine' => 'int|false', + 'getExtension' => '?ReflectionExtension', + 'getExtensionName' => 'string|false', + 'getFileName' => 'string|false', + 'getName' => 'string', + 'getNamespaceName' => 'string', + 'getNumberOfParameters' => 'int', + 'getNumberOfRequiredParameters' => 'int', + 'getParameters' => 'array', + 'getShortName' => 'string', + 'getStartLine' => 'int|false', + 'getStaticVariables' => 'array', + 'returnsReference' => 'bool', + 'hasReturnType' => 'bool', + 'getReturnType' => '?ReflectionType', + ], + 'ReflectionFunction' => [ + 'isDisabled' => 'bool', + 'invoke' => 'mixed', + 'invokeArgs' => 'mixed', + 'getClosure' => 'Closure', + 'getExecutingLine' => 'int', + 'getExecutingFile' => 'string', + 'getTrace' => 'array', + 'getFunction' => 'ReflectionFunctionAbstract', + 'getThis' => '?object', + 'getExecutingGenerator' => 'Generator', + ], + 'ReflectionMethod' => [ + 'isPublic' => 'bool', + 'isPrivate' => 'bool', + 'isProtected' => 'bool', + 'isAbstract' => 'bool', + 'isFinal' => 'bool', + 'isConstructor' => 'bool', + 'isDestructor' => 'bool', + 'getClosure' => 'Closure', + 'getModifiers' => 'int', + 'invoke' => 'mixed', + 'invokeArgs' => 'mixed', + 'getDeclaringClass' => 'ReflectionClass', + 'getPrototype' => 'ReflectionMethod', + 'setAccessible' => 'void', + ], + 'ReflectionClass' => [ + 'getName' => 'string', + 'isInternal' => 'bool', + 'isUserDefined' => 'bool', + 'isAnonymous' => 'bool', + 'isInstantiable' => 'bool', + 'isCloneable' => 'bool', + 'getFileName' => 'string|false', + 'getStartLine' => 'int|false', + 'getEndLine' => 'int|false', + 'getDocComment' => 'string|false', + 'getConstructor' => '?ReflectionMethod', + 'hasMethod' => 'bool', + 'getMethod' => 'ReflectionMethod', + 'getMethods' => 'array', + 'hasProperty' => 'bool', + 'getProperty' => 'ReflectionProperty', + 'getProperties' => 'array', + 'hasConstant' => 'bool', + 'getConstants' => 'array', + 'getReflectionConstants' => 'array', + 'getConstant' => 'mixed', + 'getReflectionConstant' => 'ReflectionClassConstant|false', + 'getInterfaces' => 'array', + 'getInterfaceNames' => 'array', + 'isInterface' => 'bool', + 'getTraits' => 'array', + 'getTraitNames' => 'array', + 'getTraitAliases' => 'array', + 'isTrait' => 'bool', + 'isAbstract' => 'bool', + 'isFinal' => 'bool', + 'getModifiers' => 'int', + 'isInstance' => 'bool', + 'newInstance' => 'object', + 'newInstanceWithoutConstructor' => 'object', + 'newInstanceArgs' => '?object', + 'getParentClass' => 'ReflectionClass|false', + 'isSubclassOf' => 'bool', + 'getStaticProperties' => '?array', + 'getStaticPropertyValue' => 'mixed', + 'setStaticPropertyValue' => 'void', + 'getDefaultProperties' => 'array', + 'isIterable' => 'bool', + 'isIterateable' => 'bool', + 'implementsInterface' => 'bool', + 'getExtension' => '?ReflectionExtension', + 'getExtensionName' => 'string|false', + 'inNamespace' => 'bool', + 'getNamespaceName' => 'string', + 'getShortName' => 'string', + 'getAttributes' => 'array', + ], + 'ReflectionProperty' => [ + 'getName' => 'string', + 'getValue' => 'mixed', + 'setValue' => 'void', + 'isInitialized' => 'bool', + 'isPublic' => 'bool', + 'isPrivate' => 'bool', + 'isProtected' => 'bool', + 'isStatic' => 'bool', + 'isDefault' => 'bool', + 'getModifiers' => 'int', + 'getDeclaringClass' => 'ReflectionClass', + 'getDocComment' => 'string|false', + 'setAccessible' => 'void', + 'getType' => '?ReflectionType', + 'hasType' => 'bool', + 'getDefaultValue' => 'mixed', + 'getAttributes' => 'array', + ], + 'ReflectionClassConstant' => [ + 'getName' => 'string', + 'getValue' => 'mixed', + 'isPublic' => 'bool', + 'isPrivate' => 'bool', + 'isProtected' => 'bool', + 'getModifiers' => 'int', + 'getDeclaringClass' => 'ReflectionClass', + 'getDocComment' => 'string|false', + 'getAttributes' => 'array', + ], + 'ReflectionParameter' => [ + 'getName' => 'string', + 'isPassedByReference' => 'bool', + 'canBePassedByValue' => 'bool', + 'getDeclaringFunction' => 'ReflectionFunctionAbstract', + 'getDeclaringClass' => '?ReflectionClass', + 'getClass' => '?ReflectionClass', + 'hasType' => 'bool', + 'getType' => '?ReflectionType', + 'isArray' => 'bool', + 'isCallable' => 'bool', + 'allowsNull' => 'bool', + 'getPosition' => 'int', + 'isOptional' => 'bool', + 'isDefaultValueAvailable' => 'bool', + 'getDefaultValue' => 'mixed', + 'isDefaultValueConstant' => 'bool', + 'getDefaultValueConstantName' => '?string', + 'isVariadic' => 'bool', + 'getAttributes' => 'array', + ], + 'ReflectionType' => [ + 'allowsNull' => 'bool', + ], + 'ReflectionNamedType' => [ + 'getName' => 'string', + 'isBuiltin' => 'bool', + ], + 'ReflectionExtension' => [ + 'getName' => 'string', + 'getVersion' => '?string', + 'getFunctions' => 'array', + 'getConstants' => 'array', + 'getINIEntries' => 'array', + 'getClasses' => 'array', + 'getClassNames' => 'array', + 'getDependencies' => 'array', + 'info' => 'void', + 'isPersistent' => 'bool', + 'isTemporary' => 'bool', + ], + 'ReflectionZendExtension' => [ + 'getName' => 'string', + 'getVersion' => 'string', + 'getAuthor' => 'string', + 'getURL' => 'string', + 'getCopyright' => 'string', + ], + 'SessionHandlerInterface' => [ + 'open' => 'bool', + 'close' => 'bool', + 'read' => 'string|false', + 'write' => 'bool', + 'destroy' => 'bool', + 'gc' => 'int|false', + ], + 'SessionIdInterface' => [ + 'create_sid' => 'string', + ], + 'SessionUpdateTimestampHandlerInterface' => [ + 'validateId' => 'bool', + 'updateTimestamp' => 'bool', + ], + 'SessionHandler' => [ + 'open' => 'bool', + 'close' => 'bool', + 'read' => 'string|false', + 'write' => 'bool', + 'destroy' => 'bool', + 'gc' => 'int|false', + 'create_sid' => 'string', + ], + 'SimpleXMLElement' => [ + 'xpath' => 'array|null|false', + 'registerXPathNamespace' => 'bool', + 'asXML' => 'string|bool', + 'saveXML' => 'string|bool', + 'getNamespaces' => 'array', + 'getDocNamespaces' => 'array|false', + 'children' => '?SimpleXMLElement', + 'attributes' => '?SimpleXMLElement', + 'addChild' => '?SimpleXMLElement', + 'addAttribute' => 'void', + 'getName' => 'string', + 'count' => 'int', + 'rewind' => 'void', + 'valid' => 'bool', + 'current' => 'SimpleXMLElement', + 'key' => 'string', + 'next' => 'void', + 'hasChildren' => 'bool', + 'getChildren' => '?SimpleXMLElement', + ], + 'SNMP' => [ + 'close' => 'bool', + 'setSecurity' => 'bool', + 'get' => 'mixed', + 'getnext' => 'mixed', + 'walk' => 'array|false', + 'set' => 'bool', + 'getErrno' => 'int', + 'getError' => 'string', + ], + 'SoapServer' => [ + 'fault' => 'void', + 'addSoapHeader' => 'void', + 'setPersistence' => 'void', + 'setClass' => 'void', + 'setObject' => 'void', + 'getFunctions' => 'array', + 'addFunction' => 'void', + 'handle' => 'void', + ], + 'SoapClient' => [ + '__call' => 'mixed', + '__soapCall' => 'mixed', + '__getFunctions' => '?array', + '__getTypes' => '?array', + '__getLastRequest' => '?string', + '__getLastResponse' => '?string', + '__getLastRequestHeaders' => '?string', + '__getLastResponseHeaders' => '?string', + '__doRequest' => '?string', + '__setCookie' => 'void', + '__getCookies' => 'array', + '__setSoapHeaders' => 'bool', + '__setLocation' => '?string', + ], + 'ArrayObject' => [ + 'offsetExists' => 'bool', + 'offsetGet' => 'mixed', + 'offsetSet' => 'void', + 'offsetUnset' => 'void', + 'append' => 'void', + 'getArrayCopy' => 'array', + 'count' => 'int', + 'getFlags' => 'int', + 'setFlags' => 'void', + 'asort' => 'bool', + 'ksort' => 'bool', + 'uasort' => 'bool', + 'uksort' => 'bool', + 'natsort' => 'bool', + 'natcasesort' => 'bool', + 'unserialize' => 'void', + 'serialize' => 'string', + '__serialize' => 'array', + '__unserialize' => 'void', + 'getIterator' => 'Iterator', + 'exchangeArray' => 'array', + 'setIteratorClass' => 'void', + 'getIteratorClass' => 'string', + '__debugInfo' => 'array', + ], + 'ArrayIterator' => [ + 'offsetExists' => 'bool', + 'offsetGet' => 'mixed', + 'offsetSet' => 'void', + 'offsetUnset' => 'void', + 'append' => 'void', + 'getArrayCopy' => 'array', + 'count' => 'int', + 'getFlags' => 'int', + 'setFlags' => 'void', + 'asort' => 'bool', + 'ksort' => 'bool', + 'uasort' => 'bool', + 'uksort' => 'bool', + 'natsort' => 'bool', + 'natcasesort' => 'bool', + 'unserialize' => 'void', + 'serialize' => 'string', + '__serialize' => 'array', + '__unserialize' => 'void', + 'rewind' => 'void', + 'current' => 'mixed', + 'key' => 'string|int|null', + 'next' => 'void', + 'valid' => 'bool', + 'seek' => 'void', + '__debugInfo' => 'array', + ], + 'RecursiveArrayIterator' => [ + 'hasChildren' => 'bool', + 'getChildren' => '?RecursiveArrayIterator', + ], + 'SplFileInfo' => [ + 'getPath' => 'string', + 'getFilename' => 'string', + 'getExtension' => 'string', + 'getBasename' => 'string', + 'getPathname' => 'string', + 'getPerms' => 'int|false', + 'getInode' => 'int|false', + 'getSize' => 'int|false', + 'getOwner' => 'int|false', + 'getGroup' => 'int|false', + 'getATime' => 'int|false', + 'getMTime' => 'int|false', + 'getCTime' => 'int|false', + 'getType' => 'string|false', + 'isWritable' => 'bool', + 'isReadable' => 'bool', + 'isExecutable' => 'bool', + 'isFile' => 'bool', + 'isDir' => 'bool', + 'isLink' => 'bool', + 'getLinkTarget' => 'string|false', + 'getRealPath' => 'string|false', + 'getFileInfo' => 'SplFileInfo', + 'getPathInfo' => '?SplFileInfo', + 'openFile' => 'SplFileObject', + 'setFileClass' => 'void', + 'setInfoClass' => 'void', + '__debugInfo' => 'array', + '_bad_state_ex' => 'void', + ], + 'DirectoryIterator' => [ + 'getFilename' => 'string', + 'getExtension' => 'string', + 'getBasename' => 'string', + 'isDot' => 'bool', + 'rewind' => 'void', + 'valid' => 'bool', + 'next' => 'void', + 'seek' => 'void', + ], + 'FilesystemIterator' => [ + 'rewind' => 'void', + 'key' => 'string', + 'current' => 'string|SplFileInfo|FilesystemIterator', + 'getFlags' => 'int', + 'setFlags' => 'void', + ], + 'RecursiveDirectoryIterator' => [ + 'hasChildren' => 'bool', + 'getChildren' => 'RecursiveDirectoryIterator', + 'getSubPath' => 'string', + 'getSubPathname' => 'string', + ], + 'GlobIterator' => [ + 'count' => 'int', + ], + 'SplFileObject' => [ + 'rewind' => 'void', + 'eof' => 'bool', + 'valid' => 'bool', + 'fgets' => 'string', + 'fread' => 'string|false', + 'fgetcsv' => 'array|false', + 'fputcsv' => 'int|false', + 'setCsvControl' => 'void', + 'getCsvControl' => 'array', + 'flock' => 'bool', + 'fflush' => 'bool', + 'ftell' => 'int|false', + 'fseek' => 'int', + 'fgetc' => 'string|false', + 'fpassthru' => 'int', + 'fscanf' => 'array|int|null', + 'fwrite' => 'int|false', + 'fstat' => 'array', + 'ftruncate' => 'bool', + 'current' => 'string|array|false', + 'key' => 'int', + 'next' => 'void', + 'setFlags' => 'void', + 'getFlags' => 'int', + 'setMaxLineLen' => 'void', + 'getMaxLineLen' => 'int', + 'hasChildren' => 'bool', + 'getChildren' => '?RecursiveIterator', + 'seek' => 'void', + 'getCurrentLine' => 'string', + ], + 'SplDoublyLinkedList' => [ + 'add' => 'void', + 'pop' => 'mixed', + 'shift' => 'mixed', + 'push' => 'void', + 'unshift' => 'void', + 'top' => 'mixed', + 'bottom' => 'mixed', + '__debugInfo' => 'array', + 'count' => 'int', + 'isEmpty' => 'bool', + 'setIteratorMode' => 'int', + 'getIteratorMode' => 'int', + 'offsetExists' => 'bool', + 'offsetGet' => 'mixed', + 'offsetSet' => 'void', + 'offsetUnset' => 'void', + 'rewind' => 'void', + 'current' => 'mixed', + 'key' => 'int', + 'prev' => 'void', + 'next' => 'void', + 'valid' => 'bool', + 'unserialize' => 'void', + 'serialize' => 'string', + '__serialize' => 'array', + '__unserialize' => 'void', + ], + 'SplQueue' => [ + 'enqueue' => 'void', + 'dequeue' => 'mixed', + ], + 'SplFixedArray' => [ + '__wakeup' => 'void', + 'count' => 'int', + 'toArray' => 'array', + 'fromArray' => 'SplFixedArray', + 'getSize' => 'int', + 'offsetExists' => 'bool', + 'offsetGet' => 'mixed', + 'offsetSet' => 'void', + 'offsetUnset' => 'void', + ], + 'SplPriorityQueue' => [ + 'compare' => 'int', + 'setExtractFlags' => 'int', + 'top' => 'mixed', + 'extract' => 'mixed', + 'count' => 'int', + 'isEmpty' => 'bool', + 'rewind' => 'void', + 'current' => 'mixed', + 'key' => 'int', + 'next' => 'void', + 'valid' => 'bool', + 'isCorrupted' => 'bool', + 'getExtractFlags' => 'int', + '__debugInfo' => 'array', + ], + 'SplHeap' => [ + 'extract' => 'mixed', + 'insert' => 'bool', + 'top' => 'mixed', + 'count' => 'int', + 'isEmpty' => 'bool', + 'rewind' => 'void', + 'current' => 'mixed', + 'key' => 'int', + 'next' => 'void', + 'valid' => 'bool', + 'recoverFromCorruption' => 'bool', + 'compare' => 'int', + 'isCorrupted' => 'bool', + '__debugInfo' => 'array', + ], + 'SplMinHeap' => [ + 'compare' => 'int', + ], + 'SplMaxHeap' => [ + 'compare' => 'int', + ], + 'EmptyIterator' => [ + 'current' => 'never', + 'next' => 'void', + 'key' => 'never', + 'valid' => 'bool', + 'rewind' => 'void', + ], + 'CallbackFilterIterator' => [ + 'accept' => 'bool', + ], + 'RecursiveCallbackFilterIterator' => [ + 'hasChildren' => 'bool', + 'getChildren' => 'RecursiveCallbackFilterIterator', + ], + 'RecursiveIterator' => [ + 'hasChildren' => 'bool', + 'getChildren' => '?RecursiveIterator', + ], + 'RecursiveIteratorIterator' => [ + 'rewind' => 'void', + 'valid' => 'bool', + 'key' => 'mixed', + 'current' => 'mixed', + 'next' => 'void', + 'getDepth' => 'int', + 'getSubIterator' => '?RecursiveIterator', + 'getInnerIterator' => 'RecursiveIterator', + 'beginIteration' => 'void', + 'endIteration' => 'void', + 'callHasChildren' => 'bool', + 'callGetChildren' => '?RecursiveIterator', + 'beginChildren' => 'void', + 'endChildren' => 'void', + 'nextElement' => 'void', + 'setMaxDepth' => 'void', + 'getMaxDepth' => 'int|false', + ], + 'OuterIterator' => [ + 'getInnerIterator' => '?Iterator', + ], + 'IteratorIterator' => [ + 'getInnerIterator' => '?Iterator', + 'rewind' => 'void', + 'valid' => 'bool', + 'key' => 'mixed', + 'current' => 'mixed', + 'next' => 'void', + ], + 'FilterIterator' => [ + 'accept' => 'bool', + 'rewind' => 'void', + 'next' => 'void', + ], + 'RecursiveFilterIterator' => [ + 'hasChildren' => 'bool', + 'getChildren' => '?RecursiveFilterIterator', + ], + 'ParentIterator' => [ + 'accept' => 'bool', + ], + 'SeekableIterator' => [ + 'seek' => 'void', + ], + 'LimitIterator' => [ + 'rewind' => 'void', + 'valid' => 'bool', + 'next' => 'void', + 'seek' => 'int', + 'getPosition' => 'int', + ], + 'CachingIterator' => [ + 'rewind' => 'void', + 'valid' => 'bool', + 'next' => 'void', + 'hasNext' => 'bool', + 'getFlags' => 'int', + 'setFlags' => 'void', + 'offsetGet' => 'mixed', + 'offsetSet' => 'void', + 'offsetUnset' => 'void', + 'offsetExists' => 'bool', + 'getCache' => 'array', + 'count' => 'int', + ], + 'RecursiveCachingIterator' => [ + 'hasChildren' => 'bool', + 'getChildren' => '?RecursiveCachingIterator', + ], + 'NoRewindIterator' => [ + 'rewind' => 'void', + 'valid' => 'bool', + 'key' => 'mixed', + 'current' => 'mixed', + 'next' => 'void', + ], + 'AppendIterator' => [ + 'append' => 'void', + 'rewind' => 'void', + 'valid' => 'bool', + 'current' => 'mixed', + 'next' => 'void', + 'getIteratorIndex' => '?int', + 'getArrayIterator' => 'ArrayIterator', + ], + 'InfiniteIterator' => [ + 'next' => 'void', + ], + 'RegexIterator' => [ + 'accept' => 'bool', + 'getMode' => 'int', + 'setMode' => 'void', + 'getFlags' => 'int', + 'setFlags' => 'void', + 'getRegex' => 'string', + 'getPregFlags' => 'int', + 'setPregFlags' => 'void', + ], + 'RecursiveRegexIterator' => [ + 'accept' => 'bool', + 'hasChildren' => 'bool', + 'getChildren' => 'RecursiveRegexIterator', + ], + 'RecursiveTreeIterator' => [ + 'key' => 'mixed', + 'current' => 'mixed', + 'getPrefix' => 'string', + 'setPostfix' => 'void', + 'setPrefixPart' => 'void', + 'getEntry' => 'string', + 'getPostfix' => 'string', + ], + 'SplObserver' => [ + 'update' => 'void', + ], + 'SplSubject' => [ + 'attach' => 'void', + 'detach' => 'void', + 'notify' => 'void', + ], + 'SplObjectStorage' => [ + 'attach' => 'void', + 'detach' => 'void', + 'contains' => 'bool', + 'addAll' => 'int', + 'removeAll' => 'int', + 'removeAllExcept' => 'int', + 'getInfo' => 'mixed', + 'setInfo' => 'void', + 'count' => 'int', + 'rewind' => 'void', + 'valid' => 'bool', + 'key' => 'int', + 'current' => 'object', + 'next' => 'void', + 'unserialize' => 'void', + 'serialize' => 'string', + 'offsetExists' => 'bool', + 'offsetGet' => 'mixed', + 'offsetSet' => 'void', + 'offsetUnset' => 'void', + 'getHash' => 'string', + '__serialize' => 'array', + '__unserialize' => 'void', + '__debugInfo' => 'array', + ], + 'MultipleIterator' => [ + 'getFlags' => 'int', + 'setFlags' => 'void', + 'attachIterator' => 'void', + 'detachIterator' => 'void', + 'containsIterator' => 'bool', + 'countIterators' => 'int', + 'rewind' => 'void', + 'valid' => 'bool', + 'key' => 'array', + 'current' => 'array', + 'next' => 'void', + '__debugInfo' => 'array', + ], + 'SQLite3' => [ + 'open' => 'void', + 'version' => 'array', + 'lastInsertRowID' => 'int', + 'lastErrorCode' => 'int', + 'lastExtendedErrorCode' => 'int', + 'lastErrorMsg' => 'string', + 'changes' => 'int', + 'busyTimeout' => 'bool', + 'loadExtension' => 'bool', + 'backup' => 'bool', + 'escapeString' => 'string', + 'prepare' => 'SQLite3Stmt|false', + 'exec' => 'bool', + 'query' => 'SQLite3Result|false', + 'querySingle' => 'mixed', + 'createFunction' => 'bool', + 'createAggregate' => 'bool', + 'createCollation' => 'bool', + 'enableExceptions' => 'bool', + 'enableExtendedResultCodes' => 'bool', + 'setAuthorizer' => 'bool', + ], + 'SQLite3Stmt' => [ + 'bindParam' => 'bool', + 'bindValue' => 'bool', + 'clear' => 'bool', + 'close' => 'bool', + 'execute' => 'SQLite3Result|false', + 'getSQL' => 'string|false', + 'paramCount' => 'int', + 'readOnly' => 'bool', + 'reset' => 'bool', + ], + 'SQLite3Result' => [ + 'numColumns' => 'int', + 'columnName' => 'string|false', + 'columnType' => 'int|false', + 'fetchArray' => 'array|false', + 'reset' => 'bool', + ], + 'Directory' => [ + 'close' => 'void', + 'rewind' => 'void', + 'read' => 'string|false', + ], + 'php_user_filter' => [ + 'filter' => 'int', + 'onCreate' => 'bool', + 'onClose' => 'void', + ], + 'tidy' => [ + 'getOpt' => 'string|int|bool', + 'cleanRepair' => 'bool', + 'parseFile' => 'bool', + 'parseString' => 'bool', + 'repairString' => 'string|false', + 'repairFile' => 'string|false', + 'diagnose' => 'bool', + 'getRelease' => 'string', + 'getConfig' => 'array', + 'getStatus' => 'int', + 'getHtmlVer' => 'int', + 'getOptDoc' => 'string|false', + 'isXhtml' => 'bool', + 'isXml' => 'bool', + 'root' => '?tidyNode', + 'head' => '?tidyNode', + 'html' => '?tidyNode', + 'body' => '?tidyNode', + ], + 'XMLReader' => [ + 'getAttribute' => '?string', + 'getAttributeNo' => '?string', + 'getAttributeNs' => '?string', + 'getParserProperty' => 'bool', + 'isValid' => 'bool', + 'lookupNamespace' => '?string', + 'moveToAttribute' => 'bool', + 'moveToAttributeNo' => 'bool', + 'moveToAttributeNs' => 'bool', + 'moveToElement' => 'bool', + 'moveToFirstAttribute' => 'bool', + 'moveToNextAttribute' => 'bool', + 'read' => 'bool', + 'next' => 'bool', + 'readInnerXml' => 'string', + 'readOuterXml' => 'string', + 'readString' => 'string', + 'setSchema' => 'bool', + 'setParserProperty' => 'bool', + 'setRelaxNGSchema' => 'bool', + 'setRelaxNGSchemaSource' => 'bool', + 'expand' => 'DOMNode|false', + ], + 'XMLWriter' => [ + 'openUri' => 'bool', + 'openMemory' => 'bool', + 'setIndent' => 'bool', + 'setIndentString' => 'bool', + 'startComment' => 'bool', + 'endComment' => 'bool', + 'startAttribute' => 'bool', + 'endAttribute' => 'bool', + 'writeAttribute' => 'bool', + 'startAttributeNs' => 'bool', + 'writeAttributeNs' => 'bool', + 'startElement' => 'bool', + 'endElement' => 'bool', + 'fullEndElement' => 'bool', + 'startElementNs' => 'bool', + 'writeElement' => 'bool', + 'writeElementNs' => 'bool', + 'startPi' => 'bool', + 'endPi' => 'bool', + 'writePi' => 'bool', + 'startCdata' => 'bool', + 'endCdata' => 'bool', + 'writeCdata' => 'bool', + 'text' => 'bool', + 'writeRaw' => 'bool', + 'startDocument' => 'bool', + 'endDocument' => 'bool', + 'writeComment' => 'bool', + 'startDtd' => 'bool', + 'endDtd' => 'bool', + 'writeDtd' => 'bool', + 'startDtdElement' => 'bool', + 'endDtdElement' => 'bool', + 'writeDtdElement' => 'bool', + 'startDtdAttlist' => 'bool', + 'endDtdAttlist' => 'bool', + 'writeDtdAttlist' => 'bool', + 'startDtdEntity' => 'bool', + 'endDtdEntity' => 'bool', + 'writeDtdEntity' => 'bool', + 'outputMemory' => 'string', + 'flush' => 'string|int', + ], + 'XSLTProcessor' => [ + 'importStylesheet' => 'bool', + 'transformToDoc' => 'DOMDocument|false', + 'transformToUri' => 'int', + 'transformToXml' => 'string|null|false', + 'setParameter' => 'bool', + 'getParameter' => 'string|false', + 'removeParameter' => 'bool', + 'hasExsltSupport' => 'bool', + 'registerPHPFunctions' => 'void', + 'setSecurityPrefs' => 'int', + 'getSecurityPrefs' => 'int', + ], + 'ZipArchive' => [ + 'open' => 'bool|int', + 'setPassword' => 'bool', + 'close' => 'bool', + 'count' => 'int', + 'getStatusString' => 'string', + 'addEmptyDir' => 'bool', + 'addFromString' => 'bool', + 'addFile' => 'bool', + 'replaceFile' => 'bool', + 'addGlob' => 'array|false', + 'addPattern' => 'array|false', + 'renameIndex' => 'bool', + 'renameName' => 'bool', + 'setArchiveComment' => 'bool', + 'getArchiveComment' => 'string|false', + 'setCommentIndex' => 'bool', + 'setCommentName' => 'bool', + 'setMtimeIndex' => 'bool', + 'setMtimeName' => 'bool', + 'getCommentIndex' => 'string|false', + 'getCommentName' => 'string|false', + 'deleteIndex' => 'bool', + 'deleteName' => 'bool', + 'statName' => 'array|false', + 'statIndex' => 'array|false', + 'locateName' => 'int|false', + 'getNameIndex' => 'string|false', + 'unchangeArchive' => 'bool', + 'unchangeAll' => 'bool', + 'unchangeIndex' => 'bool', + 'unchangeName' => 'bool', + 'extractTo' => 'bool', + 'getFromName' => 'string|false', + 'getFromIndex' => 'string|false', + 'setExternalAttributesName' => 'bool', + 'setExternalAttributesIndex' => 'bool', + 'getExternalAttributesName' => 'bool', + 'getExternalAttributesIndex' => 'bool', + 'setCompressionName' => 'bool', + 'setCompressionIndex' => 'bool', + 'setEncryptionName' => 'bool', + 'setEncryptionIndex' => 'bool', + 'registerProgressCallback' => 'bool', + 'registerCancelCallback' => 'bool', + ], + 'Exception' => [ + '__wakeup' => 'void', + ], + 'Error' => [ + '__wakeup' => 'void', + ], + 'IteratorAggregate' => [ + 'getIterator' => 'Traversable', + ], + 'Iterator' => [ + 'current' => 'mixed', + 'next' => 'void', + 'key' => 'mixed', + 'valid' => 'bool', + 'rewind' => 'void', + ], + 'ArrayAccess' => [ + 'offsetExists' => 'bool', + 'offsetGet' => 'mixed', + 'offsetSet' => 'void', + 'offsetUnset' => 'void', + ], + 'Countable' => [ + 'count' => 'int', + ], + ]; +} diff --git a/src/Symfony/Component/ErrorHandler/Resources/bin/extract-tentative-return-types.php b/src/Symfony/Component/ErrorHandler/Resources/bin/extract-tentative-return-types.php new file mode 100755 index 0000000000000..62df88ccff9a4 --- /dev/null +++ b/src/Symfony/Component/ErrorHandler/Resources/bin/extract-tentative-return-types.php @@ -0,0 +1,80 @@ +#!/usr/bin/env php + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +// Run from the root of the php-src repository, this script generates +// a table with all the methods that have a tentative return type. +// +// Usage: find -name *.stub.php | sort | /path/to/extract-tentative-return-types.php > /path/to/TentativeTypes.php + +echo << + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ErrorHandler\Resources; + +/** + * This class has been generated by extract-tentative-return-types.php. + * + * @internal + */ +class TentativeTypes +{ + public const RETURN_TYPES = [ + +EOPHP; + +while (false !== $file = fgets(STDIN)) { + $code = file_get_contents(substr($file, 0, -1)); + + if (!str_contains($code, '@tentative-return-type')) { + continue; + } + + $code = preg_split('{^\s*(?:(?:abstract )?class|interface|trait) ([^\s]++)}m', $code, -1, \PREG_SPLIT_DELIM_CAPTURE); + + if (1 === count($code)) { + continue; + } + + for ($i = 1; null !== $class = $code[$i] ?? null; $i += 2) { + $methods = $code[1 + $i]; + + if (!str_contains($methods, '@tentative-return-type')) { + continue; + } + + echo " '$class' => [\n"; + + preg_replace_callback('{@tentative-return-type.*?[\s]function ([^(]++)[^)]++\)\s*+:\s*+([^\n;\{]++)}s', function ($m) { + $m[2] = str_replace(' ', '', $m[2]); + echo " '$m[1]' => '$m[2]',\n"; + + return ''; + }, $methods); + + echo " ],\n"; + } +} + +echo <<assertSame($deprecations, [ + $this->assertSame([ 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\InternalInterface" interface is considered internal. It may change without further notice. You should not use it from "Test\Symfony\Component\ErrorHandler\Tests\ExtendsInternalsParent".', 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\InternalClass" class is considered internal. It may change without further notice. You should not use it from "Test\Symfony\Component\ErrorHandler\Tests\ExtendsInternalsParent".', 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\InternalTrait" trait is considered internal. It may change without further notice. You should not use it from "Test\Symfony\Component\ErrorHandler\Tests\ExtendsInternals".', 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\InternalClass::internalMethod()" method is considered internal. It may change without further notice. You should not extend it from "Test\Symfony\Component\ErrorHandler\Tests\ExtendsInternals".', - ]); + ], $deprecations); } public function testExtendedMethodDefinesNewParameters() @@ -292,9 +292,9 @@ class_exists(Fixtures\SubClassWithAnnotatedParameters::class, true); 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::quzMethod()" method will require a new "Quz $quz" argument in the next major version of its parent class "Symfony\Component\ErrorHandler\Tests\Fixtures\ClassWithAnnotatedParameters", not defining it is deprecated.', 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::whereAmI()" method will require a new "bool $matrix" argument in the next major version of its interface "Symfony\Component\ErrorHandler\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "$noType" argument in the next major version of its interface "Symfony\Component\ErrorHandler\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', - 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "callable(\Throwable|null $reason, mixed $value) $callback" argument in the next major version of its interface "Symfony\Component\ErrorHandler\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', + 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "callable $callback" argument in the next major version of its interface "Symfony\Component\ErrorHandler\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "string $param" argument in the next major version of its interface "Symfony\Component\ErrorHandler\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', - 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "callable ($a, $b) $anotherOne" argument in the next major version of its interface "Symfony\Component\ErrorHandler\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', + 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "callable $anotherOne" argument in the next major version of its interface "Symfony\Component\ErrorHandler\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "Type$WithDollarIsStillAType $ccc" argument in the next major version of its interface "Symfony\Component\ErrorHandler\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.', 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\SubClassWithAnnotatedParameters::isSymfony()" method will require a new "true $yes" argument in the next major version of its parent class "Symfony\Component\ErrorHandler\Tests\Fixtures\ClassWithAnnotatedParameters", not defining it is deprecated.', ], $deprecations); @@ -325,20 +325,25 @@ class_exists('Test\\'.ExtendsVirtual::class, true); error_reporting($e); restore_error_handler(); - $this->assertSame([ - 'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::staticReturningMethod()".', + $this->assertSame(array_merge( + \PHP_VERSION_ID >= 80000 ? [ + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::staticMethod()" will return "Foo&Bar" as of its next major version. Doing the same in implementation "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualAbstract" will be required when upgrading.', + ] : [], [ + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::interfaceMethod()" will return "string" as of its next major version. Doing the same in implementation "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualAbstract" will be required when upgrading.', + 'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::staticReturningMethod(): static".', 'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::sameLineInterfaceMethodNoBraces()".', 'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::newLineInterfaceMethod()": Some description!', - 'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::newLineInterfaceMethodNoBraces()": Description.', - 'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::invalidInterfaceMethod()".', - 'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::invalidInterfaceMethodNoBraces()".', + 'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::newLineInterfaceMethodNoBraces(): \stdClass": Description.', + 'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::invalidInterfaceMethod(): unknownType".', + 'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::invalidInterfaceMethodNoBraces(): unknownType|string".', 'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::complexInterfaceMethod($arg, ...$args)".', - 'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::complexInterfaceMethodTyped($arg, int ...$args)": Description ...', - 'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualParent" should implement method "static Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::staticMethodNoBraces()".', - 'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualParent" should implement method "static Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::staticMethodTyped(int $arg)": Description.', - 'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualParent" should implement method "static Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::staticMethodTypedNoBraces()".', - 'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtual" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualSubInterface::subInterfaceMethod()".', - ], $deprecations); + 'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::complexInterfaceMethodTyped($arg, int ...$args): string[]|int": Description ...', + 'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualParent" should implement method "static Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::staticMethodNoBraces(): mixed".', + 'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualParent" should implement method "static Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::staticMethodTyped(int $arg): \stdClass": Description.', + 'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualParent" should implement method "static Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::staticMethodTypedNoBraces(): \stdClass[]".', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::staticMethodNoBraces()" will return "mixed" as of its next major version. Doing the same in implementation "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualParent" will be required when upgrading.', + 'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtual" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualSubInterface::subInterfaceMethod(): string".', + ]), $deprecations); } public function testVirtualUseWithMagicCall() @@ -372,32 +377,32 @@ class_exists('Test\\'.ReturnType::class, true); restore_error_handler(); $this->assertSame(array_merge([ - 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeGrandParent::returnTypeGrandParent()" will return "string" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', - 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParentInterface::returnTypeParentInterface()" will return "string" as of its next major version. Doing the same in implementation "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', - 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeInterface::returnTypeInterface()" will return "string" as of its next major version. Doing the same in implementation "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', - 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::oneNonNullableReturnableType()" will return "void" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', - 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::oneNonNullableReturnableTypeWithNull()" will return "void" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', - 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::oneNullableReturnableType()" will return "array" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', - 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::oneNullableReturnableTypeWithNull()" will return "?bool" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', - 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::oneOtherType()" will return "\ArrayIterator" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', - 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::oneOtherTypeWithNull()" will return "?\ArrayIterator" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', - 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::manyIterables()" will return "array" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', - 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::nullableReturnableTypeNormalization()" will return "object" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', - 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::nonNullableReturnableTypeNormalization()" will return "void" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', - 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::bracketsNormalization()" will return "array" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', - 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::booleanNormalization()" will return "bool" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', - 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::callableNormalization1()" will return "callable" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', - 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::callableNormalization2()" will return "callable" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', - 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::otherTypeNormalization()" will return "\ArrayIterator" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', - 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::arrayWithLessThanSignNormalization()" will return "array" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeGrandParent::returnTypeGrandParent()" will return "string" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParentInterface::returnTypeParentInterface()" will return "string" as of its next major version. Doing the same in implementation "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeInterface::returnTypeInterface()" will return "string" as of its next major version. Doing the same in implementation "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::oneNonNullableReturnableType()" will return "void" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::oneNonNullableReturnableTypeWithNull()" will return "void" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::oneNullableReturnableType()" will return "array" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::oneNullableReturnableTypeWithNull()" will return "?bool" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::oneOtherType()" will return "\ArrayIterator" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::oneOtherTypeWithNull()" will return "?\ArrayIterator" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', ], \PHP_VERSION_ID >= 80000 ? [ + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::twoNullableReturnableTypes()" will return "int|\Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::threeReturnTypes()" will return "bool|string|null" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', + ] : [], [ + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::manyIterables()" will return "array" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::nullableReturnableTypeNormalization()" will return "object" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::nonNullableReturnableTypeNormalization()" will return "void" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::bracketsNormalization()" will return "array" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::booleanNormalization()" will return "false" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::callableNormalization1()" will return "callable" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::callableNormalization2()" will return "callable" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::otherTypeNormalization()" will return "\ArrayIterator" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::arrayWithLessThanSignNormalization()" will return "array" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::this()" will return "static" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::mixed()" will return "mixed" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::nullableMixed()" will return "mixed" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::static()" will return "static" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', - ] : [ - 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::this()" will return "object" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', - 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::static()" will return "object" as of its next major version. Doing the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" will be required when upgrading.', ]), $deprecations); } }