Exemple #1
0
 /**
  * @throws MWException
  * @param string|PPNode_DOM|DOMDocument $root
  * @param int $flags
  * @return string
  */
 public function expand($root, $flags = 0)
 {
     static $expansionDepth = 0;
     if (is_string($root)) {
         return $root;
     }
     if (++$this->parser->mPPNodeCount > $this->parser->mOptions->getMaxPPNodeCount()) {
         $this->parser->limitationWarn('node-count-exceeded', $this->parser->mPPNodeCount, $this->parser->mOptions->getMaxPPNodeCount());
         return '<span class="error">Node-count limit exceeded</span>';
     }
     if ($expansionDepth > $this->parser->mOptions->getMaxPPExpandDepth()) {
         $this->parser->limitationWarn('expansion-depth-exceeded', $expansionDepth, $this->parser->mOptions->getMaxPPExpandDepth());
         return '<span class="error">Expansion depth limit exceeded</span>';
     }
     ++$expansionDepth;
     if ($expansionDepth > $this->parser->mHighestExpansionDepth) {
         $this->parser->mHighestExpansionDepth = $expansionDepth;
     }
     if ($root instanceof PPNode_DOM) {
         $root = $root->node;
     }
     if ($root instanceof DOMDocument) {
         $root = $root->documentElement;
     }
     $outStack = array('', '');
     $iteratorStack = array(false, $root);
     $indexStack = array(0, 0);
     while (count($iteratorStack) > 1) {
         $level = count($outStack) - 1;
         $iteratorNode =& $iteratorStack[$level];
         $out =& $outStack[$level];
         $index =& $indexStack[$level];
         if ($iteratorNode instanceof PPNode_DOM) {
             $iteratorNode = $iteratorNode->node;
         }
         if (is_array($iteratorNode)) {
             if ($index >= count($iteratorNode)) {
                 // All done with this iterator
                 $iteratorStack[$level] = false;
                 $contextNode = false;
             } else {
                 $contextNode = $iteratorNode[$index];
                 $index++;
             }
         } elseif ($iteratorNode instanceof DOMNodeList) {
             if ($index >= $iteratorNode->length) {
                 // All done with this iterator
                 $iteratorStack[$level] = false;
                 $contextNode = false;
             } else {
                 $contextNode = $iteratorNode->item($index);
                 $index++;
             }
         } else {
             // Copy to $contextNode and then delete from iterator stack,
             // because this is not an iterator but we do have to execute it once
             $contextNode = $iteratorStack[$level];
             $iteratorStack[$level] = false;
         }
         if ($contextNode instanceof PPNode_DOM) {
             $contextNode = $contextNode->node;
         }
         $newIterator = false;
         if ($contextNode === false) {
             // nothing to do
         } elseif (is_string($contextNode)) {
             $out .= $contextNode;
         } elseif (is_array($contextNode) || $contextNode instanceof DOMNodeList) {
             $newIterator = $contextNode;
         } elseif ($contextNode instanceof DOMNode) {
             if ($contextNode->nodeType == XML_TEXT_NODE) {
                 $out .= $contextNode->nodeValue;
             } elseif ($contextNode->nodeName == 'template') {
                 # Double-brace expansion
                 $xpath = new DOMXPath($contextNode->ownerDocument);
                 $titles = $xpath->query('title', $contextNode);
                 $title = $titles->item(0);
                 $parts = $xpath->query('part', $contextNode);
                 if ($flags & PPFrame::NO_TEMPLATES) {
                     $newIterator = $this->virtualBracketedImplode('{{', '|', '}}', $title, $parts);
                 } else {
                     $lineStart = $contextNode->getAttribute('lineStart');
                     $params = array('title' => new PPNode_DOM($title), 'parts' => new PPNode_DOM($parts), 'lineStart' => $lineStart);
                     $ret = $this->parser->braceSubstitution($params, $this);
                     if (isset($ret['object'])) {
                         $newIterator = $ret['object'];
                     } else {
                         $out .= $ret['text'];
                     }
                 }
             } elseif ($contextNode->nodeName == 'tplarg') {
                 # Triple-brace expansion
                 $xpath = new DOMXPath($contextNode->ownerDocument);
                 $titles = $xpath->query('title', $contextNode);
                 $title = $titles->item(0);
                 $parts = $xpath->query('part', $contextNode);
                 if ($flags & PPFrame::NO_ARGS) {
                     $newIterator = $this->virtualBracketedImplode('{{{', '|', '}}}', $title, $parts);
                 } else {
                     $params = array('title' => new PPNode_DOM($title), 'parts' => new PPNode_DOM($parts));
                     $ret = $this->parser->argSubstitution($params, $this);
                     if (isset($ret['object'])) {
                         $newIterator = $ret['object'];
                     } else {
                         $out .= $ret['text'];
                     }
                 }
             } elseif ($contextNode->nodeName == 'comment') {
                 # HTML-style comment
                 # Remove it in HTML, pre+remove and STRIP_COMMENTS modes
                 # Not in RECOVER_COMMENTS mode (msgnw) though.
                 if (($this->parser->ot['html'] || $this->parser->ot['pre'] && $this->parser->mOptions->getRemoveComments() || $flags & PPFrame::STRIP_COMMENTS) && !($flags & PPFrame::RECOVER_COMMENTS)) {
                     $out .= '';
                 } elseif ($this->parser->ot['wiki'] && !($flags & PPFrame::RECOVER_COMMENTS)) {
                     # Add a strip marker in PST mode so that pstPass2() can
                     # run some old-fashioned regexes on the result.
                     # Not in RECOVER_COMMENTS mode (extractSections) though.
                     $out .= $this->parser->insertStripItem($contextNode->textContent);
                 } else {
                     # Recover the literal comment in RECOVER_COMMENTS and pre+no-remove
                     $out .= $contextNode->textContent;
                 }
             } elseif ($contextNode->nodeName == 'ignore') {
                 # Output suppression used by <includeonly> etc.
                 # OT_WIKI will only respect <ignore> in substed templates.
                 # The other output types respect it unless NO_IGNORE is set.
                 # extractSections() sets NO_IGNORE and so never respects it.
                 if (!isset($this->parent) && $this->parser->ot['wiki'] || $flags & PPFrame::NO_IGNORE) {
                     $out .= $contextNode->textContent;
                 } else {
                     $out .= '';
                 }
             } elseif ($contextNode->nodeName == 'ext') {
                 # Extension tag
                 $xpath = new DOMXPath($contextNode->ownerDocument);
                 $names = $xpath->query('name', $contextNode);
                 $attrs = $xpath->query('attr', $contextNode);
                 $inners = $xpath->query('inner', $contextNode);
                 $closes = $xpath->query('close', $contextNode);
                 if ($flags & PPFrame::NO_TAGS) {
                     $s = '<' . $this->expand($names->item(0), $flags);
                     if ($attrs->length > 0) {
                         $s .= $this->expand($attrs->item(0), $flags);
                     }
                     if ($inners->length > 0) {
                         $s .= '>' . $this->expand($inners->item(0), $flags);
                         if ($closes->length > 0) {
                             $s .= $this->expand($closes->item(0), $flags);
                         }
                     } else {
                         $s .= '/>';
                     }
                     $out .= $s;
                 } else {
                     $params = array('name' => new PPNode_DOM($names->item(0)), 'attr' => $attrs->length > 0 ? new PPNode_DOM($attrs->item(0)) : null, 'inner' => $inners->length > 0 ? new PPNode_DOM($inners->item(0)) : null, 'close' => $closes->length > 0 ? new PPNode_DOM($closes->item(0)) : null);
                     $out .= $this->parser->extensionSubstitution($params, $this);
                 }
             } elseif ($contextNode->nodeName == 'h') {
                 # Heading
                 $s = $this->expand($contextNode->childNodes, $flags);
                 # Insert a heading marker only for <h> children of <root>
                 # This is to stop extractSections from going over multiple tree levels
                 if ($contextNode->parentNode->nodeName == 'root' && $this->parser->ot['html']) {
                     # Insert heading index marker
                     $headingIndex = $contextNode->getAttribute('i');
                     $titleText = $this->title->getPrefixedDBkey();
                     $this->parser->mHeadings[] = array($titleText, $headingIndex);
                     $serial = count($this->parser->mHeadings) - 1;
                     $marker = Parser::MARKER_PREFIX . "-h-{$serial}-" . Parser::MARKER_SUFFIX;
                     $count = $contextNode->getAttribute('level');
                     $s = substr($s, 0, $count) . $marker . substr($s, $count);
                     $this->parser->mStripState->addGeneral($marker, '');
                 }
                 $out .= $s;
             } else {
                 # Generic recursive expansion
                 $newIterator = $contextNode->childNodes;
             }
         } else {
             throw new MWException(__METHOD__ . ': Invalid parameter type');
         }
         if ($newIterator !== false) {
             if ($newIterator instanceof PPNode_DOM) {
                 $newIterator = $newIterator->node;
             }
             $outStack[] = '';
             $iteratorStack[] = $newIterator;
             $indexStack[] = 0;
         } elseif ($iteratorStack[$level] === false) {
             // Return accumulated value to parent
             // With tail recursion
             while ($iteratorStack[$level] === false && $level > 0) {
                 $outStack[$level - 1] .= $out;
                 array_pop($outStack);
                 array_pop($iteratorStack);
                 array_pop($indexStack);
                 $level--;
             }
         }
     }
     --$expansionDepth;
     return $outStack[0];
 }
 /**
  * Parser function to extension tag adaptor
  * @param Parser $parser
  * @param PPFrame $frame
  * @param PPNode[] $args
  * @return string
  */
 public static function tagObj($parser, $frame, $args)
 {
     if (!count($args)) {
         return '';
     }
     $tagName = strtolower(trim($frame->expand(array_shift($args))));
     if (count($args)) {
         $inner = $frame->expand(array_shift($args));
     } else {
         $inner = null;
     }
     $attributes = [];
     foreach ($args as $arg) {
         $bits = $arg->splitArg();
         if (strval($bits['index']) === '') {
             $name = trim($frame->expand($bits['name'], PPFrame::STRIP_COMMENTS));
             $value = trim($frame->expand($bits['value']));
             if (preg_match('/^(?:["\'](.+)["\']|""|\'\')$/s', $value, $m)) {
                 $value = isset($m[1]) ? $m[1] : '';
             }
             $attributes[$name] = $value;
         }
     }
     $stripList = $parser->getStripList();
     if (!in_array($tagName, $stripList)) {
         // we can't handle this tag (at least not now), so just re-emit it as an ordinary tag
         $attrText = '';
         foreach ($attributes as $name => $value) {
             $attrText .= ' ' . htmlspecialchars($name) . '="' . htmlspecialchars($value) . '"';
         }
         if ($inner === null) {
             return "<{$tagName}{$attrText}/>";
         }
         return "<{$tagName}{$attrText}>{$inner}</{$tagName}>";
     }
     $params = ['name' => $tagName, 'inner' => $inner, 'attributes' => $attributes, 'close' => "</{$tagName}>"];
     return $parser->extensionSubstitution($params, $frame);
 }
 /**
  * Parser function to extension tag adaptor
  * @param Parser $parser
  * @param PPFrame $frame
  * @param array $args
  * @return string
  */
 public static function tagObj($parser, $frame, $args)
 {
     if (!count($args)) {
         return '';
     }
     $tagName = strtolower(trim($frame->expand(array_shift($args))));
     if (count($args)) {
         $inner = $frame->expand(array_shift($args));
     } else {
         $inner = null;
     }
     $stripList = $parser->getStripList();
     if (!in_array($tagName, $stripList)) {
         return '<span class="error">' . wfMessage('unknown_extension_tag', $tagName)->inContentLanguage()->text() . '</span>';
     }
     $attributes = array();
     foreach ($args as $arg) {
         $bits = $arg->splitArg();
         if (strval($bits['index']) === '') {
             $name = trim($frame->expand($bits['name'], PPFrame::STRIP_COMMENTS));
             $value = trim($frame->expand($bits['value']));
             if (preg_match('/^(?:["\'](.+)["\']|""|\'\')$/s', $value, $m)) {
                 $value = isset($m[1]) ? $m[1] : '';
             }
             $attributes[$name] = $value;
         }
     }
     $params = array('name' => $tagName, 'inner' => $inner, 'attributes' => $attributes, 'close' => "</{$tagName}>");
     return $parser->extensionSubstitution($params, $frame);
 }
 /**
  * @throws MWException
  * @param string|PPNode $root
  * @param int $flags
  * @return string
  */
 public function expand($root, $flags = 0)
 {
     static $expansionDepth = 0;
     if (is_string($root)) {
         return $root;
     }
     if (++$this->parser->mPPNodeCount > $this->parser->mOptions->getMaxPPNodeCount()) {
         $this->parser->limitationWarn('node-count-exceeded', $this->parser->mPPNodeCount, $this->parser->mOptions->getMaxPPNodeCount());
         return '<span class="error">Node-count limit exceeded</span>';
     }
     if ($expansionDepth > $this->parser->mOptions->getMaxPPExpandDepth()) {
         $this->parser->limitationWarn('expansion-depth-exceeded', $expansionDepth, $this->parser->mOptions->getMaxPPExpandDepth());
         return '<span class="error">Expansion depth limit exceeded</span>';
     }
     ++$expansionDepth;
     if ($expansionDepth > $this->parser->mHighestExpansionDepth) {
         $this->parser->mHighestExpansionDepth = $expansionDepth;
     }
     $outStack = array('', '');
     $iteratorStack = array(false, $root);
     $indexStack = array(0, 0);
     while (count($iteratorStack) > 1) {
         $level = count($outStack) - 1;
         $iteratorNode =& $iteratorStack[$level];
         $out =& $outStack[$level];
         $index =& $indexStack[$level];
         if (is_array($iteratorNode)) {
             if ($index >= count($iteratorNode)) {
                 // All done with this iterator
                 $iteratorStack[$level] = false;
                 $contextNode = false;
             } else {
                 $contextNode = $iteratorNode[$index];
                 $index++;
             }
         } elseif ($iteratorNode instanceof PPNode_Hash_Array) {
             if ($index >= $iteratorNode->getLength()) {
                 // All done with this iterator
                 $iteratorStack[$level] = false;
                 $contextNode = false;
             } else {
                 $contextNode = $iteratorNode->item($index);
                 $index++;
             }
         } else {
             // Copy to $contextNode and then delete from iterator stack,
             // because this is not an iterator but we do have to execute it once
             $contextNode = $iteratorStack[$level];
             $iteratorStack[$level] = false;
         }
         $newIterator = false;
         if ($contextNode === false) {
             // nothing to do
         } elseif (is_string($contextNode)) {
             $out .= $contextNode;
         } elseif (is_array($contextNode) || $contextNode instanceof PPNode_Hash_Array) {
             $newIterator = $contextNode;
         } elseif ($contextNode instanceof PPNode_Hash_Attr) {
             // No output
         } elseif ($contextNode instanceof PPNode_Hash_Text) {
             $out .= $contextNode->value;
         } elseif ($contextNode instanceof PPNode_Hash_Tree) {
             if ($contextNode->name == 'template') {
                 # Double-brace expansion
                 $bits = $contextNode->splitTemplate();
                 if ($flags & PPFrame::NO_TEMPLATES) {
                     $newIterator = $this->virtualBracketedImplode('{{', '|', '}}', $bits['title'], $bits['parts']);
                 } else {
                     $ret = $this->parser->braceSubstitution($bits, $this);
                     if (isset($ret['object'])) {
                         $newIterator = $ret['object'];
                     } else {
                         $out .= $ret['text'];
                     }
                 }
             } elseif ($contextNode->name == 'tplarg') {
                 # Triple-brace expansion
                 $bits = $contextNode->splitTemplate();
                 if ($flags & PPFrame::NO_ARGS) {
                     $newIterator = $this->virtualBracketedImplode('{{{', '|', '}}}', $bits['title'], $bits['parts']);
                 } else {
                     $ret = $this->parser->argSubstitution($bits, $this);
                     if (isset($ret['object'])) {
                         $newIterator = $ret['object'];
                     } else {
                         $out .= $ret['text'];
                     }
                 }
             } elseif ($contextNode->name == 'comment') {
                 # HTML-style comment
                 # Remove it in HTML, pre+remove and STRIP_COMMENTS modes
                 # Not in RECOVER_COMMENTS mode (msgnw) though.
                 if (($this->parser->ot['html'] || $this->parser->ot['pre'] && $this->parser->mOptions->getRemoveComments() || $flags & PPFrame::STRIP_COMMENTS) && !($flags & PPFrame::RECOVER_COMMENTS)) {
                     $out .= '';
                 } elseif ($this->parser->ot['wiki'] && !($flags & PPFrame::RECOVER_COMMENTS)) {
                     # Add a strip marker in PST mode so that pstPass2() can
                     # run some old-fashioned regexes on the result.
                     # Not in RECOVER_COMMENTS mode (extractSections) though.
                     $out .= $this->parser->insertStripItem($contextNode->firstChild->value);
                 } else {
                     # Recover the literal comment in RECOVER_COMMENTS and pre+no-remove
                     $out .= $contextNode->firstChild->value;
                 }
             } elseif ($contextNode->name == 'ignore') {
                 # Output suppression used by <includeonly> etc.
                 # OT_WIKI will only respect <ignore> in substed templates.
                 # The other output types respect it unless NO_IGNORE is set.
                 # extractSections() sets NO_IGNORE and so never respects it.
                 if (!isset($this->parent) && $this->parser->ot['wiki'] || $flags & PPFrame::NO_IGNORE) {
                     $out .= $contextNode->firstChild->value;
                 } else {
                     // $out .= '';
                 }
             } elseif ($contextNode->name == 'ext') {
                 # Extension tag
                 $bits = $contextNode->splitExt() + array('attr' => null, 'inner' => null, 'close' => null);
                 if ($flags & PPFrame::NO_TAGS) {
                     $s = '<' . $bits['name']->firstChild->value;
                     if ($bits['attr']) {
                         $s .= $bits['attr']->firstChild->value;
                     }
                     if ($bits['inner']) {
                         $s .= '>' . $bits['inner']->firstChild->value;
                         if ($bits['close']) {
                             $s .= $bits['close']->firstChild->value;
                         }
                     } else {
                         $s .= '/>';
                     }
                     $out .= $s;
                 } else {
                     $out .= $this->parser->extensionSubstitution($bits, $this);
                 }
             } elseif ($contextNode->name == 'h') {
                 # Heading
                 if ($this->parser->ot['html']) {
                     # Expand immediately and insert heading index marker
                     $s = '';
                     for ($node = $contextNode->firstChild; $node; $node = $node->nextSibling) {
                         $s .= $this->expand($node, $flags);
                     }
                     $bits = $contextNode->splitHeading();
                     $titleText = $this->title->getPrefixedDBkey();
                     $this->parser->mHeadings[] = array($titleText, $bits['i']);
                     $serial = count($this->parser->mHeadings) - 1;
                     $marker = Parser::MARKER_PREFIX . "-h-{$serial}-" . Parser::MARKER_SUFFIX;
                     $s = substr($s, 0, $bits['level']) . $marker . substr($s, $bits['level']);
                     $this->parser->mStripState->addGeneral($marker, '');
                     $out .= $s;
                 } else {
                     # Expand in virtual stack
                     $newIterator = $contextNode->getChildren();
                 }
             } else {
                 # Generic recursive expansion
                 $newIterator = $contextNode->getChildren();
             }
         } else {
             throw new MWException(__METHOD__ . ': Invalid parameter type');
         }
         if ($newIterator !== false) {
             $outStack[] = '';
             $iteratorStack[] = $newIterator;
             $indexStack[] = 0;
         } elseif ($iteratorStack[$level] === false) {
             // Return accumulated value to parent
             // With tail recursion
             while ($iteratorStack[$level] === false && $level > 0) {
                 $outStack[$level - 1] .= $out;
                 array_pop($outStack);
                 array_pop($iteratorStack);
                 array_pop($indexStack);
                 $level--;
             }
         }
     }
     --$expansionDepth;
     return $outStack[0];
 }
 /**
  * @throws MWException
  * @param $root
  * @param $flags int
  * @return string
  */
 function expand($root, $flags = 0)
 {
     static $expansionDepth = 0;
     if (is_string($root)) {
         return $root;
     }
     if (++$this->parser->mPPNodeCount > $this->parser->mOptions->getMaxPPNodeCount()) {
         return '<span class="error">Node-count limit exceeded</span>';
     }
     if ($expansionDepth > $this->parser->mOptions->getMaxPPExpandDepth()) {
         return '<span class="error">Expansion depth limit exceeded</span>';
     }
     wfProfileIn(__METHOD__);
     ++$expansionDepth;
     if ($root instanceof PPNode_DOM) {
         $root = $root->node;
     }
     if ($root instanceof DOMDocument) {
         $root = $root->documentElement;
     }
     $outStack = array('', '');
     $iteratorStack = array(false, $root);
     $indexStack = array(0, 0);
     $RTEext_1 = false;
     $RTEext_2 = false;
     while (count($iteratorStack) > 1) {
         if ($RTEext_1) {
             $RTEext_1 = false;
             $RTEext_2 = true;
         }
         $level = count($outStack) - 1;
         $iteratorNode =& $iteratorStack[$level];
         $out =& $outStack[$level];
         $index =& $indexStack[$level];
         if ($iteratorNode instanceof PPNode_DOM) {
             $iteratorNode = $iteratorNode->node;
         }
         if (is_array($iteratorNode)) {
             if ($index >= count($iteratorNode)) {
                 // All done with this iterator
                 $iteratorStack[$level] = false;
                 $contextNode = false;
             } else {
                 $contextNode = $iteratorNode[$index];
                 $index++;
             }
         } elseif ($iteratorNode instanceof DOMNodeList) {
             if ($index >= $iteratorNode->length) {
                 // All done with this iterator
                 $iteratorStack[$level] = false;
                 $contextNode = false;
             } else {
                 $contextNode = $iteratorNode->item($index);
                 $index++;
             }
         } else {
             // Copy to $contextNode and then delete from iterator stack,
             // because this is not an iterator but we do have to execute it once
             $contextNode = $iteratorStack[$level];
             $iteratorStack[$level] = false;
         }
         if ($contextNode instanceof PPNode_DOM) {
             $contextNode = $contextNode->node;
         }
         $newIterator = false;
         if ($contextNode === false) {
             // nothing to do
         } elseif (is_string($contextNode)) {
             $out .= $contextNode;
         } elseif (is_array($contextNode) || $contextNode instanceof DOMNodeList) {
             $newIterator = $contextNode;
         } elseif ($contextNode instanceof DOMNode) {
             if ($contextNode->nodeType == XML_TEXT_NODE) {
                 # RTE (Rich Text Editor) - begin
                 global $wgRTEParserEnabled;
                 if (!empty($wgRTEParserEnabled)) {
                     if ($RTEext_2) {
                         if (strpos($contextNode->nodeValue, 'table') !== false) {
                             RTE::$edgeCases[] = 'COMPLEX.11';
                         }
                     }
                 }
                 # RTE - end
                 $out .= $contextNode->nodeValue;
             } elseif ($contextNode->nodeName == 'template') {
                 # Double-brace expansion
                 $xpath = new DOMXPath($contextNode->ownerDocument);
                 $titles = $xpath->query('title', $contextNode);
                 $title = $titles->item(0);
                 $parts = $xpath->query('part', $contextNode);
                 # RTE (Rich Text Editor) - begin
                 # @author: Inez Korczyński
                 global $wgRTEParserEnabled;
                 if (!empty($wgRTEParserEnabled)) {
                     $dataIdx = RTEData::put('placeholder', array('type' => 'double-brackets', 'wikitextIdx' => $contextNode->getAttribute('_rte_wikitextidx'), 'lineStart' => $contextNode->getAttribute('lineStart'), 'title' => $title->textContent));
                     $out .= RTEMarker::generate(RTEMarker::PLACEHOLDER, $dataIdx);
                 } else {
                     if ($flags & PPFrame::NO_TEMPLATES) {
                         $newIterator = $this->virtualBracketedImplode('{{', '|', '}}', $title, $parts);
                     } else {
                         $lineStart = $contextNode->getAttribute('lineStart');
                         $params = array('title' => new PPNode_DOM($title), 'parts' => new PPNode_DOM($parts), 'lineStart' => $lineStart);
                         $ret = $this->parser->braceSubstitution($params, $this);
                         if (isset($ret['object'])) {
                             $newIterator = $ret['object'];
                         } else {
                             $out .= $ret['text'];
                         }
                     }
                 }
                 # RTE - end
             } elseif ($contextNode->nodeName == 'tplarg') {
                 # Triple-brace expansion
                 $xpath = new DOMXPath($contextNode->ownerDocument);
                 $titles = $xpath->query('title', $contextNode);
                 $title = $titles->item(0);
                 $parts = $xpath->query('part', $contextNode);
                 # RTE (Rich Text Editor) - begin
                 # @author: Wladyslaw Bodzek
                 global $wgRTEParserEnabled;
                 if (!empty($wgRTEParserEnabled)) {
                     //var_dump($contextNode->getAttribute('_rte_wikitextidx'));
                     $dataIdx = RTEData::put('placeholder', array('type' => 'tplarg', 'wikitextIdx' => $contextNode->getAttribute('_rte_wikitextidx'), 'lineStart' => $contextNode->getAttribute('lineStart'), 'title' => $title->textContent));
                     $out .= RTEMarker::generate(RTEMarker::PLACEHOLDER, $dataIdx);
                 } else {
                     if ($flags & PPFrame::NO_ARGS) {
                         $newIterator = $this->virtualBracketedImplode('{{{', '|', '}}}', $title, $parts);
                     } else {
                         $params = array('title' => new PPNode_DOM($title), 'parts' => new PPNode_DOM($parts));
                         $ret = $this->parser->argSubstitution($params, $this);
                         if (isset($ret['object'])) {
                             $newIterator = $ret['object'];
                         } else {
                             $out .= $ret['text'];
                         }
                     }
                 }
                 # RTE - end
             } elseif ($contextNode->nodeName == 'comment') {
                 # HTML-style comment
                 # Remove it in HTML, pre+remove and STRIP_COMMENTS modes
                 if ($this->parser->ot['html'] || $this->parser->ot['pre'] && $this->parser->mOptions->getRemoveComments() || $flags & PPFrame::STRIP_COMMENTS) {
                     # RTE (Rich Text Editor) - begin
                     # @author: Inez Korczyński
                     global $wgRTEParserEnabled;
                     if (!empty($wgRTEParserEnabled)) {
                         if (strlen($out) === 0 || substr($out, -1) == "\n") {
                             if (substr($contextNode->textContent, -1) == "\n") {
                                 $add = "\n";
                                 $text = substr($contextNode->textContent, 0, -1);
                             } else {
                                 $add = "";
                                 $text = $contextNode->textContent;
                             }
                             $dataIdx = RTEData::put('placeholder', array('type' => 'comment', 'wikitext' => $text));
                             $out .= RTEMarker::generate(RTEMarker::PLACEHOLDER, $dataIdx) . $add;
                         } else {
                             RTE::$edgeCases[] = 'COMMENT';
                             $out .= '';
                         }
                     } else {
                         $out .= '';
                     }
                     # RTE - end
                 } elseif ($this->parser->ot['wiki'] && !($flags & PPFrame::RECOVER_COMMENTS)) {
                     $out .= $this->parser->insertStripItem($contextNode->textContent);
                 } else {
                     $out .= $contextNode->textContent;
                 }
             } elseif ($contextNode->nodeName == 'ignore') {
                 # Output suppression used by <includeonly> etc.
                 # OT_WIKI will only respect <ignore> in substed templates.
                 # The other output types respect it unless NO_IGNORE is set.
                 # extractSections() sets NO_IGNORE and so never respects it.
                 if (!isset($this->parent) && $this->parser->ot['wiki'] || $flags & PPFrame::NO_IGNORE) {
                     $out .= $contextNode->textContent;
                 } else {
                     $out .= '';
                 }
             } elseif ($contextNode->nodeName == 'ext') {
                 # Extension tag
                 $xpath = new DOMXPath($contextNode->ownerDocument);
                 $names = $xpath->query('name', $contextNode);
                 $attrs = $xpath->query('attr', $contextNode);
                 $inners = $xpath->query('inner', $contextNode);
                 $closes = $xpath->query('close', $contextNode);
                 $params = array('name' => new PPNode_DOM($names->item(0)), 'attr' => $attrs->length > 0 ? new PPNode_DOM($attrs->item(0)) : null, 'inner' => $inners->length > 0 ? new PPNode_DOM($inners->item(0)) : null, 'close' => $closes->length > 0 ? new PPNode_DOM($closes->item(0)) : null);
                 $out .= $this->parser->extensionSubstitution($params, $this);
                 $RTEext_1 = true;
             } elseif ($contextNode->nodeName == 'h') {
                 # Heading
                 $s = $this->expand($contextNode->childNodes, $flags);
                 # Insert a heading marker only for <h> children of <root>
                 # This is to stop extractSections from going over multiple tree levels
                 if ($contextNode->parentNode->nodeName == 'root' && $this->parser->ot['html']) {
                     # Insert heading index marker
                     $headingIndex = $contextNode->getAttribute('i');
                     $titleText = $this->title->getPrefixedDBkey();
                     $this->parser->mHeadings[] = array($titleText, $headingIndex);
                     $serial = count($this->parser->mHeadings) - 1;
                     $marker = "{$this->parser->mUniqPrefix}-h-{$serial}-" . Parser::MARKER_SUFFIX;
                     $count = $contextNode->getAttribute('level');
                     $s = substr($s, 0, $count) . $marker . substr($s, $count);
                     $this->parser->mStripState->addGeneral($marker, '');
                 }
                 $out .= $s;
             } else {
                 # Generic recursive expansion
                 $newIterator = $contextNode->childNodes;
             }
         } else {
             wfProfileOut(__METHOD__);
             throw new MWException(__METHOD__ . ': Invalid parameter type');
         }
         if ($newIterator !== false) {
             if ($newIterator instanceof PPNode_DOM) {
                 $newIterator = $newIterator->node;
             }
             $outStack[] = '';
             $iteratorStack[] = $newIterator;
             $indexStack[] = 0;
         } elseif ($iteratorStack[$level] === false) {
             // Return accumulated value to parent
             // With tail recursion
             while ($iteratorStack[$level] === false && $level > 0) {
                 $outStack[$level - 1] .= $out;
                 array_pop($outStack);
                 array_pop($iteratorStack);
                 array_pop($indexStack);
                 $level--;
             }
         }
         $RTEext_2 = false;
     }
     --$expansionDepth;
     wfProfileOut(__METHOD__);
     return $outStack[0];
 }