X-Git-Url: http://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Fparser%2FPreprocessor_DOM.php;h=54079acfe454afa5dc5421ab9b607ab1011f2094;hb=7488eddb04d3c9600bef088990f1686d0f5aaac1;hp=34d589676f8680366f93720f1296fe60b7e0a1ed;hpb=24d5e941a06f5b493bbba962b82036461d92dc87;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/parser/Preprocessor_DOM.php b/includes/parser/Preprocessor_DOM.php index 34d589676f..54079acfe4 100644 --- a/includes/parser/Preprocessor_DOM.php +++ b/includes/parser/Preprocessor_DOM.php @@ -1,10 +1,24 @@ parser = $parser; @@ -19,17 +33,55 @@ class Preprocessor_DOM implements Preprocessor { } } + /** + * @return PPFrame_DOM + */ function newFrame() { return new PPFrame_DOM( $this ); } + /** + * @param $args + * @return PPCustomFrame_DOM + */ function newCustomFrame( $args ) { return new PPCustomFrame_DOM( $this, $args ); } + /** + * @param $values + * @return PPNode_DOM + */ + function newPartNodeArray( $values ) { + //NOTE: DOM manipulation is slower than building & parsing XML! (or so Tim sais) + $xml = ""; + + foreach ( $values as $k => $val ) { + + if ( is_int( $k ) ) { + $xml .= "" . htmlspecialchars( $val ) .""; + } else { + $xml .= "" . htmlspecialchars( $k ) . "=" . htmlspecialchars( $val ) . ""; + } + } + + $xml .= ""; + + $dom = new DOMDocument(); + $dom->loadXML( $xml ); + $root = $dom->documentElement; + + $node = new PPNode_DOM( $root->childNodes ); + return $node; + } + + /** + * @throws MWException + * @return bool + */ function memCheck() { if ( $this->memoryLimit === false ) { - return; + return true; } $usage = memory_get_usage(); if ( $usage > $this->memoryLimit * 0.9 ) { @@ -43,8 +95,8 @@ class Preprocessor_DOM implements Preprocessor { * Preprocess some wikitext and return the document tree. * This is the ghost of Parser::replace_variables(). * - * @param string $text The text to parse - * @param integer flags Bitwise combination of: + * @param $text String: the text to parse + * @param $flags Integer: bitwise combination of: * Parser::PTD_FOR_INCLUSION Handle / as if the text is being * included. Default is to assume a direct page view. * @@ -59,12 +111,72 @@ class Preprocessor_DOM implements Preprocessor { * cache may be implemented at a later date which takes further advantage of these strict * dependency requirements. * - * @private + * @return PPNode_DOM */ function preprocessToObj( $text, $flags = 0 ) { wfProfileIn( __METHOD__ ); - wfProfileIn( __METHOD__.'-makexml' ); + global $wgMemc, $wgPreprocessorCacheThreshold; + + $xml = false; + $cacheable = ( $wgPreprocessorCacheThreshold !== false + && strlen( $text ) > $wgPreprocessorCacheThreshold ); + if ( $cacheable ) { + wfProfileIn( __METHOD__.'-cacheable' ); + + $cacheKey = wfMemcKey( 'preprocess-xml', md5($text), $flags ); + $cacheValue = $wgMemc->get( $cacheKey ); + if ( $cacheValue ) { + $version = substr( $cacheValue, 0, 8 ); + if ( intval( $version ) == self::CACHE_VERSION ) { + $xml = substr( $cacheValue, 8 ); + // From the cache + wfDebugLog( "Preprocessor", "Loaded preprocessor XML from memcached (key $cacheKey)" ); + } + } + } + if ( $xml === false ) { + if ( $cacheable ) { + wfProfileIn( __METHOD__.'-cache-miss' ); + $xml = $this->preprocessToXml( $text, $flags ); + $cacheValue = sprintf( "%08d", self::CACHE_VERSION ) . $xml; + $wgMemc->set( $cacheKey, $cacheValue, 86400 ); + wfProfileOut( __METHOD__.'-cache-miss' ); + wfDebugLog( "Preprocessor", "Saved preprocessor XML to memcached (key $cacheKey)" ); + } else { + $xml = $this->preprocessToXml( $text, $flags ); + } + + } + wfProfileIn( __METHOD__.'-loadXML' ); + $dom = new DOMDocument; + wfSuppressWarnings(); + $result = $dom->loadXML( $xml ); + wfRestoreWarnings(); + if ( !$result ) { + // Try running the XML through UtfNormal to get rid of invalid characters + $xml = UtfNormal::cleanUp( $xml ); + // 1 << 19 == XML_PARSE_HUGE, needed so newer versions of libxml2 don't barf when the XML is >256 levels deep + $result = $dom->loadXML( $xml, 1 << 19 ); + if ( !$result ) { + throw new MWException( __METHOD__.' generated invalid XML' ); + } + } + $obj = new PPNode_DOM( $dom->documentElement ); + wfProfileOut( __METHOD__.'-loadXML' ); + if ( $cacheable ) { + wfProfileOut( __METHOD__.'-cacheable' ); + } + wfProfileOut( __METHOD__ ); + return $obj; + } + /** + * @param $text string + * @param $flags int + * @return string + */ + function preprocessToXml( $text, $flags = 0 ) { + wfProfileIn( __METHOD__ ); $rules = array( '{' => array( 'end' => '}', @@ -232,7 +344,7 @@ class Preprocessor_DOM implements Preprocessor { // Search backwards for leading whitespace $wsStart = $i ? ( $i - strspn( $revText, ' ', strlen( $text ) - $i ) ) : 0; // Search forwards for trailing whitespace - // $wsEnd will be the position of the last space + // $wsEnd will be the position of the last space (or the '>' if there's none) $wsEnd = $endPos + 2 + strspn( $text, ' ', $endPos + 3 ); // Eat the line if possible // TODO: This could theoretically be done if $wsStart == 0, i.e. for comments at @@ -259,13 +371,11 @@ class Preprocessor_DOM implements Preprocessor { if ( $stack->top ) { $part = $stack->top->getCurrentPart(); - if ( isset( $part->commentEnd ) && $part->commentEnd == $wsStart - 1 ) { - // Comments abutting, no change in visual end - $part->commentEnd = $wsEnd; - } else { + if ( ! (isset( $part->commentEnd ) && $part->commentEnd == $wsStart - 1 )) { $part->visualEnd = $wsStart; - $part->commentEnd = $endPos; } + // Else comments abutting, no change in visual end + $part->commentEnd = $endPos; } $i = $endPos + 1; $inner = substr( $text, $startPos, $endPos - $startPos + 1 ); @@ -304,7 +414,9 @@ class Preprocessor_DOM implements Preprocessor { } else { $attrEnd = $tagEndPos; // Find closing tag - if ( preg_match( "/<\/$name\s*>/i", $text, $matches, PREG_OFFSET_CAPTURE, $tagEndPos + 1 ) ) { + if ( preg_match( "/<\/" . preg_quote( $name, '/' ) . "\s*>/i", + $text, $matches, PREG_OFFSET_CAPTURE, $tagEndPos + 1 ) ) + { $inner = substr( $text, $tagEndPos + 1, $matches[0][1] - $tagEndPos - 1 ); $i = $matches[0][1] + strlen( $matches[0][0] ); $close = '' . htmlspecialchars( $matches[0][0] ) . ''; @@ -336,9 +448,7 @@ class Preprocessor_DOM implements Preprocessor { $accum .= '' . htmlspecialchars( $inner ) . ''; } $accum .= $close . ''; - } - - elseif ( $found == 'line-start' ) { + } elseif ( $found == 'line-start' ) { // Is this the start of a heading? // Line break belongs before the heading element in any case if ( $fakeLineStart ) { @@ -362,12 +472,11 @@ class Preprocessor_DOM implements Preprocessor { 'count' => $count ); $stack->push( $piece ); $accum =& $stack->getAccum(); - extract( $stack->getFlags() ); + $flags = $stack->getFlags(); + extract( $flags ); $i += $count; } - } - - elseif ( $found == 'line-end' ) { + } elseif ( $found == 'line-end' ) { $piece = $stack->top; // A heading must be open, otherwise \n wouldn't have been in the search list assert( $piece->open == "\n" ); @@ -385,7 +494,7 @@ class Preprocessor_DOM implements Preprocessor { $count = $piece->count; $equalsLength = strspn( $revText, '=', strlen( $text ) - $searchStart ); if ( $equalsLength > 0 ) { - if ( $i - $equalsLength == $piece->startPos ) { + if ( $searchStart - $equalsLength == $piece->startPos ) { // This is just a single string of equals signs on its own line // Replicate the doHeadings behaviour /={count}(.+)={count}/ // First find out how many equals signs there really are (don't stop at 6) @@ -413,7 +522,8 @@ class Preprocessor_DOM implements Preprocessor { // Unwind the stack $stack->pop(); $accum =& $stack->getAccum(); - extract( $stack->getFlags() ); + $flags = $stack->getFlags(); + extract( $flags ); // Append the result to the enclosing accumulator $accum .= $element; @@ -422,9 +532,7 @@ class Preprocessor_DOM implements Preprocessor { // another heading. Infinite loops are avoided because the next iteration MUST // hit the heading open case above, which unconditionally increments the // input pointer. - } - - elseif ( $found == 'open' ) { + } elseif ( $found == 'open' ) { # count opening brace characters $count = strspn( $text, $curChar, $i ); @@ -440,15 +548,14 @@ class Preprocessor_DOM implements Preprocessor { $stack->push( $piece ); $accum =& $stack->getAccum(); - extract( $stack->getFlags() ); + $flags = $stack->getFlags(); + extract( $flags ); } else { # Add literal brace(s) $accum .= htmlspecialchars( str_repeat( $curChar, $count ) ); } $i += $count; - } - - elseif ( $found == 'close' ) { + } elseif ( $found == 'close' ) { $piece = $stack->top; # lets check if there are enough characters for closing brace $maxCount = $piece->count; @@ -456,7 +563,6 @@ class Preprocessor_DOM implements Preprocessor { # check for maximum matching characters (if there are 5 closing # characters, we will probably need only 3 - depending on the rules) - $matchingCount = 0; $rule = $rules[$piece->open]; if ( $count > $rule['max'] ) { # The specified maximum exists in the callback array, unless the caller @@ -472,7 +578,7 @@ class Preprocessor_DOM implements Preprocessor { } } - if ($matchingCount <= 0) { + if ( $matchingCount <= 0 ) { # No matching element found in callback array # Output a literal closing brace and continue $accum .= htmlspecialchars( str_repeat( $curChar, $count ) ); @@ -501,7 +607,7 @@ class Preprocessor_DOM implements Preprocessor { $element = "<$name$attr>"; $element .= "$title"; $argIndex = 1; - foreach ( $parts as $partIndex => $part ) { + foreach ( $parts as $part ) { if ( isset( $part->eqpos ) ) { $argName = substr( $part->out, 0, $part->eqpos ); $argValue = substr( $part->out, $part->eqpos + 1 ); @@ -522,7 +628,7 @@ class Preprocessor_DOM implements Preprocessor { $accum =& $stack->getAccum(); # Re-add the old stack element if it still has unmatched opening characters remaining - if ($matchingCount < $piece->count) { + if ( $matchingCount < $piece->count ) { $piece->parts = array( new PPDPart ); $piece->count -= $matchingCount; # do we still qualify for any callback with remaining count? @@ -540,21 +646,17 @@ class Preprocessor_DOM implements Preprocessor { } $enclosingAccum .= str_repeat( $piece->open, $skippedBraces ); } - - extract( $stack->getFlags() ); + $flags = $stack->getFlags(); + extract( $flags ); # Add XML element to the enclosing accumulator $accum .= $element; - } - - elseif ( $found == 'pipe' ) { + } elseif ( $found == 'pipe' ) { $findEquals = true; // shortcut for getFlags() $stack->addPart(); $accum =& $stack->getAccum(); ++$i; - } - - elseif ( $found == 'equals' ) { + } elseif ( $found == 'equals' ) { $findEquals = false; // shortcut for getFlags() $stack->getCurrentPart()->eqpos = strlen( $accum ); $accum .= '='; @@ -569,24 +671,9 @@ class Preprocessor_DOM implements Preprocessor { $stack->rootAccum .= ''; $xml = $stack->rootAccum; - wfProfileOut( __METHOD__.'-makexml' ); - wfProfileIn( __METHOD__.'-loadXML' ); - $dom = new DOMDocument; - wfSuppressWarnings(); - $result = $dom->loadXML( $xml ); - wfRestoreWarnings(); - if ( !$result ) { - // Try running the XML through UtfNormal to get rid of invalid characters - $xml = UtfNormal::cleanUp( $xml ); - $result = $dom->loadXML( $xml ); - if ( !$result ) { - throw new MWException( __METHOD__.' generated invalid XML' ); - } - } - $obj = new PPNode_DOM( $dom->documentElement ); - wfProfileOut( __METHOD__.'-loadXML' ); wfProfileOut( __METHOD__ ); - return $obj; + + return $xml; } } @@ -595,7 +682,12 @@ class Preprocessor_DOM implements Preprocessor { * @ingroup Parser */ class PPDStack { - var $stack, $rootAccum, $top; + var $stack, $rootAccum; + + /** + * @var PPDStack + */ + var $top; var $out; var $elementClass = 'PPDStackElement'; @@ -608,6 +700,9 @@ class PPDStack { $this->accum =& $this->rootAccum; } + /** + * @return int + */ function count() { return count( $this->stack ); } @@ -656,6 +751,9 @@ class PPDStack { $this->accum =& $this->top->getAccum(); } + /** + * @return array + */ function getFlags() { if ( !count( $this->stack ) ) { return array( @@ -703,6 +801,9 @@ class PPDStackElement { return $this->parts[count($this->parts) - 1]; } + /** + * @return array + */ function getFlags() { $partCount = count( $this->parts ); $findPipe = $this->open != "\n" && $this->open != '['; @@ -715,6 +816,8 @@ class PPDStackElement { /** * Get the output string that would result if the close is not found. + * + * @return string */ function breakSyntax( $openingCount = false ) { if ( $this->open == "\n" ) { @@ -759,7 +862,21 @@ class PPDPart { * @ingroup Parser */ class PPFrame_DOM implements PPFrame { - var $preprocessor, $parser, $title; + + /** + * @var Preprocessor + */ + var $preprocessor; + + /** + * @var Parser + */ + var $parser; + + /** + * @var Title + */ + var $title; var $titleCache; /** @@ -770,13 +887,14 @@ class PPFrame_DOM implements PPFrame { /** * Recursion depth of this frame, top = 0 + * Note that this is NOT the same as expansion depth in expand() */ var $depth; /** * Construct a new preprocessor frame. - * @param Preprocessor $preprocessor The parent preprocessor + * @param $preprocessor Preprocessor The parent preprocessor */ function __construct( $preprocessor ) { $this->preprocessor = $preprocessor; @@ -790,6 +908,8 @@ class PPFrame_DOM implements PPFrame { /** * Create a new child frame * $args is optionally a multi-root PPNode or array containing the template arguments + * + * @return PPTemplateFrame_DOM */ function newChild( $args = false, $title = false ) { $namedArgs = array(); @@ -825,21 +945,27 @@ class PPFrame_DOM implements PPFrame { return new PPTemplateFrame_DOM( $this->preprocessor, $this, $numberedArgs, $namedArgs, $title ); } + /** + * @throws MWException + * @param $root + * @param $flags int + * @return string + */ function expand( $root, $flags = 0 ) { - static $depth = 0; + static $expansionDepth = 0; if ( is_string( $root ) ) { return $root; } - if ( ++$this->parser->mPPNodeCount > $this->parser->mOptions->mMaxPPNodeCount ) - { + if ( ++$this->parser->mPPNodeCount > $this->parser->mOptions->getMaxPPNodeCount() ) { return 'Node-count limit exceeded'; } - if ( $depth > $this->parser->mOptions->mMaxPPExpandDepth ) { + if ( $expansionDepth > $this->parser->mOptions->getMaxPPExpandDepth() ) { return 'Expansion depth limit exceeded'; } - ++$depth; + wfProfileIn( __METHOD__ ); + ++$expansionDepth; if ( $root instanceof PPNode_DOM ) { $root = $root->node; @@ -885,7 +1011,9 @@ class PPFrame_DOM implements PPFrame { $iteratorStack[$level] = false; } - if ( $contextNode instanceof PPNode_DOM ) $contextNode = $contextNode->node; + if ( $contextNode instanceof PPNode_DOM ) { + $contextNode = $contextNode->node; + } $newIterator = false; @@ -904,7 +1032,7 @@ class PPFrame_DOM implements PPFrame { $titles = $xpath->query( 'title', $contextNode ); $title = $titles->item( 0 ); $parts = $xpath->query( 'part', $contextNode ); - if ( $flags & self::NO_TEMPLATES ) { + if ( $flags & PPFrame::NO_TEMPLATES ) { $newIterator = $this->virtualBracketedImplode( '{{', '|', '}}', $title, $parts ); } else { $lineStart = $contextNode->getAttribute( 'lineStart' ); @@ -925,7 +1053,7 @@ class PPFrame_DOM implements PPFrame { $titles = $xpath->query( 'title', $contextNode ); $title = $titles->item( 0 ); $parts = $xpath->query( 'part', $contextNode ); - if ( $flags & self::NO_ARGS ) { + if ( $flags & PPFrame::NO_ARGS ) { $newIterator = $this->virtualBracketedImplode( '{{{', '|', '}}}', $title, $parts ); } else { $params = array( @@ -943,13 +1071,13 @@ class PPFrame_DOM implements PPFrame { # Remove it in HTML, pre+remove and STRIP_COMMENTS modes if ( $this->parser->ot['html'] || ( $this->parser->ot['pre'] && $this->parser->mOptions->getRemoveComments() ) - || ( $flags & self::STRIP_COMMENTS ) ) + || ( $flags & PPFrame::STRIP_COMMENTS ) ) { $out .= ''; } # 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 - elseif ( $this->parser->ot['wiki'] && ! ( $flags & self::RECOVER_COMMENTS ) ) { + elseif ( $this->parser->ot['wiki'] && ! ( $flags & PPFrame::RECOVER_COMMENTS ) ) { $out .= $this->parser->insertStripItem( $contextNode->textContent ); } # Recover the literal comment in RECOVER_COMMENTS and pre+no-remove @@ -961,7 +1089,7 @@ class PPFrame_DOM implements PPFrame { # OT_WIKI will only respect 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 & self::NO_IGNORE ) ) { + if ( ( !isset( $this->parent ) && $this->parser->ot['wiki'] ) || ( $flags & PPFrame::NO_IGNORE ) ) { $out .= $contextNode->textContent; } else { $out .= ''; @@ -984,11 +1112,11 @@ class PPFrame_DOM implements PPFrame { # Heading $s = $this->expand( $contextNode->childNodes, $flags ); - # Insert a heading marker only for children of - # This is to stop extractSections from going over multiple tree levels - if ( $contextNode->parentNode->nodeName == 'root' - && $this->parser->ot['html'] ) - { + # Insert a heading marker only for children of + # 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(); @@ -997,7 +1125,7 @@ class PPFrame_DOM implements PPFrame { $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->general->setPair( $marker, '' ); + $this->parser->mStripState->addGeneral( $marker, '' ); } $out .= $s; } else { @@ -1005,6 +1133,7 @@ class PPFrame_DOM implements PPFrame { $newIterator = $contextNode->childNodes; } } else { + wfProfileOut( __METHOD__ ); throw new MWException( __METHOD__.': Invalid parameter type' ); } @@ -1027,10 +1156,16 @@ class PPFrame_DOM implements PPFrame { } } } - --$depth; + --$expansionDepth; + wfProfileOut( __METHOD__ ); return $outStack[0]; } + /** + * @param $sep + * @param $flags + * @return string + */ function implodeWithFlags( $sep, $flags /*, ... */ ) { $args = array_slice( func_get_args(), 2 ); @@ -1056,6 +1191,8 @@ class PPFrame_DOM implements PPFrame { /** * Implode with no flags specified * This previously called implodeWithFlags but has now been inlined to reduce stack depth + * + * @return string */ function implode( $sep /*, ... */ ) { $args = array_slice( func_get_args(), 1 ); @@ -1063,7 +1200,9 @@ class PPFrame_DOM implements PPFrame { $first = true; $s = ''; foreach ( $args as $root ) { - if ( $root instanceof PPNode_DOM ) $root = $root->node; + if ( $root instanceof PPNode_DOM ) { + $root = $root->node; + } if ( !is_array( $root ) && !( $root instanceof DOMNodeList ) ) { $root = array( $root ); } @@ -1082,14 +1221,18 @@ class PPFrame_DOM implements PPFrame { /** * Makes an object that, when expand()ed, will be the same as one obtained * with implode() + * + * @return array */ function virtualImplode( $sep /*, ... */ ) { $args = array_slice( func_get_args(), 1 ); $out = array(); $first = true; - if ( $root instanceof PPNode_DOM ) $root = $root->node; foreach ( $args as $root ) { + if ( $root instanceof PPNode_DOM ) { + $root = $root->node; + } if ( !is_array( $root ) && !( $root instanceof DOMNodeList ) ) { $root = array( $root ); } @@ -1107,6 +1250,7 @@ class PPFrame_DOM implements PPFrame { /** * Virtual implode with brackets + * @return array */ function virtualBracketedImplode( $start, $sep, $end /*, ... */ ) { $args = array_slice( func_get_args(), 3 ); @@ -1114,7 +1258,9 @@ class PPFrame_DOM implements PPFrame { $first = true; foreach ( $args as $root ) { - if ( $root instanceof PPNode_DOM ) $root = $root->node; + if ( $root instanceof PPNode_DOM ) { + $root = $root->node; + } if ( !is_array( $root ) && !( $root instanceof DOMNodeList ) ) { $root = array( $root ); } @@ -1143,8 +1289,31 @@ class PPFrame_DOM implements PPFrame { } } + /** + * @return array + */ + function getArguments() { + return array(); + } + + /** + * @return array + */ + function getNumberedArguments() { + return array(); + } + + /** + * @return array + */ + function getNamedArguments() { + return array(); + } + /** * Returns true if there are no arguments in this frame + * + * @return bool */ function isEmpty() { return true; @@ -1156,6 +1325,8 @@ class PPFrame_DOM implements PPFrame { /** * Returns true if the infinite loop check is OK, false if a loop is detected + * + * @return bool */ function loopCheck( $title ) { return !isset( $this->loopCheckHash[$title->getPrefixedDBkey()] ); @@ -1163,10 +1334,21 @@ class PPFrame_DOM implements PPFrame { /** * Return true if the frame is a template frame + * + * @return bool */ function isTemplate() { return false; } + + /** + * Get a title of frame + * + * @return Title + */ + function getTitle() { + return $this->title; + } } /** @@ -1174,12 +1356,24 @@ class PPFrame_DOM implements PPFrame { * @ingroup Parser */ class PPTemplateFrame_DOM extends PPFrame_DOM { - var $numberedArgs, $namedArgs, $parent; + var $numberedArgs, $namedArgs; + + /** + * @var PPFrame_DOM + */ + var $parent; var $numberedExpansionCache, $namedExpansionCache; + /** + * @param $preprocessor + * @param $parent PPFrame_DOM + * @param $numberedArgs array + * @param $namedArgs array + * @param $title Title + */ function __construct( $preprocessor, $parent = false, $numberedArgs = array(), $namedArgs = array(), $title = false ) { - $this->preprocessor = $preprocessor; - $this->parser = $preprocessor->parser; + parent::__construct( $preprocessor ); + $this->parent = $parent; $this->numberedArgs = $numberedArgs; $this->namedArgs = $namedArgs; @@ -1211,20 +1405,49 @@ class PPTemplateFrame_DOM extends PPFrame_DOM { $s .= '}'; return $s; } + /** * Returns true if there are no arguments in this frame + * + * @return bool */ function isEmpty() { return !count( $this->numberedArgs ) && !count( $this->namedArgs ); } + function getArguments() { + $arguments = array(); + foreach ( array_merge( + array_keys($this->numberedArgs), + array_keys($this->namedArgs)) as $key ) { + $arguments[$key] = $this->getArgument($key); + } + return $arguments; + } + + function getNumberedArguments() { + $arguments = array(); + foreach ( array_keys($this->numberedArgs) as $key ) { + $arguments[$key] = $this->getArgument($key); + } + return $arguments; + } + + function getNamedArguments() { + $arguments = array(); + foreach ( array_keys($this->namedArgs) as $key ) { + $arguments[$key] = $this->getArgument($key); + } + return $arguments; + } + function getNumberedArgument( $index ) { if ( !isset( $this->numberedArgs[$index] ) ) { return false; } if ( !isset( $this->numberedExpansionCache[$index] ) ) { # No trimming for unnamed arguments - $this->numberedExpansionCache[$index] = $this->parent->expand( $this->numberedArgs[$index], self::STRIP_COMMENTS ); + $this->numberedExpansionCache[$index] = $this->parent->expand( $this->numberedArgs[$index], PPFrame::STRIP_COMMENTS ); } return $this->numberedExpansionCache[$index]; } @@ -1236,7 +1459,7 @@ class PPTemplateFrame_DOM extends PPFrame_DOM { if ( !isset( $this->namedExpansionCache[$name] ) ) { # Trim named arguments post-expand, for backwards compatibility $this->namedExpansionCache[$name] = trim( - $this->parent->expand( $this->namedArgs[$name], self::STRIP_COMMENTS ) ); + $this->parent->expand( $this->namedArgs[$name], PPFrame::STRIP_COMMENTS ) ); } return $this->namedExpansionCache[$name]; } @@ -1251,6 +1474,8 @@ class PPTemplateFrame_DOM extends PPFrame_DOM { /** * Return true if the frame is a template frame + * + * @return bool */ function isTemplate() { return true; @@ -1265,8 +1490,7 @@ class PPCustomFrame_DOM extends PPFrame_DOM { var $args; function __construct( $preprocessor, $args ) { - $this->preprocessor = $preprocessor; - $this->parser = $preprocessor->parser; + parent::__construct( $preprocessor ); $this->args = $args; } @@ -1286,11 +1510,17 @@ class PPCustomFrame_DOM extends PPFrame_DOM { return $s; } + /** + * @return bool + */ function isEmpty() { return !count( $this->args ); } function getArgument( $index ) { + if ( !isset( $this->args[$index] ) ) { + return false; + } return $this->args[$index]; } } @@ -1299,14 +1529,22 @@ class PPCustomFrame_DOM extends PPFrame_DOM { * @ingroup Parser */ class PPNode_DOM implements PPNode { + + /** + * @var DOMElement + */ var $node; + var $xpath; function __construct( $node, $xpath = false ) { $this->node = $node; } - function __get( $name ) { - if ( $name == 'xpath' ) { + /** + * @return DOMXPath + */ + function getXPath() { + if ( $this->xpath === null ) { $this->xpath = new DOMXPath( $this->node->ownerDocument ); } return $this->xpath; @@ -1324,22 +1562,39 @@ class PPNode_DOM implements PPNode { return $s; } + /** + * @return bool|PPNode_DOM + */ function getChildren() { return $this->node->childNodes ? new self( $this->node->childNodes ) : false; } + /** + * @return bool|PPNode_DOM + */ function getFirstChild() { return $this->node->firstChild ? new self( $this->node->firstChild ) : false; } + /** + * @return bool|PPNode_DOM + */ function getNextSibling() { return $this->node->nextSibling ? new self( $this->node->nextSibling ) : false; } + /** + * @param $type + * + * @return bool|PPNode_DOM + */ function getChildrenOfType( $type ) { - return new self( $this->xpath->query( $type, $this->node ) ); + return new self( $this->getXPath()->query( $type, $this->node ) ); } + /** + * @return int + */ function getLength() { if ( $this->node instanceof DOMNodeList ) { return $this->node->length; @@ -1348,11 +1603,18 @@ class PPNode_DOM implements PPNode { } } + /** + * @param $i + * @return bool|PPNode_DOM + */ function item( $i ) { $item = $this->node->item( $i ); return $item ? new self( $item ) : false; } + /** + * @return string + */ function getName() { if ( $this->node instanceof DOMNodeList ) { return '#nodelist'; @@ -1366,10 +1628,13 @@ class PPNode_DOM implements PPNode { * name PPNode name * index String index * value PPNode value + * + * @return array */ function splitArg() { - $names = $this->xpath->query( 'name', $this->node ); - $values = $this->xpath->query( 'value', $this->node ); + $xpath = $this->getXPath(); + $names = $xpath->query( 'name', $this->node ); + $values = $xpath->query( 'value', $this->node ); if ( !$names->length || !$values->length ) { throw new MWException( 'Invalid brace node passed to ' . __METHOD__ ); } @@ -1384,12 +1649,15 @@ class PPNode_DOM implements PPNode { /** * Split an node into an associative array containing name, attr, inner and close * All values in the resulting array are PPNodes. Inner and close are optional. + * + * @return array */ function splitExt() { - $names = $this->xpath->query( 'name', $this->node ); - $attrs = $this->xpath->query( 'attr', $this->node ); - $inners = $this->xpath->query( 'inner', $this->node ); - $closes = $this->xpath->query( 'close', $this->node ); + $xpath = $this->getXPath(); + $names = $xpath->query( 'name', $this->node ); + $attrs = $xpath->query( 'attr', $this->node ); + $inners = $xpath->query( 'inner', $this->node ); + $closes = $xpath->query( 'close', $this->node ); if ( !$names->length || !$attrs->length ) { throw new MWException( 'Invalid ext node passed to ' . __METHOD__ ); } @@ -1407,9 +1675,10 @@ class PPNode_DOM implements PPNode { /** * Split a node + * @return array */ function splitHeading() { - if ( !$this->nodeName == 'h' ) { + if ( $this->getName() !== 'h' ) { throw new MWException( 'Invalid h node passed to ' . __METHOD__ ); } return array(