MediaWiki  1.30.0
Preprocessor_Hash.php
Go to the documentation of this file.
1 <?php
42 // @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
44  // @codingStandardsIgnoreEnd
45 
49  public $parser;
50 
51  const CACHE_PREFIX = 'preprocess-hash';
52  const CACHE_VERSION = 2;
53 
54  public function __construct( $parser ) {
55  $this->parser = $parser;
56  }
57 
61  public function newFrame() {
62  return new PPFrame_Hash( $this );
63  }
64 
69  public function newCustomFrame( $args ) {
70  return new PPCustomFrame_Hash( $this, $args );
71  }
72 
77  public function newPartNodeArray( $values ) {
78  $list = [];
79 
80  foreach ( $values as $k => $val ) {
81  if ( is_int( $k ) ) {
82  $store = [ [ 'part', [
83  [ 'name', [ [ '@index', [ $k ] ] ] ],
84  [ 'value', [ strval( $val ) ] ],
85  ] ] ];
86  } else {
87  $store = [ [ 'part', [
88  [ 'name', [ strval( $k ) ] ],
89  '=',
90  [ 'value', [ strval( $val ) ] ],
91  ] ] ];
92  }
93 
94  $list[] = new PPNode_Hash_Tree( $store, 0 );
95  }
96 
97  $node = new PPNode_Hash_Array( $list );
98  return $node;
99  }
100 
119  public function preprocessToObj( $text, $flags = 0 ) {
121 
122  $tree = $this->cacheGetTree( $text, $flags );
123  if ( $tree !== false ) {
124  $store = json_decode( $tree );
125  if ( is_array( $store ) ) {
126  return new PPNode_Hash_Tree( $store, 0 );
127  }
128  }
129 
130  $forInclusion = $flags & Parser::PTD_FOR_INCLUSION;
131 
132  $xmlishElements = $this->parser->getStripList();
133  $xmlishAllowMissingEndTag = [ 'includeonly', 'noinclude', 'onlyinclude' ];
134  $enableOnlyinclude = false;
135  if ( $forInclusion ) {
136  $ignoredTags = [ 'includeonly', '/includeonly' ];
137  $ignoredElements = [ 'noinclude' ];
138  $xmlishElements[] = 'noinclude';
139  if ( strpos( $text, '<onlyinclude>' ) !== false
140  && strpos( $text, '</onlyinclude>' ) !== false
141  ) {
142  $enableOnlyinclude = true;
143  }
144  } else {
145  $ignoredTags = [ 'noinclude', '/noinclude', 'onlyinclude', '/onlyinclude' ];
146  $ignoredElements = [ 'includeonly' ];
147  $xmlishElements[] = 'includeonly';
148  }
149  $xmlishRegex = implode( '|', array_merge( $xmlishElements, $ignoredTags ) );
150 
151  // Use "A" modifier (anchored) instead of "^", because ^ doesn't work with an offset
152  $elementsRegex = "~($xmlishRegex)(?:\s|\/>|>)|(!--)~iA";
153 
154  $stack = new PPDStack_Hash;
155 
156  $searchBase = "[{<\n";
157  if ( !$wgDisableLangConversion ) {
158  $searchBase .= '-';
159  }
160 
161  // For fast reverse searches
162  $revText = strrev( $text );
163  $lengthText = strlen( $text );
164 
165  // Input pointer, starts out pointing to a pseudo-newline before the start
166  $i = 0;
167  // Current accumulator. See the doc comment for Preprocessor_Hash for the format.
168  $accum =& $stack->getAccum();
169  // True to find equals signs in arguments
170  $findEquals = false;
171  // True to take notice of pipe characters
172  $findPipe = false;
173  $headingIndex = 1;
174  // True if $i is inside a possible heading
175  $inHeading = false;
176  // True if there are no more greater-than (>) signs right of $i
177  $noMoreGT = false;
178  // Map of tag name => true if there are no more closing tags of given type right of $i
179  $noMoreClosingTag = [];
180  // True to ignore all input up to the next <onlyinclude>
181  $findOnlyinclude = $enableOnlyinclude;
182  // Do a line-start run without outputting an LF character
183  $fakeLineStart = true;
184 
185  while ( true ) {
186  // $this->memCheck();
187 
188  if ( $findOnlyinclude ) {
189  // Ignore all input up to the next <onlyinclude>
190  $startPos = strpos( $text, '<onlyinclude>', $i );
191  if ( $startPos === false ) {
192  // Ignored section runs to the end
193  $accum[] = [ 'ignore', [ substr( $text, $i ) ] ];
194  break;
195  }
196  $tagEndPos = $startPos + strlen( '<onlyinclude>' ); // past-the-end
197  $accum[] = [ 'ignore', [ substr( $text, $i, $tagEndPos - $i ) ] ];
198  $i = $tagEndPos;
199  $findOnlyinclude = false;
200  }
201 
202  if ( $fakeLineStart ) {
203  $found = 'line-start';
204  $curChar = '';
205  } else {
206  # Find next opening brace, closing brace or pipe
207  $search = $searchBase;
208  if ( $stack->top === false ) {
209  $currentClosing = '';
210  } elseif (
211  $stack->top->close === '}-' &&
212  $stack->top->count > 2
213  ) {
214  # adjust closing for -{{{...{{
215  $currentClosing = '}';
216  $search .= $currentClosing;
217  } else {
218  $currentClosing = $stack->top->close;
219  $search .= $currentClosing;
220  }
221  if ( $findPipe ) {
222  $search .= '|';
223  }
224  if ( $findEquals ) {
225  // First equals will be for the template
226  $search .= '=';
227  }
228  $rule = null;
229  # Output literal section, advance input counter
230  $literalLength = strcspn( $text, $search, $i );
231  if ( $literalLength > 0 ) {
232  self::addLiteral( $accum, substr( $text, $i, $literalLength ) );
233  $i += $literalLength;
234  }
235  if ( $i >= $lengthText ) {
236  if ( $currentClosing == "\n" ) {
237  // Do a past-the-end run to finish off the heading
238  $curChar = '';
239  $found = 'line-end';
240  } else {
241  # All done
242  break;
243  }
244  } else {
245  $curChar = $curTwoChar = $text[$i];
246  if ( ( $i + 1 ) < $lengthText ) {
247  $curTwoChar .= $text[$i + 1];
248  }
249  if ( $curChar == '|' ) {
250  $found = 'pipe';
251  } elseif ( $curChar == '=' ) {
252  $found = 'equals';
253  } elseif ( $curChar == '<' ) {
254  $found = 'angle';
255  } elseif ( $curChar == "\n" ) {
256  if ( $inHeading ) {
257  $found = 'line-end';
258  } else {
259  $found = 'line-start';
260  }
261  } elseif ( $curTwoChar == $currentClosing ) {
262  $found = 'close';
263  $curChar = $curTwoChar;
264  } elseif ( $curChar == $currentClosing ) {
265  $found = 'close';
266  } elseif ( isset( $this->rules[$curTwoChar] ) ) {
267  $curChar = $curTwoChar;
268  $found = 'open';
269  $rule = $this->rules[$curChar];
270  } elseif ( isset( $this->rules[$curChar] ) ) {
271  $found = 'open';
272  $rule = $this->rules[$curChar];
273  } else {
274  # Some versions of PHP have a strcspn which stops on
275  # null characters; ignore these and continue.
276  # We also may get '-' and '}' characters here which
277  # don't match -{ or $currentClosing. Add these to
278  # output and continue.
279  if ( $curChar == '-' || $curChar == '}' ) {
280  self::addLiteral( $accum, $curChar );
281  }
282  ++$i;
283  continue;
284  }
285  }
286  }
287 
288  if ( $found == 'angle' ) {
289  $matches = false;
290  // Handle </onlyinclude>
291  if ( $enableOnlyinclude
292  && substr( $text, $i, strlen( '</onlyinclude>' ) ) == '</onlyinclude>'
293  ) {
294  $findOnlyinclude = true;
295  continue;
296  }
297 
298  // Determine element name
299  if ( !preg_match( $elementsRegex, $text, $matches, 0, $i + 1 ) ) {
300  // Element name missing or not listed
301  self::addLiteral( $accum, '<' );
302  ++$i;
303  continue;
304  }
305  // Handle comments
306  if ( isset( $matches[2] ) && $matches[2] == '!--' ) {
307  // To avoid leaving blank lines, when a sequence of
308  // space-separated comments is both preceded and followed by
309  // a newline (ignoring spaces), then
310  // trim leading and trailing spaces and the trailing newline.
311 
312  // Find the end
313  $endPos = strpos( $text, '-->', $i + 4 );
314  if ( $endPos === false ) {
315  // Unclosed comment in input, runs to end
316  $inner = substr( $text, $i );
317  $accum[] = [ 'comment', [ $inner ] ];
318  $i = $lengthText;
319  } else {
320  // Search backwards for leading whitespace
321  $wsStart = $i ? ( $i - strspn( $revText, " \t", $lengthText - $i ) ) : 0;
322 
323  // Search forwards for trailing whitespace
324  // $wsEnd will be the position of the last space (or the '>' if there's none)
325  $wsEnd = $endPos + 2 + strspn( $text, " \t", $endPos + 3 );
326 
327  // Keep looking forward as long as we're finding more
328  // comments.
329  $comments = [ [ $wsStart, $wsEnd ] ];
330  while ( substr( $text, $wsEnd + 1, 4 ) == '<!--' ) {
331  $c = strpos( $text, '-->', $wsEnd + 4 );
332  if ( $c === false ) {
333  break;
334  }
335  $c = $c + 2 + strspn( $text, " \t", $c + 3 );
336  $comments[] = [ $wsEnd + 1, $c ];
337  $wsEnd = $c;
338  }
339 
340  // Eat the line if possible
341  // TODO: This could theoretically be done if $wsStart == 0, i.e. for comments at
342  // the overall start. That's not how Sanitizer::removeHTMLcomments() did it, but
343  // it's a possible beneficial b/c break.
344  if ( $wsStart > 0 && substr( $text, $wsStart - 1, 1 ) == "\n"
345  && substr( $text, $wsEnd + 1, 1 ) == "\n"
346  ) {
347  // Remove leading whitespace from the end of the accumulator
348  $wsLength = $i - $wsStart;
349  $endIndex = count( $accum ) - 1;
350 
351  // Sanity check
352  if ( $wsLength > 0
353  && $endIndex >= 0
354  && is_string( $accum[$endIndex] )
355  && strspn( $accum[$endIndex], " \t", -$wsLength ) === $wsLength
356  ) {
357  $accum[$endIndex] = substr( $accum[$endIndex], 0, -$wsLength );
358  }
359 
360  // Dump all but the last comment to the accumulator
361  foreach ( $comments as $j => $com ) {
362  $startPos = $com[0];
363  $endPos = $com[1] + 1;
364  if ( $j == ( count( $comments ) - 1 ) ) {
365  break;
366  }
367  $inner = substr( $text, $startPos, $endPos - $startPos );
368  $accum[] = [ 'comment', [ $inner ] ];
369  }
370 
371  // Do a line-start run next time to look for headings after the comment
372  $fakeLineStart = true;
373  } else {
374  // No line to eat, just take the comment itself
375  $startPos = $i;
376  $endPos += 2;
377  }
378 
379  if ( $stack->top ) {
380  $part = $stack->top->getCurrentPart();
381  if ( !( isset( $part->commentEnd ) && $part->commentEnd == $wsStart - 1 ) ) {
382  $part->visualEnd = $wsStart;
383  }
384  // Else comments abutting, no change in visual end
385  $part->commentEnd = $endPos;
386  }
387  $i = $endPos + 1;
388  $inner = substr( $text, $startPos, $endPos - $startPos + 1 );
389  $accum[] = [ 'comment', [ $inner ] ];
390  }
391  continue;
392  }
393  $name = $matches[1];
394  $lowerName = strtolower( $name );
395  $attrStart = $i + strlen( $name ) + 1;
396 
397  // Find end of tag
398  $tagEndPos = $noMoreGT ? false : strpos( $text, '>', $attrStart );
399  if ( $tagEndPos === false ) {
400  // Infinite backtrack
401  // Disable tag search to prevent worst-case O(N^2) performance
402  $noMoreGT = true;
403  self::addLiteral( $accum, '<' );
404  ++$i;
405  continue;
406  }
407 
408  // Handle ignored tags
409  if ( in_array( $lowerName, $ignoredTags ) ) {
410  $accum[] = [ 'ignore', [ substr( $text, $i, $tagEndPos - $i + 1 ) ] ];
411  $i = $tagEndPos + 1;
412  continue;
413  }
414 
415  $tagStartPos = $i;
416  if ( $text[$tagEndPos - 1] == '/' ) {
417  // Short end tag
418  $attrEnd = $tagEndPos - 1;
419  $inner = null;
420  $i = $tagEndPos + 1;
421  $close = null;
422  } else {
423  $attrEnd = $tagEndPos;
424  // Find closing tag
425  if (
426  !isset( $noMoreClosingTag[$name] ) &&
427  preg_match( "/<\/" . preg_quote( $name, '/' ) . "\s*>/i",
428  $text, $matches, PREG_OFFSET_CAPTURE, $tagEndPos + 1 )
429  ) {
430  $inner = substr( $text, $tagEndPos + 1, $matches[0][1] - $tagEndPos - 1 );
431  $i = $matches[0][1] + strlen( $matches[0][0] );
432  $close = $matches[0][0];
433  } else {
434  // No end tag
435  if ( in_array( $name, $xmlishAllowMissingEndTag ) ) {
436  // Let it run out to the end of the text.
437  $inner = substr( $text, $tagEndPos + 1 );
438  $i = $lengthText;
439  $close = null;
440  } else {
441  // Don't match the tag, treat opening tag as literal and resume parsing.
442  $i = $tagEndPos + 1;
443  self::addLiteral( $accum,
444  substr( $text, $tagStartPos, $tagEndPos + 1 - $tagStartPos ) );
445  // Cache results, otherwise we have O(N^2) performance for input like <foo><foo><foo>...
446  $noMoreClosingTag[$name] = true;
447  continue;
448  }
449  }
450  }
451  // <includeonly> and <noinclude> just become <ignore> tags
452  if ( in_array( $lowerName, $ignoredElements ) ) {
453  $accum[] = [ 'ignore', [ substr( $text, $tagStartPos, $i - $tagStartPos ) ] ];
454  continue;
455  }
456 
457  if ( $attrEnd <= $attrStart ) {
458  $attr = '';
459  } else {
460  // Note that the attr element contains the whitespace between name and attribute,
461  // this is necessary for precise reconstruction during pre-save transform.
462  $attr = substr( $text, $attrStart, $attrEnd - $attrStart );
463  }
464 
465  $children = [
466  [ 'name', [ $name ] ],
467  [ 'attr', [ $attr ] ] ];
468  if ( $inner !== null ) {
469  $children[] = [ 'inner', [ $inner ] ];
470  }
471  if ( $close !== null ) {
472  $children[] = [ 'close', [ $close ] ];
473  }
474  $accum[] = [ 'ext', $children ];
475  } elseif ( $found == 'line-start' ) {
476  // Is this the start of a heading?
477  // Line break belongs before the heading element in any case
478  if ( $fakeLineStart ) {
479  $fakeLineStart = false;
480  } else {
481  self::addLiteral( $accum, $curChar );
482  $i++;
483  }
484 
485  $count = strspn( $text, '=', $i, 6 );
486  if ( $count == 1 && $findEquals ) {
487  // DWIM: This looks kind of like a name/value separator.
488  // Let's let the equals handler have it and break the potential
489  // heading. This is heuristic, but AFAICT the methods for
490  // completely correct disambiguation are very complex.
491  } elseif ( $count > 0 ) {
492  $piece = [
493  'open' => "\n",
494  'close' => "\n",
495  'parts' => [ new PPDPart_Hash( str_repeat( '=', $count ) ) ],
496  'startPos' => $i,
497  'count' => $count ];
498  $stack->push( $piece );
499  $accum =& $stack->getAccum();
500  extract( $stack->getFlags() );
501  $i += $count;
502  }
503  } elseif ( $found == 'line-end' ) {
504  $piece = $stack->top;
505  // A heading must be open, otherwise \n wouldn't have been in the search list
506  assert( $piece->open === "\n" );
507  $part = $piece->getCurrentPart();
508  // Search back through the input to see if it has a proper close.
509  // Do this using the reversed string since the other solutions
510  // (end anchor, etc.) are inefficient.
511  $wsLength = strspn( $revText, " \t", $lengthText - $i );
512  $searchStart = $i - $wsLength;
513  if ( isset( $part->commentEnd ) && $searchStart - 1 == $part->commentEnd ) {
514  // Comment found at line end
515  // Search for equals signs before the comment
516  $searchStart = $part->visualEnd;
517  $searchStart -= strspn( $revText, " \t", $lengthText - $searchStart );
518  }
519  $count = $piece->count;
520  $equalsLength = strspn( $revText, '=', $lengthText - $searchStart );
521  if ( $equalsLength > 0 ) {
522  if ( $searchStart - $equalsLength == $piece->startPos ) {
523  // This is just a single string of equals signs on its own line
524  // Replicate the doHeadings behavior /={count}(.+)={count}/
525  // First find out how many equals signs there really are (don't stop at 6)
526  $count = $equalsLength;
527  if ( $count < 3 ) {
528  $count = 0;
529  } else {
530  $count = min( 6, intval( ( $count - 1 ) / 2 ) );
531  }
532  } else {
533  $count = min( $equalsLength, $count );
534  }
535  if ( $count > 0 ) {
536  // Normal match, output <h>
537  $element = [ [ 'possible-h',
538  array_merge(
539  [
540  [ '@level', [ $count ] ],
541  [ '@i', [ $headingIndex++ ] ]
542  ],
543  $accum
544  )
545  ] ];
546  } else {
547  // Single equals sign on its own line, count=0
548  $element = $accum;
549  }
550  } else {
551  // No match, no <h>, just pass down the inner text
552  $element = $accum;
553  }
554  // Unwind the stack
555  $stack->pop();
556  $accum =& $stack->getAccum();
557  extract( $stack->getFlags() );
558 
559  // Append the result to the enclosing accumulator
560  array_splice( $accum, count( $accum ), 0, $element );
561 
562  // Note that we do NOT increment the input pointer.
563  // This is because the closing linebreak could be the opening linebreak of
564  // another heading. Infinite loops are avoided because the next iteration MUST
565  // hit the heading open case above, which unconditionally increments the
566  // input pointer.
567  } elseif ( $found == 'open' ) {
568  # count opening brace characters
569  $curLen = strlen( $curChar );
570  $count = ( $curLen > 1 ) ?
571  # allow the final character to repeat
572  strspn( $text, $curChar[$curLen - 1], $i + 1 ) + 1 :
573  strspn( $text, $curChar, $i );
574 
575  # we need to add to stack only if opening brace count is enough for one of the rules
576  if ( $count >= $rule['min'] ) {
577  # Add it to the stack
578  $piece = [
579  'open' => $curChar,
580  'close' => $rule['end'],
581  'count' => $count,
582  'lineStart' => ( $i > 0 && $text[$i - 1] == "\n" ),
583  ];
584 
585  $stack->push( $piece );
586  $accum =& $stack->getAccum();
587  extract( $stack->getFlags() );
588  } else {
589  # Add literal brace(s)
590  self::addLiteral( $accum, str_repeat( $curChar, $count ) );
591  }
592  $i += $count;
593  } elseif ( $found == 'close' ) {
594  $piece = $stack->top;
595  # lets check if there are enough characters for closing brace
596  $maxCount = $piece->count;
597  if ( $piece->close === '}-' && $curChar === '}' ) {
598  $maxCount--; # don't try to match closing '-' as a '}'
599  }
600  $curLen = strlen( $curChar );
601  $count = ( $curLen > 1 ) ? $curLen :
602  strspn( $text, $curChar, $i, $maxCount );
603 
604  # check for maximum matching characters (if there are 5 closing
605  # characters, we will probably need only 3 - depending on the rules)
606  $rule = $this->rules[$piece->open];
607  if ( $piece->close === '}-' && $piece->count > 2 ) {
608  # tweak for -{..{{ }}..}-
609  $rule = $this->rules['{'];
610  }
611  if ( $count > $rule['max'] ) {
612  # The specified maximum exists in the callback array, unless the caller
613  # has made an error
614  $matchingCount = $rule['max'];
615  } else {
616  # Count is less than the maximum
617  # Skip any gaps in the callback array to find the true largest match
618  # Need to use array_key_exists not isset because the callback can be null
619  $matchingCount = $count;
620  while ( $matchingCount > 0 && !array_key_exists( $matchingCount, $rule['names'] ) ) {
621  --$matchingCount;
622  }
623  }
624 
625  if ( $matchingCount <= 0 ) {
626  # No matching element found in callback array
627  # Output a literal closing brace and continue
628  $endText = substr( $text, $i, $count );
629  self::addLiteral( $accum, $endText );
630  $i += $count;
631  continue;
632  }
633  $name = $rule['names'][$matchingCount];
634  if ( $name === null ) {
635  // No element, just literal text
636  $endText = substr( $text, $i, $matchingCount );
637  $element = $piece->breakSyntax( $matchingCount );
638  self::addLiteral( $element, $endText );
639  } else {
640  # Create XML element
641  $parts = $piece->parts;
642  $titleAccum = $parts[0]->out;
643  unset( $parts[0] );
644 
645  $children = [];
646 
647  # The invocation is at the start of the line if lineStart is set in
648  # the stack, and all opening brackets are used up.
649  if ( $maxCount == $matchingCount && !empty( $piece->lineStart ) ) {
650  $children[] = [ '@lineStart', [ 1 ] ];
651  }
652  $titleNode = [ 'title', $titleAccum ];
653  $children[] = $titleNode;
654  $argIndex = 1;
655  foreach ( $parts as $part ) {
656  if ( isset( $part->eqpos ) ) {
657  $equalsNode = $part->out[$part->eqpos];
658  $nameNode = [ 'name', array_slice( $part->out, 0, $part->eqpos ) ];
659  $valueNode = [ 'value', array_slice( $part->out, $part->eqpos + 1 ) ];
660  $partNode = [ 'part', [ $nameNode, $equalsNode, $valueNode ] ];
661  $children[] = $partNode;
662  } else {
663  $nameNode = [ 'name', [ [ '@index', [ $argIndex++ ] ] ] ];
664  $valueNode = [ 'value', $part->out ];
665  $partNode = [ 'part', [ $nameNode, $valueNode ] ];
666  $children[] = $partNode;
667  }
668  }
669  $element = [ [ $name, $children ] ];
670  }
671 
672  # Advance input pointer
673  $i += $matchingCount;
674 
675  # Unwind the stack
676  $stack->pop();
677  $accum =& $stack->getAccum();
678 
679  # Re-add the old stack element if it still has unmatched opening characters remaining
680  if ( $matchingCount < $piece->count ) {
681  $piece->parts = [ new PPDPart_Hash ];
682  $piece->count -= $matchingCount;
683  # do we still qualify for any callback with remaining count?
684  $min = $this->rules[$piece->open]['min'];
685  if ( $piece->count >= $min ) {
686  $stack->push( $piece );
687  $accum =& $stack->getAccum();
688  } else {
689  $s = substr( $piece->open, 0, -1 );
690  $s .= str_repeat(
691  substr( $piece->open, -1 ),
692  $piece->count - strlen( $s )
693  );
694  self::addLiteral( $accum, $s );
695  }
696  }
697 
698  extract( $stack->getFlags() );
699 
700  # Add XML element to the enclosing accumulator
701  array_splice( $accum, count( $accum ), 0, $element );
702  } elseif ( $found == 'pipe' ) {
703  $findEquals = true; // shortcut for getFlags()
704  $stack->addPart();
705  $accum =& $stack->getAccum();
706  ++$i;
707  } elseif ( $found == 'equals' ) {
708  $findEquals = false; // shortcut for getFlags()
709  $accum[] = [ 'equals', [ '=' ] ];
710  $stack->getCurrentPart()->eqpos = count( $accum ) - 1;
711  ++$i;
712  } elseif ( $found == 'dash' ) {
713  self::addLiteral( $accum, '-' );
714  ++$i;
715  }
716  }
717 
718  # Output any remaining unclosed brackets
719  foreach ( $stack->stack as $piece ) {
720  array_splice( $stack->rootAccum, count( $stack->rootAccum ), 0, $piece->breakSyntax() );
721  }
722 
723  # Enable top-level headings
724  foreach ( $stack->rootAccum as &$node ) {
725  if ( is_array( $node ) && $node[PPNode_Hash_Tree::NAME] === 'possible-h' ) {
726  $node[PPNode_Hash_Tree::NAME] = 'h';
727  }
728  }
729 
730  $rootStore = [ [ 'root', $stack->rootAccum ] ];
731  $rootNode = new PPNode_Hash_Tree( $rootStore, 0 );
732 
733  // Cache
734  $tree = json_encode( $rootStore, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
735  if ( $tree !== false ) {
736  $this->cacheSetTree( $text, $flags, $tree );
737  }
738 
739  return $rootNode;
740  }
741 
742  private static function addLiteral( array &$accum, $text ) {
743  $n = count( $accum );
744  if ( $n && is_string( $accum[$n - 1] ) ) {
745  $accum[$n - 1] .= $text;
746  } else {
747  $accum[] = $text;
748  }
749  }
750 }
751 
756 // @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
757 class PPDStack_Hash extends PPDStack {
758  // @codingStandardsIgnoreEnd
759 
760  public function __construct() {
761  $this->elementClass = 'PPDStackElement_Hash';
762  parent::__construct();
763  $this->rootAccum = [];
764  }
765 }
766 
770 // @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
771 class PPDStackElement_Hash extends PPDStackElement {
772  // @codingStandardsIgnoreEnd
773 
774  public function __construct( $data = [] ) {
775  $this->partClass = 'PPDPart_Hash';
776  parent::__construct( $data );
777  }
778 
785  public function breakSyntax( $openingCount = false ) {
786  if ( $this->open == "\n" ) {
787  $accum = $this->parts[0]->out;
788  } else {
789  if ( $openingCount === false ) {
790  $openingCount = $this->count;
791  }
792  $s = substr( $this->open, 0, -1 );
793  $s .= str_repeat(
794  substr( $this->open, -1 ),
795  $openingCount - strlen( $s )
796  );
797  $accum = [ $s ];
798  $lastIndex = 0;
799  $first = true;
800  foreach ( $this->parts as $part ) {
801  if ( $first ) {
802  $first = false;
803  } elseif ( is_string( $accum[$lastIndex] ) ) {
804  $accum[$lastIndex] .= '|';
805  } else {
806  $accum[++$lastIndex] = '|';
807  }
808  foreach ( $part->out as $node ) {
809  if ( is_string( $node ) && is_string( $accum[$lastIndex] ) ) {
810  $accum[$lastIndex] .= $node;
811  } else {
812  $accum[++$lastIndex] = $node;
813  }
814  }
815  }
816  }
817  return $accum;
818  }
819 }
820 
824 // @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
825 class PPDPart_Hash extends PPDPart {
826  // @codingStandardsIgnoreEnd
827 
828  public function __construct( $out = '' ) {
829  if ( $out !== '' ) {
830  $accum = [ $out ];
831  } else {
832  $accum = [];
833  }
834  parent::__construct( $accum );
835  }
836 }
837 
842 // @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
843 class PPFrame_Hash implements PPFrame {
844  // @codingStandardsIgnoreEnd
845 
849  public $parser;
850 
854  public $preprocessor;
855 
859  public $title;
860  public $titleCache;
861 
866  public $loopCheckHash;
867 
872  public $depth;
873 
874  private $volatile = false;
875  private $ttl = null;
876 
880  protected $childExpansionCache;
881 
886  public function __construct( $preprocessor ) {
887  $this->preprocessor = $preprocessor;
888  $this->parser = $preprocessor->parser;
889  $this->title = $this->parser->mTitle;
890  $this->titleCache = [ $this->title ? $this->title->getPrefixedDBkey() : false ];
891  $this->loopCheckHash = [];
892  $this->depth = 0;
893  $this->childExpansionCache = [];
894  }
895 
906  public function newChild( $args = false, $title = false, $indexOffset = 0 ) {
907  $namedArgs = [];
908  $numberedArgs = [];
909  if ( $title === false ) {
910  $title = $this->title;
911  }
912  if ( $args !== false ) {
913  if ( $args instanceof PPNode_Hash_Array ) {
914  $args = $args->value;
915  } elseif ( !is_array( $args ) ) {
916  throw new MWException( __METHOD__ . ': $args must be array or PPNode_Hash_Array' );
917  }
918  foreach ( $args as $arg ) {
919  $bits = $arg->splitArg();
920  if ( $bits['index'] !== '' ) {
921  // Numbered parameter
922  $index = $bits['index'] - $indexOffset;
923  if ( isset( $namedArgs[$index] ) || isset( $numberedArgs[$index] ) ) {
924  $this->parser->getOutput()->addWarning( wfMessage( 'duplicate-args-warning',
925  wfEscapeWikiText( $this->title ),
926  wfEscapeWikiText( $title ),
927  wfEscapeWikiText( $index ) )->text() );
928  $this->parser->addTrackingCategory( 'duplicate-args-category' );
929  }
930  $numberedArgs[$index] = $bits['value'];
931  unset( $namedArgs[$index] );
932  } else {
933  // Named parameter
934  $name = trim( $this->expand( $bits['name'], PPFrame::STRIP_COMMENTS ) );
935  if ( isset( $namedArgs[$name] ) || isset( $numberedArgs[$name] ) ) {
936  $this->parser->getOutput()->addWarning( wfMessage( 'duplicate-args-warning',
937  wfEscapeWikiText( $this->title ),
938  wfEscapeWikiText( $title ),
939  wfEscapeWikiText( $name ) )->text() );
940  $this->parser->addTrackingCategory( 'duplicate-args-category' );
941  }
942  $namedArgs[$name] = $bits['value'];
943  unset( $numberedArgs[$name] );
944  }
945  }
946  }
947  return new PPTemplateFrame_Hash( $this->preprocessor, $this, $numberedArgs, $namedArgs, $title );
948  }
949 
957  public function cachedExpand( $key, $root, $flags = 0 ) {
958  // we don't have a parent, so we don't have a cache
959  return $this->expand( $root, $flags );
960  }
961 
968  public function expand( $root, $flags = 0 ) {
969  static $expansionDepth = 0;
970  if ( is_string( $root ) ) {
971  return $root;
972  }
973 
974  if ( ++$this->parser->mPPNodeCount > $this->parser->mOptions->getMaxPPNodeCount() ) {
975  $this->parser->limitationWarn( 'node-count-exceeded',
976  $this->parser->mPPNodeCount,
977  $this->parser->mOptions->getMaxPPNodeCount()
978  );
979  return '<span class="error">Node-count limit exceeded</span>';
980  }
981  if ( $expansionDepth > $this->parser->mOptions->getMaxPPExpandDepth() ) {
982  $this->parser->limitationWarn( 'expansion-depth-exceeded',
983  $expansionDepth,
984  $this->parser->mOptions->getMaxPPExpandDepth()
985  );
986  return '<span class="error">Expansion depth limit exceeded</span>';
987  }
988  ++$expansionDepth;
989  if ( $expansionDepth > $this->parser->mHighestExpansionDepth ) {
990  $this->parser->mHighestExpansionDepth = $expansionDepth;
991  }
992 
993  $outStack = [ '', '' ];
994  $iteratorStack = [ false, $root ];
995  $indexStack = [ 0, 0 ];
996 
997  while ( count( $iteratorStack ) > 1 ) {
998  $level = count( $outStack ) - 1;
999  $iteratorNode =& $iteratorStack[$level];
1000  $out =& $outStack[$level];
1001  $index =& $indexStack[$level];
1002 
1003  if ( is_array( $iteratorNode ) ) {
1004  if ( $index >= count( $iteratorNode ) ) {
1005  // All done with this iterator
1006  $iteratorStack[$level] = false;
1007  $contextNode = false;
1008  } else {
1009  $contextNode = $iteratorNode[$index];
1010  $index++;
1011  }
1012  } elseif ( $iteratorNode instanceof PPNode_Hash_Array ) {
1013  if ( $index >= $iteratorNode->getLength() ) {
1014  // All done with this iterator
1015  $iteratorStack[$level] = false;
1016  $contextNode = false;
1017  } else {
1018  $contextNode = $iteratorNode->item( $index );
1019  $index++;
1020  }
1021  } else {
1022  // Copy to $contextNode and then delete from iterator stack,
1023  // because this is not an iterator but we do have to execute it once
1024  $contextNode = $iteratorStack[$level];
1025  $iteratorStack[$level] = false;
1026  }
1027 
1028  $newIterator = false;
1029  $contextName = false;
1030  $contextChildren = false;
1031 
1032  if ( $contextNode === false ) {
1033  // nothing to do
1034  } elseif ( is_string( $contextNode ) ) {
1035  $out .= $contextNode;
1036  } elseif ( $contextNode instanceof PPNode_Hash_Array ) {
1037  $newIterator = $contextNode;
1038  } elseif ( $contextNode instanceof PPNode_Hash_Attr ) {
1039  // No output
1040  } elseif ( $contextNode instanceof PPNode_Hash_Text ) {
1041  $out .= $contextNode->value;
1042  } elseif ( $contextNode instanceof PPNode_Hash_Tree ) {
1043  $contextName = $contextNode->name;
1044  $contextChildren = $contextNode->getRawChildren();
1045  } elseif ( is_array( $contextNode ) ) {
1046  // Node descriptor array
1047  if ( count( $contextNode ) !== 2 ) {
1048  throw new MWException( __METHOD__.
1049  ': found an array where a node descriptor should be' );
1050  }
1051  list( $contextName, $contextChildren ) = $contextNode;
1052  } else {
1053  throw new MWException( __METHOD__ . ': Invalid parameter type' );
1054  }
1055 
1056  // Handle node descriptor array or tree object
1057  if ( $contextName === false ) {
1058  // Not a node, already handled above
1059  } elseif ( $contextName[0] === '@' ) {
1060  // Attribute: no output
1061  } elseif ( $contextName === 'template' ) {
1062  # Double-brace expansion
1063  $bits = PPNode_Hash_Tree::splitRawTemplate( $contextChildren );
1064  if ( $flags & PPFrame::NO_TEMPLATES ) {
1065  $newIterator = $this->virtualBracketedImplode(
1066  '{{', '|', '}}',
1067  $bits['title'],
1068  $bits['parts']
1069  );
1070  } else {
1071  $ret = $this->parser->braceSubstitution( $bits, $this );
1072  if ( isset( $ret['object'] ) ) {
1073  $newIterator = $ret['object'];
1074  } else {
1075  $out .= $ret['text'];
1076  }
1077  }
1078  } elseif ( $contextName === 'tplarg' ) {
1079  # Triple-brace expansion
1080  $bits = PPNode_Hash_Tree::splitRawTemplate( $contextChildren );
1081  if ( $flags & PPFrame::NO_ARGS ) {
1082  $newIterator = $this->virtualBracketedImplode(
1083  '{{{', '|', '}}}',
1084  $bits['title'],
1085  $bits['parts']
1086  );
1087  } else {
1088  $ret = $this->parser->argSubstitution( $bits, $this );
1089  if ( isset( $ret['object'] ) ) {
1090  $newIterator = $ret['object'];
1091  } else {
1092  $out .= $ret['text'];
1093  }
1094  }
1095  } elseif ( $contextName === 'comment' ) {
1096  # HTML-style comment
1097  # Remove it in HTML, pre+remove and STRIP_COMMENTS modes
1098  # Not in RECOVER_COMMENTS mode (msgnw) though.
1099  if ( ( $this->parser->ot['html']
1100  || ( $this->parser->ot['pre'] && $this->parser->mOptions->getRemoveComments() )
1101  || ( $flags & PPFrame::STRIP_COMMENTS )
1102  ) && !( $flags & PPFrame::RECOVER_COMMENTS )
1103  ) {
1104  $out .= '';
1105  } elseif ( $this->parser->ot['wiki'] && !( $flags & PPFrame::RECOVER_COMMENTS ) ) {
1106  # Add a strip marker in PST mode so that pstPass2() can
1107  # run some old-fashioned regexes on the result.
1108  # Not in RECOVER_COMMENTS mode (extractSections) though.
1109  $out .= $this->parser->insertStripItem( $contextChildren[0] );
1110  } else {
1111  # Recover the literal comment in RECOVER_COMMENTS and pre+no-remove
1112  $out .= $contextChildren[0];
1113  }
1114  } elseif ( $contextName === 'ignore' ) {
1115  # Output suppression used by <includeonly> etc.
1116  # OT_WIKI will only respect <ignore> in substed templates.
1117  # The other output types respect it unless NO_IGNORE is set.
1118  # extractSections() sets NO_IGNORE and so never respects it.
1119  if ( ( !isset( $this->parent ) && $this->parser->ot['wiki'] )
1120  || ( $flags & PPFrame::NO_IGNORE )
1121  ) {
1122  $out .= $contextChildren[0];
1123  } else {
1124  // $out .= '';
1125  }
1126  } elseif ( $contextName === 'ext' ) {
1127  # Extension tag
1128  $bits = PPNode_Hash_Tree::splitRawExt( $contextChildren ) +
1129  [ 'attr' => null, 'inner' => null, 'close' => null ];
1130  if ( $flags & PPFrame::NO_TAGS ) {
1131  $s = '<' . $bits['name']->getFirstChild()->value;
1132  if ( $bits['attr'] ) {
1133  $s .= $bits['attr']->getFirstChild()->value;
1134  }
1135  if ( $bits['inner'] ) {
1136  $s .= '>' . $bits['inner']->getFirstChild()->value;
1137  if ( $bits['close'] ) {
1138  $s .= $bits['close']->getFirstChild()->value;
1139  }
1140  } else {
1141  $s .= '/>';
1142  }
1143  $out .= $s;
1144  } else {
1145  $out .= $this->parser->extensionSubstitution( $bits, $this );
1146  }
1147  } elseif ( $contextName === 'h' ) {
1148  # Heading
1149  if ( $this->parser->ot['html'] ) {
1150  # Expand immediately and insert heading index marker
1151  $s = $this->expand( $contextChildren, $flags );
1152  $bits = PPNode_Hash_Tree::splitRawHeading( $contextChildren );
1153  $titleText = $this->title->getPrefixedDBkey();
1154  $this->parser->mHeadings[] = [ $titleText, $bits['i'] ];
1155  $serial = count( $this->parser->mHeadings ) - 1;
1156  $marker = Parser::MARKER_PREFIX . "-h-$serial-" . Parser::MARKER_SUFFIX;
1157  $s = substr( $s, 0, $bits['level'] ) . $marker . substr( $s, $bits['level'] );
1158  $this->parser->mStripState->addGeneral( $marker, '' );
1159  $out .= $s;
1160  } else {
1161  # Expand in virtual stack
1162  $newIterator = $contextChildren;
1163  }
1164  } else {
1165  # Generic recursive expansion
1166  $newIterator = $contextChildren;
1167  }
1168 
1169  if ( $newIterator !== false ) {
1170  $outStack[] = '';
1171  $iteratorStack[] = $newIterator;
1172  $indexStack[] = 0;
1173  } elseif ( $iteratorStack[$level] === false ) {
1174  // Return accumulated value to parent
1175  // With tail recursion
1176  while ( $iteratorStack[$level] === false && $level > 0 ) {
1177  $outStack[$level - 1] .= $out;
1178  array_pop( $outStack );
1179  array_pop( $iteratorStack );
1180  array_pop( $indexStack );
1181  $level--;
1182  }
1183  }
1184  }
1185  --$expansionDepth;
1186  return $outStack[0];
1187  }
1188 
1195  public function implodeWithFlags( $sep, $flags /*, ... */ ) {
1196  $args = array_slice( func_get_args(), 2 );
1197 
1198  $first = true;
1199  $s = '';
1200  foreach ( $args as $root ) {
1201  if ( $root instanceof PPNode_Hash_Array ) {
1202  $root = $root->value;
1203  }
1204  if ( !is_array( $root ) ) {
1205  $root = [ $root ];
1206  }
1207  foreach ( $root as $node ) {
1208  if ( $first ) {
1209  $first = false;
1210  } else {
1211  $s .= $sep;
1212  }
1213  $s .= $this->expand( $node, $flags );
1214  }
1215  }
1216  return $s;
1217  }
1218 
1226  public function implode( $sep /*, ... */ ) {
1227  $args = array_slice( func_get_args(), 1 );
1228 
1229  $first = true;
1230  $s = '';
1231  foreach ( $args as $root ) {
1232  if ( $root instanceof PPNode_Hash_Array ) {
1233  $root = $root->value;
1234  }
1235  if ( !is_array( $root ) ) {
1236  $root = [ $root ];
1237  }
1238  foreach ( $root as $node ) {
1239  if ( $first ) {
1240  $first = false;
1241  } else {
1242  $s .= $sep;
1243  }
1244  $s .= $this->expand( $node );
1245  }
1246  }
1247  return $s;
1248  }
1249 
1258  public function virtualImplode( $sep /*, ... */ ) {
1259  $args = array_slice( func_get_args(), 1 );
1260  $out = [];
1261  $first = true;
1262 
1263  foreach ( $args as $root ) {
1264  if ( $root instanceof PPNode_Hash_Array ) {
1265  $root = $root->value;
1266  }
1267  if ( !is_array( $root ) ) {
1268  $root = [ $root ];
1269  }
1270  foreach ( $root as $node ) {
1271  if ( $first ) {
1272  $first = false;
1273  } else {
1274  $out[] = $sep;
1275  }
1276  $out[] = $node;
1277  }
1278  }
1279  return new PPNode_Hash_Array( $out );
1280  }
1281 
1291  public function virtualBracketedImplode( $start, $sep, $end /*, ... */ ) {
1292  $args = array_slice( func_get_args(), 3 );
1293  $out = [ $start ];
1294  $first = true;
1295 
1296  foreach ( $args as $root ) {
1297  if ( $root instanceof PPNode_Hash_Array ) {
1298  $root = $root->value;
1299  }
1300  if ( !is_array( $root ) ) {
1301  $root = [ $root ];
1302  }
1303  foreach ( $root as $node ) {
1304  if ( $first ) {
1305  $first = false;
1306  } else {
1307  $out[] = $sep;
1308  }
1309  $out[] = $node;
1310  }
1311  }
1312  $out[] = $end;
1313  return new PPNode_Hash_Array( $out );
1314  }
1315 
1316  public function __toString() {
1317  return 'frame{}';
1318  }
1319 
1324  public function getPDBK( $level = false ) {
1325  if ( $level === false ) {
1326  return $this->title->getPrefixedDBkey();
1327  } else {
1328  return isset( $this->titleCache[$level] ) ? $this->titleCache[$level] : false;
1329  }
1330  }
1331 
1335  public function getArguments() {
1336  return [];
1337  }
1338 
1342  public function getNumberedArguments() {
1343  return [];
1344  }
1345 
1349  public function getNamedArguments() {
1350  return [];
1351  }
1352 
1358  public function isEmpty() {
1359  return true;
1360  }
1361 
1366  public function getArgument( $name ) {
1367  return false;
1368  }
1369 
1377  public function loopCheck( $title ) {
1378  return !isset( $this->loopCheckHash[$title->getPrefixedDBkey()] );
1379  }
1380 
1386  public function isTemplate() {
1387  return false;
1388  }
1389 
1395  public function getTitle() {
1396  return $this->title;
1397  }
1398 
1404  public function setVolatile( $flag = true ) {
1405  $this->volatile = $flag;
1406  }
1407 
1413  public function isVolatile() {
1414  return $this->volatile;
1415  }
1416 
1422  public function setTTL( $ttl ) {
1423  if ( $ttl !== null && ( $this->ttl === null || $ttl < $this->ttl ) ) {
1424  $this->ttl = $ttl;
1425  }
1426  }
1427 
1433  public function getTTL() {
1434  return $this->ttl;
1435  }
1436 }
1437 
1442 // @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
1443 class PPTemplateFrame_Hash extends PPFrame_Hash {
1444  // @codingStandardsIgnoreEnd
1445 
1446  public $numberedArgs, $namedArgs, $parent;
1447  public $numberedExpansionCache, $namedExpansionCache;
1448 
1456  public function __construct( $preprocessor, $parent = false, $numberedArgs = [],
1457  $namedArgs = [], $title = false
1458  ) {
1459  parent::__construct( $preprocessor );
1460 
1461  $this->parent = $parent;
1462  $this->numberedArgs = $numberedArgs;
1463  $this->namedArgs = $namedArgs;
1464  $this->title = $title;
1465  $pdbk = $title ? $title->getPrefixedDBkey() : false;
1466  $this->titleCache = $parent->titleCache;
1467  $this->titleCache[] = $pdbk;
1468  $this->loopCheckHash = /*clone*/ $parent->loopCheckHash;
1469  if ( $pdbk !== false ) {
1470  $this->loopCheckHash[$pdbk] = true;
1471  }
1472  $this->depth = $parent->depth + 1;
1473  $this->numberedExpansionCache = $this->namedExpansionCache = [];
1474  }
1475 
1476  public function __toString() {
1477  $s = 'tplframe{';
1478  $first = true;
1479  $args = $this->numberedArgs + $this->namedArgs;
1480  foreach ( $args as $name => $value ) {
1481  if ( $first ) {
1482  $first = false;
1483  } else {
1484  $s .= ', ';
1485  }
1486  $s .= "\"$name\":\"" .
1487  str_replace( '"', '\\"', $value->__toString() ) . '"';
1488  }
1489  $s .= '}';
1490  return $s;
1491  }
1492 
1500  public function cachedExpand( $key, $root, $flags = 0 ) {
1501  if ( isset( $this->parent->childExpansionCache[$key] ) ) {
1502  return $this->parent->childExpansionCache[$key];
1503  }
1504  $retval = $this->expand( $root, $flags );
1505  if ( !$this->isVolatile() ) {
1506  $this->parent->childExpansionCache[$key] = $retval;
1507  }
1508  return $retval;
1509  }
1510 
1516  public function isEmpty() {
1517  return !count( $this->numberedArgs ) && !count( $this->namedArgs );
1518  }
1519 
1523  public function getArguments() {
1524  $arguments = [];
1525  foreach ( array_merge(
1526  array_keys( $this->numberedArgs ),
1527  array_keys( $this->namedArgs ) ) as $key ) {
1528  $arguments[$key] = $this->getArgument( $key );
1529  }
1530  return $arguments;
1531  }
1532 
1536  public function getNumberedArguments() {
1537  $arguments = [];
1538  foreach ( array_keys( $this->numberedArgs ) as $key ) {
1539  $arguments[$key] = $this->getArgument( $key );
1540  }
1541  return $arguments;
1542  }
1543 
1547  public function getNamedArguments() {
1548  $arguments = [];
1549  foreach ( array_keys( $this->namedArgs ) as $key ) {
1550  $arguments[$key] = $this->getArgument( $key );
1551  }
1552  return $arguments;
1553  }
1554 
1559  public function getNumberedArgument( $index ) {
1560  if ( !isset( $this->numberedArgs[$index] ) ) {
1561  return false;
1562  }
1563  if ( !isset( $this->numberedExpansionCache[$index] ) ) {
1564  # No trimming for unnamed arguments
1565  $this->numberedExpansionCache[$index] = $this->parent->expand(
1566  $this->numberedArgs[$index],
1567  PPFrame::STRIP_COMMENTS
1568  );
1569  }
1570  return $this->numberedExpansionCache[$index];
1571  }
1572 
1577  public function getNamedArgument( $name ) {
1578  if ( !isset( $this->namedArgs[$name] ) ) {
1579  return false;
1580  }
1581  if ( !isset( $this->namedExpansionCache[$name] ) ) {
1582  # Trim named arguments post-expand, for backwards compatibility
1583  $this->namedExpansionCache[$name] = trim(
1584  $this->parent->expand( $this->namedArgs[$name], PPFrame::STRIP_COMMENTS ) );
1585  }
1586  return $this->namedExpansionCache[$name];
1587  }
1588 
1593  public function getArgument( $name ) {
1594  $text = $this->getNumberedArgument( $name );
1595  if ( $text === false ) {
1596  $text = $this->getNamedArgument( $name );
1597  }
1598  return $text;
1599  }
1600 
1606  public function isTemplate() {
1607  return true;
1608  }
1609 
1610  public function setVolatile( $flag = true ) {
1611  parent::setVolatile( $flag );
1612  $this->parent->setVolatile( $flag );
1613  }
1614 
1615  public function setTTL( $ttl ) {
1616  parent::setTTL( $ttl );
1617  $this->parent->setTTL( $ttl );
1618  }
1619 }
1620 
1625 // @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
1626 class PPCustomFrame_Hash extends PPFrame_Hash {
1627  // @codingStandardsIgnoreEnd
1628 
1629  public $args;
1630 
1631  public function __construct( $preprocessor, $args ) {
1632  parent::__construct( $preprocessor );
1633  $this->args = $args;
1634  }
1635 
1636  public function __toString() {
1637  $s = 'cstmframe{';
1638  $first = true;
1639  foreach ( $this->args as $name => $value ) {
1640  if ( $first ) {
1641  $first = false;
1642  } else {
1643  $s .= ', ';
1644  }
1645  $s .= "\"$name\":\"" .
1646  str_replace( '"', '\\"', $value->__toString() ) . '"';
1647  }
1648  $s .= '}';
1649  return $s;
1650  }
1651 
1655  public function isEmpty() {
1656  return !count( $this->args );
1657  }
1658 
1663  public function getArgument( $index ) {
1664  if ( !isset( $this->args[$index] ) ) {
1665  return false;
1666  }
1667  return $this->args[$index];
1668  }
1669 
1670  public function getArguments() {
1671  return $this->args;
1672  }
1673 }
1674 
1678 // @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
1679 class PPNode_Hash_Tree implements PPNode {
1680  // @codingStandardsIgnoreEnd
1681 
1682  public $name;
1683 
1689  private $rawChildren;
1690 
1694  private $store;
1695 
1699  private $index;
1700 
1705  const NAME = 0;
1706 
1711  const CHILDREN = 1;
1712 
1720  public function __construct( array $store, $index ) {
1721  $this->store = $store;
1722  $this->index = $index;
1723  list( $this->name, $this->rawChildren ) = $this->store[$index];
1724  }
1725 
1734  public static function factory( array $store, $index ) {
1735  if ( !isset( $store[$index] ) ) {
1736  return false;
1737  }
1738 
1739  $descriptor = $store[$index];
1740  if ( is_string( $descriptor ) ) {
1741  $class = 'PPNode_Hash_Text';
1742  } elseif ( is_array( $descriptor ) ) {
1743  if ( $descriptor[self::NAME][0] === '@' ) {
1744  $class = 'PPNode_Hash_Attr';
1745  } else {
1746  $class = 'PPNode_Hash_Tree';
1747  }
1748  } else {
1749  throw new MWException( __METHOD__.': invalid node descriptor' );
1750  }
1751  return new $class( $store, $index );
1752  }
1753 
1757  public function __toString() {
1758  $inner = '';
1759  $attribs = '';
1760  for ( $node = $this->getFirstChild(); $node; $node = $node->getNextSibling() ) {
1761  if ( $node instanceof PPNode_Hash_Attr ) {
1762  $attribs .= ' ' . $node->name . '="' . htmlspecialchars( $node->value ) . '"';
1763  } else {
1764  $inner .= $node->__toString();
1765  }
1766  }
1767  if ( $inner === '' ) {
1768  return "<{$this->name}$attribs/>";
1769  } else {
1770  return "<{$this->name}$attribs>$inner</{$this->name}>";
1771  }
1772  }
1773 
1777  public function getChildren() {
1778  $children = [];
1779  foreach ( $this->rawChildren as $i => $child ) {
1780  $children[] = self::factory( $this->rawChildren, $i );
1781  }
1782  return new PPNode_Hash_Array( $children );
1783  }
1784 
1792  public function getFirstChild() {
1793  if ( !isset( $this->rawChildren[0] ) ) {
1794  return false;
1795  } else {
1796  return self::factory( $this->rawChildren, 0 );
1797  }
1798  }
1799 
1807  public function getNextSibling() {
1808  return self::factory( $this->store, $this->index + 1 );
1809  }
1810 
1817  public function getChildrenOfType( $name ) {
1818  $children = [];
1819  foreach ( $this->rawChildren as $i => $child ) {
1820  if ( is_array( $child ) && $child[self::NAME] === $name ) {
1821  $children[] = self::factory( $this->rawChildren, $i );
1822  }
1823  }
1824  return new PPNode_Hash_Array( $children );
1825  }
1826 
1831  public function getRawChildren() {
1832  return $this->rawChildren;
1833  }
1834 
1838  public function getLength() {
1839  return false;
1840  }
1841 
1846  public function item( $i ) {
1847  return false;
1848  }
1849 
1853  public function getName() {
1854  return $this->name;
1855  }
1856 
1866  public function splitArg() {
1867  return self::splitRawArg( $this->rawChildren );
1868  }
1869 
1875  public static function splitRawArg( array $children ) {
1876  $bits = [];
1877  foreach ( $children as $i => $child ) {
1878  if ( !is_array( $child ) ) {
1879  continue;
1880  }
1881  if ( $child[self::NAME] === 'name' ) {
1882  $bits['name'] = new self( $children, $i );
1883  if ( isset( $child[self::CHILDREN][0][self::NAME] )
1884  && $child[self::CHILDREN][0][self::NAME] === '@index'
1885  ) {
1886  $bits['index'] = $child[self::CHILDREN][0][self::CHILDREN][0];
1887  }
1888  } elseif ( $child[self::NAME] === 'value' ) {
1889  $bits['value'] = new self( $children, $i );
1890  }
1891  }
1892 
1893  if ( !isset( $bits['name'] ) ) {
1894  throw new MWException( 'Invalid brace node passed to ' . __METHOD__ );
1895  }
1896  if ( !isset( $bits['index'] ) ) {
1897  $bits['index'] = '';
1898  }
1899  return $bits;
1900  }
1901 
1909  public function splitExt() {
1910  return self::splitRawExt( $this->rawChildren );
1911  }
1912 
1918  public static function splitRawExt( array $children ) {
1919  $bits = [];
1920  foreach ( $children as $i => $child ) {
1921  if ( !is_array( $child ) ) {
1922  continue;
1923  }
1924  switch ( $child[self::NAME] ) {
1925  case 'name':
1926  $bits['name'] = new self( $children, $i );
1927  break;
1928  case 'attr':
1929  $bits['attr'] = new self( $children, $i );
1930  break;
1931  case 'inner':
1932  $bits['inner'] = new self( $children, $i );
1933  break;
1934  case 'close':
1935  $bits['close'] = new self( $children, $i );
1936  break;
1937  }
1938  }
1939  if ( !isset( $bits['name'] ) ) {
1940  throw new MWException( 'Invalid ext node passed to ' . __METHOD__ );
1941  }
1942  return $bits;
1943  }
1944 
1951  public function splitHeading() {
1952  if ( $this->name !== 'h' ) {
1953  throw new MWException( 'Invalid h node passed to ' . __METHOD__ );
1954  }
1955  return self::splitRawHeading( $this->rawChildren );
1956  }
1957 
1963  public static function splitRawHeading( array $children ) {
1964  $bits = [];
1965  foreach ( $children as $i => $child ) {
1966  if ( !is_array( $child ) ) {
1967  continue;
1968  }
1969  if ( $child[self::NAME] === '@i' ) {
1970  $bits['i'] = $child[self::CHILDREN][0];
1971  } elseif ( $child[self::NAME] === '@level' ) {
1972  $bits['level'] = $child[self::CHILDREN][0];
1973  }
1974  }
1975  if ( !isset( $bits['i'] ) ) {
1976  throw new MWException( 'Invalid h node passed to ' . __METHOD__ );
1977  }
1978  return $bits;
1979  }
1980 
1987  public function splitTemplate() {
1988  return self::splitRawTemplate( $this->rawChildren );
1989  }
1990 
1996  public static function splitRawTemplate( array $children ) {
1997  $parts = [];
1998  $bits = [ 'lineStart' => '' ];
1999  foreach ( $children as $i => $child ) {
2000  if ( !is_array( $child ) ) {
2001  continue;
2002  }
2003  switch ( $child[self::NAME] ) {
2004  case 'title':
2005  $bits['title'] = new self( $children, $i );
2006  break;
2007  case 'part':
2008  $parts[] = new self( $children, $i );
2009  break;
2010  case '@lineStart':
2011  $bits['lineStart'] = '1';
2012  break;
2013  }
2014  }
2015  if ( !isset( $bits['title'] ) ) {
2016  throw new MWException( 'Invalid node passed to ' . __METHOD__ );
2017  }
2018  $bits['parts'] = new PPNode_Hash_Array( $parts );
2019  return $bits;
2020  }
2021 }
2022 
2026 // @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
2027 class PPNode_Hash_Text implements PPNode {
2028  // @codingStandardsIgnoreEnd
2029 
2030  public $value;
2031  private $store, $index;
2032 
2040  public function __construct( array $store, $index ) {
2041  $this->value = $store[$index];
2042  if ( !is_scalar( $this->value ) ) {
2043  throw new MWException( __CLASS__ . ' given object instead of string' );
2044  }
2045  $this->store = $store;
2046  $this->index = $index;
2047  }
2048 
2049  public function __toString() {
2050  return htmlspecialchars( $this->value );
2051  }
2052 
2053  public function getNextSibling() {
2054  return PPNode_Hash_Tree::factory( $this->store, $this->index + 1 );
2055  }
2056 
2057  public function getChildren() {
2058  return false;
2059  }
2060 
2061  public function getFirstChild() {
2062  return false;
2063  }
2064 
2065  public function getChildrenOfType( $name ) {
2066  return false;
2067  }
2068 
2069  public function getLength() {
2070  return false;
2071  }
2072 
2073  public function item( $i ) {
2074  return false;
2075  }
2076 
2077  public function getName() {
2078  return '#text';
2079  }
2080 
2081  public function splitArg() {
2082  throw new MWException( __METHOD__ . ': not supported' );
2083  }
2084 
2085  public function splitExt() {
2086  throw new MWException( __METHOD__ . ': not supported' );
2087  }
2088 
2089  public function splitHeading() {
2090  throw new MWException( __METHOD__ . ': not supported' );
2091  }
2092 }
2093 
2097 // @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
2098 class PPNode_Hash_Array implements PPNode {
2099  // @codingStandardsIgnoreEnd
2100 
2101  public $value;
2102 
2103  public function __construct( $value ) {
2104  $this->value = $value;
2105  }
2106 
2107  public function __toString() {
2108  return var_export( $this, true );
2109  }
2110 
2111  public function getLength() {
2112  return count( $this->value );
2113  }
2114 
2115  public function item( $i ) {
2116  return $this->value[$i];
2117  }
2118 
2119  public function getName() {
2120  return '#nodelist';
2121  }
2122 
2123  public function getNextSibling() {
2124  return false;
2125  }
2126 
2127  public function getChildren() {
2128  return false;
2129  }
2130 
2131  public function getFirstChild() {
2132  return false;
2133  }
2134 
2135  public function getChildrenOfType( $name ) {
2136  return false;
2137  }
2138 
2139  public function splitArg() {
2140  throw new MWException( __METHOD__ . ': not supported' );
2141  }
2142 
2143  public function splitExt() {
2144  throw new MWException( __METHOD__ . ': not supported' );
2145  }
2146 
2147  public function splitHeading() {
2148  throw new MWException( __METHOD__ . ': not supported' );
2149  }
2150 }
2151 
2155 // @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
2156 class PPNode_Hash_Attr implements PPNode {
2157  // @codingStandardsIgnoreEnd
2158 
2159  public $name, $value;
2160  private $store, $index;
2161 
2169  public function __construct( array $store, $index ) {
2170  $descriptor = $store[$index];
2171  if ( $descriptor[PPNode_Hash_Tree::NAME][0] !== '@' ) {
2172  throw new MWException( __METHOD__.': invalid name in attribute descriptor' );
2173  }
2174  $this->name = substr( $descriptor[PPNode_Hash_Tree::NAME], 1 );
2175  $this->value = $descriptor[PPNode_Hash_Tree::CHILDREN][0];
2176  $this->store = $store;
2177  $this->index = $index;
2178  }
2179 
2180  public function __toString() {
2181  return "<@{$this->name}>" . htmlspecialchars( $this->value ) . "</@{$this->name}>";
2182  }
2183 
2184  public function getName() {
2185  return $this->name;
2186  }
2187 
2188  public function getNextSibling() {
2189  return PPNode_Hash_Tree::factory( $this->store, $this->index + 1 );
2190  }
2191 
2192  public function getChildren() {
2193  return false;
2194  }
2195 
2196  public function getFirstChild() {
2197  return false;
2198  }
2199 
2200  public function getChildrenOfType( $name ) {
2201  return false;
2202  }
2203 
2204  public function getLength() {
2205  return false;
2206  }
2207 
2208  public function item( $i ) {
2209  return false;
2210  }
2211 
2212  public function splitArg() {
2213  throw new MWException( __METHOD__ . ': not supported' );
2214  }
2215 
2216  public function splitExt() {
2217  throw new MWException( __METHOD__ . ': not supported' );
2218  }
2219 
2220  public function splitHeading() {
2221  throw new MWException( __METHOD__ . ': not supported' );
2222  }
2223 }
part
in this case you re responsible for computing and outputting the entire conflict part
Definition: hooks.txt:1411
PPNode_Hash_Tree\getLength
getLength()
Definition: Preprocessor_Hash.php:1838
PPNode_Hash_Tree\getChildrenOfType
getChildrenOfType( $name)
Get an array of the children with a given node name.
Definition: Preprocessor_Hash.php:1817
type
This document describes the state of Postgres support in and is fairly well maintained The main code is very well while extensions are very hit and miss it is probably the most supported database after MySQL Much of the work in making MediaWiki database agnostic came about through the work of creating Postgres as and are nearing end of but without copying over all the usage comments General notes on the but these can almost always be programmed around *Although Postgres has a true BOOLEAN type
Definition: postgres.txt:22
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:187
PPNode_Hash_Tree\item
item( $i)
Definition: Preprocessor_Hash.php:1846
PPDPart_Hash
Definition: Preprocessor_Hash.php:825
PPNode_Hash_Tree\$store
$store
The store array for the siblings of this node, including this node itself.
Definition: Preprocessor_Hash.php:1694
PPNode_Hash_Array\splitHeading
splitHeading()
Split an "<h>" node.
Definition: Preprocessor_Hash.php:2147
PPNode_Hash_Tree\getFirstChild
getFirstChild()
Get the first child, or false if there is none.
Definition: Preprocessor_Hash.php:1792
captcha-old.count
count
Definition: captcha-old.py:249
PPNode_Hash_Tree\splitRawExt
static splitRawExt(array $children)
Like splitExt() but for a raw child array.
Definition: Preprocessor_Hash.php:1918
text
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add text
Definition: design.txt:12
Preprocessor_Hash\__construct
__construct( $parser)
Definition: Preprocessor_Hash.php:54
PPFrame_Hash
An expansion frame, used as a context to expand the result of preprocessToObj()
Definition: Preprocessor_Hash.php:843
wiki
Prior to maintenance scripts were a hodgepodge of code that had no cohesion or formal method of action Beginning maintenance scripts have been cleaned up to use a unified class Directory structure How to run a script How to write your own DIRECTORY STRUCTURE The maintenance directory of a MediaWiki installation contains several all of which have unique purposes HOW TO RUN A SCRIPT Ridiculously just call php someScript php that s in the top level maintenance directory if not default wiki
Definition: maintenance.txt:1
PPNode_Hash_Text\splitHeading
splitHeading()
Split an "<h>" node.
Definition: Preprocessor_Hash.php:2089
PPNode_Hash_Tree\splitHeading
splitHeading()
Split an "<h>" node.
Definition: Preprocessor_Hash.php:1951
PPNode_Hash_Attr\splitExt
splitExt()
Split an "<ext>" node into an associative array containing name, attr, inner and close All values in ...
Definition: Preprocessor_Hash.php:2216
PPCustomFrame_Hash\isEmpty
isEmpty()
Definition: Preprocessor_Hash.php:1655
PPNode_Hash_Text\getChildrenOfType
getChildrenOfType( $name)
Get all children of this tree node which have a given name.
Definition: Preprocessor_Hash.php:2065
PPNode_Hash_Tree\splitExt
splitExt()
Split an "<ext>" node into an associative array containing name, attr, inner and close All values in ...
Definition: Preprocessor_Hash.php:1909
PPNode_Hash_Text\getLength
getLength()
Returns the length of the array, or false if this is not an array-type node.
Definition: Preprocessor_Hash.php:2069
PPNode_Hash_Tree\splitRawArg
static splitRawArg(array $children)
Like splitArg() but for a raw child array.
Definition: Preprocessor_Hash.php:1875
Preprocessor_Hash\newFrame
newFrame()
Definition: Preprocessor_Hash.php:61
$s
$s
Definition: mergeMessageFileList.php:188
PPNode_Hash_Tree\splitRawHeading
static splitRawHeading(array $children)
Like splitHeading() but for a raw child array.
Definition: Preprocessor_Hash.php:1963
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:302
so
c Accompany it with the information you received as to the offer to distribute corresponding source complete source code means all the source code for all modules it plus any associated interface definition plus the scripts used to control compilation and installation of the executable as a special the source code distributed need not include anything that is normally and so on of the operating system on which the executable unless that component itself accompanies the executable If distribution of executable or object code is made by offering access to copy from a designated then offering equivalent access to copy the source code from the same place counts as distribution of the source even though third parties are not compelled to copy the source along with the object code You may not or distribute the Program except as expressly provided under this License Any attempt otherwise to sublicense or distribute the Program is and will automatically terminate your rights under this License parties who have received or from you under this License will not have their licenses terminated so long as such parties remain in full compliance You are not required to accept this since you have not signed it nothing else grants you permission to modify or distribute the Program or its derivative works These actions are prohibited by law if you do not accept this License by modifying or distributing the you indicate your acceptance of this License to do so
Definition: COPYING.txt:185
Preprocessor_Hash
Differences from DOM schema:
Definition: Preprocessor_Hash.php:43
PPNode_Hash_Text\__construct
__construct(array $store, $index)
Construct an object using the data from $store[$index].
Definition: Preprocessor_Hash.php:2040
PPNode_Hash_Array\splitExt
splitExt()
Split an "<ext>" node into an associative array containing name, attr, inner and close All values in ...
Definition: Preprocessor_Hash.php:2143
names
alter the names
Definition: COPYING.txt:329
PPNode_Hash_Attr\$store
$store
Definition: Preprocessor_Hash.php:2160
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
Preprocessor
Definition: Preprocessor.php:29
PPNode_Hash_Array\splitArg
splitArg()
Split a "<part>" node into an associative array containing: name PPNode name index String index value...
Definition: Preprocessor_Hash.php:2139
Preprocessor_Hash\newCustomFrame
newCustomFrame( $args)
Definition: Preprocessor_Hash.php:69
PPNode_Hash_Array\getFirstChild
getFirstChild()
Get the first child of a tree node.
Definition: Preprocessor_Hash.php:2131
PPNode_Hash_Attr\item
item( $i)
Returns an item of an array-type node.
Definition: Preprocessor_Hash.php:2208
title
to move a page</td >< td > &*You are moving the page across *A non empty talk page already exists under the new or *You uncheck the box below In those you will have to move or merge the page manually if desired</td >< td > be sure to &You are responsible for making sure that links continue to point where they are supposed to go Note that the page will &a page at the new title
Definition: All_system_messages.txt:2696
PPNode_Hash_Attr\getFirstChild
getFirstChild()
Get the first child of a tree node.
Definition: Preprocessor_Hash.php:2196
PPNode_Hash_Attr\splitHeading
splitHeading()
Split an "<h>" node.
Definition: Preprocessor_Hash.php:2220
PPNode_Hash_Tree\$name
$name
Definition: Preprocessor_Hash.php:1682
PPNode_Hash_Attr\getChildrenOfType
getChildrenOfType( $name)
Get all children of this tree node which have a given name.
Definition: Preprocessor_Hash.php:2200
MWException
MediaWiki exception.
Definition: MWException.php:26
PPNode_Hash_Tree\CHILDREN
const CHILDREN
The offset of the child list within descriptors, used in some places for readability.
Definition: Preprocessor_Hash.php:1711
PPNode_Hash_Array\getChildrenOfType
getChildrenOfType( $name)
Get all children of this tree node which have a given name.
Definition: Preprocessor_Hash.php:2135
a
</source > ! result< div class="mw-highlight mw-content-ltr" dir="ltr">< pre >< span ></span >< span class="kd"> var</span >< span class="nx"> a</span >< span class="p"></span ></pre ></div > ! end ! test Multiline< source/> in lists !input *< source > a b</source > *foo< source > a b</source > ! html< ul >< li >< div class="mw-highlight mw-content-ltr" dir="ltr">< pre > a b</pre ></div ></li ></ul >< ul >< li > foo< div class="mw-highlight mw-content-ltr" dir="ltr">< pre > a b</pre ></div ></li ></ul > ! html tidy< ul >< li >< div class="mw-highlight mw-content-ltr" dir="ltr">< pre > a b</pre ></div ></li ></ul >< ul >< li > foo< div class="mw-highlight mw-content-ltr" dir="ltr">< pre > a b</pre ></div ></li ></ul > ! end ! test Custom attributes !input< source lang="javascript" id="foo" class="bar" dir="rtl" style="font-size: larger;"> var a
Definition: parserTests.txt:89
PPNode_Hash_Tree\getNextSibling
getNextSibling()
Get the next sibling, or false if there is none.
Definition: Preprocessor_Hash.php:1807
PPNode_Hash_Text\getNextSibling
getNextSibling()
Get the next sibling of any node.
Definition: Preprocessor_Hash.php:2053
PPNode_Hash_Text\__toString
__toString()
Definition: Preprocessor_Hash.php:2049
PPNode_Hash_Array\getLength
getLength()
Returns the length of the array, or false if this is not an array-type node.
Definition: Preprocessor_Hash.php:2111
Preprocessor_Hash\newPartNodeArray
newPartNodeArray( $values)
Definition: Preprocessor_Hash.php:77
PPNode_Hash_Tree\splitTemplate
splitTemplate()
Split a "<template>" or "<tplarg>" node.
Definition: Preprocessor_Hash.php:1987
PPNode_Hash_Tree\__toString
__toString()
Convert a node to XML, for debugging.
Definition: Preprocessor_Hash.php:1757
$matches
$matches
Definition: NoLocalSettings.php:24
PPDStackElement_Hash
Definition: Preprocessor_Hash.php:771
PPNode
There are three types of nodes:
Definition: Preprocessor.php:359
$attribs
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses after processing & $attribs
Definition: hooks.txt:1965
value
$status value
Definition: SyntaxHighlight.class.php:315
Preprocessor_Hash\CACHE_VERSION
const CACHE_VERSION
Definition: Preprocessor_Hash.php:52
store
MediaWiki s SiteStore can be cached and stored in a flat in a json format If the SiteStore is frequently the file cache may provide a performance benefit over a database store
Definition: sitescache.txt:1
Preprocessor_Hash\$parser
Parser $parser
Definition: Preprocessor_Hash.php:49
PPNode_Hash_Attr\$value
$value
Definition: Preprocessor_Hash.php:2159
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
PPNode_Hash_Tree\splitRawTemplate
static splitRawTemplate(array $children)
Like splitTemplate() but for a raw child array.
Definition: Preprocessor_Hash.php:1996
PPNode_Hash_Attr\getLength
getLength()
Returns the length of the array, or false if this is not an array-type node.
Definition: Preprocessor_Hash.php:2204
PPNode_Hash_Tree\__construct
__construct(array $store, $index)
Construct an object using the data from $store[$index].
Definition: Preprocessor_Hash.php:1720
$wgDisableLangConversion
$wgDisableLangConversion
Whether to enable language variant conversion.
Definition: DefaultSettings.php:3032
Preprocessor_Hash\CACHE_PREFIX
const CACHE_PREFIX
Definition: Preprocessor_Hash.php:51
list
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
PPNode_Hash_Text\splitExt
splitExt()
Split an "<ext>" node into an associative array containing name, attr, inner and close All values in ...
Definition: Preprocessor_Hash.php:2085
or
or
Definition: COPYING.txt:140
PPNode_Hash_Text\$value
$value
Definition: Preprocessor_Hash.php:2030
PPNode_Hash_Array
Definition: Preprocessor_Hash.php:2098
root
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such and we might be restricted by PHP settings such as safe mode or open_basedir We cannot assume that the software even has read access anywhere useful Many shared hosts run all users web applications under the same so they can t rely on Unix and must forbid reads to even standard directories like tmp lest users read each others files We cannot assume that the user has the ability to install or run any programs not written as web accessible PHP scripts Since anything that works on cheap shared hosting will work if you have shell or root access MediaWiki s design is based around catering to the lowest common denominator Although we support higher end setups as the way many things work by default is tailored toward shared hosting These defaults are unconventional from the point of view of and they certainly aren t ideal for someone who s installing MediaWiki as root
Definition: distributors.txt:39
PPNode_Hash_Text\getFirstChild
getFirstChild()
Get the first child of a tree node.
Definition: Preprocessor_Hash.php:2061
PPNode_Hash_Tree\getRawChildren
getRawChildren()
Get the raw child array.
Definition: Preprocessor_Hash.php:1831
PPNode_Hash_Text\$store
$store
Definition: Preprocessor_Hash.php:2031
PPNode_Hash_Tree\getName
getName()
Definition: Preprocessor_Hash.php:1853
$value
$value
Definition: styleTest.css.php:45
Preprocessor_Hash\preprocessToObj
preprocessToObj( $text, $flags=0)
Preprocess some wikitext and return the document tree.
Definition: Preprocessor_Hash.php:119
PPNode_Hash_Tree\getChildren
getChildren()
Definition: Preprocessor_Hash.php:1777
PPNode_Hash_Array\__construct
__construct( $value)
Definition: Preprocessor_Hash.php:2103
PPNode_Hash_Text\getName
getName()
Get the name of this node.
Definition: Preprocessor_Hash.php:2077
PPNode_Hash_Text\getChildren
getChildren()
Get an array-type node containing the children of this node.
Definition: Preprocessor_Hash.php:2057
PPNode_Hash_Tree\$index
$index
The index into $this->store which contains the descriptor of this node.
Definition: Preprocessor_Hash.php:1699
PPNode_Hash_Array\item
item( $i)
Returns an item of an array-type node.
Definition: Preprocessor_Hash.php:2115
PPNode_Hash_Array\getChildren
getChildren()
Get an array-type node containing the children of this node.
Definition: Preprocessor_Hash.php:2127
PPNode_Hash_Attr\getChildren
getChildren()
Get an array-type node containing the children of this node.
Definition: Preprocessor_Hash.php:2192
PPNode_Hash_Array\getName
getName()
Get the name of this node.
Definition: Preprocessor_Hash.php:2119
PPNode_Hash_Attr\getNextSibling
getNextSibling()
Get the next sibling of any node.
Definition: Preprocessor_Hash.php:2188
PPNode_Hash_Attr
Definition: Preprocessor_Hash.php:2156
$args
if( $line===false) $args
Definition: cdb.php:63
PPNode_Hash_Text
Definition: Preprocessor_Hash.php:2027
PPCustomFrame_Hash\getArgument
getArgument( $index)
Definition: Preprocessor_Hash.php:1663
PPNode_Hash_Tree\$rawChildren
$rawChildren
The store array for children of this node.
Definition: Preprocessor_Hash.php:1689
PPNode_Hash_Text\item
item( $i)
Returns an item of an array-type node.
Definition: Preprocessor_Hash.php:2073
PPNode_Hash_Attr\getName
getName()
Get the name of this node.
Definition: Preprocessor_Hash.php:2184
PPDStack_Hash
Stack class to help Preprocessor::preprocessToObj()
Definition: Preprocessor_Hash.php:757
PPNode_Hash_Array\$value
$value
Definition: Preprocessor_Hash.php:2101
Preprocessor_Hash\addLiteral
static addLiteral(array &$accum, $text)
Definition: Preprocessor_Hash.php:742
PPNode_Hash_Attr\splitArg
splitArg()
Split a "<part>" node into an associative array containing: name PPNode name index String index value...
Definition: Preprocessor_Hash.php:2212
PPNode_Hash_Array\getNextSibling
getNextSibling()
Get the next sibling of any node.
Definition: Preprocessor_Hash.php:2123
PPNode_Hash_Array\__toString
__toString()
Definition: Preprocessor_Hash.php:2107
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
PPCustomFrame_Hash\getArguments
getArguments()
Definition: Preprocessor_Hash.php:1670
PPCustomFrame_Hash
Expansion frame with custom arguments.
Definition: Preprocessor_Hash.php:1626
captcha-old.parser
parser
Definition: captcha-old.py:210
name
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at name
Definition: design.txt:12
PPNode_Hash_Attr\__construct
__construct(array $store, $index)
Construct an object using the data from $store[$index].
Definition: Preprocessor_Hash.php:2169
Preprocessor\cacheGetTree
cacheGetTree( $text, $flags)
Attempt to load a precomputed document tree for some given wikitext from the cache.
Definition: Preprocessor.php:96
PPNode_Hash_Text\splitArg
splitArg()
Split a "<part>" node into an associative array containing: name PPNode name index String index value...
Definition: Preprocessor_Hash.php:2081
PPNode_Hash_Tree
Definition: Preprocessor_Hash.php:1679
PPNode_Hash_Attr\__toString
__toString()
Definition: Preprocessor_Hash.php:2180
captcha-old.args
args
Definition: captcha-old.py:227
PPNode_Hash_Tree\factory
static factory(array $store, $index)
Construct an appropriate PPNode_Hash_* object with a class that depends on what is at the relevant st...
Definition: Preprocessor_Hash.php:1734
PPNode_Hash_Tree\NAME
const NAME
The offset of the name within descriptors, used in some places for readability.
Definition: Preprocessor_Hash.php:1705
$flags
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2801
PPNode_Hash_Tree\splitArg
splitArg()
Split a "<part>" node into an associative array containing:
Definition: Preprocessor_Hash.php:1866
array
the array() calling protocol came about after MediaWiki 1.4rc1.