From f22e1188d305a5c4a9fb8103f7f10b68205d3b5c Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 4 May 2022 00:42:13 +0200 Subject: [PATCH] order wip --- src/Latte/Compiler/TemplateParser.php | 66 +++++++++++++++++++---- src/Latte/Compiler/TemplateParserHtml.php | 10 ++-- src/Latte/Engine.php | 10 ++-- src/Latte/Extension.php | 12 +++++ src/Latte/Sandbox/SandboxExtension.php | 2 +- 5 files changed, 78 insertions(+), 22 deletions(-) diff --git a/src/Latte/Compiler/TemplateParser.php b/src/Latte/Compiler/TemplateParser.php index 2da251c359..9f194b98f4 100644 --- a/src/Latte/Compiler/TemplateParser.php +++ b/src/Latte/Compiler/TemplateParser.php @@ -37,8 +37,8 @@ final class TemplateParser /** @var array */ private array $tagParsers = []; - /** @var array */ - private array $attrParsers = []; + /** @var array */ + private array $attrParsersInfo = []; private TemplateParserHtml $html; private ?TokenStream $stream = null; @@ -50,18 +50,17 @@ final class TemplateParser private $lastResolver; - /** @param array $parsers */ + /** @param array $parsers */ public function addTags(array $parsers): static { - foreach ($parsers as $name => $parser) { + foreach ($parsers as $name => $info) { + $info = $info instanceof \stdClass ? $info : (object) ['parser' => $info]; if (str_starts_with($name, 'n:')) { - $this->attrParsers[substr($name, 2)] = $parser; + $this->attrParsersInfo[substr($name, 2)] = $info; } else { - $this->tagParsers[$name] = $parser; - if (Helpers::toReflection($parser)->isGenerator()) { - $this->attrParsers[$name] = $parser; - $this->attrParsers[Tag::PrefixInner . '-' . $name] = $parser; - $this->attrParsers[Tag::PrefixTag . '-' . $name] = $parser; + $this->tagParsers[$name] = $info->parser; + if ($info->generator = Helpers::toReflection($info->parser)->isGenerator()) { + $this->attrParsersInfo[$name] = $info; } } } @@ -77,7 +76,7 @@ public function addTags(array $parsers): static public function parse(string $template, TemplateLexer $lexer): Nodes\TemplateNode { $this->lexer = $lexer; - $this->html = new TemplateParserHtml($this, $this->attrParsers); + $this->html = new TemplateParserHtml($this, $this->sortAttrParsers()); $this->stream = new TokenStream($lexer->tokenize($template, $this->contentType)); $headLength = 0; @@ -320,6 +319,51 @@ private function getTagParser(string $name, Position $pos): callable } + private function sortAttrParsers(): array + { + $list = $this->attrParsersInfo; + + foreach ($list as $name => $info) { + if (!($info->before ?? $info->after ?? null)) { + continue; + } + + unset($list[$name]); + $names = array_keys($list); + $best = null; + + foreach ((array) $info->after as $target) { + if (isset($list[$target])) { + $pos = array_search($target, $names, true); + $best = min($pos - 1, $best ?? $pos); + } + } + + foreach ((array) ($info->before ?? null) as $target) { + if (isset($list[$target])) { + $pos = array_search($target, $names, true); + $best = max($pos + 1, $best); + } + } + + $list = array_slice($list, 0, $best, true) + + [$name => $info] + + array_slice($list, $best, null, true); + } + + $parsers = []; + foreach ($list as $name => $info) { + $parsers[$name] = $info->parser; + if ($info->generator ?? false) { + $parsers[Tag::PrefixInner . '-' . $name] = $info->parser; + $parsers[Tag::PrefixTag . '-' . $name] = $info->parser; + } + } + + return $parsers; + } + + private function checkEndTag(Tag $start, ?Tag $end): void { if (!$end) { diff --git a/src/Latte/Compiler/TemplateParserHtml.php b/src/Latte/Compiler/TemplateParserHtml.php index 3ee00a8865..41c6220cbc 100644 --- a/src/Latte/Compiler/TemplateParserHtml.php +++ b/src/Latte/Compiler/TemplateParserHtml.php @@ -85,7 +85,7 @@ private function parseElement(): Node $stream = $this->parser->getStream(); $void = $this->resolveVoidness($elem); - $attrs = $this->prepareNAttributes($elem->nAttributes, $void); + $attrs = $this->prepareNAttrs($elem->nAttributes, $void); $outerNodes = $this->openNAttrNodes($attrs[Tag::PrefixNone] ?? []); $tagNodes = $this->openNAttrNodes($attrs[Tag::PrefixTag] ?? []); $elem->tagNode = $this->finishNAttrNodes($elem->tagNode, $tagNodes); @@ -412,10 +412,10 @@ private function isClosingTag(string $name): bool } - private function prepareNAttributes(array $attrs, bool $void): array + private function prepareNAttrs(array $attrs, bool $void): array { $res = []; - foreach ($this->attrParsers as $name => $parser) { + foreach ($this->attrParsers as $name => $foo) { if ($tag = $attrs[$name] ?? null) { $prefix = substr($name, 0, (int) strpos($name, '-')); if (!$prefix || !$void) { @@ -443,7 +443,7 @@ private function openNAttrNodes(array $toOpen): array { $toClose = []; foreach ($toOpen as $tag) { - $parser = $this->getTagParser($tag->name, $tag->position); + $parser = $this->getAttrParser($tag->name, $tag->position); $this->parser->pushTag($tag); $res = $parser($tag, $this->parser); if ($res instanceof \Generator && $res->valid()) { @@ -484,7 +484,7 @@ private function finishNAttrNodes(AreaNode $node, array $toClose): AreaNode /** @return callable(Tag, TemplateParser): (Node|\Generator|void) */ - private function getTagParser(string $name, Position $pos): callable + private function getAttrParser(string $name, Position $pos): callable { if (!isset($this->attrParsers[$name])) { $hint = ($t = Helpers::getSuggestion(array_keys($this->attrParsers), $name)) diff --git a/src/Latte/Engine.php b/src/Latte/Engine.php index 5177692c28..c5a127fefb 100644 --- a/src/Latte/Engine.php +++ b/src/Latte/Engine.php @@ -132,8 +132,8 @@ public function compile(string $name): string foreach ($this->extensions as $extension) { $extension->beforeCompile($this); $parser->addTags($extension->getTags()); - foreach ($extension->getPasses() as $priority => $pass) { - $passes[] = [$pass, $priority]; + foreach ($extension->getPasses() as $pass) { + $passes[] = $pass instanceof \stdClass ? $pass : (object) ['pass' => $pass]; } } @@ -148,9 +148,9 @@ public function compile(string $name): string $context->setContentType($parser->getContentType()); - usort($passes, fn($a, $b) => $a[1] <=> $b[1]); - foreach ($passes as [$pass]) { - $pass($node, $context); + usort($passes, fn($a, $b) => ($a->order ?? 0) <=> ($b->order ?? 0)); + foreach ($passes as $pass) { + ($pass->pass)($node, $context); } $code = $generator->generate( diff --git a/src/Latte/Extension.php b/src/Latte/Extension.php index f99cd01acd..e419bbd0c3 100644 --- a/src/Latte/Extension.php +++ b/src/Latte/Extension.php @@ -79,4 +79,16 @@ public function getProviders(): array public function beforeRender(Engine $engine): void { } + + + public static function tag($parser, array|string $before = [], array|string $after = []): \stdClass + { + return (object) get_defined_vars(); + } + + + public static function pass($pass, int $order): \stdClass + { + return (object) get_defined_vars(); + } } diff --git a/src/Latte/Sandbox/SandboxExtension.php b/src/Latte/Sandbox/SandboxExtension.php index c162d65524..bbaa2e20b3 100644 --- a/src/Latte/Sandbox/SandboxExtension.php +++ b/src/Latte/Sandbox/SandboxExtension.php @@ -43,7 +43,7 @@ public function getTags(): array public function getPasses(): array { return $this->policy - ? [-10 => [$this, 'processPass']] + ? [self::pass([$this, 'processPass'], order: -10)] : []; }