node * @param Tag $tag Tag this template belongs to */ public function check(DOMElement $template, Tag $tag): void { $this->checkXslElements($template); $this->checkXPathExpressions($template); } /** * Check given XPath expression */ protected function checkXPathExpression(string $expr): void { preg_match_all('("[^"]*+"|\'[^\']*+\'|((?:[a-z]++-)*+[a-z]++)(?=\\s*\\())', $expr, $m); foreach (array_filter($m[1]) as $funcName) { if (!in_array($funcName, $this->supportedFunctions, true) && !in_array($funcName, $this->supportedOperators, true)) { throw new RuntimeException('XPath function ' . $funcName . '() is not supported'); } } } /** * Check all XPath expressions in given template */ protected function checkXPathExpressions(DOMElement $template): void { foreach ($this->getXPathExpressions($template) as $expr) { $this->checkXPathExpression($expr); } } /** * Check all XSL elements in given template */ protected function checkXslElements(DOMElement $template): void { $xpath = new DOMXPath($template->ownerDocument); $nodes = $xpath->query('/xsl:template//xsl:*'); foreach ($nodes as $node) { if (!in_array($node->localName, $this->supportedElements, true)) { throw new RuntimeException('xsl:' . $node->localName . ' elements are not supported'); } $methodName = 'checkXsl' . str_replace(' ', '', ucwords(str_replace('-', ' ', $node->localName))) . 'Element'; if (method_exists($this, $methodName)) { $this->$methodName($node); } } } /** * Return all XPath expressions in given template */ protected function getXPathExpressions(DOMElement $template): array { $exprs = []; $xpath = new DOMXPath($template->ownerDocument); $query = '//xsl:*/@name | //*[namespace-uri() != "' . self::XMLNS_XSL . '"]/@*[contains(., "{")]'; foreach ($xpath->query($query) as $attribute) { foreach (AVTHelper::parse($attribute->value) as [$type, $content]) { if ($type === 'expression') { $exprs[] = $content; } } } $query = '//xsl:*/@select | //xsl:*/@test'; foreach ($xpath->query($query) as $attribute) { $exprs[] = $attribute->value; } return $exprs; } }