Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
82.14% covered (warning)
82.14%
23 / 28
57.14% covered (warning)
57.14%
4 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
TitleHelper
82.14% covered (warning)
82.14%
23 / 28
57.14% covered (warning)
57.14%
4 / 7
23.51
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
4
 makeTitle
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
5
 makeRedirectTitle
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
4
 isExternal
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 identifyInterwikiPrefix
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
3.33
 sanitizeSectionFragment
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getNamespaceText
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace CirrusSearch\Search;
4
5use CirrusSearch\InterwikiResolver;
6use CirrusSearch\Util;
7use MediaWiki\MediaWikiServices;
8use Title;
9use WikiMap;
10
11/**
12 * Utility class build MW Title from elastica Result/ResultSet classes
13 * This class can be used in all classes that need to build a Title
14 * by reading the elasticsearch output.
15 */
16class TitleHelper {
17    /**
18     * @var string
19     */
20    private $hostWikiID;
21
22    /**
23     * @var InterwikiResolver
24     */
25    private $interwikiResolver;
26
27    /**
28     * @var callable accepts a string and returns a string
29     */
30    private $linkSanitizer;
31
32    /**
33     * @param string|null $hostWikiID
34     * @param InterwikiResolver|null $interwikiResolver
35     * @param callable|null $linkSanitizer
36     */
37    public function __construct( $hostWikiID = null, InterwikiResolver $interwikiResolver = null, callable $linkSanitizer = null ) {
38        $this->hostWikiID = $hostWikiID ?: WikiMap::getCurrentWikiId();
39        $this->interwikiResolver = $interwikiResolver ?: MediaWikiServices::getInstance()->getService( InterwikiResolver::SERVICE );
40        $this->linkSanitizer = $linkSanitizer ?: [ \Sanitizer::class, 'escapeIdForLink' ];
41    }
42
43    /**
44     * Create a title. When making interwiki titles we should be providing the
45     * namespace text as a portion of the text, rather than a namespace id,
46     * because namespace id's are not consistent across wiki's. This
47     * additionally prevents the local wiki from localizing the namespace text
48     * when it should be using the localized name of the remote wiki.
49     *
50     * @param \Elastica\Result $r int $namespace
51     * @return Title
52     */
53    public function makeTitle( \Elastica\Result $r ): Title {
54        $iwPrefix = $this->identifyInterwikiPrefix( $r );
55        if ( empty( $iwPrefix ) && $r->namespace !== null && $r->title !== null ) {
56            return Title::makeTitle( $r->namespace, $r->title );
57        }
58
59        $nsPrefix = $r->namespace_text ? $r->namespace_text . ':' : '';
60        return Title::makeTitle( 0, $nsPrefix . $r->title, '', $iwPrefix ?? '' );
61    }
62
63    /**
64     * Build a Title to a redirect, this always works for internal titles.
65     * For external titles we need to use the namespace_text which is only
66     * valid if the redirect namespace is equals to the target title namespace.
67     * If the namespaces do not match we return null.
68     *
69     * @param \Elastica\Result $r
70     * @param string $redirectText
71     * @param int $redirNamespace
72     * @return Title|null the Title to the Redirect or null if we can't build it
73     */
74    public function makeRedirectTitle( \Elastica\Result $r, $redirectText, $redirNamespace ) {
75        $iwPrefix = self::identifyInterwikiPrefix( $r );
76        if ( empty( $iwPrefix ) ) {
77            return Title::makeTitle( $redirNamespace, $redirectText );
78        }
79        if ( $redirNamespace === $r->namespace ) {
80            $nsPrefix = $r->namespace_text ? $r->namespace_text . ':' : '';
81            return Title::makeTitle(
82                0,
83                $nsPrefix . $redirectText,
84                '',
85                $iwPrefix
86            );
87        } else {
88            // redir namespace does not match, we can't
89            // build this title.
90            // The caller should fallback to the target title.
91            return null;
92        }
93    }
94
95    /**
96     * @param \Elastica\Result $r
97     * @return bool true if this result refers to an external Title
98     */
99    public function isExternal( \Elastica\Result $r ) {
100        if ( isset( $r->wiki ) && $r->wiki !== $this->hostWikiID ) {
101            return true;
102        }
103        // no wiki is suspicious, should we log a warning?
104        return false;
105    }
106
107    /**
108     * @param \Elastica\Result $r
109     * @return string|null the interwiki prefix for this result or null or
110     * empty if local.
111     */
112    private function identifyInterwikiPrefix( $r ) {
113        if ( isset( $r->wiki ) && $r->wiki !== $this->hostWikiID ) {
114            return $this->interwikiResolver->getInterwikiPrefix( $r->wiki );
115        }
116        // no wiki is suspicious, should we log something?
117        return null;
118    }
119
120    /**
121     * @param string $id
122     * @return string
123     */
124    public function sanitizeSectionFragment( $id ) {
125        return ( $this->linkSanitizer )( $id );
126    }
127
128    /**
129     * @param Title $title
130     * @return string
131     */
132    public function getNamespaceText( Title $title ) {
133        return Util::getNamespaceText( $title );
134    }
135}