Design improvements to sites code

Change-Id: I08ffa6a97093abbe85169f664b97498c5f39bf8e
This commit is contained in:
jeroendedauw 2013-01-18 10:34:14 +01:00
parent e45d154faa
commit a00337c3f8
17 changed files with 1230 additions and 1578 deletions

View file

@ -883,11 +883,12 @@ $wgAutoloadLocalClasses = array(
# includes/site
'MediaWikiSite' => 'includes/site/MediaWikiSite.php',
'Site' => 'includes/site/Site.php',
'SiteArray' => 'includes/site/SiteArray.php',
'SiteObject' => 'includes/site/Site.php',
'SiteArray' => 'includes/site/SiteList.php',
'SiteList' => 'includes/site/SiteList.php',
'SiteObject' => 'includes/site/SiteObject.php',
'Sites' => 'includes/site/Sites.php',
'SitesTable' => 'includes/site/SitesTable.php',
'SiteSQLStore' => 'includes/site/SiteSQLStore.php',
'Sites' => 'includes/site/SiteSQLStore.php',
'SiteStore' => 'includes/site/SiteStore.php',
# includes/specials
'ActiveUsersPager' => 'includes/specials/SpecialActiveusers.php',

View file

@ -32,23 +32,34 @@
*
* @ingroup Site
*/
class MediaWikiSite extends SiteObject {
class MediaWikiSite extends Site {
const PATH_FILE = 'file_path';
const PATH_PAGE = 'page_path';
/**
* @since 1.21
* @deprecated Just use the constructor or the factory Site::newForType
*
* @param integer $globalId
*
* @return MediaWikiSite
*/
public static function newFromGlobalId( $globalId ) {
return SitesTable::singleton()->newRow( array(
'type' => Site::TYPE_MEDIAWIKI,
'global_key' => $globalId,
), true );
$site = new static();
$site->setGlobalId( $globalId );
return $site;
}
/**
* Constructor.
*
* @since 1.21
*
* @param string $type
*/
public function __construct( $type = self::TYPE_MEDIAWIKI ) {
parent::__construct( $type );
}
/**
@ -167,7 +178,7 @@ class MediaWikiSite extends SiteObject {
* @param array $externalData A reply from the API on a external server.
* @param string $pageTitle Identifies the page at the external site, needing normalization.
*
* @return array|false a 'page' structure representing the page identified by $pageTitle.
* @return array|boolean a 'page' structure representing the page identified by $pageTitle.
*/
private static function extractPageRecord( $externalData, $pageTitle ) {
// If there is a special case with only one returned page
@ -290,16 +301,16 @@ class MediaWikiSite extends SiteObject {
}
/**
* @see Site::getPagePath
* @see Site::getPageUrl
*
* This implementation returns a URL constructed using the path returned by getLinkPath().
* In addition to the default behaviour implemented by SiteObject::getPageUrl(), this
* In addition to the default behaviour implemented by Site::getPageUrl(), this
* method converts the $pageName to DBKey-format by replacing spaces with underscores
* before using it in the URL.
*
* @since 1.21
*
* @param $pagename string: Page name (default: false)
* @param string|boolean $pageName Page name or false (default: false)
*
* @return string
*/
@ -325,7 +336,7 @@ class MediaWikiSite extends SiteObject {
*
* @since 1.21
*
* @param string|false $path
* @param string|boolean $path
*
* @return string
*/

View file

@ -1,7 +1,7 @@
<?php
/**
* Interface for site objects.
* Represents a single site.
*
* 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
@ -26,7 +26,7 @@
* @license GNU GPL v2+
* @author Jeroen De Dauw < jeroendedauw@gmail.com >
*/
interface Site {
class Site {
const TYPE_UNKNOWN = 'unknown';
const TYPE_MEDIAWIKI = 'mediawiki';
@ -38,23 +38,119 @@ interface Site {
const SOURCE_LOCAL = 'local';
const PATH_LINK = 'link';
/**
* @since 1.21
*
* @var string|null
*/
protected $globalId = null;
/**
* @since 1.21
*
* @var string
*/
protected $type = self::TYPE_UNKNOWN;
/**
* @since 1.21
*
* @var string
*/
protected $group = self::GROUP_NONE;
/**
* @since 1.21
*
* @var string
*/
protected $source = self::SOURCE_LOCAL;
/**
* @since 1.21
*
* @var string|null
*/
protected $languageCode = null;
/**
* Holds the local ids for this site.
* local id type => [ ids for this type (strings) ]
*
* @since 1.21
*
* @var array[]
*/
protected $localIds = array();
/**
* @since 1.21
*
* @var array
*/
protected $extraData = array();
/**
* @since 1.21
*
* @var array
*/
protected $extraConfig = array();
/**
* @since 1.21
*
* @var bool
*/
protected $forward = false;
/**
* @since 1.21
*
* @var int|null
*/
protected $internalId = null;
/**
* Constructor.
*
* @since 1.21
*
* @param string $type
*/
public function __construct( $type = self::TYPE_UNKNOWN ) {
$this->type = $type;
}
/**
* Returns the global site identifier (ie enwiktionary).
*
* @since 1.21
*
* @return string
* @return string|null
*/
public function getGlobalId();
public function getGlobalId() {
return $this->globalId;
}
/**
* Sets the global site identifier (ie enwiktionary).
*
* @since 1.21
*
* @param string $globalId
* @param string|null $globalId
*
* @throws MWException
*/
public function setGlobalId( $globalId );
public function setGlobalId( $globalId ) {
if ( $globalId !== null && !is_string( $globalId ) ) {
throw new MWException( '$globalId needs to be string or null' );
}
$this->globalId = $globalId;
}
/**
* Returns the type of the site (ie mediawiki).
@ -63,17 +159,9 @@ interface Site {
*
* @return string
*/
public function getType();
/**
* Sets the type of the site (ie mediawiki).
* TODO: remove, we cannot change this after instantiation
*
* @since 1.21
*
* @param string $type
*/
public function setType( $type );
public function getType() {
return $this->type;
}
/**
* Gets the type of the site (ie wikipedia).
@ -82,7 +170,9 @@ interface Site {
*
* @return string
*/
public function getGroup();
public function getGroup() {
return $this->group;
}
/**
* Sets the type of the site (ie wikipedia).
@ -90,8 +180,16 @@ interface Site {
* @since 1.21
*
* @param string $group
*
* @throws MWException
*/
public function setGroup( $group );
public function setGroup( $group ) {
if ( !is_string( $group ) ) {
throw new MWException( '$group needs to be a string' );
}
$this->group = $group;
}
/**
* Returns the source of the site data (ie 'local', 'wikidata', 'my-magical-repo').
@ -100,7 +198,9 @@ interface Site {
*
* @return string
*/
public function getSource();
public function getSource() {
return $this->source;
}
/**
* Sets the source of the site data (ie 'local', 'wikidata', 'my-magical-repo').
@ -108,18 +208,46 @@ interface Site {
* @since 1.21
*
* @param string $source
*
* @throws MWException
*/
public function setSource( $source );
public function setSource( $source ) {
if ( !is_string( $source ) ) {
throw new MWException( '$source needs to be a string' );
}
$this->source = $source;
}
/**
* Returns the protocol of the site, ie 'http://', 'irc://', '//'
* Or false if it's not known.
* Gets if site.tld/path/key:pageTitle should forward users to the page on
* the actual site, where "key" is the local identifier.
*
* @since 1.21
*
* @return string|false
* @return boolean
*/
public function getProtocol();
public function shouldForward() {
return $this->forward;
}
/**
* Sets if site.tld/path/key:pageTitle should forward users to the page on
* the actual site, where "key" is the local identifier.
*
* @since 1.21
*
* @param boolean $shouldForward
*
* @throws MWException
*/
public function setForward( $shouldForward ) {
if ( !is_bool( $shouldForward ) ) {
throw new MWException( '$shouldForward needs to be a boolean' );
}
$this->forward = $shouldForward;
}
/**
* Returns the domain of the site, ie en.wikipedia.org
@ -127,147 +255,48 @@ interface Site {
*
* @since 1.21
*
* @return string|false
* @return string|null
*/
public function getDomain();
public function getDomain() {
$path = $this->getLinkPath();
if ( $path === null ) {
return null;
}
return parse_url( $path, PHP_URL_HOST );
}
/**
* Returns the full URL for the given page on the site.
* Or false if the needed information is not known.
*
* This generated URL is usually based upon the path returned by getLinkPath(),
* but this is not a requirement.
*
* @since 1.21
* @see Site::getLinkPath()
*
* @param bool|String $page
*
* @return string|false
*/
public function getPageUrl( $page = false );
/**
* Returns language code of the sites primary language.
* Or false if it's not known.
* Returns the protocol of the site.
*
* @since 1.21
*
* @return string|false
* @throws MWException
* @return string
*/
public function getLanguageCode();
public function getProtocol() {
$path = $this->getLinkPath();
/**
* Sets language code of the sites primary language.
*
* @since 1.21
*
* @param string $languageCode
*/
public function setLanguageCode( $languageCode );
if ( $path === null ) {
return '';
}
/**
* Returns the normalized, canonical form of the given page name.
* How normalization is performed or what the properties of a normalized name are depends on the site.
* The general contract of this method is that the normalized form shall refer to the same content
* as the original form, and any other page name referring to the same content will have the same normalized form.
*
* Note that this method may call out to the target site to perform the normalization, so it may be slow
* and fail due to IO errors.
*
* @since 1.21
*
* @param string $pageName
*
* @return string the normalized page name
*/
public function normalizePageName( $pageName );
$protocol = parse_url( $path, PHP_URL_SCHEME );
/**
* Returns the interwiki link identifiers that can be used for this site.
*
* @since 1.21
*
* @return array of string
*/
public function getInterwikiIds();
// Malformed URL
if ( $protocol === false ) {
throw new MWException( "failed to parse URL '$path'" );
}
/**
* Returns the equivalent link identifiers that can be used to make
* the site show up in interfaces such as the "language links" section.
*
* @since 1.21
*
* @return array of string
*/
public function getNavigationIds();
// No schema
if ( $protocol === null ) {
// Used for protocol relative URLs
$protocol = '';
}
/**
* Adds an local identifier to the site.
*
* @since 1.21
*
* @param string $type The type of the identifier, element of the Site::ID_ enum
* @param string $identifier
*/
public function addLocalId( $type, $identifier );
/**
* Adds an interwiki id to the site.
*
* @since 1.21
*
* @param string $identifier
*/
public function addInterwikiId( $identifier );
/**
* Adds a navigation id to the site.
*
* @since 1.21
*
* @param string $identifier
*/
public function addNavigationId( $identifier );
/**
* Saves the site.
*
* @since 1.21
*
* @param string|null $functionName
*/
public function save( $functionName = null );
/**
* Returns the internal ID of the site.
*
* @since 1.21
*
* @return integer
*/
public function getInternalId();
/**
* Sets the provided url as path of the specified type.
*
* @since 1.21
*
* @param string $pathType
* @param string $fullUrl
*/
public function setPath( $pathType, $fullUrl );
/**
* Returns the path of the provided type or false if there is no such path.
*
* @since 1.21
*
* @param string $pathType
*
* @return string|false
*/
public function getPath( $pathType );
return $protocol;
}
/**
* Sets the path used to construct links with.
@ -276,23 +305,295 @@ interface Site {
* @param string $fullUrl
*
* @since 1.21
*
* @throws MWException
*/
public function setLinkPath( $fullUrl );
public function setLinkPath( $fullUrl ) {
$type = $this->getLinkPathType();
if ( $type === null ) {
throw new MWException( "This Site does not support link paths." );
}
$this->setPath( $type, $fullUrl );
}
/**
* Returns the path used to construct links with or false if there is no such path.
*
* Shall be equivalent to getPath( getLinkPathType() ).
*
* @return string|false
* @return string|null
*/
public function getLinkPath();
public function getLinkPath() {
$type = $this->getLinkPathType();
return $type === null ? null: $this->getPath( $type );
}
/**
* Returns the path type used to construct links with.
* Returns the main path type, that is the type of the path that should generally be used to construct links
* to the target site.
*
* @return string|false
* This default implementation returns Site::PATH_LINK as the default path type. Subclasses can override this
* to define a different default path type, or return false to disable site links.
*
* @since 1.21
*
* @return string|null
*/
public function getLinkPathType();
public function getLinkPathType() {
return self::PATH_LINK;
}
/**
* Returns the full URL for the given page on the site.
* Or false if the needed information is not known.
*
* This generated URL is usually based upon the path returned by getLinkPath(),
* but this is not a requirement.
*
* This implementation returns a URL constructed using the path returned by getLinkPath().
*
* @since 1.21
*
* @param bool|String $pageName
*
* @return string|boolean false
*/
public function getPageUrl( $pageName = false ) {
$url = $this->getLinkPath();
if ( $url === false ) {
return false;
}
if ( $pageName !== false ) {
$url = str_replace( '$1', rawurlencode( $pageName ), $url ) ;
}
return $url;
}
/**
* Returns $pageName without changes.
* Subclasses may override this to apply some kind of normalization.
*
* @see Site::normalizePageName
*
* @since 1.21
*
* @param string $pageName
*
* @return string
*/
public function normalizePageName( $pageName ) {
return $pageName;
}
/**
* Returns the type specific fields.
*
* @since 1.21
*
* @return array
*/
public function getExtraData() {
return $this->extraData;
}
/**
* Sets the type specific fields.
*
* @since 1.21
*
* @param array $extraData
*/
public function setExtraData( array $extraData ) {
$this->extraData = $extraData;
}
/**
* Returns the type specific config.
*
* @since 1.21
*
* @return array
*/
public function getExtraConfig() {
return $this->extraConfig;
}
/**
* Sets the type specific config.
*
* @since 1.21
*
* @param array $extraConfig
*/
public function setExtraConfig( array $extraConfig ) {
$this->extraConfig = $extraConfig;
}
/**
* Returns language code of the sites primary language.
* Or null if it's not known.
*
* @since 1.21
*
* @return string|null
*/
public function getLanguageCode() {
return $this->languageCode;
}
/**
* Sets language code of the sites primary language.
*
* @since 1.21
*
* @param string $languageCode
*/
public function setLanguageCode( $languageCode ) {
$this->languageCode = $languageCode;
}
/**
* Returns the set internal identifier for the site.
*
* @since 1.21
*
* @return string|null
*/
public function getInternalId() {
return $this->internalId;
}
/**
* Sets the internal identifier for the site.
* This typically is a primary key in a db table.
*
* @since 1.21
*
* @param int|null $internalId
*/
public function setInternalId( $internalId = null ) {
$this->internalId = $internalId;
}
/**
* Adds a local identifier.
*
* @since 1.21
*
* @param string $type
* @param string $identifier
*/
public function addLocalId( $type, $identifier ) {
if ( $this->localIds === false ) {
$this->localIds = array();
}
if ( !array_key_exists( $type, $this->localIds ) ) {
$this->localIds[$type] = array();
}
if ( !in_array( $identifier, $this->localIds[$type] ) ) {
$this->localIds[$type][] = $identifier;
}
}
/**
* Adds an interwiki id to the site.
*
* @since 1.21
*
* @param string $identifier
*/
public function addInterwikiId( $identifier ) {
$this->addLocalId( self::ID_INTERWIKI, $identifier );
}
/**
* Adds a navigation id to the site.
*
* @since 1.21
*
* @param string $identifier
*/
public function addNavigationId( $identifier ) {
$this->addLocalId( self::ID_EQUIVALENT, $identifier );
}
/**
* Returns the interwiki link identifiers that can be used for this site.
*
* @since 1.21
*
* @return string[]
*/
public function getInterwikiIds() {
return array_key_exists( self::ID_INTERWIKI, $this->localIds ) ? $this->localIds[self::ID_INTERWIKI] : array();
}
/**
* Returns the equivalent link identifiers that can be used to make
* the site show up in interfaces such as the "language links" section.
*
* @since 1.21
*
* @return string[]
*/
public function getNavigationIds() {
return array_key_exists( self::ID_EQUIVALENT, $this->localIds ) ? $this->localIds[self::ID_EQUIVALENT] : array();
}
/**
* Returns all local ids
*
* @since 1.21
*
* @return array[]
*/
public function getLocalIds() {
return $this->localIds;
}
/**
* Sets the path used to construct links with.
* Shall be equivalent to setPath( getLinkPathType(), $fullUrl ).
*
* @since 1.21
*
* @param string $pathType
* @param string $fullUrl
*
* @throws MWException
*/
public function setPath( $pathType, $fullUrl ) {
if ( !is_string( $fullUrl ) ) {
throw new MWException( '$fullUrl needs to be a string' );
}
if ( !array_key_exists( 'paths', $this->extraData ) ) {
$this->extraData['paths'] = array();
}
$this->extraData['paths'][$pathType] = $fullUrl;
}
/**
* Returns the path of the provided type or false if there is no such path.
*
* @since 1.21
*
* @param string $pathType
*
* @return string|null
*/
public function getPath( $pathType ) {
$paths = $this->getAllPaths();
return array_key_exists( $pathType, $paths ) ? $paths[$pathType] : null;
}
/**
* Returns the paths as associative array.
@ -300,9 +601,11 @@ interface Site {
*
* @since 1.21
*
* @return array of string
* @return string[]
*/
public function getAllPaths();
public function getAllPaths() {
return array_key_exists( 'paths', $this->extraData ) ? $this->extraData['paths'] : array();
}
/**
* Removes the path of the provided type if it's set.
@ -311,6 +614,34 @@ interface Site {
*
* @param string $pathType
*/
public function removePath( $pathType );
public function removePath( $pathType ) {
if ( array_key_exists( 'paths', $this->extraData ) ) {
unset( $this->extraData['paths'][$pathType] );
}
}
}
// TODO: config
/**
* @since 1.21
*
* @param string $siteType
*
* @return Site
*/
public static function newForType( $siteType ) {
global $wgSiteTypes;
if ( array_key_exists( $siteType, $wgSiteTypes ) ) {
return new $wgSiteTypes[$siteType]();
}
return new Site();
}
}
/**
* @deprecated
*/
class SiteObject extends Site {}

View file

@ -1,260 +0,0 @@
<?php
/**
* Implementation of SiteList using GenericArrayObject.
*
* 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
*
* @since 1.21
*
* @file
* @ingroup Site
*
* @license GNU GPL v2+
* @author Jeroen De Dauw < jeroendedauw@gmail.com >
*/
class SiteArray extends GenericArrayObject implements SiteList {
/**
* Update this version number when the SiteArray format
* changes in an incompatible way
*
* @since 1.21
*
* @var integer
*/
const CACHE_VERSION = 1;
/**
* Version number of the SiteArray format of the currently used object
*
* @since 1.21
*
* @var integer
*/
public $cacheVersion = self::CACHE_VERSION;
/**
* Internal site identifiers pointing to their sites offset value.
*
* @since 1.21
*
* @var array of integer
*/
protected $byInternalId = array();
/**
* Global site identifiers pointing to their sites offset value.
*
* @since 1.21
*
* @var array of string
*/
protected $byGlobalId = array();
/**
* @see GenericArrayObject::getObjectType
*
* @since 1.21
*
* @return string
*/
public function getObjectType() {
return 'Site';
}
/**
* @see GenericArrayObject::preSetElement
*
* @since 1.21
*
* @param int|string $index
* @param Site $site
*
* @return boolean
*/
protected function preSetElement( $index, $site ) {
if ( $this->hasSite( $site->getGlobalId() ) ) {
$this->removeSite( $site->getGlobalId() );
}
$this->byGlobalId[$site->getGlobalId()] = $index;
$this->byInternalId[$site->getInternalId()] = $index;
return true;
}
/**
* @see ArrayObject::offsetUnset()
*
* @since 1.21
*
* @param mixed $index
*/
public function offsetUnset( $index ) {
if ( $this->offsetExists( $index ) ) {
/**
* @var Site $site
*/
$site = $this->offsetGet( $index );
unset( $this->byGlobalId[$site->getGlobalId()] );
unset( $this->byInternalId[$site->getInternalId()] );
}
parent::offsetUnset( $index );
}
/**
* @see SiteList::getGlobalIdentifiers
*
* @since 1.21
*
* @return array
*/
public function getGlobalIdentifiers() {
return array_keys( $this->byGlobalId );
}
/**
* @see SiteList::hasSite
*
* @param string $globalSiteId
*
* @return boolean
*/
public function hasSite( $globalSiteId ) {
return array_key_exists( $globalSiteId, $this->byGlobalId );
}
/**
* @see SiteList::getSite
*
* @since 1.21
*
* @param string $globalSiteId
*
* @return Site
*/
public function getSite( $globalSiteId ) {
return $this->offsetGet( $this->byGlobalId[$globalSiteId] );
}
/**
* @see SiteList::removeSite
*
* @since 1.21
*
* @param string $globalSiteId
*/
public function removeSite( $globalSiteId ) {
$this->offsetUnset( $this->byGlobalId[$globalSiteId] );
}
/**
* @see SiteList::isEmpty
*
* @since 1.21
*
* @return boolean
*/
public function isEmpty() {
return $this->byGlobalId === array();
}
/**
* @see SiteList::hasInternalId
*
* @param integer $id
*
* @return boolean
*/
public function hasInternalId( $id ) {
return array_key_exists( $id, $this->byInternalId );
}
/**
* @see SiteList::getSiteByInternalId
*
* @since 1.21
*
* @param integer $id
*
* @return Site
*/
public function getSiteByInternalId( $id ) {
return $this->offsetGet( $this->byInternalId[$id] );
}
/**
* @see SiteList::removeSiteByInternalId
*
* @since 1.21
*
* @param integer $id
*/
public function removeSiteByInternalId( $id ) {
$this->offsetUnset( $this->byInternalId[$id] );
}
/**
* @see SiteList::setSite
*
* @since 1.21
*
* @param Site $site
*/
public function setSite( Site $site ) {
$this[] = $site;
}
/**
* @see GenericArrayObject::getSerializationData
*
* @since 1.21
*
* @return array
*/
protected function getSerializationData() {
return array_merge(
parent::getSerializationData(),
array(
'cacheVersion' => self::CACHE_VERSION,
'internalIds' => $this->byInternalId,
'globalIds' => $this->byGlobalId,
)
);
}
/**
* @see GenericArrayObject::unserialize
*
* @since 1.21
*
* @param string $serialization
*
* @return array
*/
public function unserialize( $serialization ) {
$serializationData = parent::unserialize( $serialization );
$this->cacheVersion = $serializationData['cacheVersion'];
$this->byInternalId = $serializationData['internalIds'];
$this->byGlobalId = $serializationData['globalIds'];
return $serializationData;
}
}

View file

@ -1,7 +1,7 @@
<?php
/**
* Interface for lists of Site objects.
* Collection of Site objects.
*
* 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
@ -26,7 +26,78 @@
* @license GNU GPL v2+
* @author Jeroen De Dauw < jeroendedauw@gmail.com >
*/
interface SiteList extends Countable, Traversable, Serializable, ArrayAccess {
class SiteList extends GenericArrayObject {
/**
* Internal site identifiers pointing to their sites offset value.
*
* @since 1.21
*
* @var array of integer
*/
protected $byInternalId = array();
/**
* Global site identifiers pointing to their sites offset value.
*
* @since 1.21
*
* @var array of string
*/
protected $byGlobalId = array();
/**
* @see GenericArrayObject::getObjectType
*
* @since 1.21
*
* @return string
*/
public function getObjectType() {
return 'Site';
}
/**
* @see GenericArrayObject::preSetElement
*
* @since 1.21
*
* @param int|string $index
* @param Site $site
*
* @return boolean
*/
protected function preSetElement( $index, $site ) {
if ( $this->hasSite( $site->getGlobalId() ) ) {
$this->removeSite( $site->getGlobalId() );
}
$this->byGlobalId[$site->getGlobalId()] = $index;
$this->byInternalId[$site->getInternalId()] = $index;
return true;
}
/**
* @see ArrayObject::offsetUnset()
*
* @since 1.21
*
* @param mixed $index
*/
public function offsetUnset( $index ) {
if ( $this->offsetExists( $index ) ) {
/**
* @var Site $site
*/
$site = $this->offsetGet( $index );
unset( $this->byGlobalId[$site->getGlobalId()] );
unset( $this->byInternalId[$site->getInternalId()] );
}
parent::offsetUnset( $index );
}
/**
* Returns all the global site identifiers.
@ -36,7 +107,9 @@ interface SiteList extends Countable, Traversable, Serializable, ArrayAccess {
*
* @return array
*/
public function getGlobalIdentifiers();
public function getGlobalIdentifiers() {
return array_keys( $this->byGlobalId );
}
/**
* Returns if the list contains the site with the provided global site identifier.
@ -45,7 +118,9 @@ interface SiteList extends Countable, Traversable, Serializable, ArrayAccess {
*
* @return boolean
*/
public function hasSite( $globalSiteId );
public function hasSite( $globalSiteId ) {
return array_key_exists( $globalSiteId, $this->byGlobalId );
}
/**
* Returns the Site with the provided global site identifier.
@ -57,7 +132,9 @@ interface SiteList extends Countable, Traversable, Serializable, ArrayAccess {
*
* @return Site
*/
public function getSite( $globalSiteId );
public function getSite( $globalSiteId ) {
return $this->offsetGet( $this->byGlobalId[$globalSiteId] );
}
/**
* Removes the site with the specified global site identifier.
@ -67,7 +144,20 @@ interface SiteList extends Countable, Traversable, Serializable, ArrayAccess {
*
* @param string $globalSiteId
*/
public function removeSite( $globalSiteId );
public function removeSite( $globalSiteId ) {
$this->offsetUnset( $this->byGlobalId[$globalSiteId] );
}
/**
* Returns if the list contains no sites.
*
* @since 1.21
*
* @return boolean
*/
public function isEmpty() {
return $this->byGlobalId === array();
}
/**
* Returns if the list contains the site with the provided site id.
@ -76,7 +166,9 @@ interface SiteList extends Countable, Traversable, Serializable, ArrayAccess {
*
* @return boolean
*/
public function hasInternalId( $id );
public function hasInternalId( $id ) {
return array_key_exists( $id, $this->byInternalId );
}
/**
* Returns the Site with the provided site id.
@ -88,7 +180,9 @@ interface SiteList extends Countable, Traversable, Serializable, ArrayAccess {
*
* @return Site
*/
public function getSiteByInternalId( $id );
public function getSiteByInternalId( $id ) {
return $this->offsetGet( $this->byInternalId[$id] );
}
/**
* Removes the site with the specified site id.
@ -98,7 +192,9 @@ interface SiteList extends Countable, Traversable, Serializable, ArrayAccess {
*
* @param integer $id
*/
public function removeSiteByInternalId( $id );
public function removeSiteByInternalId( $id ) {
$this->offsetUnset( $this->byInternalId[$id] );
}
/**
* Sets a site in the list. If the site was not there,
@ -108,15 +204,72 @@ interface SiteList extends Countable, Traversable, Serializable, ArrayAccess {
*
* @param Site $site
*/
public function setSite( Site $site );
public function setSite( Site $site ) {
$this[] = $site;
}
/**
* Returns if the site list contains no sites.
* Returns the sites that are in the provided group.
*
* @since 1.21
*
* @return boolean
* @param string $groupName
*
* @return SiteList
*/
public function isEmpty();
public function getGroup( $groupName ) {
$group = new self();
}
/**
* @var \Site $site
*/
foreach ( $this as $site ) {
if ( $site->getGroup() === $groupName ) {
$group[] = $site;
}
}
return $group;
}
/**
* @see GenericArrayObject::getSerializationData
*
* @since 1.21
*
* @return array
*/
protected function getSerializationData() {
return array_merge(
parent::getSerializationData(),
array(
'internalIds' => $this->byInternalId,
'globalIds' => $this->byGlobalId,
)
);
}
/**
* @see GenericArrayObject::unserialize
*
* @since 1.21
*
* @param string $serialization
*
* @return array
*/
public function unserialize( $serialization ) {
$serializationData = parent::unserialize( $serialization );
$this->byInternalId = $serializationData['internalIds'];
$this->byGlobalId = $serializationData['globalIds'];
return $serializationData;
}
}
/**
* @deprecated
*/
class SiteArray extends SiteList {}

View file

@ -1,570 +0,0 @@
<?php
/**
* Class representing a single site.
*
* 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
*
* @since 1.21
*
* @file
* @ingroup Site
*
* @license GNU GPL v2+
* @author Jeroen De Dauw < jeroendedauw@gmail.com >
* @author Daniel Werner
*/
class SiteObject extends ORMRow implements Site {
const PATH_LINK = 'link';
/**
* Holds the local ids for this site.
* You can obtain them via @see getLocalIds
*
* @since 1.21
*
* @var array|false
*/
protected $localIds = false;
/**
* @see Site::getGlobalId
*
* @since 1.21
*
* @return string
*/
public function getGlobalId() {
return $this->getField( 'global_key' );
}
/**
* @see Site::setGlobalId
*
* @since 1.21
*
* @param string $globalId
*/
public function setGlobalId( $globalId ) {
$this->setField( 'global_key', $globalId );
}
/**
* @see Site::getType
*
* @since 1.21
*
* @return string
*/
public function getType() {
return $this->getField( 'type' );
}
/**
* @see Site::setType
*
* @since 1.21
*
* @param string $type
*/
public function setType( $type ) {
$this->setField( 'type', $type );
}
/**
* @see Site::getGroup
*
* @since 1.21
*
* @return string
*/
public function getGroup() {
return $this->getField( 'group' );
}
/**
* @see Site::setGroup
*
* @since 1.21
*
* @param string $group
*/
public function setGroup( $group ) {
$this->setField( 'group', $group );
}
/**
* @see Site::getSource
*
* @since 1.21
*
* @return string
*/
public function getSource() {
return $this->getField( 'source' );
}
/**
* @see Site::setSource
*
* @since 1.21
*
* @param string $source
*/
public function setSource( $source ) {
$this->setField( 'source', $source );
}
/**
* @see Site::getDomain
*
* @since 1.21
*
* @return string|false
*/
public function getDomain() {
$path = $this->getLinkPath();
if ( $path === false ) {
return false;
}
return parse_url( $path, PHP_URL_HOST );
}
/**
* @see Site::getProtocol
*
* @since 1.21
*
* @throws MWException
* @return string|false
*/
public function getProtocol() {
$path = $this->getLinkPath();
if ( $path === false ) {
return '';
}
$protocol = parse_url( $path, PHP_URL_SCHEME );
// Malformed URL
if ( $protocol === false ) {
throw new MWException( "failed to parse URL $path" );
}
// No schema
if ( $protocol === null ) {
// Used for protocol relative URLs
$protocol = '';
}
return $protocol;
}
/**
* Sets the path used to construct links with.
* @see Site::setLinkPath
*
* @param string $fullUrl
*
* @since 1.21
*
* @throws MWException
*/
public function setLinkPath( $fullUrl ) {
$type = $this->getLinkPathType();
if ( $type === false ) {
throw new MWException( "This SiteObject does not support link paths." );
}
$this->setPath( $type, $fullUrl );
}
/**
* Returns the path path used to construct links with or false if there is no such path.
*
* @see Site::getLinkPath
*
* @return string|false
*/
public function getLinkPath() {
$type = $this->getLinkPathType();
return $type === false ? false : $this->getPath( $type );
}
/**
* @see Site::getLinkPathType
*
* Returns the main path type, that is the type of the path that should generally be used to construct links
* to the target site.
*
* This default implementation returns SiteObject::PATH_LINK as the default path type. Subclasses can override this
* to define a different default path type, or return false to disable site links.
*
* @since 1.21
*
* @return string|false
*/
public function getLinkPathType() {
return self::PATH_LINK;
}
/**
* @see Site::getPageUrl
*
* This implementation returns a URL constructed using the path returned by getLinkPath().
*
* @since 1.21
*
* @param bool|String $pageName
*
* @return string|false
*/
public function getPageUrl( $pageName = false ) {
$url = $this->getLinkPath();
if ( $url === false ) {
return false;
}
if ( $pageName !== false ) {
$url = str_replace( '$1', rawurlencode( $pageName ), $url ) ;
}
return $url;
}
/**
* Returns $pageName without changes.
* Subclasses may override this to apply some kind of normalization.
*
* @see Site::normalizePageName
*
* @since 1.21
*
* @param string $pageName
*
* @return string
*/
public function normalizePageName( $pageName ) {
return $pageName;
}
/**
* Returns the value of a type specific field, or the value
* of the $default parameter in case it's not set.
*
* @since 1.21
*
* @param string $fieldName
* @param mixed $default
*
* @return array
*/
protected function getExtraData( $fieldName, $default = null ) {
$data = $this->getField( 'data', array() );
return array_key_exists( $fieldName,$data ) ? $data[$fieldName] : $default;
}
/**
* Sets the value of a type specific field.
* @since 1.21
*
* @param string $fieldName
* @param mixed $value
*/
protected function setExtraData( $fieldName, $value = null ) {
$data = $this->getField( 'data', array() );
$data[$fieldName] = $value;
$this->setField( 'data', $data );
}
/**
* @see Site::getLanguageCode
*
* @since 1.21
*
* @return string|false
*/
public function getLanguageCode() {
return $this->getField( 'language', false );
}
/**
* @see Site::setLanguageCode
*
* @since 1.21
*
* @param string $languageCode
*/
public function setLanguageCode( $languageCode ) {
$this->setField( 'language', $languageCode );
}
/**
* Returns the local identifiers of this site.
*
* @since 1.21
*
* @param string $type
*
* @return array
*/
protected function getLocalIds( $type ) {
if ( $this->localIds === false ) {
$this->loadLocalIds();
}
return array_key_exists( $type, $this->localIds ) ? $this->localIds[$type] : array();
}
/**
* Loads the local ids for the site.
*
* @since 1.21
*/
protected function loadLocalIds() {
$dbr = wfGetDB( $this->getTable()->getReadDb() );
$ids = $dbr->select(
'site_identifiers',
array(
'si_type',
'si_key',
),
array(
'si_site' => $this->getId(),
),
__METHOD__
);
$this->localIds = array();
foreach ( $ids as $id ) {
$this->addLocalId( $id->si_type, $id->si_key );
}
}
/**
* Adds a local identifier.
*
* @since 1.21
*
* @param string $type
* @param string $identifier
*/
public function addLocalId( $type, $identifier ) {
if ( $this->localIds === false ) {
$this->localIds = array();
}
if ( !array_key_exists( $type, $this->localIds ) ) {
$this->localIds[$type] = array();
}
if ( !in_array( $identifier, $this->localIds[$type] ) ) {
$this->localIds[$type][] = $identifier;
}
}
/**
* @see Site::addInterwikiId
*
* @since 1.21
*
* @param string $identifier
*/
public function addInterwikiId( $identifier ) {
$this->addLocalId( 'interwiki', $identifier );
}
/**
* @see Site::addNavigationId
*
* @since 1.21
*
* @param string $identifier
*/
public function addNavigationId( $identifier ) {
$this->addLocalId( 'equivalent', $identifier );
}
/**
* @see Site::getInterwikiIds
*
* @since 1.21
*
* @return array of string
*/
public function getInterwikiIds() {
return $this->getLocalIds( 'interwiki' );
}
/**
* @see Site::getNavigationIds
*
* @since 1.21
*
* @return array of string
*/
public function getNavigationIds() {
return $this->getLocalIds( 'equivalent' );
}
/**
* @see Site::getInternalId
*
* @since 1.21
*
* @return integer
*/
public function getInternalId() {
return $this->getId();
}
/**
* @see IORMRow::save
* @see Site::save
*
* @since 1.21
*
* @param string|null $functionName
*
* @return boolean Success indicator
*/
public function save( $functionName = null ) {
$dbw = $this->table->getWriteDbConnection();
$trx = $dbw->trxLevel();
if ( $trx == 0 ) {
$dbw->begin( __METHOD__ );
}
$this->setField( 'protocol', $this->getProtocol() );
$this->setField( 'domain', strrev( $this->getDomain() ) . '.' );
$existedAlready = $this->hasIdField();
$success = parent::save( $functionName );
if ( $success && $existedAlready ) {
$dbw->delete(
'site_identifiers',
array( 'si_site' => $this->getId() ),
__METHOD__
);
}
if ( $success && $this->localIds !== false ) {
foreach ( $this->localIds as $type => $ids ) {
foreach ( $ids as $id ) {
$dbw->insert(
'site_identifiers',
array(
'si_site' => $this->getId(),
'si_type' => $type,
'si_key' => $id,
),
__METHOD__
);
}
}
}
if ( $trx == 0 ) {
$dbw->commit( __METHOD__ );
}
return $success;
}
/**
* @since 1.21
*
* @see ORMRow::onRemoved
*/
protected function onRemoved() {
$dbw = $this->table->getWriteDbConnection();
$dbw->delete(
'site_identifiers',
array(
'si_site' => $this->getId()
),
__METHOD__
);
parent::onRemoved();
}
/**
* @see Site::setPath
*
* @since 1.21
*
* @param string $pathType
* @param string $fullUrl
*/
public function setPath( $pathType, $fullUrl ) {
$paths = $this->getExtraData( 'paths', array() );
$paths[$pathType] = $fullUrl;
$this->setExtraData( 'paths', $paths );
}
/**
* @see Sitres::getPath
*
* @since 1.21
*
* @param string $pathType
*
* @return string|false
*/
public function getPath( $pathType ) {
$paths = $this->getExtraData( 'paths', array() );
return array_key_exists( $pathType, $paths ) ? $paths[$pathType] : false;
}
/**
* @see Sitres::getAll
*
* @since 1.21
*
* @return array of string
*/
public function getAllPaths() {
return $this->getExtraData( 'paths', array() );
}
/**
* @see Sitres::removePath
*
* @since 1.21
*
* @param string $pathType
*/
public function removePath( $pathType ) {
$paths = $this->getExtraData( 'paths', array() );
unset( $paths[$pathType] );
$this->setExtraData( 'paths', $paths );
}
}

View file

@ -0,0 +1,375 @@
<?php
/**
* Represents the site configuration of a wiki.
* Holds a list of sites (ie SiteList) and takes care
* of retrieving and caching site information when appropriate.
*
* 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
*
* @since 1.21
*
* @file
* @ingroup Site
*
* @license GNU GPL v2+
* @author Jeroen De Dauw < jeroendedauw@gmail.com >
*/
class SiteSQLStore implements SiteStore {
/**
* @since 1.21
*
* @var SiteList|null
*/
protected $sites = null;
/**
* @var ORMTable
*/
protected $sitesTable;
/**
* @since 1.21
*
* @param ORMTable|null $sitesTable
*
* @return SiteStore
*/
public static function newInstance( ORMTable $sitesTable = null ) {
return new static( $sitesTable );
}
/**
* Constructor.
*
* @since 1.21
*
* @param ORMTable|null $sitesTable
*/
protected function __construct( ORMTable $sitesTable = null ) {
if ( $sitesTable === null ) {
$sitesTable = $this->newSitesTable();
}
$this->sitesTable = $sitesTable;
}
/**
* @see SiteStore::getSites
*
* @since 1.21
*
* @param string $source either 'cache' or 'recache'
*
* @return SiteList
*/
public function getSites( $source = 'cache' ) {
if ( $source === 'cache' ) {
if ( $this->sites === null ) {
$cache = wfGetMainCache();
$sites = $cache->get( wfMemcKey( 'SiteList' ) );
if ( is_object( $sites ) ) {
$this->sites = $sites;
} else {
$this->loadSites();
}
}
}
else {
$this->loadSites();
}
return $this->sites;
}
/**
* Returns a new Site object constructed from the provided ORMRow.
*
* @since 1.21
*
* @param ORMRow $siteRow
*
* @return Site
*/
protected function siteFromRow( ORMRow $siteRow ) {
$site = Site::newForType( $siteRow->getField( 'type', Site::TYPE_UNKNOWN ) );
$site->setGlobalId( $siteRow->getField( 'global_key' ) );
if ( $siteRow->hasField( 'forward' ) ) {
$site->setForward( $siteRow->getField( 'forward' ) );
}
if ( $siteRow->hasField( 'group' ) ) {
$site->setGroup( $siteRow->getField( 'group' ) );
}
if ( $siteRow->hasField( 'language' ) ) {
$site->setLanguageCode( $siteRow->getField( 'language' ) === '' ? null : $siteRow->getField( 'language' ) );
}
if ( $siteRow->hasField( 'source' ) ) {
$site->setSource( $siteRow->getField( 'source' ) );
}
if ( $siteRow->hasField( 'data' ) ) {
$site->setExtraData( $siteRow->getField( 'data' ) );
}
if ( $siteRow->hasField( 'config' ) ) {
$site->setExtraConfig( $siteRow->getField( 'config' ) );
}
return $site;
}
/**
* Fetches the site from the database and loads them into the sites field.
*
* @since 1.21
*/
protected function loadSites() {
$this->sites = new SiteList();
foreach ( $this->sitesTable->select() as $siteRow ) {
$this->sites[] = $this->siteFromRow( $siteRow );
}
// Batch load the local site identifiers.
$ids = wfGetDB( $this->sitesTable->getReadDb() )->select(
'site_identifiers',
array(
'si_site',
'si_type',
'si_key',
),
array(),
__METHOD__
);
foreach ( $ids as $id ) {
if ( $this->sites->hasInternalId( $id->si_site ) ) {
$site = $this->sites->getSiteByInternalId( $id->si_site );
$site->addLocalId( $id->si_type, $id->si_key );
$this->sites->setSite( $site );
}
}
$cache = wfGetMainCache();
$cache->set( wfMemcKey( 'SiteList' ), $this->sites );
}
/**
* @see SiteStore::getSite
*
* @since 1.21
*
* @param string $globalId
* @param string $source
*
* @return Site|null
*/
public function getSite( $globalId, $source = 'cache' ) {
$sites = $this->getSites( $source );
return $sites->hasSite( $globalId ) ? $sites->getSite( $globalId ) : null;
}
/**
* @see SiteStore::saveSite
*
* @since 1.21
*
* @param Site $site
*
* @return boolean Success indicator
*/
public function saveSite( Site $site ) {
return $this->saveSites( array( $site ) );
}
/**
* @see SiteStore::saveSites
*
* @since 1.21
*
* @param Site[] $sites
*
* @return boolean Success indicator
*/
public function saveSites( array $sites ) {
if ( empty( $sites ) ) {
return true;
}
$dbw = $this->sitesTable->getWriteDbConnection();
$trx = $dbw->trxLevel();
if ( $trx == 0 ) {
$dbw->begin( __METHOD__ );
}
$success = true;
$internalIds = array();
$localIds = array();
foreach ( $sites as $site ) {
$fields = array(
// Site data
'global_key' => $site->getGlobalId(), // TODO: check not null
'type' => $site->getType(),
'group' => $site->getGroup(),
'source' => $site->getSource(),
'language' => $site->getLanguageCode() === null ? '' : $site->getLanguageCode(),
'protocol' => $site->getProtocol(),
'domain' => strrev( $site->getDomain() ) . '.',
'data' => $site->getExtraData(),
// Site config
'forward' => $site->shouldForward(),
'config' => $site->getExtraConfig(),
);
if ( $site->getInternalId() !== null ) {
$fields['id'] = $site->getInternalId();
$internalIds[] = $site->getInternalId();
}
$siteRow = new ORMRow( $this->sitesTable, $fields );
$success = $siteRow->save( __METHOD__ ) && $success;
foreach ( $site->getLocalIds() as $idType => $ids ) {
foreach ( $ids as $id ) {
$localIds[] = array( $siteRow->getId(), $idType, $id );
}
}
}
if ( $internalIds !== array() ) {
$dbw->delete(
'site_identifiers',
array( 'si_site' => $internalIds ),
__METHOD__
);
}
foreach ( $localIds as $localId ) {
$dbw->insert(
'site_identifiers',
array(
'si_site' => $localId[0],
'si_type' => $localId[1],
'si_key' => $localId[2],
),
__METHOD__
);
}
if ( $trx == 0 ) {
$dbw->commit( __METHOD__ );
}
return $success;
}
/**
* @since 1.21
*
* @return ORMTable
*/
protected function newSitesTable() {
return new ORMTable(
'sites',
array(
'id' => 'id',
// Site data
'global_key' => 'str',
'type' => 'str',
'group' => 'str',
'source' => 'str',
'language' => 'str',
'protocol' => 'str',
'domain' => 'str',
'data' => 'array',
// Site config
'forward' => 'bool',
'config' => 'array',
),
array(
'type' => Site::TYPE_UNKNOWN,
'group' => Site::GROUP_NONE,
'source' => Site::SOURCE_LOCAL,
'data' => array(),
'forward' => false,
'config' => array(),
'language' => '',
),
'ORMRow',
'site_'
);
}
}
/**
* @deprecated
*/
class Sites extends SiteSQLStore {
/**
* Factory for creating new site objects.
*
* @since 1.21
* @deprecated
*
* @param string|boolean false $globalId
*
* @return Site
*/
public static function newSite( $globalId = false ) {
$site = new Site();
if ( $globalId !== false ) {
$site->setGlobalId( $globalId );
}
return $site;
}
/**
* @deprecated
* @return SiteStore
*/
public static function singleton() {
return new static();
}
/**
* @deprecated
* @return SiteList
*/
public function getSiteGroup( $group ) {
return $this->getSites()->getGroup( $group );
}
}

View file

@ -0,0 +1,54 @@
<?php
interface SiteStore {
/**
* Saves the provided site.
*
* @since 1.21
*
* @param Site $site
*
* @return boolean Success indicator
*/
public function saveSite( Site $site );
/**
* Saves the provided sites.
*
* @since 1.21
*
* @param Site[] $sites
*
* @return boolean Success indicator
*/
public function saveSites( array $sites );
/**
* Returns the site with provided global id, or null if there is no such site.
*
* @since 1.21
*
* @param string $globalId
* @param string $source either 'cache' or 'recache'.
* If 'cache', the values are allowed (but not obliged) to come from a cache.
*
* @return Site|null
*/
public function getSite( $globalId, $source = 'cache' );
/**
* Returns a list of all sites. By default this site is
* fetched from the cache, which can be changed to loading
* the list from the database using the $useCache parameter.
*
* @since 1.21
*
* @param string $source either 'cache' or 'recache'.
* If 'cache', the values are allowed (but not obliged) to come from a cache.
*
* @return SiteList
*/
public function getSites( $source = 'cache' );
}

View file

@ -1,191 +0,0 @@
<?php
/**
* Represents the site configuration of a wiki.
* Holds a list of sites (ie SiteList) and takes care
* of retrieving and caching site information when appropriate.
*
* 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
*
* @since 1.21
*
* @file
* @ingroup Site
*
* @license GNU GPL v2+
* @author Jeroen De Dauw < jeroendedauw@gmail.com >
*/
class Sites {
/**
* @since 1.21
* @var SiteList|null
*/
protected $sites = null;
/**
* Constructor.
*
* @since 1.21
*/
protected function __construct() {}
/**
* Returns an instance of Sites.
*
* @since 1.21
*
* @return Sites
*/
public static function singleton() {
static $instance = false;
if ( $instance === false ) {
$instance = new static();
}
return $instance;
}
/**
* Factory for creating new site objects.
*
* @since 1.21
*
* @param string|boolean false $globalId
*
* @return Site
*/
public static function newSite( $globalId = false ) {
/**
* @var Site $site
*/
$site = SitesTable::singleton()->newRow( array(), true );
if ( $globalId !== false ) {
$site->setGlobalId( $globalId );
}
return $site;
}
/**
* Returns a list of all sites. By default this site is
* fetched from the cache, which can be changed to loading
* the list from the database using the $useCache parameter.
*
* @since 1.21
*
* @param string $source either 'cache' or 'recache'
*
* @return SiteList
*/
public function getSites( $source = 'cache' ) {
if ( $source === 'cache' ) {
if ( $this->sites === null ) {
$cache = wfGetMainCache();
$sites = $cache->get( wfMemcKey( 'SiteList' ) );
if ( is_object( $sites ) && isset( $sites->cacheVersion ) && $sites->cacheVersion === SiteArray::CACHE_VERSION ) {
$this->sites = $sites;
} else {
$this->loadSites();
}
}
}
else {
$this->loadSites();
}
return $this->sites;
}
/**
* Returns a list of sites in the given group. Calling getGroup() on any of
* the sites in the resulting SiteList shall return $group.
*
* @since 1.21
*
* @param string $group th group to get.
*
* @return SiteList
*/
public function getSiteGroup( $group ) {
$sites = self::getSites();
$siteGroup = new SiteArray();
/* @var Site $site */
foreach ( $sites as $site ) {
if ( $site->getGroup() == $group ) {
$siteGroup->append( $site );
}
}
return $siteGroup;
}
/**
* Fetches the site from the database and loads them into the sites field.
*
* @since 1.21
*/
protected function loadSites() {
$this->sites = new SiteArray( SitesTable::singleton()->select() );
// Batch load the local site identifiers.
$dbr = wfGetDB( SitesTable::singleton()->getReadDb() );
$ids = $dbr->select(
'site_identifiers',
array(
'si_site',
'si_type',
'si_key',
),
array(),
__METHOD__
);
foreach ( $ids as $id ) {
if ( $this->sites->hasInternalId( $id->si_site ) ) {
$site = $this->sites->getSiteByInternalId( $id->si_site );
$site->addLocalId( $id->si_type, $id->si_key );
$this->sites->setSite( $site );
}
}
$cache = wfGetMainCache();
$cache->set( wfMemcKey( 'SiteList' ), $this->sites );
}
/**
* Returns the site with provided global id, or false if there is no such site.
*
* @since 1.21
*
* @param string $globalId
* @param string $source
*
* @return Site|false
*/
public function getSite( $globalId, $source = 'cache' ) {
$sites = $this->getSites( $source );
return $sites->hasSite( $globalId ) ? $sites->getSite( $globalId ) : false;
}
}

View file

@ -1,136 +0,0 @@
<?php
/**
* Represents the sites database table.
* All access to this table should be done through this class.
*
* 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
*
* @since 1.21
*
* @file
* @ingroup Site
*
* @license GNU GPL v2+
* @author Jeroen De Dauw < jeroendedauw@gmail.com >
*/
class SitesTable extends ORMTable {
/**
* @see IORMTable::getName()
* @since 1.21
* @return string
*/
public function getName() {
return 'sites';
}
/**
* @see IORMTable::getFieldPrefix()
* @since 1.21
* @return string
*/
public function getFieldPrefix() {
return 'site_';
}
/**
* @see IORMTable::getRowClass()
* @since 1.21
* @return string
*/
public function getRowClass() {
return 'SiteObject';
}
/**
* @see IORMTable::getFields()
* @since 1.21
* @return array
*/
public function getFields() {
return array(
'id' => 'id',
// Site data
'global_key' => 'str',
'type' => 'str',
'group' => 'str',
'source' => 'str',
'language' => 'str',
'protocol' => 'str',
'domain' => 'str',
'data' => 'array',
// Site config
'forward' => 'bool',
'config' => 'array',
);
}
/**
* @see IORMTable::getDefaults()
* @since 1.21
* @return array
*/
public function getDefaults() {
return array(
'type' => Site::TYPE_UNKNOWN,
'group' => Site::GROUP_NONE,
'source' => Site::SOURCE_LOCAL,
'data' => array(),
'forward' => false,
'config' => array(),
'language' => 'en', // XXX: can we default to '' instead?
);
}
/**
* Returns the class name for the provided site type.
*
* @since 1.21
*
* @param integer $siteType
*
* @return string
*/
protected static function getClassForType( $siteType ) {
global $wgSiteTypes;
return array_key_exists( $siteType, $wgSiteTypes ) ? $wgSiteTypes[$siteType] : 'SiteObject';
}
/**
* Factory method to construct a new Site instance.
*
* @since 1.21
*
* @param array $data
* @param boolean $loadDefaults
*
* @return Site
*/
public function newRow( array $data, $loadDefaults = false ) {
if ( !array_key_exists( 'type', $data ) ) {
$data['type'] = Site::TYPE_UNKNOWN;
}
$class = static::getClassForType( $data['type'] );
return new $class( $this, $data, $loadDefaults );
}
}

View file

@ -77,7 +77,7 @@ $wgAutoloadClasses += array(
'GenericArrayObjectTest' => "$testDir/phpunit/includes/libs/GenericArrayObjectTest.php",
# tests/phpunit/includes/site
'SiteObjectTest' => "$testDir/phpunit/includes/site/SiteObjectTest.php",
'SiteTest' => "$testDir/phpunit/includes/site/SiteTest.php",
'TestSites' => "$testDir/phpunit/includes/site/TestSites.php",
# tests/phpunit/languages

View file

@ -25,32 +25,15 @@
* @ingroup Test
*
* @group Site
* @group Database
*
* @licence GNU GPL v2+
* @author Jeroen De Dauw < jeroendedauw@gmail.com >
*/
class MediaWikiSiteTest extends SiteObjectTest {
public function setUp() {
parent::setUp();
static $hasSites = false;
if ( !$hasSites ) {
TestSites::insertIntoDb();
$hasSites = true;
}
}
public function testFactoryConstruction() {
$this->assertInstanceOf( 'MediaWikiSite', MediaWikiSite::newFromGlobalId( 'enwiki' ) );
$this->assertInstanceOf( 'Site', MediaWikiSite::newFromGlobalId( 'enwiki' ) );
$this->assertInstanceOf( 'MediaWikiSite', SitesTable::singleton()->newRow( array( 'type' => Site::TYPE_MEDIAWIKI ) ) );
}
class MediaWikiSiteTest extends SiteTest {
public function testNormalizePageTitle() {
$site = MediaWikiSite::newFromGlobalId( 'enwiki' );
$site = new MediaWikiSite();
$site->setGlobalId( 'enwiki' );
//NOTE: this does not actually call out to the enwiki site to perform the normalization,
// but uses a local Title object to do so. This is hardcoded on SiteLink::normalizePageTitle
@ -73,8 +56,7 @@ class MediaWikiSiteTest extends SiteObjectTest {
* @dataProvider fileUrlProvider
*/
public function testGetFileUrl( $url, $filePath, $pathArgument, $expected ) {
$site = MediaWikiSite::newFromGlobalId( 'enwiki' );
$site = new MediaWikiSite();
$site->setFilePath( $url . $filePath );
$this->assertEquals( $expected, $site->getFileUrl( $pathArgument ) );
@ -97,10 +79,9 @@ class MediaWikiSiteTest extends SiteObjectTest {
* @dataProvider provideGetPageUrl
*/
public function testGetPageUrl( $path, $page, $expected ) {
/* @var MediaWikiSite $site */
$site = MediaWikiSite::newFromGlobalId( 'enwiki' );
$site = new MediaWikiSite();
$site->setLinkPath( $path );
$this->assertContains( $path, $site->getPageUrl() );
$this->assertContains( $expected, $site->getPageUrl( $page ) );
}

View file

@ -1,92 +0,0 @@
<?php
/**
* Tests for the SiteArray class.
* The tests for methods defined in the SiteList interface are in SiteListTest.
*
* 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
*Both
* Bith
* @file
* @since 1.21
*
* @ingroup Site
* @ingroup Test
*
* @group Site
*
* @licence GNU GPL v2+
* @author Jeroen De Dauw < jeroendedauw@gmail.com >
*/
class SiteArrayTest extends GenericArrayObjectTest {
/**
* @see GenericArrayObjectTest::elementInstancesProvider
*
* @since 1.21
*
* @return array
*/
public function elementInstancesProvider() {
$sites = TestSites::getSites();
$siteArrays = array();
$siteArrays[] = $sites;
$siteArrays[] = array( array_shift( $sites ) );
$siteArrays[] = array( array_shift( $sites ), array_shift( $sites ) );
return $this->arrayWrap( $siteArrays );
}
/**
* @see GenericArrayObjectTest::getInstanceClass
*
* @since 1.21
*
* @return array
*/
public function getInstanceClass() {
return 'SiteArray';
}
/**
* @dataProvider instanceProvider
*
* @since 1.21
*
* @param SiteArray $list
*/
public function testSerializationMore( SiteArray $list ) {
$serialization = serialize( $list );
/**
* @var SiteArray $copy
*/
$copy = unserialize( $serialization );
$this->assertArrayEquals( $list->getGlobalIdentifiers(), $copy->getGlobalIdentifiers() );
/**
* @var Site $site
*/
foreach ( $list as $site ) {
$this->assertTrue( $copy->hasInternalId( $site->getInternalId() ) );
}
}
}

View file

@ -1,7 +1,7 @@
<?php
/**
* Tests for the SiteList implementing classes.
* Tests for the SiteList class.
*
* 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
@ -41,7 +41,7 @@ class SiteListTest extends MediaWikiTestCase {
$listInstances = array();
foreach ( $sitesArrays as $sitesArray ) {
$listInstances[] = new SiteArray( $sitesArray[0] );
$listInstances[] = new SiteList( $sitesArray[0] );
}
return $this->arrayWrap( $listInstances );
@ -164,5 +164,28 @@ class SiteListTest extends MediaWikiTestCase {
$this->assertArrayEquals( $expected, $identifiers );
}
/**
* @dataProvider siteListProvider
*
* @since 1.21
*
* @param SiteList $list
*/
public function testSerialization( SiteList $list ) {
$serialization = serialize( $list );
/**
* @var SiteArray $copy
*/
$copy = unserialize( $serialization );
$this->assertArrayEquals( $list->getGlobalIdentifiers(), $copy->getGlobalIdentifiers() );
/**
* @var Site $site
*/
foreach ( $list as $site ) {
$this->assertTrue( $copy->hasInternalId( $site->getInternalId() ) );
}
}
}

View file

@ -1,7 +1,7 @@
<?php
/**
* Tests for the Sites class.
* Tests for the SiteSQLStore class.
*
* 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
@ -30,58 +30,56 @@
* @licence GNU GPL v2+
* @author Jeroen De Dauw < jeroendedauw@gmail.com >
*/
class SitesTest extends MediaWikiTestCase {
public function setUp() {
parent::setUp();
TestSites::insertIntoDb();
}
public function testSingleton() {
$this->assertInstanceOf( 'Sites', Sites::singleton() );
$this->assertTrue( Sites::singleton() === Sites::singleton() );
}
class SiteSQLStoreTest extends MediaWikiTestCase {
public function testGetSites() {
$this->assertInstanceOf( 'SiteList', Sites::singleton()->getSites() );
}
$expectedSites = TestSites::getSites();
TestSites::insertIntoDb();
$sitesTable = SiteSQLStore::newInstance();
public function testGetSite() {
$count = 0;
$sites = Sites::singleton()->getSites();
$sites = $sitesTable->getSites();
$this->assertInstanceOf( 'SiteList', $sites );
/**
* @var Site $site
*/
foreach ( $sites as $site ) {
$this->assertInstanceOf( 'Site', $site );
}
$this->assertEquals(
$site,
Sites::singleton()->getSite( $site->getGlobalId() )
);
if ( ++$count > 100 ) {
break;
foreach ( $expectedSites as $site ) {
if ( $site->getGlobalId() !== null ) {
$this->assertTrue( $sites->hasSite( $site->getGlobalId() ) );
}
}
}
public function testNewSite() {
$this->assertInstanceOf( 'Site', Sites::newSite() );
$this->assertInstanceOf( 'Site', Sites::newSite( 'enwiki' ) );
}
public function testSaveSites() {
$sitesTable = SiteSQLStore::newInstance();
public function testGetGroup() {
$wikipedias = Sites::singleton()->getSiteGroup( "wikipedia" );
$sites = array();
$this->assertFalse( $wikipedias->isEmpty() );
$site = new Site();
$site->setGlobalId( 'ertrywuutr' );
$site->setLanguageCode( 'en' );
$sites[] = $site;
/* @var Site $site */
foreach ( $wikipedias as $site ) {
$this->assertEquals( 'wikipedia', $site->getGroup() );
}
$site = new MediaWikiSite();
$site->setGlobalId( 'sdfhxujgkfpth' );
$site->setLanguageCode( 'nl' );
$sites[] = $site;
$this->assertTrue( $sitesTable->saveSites( $sites ) );
$site = $sitesTable->getSite( 'ertrywuutr', 'nocache' );
$this->assertInstanceOf( 'Site', $site );
$this->assertEquals( 'en', $site->getLanguageCode() );
$site = $sitesTable->getSite( 'sdfhxujgkfpth', 'nocache' );
$this->assertInstanceOf( 'Site', $site );
$this->assertEquals( 'nl', $site->getLanguageCode() );
}
}

View file

@ -1,7 +1,7 @@
<?php
/**
* Tests for the SiteObject class.
* Tests for the Site class.
*
* 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
@ -25,50 +25,28 @@
* @ingroup Test
*
* @group Site
* @group Database
*
* @licence GNU GPL v2+
* @author Jeroen De Dauw < jeroendedauw@gmail.com >
*/
class SiteObjectTest extends ORMRowTest {
class SiteTest extends MediaWikiTestCase {
/**
* @see ORMRowTest::getRowClass
* @since 1.21
* @return string
*/
protected function getRowClass() {
return 'SiteObject';
}
public function instanceProvider() {
$instances = array();
/**
* @see ORMRowTest::getTableInstance
* @since 1.21
* @return IORMTable
*/
protected function getTableInstance() {
return SitesTable::singleton();
}
$instances[] = new Site();
/**
* @see ORMRowTest::constructorTestProvider
* @since 1.21
* @return array
*/
public function constructorTestProvider() {
$argLists = array();
$site = new Site();
$site->setGlobalId( 'enwiki' );
$site->setInternalId( 42 );
$instances[] = $site;
$argLists[] = array( 'global_key' => 'foo' );
$site = new MediaWikiSite();
$site->setGlobalId( 'nlwiki' );
$site->setLanguageCode( 'nl' );
$instances[] = $site;
$argLists[] = array( 'global_key' => 'bar', 'type' => Site::TYPE_MEDIAWIKI );
$constructorArgs = array();
foreach ( $argLists as $argList ) {
$constructorArgs[] = array( $argList, true );
}
return $constructorArgs;
return $this->arrayWrap( $instances );
}
/**
@ -110,7 +88,7 @@ class SiteObjectTest extends ORMRowTest {
* @param Site $site
*/
public function testGetLanguageCode( Site $site ) {
$this->assertTypeOrFalse( 'string', $site->getLanguageCode() );
$this->assertTypeOrValue( 'string', $site->getLanguageCode(), null );
}
/**
@ -135,7 +113,7 @@ class SiteObjectTest extends ORMRowTest {
* @param Site $site
*/
public function testGetGlobalId( Site $site ) {
$this->assertInternalType( 'string', $site->getGlobalId() );
$this->assertTypeOrValue( 'string', $site->getGlobalId(), null );
}
/**
@ -160,9 +138,9 @@ class SiteObjectTest extends ORMRowTest {
* @param Site $site
*/
public function testGetPath( Site $site ) {
$this->assertTypeOrFalse( 'string', $site->getPath( 'page_path' ) );
$this->assertTypeOrFalse( 'string', $site->getPath( 'file_path' ) );
$this->assertTypeOrFalse( 'string', $site->getPath( 'foobar' ) );
$this->assertTypeOrValue( 'string', $site->getPath( 'page_path' ), null );
$this->assertTypeOrValue( 'string', $site->getPath( 'file_path' ), null );
$this->assertTypeOrValue( 'string', $site->getPath( 'foobar' ), null );
}
/**
@ -194,13 +172,12 @@ class SiteObjectTest extends ORMRowTest {
$this->assertEquals( $count, count( $site->getAllPaths() ) );
$this->assertFalse( $site->getPath( 'foobar' ) );
$this->assertFalse( $site->getPath( 'spam' ) );
$this->assertNull( $site->getPath( 'foobar' ) );
$this->assertNull( $site->getPath( 'spam' ) );
}
public function testSetLinkPath() {
/* @var SiteObject $site */
$site = $this->getRowInstance( $this->getMockFields(), false );
$site = new Site();
$path = "TestPath/$1";
$site->setLinkPath( $path );
@ -208,8 +185,7 @@ class SiteObjectTest extends ORMRowTest {
}
public function testGetLinkPathType() {
/* @var SiteObject $site */
$site = $this->getRowInstance( $this->getMockFields(), false );
$site = new Site();
$path = 'TestPath/$1';
$site->setLinkPath( $path );
@ -221,8 +197,7 @@ class SiteObjectTest extends ORMRowTest {
}
public function testSetPath() {
/* @var SiteObject $site */
$site = $this->getRowInstance( $this->getMockFields(), false );
$site = new Site();
$path = 'TestPath/$1';
$site->setPath( 'foo', $path );
@ -231,8 +206,7 @@ class SiteObjectTest extends ORMRowTest {
}
public function testProtocolRelativePath() {
/* @var SiteObject $site */
$site = $this->getRowInstance( $this->getMockFields(), false );
$site = new Site();
$type = $site->getLinkPathType();
$path = '//acme.com/'; // protocol-relative URL
@ -244,7 +218,7 @@ class SiteObjectTest extends ORMRowTest {
public function provideGetPageUrl() {
//NOTE: the assumption that the URL is built by replacing $1
// with the urlencoded version of $page
// is true for SiteObject but not guaranteed for subclasses.
// is true for Site but not guaranteed for subclasses.
// Subclasses need to override this provider appropriately.
return array(
@ -270,11 +244,10 @@ class SiteObjectTest extends ORMRowTest {
* @dataProvider provideGetPageUrl
*/
public function testGetPageUrl( $path, $page, $expected ) {
/* @var SiteObject $site */
$site = $this->getRowInstance( $this->getMockFields(), false );
$site = new Site();
//NOTE: the assumption that getPageUrl is based on getLinkPath
// is true for SiteObject but not guaranteed for subclasses.
// is true for Site but not guaranteed for subclasses.
// Subclasses need to override this test case appropriately.
$site->setLinkPath( $path );
$this->assertContains( $path, $site->getPageUrl() );

View file

@ -39,21 +39,22 @@ class TestSites {
public static function getSites() {
$sites = array();
$site = Sites::newSite( 'foobar' );
$site = new Site();
$site->setGlobalId( 'foobar' );
$sites[] = $site;
$site = Sites::newSite( 'enwiktionary' );
$site = new MediaWikiSite();
$site->setGlobalId( 'enwiktionary' );
$site->setGroup( 'wiktionary' );
$site->setType( Site::TYPE_MEDIAWIKI );
$site->setLanguageCode( 'en' );
$site->addNavigationId( 'enwiktionary' );
$site->setPath( MediaWikiSite::PATH_PAGE, "https://en.wiktionary.org/wiki/$1" );
$site->setPath( MediaWikiSite::PATH_FILE, "https://en.wiktionary.org/w/$1" );
$sites[] = $site;
$site = Sites::newSite( 'dewiktionary' );
$site = new MediaWikiSite();
$site->setGlobalId( 'dewiktionary' );
$site->setGroup( 'wiktionary' );
$site->setType( Site::TYPE_MEDIAWIKI );
$site->setLanguageCode( 'de' );
$site->addInterwikiId( 'dewiktionary' );
$site->addInterwikiId( 'wiktionaryde' );
@ -61,9 +62,9 @@ class TestSites {
$site->setPath( MediaWikiSite::PATH_FILE, "https://de.wiktionary.org/w/$1" );
$sites[] = $site;
$site = Sites::newSite( 'spam' );
$site = new Site();
$site->setGlobalId( 'spam' );
$site->setGroup( 'spam' );
$site->setType( Site::TYPE_UNKNOWN );
$site->setLanguageCode( 'en' );
$site->addNavigationId( 'spam' );
$site->addNavigationId( 'spamz' );
@ -72,9 +73,9 @@ class TestSites {
$sites[] = $site;
foreach ( array( 'en', 'de', 'nl', 'sv', 'sr', 'no', 'nn' ) as $langCode ) {
$site = Sites::newSite( $langCode . 'wiki' );
$site = new MediaWikiSite();
$site->setGlobalId( $langCode . 'wiki' );
$site->setGroup( 'wikipedia' );
$site->setType( Site::TYPE_MEDIAWIKI );
$site->setLanguageCode( $langCode );
$site->addInterwikiId( $langCode );
$site->addNavigationId( $langCode );
@ -94,21 +95,21 @@ class TestSites {
public static function insertIntoDb() {
$dbw = wfGetDB( DB_MASTER );
$dbw->begin( __METHOD__ );
$trx = $dbw->trxLevel();
if ( $trx == 0 ) {
$dbw->begin( __METHOD__ );
}
$dbw->delete( 'sites', '*', __METHOD__ );
$dbw->delete( 'site_identifiers', '*', __METHOD__ );
/**
* @var Site $site
*/
foreach ( TestSites::getSites() as $site ) {
$site->save();
$sitesTable = SiteSQLStore::newInstance();
$sitesTable->saveSites( TestSites::getSites() );
if ( $trx == 0 ) {
$dbw->commit( __METHOD__ );
}
$dbw->commit( __METHOD__ );
Sites::singleton()->getSites( false ); // re-cache
}
}