Skip to content

Commit d57f0fc

Browse files
falkenhawkpartikus
authored andcommitted
Optimize compiled factory definitions
- compile final function with injected dependencies - only for closure factories for now (as the first step)
1 parent 8097948 commit d57f0fc

File tree

1 file changed

+78
-2
lines changed

1 file changed

+78
-2
lines changed

src/Compiler/Compiler.php

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace DI\Compiler;
66

77
use function chmod;
8+
use DI\Container;
89
use DI\Definition\ArrayDefinition;
910
use DI\Definition\DecoratorDefinition;
1011
use DI\Definition\Definition;
@@ -18,10 +19,12 @@
1819
use DI\Definition\ValueDefinition;
1920
use DI\DependencyException;
2021
use DI\Proxy\ProxyFactory;
21-
use function dirname;
22-
use function file_put_contents;
2322
use InvalidArgumentException;
2423
use Laravel\SerializableClosure\Support\ReflectionClosure;
24+
use ReflectionFunction;
25+
use ReflectionFunctionAbstract;
26+
use function dirname;
27+
use function file_put_contents;
2528
use function rename;
2629
use function sprintf;
2730
use function tempnam;
@@ -276,6 +279,35 @@ private function compileDefinition(string $entryName, Definition $definition) :
276279
));
277280
}
278281

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
279311
$definitionParameters = '';
280312
if (!empty($definition->getParameters())) {
281313
$definitionParameters = ', ' . $this->compileValue($definition->getParameters());
@@ -307,6 +339,11 @@ public function compileValue(mixed $value) : string
307339
throw new InvalidDefinition($errorMessage);
308340
}
309341

342+
// one step ahead to skip CompiledContainer->resolveFactory
343+
if ($value instanceof RequestedEntryHolder) {
344+
return 'new DI\Compiler\RequestedEntryHolder(\'' . $value->getName() . '\')';
345+
}
346+
310347
if ($value instanceof Definition) {
311348
// Give it an arbitrary unique name
312349
$subEntryName = 'subEntry' . (++$this->subEntryCounter);
@@ -364,6 +401,10 @@ private function isCompilable($value) : string|bool
364401
if ($value instanceof \Closure) {
365402
return true;
366403
}
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+
}
367408
/** @psalm-suppress UndefinedClass */
368409
if ((\PHP_VERSION_ID >= 80100) && ($value instanceof \UnitEnum)) {
369410
return true;
@@ -399,4 +440,39 @@ private function compileClosure(\Closure $closure) : string
399440

400441
return trim($code, "\t\n\r;");
401442
}
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+
}
402478
}

0 commit comments

Comments
 (0)