Skip to content

Commit bfed454

Browse files
committed
added ClassManipulator
1 parent 60820ea commit bfed454

File tree

5 files changed

+150
-53
lines changed

5 files changed

+150
-53
lines changed

src/PhpGenerator/ClassManipulator.php

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the Nette Framework (https://nette.org)
5+
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Nette\PhpGenerator;
11+
12+
use Nette;
13+
14+
15+
final class ClassManipulator
16+
{
17+
public function __construct(
18+
private ClassType $class,
19+
) {
20+
}
21+
22+
23+
/**
24+
* Inherits property from parent class.
25+
*/
26+
public function inheritProperty(string $name, bool $returnIfExists = false): Property
27+
{
28+
$extends = $this->class->getExtends();
29+
if ($this->class->hasProperty($name)) {
30+
return $returnIfExists
31+
? $this->class->getProperty($name)
32+
: throw new Nette\InvalidStateException("Cannot inherit property '$name', because it already exists.");
33+
34+
} elseif (!$extends) {
35+
throw new Nette\InvalidStateException("Class '{$this->class->getName()}' has not setExtends() set.");
36+
}
37+
38+
try {
39+
$rp = new \ReflectionProperty($extends, $name);
40+
} catch (\ReflectionException) {
41+
throw new Nette\InvalidStateException("Property '$name' has not been found in ancestor {$extends}");
42+
}
43+
44+
$property = (new Factory)->fromPropertyReflection($rp);
45+
$this->class->addMember($property);
46+
return $property;
47+
}
48+
49+
50+
/**
51+
* Inherits method from parent class or interface.
52+
*/
53+
public function inheritMethod(string $name, bool $returnIfExists = false): Method
54+
{
55+
$parents = [...(array) $this->class->getExtends(), ...$this->class->getImplements()];
56+
if ($this->class->hasMethod($name)) {
57+
return $returnIfExists
58+
? $this->class->getMethod($name)
59+
: throw new Nette\InvalidStateException("Cannot inherit method '$name', because it already exists.");
60+
61+
} elseif (!$parents) {
62+
throw new Nette\InvalidStateException("Class '{$this->class->getName()}' has neither setExtends() nor setImplements() set.");
63+
}
64+
65+
foreach ($parents as $parent) {
66+
try {
67+
$rm = new \ReflectionMethod($parent, $name);
68+
} catch (\ReflectionException) {
69+
continue;
70+
}
71+
$method = (new Factory)->fromMethodReflection($rm);
72+
$this->class->addMember($method);
73+
return $method;
74+
}
75+
76+
throw new Nette\InvalidStateException("Method '$name' has not been found in any ancestor: " . implode(', ', $parents));
77+
}
78+
79+
80+
/**
81+
* Automatically implements all methods from the given interface.
82+
*/
83+
public function implementInterface(string $interfaceName): void
84+
{
85+
$interface = new \ReflectionClass($interfaceName);
86+
if (!$interface->isInterface()) {
87+
throw new Nette\InvalidArgumentException("Class '$interfaceName' is not an interface.");
88+
}
89+
90+
$this->class->addImplement($interfaceName);
91+
foreach ($interface->getMethods() as $method) {
92+
$this->inheritMethod($method->getName(), returnIfExists: true);
93+
}
94+
}
95+
}

src/PhpGenerator/ClassType.php

Lines changed: 4 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -195,55 +195,20 @@ public function addMember(Method|Property|Constant|TraitUse $member, bool $overw
195195

196196

197197
/**
198-
* Inherits property from parent class.
198+
* @deprecated use ClassManipulator::inheritProperty()
199199
*/
200200
public function inheritProperty(string $name, bool $returnIfExists = false): Property
201201
{
202-
if (isset($this->properties[$name])) {
203-
return $returnIfExists
204-
? $this->properties[$name]
205-
: throw new Nette\InvalidStateException("Cannot inherit property '$name', because it already exists.");
206-
207-
} elseif (!$this->extends) {
208-
throw new Nette\InvalidStateException("Class '{$this->getName()}' has not setExtends() set.");
209-
}
210-
211-
try {
212-
$rp = new \ReflectionProperty($this->extends, $name);
213-
} catch (\ReflectionException) {
214-
throw new Nette\InvalidStateException("Property '$name' has not been found in ancestor {$this->extends}");
215-
}
216-
217-
return $this->properties[$name] = (new Factory)->fromPropertyReflection($rp);
202+
return (new ClassManipulator($this))->inheritProperty($name, $returnIfExists);
218203
}
219204

220205

221206
/**
222-
* Inherits method from parent class or interface.
207+
* @deprecated use ClassManipulator::inheritMethod()
223208
*/
224209
public function inheritMethod(string $name, bool $returnIfExists = false): Method
225210
{
226-
$lower = strtolower($name);
227-
$parents = [...(array) $this->extends, ...$this->implements];
228-
if (isset($this->methods[$lower])) {
229-
return $returnIfExists
230-
? $this->methods[$lower]
231-
: throw new Nette\InvalidStateException("Cannot inherit method '$name', because it already exists.");
232-
233-
} elseif (!$parents) {
234-
throw new Nette\InvalidStateException("Class '{$this->getName()}' has neither setExtends() nor setImplements() set.");
235-
}
236-
237-
foreach ($parents as $parent) {
238-
try {
239-
$rm = new \ReflectionMethod($parent, $name);
240-
} catch (\ReflectionException) {
241-
continue;
242-
}
243-
return $this->methods[$lower] = (new Factory)->fromMethodReflection($rm);
244-
}
245-
246-
throw new Nette\InvalidStateException("Method '$name' has not been found in any ancestor: " . implode(', ', $parents));
211+
return (new ClassManipulator($this))->inheritMethod($name, $returnIfExists);
247212
}
248213

249214

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Nette\PhpGenerator\ClassManipulator;
6+
use Nette\PhpGenerator\ClassType;
7+
use Tester\Assert;
8+
9+
require __DIR__ . '/../bootstrap.php';
10+
11+
12+
interface TestInterface
13+
{
14+
public function testMethod();
15+
}
16+
17+
$class = new ClassType('TestClass');
18+
$manipulator = new ClassManipulator($class);
19+
20+
// Test valid interface implementation
21+
$manipulator->implementInterface(TestInterface::class);
22+
Assert::true(in_array(TestInterface::class, $class->getImplements(), true));
23+
Assert::true($class->hasMethod('testMethod'));
24+
25+
// Test exception for non-interface
26+
Assert::exception(
27+
fn() => $manipulator->implementInterface(stdClass::class),
28+
InvalidArgumentException::class,
29+
);

tests/PhpGenerator/Method.inherit.phpt renamed to tests/PhpGenerator/ClassManipulator.inheritMethod.phpt

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
declare(strict_types=1);
44

5+
use Nette\PhpGenerator\ClassManipulator;
6+
use Nette\PhpGenerator\ClassType;
57
use Tester\Assert;
68

79
require __DIR__ . '/../bootstrap.php';
@@ -16,36 +18,38 @@ class Foo
1618

1719

1820
// missing parent
19-
$class = new Nette\PhpGenerator\ClassType('Test');
21+
$class = new ClassType('Test');
22+
$manipulator = new ClassManipulator($class);
2023
Assert::exception(
21-
fn() => $class->inheritMethod('bar'),
24+
fn() => $manipulator->inheritMethod('bar'),
2225
Nette\InvalidStateException::class,
2326
"Class 'Test' has neither setExtends() nor setImplements() set.",
2427
);
2528

2629
$class->setExtends('Unknown1');
2730
$class->addImplement('Unknown2');
2831
Assert::exception(
29-
fn() => $class->inheritMethod('bar'),
32+
fn() => $manipulator->inheritMethod('bar'),
3033
Nette\InvalidStateException::class,
3134
"Method 'bar' has not been found in any ancestor: Unknown1, Unknown2",
3235
);
3336

3437

3538
// implement method
36-
$class = new Nette\PhpGenerator\ClassType('Test');
39+
$class = new ClassType('Test');
3740
$class->setExtends(Foo::class);
38-
$method = $class->inheritMethod('bar');
41+
$manipulator = new ClassManipulator($class);
42+
$method = $manipulator->inheritMethod('bar');
3943
Assert::match(<<<'XX'
4044
public function bar(int $a, ...$b): void
4145
{
4246
}
4347

4448
XX, (string) $method);
4549

46-
Assert::same($method, $class->inheritMethod('bar', returnIfExists: true));
50+
Assert::same($method, $manipulator->inheritMethod('bar', returnIfExists: true));
4751
Assert::exception(
48-
fn() => $class->inheritMethod('bar', returnIfExists: false),
52+
fn() => $manipulator->inheritMethod('bar', returnIfExists: false),
4953
Nette\InvalidStateException::class,
5054
"Cannot inherit method 'bar', because it already exists.",
5155
);

tests/PhpGenerator/Property.inherit.phpt renamed to tests/PhpGenerator/ClassManipulator.inheritProperty.phpt

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
declare(strict_types=1);
44

5+
use Nette\PhpGenerator\ClassManipulator;
6+
use Nette\PhpGenerator\ClassType;
57
use Tester\Assert;
68

79
require __DIR__ . '/../bootstrap.php';
@@ -14,25 +16,27 @@ class Foo
1416

1517

1618
// missing parent
17-
$class = new Nette\PhpGenerator\ClassType('Test');
19+
$class = new ClassType('Test');
20+
$manipulator = new ClassManipulator($class);
1821
Assert::exception(
19-
fn() => $class->inheritProperty('bar'),
22+
fn() => $manipulator->inheritProperty('bar'),
2023
Nette\InvalidStateException::class,
2124
"Class 'Test' has not setExtends() set.",
2225
);
2326

2427
$class->setExtends('Unknown');
2528
Assert::exception(
26-
fn() => $class->inheritProperty('bar'),
29+
fn() => $manipulator->inheritProperty('bar'),
2730
Nette\InvalidStateException::class,
2831
"Property 'bar' has not been found in ancestor Unknown",
2932
);
3033

3134

3235
// implement property
33-
$class = new Nette\PhpGenerator\ClassType('Test');
36+
$class = new ClassType('Test');
3437
$class->setExtends(Foo::class);
35-
$prop = $class->inheritProperty('bar');
38+
$manipulator = new ClassManipulator($class);
39+
$prop = $manipulator->inheritProperty('bar');
3640
Assert::match(<<<'XX'
3741
class Test extends Foo
3842
{
@@ -41,9 +45,9 @@ Assert::match(<<<'XX'
4145

4246
XX, (string) $class);
4347

44-
Assert::same($prop, $class->inheritProperty('bar', returnIfExists: true));
48+
Assert::same($prop, $manipulator->inheritProperty('bar', returnIfExists: true));
4549
Assert::exception(
46-
fn() => $class->inheritProperty('bar', returnIfExists: false),
50+
fn() => $manipulator->inheritProperty('bar', returnIfExists: false),
4751
Nette\InvalidStateException::class,
4852
"Cannot inherit property 'bar', because it already exists.",
4953
);

0 commit comments

Comments
 (0)