Skip to content

Commit 2208eb1

Browse files
committed
added inheritMethod()
1 parent 225c3ec commit 2208eb1

File tree

2 files changed

+105
-0
lines changed

2 files changed

+105
-0
lines changed

src/PhpGenerator/Traits/MethodsAware.php

+39
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,43 @@ public function hasMethod(string $name): bool
7979
{
8080
return isset($this->methods[strtolower($name)]);
8181
}
82+
83+
84+
public function inheritMethod(string $name, bool $callParent = false, bool $returnIfExists = false): Method
85+
{
86+
$lower = strtolower($name);
87+
if (isset($this->methods[$lower])) {
88+
return $returnIfExists
89+
? $this->methods[$lower]
90+
: throw new Nette\InvalidStateException("Cannot inherit method '$name', because it already exists.");
91+
}
92+
93+
$parents = match (true) {
94+
$this instanceof Nette\PhpGenerator\ClassType => [...(array) $this->getExtends(), ...$this->getImplements()],
95+
$this instanceof Nette\PhpGenerator\InterfaceType => $this->getExtends(),
96+
$this instanceof Nette\PhpGenerator\EnumType => $this->getImplements(),
97+
};
98+
if (!$parents) {
99+
throw new Nette\InvalidStateException("Class '{$this->getName()}' has neither setExtends() nor setImplements() set.");
100+
}
101+
102+
foreach ($parents as $parent) {
103+
try {
104+
$rm = new \ReflectionMethod($parent, $name);
105+
} catch (\ReflectionException) {
106+
continue;
107+
}
108+
$method = Method::from([$parent, $name]);
109+
if ($callParent) {
110+
$params = array_map(fn(\ReflectionParameter $param) => '$' . $param->getName(), $rm->getParameters());
111+
if ($rm->isVariadic()) {
112+
$params[] = '...' . array_pop($params);
113+
}
114+
$method->setBody('parent::' . $rm->getName() . '(' . implode(', ', $params) . ');');
115+
}
116+
return $this->methods[$lower] = $method;
117+
}
118+
119+
throw new Nette\InvalidStateException("Method '$name' has not been found in any ancestor: " . implode(', ', $parents));
120+
}
82121
}
+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Tester\Assert;
6+
7+
require __DIR__ . '/../bootstrap.php';
8+
9+
10+
class Foo
11+
{
12+
public function bar(int $a, ...$b): void
13+
{
14+
}
15+
}
16+
17+
18+
// missing parent
19+
$class = new Nette\PhpGenerator\ClassType('Test');
20+
Assert::exception(
21+
fn() => $class->inheritMethod('bar'),
22+
Nette\InvalidStateException::class,
23+
"Class 'Test' has neither setExtends() nor setImplements() set.",
24+
);
25+
26+
$class->setExtends('Unknown1');
27+
$class->addImplement('Unknown2');
28+
Assert::exception(
29+
fn() => $class->inheritMethod('bar'),
30+
Nette\InvalidStateException::class,
31+
"Method 'bar' has not been found in any ancestor: Unknown1, Unknown2",
32+
);
33+
34+
35+
// implemented method
36+
$class = new Nette\PhpGenerator\ClassType('Test');
37+
$class->setExtends(Foo::class);
38+
$method = $class->inheritMethod('bar');
39+
Assert::match(<<<'XX'
40+
public function bar(int $a, ...$b): void
41+
{
42+
}
43+
44+
XX, (string) $method);
45+
46+
$class = new Nette\PhpGenerator\ClassType('Test');
47+
$class->setExtends(Foo::class);
48+
$method = $class->inheritMethod('Bar', callParent: true); // intentionally case insensitive
49+
Assert::match(<<<'XX'
50+
public function bar(int $a, ...$b): void
51+
{
52+
parent::bar($a, ...$b);
53+
}
54+
55+
XX, (string) $method);
56+
57+
58+
// exists
59+
$class = new Nette\PhpGenerator\ClassType('Test');
60+
$method = $class->addMethod('bar');
61+
Assert::same($method, $class->inheritMethod('bar', returnIfExists: true));
62+
Assert::exception(
63+
fn() => $class->inheritMethod('bar', returnIfExists: false),
64+
Nette\InvalidStateException::class,
65+
"Cannot inherit method 'bar', because it already exists.",
66+
);

0 commit comments

Comments
 (0)