diff --git a/src/Latte/Compiler/TemplateGenerator.php b/src/Latte/Compiler/TemplateGenerator.php index 4f754892c..c522437b3 100644 --- a/src/Latte/Compiler/TemplateGenerator.php +++ b/src/Latte/Compiler/TemplateGenerator.php @@ -11,6 +11,8 @@ use Latte; use Latte\ContentType; +use Latte\Essential\Blueprint; +use Nette\PhpGenerator as Php; /** @@ -38,6 +40,7 @@ public function generate( string $className, ?string $comment = null, bool $strictMode = false, + array $filters = [], ): string { $context = new PrintContext($node->contentType); $scope = $context->getVariableScope(); @@ -78,13 +81,18 @@ public function generate( . ($method['body'] ? "\t\t$method[body]\n" : '') . "\t}"; } + $comment .= "\n@property Filters$className \$filters"; + $comment = str_replace('*/', '* /', $comment); + $comment = str_replace("\n", "\n * ", "/**\n" . trim($comment)) . "\n */\n"; + $code = "generateStub($node, 'Filters' . $className, $filters); $code = PhpHelpers::optimizeEcho($code); $code = PhpHelpers::reformatCode($code); @@ -124,6 +132,39 @@ private function generateBlocks(array $blocks, PrintContext $context): void } + private function generateStub(Node $node, string $className, $filters): string + { + if (!class_exists(Php\ClassType::class)) { + return ''; + } + + $used = []; + (new NodeTraverser)->traverse($node, function (Node $node) use (&$used) { + if ($node instanceof Nodes\Php\FilterNode) { + $used[$node->name->name] = true; + } + }); + + $class = new Php\ClassType($className); + $filters = array_intersect_key($filters, $used); + foreach ($filters as $name => $callback) { + $func = (new Php\Factory)->fromCallable($callback); + $type = Blueprint::printType($func->getReturnType(), $func->isReturnNullable(), null) ?: 'mixed'; + $params = []; + $list = $func->getParameters(); + foreach ($list as $param) { + $variadic = $func->isVariadic() && $param === end($list); + $params[] = (Blueprint::printType($param->getType(), $param->isNullable(), null) ?: 'mixed') + . ($variadic ? '...' : ''); + } + + $class->addComment('@property callable(' . implode(', ', $params) . "): $type \$$name"); + } + + return (string) $class; + } + + /** * @param Nodes\Php\ParameterNode[] $params */ diff --git a/src/Latte/Engine.php b/src/Latte/Engine.php index 3a2bdd7e0..2962b845e 100644 --- a/src/Latte/Engine.php +++ b/src/Latte/Engine.php @@ -191,6 +191,7 @@ public function generate(TemplateNode $node, string $name): string $this->getTemplateClass($name), $comment, $this->strictTypes, + $this->getFilters(), ); } diff --git a/src/Latte/Essential/Blueprint.php b/src/Latte/Essential/Blueprint.php index 51622e7a0..19914417b 100644 --- a/src/Latte/Essential/Blueprint.php +++ b/src/Latte/Essential/Blueprint.php @@ -95,7 +95,7 @@ public function addFunctions(Php\ClassType $class, array $funcs): void } - private function printType(?string $type, bool $nullable, ?Php\PhpNamespace $namespace): string + public static function printType(?string $type, bool $nullable, ?Php\PhpNamespace $namespace): string { if ($type === null) { return ''; @@ -123,7 +123,7 @@ public function printParameters( $list = $function->getParameters(); foreach ($list as $param) { $variadic = $function->isVariadic() && $param === end($list); - $params[] = ltrim($this->printType($param->getType(), $param->isNullable(), $namespace) . ' ') + $params[] = ltrim(self::printType($param->getType(), $param->isNullable(), $namespace) . ' ') . ($param->isReference() ? '&' : '') . ($variadic ? '...' : '') . '$' . $param->getName()