From 92cf49df5ca0c5dea25f5ae651996d79a2ce25af Mon Sep 17 00:00:00 2001 From: Brad Jorsch Date: Wed, 22 Nov 2017 13:12:23 -0500 Subject: [PATCH 1/1] ParserOutput: Add stateless transforms to getText() The stateful transforms are deprecated. Inspired by Krinkle's If2fb32fc. Bug: T171797 Change-Id: Ied5fe1a6159c2d4fa48170042b44d735ce7b6f9b --- RELEASE-NOTES-1.31 | 14 ++ docs/hooks.txt | 6 + includes/parser/ParserOptions.php | 2 + includes/parser/ParserOutput.php | 51 +++- .../includes/parser/ParserOutputTest.php | 229 ++++++++++++++++++ 5 files changed, 298 insertions(+), 4 deletions(-) diff --git a/RELEASE-NOTES-1.31 b/RELEASE-NOTES-1.31 index b32e3e755e..139773b573 100644 --- a/RELEASE-NOTES-1.31 +++ b/RELEASE-NOTES-1.31 @@ -31,6 +31,8 @@ production. [[iw:User:Example|iw>Example]]. * (T111605) The 'ImportHandleUnknownUser' hook allows extensions to auto-create users during an import. +* Added a hook, ParserOutputPostCacheTransform, to allow extensions to affect + the ParserOutput::getText() post-cache transformations. === External library changes in 1.31 === @@ -119,6 +121,18 @@ changes to languages because of Phabricator reports. * The Block class will no longer accept usable-but-missing usernames for 'byText' or ->setBlocker(). Callers should either ensure the blocker exists locally or use a new interwiki-format username like "iw>Example". +* The following methods that get and set ParserOutput state are deprecated. + Callers should use the new stateless $options parameter to + ParserOutput::getText() instead. + * ParserOptions::getEditSection() + * ParserOptions::setEditSection() + * ParserOutput::getEditSectionTokens() + * ParserOutput::setEditSectionTokens() + * ParserOutput::getTOCEnabled() + * ParserOutput::setTOCEnabled() + * OutputPage::enableSectionEditLinks() + * OutputPage::sectionEditLinksEnabled() + * The public ParserOutput state fields $mTOCEnabled and $mEditSectionTokens are also deprecated. == Compatibility == MediaWiki 1.31 requires PHP 5.5.9 or later. There is experimental support for diff --git a/docs/hooks.txt b/docs/hooks.txt index 685a182a55..29883b25a6 100644 --- a/docs/hooks.txt +++ b/docs/hooks.txt @@ -2594,6 +2594,12 @@ RejectParserCacheValue hook) because MediaWiki won't do it for you. callable here. The callable is passed the ParserOptions object and the option name. +'ParserOutputPostCacheTransform': Called from ParserOutput::getText() to do +post-cache transforms. +$parserOutput: The ParserOutput object. +&$text: The text being transformed, before core transformations are done. +&$options: The options array being used for the transformation. + 'ParserSectionCreate': Called each time the parser creates a document section from wikitext. Use this to apply per-section modifications to HTML (like wrapping the section in a DIV). Caveat: DIVs are valid wikitext, and a DIV diff --git a/includes/parser/ParserOptions.php b/includes/parser/ParserOptions.php index 5e2845ffe2..f99089b895 100644 --- a/includes/parser/ParserOptions.php +++ b/includes/parser/ParserOptions.php @@ -869,6 +869,7 @@ class ParserOptions { /** * Create "edit section" links? + * @deprecated since 1.31, use ParserOutput::getText() options instead. * @return bool */ public function getEditSection() { @@ -877,6 +878,7 @@ class ParserOptions { /** * Create "edit section" links? + * @deprecated since 1.31, use ParserOutput::getText() options instead. * @param bool|null $x New value (null is no change) * @return bool Old value */ diff --git a/includes/parser/ParserOutput.php b/includes/parser/ParserOutput.php index 3480a51f3c..59c27e5db1 100644 --- a/includes/parser/ParserOutput.php +++ b/includes/parser/ParserOutput.php @@ -144,6 +144,7 @@ class ParserOutput extends CacheTime { public $mSections = []; /** + * @deprecated since 1.31 Use getText() options. * @var bool $mEditSectionTokens prefix/suffix markers if edit sections were output as tokens. */ public $mEditSectionTokens = false; @@ -164,6 +165,7 @@ class ParserOutput extends CacheTime { public $mTimestamp; /** + * @deprecated since 1.31 Use getText() options. * @var bool $mTOCEnabled Whether TOC should be shown, can't override __NOTOC__. */ public $mTOCEnabled = true; @@ -250,9 +252,38 @@ class ParserOutput extends CacheTime { return $this->mText; } - public function getText() { + /** + * Get the output HTML + * + * @param array $options (since 1.31) Transformations to apply to the HTML + * - allowTOC: (bool) Show the TOC, assuming there were enough headings + * to generate one and `__NOTOC__` wasn't used. Default is true, + * but might be statefully overridden. + * - enableSectionEditLinks: (bool) Include section edit links, assuming + * section edit link tokens are present in the HTML. Default is true, + * but might be statefully overridden. + * @return string HTML + */ + public function getText( $options = [] ) { + // @todo Warn if !array_key_exists( 'allowTOC', $options ) && empty( $this->mTOCEnabled ) + + // @todo Warn if !array_key_exists( 'enableSectionEditLinks', $options ) + // && !$this->mEditSectionTokens + // Note that while $this->mEditSectionTokens defaults to false, + // ParserOptions->getEditSection() defaults to true and Parser copies + // that to us so true makes more sense as the stateless default. + + $options += [ + // empty() here because old cached versions might lack the field somehow. + // In that situation, the historical behavior (possibly buggy) is to remove the TOC. + 'allowTOC' => !empty( $this->mTOCEnabled ), + 'enableSectionEditLinks' => $this->mEditSectionTokens, + ]; $text = $this->mText; - if ( $this->mEditSectionTokens ) { + + Hooks::runWithoutAbort( 'ParserOutputPostCacheTransform', [ $this, &$text, &$options ] ); + + if ( $options['enableSectionEditLinks'] ) { $text = preg_replace_callback( self::EDITSECTION_REGEX, function ( $m ) { @@ -278,8 +309,7 @@ class ParserOutput extends CacheTime { $text = preg_replace( self::EDITSECTION_REGEX, '', $text ); } - // If you have an old cached version of this class - sorry, you can't disable the TOC - if ( isset( $this->mTOCEnabled ) && $this->mTOCEnabled ) { + if ( $options['allowTOC'] ) { $text = str_replace( [ Parser::TOC_START, Parser::TOC_END ], '', $text ); } else { $text = preg_replace( @@ -288,6 +318,7 @@ class ParserOutput extends CacheTime { $text ); } + return $text; } @@ -339,6 +370,9 @@ class ParserOutput extends CacheTime { return $this->mSections; } + /** + * @deprecated since 1.31 Use getText() options. + */ public function getEditSectionTokens() { return $this->mEditSectionTokens; } @@ -426,6 +460,9 @@ class ParserOutput extends CacheTime { return $this->mLimitReportJSData; } + /** + * @deprecated since 1.31 Use getText() options. + */ public function getTOCEnabled() { return $this->mTOCEnabled; } @@ -454,6 +491,9 @@ class ParserOutput extends CacheTime { return wfSetVar( $this->mSections, $toc ); } + /** + * @deprecated since 1.31 Use getText() options. + */ public function setEditSectionTokens( $t ) { return wfSetVar( $this->mEditSectionTokens, $t ); } @@ -470,6 +510,9 @@ class ParserOutput extends CacheTime { return wfSetVar( $this->mTimestamp, $timestamp ); } + /** + * @deprecated since 1.31 Use getText() options. + */ public function setTOCEnabled( $flag ) { return wfSetVar( $this->mTOCEnabled, $flag ); } diff --git a/tests/phpunit/includes/parser/ParserOutputTest.php b/tests/phpunit/includes/parser/ParserOutputTest.php index ec8f0d076d..441d60df7b 100644 --- a/tests/phpunit/includes/parser/ParserOutputTest.php +++ b/tests/phpunit/includes/parser/ParserOutputTest.php @@ -1,5 +1,7 @@ assertArrayNotHasKey( 'foo', $properties ); } + /** + * @covers ParserOutput::getText + * @dataProvider provideGetText + * @param array $options Options to getText() + * @param array $poState ParserOptions state fields to set + * @param string $text Parser text + * @param string $expect Expected output + */ + public function testGetText( $options, $poState, $text, $expect ) { + $this->setMwGlobals( [ + 'wgArticlePath' => '/wiki/$1', + 'wgScriptPath' => '/w', + 'wgScript' => '/w/index.php', + ] ); + + $po = new ParserOutput( $text ); + + // Emulate Parser + $po->setEditSectionTokens( true ); + + if ( $poState ) { + $wrap = TestingAccessWrapper::newFromObject( $po ); + foreach ( $poState as $key => $value ) { + $wrap->$key = $value; + } + } + + $actual = $po->getText( $options ); + $this->assertSame( $expect, $actual ); + } + + public static function provideGetText() { + // @codingStandardsIgnoreStart Generic.Files.LineLength + $text = <<Test document. +

+ + +

Section 1Section 1

+

One +

+

Section 2Section 2

+

Two +

+

Section 2.1Section 2.1

+

Two point one +

+

Section 3Section 3

+

Three +

+EOF; + + return [ + 'No stateless options, default state' => [ + [], [], $text, <<Test document. +

+ + +

Section 1[edit]

+

One +

+

Section 2[edit]

+

Two +

+

Section 2.1[edit]

+

Two point one +

+

Section 3[edit]

+

Three +

+EOF + ], + 'No stateless options, TOC statefully disabled' => [ + [], [ 'mTOCEnabled' => false ], $text, <<Test document. +

+ +

Section 1[edit]

+

One +

+

Section 2[edit]

+

Two +

+

Section 2.1[edit]

+

Two point one +

+

Section 3[edit]

+

Three +

+EOF + ], + 'No stateless options, section edits statefully disabled' => [ + [], [ 'mEditSectionTokens' => false ], $text, <<Test document. +

+ + +

Section 1

+

One +

+

Section 2

+

Two +

+

Section 2.1

+

Two point one +

+

Section 3

+

Three +

+EOF + ], + 'Stateless options override stateful settings' => [ + [ 'allowTOC' => true, 'enableSectionEditLinks' => true ], + [ 'mTOCEnabled' => false, 'mEditSectionTokens' => false ], + $text, <<Test document. +

+ + +

Section 1[edit]

+

One +

+

Section 2[edit]

+

Two +

+

Section 2.1[edit]

+

Two point one +

+

Section 3[edit]

+

Three +

+EOF + ], + 'Statelessly disable section edit links' => [ + [ 'enableSectionEditLinks' => false ], [], $text, <<Test document. +

+ + +

Section 1

+

One +

+

Section 2

+

Two +

+

Section 2.1

+

Two point one +

+

Section 3

+

Three +

+EOF + ], + 'Statelessly disable TOC' => [ + [ 'allowTOC' => false ], [], $text, <<Test document. +

+ +

Section 1[edit]

+

One +

+

Section 2[edit]

+

Two +

+

Section 2.1[edit]

+

Two point one +

+

Section 3[edit]

+

Three +

+EOF + ], + ]; + // @codingStandardsIgnoreEnd + } + } -- 2.20.1