MediaWiki  1.32.5
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  // FIXME: Don't use assert()
508  // phpcs:ignore MediaWiki.Usage.ForbiddenFunctions.assert
509  assert( $piece->open === "\n" );
510  $part = $piece->getCurrentPart();
511  // Search back through the input to see if it has a proper close.
512  // Do this using the reversed string since the other solutions
513  // (end anchor, etc.) are inefficient.
514  $wsLength = strspn( $revText, " \t", $lengthText - $i );
515  $searchStart = $i - $wsLength;
516  if ( isset( $part->commentEnd ) && $searchStart - 1 == $part->commentEnd ) {
517  // Comment found at line end
518  // Search for equals signs before the comment
519  $searchStart = $part->visualEnd;
520  $searchStart -= strspn( $revText, " \t", $lengthText - $searchStart );
521  }
522  $count = $piece->count;
523  $equalsLength = strspn( $revText, '=', $lengthText - $searchStart );
524  if ( $equalsLength > 0 ) {
525  if ( $searchStart - $equalsLength == $piece->startPos ) {
526  // This is just a single string of equals signs on its own line
527  // Replicate the doHeadings behavior /={count}(.+)={count}/
528  // First find out how many equals signs there really are (don't stop at 6)
529  $count = $equalsLength;
530  if ( $count < 3 ) {
531  $count = 0;
532  } else {
533  $count = min( 6, intval( ( $count - 1 ) / 2 ) );
534  }
535  } else {
536  $count = min( $equalsLength, $count );
537  }
538  if ( $count > 0 ) {
539  // Normal match, output <h>
540  $element = [ [ 'possible-h',
541  array_merge(
542  [
543  [ '@level', [ $count ] ],
544  [ '@i', [ $headingIndex++ ] ]
545  ],
546  $accum
547  )
548  ] ];
549  } else {
550  // Single equals sign on its own line, count=0
551  $element = $accum;
552  }
553  } else {
554  // No match, no <h>, just pass down the inner text
555  $element = $accum;
556  }
557  // Unwind the stack
558  $stack->pop();
559  $accum =& $stack->getAccum();
560  $stackFlags = $stack->getFlags();
561  if ( isset( $stackFlags['findEquals'] ) ) {
562  $findEquals = $stackFlags['findEquals'];
563  }
564  if ( isset( $stackFlags['findPipe'] ) ) {
565  $findPipe = $stackFlags['findPipe'];
566  }
567  if ( isset( $stackFlags['inHeading'] ) ) {
568  $inHeading = $stackFlags['inHeading'];
569  }
570 
571  // Append the result to the enclosing accumulator
572  array_splice( $accum, count( $accum ), 0, $element );
573 
574  // Note that we do NOT increment the input pointer.
575  // This is because the closing linebreak could be the opening linebreak of
576  // another heading. Infinite loops are avoided because the next iteration MUST
577  // hit the heading open case above, which unconditionally increments the
578  // input pointer.
579  } elseif ( $found == 'open' ) {
580  # count opening brace characters
581  $curLen = strlen( $curChar );
582  $count = ( $curLen > 1 ) ?
583  # allow the final character to repeat
584  strspn( $text, $curChar[$curLen - 1], $i + 1 ) + 1 :
585  strspn( $text, $curChar, $i );
586 
587  $savedPrefix = '';
588  $lineStart = ( $i > 0 && $text[$i - 1] == "\n" );
589 
590  if ( $curChar === "-{" && $count > $curLen ) {
591  // -{ => {{ transition because rightmost wins
592  $savedPrefix = '-';
593  $i++;
594  $curChar = '{';
595  $count--;
596  $rule = $this->rules[$curChar];
597  }
598 
599  # we need to add to stack only if opening brace count is enough for one of the rules
600  if ( $count >= $rule['min'] ) {
601  # Add it to the stack
602  $piece = [
603  'open' => $curChar,
604  'close' => $rule['end'],
605  'savedPrefix' => $savedPrefix,
606  'count' => $count,
607  'lineStart' => $lineStart,
608  ];
609 
610  $stack->push( $piece );
611  $accum =& $stack->getAccum();
612  $stackFlags = $stack->getFlags();
613  if ( isset( $stackFlags['findEquals'] ) ) {
614  $findEquals = $stackFlags['findEquals'];
615  }
616  if ( isset( $stackFlags['findPipe'] ) ) {
617  $findPipe = $stackFlags['findPipe'];
618  }
619  if ( isset( $stackFlags['inHeading'] ) ) {
620  $inHeading = $stackFlags['inHeading'];
621  }
622  } else {
623  # Add literal brace(s)
624  self::addLiteral( $accum, $savedPrefix . str_repeat( $curChar, $count ) );
625  }
626  $i += $count;
627  } elseif ( $found == 'close' ) {
628  $piece = $stack->top;
629  # lets check if there are enough characters for closing brace
630  $maxCount = $piece->count;
631  if ( $piece->close === '}-' && $curChar === '}' ) {
632  $maxCount--; # don't try to match closing '-' as a '}'
633  }
634  $curLen = strlen( $curChar );
635  $count = ( $curLen > 1 ) ? $curLen :
636  strspn( $text, $curChar, $i, $maxCount );
637 
638  # check for maximum matching characters (if there are 5 closing
639  # characters, we will probably need only 3 - depending on the rules)
640  $rule = $this->rules[$piece->open];
641  if ( $count > $rule['max'] ) {
642  # The specified maximum exists in the callback array, unless the caller
643  # has made an error
644  $matchingCount = $rule['max'];
645  } else {
646  # Count is less than the maximum
647  # Skip any gaps in the callback array to find the true largest match
648  # Need to use array_key_exists not isset because the callback can be null
649  $matchingCount = $count;
650  while ( $matchingCount > 0 && !array_key_exists( $matchingCount, $rule['names'] ) ) {
651  --$matchingCount;
652  }
653  }
654 
655  if ( $matchingCount <= 0 ) {
656  # No matching element found in callback array
657  # Output a literal closing brace and continue
658  $endText = substr( $text, $i, $count );
659  self::addLiteral( $accum, $endText );
660  $i += $count;
661  continue;
662  }
663  $name = $rule['names'][$matchingCount];
664  if ( $name === null ) {
665  // No element, just literal text
666  $endText = substr( $text, $i, $matchingCount );
667  $element = $piece->breakSyntax( $matchingCount );
668  self::addLiteral( $element, $endText );
669  } else {
670  # Create XML element
671  $parts = $piece->parts;
672  $titleAccum = $parts[0]->out;
673  unset( $parts[0] );
674 
675  $children = [];
676 
677  # The invocation is at the start of the line if lineStart is set in
678  # the stack, and all opening brackets are used up.
679  if ( $maxCount == $matchingCount &&
680  !empty( $piece->lineStart ) &&
681  strlen( $piece->savedPrefix ) == 0 ) {
682  $children[] = [ '@lineStart', [ 1 ] ];
683  }
684  $titleNode = [ 'title', $titleAccum ];
685  $children[] = $titleNode;
686  $argIndex = 1;
687  foreach ( $parts as $part ) {
688  if ( isset( $part->eqpos ) ) {
689  $equalsNode = $part->out[$part->eqpos];
690  $nameNode = [ 'name', array_slice( $part->out, 0, $part->eqpos ) ];
691  $valueNode = [ 'value', array_slice( $part->out, $part->eqpos + 1 ) ];
692  $partNode = [ 'part', [ $nameNode, $equalsNode, $valueNode ] ];
693  $children[] = $partNode;
694  } else {
695  $nameNode = [ 'name', [ [ '@index', [ $argIndex++ ] ] ] ];
696  $valueNode = [ 'value', $part->out ];
697  $partNode = [ 'part', [ $nameNode, $valueNode ] ];
698  $children[] = $partNode;
699  }
700  }
701  $element = [ [ $name, $children ] ];
702  }
703 
704  # Advance input pointer
705  $i += $matchingCount;
706 
707  # Unwind the stack
708  $stack->pop();
709  $accum =& $stack->getAccum();
710 
711  # Re-add the old stack element if it still has unmatched opening characters remaining
712  if ( $matchingCount < $piece->count ) {
713  $piece->parts = [ new PPDPart_Hash ];
714  $piece->count -= $matchingCount;
715  # do we still qualify for any callback with remaining count?
716  $min = $this->rules[$piece->open]['min'];
717  if ( $piece->count >= $min ) {
718  $stack->push( $piece );
719  $accum =& $stack->getAccum();
720  } elseif ( $piece->count == 1 && $piece->open === '{' && $piece->savedPrefix === '-' ) {
721  $piece->savedPrefix = '';
722  $piece->open = '-{';
723  $piece->count = 2;
724  $piece->close = $this->rules[$piece->open]['end'];
725  $stack->push( $piece );
726  $accum =& $stack->getAccum();
727  } else {
728  $s = substr( $piece->open, 0, -1 );
729  $s .= str_repeat(
730  substr( $piece->open, -1 ),
731  $piece->count - strlen( $s )
732  );
733  self::addLiteral( $accum, $piece->savedPrefix . $s );
734  }
735  } elseif ( $piece->savedPrefix !== '' ) {
736  self::addLiteral( $accum, $piece->savedPrefix );
737  }
738 
739  $stackFlags = $stack->getFlags();
740  if ( isset( $stackFlags['findEquals'] ) ) {
741  $findEquals = $stackFlags['findEquals'];
742  }
743  if ( isset( $stackFlags['findPipe'] ) ) {
744  $findPipe = $stackFlags['findPipe'];
745  }
746  if ( isset( $stackFlags['inHeading'] ) ) {
747  $inHeading = $stackFlags['inHeading'];
748  }
749 
750  # Add XML element to the enclosing accumulator
751  array_splice( $accum, count( $accum ), 0, $element );
752  } elseif ( $found == 'pipe' ) {
753  $findEquals = true; // shortcut for getFlags()
754  $stack->addPart();
755  $accum =& $stack->getAccum();
756  ++$i;
757  } elseif ( $found == 'equals' ) {
758  $findEquals = false; // shortcut for getFlags()
759  $accum[] = [ 'equals', [ '=' ] ];
760  $stack->getCurrentPart()->eqpos = count( $accum ) - 1;
761  ++$i;
762  }
763  }
764 
765  # Output any remaining unclosed brackets
766  foreach ( $stack->stack as $piece ) {
767  array_splice( $stack->rootAccum, count( $stack->rootAccum ), 0, $piece->breakSyntax() );
768  }
769 
770  # Enable top-level headings
771  foreach ( $stack->rootAccum as &$node ) {
772  if ( is_array( $node ) && $node[PPNode_Hash_Tree::NAME] === 'possible-h' ) {
773  $node[PPNode_Hash_Tree::NAME] = 'h';
774  }
775  }
776 
777  $rootStore = [ [ 'root', $stack->rootAccum ] ];
778  $rootNode = new PPNode_Hash_Tree( $rootStore, 0 );
779 
780  // Cache
781  $tree = json_encode( $rootStore, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
782  if ( $tree !== false ) {
783  $this->cacheSetTree( $text, $flags, $tree );
784  }
785 
786  return $rootNode;
787  }
788 
789  private static function addLiteral( array &$accum, $text ) {
790  $n = count( $accum );
791  if ( $n && is_string( $accum[$n - 1] ) ) {
792  $accum[$n - 1] .= $text;
793  } else {
794  $accum[] = $text;
795  }
796  }
797 }
798 
803 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
804 class PPDStack_Hash extends PPDStack {
805 
806  public function __construct() {
807  $this->elementClass = PPDStackElement_Hash::class;
808  parent::__construct();
809  $this->rootAccum = [];
810  }
811 }
812 
816 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
817 class PPDStackElement_Hash extends PPDStackElement {
818 
819  public function __construct( $data = [] ) {
820  $this->partClass = PPDPart_Hash::class;
821  parent::__construct( $data );
822  }
823 
830  public function breakSyntax( $openingCount = false ) {
831  if ( $this->open == "\n" ) {
832  $accum = array_merge( [ $this->savedPrefix ], $this->parts[0]->out );
833  } else {
834  if ( $openingCount === false ) {
835  $openingCount = $this->count;
836  }
837  $s = substr( $this->open, 0, -1 );
838  $s .= str_repeat(
839  substr( $this->open, -1 ),
840  $openingCount - strlen( $s )
841  );
842  $accum = [ $this->savedPrefix . $s ];
843  $lastIndex = 0;
844  $first = true;
845  foreach ( $this->parts as $part ) {
846  if ( $first ) {
847  $first = false;
848  } elseif ( is_string( $accum[$lastIndex] ) ) {
849  $accum[$lastIndex] .= '|';
850  } else {
851  $accum[++$lastIndex] = '|';
852  }
853  foreach ( $part->out as $node ) {
854  if ( is_string( $node ) && is_string( $accum[$lastIndex] ) ) {
855  $accum[$lastIndex] .= $node;
856  } else {
857  $accum[++$lastIndex] = $node;
858  }
859  }
860  }
861  }
862  return $accum;
863  }
864 }
865 
869 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
870 class PPDPart_Hash extends PPDPart {
871 
872  public function __construct( $out = '' ) {
873  if ( $out !== '' ) {
874  $accum = [ $out ];
875  } else {
876  $accum = [];
877  }
878  parent::__construct( $accum );
879  }
880 }
881 
886 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
887 class PPFrame_Hash implements PPFrame {
888 
892  public $parser;
893 
897  public $preprocessor;
898 
902  public $title;
903  public $titleCache;
904 
909  public $loopCheckHash;
910 
915  public $depth;
916 
917  private $volatile = false;
918  private $ttl = null;
919 
923  protected $childExpansionCache;
924 
929  public function __construct( $preprocessor ) {
930  $this->preprocessor = $preprocessor;
931  $this->parser = $preprocessor->parser;
932  $this->title = $this->parser->mTitle;
933  $this->titleCache = [ $this->title ? $this->title->getPrefixedDBkey() : false ];
934  $this->loopCheckHash = [];
935  $this->depth = 0;
936  $this->childExpansionCache = [];
937  }
938 
949  public function newChild( $args = false, $title = false, $indexOffset = 0 ) {
950  $namedArgs = [];
951  $numberedArgs = [];
952  if ( $title === false ) {
953  $title = $this->title;
954  }
955  if ( $args !== false ) {
956  if ( $args instanceof PPNode_Hash_Array ) {
957  $args = $args->value;
958  } elseif ( !is_array( $args ) ) {
959  throw new MWException( __METHOD__ . ': $args must be array or PPNode_Hash_Array' );
960  }
961  foreach ( $args as $arg ) {
962  $bits = $arg->splitArg();
963  if ( $bits['index'] !== '' ) {
964  // Numbered parameter
965  $index = $bits['index'] - $indexOffset;
966  if ( isset( $namedArgs[$index] ) || isset( $numberedArgs[$index] ) ) {
967  $this->parser->getOutput()->addWarning( wfMessage( 'duplicate-args-warning',
968  wfEscapeWikiText( $this->title ),
969  wfEscapeWikiText( $title ),
970  wfEscapeWikiText( $index ) )->text() );
971  $this->parser->addTrackingCategory( 'duplicate-args-category' );
972  }
973  $numberedArgs[$index] = $bits['value'];
974  unset( $namedArgs[$index] );
975  } else {
976  // Named parameter
977  $name = trim( $this->expand( $bits['name'], PPFrame::STRIP_COMMENTS ) );
978  if ( isset( $namedArgs[$name] ) || isset( $numberedArgs[$name] ) ) {
979  $this->parser->getOutput()->addWarning( wfMessage( 'duplicate-args-warning',
980  wfEscapeWikiText( $this->title ),
981  wfEscapeWikiText( $title ),
982  wfEscapeWikiText( $name ) )->text() );
983  $this->parser->addTrackingCategory( 'duplicate-args-category' );
984  }
985  $namedArgs[$name] = $bits['value'];
986  unset( $numberedArgs[$name] );
987  }
988  }
989  }
990  return new PPTemplateFrame_Hash( $this->preprocessor, $this, $numberedArgs, $namedArgs, $title );
991  }
992 
1000  public function cachedExpand( $key, $root, $flags = 0 ) {
1001  // we don't have a parent, so we don't have a cache
1002  return $this->expand( $root, $flags );
1003  }
1004 
1011  public function expand( $root, $flags = 0 ) {
1012  static $expansionDepth = 0;
1013  if ( is_string( $root ) ) {
1014  return $root;
1015  }
1016 
1017  if ( ++$this->parser->mPPNodeCount > $this->parser->mOptions->getMaxPPNodeCount() ) {
1018  $this->parser->limitationWarn( 'node-count-exceeded',
1019  $this->parser->mPPNodeCount,
1020  $this->parser->mOptions->getMaxPPNodeCount()
1021  );
1022  return '<span class="error">Node-count limit exceeded</span>';
1023  }
1024  if ( $expansionDepth > $this->parser->mOptions->getMaxPPExpandDepth() ) {
1025  $this->parser->limitationWarn( 'expansion-depth-exceeded',
1026  $expansionDepth,
1027  $this->parser->mOptions->getMaxPPExpandDepth()
1028  );
1029  return '<span class="error">Expansion depth limit exceeded</span>';
1030  }
1031  ++$expansionDepth;
1032  if ( $expansionDepth > $this->parser->mHighestExpansionDepth ) {
1033  $this->parser->mHighestExpansionDepth = $expansionDepth;
1034  }
1035 
1036  $outStack = [ '', '' ];
1037  $iteratorStack = [ false, $root ];
1038  $indexStack = [ 0, 0 ];
1039 
1040  while ( count( $iteratorStack ) > 1 ) {
1041  $level = count( $outStack ) - 1;
1042  $iteratorNode =& $iteratorStack[$level];
1043  $out =& $outStack[$level];
1044  $index =& $indexStack[$level];
1045 
1046  if ( is_array( $iteratorNode ) ) {
1047  if ( $index >= count( $iteratorNode ) ) {
1048  // All done with this iterator
1049  $iteratorStack[$level] = false;
1050  $contextNode = false;
1051  } else {
1052  $contextNode = $iteratorNode[$index];
1053  $index++;
1054  }
1055  } elseif ( $iteratorNode instanceof PPNode_Hash_Array ) {
1056  if ( $index >= $iteratorNode->getLength() ) {
1057  // All done with this iterator
1058  $iteratorStack[$level] = false;
1059  $contextNode = false;
1060  } else {
1061  $contextNode = $iteratorNode->item( $index );
1062  $index++;
1063  }
1064  } else {
1065  // Copy to $contextNode and then delete from iterator stack,
1066  // because this is not an iterator but we do have to execute it once
1067  $contextNode = $iteratorStack[$level];
1068  $iteratorStack[$level] = false;
1069  }
1070 
1071  $newIterator = false;
1072  $contextName = false;
1073  $contextChildren = false;
1074 
1075  if ( $contextNode === false ) {
1076  // nothing to do
1077  } elseif ( is_string( $contextNode ) ) {
1078  $out .= $contextNode;
1079  } elseif ( $contextNode instanceof PPNode_Hash_Array ) {
1080  $newIterator = $contextNode;
1081  } elseif ( $contextNode instanceof PPNode_Hash_Attr ) {
1082  // No output
1083  } elseif ( $contextNode instanceof PPNode_Hash_Text ) {
1084  $out .= $contextNode->value;
1085  } elseif ( $contextNode instanceof PPNode_Hash_Tree ) {
1086  $contextName = $contextNode->name;
1087  $contextChildren = $contextNode->getRawChildren();
1088  } elseif ( is_array( $contextNode ) ) {
1089  // Node descriptor array
1090  if ( count( $contextNode ) !== 2 ) {
1091  throw new MWException( __METHOD__ .
1092  ': found an array where a node descriptor should be' );
1093  }
1094  list( $contextName, $contextChildren ) = $contextNode;
1095  } else {
1096  throw new MWException( __METHOD__ . ': Invalid parameter type' );
1097  }
1098 
1099  // Handle node descriptor array or tree object
1100  if ( $contextName === false ) {
1101  // Not a node, already handled above
1102  } elseif ( $contextName[0] === '@' ) {
1103  // Attribute: no output
1104  } elseif ( $contextName === 'template' ) {
1105  # Double-brace expansion
1106  $bits = PPNode_Hash_Tree::splitRawTemplate( $contextChildren );
1107  if ( $flags & PPFrame::NO_TEMPLATES ) {
1108  $newIterator = $this->virtualBracketedImplode(
1109  '{{', '|', '}}',
1110  $bits['title'],
1111  $bits['parts']
1112  );
1113  } else {
1114  $ret = $this->parser->braceSubstitution( $bits, $this );
1115  if ( isset( $ret['object'] ) ) {
1116  $newIterator = $ret['object'];
1117  } else {
1118  $out .= $ret['text'];
1119  }
1120  }
1121  } elseif ( $contextName === 'tplarg' ) {
1122  # Triple-brace expansion
1123  $bits = PPNode_Hash_Tree::splitRawTemplate( $contextChildren );
1124  if ( $flags & PPFrame::NO_ARGS ) {
1125  $newIterator = $this->virtualBracketedImplode(
1126  '{{{', '|', '}}}',
1127  $bits['title'],
1128  $bits['parts']
1129  );
1130  } else {
1131  $ret = $this->parser->argSubstitution( $bits, $this );
1132  if ( isset( $ret['object'] ) ) {
1133  $newIterator = $ret['object'];
1134  } else {
1135  $out .= $ret['text'];
1136  }
1137  }
1138  } elseif ( $contextName === 'comment' ) {
1139  # HTML-style comment
1140  # Remove it in HTML, pre+remove and STRIP_COMMENTS modes
1141  # Not in RECOVER_COMMENTS mode (msgnw) though.
1142  if ( ( $this->parser->ot['html']
1143  || ( $this->parser->ot['pre'] && $this->parser->mOptions->getRemoveComments() )
1144  || ( $flags & PPFrame::STRIP_COMMENTS )
1145  ) && !( $flags & PPFrame::RECOVER_COMMENTS )
1146  ) {
1147  $out .= '';
1148  } elseif ( $this->parser->ot['wiki'] && !( $flags & PPFrame::RECOVER_COMMENTS ) ) {
1149  # Add a strip marker in PST mode so that pstPass2() can
1150  # run some old-fashioned regexes on the result.
1151  # Not in RECOVER_COMMENTS mode (extractSections) though.
1152  $out .= $this->parser->insertStripItem( $contextChildren[0] );
1153  } else {
1154  # Recover the literal comment in RECOVER_COMMENTS and pre+no-remove
1155  $out .= $contextChildren[0];
1156  }
1157  } elseif ( $contextName === 'ignore' ) {
1158  # Output suppression used by <includeonly> etc.
1159  # OT_WIKI will only respect <ignore> in substed templates.
1160  # The other output types respect it unless NO_IGNORE is set.
1161  # extractSections() sets NO_IGNORE and so never respects it.
1162  if ( ( !isset( $this->parent ) && $this->parser->ot['wiki'] )
1163  || ( $flags & PPFrame::NO_IGNORE )
1164  ) {
1165  $out .= $contextChildren[0];
1166  } else {
1167  // $out .= '';
1168  }
1169  } elseif ( $contextName === 'ext' ) {
1170  # Extension tag
1171  $bits = PPNode_Hash_Tree::splitRawExt( $contextChildren ) +
1172  [ 'attr' => null, 'inner' => null, 'close' => null ];
1173  if ( $flags & PPFrame::NO_TAGS ) {
1174  $s = '<' . $bits['name']->getFirstChild()->value;
1175  if ( $bits['attr'] ) {
1176  $s .= $bits['attr']->getFirstChild()->value;
1177  }
1178  if ( $bits['inner'] ) {
1179  $s .= '>' . $bits['inner']->getFirstChild()->value;
1180  if ( $bits['close'] ) {
1181  $s .= $bits['close']->getFirstChild()->value;
1182  }
1183  } else {
1184  $s .= '/>';
1185  }
1186  $out .= $s;
1187  } else {
1188  $out .= $this->parser->extensionSubstitution( $bits, $this );
1189  }
1190  } elseif ( $contextName === 'h' ) {
1191  # Heading
1192  if ( $this->parser->ot['html'] ) {
1193  # Expand immediately and insert heading index marker
1194  $s = $this->expand( $contextChildren, $flags );
1195  $bits = PPNode_Hash_Tree::splitRawHeading( $contextChildren );
1196  $titleText = $this->title->getPrefixedDBkey();
1197  $this->parser->mHeadings[] = [ $titleText, $bits['i'] ];
1198  $serial = count( $this->parser->mHeadings ) - 1;
1199  $marker = Parser::MARKER_PREFIX . "-h-$serial-" . Parser::MARKER_SUFFIX;
1200  $s = substr( $s, 0, $bits['level'] ) . $marker . substr( $s, $bits['level'] );
1201  $this->parser->mStripState->addGeneral( $marker, '' );
1202  $out .= $s;
1203  } else {
1204  # Expand in virtual stack
1205  $newIterator = $contextChildren;
1206  }
1207  } else {
1208  # Generic recursive expansion
1209  $newIterator = $contextChildren;
1210  }
1211 
1212  if ( $newIterator !== false ) {
1213  $outStack[] = '';
1214  $iteratorStack[] = $newIterator;
1215  $indexStack[] = 0;
1216  } elseif ( $iteratorStack[$level] === false ) {
1217  // Return accumulated value to parent
1218  // With tail recursion
1219  while ( $iteratorStack[$level] === false && $level > 0 ) {
1220  $outStack[$level - 1] .= $out;
1221  array_pop( $outStack );
1222  array_pop( $iteratorStack );
1223  array_pop( $indexStack );
1224  $level--;
1225  }
1226  }
1227  }
1228  --$expansionDepth;
1229  return $outStack[0];
1230  }
1231 
1238  public function implodeWithFlags( $sep, $flags /*, ... */ ) {
1239  $args = array_slice( func_get_args(), 2 );
1240 
1241  $first = true;
1242  $s = '';
1243  foreach ( $args as $root ) {
1244  if ( $root instanceof PPNode_Hash_Array ) {
1245  $root = $root->value;
1246  }
1247  if ( !is_array( $root ) ) {
1248  $root = [ $root ];
1249  }
1250  foreach ( $root as $node ) {
1251  if ( $first ) {
1252  $first = false;
1253  } else {
1254  $s .= $sep;
1255  }
1256  $s .= $this->expand( $node, $flags );
1257  }
1258  }
1259  return $s;
1260  }
1261 
1269  public function implode( $sep /*, ... */ ) {
1270  $args = array_slice( func_get_args(), 1 );
1271 
1272  $first = true;
1273  $s = '';
1274  foreach ( $args as $root ) {
1275  if ( $root instanceof PPNode_Hash_Array ) {
1276  $root = $root->value;
1277  }
1278  if ( !is_array( $root ) ) {
1279  $root = [ $root ];
1280  }
1281  foreach ( $root as $node ) {
1282  if ( $first ) {
1283  $first = false;
1284  } else {
1285  $s .= $sep;
1286  }
1287  $s .= $this->expand( $node );
1288  }
1289  }
1290  return $s;
1291  }
1292 
1301  public function virtualImplode( $sep /*, ... */ ) {
1302  $args = array_slice( func_get_args(), 1 );
1303  $out = [];
1304  $first = true;
1305 
1306  foreach ( $args as $root ) {
1307  if ( $root instanceof PPNode_Hash_Array ) {
1308  $root = $root->value;
1309  }
1310  if ( !is_array( $root ) ) {
1311  $root = [ $root ];
1312  }
1313  foreach ( $root as $node ) {
1314  if ( $first ) {
1315  $first = false;
1316  } else {
1317  $out[] = $sep;
1318  }
1319  $out[] = $node;
1320  }
1321  }
1322  return new PPNode_Hash_Array( $out );
1323  }
1324 
1334  public function virtualBracketedImplode( $start, $sep, $end /*, ... */ ) {
1335  $args = array_slice( func_get_args(), 3 );
1336  $out = [ $start ];
1337  $first = true;
1338 
1339  foreach ( $args as $root ) {
1340  if ( $root instanceof PPNode_Hash_Array ) {
1341  $root = $root->value;
1342  }
1343  if ( !is_array( $root ) ) {
1344  $root = [ $root ];
1345  }
1346  foreach ( $root as $node ) {
1347  if ( $first ) {
1348  $first = false;
1349  } else {
1350  $out[] = $sep;
1351  }
1352  $out[] = $node;
1353  }
1354  }
1355  $out[] = $end;
1356  return new PPNode_Hash_Array( $out );
1357  }
1358 
1359  public function __toString() {
1360  return 'frame{}';
1361  }
1362 
1367  public function getPDBK( $level = false ) {
1368  if ( $level === false ) {
1369  return $this->title->getPrefixedDBkey();
1370  } else {
1371  return $this->titleCache[$level] ?? false;
1372  }
1373  }
1374 
1378  public function getArguments() {
1379  return [];
1380  }
1381 
1385  public function getNumberedArguments() {
1386  return [];
1387  }
1388 
1392  public function getNamedArguments() {
1393  return [];
1394  }
1395 
1401  public function isEmpty() {
1402  return true;
1403  }
1404 
1409  public function getArgument( $name ) {
1410  return false;
1411  }
1412 
1420  public function loopCheck( $title ) {
1421  return !isset( $this->loopCheckHash[$title->getPrefixedDBkey()] );
1422  }
1423 
1429  public function isTemplate() {
1430  return false;
1431  }
1432 
1438  public function getTitle() {
1439  return $this->title;
1440  }
1441 
1447  public function setVolatile( $flag = true ) {
1448  $this->volatile = $flag;
1449  }
1450 
1456  public function isVolatile() {
1457  return $this->volatile;
1458  }
1459 
1465  public function setTTL( $ttl ) {
1466  if ( $ttl !== null && ( $this->ttl === null || $ttl < $this->ttl ) ) {
1467  $this->ttl = $ttl;
1468  }
1469  }
1470 
1476  public function getTTL() {
1477  return $this->ttl;
1478  }
1479 }
1480 
1485 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
1486 class PPTemplateFrame_Hash extends PPFrame_Hash {
1487 
1488  public $numberedArgs, $namedArgs, $parent;
1489  public $numberedExpansionCache, $namedExpansionCache;
1490 
1498  public function __construct( $preprocessor, $parent = false, $numberedArgs = [],
1499  $namedArgs = [], $title = false
1500  ) {
1501  parent::__construct( $preprocessor );
1502 
1503  $this->parent = $parent;
1504  $this->numberedArgs = $numberedArgs;
1505  $this->namedArgs = $namedArgs;
1506  $this->title = $title;
1507  $pdbk = $title ? $title->getPrefixedDBkey() : false;
1508  $this->titleCache = $parent->titleCache;
1509  $this->titleCache[] = $pdbk;
1510  $this->loopCheckHash = /*clone*/ $parent->loopCheckHash;
1511  if ( $pdbk !== false ) {
1512  $this->loopCheckHash[$pdbk] = true;
1513  }
1514  $this->depth = $parent->depth + 1;
1515  $this->numberedExpansionCache = $this->namedExpansionCache = [];
1516  }
1517 
1518  public function __toString() {
1519  $s = 'tplframe{';
1520  $first = true;
1521  $args = $this->numberedArgs + $this->namedArgs;
1522  foreach ( $args as $name => $value ) {
1523  if ( $first ) {
1524  $first = false;
1525  } else {
1526  $s .= ', ';
1527  }
1528  $s .= "\"$name\":\"" .
1529  str_replace( '"', '\\"', $value->__toString() ) . '"';
1530  }
1531  $s .= '}';
1532  return $s;
1533  }
1534 
1542  public function cachedExpand( $key, $root, $flags = 0 ) {
1543  if ( isset( $this->parent->childExpansionCache[$key] ) ) {
1544  return $this->parent->childExpansionCache[$key];
1545  }
1546  $retval = $this->expand( $root, $flags );
1547  if ( !$this->isVolatile() ) {
1548  $this->parent->childExpansionCache[$key] = $retval;
1549  }
1550  return $retval;
1551  }
1552 
1558  public function isEmpty() {
1559  return !count( $this->numberedArgs ) && !count( $this->namedArgs );
1560  }
1561 
1565  public function getArguments() {
1566  $arguments = [];
1567  foreach ( array_merge(
1568  array_keys( $this->numberedArgs ),
1569  array_keys( $this->namedArgs ) ) as $key ) {
1570  $arguments[$key] = $this->getArgument( $key );
1571  }
1572  return $arguments;
1573  }
1574 
1578  public function getNumberedArguments() {
1579  $arguments = [];
1580  foreach ( array_keys( $this->numberedArgs ) as $key ) {
1581  $arguments[$key] = $this->getArgument( $key );
1582  }
1583  return $arguments;
1584  }
1585 
1589  public function getNamedArguments() {
1590  $arguments = [];
1591  foreach ( array_keys( $this->namedArgs ) as $key ) {
1592  $arguments[$key] = $this->getArgument( $key );
1593  }
1594  return $arguments;
1595  }
1596 
1601  public function getNumberedArgument( $index ) {
1602  if ( !isset( $this->numberedArgs[$index] ) ) {
1603  return false;
1604  }
1605  if ( !isset( $this->numberedExpansionCache[$index] ) ) {
1606  # No trimming for unnamed arguments
1607  $this->numberedExpansionCache[$index] = $this->parent->expand(
1608  $this->numberedArgs[$index],
1609  PPFrame::STRIP_COMMENTS
1610  );
1611  }
1612  return $this->numberedExpansionCache[$index];
1613  }
1614 
1619  public function getNamedArgument( $name ) {
1620  if ( !isset( $this->namedArgs[$name] ) ) {
1621  return false;
1622  }
1623  if ( !isset( $this->namedExpansionCache[$name] ) ) {
1624  # Trim named arguments post-expand, for backwards compatibility
1625  $this->namedExpansionCache[$name] = trim(
1626  $this->parent->expand( $this->namedArgs[$name], PPFrame::STRIP_COMMENTS ) );
1627  }
1628  return $this->namedExpansionCache[$name];
1629  }
1630 
1635  public function getArgument( $name ) {
1636  $text = $this->getNumberedArgument( $name );
1637  if ( $text === false ) {
1638  $text = $this->getNamedArgument( $name );
1639  }
1640  return $text;
1641  }
1642 
1648  public function isTemplate() {
1649  return true;
1650  }
1651 
1652  public function setVolatile( $flag = true ) {
1653  parent::setVolatile( $flag );
1654  $this->parent->setVolatile( $flag );
1655  }
1656 
1657  public function setTTL( $ttl ) {
1658  parent::setTTL( $ttl );
1659  $this->parent->setTTL( $ttl );
1660  }
1661 }
1662 
1667 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
1668 class PPCustomFrame_Hash extends PPFrame_Hash {
1669 
1670  public $args;
1671 
1672  public function __construct( $preprocessor, $args ) {
1673  parent::__construct( $preprocessor );
1674  $this->args = $args;
1675  }
1676 
1677  public function __toString() {
1678  $s = 'cstmframe{';
1679  $first = true;
1680  foreach ( $this->args as $name => $value ) {
1681  if ( $first ) {
1682  $first = false;
1683  } else {
1684  $s .= ', ';
1685  }
1686  $s .= "\"$name\":\"" .
1687  str_replace( '"', '\\"', $value->__toString() ) . '"';
1688  }
1689  $s .= '}';
1690  return $s;
1691  }
1692 
1696  public function isEmpty() {
1697  return !count( $this->args );
1698  }
1699 
1704  public function getArgument( $index ) {
1705  if ( !isset( $this->args[$index] ) ) {
1706  return false;
1707  }
1708  return $this->args[$index];
1709  }
1710 
1711  public function getArguments() {
1712  return $this->args;
1713  }
1714 }
1715 
1719 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
1720 class PPNode_Hash_Tree implements PPNode {
1721 
1722  public $name;
1723 
1729  private $rawChildren;
1730 
1734  private $store;
1735 
1739  private $index;
1740 
1745  const NAME = 0;
1746 
1751  const CHILDREN = 1;
1752 
1760  public function __construct( array $store, $index ) {
1761  $this->store = $store;
1762  $this->index = $index;
1763  list( $this->name, $this->rawChildren ) = $this->store[$index];
1764  }
1765 
1774  public static function factory( array $store, $index ) {
1775  if ( !isset( $store[$index] ) ) {
1776  return false;
1777  }
1778 
1779  $descriptor = $store[$index];
1780  if ( is_string( $descriptor ) ) {
1781  $class = PPNode_Hash_Text::class;
1782  } elseif ( is_array( $descriptor ) ) {
1783  if ( $descriptor[self::NAME][0] === '@' ) {
1784  $class = PPNode_Hash_Attr::class;
1785  } else {
1786  $class = self::class;
1787  }
1788  } else {
1789  throw new MWException( __METHOD__ . ': invalid node descriptor' );
1790  }
1791  return new $class( $store, $index );
1792  }
1793 
1797  public function __toString() {
1798  $inner = '';
1799  $attribs = '';
1800  for ( $node = $this->getFirstChild(); $node; $node = $node->getNextSibling() ) {
1801  if ( $node instanceof PPNode_Hash_Attr ) {
1802  $attribs .= ' ' . $node->name . '="' . htmlspecialchars( $node->value ) . '"';
1803  } else {
1804  $inner .= $node->__toString();
1805  }
1806  }
1807  if ( $inner === '' ) {
1808  return "<{$this->name}$attribs/>";
1809  } else {
1810  return "<{$this->name}$attribs>$inner</{$this->name}>";
1811  }
1812  }
1813 
1817  public function getChildren() {
1818  $children = [];
1819  foreach ( $this->rawChildren as $i => $child ) {
1820  $children[] = self::factory( $this->rawChildren, $i );
1821  }
1822  return new PPNode_Hash_Array( $children );
1823  }
1824 
1832  public function getFirstChild() {
1833  if ( !isset( $this->rawChildren[0] ) ) {
1834  return false;
1835  } else {
1836  return self::factory( $this->rawChildren, 0 );
1837  }
1838  }
1839 
1847  public function getNextSibling() {
1848  return self::factory( $this->store, $this->index + 1 );
1849  }
1850 
1857  public function getChildrenOfType( $name ) {
1858  $children = [];
1859  foreach ( $this->rawChildren as $i => $child ) {
1860  if ( is_array( $child ) && $child[self::NAME] === $name ) {
1861  $children[] = self::factory( $this->rawChildren, $i );
1862  }
1863  }
1864  return new PPNode_Hash_Array( $children );
1865  }
1866 
1871  public function getRawChildren() {
1872  return $this->rawChildren;
1873  }
1874 
1878  public function getLength() {
1879  return false;
1880  }
1881 
1886  public function item( $i ) {
1887  return false;
1888  }
1889 
1893  public function getName() {
1894  return $this->name;
1895  }
1896 
1906  public function splitArg() {
1907  return self::splitRawArg( $this->rawChildren );
1908  }
1909 
1915  public static function splitRawArg( array $children ) {
1916  $bits = [];
1917  foreach ( $children as $i => $child ) {
1918  if ( !is_array( $child ) ) {
1919  continue;
1920  }
1921  if ( $child[self::NAME] === 'name' ) {
1922  $bits['name'] = new self( $children, $i );
1923  if ( isset( $child[self::CHILDREN][0][self::NAME] )
1924  && $child[self::CHILDREN][0][self::NAME] === '@index'
1925  ) {
1926  $bits['index'] = $child[self::CHILDREN][0][self::CHILDREN][0];
1927  }
1928  } elseif ( $child[self::NAME] === 'value' ) {
1929  $bits['value'] = new self( $children, $i );
1930  }
1931  }
1932 
1933  if ( !isset( $bits['name'] ) ) {
1934  throw new MWException( 'Invalid brace node passed to ' . __METHOD__ );
1935  }
1936  if ( !isset( $bits['index'] ) ) {
1937  $bits['index'] = '';
1938  }
1939  return $bits;
1940  }
1941 
1949  public function splitExt() {
1950  return self::splitRawExt( $this->rawChildren );
1951  }
1952 
1958  public static function splitRawExt( array $children ) {
1959  $bits = [];
1960  foreach ( $children as $i => $child ) {
1961  if ( !is_array( $child ) ) {
1962  continue;
1963  }
1964  switch ( $child[self::NAME] ) {
1965  case 'name':
1966  $bits['name'] = new self( $children, $i );
1967  break;
1968  case 'attr':
1969  $bits['attr'] = new self( $children, $i );
1970  break;
1971  case 'inner':
1972  $bits['inner'] = new self( $children, $i );
1973  break;
1974  case 'close':
1975  $bits['close'] = new self( $children, $i );
1976  break;
1977  }
1978  }
1979  if ( !isset( $bits['name'] ) ) {
1980  throw new MWException( 'Invalid ext node passed to ' . __METHOD__ );
1981  }
1982  return $bits;
1983  }
1984 
1991  public function splitHeading() {
1992  if ( $this->name !== 'h' ) {
1993  throw new MWException( 'Invalid h node passed to ' . __METHOD__ );
1994  }
1995  return self::splitRawHeading( $this->rawChildren );
1996  }
1997 
2003  public static function splitRawHeading( array $children ) {
2004  $bits = [];
2005  foreach ( $children as $i => $child ) {
2006  if ( !is_array( $child ) ) {
2007  continue;
2008  }
2009  if ( $child[self::NAME] === '@i' ) {
2010  $bits['i'] = $child[self::CHILDREN][0];
2011  } elseif ( $child[self::NAME] === '@level' ) {
2012  $bits['level'] = $child[self::CHILDREN][0];
2013  }
2014  }
2015  if ( !isset( $bits['i'] ) ) {
2016  throw new MWException( 'Invalid h node passed to ' . __METHOD__ );
2017  }
2018  return $bits;
2019  }
2020 
2027  public function splitTemplate() {
2028  return self::splitRawTemplate( $this->rawChildren );
2029  }
2030 
2036  public static function splitRawTemplate( array $children ) {
2037  $parts = [];
2038  $bits = [ 'lineStart' => '' ];
2039  foreach ( $children as $i => $child ) {
2040  if ( !is_array( $child ) ) {
2041  continue;
2042  }
2043  switch ( $child[self::NAME] ) {
2044  case 'title':
2045  $bits['title'] = new self( $children, $i );
2046  break;
2047  case 'part':
2048  $parts[] = new self( $children, $i );
2049  break;
2050  case '@lineStart':
2051  $bits['lineStart'] = '1';
2052  break;
2053  }
2054  }
2055  if ( !isset( $bits['title'] ) ) {
2056  throw new MWException( 'Invalid node passed to ' . __METHOD__ );
2057  }
2058  $bits['parts'] = new PPNode_Hash_Array( $parts );
2059  return $bits;
2060  }
2061 }
2062 
2066 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
2067 class PPNode_Hash_Text implements PPNode {
2068 
2069  public $value;
2070  private $store, $index;
2071 
2079  public function __construct( array $store, $index ) {
2080  $this->value = $store[$index];
2081  if ( !is_scalar( $this->value ) ) {
2082  throw new MWException( __CLASS__ . ' given object instead of string' );
2083  }
2084  $this->store = $store;
2085  $this->index = $index;
2086  }
2087 
2088  public function __toString() {
2089  return htmlspecialchars( $this->value );
2090  }
2091 
2092  public function getNextSibling() {
2093  return PPNode_Hash_Tree::factory( $this->store, $this->index + 1 );
2094  }
2095 
2096  public function getChildren() {
2097  return false;
2098  }
2099 
2100  public function getFirstChild() {
2101  return false;
2102  }
2103 
2104  public function getChildrenOfType( $name ) {
2105  return false;
2106  }
2107 
2108  public function getLength() {
2109  return false;
2110  }
2111 
2112  public function item( $i ) {
2113  return false;
2114  }
2115 
2116  public function getName() {
2117  return '#text';
2118  }
2119 
2120  public function splitArg() {
2121  throw new MWException( __METHOD__ . ': not supported' );
2122  }
2123 
2124  public function splitExt() {
2125  throw new MWException( __METHOD__ . ': not supported' );
2126  }
2127 
2128  public function splitHeading() {
2129  throw new MWException( __METHOD__ . ': not supported' );
2130  }
2131 }
2132 
2136 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
2137 class PPNode_Hash_Array implements PPNode {
2138 
2139  public $value;
2140 
2141  public function __construct( $value ) {
2142  $this->value = $value;
2143  }
2144 
2145  public function __toString() {
2146  return var_export( $this, true );
2147  }
2148 
2149  public function getLength() {
2150  return count( $this->value );
2151  }
2152 
2153  public function item( $i ) {
2154  return $this->value[$i];
2155  }
2156 
2157  public function getName() {
2158  return '#nodelist';
2159  }
2160 
2161  public function getNextSibling() {
2162  return false;
2163  }
2164 
2165  public function getChildren() {
2166  return false;
2167  }
2168 
2169  public function getFirstChild() {
2170  return false;
2171  }
2172 
2173  public function getChildrenOfType( $name ) {
2174  return false;
2175  }
2176 
2177  public function splitArg() {
2178  throw new MWException( __METHOD__ . ': not supported' );
2179  }
2180 
2181  public function splitExt() {
2182  throw new MWException( __METHOD__ . ': not supported' );
2183  }
2184 
2185  public function splitHeading() {
2186  throw new MWException( __METHOD__ . ': not supported' );
2187  }
2188 }
2189 
2193 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
2194 class PPNode_Hash_Attr implements PPNode {
2195 
2196  public $name, $value;
2197  private $store, $index;
2198 
2206  public function __construct( array $store, $index ) {
2207  $descriptor = $store[$index];
2208  if ( $descriptor[PPNode_Hash_Tree::NAME][0] !== '@' ) {
2209  throw new MWException( __METHOD__ . ': invalid name in attribute descriptor' );
2210  }
2211  $this->name = substr( $descriptor[PPNode_Hash_Tree::NAME], 1 );
2212  $this->value = $descriptor[PPNode_Hash_Tree::CHILDREN][0];
2213  $this->store = $store;
2214  $this->index = $index;
2215  }
2216 
2217  public function __toString() {
2218  return "<@{$this->name}>" . htmlspecialchars( $this->value ) . "</@{$this->name}>";
2219  }
2220 
2221  public function getName() {
2222  return $this->name;
2223  }
2224 
2225  public function getNextSibling() {
2226  return PPNode_Hash_Tree::factory( $this->store, $this->index + 1 );
2227  }
2228 
2229  public function getChildren() {
2230  return false;
2231  }
2232 
2233  public function getFirstChild() {
2234  return false;
2235  }
2236 
2237  public function getChildrenOfType( $name ) {
2238  return false;
2239  }
2240 
2241  public function getLength() {
2242  return false;
2243  }
2244 
2245  public function item( $i ) {
2246  return false;
2247  }
2248 
2249  public function splitArg() {
2250  throw new MWException( __METHOD__ . ': not supported' );
2251  }
2252 
2253  public function splitExt() {
2254  throw new MWException( __METHOD__ . ': not supported' );
2255  }
2256 
2257  public function splitHeading() {
2258  throw new MWException( __METHOD__ . ': not supported' );
2259  }
2260 }
part
in this case you re responsible for computing and outputting the entire conflict part
Definition: hooks.txt:1462
PPNode_Hash_Tree\getLength
getLength()
Definition: Preprocessor_Hash.php:1878
PPNode_Hash_Tree\getChildrenOfType
getChildrenOfType( $name)
Get an array of the children with a given node name.
Definition: Preprocessor_Hash.php:1857
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:1886
PPDPart_Hash
Definition: Preprocessor_Hash.php:870
PPNode_Hash_Tree\$store
$store
The store array for the siblings of this node, including this node itself.
Definition: Preprocessor_Hash.php:1734
PPNode_Hash_Array\splitHeading
splitHeading()
Split an "<h>" node.
Definition: Preprocessor_Hash.php:2185
PPNode_Hash_Tree\getFirstChild
getFirstChild()
Get the first child, or false if there is none.
Definition: Preprocessor_Hash.php:1832
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:1958
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:887
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:2128
PPNode_Hash_Tree\splitHeading
splitHeading()
Split an "<h>" node.
Definition: Preprocessor_Hash.php:1991
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:2253
PPCustomFrame_Hash\isEmpty
isEmpty()
Definition: Preprocessor_Hash.php:1696
PPNode_Hash_Text\getChildrenOfType
getChildrenOfType( $name)
Get all children of this tree node which have a given name.
Definition: Preprocessor_Hash.php:2104
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:1949
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:2108
PPNode_Hash_Tree\splitRawArg
static splitRawArg(array $children)
Like splitArg() but for a raw child array.
Definition: Preprocessor_Hash.php:1915
Preprocessor_Hash\newFrame
newFrame()
Definition: Preprocessor_Hash.php:60
$s
$s
Definition: mergeMessageFileList.php:187
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
value
if( $inline) $status value
Definition: SyntaxHighlight.php:345
PPNode_Hash_Tree\splitRawHeading
static splitRawHeading(array $children)
Like splitHeading() but for a raw child array.
Definition: Preprocessor_Hash.php:2003
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:2079
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:2181
names
alter the names
Definition: COPYING.txt:329
PPNode_Hash_Attr\$store
$store
Definition: Preprocessor_Hash.php:2197
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:2177
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:2169
PPNode_Hash_Attr\item
item( $i)
Returns an item of an array-type node.
Definition: Preprocessor_Hash.php:2245
PPNode_Hash_Attr\getFirstChild
getFirstChild()
Get the first child of a tree node.
Definition: Preprocessor_Hash.php:2233
name
and how to run hooks for an and one after Each event has a name
Definition: hooks.txt:6
PPNode_Hash_Attr\splitHeading
splitHeading()
Split an "<h>" node.
Definition: Preprocessor_Hash.php:2257
PPNode_Hash_Tree\$name
$name
Definition: Preprocessor_Hash.php:1722
PPNode_Hash_Attr\getChildrenOfType
getChildrenOfType( $name)
Get all children of this tree node which have a given name.
Definition: Preprocessor_Hash.php:2237
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:1751
PPNode_Hash_Array\getChildrenOfType
getChildrenOfType( $name)
Get all children of this tree node which have a given name.
Definition: Preprocessor_Hash.php:2173
PPNode_Hash_Tree\getNextSibling
getNextSibling()
Get the next sibling, or false if there is none.
Definition: Preprocessor_Hash.php:1847
PPNode_Hash_Text\getNextSibling
getNextSibling()
Get the next sibling of any node.
Definition: Preprocessor_Hash.php:2092
PPNode_Hash_Text\__toString
__toString()
Definition: Preprocessor_Hash.php:2088
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:2149
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:2027
PPNode_Hash_Tree\__toString
__toString()
Convert a node to XML, for debugging.
Definition: Preprocessor_Hash.php:1797
$matches
$matches
Definition: NoLocalSettings.php:24
PPNode
There are three types of nodes:
Definition: Preprocessor.php:360
$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:2044
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:1
Preprocessor_Hash\$parser
Parser $parser
Definition: Preprocessor_Hash.php:48
PPNode_Hash_Attr\$value
$value
Definition: Preprocessor_Hash.php:2196
PPNode_Hash_Tree\splitRawTemplate
static splitRawTemplate(array $children)
Like splitTemplate() but for a raw child array.
Definition: Preprocessor_Hash.php:2036
array
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
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:2241
PPNode_Hash_Tree\__construct
__construct(array $store, $index)
Construct an object using the data from $store[$index].
Definition: Preprocessor_Hash.php:1760
$wgDisableLangConversion
$wgDisableLangConversion
Whether to enable language variant conversion.
Definition: DefaultSettings.php:3111
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:2124
or
or
Definition: COPYING.txt:140
PPNode_Hash_Text\$value
$value
Definition: Preprocessor_Hash.php:2069
PPNode_Hash_Array
Definition: Preprocessor_Hash.php:2137
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:302
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:2100
PPNode_Hash_Tree\getRawChildren
getRawChildren()
Get the raw child array.
Definition: Preprocessor_Hash.php:1871
PPNode_Hash_Text\$store
$store
Definition: Preprocessor_Hash.php:2070
PPNode_Hash_Tree\getName
getName()
Definition: Preprocessor_Hash.php:1893
$value
$value
Definition: styleTest.css.php:49
Preprocessor_Hash\preprocessToObj
preprocessToObj( $text, $flags=0)
Preprocess some wikitext and return the document tree.
Definition: Preprocessor_Hash.php:118
title
title
Definition: parserTests.txt:239
PPNode_Hash_Tree\getChildren
getChildren()
Definition: Preprocessor_Hash.php:1817
PPNode_Hash_Array\__construct
__construct( $value)
Definition: Preprocessor_Hash.php:2141
PPNode_Hash_Text\getName
getName()
Get the name of this node.
Definition: Preprocessor_Hash.php:2116
PPNode_Hash_Text\getChildren
getChildren()
Get an array-type node containing the children of this node.
Definition: Preprocessor_Hash.php:2096
PPNode_Hash_Tree\$index
$index
The index into $this->store which contains the descriptor of this node.
Definition: Preprocessor_Hash.php:1739
PPNode_Hash_Array\item
item( $i)
Returns an item of an array-type node.
Definition: Preprocessor_Hash.php:2153
PPNode_Hash_Array\getChildren
getChildren()
Get an array-type node containing the children of this node.
Definition: Preprocessor_Hash.php:2165
PPNode_Hash_Attr\getChildren
getChildren()
Get an array-type node containing the children of this node.
Definition: Preprocessor_Hash.php:2229
PPNode_Hash_Array\getName
getName()
Get the name of this node.
Definition: Preprocessor_Hash.php:2157
PPNode_Hash_Attr\getNextSibling
getNextSibling()
Get the next sibling of any node.
Definition: Preprocessor_Hash.php:2225
PPNode_Hash_Attr
Definition: Preprocessor_Hash.php:2194
text
This list may contain false positives That usually means there is additional text with links below the first Each row contains links to the first and second as well as the first line of the second redirect text
Definition: All_system_messages.txt:1267
$args
if( $line===false) $args
Definition: cdb.php:64
PPNode_Hash_Text
Definition: Preprocessor_Hash.php:2067
PPCustomFrame_Hash\getArgument
getArgument( $index)
Definition: Preprocessor_Hash.php:1704
PPNode_Hash_Tree\$rawChildren
$rawChildren
The store array for children of this node.
Definition: Preprocessor_Hash.php:1729
PPNode_Hash_Text\item
item( $i)
Returns an item of an array-type node.
Definition: Preprocessor_Hash.php:2112
PPNode_Hash_Attr\getName
getName()
Get the name of this node.
Definition: Preprocessor_Hash.php:2221
PPDStack_Hash
Stack class to help Preprocessor::preprocessToObj()
Definition: Preprocessor_Hash.php:804
PPNode_Hash_Array\$value
$value
Definition: Preprocessor_Hash.php:2139
Preprocessor_Hash\addLiteral
static addLiteral(array &$accum, $text)
Definition: Preprocessor_Hash.php:789
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:2249
PPNode_Hash_Array\getNextSibling
getNextSibling()
Get the next sibling of any node.
Definition: Preprocessor_Hash.php:2161
PPNode_Hash_Array\__toString
__toString()
Definition: Preprocessor_Hash.php:2145
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:1711
PPCustomFrame_Hash
Expansion frame with custom arguments.
Definition: Preprocessor_Hash.php:1668
captcha-old.parser
parser
Definition: captcha-old.py:210
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:52
PPNode_Hash_Attr\__construct
__construct(array $store, $index)
Construct an object using the data from $store[$index].
Definition: Preprocessor_Hash.php:2206
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:2120
PPNode_Hash_Tree
Definition: Preprocessor_Hash.php:1720
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
PPNode_Hash_Attr\__toString
__toString()
Definition: Preprocessor_Hash.php:2217
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:1774
PPNode_Hash_Tree\NAME
const NAME
The offset of the name within descriptors, used in some places for readability.
Definition: Preprocessor_Hash.php:1745
PPNode_Hash_Tree\splitArg
splitArg()
Split a "<part>" node into an associative array containing:
Definition: Preprocessor_Hash.php:1906