1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* This file is part of the Composite Utils package. |
4
|
|
|
* |
5
|
|
|
* (c) Emily Shepherd <[email protected]> |
6
|
|
|
* |
7
|
|
|
* For the full copyright and license information, please view the |
8
|
|
|
* LICENSE.md file that was distributed with this source code. |
9
|
|
|
* |
10
|
|
|
* @package spaark/composite-utils |
11
|
|
|
* @author Emily Shepherd <[email protected]> |
12
|
|
|
* @license MIT |
13
|
|
|
*/ |
14
|
|
|
|
15
|
|
|
namespace Spaark\CompositeUtils\Factory\Reflection; |
16
|
|
|
|
17
|
|
|
use Spaark\CompositeUtils\Model\Reflection\ReflectionComposite; |
18
|
|
|
use Spaark\CompositeUtils\Model\Reflection\ReflectionMethod; |
19
|
|
|
use Spaark\CompositeUtils\Model\Reflection\Type\BooleanType; |
20
|
|
|
use Spaark\CompositeUtils\Model\Reflection\Type\CollectionType; |
21
|
|
|
use Spaark\CompositeUtils\Model\Reflection\Type\IntegerType; |
22
|
|
|
use Spaark\CompositeUtils\Model\Reflection\Type\MixedType; |
23
|
|
|
use Spaark\CompositeUtils\Model\Reflection\Type\ObjectType; |
24
|
|
|
use Spaark\CompositeUtils\Model\Reflection\Type\StringType; |
25
|
|
|
use Spaark\CompositeUtils\Model\Reflection\Type\GenericType; |
26
|
|
|
use Spaark\CompositeUtils\Model\Generic\GenericContext; |
27
|
|
|
use Spaark\CompositeUtils\Service\RawPropertyAccessor; |
28
|
|
|
use Spaark\CompositeUtils\Service\GenericNameProvider; |
29
|
|
|
use Spaark\CompositeUtils\Traits\AutoConstructTrait; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Generates the code for a generic class |
33
|
|
|
*/ |
34
|
|
|
class GenericCompositeGenerator |
35
|
|
|
{ |
36
|
|
|
use AutoConstructTrait; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* @var ReflectionComposite |
40
|
|
|
* @construct required |
41
|
|
|
*/ |
42
|
|
|
protected $reflect; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* @var GenericNameProvider |
46
|
|
|
*/ |
47
|
|
|
protected $nameProvider; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* Creates an ObjectType from the given list of generics |
51
|
|
|
* |
52
|
|
|
* @param AbstractType[] $generics |
53
|
|
|
*/ |
54
|
1 |
|
private function createObject(...$generics) : ObjectType |
55
|
|
|
{ |
56
|
1 |
|
$object = new ObjectType(get_class($this->reflect), ''); |
|
|
|
|
57
|
1 |
|
$i = 0; |
58
|
|
|
|
59
|
1 |
|
foreach ($this->reflect->generics as $name => $value) |
|
|
|
|
60
|
|
|
{ |
61
|
1 |
|
$object->generics[] = $generics[$i++] ?? $value; |
|
|
|
|
62
|
|
|
} |
63
|
|
|
|
64
|
1 |
|
return $object; |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Generate class code for the given generics |
69
|
|
|
* |
70
|
|
|
* @param AbstractType[] $generics |
71
|
|
|
* @return string |
72
|
|
|
*/ |
73
|
1 |
|
public function generateClassCode(...$generics) : string |
74
|
|
|
{ |
75
|
1 |
|
$object = $this->createObject(...$generics); |
76
|
1 |
|
$this->nameProvider = new GenericNameProvider |
77
|
|
|
( |
78
|
1 |
|
new GenericContext($object, $this->reflect) |
79
|
|
|
); |
80
|
1 |
|
$class = $this->nameProvider->inferName($object); |
81
|
1 |
|
$originalClass = get_class($this->reflect); |
82
|
1 |
|
$i = 0; |
|
|
|
|
83
|
|
|
|
84
|
|
|
$code = |
85
|
1 |
|
'<?php namespace ' . $class->namespace . ';' |
86
|
1 |
|
. 'class ' . $class->classname . ' ' |
87
|
1 |
|
. 'extends ' . $originalClass |
88
|
1 |
|
. '{'; |
89
|
|
|
|
90
|
1 |
|
foreach ($this->reflect->methods as $method) |
91
|
|
|
{ |
92
|
1 |
|
$code .= $this->generateMethodCode($method); |
93
|
|
|
} |
94
|
|
|
|
95
|
1 |
|
$code .= '}'; |
96
|
|
|
|
97
|
1 |
|
return $code; |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* Generates the method code for the current class being generated |
102
|
|
|
* |
103
|
|
|
* @param ReflectionMethod $method |
104
|
|
|
* @return string |
105
|
|
|
*/ |
106
|
1 |
|
public function generateMethodCode(ReflectionMethod $method) |
107
|
|
|
: string |
108
|
|
|
{ |
109
|
1 |
|
$params = []; |
110
|
1 |
|
$newParams = []; |
111
|
1 |
|
$paramNames = []; |
112
|
1 |
|
foreach ($method->parameters as $i => $param) |
113
|
|
|
{ |
114
|
1 |
|
$paramNames[] = $name = ' $' . $param->name; |
115
|
1 |
|
$params[] = $method->nativeParameters[$i] . $name; |
|
|
|
|
116
|
1 |
|
$newParams[] = |
117
|
1 |
|
$this->nameProvider->inferName($param->type) . $name; |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
return |
121
|
1 |
|
($method->scope === 'static' ? 'static ' : '') |
122
|
1 |
|
. 'function ' . $method->name |
123
|
1 |
|
. '(' . implode(',', $params) . '){' |
124
|
1 |
|
. '__generic_' . $method->name |
125
|
1 |
|
. '(' . implode(',', $paramNames) . ');}' |
126
|
|
|
. "\n" |
127
|
1 |
|
. 'function __generic_' . $method->name |
128
|
1 |
|
. '(' . implode(',', $newParams) . '){' |
129
|
1 |
|
. 'parent::' . $method->name |
130
|
1 |
|
. '(' . implode(',', $paramNames) . ');}' |
131
|
1 |
|
. "\n"; |
132
|
|
|
} |
133
|
|
|
} |
134
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.