diff --git a/src/Symfony/Component/Config/Resource/ReflectionClassResource.php b/src/Symfony/Component/Config/Resource/ReflectionClassResource.php index cfab1f6c10..294da3e10c 100644 --- a/src/Symfony/Component/Config/Resource/ReflectionClassResource.php +++ b/src/Symfony/Component/Config/Resource/ReflectionClassResource.php @@ -177,6 +177,7 @@ class ReflectionClassResource implements SelfCheckingResourceInterface, \Seriali if (!$parametersWithUndefinedConstants) { yield preg_replace('/^ @@.*/m', '', $m); } else { + $t = \PHP_VERSION_ID >= 70000 ? $m->getReturnType() : ''; $stack = [ $m->getDocComment(), $m->getName(), @@ -187,15 +188,16 @@ class ReflectionClassResource implements SelfCheckingResourceInterface, \Seriali $m->isPrivate(), $m->isProtected(), $m->returnsReference(), - \PHP_VERSION_ID >= 70000 && $m->hasReturnType() ? (\PHP_VERSION_ID >= 70100 ? $m->getReturnType()->getName() : (string) $m->getReturnType()) : '', + $t instanceof \ReflectionNamedType ? ((string) $t->allowsNull()).$t->getName() : (string) $t, ]; foreach ($m->getParameters() as $p) { if (!isset($parametersWithUndefinedConstants[$p->name])) { $stack[] = (string) $p; } else { + $t = \PHP_VERSION_ID >= 70000 ? $p->getType() : ''; $stack[] = $p->isOptional(); - $stack[] = \PHP_VERSION_ID >= 70000 && $p->hasType() ? (\PHP_VERSION_ID >= 70100 ? $p->getType()->getName() : (string) $p->getType()) : ''; + $stack[] = $t instanceof \ReflectionNamedType ? $t->getName() : (string) $t; $stack[] = $p->isPassedByReference(); $stack[] = \PHP_VERSION_ID >= 50600 ? $p->isVariadic() : ''; $stack[] = $p->getName(); diff --git a/src/Symfony/Component/DependencyInjection/LazyProxy/ProxyHelper.php b/src/Symfony/Component/DependencyInjection/LazyProxy/ProxyHelper.php index cb19c729c1..bfa65f56f0 100644 --- a/src/Symfony/Component/DependencyInjection/LazyProxy/ProxyHelper.php +++ b/src/Symfony/Component/DependencyInjection/LazyProxy/ProxyHelper.php @@ -39,26 +39,36 @@ class ProxyHelper if (!$type) { return null; } - if (!\is_string($type)) { - $name = $type instanceof \ReflectionNamedType ? $type->getName() : $type->__toString(); - if ($type->isBuiltin()) { - return $noBuiltin ? null : $name; + $types = []; + + foreach ($type instanceof \ReflectionUnionType ? $type->getTypes() : [$type] as $type) { + $name = $type instanceof \ReflectionNamedType ? $type->getName() : (string) $type; + + if (!\is_string($type) && $type->isBuiltin()) { + if (!$noBuiltin) { + $types[] = $name; + } + continue; + } + + $lcName = strtolower($name); + $prefix = $noBuiltin ? '' : '\\'; + + if ('self' !== $lcName && 'parent' !== $lcName) { + $types[] = '' !== $prefix ? $prefix.$name : $name; + continue; + } + if (!$r instanceof \ReflectionMethod) { + continue; + } + if ('self' === $lcName) { + $types[] = $prefix.$r->getDeclaringClass()->name; + } else { + $types[] = ($parent = $r->getDeclaringClass()->getParentClass()) ? $prefix.$parent->name : null; } } - $lcName = strtolower($name); - $prefix = $noBuiltin ? '' : '\\'; - if ('self' !== $lcName && 'parent' !== $lcName) { - return $prefix.$name; - } - if (!$r instanceof \ReflectionMethod) { - return null; - } - if ('self' === $lcName) { - return $prefix.$r->getDeclaringClass()->name; - } - - return ($parent = $r->getDeclaringClass()->getParentClass()) ? $prefix.$parent->name : null; + return $types ? implode('|', $types) : null; } } diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index 7354caf8a6..ffb101e512 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -1076,7 +1076,7 @@ class OptionsResolver implements Options return ($class = $parameter->getClass()) ? $class->name : null; } - if (!($type = $parameter->getType()) || $type->isBuiltin()) { + if (!($type = $parameter->getType()) instanceof \ReflectionNamedType || $type->isBuiltin()) { return null; } diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 99aa9a3ebc..07e0204cd2 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -519,8 +519,9 @@ class PropertyAccessor implements PropertyAccessorInterface // handle uninitialized properties in PHP >= 7.4 if (\PHP_VERSION_ID >= 70400 && preg_match('/^Typed property ([\w\\\]+)::\$(\w+) must not be accessed before initialization$/', $e->getMessage(), $matches)) { $r = new \ReflectionProperty($matches[1], $matches[2]); + $type = ($type = $r->getType()) instanceof \ReflectionNamedType ? $type->getName() : (string) $type; - throw new AccessException(sprintf('The property "%s::$%s" is not readable because it is typed "%s". You should initialize it or declare a default value instead.', $r->getDeclaringClass()->getName(), $r->getName(), $r->getType()->getName()), 0, $e); + throw new AccessException(sprintf('The property "%s::$%s" is not readable because it is typed "%s". You should initialize it or declare a default value instead.', $r->getDeclaringClass()->getName(), $r->getName(), $type), 0, $e); } throw $e; diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php index 3306946a9b..cbcb349d2e 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php @@ -187,26 +187,26 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp $type = $this->extractFromReflectionType($reflectionType, $reflectionMethod); // HHVM reports variadics with "array" but not builtin type hints - if (!$reflectionType->isBuiltin() && Type::BUILTIN_TYPE_ARRAY === $type->getBuiltinType()) { + if (1 === \count($type) && !$reflectionType->isBuiltin() && Type::BUILTIN_TYPE_ARRAY === $type[0]->getBuiltinType()) { return null; } } elseif (preg_match('/^(?:[^ ]++ ){4}([a-zA-Z_\x7F-\xFF][^ ]++)/', $reflectionParameter, $info)) { if (Type::BUILTIN_TYPE_ARRAY === $info[1]) { - $type = new Type(Type::BUILTIN_TYPE_ARRAY, $reflectionParameter->allowsNull(), null, true); + $type = [new Type(Type::BUILTIN_TYPE_ARRAY, $reflectionParameter->allowsNull(), null, true)]; } elseif (Type::BUILTIN_TYPE_CALLABLE === $info[1]) { - $type = new Type(Type::BUILTIN_TYPE_CALLABLE, $reflectionParameter->allowsNull()); + $type = [new Type(Type::BUILTIN_TYPE_CALLABLE, $reflectionParameter->allowsNull())]; } else { - $type = new Type(Type::BUILTIN_TYPE_OBJECT, $reflectionParameter->allowsNull(), $this->resolveTypeName($info[1], $reflectionMethod)); + $type = [new Type(Type::BUILTIN_TYPE_OBJECT, $reflectionParameter->allowsNull(), $this->resolveTypeName($info[1], $reflectionMethod))]; } } else { return null; } - if (\in_array($prefix, $this->arrayMutatorPrefixes)) { - $type = new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $type); + if (1 === \count($type) && \in_array($prefix, $this->arrayMutatorPrefixes)) { + $type = [new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $type[0])]; } - return [$type]; + return $type; } /** @@ -225,7 +225,7 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp } if ($this->supportsParameterType && $reflectionType = $reflectionMethod->getReturnType()) { - return [$this->extractFromReflectionType($reflectionType, $reflectionMethod)]; + return $this->extractFromReflectionType($reflectionType, $reflectionMethod); } return \in_array($prefix, ['is', 'can']) ? [new Type(Type::BUILTIN_TYPE_BOOL)] : null; @@ -234,24 +234,28 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp /** * Extracts data from the PHP 7 reflection type. * - * @return Type + * @return Type[] */ private function extractFromReflectionType(\ReflectionType $reflectionType, \ReflectionMethod $reflectionMethod) { - $phpTypeOrClass = $reflectionType instanceof \ReflectionNamedType ? $reflectionType->getName() : $reflectionType->__toString(); + $types = []; $nullable = $reflectionType->allowsNull(); - if (Type::BUILTIN_TYPE_ARRAY === $phpTypeOrClass) { - $type = new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true); - } elseif ('void' === $phpTypeOrClass) { - $type = new Type(Type::BUILTIN_TYPE_NULL, $nullable); - } elseif ($reflectionType->isBuiltin()) { - $type = new Type($phpTypeOrClass, $nullable); - } else { - $type = new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $this->resolveTypeName($phpTypeOrClass, $reflectionMethod)); + foreach ($reflectionType instanceof \ReflectionUnionType ? $reflectionType->getTypes() : [$reflectionType] as $type) { + $phpTypeOrClass = $reflectionType instanceof \ReflectionNamedType ? $reflectionType->getName() : (string) $type; + + if (Type::BUILTIN_TYPE_ARRAY === $phpTypeOrClass) { + $types[] = new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true); + } elseif ('void' === $phpTypeOrClass || 'null' === $phpTypeOrClass) { + $types[] = new Type(Type::BUILTIN_TYPE_NULL, $nullable); + } elseif ($reflectionType->isBuiltin()) { + $types[] = new Type($phpTypeOrClass, $nullable); + } else { + $types[] = new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $this->resolveTypeName($phpTypeOrClass, $reflectionMethod)); + } } - return $type; + return $types; } private function resolveTypeName($name, \ReflectionMethod $reflectionMethod) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index 71b1e38d37..e88b5a2110 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -388,7 +388,7 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N try { if (\PHP_VERSION_ID < 70100 && null !== $parameterClass = $parameter->getClass()) { $parameterClass = $parameterClass->name; - } elseif (\PHP_VERSION_ID >= 70100 && ($parameterType = $parameter->getType()) && !$parameterType->isBuiltin()) { + } elseif (\PHP_VERSION_ID >= 70100 && ($parameterType = $parameter->getType()) instanceof \ReflectionNamedType && !$parameterType->isBuiltin()) { $parameterClass = $parameterType->getName(); new \ReflectionClass($parameterClass); // throws a \ReflectionException if the class doesn't exist } else { diff --git a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php index 067da6bbd3..31de78e961 100644 --- a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php @@ -91,7 +91,7 @@ class ReflectionCaster $prefix = Caster::PREFIX_VIRTUAL; $a += [ - $prefix.'name' => $c instanceof \ReflectionNamedType ? $c->getName() : $c->__toString(), + $prefix.'name' => $c instanceof \ReflectionNamedType ? $c->getName() : (string) $c, $prefix.'allowsNull' => $c->allowsNull(), $prefix.'isBuiltin' => $c->isBuiltin(), ]; @@ -178,7 +178,7 @@ class ReflectionCaster if (isset($a[$prefix.'returnType'])) { $v = $a[$prefix.'returnType']; - $v = $v instanceof \ReflectionNamedType ? $v->getName() : $v->__toString(); + $v = $v instanceof \ReflectionNamedType ? $v->getName() : (string) $v; $a[$prefix.'returnType'] = new ClassStub($a[$prefix.'returnType']->allowsNull() ? '?'.$v : $v, [class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '']); } if (isset($a[$prefix.'class'])) { @@ -247,7 +247,7 @@ class ReflectionCaster if (method_exists($c, 'getType')) { if ($v = $c->getType()) { - $a[$prefix.'typeHint'] = $v instanceof \ReflectionNamedType ? $v->getName() : $v->__toString(); + $a[$prefix.'typeHint'] = $v instanceof \ReflectionNamedType ? $v->getName() : (string) $v; } } elseif (preg_match('/^(?:[^ ]++ ){4}([a-zA-Z_\x7F-\xFF][^ ]++)/', $c, $v)) { $a[$prefix.'typeHint'] = $v[1];