Parser: Refactor parsing of [[File:...|link=...]] syntax for reusability
authorBartosz Dziewoński <matma.rex@gmail.com>
Sat, 19 May 2018 13:29:52 +0000 (15:29 +0200)
committerBartosz Dziewoński <matma.rex@gmail.com>
Wed, 26 Sep 2018 11:36:32 +0000 (13:36 +0200)
Change-Id: I91467297de4b7c532448a4c20b9a0dc8216c7200

includes/parser/Parser.php
tests/phpunit/includes/ExtraParserTest.php

index 7d5a362..572dce0 100644 (file)
@@ -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=<value>]]`).
+        *
+        * 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
index 94de088..7ce4d1e 100644 (file)
@@ -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',
+                       ],
+               ];
+       }
 }