1
|
|
|
<?php declare(strict_types = 1); |
2
|
|
|
/** |
3
|
|
|
* Created by PhpStorm. |
4
|
|
|
* User: root |
5
|
|
|
* Date: 02.08.16 |
6
|
|
|
* Time: 0:46. |
7
|
|
|
*/ |
8
|
|
|
namespace samsonframework\container\definition; |
9
|
|
|
|
10
|
|
|
use samsonframework\container\definition\exception\MethodDefinitionNotFoundException; |
11
|
|
|
use samsonframework\container\definition\exception\PropertyDefinitionNotFoundException; |
12
|
|
|
use samsonframework\container\definition\reference\ClassReference; |
13
|
|
|
use samsonframework\container\definition\reference\ReferenceInterface; |
14
|
|
|
use samsonframework\container\definition\scope\AbstractScope; |
15
|
|
|
use samsonframework\container\definition\exception\MethodDefinitionAlreadyExistsException; |
16
|
|
|
use samsonframework\container\definition\exception\PropertyDefinitionAlreadyExistsException; |
17
|
|
|
use samsonframework\container\definition\exception\ScopeAlreadyExistsException; |
18
|
|
|
use samsonframework\container\definition\exception\ScopeNotFoundException; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Class ClassDefinition |
22
|
|
|
* |
23
|
|
|
* @author Ruslan Molodyko <[email protected]> |
24
|
|
|
*/ |
25
|
|
|
class ClassDefinition extends AbstractDefinition implements ClassBuilderInterface |
26
|
|
|
{ |
27
|
|
|
/** @var string Class name with namespace */ |
28
|
|
|
protected $className; |
29
|
|
|
/** @var string Class name space */ |
30
|
|
|
protected $nameSpace; |
31
|
|
|
/** @var string Service name */ |
32
|
|
|
protected $serviceName; |
33
|
|
|
/** @var array Class container scopes */ |
34
|
|
|
protected $scopes = []; |
35
|
|
|
/** @var bool Is singleton */ |
36
|
|
|
protected $isSingleton = false; |
37
|
|
|
/** @var bool Is class definition was analyzed */ |
38
|
|
|
protected $isAnalyzed = false; |
39
|
|
|
|
40
|
|
|
/** @var MethodDefinition[] Methods collection */ |
41
|
|
|
protected $methodsCollection = []; |
42
|
|
|
/** @var PropertyDefinition[] Property collection */ |
43
|
|
|
protected $propertiesCollection = []; |
44
|
|
|
|
45
|
|
|
/** {@inheritdoc} */ |
46
|
9 |
|
public function defineConstructor(): MethodBuilderInterface |
47
|
|
|
{ |
48
|
|
|
/** Add constructor method manually */ |
49
|
9 |
|
return $this->defineMethod('__construct'); |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
/** {@inheritdoc} */ |
53
|
13 |
|
public function defineMethod(string $methodName): MethodBuilderInterface |
54
|
|
|
{ |
55
|
13 |
|
if (array_key_exists($methodName, $this->methodsCollection)) { |
56
|
2 |
|
throw new MethodDefinitionAlreadyExistsException(); |
57
|
|
|
} |
58
|
|
|
|
59
|
13 |
|
$methodDefinition = new MethodDefinition($this, $methodName); |
|
|
|
|
60
|
13 |
|
$methodDefinition->setMethodName($methodName); |
61
|
|
|
|
62
|
13 |
|
$this->methodsCollection[$methodName] = $methodDefinition; |
63
|
|
|
|
64
|
13 |
|
return $methodDefinition; |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
/** {@inheritdoc} */ |
68
|
9 |
|
public function defineProperty(string $propertyName): PropertyBuilderInterface |
69
|
|
|
{ |
70
|
9 |
|
if (array_key_exists($propertyName, $this->propertiesCollection)) { |
71
|
1 |
|
throw new PropertyDefinitionAlreadyExistsException(); |
72
|
|
|
} |
73
|
|
|
|
74
|
9 |
|
$propertyDefinition = new PropertyDefinition($this); |
75
|
9 |
|
$propertyDefinition->setPropertyName($propertyName); |
76
|
|
|
|
77
|
9 |
|
$this->propertiesCollection[$propertyName] = $propertyDefinition; |
78
|
|
|
|
79
|
9 |
|
return $propertyDefinition; |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/** {@inheritdoc} */ |
83
|
2 |
|
public function defineIsPrototype(): ClassBuilderInterface |
84
|
|
|
{ |
85
|
2 |
|
$this->isSingleton = false; |
86
|
|
|
|
87
|
2 |
|
return $this; |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
/** {@inheritdoc} */ |
91
|
2 |
|
public function defineIsSingleton(): ClassBuilderInterface |
92
|
|
|
{ |
93
|
2 |
|
$this->isSingleton = true; |
94
|
|
|
|
95
|
2 |
|
return $this; |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* Get namespace |
100
|
|
|
* |
101
|
|
|
* @return string |
102
|
|
|
*/ |
103
|
2 |
|
public function getNameSpace(): string |
104
|
|
|
{ |
105
|
2 |
|
return $this->nameSpace; |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* @param string $nameSpace |
110
|
|
|
* @return ClassDefinition |
111
|
|
|
*/ |
112
|
6 |
|
public function setNameSpace(string $nameSpace): ClassDefinition |
113
|
|
|
{ |
114
|
6 |
|
$this->nameSpace = $nameSpace; |
115
|
|
|
|
116
|
6 |
|
return $this; |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* Add scope to definition |
121
|
|
|
* |
122
|
|
|
* @param AbstractScope $scope |
123
|
|
|
* @return ClassDefinition |
124
|
|
|
* @throws ScopeAlreadyExistsException |
125
|
|
|
*/ |
126
|
4 |
|
public function addScope(AbstractScope $scope): ClassDefinition |
127
|
|
|
{ |
128
|
4 |
|
if ($this->hasScope($scope::getId())) { |
129
|
1 |
|
throw new ScopeAlreadyExistsException(); |
130
|
|
|
} |
131
|
|
|
|
132
|
4 |
|
$this->scopes[$scope::getId()] = $scope; |
133
|
|
|
|
134
|
4 |
|
return $this; |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* Remove scope from definition |
139
|
|
|
* |
140
|
|
|
* @param string $id |
141
|
|
|
* @return ClassDefinition |
142
|
|
|
* @throws ScopeNotFoundException |
143
|
|
|
*/ |
144
|
2 |
|
public function removeScope(string $id): ClassDefinition |
145
|
|
|
{ |
146
|
2 |
|
if (!$this->hasScope($id)) { |
147
|
1 |
|
throw new ScopeNotFoundException(); |
148
|
|
|
} |
149
|
|
|
|
150
|
1 |
|
unset($this->scopes[$id]); |
151
|
|
|
|
152
|
1 |
|
return $this; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* Check if scope exists in definition |
157
|
|
|
* |
158
|
|
|
* @param string $id |
159
|
|
|
* @return bool |
160
|
|
|
*/ |
161
|
4 |
|
public function hasScope(string $id): bool |
162
|
|
|
{ |
163
|
4 |
|
return array_key_exists($id, $this->scopes); |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* Get scope from definition |
168
|
|
|
* |
169
|
|
|
* @param string $id |
170
|
|
|
* @return mixed |
171
|
|
|
* @throws ScopeNotFoundException |
172
|
|
|
*/ |
173
|
2 |
|
public function getScope(string $id): AbstractScope |
174
|
|
|
{ |
175
|
2 |
|
if (!$this->hasScope($id)) { |
176
|
1 |
|
throw new ScopeNotFoundException(); |
177
|
|
|
} |
178
|
1 |
|
return $this->scopes[$id]; |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* Get all scopes |
183
|
|
|
* |
184
|
|
|
* @return AbstractScope[] |
185
|
|
|
*/ |
186
|
3 |
|
public function getScopes(): array |
187
|
|
|
{ |
188
|
3 |
|
return $this->scopes; |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* Get class name |
193
|
|
|
* |
194
|
|
|
* @return string |
195
|
|
|
*/ |
196
|
8 |
|
public function getClassName(): string |
197
|
|
|
{ |
198
|
8 |
|
return $this->className; |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
/** |
202
|
|
|
* @param string|ClassReference $className |
203
|
|
|
* @return ClassDefinition |
204
|
|
|
* @throws \InvalidArgumentException |
205
|
|
|
*/ |
206
|
28 |
|
public function setClassName($className): ClassDefinition |
207
|
|
|
{ |
208
|
28 |
|
if ($className instanceof ClassReference) { |
209
|
16 |
|
$this->className = $className->getClassName(); |
210
|
12 |
|
} elseif (is_string($className)) { |
211
|
12 |
|
$this->className = $className; |
212
|
|
|
} else { |
213
|
|
|
throw new \InvalidArgumentException(); |
214
|
|
|
} |
215
|
|
|
|
216
|
28 |
|
return $this; |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
/** |
220
|
|
|
* @return string|null |
221
|
|
|
*/ |
222
|
3 |
|
public function getServiceName() |
223
|
|
|
{ |
224
|
3 |
|
return $this->serviceName; |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
/** |
228
|
|
|
* @param string $serviceName |
229
|
|
|
* @return ClassDefinition |
230
|
|
|
*/ |
231
|
2 |
|
public function setServiceName(string $serviceName): ClassDefinition |
232
|
|
|
{ |
233
|
2 |
|
$this->serviceName = $serviceName; |
234
|
|
|
|
235
|
2 |
|
return $this; |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
/** |
239
|
|
|
* @return boolean |
240
|
|
|
*/ |
241
|
4 |
|
public function isSingleton(): bool |
242
|
|
|
{ |
243
|
4 |
|
return $this->isSingleton; |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
/** |
247
|
|
|
* @param boolean $isSingleton |
248
|
|
|
* @return ClassDefinition |
249
|
|
|
*/ |
250
|
1 |
|
public function setIsSingleton(bool $isSingleton): ClassDefinition |
251
|
|
|
{ |
252
|
1 |
|
$this->isSingleton = $isSingleton; |
253
|
|
|
|
254
|
1 |
|
return $this; |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* @return PropertyDefinition[] |
259
|
|
|
*/ |
260
|
4 |
|
public function getPropertiesCollection(): array |
261
|
|
|
{ |
262
|
4 |
|
return $this->propertiesCollection; |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
/** |
266
|
|
|
* @return MethodDefinition[] |
267
|
|
|
*/ |
268
|
4 |
|
public function getMethodsCollection(): array |
269
|
|
|
{ |
270
|
4 |
|
return $this->methodsCollection; |
271
|
|
|
} |
272
|
|
|
|
273
|
|
|
/** |
274
|
|
|
* Get existing or define new method |
275
|
|
|
* |
276
|
|
|
* @param string $methodName |
277
|
|
|
* @return MethodDefinition |
278
|
|
|
* @throws MethodDefinitionAlreadyExistsException |
279
|
|
|
* @throws MethodDefinitionNotFoundException |
280
|
|
|
*/ |
281
|
2 |
|
public function setupMethod(string $methodName): MethodDefinition |
282
|
|
|
{ |
283
|
|
|
// Get existing method |
284
|
2 |
|
if ($this->hasMethod($methodName)) { |
285
|
2 |
|
return $this->getMethod($methodName); |
286
|
|
|
} else { // Or define new method |
287
|
|
|
return $this->defineMethod($methodName); |
288
|
|
|
} |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
/** |
292
|
|
|
* Get existing or define new property |
293
|
|
|
* |
294
|
|
|
* @param string $propertyName |
295
|
|
|
* @return PropertyDefinition |
296
|
|
|
* @throws PropertyDefinitionNotFoundException |
297
|
|
|
* @throws PropertyDefinitionAlreadyExistsException |
298
|
|
|
*/ |
299
|
2 |
|
public function setupProperty(string $propertyName): PropertyDefinition |
300
|
|
|
{ |
301
|
|
|
// Get existing property |
302
|
2 |
|
if ($this->hasProperty($propertyName)) { |
303
|
2 |
|
return $this->getProperty($propertyName); |
304
|
|
|
} else { // Or define new property |
305
|
|
|
return $this->defineProperty($propertyName); |
306
|
|
|
} |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
/** |
310
|
|
|
* Has property definition |
311
|
|
|
* |
312
|
|
|
* @param string $propertyName |
313
|
|
|
* @return bool |
314
|
|
|
*/ |
315
|
4 |
|
public function hasProperty(string $propertyName): bool |
316
|
|
|
{ |
317
|
4 |
|
return array_key_exists($propertyName, $this->propertiesCollection); |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
/** |
321
|
|
|
* Get property definition |
322
|
|
|
* |
323
|
|
|
* @param $propertyName |
324
|
|
|
* @return PropertyDefinition |
325
|
|
|
* @throws PropertyDefinitionNotFoundException |
326
|
|
|
*/ |
327
|
3 |
|
public function getProperty($propertyName): PropertyDefinition |
328
|
|
|
{ |
329
|
3 |
|
if (!$this->hasProperty($propertyName)) { |
330
|
|
|
throw new PropertyDefinitionNotFoundException(); |
331
|
|
|
} |
332
|
3 |
|
return $this->propertiesCollection[$propertyName]; |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
/** |
336
|
|
|
* Has method definition |
337
|
|
|
* |
338
|
|
|
* @param string $methodName |
339
|
|
|
* @return bool |
340
|
|
|
*/ |
341
|
4 |
|
public function hasMethod(string $methodName): bool |
342
|
|
|
{ |
343
|
4 |
|
return array_key_exists($methodName, $this->methodsCollection); |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
/** |
347
|
|
|
* Get method definition |
348
|
|
|
* |
349
|
|
|
* @param $methodName |
350
|
|
|
* @return MethodDefinition |
351
|
|
|
* @throws MethodDefinitionNotFoundException |
352
|
|
|
*/ |
353
|
4 |
|
public function getMethod($methodName): MethodDefinition |
354
|
|
|
{ |
355
|
4 |
|
if (!$this->hasMethod($methodName)) { |
356
|
|
|
throw new MethodDefinitionNotFoundException(); |
357
|
|
|
} |
358
|
4 |
|
return $this->methodsCollection[$methodName]; |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
/** |
362
|
|
|
* @return boolean |
363
|
|
|
*/ |
364
|
7 |
|
public function isAnalyzed(): bool |
365
|
|
|
{ |
366
|
7 |
|
return $this->isAnalyzed; |
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
/** |
370
|
|
|
* @param boolean $isAnalyzed |
371
|
|
|
* @return ClassDefinition |
372
|
|
|
*/ |
373
|
6 |
|
public function setIsAnalyzed(bool $isAnalyzed): ClassDefinition |
374
|
|
|
{ |
375
|
6 |
|
$this->isAnalyzed = $isAnalyzed; |
376
|
|
|
|
377
|
6 |
|
return $this; |
378
|
|
|
} |
379
|
|
|
} |
380
|
|
|
|
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.