From 1c9664d18a698f0bc28cd01de9460ebb07b900c4 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Bartosz=20Dziewo=C5=84ski?= Date: Sat, 19 May 2018 15:29:52 +0200 Subject: [PATCH] Parser: Refactor parsing of [[File:...|link=...]] syntax for reusability Change-Id: I91467297de4b7c532448a4c20b9a0dc8216c7200 --- includes/parser/Parser.php | 87 ++++++++++++++-------- tests/phpunit/includes/ExtraParserTest.php | 76 +++++++++++++++++++ 2 files changed, 131 insertions(+), 32 deletions(-) diff --git a/includes/parser/Parser.php b/includes/parser/Parser.php index 7d5a362b38..572dce0622 100644 --- a/includes/parser/Parser.php +++ b/includes/parser/Parser.php @@ -5143,24 +5143,18 @@ class Parser { break; case 'gallery-internal-link': $linkValue = strip_tags( $this->replaceLinkHoldersText( $match ) ); - $chars = self::EXT_LINK_URL_CLASS; - $addr = self::EXT_LINK_ADDR; - $prots = $this->mUrlProtocols; - // check to see if link matches an absolute url, if not then it must be a wiki link. if ( preg_match( '/^-{R|(.*)}-$/', $linkValue ) ) { // Result of LanguageConverter::markNoConversion // invoked on an external link. $linkValue = substr( $linkValue, 4, -2 ); } - if ( preg_match( "/^($prots)$addr$chars*$/u", $linkValue ) ) { - $link = $linkValue; - $this->mOutput->addExternalLink( $link ); - } else { - $localLinkTitle = Title::newFromText( $linkValue ); - if ( $localLinkTitle !== null ) { - $this->mOutput->addLink( $localLinkTitle ); - $link = $localLinkTitle->getLinkURL(); - } + list( $type, $target ) = $this->parseLinkParameter( $linkValue ); + if ( $type === 'link-url' ) { + $link = $target; + $this->mOutput->addExternalLink( $target ); + } elseif ( $type === 'link-title' ) { + $link = $target->getLinkURL(); + $this->mOutput->addLink( $target ); } break; default: @@ -5342,29 +5336,16 @@ class Parser { $value = $this->stripAltText( $value, $holders ); break; case 'link': - $chars = self::EXT_LINK_URL_CLASS; - $addr = self::EXT_LINK_ADDR; - $prots = $this->mUrlProtocols; - if ( $value === '' ) { - $paramName = 'no-link'; - $value = true; + list( $paramName, $value ) = $this->parseLinkParameter( $value ); + if ( $paramName ) { $validated = true; - } elseif ( preg_match( "/^((?i)$prots)/", $value ) ) { - if ( preg_match( "/^((?i)$prots)$addr$chars*$/u", $value, $m ) ) { - $paramName = 'link-url'; - $this->mOutput->addExternalLink( $value ); + if ( $paramName === 'no-link' ) { + $value = true; + } + if ( $paramName === 'link-url' ) { if ( $this->mOptions->getExternalLinkTarget() ) { $params[$type]['link-target'] = $this->mOptions->getExternalLinkTarget(); } - $validated = true; - } - } else { - $linkTitle = Title::newFromText( $value ); - if ( $linkTitle ) { - $paramName = 'link-title'; - $value = $linkTitle; - $this->mOutput->addLink( $linkTitle ); - $validated = true; } } break; @@ -5459,6 +5440,48 @@ class Parser { return $ret; } + /** + * Parse the value of 'link' parameter in image syntax (`[[File:Foo.jpg|link=]]`). + * + * Adds an entry to appropriate link tables. + * + * @since 1.32 + * @return array of `[ type, target ]`, where: + * - `type` is one of: + * - `null`: Given value is not a valid link target, use default + * - `'no-link'`: Given value is empty, do not generate a link + * - `'link-url'`: Given value is a valid external link + * - `'link-title'`: Given value is a valid internal link + * - `target` is: + * - When `type` is `null` or `'no-link'`: `false` + * - When `type` is `'link-url'`: URL string corresponding to given value + * - When `type` is `'link-title'`: Title object corresponding to given value + */ + public function parseLinkParameter( $value ) { + $chars = self::EXT_LINK_URL_CLASS; + $addr = self::EXT_LINK_ADDR; + $prots = $this->mUrlProtocols; + $type = null; + $target = false; + if ( $value === '' ) { + $type = 'no-link'; + } elseif ( preg_match( "/^((?i)$prots)/", $value ) ) { + if ( preg_match( "/^((?i)$prots)$addr$chars*$/u", $value, $m ) ) { + $this->mOutput->addExternalLink( $value ); + $type = 'link-url'; + $target = $value; + } + } else { + $linkTitle = Title::newFromText( $value ); + if ( $linkTitle ) { + $this->mOutput->addLink( $linkTitle ); + $type = 'link-title'; + $target = $linkTitle; + } + } + return [ $type, $target ]; + } + /** * @param string $caption * @param LinkHolderArray|bool $holders diff --git a/tests/phpunit/includes/ExtraParserTest.php b/tests/phpunit/includes/ExtraParserTest.php index 94de0880d3..7ce4d1efc6 100644 --- a/tests/phpunit/includes/ExtraParserTest.php +++ b/tests/phpunit/includes/ExtraParserTest.php @@ -215,4 +215,80 @@ class ExtraParserTest extends MediaWikiTestCase { $result = $parserOutput->getCategoryLinks(); $this->assertEmpty( $result ); } + + /** + * @covers Parser::parseLinkParameter + * @dataProvider provideParseLinkParameter + */ + public function testParseLinkParameter( $input, $expected, $expectedLinks, $desc ) { + $this->parser->startExternalParse( Title::newFromText( __FUNCTION__ ), + $this->options, Parser::OT_HTML ); + $output = $this->parser->parseLinkParameter( $input ); + + $this->assertEquals( $expected[0], $output[0], "$desc (type)" ); + + if ( $expected[0] === 'link-title' ) { + $this->assertTrue( $expected[1]->equals( $output[1] ), "$desc (target)" ); + } else { + $this->assertEquals( $expected[1], $output[1], "$desc (target)" ); + } + + foreach ( $expectedLinks as $func => $expected ) { + $output = $this->parser->getOutput()->$func(); + $this->assertEquals( $expected, $output, "$desc ($func)" ); + } + } + + public static function provideParseLinkParameter() { + return [ + [ + '', + [ 'no-link', false ], + [], + 'Return no link when requested', + ], + [ + 'https://example.com/', + [ 'link-url', 'https://example.com/' ], + [ 'getExternalLinks' => [ 'https://example.com/' => 1 ] ], + 'External link', + ], + [ + '//example.com/', + [ 'link-url', '//example.com/' ], + [ 'getExternalLinks' => [ '//example.com/' => 1 ] ], + 'External link', + ], + [ + 'Test', + [ 'link-title', Title::newFromText( 'Test' ) ], + [ 'getLinks' => [ 0 => [ 'Test' => 0 ] ] ], + 'Internal link', + ], + [ + 'mw:Test', + [ 'link-title', Title::newFromText( 'mw:Test' ) ], + [ 'getInterwikiLinks' => [ 'mw' => [ 'Test' => 1 ] ] ], + 'Internal link (interwiki)', + ], + [ + 'https://', + [ null, false ], + [], + 'Invalid link target', + ], + [ + '<>', + [ null, false ], + [], + 'Invalid link target', + ], + [ + ' ', + [ null, false ], + [], + 'Invalid link target', + ], + ]; + } } -- 2.20.1