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