Completed
Push — master ( e82316...d26e62 )
by Josh
24:23 queued 19:42
created

FoldConstantXPathExpressions::evaluate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 1
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
* @package   s9e\TextFormatter
5
* @copyright Copyright (c) 2010-2016 The s9e Authors
6
* @license   http://www.opensource.org/licenses/mit-license.php The MIT License
7
*/
8
namespace s9e\TextFormatter\Configurator\TemplateNormalizations;
9
10
use DOMDocument;
11
use DOMXPath;
12
use s9e\TextFormatter\Configurator\Helpers\XPathHelper;
13
14
class FoldConstantXPathExpressions extends AbstractConstantFolding
15
{
16
	/**
17
	* @var DOMXPath
18
	*/
19
	protected $xpath;
20
21
	/**
22
	* Constructor
23
	*/
24
	public function __construct()
25
	{
26
		$this->xpath = new DOMXPath(new DOMDocument);
27
	}
28
29
	/**
30
	* {@inheritdoc}
31
	*/
32
	protected function getOptimizationPasses()
33
	{
34
		return [
35
			'(^(?:"[^"]*"|\'[^\']*\'|\\.[0-9]|[^"$&\'./:<=>@[\\]])++$)' => 'foldConstantXPathExpression'
36
		];
37
	}
38
39
	/**
40
	* Test whether a value can be serialized to an XPath literal
41
	*
42
	* @param  mixed $value
43
	* @return bool
44
	*/
45
	protected function canBeSerialized($value)
46
	{
47
		return (is_string($value) || is_integer($value) || is_float($value));
48
	}
49
50
	/**
51
	* Test whether given string contains an unsupported expression
52
	*
53
	* Will test for keywords, nodes and node functions as well as a few unsupported functions
54
	* such as format-number()
55
	*
56
	* @link https://www.w3.org/TR/xpath/#section-Node-Set-Functions
57
	* @link https://www.w3.org/TR/xslt#add-func
58
	*
59
	* @param  string $expr
60
	* @return bool
61
	*/
62
	protected function containsUnsupportedExpression($expr)
63
	{
64
		// Remove strings to avoid false-positives
65
		$expr = preg_replace('("[^"]*"|\'[^\']*\')', '', $expr);
66
67
		return (bool) preg_match('([a-z](?![a-z\\(])|(?:comment|text|processing-instruction|node|last|position|count|id|local-name|namespace-uri|name|document|key|format-number|current|unparsed-entity-uri|generate-id|system-property)\\()i', $expr);
68
	}
69
70
	/**
71
	* Evaluate given expression without raising any warnings
72
	*
73
	* @param  string $expr
74
	* @return mixed
75
	*/
76
	protected function evaluate($expr)
77
	{
78
		$useErrors = libxml_use_internal_errors(true);
79
		$result    = $this->xpath->evaluate($expr);
80
		libxml_use_internal_errors($useErrors);
81
82
		return $result;
83
	}
84
85
	/**
86
	* Evaluate and replace a constant XPath expression
87
	*
88
	* @param  array  $m
89
	* @return string
90
	*/
91
	protected function foldConstantXPathExpression(array $m)
92
	{
93
		$expr = $m[0];
94
		if (!$this->containsUnsupportedExpression($expr))
95
		{
96
			$result = $this->evaluate($expr);
97
			if ($this->canBeSerialized($result))
98
			{
99
				$foldedExpr = XPathHelper::export($result);
100
				if (strlen($foldedExpr) < strlen($expr))
101
				{
102
					$expr = $foldedExpr;
103
				}
104
			}
105
		}
106
107
		return $expr;
108
	}
109
}