Site.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  1. <?php
  2. /**
  3. * Represents a single site.
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program; if not, write to the Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. * http://www.gnu.org/copyleft/gpl.html
  19. *
  20. * @since 1.21
  21. *
  22. * @file
  23. * @ingroup Site
  24. *
  25. * @license GPL-2.0-or-later
  26. * @author Jeroen De Dauw < jeroendedauw@gmail.com >
  27. */
  28. class Site implements Serializable {
  29. const TYPE_UNKNOWN = 'unknown';
  30. const TYPE_MEDIAWIKI = 'mediawiki';
  31. const GROUP_NONE = 'none';
  32. const ID_INTERWIKI = 'interwiki';
  33. const ID_EQUIVALENT = 'equivalent';
  34. const SOURCE_LOCAL = 'local';
  35. const PATH_LINK = 'link';
  36. /**
  37. * A version ID that identifies the serialization structure used by getSerializationData()
  38. * and unserialize(). This is useful for constructing cache keys in cases where the cache relies
  39. * on serialization for storing the SiteList.
  40. *
  41. * @var string A string uniquely identifying the version of the serialization structure.
  42. */
  43. const SERIAL_VERSION_ID = '2013-01-23';
  44. /**
  45. * @since 1.21
  46. *
  47. * @var string|null
  48. */
  49. protected $globalId = null;
  50. /**
  51. * @since 1.21
  52. *
  53. * @var string
  54. */
  55. protected $type = self::TYPE_UNKNOWN;
  56. /**
  57. * @since 1.21
  58. *
  59. * @var string
  60. */
  61. protected $group = self::GROUP_NONE;
  62. /**
  63. * @since 1.21
  64. *
  65. * @var string
  66. */
  67. protected $source = self::SOURCE_LOCAL;
  68. /**
  69. * @since 1.21
  70. *
  71. * @var string|null
  72. */
  73. protected $languageCode = null;
  74. /**
  75. * Holds the local ids for this site.
  76. * local id type => [ ids for this type (strings) ]
  77. *
  78. * @since 1.21
  79. *
  80. * @var array[]|false
  81. */
  82. protected $localIds = [];
  83. /**
  84. * @since 1.21
  85. *
  86. * @var array
  87. */
  88. protected $extraData = [];
  89. /**
  90. * @since 1.21
  91. *
  92. * @var array
  93. */
  94. protected $extraConfig = [];
  95. /**
  96. * @since 1.21
  97. *
  98. * @var bool
  99. */
  100. protected $forward = false;
  101. /**
  102. * @since 1.21
  103. *
  104. * @var int|null
  105. */
  106. protected $internalId = null;
  107. /**
  108. * @since 1.21
  109. *
  110. * @param string $type
  111. */
  112. public function __construct( $type = self::TYPE_UNKNOWN ) {
  113. $this->type = $type;
  114. }
  115. /**
  116. * Returns the global site identifier (ie enwiktionary).
  117. *
  118. * @since 1.21
  119. *
  120. * @return string|null
  121. */
  122. public function getGlobalId() {
  123. return $this->globalId;
  124. }
  125. /**
  126. * Sets the global site identifier (ie enwiktionary).
  127. *
  128. * @since 1.21
  129. *
  130. * @param string|null $globalId
  131. *
  132. * @throws MWException
  133. */
  134. public function setGlobalId( $globalId ) {
  135. if ( $globalId !== null && !is_string( $globalId ) ) {
  136. throw new MWException( '$globalId needs to be string or null' );
  137. }
  138. $this->globalId = $globalId;
  139. }
  140. /**
  141. * Returns the type of the site (ie mediawiki).
  142. *
  143. * @since 1.21
  144. *
  145. * @return string
  146. */
  147. public function getType() {
  148. return $this->type;
  149. }
  150. /**
  151. * Gets the group of the site (ie wikipedia).
  152. *
  153. * @since 1.21
  154. *
  155. * @return string
  156. */
  157. public function getGroup() {
  158. return $this->group;
  159. }
  160. /**
  161. * Sets the group of the site (ie wikipedia).
  162. *
  163. * @since 1.21
  164. *
  165. * @param string $group
  166. *
  167. * @throws MWException
  168. */
  169. public function setGroup( $group ) {
  170. if ( !is_string( $group ) ) {
  171. throw new MWException( '$group needs to be a string' );
  172. }
  173. $this->group = $group;
  174. }
  175. /**
  176. * Returns the source of the site data (ie 'local', 'wikidata', 'my-magical-repo').
  177. *
  178. * @since 1.21
  179. *
  180. * @return string
  181. */
  182. public function getSource() {
  183. return $this->source;
  184. }
  185. /**
  186. * Sets the source of the site data (ie 'local', 'wikidata', 'my-magical-repo').
  187. *
  188. * @since 1.21
  189. *
  190. * @param string $source
  191. *
  192. * @throws MWException
  193. */
  194. public function setSource( $source ) {
  195. if ( !is_string( $source ) ) {
  196. throw new MWException( '$source needs to be a string' );
  197. }
  198. $this->source = $source;
  199. }
  200. /**
  201. * Gets if site.tld/path/key:pageTitle should forward users to the page on
  202. * the actual site, where "key" is the local identifier.
  203. *
  204. * @since 1.21
  205. *
  206. * @return bool
  207. */
  208. public function shouldForward() {
  209. return $this->forward;
  210. }
  211. /**
  212. * Sets if site.tld/path/key:pageTitle should forward users to the page on
  213. * the actual site, where "key" is the local identifier.
  214. *
  215. * @since 1.21
  216. *
  217. * @param bool $shouldForward
  218. *
  219. * @throws MWException
  220. */
  221. public function setForward( $shouldForward ) {
  222. if ( !is_bool( $shouldForward ) ) {
  223. throw new MWException( '$shouldForward needs to be a boolean' );
  224. }
  225. $this->forward = $shouldForward;
  226. }
  227. /**
  228. * Returns the domain of the site, ie en.wikipedia.org
  229. * Or false if it's not known.
  230. *
  231. * @since 1.21
  232. *
  233. * @return string|null
  234. */
  235. public function getDomain() {
  236. $path = $this->getLinkPath();
  237. if ( $path === null ) {
  238. return null;
  239. }
  240. return parse_url( $path, PHP_URL_HOST );
  241. }
  242. /**
  243. * Returns the protocol of the site.
  244. *
  245. * @since 1.21
  246. *
  247. * @throws MWException
  248. * @return string
  249. */
  250. public function getProtocol() {
  251. $path = $this->getLinkPath();
  252. if ( $path === null ) {
  253. return '';
  254. }
  255. $protocol = parse_url( $path, PHP_URL_SCHEME );
  256. // Malformed URL
  257. if ( $protocol === false ) {
  258. throw new MWException( "failed to parse URL '$path'" );
  259. }
  260. // No schema
  261. if ( $protocol === null ) {
  262. // Used for protocol relative URLs
  263. $protocol = '';
  264. }
  265. return $protocol;
  266. }
  267. /**
  268. * Sets the path used to construct links with.
  269. * Shall be equivalent to setPath( getLinkPathType(), $fullUrl ).
  270. *
  271. * @param string $fullUrl
  272. *
  273. * @since 1.21
  274. *
  275. * @throws MWException
  276. */
  277. public function setLinkPath( $fullUrl ) {
  278. $type = $this->getLinkPathType();
  279. if ( $type === null ) {
  280. throw new MWException( "This Site does not support link paths." );
  281. }
  282. $this->setPath( $type, $fullUrl );
  283. }
  284. /**
  285. * Returns the path used to construct links with or false if there is no such path.
  286. *
  287. * Shall be equivalent to getPath( getLinkPathType() ).
  288. *
  289. * @return string|null
  290. */
  291. public function getLinkPath() {
  292. $type = $this->getLinkPathType();
  293. return $type === null ? null : $this->getPath( $type );
  294. }
  295. /**
  296. * Returns the main path type, that is the type of the path that should
  297. * generally be used to construct links to the target site.
  298. *
  299. * This default implementation returns Site::PATH_LINK as the default path
  300. * type. Subclasses can override this to define a different default path
  301. * type, or return false to disable site links.
  302. *
  303. * @since 1.21
  304. *
  305. * @return string|null
  306. */
  307. public function getLinkPathType() {
  308. return self::PATH_LINK;
  309. }
  310. /**
  311. * Returns the full URL for the given page on the site.
  312. * Or null if the needed information is not known.
  313. *
  314. * This generated URL is usually based upon the path returned by getLinkPath(),
  315. * but this is not a requirement.
  316. *
  317. * This implementation returns a URL constructed using the path returned by getLinkPath().
  318. *
  319. * @since 1.21
  320. *
  321. * @param bool|string $pageName
  322. *
  323. * @return string|null
  324. */
  325. public function getPageUrl( $pageName = false ) {
  326. $url = $this->getLinkPath();
  327. if ( $url === null ) {
  328. return null;
  329. }
  330. if ( $pageName !== false ) {
  331. $url = str_replace( '$1', rawurlencode( $pageName ), $url );
  332. }
  333. return $url;
  334. }
  335. /**
  336. * Attempt to normalize the page name in some fashion.
  337. * May return false to indicate various kinds of failure.
  338. *
  339. * This implementation returns $pageName without changes.
  340. *
  341. * @see Site::normalizePageName
  342. *
  343. * @since 1.21
  344. *
  345. * @param string $pageName
  346. *
  347. * @return string|false
  348. */
  349. public function normalizePageName( $pageName ) {
  350. return $pageName;
  351. }
  352. /**
  353. * Returns the type specific fields.
  354. *
  355. * @since 1.21
  356. *
  357. * @return array
  358. */
  359. public function getExtraData() {
  360. return $this->extraData;
  361. }
  362. /**
  363. * Sets the type specific fields.
  364. *
  365. * @since 1.21
  366. *
  367. * @param array $extraData
  368. */
  369. public function setExtraData( array $extraData ) {
  370. $this->extraData = $extraData;
  371. }
  372. /**
  373. * Returns the type specific config.
  374. *
  375. * @since 1.21
  376. *
  377. * @return array
  378. */
  379. public function getExtraConfig() {
  380. return $this->extraConfig;
  381. }
  382. /**
  383. * Sets the type specific config.
  384. *
  385. * @since 1.21
  386. *
  387. * @param array $extraConfig
  388. */
  389. public function setExtraConfig( array $extraConfig ) {
  390. $this->extraConfig = $extraConfig;
  391. }
  392. /**
  393. * Returns language code of the sites primary language.
  394. * Or null if it's not known.
  395. *
  396. * @since 1.21
  397. *
  398. * @return string|null
  399. */
  400. public function getLanguageCode() {
  401. return $this->languageCode;
  402. }
  403. /**
  404. * Sets language code of the sites primary language.
  405. *
  406. * @since 1.21
  407. *
  408. * @param string|null $languageCode
  409. */
  410. public function setLanguageCode( $languageCode ) {
  411. if ( $languageCode !== null && !Language::isValidCode( $languageCode ) ) {
  412. throw new InvalidArgumentException( "$languageCode is not a valid language code." );
  413. }
  414. $this->languageCode = $languageCode;
  415. }
  416. /**
  417. * Returns the set internal identifier for the site.
  418. *
  419. * @since 1.21
  420. *
  421. * @return string|null
  422. */
  423. public function getInternalId() {
  424. return $this->internalId;
  425. }
  426. /**
  427. * Sets the internal identifier for the site.
  428. * This typically is a primary key in a db table.
  429. *
  430. * @since 1.21
  431. *
  432. * @param int|null $internalId
  433. */
  434. public function setInternalId( $internalId = null ) {
  435. $this->internalId = $internalId;
  436. }
  437. /**
  438. * Adds a local identifier.
  439. *
  440. * @since 1.21
  441. *
  442. * @param string $type
  443. * @param string $identifier
  444. */
  445. public function addLocalId( $type, $identifier ) {
  446. if ( $this->localIds === false ) {
  447. $this->localIds = [];
  448. }
  449. if ( !array_key_exists( $type, $this->localIds ) ) {
  450. $this->localIds[$type] = [];
  451. }
  452. if ( !in_array( $identifier, $this->localIds[$type] ) ) {
  453. $this->localIds[$type][] = $identifier;
  454. }
  455. }
  456. /**
  457. * Adds an interwiki id to the site.
  458. *
  459. * @since 1.21
  460. *
  461. * @param string $identifier
  462. */
  463. public function addInterwikiId( $identifier ) {
  464. $this->addLocalId( self::ID_INTERWIKI, $identifier );
  465. }
  466. /**
  467. * Adds a navigation id to the site.
  468. *
  469. * @since 1.21
  470. *
  471. * @param string $identifier
  472. */
  473. public function addNavigationId( $identifier ) {
  474. $this->addLocalId( self::ID_EQUIVALENT, $identifier );
  475. }
  476. /**
  477. * Returns the interwiki link identifiers that can be used for this site.
  478. *
  479. * @since 1.21
  480. *
  481. * @return string[]
  482. */
  483. public function getInterwikiIds() {
  484. return array_key_exists( self::ID_INTERWIKI, $this->localIds )
  485. ? $this->localIds[self::ID_INTERWIKI]
  486. : [];
  487. }
  488. /**
  489. * Returns the equivalent link identifiers that can be used to make
  490. * the site show up in interfaces such as the "language links" section.
  491. *
  492. * @since 1.21
  493. *
  494. * @return string[]
  495. */
  496. public function getNavigationIds() {
  497. return array_key_exists( self::ID_EQUIVALENT, $this->localIds )
  498. ? $this->localIds[self::ID_EQUIVALENT] :
  499. [];
  500. }
  501. /**
  502. * Returns all local ids
  503. *
  504. * @since 1.21
  505. *
  506. * @return array[]
  507. */
  508. public function getLocalIds() {
  509. return $this->localIds;
  510. }
  511. /**
  512. * Sets the path used to construct links with.
  513. * Shall be equivalent to setPath( getLinkPathType(), $fullUrl ).
  514. *
  515. * @since 1.21
  516. *
  517. * @param string $pathType
  518. * @param string $fullUrl
  519. *
  520. * @throws MWException
  521. */
  522. public function setPath( $pathType, $fullUrl ) {
  523. if ( !is_string( $fullUrl ) ) {
  524. throw new MWException( '$fullUrl needs to be a string' );
  525. }
  526. if ( !array_key_exists( 'paths', $this->extraData ) ) {
  527. $this->extraData['paths'] = [];
  528. }
  529. $this->extraData['paths'][$pathType] = $fullUrl;
  530. }
  531. /**
  532. * Returns the path of the provided type or false if there is no such path.
  533. *
  534. * @since 1.21
  535. *
  536. * @param string $pathType
  537. *
  538. * @return string|null
  539. */
  540. public function getPath( $pathType ) {
  541. $paths = $this->getAllPaths();
  542. return array_key_exists( $pathType, $paths ) ? $paths[$pathType] : null;
  543. }
  544. /**
  545. * Returns the paths as associative array.
  546. * The keys are path types, the values are the path urls.
  547. *
  548. * @since 1.21
  549. *
  550. * @return string[]
  551. */
  552. public function getAllPaths() {
  553. return array_key_exists( 'paths', $this->extraData ) ? $this->extraData['paths'] : [];
  554. }
  555. /**
  556. * Removes the path of the provided type if it's set.
  557. *
  558. * @since 1.21
  559. *
  560. * @param string $pathType
  561. */
  562. public function removePath( $pathType ) {
  563. if ( array_key_exists( 'paths', $this->extraData ) ) {
  564. unset( $this->extraData['paths'][$pathType] );
  565. }
  566. }
  567. /**
  568. * @since 1.21
  569. *
  570. * @param string $siteType
  571. *
  572. * @return Site
  573. */
  574. public static function newForType( $siteType ) {
  575. global $wgSiteTypes;
  576. if ( array_key_exists( $siteType, $wgSiteTypes ) ) {
  577. return new $wgSiteTypes[$siteType]();
  578. }
  579. return new Site();
  580. }
  581. /**
  582. * @see Serializable::serialize
  583. *
  584. * @since 1.21
  585. *
  586. * @return string
  587. */
  588. public function serialize() {
  589. $fields = [
  590. 'globalid' => $this->globalId,
  591. 'type' => $this->type,
  592. 'group' => $this->group,
  593. 'source' => $this->source,
  594. 'language' => $this->languageCode,
  595. 'localids' => $this->localIds,
  596. 'config' => $this->extraConfig,
  597. 'data' => $this->extraData,
  598. 'forward' => $this->forward,
  599. 'internalid' => $this->internalId,
  600. ];
  601. return serialize( $fields );
  602. }
  603. /**
  604. * @see Serializable::unserialize
  605. *
  606. * @since 1.21
  607. *
  608. * @param string $serialized
  609. */
  610. public function unserialize( $serialized ) {
  611. $fields = unserialize( $serialized );
  612. $this->__construct( $fields['type'] );
  613. $this->setGlobalId( $fields['globalid'] );
  614. $this->setGroup( $fields['group'] );
  615. $this->setSource( $fields['source'] );
  616. $this->setLanguageCode( $fields['language'] );
  617. $this->localIds = $fields['localids'];
  618. $this->setExtraConfig( $fields['config'] );
  619. $this->setExtraData( $fields['data'] );
  620. $this->setForward( $fields['forward'] );
  621. $this->setInternalId( $fields['internalid'] );
  622. }
  623. }