Completed
Push — master ( f26bc0...123a08 )
by mw
13s
created

Localizer::getPreferredLanguageByRule()   B

Complexity

Conditions 6
Paths 16

Size

Total Lines 28
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 6.1308

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 28
ccs 11
cts 13
cp 0.8462
rs 8.439
cc 6
eloc 14
nc 16
nop 1
crap 6.1308
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 66
	public function __construct( Language $contentLanguage ) {
32 66
		$this->contentLanguage = $contentLanguage;
33 66
	}
34
35
	/**
36
	 * @since 2.1
37
	 *
38
	 * @return Localizer
39
	 */
40 239
	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 239
		if ( self::$instance === null ) {
43 61
			self::$instance = new self( $GLOBALS['wgContLang'] );
44
		}
45
46 239
		return self::$instance;
47
	}
48
49
	/**
50
	 * @since 2.1
51
	 */
52 73
	public static function clear() {
53 73
		self::$instance = null;
54 73
	}
55
56
	/**
57
	 * @since 2.1
58
	 *
59
	 * @param DIWikiPage|Title|null $title
60
	 *
61
	 * @return Language
62
	 */
63 27
	public function getContentLanguage( $title = null ) {
64
65 27
		if ( $title instanceof DIWikiPage ) {
66 8
			$title = $title->getTitle();
67
		}
68
69 27
		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...
70 8
			return $title->getPageLanguage();
71
		}
72
73 20
		return $this->contentLanguage;
74
	}
75
76
	/**
77
	 * @since 2.4
78
	 *
79
	 * @return Language
80
	 */
81 234
	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...
82 234
		return $GLOBALS['wgLang'];
83
	}
84
85
	/**
86
	 * By rule means that
87
	 *
88
	 * 1. If the page content language is different from the content language then
89
	 * use the page content language (as it is clear that the page content was
90
	 * intended to be in a specific language)
91
	 * 2. If the page content language and content language are indifferent then
92
	 * use the user language
93
	 * 3. If the page content language and user language could not be determined then
94
	 * use the content language
95
	 *
96
	 * General rules:
97
	 * - Special pages are in the user language
98
	 * - Display of values (DV) should follow rules outlined above
99
	 * - Storage of values (DI) should always use the content language
100
	 *
101
	 * Notes:
102
	 * - The page content language is the language in which the content of a page is
103
	 * written in wikitext
104
	 *
105
	 * @since 2.4
106
	 *
107
	 * @param DIWikiPage|Title|null $title
108
	 *
109
	 * @return Language
110
	 */
111 2
	public function getPreferredLanguageByRule( $title = null ) {
112
113 2
		$pageLanguage = '';
114 2
		$language = $this->getUserLanguage();
115
116 2
		if ( $title instanceof DIWikiPage ) {
117
			$title = $title->getTitle();
118
		}
119
120 2
		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...
121 1
			$pageLanguage = $title->getPageLanguage();
122
		}
123
124
		// If the page language is different from the global content language
125
		// then we assume that an explicit language object was given otherwise
126
		// the Title is using the content language as fallback
127
		if (
128 2
			$pageLanguage !== '' &&
129 2
			$pageLanguage->getCode() !== $this->getContentLanguage()->getCode() ) {
130 1
			$language = $pageLanguage;
131
		}
132
133 2
		if ( $language === '' ) {
134
			$language = $this->getContentLanguage();
135
		}
136
137 2
		return $language;
138
	}
139
140
	/**
141
	 * @since 2.4
142
	 *
143
	 * @param string $languageCode
144
	 *
145
	 * @return Language
146
	 */
147 3
	public function getLanguage( $languageCode = '' ) {
148
149 3
		if ( $languageCode === '' ) {
150 1
			return $this->getContentLanguage();
151
		}
152
153 2
		return Language::factory( $languageCode );
154
	}
155
156
	/**
157
	 * @since 2.4
158
	 *
159
	 * @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...
160
	 *
161
	 * @return ExtraneousLanguage
162
	 */
