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;
77 foreach ( $values
as $k => $val ) {
79 $xml .=
"<part><name index=\"$k\"/><value>"
80 . htmlspecialchars( $val ) .
"</value></part>";
82 $xml .=
"<part><name>" . htmlspecialchars( $k )
83 .
"</name>=<value>" . htmlspecialchars( $val ) .
"</value></part>";
89 $dom =
new DOMDocument();
90 MediaWiki\suppressWarnings();
91 $result = $dom->loadXML( $xml );
92 MediaWiki\restoreWarnings();
95 $xml = UtfNormal\Validator::cleanUp( $xml );
98 $result = $dom->loadXML( $xml, 1 << 19 );
102 throw new MWException(
'Parameters passed to ' . __METHOD__ .
' result in invalid XML' );
105 $root = $dom->documentElement;
115 if ( $this->memoryLimit ===
false ) {
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)" );
123 return $usage <= $this->memoryLimit * 0.8;
153 if ( $xml ===
false ) {
160 $this->parser->mGeneratedPPNodeCount += substr_count( $xml,
'<' );
161 $max = $this->parser->mOptions->getMaxGeneratedPPNodeCount();
162 if ( $this->parser->mGeneratedPPNodeCount > $max ) {
164 throw new MWException( __METHOD__ .
': generated node count limit exceeded' );
167 $dom =
new DOMDocument;
168 MediaWiki\suppressWarnings();
169 $result = $dom->loadXML( $xml );
170 MediaWiki\restoreWarnings();
173 $xml = UtfNormal\Validator::cleanUp( $xml );
176 $result = $dom->loadXML( $xml, 1 << 19 );
179 $obj =
new PPNode_DOM( $dom->documentElement );
185 throw new MWException( __METHOD__ .
' generated invalid XML' );
198 $xmlishElements = $this->parser->getStripList();
199 $xmlishAllowMissingEndTag = [
'includeonly',
'noinclude',
'onlyinclude' ];
200 $enableOnlyinclude =
false;
201 if ( $forInclusion ) {
202 $ignoredTags = [
'includeonly',
'/includeonly' ];
203 $ignoredElements = [
'noinclude' ];
204 $xmlishElements[] =
'noinclude';
205 if ( strpos( $text,
'<onlyinclude>' ) !==
false
206 && strpos( $text,
'</onlyinclude>' ) !==
false
208 $enableOnlyinclude =
true;
211 $ignoredTags = [
'noinclude',
'/noinclude',
'onlyinclude',
'/onlyinclude' ];
212 $ignoredElements = [
'includeonly' ];
213 $xmlishElements[] =
'includeonly';
215 $xmlishRegex = implode(
'|', array_merge( $xmlishElements, $ignoredTags ) );
218 $elementsRegex =
"~($xmlishRegex)(?:\s|\/>|>)|(!--)~iA";
222 $searchBase =
"[{<\n"; # }
224 $revText = strrev( $text );
225 $lengthText = strlen( $text );
230 $accum =& $stack->getAccum();
242 $noMoreClosingTag = [];
244 $findOnlyinclude = $enableOnlyinclude;
246 $fakeLineStart =
true;
251 if ( $findOnlyinclude ) {
253 $startPos = strpos( $text,
'<onlyinclude>', $i );
254 if ( $startPos ===
false ) {
256 $accum .=
'<ignore>' . htmlspecialchars( substr( $text, $i ) ) .
'</ignore>';
259 $tagEndPos = $startPos + strlen(
'<onlyinclude>' );
260 $accum .=
'<ignore>' . htmlspecialchars( substr( $text, $i, $tagEndPos - $i ) ) .
'</ignore>';
262 $findOnlyinclude =
false;
265 if ( $fakeLineStart ) {
266 $found =
'line-start';
269 # Find next opening brace, closing brace or pipe
270 $search = $searchBase;
271 if ( $stack->top ===
false ) {
272 $currentClosing =
'';
274 $currentClosing = $stack->top->close;
275 $search .= $currentClosing;
285 # Output literal section, advance input counter
286 $literalLength = strcspn( $text, $search, $i );
287 if ( $literalLength > 0 ) {
288 $accum .= htmlspecialchars( substr( $text, $i, $literalLength ) );
289 $i += $literalLength;
291 if ( $i >= $lengthText ) {
292 if ( $currentClosing ==
"\n" ) {
301 $curChar = $text[$i];
302 if ( $curChar ==
'|' ) {
304 } elseif ( $curChar ==
'=' ) {
306 } elseif ( $curChar ==
'<' ) {
308 } elseif ( $curChar ==
"\n" ) {
312 $found =
'line-start';
314 } elseif ( $curChar == $currentClosing ) {
316 } elseif ( isset( $this->rules[$curChar] ) ) {
318 $rule = $this->rules[$curChar];
320 # Some versions of PHP have a strcspn which stops on null characters
321 # Ignore and continue
328 if ( $found ==
'angle' ) {
331 if ( $enableOnlyinclude
332 && substr( $text, $i, strlen(
'</onlyinclude>' ) ) ==
'</onlyinclude>'
334 $findOnlyinclude =
true;
339 if ( !preg_match( $elementsRegex, $text,
$matches, 0, $i + 1 ) ) {
354 $endPos = strpos( $text,
'-->', $i + 4 );
355 if ( $endPos ===
false ) {
357 $inner = substr( $text, $i );
358 $accum .=
'<comment>' . htmlspecialchars( $inner ) .
'</comment>';
362 $wsStart = $i ? ( $i - strspn( $revText,
" \t", $lengthText - $i ) ) : 0;
366 $wsEnd = $endPos + 2 + strspn( $text,
" \t", $endPos + 3 );
370 $comments = [ [ $wsStart, $wsEnd ] ];
371 while ( substr( $text, $wsEnd + 1, 4 ) ==
'<!--' ) {
372 $c = strpos( $text,
'-->', $wsEnd + 4 );
373 if ( $c ===
false ) {
376 $c = $c + 2 + strspn( $text,
" \t", $c + 3 );
377 $comments[] = [ $wsEnd + 1, $c ];
385 if ( $wsStart > 0 && substr( $text, $wsStart - 1, 1 ) ==
"\n"
386 && substr( $text, $wsEnd + 1, 1 ) ==
"\n"
390 $wsLength = $i - $wsStart;
392 && strspn( $accum,
" \t", -$wsLength ) === $wsLength
394 $accum = substr( $accum, 0, -$wsLength );
398 foreach ( $comments
as $j => $com ) {
400 $endPos = $com[1] + 1;
401 if ( $j == ( count( $comments ) - 1 ) ) {
404 $inner = substr( $text, $startPos, $endPos - $startPos );
405 $accum .=
'<comment>' . htmlspecialchars( $inner ) .
'</comment>';
409 $fakeLineStart =
true;
417 $part = $stack->top->getCurrentPart();
418 if ( !( isset( $part->commentEnd ) && $part->commentEnd == $wsStart - 1 ) ) {
419 $part->visualEnd = $wsStart;
422 $part->commentEnd = $endPos;
425 $inner = substr( $text, $startPos, $endPos - $startPos + 1 );
426 $accum .=
'<comment>' . htmlspecialchars( $inner ) .
'</comment>';
431 $lowerName = strtolower( $name );
432 $attrStart = $i + strlen( $name ) + 1;
435 $tagEndPos = $noMoreGT ?
false : strpos( $text,
'>', $attrStart );
436 if ( $tagEndPos ===
false ) {
446 if ( in_array( $lowerName, $ignoredTags ) ) {
448 . htmlspecialchars( substr( $text, $i, $tagEndPos - $i + 1 ) )
455 if ( $text[$tagEndPos - 1] ==
'/' ) {
456 $attrEnd = $tagEndPos - 1;
461 $attrEnd = $tagEndPos;
464 !isset( $noMoreClosingTag[$name] ) &&
465 preg_match(
"/<\/" . preg_quote( $name,
'/' ) .
"\s*>/i",
466 $text,
$matches, PREG_OFFSET_CAPTURE, $tagEndPos + 1 )
468 $inner = substr( $text, $tagEndPos + 1,
$matches[0][1] - $tagEndPos - 1 );
470 $close =
'<close>' . htmlspecialchars(
$matches[0][0] ) .
'</close>';
473 if ( in_array( $name, $xmlishAllowMissingEndTag ) ) {
475 $inner = substr( $text, $tagEndPos + 1 );
481 $accum .= htmlspecialchars( substr( $text, $tagStartPos, $tagEndPos + 1 - $tagStartPos ) );
483 $noMoreClosingTag[
$name] =
true;
489 if ( in_array( $lowerName, $ignoredElements ) ) {
490 $accum .=
'<ignore>' . htmlspecialchars( substr( $text, $tagStartPos, $i - $tagStartPos ) )
496 if ( $attrEnd <= $attrStart ) {
499 $attr = substr( $text, $attrStart, $attrEnd - $attrStart );
501 $accum .=
'<name>' . htmlspecialchars( $name ) .
'</name>' .
504 '<attr>' . htmlspecialchars( $attr ) .
'</attr>';
505 if ( $inner !== null ) {
506 $accum .=
'<inner>' . htmlspecialchars( $inner ) .
'</inner>';
508 $accum .= $close .
'</ext>';
509 } elseif ( $found ==
'line-start' ) {
512 if ( $fakeLineStart ) {
513 $fakeLineStart =
false;
519 $count = strspn( $text,
'=', $i, 6 );
520 if (
$count == 1 && $findEquals ) {
533 $stack->push( $piece );
534 $accum =& $stack->getAccum();
535 $flags = $stack->getFlags();
539 } elseif ( $found ==
'line-end' ) {
540 $piece = $stack->top;
542 assert( $piece->open ===
"\n" );
543 $part = $piece->getCurrentPart();
547 $wsLength = strspn( $revText,
" \t", $lengthText - $i );
548 $searchStart = $i - $wsLength;
549 if ( isset( $part->commentEnd ) && $searchStart - 1 == $part->commentEnd ) {
552 $searchStart = $part->visualEnd;
553 $searchStart -= strspn( $revText,
" \t", $lengthText - $searchStart );
556 $equalsLength = strspn( $revText,
'=', $lengthText - $searchStart );
557 if ( $equalsLength > 0 ) {
558 if ( $searchStart - $equalsLength == $piece->startPos ) {
573 $element =
"<h level=\"$count\" i=\"$headingIndex\">$accum</h>";
585 $accum =& $stack->getAccum();
586 $flags = $stack->getFlags();
596 } elseif ( $found ==
'open' ) {
597 # count opening brace characters
598 $count = strspn( $text, $curChar, $i );
600 # we need to add to stack only if opening brace count is enough for one of the rules
601 if (
$count >= $rule[
'min'] ) {
602 # Add it to the stack
605 'close' => $rule[
'end'],
607 'lineStart' => ( $i > 0 && $text[$i - 1] ==
"\n" ),
610 $stack->push( $piece );
611 $accum =& $stack->getAccum();
612 $flags = $stack->getFlags();
615 # Add literal brace(s)
616 $accum .= htmlspecialchars( str_repeat( $curChar,
$count ) );
619 } elseif ( $found ==
'close' ) {
620 $piece = $stack->top;
621 # lets check if there are enough characters for closing brace
622 $maxCount = $piece->count;
623 $count = strspn( $text, $curChar, $i, $maxCount );
625 # check for maximum matching characters (if there are 5 closing
626 # characters, we will probably need only 3 - depending on the rules)
627 $rule = $this->rules[$piece->open];
628 if (
$count > $rule[
'max'] ) {
629 # The specified maximum exists in the callback array, unless the caller
631 $matchingCount = $rule[
'max'];
633 # Count is less than the maximum
634 # Skip any gaps in the callback array to find the true largest match
635 # Need to use array_key_exists not isset because the callback can be null
637 while ( $matchingCount > 0 && !array_key_exists( $matchingCount, $rule[
'names'] ) ) {
642 if ( $matchingCount <= 0 ) {
643 # No matching element found in callback array
644 # Output a literal closing brace and continue
645 $accum .= htmlspecialchars( str_repeat( $curChar,
$count ) );
649 $name = $rule[
'names'][$matchingCount];
650 if ( $name === null ) {
652 $element = $piece->breakSyntax( $matchingCount ) . str_repeat( $rule[
'end'], $matchingCount );
655 # Note: $parts is already XML, does not need to be encoded further
656 $parts = $piece->parts;
660 # The invocation is at the start of the line if lineStart is set in
661 # the stack, and all opening brackets are used up.
662 if ( $maxCount == $matchingCount && !empty( $piece->lineStart ) ) {
663 $attr =
' lineStart="1"';
668 $element =
"<$name$attr>";
669 $element .=
"<title>$title</title>";
671 foreach ( $parts
as $part ) {
672 if ( isset( $part->eqpos ) ) {
673 $argName = substr( $part->out, 0, $part->eqpos );
674 $argValue = substr( $part->out, $part->eqpos + 1 );
675 $element .=
"<part><name>$argName</name>=<value>$argValue</value></part>";
677 $element .=
"<part><name index=\"$argIndex\" /><value>{$part->out}</value></part>";
681 $element .=
"</$name>";
684 # Advance input pointer
685 $i += $matchingCount;
689 $accum =& $stack->getAccum();
691 # Re-add the old stack element if it still has unmatched opening characters remaining
692 if ( $matchingCount < $piece->count ) {
693 $piece->parts = [
new PPDPart ];
694 $piece->count -= $matchingCount;
695 # do we still qualify for any callback with remaining count?
696 $min = $this->rules[$piece->open][
'min'];
697 if ( $piece->count >= $min ) {
698 $stack->push( $piece );
699 $accum =& $stack->getAccum();
701 $accum .= str_repeat( $piece->open, $piece->count );
704 $flags = $stack->getFlags();
707 # Add XML element to the enclosing accumulator
709 } elseif ( $found ==
'pipe' ) {
712 $accum =& $stack->getAccum();
714 } elseif ( $found ==
'equals' ) {
716 $stack->getCurrentPart()->eqpos = strlen( $accum );
722 # Output any remaining unclosed brackets
723 foreach ( $stack->stack
as $piece ) {
724 $stack->rootAccum .= $piece->breakSyntax();
726 $stack->rootAccum .=
'</root>';
727 $xml = $stack->rootAccum;
752 $this->rootAccum =
'';
760 return count( $this->stack );
768 if ( $this->top ===
false ) {
771 return $this->top->getCurrentPart();
775 public function push( $data ) {
776 if ( $data instanceof $this->elementClass ) {
777 $this->stack[] = $data;
780 $this->stack[] =
new $class( $data );
782 $this->top = $this->stack[
count( $this->stack ) - 1];
783 $this->accum =& $this->top->getAccum();
787 if ( !
count( $this->stack ) ) {
788 throw new MWException( __METHOD__ .
': no elements remaining' );
790 $temp = array_pop( $this->stack );
792 if (
count( $this->stack ) ) {
793 $this->top = $this->stack[
count( $this->stack ) - 1];
794 $this->accum =& $this->top->getAccum();
796 $this->top = self::$false;
803 $this->top->addPart(
$s );
804 $this->accum =& $this->top->getAccum();
811 if ( !
count( $this->stack ) ) {
813 'findEquals' =>
false,
815 'inHeading' =>
false,
818 return $this->top->getFlags();
857 $this->parts = [
new $class ];
859 foreach ( $data
as $name =>
$value ) {
865 return $this->parts[count( $this->parts ) - 1]->out;
870 $this->parts[] =
new $class(
$s );
874 return $this->parts[count( $this->parts ) - 1];
881 $partCount = count( $this->parts );
882 $findPipe = $this->
open !=
"\n" && $this->
open !=
'[';
884 'findPipe' => $findPipe,
885 'findEquals' => $findPipe && $partCount > 1 && !isset( $this->parts[$partCount - 1]->eqpos ),
886 'inHeading' => $this->
open ==
"\n",
897 if ( $this->
open ==
"\n" ) {
898 $s = $this->parts[0]->out;
900 if ( $openingCount ===
false ) {
903 $s = str_repeat( $this->
open, $openingCount );
905 foreach ( $this->parts
as $part ) {
988 $this->
title = $this->parser->mTitle;
989 $this->titleCache = [ $this->
title ? $this->
title->getPrefixedDBkey() :
false ];
990 $this->loopCheckHash = [];
992 $this->childExpansionCache = [];
1007 if (
$title ===
false ) {
1010 if (
$args !==
false ) {
1016 if ( $arg instanceof PPNode ) {
1019 if ( !$xpath || $xpath->document !== $arg->ownerDocument ) {
1020 $xpath =
new DOMXPath( $arg->ownerDocument );
1023 $nameNodes = $xpath->query(
'name', $arg );
1024 $value = $xpath->query(
'value', $arg );
1025 if ( $nameNodes->item( 0 )->hasAttributes() ) {
1027 $index = $nameNodes->item( 0 )->attributes->getNamedItem(
'index' )->textContent;
1028 $index = $index - $indexOffset;
1029 if ( isset( $namedArgs[$index] ) || isset( $numberedArgs[$index] ) ) {
1030 $this->parser->getOutput()->addWarning(
wfMessage(
'duplicate-args-warning',
1034 $this->parser->addTrackingCategory(
'duplicate-args-category' );
1036 $numberedArgs[$index] =
$value->item( 0 );
1037 unset( $namedArgs[$index] );
1041 if ( isset( $namedArgs[$name] ) || isset( $numberedArgs[$name] ) ) {
1042 $this->parser->getOutput()->addWarning(
wfMessage(
'duplicate-args-warning',
1046 $this->parser->addTrackingCategory(
'duplicate-args-category' );
1049 unset( $numberedArgs[$name] );
1075 static $expansionDepth = 0;
1076 if ( is_string( $root ) ) {
1080 if ( ++$this->parser->mPPNodeCount > $this->parser->mOptions->getMaxPPNodeCount() ) {
1081 $this->parser->limitationWarn(
'node-count-exceeded',
1082 $this->parser->mPPNodeCount,
1083 $this->parser->mOptions->getMaxPPNodeCount()
1085 return '<span class="error">Node-count limit exceeded</span>';
1088 if ( $expansionDepth > $this->parser->mOptions->getMaxPPExpandDepth() ) {
1089 $this->parser->limitationWarn(
'expansion-depth-exceeded',
1091 $this->parser->mOptions->getMaxPPExpandDepth()
1093 return '<span class="error">Expansion depth limit exceeded</span>';
1096 if ( $expansionDepth > $this->parser->mHighestExpansionDepth ) {
1097 $this->parser->mHighestExpansionDepth = $expansionDepth;
1101 $root = $root->node;
1103 if ( $root instanceof DOMDocument ) {
1104 $root = $root->documentElement;
1107 $outStack = [
'',
'' ];
1108 $iteratorStack = [
false, $root ];
1109 $indexStack = [ 0, 0 ];
1111 while ( count( $iteratorStack ) > 1 ) {
1112 $level = count( $outStack ) - 1;
1113 $iteratorNode =& $iteratorStack[$level];
1114 $out =& $outStack[$level];
1115 $index =& $indexStack[$level];
1117 if ( $iteratorNode instanceof PPNode_DOM ) {
1118 $iteratorNode = $iteratorNode->node;
1121 if ( is_array( $iteratorNode ) ) {
1122 if ( $index >= count( $iteratorNode ) ) {
1124 $iteratorStack[$level] =
false;
1125 $contextNode =
false;
1127 $contextNode = $iteratorNode[$index];
1130 } elseif ( $iteratorNode instanceof DOMNodeList ) {
1131 if ( $index >= $iteratorNode->length ) {
1133 $iteratorStack[$level] =
false;
1134 $contextNode =
false;
1136 $contextNode = $iteratorNode->item( $index );
1142 $contextNode = $iteratorStack[$level];
1143 $iteratorStack[$level] =
false;
1146 if ( $contextNode instanceof PPNode_DOM ) {
1147 $contextNode = $contextNode->node;
1150 $newIterator =
false;
1152 if ( $contextNode ===
false ) {
1154 } elseif ( is_string( $contextNode ) ) {
1155 $out .= $contextNode;
1156 } elseif ( is_array( $contextNode ) || $contextNode instanceof DOMNodeList ) {
1157 $newIterator = $contextNode;
1158 } elseif ( $contextNode instanceof DOMNode ) {
1159 if ( $contextNode->nodeType == XML_TEXT_NODE ) {
1160 $out .= $contextNode->nodeValue;
1161 } elseif ( $contextNode->nodeName ==
'template' ) {
1162 # Double-brace expansion
1163 $xpath =
new DOMXPath( $contextNode->ownerDocument );
1164 $titles = $xpath->query(
'title', $contextNode );
1166 $parts = $xpath->query(
'part', $contextNode );
1170 $lineStart = $contextNode->getAttribute(
'lineStart' );
1172 'title' =>
new PPNode_DOM(
$title ),
1173 'parts' =>
new PPNode_DOM( $parts ),
1174 'lineStart' => $lineStart ];
1175 $ret = $this->parser->braceSubstitution(
$params, $this );
1176 if ( isset(
$ret[
'object'] ) ) {
1177 $newIterator =
$ret[
'object'];
1182 } elseif ( $contextNode->nodeName ==
'tplarg' ) {
1183 # Triple-brace expansion
1184 $xpath =
new DOMXPath( $contextNode->ownerDocument );
1185 $titles = $xpath->query(
'title', $contextNode );
1187 $parts = $xpath->query(
'part', $contextNode );
1192 'title' =>
new PPNode_DOM(
$title ),
1193 'parts' =>
new PPNode_DOM( $parts ) ];
1194 $ret = $this->parser->argSubstitution(
$params, $this );
1195 if ( isset(
$ret[
'object'] ) ) {
1196 $newIterator =
$ret[
'object'];
1201 } elseif ( $contextNode->nodeName ==
'comment' ) {
1202 # HTML-style comment
1203 # Remove it in HTML, pre+remove and STRIP_COMMENTS modes
1204 # Not in RECOVER_COMMENTS mode (msgnw) though.
1205 if ( ( $this->parser->ot[
'html']
1206 || ( $this->parser->ot[
'pre'] && $this->parser->mOptions->getRemoveComments() )
1211 } elseif ( $this->parser->ot[
'wiki'] && !(
$flags & PPFrame::RECOVER_COMMENTS ) ) {
1212 # Add a strip marker in PST mode so that pstPass2() can
1213 # run some old-fashioned regexes on the result.
1214 # Not in RECOVER_COMMENTS mode (extractSections) though.
1215 $out .= $this->parser->insertStripItem( $contextNode->textContent );
1217 # Recover the literal comment in RECOVER_COMMENTS and pre+no-remove
1218 $out .= $contextNode->textContent;
1220 } elseif ( $contextNode->nodeName ==
'ignore' ) {
1221 # Output suppression used by <includeonly> etc.
1222 # OT_WIKI will only respect <ignore> in substed templates.
1223 # The other output types respect it unless NO_IGNORE is set.
1224 # extractSections() sets NO_IGNORE and so never respects it.
1225 if ( ( !isset( $this->parent ) && $this->parser->ot[
'wiki'] )
1228 $out .= $contextNode->textContent;
1232 } elseif ( $contextNode->nodeName ==
'ext' ) {
1234 $xpath =
new DOMXPath( $contextNode->ownerDocument );
1235 $names = $xpath->query(
'name', $contextNode );
1236 $attrs = $xpath->query(
'attr', $contextNode );
1237 $inners = $xpath->query(
'inner', $contextNode );
1238 $closes = $xpath->query(
'close', $contextNode );
1241 if ( $attrs->length > 0 ) {
1244 if ( $inners->length > 0 ) {
1246 if ( $closes->length > 0 ) {
1255 'name' =>
new PPNode_DOM( $names->item( 0 ) ),
1256 'attr' => $attrs->length > 0 ?
new PPNode_DOM( $attrs->item( 0 ) ) : null,
1257 'inner' => $inners->length > 0 ?
new PPNode_DOM( $inners->item( 0 ) ) : null,
1258 'close' => $closes->length > 0 ?
new PPNode_DOM( $closes->item( 0 ) ) : null,
1260 $out .= $this->parser->extensionSubstitution(
$params, $this );
1262 } elseif ( $contextNode->nodeName ==
'h' ) {
1266 # Insert a heading marker only for <h> children of <root>
1267 # This is to stop extractSections from going over multiple tree levels
1268 if ( $contextNode->parentNode->nodeName ==
'root' && $this->parser->ot[
'html'] ) {
1269 # Insert heading index marker
1270 $headingIndex = $contextNode->getAttribute(
'i' );
1271 $titleText = $this->
title->getPrefixedDBkey();
1272 $this->parser->mHeadings[] = [ $titleText, $headingIndex ];
1273 $serial = count( $this->parser->mHeadings ) - 1;
1275 $count = $contextNode->getAttribute(
'level' );
1277 $this->parser->mStripState->addGeneral( $marker,
'' );
1281 # Generic recursive expansion
1282 $newIterator = $contextNode->childNodes;
1285 throw new MWException( __METHOD__ .
': Invalid parameter type' );
1288 if ( $newIterator !==
false ) {
1289 if ( $newIterator instanceof PPNode_DOM ) {
1290 $newIterator = $newIterator->node;
1293 $iteratorStack[] = $newIterator;
1295 } elseif ( $iteratorStack[$level] ===
false ) {
1298 while ( $iteratorStack[$level] ===
false && $level > 0 ) {
1299 $outStack[$level - 1] .=
$out;
1300 array_pop( $outStack );
1301 array_pop( $iteratorStack );
1302 array_pop( $indexStack );
1308 return $outStack[0];
1318 $args = array_slice( func_get_args(), 2 );
1324 $root = $root->node;
1326 if ( !is_array( $root ) && !( $root instanceof DOMNodeList ) ) {
1329 foreach ( $root
as $node ) {
1350 $args = array_slice( func_get_args(), 1 );
1356 $root = $root->node;
1358 if ( !is_array( $root ) && !( $root instanceof DOMNodeList ) ) {
1361 foreach ( $root
as $node ) {
1382 $args = array_slice( func_get_args(), 1 );
1388 $root = $root->node;
1390 if ( !is_array( $root ) && !( $root instanceof DOMNodeList ) ) {
1393 foreach ( $root
as $node ) {
1414 $args = array_slice( func_get_args(), 3 );
1420 $root = $root->node;
1422 if ( !is_array( $root ) && !( $root instanceof DOMNodeList ) ) {
1425 foreach ( $root
as $node ) {
1443 if ( $level ===
false ) {
1444 return $this->
title->getPrefixedDBkey();
1446 return isset( $this->titleCache[$level] ) ? $this->titleCache[$level] :
false;
1522 $this->
volatile = $flag;
1540 if (
$ttl !== null && ( $this->ttl === null || $ttl < $this->ttl ) ) {
1579 $namedArgs = [],
$title =
false
1588 $this->titleCache =
$parent->titleCache;
1589 $this->titleCache[] = $pdbk;
1590 $this->loopCheckHash =
$parent->loopCheckHash;
1591 if ( $pdbk !==
false ) {
1592 $this->loopCheckHash[$pdbk] =
true;
1594 $this->depth =
$parent->depth + 1;
1595 $this->numberedExpansionCache = $this->namedExpansionCache = [];
1608 $s .=
"\"$name\":\"" .
1609 str_replace(
'"',
'\\"',
$value->ownerDocument->saveXML(
$value ) ) .
'"';
1623 if ( isset( $this->parent->childExpansionCache[$key] ) ) {
1624 return $this->parent->childExpansionCache[$key];
1628 $this->parent->childExpansionCache[$key] =
$retval;
1639 return !count( $this->numberedArgs ) && !count( $this->namedArgs );
1644 foreach ( array_merge(
1645 array_keys( $this->numberedArgs ),
1646 array_keys( $this->namedArgs ) )
as $key ) {
1654 foreach ( array_keys( $this->numberedArgs )
as $key ) {
1662 foreach ( array_keys( $this->namedArgs )
as $key ) {
1673 if ( !isset( $this->numberedArgs[$index] ) ) {
1676 if ( !isset( $this->numberedExpansionCache[$index] ) ) {
1677 # No trimming for unnamed arguments
1678 $this->numberedExpansionCache[$index] = $this->parent->expand(
1679 $this->numberedArgs[$index],
1683 return $this->numberedExpansionCache[$index];
1691 if ( !isset( $this->namedArgs[$name] ) ) {
1694 if ( !isset( $this->namedExpansionCache[$name] ) ) {
1695 # Trim named arguments post-expand, for backwards compatibility
1696 $this->namedExpansionCache[
$name] = trim(
1699 return $this->namedExpansionCache[
$name];
1708 if ( $text ===
false ) {
1724 parent::setVolatile( $flag );
1725 $this->parent->setVolatile( $flag );
1729 parent::setTTL(
$ttl );
1730 $this->parent->setTTL(
$ttl );
1746 $this->args =
$args;
1752 foreach ( $this->args
as $name =>
$value ) {
1758 $s .=
"\"$name\":\"" .
1759 str_replace(
'"',
'\\"',
$value->__toString() ) .
'"';
1769 return !count( $this->args );
1777 if ( !isset( $this->args[$index] ) ) {
1780 return $this->args[$index];
1802 $this->node =
$node;
1809 if ( $this->xpath === null ) {
1810 $this->xpath =
new DOMXPath( $this->node->ownerDocument );
1816 if ( $this->node instanceof DOMNodeList ) {
1818 foreach ( $this->node
as $node ) {
1819 $s .= $node->ownerDocument->saveXML( $node );
1822 $s = $this->node->ownerDocument->saveXML( $this->node );
1831 return $this->node->childNodes ?
new self( $this->node->childNodes ) :
false;
1838 return $this->node->firstChild ?
new self( $this->node->firstChild ) :
false;
1845 return $this->node->nextSibling ?
new self( $this->node->nextSibling ) :
false;
1854 return new self( $this->
getXPath()->query(
$type, $this->node ) );
1861 if ( $this->node instanceof DOMNodeList ) {
1862 return $this->node->length;
1873 $item = $this->node->item( $i );
1874 return $item ?
new self( $item ) :
false;
1881 if ( $this->node instanceof DOMNodeList ) {
1884 return $this->node->nodeName;
1899 $names =
$xpath->query(
'name', $this->node );
1900 $values =
$xpath->query(
'value', $this->node );
1901 if ( !$names->length || !$values->length ) {
1902 throw new MWException(
'Invalid brace node passed to ' . __METHOD__ );
1904 $name = $names->item( 0 );
1905 $index = $name->getAttribute(
'index' );
1907 'name' =>
new self(
$name ),
1909 'value' =>
new self( $values->item( 0 ) ) ];
1921 $names =
$xpath->query(
'name', $this->node );
1922 $attrs =
$xpath->query(
'attr', $this->node );
1923 $inners =
$xpath->query(
'inner', $this->node );
1924 $closes =
$xpath->query(
'close', $this->node );
1925 if ( !$names->length || !$attrs->length ) {
1926 throw new MWException(
'Invalid ext node passed to ' . __METHOD__ );
1929 'name' =>
new self( $names->item( 0 ) ),
1930 'attr' =>
new self( $attrs->item( 0 ) ) ];
1931 if ( $inners->length ) {
1932 $parts[
'inner'] =
new self( $inners->item( 0 ) );
1934 if ( $closes->length ) {
1935 $parts[
'close'] =
new self( $closes->item( 0 ) );
1946 if ( $this->
getName() !==
'h' ) {
1947 throw new MWException(
'Invalid h node passed to ' . __METHOD__ );
1950 'i' => $this->node->getAttribute(
'i' ),
1951 'level' => $this->node->getAttribute(
'level' ),
preprocessToXml($text, $flags=0)
bool $lineStart
True if the open char appeared at the start of the input line.
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
Stack class to help Preprocessor::preprocessToObj()
processing should stop and the error should be shown to the user * false
loopCheck($title)
Returns true if the infinite loop check is OK, false if a loop is detected.
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
int $count
Number of opening characters found (number of "=" for heading)
Expansion frame with template arguments.
There are three types of nodes:
getTitle()
Get a title of frame.
getArguments()
Returns all arguments of this frame.
it s the revision text itself In either if gzip is the revision text is gzipped $flags
virtualBracketedImplode($start, $sep, $end)
Virtual implode with brackets.
cachedExpand($key, $root, $flags=0)
getArguments()
Returns all arguments of this frame.
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:Associative array mapping language codes to prefixed links of the form"language:title".&$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
isTemplate()
Return true if the frame is a template frame.
newPartNodeArray($values)
array cacheSetTree($text, $flags, $tree)
Store a document tree in the cache.
isTemplate()
Return true if the frame is a template frame.
splitHeading()
Split a "" node.
wfEscapeWikiText($text)
Escapes the given text so that it may be output using addWikiText() without any linking, formatting, etc.
string $open
Opening character (\n for heading)
__construct($preprocessor)
Construct a new preprocessor frame.
implodeWithFlags($sep, $flags)
isEmpty()
Returns true if there are no arguments in this frame.
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 unsetoffset-wrap String Wrap the message in html(usually something like"<
setTTL($ttl)
Set the TTL of the output of this frame and all of its ancestors.
Expansion frame with custom arguments.
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...
implode($sep)
Implode with no flags specified This previously called implodeWithFlags but has now been inlined to r...
namespace and then decline to actually register it file or subcat img or subcat $title
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
Preprocessor $preprocessor
string $close
Matching closing character.
An expansion frame, used as a context to expand the result of preprocessToObj()
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
$loopCheckHash
Hashtable listing templates which are disallowed for expansion in this frame, having been encountered...
preprocessToObj($text, $flags=0)
Preprocess some wikitext and return the document tree.
__construct($preprocessor, $args)
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
__construct($node, $xpath=false)
cachedExpand($key, $root, $flags=0)
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
getNumberedArgument($index)
setVolatile($flag=true)
Set the "volatile" flag.
isVolatile()
Get the volatile flag.
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist 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 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
array $childExpansionCache
setVolatile($flag=true)
Set the volatile flag.
$depth
Recursion depth of this frame, top = 0 Note that this is NOT the same as expansion depth in expand() ...
splitArg()
Split a "
" node into an associative array containing:
getNamedArguments()
Returns all named arguments of this frame.
PPDPart[] $parts
Array of PPDPart objects describing pipe-separated parts.
isEmpty()
Returns true if there are no arguments in this frame.
__construct($preprocessor, $parent=false, $numberedArgs=[], $namedArgs=[], $title=false)
virtualImplode($sep)
Makes an object that, when expand()ed, will be the same as one obtained with implode() ...
splitExt()
Split an "" node into an associative array containing name, attr, inner and close All values in ...
breakSyntax($openingCount=false)
Get the output string that would result if the close is not found.
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 local account incomplete not yet checked for validity & $retval
string $out
Output accumulator string.
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 one of or reset 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
getNumberedArguments()
Returns all numbered arguments of this frame.
cacheGetTree($text, $flags)
Attempt to load a precomputed document tree for some given wikitext from the cache.
getPrefixedDBkey()
Get the prefixed database key form.
Allows to change the fields on the form that will be generated $name