Exemplo n.º 1
0
 /**
  * @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 = ['', ''];
     $iteratorStack = [false, $root];
     $indexStack = [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;
         $contextName = false;
         $contextChildren = false;
         if ($contextNode === false) {
             // nothing to do
         } elseif (is_string($contextNode)) {
             $out .= $contextNode;
         } elseif ($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) {
             $contextName = $contextNode->name;
             $contextChildren = $contextNode->getRawChildren();
         } elseif (is_array($contextNode)) {
             // Node descriptor array
             if (count($contextNode) !== 2) {
                 throw new MWException(__METHOD__ . ': found an array where a node descriptor should be');
             }
             list($contextName, $contextChildren) = $contextNode;
         } else {
             throw new MWException(__METHOD__ . ': Invalid parameter type');
         }
         // Handle node descriptor array or tree object
         if ($contextName === false) {
             // Not a node, already handled above
         } elseif ($contextName[0] === '@') {
             // Attribute: no output
         } elseif ($contextName === 'template') {
             # Double-brace expansion
             $bits = PPNode_Hash_Tree::splitRawTemplate($contextChildren);
             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 ($contextName === 'tplarg') {
             # Triple-brace expansion
             $bits = PPNode_Hash_Tree::splitRawTemplate($contextChildren);
             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 ($contextName === '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($contextChildren[0]);
             } else {
                 # Recover the literal comment in RECOVER_COMMENTS and pre+no-remove
                 $out .= $contextChildren[0];
             }
         } elseif ($contextName === '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 .= $contextChildren[0];
             } else {
                 // $out .= '';
             }
         } elseif ($contextName === 'ext') {
             # Extension tag
             $bits = PPNode_Hash_Tree::splitRawExt($contextChildren) + ['attr' => null, 'inner' => null, 'close' => null];
             if ($flags & PPFrame::NO_TAGS) {
                 $s = '<' . $bits['name']->getFirstChild()->value;
                 if ($bits['attr']) {
                     $s .= $bits['attr']->getFirstChild()->value;
                 }
                 if ($bits['inner']) {
                     $s .= '>' . $bits['inner']->getFirstChild()->value;
                     if ($bits['close']) {
                         $s .= $bits['close']->getFirstChild()->value;
                     }
                 } else {
                     $s .= '/>';
                 }
                 $out .= $s;
             } else {
                 $out .= $this->parser->extensionSubstitution($bits, $this);
             }
         } elseif ($contextName === 'h') {
             # Heading
             if ($this->parser->ot['html']) {
                 # Expand immediately and insert heading index marker
                 $s = $this->expand($contextChildren, $flags);
                 $bits = PPNode_Hash_Tree::splitRawHeading($contextChildren);
                 $titleText = $this->title->getPrefixedDBkey();
                 $this->parser->mHeadings[] = [$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 = $contextChildren;
             }
         } else {
             # Generic recursive expansion
             $newIterator = $contextChildren;
         }
         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];
 }