Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
57.45% covered (warning)
57.45%
54 / 94
42.11% covered (danger)
42.11%
16 / 38
CRAP
0.00% covered (danger)
0.00%
0 / 1
Site
58.06% covered (warning)
58.06%
54 / 93
42.11% covered (danger)
42.11%
16 / 38
251.41
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
71.43% covered (warning)
71.43%
5 / 7
0.00% covered (danger)
0.00%
0 / 1
3.21
 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 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 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%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getNavigationIds
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 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        // Used for protocol relative URLs
282        return $protocol ?? '';
283    }
284
285    /**
286     * Set the path used to construct links with.
287     *
288     * Shall be equivalent to setPath( getLinkPathType(), $fullUrl ).
289     *
290     * @param string $fullUrl
291     * @since 1.21
292     */
293    public function setLinkPath( $fullUrl ) {
294        $type = $this->getLinkPathType();
295
296        if ( $type === null ) {
297            throw new RuntimeException( "This Site does not support link paths." );
298        }
299
300        $this->setPath( $type, $fullUrl );
301    }
302
303    /**
304     * Returns the path used to construct links with or false if there is no such path.
305     *
306     * Shall be equivalent to getPath( getLinkPathType() ).
307     *
308     * @return string|null
309     */
310    public function getLinkPath() {
311        $type = $this->getLinkPathType();
312        return $type === null ? null : $this->getPath( $type );
313    }
314
315    /**
316     * Returns the main path type, that is the type of the path that should
317     * generally be used to construct links to the target site.
318     *
319     * This default implementation returns Site::PATH_LINK as the default path
320     * type. Subclasses can override this to define a different default path
321     * type, or return false to disable site links.
322     *
323     * @since 1.21
324     *
325     * @return string|null
326     */
327    public function getLinkPathType() {
328        return self::PATH_LINK;
329    }
330
331    /**
332     * Get the full URL for the given page on the site.
333     *
334     * Returns null if the needed information is not known.
335     *
336     * This generated URL is usually based upon the path returned by getLinkPath(),
337     * but this is not a requirement.
338     *
339     * This implementation returns a URL constructed using the path returned by getLinkPath().
340     *
341     * @since 1.21
342     * @param string|false $pageName
343     * @return string|null
344     */
345    public function getPageUrl( $pageName = false ) {
346        $url = $this->getLinkPath();
347
348        if ( $url === null ) {
349            return null;
350        }
351
352        if ( $pageName !== false ) {
353            $url = str_replace( '$1', rawurlencode( $pageName ), $url );
354        }
355
356        return $url;
357    }
358
359    /**
360     * Attempt to normalize the page name in some fashion.
361     * May return false to indicate various kinds of failure.
362     *
363     * This implementation returns $pageName without changes.
364     *
365     * @see Site::normalizePageName
366     *
367     * @since 1.21
368     * @since 1.37 Added $followRedirect
369     *
370     * @param string $pageName
371     * @param int $followRedirect either MediaWikiPageNameNormalizer::FOLLOW_REDIRECT or
372     * MediaWikiPageNameNormalizer::NOFOLLOW_REDIRECT
373     *
374     * @return string|false
375     */
376    public function normalizePageName( $pageName, $followRedirect = MediaWikiPageNameNormalizer::FOLLOW_REDIRECT ) {
377        return $pageName;
378    }
379
380    /**
381     * Returns the type specific fields.
382     *
383     * @since 1.21
384     *
385     * @return array
386     */
387    public function getExtraData() {
388        return $this->extraData;
389    }
390
391    /**
392     * Sets the type specific fields.
393     *
394     * @since 1.21
395     *
396     * @param array $extraData
397     */
398    public function setExtraData( array $extraData ) {
399        $this->extraData = $extraData;
400    }
401
402    /**
403     * Returns the type specific config.
404     *
405     * @since 1.21
406     *
407     * @return array
408     */
409    public function getExtraConfig() {
410        return $this->extraConfig;
411    }
412
413    /**
414     * Sets the type specific config.
415     *
416     * @since 1.21
417     *
418     * @param array $extraConfig
419     */
420    public function setExtraConfig( array $extraConfig ) {
421        $this->extraConfig = $extraConfig;
422    }
423
424    /**
425     * Returns language code of the sites primary language.
426     * Or null if it's not known.
427     *
428     * @since 1.21
429     *
430     * @return string|null
431     */
432    public function getLanguageCode() {
433        return $this->languageCode;
434    }
435
436    /**
437     * Sets language code of the sites primary language.
438     *
439     * @since 1.21
440     *
441     * @param string|null $languageCode
442     */
443    public function setLanguageCode( $languageCode ) {
444        if ( $languageCode !== null &&
445            !MediaWikiServices::getInstance()->getLanguageNameUtils()->isValidCode( $languageCode ) ) {
446            throw new InvalidArgumentException( "$languageCode is not a valid language code." );
447        }
448        $this->languageCode = $languageCode;
449    }
450
451    /**
452     * Returns the set internal identifier for the site.
453     *
454     * @since 1.21
455     *
456     * @return int|null
457     */
458    public function getInternalId() {
459        return $this->internalId;
460    }
461
462    /**
463     * Sets the internal identifier for the site.
464     * This typically is a primary key in a db table.
465     *
466     * @since 1.21
467     *
468     * @param int|null $internalId
469     */
470    public function setInternalId( $internalId = null ) {
471        $this->internalId = $internalId;
472    }
473
474    /**
475     * Adds a local identifier.
476     *
477     * @since 1.21
478     *
479     * @param string $type
480     * @param string $identifier
481     */
482    public function addLocalId( $type, $identifier ) {
483        if ( $this->localIds === false ) {
484            $this->localIds = [];
485        }
486
487        $this->localIds[$type] ??= [];
488
489        if ( !in_array( $identifier, $this->localIds[$type] ) ) {
490            $this->localIds[$type][] = $identifier;
491        }
492    }
493
494    /**
495     * Adds an interwiki id to the site.
496     *
497     * @since 1.21
498     *
499     * @param string $identifier
500     */
501    public function addInterwikiId( $identifier ) {
502        $this->addLocalId( self::ID_INTERWIKI, $identifier );
503    }
504
505    /**
506     * Adds a navigation id to the site.
507     *
508     * @since 1.21
509     *
510     * @param string $identifier
511     */
512    public function addNavigationId( $identifier ) {
513        $this->addLocalId( self::ID_EQUIVALENT, $identifier );
514    }
515
516    /**
517     * Returns the interwiki link identifiers that can be used for this site.
518     *
519     * @since 1.21
520     *
521     * @return string[]
522     */
523    public function getInterwikiIds() {
524        return $this->localIds[self::ID_INTERWIKI] ?? [];
525    }
526
527    /**
528     * Returns the equivalent link identifiers that can be used to make
529     * the site show up in interfaces such as the "language links" section.
530     *
531     * @since 1.21
532     *
533     * @return string[]
534     */
535    public function getNavigationIds() {
536        return $this->localIds[self::ID_EQUIVALENT] ?? [];
537    }
538
539    /**
540     * Returns all local ids
541     *
542     * @since 1.21
543     *
544     * @return array[]
545     */
546    public function getLocalIds() {
547        return $this->localIds;
548    }
549
550    /**
551     * Set the path used to construct links with.
552     *
553     * Shall be equivalent to setPath( getLinkPathType(), $fullUrl ).
554     *
555     * @since 1.21
556     * @param string $pathType
557     * @param string $fullUrl
558     */
559    public function setPath( $pathType, string $fullUrl ) {
560        $this->extraData['paths'][$pathType] = $fullUrl;
561    }
562
563    /**
564     * Returns the path of the provided type or null if there is no such path.
565     *
566     * @since 1.21
567     *
568     * @param string $pathType
569     *
570     * @return string|null
571     */
572    public function getPath( $pathType ) {
573        $paths = $this->getAllPaths();
574        return $paths[$pathType] ?? null;
575    }
576
577    /**
578     * Returns the paths as associative array.
579     * The keys are path types, the values are the path urls.
580     *
581     * @since 1.21
582     *
583     * @return string[]
584     */
585    public function getAllPaths() {
586        return $this->extraData['paths'] ?? [];
587    }
588
589    /**
590     * Removes the path of the provided type if it's set.
591     *
592     * @since 1.21
593     *
594     * @param string $pathType
595     */
596    public function removePath( $pathType ) {
597        if ( array_key_exists( 'paths', $this->extraData ) ) {
598            unset( $this->extraData['paths'][$pathType] );
599        }
600    }
601
602    /**
603     * @since 1.21
604     *
605     * @param string $siteType
606     *
607     * @return Site
608     */
609    public static function newForType( $siteType ) {
610        /** @var class-string<Site>[] $siteTypes */
611        $siteTypes = MediaWikiServices::getInstance()->getMainConfig()->get(
612            MainConfigNames::SiteTypes
613        );
614
615        if ( array_key_exists( $siteType, $siteTypes ) ) {
616            return new $siteTypes[$siteType]();
617        }
618
619        return new Site();
620    }
621
622    /**
623     * @see Serializable::serialize
624     *
625     * @since 1.38
626     *
627     * @return array
628     */
629    public function __serialize() {
630        return [
631            'globalid' => $this->globalId,
632            'type' => $this->type,
633            'group' => $this->group,
634            'source' => $this->source,
635            'language' => $this->languageCode,
636            'localids' => $this->localIds,
637            'config' => $this->extraConfig,
638            'data' => $this->extraData,
639            'forward' => $this->forward,
640            'internalid' => $this->internalId,
641        ];
642    }
643
644    /**
645     * @see Serializable::unserialize
646     *
647     * @since 1.38
648     *
649     * @param array $fields
650     */
651    public function __unserialize( $fields ) {
652        $this->__construct( $fields['type'] );
653
654        $this->setGlobalId( $fields['globalid'] );
655        $this->setGroup( $fields['group'] );
656        $this->setSource( $fields['source'] );
657        $this->setLanguageCode( $fields['language'] );
658        $this->localIds = $fields['localids'];
659        $this->setExtraConfig( $fields['config'] );
660        $this->setExtraData( $fields['data'] );
661        $this->setForward( $fields['forward'] );
662        $this->setInternalId( $fields['internalid'] );
663    }
664}
665
666/** @deprecated class alias since 1.42 */
667class_alias( Site::class, 'Site' );