/** * @param Node $node * @param string $name * @param string $parent * @param string[] $includes * @param string[] $filters */ function __construct(Node $node, $name = NULL, $parent = NULL, array $includes = NULL, array $filters = NULL) { $this->m_name = $name != NULL ? $name : $node->name(); $this->m_root = $node; if ($includes) { $this->m_parent = $parent; $this->m_includes = $includes; $this->m_filters = $filters; } else { // check first child for inheritance if (($firstChild = $node->firstToken()) && $firstChild->type() === TokenType::T_INHERIT) { $this->m_parent = $firstChild->name(); } // include dependency names $this->m_includes = array_map('strval', iterator_to_array($node->GetChildrenByType(TokenType::T_INCLUDE, true))); /** @var EvalNameToken $token */ $this->m_filters = []; foreach ($node->GetChildrenByClass(EvalNameToken::class, true) as $token) { foreach ($token->filters() as $filter) { $this->m_filters[$filter] = $filter; } } } }
/** * @param Node $node * @param array $definitions */ public function replaceContents($node, array $definitions = NULL) { // remove existing children foreach ($this->m_children as $child) { if ($child instanceof self) { $child->m_parent = NULL; } } $this->m_children = []; // add children from other node foreach ($node->m_children as $child) { if ($child instanceof IToken) { if ($child instanceof self) { $newChild = new self($child->m_token, $this); $newChild->replaceContents($child, $definitions); $child = $newChild; } else { if ($definitions != NULL && $child->type() === TokenType::T_VARIABLE && isset($definitions[$child->name()])) { // replace this token/node with a new node $def = $definitions[$child->name()]; $token = $child instanceof self ? $child->m_token : $child; $child = new Node($token, $this); $child->replaceContents($def); } } } $this->m_children[] = $child; } }
private static function parse(Generator $files) { $templates = []; /** @var Node[] $stack */ $stack = []; /** @var Node $node */ $node = NULL; foreach ($files as $tokens) { foreach ($tokens as $token) { if (is_string($token)) { if ($node !== NULL) { $node->addChild($token); } /*else { if (!$token instanceof TextToken) { // something other than whitespace/template at the root level } }*/ } else { if (is_int($token)) { if ($token === TokenType::T_CLOSE) { $type = $node->type(); if ($type === TokenType::T_TEMPLATE || $type === TokenType::T_SOURCE) { // add to template list $name = $node->name(); if ($type === TokenType::T_SOURCE) { // generate name for "anonymous" template (there will be something on the stack for this type) $parent = end($stack); while ($parent->parent()) { $parent = $parent->parent(); } $templateParentPrefix = $parent->name(); $name = TokenType::T_ANONYMOUS_TEMPLATE_PREFIX . $templateParentPrefix . TokenType::T_ANONYMOUS_TEMPLATE_DELIMITER . $name; } if (isset($templates[$name])) { throw new \Exception("Duplicate template definition: `{$name}`"); } $templates[$name] = new Template($node, $name); // templates don't have a parent, so check the nesting stack if ($parent = array_pop($stack)) { // this is a nested template (replace with include token) $includeToken = new NameToken(TokenType::T_INCLUDE, $name); $parent->AddChild($includeToken); } $node = $parent; } else { $node = $node->parent(); } } } else { if ($token instanceof IToken) { $type = $token->type(); if ($type === TokenType::T_TEMPLATE || $type === TokenType::T_SOURCE) { if ($node != NULL) { // save current node if this is a nested definition $stack[] = $node; } else { if ($type != TokenType::T_TEMPLATE) { throw new \Exception('Only explicit templates allowed at root level'); } } // start new template node (with no parent) $node = new Node($token); } else { if (!TokenType::void($type)) { if ($node != NULL) { // this is not a self-closing tag, so start a new node now $child = new Node($token, $node); $node->addChild($child); $node = $child; } } else { if ($node != NULL) { $node->addChild($token); } } } } } } } } return $templates; }