}
}
}
-
-/**
- * Stack class to help Preprocessor::preprocessToObj()
- * @ingroup Parser
- */
-// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
-class PPDStack_Hash extends PPDStack {
-
- public function __construct() {
- $this->elementClass = PPDStackElement_Hash::class;
- parent::__construct();
- $this->rootAccum = [];
- }
-}
-
-/**
- * @ingroup Parser
- */
-// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
-class PPDStackElement_Hash extends PPDStackElement {
-
- public function __construct( $data = [] ) {
- $this->partClass = PPDPart_Hash::class;
- parent::__construct( $data );
- }
-
- /**
- * Get the accumulator that would result if the close is not found.
- *
- * @param int|bool $openingCount
- * @return array
- */
- public function breakSyntax( $openingCount = false ) {
- if ( $this->open == "\n" ) {
- $accum = array_merge( [ $this->savedPrefix ], $this->parts[0]->out );
- } else {
- if ( $openingCount === false ) {
- $openingCount = $this->count;
- }
- $s = substr( $this->open, 0, -1 );
- $s .= str_repeat(
- substr( $this->open, -1 ),
- $openingCount - strlen( $s )
- );
- $accum = [ $this->savedPrefix . $s ];
- $lastIndex = 0;
- $first = true;
- foreach ( $this->parts as $part ) {
- if ( $first ) {
- $first = false;
- } elseif ( is_string( $accum[$lastIndex] ) ) {
- $accum[$lastIndex] .= '|';
- } else {
- $accum[++$lastIndex] = '|';
- }
- foreach ( $part->out as $node ) {
- if ( is_string( $node ) && is_string( $accum[$lastIndex] ) ) {
- $accum[$lastIndex] .= $node;
- } else {
- $accum[++$lastIndex] = $node;
- }
- }
- }
- }
- return $accum;
- }
-}
-
-/**
- * @ingroup Parser
- */
-// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
-class PPDPart_Hash extends PPDPart {
-
- public function __construct( $out = '' ) {
- if ( $out !== '' ) {
- $accum = [ $out ];
- } else {
- $accum = [];
- }
- parent::__construct( $accum );
- }
-}
-
-/**
- * An expansion frame, used as a context to expand the result of preprocessToObj()
- * @ingroup Parser
- */
-// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
-class PPFrame_Hash implements PPFrame {
-
- /**
- * @var Parser
- */
- public $parser;
-
- /**
- * @var Preprocessor
- */
- public $preprocessor;
-
- /**
- * @var Title
- */
- public $title;
- public $titleCache;
-
- /**
- * Hashtable listing templates which are disallowed for expansion in this frame,
- * having been encountered previously in parent frames.
- */
- public $loopCheckHash;
-
- /**
- * Recursion depth of this frame, top = 0
- * Note that this is NOT the same as expansion depth in expand()
- */
- public $depth;
-
- private $volatile = false;
- private $ttl = null;
-
- /**
- * @var array
- */
- protected $childExpansionCache;
-
- /**
- * Construct a new preprocessor frame.
- * @param Preprocessor $preprocessor The parent preprocessor
- */
- public function __construct( $preprocessor ) {
- $this->preprocessor = $preprocessor;
- $this->parser = $preprocessor->parser;
- $this->title = $this->parser->mTitle;
- $this->titleCache = [ $this->title ? $this->title->getPrefixedDBkey() : false ];
- $this->loopCheckHash = [];
- $this->depth = 0;
- $this->childExpansionCache = [];
- }
-
- /**
- * Create a new child frame
- * $args is optionally a multi-root PPNode or array containing the template arguments
- *
- * @param array|bool|PPNode_Hash_Array $args
- * @param Title|bool $title
- * @param int $indexOffset
- * @throws MWException
- * @return PPTemplateFrame_Hash
- */
- public function newChild( $args = false, $title = false, $indexOffset = 0 ) {
- $namedArgs = [];
- $numberedArgs = [];
- if ( $title === false ) {
- $title = $this->title;
- }
- if ( $args !== false ) {
- if ( $args instanceof PPNode_Hash_Array ) {
- $args = $args->value;
- } elseif ( !is_array( $args ) ) {
- throw new MWException( __METHOD__ . ': $args must be array or PPNode_Hash_Array' );
- }
- foreach ( $args as $arg ) {
- $bits = $arg->splitArg();
- if ( $bits['index'] !== '' ) {
- // Numbered parameter
- $index = $bits['index'] - $indexOffset;
- if ( isset( $namedArgs[$index] ) || isset( $numberedArgs[$index] ) ) {
- $this->parser->getOutput()->addWarning( wfMessage( 'duplicate-args-warning',
- wfEscapeWikiText( $this->title ),
- wfEscapeWikiText( $title ),
- wfEscapeWikiText( $index ) )->text() );
- $this->parser->addTrackingCategory( 'duplicate-args-category' );
- }
- $numberedArgs[$index] = $bits['value'];
- unset( $namedArgs[$index] );
- } else {
- // Named parameter
- $name = trim( $this->expand( $bits['name'], PPFrame::STRIP_COMMENTS ) );
- if ( isset( $namedArgs[$name] ) || isset( $numberedArgs[$name] ) ) {
- $this->parser->getOutput()->addWarning( wfMessage( 'duplicate-args-warning',
- wfEscapeWikiText( $this->title ),
- wfEscapeWikiText( $title ),
- wfEscapeWikiText( $name ) )->text() );
- $this->parser->addTrackingCategory( 'duplicate-args-category' );
- }
- $namedArgs[$name] = $bits['value'];
- unset( $numberedArgs[$name] );
- }
- }
- }
- return new PPTemplateFrame_Hash( $this->preprocessor, $this, $numberedArgs, $namedArgs, $title );
- }
-
- /**
- * @throws MWException
- * @param string|int $key
- * @param string|PPNode $root
- * @param int $flags
- * @return string
- */
- public function cachedExpand( $key, $root, $flags = 0 ) {
- // we don't have a parent, so we don't have a cache
- return $this->expand( $root, $flags );
- }
-
- /**
- * @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];
- }
-
- /**
- * @param string $sep
- * @param int $flags
- * @param string|PPNode ...$args
- * @return string
- */
- public function implodeWithFlags( $sep, $flags, ...$args ) {
- $first = true;
- $s = '';
- foreach ( $args as $root ) {
- if ( $root instanceof PPNode_Hash_Array ) {
- $root = $root->value;
- }
- if ( !is_array( $root ) ) {
- $root = [ $root ];
- }
- foreach ( $root as $node ) {
- if ( $first ) {
- $first = false;
- } else {
- $s .= $sep;
- }
- $s .= $this->expand( $node, $flags );
- }
- }
- return $s;
- }
-
- /**
- * Implode with no flags specified
- * This previously called implodeWithFlags but has now been inlined to reduce stack depth
- * @param string $sep
- * @param string|PPNode ...$args
- * @return string
- */
- public function implode( $sep, ...$args ) {
- $first = true;
- $s = '';
- foreach ( $args as $root ) {
- if ( $root instanceof PPNode_Hash_Array ) {
- $root = $root->value;
- }
- if ( !is_array( $root ) ) {
- $root = [ $root ];
- }
- foreach ( $root as $node ) {
- if ( $first ) {
- $first = false;
- } else {
- $s .= $sep;
- }
- $s .= $this->expand( $node );
- }
- }
- return $s;
- }
-
- /**
- * Makes an object that, when expand()ed, will be the same as one obtained
- * with implode()
- *
- * @param string $sep
- * @param string|PPNode ...$args
- * @return PPNode_Hash_Array
- */
- public function virtualImplode( $sep, ...$args ) {
- $out = [];
- $first = true;
-
- foreach ( $args as $root ) {
- if ( $root instanceof PPNode_Hash_Array ) {
- $root = $root->value;
- }
- if ( !is_array( $root ) ) {
- $root = [ $root ];
- }
- foreach ( $root as $node ) {
- if ( $first ) {
- $first = false;
- } else {
- $out[] = $sep;
- }
- $out[] = $node;
- }
- }
- return new PPNode_Hash_Array( $out );
- }
-
- /**
- * Virtual implode with brackets
- *
- * @param string $start
- * @param string $sep
- * @param string $end
- * @param string|PPNode ...$args
- * @return PPNode_Hash_Array
- */
- public function virtualBracketedImplode( $start, $sep, $end, ...$args ) {
- $out = [ $start ];
- $first = true;
-
- foreach ( $args as $root ) {
- if ( $root instanceof PPNode_Hash_Array ) {
- $root = $root->value;
- }
- if ( !is_array( $root ) ) {
- $root = [ $root ];
- }
- foreach ( $root as $node ) {
- if ( $first ) {
- $first = false;
- } else {
- $out[] = $sep;
- }
- $out[] = $node;
- }
- }
- $out[] = $end;
- return new PPNode_Hash_Array( $out );
- }
-
- public function __toString() {
- return 'frame{}';
- }
-
- /**
- * @param bool $level
- * @return array|bool|string
- */
- public function getPDBK( $level = false ) {
- if ( $level === false ) {
- return $this->title->getPrefixedDBkey();
- } else {
- return $this->titleCache[$level] ?? false;
- }
- }
-
- /**
- * @return array
- */
- public function getArguments() {
- return [];
- }
-
- /**
- * @return array
- */
- public function getNumberedArguments() {
- return [];
- }
-
- /**
- * @return array
- */
- public function getNamedArguments() {
- return [];
- }
-
- /**
- * Returns true if there are no arguments in this frame
- *
- * @return bool
- */
- public function isEmpty() {
- return true;
- }
-
- /**
- * @param int|string $name
- * @return bool Always false in this implementation.
- */
- public function getArgument( $name ) {
- return false;
- }
-
- /**
- * Returns true if the infinite loop check is OK, false if a loop is detected
- *
- * @param Title $title
- *
- * @return bool
- */
- public function loopCheck( $title ) {
- return !isset( $this->loopCheckHash[$title->getPrefixedDBkey()] );
- }
-
- /**
- * Return true if the frame is a template frame
- *
- * @return bool
- */
- public function isTemplate() {
- return false;
- }
-
- /**
- * Get a title of frame
- *
- * @return Title
- */
- public function getTitle() {
- return $this->title;
- }
-
- /**
- * Set the volatile flag
- *
- * @param bool $flag
- */
- public function setVolatile( $flag = true ) {
- $this->volatile = $flag;
- }
-
- /**
- * Get the volatile flag
- *
- * @return bool
- */
- public function isVolatile() {
- return $this->volatile;
- }
-
- /**
- * Set the TTL
- *
- * @param int $ttl
- */
- public function setTTL( $ttl ) {
- if ( $ttl !== null && ( $this->ttl === null || $ttl < $this->ttl ) ) {
- $this->ttl = $ttl;
- }
- }
-
- /**
- * Get the TTL
- *
- * @return int|null
- */
- public function getTTL() {
- return $this->ttl;
- }
-}
-
-/**
- * Expansion frame with template arguments
- * @ingroup Parser
- */
-// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
-class PPTemplateFrame_Hash extends PPFrame_Hash {
-
- public $numberedArgs, $namedArgs, $parent;
- public $numberedExpansionCache, $namedExpansionCache;
-
- /**
- * @param Preprocessor $preprocessor
- * @param bool|PPFrame $parent
- * @param array $numberedArgs
- * @param array $namedArgs
- * @param bool|Title $title
- */
- public function __construct( $preprocessor, $parent = false, $numberedArgs = [],
- $namedArgs = [], $title = false
- ) {
- parent::__construct( $preprocessor );
-
- $this->parent = $parent;
- $this->numberedArgs = $numberedArgs;
- $this->namedArgs = $namedArgs;
- $this->title = $title;
- $pdbk = $title ? $title->getPrefixedDBkey() : false;
- $this->titleCache = $parent->titleCache;
- $this->titleCache[] = $pdbk;
- $this->loopCheckHash = /*clone*/ $parent->loopCheckHash;
- if ( $pdbk !== false ) {
- $this->loopCheckHash[$pdbk] = true;
- }
- $this->depth = $parent->depth + 1;
- $this->numberedExpansionCache = $this->namedExpansionCache = [];
- }
-
- public function __toString() {
- $s = 'tplframe{';
- $first = true;
- $args = $this->numberedArgs + $this->namedArgs;
- foreach ( $args as $name => $value ) {
- if ( $first ) {
- $first = false;
- } else {
- $s .= ', ';
- }
- $s .= "\"$name\":\"" .
- str_replace( '"', '\\"', $value->__toString() ) . '"';
- }
- $s .= '}';
- return $s;
- }
-
- /**
- * @throws MWException
- * @param string|int $key
- * @param string|PPNode $root
- * @param int $flags
- * @return string
- */
- public function cachedExpand( $key, $root, $flags = 0 ) {
- if ( isset( $this->parent->childExpansionCache[$key] ) ) {
- return $this->parent->childExpansionCache[$key];
- }
- $retval = $this->expand( $root, $flags );
- if ( !$this->isVolatile() ) {
- $this->parent->childExpansionCache[$key] = $retval;
- }
- return $retval;
- }
-
- /**
- * Returns true if there are no arguments in this frame
- *
- * @return bool
- */
- public function isEmpty() {
- return !count( $this->numberedArgs ) && !count( $this->namedArgs );
- }
-
- /**
- * @return array
- */
- public function getArguments() {
- $arguments = [];
- foreach ( array_merge(
- array_keys( $this->numberedArgs ),
- array_keys( $this->namedArgs ) ) as $key ) {
- $arguments[$key] = $this->getArgument( $key );
- }
- return $arguments;
- }
-
- /**
- * @return array
- */
- public function getNumberedArguments() {
- $arguments = [];
- foreach ( array_keys( $this->numberedArgs ) as $key ) {
- $arguments[$key] = $this->getArgument( $key );
- }
- return $arguments;
- }
-
- /**
- * @return array
- */
- public function getNamedArguments() {
- $arguments = [];
- foreach ( array_keys( $this->namedArgs ) as $key ) {
- $arguments[$key] = $this->getArgument( $key );
- }
- return $arguments;
- }
-
- /**
- * @param int $index
- * @return string|bool
- */
- public 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],
- PPFrame::STRIP_COMMENTS
- );
- }
- return $this->numberedExpansionCache[$index];
- }
-
- /**
- * @param string $name
- * @return string|bool
- */
- public function getNamedArgument( $name ) {
- if ( !isset( $this->namedArgs[$name] ) ) {
- return false;
- }
- if ( !isset( $this->namedExpansionCache[$name] ) ) {
- # Trim named arguments post-expand, for backwards compatibility
- $this->namedExpansionCache[$name] = trim(
- $this->parent->expand( $this->namedArgs[$name], PPFrame::STRIP_COMMENTS ) );
- }
- return $this->namedExpansionCache[$name];
- }
-
- /**
- * @param int|string $name
- * @return string|bool
- */
- public function getArgument( $name ) {
- $text = $this->getNumberedArgument( $name );
- if ( $text === false ) {
- $text = $this->getNamedArgument( $name );
- }
- return $text;
- }
-
- /**
- * Return true if the frame is a template frame
- *
- * @return bool
- */
- public function isTemplate() {
- return true;
- }
-
- public function setVolatile( $flag = true ) {
- parent::setVolatile( $flag );
- $this->parent->setVolatile( $flag );
- }
-
- public function setTTL( $ttl ) {
- parent::setTTL( $ttl );
- $this->parent->setTTL( $ttl );
- }
-}
-
-/**
- * Expansion frame with custom arguments
- * @ingroup Parser
- */
-// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
-class PPCustomFrame_Hash extends PPFrame_Hash {
-
- public $args;
-
- public function __construct( $preprocessor, $args ) {
- parent::__construct( $preprocessor );
- $this->args = $args;
- }
-
- public function __toString() {
- $s = 'cstmframe{';
- $first = true;
- foreach ( $this->args as $name => $value ) {
- if ( $first ) {
- $first = false;
- } else {
- $s .= ', ';
- }
- $s .= "\"$name\":\"" .
- str_replace( '"', '\\"', $value->__toString() ) . '"';
- }
- $s .= '}';
- return $s;
- }
-
- /**
- * @return bool
- */
- public function isEmpty() {
- return !count( $this->args );
- }
-
- /**
- * @param int|string $index
- * @return string|bool
- */
- public function getArgument( $index ) {
- return $this->args[$index] ?? false;
- }
-
- public function getArguments() {
- return $this->args;
- }
-}
-
-/**
- * @ingroup Parser
- */
-// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
-class PPNode_Hash_Tree implements PPNode {
-
- public $name;
-
- /**
- * The store array for children of this node. It is "raw" in the sense that
- * nodes are two-element arrays ("descriptors") rather than PPNode_Hash_*
- * objects.
- */
- private $rawChildren;
-
- /**
- * The store array for the siblings of this node, including this node itself.
- */
- private $store;
-
- /**
- * The index into $this->store which contains the descriptor of this node.
- */
- private $index;
-
- /**
- * The offset of the name within descriptors, used in some places for
- * readability.
- */
- const NAME = 0;
-
- /**
- * The offset of the child list within descriptors, used in some places for
- * readability.
- */
- const CHILDREN = 1;
-
- /**
- * Construct an object using the data from $store[$index]. The rest of the
- * store array can be accessed via getNextSibling().
- *
- * @param array $store
- * @param int $index
- */
- public function __construct( array $store, $index ) {
- $this->store = $store;
- $this->index = $index;
- list( $this->name, $this->rawChildren ) = $this->store[$index];
- }
-
- /**
- * Construct an appropriate PPNode_Hash_* object with a class that depends
- * on what is at the relevant store index.
- *
- * @param array $store
- * @param int $index
- * @return PPNode_Hash_Tree|PPNode_Hash_Attr|PPNode_Hash_Text
- */
- public static function factory( array $store, $index ) {
- if ( !isset( $store[$index] ) ) {
- return false;
- }
-
- $descriptor = $store[$index];
- if ( is_string( $descriptor ) ) {
- $class = PPNode_Hash_Text::class;
- } elseif ( is_array( $descriptor ) ) {
- if ( $descriptor[self::NAME][0] === '@' ) {
- $class = PPNode_Hash_Attr::class;
- } else {
- $class = self::class;
- }
- } else {
- throw new MWException( __METHOD__ . ': invalid node descriptor' );
- }
- return new $class( $store, $index );
- }
-
- /**
- * Convert a node to XML, for debugging
- */
- public function __toString() {
- $inner = '';
- $attribs = '';
- for ( $node = $this->getFirstChild(); $node; $node = $node->getNextSibling() ) {
- if ( $node instanceof PPNode_Hash_Attr ) {
- $attribs .= ' ' . $node->name . '="' . htmlspecialchars( $node->value ) . '"';
- } else {
- $inner .= $node->__toString();
- }
- }
- if ( $inner === '' ) {
- return "<{$this->name}$attribs/>";
- } else {
- return "<{$this->name}$attribs>$inner</{$this->name}>";
- }
- }
-
- /**
- * @return PPNode_Hash_Array
- */
- public function getChildren() {
- $children = [];
- foreach ( $this->rawChildren as $i => $child ) {
- $children[] = self::factory( $this->rawChildren, $i );
- }
- return new PPNode_Hash_Array( $children );
- }
-
- /**
- * Get the first child, or false if there is none. Note that this will
- * return a temporary proxy object: different instances will be returned
- * if this is called more than once on the same node.
- *
- * @return PPNode_Hash_Tree|PPNode_Hash_Attr|PPNode_Hash_Text|bool
- */
- public function getFirstChild() {
- if ( !isset( $this->rawChildren[0] ) ) {
- return false;
- } else {
- return self::factory( $this->rawChildren, 0 );
- }
- }
-
- /**
- * Get the next sibling, or false if there is none. Note that this will
- * return a temporary proxy object: different instances will be returned
- * if this is called more than once on the same node.
- *
- * @return PPNode_Hash_Tree|PPNode_Hash_Attr|PPNode_Hash_Text|bool
- */
- public function getNextSibling() {
- return self::factory( $this->store, $this->index + 1 );
- }
-
- /**
- * Get an array of the children with a given node name
- *
- * @param string $name
- * @return PPNode_Hash_Array
- */
- public function getChildrenOfType( $name ) {
- $children = [];
- foreach ( $this->rawChildren as $i => $child ) {
- if ( is_array( $child ) && $child[self::NAME] === $name ) {
- $children[] = self::factory( $this->rawChildren, $i );
- }
- }
- return new PPNode_Hash_Array( $children );
- }
-
- /**
- * Get the raw child array. For internal use.
- * @return array
- */
- public function getRawChildren() {
- return $this->rawChildren;
- }
-
- /**
- * @return bool
- */
- public function getLength() {
- return false;
- }
-
- /**
- * @param int $i
- * @return bool
- */
- public function item( $i ) {
- return false;
- }
-
- /**
- * @return string
- */
- public function getName() {
- return $this->name;
- }
-
- /**
- * Split a "<part>" node into an associative array containing:
- * - name PPNode name
- * - index String index
- * - value PPNode value
- *
- * @throws MWException
- * @return array
- */
- public function splitArg() {
- return self::splitRawArg( $this->rawChildren );
- }
-
- /**
- * Like splitArg() but for a raw child array. For internal use only.
- * @param array $children
- * @return array
- */
- public static function splitRawArg( array $children ) {
- $bits = [];
- foreach ( $children as $i => $child ) {
- if ( !is_array( $child ) ) {
- continue;
- }
- if ( $child[self::NAME] === 'name' ) {
- $bits['name'] = new self( $children, $i );
- if ( isset( $child[self::CHILDREN][0][self::NAME] )
- && $child[self::CHILDREN][0][self::NAME] === '@index'
- ) {
- $bits['index'] = $child[self::CHILDREN][0][self::CHILDREN][0];
- }
- } elseif ( $child[self::NAME] === 'value' ) {
- $bits['value'] = new self( $children, $i );
- }
- }
-
- if ( !isset( $bits['name'] ) ) {
- throw new MWException( 'Invalid brace node passed to ' . __METHOD__ );
- }
- if ( !isset( $bits['index'] ) ) {
- $bits['index'] = '';
- }
- return $bits;
- }
-
- /**
- * Split an "<ext>" node into an associative array containing name, attr, inner and close
- * All values in the resulting array are PPNodes. Inner and close are optional.
- *
- * @throws MWException
- * @return array
- */
- public function splitExt() {
- return self::splitRawExt( $this->rawChildren );
- }
-
- /**
- * Like splitExt() but for a raw child array. For internal use only.
- * @param array $children
- * @return array
- */
- public static function splitRawExt( array $children ) {
- $bits = [];
- foreach ( $children as $i => $child ) {
- if ( !is_array( $child ) ) {
- continue;
- }
- switch ( $child[self::NAME] ) {
- case 'name':
- $bits['name'] = new self( $children, $i );
- break;
- case 'attr':
- $bits['attr'] = new self( $children, $i );
- break;
- case 'inner':
- $bits['inner'] = new self( $children, $i );
- break;
- case 'close':
- $bits['close'] = new self( $children, $i );
- break;
- }
- }
- if ( !isset( $bits['name'] ) ) {
- throw new MWException( 'Invalid ext node passed to ' . __METHOD__ );
- }
- return $bits;
- }
-
- /**
- * Split an "<h>" node
- *
- * @throws MWException
- * @return array
- */
- public function splitHeading() {
- if ( $this->name !== 'h' ) {
- throw new MWException( 'Invalid h node passed to ' . __METHOD__ );
- }
- return self::splitRawHeading( $this->rawChildren );
- }
-
- /**
- * Like splitHeading() but for a raw child array. For internal use only.
- * @param array $children
- * @return array
- */
- public static function splitRawHeading( array $children ) {
- $bits = [];
- foreach ( $children as $i => $child ) {
- if ( !is_array( $child ) ) {
- continue;
- }
- if ( $child[self::NAME] === '@i' ) {
- $bits['i'] = $child[self::CHILDREN][0];
- } elseif ( $child[self::NAME] === '@level' ) {
- $bits['level'] = $child[self::CHILDREN][0];
- }
- }
- if ( !isset( $bits['i'] ) ) {
- throw new MWException( 'Invalid h node passed to ' . __METHOD__ );
- }
- return $bits;
- }
-
- /**
- * Split a "<template>" or "<tplarg>" node
- *
- * @throws MWException
- * @return array
- */
- public function splitTemplate() {
- return self::splitRawTemplate( $this->rawChildren );
- }
-
- /**
- * Like splitTemplate() but for a raw child array. For internal use only.
- * @param array $children
- * @return array
- */
- public static function splitRawTemplate( array $children ) {
- $parts = [];
- $bits = [ 'lineStart' => '' ];
- foreach ( $children as $i => $child ) {
- if ( !is_array( $child ) ) {
- continue;
- }
- switch ( $child[self::NAME] ) {
- case 'title':
- $bits['title'] = new self( $children, $i );
- break;
- case 'part':
- $parts[] = new self( $children, $i );
- break;
- case '@lineStart':
- $bits['lineStart'] = '1';
- break;
- }
- }
- if ( !isset( $bits['title'] ) ) {
- throw new MWException( 'Invalid node passed to ' . __METHOD__ );
- }
- $bits['parts'] = new PPNode_Hash_Array( $parts );
- return $bits;
- }
-}
-
-/**
- * @ingroup Parser
- */
-// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
-class PPNode_Hash_Text implements PPNode {
-
- public $value;
- private $store, $index;
-
- /**
- * Construct an object using the data from $store[$index]. The rest of the
- * store array can be accessed via getNextSibling().
- *
- * @param array $store
- * @param int $index
- */
- public function __construct( array $store, $index ) {
- $this->value = $store[$index];
- if ( !is_scalar( $this->value ) ) {
- throw new MWException( __CLASS__ . ' given object instead of string' );
- }
- $this->store = $store;
- $this->index = $index;
- }
-
- public function __toString() {
- return htmlspecialchars( $this->value );
- }
-
- public function getNextSibling() {
- return PPNode_Hash_Tree::factory( $this->store, $this->index + 1 );
- }
-
- public function getChildren() {
- return false;
- }
-
- public function getFirstChild() {
- return false;
- }
-
- public function getChildrenOfType( $name ) {
- return false;
- }
-
- public function getLength() {
- return false;
- }
-
- public function item( $i ) {
- return false;
- }
-
- public function getName() {
- return '#text';
- }
-
- public function splitArg() {
- throw new MWException( __METHOD__ . ': not supported' );
- }
-
- public function splitExt() {
- throw new MWException( __METHOD__ . ': not supported' );
- }
-
- public function splitHeading() {
- throw new MWException( __METHOD__ . ': not supported' );
- }
-}
-
-/**
- * @ingroup Parser
- */
-// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
-class PPNode_Hash_Array implements PPNode {
-
- public $value;
-
- public function __construct( $value ) {
- $this->value = $value;
- }
-
- public function __toString() {
- return var_export( $this, true );
- }
-
- public function getLength() {
- return count( $this->value );
- }
-
- public function item( $i ) {
- return $this->value[$i];
- }
-
- public function getName() {
- return '#nodelist';
- }
-
- public function getNextSibling() {
- return false;
- }
-
- public function getChildren() {
- return false;
- }
-
- public function getFirstChild() {
- return false;
- }
-
- public function getChildrenOfType( $name ) {
- return false;
- }
-
- public function splitArg() {
- throw new MWException( __METHOD__ . ': not supported' );
- }
-
- public function splitExt() {
- throw new MWException( __METHOD__ . ': not supported' );
- }
-
- public function splitHeading() {
- throw new MWException( __METHOD__ . ': not supported' );
- }
-}
-
-/**
- * @ingroup Parser
- */
-// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
-class PPNode_Hash_Attr implements PPNode {
-
- public $name, $value;
- private $store, $index;
-
- /**
- * Construct an object using the data from $store[$index]. The rest of the
- * store array can be accessed via getNextSibling().
- *
- * @param array $store
- * @param int $index
- */
- public function __construct( array $store, $index ) {
- $descriptor = $store[$index];
- if ( $descriptor[PPNode_Hash_Tree::NAME][0] !== '@' ) {
- throw new MWException( __METHOD__ . ': invalid name in attribute descriptor' );
- }
- $this->name = substr( $descriptor[PPNode_Hash_Tree::NAME], 1 );
- $this->value = $descriptor[PPNode_Hash_Tree::CHILDREN][0];
- $this->store = $store;
- $this->index = $index;
- }
-
- public function __toString() {
- return "<@{$this->name}>" . htmlspecialchars( $this->value ) . "</@{$this->name}>";
- }
-
- public function getName() {
- return $this->name;
- }
-
- public function getNextSibling() {
- return PPNode_Hash_Tree::factory( $this->store, $this->index + 1 );
- }
-
- public function getChildren() {
- return false;
- }
-
- public function getFirstChild() {
- return false;
- }
-
- public function getChildrenOfType( $name ) {
- return false;
- }
-
- public function getLength() {
- return false;
- }
-
- public function item( $i ) {
- return false;
- }
-
- public function splitArg() {
- throw new MWException( __METHOD__ . ': not supported' );
- }
-
- public function splitExt() {
- throw new MWException( __METHOD__ . ': not supported' );
- }
-
- public function splitHeading() {
- throw new MWException( __METHOD__ . ': not supported' );
- }
-}