|
5 | 5 | namespace DI\Compiler; |
6 | 6 |
|
7 | 7 | use function chmod; |
| 8 | +use DI\Container; |
8 | 9 | use DI\Definition\ArrayDefinition; |
9 | 10 | use DI\Definition\DecoratorDefinition; |
10 | 11 | use DI\Definition\Definition; |
|
18 | 19 | use DI\Definition\ValueDefinition; |
19 | 20 | use DI\DependencyException; |
20 | 21 | use DI\Proxy\ProxyFactory; |
21 | | -use function dirname; |
22 | | -use function file_put_contents; |
23 | 22 | use InvalidArgumentException; |
24 | 23 | use Laravel\SerializableClosure\Support\ReflectionClosure; |
| 24 | +use ReflectionFunction; |
| 25 | +use ReflectionFunctionAbstract; |
| 26 | +use function dirname; |
| 27 | +use function file_put_contents; |
25 | 28 | use function rename; |
26 | 29 | use function sprintf; |
27 | 30 | use function tempnam; |
@@ -276,6 +279,35 @@ private function compileDefinition(string $entryName, Definition $definition) : |
276 | 279 | )); |
277 | 280 | } |
278 | 281 |
|
| 282 | + if ($value instanceof \Closure) { |
| 283 | + $reflection = new ReflectionFunction($value); |
| 284 | + $requestedEntry = new RequestedEntryHolder($entryName); |
| 285 | + $parametersByClassName = [ |
| 286 | + 'DI\Factory\RequestedEntry' => $requestedEntry, |
| 287 | + ]; |
| 288 | + // default non-typehinted parameters |
| 289 | + $defaultParameters = [new Reference(Container::class), $requestedEntry]; |
| 290 | + |
| 291 | + $resolvedParameters = $this->resolveFactoryParameters( |
| 292 | + $reflection, |
| 293 | + $definition->getParameters(), |
| 294 | + $parametersByClassName, |
| 295 | + $defaultParameters |
| 296 | + ); |
| 297 | + |
| 298 | + $definitionParameters = array_map(function ($value) { |
| 299 | + return $this->compileValue($value); |
| 300 | + }, $resolvedParameters); |
| 301 | + |
| 302 | + $code = sprintf( |
| 303 | + 'return (%s)(%s);', |
| 304 | + $this->compileValue($value), |
| 305 | + implode(', ', $definitionParameters) |
| 306 | + ); |
| 307 | + break; |
| 308 | + } |
| 309 | + |
| 310 | + // todo optimize other (non-closure) factories |
279 | 311 | $definitionParameters = ''; |
280 | 312 | if (!empty($definition->getParameters())) { |
281 | 313 | $definitionParameters = ', ' . $this->compileValue($definition->getParameters()); |
@@ -307,6 +339,11 @@ public function compileValue(mixed $value) : string |
307 | 339 | throw new InvalidDefinition($errorMessage); |
308 | 340 | } |
309 | 341 |
|
| 342 | + // one step ahead to skip CompiledContainer->resolveFactory |
| 343 | + if ($value instanceof RequestedEntryHolder) { |
| 344 | + return 'new DI\Compiler\RequestedEntryHolder(\'' . $value->getName() . '\')'; |
| 345 | + } |
| 346 | + |
310 | 347 | if ($value instanceof Definition) { |
311 | 348 | // Give it an arbitrary unique name |
312 | 349 | $subEntryName = 'subEntry' . (++$this->subEntryCounter); |
@@ -364,6 +401,10 @@ private function isCompilable($value) : string|bool |
364 | 401 | if ($value instanceof \Closure) { |
365 | 402 | return true; |
366 | 403 | } |
| 404 | + // added for skipping CompiledContainer->resolveFactory - there is a special case for this in compileValue method |
| 405 | + if ($value instanceof RequestedEntryHolder) { |
| 406 | + return true; |
| 407 | + } |
367 | 408 | /** @psalm-suppress UndefinedClass */ |
368 | 409 | if ((\PHP_VERSION_ID >= 80100) && ($value instanceof \UnitEnum)) { |
369 | 410 | return true; |
@@ -399,4 +440,39 @@ private function compileClosure(\Closure $closure) : string |
399 | 440 |
|
400 | 441 | return trim($code, "\t\n\r;"); |
401 | 442 | } |
| 443 | + |
| 444 | + public function resolveFactoryParameters( |
| 445 | + ReflectionFunctionAbstract $reflection, |
| 446 | + array $definitionParameters = [], |
| 447 | + array $parametersByClassName = [], |
| 448 | + array $defaultParameters = [] |
| 449 | + ) { |
| 450 | + $resolvedParameters = []; |
| 451 | + $parameters = $reflection->getParameters(); |
| 452 | + |
| 453 | + foreach ($parameters as $index => $parameter) { |
| 454 | + $name = $parameter->getName(); |
| 455 | + if (array_key_exists($name, $definitionParameters)) { |
| 456 | + $resolvedParameters[$index] = $definitionParameters[$name]; |
| 457 | + continue; |
| 458 | + } |
| 459 | + |
| 460 | + $parameterClass = $parameter->getClass(); |
| 461 | + if (!$parameterClass) { |
| 462 | + if (array_key_exists($index, $defaultParameters)) { |
| 463 | + // take default parameters, when no typehint |
| 464 | + $resolvedParameters[$index] = $defaultParameters[$index]; |
| 465 | + } |
| 466 | + continue; |
| 467 | + } |
| 468 | + |
| 469 | + if (isset($parametersByClassName[$parameterClass->name])) { |
| 470 | + $resolvedParameters[$index] = $parametersByClassName[$parameterClass->name]; |
| 471 | + } else { |
| 472 | + $resolvedParameters[$index] = new Reference($parameterClass->name); |
| 473 | + } |
| 474 | + } |
| 475 | + |
| 476 | + return $resolvedParameters; |
| 477 | + } |
402 | 478 | } |
0 commit comments