function macro($macro, $content = '', $modifiers = '') { if (func_num_args() === 1) { list(, $macro, $content, $modifiers) = NString::match($macro, '#^(/?[a-z0-9.:]+)?(.*?)(\\|[a-z](?:' . NLatteFilter::RE_STRING . '|[^\'"]+)*)?$()#is'); $content = trim($content); } if ($macro === '') { $macro = substr($content, 0, 2); if (!isset($this->macros[$macro])) { $macro = substr($content, 0, 1); if (!isset($this->macros[$macro])) { return FALSE; } } $content = substr($content, strlen($macro)); } elseif (!isset($this->macros[$macro])) { return FALSE; } $closing = $macro[0] === '/'; if ($closing) { $node = array_pop($this->nodes); if (!$node || "/{$node->name}" !== $macro || $content && !NString::startsWith("{$node->content} ", "{$content} ") || $modifiers) { $macro .= $content ? ' ' : ''; throw new NLatteException("Unexpected macro {{$macro}{$content}{$modifiers}}" . ($node ? ", expecting {/{$node->name}}" . ($content && $node->content ? " or eventually {/{$node->name} {$node->content}}" : '') : ''), 0, $this->filter->line); } $node->content = $node->modifiers = ''; } else { $node = (object) NULL; $node->name = $macro; $node->content = $content; $node->modifiers = $modifiers; if (isset($this->macros["/{$macro}"])) { $this->nodes[] = $node; } } $This = $this; return NString::replace($this->macros[$macro], '#%(.*?)%#', callback(create_function('$m', 'extract(NClosureFix::$vars[' . NClosureFix::uses(array('This' => $This, 'node' => $node)) . '], EXTR_REFS); if ($m[1]) { return callback($m[1][0] === \':\' ? array($This, substr($m[1], 1)) : $m[1]) ->invoke($node->content, $node->modifiers); } else { return $This->formatMacroArgs($node->content); } '))); }
private function contextTag() { $matches = $this->match('~ (?P<end>\\ ?/?>)(?P<tagnewline>[\\ \\t]*(?=\\r|\\n))?| ## end of HTML tag ' . $this->macroRe . '| ## curly tag \\s*(?P<attr>[^\\s/>={]+)(?:\\s*=\\s*(?P<value>["\']|[^\\s/>{]+))? ## begin of HTML attribute ~xsi'); if (!$matches || !empty($matches['macro'])) { } elseif (!empty($matches['end'])) { $tag = end($this->tags); $isEmpty = !$tag->closing && (strpos($matches['end'], '/') !== FALSE || isset(NHtml::$emptyElements[strtolower($tag->name)])); if ($isEmpty) { $matches[0] = (NHtml::$xhtml ? ' />' : '>') . (isset($matches['tagnewline']) ? $matches['tagnewline'] : ''); } if ($tag->isMacro || !empty($tag->attrs)) { if ($tag->isMacro) { $code = $this->handler->tagMacro(substr($tag->name, strlen(self::HTML_PREFIX)), $tag->attrs, $tag->closing); if ($code === NULL) { throw new InvalidStateException("Unknown tag-macro <{$tag->name}> on line {$this->line}."); } if ($isEmpty) { $code .= $this->handler->tagMacro(substr($tag->name, strlen(self::HTML_PREFIX)), $tag->attrs, TRUE); } } else { $code = substr($this->output, $tag->pos) . $matches[0] . (isset($matches['tagnewline']) ? "\n" : ''); $code = $this->handler->attrsMacro($code, $tag->attrs, $tag->closing); if ($code === NULL) { throw new InvalidStateException("Unknown macro-attribute " . self::HTML_PREFIX . implode(' or ' . self::HTML_PREFIX, array_keys($tag->attrs)) . " on line {$this->line}."); } if ($isEmpty) { $code = $this->handler->attrsMacro($code, $tag->attrs, TRUE); } } $this->output = substr_replace($this->output, $code, $tag->pos); $matches[0] = ''; } if ($isEmpty) { $tag->closing = TRUE; } if (!$tag->closing && (strcasecmp($tag->name, 'script') === 0 || strcasecmp($tag->name, 'style') === 0)) { $this->context = self::CONTEXT_CDATA; $this->escape = strcasecmp($tag->name, 'style') ? 'NTemplateHelpers::escapeJs' : 'NTemplateHelpers::escapeCss'; } else { $this->context = self::CONTEXT_TEXT; $this->escape = 'NTemplateHelpers::escapeHtml'; if ($tag->closing) { array_pop($this->tags); } } } else { $name = $matches['attr']; $value = empty($matches['value']) ? TRUE : $matches['value']; if ($isSpecial = NString::startsWith($name, self::HTML_PREFIX)) { $name = substr($name, strlen(self::HTML_PREFIX)); } $tag = end($this->tags); if ($isSpecial || $tag->isMacro) { if ($value === '"' || $value === "'") { if ($matches = $this->match('~(.*?)' . $value . '~xsi')) { $value = $matches[1]; } } $tag->attrs[$name] = $value; $matches[0] = ''; } elseif ($value === '"' || $value === "'") { $this->context = self::CONTEXT_ATTRIBUTE; $this->quote = $value; $this->escape = strncasecmp($name, 'on', 2) ? strcasecmp($name, 'style') ? 'NTemplateHelpers::escapeHtml' : 'NTemplateHelpers::escapeHtmlCss' : 'NTemplateHelpers::escapeHtmlJs'; } } return $matches; }