MediaWiki  REL1_31
Preprocessor_Hash.php
Go to the documentation of this file.
1 <?php
42 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
44 
48  public $parser;
49 
50  const CACHE_PREFIX = 'preprocess-hash';
51  const CACHE_VERSION = 2;
52 
53  public function __construct( $parser ) {
54  $this->parser = $parser;
55  }
56 
60  public function newFrame() {
61  return new PPFrame_Hash( $this );
62  }
63 
68  public function newCustomFrame( $args ) {
69  return new PPCustomFrame_Hash( $this, $args );
70  }
71 
76  public function newPartNodeArray( $values ) {
77  $list = [];
78 
79  foreach ( $values as $k => $val ) {
80  if ( is_int( $k ) ) {
81  $store = [ [ 'part', [
82  [ 'name', [ [ '@index', [ $k ] ] ] ],
83  [ 'value', [ strval( $val ) ] ],
84  ] ] ];
85  } else {
86  $store = [ [ 'part', [
87  [ 'name', [ strval( $k ) ] ],
88  '=',
89  [ 'value', [ strval( $val ) ] ],
90  ] ] ];
91  }
92 
93  $list[] = new PPNode_Hash_Tree( $store, 0 );
94  }
95 
96  $node = new PPNode_Hash_Array( $list );
97  return $node;
98  }
99 
118  public function preprocessToObj( $text, $flags = 0 ) {
120 
121  $tree = $this->cacheGetTree( $text, $flags );
122  if ( $tree !== false ) {
123  $store = json_decode( $tree );
124  if ( is_array( $store ) ) {
125  return new PPNode_Hash_Tree( $store, 0 );
126  }
127  }
128 
129  $forInclusion = $flags & Parser::PTD_FOR_INCLUSION;
130 
131  $xmlishElements = $this->parser->getStripList();
132  $xmlishAllowMissingEndTag = [ 'includeonly', 'noinclude', 'onlyinclude' ];
133  $enableOnlyinclude = false;
134  if ( $forInclusion ) {
135  $ignoredTags = [ 'includeonly', '/includeonly' ];
136  $ignoredElements = [ 'noinclude' ];
137  $xmlishElements[] = 'noinclude';
138  if ( strpos( $text, '<onlyinclude>' ) !== false
139  && strpos( $text, '</onlyinclude>' ) !== false
140  ) {
141  $enableOnlyinclude = true;
142  }
143  } else {
144  $ignoredTags = [ 'noinclude', '/noinclude', 'onlyinclude', '/onlyinclude' ];
145  $ignoredElements = [ 'includeonly' ];
146  $xmlishElements[] = 'includeonly';
147  }
148  $xmlishRegex = implode( '|', array_merge( $xmlishElements, $ignoredTags ) );
149 
150  // Use "A" modifier (anchored) instead of "^", because ^ doesn't work with an offset
151  $elementsRegex = "~($xmlishRegex)(?:\s|\/>|>)|(!--)~iA";
152 
153  $stack = new PPDStack_Hash;
154 
155  $searchBase = "[{<\n";
156  if ( !$wgDisableLangConversion ) {
157  $searchBase .= '-';
158  }
159 
160  // For fast reverse searches
161  $revText = strrev( $text );
162  $lengthText = strlen( $text );
163 
164  // Input pointer, starts out pointing to a pseudo-newline before the start
165  $i = 0;
166  // Current accumulator. See the doc comment for Preprocessor_Hash for the format.
167  $accum =& $stack->getAccum();
168  // True to find equals signs in arguments
169  $findEquals = false;
170  // True to take notice of pipe characters
171  $findPipe = false;
172  $headingIndex = 1;
173  // True if $i is inside a possible heading
174  $inHeading = false;
175  // True if there are no more greater-than (>) signs right of $i
176  $noMoreGT = false;
177  // Map of tag name => true if there are no more closing tags of given type right of $i
178  $noMoreClosingTag = [];
179  // True to ignore all input up to the next <onlyinclude>
180  $findOnlyinclude = $enableOnlyinclude;
181  // Do a line-start run without outputting an LF character
182  $fakeLineStart = true;
183 
184  while ( true ) {
185  // $this->memCheck();
186 
187  if ( $findOnlyinclude ) {
188  // Ignore all input up to the next <onlyinclude>
189  $startPos = strpos( $text, '<onlyinclude>', $i );
190  if ( $startPos === false ) {
191  // Ignored section runs to the end
192  $accum[] = [ 'ignore', [ substr( $text, $i ) ] ];
193  break;
194  }
195  $tagEndPos = $startPos + strlen( '<onlyinclude>' ); // past-the-end
196  $accum[] = [ 'ignore', [ substr( $text, $i, $tagEndPos - $i ) ] ];
197  $i = $tagEndPos;
198  $findOnlyinclude = false;
199  }
200 
201  if ( $fakeLineStart ) {
202  $found = 'line-start';
203  $curChar = '';
204  } else {
205  # Find next opening brace, closing brace or pipe
206  $search = $searchBase;
207  if ( $stack->top === false ) {
208  $currentClosing = '';
209  } else {
210  $currentClosing = $stack->top->close;
211  $search .= $currentClosing;
212  }
213  if ( $findPipe ) {
214  $search .= '|';
215  }
216  if ( $findEquals ) {
217  // First equals will be for the template
218  $search .= '=';
219  }
220  $rule = null;
221  # Output literal section, advance input counter
222  $literalLength = strcspn( $text, $search, $i );
223  if ( $literalLength > 0 ) {
224  self::addLiteral( $accum, substr( $text, $i, $literalLength ) );
225  $i += $literalLength;
226  }
227  if ( $i >= $lengthText ) {
228  if ( $currentClosing == "\n" ) {
229  // Do a past-the-end run to finish off the heading
230  $curChar = '';
231  $found = 'line-end';
232  } else {
233  # All done
234  break;
235  }
236  } else {
237  $curChar = $curTwoChar = $text[$i];
238  if ( ( $i + 1 ) < $lengthText ) {
239  $curTwoChar .= $text[$i + 1];
240  }
241  if ( $curChar == '|' ) {
242  $found = 'pipe';
243  } elseif ( $curChar == '=' ) {
244  $found = 'equals';
245  } elseif ( $curChar == '<' ) {
246  $found = 'angle';
247  } elseif ( $curChar == "\n" ) {
248  if ( $inHeading ) {
249  $found = 'line-end';
250  } else {
251  $found = 'line-start';
252  }
253  } elseif ( $curTwoChar == $currentClosing ) {
254  $found = 'close';
255  $curChar = $curTwoChar;
256  } elseif ( $curChar == $currentClosing ) {
257  $found = 'close';
258  } elseif ( isset( $this->rules[$curTwoChar] ) ) {
259  $curChar = $curTwoChar;
260  $found = 'open';
261  $rule = $this->rules[$curChar];
262  } elseif ( isset( $this->rules[$curChar] ) ) {
263  $found = 'open';
264  $rule = $this->rules[$curChar];
265  } else {
266  # Some versions of PHP have a strcspn which stops on
267  # null characters; ignore these and continue.
268  # We also may get '-' and '}' characters here which
269  # don't match -{ or $currentClosing. Add these to
270  # output and continue.
271  if ( $curChar == '-' || $curChar == '}' ) {
272  self::addLiteral( $accum, $curChar );
273  }
274  ++$i;
275  continue;
276  }
277  }
278  }
279 
280  if ( $found == 'angle' ) {
281  $matches = false;
282  // Handle </onlyinclude>
283  if ( $enableOnlyinclude
284  && substr( $text, $i, strlen( '</onlyinclude>' ) ) == '</onlyinclude>'
285  ) {
286  $findOnlyinclude = true;
287  continue;
288  }
289 
290  // Determine element name
291  if ( !preg_match( $elementsRegex, $text, $matches, 0, $i + 1 ) ) {
292  // Element name missing or not listed
293  self::addLiteral( $accum, '<' );
294  ++$i;
295  continue;
296  }
297  // Handle comments
298  if ( isset( $matches[2] ) && $matches[2] == '!--' ) {
299  // To avoid leaving blank lines, when a sequence of
300  // space-separated comments is both preceded and followed by
301  // a newline (ignoring spaces), then
302  // trim leading and trailing spaces and the trailing newline.
303 
304  // Find the end
305  $endPos = strpos( $text, '-->', $i + 4 );
306  if ( $endPos === false ) {
307  // Unclosed comment in input, runs to end
308  $inner = substr( $text, $i );
309  $accum[] = [ 'comment', [ $inner ] ];
310  $i = $lengthText;
311  } else {
312  // Search backwards for leading whitespace
313  $wsStart = $i ? ( $i - strspn( $revText, " \t", $lengthText - $i ) ) : 0;
314 
315  // Search forwards for trailing whitespace
316  // $wsEnd will be the position of the last space (or the '>' if there's none)
317  $wsEnd = $endPos + 2 + strspn( $text, " \t", $endPos + 3 );
318 
319  // Keep looking forward as long as we're finding more
320  // comments.
321  $comments = [ [ $wsStart, $wsEnd ] ];
322  while ( substr( $text, $wsEnd + 1, 4 ) == '<!--' ) {
323  $c = strpos( $text, '-->', $wsEnd + 4 );
324  if ( $c === false ) {
325  break;
326  }
327  $c = $c + 2 + strspn( $text, " \t", $c + 3 );
328  $comments[] = [ $wsEnd + 1, $c ];
329  $wsEnd = $c;
330  }
331 
332  // Eat the line if possible
333  // TODO: This could theoretically be done if $wsStart == 0, i.e. for comments at
334  // the overall start. That's not how Sanitizer::removeHTMLcomments() did it, but
335  // it's a possible beneficial b/c break.
336  if ( $wsStart > 0 && substr( $text, $wsStart - 1, 1 ) == "\n"
337  && substr( $text, $wsEnd + 1, 1 ) == "\n"
338  ) {
339  // Remove leading whitespace from the end of the accumulator
340  $wsLength = $i - $wsStart;
341  $endIndex = count( $accum ) - 1;
342 
343  // Sanity check
344  if ( $wsLength > 0
345  && $endIndex >= 0
346  && is_string( $accum[$endIndex] )
347  && strspn( $accum[$endIndex], " \t", -$wsLength ) === $wsLength
348  ) {
349  $accum[$endIndex] = substr( $accum[$endIndex], 0, -$wsLength );
350  }
351 
352  // Dump all but the last comment to the accumulator
353  foreach ( $comments as $j => $com ) {
354  $startPos = $com[0];
355  $endPos = $com[1] + 1;
356  if ( $j == ( count( $comments ) - 1 ) ) {
357  break;
358  }
359  $inner = substr( $text, $startPos, $endPos - $startPos );
360  $accum[] = [ 'comment', [ $inner ] ];
361  }
362 
363  // Do a line-start run next time to look for headings after the comment
364  $fakeLineStart = true;
365  } else {
366  // No line to eat, just take the comment itself
367  $startPos = $i;
368  $endPos += 2;
369  }
370 
371  if ( $stack->top ) {
372  $part = $stack->top->getCurrentPart();
373  if ( !( isset( $part->commentEnd ) && $part->commentEnd == $wsStart - 1 ) ) {
374  $part->visualEnd = $wsStart;
375  }
376  // Else comments abutting, no change in visual end
377  $part->commentEnd = $endPos;
378  }
379  $i = $endPos + 1;
380  $inner = substr( $text, $startPos, $endPos - $startPos + 1 );
381  $accum[] = [ 'comment', [ $inner ] ];
382  }
383  continue;
384  }
385  $name = $matches[1];
386  $lowerName = strtolower( $name );
387  $attrStart = $i + strlen( $name ) + 1;
388 
389  // Find end of tag
390  $tagEndPos = $noMoreGT ? false : strpos( $text, '>', $attrStart );
391  if ( $tagEndPos === false ) {
392  // Infinite backtrack
393  // Disable tag search to prevent worst-case O(N^2) performance
394  $noMoreGT = true;
395  self::addLiteral( $accum, '<' );
396  ++$i;
397  continue;
398  }
399 
400  // Handle ignored tags
401  if ( in_array( $lowerName, $ignoredTags ) ) {
402  $accum[] = [ 'ignore', [ substr( $text, $i, $tagEndPos - $i + 1 ) ] ];
403  $i = $tagEndPos + 1;
404  continue;
405  }
406 
407  $tagStartPos = $i;
408  if ( $text[$tagEndPos - 1] == '/' ) {
409  // Short end tag
410  $attrEnd = $tagEndPos - 1;
411  $inner = null;
412  $i = $tagEndPos + 1;
413  $close = null;
414  } else {
415  $attrEnd = $tagEndPos;
416  // Find closing tag
417  if (
418  !isset( $noMoreClosingTag[$name] ) &&
419  preg_match( "/<\/" . preg_quote( $name, '/' ) . "\s*>/i",
420  $text, $matches, PREG_OFFSET_CAPTURE, $tagEndPos + 1 )
421  ) {
422  $inner = substr( $text, $tagEndPos + 1, $matches[0][1] - $tagEndPos - 1 );
423  $i = $matches[0][1] + strlen( $matches[0][0] );
424  $close = $matches[0][0];
425  } else {
426  // No end tag
427  if ( in_array( $name, $xmlishAllowMissingEndTag ) ) {
428  // Let it run out to the end of the text.
429  $inner = substr( $text, $tagEndPos + 1 );
430  $i = $lengthText;
431  $close = null;
432  } else {
433  // Don't match the tag, treat opening tag as literal and resume parsing.
434  $i = $tagEndPos + 1;
435  self::addLiteral( $accum,
436  substr( $text, $tagStartPos, $tagEndPos + 1 - $tagStartPos ) );
437  // Cache results, otherwise we have O(N^2) performance for input like <foo><foo><foo>...
438  $noMoreClosingTag[$name] = true;
439  continue;
440  }
441  }
442  }
443  // <includeonly> and <noinclude> just become <ignore> tags
444  if ( in_array( $lowerName, $ignoredElements ) ) {
445  $accum[] = [ 'ignore', [ substr( $text, $tagStartPos, $i - $tagStartPos ) ] ];
446  continue;
447  }
448 
449  if ( $attrEnd <= $attrStart ) {
450  $attr = '';
451  } else {
452  // Note that the attr element contains the whitespace between name and attribute,
453  // this is necessary for precise reconstruction during pre-save transform.
454  $attr = substr( $text, $attrStart, $attrEnd - $attrStart );
455  }
456 
457  $children = [
458  [ 'name', [ $name ] ],
459  [ 'attr', [ $attr ] ] ];
460  if ( $inner !== null ) {
461  $children[] = [ 'inner', [ $inner ] ];
462  }
463  if ( $close !== null ) {
464  $children[] = [ 'close', [ $close ] ];
465  }
466  $accum[] = [ 'ext', $children ];
467  } elseif ( $found == 'line-start' ) {
468  // Is this the start of a heading?
469  // Line break belongs before the heading element in any case
470  if ( $fakeLineStart ) {
471  $fakeLineStart = false;
472  } else {
473  self::addLiteral( $accum, $curChar );
474  $i++;
475  }
476 
477  $count = strspn( $text, '=', $i, 6 );
478  if ( $count == 1 && $findEquals ) {
479  // DWIM: This looks kind of like a name/value separator.
480  // Let's let the equals handler have it and break the potential
481  // heading. This is heuristic, but AFAICT the methods for
482  // completely correct disambiguation are very complex.
483  } elseif ( $count > 0 ) {
484  $piece = [
485  'open' => "\n",
486  'close' => "\n",
487  'parts' => [ new PPDPart_Hash( str_repeat( '=', $count ) ) ],
488  'startPos' => $i,
489  'count' => $count ];
490  $stack->push( $piece );
491  $accum =& $stack->getAccum();
492  $stackFlags = $stack->getFlags();
493  if ( isset( $stackFlags['findEquals'] ) ) {
494  $findEquals = $stackFlags['findEquals'];
495  }
496  if ( isset( $stackFlags['findPipe'] ) ) {
497  $findPipe = $stackFlags['findPipe'];
498  }
499  if ( isset( $stackFlags['inHeading'] ) ) {
500  $inHeading = $stackFlags['inHeading'];
501  }
502  $i += $count;
503  }
504  } elseif ( $found == 'line-end' ) {
505  $piece = $stack->top;
506  // A heading must be open, otherwise \n wouldn't have been in the search list
507  assert( $piece->open === "\n" );
508  $part = $piece->getCurrentPart();
509  // Search back through the input to see if it has a proper close.
510  // Do this using the reversed string since the other solutions
511  // (end anchor, etc.) are inefficient.
512  $wsLength = strspn( $revText, " \t", $lengthText - $i );
513  $searchStart = $i - $wsLength;
514  if ( isset( $part->commentEnd ) && $searchStart - 1 == $part->commentEnd ) {
515  // Comment found at line end
516  // Search for equals signs before the comment
517  $searchStart = $part->visualEnd;
518  $searchStart -= strspn( $revText, " \t", $lengthText - $searchStart );
519  }
520  $count = $piece->count;
521  $equalsLength = strspn( $revText, '=', $lengthText - $searchStart );
522  if ( $equalsLength > 0 ) {
523  if ( $searchStart - $equalsLength == $piece->startPos ) {
524  // This is just a single string of equals signs on its own line
525  // Replicate the doHeadings behavior /={count}(.+)={count}/
526  // First find out how many equals signs there really are (don't stop at 6)
527  $count = $equalsLength;
528  if ( $count < 3 ) {
529  $count = 0;
530  } else {
531  $count = min( 6, intval( ( $count - 1 ) / 2 ) );
532  }
533  } else {
534  $count = min( $equalsLength, $count );
535  }
536  if ( $count > 0 ) {
537  // Normal match, output <h>
538  $element = [ [ 'possible-h',
539  array_merge(
540  [
541  [ '@level', [ $count ] ],
542  [ '@i', [ $headingIndex++ ] ]
543  ],
544  $accum
545  )
546  ] ];
547  } else {
548  // Single equals sign on its own line, count=0
549  $element = $accum;
550  }
551  } else {
552  // No match, no <h>, just pass down the inner text
553  $element = $accum;
554  }
555  // Unwind the stack
556  $stack->pop();
557  $accum =& $stack->getAccum();
558  $stackFlags = $stack->getFlags();
559  if ( isset( $stackFlags['findEquals'] ) ) {
560  $findEquals = $stackFlags['findEquals'];
561  }
562  if ( isset( $stackFlags['findPipe'] ) ) {
563  $findPipe = $stackFlags['findPipe'];
564  }
565  if ( isset( $stackFlags['inHeading'] ) ) {
566  $inHeading = $stackFlags['inHeading'];
567  }
568 
569  // Append the result to the enclosing accumulator
570  array_splice( $accum, count( $accum ), 0, $element );
571 
572  // Note that we do NOT increment the input pointer.
573  // This is because the closing linebreak could be the opening linebreak of
574  // another heading. Infinite loops are avoided because the next iteration MUST
575  // hit the heading open case above, which unconditionally increments the
576  // input pointer.
577  } elseif ( $found == 'open' ) {
578  # count opening brace characters
579  $curLen = strlen( $curChar );
580  $count = ( $curLen > 1 ) ?
581  # allow the final character to repeat
582  strspn( $text, $curChar[$curLen - 1], $i + 1 ) + 1 :
583  strspn( $text, $curChar, $i );
584 
585  $savedPrefix = '';
586  $lineStart = ( $i > 0 && $text[$i - 1] == "\n" );
587 
588  if ( $curChar === "-{" && $count > $curLen ) {
589  // -{ => {{ transition because rightmost wins
590  $savedPrefix = '-';
591  $i++;
592  $curChar = '{';
593  $count--;
594  $rule = $this->rules[$curChar];
595  }
596 
597  # we need to add to stack only if opening brace count is enough for one of the rules
598  if ( $count >= $rule['min'] ) {
599  # Add it to the stack
600  $piece = [
601  'open' => $curChar,
602  'close' => $rule['end'],
603  'savedPrefix' => $savedPrefix,
604  'count' => $count,
605  'lineStart' => $lineStart,
606  ];
607 
608  $stack->push( $piece );
609  $accum =& $stack->getAccum();
610  $stackFlags = $stack->getFlags();
611  if ( isset( $stackFlags['findEquals'] ) ) {
612  $findEquals = $stackFlags['findEquals'];
613  }
614  if ( isset( $stackFlags['findPipe'] ) ) {
615  $findPipe = $stackFlags['findPipe'];
616  }
617  if ( isset( $stackFlags['inHeading'] ) ) {
618  $inHeading = $stackFlags['inHeading'];
619  }
620  } else {
621  # Add literal brace(s)
622  self::addLiteral( $accum, $savedPrefix . str_repeat( $curChar, $count ) );
623  }
624  $i += $count;
625  } elseif ( $found == 'close' ) {
626  $piece = $stack->top;
627  # lets check if there are enough characters for closing brace
628  $maxCount = $piece->count;
629  if ( $piece->close === '}-' && $curChar === '}' ) {
630  $maxCount--; # don't try to match closing '-' as a '}'
631  }
632  $curLen = strlen( $curChar );
633  $count = ( $curLen > 1 ) ? $curLen :
634  strspn( $text, $curChar, $i, $maxCount );
635 
636  # check for maximum matching characters (if there are 5 closing
637  # characters, we will probably need only 3 - depending on the rules)
638  $rule = $this->rules[$piece->open];
639  if ( $count > $rule['max'] ) {
640  # The specified maximum exists in the callback array, unless the caller
641  # has made an error
642  $matchingCount = $rule['max'];
643  } else {
644  # Count is less than the maximum
645  # Skip any gaps in the callback array to find the true largest match
646  # Need to use array_key_exists not isset because the callback can be null
647  $matchingCount = $count;
648  while ( $matchingCount > 0 && !array_key_exists( $matchingCount, $rule['names'] ) ) {
649  --$matchingCount;
650  }
651  }
652 
653  if ( $matchingCount <= 0 ) {
654  # No matching element found in callback array
655  # Output a literal closing brace and continue
656  $endText = substr( $text, $i, $count );
657  self::addLiteral( $accum, $endText );
658  $i += $count;
659  continue;
660  }
661  $name = $rule['names'][$matchingCount];
662  if ( $name === null ) {
663  // No element, just literal text
664  $endText = substr( $text, $i, $matchingCount );
665  $element = $piece->breakSyntax( $matchingCount );
666  self::addLiteral( $element, $endText );
667  } else {
668  # Create XML element
669  $parts = $piece->parts;
670  $titleAccum = $parts[0]->out;
671  unset( $parts[0] );
672 
673  $children = [];
674 
675  # The invocation is at the start of the line if lineStart is set in
676  # the stack, and all opening brackets are used up.
677  if ( $maxCount == $matchingCount &&
678  !empty( $piece->lineStart ) &&
679  strlen( $piece->savedPrefix ) == 0 ) {
680  $children[] = [ '@lineStart', [ 1 ] ];
681  }
682  $titleNode = [ 'title', $titleAccum ];
683  $children[] = $titleNode;
684  $argIndex = 1;
685  foreach ( $parts as $part ) {
686  if ( isset( $part->eqpos ) ) {
687  $equalsNode = $part->out[$part->eqpos];
688  $nameNode = [ 'name', array_slice( $part->out, 0, $part->eqpos ) ];
689  $valueNode = [ 'value', array_slice( $part->out, $part->eqpos + 1 ) ];
690  $partNode = [ 'part', [ $nameNode, $equalsNode, $valueNode ] ];
691  $children[] = $partNode;
692  } else {
693  $nameNode = [ 'name', [ [ '@index', [ $argIndex++ ] ] ] ];
694  $valueNode = [ 'value', $part->out ];
695  $partNode = [ 'part', [ $nameNode, $valueNode ] ];
696  $children[] = $partNode;
697  }
698  }
699  $element = [ [ $name, $children ] ];
700  }
701 
702  # Advance input pointer
703  $i += $matchingCount;
704 
705  # Unwind the stack
706  $stack->pop();
707  $accum =& $stack->getAccum();
708 
709  # Re-add the old stack element if it still has unmatched opening characters remaining
710  if ( $matchingCount < $piece->count ) {
711  $piece->parts = [ new PPDPart_Hash ];
712  $piece->count -= $matchingCount;
713  # do we still qualify for any callback with remaining count?
714  $min = $this->rules[$piece->open]['min'];
715  if ( $piece->count >= $min ) {
716  $stack->push( $piece );
717  $accum =& $stack->getAccum();
718  } elseif ( $piece->count == 1 && $piece->open === '{' && $piece->savedPrefix === '-' ) {
719  $piece->savedPrefix = '';
720  $piece->open = '-{';
721  $piece->count = 2;
722  $piece->close = $this->rules[$piece->open]['end'];
723  $stack->push( $piece );
724  $accum =& $stack->getAccum();
725  } else {
726  $s = substr( $piece->open, 0, -1 );
727  $s .= str_repeat(
728  substr( $piece->open, -1 ),
729  $piece->count - strlen( $s )
730  );
731  self::addLiteral( $accum, $piece->savedPrefix . $s );
732  }
733  } elseif ( $piece->savedPrefix !== '' ) {
734  self::addLiteral( $accum, $piece->savedPrefix );
735  }
736 
737  $stackFlags = $stack->getFlags();
738  if ( isset( $stackFlags['findEquals'] ) ) {
739  $findEquals = $stackFlags['findEquals'];
740  }
741  if ( isset( $stackFlags['findPipe'] ) ) {
742  $findPipe = $stackFlags['findPipe'];
743  }
744  if ( isset( $stackFlags['inHeading'] ) ) {
745  $inHeading = $stackFlags['inHeading'];
746  }
747 
748  # Add XML element to the enclosing accumulator
749  array_splice( $accum, count( $accum ), 0, $element );
750  } elseif ( $found == 'pipe' ) {
751  $findEquals = true; // shortcut for getFlags()
752  $stack->addPart();
753  $accum =& $stack->getAccum();
754  ++$i;
755  } elseif ( $found == 'equals' ) {
756  $findEquals = false; // shortcut for getFlags()
757  $accum[] = [ 'equals', [ '=' ] ];
758  $stack->getCurrentPart()->eqpos = count( $accum ) - 1;
759  ++$i;
760  }
761  }
762 
763  # Output any remaining unclosed brackets
764  foreach ( $stack->stack as $piece ) {
765  array_splice( $stack->rootAccum, count( $stack->rootAccum ), 0, $piece->breakSyntax() );
766  }
767 
768  # Enable top-level headings
769  foreach ( $stack->rootAccum as &$node ) {
770  if ( is_array( $node ) && $node[PPNode_Hash_Tree::NAME] === 'possible-h' ) {
771  $node[PPNode_Hash_Tree::NAME] = 'h';
772  }
773  }
774 
775  $rootStore = [ [ 'root', $stack->rootAccum ] ];
776  $rootNode = new PPNode_Hash_Tree( $rootStore, 0 );
777 
778  // Cache
779  $tree = json_encode( $rootStore, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
780  if ( $tree !== false ) {
781  $this->cacheSetTree( $text, $flags, $tree );
782  }
783 
784  return $rootNode;
785  }
786 
787  private static function addLiteral( array &$accum, $text ) {
788  $n = count( $accum );
789  if ( $n && is_string( $accum[$n - 1] ) ) {
790  $accum[$n - 1] .= $text;
791  } else {
792  $accum[] = $text;
793  }
794  }
795 }
796 
801 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
802 class PPDStack_Hash extends PPDStack {
803 
804  public function __construct() {
805  $this->elementClass = PPDStackElement_Hash::class;
806  parent::__construct();
807  $this->rootAccum = [];
808  }
809 }
810 
814 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
815 class PPDStackElement_Hash extends PPDStackElement {
816 
817  public function __construct( $data = [] ) {
818  $this->partClass = PPDPart_Hash::class;
819  parent::__construct( $data );
820  }
821 
828  public function breakSyntax( $openingCount = false ) {
829  if ( $this->open == "\n" ) {
830  $accum = array_merge( [ $this->savedPrefix ], $this->parts[0]->out );
831  } else {
832  if ( $openingCount === false ) {
833  $openingCount = $this->count;
834  }
835  $s = substr( $this->open, 0, -1 );
836  $s .= str_repeat(
837  substr( $this->open, -1 ),
838  $openingCount - strlen( $s )
839  );
840  $accum = [ $this->savedPrefix . $s ];
841  $lastIndex = 0;
842  $first = true;
843  foreach ( $this->parts as $part ) {
844  if ( $first ) {
845  $first = false;
846  } elseif ( is_string( $accum[$lastIndex] ) ) {
847  $accum[$lastIndex] .= '|';
848  } else {
849  $accum[++$lastIndex] = '|';
850  }
851  foreach ( $part->out as $node ) {
852  if ( is_string( $node ) && is_string( $accum[$lastIndex] ) ) {
853  $accum[$lastIndex] .= $node;
854  } else {
855  $accum[++$lastIndex] = $node;
856  }
857  }
858  }
859  }
860  return $accum;
861  }
862 }
863 
867 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
868 class PPDPart_Hash extends PPDPart {
869 
870  public function __construct( $out = '' ) {
871  if ( $out !== '' ) {
872  $accum = [ $out ];
873  } else {
874  $accum = [];
875  }
876  parent::__construct( $accum );
877  }
878 }
879 
884 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
885 class PPFrame_Hash implements PPFrame {
886 
890  public $parser;
891 
895  public $preprocessor;
896 
900  public $title;
901  public $titleCache;
902 
907  public $loopCheckHash;
908 
913  public $depth;
914 
915  private $volatile = false;
916  private $ttl = null;
917 
921  protected $childExpansionCache;
922 
927  public function __construct( $preprocessor ) {
928  $this->preprocessor = $preprocessor;
929  $this->parser = $preprocessor->parser;
930  $this->title = $this->parser->mTitle;
931  $this->titleCache = [ $this->title ? $this->title->getPrefixedDBkey() : false ];
932  $this->loopCheckHash = [];
933  $this->depth = 0;
934  $this->childExpansionCache = [];
935  }
936 
947  public function newChild( $args = false, $title = false, $indexOffset = 0 ) {
948  $namedArgs = [];
949  $numberedArgs = [];
950  if ( $title === false ) {
951  $title = $this->title;
952  }
953  if ( $args !== false ) {
954  if ( $args instanceof PPNode_Hash_Array ) {
955  $args = $args->value;
956  } elseif ( !is_array( $args ) ) {
957  throw new MWException( __METHOD__ . ': $args must be array or PPNode_Hash_Array' );
958  }
959  foreach ( $args as $arg ) {
960  $bits = $arg->splitArg();
961  if ( $bits['index'] !== '' ) {
962  // Numbered parameter
963  $index = $bits['index'] - $indexOffset;
964  if ( isset( $namedArgs[$index] ) || isset( $numberedArgs[$index] ) ) {
965  $this->parser->getOutput()->addWarning( wfMessage( 'duplicate-args-warning',
966  wfEscapeWikiText( $this->title ),
967  wfEscapeWikiText( $title ),
968  wfEscapeWikiText( $index ) )->text() );
969  $this->parser->addTrackingCategory( 'duplicate-args-category' );
970  }
971  $numberedArgs[$index] = $bits['value'];
972  unset( $namedArgs[$index] );
973  } else {
974  // Named parameter
975  $name = trim( $this->expand( $bits['name'], PPFrame::STRIP_COMMENTS ) );
976  if ( isset( $namedArgs[$name] ) || isset( $numberedArgs[$name] ) ) {
977  $this->parser->getOutput()->addWarning( wfMessage( 'duplicate-args-warning',
978  wfEscapeWikiText( $this->title ),
979  wfEscapeWikiText( $title ),
980  wfEscapeWikiText( $name ) )->text() );
981  $this->parser->addTrackingCategory( 'duplicate-args-category' );
982  }
983  $namedArgs[$name] = $bits['value'];
984  unset( $numberedArgs[$name] );
985  }
986  }
987  }
988  return new PPTemplateFrame_Hash( $this->preprocessor, $this, $numberedArgs, $namedArgs, $title );
989  }
990 
998  public function cachedExpand( $key, $root, $flags = 0 ) {
999  // we don't have a parent, so we don't have a cache
1000  return $this->expand( $root, $flags );
1001  }
1002 
1009  public function expand( $root, $flags = 0 ) {
1010  static $expansionDepth = 0;
1011  if ( is_string( $root ) ) {
1012  return $root;
1013  }
1014 
1015  if ( ++$this->parser->mPPNodeCount > $this->parser->mOptions->getMaxPPNodeCount() ) {
1016  $this->parser->limitationWarn( 'node-count-exceeded',
1017  $this->parser->mPPNodeCount,
1018  $this->parser->mOptions->getMaxPPNodeCount()
1019  );
1020  return '<span class="error">Node-count limit exceeded</span>';
1021  }
1022  if ( $expansionDepth > $this->parser->mOptions->getMaxPPExpandDepth() ) {
1023  $this->parser->limitationWarn( 'expansion-depth-exceeded',
1024  $expansionDepth,
1025  $this->parser->mOptions->getMaxPPExpandDepth()
1026  );
1027  return '<span class="error">Expansion depth limit exceeded</span>';
1028  }
1029  ++$expansionDepth;
1030  if ( $expansionDepth > $this->parser->mHighestExpansionDepth ) {
1031  $this->parser->mHighestExpansionDepth = $expansionDepth;
1032  }
1033 
1034  $outStack = [ '', '' ];
1035  $iteratorStack = [ false, $root ];
1036  $indexStack = [ 0, 0 ];
1037 
1038  while ( count( $iteratorStack ) > 1 ) {
1039  $level = count( $outStack ) - 1;
1040  $iteratorNode =& $iteratorStack[$level];
1041  $out =& $outStack[$level];
1042  $index =& $indexStack[$level];
1043 
1044  if ( is_array( $iteratorNode ) ) {
1045  if ( $index >= count( $iteratorNode ) ) {
1046  // All done with this iterator
1047  $iteratorStack[$level] = false;
1048  $contextNode = false;
1049  } else {
1050  $contextNode = $iteratorNode[$index];
1051  $index++;
1052  }
1053  } elseif ( $iteratorNode instanceof PPNode_Hash_Array ) {
1054  if ( $index >= $iteratorNode->getLength() ) {
1055  // All done with this iterator
1056  $iteratorStack[$level] = false;
1057  $contextNode = false;
1058  } else {
1059  $contextNode = $iteratorNode->item( $index );
1060  $index++;
1061  }
1062  } else {
1063  // Copy to $contextNode and then delete from iterator stack,
1064  // because this is not an iterator but we do have to execute it once
1065  $contextNode = $iteratorStack[$level];
1066  $iteratorStack[$level] = false;
1067  }
1068 
1069  $newIterator = false;
1070  $contextName = false;
1071  $contextChildren = false;
1072 
1073  if ( $contextNode === false ) {
1074  // nothing to do
1075  } elseif ( is_string( $contextNode ) ) {
1076  $out .= $contextNode;
1077  } elseif ( $contextNode instanceof PPNode_Hash_Array ) {
1078  $newIterator = $contextNode;
1079  } elseif ( $contextNode instanceof PPNode_Hash_Attr ) {
1080  // No output
1081  } elseif ( $contextNode instanceof PPNode_Hash_Text ) {
1082  $out .= $contextNode->value;
1083  } elseif ( $contextNode instanceof PPNode_Hash_Tree ) {
1084  $contextName = $contextNode->name;
1085  $contextChildren = $contextNode->getRawChildren();
1086  } elseif ( is_array( $contextNode ) ) {
1087  // Node descriptor array
1088  if ( count( $contextNode ) !== 2 ) {
1089  throw new MWException( __METHOD__.
1090  ': found an array where a node descriptor should be' );
1091  }
1092  list( $contextName, $contextChildren ) = $contextNode;
1093  } else {
1094  throw new MWException( __METHOD__ . ': Invalid parameter type' );
1095  }
1096 
1097  // Handle node descriptor array or tree object
1098  if ( $contextName === false ) {
1099  // Not a node, already handled above
1100  } elseif ( $contextName[0] === '@' ) {
1101  // Attribute: no output
1102  } elseif ( $contextName === 'template' ) {
1103  # Double-brace expansion
1104  $bits = PPNode_Hash_Tree::splitRawTemplate( $contextChildren );
1105  if ( $flags & PPFrame::NO_TEMPLATES ) {
1106  $newIterator = $this->virtualBracketedImplode(
1107  '{{', '|', '}}',
1108  $bits['title'],
1109  $bits['parts']
1110  );
1111  } else {
1112  $ret = $this->parser->braceSubstitution( $bits, $this );
1113  if ( isset( $ret['object'] ) ) {
1114  $newIterator = $ret['object'];
1115  } else {
1116  $out .= $ret['text'];
1117  }
1118  }
1119  } elseif ( $contextName === 'tplarg' ) {
1120  # Triple-brace expansion
1121  $bits = PPNode_Hash_Tree::splitRawTemplate( $contextChildren );
1122  if ( $flags & PPFrame::NO_ARGS ) {
1123  $newIterator = $this->virtualBracketedImplode(
1124  '{{{', '|', '}}}',
1125  $bits['title'],
1126  $bits['parts']
1127  );
1128  } else {
1129  $ret = $this->parser->argSubstitution( $bits, $this );
1130  if ( isset( $ret['object'] ) ) {
1131  $newIterator = $ret['object'];
1132  } else {
1133  $out .= $ret['text'];
1134  }
1135  }
1136  } elseif ( $contextName === 'comment' ) {
1137  # HTML-style comment
1138  # Remove it in HTML, pre+remove and STRIP_COMMENTS modes
1139  # Not in RECOVER_COMMENTS mode (msgnw) though.
1140  if ( ( $this->parser->ot['html']
1141  || ( $this->parser->ot['pre'] && $this->parser->mOptions->getRemoveComments() )
1142  || ( $flags & PPFrame::STRIP_COMMENTS )
1143  ) && !( $flags & PPFrame::RECOVER_COMMENTS )
1144  ) {
1145  $out .= '';
1146  } elseif ( $this->parser->ot['wiki'] && !( $flags & PPFrame::RECOVER_COMMENTS ) ) {
1147  # Add a strip marker in PST mode so that pstPass2() can
1148  # run some old-fashioned regexes on the result.
1149  # Not in RECOVER_COMMENTS mode (extractSections) though.
1150  $out .= $this->parser->insertStripItem( $contextChildren[0] );
1151  } else {
1152  # Recover the literal comment in RECOVER_COMMENTS and pre+no-remove
1153  $out .= $contextChildren[0];
1154  }
1155  } elseif ( $contextName === 'ignore' ) {
1156  # Output suppression used by <includeonly> etc.
1157  # OT_WIKI will only respect <ignore> in substed templates.
1158  # The other output types respect it unless NO_IGNORE is set.
1159  # extractSections() sets NO_IGNORE and so never respects it.
1160  if ( ( !isset( $this->parent ) && $this->parser->ot['wiki'] )
1161  || ( $flags & PPFrame::NO_IGNORE )
1162  ) {
1163  $out .= $contextChildren[0];
1164  } else {
1165  // $out .= '';
1166  }
1167  } elseif ( $contextName === 'ext' ) {
1168  # Extension tag
1169  $bits = PPNode_Hash_Tree::splitRawExt( $contextChildren ) +
1170  [ 'attr' => null, 'inner' => null, 'close' => null ];
1171  if ( $flags & PPFrame::NO_TAGS ) {
1172  $s = '<' . $bits['name']->getFirstChild()->value;
1173  if ( $bits['attr'] ) {
1174  $s .= $bits['attr']->getFirstChild()->value;
1175  }
1176  if ( $bits['inner'] ) {
1177  $s .= '>' . $bits['inner']->getFirstChild()->value;
1178  if ( $bits['close'] ) {
1179  $s .= $bits['close']->getFirstChild()->value;
1180  }
1181  } else {
1182  $s .= '/>';
1183  }
1184  $out .= $s;
1185  } else {
1186  $out .= $this->parser->extensionSubstitution( $bits, $this );
1187  }
1188  } elseif ( $contextName === 'h' ) {
1189  # Heading
1190  if ( $this->parser->ot['html'] ) {
1191  # Expand immediately and insert heading index marker
1192  $s = $this->expand( $contextChildren, $flags );
1193  $bits = PPNode_Hash_Tree::splitRawHeading( $contextChildren );
1194  $titleText = $this->title->getPrefixedDBkey();
1195  $this->parser->mHeadings[] = [ $titleText, $bits['i'] ];
1196  $serial = count( $this->parser->mHeadings ) - 1;
1197  $marker = Parser::MARKER_PREFIX . "-h-$serial-" . Parser::MARKER_SUFFIX;
1198  $s = substr( $s, 0, $bits['level'] ) . $marker . substr( $s, $bits['level'] );
1199  $this->parser->mStripState->addGeneral( $marker, '' );
1200  $out .= $s;
1201  } else {
1202  # Expand in virtual stack
1203  $newIterator = $contextChildren;
1204  }
1205  } else {
1206  # Generic recursive expansion
1207  $newIterator = $contextChildren;
1208  }
1209 
1210  if ( $newIterator !== false ) {
1211  $outStack[] = '';
1212  $iteratorStack[] = $newIterator;
1213  $indexStack[] = 0;
1214  } elseif ( $iteratorStack[$level] === false ) {
1215  // Return accumulated value to parent
1216  // With tail recursion
1217  while ( $iteratorStack[$level] === false && $level > 0 ) {
1218  $outStack[$level - 1] .= $out;
1219  array_pop( $outStack );
1220  array_pop( $iteratorStack );
1221  array_pop( $indexStack );
1222  $level--;
1223  }
1224  }
1225  }
1226  --$expansionDepth;
1227  return $outStack[0];
1228  }
1229 
1236  public function implodeWithFlags( $sep, $flags /*, ... */ ) {
1237  $args = array_slice( func_get_args(), 2 );
1238 
1239  $first = true;
1240  $s = '';
1241  foreach ( $args as $root ) {
1242  if ( $root instanceof PPNode_Hash_Array ) {
1243  $root = $root->value;
1244  }
1245  if ( !is_array( $root ) ) {
1246  $root = [ $root ];
1247  }
1248  foreach ( $root as $node ) {
1249  if ( $first ) {
1250  $first = false;
1251  } else {
1252  $s .= $sep;
1253  }
1254  $s .= $this->expand( $node, $flags );
1255  }
1256  }
1257  return $s;
1258  }
1259 
1267  public function implode( $sep /*, ... */ ) {
1268  $args = array_slice( func_get_args(), 1 );
1269 
1270  $first = true;
1271  $s = '';
1272  foreach ( $args as $root ) {
1273  if ( $root instanceof PPNode_Hash_Array ) {
1274  $root = $root->value;
1275  }
1276  if ( !is_array( $root ) ) {
1277  $root = [ $root ];
1278  }
1279  foreach ( $root as $node ) {
1280  if ( $first ) {
1281  $first = false;
1282  } else {
1283  $s .= $sep;
1284  }
1285  $s .= $this->expand( $node );
1286  }
1287  }
1288  return $s;
1289  }
1290 
1299  public function virtualImplode( $sep /*, ... */ ) {
1300  $args = array_slice( func_get_args(), 1 );
1301  $out = [];
1302  $first = true;
1303 
1304  foreach ( $args as $root ) {
1305  if ( $root instanceof PPNode_Hash_Array ) {
1306  $root = $root->value;
1307  }
1308  if ( !is_array( $root ) ) {
1309  $root = [ $root ];
1310  }
1311  foreach ( $root as $node ) {
1312  if ( $first ) {
1313  $first = false;
1314  } else {
1315  $out[] = $sep;
1316  }
1317  $out[] = $node;
1318  }
1319  }
1320  return new PPNode_Hash_Array( $out );
1321  }
1322 
1332  public function virtualBracketedImplode( $start, $sep, $end /*, ... */ ) {
1333  $args = array_slice( func_get_args(), 3 );
1334  $out = [ $start ];
1335  $first = true;
1336 
1337  foreach ( $args as $root ) {
1338  if ( $root instanceof PPNode_Hash_Array ) {
1339  $root = $root->value;
1340  }
1341  if ( !is_array( $root ) ) {
1342  $root = [ $root ];
1343  }
1344  foreach ( $root as $node ) {
1345  if ( $first ) {
1346  $first = false;
1347  } else {
1348  $out[] = $sep;
1349  }
1350  $out[] = $node;
1351  }
1352  }
1353  $out[] = $end;
1354  return new PPNode_Hash_Array( $out );
1355  }
1356 
1357  public function __toString() {
1358  return 'frame{}';
1359  }
1360 
1365  public function getPDBK( $level = false ) {
1366  if ( $level === false ) {
1367  return $this->title->getPrefixedDBkey();
1368  } else {
1369  return isset( $this->titleCache[$level] ) ? $this->titleCache[$level] : false;
1370  }
1371  }
1372 
1376  public function getArguments() {
1377  return [];
1378  }
1379 
1383  public function getNumberedArguments() {
1384  return [];
1385  }
1386 
1390  public function getNamedArguments() {
1391  return [];
1392  }
1393 
1399  public function isEmpty() {
1400  return true;
1401  }
1402 
1407  public function getArgument( $name ) {
1408  return false;
1409  }
1410 
1418  public function loopCheck( $title ) {
1419  return !isset( $this->loopCheckHash[$title->getPrefixedDBkey()] );
1420  }
1421 
1427  public function isTemplate() {
1428  return false;
1429  }
1430 
1436  public function getTitle() {
1437  return $this->title;
1438  }
1439 
1445  public function setVolatile( $flag = true ) {
1446  $this->volatile = $flag;
1447  }
1448 
1454  public function isVolatile() {
1455  return $this->volatile;
1456  }
1457 
1463  public function setTTL( $ttl ) {
1464  if ( $ttl !== null && ( $this->ttl === null || $ttl < $this->ttl ) ) {
1465  $this->ttl = $ttl;
1466  }
1467  }
1468 
1474  public function getTTL() {
1475  return $this->ttl;
1476  }
1477 }
1478 
1483 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
1484 class PPTemplateFrame_Hash extends PPFrame_Hash {
1485 
1486  public $numberedArgs, $namedArgs, $parent;
1487  public $numberedExpansionCache, $namedExpansionCache;
1488 
1496  public function __construct( $preprocessor, $parent = false, $numberedArgs = [],
1497  $namedArgs = [], $title = false
1498  ) {
1499  parent::__construct( $preprocessor );
1500 
1501  $this->parent = $parent;
1502  $this->numberedArgs = $numberedArgs;
1503  $this->namedArgs = $namedArgs;
1504  $this->title = $title;
1505  $pdbk = $title ? $title->getPrefixedDBkey() : false;
1506  $this->titleCache = $parent->titleCache;
1507  $this->titleCache[] = $pdbk;
1508  $this->loopCheckHash = /*clone*/ $parent->loopCheckHash;
1509  if ( $pdbk !== false ) {
1510  $this->loopCheckHash[$pdbk] = true;
1511  }
1512  $this->depth = $parent->depth + 1;
1513  $this->numberedExpansionCache = $this->namedExpansionCache = [];
1514  }
1515 
1516  public function __toString() {
1517  $s = 'tplframe{';
1518  $first = true;
1519  $args = $this->numberedArgs + $this->namedArgs;
1520  foreach ( $args as $name => $value ) {
1521  if ( $first ) {
1522  $first = false;
1523  } else {
1524  $s .= ', ';
1525  }
1526  $s .= "\"$name\":\"" .
1527  str_replace( '"', '\\"', $value->__toString() ) . '"';
1528  }
1529  $s .= '}';
1530  return $s;
1531  }
1532 
1540  public function cachedExpand( $key, $root, $flags = 0 ) {
1541  if ( isset( $this->parent->childExpansionCache[$key] ) ) {
1542  return $this->parent->childExpansionCache[$key];
1543  }
1544  $retval = $this->expand( $root, $flags );
1545  if ( !$this->isVolatile() ) {
1546  $this->parent->childExpansionCache[$key] = $retval;
1547  }
1548  return $retval;
1549  }
1550 
1556  public function isEmpty() {
1557  return !count( $this->numberedArgs ) && !count( $this->namedArgs );
1558  }
1559 
1563  public function getArguments() {
1564  $arguments = [];
1565  foreach ( array_merge(
1566  array_keys( $this->numberedArgs ),
1567  array_keys( $this->namedArgs ) ) as $key ) {
1568  $arguments[$key] = $this->getArgument( $key );
1569  }
1570  return $arguments;
1571  }
1572 
1576  public function getNumberedArguments() {
1577  $arguments = [];
1578  foreach ( array_keys( $this->numberedArgs ) as $key ) {
1579  $arguments[$key] = $this->getArgument( $key );
1580  }
1581  return $arguments;
1582  }
1583 
1587  public function getNamedArguments() {
1588  $arguments = [];
1589  foreach ( array_keys( $this->namedArgs ) as $key ) {
1590  $arguments[$key] = $this->getArgument( $key );
1591  }
1592  return $arguments;
1593  }
1594 
1599  public function getNumberedArgument( $index ) {
1600  if ( !isset( $this->numberedArgs[$index] ) ) {
1601  return false;
1602  }
1603  if ( !isset( $this->numberedExpansionCache[$index] ) ) {
1604  # No trimming for unnamed arguments
1605  $this->numberedExpansionCache[$index] = $this->parent->expand(
1606  $this->numberedArgs[$index],
1607  PPFrame::STRIP_COMMENTS
1608  );
1609  }
1610  return $this->numberedExpansionCache[$index];
1611  }
1612 
1617  public function getNamedArgument( $name ) {
1618  if ( !isset( $this->namedArgs[$name] ) ) {
1619  return false;
1620  }
1621  if ( !isset( $this->namedExpansionCache[$name] ) ) {
1622  # Trim named arguments post-expand, for backwards compatibility
1623  $this->namedExpansionCache[$name] = trim(
1624  $this->parent->expand( $this->namedArgs[$name], PPFrame::STRIP_COMMENTS ) );
1625  }
1626  return $this->namedExpansionCache[$name];
1627  }
1628 
1633  public function getArgument( $name ) {
1634  $text = $this->getNumberedArgument( $name );
1635  if ( $text === false ) {
1636  $text = $this->getNamedArgument( $name );
1637  }
1638  return $text;
1639  }
1640 
1646  public function isTemplate() {
1647  return true;
1648  }
1649 
1650  public function setVolatile( $flag = true ) {
1651  parent::setVolatile( $flag );
1652  $this->parent->setVolatile( $flag );
1653  }
1654 
1655  public function setTTL( $ttl ) {
1656  parent::setTTL( $ttl );
1657  $this->parent->setTTL( $ttl );
1658  }
1659 }
1660 
1665 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
1666 class PPCustomFrame_Hash extends PPFrame_Hash {
1667 
1668  public $args;
1669 
1670  public function __construct( $preprocessor, $args ) {
1671  parent::__construct( $preprocessor );
1672  $this->args = $args;
1673  }
1674 
1675  public function __toString() {
1676  $s = 'cstmframe{';
1677  $first = true;
1678  foreach ( $this->args as $name => $value ) {
1679  if ( $first ) {
1680  $first = false;
1681  } else {
1682  $s .= ', ';
1683  }
1684  $s .= "\"$name\":\"" .
1685  str_replace( '"', '\\"', $value->__toString() ) . '"';
1686  }
1687  $s .= '}';
1688  return $s;
1689  }
1690 
1694  public function isEmpty() {
1695  return !count( $this->args );
1696  }
1697 
1702  public function getArgument( $index ) {
1703  if ( !isset( $this->args[$index] ) ) {
1704  return false;
1705  }
1706  return $this->args[$index];
1707  }
1708 
1709  public function getArguments() {
1710  return $this->args;
1711  }
1712 }
1713 
1717 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
1718 class PPNode_Hash_Tree implements PPNode {
1719 
1720  public $name;
1721 
1727  private $rawChildren;
1728 
1732  private $store;
1733 
1737  private $index;
1738 
1743  const NAME = 0;
1744 
1749  const CHILDREN = 1;
1750 
1758  public function __construct( array $store, $index ) {
1759  $this->store = $store;
1760  $this->index = $index;
1761  list( $this->name, $this->rawChildren ) = $this->store[$index];
1762  }
1763 
1772  public static function factory( array $store, $index ) {
1773  if ( !isset( $store[$index] ) ) {
1774  return false;
1775  }
1776 
1777  $descriptor = $store[$index];
1778  if ( is_string( $descriptor ) ) {
1779  $class = PPNode_Hash_Text::class;
1780  } elseif ( is_array( $descriptor ) ) {
1781  if ( $descriptor[self::NAME][0] === '@' ) {
1782  $class = PPNode_Hash_Attr::class;
1783  } else {
1784  $class = self::class;
1785  }
1786  } else {
1787  throw new MWException( __METHOD__.': invalid node descriptor' );
1788  }
1789  return new $class( $store, $index );
1790  }
1791 
1795  public function __toString() {
1796  $inner = '';
1797  $attribs = '';
1798  for ( $node = $this->getFirstChild(); $node; $node = $node->getNextSibling() ) {
1799  if ( $node instanceof PPNode_Hash_Attr ) {
1800  $attribs .= ' ' . $node->name . '="' . htmlspecialchars( $node->value ) . '"';
1801  } else {
1802  $inner .= $node->__toString();
1803  }
1804  }
1805  if ( $inner === '' ) {
1806  return "<{$this->name}$attribs/>";
1807  } else {
1808  return "<{$this->name}$attribs>$inner</{$this->name}>";
1809  }
1810  }
1811 
1815  public function getChildren() {
1816  $children = [];
1817  foreach ( $this->rawChildren as $i => $child ) {
1818  $children[] = self::factory( $this->rawChildren, $i );
1819  }
1820  return new PPNode_Hash_Array( $children );
1821  }
1822 
1830  public function getFirstChild() {
1831  if ( !isset( $this->rawChildren[0] ) ) {
1832  return false;
1833  } else {
1834  return self::factory( $this->rawChildren, 0 );
1835  }
1836  }
1837 
1845  public function getNextSibling() {
1846  return self::factory( $this->store, $this->index + 1 );
1847  }
1848 
1855  public function getChildrenOfType( $name ) {
1856  $children = [];
1857  foreach ( $this->rawChildren as $i => $child ) {
1858  if ( is_array( $child ) && $child[self::NAME] === $name ) {
1859  $children[] = self::factory( $this->rawChildren, $i );
1860  }
1861  }
1862  return new PPNode_Hash_Array( $children );
1863  }
1864 
1869  public function getRawChildren() {
1870  return $this->rawChildren;
1871  }
1872 
1876  public function getLength() {
1877  return false;
1878  }
1879 
1884  public function item( $i ) {
1885  return false;
1886  }
1887 
1891  public function getName() {
1892  return $this->name;
1893  }
1894 
1904  public function splitArg() {
1905  return self::splitRawArg( $this->rawChildren );
1906  }
1907 
1913  public static function splitRawArg( array $children ) {
1914  $bits = [];
1915  foreach ( $children as $i => $child ) {
1916  if ( !is_array( $child ) ) {
1917  continue;
1918  }
1919  if ( $child[self::NAME] === 'name' ) {
1920  $bits['name'] = new self( $children, $i );
1921  if ( isset( $child[self::CHILDREN][0][self::NAME] )
1922  && $child[self::CHILDREN][0][self::NAME] === '@index'
1923  ) {
1924  $bits['index'] = $child[self::CHILDREN][0][self::CHILDREN][0];
1925  }
1926  } elseif ( $child[self::NAME] === 'value' ) {
1927  $bits['value'] = new self( $children, $i );
1928  }
1929  }
1930 
1931  if ( !isset( $bits['name'] ) ) {
1932  throw new MWException( 'Invalid brace node passed to ' . __METHOD__ );
1933  }
1934  if ( !isset( $bits['index'] ) ) {
1935  $bits['index'] = '';
1936  }
1937  return $bits;
1938  }
1939 
1947  public function splitExt() {
1948  return self::splitRawExt( $this->rawChildren );
1949  }
1950 
1956  public static function splitRawExt( array $children ) {
1957  $bits = [];
1958  foreach ( $children as $i => $child ) {
1959  if ( !is_array( $child ) ) {
1960  continue;
1961  }
1962  switch ( $child[self::NAME] ) {
1963  case 'name':
1964  $bits['name'] = new self( $children, $i );
1965  break;
1966  case 'attr':
1967  $bits['attr'] = new self( $children, $i );
1968  break;
1969  case 'inner':
1970  $bits['inner'] = new self( $children, $i );
1971  break;
1972  case 'close':
1973  $bits['close'] = new self( $children, $i );
1974  break;
1975  }
1976  }
1977  if ( !isset( $bits['name'] ) ) {
1978  throw new MWException( 'Invalid ext node passed to ' . __METHOD__ );
1979  }
1980  return $bits;
1981  }
1982 
1989  public function splitHeading() {
1990  if ( $this->name !== 'h' ) {
1991  throw new MWException( 'Invalid h node passed to ' . __METHOD__ );
1992  }
1993  return self::splitRawHeading( $this->rawChildren );
1994  }
1995 
2001  public static function splitRawHeading( array $children ) {
2002  $bits = [];
2003  foreach ( $children as $i => $child ) {
2004  if ( !is_array( $child ) ) {
2005  continue;
2006  }
2007  if ( $child[self::NAME] === '@i' ) {
2008  $bits['i'] = $child[self::CHILDREN][0];
2009  } elseif ( $child[self::NAME] === '@level' ) {
2010  $bits['level'] = $child[self::CHILDREN][0];
2011  }
2012  }
2013  if ( !isset( $bits['i'] ) ) {
2014  throw new MWException( 'Invalid h node passed to ' . __METHOD__ );
2015  }
2016  return $bits;
2017  }
2018 
2025  public function splitTemplate() {
2026  return self::splitRawTemplate( $this->rawChildren );
2027  }
2028 
2034  public static function splitRawTemplate( array $children ) {
2035  $parts = [];
2036  $bits = [ 'lineStart' => '' ];
2037  foreach ( $children as $i => $child ) {
2038  if ( !is_array( $child ) ) {
2039  continue;
2040  }
2041  switch ( $child[self::NAME] ) {
2042  case 'title':
2043  $bits['title'] = new self( $children, $i );
2044  break;
2045  case 'part':
2046  $parts[] = new self( $children, $i );
2047  break;
2048  case '@lineStart':
2049  $bits['lineStart'] = '1';
2050  break;
2051  }
2052  }
2053  if ( !isset( $bits['title'] ) ) {
2054  throw new MWException( 'Invalid node passed to ' . __METHOD__ );
2055  }
2056  $bits['parts'] = new PPNode_Hash_Array( $parts );
2057  return $bits;
2058  }
2059 }
2060 
2064 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
2065 class PPNode_Hash_Text implements PPNode {
2066 
2067  public $value;
2068  private $store, $index;
2069 
2077  public function __construct( array $store, $index ) {
2078  $this->value = $store[$index];
2079  if ( !is_scalar( $this->value ) ) {
2080  throw new MWException( __CLASS__ . ' given object instead of string' );
2081  }
2082  $this->store = $store;
2083  $this->index = $index;
2084  }
2085 
2086  public function __toString() {
2087  return htmlspecialchars( $this->value );
2088  }
2089 
2090  public function getNextSibling() {
2091  return PPNode_Hash_Tree::factory( $this->store, $this->index + 1 );
2092  }
2093 
2094  public function getChildren() {
2095  return false;
2096  }
2097 
2098  public function getFirstChild() {
2099  return false;
2100  }
2101 
2102  public function getChildrenOfType( $name ) {
2103  return false;
2104  }
2105 
2106  public function getLength() {
2107  return false;
2108  }
2109 
2110  public function item( $i ) {
2111  return false;
2112  }
2113 
2114  public function getName() {
2115  return '#text';
2116  }
2117 
2118  public function splitArg() {
2119  throw new MWException( __METHOD__ . ': not supported' );
2120  }
2121 
2122  public function splitExt() {
2123  throw new MWException( __METHOD__ . ': not supported' );
2124  }
2125 
2126  public function splitHeading() {
2127  throw new MWException( __METHOD__ . ': not supported' );
2128  }
2129 }
2130 
2134 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
2135 class PPNode_Hash_Array implements PPNode {
2136 
2137  public $value;
2138 
2139  public function __construct( $value ) {
2140  $this->value = $value;
2141  }
2142 
2143  public function __toString() {
2144  return var_export( $this, true );
2145  }
2146 
2147  public function getLength() {
2148  return count( $this->value );
2149  }
2150 
2151  public function item( $i ) {
2152  return $this->value[$i];
2153  }
2154 
2155  public function getName() {
2156  return '#nodelist';
2157  }
2158 
2159  public function getNextSibling() {
2160  return false;
2161  }
2162 
2163  public function getChildren() {
2164  return false;
2165  }
2166 
2167  public function getFirstChild() {
2168  return false;
2169  }
2170 
2171  public function getChildrenOfType( $name ) {
2172  return false;
2173  }
2174 
2175  public function splitArg() {
2176  throw new MWException( __METHOD__ . ': not supported' );
2177  }
2178 
2179  public function splitExt() {
2180  throw new MWException( __METHOD__ . ': not supported' );
2181  }
2182 
2183  public function splitHeading() {
2184  throw new MWException( __METHOD__ . ': not supported' );
2185  }
2186 }
2187 
2191 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
2192 class PPNode_Hash_Attr implements PPNode {
2193 
2194  public $name, $value;
2195  private $store, $index;
2196 
2204  public function __construct( array $store, $index ) {
2205  $descriptor = $store[$index];
2206  if ( $descriptor[PPNode_Hash_Tree::NAME][0] !== '@' ) {
2207  throw new MWException( __METHOD__.': invalid name in attribute descriptor' );
2208  }
2209  $this->name = substr( $descriptor[PPNode_Hash_Tree::NAME], 1 );
2210  $this->value = $descriptor[PPNode_Hash_Tree::CHILDREN][0];
2211  $this->store = $store;
2212  $this->index = $index;
2213  }
2214 
2215  public function __toString() {
2216  return "<@{$this->name}>" . htmlspecialchars( $this->value ) . "</@{$this->name}>";
2217  }
2218 
2219  public function getName() {
2220  return $this->name;
2221  }
2222 
2223  public function getNextSibling() {
2224  return PPNode_Hash_Tree::factory( $this->store, $this->index + 1 );
2225  }
2226 
2227  public function getChildren() {
2228  return false;
2229  }
2230 
2231  public function getFirstChild() {
2232  return false;
2233  }
2234 
2235  public function getChildrenOfType( $name ) {
2236  return false;
2237  }
2238 
2239  public function getLength() {
2240  return false;
2241  }
2242 
2243  public function item( $i ) {
2244  return false;
2245  }
2246 
2247  public function splitArg() {
2248  throw new MWException( __METHOD__ . ': not supported' );
2249  }
2250 
2251  public function splitExt() {
2252  throw new MWException( __METHOD__ . ': not supported' );
2253  }
2254 
2255  public function splitHeading() {
2256  throw new MWException( __METHOD__ . ': not supported' );
2257  }
2258 }
PPNode_Hash_Tree\getLength
getLength()
Definition: Preprocessor_Hash.php:1876
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 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:30
PPNode_Hash_Tree\getChildrenOfType
getChildrenOfType( $name)
Get an array of the children with a given node name.
Definition: Preprocessor_Hash.php:1855
PPNode_Hash_Tree\item
item( $i)
Definition: Preprocessor_Hash.php:1884
PPDPart_Hash
Definition: Preprocessor_Hash.php:868
names
within a display generated by the Derivative if and wherever such third party notices normally appear The contents of the NOTICE file are for informational purposes only and do not modify the License You may add Your own attribution notices within Derivative Works that You alongside or as an addendum to the NOTICE text from the provided that such additional attribution notices cannot be construed as modifying the License You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for or distribution of Your or for any such Derivative Works as a provided Your and distribution of the Work otherwise complies with the conditions stated in this License Submission of Contributions Unless You explicitly state any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this without any additional terms or conditions Notwithstanding the nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions Trademarks This License does not grant permission to use the trade names
Definition: APACHE-LICENSE-2.0.txt:140
PPNode_Hash_Tree\$store
$store
The store array for the siblings of this node, including this node itself.
Definition: Preprocessor_Hash.php:1732
PPNode_Hash_Array\splitHeading
splitHeading()
Split an "<h>" node.
Definition: Preprocessor_Hash.php:2183
array
the array() calling protocol came about after MediaWiki 1.4rc1.
PPNode_Hash_Tree\getFirstChild
getFirstChild()
Get the first child, or false if there is none.
Definition: Preprocessor_Hash.php:1830
PPNode_Hash_Tree\splitRawExt
static splitRawExt(array $children)
Like splitExt() but for a raw child array.
Definition: Preprocessor_Hash.php:1956
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:18
Preprocessor_Hash\__construct
__construct( $parser)
Definition: Preprocessor_Hash.php:53
PPFrame_Hash
An expansion frame, used as a context to expand the result of preprocessToObj()
Definition: Preprocessor_Hash.php:885
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:27
PPNode_Hash_Text\splitHeading
splitHeading()
Split an "<h>" node.
Definition: Preprocessor_Hash.php:2126
PPNode_Hash_Tree\splitHeading
splitHeading()
Split an "<h>" node.
Definition: Preprocessor_Hash.php:1989
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:2251
PPCustomFrame_Hash\isEmpty
isEmpty()
Definition: Preprocessor_Hash.php:1694
PPNode_Hash_Text\getChildrenOfType
getChildrenOfType( $name)
Get all children of this tree node which have a given name.
Definition: Preprocessor_Hash.php:2102
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:1947
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:2106
PPNode_Hash_Tree\splitRawArg
static splitRawArg(array $children)
Like splitArg() but for a raw child array.
Definition: Preprocessor_Hash.php:1913
Preprocessor_Hash\newFrame
newFrame()
Definition: Preprocessor_Hash.php:60
$s
$s
Definition: mergeMessageFileList.php:187
PPNode_Hash_Tree\splitRawHeading
static splitRawHeading(array $children)
Like splitHeading() but for a raw child array.
Definition: Preprocessor_Hash.php:2001
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:2077
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:2179
PPNode_Hash_Attr\$store
$store
Definition: Preprocessor_Hash.php:2195
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:37
part
in this case you re responsible for computing and outputting the entire conflict part
Definition: hooks.txt:1421
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:2175
Preprocessor_Hash\newCustomFrame
newCustomFrame( $args)
Definition: Preprocessor_Hash.php:68
PPNode_Hash_Array\getFirstChild
getFirstChild()
Get the first child of a tree node.
Definition: Preprocessor_Hash.php:2167
PPNode_Hash_Attr\item
item( $i)
Returns an item of an array-type node.
Definition: Preprocessor_Hash.php:2243
PPNode_Hash_Attr\getFirstChild
getFirstChild()
Get the first child of a tree node.
Definition: Preprocessor_Hash.php:2231
PPNode_Hash_Attr\splitHeading
splitHeading()
Split an "<h>" node.
Definition: Preprocessor_Hash.php:2255
PPNode_Hash_Tree\$name
$name
Definition: Preprocessor_Hash.php:1720
PPNode_Hash_Attr\getChildrenOfType
getChildrenOfType( $name)
Get all children of this tree node which have a given name.
Definition: Preprocessor_Hash.php:2235
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:1749
PPNode_Hash_Array\getChildrenOfType
getChildrenOfType( $name)
Get all children of this tree node which have a given name.
Definition: Preprocessor_Hash.php:2171
PPNode_Hash_Tree\getNextSibling
getNextSibling()
Get the next sibling, or false if there is none.
Definition: Preprocessor_Hash.php:1845
PPNode_Hash_Text\getNextSibling
getNextSibling()
Get the next sibling of any node.
Definition: Preprocessor_Hash.php:2090
PPNode_Hash_Text\__toString
__toString()
Definition: Preprocessor_Hash.php:2086
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:2147
Preprocessor_Hash\newPartNodeArray
newPartNodeArray( $values)
Definition: Preprocessor_Hash.php:76
PPNode_Hash_Tree\splitTemplate
splitTemplate()
Split a "<template>" or "<tplarg>" node.
Definition: Preprocessor_Hash.php:2025
PPNode_Hash_Tree\__toString
__toString()
Convert a node to XML, for debugging.
Definition: Preprocessor_Hash.php:1795
$matches
$matches
Definition: NoLocalSettings.php:24
PPNode
There are three types of nodes:
Definition: Preprocessor.php:359
title
title
Definition: parserTests.txt:219
Preprocessor_Hash\CACHE_VERSION
const CACHE_VERSION
Definition: Preprocessor_Hash.php:51
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:4
Preprocessor_Hash\$parser
Parser $parser
Definition: Preprocessor_Hash.php:48
Parser\PTD_FOR_INCLUSION
const PTD_FOR_INCLUSION
Definition: Parser.php:107
PPNode_Hash_Attr\$value
$value
Definition: Preprocessor_Hash.php:2194
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:95
PPNode_Hash_Tree\splitRawTemplate
static splitRawTemplate(array $children)
Like splitTemplate() but for a raw child array.
Definition: Preprocessor_Hash.php:2034
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:2239
PPNode_Hash_Tree\__construct
__construct(array $store, $index)
Construct an object using the data from $store[$index].
Definition: Preprocessor_Hash.php:1758
$wgDisableLangConversion
$wgDisableLangConversion
Whether to enable language variant conversion.
Definition: DefaultSettings.php:3058
Preprocessor_Hash\CACHE_PREFIX
const CACHE_PREFIX
Definition: Preprocessor_Hash.php:50
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:2122
PPNode_Hash_Text\$value
$value
Definition: Preprocessor_Hash.php:2067
PPNode_Hash_Array
Definition: Preprocessor_Hash.php:2135
$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:2014
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:2098
PPNode_Hash_Tree\getRawChildren
getRawChildren()
Get the raw child array.
Definition: Preprocessor_Hash.php:1869
PPNode_Hash_Text\$store
$store
Definition: Preprocessor_Hash.php:2068
PPNode_Hash_Tree\getName
getName()
Definition: Preprocessor_Hash.php:1891
$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:118
PPNode_Hash_Tree\getChildren
getChildren()
Definition: Preprocessor_Hash.php:1815
PPNode_Hash_Array\__construct
__construct( $value)
Definition: Preprocessor_Hash.php:2139
PPNode_Hash_Text\getName
getName()
Get the name of this node.
Definition: Preprocessor_Hash.php:2114
PPNode_Hash_Text\getChildren
getChildren()
Get an array-type node containing the children of this node.
Definition: Preprocessor_Hash.php:2094
PPNode_Hash_Tree\$index
$index
The index into $this->store which contains the descriptor of this node.
Definition: Preprocessor_Hash.php:1737
PPNode_Hash_Array\item
item( $i)
Returns an item of an array-type node.
Definition: Preprocessor_Hash.php:2151
so
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to and or sell copies of the and to permit persons to whom the Software is furnished to do so
Definition: LICENSE.txt:13
Parser
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
Definition: Parser.php:70
PPNode_Hash_Array\getChildren
getChildren()
Get an array-type node containing the children of this node.
Definition: Preprocessor_Hash.php:2163
PPNode_Hash_Attr\getChildren
getChildren()
Get an array-type node containing the children of this node.
Definition: Preprocessor_Hash.php:2227
PPNode_Hash_Array\getName
getName()
Get the name of this node.
Definition: Preprocessor_Hash.php:2155
PPNode_Hash_Attr\getNextSibling
getNextSibling()
Get the next sibling of any node.
Definition: Preprocessor_Hash.php:2223
PPNode_Hash_Attr
Definition: Preprocessor_Hash.php:2192
$args
if( $line===false) $args
Definition: cdb.php:64
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:302
PPNode_Hash_Text
Definition: Preprocessor_Hash.php:2065
PPCustomFrame_Hash\getArgument
getArgument( $index)
Definition: Preprocessor_Hash.php:1702
PPNode_Hash_Tree\$rawChildren
$rawChildren
The store array for children of this node.
Definition: Preprocessor_Hash.php:1727
PPNode_Hash_Text\item
item( $i)
Returns an item of an array-type node.
Definition: Preprocessor_Hash.php:2110
PPNode_Hash_Attr\getName
getName()
Get the name of this node.
Definition: Preprocessor_Hash.php:2219
PPDStack_Hash
Stack class to help Preprocessor::preprocessToObj()
Definition: Preprocessor_Hash.php:802
PPNode_Hash_Array\$value
$value
Definition: Preprocessor_Hash.php:2137
Preprocessor_Hash\addLiteral
static addLiteral(array &$accum, $text)
Definition: Preprocessor_Hash.php:787
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:2247
or
or
Definition: APACHE-LICENSE-2.0.txt:114
PPNode_Hash_Array\getNextSibling
getNextSibling()
Get the next sibling of any node.
Definition: Preprocessor_Hash.php:2159
PPNode_Hash_Array\__toString
__toString()
Definition: Preprocessor_Hash.php:2143
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:22
PPCustomFrame_Hash\getArguments
getArguments()
Definition: Preprocessor_Hash.php:1709
PPCustomFrame_Hash
Expansion frame with custom arguments.
Definition: Preprocessor_Hash.php:1666
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
class
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:56
PPNode_Hash_Attr\__construct
__construct(array $store, $index)
Construct an object using the data from $store[$index].
Definition: Preprocessor_Hash.php:2204
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:2118
PPNode_Hash_Tree
Definition: Preprocessor_Hash.php:1718
PPNode_Hash_Attr\__toString
__toString()
Definition: Preprocessor_Hash.php:2215
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:187
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:1772
PPNode_Hash_Tree\NAME
const NAME
The offset of the name within descriptors, used in some places for readability.
Definition: Preprocessor_Hash.php:1743
PPNode_Hash_Tree\splitArg
splitArg()
Split a "<part>" node into an associative array containing:
Definition: Preprocessor_Hash.php:1904