Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
83.33% covered (warning)
83.33%
45 / 54
91.67% covered (success)
91.67%
11 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
TitleValue
84.91% covered (warning)
84.91%
45 / 53
91.67% covered (success)
91.67%
11 / 12
25.98
0.00% covered (danger)
0.00%
0 / 1
 tryNew
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 newFromPage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 newFromLinkTarget
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 castPageToLinkTarget
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 assertValidSpec
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
6
 getNamespace
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFragment
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDBkey
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 createFragmentTarget
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 getInterwiki
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __toString
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2/**
3 * Representation of a page title within MediaWiki.
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 * @file
21 * @author Daniel Kinzler
22 */
23
24namespace MediaWiki\Title;
25
26use InvalidArgumentException;
27use MediaWiki\Linker\LinkTarget;
28use MediaWiki\Page\PageReference;
29use Stringable;
30use Wikimedia\Assert\Assert;
31use Wikimedia\Assert\ParameterAssertionException;
32use Wikimedia\Assert\ParameterTypeException;
33use Wikimedia\Parsoid\Core\LinkTarget as ParsoidLinkTarget;
34use Wikimedia\Parsoid\Core\LinkTargetTrait;
35
36/**
37 * Represents the target of a wiki link.
38 *
39 * @note In contrast to Title, this is designed to be a plain value object. That is,
40 * it is immutable, does not use global state, and causes no side effects.
41 *
42 * @newable
43 *
44 * @see https://www.mediawiki.org/wiki/Manual:Modeling_pages
45 * @since 1.23
46 */
47class TitleValue implements Stringable, LinkTarget {
48    use LinkTargetTrait;
49
50    /** @var int */
51    private $namespace;
52
53    /** @var string */
54    private $dbkey;
55
56    /** @var string */
57    private $fragment;
58
59    /** @var string */
60    private $interwiki;
61
62    /**
63     * Text form including namespace/interwiki, initialised on demand
64     *
65     * Only public to share cache with TitleFormatter
66     *
67     * @internal
68     * @var string
69     */
70    public $prefixedText = null;
71
72    /**
73     * Constructs a TitleValue, or returns null if the parameters are not valid.
74     *
75     * @note This does not perform any normalization, and only basic validation.
76     * For full normalization and validation, use TitleParser::makeTitleValueSafe().
77     *
78     * @param int $namespace The namespace ID. This is not validated.
79     * @param string $title The page title in either DBkey or text form. No normalization is applied
80     *   beyond underscore/space conversion.
81     * @param string $fragment The fragment title. Use '' to represent the whole page.
82     *   No validation or normalization is applied.
83     * @param string $interwiki The interwiki component.
84     *   No validation or normalization is applied.
85     * @return TitleValue|null
86     * @throws InvalidArgumentException
87     */
88    public static function tryNew( $namespace, $title, $fragment = '', $interwiki = '' ) {
89        if ( !is_int( $namespace ) ) {
90            throw new ParameterTypeException( '$namespace', 'int' );
91        }
92
93        try {
94            return new static( $namespace, $title, $fragment, $interwiki );
95        } catch ( ParameterAssertionException $ex ) {
96            return null;
97        }
98    }
99
100    /**
101     * Create a TitleValue from a local PageReference.
102     *
103     * @note The PageReference may belong to another wiki. In that case, the resulting TitleValue
104     *       is also logically bound to that other wiki. No attempt is made to map the
105     *       PageReference wiki ID to an interwiki prefix for the TitleValue.
106     *
107     * @since 1.36
108     * @param PageReference $page
109     * @return TitleValue
110     */
111    public static function newFromPage( PageReference $page ): TitleValue {
112        return new TitleValue( $page->getNamespace(), $page->getDBkey() );
113    }
114
115    /**
116     * Create a TitleValue from a LinkTarget
117     * @param ParsoidLinkTarget $linkTarget
118     * @return TitleValue
119     * @since 1.42
120     */
121    public static function newFromLinkTarget( ParsoidLinkTarget $linkTarget ): TitleValue {
122        if ( $linkTarget instanceof TitleValue ) {
123            return $linkTarget;
124        }
125        return new TitleValue(
126            $linkTarget->getNamespace(),
127            $linkTarget->getDBkey(),
128            $linkTarget->getFragment(),
129            $linkTarget->getInterwiki()
130        );
131    }
132
133    /**
134     * Casts a PageReference to a LinkTarget.
135     *
136     * If $page is null, null is returned.
137     * If $page is also an instance of LinkTarget, $page is returned unchanged.
138     *
139     * @see newFromPage()
140     * @since 1.37
141     * @param PageReference|null $page
142     * @return LinkTarget|null
143     */
144    public static function castPageToLinkTarget( ?PageReference $page ): ?LinkTarget {
145        if ( !$page || $page instanceof LinkTarget ) {
146            return $page;
147        }
148
149        return self::newFromPage( $page );
150    }
151
152    /**
153     * Construct a TitleValue.
154     *
155     * @note TitleValue expects a valid namespace and name; typically, a TitleValue is constructed
156     * either from a database entry, or by a TitleParser. For constructing a TitleValue from user
157     * input or external sources, use a TitleParser.
158     *
159     * @stable to call
160     * @param int $namespace The namespace ID. This is not validated.
161     * @param string $title The page title in either DBkey or text form. No normalization is applied
162     *   beyond underscore/space conversion.
163     * @param string $fragment The fragment title. Use '' to represent the whole page.
164     *   No validation or normalization is applied.
165     * @param string $interwiki The interwiki component.
166     *   No validation or normalization is applied.
167     * @throws InvalidArgumentException
168     */
169    public function __construct( $namespace, $title, $fragment = '', $interwiki = '' ) {
170        self::assertValidSpec( $namespace, $title, $fragment, $interwiki );
171
172        $this->namespace = $namespace;
173        $this->dbkey = strtr( $title, ' ', '_' );
174        $this->fragment = $fragment;
175        $this->interwiki = $interwiki;
176    }
177
178    /**
179     * Assert that the given parameters could be used to construct a TitleValue object.
180     *
181     * Performs basic syntax and consistency checks. Does not perform full validation,
182     * use TitleParser::makeTitleValueSafe() for that.
183     *
184     * @param int $namespace
185     * @param string $title
186     * @param string $fragment
187     * @param string $interwiki
188     * @throws InvalidArgumentException if the combination of parameters is not valid for
189     *  constructing a TitleValue.
190     */
191    public static function assertValidSpec( $namespace, $title, $fragment = '', $interwiki = '' ) {
192        if ( !is_int( $namespace ) ) {
193            throw new ParameterTypeException( '$namespace', 'int' );
194        }
195        if ( !is_string( $title ) ) {
196            throw new ParameterTypeException( '$title', 'string' );
197        }
198        if ( !is_string( $fragment ) ) {
199            throw new ParameterTypeException( '$fragment', 'string' );
200        }
201        if ( !is_string( $interwiki ) ) {
202            throw new ParameterTypeException( '$interwiki', 'string' );
203        }
204
205        Assert::parameter( !preg_match( '/^[_ ]|[\r\n\t]|[_ ]$/', $title ), '$title',
206            "invalid name '$title'" );
207
208        // NOTE: As of MW 1.34, [[#]] is rendered as a valid link, pointing to the empty
209        // page title, effectively leading to the wiki's main page. This means that a completely
210        // empty TitleValue has to be considered valid, for consistency with Title.
211        // Also note that [[#foo]] is a valid on-page section links, and that [[acme:#foo]] is
212        // a valid interwiki link.
213        Assert::parameter(
214            $title !== '' || $namespace === NS_MAIN,
215            '$title',
216            'should not be empty unless namespace is main'
217        );
218    }
219
220    public function getNamespace(): int {
221        return $this->namespace;
222    }
223
224    public function getFragment(): string {
225        return $this->fragment;
226    }
227
228    public function getDBkey(): string {
229        return $this->dbkey;
230    }
231
232    public function createFragmentTarget( string $fragment ): self {
233        return new TitleValue(
234            $this->namespace,
235            $this->dbkey,
236            $fragment,
237            $this->interwiki
238        );
239    }
240
241    public function getInterwiki(): string {
242        return $this->interwiki;
243    }
244
245    /**
246     * Returns a string representation of the title, for logging. This is purely informative
247     * and must not be used programmatically. Use the appropriate TitleFormatter to generate
248     * the correct string representation for a given use.
249     *
250     * @since 1.23
251     * @return string
252     */
253    public function __toString(): string {
254        $name = $this->namespace . ':' . $this->dbkey;
255
256        if ( $this->fragment !== '' ) {
257            $name .= '#' . $this->fragment;
258        }
259
260        if ( $this->interwiki !== '' ) {
261            $name = $this->interwiki . ':' . $name;
262        }
263
264        return $name;
265    }
266}
267
268/** @deprecated class alias since 1.41 */
269class_alias( TitleValue::class, 'TitleValue' );