Passed
Push — master ( d34f9d...e217a6 )
by Josh
02:33
created

AbstractNormalization::normalizeText()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 0
dl 0
loc 2
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
/**
4
* @package   s9e\TextFormatter
5
* @copyright Copyright (c) 2010-2023 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 DOMAttr;
11
use DOMComment;
12
use DOMElement;
13
use DOMNode;
14
use DOMText;
15
use DOMXPath;
16
17
abstract class AbstractNormalization
18
{
19
	/**
20
	* XSL namespace
21
	*/
22
	const XMLNS_XSL = 'http://www.w3.org/1999/XSL/Transform';
23
24
	/**
25
	* @var DOMDocument Document that holds the template being normalized
0 ignored issues
show
Bug introduced by
The type s9e\TextFormatter\Config...malizations\DOMDocument was not found. Did you mean DOMDocument? If so, make sure to prefix the type with \.
Loading history...
26
	*/
27
	protected $ownerDocument;
28
29
	/**
30
	* @var string[] XPath queries used to retrieve nodes of interest
31
	*/
32
	protected $queries = [];
33
34
	/**
35
	* @var DOMXPath
36
	*/
37
	protected $xpath;
38
39
	/**
40
	* Apply this normalization rule to given template
41
	*
42
	* @param  DOMElement $template <xsl:template/> node
43
	* @return void
44 4
	*/
45
	public function normalize(DOMElement $template)
46 4
	{
47 4
		$this->ownerDocument = $template->ownerDocument;
0 ignored issues
show
Documentation Bug introduced by
It seems like $template->ownerDocument can also be of type DOMDocument. However, the property $ownerDocument is declared as type s9e\TextFormatter\Config...malizations\DOMDocument. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
48 4
		$this->xpath         = new DOMXPath($this->ownerDocument);
0 ignored issues
show
Bug introduced by
It seems like $this->ownerDocument can also be of type null; however, parameter $document of DOMXPath::__construct() does only seem to accept DOMDocument, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

48
		$this->xpath         = new DOMXPath(/** @scrutinizer ignore-type */ $this->ownerDocument);
Loading history...
49 4
		$this->xpath->registerNamespace('xsl', self::XMLNS_XSL);
50
		foreach ($this->getNodes() as $node)
51 4
		{
52
			// Ignore nodes that have been removed from the document
53 4
			if ($node->parentNode)
54
			{
55
				$this->normalizeNode($node);
56
			}
57
		}
58
		$this->reset();
59
	}
60
61
	/**
62
	* Create an element in current template
63 1
	*
64
	* @param  string     $nodeName
65 1
	* @param  string     $textContent
66 1
	* @return DOMElement
67
	*/
68
	protected function createElement($nodeName, $textContent = '')
69 1
	{
70
		$methodName = 'createElement';
71 1
		$args       = [$nodeName];
72
73
		// Add the text content for the new element
74
		if ($textContent !== '')
75 1
		{
76 1
			$args[] = htmlspecialchars($textContent, ENT_NOQUOTES, 'UTF-8');
77
		}
78 1
79 1
		// Handle namespaced elements
80
		$prefix = strstr($nodeName, ':', true);
81
		if ($prefix > '')
82 1
		{
83
			$methodName .= 'NS';
84
			array_unshift($args, $this->ownerDocument->lookupNamespaceURI($prefix));
85
		}
86
87
		return call_user_func_array([$this->ownerDocument, $methodName], $args);
88
	}
89
90
	/**
91 1
	* Create an xsl:text element or a text node in current template
92
	*
93 1
	* @param  string  $content
94 1
	* @return DOMNode
95 1
	*/
96
	protected function createText($content)
97
	{
98
		return (trim($content) === '')
99
		     ? $this->createElement('xsl:text', $content)
100
		     : $this->ownerDocument->createTextNode($content);
101
	}
102
103
	/**
104 1
	* Create a text node in current template
105
	*
106 1
	* @param  string  $content
107
	* @return DOMText
108
	*/
109
	protected function createTextNode($content)
110
	{
111
		return $this->ownerDocument->createTextNode($content);
112
	}
113
114 4
	/**
115
	* Query and return a list of nodes of interest
116 4
	*
117
	* @return DOMNode[]
118 4
	*/
119
	protected function getNodes()
120
	{
121
		$query = implode(' | ', $this->queries);
122
123
		return ($query === '') ? [] : $this->xpath($query);
124
	}
125
126
	/**
127
	* Test whether given node is an XSL element
128 1
	*
129
	* @param  DOMNode $node
130 1
	* @param  string  $localName
131
	* @return bool
132
	*/
133
	protected function isXsl(DOMNode $node, $localName = null)
134
	{
135
		return ($node->namespaceURI === self::XMLNS_XSL && (!isset($localName) || $localName === $node->localName));
136
	}
137
138
	/**
139 1
	* Make an ASCII string lowercase
140
	*
141 1
	* @param  string $str Original string
142
	* @return string      Lowercased string
143
	*/
144
	protected function lowercase($str)
145
	{
146
		return strtr($str, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
147
	}
148
149
	/**
150
	* Normalize given attribute
151
	*
152
	* @param  DOMAttr $attribute
153
	* @return void
154
	*/
155
	protected function normalizeAttribute(DOMAttr $attribute)
156
	{
157
	}
158
159
	/**
160
	* Normalize given element
161
	*
162
	* @param  DOMElement $element
163
	* @return void
164
	*/
165
	protected function normalizeElement(DOMElement $element)
166
	{
167
	}
168
169
	/**
170 4
	* Normalize given node
171
	*
172 4
	* @param  DOMNode $node
173
	* @return void
174
	*/
175 1
	protected function normalizeNode(DOMNode $node)
176
	{
177 4
		if ($node instanceof DOMElement)
178
		{
179 3
			$this->normalizeElement($node);
180
		}
181 1
		elseif ($node instanceof DOMAttr)
182
		{
183 1
			$this->normalizeAttribute($node);
184
		}
185
		elseif ($node instanceof DOMText)
186
		{
187
			$this->normalizeText($node);
188
		}
189
	}
190
191
	/**
192 4
	* Normalize given text node
193
	*/
194 4
	protected function normalizeText(DOMText $node): void
195 4
	{
196
	}
197
198
	/**
199
	* Reset this instance's properties after usage
200
	*
201
	* @return void
202
	*/
203
	protected function reset()
204
	{
205
		$this->ownerDocument = null;
206
		$this->xpath         = null;
207 4
	}
208
209 4
	/**
210
	* Evaluate given XPath expression
211 4
	*
212
	* For convenience, $XSL is replaced with the XSL namespace URI as a string
213
	*
214
	* @param  string    $query XPath query
215
	* @param  DOMNode   $node  Context node
216
	* @return DOMNode[]
217
	*/
218
	protected function xpath($query, DOMNode $node = null)
219
	{
220
		$query = str_replace('$XSL', '"' . self::XMLNS_XSL . '"', $query);
221
222
		return iterator_to_array($this->xpath->query($query, $node));
223
	}
224
}