Split parser related files to have one class in one file
Change-Id: I36b26609ccb3f135a22961b32a46cdc06603b3e4
This commit is contained in:
parent
f415b6e162
commit
4226fada45
24 changed files with 3359 additions and 2982 deletions
|
|
@ -189,9 +189,6 @@
|
|||
Whitelist existing violations, but enable the sniff to prevent
|
||||
any new occurrences.
|
||||
-->
|
||||
<exclude-pattern>*/includes/parser/Preprocessor_DOM\.php</exclude-pattern>
|
||||
<exclude-pattern>*/includes/parser/Preprocessor_Hash\.php</exclude-pattern>
|
||||
<exclude-pattern>*/includes/parser/Preprocessor\.php</exclude-pattern>
|
||||
<exclude-pattern>*/maintenance/dumpIterator\.php</exclude-pattern>
|
||||
<exclude-pattern>*/maintenance/Maintenance\.php</exclude-pattern>
|
||||
<exclude-pattern>*/maintenance/findDeprecated\.php</exclude-pattern>
|
||||
|
|
|
|||
38
autoload.php
38
autoload.php
|
|
@ -1050,28 +1050,28 @@ $wgAutoloadLocalClasses = [
|
|||
'PHPVersionCheck' => __DIR__ . '/includes/PHPVersionCheck.php',
|
||||
'PNGHandler' => __DIR__ . '/includes/media/PNGHandler.php',
|
||||
'PNGMetadataExtractor' => __DIR__ . '/includes/media/PNGMetadataExtractor.php',
|
||||
'PPCustomFrame_DOM' => __DIR__ . '/includes/parser/Preprocessor_DOM.php',
|
||||
'PPCustomFrame_Hash' => __DIR__ . '/includes/parser/Preprocessor_Hash.php',
|
||||
'PPDPart' => __DIR__ . '/includes/parser/Preprocessor_DOM.php',
|
||||
'PPDPart_Hash' => __DIR__ . '/includes/parser/Preprocessor_Hash.php',
|
||||
'PPDStack' => __DIR__ . '/includes/parser/Preprocessor_DOM.php',
|
||||
'PPDStackElement' => __DIR__ . '/includes/parser/Preprocessor_DOM.php',
|
||||
'PPDStackElement_Hash' => __DIR__ . '/includes/parser/Preprocessor_Hash.php',
|
||||
'PPDStack_Hash' => __DIR__ . '/includes/parser/Preprocessor_Hash.php',
|
||||
'PPFrame' => __DIR__ . '/includes/parser/Preprocessor.php',
|
||||
'PPFrame_DOM' => __DIR__ . '/includes/parser/Preprocessor_DOM.php',
|
||||
'PPFrame_Hash' => __DIR__ . '/includes/parser/Preprocessor_Hash.php',
|
||||
'PPCustomFrame_DOM' => __DIR__ . '/includes/parser/PPCustomFrame_DOM.php',
|
||||
'PPCustomFrame_Hash' => __DIR__ . '/includes/parser/PPCustomFrame_Hash.php',
|
||||
'PPDPart' => __DIR__ . '/includes/parser/PPDPart.php',
|
||||
'PPDPart_Hash' => __DIR__ . '/includes/parser/PPDPart_Hash.php',
|
||||
'PPDStack' => __DIR__ . '/includes/parser/PPDStack.php',
|
||||
'PPDStackElement' => __DIR__ . '/includes/parser/PPDStackElement.php',
|
||||
'PPDStackElement_Hash' => __DIR__ . '/includes/parser/PPDStackElement_Hash.php',
|
||||
'PPDStack_Hash' => __DIR__ . '/includes/parser/PPDStack_Hash.php',
|
||||
'PPFrame' => __DIR__ . '/includes/parser/PPFrame.php',
|
||||
'PPFrame_DOM' => __DIR__ . '/includes/parser/PPFrame_DOM.php',
|
||||
'PPFrame_Hash' => __DIR__ . '/includes/parser/PPFrame_Hash.php',
|
||||
'PPFuzzTest' => __DIR__ . '/maintenance/preprocessorFuzzTest.php',
|
||||
'PPFuzzTester' => __DIR__ . '/maintenance/preprocessorFuzzTest.php',
|
||||
'PPFuzzUser' => __DIR__ . '/maintenance/preprocessorFuzzTest.php',
|
||||
'PPNode' => __DIR__ . '/includes/parser/Preprocessor.php',
|
||||
'PPNode_DOM' => __DIR__ . '/includes/parser/Preprocessor_DOM.php',
|
||||
'PPNode_Hash_Array' => __DIR__ . '/includes/parser/Preprocessor_Hash.php',
|
||||
'PPNode_Hash_Attr' => __DIR__ . '/includes/parser/Preprocessor_Hash.php',
|
||||
'PPNode_Hash_Text' => __DIR__ . '/includes/parser/Preprocessor_Hash.php',
|
||||
'PPNode_Hash_Tree' => __DIR__ . '/includes/parser/Preprocessor_Hash.php',
|
||||
'PPTemplateFrame_DOM' => __DIR__ . '/includes/parser/Preprocessor_DOM.php',
|
||||
'PPTemplateFrame_Hash' => __DIR__ . '/includes/parser/Preprocessor_Hash.php',
|
||||
'PPNode' => __DIR__ . '/includes/parser/PPNode.php',
|
||||
'PPNode_DOM' => __DIR__ . '/includes/parser/PPNode_DOM.php',
|
||||
'PPNode_Hash_Array' => __DIR__ . '/includes/parser/PPNode_Hash_Array.php',
|
||||
'PPNode_Hash_Attr' => __DIR__ . '/includes/parser/PPNode_Hash_Attr.php',
|
||||
'PPNode_Hash_Text' => __DIR__ . '/includes/parser/PPNode_Hash_Text.php',
|
||||
'PPNode_Hash_Tree' => __DIR__ . '/includes/parser/PPNode_Hash_Tree.php',
|
||||
'PPTemplateFrame_DOM' => __DIR__ . '/includes/parser/PPTemplateFrame_DOM.php',
|
||||
'PPTemplateFrame_Hash' => __DIR__ . '/includes/parser/PPTemplateFrame_Hash.php',
|
||||
'PackedHoverImageGallery' => __DIR__ . '/includes/gallery/PackedHoverImageGallery.php',
|
||||
'PackedImageGallery' => __DIR__ . '/includes/gallery/PackedImageGallery.php',
|
||||
'PackedOverlayImageGallery' => __DIR__ . '/includes/gallery/PackedOverlayImageGallery.php',
|
||||
|
|
|
|||
70
includes/parser/PPCustomFrame_DOM.php
Normal file
70
includes/parser/PPCustomFrame_DOM.php
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
* @ingroup Parser
|
||||
*/
|
||||
|
||||
/**
|
||||
* Expansion frame with custom arguments
|
||||
* @ingroup Parser
|
||||
*/
|
||||
// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
|
||||
class PPCustomFrame_DOM extends PPFrame_DOM {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
70
includes/parser/PPCustomFrame_Hash.php
Normal file
70
includes/parser/PPCustomFrame_Hash.php
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
* @ingroup Parser
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
39
includes/parser/PPDPart.php
Normal file
39
includes/parser/PPDPart.php
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
* @ingroup Parser
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup Parser
|
||||
*/
|
||||
class PPDPart {
|
||||
/**
|
||||
* @var string Output accumulator string
|
||||
*/
|
||||
public $out;
|
||||
|
||||
// Optional member variables:
|
||||
// eqpos Position of equals sign in output accumulator
|
||||
// commentEnd Past-the-end input pointer for the last comment encountered
|
||||
// visualEnd Past-the-end input pointer for the end of the accumulator minus comments
|
||||
|
||||
public function __construct( $out = '' ) {
|
||||
$this->out = $out;
|
||||
}
|
||||
}
|
||||
36
includes/parser/PPDPart_Hash.php
Normal file
36
includes/parser/PPDPart_Hash.php
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
* @ingroup Parser
|
||||
*/
|
||||
|
||||
/**
|
||||
* @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 );
|
||||
}
|
||||
}
|
||||
113
includes/parser/PPDStack.php
Normal file
113
includes/parser/PPDStack.php
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
* @ingroup Parser
|
||||
*/
|
||||
|
||||
/**
|
||||
* Stack class to help Preprocessor::preprocessToObj()
|
||||
* @ingroup Parser
|
||||
*/
|
||||
class PPDStack {
|
||||
public $stack, $rootAccum;
|
||||
|
||||
/**
|
||||
* @var PPDStack
|
||||
*/
|
||||
public $top;
|
||||
public $out;
|
||||
public $elementClass = PPDStackElement::class;
|
||||
|
||||
public static $false = false;
|
||||
|
||||
public function __construct() {
|
||||
$this->stack = [];
|
||||
$this->top = false;
|
||||
$this->rootAccum = '';
|
||||
$this->accum =& $this->rootAccum;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function count() {
|
||||
return count( $this->stack );
|
||||
}
|
||||
|
||||
public function &getAccum() {
|
||||
return $this->accum;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|PPDPart
|
||||
*/
|
||||
public function getCurrentPart() {
|
||||
if ( $this->top === false ) {
|
||||
return false;
|
||||
} else {
|
||||
return $this->top->getCurrentPart();
|
||||
}
|
||||
}
|
||||
|
||||
public function push( $data ) {
|
||||
if ( $data instanceof $this->elementClass ) {
|
||||
$this->stack[] = $data;
|
||||
} else {
|
||||
$class = $this->elementClass;
|
||||
$this->stack[] = new $class( $data );
|
||||
}
|
||||
$this->top = $this->stack[count( $this->stack ) - 1];
|
||||
$this->accum =& $this->top->getAccum();
|
||||
}
|
||||
|
||||
public function pop() {
|
||||
if ( $this->stack === [] ) {
|
||||
throw new MWException( __METHOD__ . ': no elements remaining' );
|
||||
}
|
||||
$temp = array_pop( $this->stack );
|
||||
|
||||
if ( count( $this->stack ) ) {
|
||||
$this->top = $this->stack[count( $this->stack ) - 1];
|
||||
$this->accum =& $this->top->getAccum();
|
||||
} else {
|
||||
$this->top = self::$false;
|
||||
$this->accum =& $this->rootAccum;
|
||||
}
|
||||
return $temp;
|
||||
}
|
||||
|
||||
public function addPart( $s = '' ) {
|
||||
$this->top->addPart( $s );
|
||||
$this->accum =& $this->top->getAccum();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getFlags() {
|
||||
if ( $this->stack === [] ) {
|
||||
return [
|
||||
'findEquals' => false,
|
||||
'findPipe' => false,
|
||||
'inHeading' => false,
|
||||
];
|
||||
} else {
|
||||
return $this->top->getFlags();
|
||||
}
|
||||
}
|
||||
}
|
||||
129
includes/parser/PPDStackElement.php
Normal file
129
includes/parser/PPDStackElement.php
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
* @ingroup Parser
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup Parser
|
||||
*/
|
||||
class PPDStackElement {
|
||||
/**
|
||||
* @var string Opening character (\n for heading)
|
||||
*/
|
||||
public $open;
|
||||
|
||||
/**
|
||||
* @var string Matching closing character
|
||||
*/
|
||||
public $close;
|
||||
|
||||
/**
|
||||
* @var string Saved prefix that may affect later processing,
|
||||
* e.g. to differentiate `-{{{{` and `{{{{` after later seeing `}}}`.
|
||||
*/
|
||||
public $savedPrefix = '';
|
||||
|
||||
/**
|
||||
* @var int Number of opening characters found (number of "=" for heading)
|
||||
*/
|
||||
public $count;
|
||||
|
||||
/**
|
||||
* @var PPDPart[] Array of PPDPart objects describing pipe-separated parts.
|
||||
*/
|
||||
public $parts;
|
||||
|
||||
/**
|
||||
* @var bool True if the open char appeared at the start of the input line.
|
||||
* Not set for headings.
|
||||
*/
|
||||
public $lineStart;
|
||||
|
||||
public $partClass = PPDPart::class;
|
||||
|
||||
public function __construct( $data = [] ) {
|
||||
$class = $this->partClass;
|
||||
$this->parts = [ new $class ];
|
||||
|
||||
foreach ( $data as $name => $value ) {
|
||||
$this->$name = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function &getAccum() {
|
||||
return $this->parts[count( $this->parts ) - 1]->out;
|
||||
}
|
||||
|
||||
public function addPart( $s = '' ) {
|
||||
$class = $this->partClass;
|
||||
$this->parts[] = new $class( $s );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PPDPart
|
||||
*/
|
||||
public function getCurrentPart() {
|
||||
return $this->parts[count( $this->parts ) - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getFlags() {
|
||||
$partCount = count( $this->parts );
|
||||
$findPipe = $this->open != "\n" && $this->open != '[';
|
||||
return [
|
||||
'findPipe' => $findPipe,
|
||||
'findEquals' => $findPipe && $partCount > 1 && !isset( $this->parts[$partCount - 1]->eqpos ),
|
||||
'inHeading' => $this->open == "\n",
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the output string that would result if the close is not found.
|
||||
*
|
||||
* @param bool|int $openingCount
|
||||
* @return string
|
||||
*/
|
||||
public function breakSyntax( $openingCount = false ) {
|
||||
if ( $this->open == "\n" ) {
|
||||
$s = $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 )
|
||||
);
|
||||
$s = $this->savedPrefix . $s;
|
||||
$first = true;
|
||||
foreach ( $this->parts as $part ) {
|
||||
if ( $first ) {
|
||||
$first = false;
|
||||
} else {
|
||||
$s .= '|';
|
||||
}
|
||||
$s .= $part->out;
|
||||
}
|
||||
}
|
||||
return $s;
|
||||
}
|
||||
}
|
||||
73
includes/parser/PPDStackElement_Hash.php
Normal file
73
includes/parser/PPDStackElement_Hash.php
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
* @ingroup Parser
|
||||
*/
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
34
includes/parser/PPDStack_Hash.php
Normal file
34
includes/parser/PPDStack_Hash.php
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
* @ingroup Parser
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 = [];
|
||||
}
|
||||
}
|
||||
204
includes/parser/PPFrame.php
Normal file
204
includes/parser/PPFrame.php
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
* @ingroup Parser
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup Parser
|
||||
*/
|
||||
interface PPFrame {
|
||||
const NO_ARGS = 1;
|
||||
const NO_TEMPLATES = 2;
|
||||
const STRIP_COMMENTS = 4;
|
||||
const NO_IGNORE = 8;
|
||||
const RECOVER_COMMENTS = 16;
|
||||
const NO_TAGS = 32;
|
||||
|
||||
const RECOVER_ORIG = self::NO_ARGS | self::NO_TEMPLATES | self::NO_IGNORE |
|
||||
self::RECOVER_COMMENTS | self::NO_TAGS;
|
||||
|
||||
/** This constant exists when $indexOffset is supported in newChild() */
|
||||
const SUPPORTS_INDEX_OFFSET = 1;
|
||||
|
||||
/**
|
||||
* Create a child frame
|
||||
*
|
||||
* @param array|bool $args
|
||||
* @param bool|Title $title
|
||||
* @param int $indexOffset A number subtracted from the index attributes of the arguments
|
||||
*
|
||||
* @return PPFrame
|
||||
*/
|
||||
public function newChild( $args = false, $title = false, $indexOffset = 0 );
|
||||
|
||||
/**
|
||||
* Expand a document tree node, caching the result on its parent with the given key
|
||||
* @param string|int $key
|
||||
* @param string|PPNode $root
|
||||
* @param int $flags
|
||||
* @return string
|
||||
*/
|
||||
public function cachedExpand( $key, $root, $flags = 0 );
|
||||
|
||||
/**
|
||||
* Expand a document tree node
|
||||
* @param string|PPNode $root
|
||||
* @param int $flags
|
||||
* @return string
|
||||
*/
|
||||
public function expand( $root, $flags = 0 );
|
||||
|
||||
/**
|
||||
* Implode with flags for expand()
|
||||
* @param string $sep
|
||||
* @param int $flags
|
||||
* @param string|PPNode $args,...
|
||||
* @return string
|
||||
*/
|
||||
public function implodeWithFlags( $sep, $flags /*, ... */ );
|
||||
|
||||
/**
|
||||
* Implode with no flags specified
|
||||
* @param string $sep
|
||||
* @param string|PPNode $args,...
|
||||
* @return string
|
||||
*/
|
||||
public function implode( $sep /*, ... */ );
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function virtualImplode( $sep /*, ... */ );
|
||||
|
||||
/**
|
||||
* Virtual implode with brackets
|
||||
* @param string $start
|
||||
* @param string $sep
|
||||
* @param string $end
|
||||
* @param string|PPNode $args,...
|
||||
* @return PPNode
|
||||
*/
|
||||
public function virtualBracketedImplode( $start, $sep, $end /*, ... */ );
|
||||
|
||||
/**
|
||||
* Returns true if there are no arguments in this frame
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty();
|
||||
|
||||
/**
|
||||
* Returns all arguments of this frame
|
||||
* @return array
|
||||
*/
|
||||
public function getArguments();
|
||||
|
||||
/**
|
||||
* Returns all numbered arguments of this frame
|
||||
* @return array
|
||||
*/
|
||||
public function getNumberedArguments();
|
||||
|
||||
/**
|
||||
* Returns all named arguments of this frame
|
||||
* @return array
|
||||
*/
|
||||
public function getNamedArguments();
|
||||
|
||||
/**
|
||||
* Get an argument to this frame by name
|
||||
* @param int|string $name
|
||||
* @return string|bool
|
||||
*/
|
||||
public function getArgument( $name );
|
||||
|
||||
/**
|
||||
* 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 true if the frame is a template frame
|
||||
* @return bool
|
||||
*/
|
||||
public function isTemplate();
|
||||
|
||||
/**
|
||||
* Set the "volatile" flag.
|
||||
*
|
||||
* Note that this is somewhat of a "hack" in order to make extensions
|
||||
* with side effects (such as Cite) work with the PHP parser. New
|
||||
* extensions should be written in a way that they do not need this
|
||||
* function, because other parsers (such as Parsoid) are not guaranteed
|
||||
* to respect it, and it may be removed in the future.
|
||||
*
|
||||
* @param bool $flag
|
||||
*/
|
||||
public function setVolatile( $flag = true );
|
||||
|
||||
/**
|
||||
* Get the "volatile" flag.
|
||||
*
|
||||
* Callers should avoid caching the result of an expansion if it has the
|
||||
* volatile flag set.
|
||||
*
|
||||
* @see self::setVolatile()
|
||||
* @return bool
|
||||
*/
|
||||
public function isVolatile();
|
||||
|
||||
/**
|
||||
* Get the TTL of the frame's output.
|
||||
*
|
||||
* This is the maximum amount of time, in seconds, that this frame's
|
||||
* output should be cached for. A value of null indicates that no
|
||||
* maximum has been specified.
|
||||
*
|
||||
* Note that this TTL only applies to caching frames as parts of pages.
|
||||
* It is not relevant to caching the entire rendered output of a page.
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getTTL();
|
||||
|
||||
/**
|
||||
* Set the TTL of the output of this frame and all of its ancestors.
|
||||
* Has no effect if the new TTL is greater than the one already set.
|
||||
* Note that it is the caller's responsibility to change the cache
|
||||
* expiry of the page as a whole, if such behavior is desired.
|
||||
*
|
||||
* @see self::getTTL()
|
||||
* @param int $ttl
|
||||
*/
|
||||
public function setTTL( $ttl );
|
||||
|
||||
/**
|
||||
* Get a title of frame
|
||||
*
|
||||
* @return Title
|
||||
*/
|
||||
public function getTitle();
|
||||
}
|
||||
631
includes/parser/PPFrame_DOM.php
Normal file
631
includes/parser/PPFrame_DOM.php
Normal file
|
|
@ -0,0 +1,631 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
* @ingroup Parser
|
||||
*/
|
||||
|
||||
/**
|
||||
* An expansion frame, used as a context to expand the result of preprocessToObj()
|
||||
* @ingroup Parser
|
||||
*/
|
||||
// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
|
||||
class PPFrame_DOM implements PPFrame {
|
||||
|
||||
/**
|
||||
* @var Preprocessor
|
||||
*/
|
||||
public $preprocessor;
|
||||
|
||||
/**
|
||||
* @var Parser
|
||||
*/
|
||||
public $parser;
|
||||
|
||||
/**
|
||||
* @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 bool|array $args
|
||||
* @param Title|bool $title
|
||||
* @param int $indexOffset
|
||||
* @return PPTemplateFrame_DOM
|
||||
*/
|
||||
public function newChild( $args = false, $title = false, $indexOffset = 0 ) {
|
||||
$namedArgs = [];
|
||||
$numberedArgs = [];
|
||||
if ( $title === false ) {
|
||||
$title = $this->title;
|
||||
}
|
||||
if ( $args !== false ) {
|
||||
$xpath = false;
|
||||
if ( $args instanceof PPNode ) {
|
||||
$args = $args->node;
|
||||
}
|
||||
foreach ( $args as $arg ) {
|
||||
if ( $arg instanceof PPNode ) {
|
||||
$arg = $arg->node;
|
||||
}
|
||||
if ( !$xpath || $xpath->document !== $arg->ownerDocument ) {
|
||||
$xpath = new DOMXPath( $arg->ownerDocument );
|
||||
}
|
||||
|
||||
$nameNodes = $xpath->query( 'name', $arg );
|
||||
$value = $xpath->query( 'value', $arg );
|
||||
if ( $nameNodes->item( 0 )->hasAttributes() ) {
|
||||
// Numbered parameter
|
||||
$index = $nameNodes->item( 0 )->attributes->getNamedItem( 'index' )->textContent;
|
||||
$index = $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] = $value->item( 0 );
|
||||
unset( $namedArgs[$index] );
|
||||
} else {
|
||||
// Named parameter
|
||||
$name = trim( $this->expand( $nameNodes->item( 0 ), 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] = $value->item( 0 );
|
||||
unset( $numberedArgs[$name] );
|
||||
}
|
||||
}
|
||||
}
|
||||
return new PPTemplateFrame_DOM( $this->preprocessor, $this, $numberedArgs, $namedArgs, $title );
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws MWException
|
||||
* @param string|int $key
|
||||
* @param string|PPNode_DOM|DOMDocument $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_DOM|DOMDocument $root
|
||||
* @param int $flags
|
||||
* @return string
|
||||
*/
|
||||
public function expand( $root, $flags = 0 ) {
|
||||
static $expansionDepth = 0;
|
||||
if ( is_string( $root ) ) {
|
||||
return $root;
|
||||
}
|
||||
|
||||
if ( ++$this->parser->mPPNodeCount > $this->parser->mOptions->getMaxPPNodeCount() ) {
|
||||
$this->parser->limitationWarn( 'node-count-exceeded',
|
||||
$this->parser->mPPNodeCount,
|
||||
$this->parser->mOptions->getMaxPPNodeCount()
|
||||
);
|
||||
return '<span class="error">Node-count limit exceeded</span>';
|
||||
}
|
||||
|
||||
if ( $expansionDepth > $this->parser->mOptions->getMaxPPExpandDepth() ) {
|
||||
$this->parser->limitationWarn( 'expansion-depth-exceeded',
|
||||
$expansionDepth,
|
||||
$this->parser->mOptions->getMaxPPExpandDepth()
|
||||
);
|
||||
return '<span class="error">Expansion depth limit exceeded</span>';
|
||||
}
|
||||
++$expansionDepth;
|
||||
if ( $expansionDepth > $this->parser->mHighestExpansionDepth ) {
|
||||
$this->parser->mHighestExpansionDepth = $expansionDepth;
|
||||
}
|
||||
|
||||
if ( $root instanceof PPNode_DOM ) {
|
||||
$root = $root->node;
|
||||
}
|
||||
if ( $root instanceof DOMDocument ) {
|
||||
$root = $root->documentElement;
|
||||
}
|
||||
|
||||
$outStack = [ '', '' ];
|
||||
$iteratorStack = [ false, $root ];
|
||||
$indexStack = [ 0, 0 ];
|
||||
|
||||
while ( count( $iteratorStack ) > 1 ) {
|
||||
$level = count( $outStack ) - 1;
|
||||
$iteratorNode =& $iteratorStack[$level];
|
||||
$out =& $outStack[$level];
|
||||
$index =& $indexStack[$level];
|
||||
|
||||
if ( $iteratorNode instanceof PPNode_DOM ) {
|
||||
$iteratorNode = $iteratorNode->node;
|
||||
}
|
||||
|
||||
if ( is_array( $iteratorNode ) ) {
|
||||
if ( $index >= count( $iteratorNode ) ) {
|
||||
// All done with this iterator
|
||||
$iteratorStack[$level] = false;
|
||||
$contextNode = false;
|
||||
} else {
|
||||
$contextNode = $iteratorNode[$index];
|
||||
$index++;
|
||||
}
|
||||
} elseif ( $iteratorNode instanceof DOMNodeList ) {
|
||||
if ( $index >= $iteratorNode->length ) {
|
||||
// All done with this iterator
|
||||
$iteratorStack[$level] = false;
|
||||
$contextNode = false;
|
||||
} else {
|
||||
$contextNode = $iteratorNode->item( $index );
|
||||
$index++;
|
||||
}
|
||||
} else {
|
||||
// Copy to $contextNode and then delete from iterator stack,
|
||||
// because this is not an iterator but we do have to execute it once
|
||||
$contextNode = $iteratorStack[$level];
|
||||
$iteratorStack[$level] = false;
|
||||
}
|
||||
|
||||
if ( $contextNode instanceof PPNode_DOM ) {
|
||||
$contextNode = $contextNode->node;
|
||||
}
|
||||
|
||||
$newIterator = false;
|
||||
|
||||
if ( $contextNode === false ) {
|
||||
// nothing to do
|
||||
} elseif ( is_string( $contextNode ) ) {
|
||||
$out .= $contextNode;
|
||||
} elseif ( is_array( $contextNode ) || $contextNode instanceof DOMNodeList ) {
|
||||
$newIterator = $contextNode;
|
||||
} elseif ( $contextNode instanceof DOMNode ) {
|
||||
if ( $contextNode->nodeType == XML_TEXT_NODE ) {
|
||||
$out .= $contextNode->nodeValue;
|
||||
} elseif ( $contextNode->nodeName == 'template' ) {
|
||||
# Double-brace expansion
|
||||
$xpath = new DOMXPath( $contextNode->ownerDocument );
|
||||
$titles = $xpath->query( 'title', $contextNode );
|
||||
$title = $titles->item( 0 );
|
||||
$parts = $xpath->query( 'part', $contextNode );
|
||||
if ( $flags & PPFrame::NO_TEMPLATES ) {
|
||||
$newIterator = $this->virtualBracketedImplode( '{{', '|', '}}', $title, $parts );
|
||||
} else {
|
||||
$lineStart = $contextNode->getAttribute( 'lineStart' );
|
||||
$params = [
|
||||
'title' => new PPNode_DOM( $title ),
|
||||
'parts' => new PPNode_DOM( $parts ),
|
||||
'lineStart' => $lineStart ];
|
||||
$ret = $this->parser->braceSubstitution( $params, $this );
|
||||
if ( isset( $ret['object'] ) ) {
|
||||
$newIterator = $ret['object'];
|
||||
} else {
|
||||
$out .= $ret['text'];
|
||||
}
|
||||
}
|
||||
} elseif ( $contextNode->nodeName == 'tplarg' ) {
|
||||
# Triple-brace expansion
|
||||
$xpath = new DOMXPath( $contextNode->ownerDocument );
|
||||
$titles = $xpath->query( 'title', $contextNode );
|
||||
$title = $titles->item( 0 );
|
||||
$parts = $xpath->query( 'part', $contextNode );
|
||||
if ( $flags & PPFrame::NO_ARGS ) {
|
||||
$newIterator = $this->virtualBracketedImplode( '{{{', '|', '}}}', $title, $parts );
|
||||
} else {
|
||||
$params = [
|
||||
'title' => new PPNode_DOM( $title ),
|
||||
'parts' => new PPNode_DOM( $parts ) ];
|
||||
$ret = $this->parser->argSubstitution( $params, $this );
|
||||
if ( isset( $ret['object'] ) ) {
|
||||
$newIterator = $ret['object'];
|
||||
} else {
|
||||
$out .= $ret['text'];
|
||||
}
|
||||
}
|
||||
} elseif ( $contextNode->nodeName == 'comment' ) {
|
||||
# HTML-style comment
|
||||
# Remove it in HTML, pre+remove and STRIP_COMMENTS modes
|
||||
# Not in RECOVER_COMMENTS mode (msgnw) though.
|
||||
if ( ( $this->parser->ot['html']
|
||||
|| ( $this->parser->ot['pre'] && $this->parser->mOptions->getRemoveComments() )
|
||||
|| ( $flags & PPFrame::STRIP_COMMENTS )
|
||||
) && !( $flags & PPFrame::RECOVER_COMMENTS )
|
||||
) {
|
||||
$out .= '';
|
||||
} elseif ( $this->parser->ot['wiki'] && !( $flags & PPFrame::RECOVER_COMMENTS ) ) {
|
||||
# Add a strip marker in PST mode so that pstPass2() can
|
||||
# run some old-fashioned regexes on the result.
|
||||
# Not in RECOVER_COMMENTS mode (extractSections) though.
|
||||
$out .= $this->parser->insertStripItem( $contextNode->textContent );
|
||||
} else {
|
||||
# Recover the literal comment in RECOVER_COMMENTS and pre+no-remove
|
||||
$out .= $contextNode->textContent;
|
||||
}
|
||||
} elseif ( $contextNode->nodeName == 'ignore' ) {
|
||||
# Output suppression used by <includeonly> etc.
|
||||
# OT_WIKI will only respect <ignore> in substed templates.
|
||||
# The other output types respect it unless NO_IGNORE is set.
|
||||
# extractSections() sets NO_IGNORE and so never respects it.
|
||||
if ( ( !isset( $this->parent ) && $this->parser->ot['wiki'] )
|
||||
|| ( $flags & PPFrame::NO_IGNORE )
|
||||
) {
|
||||
$out .= $contextNode->textContent;
|
||||
} else {
|
||||
$out .= '';
|
||||
}
|
||||
} elseif ( $contextNode->nodeName == 'ext' ) {
|
||||
# Extension tag
|
||||
$xpath = new DOMXPath( $contextNode->ownerDocument );
|
||||
$names = $xpath->query( 'name', $contextNode );
|
||||
$attrs = $xpath->query( 'attr', $contextNode );
|
||||
$inners = $xpath->query( 'inner', $contextNode );
|
||||
$closes = $xpath->query( 'close', $contextNode );
|
||||
if ( $flags & PPFrame::NO_TAGS ) {
|
||||
$s = '<' . $this->expand( $names->item( 0 ), $flags );
|
||||
if ( $attrs->length > 0 ) {
|
||||
$s .= $this->expand( $attrs->item( 0 ), $flags );
|
||||
}
|
||||
if ( $inners->length > 0 ) {
|
||||
$s .= '>' . $this->expand( $inners->item( 0 ), $flags );
|
||||
if ( $closes->length > 0 ) {
|
||||
$s .= $this->expand( $closes->item( 0 ), $flags );
|
||||
}
|
||||
} else {
|
||||
$s .= '/>';
|
||||
}
|
||||
$out .= $s;
|
||||
} else {
|
||||
$params = [
|
||||
'name' => new PPNode_DOM( $names->item( 0 ) ),
|
||||
'attr' => $attrs->length > 0 ? new PPNode_DOM( $attrs->item( 0 ) ) : null,
|
||||
'inner' => $inners->length > 0 ? new PPNode_DOM( $inners->item( 0 ) ) : null,
|
||||
'close' => $closes->length > 0 ? new PPNode_DOM( $closes->item( 0 ) ) : null,
|
||||
];
|
||||
$out .= $this->parser->extensionSubstitution( $params, $this );
|
||||
}
|
||||
} elseif ( $contextNode->nodeName == 'h' ) {
|
||||
# Heading
|
||||
$s = $this->expand( $contextNode->childNodes, $flags );
|
||||
|
||||
# Insert a heading marker only for <h> children of <root>
|
||||
# This is to stop extractSections from going over multiple tree levels
|
||||
if ( $contextNode->parentNode->nodeName == 'root' && $this->parser->ot['html'] ) {
|
||||
# Insert heading index marker
|
||||
$headingIndex = $contextNode->getAttribute( 'i' );
|
||||
$titleText = $this->title->getPrefixedDBkey();
|
||||
$this->parser->mHeadings[] = [ $titleText, $headingIndex ];
|
||||
$serial = count( $this->parser->mHeadings ) - 1;
|
||||
$marker = Parser::MARKER_PREFIX . "-h-$serial-" . Parser::MARKER_SUFFIX;
|
||||
$count = $contextNode->getAttribute( 'level' );
|
||||
$s = substr( $s, 0, $count ) . $marker . substr( $s, $count );
|
||||
$this->parser->mStripState->addGeneral( $marker, '' );
|
||||
}
|
||||
$out .= $s;
|
||||
} else {
|
||||
# Generic recursive expansion
|
||||
$newIterator = $contextNode->childNodes;
|
||||
}
|
||||
} else {
|
||||
throw new MWException( __METHOD__ . ': Invalid parameter type' );
|
||||
}
|
||||
|
||||
if ( $newIterator !== false ) {
|
||||
if ( $newIterator instanceof PPNode_DOM ) {
|
||||
$newIterator = $newIterator->node;
|
||||
}
|
||||
$outStack[] = '';
|
||||
$iteratorStack[] = $newIterator;
|
||||
$indexStack[] = 0;
|
||||
} elseif ( $iteratorStack[$level] === false ) {
|
||||
// Return accumulated value to parent
|
||||
// With tail recursion
|
||||
while ( $iteratorStack[$level] === false && $level > 0 ) {
|
||||
$outStack[$level - 1] .= $out;
|
||||
array_pop( $outStack );
|
||||
array_pop( $iteratorStack );
|
||||
array_pop( $indexStack );
|
||||
$level--;
|
||||
}
|
||||
}
|
||||
}
|
||||
--$expansionDepth;
|
||||
return $outStack[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sep
|
||||
* @param int $flags
|
||||
* @param string|PPNode_DOM|DOMDocument ...$args
|
||||
* @return string
|
||||
*/
|
||||
public function implodeWithFlags( $sep, $flags, ...$args ) {
|
||||
$first = true;
|
||||
$s = '';
|
||||
foreach ( $args as $root ) {
|
||||
if ( $root instanceof PPNode_DOM ) {
|
||||
$root = $root->node;
|
||||
}
|
||||
if ( !is_array( $root ) && !( $root instanceof DOMNodeList ) ) {
|
||||
$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_DOM|DOMDocument ...$args
|
||||
* @return string
|
||||
*/
|
||||
public function implode( $sep, ...$args ) {
|
||||
$first = true;
|
||||
$s = '';
|
||||
foreach ( $args as $root ) {
|
||||
if ( $root instanceof PPNode_DOM ) {
|
||||
$root = $root->node;
|
||||
}
|
||||
if ( !is_array( $root ) && !( $root instanceof DOMNodeList ) ) {
|
||||
$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_DOM|DOMDocument ...$args
|
||||
* @return array
|
||||
*/
|
||||
public function virtualImplode( $sep, ...$args ) {
|
||||
$out = [];
|
||||
$first = true;
|
||||
|
||||
foreach ( $args as $root ) {
|
||||
if ( $root instanceof PPNode_DOM ) {
|
||||
$root = $root->node;
|
||||
}
|
||||
if ( !is_array( $root ) && !( $root instanceof DOMNodeList ) ) {
|
||||
$root = [ $root ];
|
||||
}
|
||||
foreach ( $root as $node ) {
|
||||
if ( $first ) {
|
||||
$first = false;
|
||||
} else {
|
||||
$out[] = $sep;
|
||||
}
|
||||
$out[] = $node;
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Virtual implode with brackets
|
||||
* @param string $start
|
||||
* @param string $sep
|
||||
* @param string $end
|
||||
* @param string|PPNode_DOM|DOMDocument ...$args
|
||||
* @return array
|
||||
*/
|
||||
public function virtualBracketedImplode( $start, $sep, $end, ...$args ) {
|
||||
$out = [ $start ];
|
||||
$first = true;
|
||||
|
||||
foreach ( $args as $root ) {
|
||||
if ( $root instanceof PPNode_DOM ) {
|
||||
$root = $root->node;
|
||||
}
|
||||
if ( !is_array( $root ) && !( $root instanceof DOMNodeList ) ) {
|
||||
$root = [ $root ];
|
||||
}
|
||||
foreach ( $root as $node ) {
|
||||
if ( $first ) {
|
||||
$first = false;
|
||||
} else {
|
||||
$out[] = $sep;
|
||||
}
|
||||
$out[] = $node;
|
||||
}
|
||||
}
|
||||
$out[] = $end;
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function __toString() {
|
||||
return 'frame{}';
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
613
includes/parser/PPFrame_Hash.php
Normal file
613
includes/parser/PPFrame_Hash.php
Normal file
|
|
@ -0,0 +1,613 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
* @ingroup Parser
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
112
includes/parser/PPNode.php
Normal file
112
includes/parser/PPNode.php
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
* @ingroup Parser
|
||||
*/
|
||||
|
||||
/**
|
||||
* There are three types of nodes:
|
||||
* * Tree nodes, which have a name and contain other nodes as children
|
||||
* * Array nodes, which also contain other nodes but aren't considered part of a tree
|
||||
* * Leaf nodes, which contain the actual data
|
||||
*
|
||||
* This interface provides access to the tree structure and to the contents of array nodes,
|
||||
* but it does not provide access to the internal structure of leaf nodes. Access to leaf
|
||||
* data is provided via two means:
|
||||
* * PPFrame::expand(), which provides expanded text
|
||||
* * The PPNode::split*() functions, which provide metadata about certain types of tree node
|
||||
* @ingroup Parser
|
||||
*/
|
||||
interface PPNode {
|
||||
/**
|
||||
* Get an array-type node containing the children of this node.
|
||||
* Returns false if this is not a tree node.
|
||||
* @return PPNode
|
||||
*/
|
||||
public function getChildren();
|
||||
|
||||
/**
|
||||
* Get the first child of a tree node. False if there isn't one.
|
||||
*
|
||||
* @return PPNode
|
||||
*/
|
||||
public function getFirstChild();
|
||||
|
||||
/**
|
||||
* Get the next sibling of any node. False if there isn't one
|
||||
* @return PPNode
|
||||
*/
|
||||
public function getNextSibling();
|
||||
|
||||
/**
|
||||
* Get all children of this tree node which have a given name.
|
||||
* Returns an array-type node, or false if this is not a tree node.
|
||||
* @param string $type
|
||||
* @return bool|PPNode
|
||||
*/
|
||||
public function getChildrenOfType( $type );
|
||||
|
||||
/**
|
||||
* Returns the length of the array, or false if this is not an array-type node
|
||||
*/
|
||||
public function getLength();
|
||||
|
||||
/**
|
||||
* Returns an item of an array-type node
|
||||
* @param int $i
|
||||
* @return bool|PPNode
|
||||
*/
|
||||
public function item( $i );
|
||||
|
||||
/**
|
||||
* Get the name of this node. The following names are defined here:
|
||||
*
|
||||
* h A heading node.
|
||||
* template A double-brace node.
|
||||
* tplarg A triple-brace node.
|
||||
* title The first argument to a template or tplarg node.
|
||||
* part Subsequent arguments to a template or tplarg node.
|
||||
* #nodelist An array-type node
|
||||
*
|
||||
* The subclass may define various other names for tree and leaf nodes.
|
||||
* @return string
|
||||
*/
|
||||
public function getName();
|
||||
|
||||
/**
|
||||
* Split a "<part>" node into an associative array containing:
|
||||
* name PPNode name
|
||||
* index String index
|
||||
* value PPNode value
|
||||
* @return array
|
||||
*/
|
||||
public function splitArg();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return array
|
||||
*/
|
||||
public function splitExt();
|
||||
|
||||
/**
|
||||
* Split an "<h>" node
|
||||
* @return array
|
||||
*/
|
||||
public function splitHeading();
|
||||
}
|
||||
188
includes/parser/PPNode_DOM.php
Normal file
188
includes/parser/PPNode_DOM.php
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
* @ingroup Parser
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup Parser
|
||||
*/
|
||||
// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
|
||||
class PPNode_DOM implements PPNode {
|
||||
|
||||
/**
|
||||
* @var DOMElement
|
||||
*/
|
||||
public $node;
|
||||
public $xpath;
|
||||
|
||||
public function __construct( $node, $xpath = false ) {
|
||||
$this->node = $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DOMXPath
|
||||
*/
|
||||
public function getXPath() {
|
||||
if ( $this->xpath === null ) {
|
||||
$this->xpath = new DOMXPath( $this->node->ownerDocument );
|
||||
}
|
||||
return $this->xpath;
|
||||
}
|
||||
|
||||
public function __toString() {
|
||||
if ( $this->node instanceof DOMNodeList ) {
|
||||
$s = '';
|
||||
foreach ( $this->node as $node ) {
|
||||
$s .= $node->ownerDocument->saveXML( $node );
|
||||
}
|
||||
} else {
|
||||
$s = $this->node->ownerDocument->saveXML( $this->node );
|
||||
}
|
||||
return $s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|PPNode_DOM
|
||||
*/
|
||||
public function getChildren() {
|
||||
return $this->node->childNodes ? new self( $this->node->childNodes ) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|PPNode_DOM
|
||||
*/
|
||||
public function getFirstChild() {
|
||||
return $this->node->firstChild ? new self( $this->node->firstChild ) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|PPNode_DOM
|
||||
*/
|
||||
public function getNextSibling() {
|
||||
return $this->node->nextSibling ? new self( $this->node->nextSibling ) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
*
|
||||
* @return bool|PPNode_DOM
|
||||
*/
|
||||
public function getChildrenOfType( $type ) {
|
||||
return new self( $this->getXPath()->query( $type, $this->node ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLength() {
|
||||
if ( $this->node instanceof DOMNodeList ) {
|
||||
return $this->node->length;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $i
|
||||
* @return bool|PPNode_DOM
|
||||
*/
|
||||
public function item( $i ) {
|
||||
$item = $this->node->item( $i );
|
||||
return $item ? new self( $item ) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName() {
|
||||
if ( $this->node instanceof DOMNodeList ) {
|
||||
return '#nodelist';
|
||||
} else {
|
||||
return $this->node->nodeName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
$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__ );
|
||||
}
|
||||
$name = $names->item( 0 );
|
||||
$index = $name->getAttribute( 'index' );
|
||||
return [
|
||||
'name' => new self( $name ),
|
||||
'index' => $index,
|
||||
'value' => new self( $values->item( 0 ) ) ];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
$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__ );
|
||||
}
|
||||
$parts = [
|
||||
'name' => new self( $names->item( 0 ) ),
|
||||
'attr' => new self( $attrs->item( 0 ) ) ];
|
||||
if ( $inners->length ) {
|
||||
$parts['inner'] = new self( $inners->item( 0 ) );
|
||||
}
|
||||
if ( $closes->length ) {
|
||||
$parts['close'] = new self( $closes->item( 0 ) );
|
||||
}
|
||||
return $parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a "<h>" node
|
||||
* @throws MWException
|
||||
* @return array
|
||||
*/
|
||||
public function splitHeading() {
|
||||
if ( $this->getName() !== 'h' ) {
|
||||
throw new MWException( 'Invalid h node passed to ' . __METHOD__ );
|
||||
}
|
||||
return [
|
||||
'i' => $this->node->getAttribute( 'i' ),
|
||||
'level' => $this->node->getAttribute( 'level' ),
|
||||
'contents' => $this->getChildren()
|
||||
];
|
||||
}
|
||||
}
|
||||
77
includes/parser/PPNode_Hash_Array.php
Normal file
77
includes/parser/PPNode_Hash_Array.php
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
* @ingroup Parser
|
||||
*/
|
||||
|
||||
/**
|
||||
* @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' );
|
||||
}
|
||||
}
|
||||
92
includes/parser/PPNode_Hash_Attr.php
Normal file
92
includes/parser/PPNode_Hash_Attr.php
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
* @ingroup Parser
|
||||
*/
|
||||
|
||||
/**
|
||||
* @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' );
|
||||
}
|
||||
}
|
||||
90
includes/parser/PPNode_Hash_Text.php
Normal file
90
includes/parser/PPNode_Hash_Text.php
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
* @ingroup Parser
|
||||
*/
|
||||
|
||||
/**
|
||||
* @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' );
|
||||
}
|
||||
}
|
||||
369
includes/parser/PPNode_Hash_Tree.php
Normal file
369
includes/parser/PPNode_Hash_Tree.php
Normal file
|
|
@ -0,0 +1,369 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
* @ingroup Parser
|
||||
*/
|
||||
|
||||
/**
|
||||
* @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|false
|
||||
* @throws MWException
|
||||
*/
|
||||
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
|
||||
* @return string
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
198
includes/parser/PPTemplateFrame_DOM.php
Normal file
198
includes/parser/PPTemplateFrame_DOM.php
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
* @ingroup Parser
|
||||
*/
|
||||
|
||||
/**
|
||||
* Expansion frame with template arguments
|
||||
* @ingroup Parser
|
||||
*/
|
||||
// phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
|
||||
class PPTemplateFrame_DOM extends PPFrame_DOM {
|
||||
|
||||
public $numberedArgs, $namedArgs;
|
||||
|
||||
/**
|
||||
* @var PPFrame_DOM
|
||||
*/
|
||||
public $parent;
|
||||
public $numberedExpansionCache, $namedExpansionCache;
|
||||
|
||||
/**
|
||||
* @param Preprocessor $preprocessor
|
||||
* @param bool|PPFrame_DOM $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->ownerDocument->saveXML( $value ) ) . '"';
|
||||
}
|
||||
$s .= '}';
|
||||
return $s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws MWException
|
||||
* @param string|int $key
|
||||
* @param string|PPNode_DOM|DOMDocument $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 );
|
||||
}
|
||||
|
||||
public function getArguments() {
|
||||
$arguments = [];
|
||||
foreach ( array_merge(
|
||||
array_keys( $this->numberedArgs ),
|
||||
array_keys( $this->namedArgs ) ) as $key ) {
|
||||
$arguments[$key] = $this->getArgument( $key );
|
||||
}
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
public function getNumberedArguments() {
|
||||
$arguments = [];
|
||||
foreach ( array_keys( $this->numberedArgs ) as $key ) {
|
||||
$arguments[$key] = $this->getArgument( $key );
|
||||
}
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
202
includes/parser/PPTemplateFrame_Hash.php
Normal file
202
includes/parser/PPTemplateFrame_Hash.php
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
* @ingroup Parser
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 );
|
||||
}
|
||||
}
|
||||
|
|
@ -164,279 +164,3 @@ abstract class Preprocessor {
|
|||
*/
|
||||
abstract public function preprocessToObj( $text, $flags = 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup Parser
|
||||
*/
|
||||
interface PPFrame {
|
||||
const NO_ARGS = 1;
|
||||
const NO_TEMPLATES = 2;
|
||||
const STRIP_COMMENTS = 4;
|
||||
const NO_IGNORE = 8;
|
||||
const RECOVER_COMMENTS = 16;
|
||||
const NO_TAGS = 32;
|
||||
|
||||
const RECOVER_ORIG = self::NO_ARGS | self::NO_TEMPLATES | self::NO_IGNORE |
|
||||
self::RECOVER_COMMENTS | self::NO_TAGS;
|
||||
|
||||
/** This constant exists when $indexOffset is supported in newChild() */
|
||||
const SUPPORTS_INDEX_OFFSET = 1;
|
||||
|
||||
/**
|
||||
* Create a child frame
|
||||
*
|
||||
* @param array|bool $args
|
||||
* @param bool|Title $title
|
||||
* @param int $indexOffset A number subtracted from the index attributes of the arguments
|
||||
*
|
||||
* @return PPFrame
|
||||
*/
|
||||
public function newChild( $args = false, $title = false, $indexOffset = 0 );
|
||||
|
||||
/**
|
||||
* Expand a document tree node, caching the result on its parent with the given key
|
||||
* @param string|int $key
|
||||
* @param string|PPNode $root
|
||||
* @param int $flags
|
||||
* @return string
|
||||
*/
|
||||
public function cachedExpand( $key, $root, $flags = 0 );
|
||||
|
||||
/**
|
||||
* Expand a document tree node
|
||||
* @param string|PPNode $root
|
||||
* @param int $flags
|
||||
* @return string
|
||||
*/
|
||||
public function expand( $root, $flags = 0 );
|
||||
|
||||
/**
|
||||
* Implode with flags for expand()
|
||||
* @param string $sep
|
||||
* @param int $flags
|
||||
* @param string|PPNode $args,...
|
||||
* @return string
|
||||
*/
|
||||
public function implodeWithFlags( $sep, $flags /*, ... */ );
|
||||
|
||||
/**
|
||||
* Implode with no flags specified
|
||||
* @param string $sep
|
||||
* @param string|PPNode $args,...
|
||||
* @return string
|
||||
*/
|
||||
public function implode( $sep /*, ... */ );
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function virtualImplode( $sep /*, ... */ );
|
||||
|
||||
/**
|
||||
* Virtual implode with brackets
|
||||
* @param string $start
|
||||
* @param string $sep
|
||||
* @param string $end
|
||||
* @param string|PPNode $args,...
|
||||
* @return PPNode
|
||||
*/
|
||||
public function virtualBracketedImplode( $start, $sep, $end /*, ... */ );
|
||||
|
||||
/**
|
||||
* Returns true if there are no arguments in this frame
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty();
|
||||
|
||||
/**
|
||||
* Returns all arguments of this frame
|
||||
* @return array
|
||||
*/
|
||||
public function getArguments();
|
||||
|
||||
/**
|
||||
* Returns all numbered arguments of this frame
|
||||
* @return array
|
||||
*/
|
||||
public function getNumberedArguments();
|
||||
|
||||
/**
|
||||
* Returns all named arguments of this frame
|
||||
* @return array
|
||||
*/
|
||||
public function getNamedArguments();
|
||||
|
||||
/**
|
||||
* Get an argument to this frame by name
|
||||
* @param int|string $name
|
||||
* @return string|bool
|
||||
*/
|
||||
public function getArgument( $name );
|
||||
|
||||
/**
|
||||
* 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 true if the frame is a template frame
|
||||
* @return bool
|
||||
*/
|
||||
public function isTemplate();
|
||||
|
||||
/**
|
||||
* Set the "volatile" flag.
|
||||
*
|
||||
* Note that this is somewhat of a "hack" in order to make extensions
|
||||
* with side effects (such as Cite) work with the PHP parser. New
|
||||
* extensions should be written in a way that they do not need this
|
||||
* function, because other parsers (such as Parsoid) are not guaranteed
|
||||
* to respect it, and it may be removed in the future.
|
||||
*
|
||||
* @param bool $flag
|
||||
*/
|
||||
public function setVolatile( $flag = true );
|
||||
|
||||
/**
|
||||
* Get the "volatile" flag.
|
||||
*
|
||||
* Callers should avoid caching the result of an expansion if it has the
|
||||
* volatile flag set.
|
||||
*
|
||||
* @see self::setVolatile()
|
||||
* @return bool
|
||||
*/
|
||||
public function isVolatile();
|
||||
|
||||
/**
|
||||
* Get the TTL of the frame's output.
|
||||
*
|
||||
* This is the maximum amount of time, in seconds, that this frame's
|
||||
* output should be cached for. A value of null indicates that no
|
||||
* maximum has been specified.
|
||||
*
|
||||
* Note that this TTL only applies to caching frames as parts of pages.
|
||||
* It is not relevant to caching the entire rendered output of a page.
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getTTL();
|
||||
|
||||
/**
|
||||
* Set the TTL of the output of this frame and all of its ancestors.
|
||||
* Has no effect if the new TTL is greater than the one already set.
|
||||
* Note that it is the caller's responsibility to change the cache
|
||||
* expiry of the page as a whole, if such behavior is desired.
|
||||
*
|
||||
* @see self::getTTL()
|
||||
* @param int $ttl
|
||||
*/
|
||||
public function setTTL( $ttl );
|
||||
|
||||
/**
|
||||
* Get a title of frame
|
||||
*
|
||||
* @return Title
|
||||
*/
|
||||
public function getTitle();
|
||||
}
|
||||
|
||||
/**
|
||||
* There are three types of nodes:
|
||||
* * Tree nodes, which have a name and contain other nodes as children
|
||||
* * Array nodes, which also contain other nodes but aren't considered part of a tree
|
||||
* * Leaf nodes, which contain the actual data
|
||||
*
|
||||
* This interface provides access to the tree structure and to the contents of array nodes,
|
||||
* but it does not provide access to the internal structure of leaf nodes. Access to leaf
|
||||
* data is provided via two means:
|
||||
* * PPFrame::expand(), which provides expanded text
|
||||
* * The PPNode::split*() functions, which provide metadata about certain types of tree node
|
||||
* @ingroup Parser
|
||||
*/
|
||||
interface PPNode {
|
||||
/**
|
||||
* Get an array-type node containing the children of this node.
|
||||
* Returns false if this is not a tree node.
|
||||
* @return PPNode
|
||||
*/
|
||||
public function getChildren();
|
||||
|
||||
/**
|
||||
* Get the first child of a tree node. False if there isn't one.
|
||||
*
|
||||
* @return PPNode
|
||||
*/
|
||||
public function getFirstChild();
|
||||
|
||||
/**
|
||||
* Get the next sibling of any node. False if there isn't one
|
||||
* @return PPNode
|
||||
*/
|
||||
public function getNextSibling();
|
||||
|
||||
/**
|
||||
* Get all children of this tree node which have a given name.
|
||||
* Returns an array-type node, or false if this is not a tree node.
|
||||
* @param string $type
|
||||
* @return bool|PPNode
|
||||
*/
|
||||
public function getChildrenOfType( $type );
|
||||
|
||||
/**
|
||||
* Returns the length of the array, or false if this is not an array-type node
|
||||
*/
|
||||
public function getLength();
|
||||
|
||||
/**
|
||||
* Returns an item of an array-type node
|
||||
* @param int $i
|
||||
* @return bool|PPNode
|
||||
*/
|
||||
public function item( $i );
|
||||
|
||||
/**
|
||||
* Get the name of this node. The following names are defined here:
|
||||
*
|
||||
* h A heading node.
|
||||
* template A double-brace node.
|
||||
* tplarg A triple-brace node.
|
||||
* title The first argument to a template or tplarg node.
|
||||
* part Subsequent arguments to a template or tplarg node.
|
||||
* #nodelist An array-type node
|
||||
*
|
||||
* The subclass may define various other names for tree and leaf nodes.
|
||||
* @return string
|
||||
*/
|
||||
public function getName();
|
||||
|
||||
/**
|
||||
* Split a "<part>" node into an associative array containing:
|
||||
* name PPNode name
|
||||
* index String index
|
||||
* value PPNode value
|
||||
* @return array
|
||||
*/
|
||||
public function splitArg();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return array
|
||||
*/
|
||||
public function splitExt();
|
||||
|
||||
/**
|
||||
* Split an "<h>" node
|
||||
* @return array
|
||||
*/
|
||||
public function splitHeading();
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue