X-Git-Url: http://git.cyclocoop.org/%7B%24www_url%7Dadmin/compta/categories/supprimer.php?a=blobdiff_plain;f=includes%2Fparser%2FPreprocessor_Hash.php;h=66f081fe7713f8c9babdb8d11ec05d9d07872cab;hb=4226fada45a83ad56e91ce7638d17d12fecec8ca;hp=eb869e21c3513f239cdb1c0de577e7dd97520f5b;hpb=f96aaf6833d052d10d46af47540fff995362433e;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/parser/Preprocessor_Hash.php b/includes/parser/Preprocessor_Hash.php index eb869e21c3..66f081fe77 100644 --- a/includes/parser/Preprocessor_Hash.php +++ b/includes/parser/Preprocessor_Hash.php @@ -795,1463 +795,3 @@ class Preprocessor_Hash extends Preprocessor { } } } - -/** - * 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 'Node-count limit exceeded'; - } - if ( $expansionDepth > $this->parser->mOptions->getMaxPPExpandDepth() ) { - $this->parser->limitationWarn( 'expansion-depth-exceeded', - $expansionDepth, - $this->parser->mOptions->getMaxPPExpandDepth() - ); - return 'Expansion depth limit exceeded'; - } - ++$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 etc. - # 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 & 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 = array_slice( func_get_args(), 2 ); - - $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 = array_slice( func_get_args(), 1 ); - - $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 = array_slice( func_get_args(), 1 ); - $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 = array_slice( func_get_args(), 3 ); - $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>$innername}>"; - } - } - - /** - * @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 "" 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 "" 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 "" 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 "