MediaWiki master
PPNode_Hash_Tree.php
Go to the documentation of this file.
1<?php
8namespace MediaWiki\Parser;
9
10use BadMethodCallException;
11use InvalidArgumentException;
12use Stringable;
13
17// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
18class PPNode_Hash_Tree implements Stringable, PPNode {
19
21 public $name;
22
29 private $rawChildren;
30
35 private $store;
36
41 private $index;
42
47 public const NAME = 0;
48
53 public const CHILDREN = 1;
54
62 public function __construct( array $store, $index ) {
63 $this->store = $store;
64 $this->index = $index;
65 [ $this->name, $this->rawChildren ] = $this->store[$index];
66 }
67
76 public static function factory( array $store, $index ) {
77 if ( !isset( $store[$index] ) ) {
78 return false;
79 }
80
81 $descriptor = $store[$index];
82 if ( is_string( $descriptor ) ) {
83 $class = PPNode_Hash_Text::class;
84 } elseif ( is_array( $descriptor ) ) {
85 if ( $descriptor[self::NAME][0] === '@' ) {
86 $class = PPNode_Hash_Attr::class;
87 } else {
88 $class = self::class;
89 }
90 } else {
91 throw new InvalidArgumentException( __METHOD__ . ': invalid node descriptor' );
92 }
93 return new $class( $store, $index );
94 }
95
100 public function __toString() {
101 $inner = '';
102 $attribs = '';
103 for ( $node = $this->getFirstChild(); $node; $node = $node->getNextSibling() ) {
104 if ( $node instanceof PPNode_Hash_Attr ) {
105 $attribs .= ' ' . $node->name .
106 '="' . htmlspecialchars( $node->value, ENT_COMPAT ) . '"';
107 } else {
108 $inner .= $node->__toString();
109 }
110 }
111 if ( $inner === '' ) {
112 return "<{$this->name}$attribs/>";
113 } else {
114 return "<{$this->name}$attribs>$inner</{$this->name}>";
115 }
116 }
117
121 public function getChildren() {
122 $children = [];
123 foreach ( $this->rawChildren as $i => $child ) {
124 $children[] = self::factory( $this->rawChildren, $i );
125 }
126 return new PPNode_Hash_Array( $children );
127 }
128
136 public function getFirstChild() {
137 if ( !isset( $this->rawChildren[0] ) ) {
138 return false;
139 } else {
140 return self::factory( $this->rawChildren, 0 );
141 }
142 }
143
151 public function getNextSibling() {
152 return self::factory( $this->store, $this->index + 1 );
153 }
154
161 public function getChildrenOfType( $name ) {
162 $children = [];
163 foreach ( $this->rawChildren as $i => $child ) {
164 if ( is_array( $child ) && $child[self::NAME] === $name ) {
165 $children[] = self::factory( $this->rawChildren, $i );
166 }
167 }
168 return new PPNode_Hash_Array( $children );
169 }
170
175 public function getRawChildren() {
176 return $this->rawChildren;
177 }
178
182 public function getLength() {
183 return false;
184 }
185
190 public function item( $i ) {
191 return false;
192 }
193
197 public function getName() {
198 return $this->name;
199 }
200
209 public function splitArg() {
210 return self::splitRawArg( $this->rawChildren );
211 }
212
218 public static function splitRawArg( array $children ) {
219 $bits = [];
220 foreach ( $children as $i => $child ) {
221 if ( !is_array( $child ) ) {
222 continue;
223 }
224 if ( $child[self::NAME] === 'name' ) {
225 $bits['name'] = new self( $children, $i );
226 if ( isset( $child[self::CHILDREN][0][self::NAME] )
227 && $child[self::CHILDREN][0][self::NAME] === '@index'
228 ) {
229 $bits['index'] = $child[self::CHILDREN][0][self::CHILDREN][0];
230 }
231 } elseif ( $child[self::NAME] === 'value' ) {
232 $bits['value'] = new self( $children, $i );
233 }
234 }
235
236 if ( !isset( $bits['name'] ) ) {
237 throw new InvalidArgumentException( 'Invalid brace node passed to ' . __METHOD__ );
238 }
239 if ( !isset( $bits['index'] ) ) {
240 $bits['index'] = '';
241 }
242 return $bits;
243 }
244
251 public function splitExt() {
252 return self::splitRawExt( $this->rawChildren );
253 }
254
260 public static function splitRawExt( array $children ) {
261 $bits = [];
262 foreach ( $children as $i => $child ) {
263 if ( !is_array( $child ) ) {
264 continue;
265 }
266 switch ( $child[self::NAME] ) {
267 case 'name':
268 $bits['name'] = new self( $children, $i );
269 break;
270 case 'attr':
271 $bits['attr'] = new self( $children, $i );
272 break;
273 case 'inner':
274 $bits['inner'] = new self( $children, $i );
275 break;
276 case 'close':
277 $bits['close'] = new self( $children, $i );
278 break;
279 }
280 }
281 if ( !isset( $bits['name'] ) ) {
282 throw new InvalidArgumentException( 'Invalid ext node passed to ' . __METHOD__ );
283 }
284 return $bits;
285 }
286
292 public function splitHeading() {
293 if ( $this->name !== 'h' ) {
294 throw new BadMethodCallException( 'Invalid h node passed to ' . __METHOD__ );
295 }
296 return self::splitRawHeading( $this->rawChildren );
297 }
298
304 public static function splitRawHeading( array $children ) {
305 $bits = [];
306 foreach ( $children as $child ) {
307 if ( !is_array( $child ) ) {
308 continue;
309 }
310 if ( $child[self::NAME] === '@i' ) {
311 $bits['i'] = $child[self::CHILDREN][0];
312 } elseif ( $child[self::NAME] === '@level' ) {
313 $bits['level'] = $child[self::CHILDREN][0];
314 }
315 }
316 if ( !isset( $bits['i'] ) ) {
317 throw new InvalidArgumentException( 'Invalid h node passed to ' . __METHOD__ );
318 }
319 return $bits;
320 }
321
327 public function splitTemplate() {
328 return self::splitRawTemplate( $this->rawChildren );
329 }
330
337 public static function splitRawTemplate( array $children ) {
338 $parts = [];
339 $bits = [ 'lineStart' => '' ];
340 foreach ( $children as $i => $child ) {
341 if ( !is_array( $child ) ) {
342 continue;
343 }
344 switch ( $child[self::NAME] ) {
345 case 'title':
346 $bits['title'] = new self( $children, $i );
347 break;
348 case 'part':
349 $parts[] = new self( $children, $i );
350 break;
351 case '@lineStart':
352 $bits['lineStart'] = '1';
353 break;
354 }
355 }
356 if ( !isset( $bits['title'] ) ) {
357 throw new InvalidArgumentException( 'Invalid node passed to ' . __METHOD__ );
358 }
359 $bits['parts'] = new PPNode_Hash_Array( $parts );
360 return $bits;
361 }
362}
363
365class_alias( PPNode_Hash_Tree::class, 'PPNode_Hash_Tree' );
splitArg()
Split a "<part>" node into an associative array containing:
const CHILDREN
The offset of the child list within descriptors, used in some places for readability.
static splitRawArg(array $children)
Like splitArg() but for a raw child array.
__toString()
Convert a node to XML, for debugging.
static factory(array $store, $index)
Construct an appropriate PPNode_Hash_* object with a class that depends on what is at the relevant st...
splitTemplate()
Split a "<template>" or "<tplarg>" node.
getChildrenOfType( $name)
Get an array of the children with a given node name.
static splitRawHeading(array $children)
Like splitHeading() but for a raw child array.
getFirstChild()
Get the first child, or false if there is none.
splitExt()
Split an "<ext>" node into an associative array containing name, attr, inner and close All values in ...
__construct(array $store, $index)
Construct an object using the data from $store[$index].
getRawChildren()
Get the raw child array.
getNextSibling()
Get the next sibling, or false if there is none.
static splitRawExt(array $children)
Like splitExt() but for a raw child array.
const NAME
The offset of the name within descriptors, used in some places for readability.
static splitRawTemplate(array $children)
Like splitTemplate() but for a raw child array.
There are three types of nodes:
Definition PPNode.php:23