Completed
Push — master ( d2d28e...1c2760 )
by mw
35:37
created

includes/datavalues/SMW_DV_Number.php (8 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
use SMW\DataValues\ValueFormatters\DataValueFormatter;
4
use SMW\IntlNumberFormatter;
5
use SMW\Localizer;
6
use SMW\Message;
7
use SMW\ApplicationFactory;
8
9
/**
10
 * @ingroup SMWDataValues
11
 */
12
13
/**
14
 * This datavalue implements numerical datavalues, and supports optional
15
 * unit conversions. It parses and manages unit strings, since even plain
16
 * numbers may have (not further specified) units that are stored. However,
17
 * only subclasses implement full unit conversion by extending the methods
18
 * convertToMainUnit() and makeConversionValues().
19
 *
20
 * Units work as follows: a unit is a string, but many such strings might
21
 * refer to the same unit of measurement. There is always one string, that
22
 * canonically represents the unit, and we will call this version of writing
23
 * the unit the /unit id/. IDs for units are needed for tasks like duplicate
24
 * avoidance. If no conversion information is given, any unit is its own ID.
25
 * In any case, units are /normalised/, i.e. given a more standardised meaning
26
 * before being processed. All units, IDs or otherwise, should be suitable for
27
 * printout in wikitext, and main IDs should moreover be suitable for printout
28
 * in HTML.
29
 *
30
 * Subclasses that support unit conversion may interpret the output format set
31
 * via setOutputFormat() to allow a unit to be selected for display. Note that
32
 * this setting does not affect the internal representation of the value
33
 * though. So chosing a specific output format will change the behavior of
34
 * output functions like getLongWikiText(), but not of functions that access
35
 * the value itself, such as getUnit() or getDBKeys().
36
 *
37
 * @author Markus Krötzsch
38
 * @ingroup SMWDataValues
39
 *
40
 * @todo Wiki-HTML-conversion for unit strings must be revisited, as the current
41
 * solution might be unsafe.
42
 */
43
class SMWNumberValue extends SMWDataValue {
44
45
	/**
46
	 * Array with entries unit=>value, mapping a normalized unit to the
47
	 * converted value. Used for conversion tooltips.
48
	 * @var array
49
	 */
50
	protected $m_unitvalues;
51
52
	/**
53
	 * Whether the unit is preferred as prefix or not
54
	 *
55
	 * @var array
56
	 */
57
	protected $prefixalUnitPreference = array();
58
59
	/**
60
	 * Canonical identifier for the unit that the user gave as input. Used
61
	 * to avoid printing this in conversion tooltips again. If the
62
	 * outputformat was set to show another unit, then the values of
63
	 * $m_caption and $m_unitin will be updated as if the formatted string
64
	 * had been the original user input, i.e. the two values reflect what
65
	 * is currently printed.
66
	 * @var string
67
	 */
68
	protected $m_unitin;
69
70
	/**
71
	 * @var integer|null
72
	 */
73
	protected $precision = null;
74
75
	/**
76
	 * @var IntlNumberFormatter
77
	 */
78
	private $intlNumberFormatter = null;
79
80
	/**
81
	 * @since 2.4
82
	 *
83
	 * @param string $typeid
84
	 */
85 52
	public function __construct( $typeid = '' ) {
86 52
		parent::__construct( $typeid );
87 52
		$this->intlNumberFormatter = IntlNumberFormatter::getInstance();
88 52
		$this->intlNumberFormatter->reset();
89 52
	}
90
91
	/**
92
	 * Parse a string of the form "number unit" where unit is optional. The
93
	 * results are stored in the $number and $unit parameters. Returns an
94
	 * error code.
95
	 * @param $value string to parse
96
	 * @param $number call-by-ref parameter that will be set to the numerical value
97
	 * @param $unit call-by-ref parameter that will be set to the "unit" string (after the number)
98
	 * @return integer 0 (no errors), 1 (no number found at all), 2 (number
99
	 * too large for this platform)
100
	 */
101 50
	public function parseNumberValue( $value, &$number, &$unit, &$asPrefix = false ) {
102
103 50
		$intlNumberFormatter = $this->getNumberFormatter();
104
105
		// Parse to find $number and (possibly) $unit
106 50
		$kiloseparator = $intlNumberFormatter->getSeparatorByLanguage(
107 50
			IntlNumberFormatter::THOUSANDS_SEPARATOR,
108 50
			IntlNumberFormatter::CONTENT_LANGUAGE
109
		);
110
111 50
		$decseparator = $intlNumberFormatter->getSeparatorByLanguage(
112 50
			IntlNumberFormatter::DECIMAL_SEPARATOR,
113 50
			IntlNumberFormatter::CONTENT_LANGUAGE
114
		);
115
116
		// #753
117
		$regex = '/([-+]?\s*(?:' .
118
				// Either numbers like 10,000.99 that start with a digit
119 50
				'\d+(?:\\' . $kiloseparator . '\d\d\d)*(?:\\' . $decseparator . '\d+)?' .
120
				// or numbers like .001 that start with the decimal separator
121 50
				'|\\' . $decseparator . '\d+' .
122 50
				')\s*(?:[eE][-+]?\d+)?)/u';
123
124
		// #1718 Whether to preserve spaces in unit labels or not (e.g. sq mi, sqmi)
125 50
		$space = $this->isEnabledFeature( SMW_DV_NUMV_USPACE ) ? ' ' : '';
126
127 50
		$parts = preg_split(
128
			$regex,
129 50
			trim( str_replace( array( '&nbsp;', '&#160;', '&thinsp;', ' ' ), $space, $value ) ),
130 50
			2,
131 50
			PREG_SPLIT_DELIM_CAPTURE
132
		);
133
134 50
		if ( count( $parts ) >= 2 ) {
135 50
			$numstring = str_replace( $kiloseparator, '', preg_replace( '/\s*/u', '', $parts[1] ) ); // simplify
136 50
			if ( $decseparator != '.' ) {
137 3
				$numstring = str_replace( $decseparator, '.', $numstring );
138
			}
139 50
			list( $number ) = sscanf( $numstring, "%f" );
140 50
			if ( count( $parts ) >= 3  ) {
141 50
				$asPrefix = $parts[0] !== '';
142 50
				$unit = $this->normalizeUnit( $parts[0] !== '' ? $parts[0] : $parts[2] );
143
			}
144
		}
145
146 50
		if ( ( count( $parts ) == 1 ) || ( $numstring === '' ) ) { // no number found
0 ignored issues
show
The variable $numstring does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
147 1
			return 1;
148 50
		} elseif ( is_infinite( $number ) ) { // number is too large for this platform
149
			return 2;
150
		} else {
151 50
			return 0;
152
		}
153
	}
154
155
	/**
156
	 * @see DataValue::parseUserValue
157
	 */
158 51
	protected function parseUserValue( $value ) {
159
		// Set caption
160 51
		if ( $this->m_caption === false ) {
161 51
			$this->m_caption = $value;
162
		}
163
164 51
		if ( $value !== '' && $value{0} === ':' ) {
165 1
			$this->addErrorMsg( array( 'smw-datavalue-invalid-number', $value ) );
166 1
			return;
167
		}
168
169 50
		$this->m_unitin = false;
170 50
		$this->m_unitvalues = false;
0 ignored issues
show
Documentation Bug introduced by
It seems like false of type false is incompatible with the declared type array of property $m_unitvalues.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
171 50
		$number = $unit = '';
172 50
		$error = $this->parseNumberValue( $value, $number, $unit );
173
174 50
		if ( $error == 1 ) { // no number found
175 1
			$this->addErrorMsg( array( 'smw_nofloat', $value ) );
176 50
		} elseif ( $error == 2 ) { // number is too large for this platform
177
			$this->addErrorMsg( array( 'smw_infinite', $value ) );
178 50
		} elseif ( $this->getTypeID() === '_num' && $unit !== '' ) {
179 1
			$this->addErrorMsg( array( 'smw-datavalue-number-textnotallowed', $unit, $number ) );
180 50
		} elseif ( $number === null ) {
181
			$this->addErrorMsg( array( 'smw-datavalue-number-nullnotallowed', $value ) ); // #1628
182 50
		} elseif ( $this->convertToMainUnit( $number, $unit ) === false ) { // so far so good: now convert unit and check if it is allowed
183 9
			$this->addErrorMsg( array( 'smw_unitnotallowed', $unit ) );
184
		} // note that convertToMainUnit() also sets m_dataitem if valid
185 50
	}
186
187
	/**
188
	 * @see SMWDataValue::loadDataItem()
189
	 * @param $dataitem SMWDataItem
190
	 * @return boolean
191
	 */
192 31
	protected function loadDataItem( SMWDataItem $dataItem ) {
193
194 31
		if ( $dataItem->getDIType() !== SMWDataItem::TYPE_NUMBER ) {
195
			return false;
196
		}
197
198 31
		$this->m_dataitem = $dataItem;
199 31
		$this->m_caption = false;
200 31
		$this->m_unitin = false;
201 31
		$this->makeUserValue();
202 31
		$this->m_unitvalues = false;
0 ignored issues
show
Documentation Bug introduced by
It seems like false of type false is incompatible with the declared type array of property $m_unitvalues.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
203
204 31
		return true;
205
	}
206
207
	/**
208
	 * @see DataValue::setOutputFormat
209
	 *
210
	 * @param $string $formatstring
211
	 */
212 8
	public function setOutputFormat( $formatstring ) {
213
214 8
		if ( $formatstring == $this->m_outformat ) {
215
			return null;
216
		}
217
218
		// #1591
219 8
		$this->findPreferredLanguageFrom( $formatstring );
220
221
		// #1335
222 8
		$this->m_outformat = $this->findPrecisionFrom( $formatstring );
223
224 8
		if ( $this->isValid() ) { // update caption/unitin for this format
225 8
			$this->m_caption = false;
226 8
			$this->m_unitin = false;
227 8
			$this->makeUserValue();
228
		}
229 8
	}
230
231
	/**
232
	 * @since 2.4
233
	 *
234
	 * @return float
235
	 */
236 38
	public function getLocalizedFormattedNumber( $value ) {
237 38
		return $this->getNumberFormatter()->format( $value, $this->getPreferredDisplayPrecision() );
238
	}
239
240
	/**
241
	 * @since 2.4
242
	 *
243
	 * @return float
244
	 */
245 18
	public function getNormalizedFormattedNumber( $value ) {
246 18
		return $this->getNumberFormatter()->format( $value, $this->getPreferredDisplayPrecision(), IntlNumberFormatter::VALUE_FORMAT );
247
	}
248
249
	/**
250
	 * @see DataValue::getShortWikiText
251
	 *
252
	 * @return string
253
	 */
254 36
	public function getShortWikiText( $linker = null ) {
255 36
		return $this->getDataValueFormatter()->format( DataValueFormatter::WIKI_SHORT, $linker );
256
	}
257
258
	/**
259
	 * @see DataValue::getShortHTMLText
260
	 *
261
	 * @return string
262
	 */
263 1
	public function getShortHTMLText( $linker = null ) {
264 1
		return $this->getDataValueFormatter()->format( DataValueFormatter::HTML_SHORT, $linker );
265
	}
266
267
	/**
268
	 * @see DataValue::getLongWikiText
269
	 *
270
	 * @return string
271
	 */
272
	public function getLongWikiText( $linker = null ) {
273
		return $this->getDataValueFormatter()->format( DataValueFormatter::WIKI_LONG, $linker );
274
	}
275
276
	/**
277
	 * @see DataValue::getLongHTMLText
278
	 *
279
	 * @return string
280
	 */
281 2
	public function getLongHTMLText( $linker = null ) {
282 2
		return $this->getDataValueFormatter()->format( DataValueFormatter::HTML_LONG, $linker );
283
	}
284
285 22
	public function getNumber() {
286 22
		return $this->isValid() ? $this->m_dataitem->getNumber() : 32202;
287
	}
288
289 15
	public function getWikiValue() {
290 15
		return $this->getDataValueFormatter()->format( DataValueFormatter::VALUE );
291
	}
292
293
	/**
294
	 * @see DataVelue::getInfolinks
295
	 *
296
	 * @return array
297
	 */
298 1
	public function getInfolinks() {
299
300
		// When generating an infoLink, use the normalized value without any
301
		// precision limitation
302 1
		$this->setOption( 'no.displayprecision', true );
303 1
		$this->setOption( 'content.language', Message::CONTENT_LANGUAGE );
304 1
		$infoLinks = parent::getInfolinks();
305 1
		$this->setOption( 'no.displayprecision', false );
306
307 1
		return $infoLinks;
308
	}
309
310
	/**
311
	 * @since 2.4
312
	 *
313
	 * @return string
314
	 */
315 34
	public function getCanonicalMainUnit() {
316 34
		return $this->m_unitin;
317
	}
318
319
	/**
320
	 * Returns array of converted unit-value-pairs that can be
321
	 * printed.
322
	 *
323
	 * @since 2.4
324
	 *
325
	 * @return array
326
	 */
327 34
	public function getConvertedUnitValues() {
328 34
		$this->makeConversionValues();
329 34
		return $this->m_unitvalues;
330
	}
331
332
	/**
333
	 * Return the unit in which the returned value is to be interpreted.
334
	 * This string is a plain UTF-8 string without wiki or html markup.
335
	 * The returned value is a canonical ID for the main unit.
336
	 * Returns the empty string if no unit is given for the value.
337
	 * Overwritten by subclasses that support units.
338
	 */
339 11
	public function getUnit() {
340 11
		return '';
341
	}
342
343
	/**
344
	 * @since 2.4
345
	 *
346
	 * @param string $unit
347
	 *
348
	 * @return boolean
349
	 */
350 17
	public function hasPrefixalUnitPreference( $unit ) {
351 17
		return isset( $this->prefixalUnitPreference[$unit] ) && $this->prefixalUnitPreference[$unit];
352
	}
353
354
	/**
355
	 * Create links to mapping services based on a wiki-editable message.
356
	 * The parameters available to the message are:
357
	 * $1: string of numerical value in English punctuation
358
	 * $2: string of integer version of value, in English punctuation
359
	 *
360
	 * @return array
361
	 */
362 1
	protected function getServiceLinkParams() {
363 1
		if ( $this->isValid() ) {
364 1
			return array( strval( $this->m_dataitem->getNumber() ), strval( round( $this->m_dataitem->getNumber() ) ) );
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array(strval($thi...taitem->getNumber()))); (string[]) is incompatible with the return type of the parent method SMWDataValue::getServiceLinkParams of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
365
		} else {
366
			return array();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array(); (array) is incompatible with the return type of the parent method SMWDataValue::getServiceLinkParams of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
367
		}
368
	}
369
370
	/**
371
	 * Transform a (typically unit-) string into a normalised form,
372
	 * so that, e.g., "km²" and "km<sup>2</sup>" do not need to be
373
	 * distinguished.
374
	 */
375 50
	public function normalizeUnit( $unit ) {
376 50
		$unit = str_replace( array( '[[', ']]' ), '', trim( $unit ) ); // allow simple links to be used inside annotations
377 50
		$unit = str_replace( array( '²', '<sup>2</sup>' ), '&sup2;', $unit );
378 50
		$unit = str_replace( array( '³', '<sup>3</sup>' ), '&sup3;', $unit );
379 50
		return smwfXMLContentEncode( $unit );
380
	}
381
382
	/**
383
	 * Compute the value based on the given input number and unit string.
384
	 * If the unit is not supported, return false, otherwise return true.
385
	 * This is called when parsing user input, where the given unit value
386
	 * has already been normalized.
387
	 *
388
	 * This class does not support any (non-empty) units, but subclasses
389
	 * may overwrite this behavior.
390
	 * @param $number float value obtained by parsing user input
391
	 * @param $unit string after the numericla user input
392
	 * @return boolean specifying if the unit string is allowed
393
	 */
394 39
	protected function convertToMainUnit( $number, $unit ) {
395 39
		$this->m_dataitem = new SMWDINumber( $number );
396 39
		$this->m_unitin = '';
397 39
		return ( $unit === '' );
398
	}
399
400
	/**
401
	 * This method creates an array of unit-value-pairs that should be
402
	 * printed. Units are the keys and should be canonical unit IDs.
403
	 * The result is stored in $this->m_unitvalues. Again, any class that
404
	 * requires effort for doing this should first check whether the array
405
	 * is already set (i.e. not false) before doing any work.
406
	 * Note that the values should be plain numbers. Output formatting is done
407
	 * later when needed.  Also, it should be checked if the value is valid
408
	 * before trying to calculate with its contents.
409
	 * This method also must call or implement convertToMainUnit().
410
	 *
411
	 * Overwritten by subclasses that support units.
412
	 */
413 26
	protected function makeConversionValues() {
414 26
		$this->m_unitvalues = array( '' => $this->m_dataitem->getNumber() );
415 26
	}
416
417
	/**
418
	 * This method is used when no user input was given to find the best
419
	 * values for m_unitin and m_caption. After conversion,
420
	 * these fields will look as if they were generated from user input,
421
	 * and convertToMainUnit() will have been called (if not, it would be
422
	 * blocked by the presence of m_unitin).
423
	 *
424
	 * Overwritten by subclasses that support units.
425
	 */
426 24
	protected function makeUserValue() {
427 24
		$this->m_caption = '';
428
429 24
		$number = $this->m_dataitem->getNumber();
430
431
		// -u is the format for displaying the unit only
432 24
		if ( $this->m_outformat == '-u' ) {
433
			$this->m_caption = '';
434 24
		} elseif ( ( $this->m_outformat != '-' ) && ( $this->m_outformat != '-n' ) ) {
435 24
			$this->m_caption = $this->getLocalizedFormattedNumber( $number );
436
		} else {
437 2
			$this->m_caption = $this->getNormalizedFormattedNumber( $number );
438
		}
439
440
		// no unit ever, so nothing to do about this
441 24
		$this->m_unitin = '';
442 24
	}
443
444
	/**
445
	 * Return an array of major unit strings (ids only recommended) supported by
446
	 * this datavalue.
447
	 *
448
	 * Overwritten by subclasses that support units.
449
	 */
450
	public function getUnitList() {
451
		return array( '' );
452
	}
453
454 38
	protected function getPreferredDisplayPrecision() {
455
456
		// In case of a value description, don't restrict the value with a display precision
457 38
		if ( $this->getProperty() === null || $this->getOptionBy( 'value.description' ) || $this->getOptionBy( 'no.displayprecision' ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->getOptionBy('value.description') of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
Bug Best Practice introduced by
The expression $this->getOptionBy('no.displayprecision') of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
458 10
			return false;
459
		}
460
461 38
		if ( $this->precision === null ) {
462 38
			$this->precision = ApplicationFactory::getInstance()->getPropertySpecificationLookup()->getDisplayPrecisionBy(
463 38
				$this->getProperty()
464
			);
465
		}
466
467 38
		return $this->precision;
468
	}
469
470 8
	private function findPrecisionFrom( $formatstring ) {
471
472 8
		if ( strpos( $formatstring, '-' ) === false ) {
473 5
			return $formatstring;
474
		}
475
476 5
		$parts = explode( '-', $formatstring );
477
478
		// Find precision from annotated -p<number of digits> formatstring which
479
		// has priority over a possible _PREC value
480 5
		foreach ( $parts as $key => $value ) {
481 5
			if ( strpos( $value, 'p' ) !== false && is_numeric( substr( $value, 1 ) ) ) {
482 2
				$this->precision = strval( substr( $value, 1 ) );
0 ignored issues
show
Documentation Bug introduced by
It seems like strval(substr($value, 1)) of type string is incompatible with the declared type integer|null of property $precision.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
483 5
				unset( $parts[$key] );
484
			}
485
		}
486
487
		// Rebuild formatstring without a possible p element to ensure other
488
		// options can be used in combination such as -n-p2 etc.
489 5
		return implode( '-', $parts );
490
	}
491
492 51
	private function getNumberFormatter() {
493
494 51
		$this->intlNumberFormatter->setOption(
495 51
			'user.language',
496 51
			$this->getOptionBy( 'user.language' )
497
		);
498
499 51
		$this->intlNumberFormatter->setOption(
500 51
			'content.language',
501 51
			$this->getOptionBy( 'content.language' )
502
		);
503
504 51
		$this->intlNumberFormatter->setOption(
505 51
			'separator.thousands',
506 51
			$this->getOptionBy( 'separator.thousands' )
507
		);
508
509 51
		$this->intlNumberFormatter->setOption(
510 51
			'separator.decimal',
511 51
			$this->getOptionBy( 'separator.decimal' )
512
		);
513
514 51
		return $this->intlNumberFormatter;
515
	}
516
517 8
	private function findPreferredLanguageFrom( &$formatstring ) {
518
		// Localized preferred user language
519 8
		if ( strpos( $formatstring, 'LOCL' ) !== false && ( $languageCode = Localizer::getLanguageCodeFrom( $formatstring ) ) !== false ) {
520 1
			$this->intlNumberFormatter->setOption(
521 1
				'preferred.language',
522
				$languageCode
523
			);
524
		}
525
526
		// Remove any remaining
527 8
		$formatstring = str_replace( array( '#LOCL', 'LOCL' ), '', $formatstring );
528 8
	}
529
530
}
531