From b1249f272bc374483cc82eb9d738462da431e56c Mon Sep 17 00:00:00 2001 From: "Mike P. Sinn" Date: Tue, 14 Sep 2021 03:08:53 +0200 Subject: [PATCH] Factory::fromClassReflection() added option to copy traits or not [Closes #89] --- src/PhpGenerator/ClassType.php | 4 +- src/PhpGenerator/Factory.php | 36 +++++++--- tests/PhpGenerator/ClassType.from.trait.phpt | 25 +++++++ .../ClassType.from.trait-use.bodies.expect | 71 +++++++++++++++++++ .../expected/ClassType.from.trait-use.expect | 66 +++++++++++++++++ .../ClassType.from.trait.bodies.expect | 1 + .../expected/ClassType.from.trait.expect | 1 + tests/PhpGenerator/fixtures/traits.php | 1 + 8 files changed, 194 insertions(+), 11 deletions(-) create mode 100644 tests/PhpGenerator/expected/ClassType.from.trait-use.bodies.expect create mode 100644 tests/PhpGenerator/expected/ClassType.from.trait-use.expect diff --git a/src/PhpGenerator/ClassType.php b/src/PhpGenerator/ClassType.php index 6e0e297c..dc742ed3 100644 --- a/src/PhpGenerator/ClassType.php +++ b/src/PhpGenerator/ClassType.php @@ -99,9 +99,9 @@ public static function enum(string $name = null, PhpNamespace $namespace = null) /** * @param string|object $class */ - public static function from($class): self + public static function from($class, bool $withBodies = false, bool $copyTraits = true): self { - return (new Factory)->fromClassReflection(new \ReflectionClass($class)); + return (new Factory)->fromClassReflection(new \ReflectionClass($class), $withBodies, $copyTraits); } diff --git a/src/PhpGenerator/Factory.php b/src/PhpGenerator/Factory.php index 3d12c02e..3b3239c0 100644 --- a/src/PhpGenerator/Factory.php +++ b/src/PhpGenerator/Factory.php @@ -10,6 +10,7 @@ namespace Nette\PhpGenerator; use Nette; +use Nette\Utils\Reflection; use PhpParser; use PhpParser\Node; use PhpParser\ParserFactory; @@ -22,8 +23,11 @@ final class Factory { use Nette\SmartObject; - public function fromClassReflection(\ReflectionClass $from, bool $withBodies = false): ClassType - { + public function fromClassReflection( + \ReflectionClass $from, + bool $withBodies = false, + bool $copyTraits = true + ): ClassType { $class = $from->isAnonymous() ? new ClassType : new ClassType($from->getShortName(), new PhpNamespace($from->getNamespaceName())); @@ -61,8 +65,12 @@ public function fromClassReflection(\ReflectionClass $from, bool $withBodies = f $props = []; foreach ($from->getProperties() as $prop) { + $declaringClass = $copyTraits + ? $prop->getDeclaringClass() + : Reflection::getPropertyDeclaringClass($prop); + if ($prop->isDefault() - && $prop->getDeclaringClass()->name === $from->name + && $declaringClass->name === $from->name && (PHP_VERSION_ID < 80000 || !$prop->isPromoted()) && !$class->isEnum() ) { @@ -73,23 +81,33 @@ public function fromClassReflection(\ReflectionClass $from, bool $withBodies = f $methods = $bodies = []; foreach ($from->getMethods() as $method) { + $realMethod = Reflection::getMethodDeclaringMethod($method); + $realClass = $realMethod->getDeclaringClass(); + $declaringClass = $copyTraits + ? $method->getDeclaringClass() + : $realClass; + if ( - $method->getDeclaringClass()->name === $from->name + $declaringClass->name === $from->name && (!$enumIface || !method_exists($enumIface, $method->name)) ) { $methods[] = $m = $this->fromMethodReflection($method); if ($withBodies) { - $srcMethod = Nette\Utils\Reflection::getMethodDeclaringMethod($method); - $srcClass = $srcMethod->getDeclaringClass()->name; - $b = $bodies[$srcClass] = $bodies[$srcClass] ?? $this->loadMethodBodies($srcMethod->getDeclaringClass()); - if (isset($b[$srcMethod->name])) { - $m->setBody($b[$srcMethod->name]); + $b = $bodies[$realClass->name] = $bodies[$realClass->name] ?? $this->loadMethodBodies($realClass); + if (isset($b[$realMethod->name])) { + $m->setBody($b[$realMethod->name]); } } } } $class->setMethods($methods); + if (!$copyTraits) { + foreach ($from->getTraitNames() as $trait) { + $class->addTrait($trait); + } + } + $consts = $cases = []; foreach ($from->getReflectionConstants() as $const) { if ($class->isEnum() && $from->hasCase($const->name)) { diff --git a/tests/PhpGenerator/ClassType.from.trait.phpt b/tests/PhpGenerator/ClassType.from.trait.phpt index 7db53c93..d423fa80 100644 --- a/tests/PhpGenerator/ClassType.from.trait.phpt +++ b/tests/PhpGenerator/ClassType.from.trait.phpt @@ -13,6 +13,7 @@ require __DIR__ . '/../bootstrap.php'; require __DIR__ . '/fixtures/traits.php'; +$res = []; $res[] = ClassType::from('Trait1'); $res[] = ClassType::from('Trait2'); $res[] = ClassType::from('Class1'); @@ -24,6 +25,18 @@ $res[] = ClassType::from('Class5'); sameFile(__DIR__ . '/expected/ClassType.from.trait.expect', implode("\n", $res)); +$res = []; +$res[] = ClassType::from('Trait1', /*withBodies:*/ false, /*copyTraits:*/ false); +$res[] = ClassType::from('Trait2', /*withBodies:*/ false, /*copyTraits:*/ false); +$res[] = ClassType::from('Class1', /*withBodies:*/ false, /*copyTraits:*/ false); +$res[] = ClassType::from('Class2', /*withBodies:*/ false, /*copyTraits:*/ false); +$res[] = ClassType::from('Class3', /*withBodies:*/ false, /*copyTraits:*/ false); +$res[] = ClassType::from('Class4', /*withBodies:*/ false, /*copyTraits:*/ false); +$res[] = ClassType::from('Class5', /*withBodies:*/ false, /*copyTraits:*/ false); + +sameFile(__DIR__ . '/expected/ClassType.from.trait-use.expect', implode("\n", $res)); + + $res = []; $res[] = ClassType::withBodiesFrom('Trait1'); $res[] = ClassType::withBodiesFrom('Trait2'); @@ -34,3 +47,15 @@ $res[] = ClassType::withBodiesFrom('Class4'); $res[] = ClassType::withBodiesFrom('Class5'); sameFile(__DIR__ . '/expected/ClassType.from.trait.bodies.expect', implode("\n", $res)); + + +$res = []; +$res[] = ClassType::from('Trait1', /*withBodies:*/ true, /*copyTraits:*/ false); +$res[] = ClassType::from('Trait2', /*withBodies:*/ true, /*copyTraits:*/ false); +$res[] = ClassType::from('Class1', /*withBodies:*/ true, /*copyTraits:*/ false); +$res[] = ClassType::from('Class2', /*withBodies:*/ true, /*copyTraits:*/ false); +$res[] = ClassType::from('Class3', /*withBodies:*/ true, /*copyTraits:*/ false); +$res[] = ClassType::from('Class4', /*withBodies:*/ true, /*copyTraits:*/ false); +$res[] = ClassType::from('Class5', /*withBodies:*/ true, /*copyTraits:*/ false); + +sameFile(__DIR__ . '/expected/ClassType.from.trait-use.bodies.expect', implode("\n", $res)); diff --git a/tests/PhpGenerator/expected/ClassType.from.trait-use.bodies.expect b/tests/PhpGenerator/expected/ClassType.from.trait-use.bodies.expect new file mode 100644 index 00000000..f7fe5e65 --- /dev/null +++ b/tests/PhpGenerator/expected/ClassType.from.trait-use.bodies.expect @@ -0,0 +1,71 @@ +/** + * Trait1 + */ +trait Trait1 +{ + public $x1; + + + public function f1() + { + echo 'Trait1::f1'; + } +} + +trait Trait2 +{ + use Trait1; + + protected $x2; + + + public function f2() + { + echo 'Trait2::f2'; + } +} + +class Class1 extends ParentClass +{ + use Trait2; +} + +class Class2 extends ParentClass +{ + use Trait2; + + public function f1() + { + echo 'Class2::f1'; + } +} + +class Class3 extends ParentClass +{ + use Trait2; + + /** info */ + public $x1; + + + public function f1() + { + echo 'Class3::f1'; + } +} + +class Class4 extends ParentClass +{ + use Trait2; + + public function aliased() + { + echo 'Class4::aliased'; + } +} + +class Class5 +{ + use Trait1; + use Trait1b; +} diff --git a/tests/PhpGenerator/expected/ClassType.from.trait-use.expect b/tests/PhpGenerator/expected/ClassType.from.trait-use.expect new file mode 100644 index 00000000..39e3a254 --- /dev/null +++ b/tests/PhpGenerator/expected/ClassType.from.trait-use.expect @@ -0,0 +1,66 @@ +/** + * Trait1 + */ +trait Trait1 +{ + public $x1; + + + public function f1() + { + } +} + +trait Trait2 +{ + use Trait1; + + protected $x2; + + + public function f2() + { + } +} + +class Class1 extends ParentClass +{ + use Trait2; +} + +class Class2 extends ParentClass +{ + use Trait2; + + public function f1() + { + } +} + +class Class3 extends ParentClass +{ + use Trait2; + + /** info */ + public $x1; + + + public function f1() + { + } +} + +class Class4 extends ParentClass +{ + use Trait2; + + public function aliased() + { + } +} + +class Class5 +{ + use Trait1; + use Trait1b; +} diff --git a/tests/PhpGenerator/expected/ClassType.from.trait.bodies.expect b/tests/PhpGenerator/expected/ClassType.from.trait.bodies.expect index f0e13493..fe05697e 100644 --- a/tests/PhpGenerator/expected/ClassType.from.trait.bodies.expect +++ b/tests/PhpGenerator/expected/ClassType.from.trait.bodies.expect @@ -67,6 +67,7 @@ class Class2 extends ParentClass class Class3 extends ParentClass { + /** info */ public $x1; protected $x2; diff --git a/tests/PhpGenerator/expected/ClassType.from.trait.expect b/tests/PhpGenerator/expected/ClassType.from.trait.expect index ea2c3cd0..c42920e6 100644 --- a/tests/PhpGenerator/expected/ClassType.from.trait.expect +++ b/tests/PhpGenerator/expected/ClassType.from.trait.expect @@ -60,6 +60,7 @@ class Class2 extends ParentClass class Class3 extends ParentClass { + /** info */ public $x1; protected $x2; diff --git a/tests/PhpGenerator/fixtures/traits.php b/tests/PhpGenerator/fixtures/traits.php index 072a369f..d144843a 100644 --- a/tests/PhpGenerator/fixtures/traits.php +++ b/tests/PhpGenerator/fixtures/traits.php @@ -69,6 +69,7 @@ class Class3 extends ParentClass Trait2::f1 as aliased; } + /** info */ public $x1;