Completed
Push — master ( be32b1...8efd81 )
by Josh
07:06
created

Element::remove()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php declare(strict_types=1);
2
3
/**
4
* @package   s9e\SweetDOM
5
* @copyright Copyright (c) 2019-2020 The s9e authors
6
* @license   http://www.opensource.org/licenses/mit-license.php The MIT License
7
*/
8
namespace s9e\SweetDOM;
9
10
use BadMethodCallException;
11
use DOMElement;
12
use DOMNode;
13
use DOMNodeList;
14
use InvalidArgumentException;
15
16
/**
17
* @method self appendXslApplyTemplates(string $select = null)
18
* @method self appendXslApplyTemplatesSibling(string $select = null)
19
* @method self appendXslAttribute(string $name, string $text = '')
20
* @method self appendXslAttributeSibling(string $name, string $text = '')
21
* @method self appendXslChoose()
22
* @method self appendXslChooseSibling()
23
* @method self appendXslComment(string $text = '')
24
* @method self appendXslCommentSibling(string $text = '')
25
* @method self appendXslCopyOf(string $select)
26
* @method self appendXslCopyOfSibling(string $select)
27
* @method self appendXslIf(string $test, string $text = '')
28
* @method self appendXslIfSibling(string $test, string $text = '')
29
* @method self appendXslOtherwise(string $text = '')
30
* @method self appendXslOtherwiseSibling(string $text = '')
31
* @method self appendXslText(string $text = '')
32
* @method self appendXslTextSibling(string $text = '')
33
* @method self appendXslValueOf(string $select)
34
* @method self appendXslValueOfSibling(string $select)
35
* @method self appendXslVariable(string $name, string $select = null)
36
* @method self appendXslVariableSibling(string $name, string $select = null)
37
* @method self appendXslWhen(string $test, string $text = '')
38
* @method self appendXslWhenSibling(string $test, string $text = '')
39
* @method self prependXslApplyTemplates(string $select = null)
40
* @method self prependXslApplyTemplatesSibling(string $select = null)
41
* @method self prependXslAttribute(string $name, string $text = '')
42
* @method self prependXslAttributeSibling(string $name, string $text = '')
43
* @method self prependXslChoose()
44
* @method self prependXslChooseSibling()
45
* @method self prependXslComment(string $text = '')
46
* @method self prependXslCommentSibling(string $text = '')
47
* @method self prependXslCopyOf(string $select)
48
* @method self prependXslCopyOfSibling(string $select)
49
* @method self prependXslIf(string $test, string $text = '')
50
* @method self prependXslIfSibling(string $test, string $text = '')
51
* @method self prependXslOtherwise(string $text = '')
52
* @method self prependXslOtherwiseSibling(string $text = '')
53
* @method self prependXslText(string $text = '')
54
* @method self prependXslTextSibling(string $text = '')
55
* @method self prependXslValueOf(string $select)
56
* @method self prependXslValueOfSibling(string $select)
57
* @method self prependXslVariable(string $name, string $select = null)
58
* @method self prependXslVariableSibling(string $name, string $select = null)
59
* @method self prependXslWhen(string $test, string $text = '')
60
* @method self prependXslWhenSibling(string $test, string $text = '')
61
*/
62
class Element extends DOMElement
63
{
64 8
	public function __call(string $name, array $arguments)
65
	{
66 8
		if (preg_match('(^(append|prepend)(xsl\\w+?)(sibling|)$)', strtolower($name), $m))
67
		{
68 7
			$callback = [$this->ownerDocument, 'create' . $m[2]];
69 7
			if (is_callable($callback))
70
			{
71 6
				$element = call_user_func_array($callback, $arguments);
72
				$where   = [
73 6
					'append'         => 'beforeend',
74
					'appendsibling'  => 'afterend',
75
					'prepend'        => 'afterbegin',
76
					'prependsibling' => 'beforebegin'
77
				];
78
79 6
				return $this->insertAdjacentElement($where[$m[1] . $m[3]], $element);
80
			}
81
		}
82
83 2
		throw new BadMethodCallException;
84
	}
85
86
	/**
87
	* Evaluate and return the result of a given XPath expression using this element as context node
88
	*
89
	* @param  string  $expr XPath expression
90
	* @return mixed
91
	*/
92 1
	public function evaluate(string $expr)
93
	{
94 1
		return $this->ownerDocument->evaluate($expr, $this);
95
	}
96
97
	/**
98
	* Evaluate and return the first element of a given XPath query using this element as context node
99
	*
100
	* @param  string       $expr XPath expression
101
	* @return DOMNode|null
102
	*/
103 1
	public function firstOf(string $expr): ?DOMNode
104
	{
105 1
		return $this->ownerDocument->firstOf($expr, $this);
106
	}
107
108
	/**
109
	* Insert given element relative to this element's position
110
	*
111
	* @param  string $where   One of 'beforebegin', 'afterbegin', 'beforeend', 'afterend'
112
	* @param  self   $element
113
	* @return self
114
	*/
115 8
	public function insertAdjacentElement(string $where, self $element): self
116
	{
117 8
		$this->insertAdjacentNode($where, $element);
118
119 7
		return $element;
120
	}
121
122
	/**
123
	* Insert given text relative to this element's position
124
	*
125
	* @param  string $where One of 'beforebegin', 'afterbegin', 'beforeend', 'afterend'
126
	* @param  string $text
127
	* @return void
128
	*/
129 1
	public function insertAdjacentText(string $where, string $text): void
130
	{
131 1
		$this->insertAdjacentXML($where, htmlspecialchars($text, ENT_XML1));
132
	}
133
134
	/**
135
	* Insert given XML relative to this element's position
136
	*
137
	* @param  string $where One of 'beforebegin', 'afterbegin', 'beforeend', 'afterend'
138
	* @param  string $xml
139
	* @return void
140
	*/
141 12
	public function insertAdjacentXML(string $where, string $xml): void
142
	{
143 12
		$fragment = $this->ownerDocument->createDocumentFragment();
144 12
		$fragment->appendXML($this->addMissingNamespaceDeclarations($xml));
145
146 12
		$this->insertAdjacentNode($where, $fragment);
147
	}
148
149
	/**
150
	* Evaluate and return the result of a given XPath query using this element as context node
151
	*
152
	* @param  string      $expr XPath expression
153
	* @return DOMNodeList
154
	*/
155 1
	public function query(string $expr): DOMNodeList
156
	{
157 1
		return $this->ownerDocument->query($expr, $this);
158
	}
159
160
	/**
161
	* Remove this element from the document
162
	*
163
	* @return void
164
	*/
165 1
	public function remove(): void
166
	{
167 1
		$this->parentNode->removeChild($this);
168
	}
169
170
	/**
171
	* Replace this element with given element
172
	*
173
	* @param  self $element Replacement element
174
	* @return self          This element
175
	*/
176 1
	public function replace(self $element): self
177
	{
178 1
		return $this->parentNode->replaceChild($element, $this);
179
	}
180
181
	/**
182
	* Add namespace declarations that may be missing in given XML
183
	*
184
	* @param  string $xml Original XML
185
	* @return string      Modified XML
186
	*/
187 12
	protected function addMissingNamespaceDeclarations(string $xml): string
188
	{
189 12
		preg_match_all('(xmlns:\\K[-\\w]++(?==))', $xml, $m);
190 12
		$prefixes = array_flip($m[0]);
191
192 12
		return preg_replace_callback(
193 12
			'(<([-\\w]++):[^>]*?\\K\\s*/?>)',
194
			function ($m) use ($prefixes)
195
			{
196 3
				$return = $m[0];
197 3
				$prefix = $m[1];
198 3
				if (!isset($prefixes[$prefix]))
199
				{
200 2
					$nsURI  = $this->lookupNamespaceURI($prefix);
201 2
					$return = ' xmlns:' . $prefix . '="' . htmlspecialchars($nsURI, ENT_XML1) . '"' . $return;
202
				}
203
204 3
				return $return;
205 12
			},
206 12
			$xml
207
		);
208
	}
209
210
	/**
211
	* Insert given node relative to this element's position
212
	*
213
	* @param  string  $where One of 'beforebegin', 'afterbegin', 'beforeend', 'afterend'
214
	* @param  DOMNode $node
215
	* @return void
216
	*/
217 20
	protected function insertAdjacentNode(string $where, DOMNode $node): void
218
	{
219 20
		$where = strtolower($where);
220 20
		if ($where === 'beforebegin')
221
		{
222 4
			$this->parentNode->insertBefore($node, $this);
223
		}
224 16
		elseif ($where === 'beforeend')
225
		{
226 3
			$this->appendChild($node);
227
		}
228 13
		elseif ($where === 'afterend')
229
		{
230 7
			$this->parentNode->insertBefore($node, $this->nextSibling);
231
		}
232 6
		elseif ($where === 'afterbegin')
233
		{
234 5
			$this->insertBefore($node, $this->firstChild);
235
		}
236
		else
237
		{
238 1
			throw new InvalidArgumentException;
239
		}
240
	}
241
}