--- /dev/null
+<?php
+namespace MediaWiki\Interwiki;
+
+/**
+ * InterwikiLookupAdapter on top of SiteLookup
+ *
+ * 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
+ *
+ * @since 1.28
+ * @ingroup InterwikiLookup
+ *
+ * @license GNU GPL v2+
+ */
+
+use Interwiki;
+use Site;
+use SiteLookup;
+use MediawikiSite;
+
+class InterwikiLookupAdapter implements InterwikiLookup {
+
+ /**
+ * @var SiteLookup
+ */
+ private $siteLookup;
+
+ /**
+ * @var Interwiki[]|null associative array mapping interwiki prefixes to Interwiki objects
+ */
+ private $interwikiMap;
+
+ function __construct(
+ SiteLookup $siteLookup,
+ array $interwikiMap = null
+ ) {
+ $this->siteLookup = $siteLookup;
+ $this->interwikiMap = $interwikiMap;
+ }
+
+ /**
+ * See InterwikiLookup::isValidInterwiki
+ * It loads the whole interwiki map.
+ *
+ * @param string $prefix Interwiki prefix to use
+ * @return bool Whether it exists
+ */
+ public function isValidInterwiki( $prefix ) {
+
+ return array_key_exists( $prefix, $this->getInterwikiMap() );
+ }
+
+ /**
+ * See InterwikiLookup::fetch
+ * It loads the whole interwiki map.
+ *
+ * @param string $prefix Interwiki prefix to use
+ * @return Interwiki|null|bool
+ */
+ public function fetch( $prefix ) {
+ if ( $prefix == '' ) {
+ return null;
+ }
+
+ if ( !$this->isValidInterwiki( $prefix ) ) {
+ return false;
+ }
+
+ return $this->interwikiMap[$prefix];
+ }
+
+ /**
+ * See InterwikiLookup::getAllPrefixes
+ *
+ * @param string|null $local If set, limits output to local/non-local interwikis
+ * @return string[] List of prefixes
+ */
+ public function getAllPrefixes( $local = null ) {
+ if ( $local === null ) {
+ return array_keys( $this->getInterwikiMap() );
+ }
+ $res = [];
+ foreach ( $this->getInterwikiMap() as $interwikiId => $interwiki ) {
+ if ( $interwiki->isLocal() === $local ) {
+ $res[] = $interwikiId;
+ }
+ }
+ return $res;
+ }
+
+ /**
+ * See InterwikiLookup::invalidateCache
+ *
+ * @param string $prefix
+ */
+ public function invalidateCache( $prefix ) {
+ if ( !isset( $this->interwikiMap[$prefix] ) ) {
+ return;
+ }
+ $globalId = $this->interwikiMap[$prefix]->getWikiID();
+ unset( $this->interwikiMap[$prefix] );
+
+ // Reload the interwiki
+ $site = $this->siteLookup->getSites()->getSite( $globalId );
+ $interwikis = $this->getSiteInterwikis( $site );
+ $this->interwikiMap = array_merge( $this->interwikiMap, [ $interwikis[$prefix] ] );
+ }
+
+ /**
+ * Load interwiki map to use as cache
+ */
+ private function loadInterwikiMap() {
+ $interwikiMap = [];
+ $siteList = $this->siteLookup->getSites();
+ foreach ( $siteList as $site ) {
+ $interwikis = $this->getSiteInterwikis( $site );
+ $interwikiMap = array_merge( $interwikiMap, $interwikis );
+ }
+ $this->interwikiMap = $interwikiMap;
+ }
+
+ /**
+ * Get interwikiMap attribute, load if needed.
+ *
+ * @return Interwiki[]
+ */
+ private function getInterwikiMap() {
+ if ( $this->interwikiMap === null ) {
+ $this->loadInterwikiMap();
+ }
+ return $this->interwikiMap;
+ }
+
+ /**
+ * Load interwikis for the given site
+ *
+ * @param Site $site
+ * @return Interwiki[]
+ */
+ private function getSiteInterwikis( Site $site ) {
+ $interwikis = [];
+ foreach ( $site->getInterwikiIds() as $interwiki ) {
+ $url = $site->getPageUrl();
+ if ( $site instanceof MediawikiSite ) {
+ $path = $site->getFileUrl( 'api.php' );
+ } else {
+ $path = '';
+ }
+ $local = $site->getSource() === 'local';
+ // TODO: How to adapt trans?
+ $interwikis[$interwiki] = new Interwiki(
+ $interwiki,
+ $url,
+ $path,
+ $site->getGlobalId(),
+ $local
+ );
+ }
+ return $interwikis;
+ }
+}
--- /dev/null
+<?php
+
+/**
+ * @covers MediaWiki\Interwiki\InterwikiLookupAdapter
+ *
+ * @group MediaWiki
+ * @group Interwiki
+ */
+use MediaWiki\Interwiki\InterwikiLookupAdapter;
+
+class InterwikiLookupAdapterTest extends MediaWikiTestCase {
+
+ /**
+ * @var InterwikiLookupAdapter
+ */
+ private $interwikiLookup;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->interwikiLookup = new InterwikiLookupAdapter(
+ $this->getSiteLookup( $this->getSites() )
+ );
+ }
+
+ public function testIsValidInterwiki() {
+ $this->assertTrue(
+ $this->interwikiLookup->isValidInterwiki( 'enwt' ),
+ 'enwt known prefix is valid'
+ );
+ $this->assertTrue(
+ $this->interwikiLookup->isValidInterwiki( 'foo' ),
+ 'foo site known prefix is valid'
+ );
+ $this->assertFalse(
+ $this->interwikiLookup->isValidInterwiki( 'xyz' ),
+ 'unknown prefix is not valid'
+ );
+ }
+
+ public function testFetch() {
+
+ $interwiki = $this->interwikiLookup->fetch( '' );
+ $this->assertNull( $interwiki );
+
+ $interwiki = $this->interwikiLookup->fetch( 'xyz' );
+ $this->assertFalse( $interwiki );
+
+ $interwiki = $this->interwikiLookup->fetch( 'foo' );
+ $this->assertInstanceOf( Interwiki::class, $interwiki );
+ $this->assertSame( 'foobar', $interwiki->getWikiID() );
+
+ $interwiki = $this->interwikiLookup->fetch( 'enwt' );
+ $this->assertInstanceOf( Interwiki::class, $interwiki );
+
+ $this->assertSame( 'https://en.wiktionary.org/wiki/$1', $interwiki->getURL(), 'getURL' );
+ $this->assertSame( 'https://en.wiktionary.org/w/api.php', $interwiki->getAPI(), 'getAPI' );
+ $this->assertSame( 'enwiktionary', $interwiki->getWikiID(), 'getWikiID' );
+ $this->assertTrue( $interwiki->isLocal(), 'isLocal' );
+ }
+
+ public function testGetAllPrefixes() {
+ $this->assertEquals(
+ [ 'foo', 'enwt' ],
+ $this->interwikiLookup->getAllPrefixes(),
+ 'getAllPrefixes()'
+ );
+
+ $this->assertEquals(
+ [ 'foo' ],
+ $this->interwikiLookup->getAllPrefixes( false ),
+ 'get external prefixes'
+ );
+
+ $this->assertEquals(
+ [ 'enwt' ],
+ $this->interwikiLookup->getAllPrefixes( true ),
+ 'get local prefixes'
+ );
+ }
+
+ private function getSiteLookup( SiteList $sites ) {
+ $siteLookup = $this->getMockBuilder( SiteLookup::class )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $siteLookup->expects( $this->any() )
+ ->method( 'getSites' )
+ ->will( $this->returnValue( $sites ) );
+
+ return $siteLookup;
+ }
+
+ private function getSites() {
+ $sites = [];
+
+ $site = new Site();
+ $site->setGlobalId( 'foobar' );
+ $site->addInterwikiId( 'foo' );
+ $site->setSource( 'external' );
+ $sites[] = $site;
+
+ $site = new MediaWikiSite();
+ $site->setGlobalId( 'enwiktionary' );
+ $site->setGroup( 'wiktionary' );
+ $site->setLanguageCode( 'en' );
+ $site->addNavigationId( 'enwiktionary' );
+ $site->addInterwikiId( 'enwt' );
+ $site->setSource( 'local' );
+ $site->setPath( MediaWikiSite::PATH_PAGE, "https://en.wiktionary.org/wiki/$1" );
+ $site->setPath( MediaWikiSite::PATH_FILE, "https://en.wiktionary.org/w/$1" );
+ $sites[] = $site;
+
+ return new SiteList( $sites );
+ }
+
+}