Merge "Normalize input to TitleParser::parseTitle()"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Mon, 15 Apr 2019 22:14:59 +0000 (22:14 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Mon, 15 Apr 2019 22:14:59 +0000 (22:14 +0000)
1  2 
includes/title/MediaWikiTitleCodec.php
tests/phpunit/includes/title/MediaWikiTitleCodecTest.php

@@@ -56,35 -56,20 +56,35 @@@ class MediaWikiTitleCodec implements Ti
         */
        protected $interwikiLookup;
  
 +      /**
 +       * @var NamespaceInfo
 +       */
 +      protected $nsInfo;
 +
        /**
         * @param Language $language The language object to use for localizing namespace names.
         * @param GenderCache $genderCache The gender cache for generating gendered namespace names
         * @param string[]|string $localInterwikis
         * @param InterwikiLookup|null $interwikiLookup
 +       * @param NamespaceInfo|null $nsInfo
         */
        public function __construct( Language $language, GenderCache $genderCache,
 -              $localInterwikis = [], $interwikiLookup = null
 +              $localInterwikis = [], InterwikiLookup $interwikiLookup = null,
 +              NamespaceInfo $nsInfo = null
        ) {
 +              if ( !$interwikiLookup ) {
 +                      wfDeprecated( __METHOD__ . ' with no InterwikiLookup argument', '1.34' );
 +                      $interwikiLookup = MediaWikiServices::getInstance()->getInterwikiLookup();
 +              }
 +              if ( !$nsInfo ) {
 +                      wfDeprecated( __METHOD__ . ' with no NamespaceInfo argument', '1.34' );
 +                      $nsInfo = MediaWikiServices::getInstance()->getNamespaceInfo();
 +              }
                $this->language = $language;
                $this->genderCache = $genderCache;
                $this->localInterwikis = (array)$localInterwikis;
 -              $this->interwikiLookup = $interwikiLookup ?:
 -                      MediaWikiServices::getInstance()->getInterwikiLookup();
 +              $this->interwikiLookup = $interwikiLookup;
 +              $this->nsInfo = $nsInfo;
        }
  
        /**
@@@ -98,7 -83,7 +98,7 @@@
         */
        public function getNamespaceName( $namespace, $text ) {
                if ( $this->language->needsGenderDistinction() &&
 -                      MWNamespace::hasGenderDistinction( $namespace )
 +                      $this->nsInfo->hasGenderDistinction( $namespace )
                ) {
                        // NOTE: we are assuming here that the title text is a user name!
                        $gender = $this->genderCache->getGenderOf( $text, __METHOD__ );
         * @return TitleValue
         */
        public function parseTitle( $text, $defaultNamespace = NS_MAIN ) {
+               // Convert things like &eacute; &#257; or &#x3017; into normalized (T16952) text
+               $filteredText = Sanitizer::decodeCharReferencesAndNormalize( $text );
                // NOTE: this is an ugly cludge that allows this class to share the
                // code for parsing with the old Title class. The parser code should
                // be refactored to avoid this.
-               $parts = $this->splitTitleString( $text, $defaultNamespace );
+               $parts = $this->splitTitleString( $filteredText, $defaultNamespace );
  
                // Relative fragment links are not supported by TitleValue
                if ( $parts['dbkey'] === '' ) {
@@@ -19,8 -19,6 +19,8 @@@
   * @author Daniel Kinzler
   */
  
 +use MediaWiki\Interwiki\InterwikiLookup;
 +
  /**
   * @covers MediaWikiTitleCodec
   *
@@@ -39,6 -37,21 +39,6 @@@ class MediaWikiTitleCodecTest extends M
                        'wgMetaNamespace' => 'Project',
                        'wgLocalInterwikis' => [ 'localtestiw' ],
                        'wgCapitalLinks' => true,
 -
 -                      // NOTE: this is why global state is evil.
 -                      // TODO: refactor access to the interwiki codes so it can be injected.
 -                      'wgHooks' => [
 -                              'InterwikiLoadPrefix' => [
 -                                      function ( $prefix, &$data ) {
 -                                              if ( $prefix === 'localtestiw' ) {
 -                                                      $data = [ 'iw_url' => 'localtestiw' ];
 -                                              } elseif ( $prefix === 'remotetestiw' ) {
 -                                                      $data = [ 'iw_url' => 'remotetestiw' ];
 -                                              }
 -                                              return false;
 -                                      }
 -                              ]
 -                      ]
                ] );
                $this->setUserLang( 'en' );
                $this->setContentLang( 'en' );
                return $genderCache;
        }
  
 +      /**
 +       * Returns a mock InterwikiLookup that only has an isValidInterwiki() method, which recognizes
 +       * 'localtestiw' and 'remotetestiw'. All other methods throw.
 +       *
 +       * @return InterwikiLookup
 +       */
 +      private function getInterwikiLookup() : InterwikiLookup {
 +              $iwLookup = $this->createMock( InterwikiLookup::class );
 +
 +              $iwLookup->expects( $this->any() )
 +                      ->method( 'isValidInterwiki' )
 +                      ->will( $this->returnCallback( function ( $prefix ) {
 +                              return $prefix === 'localtestiw' || $prefix === 'remotetestiw';
 +                      } ) );
 +
 +              $iwLookup->expects( $this->never() )
 +                      ->method( $this->callback( function ( $name ) {
 +                              return $name !== 'isValidInterwiki';
 +                      } ) );
 +
 +              return $iwLookup;
 +      }
 +
 +      /**
 +       * Returns a mock NamespaceInfo that has only a hasGenderDistinction() method, which assumes
 +       * only NS_USER and NS_USER_TALK have a gender distinction. All other methods throw.
 +       *
 +       * @return NamespaceInfo
 +       */
 +      private function getNamespaceInfo() : NamespaceInfo {
 +              $nsInfo = $this->createMock( NamespaceInfo::class );
 +
 +              $nsInfo->expects( $this->any() )
 +                      ->method( 'hasGenderDistinction' )
 +                      ->will( $this->returnCallback( function ( $ns ) {
 +                              return $ns === NS_USER || $ns === NS_USER_TALK;
 +                      } ) );
 +
 +              $nsInfo->expects( $this->never() )
 +                      ->method( $this->callback( function ( $name ) {
 +                              return $name !== 'hasGenderDistinction';
 +                      } ) );
 +
 +              return $nsInfo;
 +      }
 +
        protected function makeCodec( $lang ) {
 -              $gender = $this->getGenderCache();
 -              $lang = Language::factory( $lang );
 -              // language object can came from cache, which does not respect test settings
 -              $lang->resetNamespaces();
 -              return new MediaWikiTitleCodec( $lang, $gender );
 +              return new MediaWikiTitleCodec(
 +                      Language::factory( $lang ),
 +                      $this->getGenderCache(),
 +                      [],
 +                      $this->getInterwikiLookup(),
 +                      $this->getNamespaceInfo()
 +              );
        }
  
        public static function provideFormat() {
                                new TitleValue( NS_CATEGORY,
                                        'X' . str_repeat( 'x', 247 ) ) ],
                        [ str_repeat( 'x', 252 ), NS_MAIN, 'en',
-                               'X' . str_repeat( 'x', 251 ) ]
+                               'X' . str_repeat( 'x', 251 ) ],
+                       // Test decoding and normalization
+                       [ '&quot;n&#x303;&#34;', NS_MAIN, 'en', new TitleValue( NS_MAIN, '"ñ"' ) ],
                ];
        }