diff --git a/src/DI/Definitions/ServiceDefinition.php b/src/DI/Definitions/ServiceDefinition.php index 6cec76a86..2eccce494 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,32 @@ private function prependSelf(Statement $setup): Statement public function generateMethod(Nette\PhpGenerator\Method $method, Nette\DI\PhpGenerator $generator): void { - $code = $generator->formatStatement($this->creator) . ";\n"; - if (!$this->setup) { - $method->setBody('return ' . $code); - return; + $lines = []; + foreach ([$this->creator, ...$this->setup] as $stmt) { + $lines[] = $generator->formatStatement($stmt) . ";\n"; } - $code = '$service = ' . $code; - foreach ($this->setup as $setup) { - $code .= $generator->formatStatement($setup) . ";\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]) + : ''; + $code = "return new ReflectionClass($class::class)->newLazyGhost(function (\$service) {\n" + . Strings::indent(implode('', $lines)) + . '});'; + } elseif (count($lines) === 1) { + $code = 'return ' . $lines[0]; + } else { + $code = '$service = ' . implode('', $lines) . 'return $service;'; } - $code .= 'return $service;'; $method->setBody($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; + } }