Completed
Push — master ( e70e95...72eb9f )
by Josh
04:30
created

DisallowXPathFunction   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 81
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 9
lcom 1
cbo 3
dl 0
loc 81
ccs 20
cts 20
cp 1
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A getExpressions() 0 29 5
A __construct() 0 4 1
A check() 0 20 3
1
<?php
2
3
/**
4
* @package   s9e\TextFormatter
5
* @copyright Copyright (c) 2010-2019 The s9e Authors
6
* @license   http://www.opensource.org/licenses/mit-license.php The MIT License
7
*/
8
namespace s9e\TextFormatter\Configurator\TemplateChecks;
9
10
use DOMElement;
11
use DOMXPath;
12
use s9e\TextFormatter\Configurator\Exceptions\UnsafeTemplateException;
13
use s9e\TextFormatter\Configurator\Helpers\AVTHelper;
14
use s9e\TextFormatter\Configurator\Items\Tag;
15
use s9e\TextFormatter\Configurator\TemplateCheck;
16
17
class DisallowXPathFunction extends TemplateCheck
18
{
19
	/**
20
	* @var string Name of the disallowed function
21
	*/
22
	public $funcName;
23
24
	/**
25
	* Constructor
26
	*
27
	* @param  string $funcName Name of the disallowed function
28
	*/
29 7
	public function __construct($funcName)
30
	{
31 7
		$this->funcName = $funcName;
32
	}
33
34
	/**
35
	* Test for the presence of given XPath function
36
	*
37
	* @param  DOMElement $template <xsl:template/> node
38
	* @param  Tag        $tag      Tag this template belongs to
39
	* @return void
40
	*/
41 7
	public function check(DOMElement $template, Tag $tag)
42
	{
43
		// Regexp that matches the function call
44 7
		$regexp = '#(?!<\\pL)' . preg_quote($this->funcName, '#') . '\\s*\\(#iu';
45
46
		// Allow whitespace around colons (NOTE: colons are unnecessarily escaped by preg_quote())
47 7
		$regexp = str_replace('\\:', '\\s*:\\s*', $regexp);
48
49 7
		foreach ($this->getExpressions($template) as $expr => $node)
50
		{
51
			// Remove string literals from the expression
52 7
			$expr = preg_replace('#([\'"]).*?\\1#s', '', $expr);
53
54
			// Test whether the expression contains a document() call
55 7
			if (preg_match($regexp, $expr))
56
			{
57 7
				throw new UnsafeTemplateException('An XPath expression uses the ' . $this->funcName . '() function', $node);
58
			}
59
		}
60
	}
61
62
	/**
63
	* Get all the potential XPath expressions used in given template
64
	*
65
	* @param  DOMElement $template <xsl:template/> node
66
	* @return array                XPath expression as key, reference node as value
67
	*/
68 7
	protected function getExpressions(DOMElement $template)
69
	{
70 7
		$xpath = new DOMXPath($template->ownerDocument);
71 7
		$exprs = [];
72
73 7
		foreach ($xpath->query('//@*') as $attribute)
74
		{
75 7
			if ($attribute->parentNode->namespaceURI === self::XMLNS_XSL)
76
			{
77
				// Attribute of an XSL element. May or may not use XPath, but it shouldn't produce
78
				// false-positives
79 3
				$expr = $attribute->value;
80 3
				$exprs[$expr] = $attribute;
81
			}
82
			else
83
			{
84
				// Attribute of an HTML (or otherwise) element -- Look for inline expressions
85 4
				foreach (AVTHelper::parse($attribute->value) as $token)
86
				{
87 4
					if ($token[0] === 'expression')
88
					{
89 4
						$exprs[$token[1]] = $attribute;
90
					}
91
				}
92
			}
93
		}
94
95 7
		return $exprs;
96
	}
97
}