From a00337c3f86f0c71c7e49b57cb3e48e6bd1db5f9 Mon Sep 17 00:00:00 2001 From: jeroendedauw Date: Fri, 18 Jan 2013 10:34:14 +0100 Subject: [PATCH] Design improvements to sites code Change-Id: I08ffa6a97093abbe85169f664b97498c5f39bf8e --- includes/AutoLoader.php | 9 +- includes/site/MediaWikiSite.php | 31 +- includes/site/Site.php | 511 +++++++++++++--- includes/site/SiteArray.php | 260 -------- includes/site/SiteList.php | 181 +++++- includes/site/SiteObject.php | 570 ------------------ includes/site/SiteSQLStore.php | 375 ++++++++++++ includes/site/SiteStore.php | 54 ++ includes/site/Sites.php | 191 ------ includes/site/SitesTable.php | 136 ----- tests/TestsAutoLoader.php | 2 +- .../includes/site/MediaWikiSiteTest.php | 31 +- tests/phpunit/includes/site/SiteArrayTest.php | 92 --- tests/phpunit/includes/site/SiteListTest.php | 27 +- .../{SitesTest.php => SiteSQLStoreTest.php} | 66 +- .../site/{SiteObjectTest.php => SiteTest.php} | 83 +-- tests/phpunit/includes/site/TestSites.php | 39 +- 17 files changed, 1155 insertions(+), 1503 deletions(-) delete mode 100644 includes/site/SiteArray.php delete mode 100644 includes/site/SiteObject.php create mode 100644 includes/site/SiteSQLStore.php create mode 100644 includes/site/SiteStore.php delete mode 100644 includes/site/Sites.php delete mode 100644 includes/site/SitesTable.php delete mode 100644 tests/phpunit/includes/site/SiteArrayTest.php rename tests/phpunit/includes/site/{SitesTest.php => SiteSQLStoreTest.php} (50%) rename tests/phpunit/includes/site/{SiteObjectTest.php => SiteTest.php} (75%) diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index 5eb497d317..c1889756b9 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -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', diff --git a/includes/site/MediaWikiSite.php b/includes/site/MediaWikiSite.php index b2e2e71882..d4613aa18b 100644 --- a/includes/site/MediaWikiSite.php +++ b/includes/site/MediaWikiSite.php @@ -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 */ diff --git a/includes/site/Site.php b/includes/site/Site.php index 200a006608..b11f2b2180 100644 --- a/includes/site/Site.php +++ b/includes/site/Site.php @@ -1,7 +1,7 @@ */ -interface Site { +class Site { const TYPE_UNKNOWN = 'unknown'; const TYPE_MEDIAWIKI = 'mediawiki'; @@ -38,42 +38,130 @@ interface Site { const SOURCE_LOCAL = 'local'; + const PATH_LINK = 'link'; + /** - * Returns the global site identifier (ie enwiktionary). + * @since 1.21 * + * @var string|null + */ + protected $globalId = null; + + /** * @since 1.21 * - * @return string + * @var string */ - public function getGlobalId(); + protected $type = self::TYPE_UNKNOWN; /** - * Sets the global site identifier (ie enwiktionary). + * @since 1.21 * + * @var string + */ + protected $group = self::GROUP_NONE; + + /** * @since 1.21 * - * @param string $globalId + * @var string */ - public function setGlobalId( $globalId ); + protected $source = self::SOURCE_LOCAL; /** - * Returns the type of the site (ie mediawiki). + * @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 * - * @return string + * @var array[] */ - public function getType(); + protected $localIds = array(); /** - * Sets the type of the site (ie mediawiki). - * TODO: remove, we cannot change this after instantiation + * @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 setType( $type ); + public function __construct( $type = self::TYPE_UNKNOWN ) { + $this->type = $type; + } + + /** + * Returns the global site identifier (ie enwiktionary). + * + * @since 1.21 + * + * @return string|null + */ + public function getGlobalId() { + return $this->globalId; + } + + /** + * Sets the global site identifier (ie enwiktionary). + * + * @since 1.21 + * + * @param string|null $globalId + * + * @throws MWException + */ + 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). + * + * @since 1.21 + * + * @return string + */ + 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,9 +255,95 @@ interface Site { * * @since 1.21 * - * @return string|false + * @return string|null + */ + public function getDomain() { + $path = $this->getLinkPath(); + + if ( $path === null ) { + return null; + } + + return parse_url( $path, PHP_URL_HOST ); + } + + /** + * Returns the protocol of the site. + * + * @since 1.21 + * + * @throws MWException + * @return string */ - public function getDomain(); + public function getProtocol() { + $path = $this->getLinkPath(); + + if ( $path === null ) { + 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. + * Shall be equivalent to setPath( getLinkPathType(), $fullUrl ). + * + * @param string $fullUrl + * + * @since 1.21 + * + * @throws MWException + */ + 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|null + */ + public function getLinkPath() { + $type = $this->getLinkPathType(); + return $type === null ? null: $this->getPath( $type ); + } + + /** + * 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 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() { + return self::PATH_LINK; + } /** * Returns the full URL for the given page on the site. @@ -138,161 +352,248 @@ interface Site { * 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 - * @see Site::getLinkPath() * - * @param bool|String $page + * @param bool|String $pageName * - * @return string|false + * @return string|boolean false */ - public function getPageUrl( $page = 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 language code of the sites primary language. - * Or false if it's not known. + * Returns $pageName without changes. + * Subclasses may override this to apply some kind of normalization. + * + * @see Site::normalizePageName * * @since 1.21 * - * @return string|false + * @param string $pageName + * + * @return string */ - public function getLanguageCode(); + public function normalizePageName( $pageName ) { + return $pageName; + } /** - * Sets language code of the sites primary language. + * Returns the type specific fields. * * @since 1.21 * - * @param string $languageCode + * @return array */ - public function setLanguageCode( $languageCode ); + public function getExtraData() { + return $this->extraData; + } /** - * 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. + * Sets the type specific fields. + * + * @since 1.21 * - * 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. + * @param array $extraData + */ + public function setExtraData( array $extraData ) { + $this->extraData = $extraData; + } + + /** + * Returns the type specific config. * * @since 1.21 * - * @param string $pageName + * @return array + */ + public function getExtraConfig() { + return $this->extraConfig; + } + + /** + * Sets the type specific config. + * + * @since 1.21 * - * @return string the normalized page name + * @param array $extraConfig */ - public function normalizePageName( $pageName ); + public function setExtraConfig( array $extraConfig ) { + $this->extraConfig = $extraConfig; + } /** - * Returns the interwiki link identifiers that can be used for this site. + * Returns language code of the sites primary language. + * Or null if it's not known. * * @since 1.21 * - * @return array of string + * @return string|null */ - public function getInterwikiIds(); + public function getLanguageCode() { + return $this->languageCode; + } /** - * Returns the equivalent link identifiers that can be used to make - * the site show up in interfaces such as the "language links" section. + * Sets language code of the sites primary language. * * @since 1.21 * - * @return array of string + * @param string $languageCode */ - public function getNavigationIds(); + public function setLanguageCode( $languageCode ) { + $this->languageCode = $languageCode; + } /** - * Adds an local identifier to the site. + * Returns the set internal identifier for the site. * * @since 1.21 * - * @param string $type The type of the identifier, element of the Site::ID_ enum - * @param string $identifier + * @return string|null */ - public function addLocalId( $type, $identifier ); + public function getInternalId() { + return $this->internalId; + } /** - * Adds an interwiki id to the site. + * Sets the internal identifier for the site. + * This typically is a primary key in a db table. * * @since 1.21 * - * @param string $identifier + * @param int|null $internalId */ - public function addInterwikiId( $identifier ); + public function setInternalId( $internalId = null ) { + $this->internalId = $internalId; + } /** - * Adds a navigation id to the site. + * Adds a local identifier. * * @since 1.21 * + * @param string $type * @param string $identifier */ - public function addNavigationId( $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; + } + } /** - * Saves the site. + * Adds an interwiki id to the site. * * @since 1.21 * - * @param string|null $functionName + * @param string $identifier */ - public function save( $functionName = null ); + public function addInterwikiId( $identifier ) { + $this->addLocalId( self::ID_INTERWIKI, $identifier ); + } /** - * Returns the internal ID of the site. + * Adds a navigation id to the site. * * @since 1.21 * - * @return integer + * @param string $identifier */ - public function getInternalId(); + public function addNavigationId( $identifier ) { + $this->addLocalId( self::ID_EQUIVALENT, $identifier ); + } /** - * Sets the provided url as path of the specified type. + * Returns the interwiki link identifiers that can be used for this site. * * @since 1.21 * - * @param string $pathType - * @param string $fullUrl + * @return string[] */ - public function setPath( $pathType, $fullUrl ); + public function getInterwikiIds() { + return array_key_exists( self::ID_INTERWIKI, $this->localIds ) ? $this->localIds[self::ID_INTERWIKI] : array(); + } /** - * Returns the path of the provided type or false if there is no such 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 * - * @param string $pathType + * @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 string|false + * @return array[] */ - public function getPath( $pathType ); + 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 * - * @since 1.21 + * @throws MWException */ - public function setLinkPath( $fullUrl ); + public function setPath( $pathType, $fullUrl ) { + if ( !is_string( $fullUrl ) ) { + throw new MWException( '$fullUrl needs to be a string' ); + } - /** - * 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 - */ - public function getLinkPath(); + if ( !array_key_exists( 'paths', $this->extraData ) ) { + $this->extraData['paths'] = array(); + } + + $this->extraData['paths'][$pathType] = $fullUrl; + } /** - * Returns the path type used to construct links with. + * Returns the path of the provided type or false if there is no such path. + * + * @since 1.21 + * + * @param string $pathType * - * @return string|false + * @return string|null */ - public function getLinkPathType(); + 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] ); + } + } -} \ No newline at end of file + // 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 {} \ No newline at end of file diff --git a/includes/site/SiteArray.php b/includes/site/SiteArray.php deleted file mode 100644 index 5d2c86b220..0000000000 --- a/includes/site/SiteArray.php +++ /dev/null @@ -1,260 +0,0 @@ - - */ -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; - } - -} diff --git a/includes/site/SiteList.php b/includes/site/SiteList.php index 6273364438..35e11a151f 100644 --- a/includes/site/SiteList.php +++ b/includes/site/SiteList.php @@ -1,7 +1,7 @@ */ -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 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 */ - public function isEmpty(); + protected function getSerializationData() { + return array_merge( + parent::getSerializationData(), + array( + 'internalIds' => $this->byInternalId, + 'globalIds' => $this->byGlobalId, + ) + ); + } -} \ No newline at end of file + /** + * @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 {} \ No newline at end of file diff --git a/includes/site/SiteObject.php b/includes/site/SiteObject.php deleted file mode 100644 index 217f860c61..0000000000 --- a/includes/site/SiteObject.php +++ /dev/null @@ -1,570 +0,0 @@ - - * @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 ); - } - -} diff --git a/includes/site/SiteSQLStore.php b/includes/site/SiteSQLStore.php new file mode 100644 index 0000000000..724f115768 --- /dev/null +++ b/includes/site/SiteSQLStore.php @@ -0,0 +1,375 @@ + + */ +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 ); + } + +} \ No newline at end of file diff --git a/includes/site/SiteStore.php b/includes/site/SiteStore.php new file mode 100644 index 0000000000..2091f3c226 --- /dev/null +++ b/includes/site/SiteStore.php @@ -0,0 +1,54 @@ + - */ -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; - } - -} diff --git a/includes/site/SitesTable.php b/includes/site/SitesTable.php deleted file mode 100644 index bb12740d77..0000000000 --- a/includes/site/SitesTable.php +++ /dev/null @@ -1,136 +0,0 @@ - - */ -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 ); - } - -} \ No newline at end of file diff --git a/tests/TestsAutoLoader.php b/tests/TestsAutoLoader.php index 8373b514c9..30b9f94ac0 100644 --- a/tests/TestsAutoLoader.php +++ b/tests/TestsAutoLoader.php @@ -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 diff --git a/tests/phpunit/includes/site/MediaWikiSiteTest.php b/tests/phpunit/includes/site/MediaWikiSiteTest.php index 208ab1ef78..0cecdeeac6 100644 --- a/tests/phpunit/includes/site/MediaWikiSiteTest.php +++ b/tests/phpunit/includes/site/MediaWikiSiteTest.php @@ -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 ) ); } diff --git a/tests/phpunit/includes/site/SiteArrayTest.php b/tests/phpunit/includes/site/SiteArrayTest.php deleted file mode 100644 index 613f63f619..0000000000 --- a/tests/phpunit/includes/site/SiteArrayTest.php +++ /dev/null @@ -1,92 +0,0 @@ - - */ -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() ) ); - } - } - -} \ No newline at end of file diff --git a/tests/phpunit/includes/site/SiteListTest.php b/tests/phpunit/includes/site/SiteListTest.php index bb8367fe59..7b882126fd 100644 --- a/tests/phpunit/includes/site/SiteListTest.php +++ b/tests/phpunit/includes/site/SiteListTest.php @@ -1,7 +1,7 @@ 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() ) ); + } + } } \ No newline at end of file diff --git a/tests/phpunit/includes/site/SitesTest.php b/tests/phpunit/includes/site/SiteSQLStoreTest.php similarity index 50% rename from tests/phpunit/includes/site/SitesTest.php rename to tests/phpunit/includes/site/SiteSQLStoreTest.php index 7675d42e09..58a4e1fecb 100644 --- a/tests/phpunit/includes/site/SitesTest.php +++ b/tests/phpunit/includes/site/SiteSQLStoreTest.php @@ -1,7 +1,7 @@ */ -class SitesTest extends MediaWikiTestCase { +class SiteSQLStoreTest extends MediaWikiTestCase { - public function setUp() { - parent::setUp(); + public function testGetSites() { + $expectedSites = TestSites::getSites(); TestSites::insertIntoDb(); - } - public function testSingleton() { - $this->assertInstanceOf( 'Sites', Sites::singleton() ); - $this->assertTrue( Sites::singleton() === Sites::singleton() ); - } - - public function testGetSites() { - $this->assertInstanceOf( 'SiteList', Sites::singleton()->getSites() ); - } + $sitesTable = SiteSQLStore::newInstance(); + $sites = $sitesTable->getSites(); - public function testGetSite() { - $count = 0; - $sites = Sites::singleton()->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() ); } } diff --git a/tests/phpunit/includes/site/SiteObjectTest.php b/tests/phpunit/includes/site/SiteTest.php similarity index 75% rename from tests/phpunit/includes/site/SiteObjectTest.php rename to tests/phpunit/includes/site/SiteTest.php index 207f46c080..0336a51775 100644 --- a/tests/phpunit/includes/site/SiteObjectTest.php +++ b/tests/phpunit/includes/site/SiteTest.php @@ -1,7 +1,7 @@ */ -class SiteObjectTest extends ORMRowTest { +class SiteTest extends MediaWikiTestCase { - /** - * @see ORMRowTest::getRowClass - * @since 1.21 - * @return string - */ - protected function getRowClass() { - return 'SiteObject'; - } - - /** - * @see ORMRowTest::getTableInstance - * @since 1.21 - * @return IORMTable - */ - protected function getTableInstance() { - return SitesTable::singleton(); - } - - /** - * @see ORMRowTest::constructorTestProvider - * @since 1.21 - * @return array - */ - public function constructorTestProvider() { - $argLists = array(); - - $argLists[] = array( 'global_key' => 'foo' ); + public function instanceProvider() { + $instances = array(); - $argLists[] = array( 'global_key' => 'bar', 'type' => Site::TYPE_MEDIAWIKI ); + $instances[] = new Site(); - $constructorArgs = array(); + $site = new Site(); + $site->setGlobalId( 'enwiki' ); + $site->setInternalId( 42 ); + $instances[] = $site; - foreach ( $argLists as $argList ) { - $constructorArgs[] = array( $argList, true ); - } + $site = new MediaWikiSite(); + $site->setGlobalId( 'nlwiki' ); + $site->setLanguageCode( 'nl' ); + $instances[] = $site; - 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() ); diff --git a/tests/phpunit/includes/site/TestSites.php b/tests/phpunit/includes/site/TestSites.php index 6003a8d285..d9861cda91 100644 --- a/tests/phpunit/includes/site/TestSites.php +++ b/tests/phpunit/includes/site/TestSites.php @@ -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() ); - $dbw->commit( __METHOD__ ); - - Sites::singleton()->getSites( false ); // re-cache + if ( $trx == 0 ) { + $dbw->commit( __METHOD__ ); + } } } \ No newline at end of file -- 2.20.1