1<?php
2/**
3 * Zend Framework
4 *
5 * LICENSE
6 *
7 * This source file is subject to the new BSD license that is bundled
8 * with this package in the file LICENSE.txt.
9 * It is also available through the world-wide-web at this URL:
10 * http://framework.zend.com/license/new-bsd
11 * If you did not receive a copy of the license and are unable to
12 * obtain it through the world-wide-web, please send an email
13 * to license@zend.com so we can send you a copy immediately.
14 *
15 * @category   Zend
16 * @package    Zend_Locale
17 * @subpackage Format
18 * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
19 * @version    $Id$
20 * @license    http://framework.zend.com/license/new-bsd     New BSD License
21 */
22
23/**
24 * include needed classes
25 */
26
27/**
28 * @category   Zend
29 * @package    Zend_Locale
30 * @subpackage Format
31 * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
32 * @license    http://framework.zend.com/license/new-bsd     New BSD License
33 */
34class Zend_Locale_Format
35{
36    const STANDARD   = 'auto';
37
38    private static $_options = array('date_format'   => null,
39                                     'number_format' => null,
40                                     'format_type'   => 'iso',
41                                     'fix_date'      => false,
42                                     'locale'        => null,
43                                     'cache'         => null,
44                                     'disableCache'  => null,
45                                     'precision'     => null);
46
47    /**
48     * Sets class wide options, if no option was given, the actual set options will be returned
49     * The 'precision' option of a value is used to truncate or stretch extra digits. -1 means not to touch the extra digits.
50     * The 'locale' option helps when parsing numbers and dates using separators and month names.
51     * The date format 'format_type' option selects between CLDR/ISO date format specifier tokens and PHP's date() tokens.
52     * The 'fix_date' option enables or disables heuristics that attempt to correct invalid dates.
53     * The 'number_format' option can be used to specify a default number format string
54     * The 'date_format' option can be used to specify a default date format string, but beware of using getDate(),
55     * checkDateFormat() and getTime() after using setOptions() with a 'format'.  To use these four methods
56     * with the default date format for a locale, use array('date_format' => null, 'locale' => $locale) for their options.
57     *
58     * @param  array  $options  Array of options, keyed by option name: format_type = 'iso' | 'php', fix_date = true | false,
59     *                          locale = Zend_Locale | locale string, precision = whole number between -1 and 30
60     * @throws Zend_Locale_Exception
61     * @return array if no option was given
62     */
63    public static function setOptions(array $options = array())
64    {
65        self::$_options = self::_checkOptions($options) + self::$_options;
66        return self::$_options;
67    }
68
69    /**
70     * Internal function for checking the options array of proper input values
71     * See {@link setOptions()} for details.
72     *
73     * @param  array  $options  Array of options, keyed by option name: format_type = 'iso' | 'php', fix_date = true | false,
74     *                          locale = Zend_Locale | locale string, precision = whole number between -1 and 30
75     * @throws Zend_Locale_Exception
76     * @return array if no option was given
77     */
78    private static function _checkOptions(array $options = array())
79    {
80        if (count($options) == 0) {
81            return self::$_options;
82        }
83        foreach ($options as $name => $value) {
84            $name  = strtolower($name);
85            if ($name !== 'locale') {
86                if (gettype($value) === 'string') {
87                    $value = strtolower($value);
88                }
89            }
90
91            switch($name) {
92                case 'number_format' :
93                    if ($value == Zend_Locale_Format::STANDARD) {
94                        $locale = self::$_options['locale'];
95                        if (isset($options['locale'])) {
96                            $locale = $options['locale'];
97                        }
98                        $options['number_format'] = Zend_Locale_Data::getContent($locale, 'decimalnumber');
99                    } else if ((gettype($value) !== 'string') and ($value !== NULL)) {
100                        $stringValue = (string)(is_array($value) ? implode(' ', $value) : $value);
101                        throw new Zend_Locale_Exception("Unknown number format type '" . gettype($value) . "'. "
102                            . "Format '$stringValue' must be a valid number format string.");
103                    }
104                    break;
105
106                case 'date_format' :
107                    if ($value == Zend_Locale_Format::STANDARD) {
108                        $locale = self::$_options['locale'];
109                        if (isset($options['locale'])) {
110                            $locale = $options['locale'];
111                        }
112                        $options['date_format'] = Zend_Locale_Format::getDateFormat($locale);
113                    } else if ((gettype($value) !== 'string') and ($value !== NULL)) {
114                        $stringValue = (string)(is_array($value) ? implode(' ', $value) : $value);
115                        throw new Zend_Locale_Exception("Unknown dateformat type '" . gettype($value) . "'. "
116                            . "Format '$stringValue' must be a valid ISO or PHP date format string.");
117                    } else {
118                        if (((isset($options['format_type']) === true) and ($options['format_type'] == 'php')) or
119                            ((isset($options['format_type']) === false) and (self::$_options['format_type'] == 'php'))) {
120                            $options['date_format'] = Zend_Locale_Format::convertPhpToIsoFormat($value);
121                        }
122                    }
123                    break;
124
125                case 'format_type' :
126                    if (($value != 'php') && ($value != 'iso')) {
127                        throw new Zend_Locale_Exception("Unknown date format type '$value'. Only 'iso' and 'php'"
128                           . " are supported.");
129                    }
130                    break;
131
132                case 'fix_date' :
133                    if (($value !== true) && ($value !== false)) {
134                        throw new Zend_Locale_Exception("Enabling correction of dates must be either true or false"
135                            . "(fix_date='$value').");
136                    }
137                    break;
138
139                case 'locale' :
140                    $options['locale'] = Zend_Locale::findLocale($value);
141                    break;
142
143                case 'cache' :
144                    if ($value instanceof Zend_Cache_Core) {
145                        Zend_Locale_Data::setCache($value);
146                    }
147                    break;
148
149                case 'disablecache' :
150                    if (null !== $value) {
151                        Zend_Locale_Data::disableCache($value);
152                    }
153                    break;
154
155                case 'precision' :
156                    if ($value === NULL) {
157                        $value = -1;
158                    }
159
160                    if (($value < -1) || ($value > 30)) {
161                        throw new Zend_Locale_Exception("'$value' precision is not a whole number less than 30.");
162                    }
163                    break;
164
165                default:
166                    throw new Zend_Locale_Exception("Unknown option: '$name' = '$value'");
167                    break;
168
169            }
170        }
171
172        return $options;
173    }
174
175    /**
176     * Changes the numbers/digits within a given string from one script to another
177     * 'Decimal' representated the stardard numbers 0-9, if a script does not exist
178     * an exception will be thrown.
179     *
180     * Examples for conversion from Arabic to Latin numerals:
181     *   convertNumerals('١١٠ Tests', 'Arab'); -> returns '100 Tests'
182     * Example for conversion from Latin to Arabic numerals:
183     *   convertNumerals('100 Tests', 'Latn', 'Arab'); -> returns '١١٠ Tests'
184     *
185     * @param  string  $input  String to convert
186     * @param  string  $from   Script to parse, see {@link Zend_Locale::getScriptList()} for details.
187     * @param  string  $to     OPTIONAL Script to convert to
188     * @return string  Returns the converted input
189     * @throws Zend_Locale_Exception
190     */
191    public static function convertNumerals($input, $from, $to = null)
192    {
193        if (!self::_getUniCodeSupport()) {
194            trigger_error("Sorry, your PCRE extension does not support UTF8 which is needed for the I18N core", E_USER_NOTICE);
195        }
196
197        $from   = strtolower($from);
198        $source = Zend_Locale_Data::getContent('en', 'numberingsystem', $from);
199        if (empty($source)) {
200            throw new Zend_Locale_Exception("Unknown script '$from'. Use 'Latn' for digits 0,1,2,3,4,5,6,7,8,9.");
201        }
202
203        if ($to !== null) {
204            $to     = strtolower($to);
205            $target = Zend_Locale_Data::getContent('en', 'numberingsystem', $to);
206            if (empty($target)) {
207                throw new Zend_Locale_Exception("Unknown script '$to'. Use 'Latn' for digits 0,1,2,3,4,5,6,7,8,9.");
208            }
209        } else {
210            $target = '0123456789';
211        }
212
213        for ($x = 0; $x < 10; ++$x) {
214            $asource[$x] = "/" . iconv_substr($source, $x, 1, 'UTF-8') . "/u";
215            $atarget[$x] = iconv_substr($target, $x, 1, 'UTF-8');
216        }
217
218        return preg_replace($asource, $atarget, $input);
219    }
220
221    /**
222     * Returns the normalized number from a localized one
223     * Parsing depends on given locale (grouping and decimal)
224     *
225     * Examples for input:
226     * '2345.4356,1234' = 23455456.1234
227     * '+23,3452.123' = 233452.123
228     * '12343 ' = 12343
229     * '-9456' = -9456
230     * '0' = 0
231     *
232     * @param  string $input    Input string to parse for numbers
233     * @param  array  $options  Options: locale, precision. See {@link setOptions()} for details.
234     * @return string Returns the extracted number
235     * @throws Zend_Locale_Exception
236     */
237    public static function getNumber($input, array $options = array())
238    {
239        $options = self::_checkOptions($options) + self::$_options;
240        if (!is_string($input)) {
241            return $input;
242        }
243
244        if (!self::isNumber($input, $options)) {
245            throw new Zend_Locale_Exception('No localized value in ' . $input . ' found, or the given number does not match the localized format');
246        }
247
248        // Get correct signs for this locale
249        $symbols = Zend_Locale_Data::getList($options['locale'],'symbols');
250        // Change locale input to be default number
251        if (($input[0] == $symbols['minus']) && ('-' != $input[0])) {
252            $input = '-' . substr($input, 1);
253        }
254
255        $input = str_replace($symbols['group'],'', $input);
256        if (strpos($input, $symbols['decimal']) !== false) {
257            if ($symbols['decimal'] != '.') {
258                $input = str_replace($symbols['decimal'], ".", $input);
259            }
260
261            $pre = substr($input, strpos($input, '.') + 1);
262            if ($options['precision'] === null) {
263                $options['precision'] = strlen($pre);
264            }
265
266            if (strlen($pre) >= $options['precision']) {
267                $input = substr($input, 0, strlen($input) - strlen($pre) + $options['precision']);
268            }
269
270            if (($options['precision'] == 0) && ($input[strlen($input) - 1] == '.')) {
271                $input = substr($input, 0, -1);
272            }
273        }
274
275        return $input;
276    }
277
278    /**
279     * Returns a locale formatted number depending on the given options.
280     * The seperation and fraction sign is used from the set locale.
281     * ##0.#  -> 12345.12345 -> 12345.12345
282     * ##0.00 -> 12345.12345 -> 12345.12
283     * ##,##0.00 -> 12345.12345 -> 12,345.12
284     *
285     * @param   string  $value    Localized number string
286     * @param   array   $options  Options: number_format, locale, precision. See {@link setOptions()} for details.
287     * @return  string  locale formatted number
288     * @throws Zend_Locale_Exception
289     */
290    public static function toNumber($value, array $options = array())
291    {
292        // load class within method for speed
293
294        $value             = Zend_Locale_Math::floatalize($value);
295        $value             = Zend_Locale_Math::normalize($value);
296        $options           = self::_checkOptions($options) + self::$_options;
297        $options['locale'] = (string) $options['locale'];
298
299        // Get correct signs for this locale
300        $symbols = Zend_Locale_Data::getList($options['locale'], 'symbols');
301        $oenc = self::_getEncoding();
302        self::_setEncoding('UTF-8');
303
304        // Get format
305        $format = $options['number_format'];
306        if ($format === null) {
307            $format  = Zend_Locale_Data::getContent($options['locale'], 'decimalnumber');
308            $format  = self::_seperateFormat($format, $value, $options['precision']);
309
310            if ($options['precision'] !== null) {
311                $value   = Zend_Locale_Math::normalize(Zend_Locale_Math::round($value, $options['precision']));
312            }
313        } else {
314            // seperate negative format pattern when available
315            $format  = self::_seperateFormat($format, $value, $options['precision']);
316            if (strpos($format, '.')) {
317                if (is_numeric($options['precision'])) {
318                    $value = Zend_Locale_Math::round($value, $options['precision']);
319                } else {
320                    if (substr($format, iconv_strpos($format, '.') + 1, 3) == '###') {
321                        $options['precision'] = null;
322                    } else {
323                        $options['precision'] = iconv_strlen(iconv_substr($format, iconv_strpos($format, '.') + 1,
324                                                             iconv_strrpos($format, '0') - iconv_strpos($format, '.')));
325                        $format = iconv_substr($format, 0, iconv_strpos($format, '.') + 1) . '###'
326                                . iconv_substr($format, iconv_strrpos($format, '0') + 1);
327                    }
328                }
329            } else {
330                $value = Zend_Locale_Math::round($value, 0);
331                $options['precision'] = 0;
332            }
333            $value = Zend_Locale_Math::normalize($value);
334        }
335
336        if (iconv_strpos($format, '0') === false) {
337            self::_setEncoding($oenc);
338            throw new Zend_Locale_Exception('Wrong format... missing 0');
339        }
340
341        // get number parts
342        $pos = iconv_strpos($value, '.');
343        if ($pos !== false) {
344            if ($options['precision'] === null) {
345                $precstr = iconv_substr($value, $pos + 1);
346            } else {
347                $precstr = iconv_substr($value, $pos + 1, $options['precision']);
348                if (iconv_strlen($precstr) < $options['precision']) {
349                    $precstr = $precstr . str_pad("0", ($options['precision'] - iconv_strlen($precstr)), "0");
350                }
351            }
352        } else {
353            if ($options['precision'] > 0) {
354                $precstr = str_pad("0", ($options['precision']), "0");
355            }
356        }
357
358        if ($options['precision'] === null) {
359            if (isset($precstr)) {
360                $options['precision'] = iconv_strlen($precstr);
361            } else {
362                $options['precision'] = 0;
363            }
364        }
365
366        // get fraction and format lengths
367        if (strpos($value, '.') !== false) {
368            $number = substr((string) $value, 0, strpos($value, '.'));
369        } else {
370            $number = $value;
371        }
372
373        $prec = call_user_func(Zend_Locale_Math::$sub, $value, $number, $options['precision']);
374        $prec = Zend_Locale_Math::floatalize($prec);
375        $prec = Zend_Locale_Math::normalize($prec);
376        if (iconv_strpos($prec, '-') !== false) {
377            $prec = iconv_substr($prec, 1);
378        }
379
380        if (($prec == 0) and ($options['precision'] > 0)) {
381            $prec = "0.0";
382        }
383
384        if (($options['precision'] + 2) > iconv_strlen($prec)) {
385            $prec = str_pad((string) $prec, $options['precision'] + 2, "0", STR_PAD_RIGHT);
386        }
387
388        if (iconv_strpos($number, '-') !== false) {
389            $number = iconv_substr($number, 1);
390        }
391        $group  = iconv_strrpos($format, ',');
392        $group2 = iconv_strpos ($format, ',');
393        $point  = iconv_strpos ($format, '0');
394        // Add fraction
395        $rest = "";
396        if (iconv_strpos($format, '.')) {
397            $rest   = iconv_substr($format, iconv_strpos($format, '.') + 1);
398            $length = iconv_strlen($rest);
399            for($x = 0; $x < $length; ++$x) {
400                if (($rest[0] == '0') || ($rest[0] == '#')) {
401                    $rest = iconv_substr($rest, 1);
402                }
403            }
404            $format = iconv_substr($format, 0, iconv_strlen($format) - iconv_strlen($rest));
405        }
406
407        if ($options['precision'] == '0') {
408            if (iconv_strrpos($format, '-') != 0) {
409                $format = iconv_substr($format, 0, $point)
410                        . iconv_substr($format, iconv_strrpos($format, '#') + 2);
411            } else {
412                $format = iconv_substr($format, 0, $point);
413            }
414        } else {
415            $format = iconv_substr($format, 0, $point) . $symbols['decimal']
416                               . iconv_substr($prec, 2);
417        }
418
419        $format .= $rest;
420        // Add seperation
421        if ($group == 0) {
422            // no seperation
423            $format = $number . iconv_substr($format, $point);
424        } else if ($group == $group2) {
425            // only 1 seperation
426            $seperation = ($point - $group);
427            for ($x = iconv_strlen($number); $x > $seperation; $x -= $seperation) {
428                if (iconv_substr($number, 0, $x - $seperation) !== "") {
429                    $number = iconv_substr($number, 0, $x - $seperation) . $symbols['group']
430                            . iconv_substr($number, $x - $seperation);
431                }
432            }
433            $format = iconv_substr($format, 0, iconv_strpos($format, '#')) . $number . iconv_substr($format, $point);
434        } else {
435
436            // 2 seperations
437            if (iconv_strlen($number) > ($point - $group)) {
438                $seperation = ($point - $group);
439                $number = iconv_substr($number, 0, iconv_strlen($number) - $seperation) . $symbols['group']
440                        . iconv_substr($number, iconv_strlen($number) - $seperation);
441
442                if ((iconv_strlen($number) - 1) > ($point - $group + 1)) {
443                    $seperation2 = ($group - $group2 - 1);
444                    for ($x = iconv_strlen($number) - $seperation2 - 2; $x > $seperation2; $x -= $seperation2) {
445                        $number = iconv_substr($number, 0, $x - $seperation2) . $symbols['group']
446                                . iconv_substr($number, $x - $seperation2);
447                    }
448                }
449
450            }
451            $format = iconv_substr($format, 0, iconv_strpos($format, '#')) . $number . iconv_substr($format, $point);
452        }
453        // set negative sign
454        if (call_user_func(Zend_Locale_Math::$comp, $value, 0, $options['precision']) < 0) {
455            if (iconv_strpos($format, '-') === false) {
456                $format = $symbols['minus'] . $format;
457            } else {
458                $format = str_replace('-', $symbols['minus'], $format);
459            }
460        }
461
462        self::_setEncoding($oenc);
463        return (string) $format;
464    }
465
466    /**
467     * @param string $format
468     * @param string $value
469     * @param int $precision
470     * @return string
471     */
472    private static function _seperateFormat($format, $value, $precision)
473    {
474        if (iconv_strpos($format, ';') !== false) {
475            if (call_user_func(Zend_Locale_Math::$comp, $value, 0, $precision) < 0) {
476                $tmpformat = iconv_substr($format, iconv_strpos($format, ';') + 1);
477                if ($tmpformat[0] == '(') {
478                    $format = iconv_substr($format, 0, iconv_strpos($format, ';'));
479                } else {
480                    $format = $tmpformat;
481                }
482            } else {
483                $format = iconv_substr($format, 0, iconv_strpos($format, ';'));
484            }
485        }
486
487        return $format;
488    }
489
490
491    /**
492     * Checks if the input contains a normalized or localized number
493     *
494     * @param   string  $input    Localized number string
495     * @param   array   $options  Options: locale. See {@link setOptions()} for details.
496     * @return  boolean           Returns true if a number was found
497     */
498    public static function isNumber($input, array $options = array())
499    {
500        if (!self::_getUniCodeSupport()) {
501            trigger_error("Sorry, your PCRE extension does not support UTF8 which is needed for the I18N core", E_USER_NOTICE);
502        }
503
504        $options = self::_checkOptions($options) + self::$_options;
505
506        // Get correct signs for this locale
507        $symbols = Zend_Locale_Data::getList($options['locale'],'symbols');
508
509        $regexs = Zend_Locale_Format::_getRegexForType('decimalnumber', $options);
510        $regexs = array_merge($regexs, Zend_Locale_Format::_getRegexForType('scientificnumber', $options));
511        if (!empty($input) && ($input[0] == $symbols['decimal'])) {
512            $input = 0 . $input;
513        }
514        foreach ($regexs as $regex) {
515            preg_match($regex, $input, $found);
516            if (isset($found[0])) {
517                return true;
518            }
519        }
520
521        return false;
522    }
523
524    /**
525     * Internal method to convert cldr number syntax into regex
526     *
527     * @param  string $type
528     * @param  array  $options Options: locale. See {@link setOptions()} for details.
529     * @return string
530     * @throws Zend_Locale_Exception
531     */
532    private static function _getRegexForType($type, $options)
533    {
534        $decimal  = Zend_Locale_Data::getContent($options['locale'], $type);
535        $decimal  = preg_replace('/[^#0,;\.\-Ee]/u', '',$decimal);
536        $patterns = explode(';', $decimal);
537
538        if (count($patterns) == 1) {
539            $patterns[1] = '-' . $patterns[0];
540        }
541
542        $symbols = Zend_Locale_Data::getList($options['locale'],'symbols');
543
544        foreach($patterns as $pkey => $pattern) {
545            $regex[$pkey]  = '/^';
546            $rest   = 0;
547            $end    = null;
548            if (strpos($pattern, '.') !== false) {
549                $end     = substr($pattern, strpos($pattern, '.') + 1);
550                $pattern = substr($pattern, 0, -strlen($end) - 1);
551            }
552
553            if (strpos($pattern, ',') !== false) {
554                $parts = explode(',', $pattern);
555                $count = count($parts);
556                foreach($parts as $key => $part) {
557                    switch ($part) {
558                        case '#':
559                        case '-#':
560                            if ($part[0] == '-') {
561                                $regex[$pkey] .= '[' . $symbols['minus'] . '-]{0,1}';
562                            } else {
563                                $regex[$pkey] .= '[' . $symbols['plus'] . '+]{0,1}';
564                            }
565
566                            if (($parts[$key + 1]) == '##0')  {
567                                $regex[$pkey] .= '[0-9]{1,3}';
568                            } else if (($parts[$key + 1]) == '##') {
569                                $regex[$pkey] .= '[0-9]{1,2}';
570                            } else {
571                                throw new Zend_Locale_Exception('Unsupported token for numberformat (Pos 1):"' . $pattern . '"');
572                            }
573                            break;
574                        case '##':
575                            if ($parts[$key + 1] == '##0') {
576                                $regex[$pkey] .=  '(\\' . $symbols['group'] . '{0,1}[0-9]{2})*';
577                            } else {
578                                throw new Zend_Locale_Exception('Unsupported token for numberformat (Pos 2):"' . $pattern . '"');
579                            }
580                            break;
581                        case '##0':
582                            if ($parts[$key - 1] == '##') {
583                                $regex[$pkey] .= '[0-9]';
584                            } else if (($parts[$key - 1] == '#') || ($parts[$key - 1] == '-#')) {
585                                $regex[$pkey] .= '(\\' . $symbols['group'] . '{0,1}[0-9]{3})*';
586                            } else {
587                                throw new Zend_Locale_Exception('Unsupported token for numberformat (Pos 3):"' . $pattern . '"');
588                            }
589                            break;
590                        case '#0':
591                            if ($key == 0) {
592                                $regex[$pkey] .= '[0-9]*';
593                            } else {
594                                throw new Zend_Locale_Exception('Unsupported token for numberformat (Pos 4):"' . $pattern . '"');
595                            }
596                            break;
597                    }
598                }
599            }
600
601            if (strpos($pattern, 'E') !== false) {
602                if (($pattern == '#E0') || ($pattern == '#E00')) {
603                    $regex[$pkey] .= '[' . $symbols['plus']. '+]{0,1}[0-9]{1,}(\\' . $symbols['decimal'] . '[0-9]{1,})*[eE][' . $symbols['plus']. '+]{0,1}[0-9]{1,}';
604                } else if (($pattern == '-#E0') || ($pattern == '-#E00')) {
605                    $regex[$pkey] .= '[' . $symbols['minus']. '-]{0,1}[0-9]{1,}(\\' . $symbols['decimal'] . '[0-9]{1,})*[eE][' . $symbols['minus']. '-]{0,1}[0-9]{1,}';
606                } else {
607                    throw new Zend_Locale_Exception('Unsupported token for numberformat (Pos 5):"' . $pattern . '"');
608                }
609            }
610
611            if (!empty($end)) {
612                if ($end == '###') {
613                    $regex[$pkey] .= '(\\' . $symbols['decimal'] . '{1}[0-9]{1,}){0,1}';
614                } else if ($end == '###-') {
615                    $regex[$pkey] .= '(\\' . $symbols['decimal'] . '{1}[0-9]{1,}){0,1}[' . $symbols['minus']. '-]';
616                } else {
617                    throw new Zend_Locale_Exception('Unsupported token for numberformat (Pos 6):"' . $pattern . '"');
618                }
619            }
620
621            $regex[$pkey] .= '$/u';
622        }
623
624        return $regex;
625    }
626
627    /**
628     * Alias for getNumber
629     *
630     * @param   string  $input    Number to localize
631     * @param   array   $options  Options: locale, precision. See {@link setOptions()} for details.
632     * @return  float
633     */
634    public static function getFloat($input, array $options = array())
635    {
636        return floatval(self::getNumber($input, $options));
637    }
638
639    /**
640     * Returns a locale formatted integer number
641     * Alias for toNumber()
642     *
643     * @param   string  $value    Number to normalize
644     * @param   array   $options  Options: locale, precision. See {@link setOptions()} for details.
645     * @return  string  Locale formatted number
646     */
647    public static function toFloat($value, array $options = array())
648    {
649        $options['number_format'] = Zend_Locale_Format::STANDARD;
650        return self::toNumber($value, $options);
651    }
652
653    /**
654     * Returns if a float was found
655     * Alias for isNumber()
656     *
657     * @param   string $value  Localized number string
658     * @param   array $options Options: locale. See {@link setOptions()} for details.
659     * @return  boolean        Returns true if a number was found
660     */
661    public static function isFloat($value, array $options = array())
662    {
663        return self::isNumber($value, $options);
664    }
665
666    /**
667     * Returns the first found integer from an string
668     * Parsing depends on given locale (grouping and decimal)
669     *
670     * Examples for input:
671     * '  2345.4356,1234' = 23455456
672     * '+23,3452.123' = 233452
673     * ' 12343 ' = 12343
674     * '-9456km' = -9456
675     * '0' = 0
676     * '(-){0,1}(\d+(\.){0,1})*(\,){0,1})\d+'
677     *
678     * @param   string   $input    Input string to parse for numbers
679     * @param   array    $options  Options: locale. See {@link setOptions()} for details.
680     * @return  integer            Returns the extracted number
681     */
682    public static function getInteger($input, array $options = array())
683    {
684        $options['precision'] = 0;
685        return intval(self::getFloat($input, $options));
686    }
687
688    /**
689     * Returns a localized number
690     *
691     * @param   string  $value    Number to normalize
692     * @param   array   $options  Options: locale. See {@link setOptions()} for details.
693     * @return  string            Locale formatted number
694     */
695    public static function toInteger($value, array $options = array())
696    {
697        $options['precision'] = 0;
698        $options['number_format'] = Zend_Locale_Format::STANDARD;
699        return self::toNumber($value, $options);
700    }
701
702    /**
703     * Returns if a integer was found
704     *
705     * @param  string $value Localized number string
706     * @param  array $options Options: locale. See {@link setOptions()} for details.
707     * @return boolean Returns true if a integer was found
708     */
709    public static function isInteger($value, array $options = array())
710    {
711        if (!self::isNumber($value, $options)) {
712            return false;
713        }
714
715        if (self::getInteger($value, $options) == self::getFloat($value, $options)) {
716            return true;
717        }
718
719        return false;
720    }
721
722    /**
723     * Converts a format string from PHP's date format to ISO format
724     * Remember that Zend Date always returns localized string, so a month name which returns the english
725     * month in php's date() will return the translated month name with this function... use 'en' as locale
726     * if you are in need of the original english names
727     *
728     * The conversion has the following restrictions:
729     * 'a', 'A' - Meridiem is not explicit upper/lowercase, you have to upper/lowercase the translated value yourself
730     *
731     * @param  string  $format  Format string in PHP's date format
732     * @return string           Format string in ISO format
733     */
734    public static function convertPhpToIsoFormat($format)
735    {
736        if ($format === null) {
737            return null;
738        }
739
740        $convert = array(
741            'd' => 'dd'  , 'D' => 'EE'  , 'j' => 'd'   , 'l' => 'EEEE',
742            'N' => 'eee' , 'S' => 'SS'  , 'w' => 'e'   , 'z' => 'D'   ,
743            'W' => 'ww'  , 'F' => 'MMMM', 'm' => 'MM'  , 'M' => 'MMM' ,
744            'n' => 'M'   , 't' => 'ddd' , 'L' => 'l'   , 'o' => 'YYYY',
745            'Y' => 'yyyy', 'y' => 'yy'  , 'a' => 'a'   , 'A' => 'a'   ,
746            'B' => 'B'   , 'g' => 'h'   , 'G' => 'H'   , 'h' => 'hh'  ,
747            'H' => 'HH'  , 'i' => 'mm'  , 's' => 'ss'  , 'e' => 'zzzz',
748            'I' => 'I'   , 'O' => 'Z'   , 'P' => 'ZZZZ', 'T' => 'z'   ,
749            'Z' => 'X'   , 'c' => 'yyyy-MM-ddTHH:mm:ssZZZZ', 'r' => 'r',
750            'U' => 'U',
751        );
752        $escaped = false;
753        $inEscapedString = false;
754        $converted = array();
755        foreach (str_split($format) as $char) {
756            if (!$escaped && $char == '\\') {
757                // Next char will be escaped: let's remember it
758                $escaped = true;
759            } elseif ($escaped) {
760                if (!$inEscapedString) {
761                    // First escaped string: start the quoted chunk
762                    $converted[] = "'";
763                    $inEscapedString = true;
764                }
765                // Since the previous char was a \ and we are in the quoted
766                // chunk, let's simply add $char as it is
767                $converted[] = $char;
768                $escaped = false;
769            } elseif ($char == "'") {
770                // Single quotes need to be escaped like this
771                $converted[] = "''";
772            } else {
773                if ($inEscapedString) {
774                    // Close the single-quoted chunk
775                    $converted[] = "'";
776                    $inEscapedString = false;
777                }
778                // Convert the unescaped char if needed
779                if (isset($convert[$char])) {
780                    $converted[] = $convert[$char];
781                } else {
782                    $converted[] = $char;
783                }
784            }
785        }
786
787        return implode($converted);
788    }
789
790    /**
791     * Parse date and split in named array fields
792     *
793     * @param  string $date    Date string to parse
794     * @param  array  $options Options: format_type, fix_date, locale, date_format. See {@link setOptions()} for details.
795     * @return array Possible array members: day, month, year, hour, minute, second, fixed, format
796     * @throws Zend_Locale_Exception
797     */
798    private static function _parseDate($date, $options)
799    {
800        if (!self::_getUniCodeSupport()) {
801            trigger_error("Sorry, your PCRE extension does not support UTF8 which is needed for the I18N core", E_USER_NOTICE);
802        }
803
804        $options = self::_checkOptions($options) + self::$_options;
805        $test = array('h', 'H', 'm', 's', 'y', 'Y', 'M', 'd', 'D', 'E', 'S', 'l', 'B', 'I',
806                       'X', 'r', 'U', 'G', 'w', 'e', 'a', 'A', 'Z', 'z', 'v');
807
808        $format = $options['date_format'];
809        $number = $date; // working copy
810        $result['date_format'] = $format; // save the format used to normalize $number (convenience)
811        $result['locale'] = $options['locale']; // save the locale used to normalize $number (convenience)
812
813        $oenc = self::_getEncoding();
814        self::_setEncoding('UTF-8');
815        $day   = iconv_strpos($format, 'd');
816        $month = iconv_strpos($format, 'M');
817        $year  = iconv_strpos($format, 'y');
818        $hour  = iconv_strpos($format, 'H');
819        $min   = iconv_strpos($format, 'm');
820        $sec   = iconv_strpos($format, 's');
821        $am    = null;
822        if ($hour === false) {
823            $hour = iconv_strpos($format, 'h');
824        }
825        if ($year === false) {
826            $year = iconv_strpos($format, 'Y');
827        }
828        if ($day === false) {
829            $day = iconv_strpos($format, 'E');
830            if ($day === false) {
831                $day = iconv_strpos($format, 'D');
832            }
833        }
834
835        if ($day !== false) {
836            $parse[$day]   = 'd';
837            if (!empty($options['locale']) && ($options['locale'] !== 'root') &&
838                (!is_object($options['locale']) || ((string) $options['locale'] !== 'root'))) {
839                // erase day string
840                    $daylist = Zend_Locale_Data::getList($options['locale'], 'day');
841                foreach($daylist as $key => $name) {
842                    if (iconv_strpos($number, $name) !== false) {
843                        $number = str_replace($name, "EEEE", $number);
844                        break;
845                    }
846                }
847            }
848        }
849        $position = false;
850
851        if ($month !== false) {
852            $parse[$month] = 'M';
853            if (!empty($options['locale']) && ($options['locale'] !== 'root') &&
854                (!is_object($options['locale']) || ((string) $options['locale'] !== 'root'))) {
855                    // prepare to convert month name to their numeric equivalents, if requested,
856                    // and we have a $options['locale']
857                    $position = self::_replaceMonth($number, Zend_Locale_Data::getList($options['locale'],
858                        'month'));
859                if ($position === false) {
860                    $position = self::_replaceMonth($number, Zend_Locale_Data::getList($options['locale'],
861                        'month', array('gregorian', 'format', 'abbreviated')));
862                }
863            }
864        }
865        if ($year !== false) {
866            $parse[$year]  = 'y';
867        }
868        if ($hour !== false) {
869            $parse[$hour] = 'H';
870        }
871        if ($min !== false) {
872            $parse[$min] = 'm';
873        }
874        if ($sec !== false) {
875            $parse[$sec] = 's';
876        }
877
878        if (empty($parse)) {
879            self::_setEncoding($oenc);
880            throw new Zend_Locale_Exception("Unknown date format, neither date nor time in '" . $format . "' found");
881        }
882        ksort($parse);
883
884        // get daytime
885        if (iconv_strpos($format, 'a') !== false) {
886            if (iconv_strpos(strtoupper($number), strtoupper(Zend_Locale_Data::getContent($options['locale'], 'am'))) !== false) {
887                $am = true;
888            } else if (iconv_strpos(strtoupper($number), strtoupper(Zend_Locale_Data::getContent($options['locale'], 'pm'))) !== false) {
889                $am = false;
890            }
891        }
892
893        // split number parts
894        $split = false;
895        preg_match_all('/\d+/u', $number, $splitted);
896
897        if (count($splitted[0]) == 0) {
898            self::_setEncoding($oenc);
899            throw new Zend_Locale_Exception("No date part in '$date' found.");
900        }
901        if (count($splitted[0]) == 1) {
902            $split = 0;
903        }
904        $cnt = 0;
905        foreach($parse as $key => $value) {
906
907            switch($value) {
908                case 'd':
909                    if ($split === false) {
910                        if (count($splitted[0]) > $cnt) {
911                            $result['day']    = $splitted[0][$cnt];
912                        }
913                    } else {
914                        $result['day'] = iconv_substr($splitted[0][0], $split, 2);
915                        $split += 2;
916                    }
917                    ++$cnt;
918                    break;
919                case 'M':
920                    if ($split === false) {
921                        if (count($splitted[0]) > $cnt) {
922                            $result['month']  = $splitted[0][$cnt];
923                        }
924                    } else {
925                        $result['month'] = iconv_substr($splitted[0][0], $split, 2);
926                        $split += 2;
927                    }
928                    ++$cnt;
929                    break;
930                case 'y':
931                    $length = 2;
932                    if ((iconv_substr($format, $year, 4) == 'yyyy')
933                     || (iconv_substr($format, $year, 4) == 'YYYY')) {
934                        $length = 4;
935                    }
936
937                    if ($split === false) {
938                        if (count($splitted[0]) > $cnt) {
939                            $result['year']   = $splitted[0][$cnt];
940                        }
941                    } else {
942                        $result['year']   = iconv_substr($splitted[0][0], $split, $length);
943                        $split += $length;
944                    }
945
946                    ++$cnt;
947                    break;
948                case 'H':
949                    if ($split === false) {
950                        if (count($splitted[0]) > $cnt) {
951                            $result['hour']   = $splitted[0][$cnt];
952                        }
953                    } else {
954                        $result['hour']   = iconv_substr($splitted[0][0], $split, 2);
955                        $split += 2;
956                    }
957                    ++$cnt;
958                    break;
959                case 'm':
960                    if ($split === false) {
961                        if (count($splitted[0]) > $cnt) {
962                            $result['minute'] = $splitted[0][$cnt];
963                        }
964                    } else {
965                        $result['minute'] = iconv_substr($splitted[0][0], $split, 2);
966                        $split += 2;
967                    }
968                    ++$cnt;
969                    break;
970                case 's':
971                    if ($split === false) {
972                        if (count($splitted[0]) > $cnt) {
973                            $result['second'] = $splitted[0][$cnt];
974                        }
975                    } else {
976                        $result['second'] = iconv_substr($splitted[0][0], $split, 2);
977                        $split += 2;
978                    }
979                    ++$cnt;
980                    break;
981            }
982        }
983
984        // AM/PM correction
985        if ($hour !== false) {
986            if (($am === true) and ($result['hour'] == 12)){
987                $result['hour'] = 0;
988            } else if (($am === false) and ($result['hour'] != 12)) {
989                $result['hour'] += 12;
990            }
991        }
992
993        if ($options['fix_date'] === true) {
994            $result['fixed'] = 0; // nothing has been "fixed" by swapping date parts around (yet)
995        }
996
997        if ($day !== false) {
998            // fix false month
999            if (isset($result['day']) and isset($result['month'])) {
1000                if (($position !== false) and ((iconv_strpos($date, $result['day']) === false) or
1001                                               (isset($result['year']) and (iconv_strpos($date, $result['year']) === false)))) {
1002                    if ($options['fix_date'] !== true) {
1003                        self::_setEncoding($oenc);
1004                        throw new Zend_Locale_Exception("Unable to parse date '$date' using '" . $format
1005                            . "' (false month, $position, $month)");
1006                    }
1007                    $temp = $result['day'];
1008                    $result['day']   = $result['month'];
1009                    $result['month'] = $temp;
1010                    $result['fixed'] = 1;
1011                }
1012            }
1013
1014            // fix switched values d <> y
1015            if (isset($result['day']) and isset($result['year'])) {
1016                if ($result['day'] > 31) {
1017                    if ($options['fix_date'] !== true) {
1018                        self::_setEncoding($oenc);
1019                        throw new Zend_Locale_Exception("Unable to parse date '$date' using '"
1020                                                      . $format . "' (d <> y)");
1021                    }
1022                    $temp = $result['year'];
1023                    $result['year'] = $result['day'];
1024                    $result['day']  = $temp;
1025                    $result['fixed'] = 2;
1026                }
1027            }
1028
1029            // fix switched values M <> y
1030            if (isset($result['month']) and isset($result['year'])) {
1031                if ($result['month'] > 31) {
1032                    if ($options['fix_date'] !== true) {
1033                        self::_setEncoding($oenc);
1034                        throw new Zend_Locale_Exception("Unable to parse date '$date' using '"
1035                                                      . $format . "' (M <> y)");
1036                    }
1037                    $temp = $result['year'];
1038                    $result['year']  = $result['month'];
1039                    $result['month'] = $temp;
1040                    $result['fixed'] = 3;
1041                }
1042            }
1043
1044            // fix switched values M <> d
1045            if (isset($result['month']) and isset($result['day'])) {
1046                if ($result['month'] > 12) {
1047                    if ($options['fix_date'] !== true || $result['month'] > 31) {
1048                        self::_setEncoding($oenc);
1049                        throw new Zend_Locale_Exception("Unable to parse date '$date' using '"
1050                                                      . $format . "' (M <> d)");
1051                    }
1052                    $temp = $result['day'];
1053                    $result['day']   = $result['month'];
1054                    $result['month'] = $temp;
1055                    $result['fixed'] = 4;
1056                }
1057            }
1058        }
1059
1060        if (isset($result['year'])) {
1061            if (((iconv_strlen($result['year']) == 2) && ($result['year'] < 10)) ||
1062                (((iconv_strpos($format, 'yy') !== false) && (iconv_strpos($format, 'yyyy') === false)) ||
1063                ((iconv_strpos($format, 'YY') !== false) && (iconv_strpos($format, 'YYYY') === false)))) {
1064                if (($result['year'] >= 0) && ($result['year'] < 100)) {
1065                    if ($result['year'] < 70) {
1066                        $result['year'] = (int) $result['year'] + 100;
1067                    }
1068
1069                    $result['year'] = (int) $result['year'] + 1900;
1070                }
1071            }
1072        }
1073
1074        self::_setEncoding($oenc);
1075        return $result;
1076    }
1077
1078    /**
1079     * Search $number for a month name found in $monthlist, and replace if found.
1080     *
1081     * @param  string  $number     Date string (modified)
1082     * @param  array   $monthlist  List of month names
1083     *
1084     * @return int|false           Position of replaced string (false if nothing replaced)
1085     */
1086    protected static function _replaceMonth(&$number, $monthlist)
1087    {
1088        // If $locale was invalid, $monthlist will default to a "root" identity
1089        // mapping for each month number from 1 to 12.
1090        // If no $locale was given, or $locale was invalid, do not use this identity mapping to normalize.
1091        // Otherwise, translate locale aware month names in $number to their numeric equivalents.
1092        $position = false;
1093        if ($monthlist && $monthlist[1] != 1) {
1094            foreach($monthlist as $key => $name) {
1095                if (($position = iconv_strpos($number, $name, 0, 'UTF-8')) !== false) {
1096                    $number   = str_ireplace($name, $key, $number);
1097                    return $position;
1098                }
1099            }
1100        }
1101
1102        return false;
1103    }
1104
1105    /**
1106     * Returns the default date format for $locale.
1107     *
1108     * @param  string|Zend_Locale  $locale  OPTIONAL Locale of $number, possibly in string form (e.g. 'de_AT')
1109     * @return string  format
1110     * @throws Zend_Locale_Exception  throws an exception when locale data is broken
1111     */
1112    public static function getDateFormat($locale = null)
1113    {
1114        $format = Zend_Locale_Data::getContent($locale, 'date');
1115        if (empty($format)) {
1116            throw new Zend_Locale_Exception("failed to receive data from locale $locale");
1117        }
1118
1119        return $format;
1120    }
1121
1122    /**
1123     * Returns an array with the normalized date from an locale date
1124     * a input of 10.01.2006 without a $locale would return:
1125     * array ('day' => 10, 'month' => 1, 'year' => 2006)
1126     * The 'locale' option is only used to convert human readable day
1127     * and month names to their numeric equivalents.
1128     * The 'format' option allows specification of self-defined date formats,
1129     * when not using the default format for the 'locale'.
1130     *
1131     * @param   string  $date     Date string
1132     * @param   array   $options  Options: format_type, fix_date, locale, date_format. See {@link setOptions()} for details.
1133     * @return  array             Possible array members: day, month, year, hour, minute, second, fixed, format
1134     */
1135    public static function getDate($date, array $options = array())
1136    {
1137        $options = self::_checkOptions($options) + self::$_options;
1138        if (empty($options['date_format'])) {
1139            $options['format_type'] = 'iso';
1140            $options['date_format'] = self::getDateFormat($options['locale']);
1141        }
1142
1143        return self::_parseDate($date, $options);
1144    }
1145
1146    /**
1147     * Returns if the given datestring contains all date parts from the given format.
1148     * If no format is given, the default date format from the locale is used
1149     * If you want to check if the date is a proper date you should use Zend_Date::isDate()
1150     *
1151     * @param   string  $date     Date string
1152     * @param   array   $options  Options: format_type, fix_date, locale, date_format. See {@link setOptions()} for details.
1153     * @return  boolean
1154     */
1155    public static function checkDateFormat($date, array $options = array())
1156    {
1157        try {
1158            $date = self::getDate($date, $options);
1159        } catch (Exception $e) {
1160            return false;
1161        }
1162
1163        if (empty($options['date_format'])) {
1164            $options['format_type'] = 'iso';
1165            $options['date_format'] = self::getDateFormat(isset($options['locale']) ? $options['locale'] : null);
1166        }
1167        $options = self::_checkOptions($options) + self::$_options;
1168
1169        // day expected but not parsed
1170        if ((iconv_strpos($options['date_format'], 'd', 0, 'UTF-8') !== false) and (!isset($date['day']) or ($date['day'] === ""))) {
1171            return false;
1172        }
1173
1174        // month expected but not parsed
1175        if ((iconv_strpos($options['date_format'], 'M', 0, 'UTF-8') !== false) and (!isset($date['month']) or ($date['month'] === ""))) {
1176            return false;
1177        }
1178
1179        // year expected but not parsed
1180        if (((iconv_strpos($options['date_format'], 'Y', 0, 'UTF-8') !== false) or
1181             (iconv_strpos($options['date_format'], 'y', 0, 'UTF-8') !== false)) and (!isset($date['year']) or ($date['year'] === ""))) {
1182            return false;
1183        }
1184
1185        // second expected but not parsed
1186        if ((iconv_strpos($options['date_format'], 's', 0, 'UTF-8') !== false) and (!isset($date['second']) or ($date['second'] === ""))) {
1187            return false;
1188        }
1189
1190        // minute expected but not parsed
1191        if ((iconv_strpos($options['date_format'], 'm', 0, 'UTF-8') !== false) and (!isset($date['minute']) or ($date['minute'] === ""))) {
1192            return false;
1193        }
1194
1195        // hour expected but not parsed
1196        if (((iconv_strpos($options['date_format'], 'H', 0, 'UTF-8') !== false) or
1197             (iconv_strpos($options['date_format'], 'h', 0, 'UTF-8') !== false)) and (!isset($date['hour']) or ($date['hour'] === ""))) {
1198            return false;
1199        }
1200
1201        return true;
1202    }
1203
1204    /**
1205     * Returns the default time format for $locale.
1206     *
1207     * @param  string|Zend_Locale $locale OPTIONAL Locale of $number, possibly in string form (e.g. 'de_AT')
1208     * @return string  format
1209     * @throws Zend_Locale_Exception
1210     */
1211    public static function getTimeFormat($locale = null)
1212    {
1213        $format = Zend_Locale_Data::getContent($locale, 'time');
1214        if (empty($format)) {
1215            throw new Zend_Locale_Exception("failed to receive data from locale $locale");
1216        }
1217        return $format;
1218    }
1219
1220    /**
1221     * Returns an array with 'hour', 'minute', and 'second' elements extracted from $time
1222     * according to the order described in $format.  For a format of 'H:i:s', and
1223     * an input of 11:20:55, getTime() would return:
1224     * array ('hour' => 11, 'minute' => 20, 'second' => 55)
1225     * The optional $locale parameter may be used to help extract times from strings
1226     * containing both a time and a day or month name.
1227     *
1228     * @param   string  $time     Time string
1229     * @param   array   $options  Options: format_type, fix_date, locale, date_format. See {@link setOptions()} for details.
1230     * @return  array             Possible array members: day, month, year, hour, minute, second, fixed, format
1231     */
1232    public static function getTime($time, array $options = array())
1233    {
1234        $options = self::_checkOptions($options) + self::$_options;
1235        if (empty($options['date_format'])) {
1236            $options['format_type'] = 'iso';
1237            $options['date_format'] = self::getTimeFormat($options['locale']);
1238        }
1239        return self::_parseDate($time, $options);
1240    }
1241
1242    /**
1243     * Returns the default datetime format for $locale.
1244     *
1245     * @param  string|Zend_Locale $locale OPTIONAL Locale of $number, possibly in string form (e.g. 'de_AT')
1246     * @return string  format
1247     * @throws Zend_Locale_Exception
1248     */
1249    public static function getDateTimeFormat($locale = null)
1250    {
1251        $format = Zend_Locale_Data::getContent($locale, 'datetime');
1252        if (empty($format)) {
1253            throw new Zend_Locale_Exception("failed to receive data from locale $locale");
1254        }
1255        return $format;
1256    }
1257
1258    /**
1259     * Returns an array with 'year', 'month', 'day', 'hour', 'minute', and 'second' elements
1260     * extracted from $datetime according to the order described in $format.  For a format of 'd.M.y H:i:s',
1261     * and an input of 10.05.1985 11:20:55, getDateTime() would return:
1262     * array ('year' => 1985, 'month' => 5, 'day' => 10, 'hour' => 11, 'minute' => 20, 'second' => 55)
1263     * The optional $locale parameter may be used to help extract times from strings
1264     * containing both a time and a day or month name.
1265     *
1266     * @param   string  $datetime DateTime string
1267     * @param   array   $options  Options: format_type, fix_date, locale, date_format. See {@link setOptions()} for details.
1268     * @return  array             Possible array members: day, month, year, hour, minute, second, fixed, format
1269     */
1270    public static function getDateTime($datetime, array $options = array())
1271    {
1272        $options = self::_checkOptions($options) + self::$_options;
1273        if (empty($options['date_format'])) {
1274            $options['format_type'] = 'iso';
1275            $options['date_format'] = self::getDateTimeFormat($options['locale']);
1276        }
1277        return self::_parseDate($datetime, $options);
1278    }
1279
1280    /**
1281     * Internal method to detect of Unicode supports UTF8
1282     * which should be enabled within vanilla php installations
1283     *
1284     * @return boolean
1285     */
1286    protected static function _getUniCodeSupport()
1287    {
1288        return (@preg_match('/\pL/u', 'a')) ? true : false;
1289    }
1290
1291    /**
1292     * Internal method to retrieve the current encoding via the ini setting
1293     * default_charset for PHP >= 5.6 or iconv_get_encoding otherwise.
1294     *
1295     * @return string
1296     */
1297    protected static function _getEncoding()
1298    {
1299        $oenc = PHP_VERSION_ID < 50600
1300            ? iconv_get_encoding('internal_encoding')
1301            : ini_get('default_charset');
1302
1303        return $oenc;
1304    }
1305
1306    /**
1307     * Internal method to set the encoding via the ini setting
1308     * default_charset for PHP >= 5.6 or iconv_set_encoding otherwise.
1309     *
1310     * @param string $encoding
1311     * @return void
1312     */
1313    protected static function _setEncoding($encoding)
1314    {
1315        if (PHP_VERSION_ID < 50600) {
1316            iconv_set_encoding('internal_encoding', $encoding);
1317        } else {
1318            ini_set('default_charset', $encoding);
1319        }
1320    }
1321}
1322