diff --git a/src/PhpGenerator/ClassManipulator.php b/src/PhpGenerator/ClassManipulator.php new file mode 100644 index 00000000..1fca5064 --- /dev/null +++ b/src/PhpGenerator/ClassManipulator.php @@ -0,0 +1,95 @@ +class->getExtends(); + if ($this->class->hasProperty($name)) { + return $returnIfExists + ? $this->class->getProperty($name) + : throw new Nette\InvalidStateException("Cannot inherit property '$name', because it already exists."); + + } elseif (!$extends) { + throw new Nette\InvalidStateException("Class '{$this->class->getName()}' has not setExtends() set."); + } + + try { + $rp = new \ReflectionProperty($extends, $name); + } catch (\ReflectionException) { + throw new Nette\InvalidStateException("Property '$name' has not been found in ancestor {$extends}"); + } + + $property = (new Factory)->fromPropertyReflection($rp); + $this->class->addMember($property); + return $property; + } + + + /** + * Inherits method from parent class or interface. + */ + public function inheritMethod(string $name, bool $returnIfExists = false): Method + { + $parents = [...(array) $this->class->getExtends(), ...$this->class->getImplements()]; + if ($this->class->hasMethod($name)) { + return $returnIfExists + ? $this->class->getMethod($name) + : throw new Nette\InvalidStateException("Cannot inherit method '$name', because it already exists."); + + } elseif (!$parents) { + throw new Nette\InvalidStateException("Class '{$this->class->getName()}' has neither setExtends() nor setImplements() set."); + } + + foreach ($parents as $parent) { + try { + $rm = new \ReflectionMethod($parent, $name); + } catch (\ReflectionException) { + continue; + } + $method = (new Factory)->fromMethodReflection($rm); + $this->class->addMember($method); + return $method; + } + + throw new Nette\InvalidStateException("Method '$name' has not been found in any ancestor: " . implode(', ', $parents)); + } + + + /** + * Automatically implements all methods from the given interface. + */ + public function implementInterface(string $interfaceName): void + { + $interface = new \ReflectionClass($interfaceName); + if (!$interface->isInterface()) { + throw new Nette\InvalidArgumentException("Class '$interfaceName' is not an interface."); + } + + $this->class->addImplement($interfaceName); + foreach ($interface->getMethods() as $method) { + $this->inheritMethod($method->getName(), returnIfExists: true); + } + } +} diff --git a/src/PhpGenerator/ClassType.php b/src/PhpGenerator/ClassType.php index f8481f15..4e082685 100644 --- a/src/PhpGenerator/ClassType.php +++ b/src/PhpGenerator/ClassType.php @@ -195,55 +195,20 @@ public function addMember(Method|Property|Constant|TraitUse $member, bool $overw /** - * Inherits property from parent class. + * @deprecated use ClassManipulator::inheritProperty() */ public function inheritProperty(string $name, bool $returnIfExists = false): Property { - if (isset($this->properties[$name])) { - return $returnIfExists - ? $this->properties[$name] - : throw new Nette\InvalidStateException("Cannot inherit property '$name', because it already exists."); - - } elseif (!$this->extends) { - throw new Nette\InvalidStateException("Class '{$this->getName()}' has not setExtends() set."); - } - - try { - $rp = new \ReflectionProperty($this->extends, $name); - } catch (\ReflectionException) { - throw new Nette\InvalidStateException("Property '$name' has not been found in ancestor {$this->extends}"); - } - - return $this->properties[$name] = (new Factory)->fromPropertyReflection($rp); + return (new ClassManipulator($this))->inheritProperty($name, $returnIfExists); } /** - * Inherits method from parent class or interface. + * @deprecated use ClassManipulator::inheritMethod() */ public function inheritMethod(string $name, bool $returnIfExists = false): Method { - $lower = strtolower($name); - $parents = [...(array) $this->extends, ...$this->implements]; - if (isset($this->methods[$lower])) { - return $returnIfExists - ? $this->methods[$lower] - : throw new Nette\InvalidStateException("Cannot inherit method '$name', because it already exists."); - - } elseif (!$parents) { - throw new Nette\InvalidStateException("Class '{$this->getName()}' has neither setExtends() nor setImplements() set."); - } - - foreach ($parents as $parent) { - try { - $rm = new \ReflectionMethod($parent, $name); - } catch (\ReflectionException) { - continue; - } - return $this->methods[$lower] = (new Factory)->fromMethodReflection($rm); - } - - throw new Nette\InvalidStateException("Method '$name' has not been found in any ancestor: " . implode(', ', $parents)); + return (new ClassManipulator($this))->inheritMethod($name, $returnIfExists); } diff --git a/tests/PhpGenerator/ClassManipulator.implementInterface.phpt b/tests/PhpGenerator/ClassManipulator.implementInterface.phpt new file mode 100644 index 00000000..a0e18e52 --- /dev/null +++ b/tests/PhpGenerator/ClassManipulator.implementInterface.phpt @@ -0,0 +1,29 @@ +implementInterface(TestInterface::class); +Assert::true(in_array(TestInterface::class, $class->getImplements(), true)); +Assert::true($class->hasMethod('testMethod')); + +// Test exception for non-interface +Assert::exception( + fn() => $manipulator->implementInterface(stdClass::class), + InvalidArgumentException::class, +); diff --git a/tests/PhpGenerator/Method.inherit.phpt b/tests/PhpGenerator/ClassManipulator.inheritMethod.phpt similarity index 59% rename from tests/PhpGenerator/Method.inherit.phpt rename to tests/PhpGenerator/ClassManipulator.inheritMethod.phpt index 6fcc0523..4927e69c 100644 --- a/tests/PhpGenerator/Method.inherit.phpt +++ b/tests/PhpGenerator/ClassManipulator.inheritMethod.phpt @@ -2,6 +2,8 @@ declare(strict_types=1); +use Nette\PhpGenerator\ClassManipulator; +use Nette\PhpGenerator\ClassType; use Tester\Assert; require __DIR__ . '/../bootstrap.php'; @@ -16,9 +18,10 @@ class Foo // missing parent -$class = new Nette\PhpGenerator\ClassType('Test'); +$class = new ClassType('Test'); +$manipulator = new ClassManipulator($class); Assert::exception( - fn() => $class->inheritMethod('bar'), + fn() => $manipulator->inheritMethod('bar'), Nette\InvalidStateException::class, "Class 'Test' has neither setExtends() nor setImplements() set.", ); @@ -26,16 +29,17 @@ Assert::exception( $class->setExtends('Unknown1'); $class->addImplement('Unknown2'); Assert::exception( - fn() => $class->inheritMethod('bar'), + fn() => $manipulator->inheritMethod('bar'), Nette\InvalidStateException::class, "Method 'bar' has not been found in any ancestor: Unknown1, Unknown2", ); // implement method -$class = new Nette\PhpGenerator\ClassType('Test'); +$class = new ClassType('Test'); $class->setExtends(Foo::class); -$method = $class->inheritMethod('bar'); +$manipulator = new ClassManipulator($class); +$method = $manipulator->inheritMethod('bar'); Assert::match(<<<'XX' public function bar(int $a, ...$b): void { @@ -43,9 +47,9 @@ Assert::match(<<<'XX' XX, (string) $method); -Assert::same($method, $class->inheritMethod('bar', returnIfExists: true)); +Assert::same($method, $manipulator->inheritMethod('bar', returnIfExists: true)); Assert::exception( - fn() => $class->inheritMethod('bar', returnIfExists: false), + fn() => $manipulator->inheritMethod('bar', returnIfExists: false), Nette\InvalidStateException::class, "Cannot inherit method 'bar', because it already exists.", ); diff --git a/tests/PhpGenerator/Property.inherit.phpt b/tests/PhpGenerator/ClassManipulator.inheritProperty.phpt similarity index 57% rename from tests/PhpGenerator/Property.inherit.phpt rename to tests/PhpGenerator/ClassManipulator.inheritProperty.phpt index e110dae8..d9b9bf1b 100644 --- a/tests/PhpGenerator/Property.inherit.phpt +++ b/tests/PhpGenerator/ClassManipulator.inheritProperty.phpt @@ -2,6 +2,8 @@ declare(strict_types=1); +use Nette\PhpGenerator\ClassManipulator; +use Nette\PhpGenerator\ClassType; use Tester\Assert; require __DIR__ . '/../bootstrap.php'; @@ -14,25 +16,27 @@ class Foo // missing parent -$class = new Nette\PhpGenerator\ClassType('Test'); +$class = new ClassType('Test'); +$manipulator = new ClassManipulator($class); Assert::exception( - fn() => $class->inheritProperty('bar'), + fn() => $manipulator->inheritProperty('bar'), Nette\InvalidStateException::class, "Class 'Test' has not setExtends() set.", ); $class->setExtends('Unknown'); Assert::exception( - fn() => $class->inheritProperty('bar'), + fn() => $manipulator->inheritProperty('bar'), Nette\InvalidStateException::class, "Property 'bar' has not been found in ancestor Unknown", ); // implement property -$class = new Nette\PhpGenerator\ClassType('Test'); +$class = new ClassType('Test'); $class->setExtends(Foo::class); -$prop = $class->inheritProperty('bar'); +$manipulator = new ClassManipulator($class); +$prop = $manipulator->inheritProperty('bar'); Assert::match(<<<'XX' class Test extends Foo { @@ -41,9 +45,9 @@ Assert::match(<<<'XX' XX, (string) $class); -Assert::same($prop, $class->inheritProperty('bar', returnIfExists: true)); +Assert::same($prop, $manipulator->inheritProperty('bar', returnIfExists: true)); Assert::exception( - fn() => $class->inheritProperty('bar', returnIfExists: false), + fn() => $manipulator->inheritProperty('bar', returnIfExists: false), Nette\InvalidStateException::class, "Cannot inherit property 'bar', because it already exists.", );