MediaWiki  1.29.1
Preprocessor_DOM.php
Go to the documentation of this file.
1 <?php
27 // @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
29  // @codingStandardsIgnoreEnd
30 
34  public $parser;
35 
36  public $memoryLimit;
37 
38  const CACHE_PREFIX = 'preprocess-xml';
39 
40  public function __construct( $parser ) {
41  $this->parser = $parser;
42  $mem = ini_get( 'memory_limit' );
43  $this->memoryLimit = false;
44  if ( strval( $mem ) !== '' && $mem != -1 ) {
45  if ( preg_match( '/^\d+$/', $mem ) ) {
46  $this->memoryLimit = $mem;
47  } elseif ( preg_match( '/^(\d+)M$/i', $mem, $m ) ) {
48  $this->memoryLimit = $m[1] * 1048576;
49  }
50  }
51  }
52 
56  public function newFrame() {
57  return new PPFrame_DOM( $this );
58  }
59 
64  public function newCustomFrame( $args ) {
65  return new PPCustomFrame_DOM( $this, $args );
66  }
67 
73  public function newPartNodeArray( $values ) {
74  // NOTE: DOM manipulation is slower than building & parsing XML! (or so Tim sais)
75  $xml = "<list>";
76 
77  foreach ( $values as $k => $val ) {
78  if ( is_int( $k ) ) {
79  $xml .= "<part><name index=\"$k\"/><value>"
80  . htmlspecialchars( $val ) . "</value></part>";
81  } else {
82  $xml .= "<part><name>" . htmlspecialchars( $k )
83  . "</name>=<value>" . htmlspecialchars( $val ) . "</value></part>";
84  }
85  }
86 
87  $xml .= "</list>";
88 
89  $dom = new DOMDocument();
90  MediaWiki\suppressWarnings();
91  $result = $dom->loadXML( $xml );
92  MediaWiki\restoreWarnings();
93  if ( !$result ) {
94  // Try running the XML through UtfNormal to get rid of invalid characters
95  $xml = UtfNormal\Validator::cleanUp( $xml );
96  // 1 << 19 == XML_PARSE_HUGE, needed so newer versions of libxml2
97  // don't barf when the XML is >256 levels deep
98  $result = $dom->loadXML( $xml, 1 << 19 );
99  }
100 
101  if ( !$result ) {
102  throw new MWException( 'Parameters passed to ' . __METHOD__ . ' result in invalid XML' );
103  }
104 
105  $root = $dom->documentElement;
106  $node = new PPNode_DOM( $root->childNodes );
107  return $node;
108  }
109 
114  public function memCheck() {
115  if ( $this->memoryLimit === false ) {
116  return true;
117  }
118  $usage = memory_get_usage();
119  if ( $usage > $this->memoryLimit * 0.9 ) {
120  $limit = intval( $this->memoryLimit * 0.9 / 1048576 + 0.5 );
121  throw new MWException( "Preprocessor hit 90% memory limit ($limit MB)" );
122  }
123  return $usage <= $this->memoryLimit * 0.8;
124  }
125 
150  public function preprocessToObj( $text, $flags = 0 ) {
151 
152  $xml = $this->cacheGetTree( $text, $flags );
153  if ( $xml === false ) {
154  $xml = $this->preprocessToXml( $text, $flags );
155  $this->cacheSetTree( $text, $flags, $xml );
156  }
157 
158  // Fail if the number of elements exceeds acceptable limits
159  // Do not attempt to generate the DOM
160  $this->parser->mGeneratedPPNodeCount += substr_count( $xml, '<' );
161  $max = $this->parser->mOptions->getMaxGeneratedPPNodeCount();
162  if ( $this->parser->mGeneratedPPNodeCount > $max ) {
163  // if ( $cacheable ) { ... }
164  throw new MWException( __METHOD__ . ': generated node count limit exceeded' );
165  }
166 
167  $dom = new DOMDocument;
168  MediaWiki\suppressWarnings();
169  $result = $dom->loadXML( $xml );
170  MediaWiki\restoreWarnings();
171  if ( !$result ) {
172  // Try running the XML through UtfNormal to get rid of invalid characters
173  $xml = UtfNormal\Validator::cleanUp( $xml );
174  // 1 << 19 == XML_PARSE_HUGE, needed so newer versions of libxml2
175  // don't barf when the XML is >256 levels deep.
176  $result = $dom->loadXML( $xml, 1 << 19 );
177  }
178  if ( $result ) {
179  $obj = new PPNode_DOM( $dom->documentElement );
180  }
181 
182  // if ( $cacheable ) { ... }
183 
184  if ( !$result ) {
185  throw new MWException( __METHOD__ . ' generated invalid XML' );
186  }
187  return $obj;
188  }
189 
195  public function preprocessToXml( $text, $flags = 0 ) {
196  global $wgDisableLangConversion;
197 
198  $forInclusion = $flags & Parser::PTD_FOR_INCLUSION;
199 
200  $xmlishElements = $this->parser->getStripList();
201  $xmlishAllowMissingEndTag = [ 'includeonly', 'noinclude', 'onlyinclude' ];
202  $enableOnlyinclude = false;
203  if ( $forInclusion ) {
204  $ignoredTags = [ 'includeonly', '/includeonly' ];
205  $ignoredElements = [ 'noinclude' ];
206  $xmlishElements[] = 'noinclude';
207  if ( strpos( $text, '<onlyinclude>' ) !== false
208  && strpos( $text, '</onlyinclude>' ) !== false
209  ) {
210  $enableOnlyinclude = true;
211  }
212  } else {
213  $ignoredTags = [ 'noinclude', '/noinclude', 'onlyinclude', '/onlyinclude' ];
214  $ignoredElements = [ 'includeonly' ];
215  $xmlishElements[] = 'includeonly';
216  }
217  $xmlishRegex = implode( '|', array_merge( $xmlishElements, $ignoredTags ) );
218 
219  // Use "A" modifier (anchored) instead of "^", because ^ doesn't work with an offset
220  $elementsRegex = "~($xmlishRegex)(?:\s|\/>|>)|(!--)~iA";
221 
222  $stack = new PPDStack;
223 
224  $searchBase = "[{<\n"; # }
225  if ( !$wgDisableLangConversion ) {
226  // FIXME: disabled due to T153761
227  // $searchBase .= '-';
228  }
229 
230  // For fast reverse searches
231  $revText = strrev( $text );
232  $lengthText = strlen( $text );
233 
234  // Input pointer, starts out pointing to a pseudo-newline before the start
235  $i = 0;
236  // Current accumulator
237  $accum =& $stack->getAccum();
238  $accum = '<root>';
239  // True to find equals signs in arguments
240  $findEquals = false;
241  // True to take notice of pipe characters
242  $findPipe = false;
243  $headingIndex = 1;
244  // True if $i is inside a possible heading
245  $inHeading = false;
246  // True if there are no more greater-than (>) signs right of $i
247  $noMoreGT = false;
248  // Map of tag name => true if there are no more closing tags of given type right of $i
249  $noMoreClosingTag = [];
250  // True to ignore all input up to the next <onlyinclude>
251  $findOnlyinclude = $enableOnlyinclude;
252  // Do a line-start run without outputting an LF character
253  $fakeLineStart = true;
254 
255  while ( true ) {
256  // $this->memCheck();
257 
258  if ( $findOnlyinclude ) {
259  // Ignore all input up to the next <onlyinclude>
260  $startPos = strpos( $text, '<onlyinclude>', $i );
261  if ( $startPos === false ) {
262  // Ignored section runs to the end
263  $accum .= '<ignore>' . htmlspecialchars( substr( $text, $i ) ) . '</ignore>';
264  break;
265  }
266  $tagEndPos = $startPos + strlen( '<onlyinclude>' ); // past-the-end
267  $accum .= '<ignore>' . htmlspecialchars( substr( $text, $i, $tagEndPos - $i ) ) . '</ignore>';
268  $i = $tagEndPos;
269  $findOnlyinclude = false;
270  }
271 
272  if ( $fakeLineStart ) {
273  $found = 'line-start';
274  $curChar = '';
275  } else {
276  # Find next opening brace, closing brace or pipe
277  $search = $searchBase;
278  if ( $stack->top === false ) {
279  $currentClosing = '';
280  } else {
281  $currentClosing = $stack->top->close;
282  $search .= $currentClosing;
283  }
284  if ( $findPipe ) {
285  $search .= '|';
286  }
287  if ( $findEquals ) {
288  // First equals will be for the template
289  $search .= '=';
290  }
291  $rule = null;
292  # Output literal section, advance input counter
293  $literalLength = strcspn( $text, $search, $i );
294  if ( $literalLength > 0 ) {
295  $accum .= htmlspecialchars( substr( $text, $i, $literalLength ) );
296  $i += $literalLength;
297  }
298  if ( $i >= $lengthText ) {
299  if ( $currentClosing == "\n" ) {
300  // Do a past-the-end run to finish off the heading
301  $curChar = '';
302  $found = 'line-end';
303  } else {
304  # All done
305  break;
306  }
307  } else {
308  $curChar = $curTwoChar = $text[$i];
309  if ( ( $i + 1 ) < $lengthText ) {
310  $curTwoChar .= $text[$i + 1];
311  }
312  if ( $curChar == '|' ) {
313  $found = 'pipe';
314  } elseif ( $curChar == '=' ) {
315  $found = 'equals';
316  } elseif ( $curChar == '<' ) {
317  $found = 'angle';
318  } elseif ( $curChar == "\n" ) {
319  if ( $inHeading ) {
320  $found = 'line-end';
321  } else {
322  $found = 'line-start';
323  }
324  } elseif ( $curTwoChar == $currentClosing ) {
325  $found = 'close';
326  $curChar = $curTwoChar;
327  } elseif ( $curChar == $currentClosing ) {
328  $found = 'close';
329  } elseif ( isset( $this->rules[$curTwoChar] ) ) {
330  $curChar = $curTwoChar;
331  $found = 'open';
332  $rule = $this->rules[$curChar];
333  } elseif ( isset( $this->rules[$curChar] ) ) {
334  $found = 'open';
335  $rule = $this->rules[$curChar];
336  } elseif ( $curChar == '-' ) {
337  $found = 'dash';
338  } else {
339  # Some versions of PHP have a strcspn which stops on null characters
340  # Ignore and continue
341  ++$i;
342  continue;
343  }
344  }
345  }
346 
347  if ( $found == 'angle' ) {
348  $matches = false;
349  // Handle </onlyinclude>
350  if ( $enableOnlyinclude
351  && substr( $text, $i, strlen( '</onlyinclude>' ) ) == '</onlyinclude>'
352  ) {
353  $findOnlyinclude = true;
354  continue;
355  }
356 
357  // Determine element name
358  if ( !preg_match( $elementsRegex, $text, $matches, 0, $i + 1 ) ) {
359  // Element name missing or not listed
360  $accum .= '&lt;';
361  ++$i;
362  continue;
363  }
364  // Handle comments
365  if ( isset( $matches[2] ) && $matches[2] == '!--' ) {
366 
367  // To avoid leaving blank lines, when a sequence of
368  // space-separated comments is both preceded and followed by
369  // a newline (ignoring spaces), then
370  // trim leading and trailing spaces and the trailing newline.
371 
372  // Find the end
373  $endPos = strpos( $text, '-->', $i + 4 );
374  if ( $endPos === false ) {
375  // Unclosed comment in input, runs to end
376  $inner = substr( $text, $i );
377  $accum .= '<comment>' . htmlspecialchars( $inner ) . '</comment>';
378  $i = $lengthText;
379  } else {
380  // Search backwards for leading whitespace
381  $wsStart = $i ? ( $i - strspn( $revText, " \t", $lengthText - $i ) ) : 0;
382 
383  // Search forwards for trailing whitespace
384  // $wsEnd will be the position of the last space (or the '>' if there's none)
385  $wsEnd = $endPos + 2 + strspn( $text, " \t", $endPos + 3 );
386 
387  // Keep looking forward as long as we're finding more
388  // comments.
389  $comments = [ [ $wsStart, $wsEnd ] ];
390  while ( substr( $text, $wsEnd + 1, 4 ) == '<!--' ) {
391  $c = strpos( $text, '-->', $wsEnd + 4 );
392  if ( $c === false ) {
393  break;
394  }
395  $c = $c + 2 + strspn( $text, " \t", $c + 3 );
396  $comments[] = [ $wsEnd + 1, $c ];
397  $wsEnd = $c;
398  }
399 
400  // Eat the line if possible
401  // TODO: This could theoretically be done if $wsStart == 0, i.e. for comments at
402  // the overall start. That's not how Sanitizer::removeHTMLcomments() did it, but
403  // it's a possible beneficial b/c break.
404  if ( $wsStart > 0 && substr( $text, $wsStart - 1, 1 ) == "\n"
405  && substr( $text, $wsEnd + 1, 1 ) == "\n"
406  ) {
407  // Remove leading whitespace from the end of the accumulator
408  // Sanity check first though
409  $wsLength = $i - $wsStart;
410  if ( $wsLength > 0
411  && strspn( $accum, " \t", -$wsLength ) === $wsLength
412  ) {
413  $accum = substr( $accum, 0, -$wsLength );
414  }
415 
416  // Dump all but the last comment to the accumulator
417  foreach ( $comments as $j => $com ) {
418  $startPos = $com[0];
419  $endPos = $com[1] + 1;
420  if ( $j == ( count( $comments ) - 1 ) ) {
421  break;
422  }
423  $inner = substr( $text, $startPos, $endPos - $startPos );
424  $accum .= '<comment>' . htmlspecialchars( $inner ) . '</comment>';
425  }
426 
427  // Do a line-start run next time to look for headings after the comment
428  $fakeLineStart = true;
429  } else {
430  // No line to eat, just take the comment itself
431  $startPos = $i;
432  $endPos += 2;
433  }
434 
435  if ( $stack->top ) {
436  $part = $stack->top->getCurrentPart();
437  if ( !( isset( $part->commentEnd ) && $part->commentEnd == $wsStart - 1 ) ) {
438  $part->visualEnd = $wsStart;
439  }
440  // Else comments abutting, no change in visual end
441  $part->commentEnd = $endPos;
442  }
443  $i = $endPos + 1;
444  $inner = substr( $text, $startPos, $endPos - $startPos + 1 );
445  $accum .= '<comment>' . htmlspecialchars( $inner ) . '</comment>';
446  }
447  continue;
448  }
449  $name = $matches[1];
450  $lowerName = strtolower( $name );
451  $attrStart = $i + strlen( $name ) + 1;
452 
453  // Find end of tag
454  $tagEndPos = $noMoreGT ? false : strpos( $text, '>', $attrStart );
455  if ( $tagEndPos === false ) {
456  // Infinite backtrack
457  // Disable tag search to prevent worst-case O(N^2) performance
458  $noMoreGT = true;
459  $accum .= '&lt;';
460  ++$i;
461  continue;
462  }
463 
464  // Handle ignored tags
465  if ( in_array( $lowerName, $ignoredTags ) ) {
466  $accum .= '<ignore>'
467  . htmlspecialchars( substr( $text, $i, $tagEndPos - $i + 1 ) )
468  . '</ignore>';
469  $i = $tagEndPos + 1;
470  continue;
471  }
472 
473  $tagStartPos = $i;
474  if ( $text[$tagEndPos - 1] == '/' ) {
475  $attrEnd = $tagEndPos - 1;
476  $inner = null;
477  $i = $tagEndPos + 1;
478  $close = '';
479  } else {
480  $attrEnd = $tagEndPos;
481  // Find closing tag
482  if (
483  !isset( $noMoreClosingTag[$name] ) &&
484  preg_match( "/<\/" . preg_quote( $name, '/' ) . "\s*>/i",
485  $text, $matches, PREG_OFFSET_CAPTURE, $tagEndPos + 1 )
486  ) {
487  $inner = substr( $text, $tagEndPos + 1, $matches[0][1] - $tagEndPos - 1 );
488  $i = $matches[0][1] + strlen( $matches[0][0] );
489  $close = '<close>' . htmlspecialchars( $matches[0][0] ) . '</close>';
490  } else {
491  // No end tag
492  if ( in_array( $name, $xmlishAllowMissingEndTag ) ) {
493  // Let it run out to the end of the text.
494  $inner = substr( $text, $tagEndPos + 1 );
495  $i = $lengthText;
496  $close = '';
497  } else {
498  // Don't match the tag, treat opening tag as literal and resume parsing.
499  $i = $tagEndPos + 1;
500  $accum .= htmlspecialchars( substr( $text, $tagStartPos, $tagEndPos + 1 - $tagStartPos ) );
501  // Cache results, otherwise we have O(N^2) performance for input like <foo><foo><foo>...
502  $noMoreClosingTag[$name] = true;
503  continue;
504  }
505  }
506  }
507  // <includeonly> and <noinclude> just become <ignore> tags
508  if ( in_array( $lowerName, $ignoredElements ) ) {
509  $accum .= '<ignore>' . htmlspecialchars( substr( $text, $tagStartPos, $i - $tagStartPos ) )
510  . '</ignore>';
511  continue;
512  }
513 
514  $accum .= '<ext>';
515  if ( $attrEnd <= $attrStart ) {
516  $attr = '';
517  } else {
518  $attr = substr( $text, $attrStart, $attrEnd - $attrStart );
519  }
520  $accum .= '<name>' . htmlspecialchars( $name ) . '</name>' .
521  // Note that the attr element contains the whitespace between name and attribute,
522  // this is necessary for precise reconstruction during pre-save transform.
523  '<attr>' . htmlspecialchars( $attr ) . '</attr>';
524  if ( $inner !== null ) {
525  $accum .= '<inner>' . htmlspecialchars( $inner ) . '</inner>';
526  }
527  $accum .= $close . '</ext>';
528  } elseif ( $found == 'line-start' ) {
529  // Is this the start of a heading?
530  // Line break belongs before the heading element in any case
531  if ( $fakeLineStart ) {
532  $fakeLineStart = false;
533  } else {
534  $accum .= $curChar;
535  $i++;
536  }
537 
538  $count = strspn( $text, '=', $i, 6 );
539  if ( $count == 1 && $findEquals ) {
540  // DWIM: This looks kind of like a name/value separator.
541  // Let's let the equals handler have it and break the
542  // potential heading. This is heuristic, but AFAICT the
543  // methods for completely correct disambiguation are very
544  // complex.
545  } elseif ( $count > 0 ) {
546  $piece = [
547  'open' => "\n",
548  'close' => "\n",
549  'parts' => [ new PPDPart( str_repeat( '=', $count ) ) ],
550  'startPos' => $i,
551  'count' => $count ];
552  $stack->push( $piece );
553  $accum =& $stack->getAccum();
554  $flags = $stack->getFlags();
555  extract( $flags );
556  $i += $count;
557  }
558  } elseif ( $found == 'line-end' ) {
559  $piece = $stack->top;
560  // A heading must be open, otherwise \n wouldn't have been in the search list
561  assert( $piece->open === "\n" );
562  $part = $piece->getCurrentPart();
563  // Search back through the input to see if it has a proper close.
564  // Do this using the reversed string since the other solutions
565  // (end anchor, etc.) are inefficient.
566  $wsLength = strspn( $revText, " \t", $lengthText - $i );
567  $searchStart = $i - $wsLength;
568  if ( isset( $part->commentEnd ) && $searchStart - 1 == $part->commentEnd ) {
569  // Comment found at line end
570  // Search for equals signs before the comment
571  $searchStart = $part->visualEnd;
572  $searchStart -= strspn( $revText, " \t", $lengthText - $searchStart );
573  }
574  $count = $piece->count;
575  $equalsLength = strspn( $revText, '=', $lengthText - $searchStart );
576  if ( $equalsLength > 0 ) {
577  if ( $searchStart - $equalsLength == $piece->startPos ) {
578  // This is just a single string of equals signs on its own line
579  // Replicate the doHeadings behavior /={count}(.+)={count}/
580  // First find out how many equals signs there really are (don't stop at 6)
581  $count = $equalsLength;
582  if ( $count < 3 ) {
583  $count = 0;
584  } else {
585  $count = min( 6, intval( ( $count - 1 ) / 2 ) );
586  }
587  } else {
588  $count = min( $equalsLength, $count );
589  }
590  if ( $count > 0 ) {
591  // Normal match, output <h>
592  $element = "<h level=\"$count\" i=\"$headingIndex\">$accum</h>";
593  $headingIndex++;
594  } else {
595  // Single equals sign on its own line, count=0
596  $element = $accum;
597  }
598  } else {
599  // No match, no <h>, just pass down the inner text
600  $element = $accum;
601  }
602  // Unwind the stack
603  $stack->pop();
604  $accum =& $stack->getAccum();
605  $flags = $stack->getFlags();
606  extract( $flags );
607 
608  // Append the result to the enclosing accumulator
609  $accum .= $element;
610  // Note that we do NOT increment the input pointer.
611  // This is because the closing linebreak could be the opening linebreak of
612  // another heading. Infinite loops are avoided because the next iteration MUST
613  // hit the heading open case above, which unconditionally increments the
614  // input pointer.
615  } elseif ( $found == 'open' ) {
616  # count opening brace characters
617  $curLen = strlen( $curChar );
618  $count = ( $curLen > 1 ) ? 1 : strspn( $text, $curChar, $i );
619 
620  # we need to add to stack only if opening brace count is enough for one of the rules
621  if ( $count >= $rule['min'] ) {
622  # Add it to the stack
623  $piece = [
624  'open' => $curChar,
625  'close' => $rule['end'],
626  'count' => $count,
627  'lineStart' => ( $i > 0 && $text[$i - 1] == "\n" ),
628  ];
629 
630  $stack->push( $piece );
631  $accum =& $stack->getAccum();
632  $flags = $stack->getFlags();
633  extract( $flags );
634  } else {
635  # Add literal brace(s)
636  $accum .= htmlspecialchars( str_repeat( $curChar, $count ) );
637  }
638  $i += $curLen * $count;
639  } elseif ( $found == 'close' ) {
640  $piece = $stack->top;
641  # lets check if there are enough characters for closing brace
642  $maxCount = $piece->count;
643  $curLen = strlen( $curChar );
644  $count = ( $curLen > 1 ) ? 1 : strspn( $text, $curChar, $i, $maxCount );
645 
646  # check for maximum matching characters (if there are 5 closing
647  # characters, we will probably need only 3 - depending on the rules)
648  $rule = $this->rules[$piece->open];
649  if ( $count > $rule['max'] ) {
650  # The specified maximum exists in the callback array, unless the caller
651  # has made an error
652  $matchingCount = $rule['max'];
653  } else {
654  # Count is less than the maximum
655  # Skip any gaps in the callback array to find the true largest match
656  # Need to use array_key_exists not isset because the callback can be null
657  $matchingCount = $count;
658  while ( $matchingCount > 0 && !array_key_exists( $matchingCount, $rule['names'] ) ) {
659  --$matchingCount;
660  }
661  }
662 
663  if ( $matchingCount <= 0 ) {
664  # No matching element found in callback array
665  # Output a literal closing brace and continue
666  $accum .= htmlspecialchars( str_repeat( $curChar, $count ) );
667  $i += $curLen * $count;
668  continue;
669  }
670  $name = $rule['names'][$matchingCount];
671  if ( $name === null ) {
672  // No element, just literal text
673  $element = $piece->breakSyntax( $matchingCount ) . str_repeat( $rule['end'], $matchingCount );
674  } else {
675  # Create XML element
676  # Note: $parts is already XML, does not need to be encoded further
677  $parts = $piece->parts;
678  $title = $parts[0]->out;
679  unset( $parts[0] );
680 
681  # The invocation is at the start of the line if lineStart is set in
682  # the stack, and all opening brackets are used up.
683  if ( $maxCount == $matchingCount && !empty( $piece->lineStart ) ) {
684  $attr = ' lineStart="1"';
685  } else {
686  $attr = '';
687  }
688 
689  $element = "<$name$attr>";
690  $element .= "<title>$title</title>";
691  $argIndex = 1;
692  foreach ( $parts as $part ) {
693  if ( isset( $part->eqpos ) ) {
694  $argName = substr( $part->out, 0, $part->eqpos );
695  $argValue = substr( $part->out, $part->eqpos + 1 );
696  $element .= "<part><name>$argName</name>=<value>$argValue</value></part>";
697  } else {
698  $element .= "<part><name index=\"$argIndex\" /><value>{$part->out}</value></part>";
699  $argIndex++;
700  }
701  }
702  $element .= "</$name>";
703  }
704 
705  # Advance input pointer
706  $i += $curLen * $matchingCount;
707 
708  # Unwind the stack
709  $stack->pop();
710  $accum =& $stack->getAccum();
711 
712  # Re-add the old stack element if it still has unmatched opening characters remaining
713  if ( $matchingCount < $piece->count ) {
714  $piece->parts = [ new PPDPart ];
715  $piece->count -= $matchingCount;
716  # do we still qualify for any callback with remaining count?
717  $min = $this->rules[$piece->open]['min'];
718  if ( $piece->count >= $min ) {
719  $stack->push( $piece );
720  $accum =& $stack->getAccum();
721  } else {
722  $accum .= str_repeat( $piece->open, $piece->count );
723  }
724  }
725  $flags = $stack->getFlags();
726  extract( $flags );
727 
728  # Add XML element to the enclosing accumulator
729  $accum .= $element;
730  } elseif ( $found == 'pipe' ) {
731  $findEquals = true; // shortcut for getFlags()
732  $stack->addPart();
733  $accum =& $stack->getAccum();
734  ++$i;
735  } elseif ( $found == 'equals' ) {
736  $findEquals = false; // shortcut for getFlags()
737  $stack->getCurrentPart()->eqpos = strlen( $accum );
738  $accum .= '=';
739  ++$i;
740  } elseif ( $found == 'dash' ) {
741  $accum .= '-';
742  ++$i;
743  }
744  }
745 
746  # Output any remaining unclosed brackets
747  foreach ( $stack->stack as $piece ) {
748  $stack->rootAccum .= $piece->breakSyntax();
749  }
750  $stack->rootAccum .= '</root>';
751  $xml = $stack->rootAccum;
752 
753  return $xml;
754  }
755 }
756 
761 class PPDStack {
763 
767  public $top;
768  public $out;
769  public $elementClass = 'PPDStackElement';
770 
771  public static $false = false;
772 
773  public function __construct() {
774  $this->stack = [];
775  $this->top = false;
776  $this->rootAccum = '';
777  $this->accum =& $this->rootAccum;
778  }
779 
783  public function count() {
784  return count( $this->stack );
785  }
786 
787  public function &getAccum() {
788  return $this->accum;
789  }
790 
791  public function getCurrentPart() {
792  if ( $this->top === false ) {
793  return false;
794  } else {
795  return $this->top->getCurrentPart();
796  }
797  }
798 
799  public function push( $data ) {
800  if ( $data instanceof $this->elementClass ) {
801  $this->stack[] = $data;
802  } else {
803  $class = $this->elementClass;
804  $this->stack[] = new $class( $data );
805  }
806  $this->top = $this->stack[count( $this->stack ) - 1];
807  $this->accum =& $this->top->getAccum();
808  }
809 
810  public function pop() {
811  if ( !count( $this->stack ) ) {
812  throw new MWException( __METHOD__ . ': no elements remaining' );
813  }
814  $temp = array_pop( $this->stack );
815 
816  if ( count( $this->stack ) ) {
817  $this->top = $this->stack[count( $this->stack ) - 1];
818  $this->accum =& $this->top->getAccum();
819  } else {
820  $this->top = self::$false;
821  $this->accum =& $this->rootAccum;
822  }
823  return $temp;
824  }
825 
826  public function addPart( $s = '' ) {
827  $this->top->addPart( $s );
828  $this->accum =& $this->top->getAccum();
829  }
830 
834  public function getFlags() {
835  if ( !count( $this->stack ) ) {
836  return [
837  'findEquals' => false,
838  'findPipe' => false,
839  'inHeading' => false,
840  ];
841  } else {
842  return $this->top->getFlags();
843  }
844  }
845 }
846 
854  public $open;
855 
859  public $close;
860 
864  public $count;
865 
869  public $parts;
870 
875  public $lineStart;
876 
877  public $partClass = 'PPDPart';
878 
879  public function __construct( $data = [] ) {
880  $class = $this->partClass;
881  $this->parts = [ new $class ];
882 
883  foreach ( $data as $name => $value ) {
884  $this->$name = $value;
885  }
886  }
887 
888  public function &getAccum() {
889  return $this->parts[count( $this->parts ) - 1]->out;
890  }
891 
892  public function addPart( $s = '' ) {
893  $class = $this->partClass;
894  $this->parts[] = new $class( $s );
895  }
896 
897  public function getCurrentPart() {
898  return $this->parts[count( $this->parts ) - 1];
899  }
900 
904  public function getFlags() {
905  $partCount = count( $this->parts );
906  $findPipe = $this->open != "\n" && $this->open != '[';
907  return [
908  'findPipe' => $findPipe,
909  'findEquals' => $findPipe && $partCount > 1 && !isset( $this->parts[$partCount - 1]->eqpos ),
910  'inHeading' => $this->open == "\n",
911  ];
912  }
913 
920  public function breakSyntax( $openingCount = false ) {
921  if ( $this->open == "\n" ) {
922  $s = $this->parts[0]->out;
923  } else {
924  if ( $openingCount === false ) {
925  $openingCount = $this->count;
926  }
927  $s = str_repeat( $this->open, $openingCount );
928  $first = true;
929  foreach ( $this->parts as $part ) {
930  if ( $first ) {
931  $first = false;
932  } else {
933  $s .= '|';
934  }
935  $s .= $part->out;
936  }
937  }
938  return $s;
939  }
940 }
941 
945 class PPDPart {
949  public $out;
950 
951  // Optional member variables:
952  // eqpos Position of equals sign in output accumulator
953  // commentEnd Past-the-end input pointer for the last comment encountered
954  // visualEnd Past-the-end input pointer for the end of the accumulator minus comments
955 
956  public function __construct( $out = '' ) {
957  $this->out = $out;
958  }
959 }
960 
965 // @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
966 class PPFrame_DOM implements PPFrame {
967  // @codingStandardsIgnoreEnd
968 
973 
977  public $parser;
978 
982  public $title;
983  public $titleCache;
984 
990 
995  public $depth;
996 
997  private $volatile = false;
998  private $ttl = null;
999 
1004 
1009  public function __construct( $preprocessor ) {
1010  $this->preprocessor = $preprocessor;
1011  $this->parser = $preprocessor->parser;
1012  $this->title = $this->parser->mTitle;
1013  $this->titleCache = [ $this->title ? $this->title->getPrefixedDBkey() : false ];
1014  $this->loopCheckHash = [];
1015  $this->depth = 0;
1016  $this->childExpansionCache = [];
1017  }
1018 
1028  public function newChild( $args = false, $title = false, $indexOffset = 0 ) {
1029  $namedArgs = [];
1030  $numberedArgs = [];
1031  if ( $title === false ) {
1032  $title = $this->title;
1033  }
1034  if ( $args !== false ) {
1035  $xpath = false;
1036  if ( $args instanceof PPNode ) {
1037  $args = $args->node;
1038  }
1039  foreach ( $args as $arg ) {
1040  if ( $arg instanceof PPNode ) {
1041  $arg = $arg->node;
1042  }
1043  if ( !$xpath || $xpath->document !== $arg->ownerDocument ) {
1044  $xpath = new DOMXPath( $arg->ownerDocument );
1045  }
1046 
1047  $nameNodes = $xpath->query( 'name', $arg );
1048  $value = $xpath->query( 'value', $arg );
1049  if ( $nameNodes->item( 0 )->hasAttributes() ) {
1050  // Numbered parameter
1051  $index = $nameNodes->item( 0 )->attributes->getNamedItem( 'index' )->textContent;
1052  $index = $index - $indexOffset;
1053  if ( isset( $namedArgs[$index] ) || isset( $numberedArgs[$index] ) ) {
1054  $this->parser->getOutput()->addWarning( wfMessage( 'duplicate-args-warning',
1055  wfEscapeWikiText( $this->title ),
1057  wfEscapeWikiText( $index ) )->text() );
1058  $this->parser->addTrackingCategory( 'duplicate-args-category' );
1059  }
1060  $numberedArgs[$index] = $value->item( 0 );
1061  unset( $namedArgs[$index] );
1062  } else {
1063  // Named parameter
1064  $name = trim( $this->expand( $nameNodes->item( 0 ), PPFrame::STRIP_COMMENTS ) );
1065  if ( isset( $namedArgs[$name] ) || isset( $numberedArgs[$name] ) ) {
1066  $this->parser->getOutput()->addWarning( wfMessage( 'duplicate-args-warning',
1067  wfEscapeWikiText( $this->title ),
1069  wfEscapeWikiText( $name ) )->text() );
1070  $this->parser->addTrackingCategory( 'duplicate-args-category' );
1071  }
1072  $namedArgs[$name] = $value->item( 0 );
1073  unset( $numberedArgs[$name] );
1074  }
1075  }
1076  }
1077  return new PPTemplateFrame_DOM( $this->preprocessor, $this, $numberedArgs, $namedArgs, $title );
1078  }
1079 
1087  public function cachedExpand( $key, $root, $flags = 0 ) {
1088  // we don't have a parent, so we don't have a cache
1089  return $this->expand( $root, $flags );
1090  }
1091 
1098  public function expand( $root, $flags = 0 ) {
1099  static $expansionDepth = 0;
1100  if ( is_string( $root ) ) {
1101  return $root;
1102  }
1103 
1104  if ( ++$this->parser->mPPNodeCount > $this->parser->mOptions->getMaxPPNodeCount() ) {
1105  $this->parser->limitationWarn( 'node-count-exceeded',
1106  $this->parser->mPPNodeCount,
1107  $this->parser->mOptions->getMaxPPNodeCount()
1108  );
1109  return '<span class="error">Node-count limit exceeded</span>';
1110  }
1111 
1112  if ( $expansionDepth > $this->parser->mOptions->getMaxPPExpandDepth() ) {
1113  $this->parser->limitationWarn( 'expansion-depth-exceeded',
1114  $expansionDepth,
1115  $this->parser->mOptions->getMaxPPExpandDepth()
1116  );
1117  return '<span class="error">Expansion depth limit exceeded</span>';
1118  }
1119  ++$expansionDepth;
1120  if ( $expansionDepth > $this->parser->mHighestExpansionDepth ) {
1121  $this->parser->mHighestExpansionDepth = $expansionDepth;
1122  }
1123 
1124  if ( $root instanceof PPNode_DOM ) {
1125  $root = $root->node;
1126  }
1127  if ( $root instanceof DOMDocument ) {
1128  $root = $root->documentElement;
1129  }
1130 
1131  $outStack = [ '', '' ];
1132  $iteratorStack = [ false, $root ];
1133  $indexStack = [ 0, 0 ];
1134 
1135  while ( count( $iteratorStack ) > 1 ) {
1136  $level = count( $outStack ) - 1;
1137  $iteratorNode =& $iteratorStack[$level];
1138  $out =& $outStack[$level];
1139  $index =& $indexStack[$level];
1140 
1141  if ( $iteratorNode instanceof PPNode_DOM ) {
1142  $iteratorNode = $iteratorNode->node;
1143  }
1144 
1145  if ( is_array( $iteratorNode ) ) {
1146  if ( $index >= count( $iteratorNode ) ) {
1147  // All done with this iterator
1148  $iteratorStack[$level] = false;
1149  $contextNode = false;
1150  } else {
1151  $contextNode = $iteratorNode[$index];
1152  $index++;
1153  }
1154  } elseif ( $iteratorNode instanceof DOMNodeList ) {
1155  if ( $index >= $iteratorNode->length ) {
1156  // All done with this iterator
1157  $iteratorStack[$level] = false;
1158  $contextNode = false;
1159  } else {
1160  $contextNode = $iteratorNode->item( $index );
1161  $index++;
1162  }
1163  } else {
1164  // Copy to $contextNode and then delete from iterator stack,
1165  // because this is not an iterator but we do have to execute it once
1166  $contextNode = $iteratorStack[$level];
1167  $iteratorStack[$level] = false;
1168  }
1169 
1170  if ( $contextNode instanceof PPNode_DOM ) {
1171  $contextNode = $contextNode->node;
1172  }
1173 
1174  $newIterator = false;
1175 
1176  if ( $contextNode === false ) {
1177  // nothing to do
1178  } elseif ( is_string( $contextNode ) ) {
1179  $out .= $contextNode;
1180  } elseif ( is_array( $contextNode ) || $contextNode instanceof DOMNodeList ) {
1181  $newIterator = $contextNode;
1182  } elseif ( $contextNode instanceof DOMNode ) {
1183  if ( $contextNode->nodeType == XML_TEXT_NODE ) {
1184  $out .= $contextNode->nodeValue;
1185  } elseif ( $contextNode->nodeName == 'template' ) {
1186  # Double-brace expansion
1187  $xpath = new DOMXPath( $contextNode->ownerDocument );
1188  $titles = $xpath->query( 'title', $contextNode );
1189  $title = $titles->item( 0 );
1190  $parts = $xpath->query( 'part', $contextNode );
1191  if ( $flags & PPFrame::NO_TEMPLATES ) {
1192  $newIterator = $this->virtualBracketedImplode( '{{', '|', '}}', $title, $parts );
1193  } else {
1194  $lineStart = $contextNode->getAttribute( 'lineStart' );
1195  $params = [
1196  'title' => new PPNode_DOM( $title ),
1197  'parts' => new PPNode_DOM( $parts ),
1198  'lineStart' => $lineStart ];
1199  $ret = $this->parser->braceSubstitution( $params, $this );
1200  if ( isset( $ret['object'] ) ) {
1201  $newIterator = $ret['object'];
1202  } else {
1203  $out .= $ret['text'];
1204  }
1205  }
1206  } elseif ( $contextNode->nodeName == 'tplarg' ) {
1207  # Triple-brace expansion
1208  $xpath = new DOMXPath( $contextNode->ownerDocument );
1209  $titles = $xpath->query( 'title', $contextNode );
1210  $title = $titles->item( 0 );
1211  $parts = $xpath->query( 'part', $contextNode );
1212  if ( $flags & PPFrame::NO_ARGS ) {
1213  $newIterator = $this->virtualBracketedImplode( '{{{', '|', '}}}', $title, $parts );
1214  } else {
1215  $params = [
1216  'title' => new PPNode_DOM( $title ),
1217  'parts' => new PPNode_DOM( $parts ) ];
1218  $ret = $this->parser->argSubstitution( $params, $this );
1219  if ( isset( $ret['object'] ) ) {
1220  $newIterator = $ret['object'];
1221  } else {
1222  $out .= $ret['text'];
1223  }
1224  }
1225  } elseif ( $contextNode->nodeName == 'comment' ) {
1226  # HTML-style comment
1227  # Remove it in HTML, pre+remove and STRIP_COMMENTS modes
1228  # Not in RECOVER_COMMENTS mode (msgnw) though.
1229  if ( ( $this->parser->ot['html']
1230  || ( $this->parser->ot['pre'] && $this->parser->mOptions->getRemoveComments() )
1232  ) && !( $flags & PPFrame::RECOVER_COMMENTS )
1233  ) {
1234  $out .= '';
1235  } elseif ( $this->parser->ot['wiki'] && !( $flags & PPFrame::RECOVER_COMMENTS ) ) {
1236  # Add a strip marker in PST mode so that pstPass2() can
1237  # run some old-fashioned regexes on the result.
1238  # Not in RECOVER_COMMENTS mode (extractSections) though.
1239  $out .= $this->parser->insertStripItem( $contextNode->textContent );
1240  } else {
1241  # Recover the literal comment in RECOVER_COMMENTS and pre+no-remove
1242  $out .= $contextNode->textContent;
1243  }
1244  } elseif ( $contextNode->nodeName == 'ignore' ) {
1245  # Output suppression used by <includeonly> etc.
1246  # OT_WIKI will only respect <ignore> in substed templates.
1247  # The other output types respect it unless NO_IGNORE is set.
1248  # extractSections() sets NO_IGNORE and so never respects it.
1249  if ( ( !isset( $this->parent ) && $this->parser->ot['wiki'] )
1250  || ( $flags & PPFrame::NO_IGNORE )
1251  ) {
1252  $out .= $contextNode->textContent;
1253  } else {
1254  $out .= '';
1255  }
1256  } elseif ( $contextNode->nodeName == 'ext' ) {
1257  # Extension tag
1258  $xpath = new DOMXPath( $contextNode->ownerDocument );
1259  $names = $xpath->query( 'name', $contextNode );
1260  $attrs = $xpath->query( 'attr', $contextNode );
1261  $inners = $xpath->query( 'inner', $contextNode );
1262  $closes = $xpath->query( 'close', $contextNode );
1263  if ( $flags & PPFrame::NO_TAGS ) {
1264  $s = '<' . $this->expand( $names->item( 0 ), $flags );
1265  if ( $attrs->length > 0 ) {
1266  $s .= $this->expand( $attrs->item( 0 ), $flags );
1267  }
1268  if ( $inners->length > 0 ) {
1269  $s .= '>' . $this->expand( $inners->item( 0 ), $flags );
1270  if ( $closes->length > 0 ) {
1271  $s .= $this->expand( $closes->item( 0 ), $flags );
1272  }
1273  } else {
1274  $s .= '/>';
1275  }
1276  $out .= $s;
1277  } else {
1278  $params = [
1279  'name' => new PPNode_DOM( $names->item( 0 ) ),
1280  'attr' => $attrs->length > 0 ? new PPNode_DOM( $attrs->item( 0 ) ) : null,
1281  'inner' => $inners->length > 0 ? new PPNode_DOM( $inners->item( 0 ) ) : null,
1282  'close' => $closes->length > 0 ? new PPNode_DOM( $closes->item( 0 ) ) : null,
1283  ];
1284  $out .= $this->parser->extensionSubstitution( $params, $this );
1285  }
1286  } elseif ( $contextNode->nodeName == 'h' ) {
1287  # Heading
1288  $s = $this->expand( $contextNode->childNodes, $flags );
1289 
1290  # Insert a heading marker only for <h> children of <root>
1291  # This is to stop extractSections from going over multiple tree levels
1292  if ( $contextNode->parentNode->nodeName == 'root' && $this->parser->ot['html'] ) {
1293  # Insert heading index marker
1294  $headingIndex = $contextNode->getAttribute( 'i' );
1295  $titleText = $this->title->getPrefixedDBkey();
1296  $this->parser->mHeadings[] = [ $titleText, $headingIndex ];
1297  $serial = count( $this->parser->mHeadings ) - 1;
1298  $marker = Parser::MARKER_PREFIX . "-h-$serial-" . Parser::MARKER_SUFFIX;
1299  $count = $contextNode->getAttribute( 'level' );
1300  $s = substr( $s, 0, $count ) . $marker . substr( $s, $count );
1301  $this->parser->mStripState->addGeneral( $marker, '' );
1302  }
1303  $out .= $s;
1304  } else {
1305  # Generic recursive expansion
1306  $newIterator = $contextNode->childNodes;
1307  }
1308  } else {
1309  throw new MWException( __METHOD__ . ': Invalid parameter type' );
1310  }
1311 
1312  if ( $newIterator !== false ) {
1313  if ( $newIterator instanceof PPNode_DOM ) {
1314  $newIterator = $newIterator->node;
1315  }
1316  $outStack[] = '';
1317  $iteratorStack[] = $newIterator;
1318  $indexStack[] = 0;
1319  } elseif ( $iteratorStack[$level] === false ) {
1320  // Return accumulated value to parent
1321  // With tail recursion
1322  while ( $iteratorStack[$level] === false && $level > 0 ) {
1323  $outStack[$level - 1] .= $out;
1324  array_pop( $outStack );
1325  array_pop( $iteratorStack );
1326  array_pop( $indexStack );
1327  $level--;
1328  }
1329  }
1330  }
1331  --$expansionDepth;
1332  return $outStack[0];
1333  }
1334 
1341  public function implodeWithFlags( $sep, $flags /*, ... */ ) {
1342  $args = array_slice( func_get_args(), 2 );
1343 
1344  $first = true;
1345  $s = '';
1346  foreach ( $args as $root ) {
1347  if ( $root instanceof PPNode_DOM ) {
1348  $root = $root->node;
1349  }
1350  if ( !is_array( $root ) && !( $root instanceof DOMNodeList ) ) {
1351  $root = [ $root ];
1352  }
1353  foreach ( $root as $node ) {
1354  if ( $first ) {
1355  $first = false;
1356  } else {
1357  $s .= $sep;
1358  }
1359  $s .= $this->expand( $node, $flags );
1360  }
1361  }
1362  return $s;
1363  }
1364 
1373  public function implode( $sep /*, ... */ ) {
1374  $args = array_slice( func_get_args(), 1 );
1375 
1376  $first = true;
1377  $s = '';
1378  foreach ( $args as $root ) {
1379  if ( $root instanceof PPNode_DOM ) {
1380  $root = $root->node;
1381  }
1382  if ( !is_array( $root ) && !( $root instanceof DOMNodeList ) ) {
1383  $root = [ $root ];
1384  }
1385  foreach ( $root as $node ) {
1386  if ( $first ) {
1387  $first = false;
1388  } else {
1389  $s .= $sep;
1390  }
1391  $s .= $this->expand( $node );
1392  }
1393  }
1394  return $s;
1395  }
1396 
1405  public function virtualImplode( $sep /*, ... */ ) {
1406  $args = array_slice( func_get_args(), 1 );
1407  $out = [];
1408  $first = true;
1409 
1410  foreach ( $args as $root ) {
1411  if ( $root instanceof PPNode_DOM ) {
1412  $root = $root->node;
1413  }
1414  if ( !is_array( $root ) && !( $root instanceof DOMNodeList ) ) {
1415  $root = [ $root ];
1416  }
1417  foreach ( $root as $node ) {
1418  if ( $first ) {
1419  $first = false;
1420  } else {
1421  $out[] = $sep;
1422  }
1423  $out[] = $node;
1424  }
1425  }
1426  return $out;
1427  }
1428 
1437  public function virtualBracketedImplode( $start, $sep, $end /*, ... */ ) {
1438  $args = array_slice( func_get_args(), 3 );
1439  $out = [ $start ];
1440  $first = true;
1441 
1442  foreach ( $args as $root ) {
1443  if ( $root instanceof PPNode_DOM ) {
1444  $root = $root->node;
1445  }
1446  if ( !is_array( $root ) && !( $root instanceof DOMNodeList ) ) {
1447  $root = [ $root ];
1448  }
1449  foreach ( $root as $node ) {
1450  if ( $first ) {
1451  $first = false;
1452  } else {
1453  $out[] = $sep;
1454  }
1455  $out[] = $node;
1456  }
1457  }
1458  $out[] = $end;
1459  return $out;
1460  }
1461 
1462  public function __toString() {
1463  return 'frame{}';
1464  }
1465 
1466  public function getPDBK( $level = false ) {
1467  if ( $level === false ) {
1468  return $this->title->getPrefixedDBkey();
1469  } else {
1470  return isset( $this->titleCache[$level] ) ? $this->titleCache[$level] : false;
1471  }
1472  }
1473 
1477  public function getArguments() {
1478  return [];
1479  }
1480 
1484  public function getNumberedArguments() {
1485  return [];
1486  }
1487 
1491  public function getNamedArguments() {
1492  return [];
1493  }
1494 
1500  public function isEmpty() {
1501  return true;
1502  }
1503 
1508  public function getArgument( $name ) {
1509  return false;
1510  }
1511 
1518  public function loopCheck( $title ) {
1519  return !isset( $this->loopCheckHash[$title->getPrefixedDBkey()] );
1520  }
1521 
1527  public function isTemplate() {
1528  return false;
1529  }
1530 
1536  public function getTitle() {
1537  return $this->title;
1538  }
1539 
1545  public function setVolatile( $flag = true ) {
1546  $this->volatile = $flag;
1547  }
1548 
1554  public function isVolatile() {
1555  return $this->volatile;
1556  }
1557 
1563  public function setTTL( $ttl ) {
1564  if ( $ttl !== null && ( $this->ttl === null || $ttl < $this->ttl ) ) {
1565  $this->ttl = $ttl;
1566  }
1567  }
1568 
1574  public function getTTL() {
1575  return $this->ttl;
1576  }
1577 }
1578 
1583 // @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
1585  // @codingStandardsIgnoreEnd
1586 
1588 
1592  public $parent;
1594 
1602  public function __construct( $preprocessor, $parent = false, $numberedArgs = [],
1603  $namedArgs = [], $title = false
1604  ) {
1605  parent::__construct( $preprocessor );
1606 
1607  $this->parent = $parent;
1608  $this->numberedArgs = $numberedArgs;
1609  $this->namedArgs = $namedArgs;
1610  $this->title = $title;
1611  $pdbk = $title ? $title->getPrefixedDBkey() : false;
1612  $this->titleCache = $parent->titleCache;
1613  $this->titleCache[] = $pdbk;
1614  $this->loopCheckHash = /*clone*/ $parent->loopCheckHash;
1615  if ( $pdbk !== false ) {
1616  $this->loopCheckHash[$pdbk] = true;
1617  }
1618  $this->depth = $parent->depth + 1;
1619  $this->numberedExpansionCache = $this->namedExpansionCache = [];
1620  }
1621 
1622  public function __toString() {
1623  $s = 'tplframe{';
1624  $first = true;
1625  $args = $this->numberedArgs + $this->namedArgs;
1626  foreach ( $args as $name => $value ) {
1627  if ( $first ) {
1628  $first = false;
1629  } else {
1630  $s .= ', ';
1631  }
1632  $s .= "\"$name\":\"" .
1633  str_replace( '"', '\\"', $value->ownerDocument->saveXML( $value ) ) . '"';
1634  }
1635  $s .= '}';
1636  return $s;
1637  }
1638 
1646  public function cachedExpand( $key, $root, $flags = 0 ) {
1647  if ( isset( $this->parent->childExpansionCache[$key] ) ) {
1648  return $this->parent->childExpansionCache[$key];
1649  }
1650  $retval = $this->expand( $root, $flags );
1651  if ( !$this->isVolatile() ) {
1652  $this->parent->childExpansionCache[$key] = $retval;
1653  }
1654  return $retval;
1655  }
1656 
1662  public function isEmpty() {
1663  return !count( $this->numberedArgs ) && !count( $this->namedArgs );
1664  }
1665 
1666  public function getArguments() {
1667  $arguments = [];
1668  foreach ( array_merge(
1669  array_keys( $this->numberedArgs ),
1670  array_keys( $this->namedArgs ) ) as $key ) {
1671  $arguments[$key] = $this->getArgument( $key );
1672  }
1673  return $arguments;
1674  }
1675 
1676  public function getNumberedArguments() {
1677  $arguments = [];
1678  foreach ( array_keys( $this->numberedArgs ) as $key ) {
1679  $arguments[$key] = $this->getArgument( $key );
1680  }
1681  return $arguments;
1682  }
1683 
1684  public function getNamedArguments() {
1685  $arguments = [];
1686  foreach ( array_keys( $this->namedArgs ) as $key ) {
1687  $arguments[$key] = $this->getArgument( $key );
1688  }
1689  return $arguments;
1690  }
1691 
1696  public function getNumberedArgument( $index ) {
1697  if ( !isset( $this->numberedArgs[$index] ) ) {
1698  return false;
1699  }
1700  if ( !isset( $this->numberedExpansionCache[$index] ) ) {
1701  # No trimming for unnamed arguments
1702  $this->numberedExpansionCache[$index] = $this->parent->expand(
1703  $this->numberedArgs[$index],
1705  );
1706  }
1707  return $this->numberedExpansionCache[$index];
1708  }
1709 
1714  public function getNamedArgument( $name ) {
1715  if ( !isset( $this->namedArgs[$name] ) ) {
1716  return false;
1717  }
1718  if ( !isset( $this->namedExpansionCache[$name] ) ) {
1719  # Trim named arguments post-expand, for backwards compatibility
1720  $this->namedExpansionCache[$name] = trim(
1721  $this->parent->expand( $this->namedArgs[$name], PPFrame::STRIP_COMMENTS ) );
1722  }
1723  return $this->namedExpansionCache[$name];
1724  }
1725 
1730  public function getArgument( $name ) {
1731  $text = $this->getNumberedArgument( $name );
1732  if ( $text === false ) {
1733  $text = $this->getNamedArgument( $name );
1734  }
1735  return $text;
1736  }
1737 
1743  public function isTemplate() {
1744  return true;
1745  }
1746 
1747  public function setVolatile( $flag = true ) {
1748  parent::setVolatile( $flag );
1749  $this->parent->setVolatile( $flag );
1750  }
1751 
1752  public function setTTL( $ttl ) {
1753  parent::setTTL( $ttl );
1754  $this->parent->setTTL( $ttl );
1755  }
1756 }
1757 
1762 // @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
1764  // @codingStandardsIgnoreEnd
1765 
1766  public $args;
1767 
1768  public function __construct( $preprocessor, $args ) {
1769  parent::__construct( $preprocessor );
1770  $this->args = $args;
1771  }
1772 
1773  public function __toString() {
1774  $s = 'cstmframe{';
1775  $first = true;
1776  foreach ( $this->args as $name => $value ) {
1777  if ( $first ) {
1778  $first = false;
1779  } else {
1780  $s .= ', ';
1781  }
1782  $s .= "\"$name\":\"" .
1783  str_replace( '"', '\\"', $value->__toString() ) . '"';
1784  }
1785  $s .= '}';
1786  return $s;
1787  }
1788 
1792  public function isEmpty() {
1793  return !count( $this->args );
1794  }
1795 
1800  public function getArgument( $index ) {
1801  if ( !isset( $this->args[$index] ) ) {
1802  return false;
1803  }
1804  return $this->args[$index];
1805  }
1806 
1807  public function getArguments() {
1808  return $this->args;
1809  }
1810 }
1811 
1815 // @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps
1816 class PPNode_DOM implements PPNode {
1817  // @codingStandardsIgnoreEnd
1818 
1822  public $node;
1823  public $xpath;
1824 
1825  public function __construct( $node, $xpath = false ) {
1826  $this->node = $node;
1827  }
1828 
1832  public function getXPath() {
1833  if ( $this->xpath === null ) {
1834  $this->xpath = new DOMXPath( $this->node->ownerDocument );
1835  }
1836  return $this->xpath;
1837  }
1838 
1839  public function __toString() {
1840  if ( $this->node instanceof DOMNodeList ) {
1841  $s = '';
1842  foreach ( $this->node as $node ) {
1843  $s .= $node->ownerDocument->saveXML( $node );
1844  }
1845  } else {
1846  $s = $this->node->ownerDocument->saveXML( $this->node );
1847  }
1848  return $s;
1849  }
1850 
1854  public function getChildren() {
1855  return $this->node->childNodes ? new self( $this->node->childNodes ) : false;
1856  }
1857 
1861  public function getFirstChild() {
1862  return $this->node->firstChild ? new self( $this->node->firstChild ) : false;
1863  }
1864 
1868  public function getNextSibling() {
1869  return $this->node->nextSibling ? new self( $this->node->nextSibling ) : false;
1870  }
1871 
1877  public function getChildrenOfType( $type ) {
1878  return new self( $this->getXPath()->query( $type, $this->node ) );
1879  }
1880 
1884  public function getLength() {
1885  if ( $this->node instanceof DOMNodeList ) {
1886  return $this->node->length;
1887  } else {
1888  return false;
1889  }
1890  }
1891 
1896  public function item( $i ) {
1897  $item = $this->node->item( $i );
1898  return $item ? new self( $item ) : false;
1899  }
1900 
1904  public function getName() {
1905  if ( $this->node instanceof DOMNodeList ) {
1906  return '#nodelist';
1907  } else {
1908  return $this->node->nodeName;
1909  }
1910  }
1911 
1921  public function splitArg() {
1922  $xpath = $this->getXPath();
1923  $names = $xpath->query( 'name', $this->node );
1924  $values = $xpath->query( 'value', $this->node );
1925  if ( !$names->length || !$values->length ) {
1926  throw new MWException( 'Invalid brace node passed to ' . __METHOD__ );
1927  }
1928  $name = $names->item( 0 );
1929  $index = $name->getAttribute( 'index' );
1930  return [
1931  'name' => new self( $name ),
1932  'index' => $index,
1933  'value' => new self( $values->item( 0 ) ) ];
1934  }
1935 
1943  public function splitExt() {
1944  $xpath = $this->getXPath();
1945  $names = $xpath->query( 'name', $this->node );
1946  $attrs = $xpath->query( 'attr', $this->node );
1947  $inners = $xpath->query( 'inner', $this->node );
1948  $closes = $xpath->query( 'close', $this->node );
1949  if ( !$names->length || !$attrs->length ) {
1950  throw new MWException( 'Invalid ext node passed to ' . __METHOD__ );
1951  }
1952  $parts = [
1953  'name' => new self( $names->item( 0 ) ),
1954  'attr' => new self( $attrs->item( 0 ) ) ];
1955  if ( $inners->length ) {
1956  $parts['inner'] = new self( $inners->item( 0 ) );
1957  }
1958  if ( $closes->length ) {
1959  $parts['close'] = new self( $closes->item( 0 ) );
1960  }
1961  return $parts;
1962  }
1963 
1969  public function splitHeading() {
1970  if ( $this->getName() !== 'h' ) {
1971  throw new MWException( 'Invalid h node passed to ' . __METHOD__ );
1972  }
1973  return [
1974  'i' => $this->node->getAttribute( 'i' ),
1975  'level' => $this->node->getAttribute( 'level' ),
1976  'contents' => $this->getChildren()
1977  ];
1978  }
1979 }
PPFrame_DOM\getPDBK
getPDBK( $level=false)
Definition: Preprocessor_DOM.php:1466
Preprocessor_DOM\preprocessToXml
preprocessToXml( $text, $flags=0)
Definition: Preprocessor_DOM.php:195
PPCustomFrame_DOM\$args
$args
Definition: Preprocessor_DOM.php:1766
PPFrame_DOM\$preprocessor
Preprocessor $preprocessor
Definition: Preprocessor_DOM.php:972
Preprocessor_DOM
Definition: Preprocessor_DOM.php:28
PPFrame\STRIP_COMMENTS
const STRIP_COMMENTS
Definition: Preprocessor.php:168
PPDPart
Definition: Preprocessor_DOM.php:945
PPNode_DOM
Definition: Preprocessor_DOM.php:1816
PPNode_DOM\item
item( $i)
Definition: Preprocessor_DOM.php:1896
PPNode_DOM\$xpath
$xpath
Definition: Preprocessor_DOM.php:1823
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:189
Preprocessor_DOM\$memoryLimit
$memoryLimit
Definition: Preprocessor_DOM.php:36
Preprocessor_DOM\$parser
Parser $parser
Definition: Preprocessor_DOM.php:34
PPDStackElement\__construct
__construct( $data=[])
Definition: Preprocessor_DOM.php:879
PPFrame_DOM\implodeWithFlags
implodeWithFlags( $sep, $flags)
Definition: Preprocessor_DOM.php:1341
PPNode_DOM\$node
DOMElement $node
Definition: Preprocessor_DOM.php:1822
PPDPart\$out
string $out
Output accumulator string.
Definition: Preprocessor_DOM.php:949
PPFrame_DOM\implode
implode( $sep)
Implode with no flags specified This previously called implodeWithFlags but has now been inlined to r...
Definition: Preprocessor_DOM.php:1373
PPDStack\pop
pop()
Definition: Preprocessor_DOM.php:810
PPFrame_DOM\isVolatile
isVolatile()
Get the volatile flag.
Definition: Preprocessor_DOM.php:1554
PPFrame\NO_ARGS
const NO_ARGS
Definition: Preprocessor.php:166
PPFrame_DOM\$depth
$depth
Recursion depth of this frame, top = 0 Note that this is NOT the same as expansion depth in expand()
Definition: Preprocessor_DOM.php:995
PPTemplateFrame_DOM\$namedExpansionCache
$namedExpansionCache
Definition: Preprocessor_DOM.php:1593
captcha-old.count
count
Definition: captcha-old.py:225
Title\getPrefixedDBkey
getPrefixedDBkey()
Get the prefixed database key form.
Definition: Title.php:1439
text
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add text
Definition: design.txt:12
PPDStackElement\$parts
PPDPart[] $parts
Array of PPDPart objects describing pipe-separated parts.
Definition: Preprocessor_DOM.php:869
$result
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. Return false to stop further processing of the tag $reader:XMLReader object $logInfo:Array of information 'ImportHandlePageXMLTag':When parsing a XML tag in a page. Return false to stop further processing of the tag $reader:XMLReader object & $pageInfo:Array of information 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. Return false to stop further processing of the tag $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. Return false to stop further processing of the tag $reader:XMLReader object 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. Return false to stop further processing of the tag $reader:XMLReader object $revisionInfo:Array of information 'ImportLogInterwikiLink':Hook to change the interwiki link used in log entries and edit summaries for transwiki imports. & $fullInterwikiPrefix:Interwiki prefix, may contain colons. & $pageTitle:String that contains page title. 'ImportSources':Called when reading from the $wgImportSources configuration variable. Can be used to lazy-load the import sources list. & $importSources:The value of $wgImportSources. Modify as necessary. See the comment in DefaultSettings.php for the detail of how to structure this array. 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. & $title:Title object for the current page & $request:WebRequest & $ignoreRedirect:boolean to skip redirect check & $target:Title/string of redirect target & $article:Article object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) & $article:article(object) being checked 'IsTrustedProxy':Override the result of IP::isTrustedProxy() & $ip:IP being check & $result:Change this value to override the result of IP::isTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of Sanitizer::validateEmail(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetMagic':DEPRECATED! Use $magicWords in a file listed in $wgExtensionMessagesFiles instead. Use this to define synonyms of magic words depending of the language & $magicExtensions:associative array of magic words synonyms $lang:language code(string) 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetSpecialPageAliases':DEPRECATED! Use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead. Use to define aliases of special pages names depending of the language & $specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code:language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Array with elements of the form "language:title" in the order that they will be output. & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LanguageSelector':Hook to change the language selector available on a page. $out:The output page. $cssClassName:CSS class name of the language selector. 'LinkBegin':DEPRECATED! Use HtmlPageLinkRendererBegin instead. Used when generating internal and interwiki links in Linker::link(), before processing starts. Return false to skip default processing and return $ret. See documentation for Linker::link() for details on the expected meanings of parameters. $skin:the Skin object $target:the Title that the link is pointing to & $html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:1954
PPFrame_DOM\getNamedArguments
getNamedArguments()
Definition: Preprocessor_DOM.php:1491
PPFrame\NO_IGNORE
const NO_IGNORE
Definition: Preprocessor.php:169
PPDStack\push
push( $data)
Definition: Preprocessor_DOM.php:799
PPFrame_DOM\$titleCache
$titleCache
Definition: Preprocessor_DOM.php:983
PPDStack\$top
PPDStack $top
Definition: Preprocessor_DOM.php:767
PPFrame_DOM\$volatile
$volatile
Definition: Preprocessor_DOM.php:997
PPFrame_DOM\cachedExpand
cachedExpand( $key, $root, $flags=0)
Definition: Preprocessor_DOM.php:1087
PPDStackElement\getCurrentPart
getCurrentPart()
Definition: Preprocessor_DOM.php:897
PPFrame_DOM\getArgument
getArgument( $name)
Definition: Preprocessor_DOM.php:1508
PPDStack\$false
static $false
Definition: Preprocessor_DOM.php:771
$params
$params
Definition: styleTest.css.php:40
PPTemplateFrame_DOM\$numberedExpansionCache
$numberedExpansionCache
Definition: Preprocessor_DOM.php:1593
PPFrame_DOM\$ttl
$ttl
Definition: Preprocessor_DOM.php:998
PPTemplateFrame_DOM\getNumberedArgument
getNumberedArgument( $index)
Definition: Preprocessor_DOM.php:1696
PPDStack\$elementClass
$elementClass
Definition: Preprocessor_DOM.php:769
PPTemplateFrame_DOM\getNumberedArguments
getNumberedArguments()
Definition: Preprocessor_DOM.php:1676
$s
$s
Definition: mergeMessageFileList.php:188
PPDStackElement
Definition: Preprocessor_DOM.php:850
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:304
PPNode_DOM\splitHeading
splitHeading()
Split a "<h>" node.
Definition: Preprocessor_DOM.php:1969
PPTemplateFrame_DOM\__construct
__construct( $preprocessor, $parent=false, $numberedArgs=[], $namedArgs=[], $title=false)
Definition: Preprocessor_DOM.php:1602
PPDStackElement\$close
string $close
Matching closing character.
Definition: Preprocessor_DOM.php:859
Makefile.open
open
Definition: Makefile.py:18
PPFrame_DOM\virtualBracketedImplode
virtualBracketedImplode( $start, $sep, $end)
Virtual implode with brackets.
Definition: Preprocessor_DOM.php:1437
$type
do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached my talk my contributions etc etc otherwise the built in rate limiting checks are if enabled allows for interception of redirect as a string mapping parameter names to values & $type
Definition: hooks.txt:2536
PPNode_DOM\getXPath
getXPath()
Definition: Preprocessor_DOM.php:1832
PPDStack\getAccum
& getAccum()
Definition: Preprocessor_DOM.php:787
PPTemplateFrame_DOM\getNamedArguments
getNamedArguments()
Definition: Preprocessor_DOM.php:1684
PPDStackElement\getAccum
& getAccum()
Definition: Preprocessor_DOM.php:888
PPFrame\RECOVER_COMMENTS
const RECOVER_COMMENTS
Definition: Preprocessor.php:170
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
PPFrame\NO_TEMPLATES
const NO_TEMPLATES
Definition: Preprocessor.php:167
Preprocessor
Definition: Preprocessor.php:29
PPDStack\count
count()
Definition: Preprocessor_DOM.php:783
PPCustomFrame_DOM\__toString
__toString()
Definition: Preprocessor_DOM.php:1773
PPDStackElement\$lineStart
bool $lineStart
True if the open char appeared at the start of the input line.
Definition: Preprocessor_DOM.php:875
PPNode_DOM\__construct
__construct( $node, $xpath=false)
Definition: Preprocessor_DOM.php:1825
PPCustomFrame_DOM
Expansion frame with custom arguments.
Definition: Preprocessor_DOM.php:1763
PPNode_DOM\getName
getName()
Definition: Preprocessor_DOM.php:1904
PPFrame_DOM\isEmpty
isEmpty()
Returns true if there are no arguments in this frame.
Definition: Preprocessor_DOM.php:1500
PPCustomFrame_DOM\__construct
__construct( $preprocessor, $args)
Definition: Preprocessor_DOM.php:1768
PPTemplateFrame_DOM\isEmpty
isEmpty()
Returns true if there are no arguments in this frame.
Definition: Preprocessor_DOM.php:1662
MWException
MediaWiki exception.
Definition: MWException.php:26
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:934
PPNode_DOM\getLength
getLength()
Definition: Preprocessor_DOM.php:1884
PPFrame_DOM\setTTL
setTTL( $ttl)
Set the TTL.
Definition: Preprocessor_DOM.php:1563
$titles
linkcache txt The LinkCache class maintains a list of article titles and the information about whether or not the article exists in the database This is used to mark up links when displaying a page If the same link appears more than once on any page then it only has to be looked up once In most cases link lookups are done in batches with the LinkBatch class or the equivalent in so the link cache is mostly useful for short snippets of parsed and for links in the navigation areas of the skin The link cache was formerly used to track links used in a document for the purposes of updating the link tables This application is now deprecated To create a you can use the following $titles
Definition: linkcache.txt:17
PPFrame_DOM\loopCheck
loopCheck( $title)
Returns true if the infinite loop check is OK, false if a loop is detected.
Definition: Preprocessor_DOM.php:1518
PPDStackElement\breakSyntax
breakSyntax( $openingCount=false)
Get the output string that would result if the close is not found.
Definition: Preprocessor_DOM.php:920
PPDStack\$rootAccum
$rootAccum
Definition: Preprocessor_DOM.php:762
PPDStackElement\getFlags
getFlags()
Definition: Preprocessor_DOM.php:904
PPDStackElement\addPart
addPart( $s='')
Definition: Preprocessor_DOM.php:892
PPCustomFrame_DOM\getArgument
getArgument( $index)
Definition: Preprocessor_DOM.php:1800
PPTemplateFrame_DOM\__toString
__toString()
Definition: Preprocessor_DOM.php:1622
PPDStackElement\$count
int $count
Number of opening characters found (number of "=" for heading)
Definition: Preprocessor_DOM.php:864
$matches
$matches
Definition: NoLocalSettings.php:24
Preprocessor_DOM\__construct
__construct( $parser)
Definition: Preprocessor_DOM.php:40
PPNode
There are three types of nodes:
Definition: Preprocessor.php:358
PPDStack\addPart
addPart( $s='')
Definition: Preprocessor_DOM.php:826
PPDStackElement\$partClass
$partClass
Definition: Preprocessor_DOM.php:877
PPNode_DOM\getChildren
getChildren()
Definition: Preprocessor_DOM.php:1854
PPTemplateFrame_DOM\$parent
PPFrame_DOM $parent
Definition: Preprocessor_DOM.php:1592
$limit
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context the output can only depend on parameters provided to this hook not on global state indicating whether full HTML should be generated If generation of HTML may be but other information should still be present in the ParserOutput object to manipulate or replace but no entry for that model exists in $wgContentHandlers please use GetContentModels hook to make them known to core if desired whether it is OK to use $contentModel on $title Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok inclusive $limit
Definition: hooks.txt:1049
Preprocessor_DOM\newCustomFrame
newCustomFrame( $args)
Definition: Preprocessor_DOM.php:64
PPDStack\__construct
__construct()
Definition: Preprocessor_DOM.php:773
PPDStack\$out
$out
Definition: Preprocessor_DOM.php:768
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
PPNode_DOM\getChildrenOfType
getChildrenOfType( $type)
Definition: Preprocessor_DOM.php:1877
PPDPart\__construct
__construct( $out='')
Definition: Preprocessor_DOM.php:956
PPFrame_DOM\getTTL
getTTL()
Get the TTL.
Definition: Preprocessor_DOM.php:1574
PPTemplateFrame_DOM\$namedArgs
$namedArgs
Definition: Preprocessor_DOM.php:1587
PPDStackElement\$open
string $open
Opening character (\n for heading)
Definition: Preprocessor_DOM.php:854
PPFrame_DOM\getArguments
getArguments()
Definition: Preprocessor_DOM.php:1477
PPFrame_DOM\isTemplate
isTemplate()
Return true if the frame is a template frame.
Definition: Preprocessor_DOM.php:1527
Preprocessor_DOM\memCheck
memCheck()
Definition: Preprocessor_DOM.php:114
PPFrame_DOM\$loopCheckHash
$loopCheckHash
Hashtable listing templates which are disallowed for expansion in this frame, having been encountered...
Definition: Preprocessor_DOM.php:989
PPDStack\$stack
$stack
Definition: Preprocessor_DOM.php:762
$value
$value
Definition: styleTest.css.php:45
PPFrame_DOM\expand
expand( $root, $flags=0)
Definition: Preprocessor_DOM.php:1098
PPFrame_DOM\setVolatile
setVolatile( $flag=true)
Set the volatile flag.
Definition: Preprocessor_DOM.php:1545
$retval
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account incomplete not yet checked for validity & $retval
Definition: hooks.txt:246
PPFrame_DOM
An expansion frame, used as a context to expand the result of preprocessToObj()
Definition: Preprocessor_DOM.php:966
title
title
Definition: parserTests.txt:211
PPDStack
Stack class to help Preprocessor::preprocessToObj()
Definition: Preprocessor_DOM.php:761
PPFrame\NO_TAGS
const NO_TAGS
Definition: Preprocessor.php:171
PPFrame
Definition: Preprocessor.php:165
PPTemplateFrame_DOM\$numberedArgs
$numberedArgs
Definition: Preprocessor_DOM.php:1587
wfEscapeWikiText
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Definition: GlobalFunctions.php:1657
$ret
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 & $ret
Definition: hooks.txt:1956
PPNode_DOM\__toString
__toString()
Definition: Preprocessor_DOM.php:1839
PPTemplateFrame_DOM\getArguments
getArguments()
Definition: Preprocessor_DOM.php:1666
$args
if( $line===false) $args
Definition: cdb.php:63
Preprocessor\cacheSetTree
cacheSetTree( $text, $flags, $tree)
Store a document tree in the cache.
Definition: Preprocessor.php:66
Title
Represents a title within MediaWiki.
Definition: Title.php:39
Preprocessor_DOM\CACHE_PREFIX
const CACHE_PREFIX
Definition: Preprocessor_DOM.php:38
PPCustomFrame_DOM\isEmpty
isEmpty()
Definition: Preprocessor_DOM.php:1792
PPFrame_DOM\$parser
Parser $parser
Definition: Preprocessor_DOM.php:977
PPNode_DOM\splitArg
splitArg()
Split a "<part>" node into an associative array containing:
Definition: Preprocessor_DOM.php:1921
PPFrame_DOM\__toString
__toString()
Definition: Preprocessor_DOM.php:1462
PPTemplateFrame_DOM\cachedExpand
cachedExpand( $key, $root, $flags=0)
Definition: Preprocessor_DOM.php:1646
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
Preprocessor_DOM\newPartNodeArray
newPartNodeArray( $values)
Definition: Preprocessor_DOM.php:73
Preprocessor_DOM\newFrame
newFrame()
Definition: Preprocessor_DOM.php:56
PPFrame_DOM\getTitle
getTitle()
Get a title of frame.
Definition: Preprocessor_DOM.php:1536
PPFrame_DOM\newChild
newChild( $args=false, $title=false, $indexOffset=0)
Create a new child frame $args is optionally a multi-root PPNode or array containing the template arg...
Definition: Preprocessor_DOM.php:1028
PPFrame_DOM\$title
Title $title
Definition: Preprocessor_DOM.php:982
PPNode_DOM\getFirstChild
getFirstChild()
Definition: Preprocessor_DOM.php:1861
captcha-old.parser
parser
Definition: captcha-old.py:187
PPCustomFrame_DOM\getArguments
getArguments()
Definition: Preprocessor_DOM.php:1807
PPFrame_DOM\__construct
__construct( $preprocessor)
Construct a new preprocessor frame.
Definition: Preprocessor_DOM.php:1009
wfMessage
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "&lt
PPTemplateFrame_DOM\setTTL
setTTL( $ttl)
Set the TTL.
Definition: Preprocessor_DOM.php:1752
PPDStack\getCurrentPart
getCurrentPart()
Definition: Preprocessor_DOM.php:791
Preprocessor\cacheGetTree
cacheGetTree( $text, $flags)
Attempt to load a precomputed document tree for some given wikitext from the cache.
Definition: Preprocessor.php:95
PPFrame_DOM\getNumberedArguments
getNumberedArguments()
Definition: Preprocessor_DOM.php:1484
PPTemplateFrame_DOM\isTemplate
isTemplate()
Return true if the frame is a template frame.
Definition: Preprocessor_DOM.php:1743
captcha-old.args
args
Definition: captcha-old.py:203
PPNode_DOM\splitExt
splitExt()
Split an "<ext>" node into an associative array containing name, attr, inner and close All values in ...
Definition: Preprocessor_DOM.php:1943
$flags
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2749
Preprocessor_DOM\preprocessToObj
preprocessToObj( $text, $flags=0)
Preprocess some wikitext and return the document tree.
Definition: Preprocessor_DOM.php:150
PPTemplateFrame_DOM\getArgument
getArgument( $name)
Definition: Preprocessor_DOM.php:1730
PPFrame_DOM\$childExpansionCache
array $childExpansionCache
Definition: Preprocessor_DOM.php:1003
PPFrame_DOM\virtualImplode
virtualImplode( $sep)
Makes an object that, when expand()ed, will be the same as one obtained with implode()
Definition: Preprocessor_DOM.php:1405
array
the array() calling protocol came about after MediaWiki 1.4rc1.
PPDStack\getFlags
getFlags()
Definition: Preprocessor_DOM.php:834
PPTemplateFrame_DOM\setVolatile
setVolatile( $flag=true)
Set the volatile flag.
Definition: Preprocessor_DOM.php:1747
PPNode_DOM\getNextSibling
getNextSibling()
Definition: Preprocessor_DOM.php:1868
PPTemplateFrame_DOM\getNamedArgument
getNamedArgument( $name)
Definition: Preprocessor_DOM.php:1714
$out
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output $out
Definition: hooks.txt:783
PPTemplateFrame_DOM
Expansion frame with template arguments.
Definition: Preprocessor_DOM.php:1584