163 14
	public function getExtraneousLanguage( $language = '' ) {
164
165 14
		$languageCode = $language;
166
167 14
		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...
168 14
			$languageCode = $language->getCode();
169
		}
170
171 14
		if ( $languageCode === '' ) {
172 1
			$languageCode = $this->getContentLanguage()->getCode();
173
		}
174
175 14
		return ExtraneousLanguage::getInstance()->fetchByLanguageCode( $languageCode );
176
	}
177
178
	/**
179
	 * @since 2.1
180
	 *
181
	 * @param integer $namespaceId
182
	 *
183
	 * @return string
184
	 */
185 51
	public function getNamespaceTextById( $namespaceId ) {
186 51
		return $this->contentLanguage->getNsText( $namespaceId );
187
	}
188
189
	/**
190
	 * @since 2.1
191
	 *
192
	 * @param string $namespaceName
193
	 *
194
	 * @return integer|boolean
195
	 */
196 2
	public function getNamespaceIndexByName( $namespaceName ) {
197 2
		return $this->contentLanguage->getNsIndex( str_replace( ' ', '_', $namespaceName ) );
198
	}
199
200
	/**
201
	 * @since 2.4
202
	 *
203
	 * @param string $languageCode
204
	 *
205
	 * @return boolean
206
	 */
207 6
	public static function isSupportedLanguage( $languageCode ) {
208
209 6
		$languageCode = mb_strtolower( $languageCode );
210
211
		// FIXME 1.19 doesn't know Language::isSupportedLanguage
212 6
		if ( !method_exists( '\Language', 'isSupportedLanguage' ) ) {
213
			return Language::isValidBuiltInCode( $languageCode );
214
		}
215
216 6
		return Language::isSupportedLanguage( $languageCode );
217
	}
218
219
	/**
220
	 * @see IETF language tag / BCP 47 standards
221
	 *
222
	 * @since 2.4
223
	 *
224
	 * @param string $languageCode
225
	 *
226
	 * @return string
227
	 */
228 159
	public static function asBCP47FormattedLanguageCode( $languageCode ) {
229 159
		return wfBCP47( $languageCode );
230
	}
231
232
	/**
233
	 * @since 2.4
234
	 *
235
	 * @param string &$value
236
	 *
237
	 * @return string|false
238
	 */
239 173
	public static function getLanguageCodeFrom( &$value ) {
240
241 173
		if ( strpos( $value, '@' ) === false ) {
242 170
			return false;
243
		}
244
245 4
		if ( ( $langCode = mb_substr( strrchr( $value, "@" ), 1 ) ) !== '' ) {
246 3
			$value = str_replace( '_', ' ', substr_replace( $value, '', ( mb_strlen( $langCode ) + 1 ) * -1 ) );
247
		}
248
249
		// Do we want to check here whether isSupportedLanguage or not?
250
251 4
		return $langCode !== '' ? $langCode : false;
252
	}
253
254
	/**
255
	 * @see Language::convertDoubleWidth
256
	 *
257
	 * Convert double-width roman characters to single-width.
258
	 * range: ff00-ff5f ~= 0020-007f
259
	 *
260
	 * @param string $string
261
	 *
262
	 * @return string
263
	 */
264 33
	public static function convertDoubleWidth( $string ) {
265 33
		static $full = null;
266 33
		static $half = null;
267
268 33
		if ( $full === null ) {
269
			$fullWidth = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
270
			$halfWidth = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
271
272
			// http://php.net/manual/en/function.str-split.php, mb_str_split
273
			$length = mb_strlen( $fullWidth, "UTF-8" );
274
			$full = array();
275
276
			for ( $i = 0; $i < $length; $i += 1 ) {
277
				$full[] = mb_substr( $fullWidth, $i, 1, "UTF-8" );
278
			}
279
280
			$half = str_split( $halfWidth );
281
		}
282
283 33
		return str_replace( $full, $half, trim( $string ) );
284
	}
285
286
}
287