Completed
Push — master ( 634c3a...7d6b1f )
by smiley
02:28
created

ManipulationTrait::insert()   C

Complexity

Conditions 7
Paths 4

Size

Total Lines 31
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 7

Importance

Changes 0
Metric Value
dl 0
loc 31
c 0
b 0
f 0
ccs 12
cts 12
cp 1
rs 6.7272
cc 7
eloc 13
nc 4
nop 1
crap 7
1
<?php
2
/**
3
 * Trait ManipulationTrait
4
 *
5
 * @filesource   ManipulationTrait.php
6
 * @created      08.05.2017
7
 * @package      chillerlan\PrototypeDOM
8
 * @author       Smiley <[email protected]>
9
 * @copyright    2017 Smiley
10
 * @license      MIT
11
 */
12
13
namespace chillerlan\PrototypeDOM;
14
15
use DOMNode, DOMNodeList;
16
17
/**
18
 * @extends \DOMNode
19
 */
20
trait ManipulationTrait{
21
22
	/**
23
	 * @link http://php.net/manual/class.domnode.php#domnode.props.ownerdocument
24
	 *
25
	 * @var \chillerlan\PrototypeDOM\Document
26
	 */
27
	public $ownerDocument;
28
29
	/**
30
	 * @var \chillerlan\PrototypeDOM\Element
31
	 */
32
	public $parentNode;
33
34
	/**
35
	 * @var \chillerlan\PrototypeDOM\Element
36
	 */
37
	public $firstChild;
38
39
	/**
40
	 * @var \chillerlan\PrototypeDOM\Element
41
	 */
42
	public $nextSibling;
43
44
	/**
45
	 * @return \DOMNode
46
	 */
47 5
	public function remove():DOMNode{
48
49 5
		if(!$this->parentNode){
50
			/** @var \chillerlan\PrototypeDOM\Element $this */
51 1
			return $this;
52
		}
53
54
		/** @var \chillerlan\PrototypeDOM\Element $this */
55 5
		return $this->parentNode->removeChild($this);
56
	}
57
58
	/**
59
	 * @param \DOMNode $newnode
60
	 *
61
	 * @return \DOMNode
62
	 */
63 2
	public function replace(DOMNode $newnode):DOMNode{
64
65 2
		if(!$this->parentNode){
66
			/** @var \chillerlan\PrototypeDOM\Element $this */
67 1
			return $this;
68
		}
69
70
		/** @var \chillerlan\PrototypeDOM\Element $this */
71 2
		return $this->parentNode->replaceChild($this->_importNode($newnode), $this);
72
	}
73
74
	/**
75
	 * @param \DOMNode $wrapper
76
	 *
77
	 * @return \DOMNode
78
	 */
79 1
	public function wrap(DOMNode $wrapper):DOMNode{
80
		/** @var \chillerlan\PrototypeDOM\Element $wrapper */
81 1
		return $wrapper->insert($this->replace($wrapper));
82
	}
83
84
	/**
85
	 * @return \DOMNode
86
	 */
87 6
	public function empty():DOMNode{
88
89 6
		while($this->hasChildNodes()){
0 ignored issues
show
Bug introduced by
It seems like hasChildNodes() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
90 2
			$this->firstChild->remove();
91
		}
92
93
		/** @var \chillerlan\PrototypeDOM\Element $this */
94 6
		return $this;
95
	}
96
97
	/**
98
	 * @param string|\DOMNode|\DOMNodeList $content
99
	 *
100
	 * @return \DOMNode
101
	 */
102 5
	public function update($content):DOMNode{
103 5
		$this->empty();
104 5
		$this->insert($content);
105
106
		/** @var \chillerlan\PrototypeDOM\Element $this */
107 5
		return $this;
108
	}
109
110
	/**
111
	 * @return \DOMNode
112
	 */
113 1
	public function cleanWhitespace():DOMNode{
114 1
		$node = $this->firstChild;
115
116 1
		while($node){
117 1
			$nextNode = $node->nextSibling;
118
119 1
			if($node->nodeType === XML_TEXT_NODE && empty(trim($node->nodeValue))){
120 1
				$node->remove();
121
			}
122
123 1
			$node = $nextNode;
124
		}
125
126
		/** @var \chillerlan\PrototypeDOM\Element $this */
127 1
		return $this;
128
	}
129
130
	/**
131
	 * @param \DOMNode $newNode
132
	 *
133
	 * @return \DOMNode
134
	 */
135 5
	public function _importNode(DOMNode $newNode):DOMNode{
136 5
		return $this->ownerDocument->importNode($newNode, true);
137
	}
138
139
	/**
140
	 * Accepted insertion points are:
141
	 * - before (as element's previous sibling)
142
	 * - after (as element's next sibling)
143
	 * - top (as element's first child)
144
	 * - bottom (as element's last child)
145
	 *
146
	 * @param string|array|\DOMNode|\DOMNodeList $content
147
	 *
148
	 * @return \DOMNode
149
	 */
150 5
	public function insert($content):DOMNode{
151
152 5
		if(is_array($content)){
153
154 1
			foreach(['before', 'after', 'top', 'bottom'] as $pos){
155
156 1
				if(array_key_exists($pos, $content)){
157 1
					$nodes = $this->ownerDocument->_toNodeList($content[$pos]);
158
159 1
					if($pos === 'top'){
160 1
						$nodes->reverse();
161
					}
162
163 1
					foreach($nodes as $node){
164 1
						call_user_func_array([$this, 'insert_'.$pos], [$node]);
165
					}
166
167
				}
168
169
			}
170
171
		}
172
		else{
173 5
			foreach($this->ownerDocument->_toNodeList($content) as $node){
174 5
				$this->insert_bottom($node);
175
			}
176
		}
177
178
		/** @var \chillerlan\PrototypeDOM\Element $this */
179 5
		return $this;
180
	}
181
182
	/**
183
	 * @param \DOMNode      $node
184
	 * @param \DOMNode|null $refNode
185
	 *
186
	 * @return \DOMNode
187
	 */
188 1
	public function insert_before(DOMNode $node, DOMNode $refNode = null):DOMNode{
189
190 1
		if($this->parentNode){
191 1
			$this->parentNode->insertBefore($this->_importNode($node), $refNode ?? $this);
192
		}
193
194
		/** @var \chillerlan\PrototypeDOM\Element $this */
195 1
		return $this;
196
	}
197
198
	/**
199
	 * @param \DOMNode $node
200
	 *
201
	 * @return \DOMNode
202
	 */
203 1
	public function insert_after(DOMNode $node):DOMNode{
204 1
		!$this->nextSibling && $this->parentNode
205 1
			? $this->parentNode->insert_bottom($node)
206 1
			: $this->nextSibling->insert_before($node);
207
208
		/** @var \chillerlan\PrototypeDOM\Element $this */
209 1
		return $this;
210
	}
211
212
	/**
213
	 * @param \DOMNode $node
214
	 *
215
	 * @return \DOMNode
216
	 */
217 1
	public function insert_top(DOMNode $node):DOMNode{
218 1
		$this->hasChildNodes()
0 ignored issues
show
Bug introduced by
It seems like hasChildNodes() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
219 1
			? $this->firstChild->insert_before($node, $this->firstChild)
220
			: $this->insert_bottom($node);
221
222
		/** @var \chillerlan\PrototypeDOM\Element $this */
223 1
		return $this;
224
	}
225
226
	/**
227
	 * @param \DOMNode $node
228
	 *
229
	 * @return \DOMNode
230
	 */
231 5
	public function insert_bottom(DOMNode $node):DOMNode{
232 5
		$this->appendChild($this->_importNode($node));
0 ignored issues
show
Bug introduced by
It seems like appendChild() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
233
234
		/** @var \chillerlan\PrototypeDOM\Element $this */
235 5
		return $this;
236
	}
237
238
}
239