From 42279b7bb973c20f30f71c21a20cff53cfdc2889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Thu, 7 Mar 2024 23:12:41 +0100 Subject: [PATCH] Make `ClassLike::from` return type assert the subclass type (#154) * Make ClassLike::from return type more strict * Throw exception instead of relying on PHP type error * Improve exception message and add test --- src/PhpGenerator/ClassLike.php | 22 +++++++++++---- tests/PhpGenerator/ClassLike.typecheck.phpt | 28 ++++++++++++++++++++ tests/PhpGenerator/ClassType.from.82.phpt | 3 ++- tests/PhpGenerator/ClassType.from.phpt | 9 ++++--- tests/PhpGenerator/ClassType.from.trait.phpt | 6 ++--- 5 files changed, 55 insertions(+), 13 deletions(-) create mode 100644 tests/PhpGenerator/ClassLike.typecheck.phpt diff --git a/src/PhpGenerator/ClassLike.php b/src/PhpGenerator/ClassLike.php index ce49782d..a459d05f 100644 --- a/src/PhpGenerator/ClassLike.php +++ b/src/PhpGenerator/ClassLike.php @@ -37,18 +37,30 @@ abstract class ClassLike private ?PhpNamespace $namespace; private ?string $name; - - public static function from(string|object $class, bool $withBodies = false): self + public static function from(string|object $class, bool $withBodies = false): static { - return (new Factory) + $instance = (new Factory) ->fromClassReflection(new \ReflectionClass($class), $withBodies); + + if (!$instance instanceof static) { + $class = is_object($class) ? get_class($class) : $class; + throw new Nette\InvalidArgumentException("'$class' cannot be represented with " . static::class . ". Call " . get_class($instance) . "::" . __FUNCTION__ . "() or " . __METHOD__ . "() instead."); + } + + return $instance; } - public static function fromCode(string $code): self + public static function fromCode(string $code): static { - return (new Factory) + $instance = (new Factory) ->fromClassCode($code); + + if (!$instance instanceof static) { + throw new Nette\InvalidArgumentException("Provided code cannot be represented with " . static::class . ". Call " . get_class($instance) . "::" . __FUNCTION__ . "() or " . __METHOD__ . "() instead."); + } + + return $instance; } diff --git a/tests/PhpGenerator/ClassLike.typecheck.phpt b/tests/PhpGenerator/ClassLike.typecheck.phpt new file mode 100644 index 00000000..7085540c --- /dev/null +++ b/tests/PhpGenerator/ClassLike.typecheck.phpt @@ -0,0 +1,28 @@ + ClassType::from($class), $classes); +$res = array_map(fn($class) => ClassLike::from($class), $classes); sameFile(__DIR__ . '/expected/ClassType.from.trait-use.expect', implode("\n", $res)); -$res = array_map(fn($class) => ClassType::from($class, withBodies: true), $classes); +$res = array_map(fn($class) => ClassLike::from($class, withBodies: true), $classes); sameFile(__DIR__ . '/expected/ClassType.from.trait-use.bodies.expect', implode("\n", $res));