From f8897ba476b5427040e22922f786efaf40d81e11 Mon Sep 17 00:00:00 2001 From: Nathanael Esayeas Date: Wed, 28 Feb 2024 13:19:58 -0600 Subject: [PATCH 1/2] Update Reflector.php Signed-off-by: Nathanael Esayeas --- library/Mockery/Reflector.php | 260 ++++++++++++++++++---------------- 1 file changed, 137 insertions(+), 123 deletions(-) diff --git a/library/Mockery/Reflector.php b/library/Mockery/Reflector.php index a7a411be6..aba4dbf59 100644 --- a/library/Mockery/Reflector.php +++ b/library/Mockery/Reflector.php @@ -5,96 +5,81 @@ * * @copyright https://github.com/mockery/mockery/blob/HEAD/COPYRIGHT.md * @license https://github.com/mockery/mockery/blob/HEAD/LICENSE BSD 3-Clause License + * * @link https://github.com/mockery/mockery for the canonical source repository */ namespace Mockery; +use InvalidArgumentException; +use ReflectionClass; +use ReflectionIntersectionType; +use ReflectionMethod; +use ReflectionNamedType; +use ReflectionParameter; use ReflectionType; +use ReflectionUnionType; + +use function array_diff; +use function array_intersect; +use function array_map; +use function array_merge; +use function get_debug_type; +use function implode; +use function in_array; +use function mb_strpos; +use function method_exists; +use function sprintf; + +use const PHP_VERSION_ID; /** * @internal */ class Reflector { - private const TRAVERSABLE_ARRAY = ['\Traversable', 'array']; private const ITERABLE = ['iterable']; - /** - * Determine if the parameter is typed as an array. - * - * @param \ReflectionParameter $param - * - * @return bool - */ - public static function isArray(\ReflectionParameter $param) - { - $type = $param->getType(); - - return $type instanceof \ReflectionNamedType && $type->getName(); - } - /** - * Compute the string representation for the paramater type. - * - * @param \ReflectionParameter $param - * @param bool $withoutNullable - * - * @return string|null - */ - public static function getTypeHint(\ReflectionParameter $param, $withoutNullable = false) - { - if (!$param->hasType()) { - return null; - } - - $type = $param->getType(); - $declaringClass = $param->getDeclaringClass(); - $typeHint = self::getTypeFromReflectionType($type, $declaringClass); - - return (!$withoutNullable && $type->allowsNull()) ? self::formatNullableType($typeHint) : $typeHint; - } + private const TRAVERSABLE_ARRAY = ['\Traversable', 'array']; /** * Compute the string representation for the return type. * - * @param \ReflectionParameter $param * @param bool $withoutNullable * - * @return string|null + * @return null|string */ - public static function getReturnType(\ReflectionMethod $method, $withoutNullable = false) + public static function getReturnType(ReflectionMethod $method, $withoutNullable = false) { $type = $method->getReturnType(); - if (!$type instanceof ReflectionType && method_exists($method, 'getTentativeReturnType')) { - $type = $method->getTentativeReturnType(); + if (! $type instanceof ReflectionType && method_exists($method, 'getTentativeReturnType')) { + $type = $method->getTentativeReturnType(); } - if (!$type instanceof ReflectionType) { + if (! $type instanceof ReflectionType) { return null; } $typeHint = self::getTypeFromReflectionType($type, $method->getDeclaringClass()); - return (!$withoutNullable && $type->allowsNull()) ? self::formatNullableType($typeHint) : $typeHint; + return (! $withoutNullable && $type->allowsNull()) ? self::formatNullableType($typeHint) : $typeHint; } /** * Compute the string representation for the simplest return type. * - * @param \ReflectionParameter $param - * - * @return string|null + * @return null|string */ - public static function getSimplestReturnType(\ReflectionMethod $method) + public static function getSimplestReturnType(ReflectionMethod $method) { $type = $method->getReturnType(); - if (!$type instanceof ReflectionType && method_exists($method, 'getTentativeReturnType')) { + if (! $type instanceof ReflectionType && method_exists($method, 'getTentativeReturnType')) { $type = $method->getTentativeReturnType(); } - if (!$type instanceof ReflectionType || $type->allowsNull()) { + if (! $type instanceof ReflectionType || $type->allowsNull()) { return null; } @@ -116,72 +101,35 @@ public static function getSimplestReturnType(\ReflectionMethod $method) } /** - * Get the string representation of the given type. + * Compute the string representation for the paramater type. * - * @param \ReflectionType $type - * @param \ReflectionClass $declaringClass + * @param bool $withoutNullable * - * @return list + * @return null|string */ - private static function getTypeInformation(\ReflectionType $type, \ReflectionClass $declaringClass) + public static function getTypeHint(ReflectionParameter $param, $withoutNullable = false) { - // PHP 8 union types and PHP 8.1 intersection types can be recursively processed - if ($type instanceof \ReflectionUnionType || $type instanceof \ReflectionIntersectionType) { - $types = []; - - foreach ($type->getTypes() as $innterType) { - foreach (self::getTypeInformation($innterType, $declaringClass) as $info) { - if ($info['typeHint'] === 'null' && $info['isPrimitive']) { - continue; - } - - $types[] = $info; - } - } - - return $types; - } - - // $type must be an instance of \ReflectionNamedType - $typeHint = $type->getName(); - - // builtins can be returned as is - if ($type->isBuiltin()) { - return [ - [ - 'typeHint' => $typeHint, - 'isPrimitive' => in_array($typeHint, ['array', 'bool', 'int', 'float', 'null', 'object', 'string']), - ], - ]; + if (! $param->hasType()) { + return null; } - // 'static' can be returned as is - if ($typeHint === 'static') { - return [ - [ - 'typeHint' => $typeHint, - 'isPrimitive' => false, - ], - ]; - } + $type = $param->getType(); + $declaringClass = $param->getDeclaringClass(); + $typeHint = self::getTypeFromReflectionType($type, $declaringClass); - // 'self' needs to be resolved to the name of the declaring class - if ($typeHint === 'self') { - $typeHint = $declaringClass->getName(); - } + return (! $withoutNullable && $type->allowsNull()) ? self::formatNullableType($typeHint) : $typeHint; + } - // 'parent' needs to be resolved to the name of the parent class - if ($typeHint === 'parent') { - $typeHint = $declaringClass->getParentClass()->getName(); - } + /** + * Determine if the parameter is typed as an array. + * + * @return bool + */ + public static function isArray(ReflectionParameter $param) + { + $type = $param->getType(); - // class names need prefixing with a slash - return [ - [ - 'typeHint' => sprintf('\\%s', $typeHint), - 'isPrimitive' => false, - ], - ]; + return $type instanceof ReflectionNamedType && $type->getName(); } /** @@ -197,20 +145,20 @@ private static function formatNullableType($typeHint) return $typeHint; } - if (strpos($typeHint, 'null') !== false) { + if (mb_strpos($typeHint, 'null') !== false) { return $typeHint; } - if (\PHP_VERSION_ID < 80000) { + if (PHP_VERSION_ID < 80000) { return sprintf('?%s', $typeHint); } return sprintf('%s|null', $typeHint); } - private static function getTypeFromReflectionType(\ReflectionType $type, \ReflectionClass $declaringClass): string + private static function getTypeFromReflectionType(ReflectionType $type, ReflectionClass $declaringClass): string { - if ($type instanceof \ReflectionNamedType) { + if ($type instanceof ReflectionNamedType) { $typeHint = $type->getName(); if ($type->isBuiltin()) { @@ -222,12 +170,12 @@ private static function getTypeFromReflectionType(\ReflectionType $type, \Reflec } // 'self' needs to be resolved to the name of the declaring class - if ($typeHint === 'self'){ + if ($typeHint === 'self') { $typeHint = $declaringClass->getName(); } // 'parent' needs to be resolved to the name of the parent class - if ($typeHint === 'parent'){ + if ($typeHint === 'parent') { $typeHint = $declaringClass->getParentClass()->getName(); } @@ -235,45 +183,111 @@ private static function getTypeFromReflectionType(\ReflectionType $type, \Reflec return sprintf('\\%s', $typeHint); } - if ($type instanceof \ReflectionIntersectionType) { + if ($type instanceof ReflectionIntersectionType) { $types = array_map( - static function (\ReflectionType $type) use ($declaringClass): string { + static function (ReflectionType $type) use ($declaringClass): string { return self::getTypeFromReflectionType($type, $declaringClass); }, $type->getTypes() ); - return implode( - '&', - $types, - ); + return implode('&', $types); } - if ($type instanceof \ReflectionUnionType) { + if ($type instanceof ReflectionUnionType) { $types = array_map( - static function (\ReflectionType $type) use ($declaringClass): string { + static function (ReflectionType $type) use ($declaringClass): string { return self::getTypeFromReflectionType($type, $declaringClass); }, $type->getTypes() ); $intersect = array_intersect(self::TRAVERSABLE_ARRAY, $types); - if (self::TRAVERSABLE_ARRAY === $intersect) { + if ($intersect === self::TRAVERSABLE_ARRAY) { $types = array_merge(self::ITERABLE, array_diff($types, self::TRAVERSABLE_ARRAY)); } return implode( '|', array_map( - static function (string $type): string - { - return strpos($type, '&') === false ? $type : sprintf('(%s)', $type); + static function (string $type): string { + return mb_strpos($type, '&') === false ? $type : sprintf('(%s)', $type); }, $types ) ); } - throw new \InvalidArgumentException('Unknown ReflectionType: ' . get_debug_type($type)); + throw new InvalidArgumentException('Unknown ReflectionType: ' . get_debug_type($type)); + } + + /** + * Get the string representation of the given type. + * + * @return list + */ + private static function getTypeInformation(ReflectionType $type, ReflectionClass $declaringClass) + { + // PHP 8 union types and PHP 8.1 intersection types can be recursively processed + if ($type instanceof ReflectionUnionType || $type instanceof ReflectionIntersectionType) { + $types = []; + + foreach ($type->getTypes() as $innterType) { + foreach (self::getTypeInformation($innterType, $declaringClass) as $info) { + if ($info['typeHint'] === 'null' && $info['isPrimitive']) { + continue; + } + + $types[] = $info; + } + } + + return $types; + } + + // $type must be an instance of \ReflectionNamedType + $typeHint = $type->getName(); + + // builtins can be returned as is + if ($type->isBuiltin()) { + return [ + [ + 'typeHint' => $typeHint, + 'isPrimitive' => in_array( + $typeHint, + ['array', 'bool', 'int', 'float', 'null', 'object', 'string'], + true + ), + ], + ]; + } + + // 'static' can be returned as is + if ($typeHint === 'static') { + return [ + [ + 'typeHint' => $typeHint, + 'isPrimitive' => false, + ], + ]; + } + + // 'self' needs to be resolved to the name of the declaring class + if ($typeHint === 'self') { + $typeHint = $declaringClass->getName(); + } + + // 'parent' needs to be resolved to the name of the parent class + if ($typeHint === 'parent') { + $typeHint = $declaringClass->getParentClass()->getName(); + } + + // class names need prefixing with a slash + return [ + [ + 'typeHint' => sprintf('\\%s', $typeHint), + 'isPrimitive' => false, + ], + ]; } } From be2e0b77fb43c87a4117fec5eb03f10ba906aaf0 Mon Sep 17 00:00:00 2001 From: Nathanael Esayeas Date: Thu, 29 Feb 2024 12:07:06 -0600 Subject: [PATCH 2/2] Additional improvements Signed-off-by: Nathanael Esayeas --- library/Mockery/Reflector.php | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/library/Mockery/Reflector.php b/library/Mockery/Reflector.php index aba4dbf59..089506002 100644 --- a/library/Mockery/Reflector.php +++ b/library/Mockery/Reflector.php @@ -38,8 +38,25 @@ */ class Reflector { + /** + * List of built-in types. + * + * @var list + */ + public const BUILTIN_TYPES = ['array', 'bool', 'int', 'float', 'null', 'object', 'string']; + + /** + * Iterable. + * + * @var list + */ private const ITERABLE = ['iterable']; + /** + * Traversable array. + * + * @var list + */ private const TRAVERSABLE_ARRAY = ['\Traversable', 'array']; /** @@ -134,12 +151,8 @@ public static function isArray(ReflectionParameter $param) /** * Format the given type as a nullable type. - * - * @param string $typeHint - * - * @return string */ - private static function formatNullableType($typeHint) + private static function formatNullableType(string $typeHint): string { if ($typeHint === 'mixed') { return $typeHint; @@ -226,7 +239,7 @@ static function (string $type): string { * * @return list */ - private static function getTypeInformation(ReflectionType $type, ReflectionClass $declaringClass) + private static function getTypeInformation(ReflectionType $type, ReflectionClass $declaringClass): array { // PHP 8 union types and PHP 8.1 intersection types can be recursively processed if ($type instanceof ReflectionUnionType || $type instanceof ReflectionIntersectionType) { @@ -253,11 +266,7 @@ private static function getTypeInformation(ReflectionType $type, ReflectionClass return [ [ 'typeHint' => $typeHint, - 'isPrimitive' => in_array( - $typeHint, - ['array', 'bool', 'int', 'float', 'null', 'object', 'string'], - true - ), + 'isPrimitive' => in_array($typeHint, self::BUILTIN_TYPES, true), ], ]; }