1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* Derafu: Biblioteca PHP (Núcleo). |
7
|
|
|
* Copyright (C) Derafu <https://www.derafu.org> |
8
|
|
|
* |
9
|
|
|
* Este programa es software libre: usted puede redistribuirlo y/o modificarlo |
10
|
|
|
* bajo los términos de la Licencia Pública General Affero de GNU publicada por |
11
|
|
|
* la Fundación para el Software Libre, ya sea la versión 3 de la Licencia, o |
12
|
|
|
* (a su elección) cualquier versión posterior de la misma. |
13
|
|
|
* |
14
|
|
|
* Este programa se distribuye con la esperanza de que sea útil, pero SIN |
15
|
|
|
* GARANTÍA ALGUNA; ni siquiera la garantía implícita MERCANTIL o de APTITUD |
16
|
|
|
* PARA UN PROPÓSITO DETERMINADO. Consulte los detalles de la Licencia Pública |
17
|
|
|
* General Affero de GNU para obtener una información más detallada. |
18
|
|
|
* |
19
|
|
|
* Debería haber recibido una copia de la Licencia Pública General Affero de GNU |
20
|
|
|
* junto a este programa. |
21
|
|
|
* |
22
|
|
|
* En caso contrario, consulte <http://www.gnu.org/licenses/agpl.html>. |
23
|
|
|
*/ |
24
|
|
|
|
25
|
|
|
namespace Derafu\Lib\Core\Foundation; |
26
|
|
|
|
27
|
|
|
use Derafu\Lib\Core\Foundation\Contract\ServiceInterface; |
28
|
|
|
use Derafu\Lib\Core\Helper\Str; |
29
|
|
|
use LogicException; |
30
|
|
|
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; |
31
|
|
|
use Symfony\Component\DependencyInjection\ContainerBuilder; |
32
|
|
|
use Symfony\Component\DependencyInjection\Definition; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* Clase para modificar servicios durante la compilación. |
36
|
|
|
*/ |
37
|
|
|
class CompilerPass implements CompilerPassInterface |
38
|
|
|
{ |
39
|
|
|
/** |
40
|
|
|
* Prefijo con el que deben ser nombrados todos los servicios asociados a la |
41
|
|
|
* aplicación. |
42
|
|
|
* |
43
|
|
|
* Esto se utiliza especialmente al nombrar paquetes, componentes y workers. |
44
|
|
|
* |
45
|
|
|
* @var string |
46
|
|
|
*/ |
47
|
|
|
protected string $servicesPrefix; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* Patrones de búsqueda de clases de servicios de Foundation. |
51
|
|
|
* |
52
|
|
|
* Estas clases deben implementar además la interfaz ServiceInterface. |
53
|
|
|
* |
54
|
|
|
* @var array<string, string> |
55
|
|
|
*/ |
56
|
|
|
protected array $servicesPatterns = [ |
57
|
|
|
// Strategies. |
58
|
|
|
'strategy' => "/\\\\Package\\\\([A-Za-z0-9_]+)\\\\Component\\\\([A-Za-z0-9_]+)\\\\Worker\\\\([A-Za-z0-9_]+)\\\\([A-Za-z0-9_]+)\\\\([A-Za-z0-9_]+)Strategy$/", |
59
|
|
|
// Workers. |
60
|
|
|
'worker' => "/\\\\Package\\\\([A-Za-z0-9_]+)\\\\Component\\\\([A-Za-z0-9_]+)\\\\Contract\\\\([A-Za-z0-9]+)WorkerInterface$/", |
61
|
|
|
// Components. |
62
|
|
|
'component' => "/\\\\Package\\\\([A-Za-z0-9_]+)\\\\Component\\\\([A-Za-z0-9_]+)\\\\Contract\\\\(?:[A-Z][a-zA-Z0-9]+)ComponentInterface$/", |
63
|
|
|
// Packages. |
64
|
|
|
'package' => "/\\\\Package\\\\([A-Za-z0-9_]+)\\\\Contract\\\\(?:[A-Z][a-zA-Z0-9]+)PackageInterface$/", |
65
|
|
|
]; |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Constructor de la clase. |
69
|
|
|
* |
70
|
|
|
* @param string $servicesPrefix |
71
|
|
|
*/ |
72
|
16 |
|
public function __construct(string $servicesPrefix) |
73
|
|
|
{ |
74
|
16 |
|
$this->servicesPrefix = $servicesPrefix; |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* Procesar servicios en tiempo de compilación. |
79
|
|
|
* |
80
|
|
|
* @param ContainerBuilder $container |
81
|
|
|
* @return void |
82
|
|
|
*/ |
83
|
16 |
|
public function process(ContainerBuilder $container): void |
84
|
|
|
{ |
85
|
16 |
|
foreach ($container->getDefinitions() as $id => $definition) { |
86
|
|
|
// Omitir servicios sintéticos y abstractos. |
87
|
|
|
// - Sintéticos: si el contenedor no lo crea ni lo gestiona. |
88
|
|
|
// - Abstractos: plantilla que otros servicios heredan. |
89
|
|
|
// Solo se procesarán servicios reales (ni sintéticos ni abstractos) |
90
|
|
|
// y que son gestionados directamente por el contenedor. |
91
|
16 |
|
if ($definition->isSynthetic() || $definition->isAbstract()) { |
92
|
16 |
|
continue; |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
// Procesar paquetes, componentes y workers. |
96
|
16 |
|
$this->processFoundationService($id, $definition, $container); |
97
|
|
|
|
98
|
|
|
// Asignar los servicios como lazy. |
99
|
|
|
// Se creará un proxy y se cargará solo al acceder al servicio. Esto |
100
|
|
|
// es cuando se acceda a un método o propiedas (aunque no deberían |
101
|
|
|
// existir propiedades públicas en servicios). No se cargará el |
102
|
|
|
// servicio si solo se inyecta y guarda en el atributo de una clase. |
103
|
16 |
|
$definition->setLazy(true); |
104
|
|
|
} |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* Procesa un servicio registrado en el contenedor. |
109
|
|
|
* |
110
|
|
|
* Este método permite realizar de manera automática: |
111
|
|
|
* |
112
|
|
|
* - Crear alias para paquetes, componentes, workers y estrategias. |
113
|
|
|
* - Agregar un tag a paquetes, componentes, workers y estrategias. |
114
|
|
|
* - Marcar como servicio público los paquetes. |
115
|
|
|
* |
116
|
|
|
* @param string $id |
117
|
|
|
* @param Definition $definition |
118
|
|
|
* @param ContainerBuilder $container |
119
|
|
|
* @return void |
120
|
|
|
*/ |
121
|
16 |
|
private function processFoundationService( |
122
|
|
|
string $id, |
123
|
|
|
Definition $definition, |
124
|
|
|
ContainerBuilder $container |
125
|
|
|
): void { |
126
|
|
|
// Solo se procesan servicios que implementen `ServiceInterface`. |
127
|
|
|
if ( |
128
|
16 |
|
str_contains($id, '.') |
129
|
10 |
|
|| !str_contains($id, '\\') |
130
|
16 |
|
|| !in_array(ServiceInterface::class, (array) class_implements($id)) |
131
|
|
|
) { |
132
|
6 |
|
return; |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
// Revisar si la clase hace match con alguno de los patrones de búsqueda |
136
|
|
|
// de clases de servicios de Foundation. |
137
|
10 |
|
foreach ($this->servicesPatterns as $type => $regex) { |
138
|
10 |
|
if (preg_match($regex, $id, $matches)) { |
139
|
10 |
|
$package = Str::snake($matches[1]); |
140
|
10 |
|
$component = Str::snake($matches[2] ?? ''); |
141
|
10 |
|
$worker = Str::snake($matches[3] ?? ''); |
142
|
10 |
|
$strategyGroup = Str::snake($matches[4] ?? ''); |
143
|
10 |
|
$strategy = Str::snake($matches[5] ?? ''); |
144
|
10 |
|
if ($strategyGroup && $strategy) { |
145
|
|
|
$strategy = $strategyGroup . '.' . $strategy; |
146
|
|
|
} |
147
|
|
|
|
148
|
10 |
|
match($type) { |
149
|
10 |
|
'package' => $this->processServicePackage( |
|
|
|
|
150
|
10 |
|
$id, |
151
|
10 |
|
$definition, |
152
|
10 |
|
$package, |
153
|
10 |
|
$container |
154
|
10 |
|
), |
155
|
10 |
|
'component' => $this->processServiceComponent( |
|
|
|
|
156
|
10 |
|
$id, |
157
|
10 |
|
$definition, |
158
|
10 |
|
$package, |
159
|
10 |
|
$component, |
160
|
10 |
|
$container |
161
|
10 |
|
), |
162
|
10 |
|
'worker' => $this->processServiceWorker( |
|
|
|
|
163
|
10 |
|
$id, |
164
|
10 |
|
$definition, |
165
|
10 |
|
$package, |
166
|
10 |
|
$component, |
167
|
10 |
|
$worker, |
168
|
10 |
|
$container |
169
|
10 |
|
), |
170
|
|
|
'strategy' => $this->processServiceStrategy( |
|
|
|
|
171
|
|
|
$id, |
172
|
|
|
$definition, |
173
|
|
|
$package, |
174
|
|
|
$component, |
175
|
|
|
$worker, |
176
|
|
|
$strategy, |
177
|
|
|
$container |
178
|
|
|
), |
179
|
|
|
default => throw new LogicException(sprintf( |
180
|
|
|
'Tipo de servicio %s no es manejado por CompilerPass::processFoundationService().', |
181
|
|
|
$type |
182
|
|
|
)), |
183
|
10 |
|
}; |
184
|
|
|
} |
185
|
|
|
} |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* Procesa un servicio que representa un paquete. |
190
|
|
|
* |
191
|
|
|
* @param string $serviceId |
192
|
|
|
* @param Definition $definition |
193
|
|
|
* @param string $package |
194
|
|
|
* @param ContainerBuilder $container |
195
|
|
|
* @return void |
196
|
|
|
*/ |
197
|
10 |
|
private function processServicePackage( |
198
|
|
|
string $serviceId, |
199
|
|
|
Definition $definition, |
200
|
|
|
string $package, |
201
|
|
|
ContainerBuilder $container |
202
|
|
|
): void { |
203
|
10 |
|
$aliasId = $this->servicesPrefix . $package; |
204
|
10 |
|
$alias = $container->setAlias($aliasId, $serviceId); |
205
|
10 |
|
$alias->setPublic(true); |
206
|
|
|
|
207
|
10 |
|
$definition->addTag('package', [ |
208
|
10 |
|
'name' => $package, |
209
|
10 |
|
]); |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* Procesa un servicio que representa un componente. |
214
|
|
|
* |
215
|
|
|
* @param string $serviceId |
216
|
|
|
* @param Definition $definition |
217
|
|
|
* @param string $package |
218
|
|
|
* @param string $component |
219
|
|
|
* @param ContainerBuilder $container |
220
|
|
|
* @return void |
221
|
|
|
*/ |
222
|
10 |
|
private function processServiceComponent( |
223
|
|
|
string $serviceId, |
224
|
|
|
Definition $definition, |
225
|
|
|
string $package, |
226
|
|
|
string $component, |
227
|
|
|
ContainerBuilder $container |
228
|
|
|
): void { |
229
|
10 |
|
$aliasId = $this->servicesPrefix . $package . '.' . $component; |
230
|
10 |
|
$alias = $container->setAlias($aliasId, $serviceId); |
|
|
|
|
231
|
|
|
|
232
|
10 |
|
$definition->addTag($package . '.component', [ |
233
|
10 |
|
'name' => $component, |
234
|
10 |
|
]); |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
/** |
238
|
|
|
* Procesa un servicio que representa un worker. |
239
|
|
|
* |
240
|
|
|
* @param string $serviceId |
241
|
|
|
* @param Definition $definition |
242
|
|
|
* @param string $package |
243
|
|
|
* @param string $component |
244
|
|
|
* @param string $worker |
245
|
|
|
* @param ContainerBuilder $container |
246
|
|
|
* @return void |
247
|
|
|
*/ |
248
|
10 |
|
private function processServiceWorker( |
249
|
|
|
string $serviceId, |
250
|
|
|
Definition $definition, |
251
|
|
|
string $package, |
252
|
|
|
string $component, |
253
|
|
|
string $worker, |
254
|
|
|
ContainerBuilder $container |
255
|
|
|
): void { |
256
|
10 |
|
$aliasId = $this->servicesPrefix . $package . '.' . $component . '.' . $worker; |
257
|
10 |
|
$alias = $container->setAlias($aliasId, $serviceId); |
|
|
|
|
258
|
|
|
|
259
|
10 |
|
$definition->addTag($package . '.' . $component . '.worker', [ |
260
|
10 |
|
'name' => $worker, |
261
|
10 |
|
]); |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
/** |
265
|
|
|
* Procesa un servicio que representa un worker. |
266
|
|
|
* |
267
|
|
|
* @param string $serviceId |
268
|
|
|
* @param Definition $definition |
269
|
|
|
* @param string $package |
270
|
|
|
* @param string $component |
271
|
|
|
* @param string $worker |
272
|
|
|
* @param string $strategy |
273
|
|
|
* @param ContainerBuilder $container |
274
|
|
|
* @return void |
275
|
|
|
*/ |
276
|
|
|
private function processServiceStrategy( |
277
|
|
|
string $serviceId, |
278
|
|
|
Definition $definition, |
279
|
|
|
string $package, |
280
|
|
|
string $component, |
281
|
|
|
string $worker, |
282
|
|
|
string $strategy, |
283
|
|
|
ContainerBuilder $container |
284
|
|
|
): void { |
285
|
|
|
$aliasId = $this->servicesPrefix . $package . '.' . $component . '.' . $worker . '.' . $strategy; |
286
|
|
|
$alias = $container->setAlias($aliasId, $serviceId); |
|
|
|
|
287
|
|
|
|
288
|
|
|
$definition->addTag($package . '.' . $component . '.' . $worker . '.strategy', [ |
289
|
|
|
'name' => $strategy, |
290
|
|
|
]); |
291
|
|
|
} |
292
|
|
|
} |
293
|
|
|
|
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.