From f049f5bf0788f406baadba9d543555f720efdf07 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 25 Nov 2024 11:38:08 +0100 Subject: [PATCH] creating lazy services in PHP 8.4 WIP --- src/DI/Definitions/ServiceDefinition.php | 36 +++++++++++++++++------ src/DI/Extensions/DIExtension.php | 15 ++++++++++ src/DI/Extensions/DefinitionSchema.php | 1 + src/DI/Extensions/ServicesExtension.php | 4 +++ tests/DI/Compiler.loadConfig.include.phpt | 1 + 5 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/DI/Definitions/ServiceDefinition.php b/src/DI/Definitions/ServiceDefinition.php index 0546b54b3..9b26b2871 100644 --- a/src/DI/Definitions/ServiceDefinition.php +++ b/src/DI/Definitions/ServiceDefinition.php @@ -11,6 +11,7 @@ use Nette; use Nette\DI\ServiceCreationException; +use Nette\Utils\Strings; /** @@ -24,6 +25,8 @@ final class ServiceDefinition extends Definition { use Nette\SmartObject; + public ?bool $lazy = null; + private Statement $creator; /** @var Statement[] */ @@ -181,18 +184,33 @@ private function prependSelf(Statement $setup): Statement public function generateCode(Nette\DI\PhpGenerator $generator): string { - $code = $this->creator->generateCode($generator) . ";\n"; - if (!$this->setup) { - return 'return ' . $code; + $lines = []; + foreach ([$this->creator, ...$this->setup] as $stmt) { + $lines[] = $stmt->generateCode($generator) . ";\n"; } - $code = '$service = ' . $code; - foreach ($this->setup as $setup) { - $code .= $setup->generateCode($generator) . ";\n"; + $class = $this->creator->getEntity(); + $lazy = PHP_VERSION_ID >= 80400 + && $this->lazy + && is_string($class) + && ($ancestor = ($tmp = class_parents($class)) ? array_pop($tmp) : $class) + && !(new \ReflectionClass($ancestor))->isInternal() + && !preg_grep('#(?:func_get_arg|func_num_args)#i', $lines); // latteFactory workaround + + if ($lazy) { + $lines[0] = (new \ReflectionClass($class))->hasMethod('__construct') + ? $generator->formatPhp("\$service->__construct(...?:);\n", [$this->creator->arguments]) + : ''; + return "return new ReflectionClass($class::class)->newLazyGhost(function (\$service) {\n" + . Strings::indent(implode('', $lines)) + . '});'; + + } elseif (count($lines) === 1) { + return 'return ' . $lines[0]; + + } else { + return '$service = ' . implode('', $lines) . 'return $service;'; } - - $code .= 'return $service;'; - return $code; } diff --git a/src/DI/Extensions/DIExtension.php b/src/DI/Extensions/DIExtension.php index 5b789cffa..ce17727d4 100644 --- a/src/DI/Extensions/DIExtension.php +++ b/src/DI/Extensions/DIExtension.php @@ -10,6 +10,7 @@ namespace Nette\DI\Extensions; use Nette; +use Nette\DI\Definitions\ServiceDefinition; use Tracy; @@ -36,6 +37,7 @@ public function __construct(bool $debugMode = false) public array $excluded = []; public ?string $parentClass = null; public object $export; + public bool $lazy = false; }; $this->config->export = new class { public bool $parameters = true; @@ -56,6 +58,19 @@ public function loadConfiguration(): void } + public function beforeCompile(): void + { + if ($this->config->lazy) { + $builder = $this->getContainerBuilder(); + foreach ($builder->getDefinitions() as $def) { + if ($def instanceof ServiceDefinition) { + $def->lazy ??= true; + } + } + } + } + + public function afterCompile(Nette\PhpGenerator\ClassType $class): void { if ($this->config->parentClass) { diff --git a/src/DI/Extensions/DefinitionSchema.php b/src/DI/Extensions/DefinitionSchema.php index a49ab3faa..487ddecae 100644 --- a/src/DI/Extensions/DefinitionSchema.php +++ b/src/DI/Extensions/DefinitionSchema.php @@ -173,6 +173,7 @@ private static function getServiceSchema(): Schema 'tags' => Expect::array(), 'reset' => Expect::array(), 'alteration' => Expect::bool(), + 'lazy' => Expect::bool(), ]); } diff --git a/src/DI/Extensions/ServicesExtension.php b/src/DI/Extensions/ServicesExtension.php index 8bb50257d..075cf54a3 100644 --- a/src/DI/Extensions/ServicesExtension.php +++ b/src/DI/Extensions/ServicesExtension.php @@ -113,6 +113,10 @@ private function updateServiceDefinition(Definitions\ServiceDefinition $definiti if (isset($config->inject)) { $definition->addTag(InjectExtension::TagInject, $config->inject); } + + if (isset($config->lazy)) { + $definition->lazy = $config->lazy; + } } diff --git a/tests/DI/Compiler.loadConfig.include.phpt b/tests/DI/Compiler.loadConfig.include.phpt index 26d00f815..590afe064 100644 --- a/tests/DI/Compiler.loadConfig.include.phpt +++ b/tests/DI/Compiler.loadConfig.include.phpt @@ -49,6 +49,7 @@ Assert::equal([ 'tags' => [], 'reset' => [], 'alteration' => null, + 'lazy' => null, 'defType' => Nette\DI\Definitions\ServiceDefinition::class, ], ],