Completed
Push — master ( fae1d2...5f1b92 )
by mw
34:05
created

Localizer::getCanonicalizedUrlByNamespace()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 8
nc 1
nop 2
dl 0
loc 13
ccs 6
cts 6
cp 1
crap 1
rs 9.4285
c 1
b 0
f 1
1
<?php
2
3
namespace SMW;
4
5
use Language;
6
use Title;
7
8
/**
9
 * @license GNU GPL v2+
10
 * @since 2.1
11
 *
12
 * @author mwjames
13
 */
14
class Localizer {
15
16
	/**
17
	 * @var Localizer
18
	 */
19
	private static $instance = null;
20
21
	/**
22
	 * @var Language
23
	 */
24
	private $contentLanguage = null;
25
26
	/**
27
	 * @since 2.1
28
	 *
29
	 * @param Language $contentLanguage
30
	 */
31 95
	public function __construct( Language $contentLanguage ) {
32 95
		$this->contentLanguage = $contentLanguage;
33 95
	}
34
35
	/**
36
	 * @since 2.1
37
	 *
38
	 * @return Localizer
39
	 */
40 285
	public static function getInstance() {
0 ignored issues
show
Coding Style introduced by
getInstance uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
41
42 285
		if ( self::$instance === null ) {
43 90
			self::$instance = new self( $GLOBALS['wgContLang'] );
44
		}
45
46 285
		return self::$instance;
47
	}
48
49
	/**
50
	 * @since 2.1
51
	 */
52 104
	public static function clear() {
53 104
		self::$instance = null;
54 104
	}
55
56
	/**
57
	 * @since 2.1
58
	 *
59
	 * @return Language
60
	 */
61 218
	public function getContentLanguage() {
62 218
		return $this->contentLanguage;
63
	}
64
65
	/**
66
	 * @since 2.4
67
	 *
68
	 * @return Language
69
	 */
70 281
	public function getUserLanguage() {
0 ignored issues
show
Coding Style introduced by
getUserLanguage uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
71 281
		return $GLOBALS['wgLang'];
72
	}
73
74
	/**
75
	 * @note
76
	 *
77
	 * 1. If the page content language is availabe use it as preferred language
78
	 * (as it is clear that the page content was intended to be in a specific
79
	 * language)
80
	 * 2. If no page content language was assigned use the global content
81
	 * language
82
	 *
83
	 * General rules:
84
	 * - Special pages are in the user language
85
	 * - Display of values (DV) should use the user language if available otherwise
86
	 * use the content language as fallback
87
	 * - Storage of values (DI) should always use the content language
88
	 *
89
	 * Notes:
90
	 * - The page content language is the language in which the content of a page is
91
	 * written in wikitext
92
	 *
93
	 * @since 2.4
94
	 *
95
	 * @param DIWikiPage|Title|null $title
96
	 *
97
	 * @return Language
98
	 */
99 195
	public function getPreferredContentLanguage( $title = null ) {
100
101 195
		$language = '';
102
103 195
		if ( $title instanceof DIWikiPage ) {
104 187
			$title = $title->getTitle();
105
		}
106
107
		// If the page language is different from the global content language
108
		// then we assume that an explicit language object was given otherwise
109
		// the Title is using the content language as fallback
110 195
		if ( $title instanceof Title ) {
0 ignored issues
show
Bug introduced by
The class Title does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
111 188
			$language = $title->getPageLanguage();
112
		}
113
114 195
		return $language instanceof Language ? $language : $this->getContentLanguage();
0 ignored issues
show
Bug introduced by
The class Language does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
115
	}
116
117
	/**
118
	 * @since 2.4
119
	 *
120
	 * @param string $languageCode
121
	 *
122
	 * @return Language
123
	 */
124 7
	public function getLanguage( $languageCode = '' ) {
125
126 7
		if ( $languageCode === '' || !$languageCode || $languageCode === null ) {
127
			return $this->getContentLanguage();
128
		}
129
130 7
		return Language::factory( $languageCode );
131
	}
132
133
	/**
134
	 * @since 2.4
135
	 *
136
	 * @param Language|string $languageCode
0 ignored issues
show
Documentation introduced by
There is no parameter named $languageCode. Did you maybe mean $language?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
137
	 *
138
	 * @return ExtraneousLanguage
139
	 */
140 141
	public function getExtraneousLanguage( $language = '' ) {
141
142 141
		$languageCode = $language;
143
144 141
		if ( $language instanceof Language ) {
0 ignored issues
show
Bug introduced by
The class Language does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
145 22
			$languageCode = $language->getCode();
146
		}
147
148 141
		if ( $languageCode === '' || !$languageCode || $languageCode === null ) {
149 1
			$languageCode = $this->getContentLanguage()->getCode();
150
		}
151
152 141
		return ExtraneousLanguage::getInstance()->fetchByLanguageCode( $languageCode );
153
	}
154
155
	/**
156
	 * @since 2.1
157
	 *
158
	 * @param integer $namespaceId
159
	 *
160
	 * @return string
161
	 */
162 64
	public function getNamespaceTextById( $namespaceId ) {
163 64
		return $this->contentLanguage->getNsText( $namespaceId );
164
	}
165
166
	/**
167
	 * @since 2.1
168
	 *
169
	 * @param string $namespaceName
170
	 *
171
	 * @return integer|boolean
172
	 */
173 3
	public function getNamespaceIndexByName( $namespaceName ) {
174 3
		return $this->contentLanguage->getNsIndex( str_replace( ' ', '_', $namespaceName ) );
175
	}
176
177
	/**
178
	 * @since 2.4
179
	 *
180
	 * @param string $languageCode
181
	 *
182
	 * @return boolean
183
	 */
184 12
	public static function isSupportedLanguage( $languageCode ) {
185
186 12
		$languageCode = mb_strtolower( $languageCode );
187
188
		// FIXME 1.19 doesn't know Language::isSupportedLanguage
189 12
		if ( !method_exists( '\Language', 'isSupportedLanguage' ) ) {
190
			return Language::isValidBuiltInCode( $languageCode );
191
		}
192
193 12
		return Language::isSupportedLanguage( $languageCode );
194
	}
195
196
	/**
197
	 * @see IETF language tag / BCP 47 standards
198
	 *
199
	 * @since 2.4
200
	 *
201
	 * @param string $languageCode
202
	 *
203
	 * @return string
204
	 */
205 188
	public static function asBCP47FormattedLanguageCode( $languageCode ) {
206 188
		return wfBCP47( $languageCode );
207
	}
208
209
	/**
210
	 * @deprecated 2.5, use Localizer::getAnnotatedLanguageCodeFrom instead
211
	 * @since 2.4
212
	 *
213
	 * @param string &$value
214
	 *
215
	 * @return string|false
216
	 */
217
	public static function getLanguageCodeFrom( &$value ) {
218
		return self::getAnnotatedLanguageCodeFrom( $value );
219
	}
220
221
	/**
222
	 * @since 2.5
223
	 *
224
	 * @param integer $ns
225
	 * @param string $url
226
	 *
227
	 * @return string
228 205
	 */
229
	public function getCanonicalizedUrlByNamespace( $ns, $url ) {
230 205
231 200
		$namespace = $this->getNamespaceTextById( $ns );
232
233
		return str_replace(
234 10
			array(
235 9
				wfUrlencode( '/' . $namespace .':' ),
236
				'/' . $namespace .':'
237
			),
238
			'/' . \MWNamespace::getCanonicalName( $ns ) . ':',
239 10
			$url
240 8
		);
241
	}
242
243 2
	/**
244
	 * @since 2.4
245
	 *
246
	 * @param string &$value
247
	 *
248
	 * @return string|false
249
	 */
250
	public static function getAnnotatedLanguageCodeFrom( &$value ) {
251
252
		if ( strpos( $value, '@' ) === false ) {
253
			return false;
254
		}
255
256 41
		if ( ( $langCode = mb_substr( strrchr( $value, "@" ), 1 ) ) !== '' ) {
257 41
			$value = str_replace( '_', ' ', substr_replace( $value, '', ( mb_strlen( $langCode ) + 1 ) * -1 ) );
258 41
		}
259
260 41
		// Do we want to check here whether isSupportedLanguage or not?
261
		if ( $langCode !== '' && ctype_alpha( str_replace( array( '-' ), '', $langCode ) ) ) {
262
			return $langCode;
263
		}
264
265
		return false;
266
	}
267
268
	/**
269
	 * @see Language::convertDoubleWidth
270
	 *
271
	 * Convert double-width roman characters to single-width.
272
	 * range: ff00-ff5f ~= 0020-007f
273
	 *
274
	 * @param string $string
275 41
	 *
276
	 * @return string
277
	 */
278
	public static function convertDoubleWidth( $string ) {
279
		static $full = null;
280
		static $half = null;
281
282
		if ( $full === null ) {
283
			$fullWidth = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
284
			$halfWidth = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
285
286
			// http://php.net/manual/en/function.str-split.php, mb_str_split
287
			$length = mb_strlen( $fullWidth, "UTF-8" );
288
			$full = array();
289
290
			for ( $i = 0; $i < $length; $i += 1 ) {
291
				$full[] = mb_substr( $fullWidth, $i, 1, "UTF-8" );
292
			}
293
294
			$half = str_split( $halfWidth );
295
		}
296
297
		return str_replace( $full, $half, trim( $string ) );
298
	}
299
300
}
301