MediaWiki master
PPNode_Hash_Tree.php
Go to the documentation of this file.
1<?php
22namespace MediaWiki\Parser;
23
24use BadMethodCallException;
25use InvalidArgumentException;
26use Stringable;
27
31// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
32class PPNode_Hash_Tree implements Stringable, PPNode {
33
35 public $name;
36
43 private $rawChildren;
44
49 private $store;
50
55 private $index;
56
61 public const NAME = 0;
62
67 public const CHILDREN = 1;
68
76 public function __construct( array $store, $index ) {
77 $this->store = $store;
78 $this->index = $index;
79 [ $this->name, $this->rawChildren ] = $this->store[$index];
80 }
81
90 public static function factory( array $store, $index ) {
91 if ( !isset( $store[$index] ) ) {
92 return false;
93 }
94
95 $descriptor = $store[$index];
96 if ( is_string( $descriptor ) ) {
97 $class = PPNode_Hash_Text::class;
98 } elseif ( is_array( $descriptor ) ) {
99 if ( $descriptor[self::NAME][0] === '@' ) {
100 $class = PPNode_Hash_Attr::class;
101 } else {
102 $class = self::class;
103 }
104 } else {
105 throw new InvalidArgumentException( __METHOD__ . ': invalid node descriptor' );
106 }
107 return new $class( $store, $index );
108 }
109
114 public function __toString() {
115 $inner = '';
116 $attribs = '';
117 for ( $node = $this->getFirstChild(); $node; $node = $node->getNextSibling() ) {
118 if ( $node instanceof PPNode_Hash_Attr ) {
119 $attribs .= ' ' . $node->name .
120 '="' . htmlspecialchars( $node->value, ENT_COMPAT ) . '"';
121 } else {
122 $inner .= $node->__toString();
123 }
124 }
125 if ( $inner === '' ) {
126 return "<{$this->name}$attribs/>";
127 } else {
128 return "<{$this->name}$attribs>$inner</{$this->name}>";
129 }
130 }
131
135 public function getChildren() {
136 $children = [];
137 foreach ( $this->rawChildren as $i => $child ) {
138 $children[] = self::factory( $this->rawChildren, $i );
139 }
140 return new PPNode_Hash_Array( $children );
141 }
142
150 public function getFirstChild() {
151 if ( !isset( $this->rawChildren[0] ) ) {
152 return false;
153 } else {
154 return self::factory( $this->rawChildren, 0 );
155 }
156 }
157
165 public function getNextSibling() {
166 return self::factory( $this->store, $this->index + 1 );
167 }
168
175 public function getChildrenOfType( $name ) {
176 $children = [];
177 foreach ( $this->rawChildren as $i => $child ) {
178 if ( is_array( $child ) && $child[self::NAME] === $name ) {
179 $children[] = self::factory( $this->rawChildren, $i );
180 }
181 }
182 return new PPNode_Hash_Array( $children );
183 }
184
189 public function getRawChildren() {
190 return $this->rawChildren;
191 }
192
196 public function getLength() {
197 return false;
198 }
199
204 public function item( $i ) {
205 return false;
206 }
207
211 public function getName() {
212 return $this->name;
213 }
214
223 public function splitArg() {
224 return self::splitRawArg( $this->rawChildren );
225 }
226
232 public static function splitRawArg( array $children ) {
233 $bits = [];
234 foreach ( $children as $i => $child ) {
235 if ( !is_array( $child ) ) {
236 continue;
237 }
238 if ( $child[self::NAME] === 'name' ) {
239 $bits['name'] = new self( $children, $i );
240 if ( isset( $child[self::CHILDREN][0][self::NAME] )
241 && $child[self::CHILDREN][0][self::NAME] === '@index'
242 ) {
243 $bits['index'] = $child[self::CHILDREN][0][self::CHILDREN][0];
244 }
245 } elseif ( $child[self::NAME] === 'value' ) {
246 $bits['value'] = new self( $children, $i );
247 }
248 }
249
250 if ( !isset( $bits['name'] ) ) {
251 throw new InvalidArgumentException( 'Invalid brace node passed to ' . __METHOD__ );
252 }
253 if ( !isset( $bits['index'] ) ) {
254 $bits['index'] = '';
255 }
256 return $bits;
257 }
258
265 public function splitExt() {
266 return self::splitRawExt( $this->rawChildren );
267 }
268
274 public static function splitRawExt( array $children ) {
275 $bits = [];
276 foreach ( $children as $i => $child ) {
277 if ( !is_array( $child ) ) {
278 continue;
279 }
280 switch ( $child[self::NAME] ) {
281 case 'name':
282 $bits['name'] = new self( $children, $i );
283 break;
284 case 'attr':
285 $bits['attr'] = new self( $children, $i );
286 break;
287 case 'inner':
288 $bits['inner'] = new self( $children, $i );
289 break;
290 case 'close':
291 $bits['close'] = new self( $children, $i );
292 break;
293 }
294 }
295 if ( !isset( $bits['name'] ) ) {
296 throw new InvalidArgumentException( 'Invalid ext node passed to ' . __METHOD__ );
297 }
298 return $bits;
299 }
300
306 public function splitHeading() {
307 if ( $this->name !== 'h' ) {
308 throw new BadMethodCallException( 'Invalid h node passed to ' . __METHOD__ );
309 }
310 return self::splitRawHeading( $this->rawChildren );
311 }
312
318 public static function splitRawHeading( array $children ) {
319 $bits = [];
320 foreach ( $children as $child ) {
321 if ( !is_array( $child ) ) {
322 continue;
323 }
324 if ( $child[self::NAME] === '@i' ) {
325 $bits['i'] = $child[self::CHILDREN][0];
326 } elseif ( $child[self::NAME] === '@level' ) {
327 $bits['level'] = $child[self::CHILDREN][0];
328 }
329 }
330 if ( !isset( $bits['i'] ) ) {
331 throw new InvalidArgumentException( 'Invalid h node passed to ' . __METHOD__ );
332 }
333 return $bits;
334 }
335
341 public function splitTemplate() {
342 return self::splitRawTemplate( $this->rawChildren );
343 }
344
351 public static function splitRawTemplate( array $children ) {
352 $parts = [];
353 $bits = [ 'lineStart' => '' ];
354 foreach ( $children as $i => $child ) {
355 if ( !is_array( $child ) ) {
356 continue;
357 }
358 switch ( $child[self::NAME] ) {
359 case 'title':
360 $bits['title'] = new self( $children, $i );
361 break;
362 case 'part':
363 $parts[] = new self( $children, $i );
364 break;
365 case '@lineStart':
366 $bits['lineStart'] = '1';
367 break;
368 }
369 }
370 if ( !isset( $bits['title'] ) ) {
371 throw new InvalidArgumentException( 'Invalid node passed to ' . __METHOD__ );
372 }
373 $bits['parts'] = new PPNode_Hash_Array( $parts );
374 return $bits;
375 }
376}
377
379class_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:37