funcName = $funcName; } /** * Test for the presence of given XPath function * * @param DOMElement $template node * @param Tag $tag Tag this template belongs to * @return void */ public function check(DOMElement $template, Tag $tag) { // Regexp that matches the function call $regexp = '#(?!<\\pL)' . preg_quote($this->funcName, '#') . '\\s*\\(#iu'; // Allow whitespace around colons (NOTE: colons are unnecessarily escaped by preg_quote()) $regexp = str_replace('\\:', '\\s*:\\s*', $regexp); foreach ($this->getExpressions($template) as $expr => $node) { // Remove string literals from the expression $expr = preg_replace('#([\'"]).*?\\1#s', '', $expr); // Test whether the expression contains a document() call if (preg_match($regexp, $expr)) { throw new UnsafeTemplateException('An XPath expression uses the ' . $this->funcName . '() function', $node); } } } /** * Get all the potential XPath expressions used in given template * * @param DOMElement $template node * @return array XPath expression as key, reference node as value */ protected function getExpressions(DOMElement $template) { $xpath = new DOMXPath($template->ownerDocument); $exprs = []; foreach ($xpath->query('//@*') as $attribute) { if ($attribute->parentNode->namespaceURI === self::XMLNS_XSL) { // Attribute of an XSL element. May or may not use XPath, but it shouldn't produce // false-positives $expr = $attribute->value; $exprs[$expr] = $attribute; } else { // Attribute of an HTML (or otherwise) element -- Look for inline expressions foreach (AVTHelper::parse($attribute->value) as $token) { if ($token[0] === 'expression') { $exprs[$token[1]] = $attribute; } } } } return $exprs; } }