login consent app sql

This commit is contained in:
2022-05-03 08:54:45 +02:00
parent e7253acfd8
commit f9a6535906
1652 changed files with 187600 additions and 45 deletions

View File

@ -0,0 +1,262 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu;
use Symfony\Polyfill\Intl\Icu\Exception\MethodArgumentValueNotImplementedException;
use Symfony\Polyfill\Intl\Icu\Exception\MethodNotImplementedException;
/**
* Replacement for PHP's native {@link \Collator} class.
*
* The only methods currently supported in this class are:
*
* - {@link \__construct}
* - {@link create}
* - {@link asort}
* - {@link getErrorCode}
* - {@link getErrorMessage}
* - {@link getLocale}
*
* @author Igor Wiedler <igor@wiedler.ch>
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
abstract class Collator
{
/* Attribute constants */
public const FRENCH_COLLATION = 0;
public const ALTERNATE_HANDLING = 1;
public const CASE_FIRST = 2;
public const CASE_LEVEL = 3;
public const NORMALIZATION_MODE = 4;
public const STRENGTH = 5;
public const HIRAGANA_QUATERNARY_MODE = 6;
public const NUMERIC_COLLATION = 7;
/* Attribute constants values */
public const DEFAULT_VALUE = -1;
public const PRIMARY = 0;
public const SECONDARY = 1;
public const TERTIARY = 2;
public const DEFAULT_STRENGTH = 2;
public const QUATERNARY = 3;
public const IDENTICAL = 15;
public const OFF = 16;
public const ON = 17;
public const SHIFTED = 20;
public const NON_IGNORABLE = 21;
public const LOWER_FIRST = 24;
public const UPPER_FIRST = 25;
/* Sorting options */
public const SORT_REGULAR = 0;
public const SORT_NUMERIC = 2;
public const SORT_STRING = 1;
/**
* @param string|null $locale The locale code. The only currently supported locale is "en" (or null using the default locale, i.e. "en")
*
* @throws MethodArgumentValueNotImplementedException When $locale different than "en" or null is passed
*/
public function __construct(?string $locale)
{
if ('en' !== $locale && null !== $locale) {
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'locale', $locale, 'Only the locale "en" is supported');
}
}
/**
* Static constructor.
*
* @param string|null $locale The locale code. The only currently supported locale is "en" (or null using the default locale, i.e. "en")
*
* @return static
*
* @throws MethodArgumentValueNotImplementedException When $locale different than "en" or null is passed
*/
public static function create(?string $locale)
{
return new static($locale);
}
/**
* Sort array maintaining index association.
*
* @param array &$array Input array
* @param int $flags Flags for sorting, can be one of the following:
* Collator::SORT_REGULAR - compare items normally (don't change types)
* Collator::SORT_NUMERIC - compare items numerically
* Collator::SORT_STRING - compare items as strings
*
* @return bool True on success or false on failure
*/
public function asort(array &$array, int $flags = self::SORT_REGULAR)
{
$intlToPlainFlagMap = [
self::SORT_REGULAR => \SORT_REGULAR,
self::SORT_NUMERIC => \SORT_NUMERIC,
self::SORT_STRING => \SORT_STRING,
];
$plainSortFlag = $intlToPlainFlagMap[$flags] ?? self::SORT_REGULAR;
return asort($array, $plainSortFlag);
}
/**
* Not supported. Compare two Unicode strings.
*
* @return bool|int
*
* @see https://php.net/collator.compare
*
* @throws MethodNotImplementedException
*/
public function compare(string $string1, string $string2)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Get a value of an integer collator attribute.
*
* @return bool|int The attribute value on success or false on error
*
* @see https://php.net/collator.getattribute
*
* @throws MethodNotImplementedException
*/
public function getAttribute(int $attribute)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns collator's last error code. Always returns the U_ZERO_ERROR class constant value.
*
* @return int The error code from last collator call
*/
public function getErrorCode()
{
return Icu::U_ZERO_ERROR;
}
/**
* Returns collator's last error message. Always returns the U_ZERO_ERROR_MESSAGE class constant value.
*
* @return string The error message from last collator call
*/
public function getErrorMessage()
{
return 'U_ZERO_ERROR';
}
/**
* Returns the collator's locale.
*
* @return string The locale used to create the collator. Currently always
* returns "en".
*/
public function getLocale(int $type = Locale::ACTUAL_LOCALE)
{
return 'en';
}
/**
* Not supported. Get sorting key for a string.
*
* @return string The collation key for $string
*
* @see https://php.net/collator.getsortkey
*
* @throws MethodNotImplementedException
*/
public function getSortKey(string $string)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Get current collator's strength.
*
* @return bool|int The current collator's strength or false on failure
*
* @see https://php.net/collator.getstrength
*
* @throws MethodNotImplementedException
*/
public function getStrength()
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Set a collator's attribute.
*
* @return bool True on success or false on failure
*
* @see https://php.net/collator.setattribute
*
* @throws MethodNotImplementedException
*/
public function setAttribute(int $attribute, int $value)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Set the collator's strength.
*
* @return bool True on success or false on failure
*
* @see https://php.net/collator.setstrength
*
* @throws MethodNotImplementedException
*/
public function setStrength(int $strength)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Sort array using specified collator and sort keys.
*
* @return bool True on success or false on failure
*
* @see https://php.net/collator.sortwithsortkeys
*
* @throws MethodNotImplementedException
*/
public function sortWithSortKeys(array &$array)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Sort array using specified collator.
*
* @return bool True on success or false on failure
*
* @see https://php.net/collator.sort
*
* @throws MethodNotImplementedException
*/
public function sort(array &$array, int $flags = self::SORT_REGULAR)
{
throw new MethodNotImplementedException(__METHOD__);
}
}

View File

@ -0,0 +1,43 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu;
/**
* @author Nicolas Grekas <p@tchwork.com>
*
* @internal
*/
class Currencies
{
private static $data;
public static function getSymbol(string $currency): ?string
{
$data = self::$data ?? self::$data = require __DIR__.'/Resources/currencies.php';
return $data[$currency][0] ?? $data[strtoupper($currency)][0] ?? null;
}
public static function getFractionDigits(string $currency): int
{
$data = self::$data ?? self::$data = require __DIR__.'/Resources/currencies.php';
return $data[$currency][1] ?? $data[strtoupper($currency)][1] ?? $data['DEFAULT'][1];
}
public static function getRoundingIncrement(string $currency): int
{
$data = self::$data ?? self::$data = require __DIR__.'/Resources/currencies.php';
return $data[$currency][2] ?? $data[strtoupper($currency)][2] ?? $data['DEFAULT'][2];
}
}

View File

@ -0,0 +1,48 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu\DateFormat;
/**
* Parser and formatter for AM/PM markers format.
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
class AmPmTransformer extends Transformer
{
/**
* {@inheritdoc}
*/
public function format(\DateTime $dateTime, int $length): string
{
return $dateTime->format('A');
}
/**
* {@inheritdoc}
*/
public function getReverseMatchingRegExp(int $length): string
{
return 'AM|PM';
}
/**
* {@inheritdoc}
*/
public function extractDateOptions(string $matched, int $length): array
{
return [
'marker' => $matched,
];
}
}

View File

@ -0,0 +1,65 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu\DateFormat;
/**
* Parser and formatter for day of week format.
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
class DayOfWeekTransformer extends Transformer
{
/**
* {@inheritdoc}
*/
public function format(\DateTime $dateTime, int $length): string
{
$dayOfWeek = $dateTime->format('l');
switch ($length) {
case 4:
return $dayOfWeek;
case 5:
return $dayOfWeek[0];
case 6:
return substr($dayOfWeek, 0, 2);
default:
return substr($dayOfWeek, 0, 3);
}
}
/**
* {@inheritdoc}
*/
public function getReverseMatchingRegExp(int $length): string
{
switch ($length) {
case 4:
return 'Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday';
case 5:
return '[MTWFS]';
case 6:
return 'Mo|Tu|We|Th|Fr|Sa|Su';
default:
return 'Mon|Tue|Wed|Thu|Fri|Sat|Sun';
}
}
/**
* {@inheritdoc}
*/
public function extractDateOptions(string $matched, int $length): array
{
return [];
}
}

View File

@ -0,0 +1,48 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu\DateFormat;
/**
* Parser and formatter for day of year format.
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
class DayOfYearTransformer extends Transformer
{
/**
* {@inheritdoc}
*/
public function format(\DateTime $dateTime, int $length): string
{
$dayOfYear = (int) $dateTime->format('z') + 1;
return $this->padLeft($dayOfYear, $length);
}
/**
* {@inheritdoc}
*/
public function getReverseMatchingRegExp(int $length): string
{
return '\d{'.$length.'}';
}
/**
* {@inheritdoc}
*/
public function extractDateOptions(string $matched, int $length): array
{
return [];
}
}

View File

@ -0,0 +1,48 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu\DateFormat;
/**
* Parser and formatter for day format.
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
class DayTransformer extends Transformer
{
/**
* {@inheritdoc}
*/
public function format(\DateTime $dateTime, int $length): string
{
return $this->padLeft($dateTime->format('j'), $length);
}
/**
* {@inheritdoc}
*/
public function getReverseMatchingRegExp(int $length): string
{
return 1 === $length ? '\d{1,2}' : '\d{1,'.$length.'}';
}
/**
* {@inheritdoc}
*/
public function extractDateOptions(string $matched, int $length): array
{
return [
'day' => (int) $matched,
];
}
}

View File

@ -0,0 +1,312 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu\DateFormat;
use Symfony\Polyfill\Intl\Icu\Exception\NotImplementedException;
use Symfony\Polyfill\Intl\Icu\Icu;
/**
* Parser and formatter for date formats.
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
class FullTransformer
{
private $quoteMatch = "'(?:[^']+|'')*'";
private $implementedChars = 'MLydQqhDEaHkKmsz';
private $notImplementedChars = 'GYuwWFgecSAZvVW';
private $regExp;
/**
* @var Transformer[]
*/
private $transformers;
private $pattern;
private $timezone;
/**
* @param string $pattern The pattern to be used to format and/or parse values
* @param string $timezone The timezone to perform the date/time calculations
*/
public function __construct(string $pattern, string $timezone)
{
$this->pattern = $pattern;
$this->timezone = $timezone;
$implementedCharsMatch = $this->buildCharsMatch($this->implementedChars);
$notImplementedCharsMatch = $this->buildCharsMatch($this->notImplementedChars);
$this->regExp = "/($this->quoteMatch|$implementedCharsMatch|$notImplementedCharsMatch)/";
$this->transformers = [
'M' => new MonthTransformer(),
'L' => new MonthTransformer(),
'y' => new YearTransformer(),
'd' => new DayTransformer(),
'q' => new QuarterTransformer(),
'Q' => new QuarterTransformer(),
'h' => new Hour1201Transformer(),
'D' => new DayOfYearTransformer(),
'E' => new DayOfWeekTransformer(),
'a' => new AmPmTransformer(),
'H' => new Hour2400Transformer(),
'K' => new Hour1200Transformer(),
'k' => new Hour2401Transformer(),
'm' => new MinuteTransformer(),
's' => new SecondTransformer(),
'z' => new TimezoneTransformer(),
];
}
/**
* Format a DateTime using ICU dateformat pattern.
*
* @return string The formatted value
*/
public function format(\DateTime $dateTime): string
{
$formatted = preg_replace_callback($this->regExp, function ($matches) use ($dateTime) {
return $this->formatReplace($matches[0], $dateTime);
}, $this->pattern);
return $formatted;
}
/**
* Return the formatted ICU value for the matched date characters.
*
* @throws NotImplementedException When it encounters a not implemented date character
*/
private function formatReplace(string $dateChars, \DateTime $dateTime): string
{
$length = \strlen($dateChars);
if ($this->isQuoteMatch($dateChars)) {
return $this->replaceQuoteMatch($dateChars);
}
if (isset($this->transformers[$dateChars[0]])) {
$transformer = $this->transformers[$dateChars[0]];
return $transformer->format($dateTime, $length);
}
// handle unimplemented characters
if (false !== strpos($this->notImplementedChars, $dateChars[0])) {
throw new NotImplementedException(sprintf('Unimplemented date character "%s" in format "%s".', $dateChars[0], $this->pattern));
}
return '';
}
/**
* Parse a pattern based string to a timestamp value.
*
* @param \DateTime $dateTime A configured DateTime object to use to perform the date calculation
* @param string $value String to convert to a time value
*
* @return int|false The corresponding Unix timestamp
*
* @throws \InvalidArgumentException When the value can not be matched with pattern
*/
public function parse(\DateTime $dateTime, string $value)
{
$reverseMatchingRegExp = $this->getReverseMatchingRegExp($this->pattern);
$reverseMatchingRegExp = '/^'.$reverseMatchingRegExp.'$/';
$options = [];
if (preg_match($reverseMatchingRegExp, $value, $matches)) {
$matches = $this->normalizeArray($matches);
foreach ($this->transformers as $char => $transformer) {
if (isset($matches[$char])) {
$length = \strlen($matches[$char]['pattern']);
$options = array_merge($options, $transformer->extractDateOptions($matches[$char]['value'], $length));
}
}
// reset error code and message
Icu::setError(Icu::U_ZERO_ERROR);
return $this->calculateUnixTimestamp($dateTime, $options);
}
// behave like the intl extension
Icu::setError(Icu::U_PARSE_ERROR, 'Date parsing failed');
return false;
}
/**
* Retrieve a regular expression to match with a formatted value.
*
* @return string The reverse matching regular expression with named captures being formed by the
* transformer index in the $transformer array
*/
private function getReverseMatchingRegExp(string $pattern): string
{
$escapedPattern = preg_quote($pattern, '/');
// ICU 4.8 recognizes slash ("/") in a value to be parsed as a dash ("-") and vice-versa
// when parsing a date/time value
$escapedPattern = preg_replace('/\\\[\-|\/]/', '[\/\-]', $escapedPattern);
$reverseMatchingRegExp = preg_replace_callback($this->regExp, function ($matches) {
$length = \strlen($matches[0]);
$transformerIndex = $matches[0][0];
$dateChars = $matches[0];
if ($this->isQuoteMatch($dateChars)) {
return $this->replaceQuoteMatch($dateChars);
}
if (isset($this->transformers[$transformerIndex])) {
$transformer = $this->transformers[$transformerIndex];
$captureName = str_repeat($transformerIndex, $length);
return "(?P<$captureName>".$transformer->getReverseMatchingRegExp($length).')';
}
return null;
}, $escapedPattern);
return $reverseMatchingRegExp;
}
/**
* Check if the first char of a string is a single quote.
*/
private function isQuoteMatch(string $quoteMatch): bool
{
return "'" === $quoteMatch[0];
}
/**
* Replaces single quotes at the start or end of a string with two single quotes.
*/
private function replaceQuoteMatch(string $quoteMatch): string
{
if (preg_match("/^'+$/", $quoteMatch)) {
return str_replace("''", "'", $quoteMatch);
}
return str_replace("''", "'", substr($quoteMatch, 1, -1));
}
/**
* Builds a chars match regular expression.
*/
private function buildCharsMatch(string $specialChars): string
{
$specialCharsArray = str_split($specialChars);
$specialCharsMatch = implode('|', array_map(function ($char) {
return $char.'+';
}, $specialCharsArray));
return $specialCharsMatch;
}
/**
* Normalize a preg_replace match array, removing the numeric keys and returning an associative array
* with the value and pattern values for the matched Transformer.
*/
private function normalizeArray(array $data): array
{
$ret = [];
foreach ($data as $key => $value) {
if (!\is_string($key)) {
continue;
}
$ret[$key[0]] = [
'value' => $value,
'pattern' => $key,
];
}
return $ret;
}
/**
* Calculates the Unix timestamp based on the matched values by the reverse matching regular
* expression of parse().
*
* @return bool|int The calculated timestamp or false if matched date is invalid
*/
private function calculateUnixTimestamp(\DateTime $dateTime, array $options)
{
$options = $this->getDefaultValueForOptions($options);
$year = $options['year'];
$month = $options['month'];
$day = $options['day'];
$hour = $options['hour'];
$hourInstance = $options['hourInstance'];
$minute = $options['minute'];
$second = $options['second'];
$marker = $options['marker'];
$timezone = $options['timezone'];
// If month is false, return immediately (intl behavior)
if (false === $month) {
Icu::setError(Icu::U_PARSE_ERROR, 'Date parsing failed');
return false;
}
// Normalize hour
if ($hourInstance instanceof HourTransformer) {
$hour = $hourInstance->normalizeHour($hour, $marker);
}
// Set the timezone if different from the default one
if (null !== $timezone && $timezone !== $this->timezone) {
$dateTime->setTimezone(new \DateTimeZone($timezone));
}
// Normalize yy year
preg_match_all($this->regExp, $this->pattern, $matches);
if (\in_array('yy', $matches[0])) {
$dateTime->setTimestamp(time());
$year = $year > (int) $dateTime->format('y') + 20 ? 1900 + $year : 2000 + $year;
}
$dateTime->setDate($year, $month, $day);
$dateTime->setTime($hour, $minute, $second);
return $dateTime->getTimestamp();
}
/**
* Add sensible default values for missing items in the extracted date/time options array. The values
* are base in the beginning of the Unix era.
*/
private function getDefaultValueForOptions(array $options): array
{
return [
'year' => $options['year'] ?? 1970,
'month' => $options['month'] ?? 1,
'day' => $options['day'] ?? 1,
'hour' => $options['hour'] ?? 0,
'hourInstance' => $options['hourInstance'] ?? null,
'minute' => $options['minute'] ?? 0,
'second' => $options['second'] ?? 0,
'marker' => $options['marker'] ?? null,
'timezone' => $options['timezone'] ?? null,
];
}
}

View File

@ -0,0 +1,64 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu\DateFormat;
/**
* Parser and formatter for 12 hour format (0-11).
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
class Hour1200Transformer extends HourTransformer
{
/**
* {@inheritdoc}
*/
public function format(\DateTime $dateTime, int $length): string
{
$hourOfDay = $dateTime->format('g');
$hourOfDay = '12' === $hourOfDay ? '0' : $hourOfDay;
return $this->padLeft($hourOfDay, $length);
}
/**
* {@inheritdoc}
*/
public function normalizeHour(int $hour, string $marker = null): int
{
if ('PM' === $marker) {
$hour += 12;
}
return $hour;
}
/**
* {@inheritdoc}
*/
public function getReverseMatchingRegExp(int $length): string
{
return '\d{1,2}';
}
/**
* {@inheritdoc}
*/
public function extractDateOptions(string $matched, int $length): array
{
return [
'hour' => (int) $matched,
'hourInstance' => $this,
];
}
}

View File

@ -0,0 +1,64 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu\DateFormat;
/**
* Parser and formatter for 12 hour format (1-12).
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
class Hour1201Transformer extends HourTransformer
{
/**
* {@inheritdoc}
*/
public function format(\DateTime $dateTime, int $length): string
{
return $this->padLeft($dateTime->format('g'), $length);
}
/**
* {@inheritdoc}
*/
public function normalizeHour(int $hour, string $marker = null): int
{
if ('PM' !== $marker && 12 === $hour) {
$hour = 0;
} elseif ('PM' === $marker && 12 !== $hour) {
// If PM and hour is not 12 (1-12), sum 12 hour
$hour += 12;
}
return $hour;
}
/**
* {@inheritdoc}
*/
public function getReverseMatchingRegExp(int $length): string
{
return '\d{1,2}';
}
/**
* {@inheritdoc}
*/
public function extractDateOptions(string $matched, int $length): array
{
return [
'hour' => (int) $matched,
'hourInstance' => $this,
];
}
}

View File

@ -0,0 +1,63 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu\DateFormat;
/**
* Parser and formatter for 24 hour format (0-23).
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
class Hour2400Transformer extends HourTransformer
{
/**
* {@inheritdoc}
*/
public function format(\DateTime $dateTime, int $length): string
{
return $this->padLeft($dateTime->format('G'), $length);
}
/**
* {@inheritdoc}
*/
public function normalizeHour(int $hour, string $marker = null): int
{
if ('AM' === $marker) {
$hour = 0;
} elseif ('PM' === $marker) {
$hour = 12;
}
return $hour;
}
/**
* {@inheritdoc}
*/
public function getReverseMatchingRegExp(int $length): string
{
return '\d{1,2}';
}
/**
* {@inheritdoc}
*/
public function extractDateOptions(string $matched, int $length): array
{
return [
'hour' => (int) $matched,
'hourInstance' => $this,
];
}
}

View File

@ -0,0 +1,66 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu\DateFormat;
/**
* Parser and formatter for 24 hour format (1-24).
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
class Hour2401Transformer extends HourTransformer
{
/**
* {@inheritdoc}
*/
public function format(\DateTime $dateTime, int $length): string
{
$hourOfDay = $dateTime->format('G');
$hourOfDay = '0' === $hourOfDay ? '24' : $hourOfDay;
return $this->padLeft($hourOfDay, $length);
}
/**
* {@inheritdoc}
*/
public function normalizeHour(int $hour, string $marker = null): int
{
if ((null === $marker && 24 === $hour) || 'AM' === $marker) {
$hour = 0;
} elseif ('PM' === $marker) {
$hour = 12;
}
return $hour;
}
/**
* {@inheritdoc}
*/
public function getReverseMatchingRegExp(int $length): string
{
return '\d{1,2}';
}
/**
* {@inheritdoc}
*/
public function extractDateOptions(string $matched, int $length): array
{
return [
'hour' => (int) $matched,
'hourInstance' => $this,
];
}
}

View File

@ -0,0 +1,32 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu\DateFormat;
/**
* Base class for hour transformers.
*
* @author Eriksen Costa <eriksen.costa@infranology.com.br>
*
* @internal
*/
abstract class HourTransformer extends Transformer
{
/**
* Returns a normalized hour value suitable for the hour transformer type.
*
* @param int $hour The hour value
* @param string $marker An optional AM/PM marker
*
* @return int The normalized hour value
*/
abstract public function normalizeHour(int $hour, string $marker = null): int;
}

View File

@ -0,0 +1,50 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu\DateFormat;
/**
* Parser and formatter for minute format.
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
class MinuteTransformer extends Transformer
{
/**
* {@inheritdoc}
*/
public function format(\DateTime $dateTime, int $length): string
{
$minuteOfHour = (int) $dateTime->format('i');
return $this->padLeft($minuteOfHour, $length);
}
/**
* {@inheritdoc}
*/
public function getReverseMatchingRegExp(int $length): string
{
return 1 === $length ? '\d{1,2}' : '\d{'.$length.'}';
}
/**
* {@inheritdoc}
*/
public function extractDateOptions(string $matched, int $length): array
{
return [
'minute' => (int) $matched,
];
}
}

View File

@ -0,0 +1,136 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu\DateFormat;
/**
* Parser and formatter for month format.
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
class MonthTransformer extends Transformer
{
protected static $months = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
];
/**
* Short months names (first 3 letters).
*/
protected static $shortMonths = [];
/**
* Flipped $months array, $name => $index.
*/
protected static $flippedMonths = [];
/**
* Flipped $shortMonths array, $name => $index.
*/
protected static $flippedShortMonths = [];
public function __construct()
{
if (0 === \count(self::$shortMonths)) {
self::$shortMonths = array_map(function ($month) {
return substr($month, 0, 3);
}, self::$months);
self::$flippedMonths = array_flip(self::$months);
self::$flippedShortMonths = array_flip(self::$shortMonths);
}
}
/**
* {@inheritdoc}
*/
public function format(\DateTime $dateTime, int $length): string
{
$matchLengthMap = [
1 => 'n',
2 => 'm',
3 => 'M',
4 => 'F',
];
if (isset($matchLengthMap[$length])) {
return $dateTime->format($matchLengthMap[$length]);
}
if (5 === $length) {
return substr($dateTime->format('M'), 0, 1);
}
return $this->padLeft($dateTime->format('m'), $length);
}
/**
* {@inheritdoc}
*/
public function getReverseMatchingRegExp(int $length): string
{
switch ($length) {
case 1:
$regExp = '\d{1,2}';
break;
case 3:
$regExp = implode('|', self::$shortMonths);
break;
case 4:
$regExp = implode('|', self::$months);
break;
case 5:
$regExp = '[JFMASOND]';
break;
default:
$regExp = '\d{1,'.$length.'}';
break;
}
return $regExp;
}
/**
* {@inheritdoc}
*/
public function extractDateOptions(string $matched, int $length): array
{
if (!is_numeric($matched)) {
if (3 === $length) {
$matched = self::$flippedShortMonths[$matched] + 1;
} elseif (4 === $length) {
$matched = self::$flippedMonths[$matched] + 1;
} elseif (5 === $length) {
// IntlDateFormatter::parse() always returns false for MMMMM or LLLLL
$matched = false;
}
} else {
$matched = (int) $matched;
}
return [
'month' => $matched,
];
}
}

View File

@ -0,0 +1,66 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu\DateFormat;
/**
* Parser and formatter for quarter format.
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
class QuarterTransformer extends Transformer
{
/**
* {@inheritdoc}
*/
public function format(\DateTime $dateTime, int $length): string
{
$month = (int) $dateTime->format('n');
$quarter = (int) floor(($month - 1) / 3) + 1;
switch ($length) {
case 1:
case 2:
return $this->padLeft($quarter, $length);
case 3:
return 'Q'.$quarter;
default:
$map = [1 => '1st quarter', 2 => '2nd quarter', 3 => '3rd quarter', 4 => '4th quarter'];
return $map[$quarter];
}
}
/**
* {@inheritdoc}
*/
public function getReverseMatchingRegExp(int $length): string
{
switch ($length) {
case 1:
case 2:
return '\d{'.$length.'}';
case 3:
return 'Q\d';
default:
return '(?:1st|2nd|3rd|4th) quarter';
}
}
/**
* {@inheritdoc}
*/
public function extractDateOptions(string $matched, int $length): array
{
return [];
}
}

View File

@ -0,0 +1,50 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu\DateFormat;
/**
* Parser and formatter for the second format.
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
class SecondTransformer extends Transformer
{
/**
* {@inheritdoc}
*/
public function format(\DateTime $dateTime, int $length): string
{
$secondOfMinute = (int) $dateTime->format('s');
return $this->padLeft($secondOfMinute, $length);
}
/**
* {@inheritdoc}
*/
public function getReverseMatchingRegExp(int $length): string
{
return 1 === $length ? '\d{1,2}' : '\d{'.$length.'}';
}
/**
* {@inheritdoc}
*/
public function extractDateOptions(string $matched, int $length): array
{
return [
'second' => (int) $matched,
];
}
}

View File

@ -0,0 +1,116 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu\DateFormat;
use Symfony\Polyfill\Intl\Icu\Exception\NotImplementedException;
/**
* Parser and formatter for time zone format.
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
class TimezoneTransformer extends Transformer
{
/**
* {@inheritdoc}
*
* @throws NotImplementedException When time zone is different than UTC or GMT (Etc/GMT)
*/
public function format(\DateTime $dateTime, int $length): string
{
$timeZone = substr($dateTime->getTimezone()->getName(), 0, 3);
if (!\in_array($timeZone, ['Etc', 'UTC', 'GMT'])) {
throw new NotImplementedException('Time zone different than GMT or UTC is not supported as a formatting output.');
}
if ('Etc' === $timeZone) {
// i.e. Etc/GMT+1, Etc/UTC, Etc/Zulu
$timeZone = substr($dateTime->getTimezone()->getName(), 4);
}
// From ICU >= 59.1 GMT and UTC are no longer unified
if (\in_array($timeZone, ['UTC', 'UCT', 'Universal', 'Zulu'])) {
// offset is not supported with UTC
return $length > 3 ? 'Coordinated Universal Time' : 'UTC';
}
$offset = (int) $dateTime->format('O');
// From ICU >= 4.8, the zero offset is no more used, example: GMT instead of GMT+00:00
if (0 === $offset) {
return $length > 3 ? 'Greenwich Mean Time' : 'GMT';
}
if ($length > 3) {
return $dateTime->format('\G\M\TP');
}
return sprintf('GMT%s%d', ($offset >= 0 ? '+' : ''), $offset / 100);
}
/**
* {@inheritdoc}
*/
public function getReverseMatchingRegExp(int $length): string
{
return 'GMT[+-]\d{2}:?\d{2}';
}
/**
* {@inheritdoc}
*/
public function extractDateOptions(string $matched, int $length): array
{
return [
'timezone' => self::getEtcTimeZoneId($matched),
];
}
/**
* Get an Etc/GMT timezone identifier for the specified timezone.
*
* The PHP documentation for timezones states to not use the 'Other' time zones because them exists
* "for backwards compatibility". However all Etc/GMT time zones are in the tz database 'etcetera' file,
* which indicates they are not deprecated (neither are old names).
*
* Only GMT, Etc/Universal, Etc/Zulu, Etc/Greenwich, Etc/GMT-0, Etc/GMT+0 and Etc/GMT0 are old names and
* are linked to Etc/GMT or Etc/UTC.
*
* @param string $formattedTimeZone A GMT timezone string (GMT-03:00, e.g.)
*
* @return string A timezone identifier
*
* @see https://php.net/timezones.others
*
* @throws NotImplementedException When the GMT time zone have minutes offset different than zero
* @throws \InvalidArgumentException When the value can not be matched with pattern
*/
public static function getEtcTimeZoneId(string $formattedTimeZone): string
{
if (preg_match('/GMT(?P<signal>[+-])(?P<hours>\d{2}):?(?P<minutes>\d{2})/', $formattedTimeZone, $matches)) {
$hours = (int) $matches['hours'];
$minutes = (int) $matches['minutes'];
$signal = '-' === $matches['signal'] ? '+' : '-';
if (0 < $minutes) {
throw new NotImplementedException(sprintf('It is not possible to use a GMT time zone with minutes offset different than zero (0). GMT time zone tried: "%s".', $formattedTimeZone));
}
return 'Etc/GMT'.(0 !== $hours ? $signal.$hours : '');
}
throw new \InvalidArgumentException(sprintf('The GMT time zone "%s" does not match with the supported formats GMT[+-]HH:MM or GMT[+-]HHMM.', $formattedTimeZone));
}
}

View File

@ -0,0 +1,65 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu\DateFormat;
/**
* Parser and formatter for date formats.
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
abstract class Transformer
{
/**
* Format a value using a configured DateTime as date/time source.
*
* @param \DateTime $dateTime A DateTime object to be used to generate the formatted value
* @param int $length The formatted value string length
*
* @return string The formatted value
*/
abstract public function format(\DateTime $dateTime, int $length): string;
/**
* Returns a reverse matching regular expression of a string generated by format().
*
* @param int $length The length of the value to be reverse matched
*
* @return string The reverse matching regular expression
*/
abstract public function getReverseMatchingRegExp(int $length): string;
/**
* Extract date options from a matched value returned by the processing of the reverse matching
* regular expression.
*
* @param string $matched The matched value
* @param int $length The length of the Transformer pattern string
*
* @return array An associative array
*/
abstract public function extractDateOptions(string $matched, int $length): array;
/**
* Pad a string with zeros to the left.
*
* @param string $value The string to be padded
* @param int $length The length to pad
*
* @return string The padded string
*/
protected function padLeft(string $value, int $length): string
{
return str_pad($value, $length, '0', \STR_PAD_LEFT);
}
}

View File

@ -0,0 +1,52 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu\DateFormat;
/**
* Parser and formatter for year format.
*
* @author Igor Wiedler <igor@wiedler.ch>
*
* @internal
*/
class YearTransformer extends Transformer
{
/**
* {@inheritdoc}
*/
public function format(\DateTime $dateTime, int $length): string
{
if (2 === $length) {
return $dateTime->format('y');
}
return $this->padLeft($dateTime->format('Y'), $length);
}
/**
* {@inheritdoc}
*/
public function getReverseMatchingRegExp(int $length): string
{
return 2 === $length ? '\d{2}' : '\d{1,4}';
}
/**
* {@inheritdoc}
*/
public function extractDateOptions(string $matched, int $length): array
{
return [
'year' => (int) $matched,
];
}
}

View File

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu\Exception;
/**
* Base ExceptionInterface for the Intl component.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface ExceptionInterface extends \Throwable
{
}

View File

@ -0,0 +1,28 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu\Exception;
/**
* @author Eriksen Costa <eriksen.costa@infranology.com.br>
*/
class MethodArgumentNotImplementedException extends NotImplementedException
{
/**
* @param string $methodName The method name that raised the exception
* @param string $argName The argument name that is not implemented
*/
public function __construct(string $methodName, string $argName)
{
$message = sprintf('The %s() method\'s argument $%s behavior is not implemented.', $methodName, $argName);
parent::__construct($message);
}
}

View File

@ -0,0 +1,37 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu\Exception;
/**
* @author Eriksen Costa <eriksen.costa@infranology.com.br>
*/
class MethodArgumentValueNotImplementedException extends NotImplementedException
{
/**
* @param string $methodName The method name that raised the exception
* @param string $argName The argument name
* @param mixed $argValue The argument value that is not implemented
* @param string $additionalMessage An optional additional message to append to the exception message
*/
public function __construct(string $methodName, string $argName, $argValue, string $additionalMessage = '')
{
$message = sprintf(
'The %s() method\'s argument $%s value %s behavior is not implemented.%s',
$methodName,
$argName,
var_export($argValue, true),
'' !== $additionalMessage ? ' '.$additionalMessage.'. ' : ''
);
parent::__construct($message);
}
}

View File

@ -0,0 +1,26 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu\Exception;
/**
* @author Eriksen Costa <eriksen.costa@infranology.com.br>
*/
class MethodNotImplementedException extends NotImplementedException
{
/**
* @param string $methodName The name of the method
*/
public function __construct(string $methodName)
{
parent::__construct(sprintf('The %s() is not implemented.', $methodName));
}
}

View File

@ -0,0 +1,30 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu\Exception;
/**
* Base exception class for not implemented behaviors of the intl extension in the Locale component.
*
* @author Eriksen Costa <eriksen.costa@infranology.com.br>
*/
class NotImplementedException extends RuntimeException
{
public const INTL_INSTALL_MESSAGE = 'Please install the "intl" extension for full localization capabilities.';
/**
* @param string $message The exception message. A note to install the intl extension is appended to this string
*/
public function __construct(string $message)
{
parent::__construct($message.' '.self::INTL_INSTALL_MESSAGE);
}
}

View File

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu\Exception;
/**
* RuntimeException for the Intl component.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}

117
vendor/symfony/polyfill-intl-icu/Icu.php vendored Normal file
View File

@ -0,0 +1,117 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu;
/**
* Provides fake static versions of the global functions in the intl extension.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
abstract class Icu
{
/**
* Indicates that no error occurred.
*/
public const U_ZERO_ERROR = 0;
/**
* Indicates that an invalid argument was passed.
*/
public const U_ILLEGAL_ARGUMENT_ERROR = 1;
/**
* Indicates that the parse() operation failed.
*/
public const U_PARSE_ERROR = 9;
/**
* All known error codes.
*/
private static $errorCodes = [
self::U_ZERO_ERROR => 'U_ZERO_ERROR',
self::U_ILLEGAL_ARGUMENT_ERROR => 'U_ILLEGAL_ARGUMENT_ERROR',
self::U_PARSE_ERROR => 'U_PARSE_ERROR',
];
/**
* The error code of the last operation.
*/
private static $errorCode = self::U_ZERO_ERROR;
/**
* The error code of the last operation.
*/
private static $errorMessage = 'U_ZERO_ERROR';
/**
* Returns whether the error code indicates a failure.
*
* @param int $errorCode The error code returned by Icu::getErrorCode()
*/
public static function isFailure(int $errorCode): bool
{
return isset(self::$errorCodes[$errorCode])
&& $errorCode > self::U_ZERO_ERROR;
}
/**
* Returns the error code of the last operation.
*
* Returns Icu::U_ZERO_ERROR if no error occurred.
*
* @return int
*/
public static function getErrorCode()
{
return self::$errorCode;
}
/**
* Returns the error message of the last operation.
*
* Returns "U_ZERO_ERROR" if no error occurred.
*/
public static function getErrorMessage(): string
{
return self::$errorMessage;
}
/**
* Returns the symbolic name for a given error code.
*
* @param int $code The error code returned by Icu::getErrorCode()
*/
public static function getErrorName(int $code): string
{
return self::$errorCodes[$code] ?? '[BOGUS UErrorCode]';
}
/**
* Sets the current error.
*
* @param int $code One of the error constants in this class
* @param string $message The ICU class error message
*
* @throws \InvalidArgumentException If the code is not one of the error constants in this class
*/
public static function setError(int $code, string $message = '')
{
if (!isset(self::$errorCodes[$code])) {
throw new \InvalidArgumentException(sprintf('No such error code: "%s".', $code));
}
self::$errorMessage = $message ? sprintf('%s: %s', $message, self::$errorCodes[$code]) : self::$errorCodes[$code];
self::$errorCode = $code;
}
}

View File

@ -0,0 +1,645 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu;
use Symfony\Polyfill\Intl\Icu\DateFormat\FullTransformer;
use Symfony\Polyfill\Intl\Icu\Exception\MethodArgumentNotImplementedException;
use Symfony\Polyfill\Intl\Icu\Exception\MethodArgumentValueNotImplementedException;
use Symfony\Polyfill\Intl\Icu\Exception\MethodNotImplementedException;
/**
* Replacement for PHP's native {@link \IntlDateFormatter} class.
*
* The only methods currently supported in this class are:
*
* - {@link __construct}
* - {@link create}
* - {@link format}
* - {@link getCalendar}
* - {@link getDateType}
* - {@link getErrorCode}
* - {@link getErrorMessage}
* - {@link getLocale}
* - {@link getPattern}
* - {@link getTimeType}
* - {@link getTimeZoneId}
* - {@link isLenient}
* - {@link parse}
* - {@link setLenient}
* - {@link setPattern}
* - {@link setTimeZoneId}
* - {@link setTimeZone}
*
* @author Igor Wiedler <igor@wiedler.ch>
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
abstract class IntlDateFormatter
{
/**
* The error code from the last operation.
*
* @var int
*/
protected $errorCode = Icu::U_ZERO_ERROR;
/**
* The error message from the last operation.
*
* @var string
*/
protected $errorMessage = 'U_ZERO_ERROR';
/* date/time format types */
public const NONE = -1;
public const FULL = 0;
public const LONG = 1;
public const MEDIUM = 2;
public const SHORT = 3;
/* date format types */
public const RELATIVE_FULL = 128;
public const RELATIVE_LONG = 129;
public const RELATIVE_MEDIUM = 130;
public const RELATIVE_SHORT = 131;
/* calendar formats */
public const TRADITIONAL = 0;
public const GREGORIAN = 1;
/**
* Patterns used to format the date when no pattern is provided.
*/
private $defaultDateFormats = [
self::NONE => '',
self::FULL => 'EEEE, MMMM d, y',
self::LONG => 'MMMM d, y',
self::MEDIUM => 'MMM d, y',
self::SHORT => 'M/d/yy',
self::RELATIVE_FULL => 'EEEE, MMMM d, y',
self::RELATIVE_LONG => 'MMMM d, y',
self::RELATIVE_MEDIUM => 'MMM d, y',
self::RELATIVE_SHORT => 'M/d/yy',
];
/**
* Patterns used to format the time when no pattern is provided.
*/
private $defaultTimeFormats = [
self::FULL => 'h:mm:ss a zzzz',
self::LONG => 'h:mm:ss a z',
self::MEDIUM => 'h:mm:ss a',
self::SHORT => 'h:mm a',
];
private $dateType;
private $timeType;
/**
* @var string
*/
private $pattern;
/**
* @var \DateTimeZone
*/
private $dateTimeZone;
/**
* @var bool
*/
private $uninitializedTimeZoneId = false;
/**
* @var string
*/
private $timezoneId;
/**
* @var bool
*/
private $isRelativeDateType = false;
/**
* @param string|null $locale The locale code. The only currently supported locale is "en" (or null using the default locale, i.e. "en")
* @param \IntlTimeZone|\DateTimeZone|string|null $timezone Timezone identifier
* @param \IntlCalendar|int|null $calendar Calendar to use for formatting or parsing. The only currently
* supported value is IntlDateFormatter::GREGORIAN (or null using the default calendar, i.e. "GREGORIAN")
*
* @see https://php.net/intldateformatter.create
* @see http://userguide.icu-project.org/formatparse/datetime
*
* @throws MethodArgumentValueNotImplementedException When $locale different than "en" or null is passed
* @throws MethodArgumentValueNotImplementedException When $calendar different than GREGORIAN is passed
*/
public function __construct(?string $locale, ?int $dateType, ?int $timeType, $timezone = null, $calendar = null, ?string $pattern = '')
{
if ('en' !== $locale && null !== $locale) {
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'locale', $locale, 'Only the locale "en" is supported');
}
if (self::GREGORIAN !== $calendar && null !== $calendar) {
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'calendar', $calendar, 'Only the GREGORIAN calendar is supported');
}
if (\PHP_VERSION_ID >= 80100) {
if (null === $dateType) {
@trigger_error('Passing null to parameter #2 ($dateType) of type int is deprecated', \E_USER_DEPRECATED);
}
if (null === $timeType) {
@trigger_error('Passing null to parameter #3 ($timeType) of type int is deprecated', \E_USER_DEPRECATED);
}
}
$this->dateType = $dateType ?? self::FULL;
$this->timeType = $timeType ?? self::FULL;
if ('' === ($pattern ?? '')) {
$pattern = $this->getDefaultPattern();
}
$this->setPattern($pattern);
$this->setTimeZone($timezone);
if (\in_array($this->dateType, [self::RELATIVE_FULL, self::RELATIVE_LONG, self::RELATIVE_MEDIUM, self::RELATIVE_SHORT], true)) {
$this->isRelativeDateType = true;
}
}
/**
* Static constructor.
*
* @param string|null $locale The locale code. The only currently supported locale is "en" (or null using the default locale, i.e. "en")
* @param \IntlTimeZone|\DateTimeZone|string|null $timezone Timezone identifier
* @param \IntlCalendar|int|null $calendar Calendar to use for formatting or parsing; default is Gregorian
* One of the calendar constants
*
* @return static
*
* @see https://php.net/intldateformatter.create
* @see http://userguide.icu-project.org/formatparse/datetime
*
* @throws MethodArgumentValueNotImplementedException When $locale different than "en" or null is passed
* @throws MethodArgumentValueNotImplementedException When $calendar different than GREGORIAN is passed
*/
public static function create(?string $locale, ?int $dateType, ?int $timeType, $timezone = null, int $calendar = null, ?string $pattern = '')
{
return new static($locale, $dateType, $timeType, $timezone, $calendar, $pattern);
}
/**
* Format the date/time value (timestamp) as a string.
*
* @param int|string|\DateTimeInterface $datetime The timestamp to format
*
* @return string|bool The formatted value or false if formatting failed
*
* @see https://php.net/intldateformatter.format
*
* @throws MethodArgumentValueNotImplementedException If one of the formatting characters is not implemented
*/
public function format($datetime)
{
// intl allows timestamps to be passed as arrays - we don't
if (\is_array($datetime)) {
$message = 'Only Unix timestamps and DateTime objects are supported';
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'datetime', $datetime, $message);
}
if (\is_string($datetime) && $dt = \DateTime::createFromFormat('U', $datetime)) {
$datetime = $dt;
}
// behave like the intl extension
$argumentError = null;
if (!\is_int($datetime) && !$datetime instanceof \DateTimeInterface) {
$argumentError = sprintf('datefmt_format: string \'%s\' is not numeric, which would be required for it to be a valid date', $datetime);
}
if (null !== $argumentError) {
Icu::setError(Icu::U_ILLEGAL_ARGUMENT_ERROR, $argumentError);
$this->errorCode = Icu::getErrorCode();
$this->errorMessage = Icu::getErrorMessage();
return false;
}
if ($datetime instanceof \DateTimeInterface) {
$datetime = $datetime->format('U');
}
$pattern = $this->getPattern();
$formatted = '';
if ($this->isRelativeDateType && $formatted = $this->getRelativeDateFormat($datetime)) {
if (self::NONE === $this->timeType) {
$pattern = '';
} else {
$pattern = $this->defaultTimeFormats[$this->timeType];
if (\in_array($this->dateType, [self::RELATIVE_MEDIUM, self::RELATIVE_SHORT], true)) {
$formatted .= ', ';
} else {
$formatted .= ' at ';
}
}
}
$transformer = new FullTransformer($pattern, $this->getTimeZoneId());
$formatted .= $transformer->format($this->createDateTime($datetime));
// behave like the intl extension
Icu::setError(Icu::U_ZERO_ERROR);
$this->errorCode = Icu::getErrorCode();
$this->errorMessage = Icu::getErrorMessage();
return $formatted;
}
/**
* Not supported. Formats an object.
*
* @return string The formatted value
*
* @see https://php.net/intldateformatter.formatobject
*
* @throws MethodNotImplementedException
*/
public function formatObject($datetime, $format = null, string $locale = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns the formatter's calendar.
*
* @return int The calendar being used by the formatter. Currently always returns
* IntlDateFormatter::GREGORIAN.
*
* @see https://php.net/intldateformatter.getcalendar
*/
public function getCalendar()
{
return self::GREGORIAN;
}
/**
* Not supported. Returns the formatter's calendar object.
*
* @return object The calendar's object being used by the formatter
*
* @see https://php.net/intldateformatter.getcalendarobject
*
* @throws MethodNotImplementedException
*/
public function getCalendarObject()
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns the formatter's datetype.
*
* @return int The current value of the formatter
*
* @see https://php.net/intldateformatter.getdatetype
*/
public function getDateType()
{
return $this->dateType;
}
/**
* Returns formatter's last error code. Always returns the U_ZERO_ERROR class constant value.
*
* @return int The error code from last formatter call
*
* @see https://php.net/intldateformatter.geterrorcode
*/
public function getErrorCode()
{
return $this->errorCode;
}
/**
* Returns formatter's last error message. Always returns the U_ZERO_ERROR_MESSAGE class constant value.
*
* @return string The error message from last formatter call
*
* @see https://php.net/intldateformatter.geterrormessage
*/
public function getErrorMessage()
{
return $this->errorMessage;
}
/**
* Returns the formatter's locale.
*
* @param int $type Not supported. The locale name type to return (Locale::VALID_LOCALE or Locale::ACTUAL_LOCALE)
*
* @return string The locale used to create the formatter. Currently always
* returns "en".
*
* @see https://php.net/intldateformatter.getlocale
*/
public function getLocale(int $type = Locale::ACTUAL_LOCALE)
{
return 'en';
}
/**
* Returns the formatter's pattern.
*
* @return string The pattern string used by the formatter
*
* @see https://php.net/intldateformatter.getpattern
*/
public function getPattern()
{
return $this->pattern;
}
/**
* Returns the formatter's time type.
*
* @return int The time type used by the formatter
*
* @see https://php.net/intldateformatter.gettimetype
*/
public function getTimeType()
{
return $this->timeType;
}
/**
* Returns the formatter's timezone identifier.
*
* @return string The timezone identifier used by the formatter
*
* @see https://php.net/intldateformatter.gettimezoneid
*/
public function getTimeZoneId()
{
if (!$this->uninitializedTimeZoneId) {
return $this->timezoneId;
}
return date_default_timezone_get();
}
/**
* Not supported. Returns the formatter's timezone.
*
* @return mixed The timezone used by the formatter
*
* @see https://php.net/intldateformatter.gettimezone
*
* @throws MethodNotImplementedException
*/
public function getTimeZone()
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns whether the formatter is lenient.
*
* @return bool Currently always returns false
*
* @see https://php.net/intldateformatter.islenient
*
* @throws MethodNotImplementedException
*/
public function isLenient()
{
return false;
}
/**
* Not supported. Parse string to a field-based time value.
*
* @return string Localtime compatible array of integers: contains 24 hour clock value in tm_hour field
*
* @see https://php.net/intldateformatter.localtime
*
* @throws MethodNotImplementedException
*/
public function localtime(string $string, &$offset = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Parse string to a timestamp value.
*
* @return int|false Parsed value as a timestamp
*
* @see https://php.net/intldateformatter.parse
*
* @throws MethodArgumentNotImplementedException When $offset different than null, behavior not implemented
*/
public function parse(string $string, &$offset = null)
{
// We don't calculate the position when parsing the value
if (null !== $offset) {
throw new MethodArgumentNotImplementedException(__METHOD__, 'offset');
}
$dateTime = $this->createDateTime(0);
$transformer = new FullTransformer($this->getPattern(), $this->getTimeZoneId());
$timestamp = $transformer->parse($dateTime, $string);
// behave like the intl extension. FullTransformer::parse() set the proper error
$this->errorCode = Icu::getErrorCode();
$this->errorMessage = Icu::getErrorMessage();
return $timestamp;
}
/**
* Not supported. Set the formatter's calendar.
*
* @param \IntlCalendar|int|null $calendar
*
* @return bool true on success or false on failure
*
* @see https://php.net/intldateformatter.setcalendar
*
* @throws MethodNotImplementedException
*/
public function setCalendar($calendar)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Set the leniency of the parser.
*
* Define if the parser is strict or lenient in interpreting inputs that do not match the pattern
* exactly. Enabling lenient parsing allows the parser to accept otherwise flawed date or time
* patterns, parsing as much as possible to obtain a value. Extra space, unrecognized tokens, or
* invalid values ("February 30th") are not accepted.
*
* @param bool $lenient Sets whether the parser is lenient or not. Currently
* only false (strict) is supported.
*
* @return bool true on success or false on failure
*
* @see https://php.net/intldateformatter.setlenient
*
* @throws MethodArgumentValueNotImplementedException When $lenient is true
*/
public function setLenient(bool $lenient)
{
if ($lenient) {
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'lenient', $lenient, 'Only the strict parser is supported');
}
return true;
}
/**
* Set the formatter's pattern.
*
* @return bool true on success or false on failure
*
* @see https://php.net/intldateformatter.setpattern
* @see http://userguide.icu-project.org/formatparse/datetime
*/
public function setPattern(string $pattern)
{
$this->pattern = $pattern;
return true;
}
/**
* Sets formatterʼs timezone.
*
* @param \IntlTimeZone|\DateTimeZone|string|null $timezone
*
* @return bool true on success or false on failure
*
* @see https://php.net/intldateformatter.settimezone
*/
public function setTimeZone($timezone)
{
if ($timezone instanceof \IntlTimeZone) {
$timezone = $timezone->getID();
}
if ($timezone instanceof \DateTimeZone) {
$timezone = $timezone->getName();
// DateTimeZone returns the GMT offset timezones without the leading GMT, while our parsing requires it.
if (!empty($timezone) && ('+' === $timezone[0] || '-' === $timezone[0])) {
$timezone = 'GMT'.$timezone;
}
}
if (null === $timezone) {
$timezone = date_default_timezone_get();
$this->uninitializedTimeZoneId = true;
}
// Backup original passed time zone
$timezoneId = $timezone;
// Get an Etc/GMT time zone that is accepted for \DateTimeZone
if ('GMT' !== $timezone && 0 === strpos($timezone, 'GMT')) {
try {
$timezone = DateFormat\TimezoneTransformer::getEtcTimeZoneId($timezone);
} catch (\InvalidArgumentException $e) {
// Does nothing, will fallback to UTC
}
}
try {
$this->dateTimeZone = new \DateTimeZone($timezone);
if ('GMT' !== $timezone && $this->dateTimeZone->getName() !== $timezone) {
$timezoneId = $this->getTimeZoneId();
}
} catch (\Exception $e) {
$timezoneId = $timezone = $this->getTimeZoneId();
$this->dateTimeZone = new \DateTimeZone($timezone);
}
$this->timezoneId = $timezoneId;
return true;
}
/**
* Create and returns a DateTime object with the specified timestamp and with the
* current time zone.
*
* @return \DateTime
*/
protected function createDateTime($timestamp)
{
$dateTime = \DateTime::createFromFormat('U', $timestamp);
$dateTime->setTimezone($this->dateTimeZone);
return $dateTime;
}
/**
* Returns a pattern string based in the datetype and timetype values.
*
* @return string
*/
protected function getDefaultPattern()
{
$pattern = '';
if (self::NONE !== $this->dateType) {
$pattern = $this->defaultDateFormats[$this->dateType];
}
if (self::NONE !== $this->timeType) {
if (\in_array($this->dateType, [self::FULL, self::LONG, self::RELATIVE_FULL, self::RELATIVE_LONG], true)) {
$pattern .= ' \'at\' ';
} elseif (self::NONE !== $this->dateType) {
$pattern .= ', ';
}
$pattern .= $this->defaultTimeFormats[$this->timeType];
}
return $pattern;
}
private function getRelativeDateFormat(int $timestamp): string
{
$today = $this->createDateTime(time());
$today->setTime(0, 0, 0);
$datetime = $this->createDateTime($timestamp);
$datetime->setTime(0, 0, 0);
$interval = $today->diff($datetime);
if (false !== $interval) {
if (0 === $interval->days) {
return 'today';
}
if (1 === $interval->days) {
return 1 === $interval->invert ? 'yesterday' : 'tomorrow';
}
}
return '';
}
}

View File

@ -0,0 +1,19 @@
Copyright (c) 2004-2020 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,310 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu;
use Symfony\Polyfill\Intl\Icu\Exception\MethodNotImplementedException;
/**
* Replacement for PHP's native {@link \Locale} class.
*
* The only methods supported in this class are `getDefault` and `canonicalize`.
* All other methods will throw an exception when used.
*
* @author Eriksen Costa <eriksen.costa@infranology.com.br>
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
abstract class Locale
{
public const DEFAULT_LOCALE = null;
/* Locale method constants */
public const ACTUAL_LOCALE = 0;
public const VALID_LOCALE = 1;
/* Language tags constants */
public const LANG_TAG = 'language';
public const EXTLANG_TAG = 'extlang';
public const SCRIPT_TAG = 'script';
public const REGION_TAG = 'region';
public const VARIANT_TAG = 'variant';
public const GRANDFATHERED_LANG_TAG = 'grandfathered';
public const PRIVATE_TAG = 'private';
/**
* Not supported. Returns the best available locale based on HTTP "Accept-Language" header according to RFC 2616.
*
* @return string The corresponding locale code
*
* @see https://php.net/locale.acceptfromhttp
*
* @throws MethodNotImplementedException
*/
public static function acceptFromHttp(string $header)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns a canonicalized locale string.
*
* This polyfill doesn't implement the full-spec algorithm. It only
* canonicalizes locale strings handled by the `LocaleBundle` class.
*
* @return string
*/
public static function canonicalize(string $locale)
{
if ('' === $locale || '.' === $locale[0]) {
return self::getDefault();
}
if (!preg_match('/^([a-z]{2})[-_]([a-z]{2})(?:([a-z]{2})(?:[-_]([a-z]{2}))?)?(?:\..*)?$/i', $locale, $m)) {
return $locale;
}
if (!empty($m[4])) {
return strtolower($m[1]).'_'.ucfirst(strtolower($m[2].$m[3])).'_'.strtoupper($m[4]);
}
if (!empty($m[3])) {
return strtolower($m[1]).'_'.ucfirst(strtolower($m[2].$m[3]));
}
return strtolower($m[1]).'_'.strtoupper($m[2]);
}
/**
* Not supported. Returns a correctly ordered and delimited locale code.
*
* @return string The corresponding locale code
*
* @see https://php.net/locale.composelocale
*
* @throws MethodNotImplementedException
*/
public static function composeLocale(array $subtags)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Checks if a language tag filter matches with locale.
*
* @return string The corresponding locale code
*
* @see https://php.net/locale.filtermatches
*
* @throws MethodNotImplementedException
*/
public static function filterMatches(string $languageTag, string $locale, bool $canonicalize = false)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Returns the variants for the input locale.
*
* @return array The locale variants
*
* @see https://php.net/locale.getallvariants
*
* @throws MethodNotImplementedException
*/
public static function getAllVariants(string $locale)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Returns the default locale.
*
* @return string The default locale code. Always returns 'en'
*
* @see https://php.net/locale.getdefault
*/
public static function getDefault()
{
return 'en';
}
/**
* Not supported. Returns the localized display name for the locale language.
*
* @return string The localized language display name
*
* @see https://php.net/locale.getdisplaylanguage
*
* @throws MethodNotImplementedException
*/
public static function getDisplayLanguage(string $locale, string $displayLocale = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Returns the localized display name for the locale.
*
* @return string The localized locale display name
*
* @see https://php.net/locale.getdisplayname
*
* @throws MethodNotImplementedException
*/
public static function getDisplayName(string $locale, string $displayLocale = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Returns the localized display name for the locale region.
*
* @return string The localized region display name
*
* @see https://php.net/locale.getdisplayregion
*
* @throws MethodNotImplementedException
*/
public static function getDisplayRegion(string $locale, string $displayLocale = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Returns the localized display name for the locale script.
*
* @return string The localized script display name
*
* @see https://php.net/locale.getdisplayscript
*
* @throws MethodNotImplementedException
*/
public static function getDisplayScript(string $locale, string $displayLocale = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Returns the localized display name for the locale variant.
*
* @return string The localized variant display name
*
* @see https://php.net/locale.getdisplayvariant
*
* @throws MethodNotImplementedException
*/
public static function getDisplayVariant(string $locale, string $displayLocale = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Returns the keywords for the locale.
*
* @return array Associative array with the extracted variants
*
* @see https://php.net/locale.getkeywords
*
* @throws MethodNotImplementedException
*/
public static function getKeywords(string $locale)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Returns the primary language for the locale.
*
* @return string|null The extracted language code or null in case of error
*
* @see https://php.net/locale.getprimarylanguage
*
* @throws MethodNotImplementedException
*/
public static function getPrimaryLanguage(string $locale)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Returns the region for the locale.
*
* @return string|null The extracted region code or null if not present
*
* @see https://php.net/locale.getregion
*
* @throws MethodNotImplementedException
*/
public static function getRegion(string $locale)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Returns the script for the locale.
*
* @return string|null The extracted script code or null if not present
*
* @see https://php.net/locale.getscript
*
* @throws MethodNotImplementedException
*/
public static function getScript(string $locale)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Returns the closest language tag for the locale.
*
* @see https://php.net/locale.lookup
*
* @throws MethodNotImplementedException
*/
public static function lookup(array $languageTag, string $locale, bool $canonicalize = false, string $defaultLocale = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Returns an associative array of locale identifier subtags.
*
* @return array Associative array with the extracted subtags
*
* @see https://php.net/locale.parselocale
*
* @throws MethodNotImplementedException
*/
public static function parseLocale(string $locale)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Sets the default runtime locale.
*
* @return bool true on success or false on failure
*
* @see https://php.net/locale.setdefault
*
* @throws MethodNotImplementedException
*/
public static function setDefault(string $locale)
{
if ('en' !== $locale) {
throw new MethodNotImplementedException(__METHOD__);
}
return true;
}
}

View File

@ -0,0 +1,835 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Polyfill\Intl\Icu;
use Symfony\Polyfill\Intl\Icu\Exception\MethodArgumentNotImplementedException;
use Symfony\Polyfill\Intl\Icu\Exception\MethodArgumentValueNotImplementedException;
use Symfony\Polyfill\Intl\Icu\Exception\MethodNotImplementedException;
use Symfony\Polyfill\Intl\Icu\Exception\NotImplementedException;
/**
* Replacement for PHP's native {@link \NumberFormatter} class.
*
* The only methods currently supported in this class are:
*
* - {@link __construct}
* - {@link create}
* - {@link formatCurrency}
* - {@link format}
* - {@link getAttribute}
* - {@link getErrorCode}
* - {@link getErrorMessage}
* - {@link getLocale}
* - {@link parse}
* - {@link setAttribute}
*
* @author Eriksen Costa <eriksen.costa@infranology.com.br>
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal
*/
abstract class NumberFormatter
{
/* Format style constants */
public const PATTERN_DECIMAL = 0;
public const DECIMAL = 1;
public const CURRENCY = 2;
public const PERCENT = 3;
public const SCIENTIFIC = 4;
public const SPELLOUT = 5;
public const ORDINAL = 6;
public const DURATION = 7;
public const PATTERN_RULEBASED = 9;
public const IGNORE = 0;
public const DEFAULT_STYLE = 1;
/* Format type constants */
public const TYPE_DEFAULT = 0;
public const TYPE_INT32 = 1;
public const TYPE_INT64 = 2;
public const TYPE_DOUBLE = 3;
public const TYPE_CURRENCY = 4;
/* Numeric attribute constants */
public const PARSE_INT_ONLY = 0;
public const GROUPING_USED = 1;
public const DECIMAL_ALWAYS_SHOWN = 2;
public const MAX_INTEGER_DIGITS = 3;
public const MIN_INTEGER_DIGITS = 4;
public const INTEGER_DIGITS = 5;
public const MAX_FRACTION_DIGITS = 6;
public const MIN_FRACTION_DIGITS = 7;
public const FRACTION_DIGITS = 8;
public const MULTIPLIER = 9;
public const GROUPING_SIZE = 10;
public const ROUNDING_MODE = 11;
public const ROUNDING_INCREMENT = 12;
public const FORMAT_WIDTH = 13;
public const PADDING_POSITION = 14;
public const SECONDARY_GROUPING_SIZE = 15;
public const SIGNIFICANT_DIGITS_USED = 16;
public const MIN_SIGNIFICANT_DIGITS = 17;
public const MAX_SIGNIFICANT_DIGITS = 18;
public const LENIENT_PARSE = 19;
/* Text attribute constants */
public const POSITIVE_PREFIX = 0;
public const POSITIVE_SUFFIX = 1;
public const NEGATIVE_PREFIX = 2;
public const NEGATIVE_SUFFIX = 3;
public const PADDING_CHARACTER = 4;
public const CURRENCY_CODE = 5;
public const DEFAULT_RULESET = 6;
public const PUBLIC_RULESETS = 7;
/* Format symbol constants */
public const DECIMAL_SEPARATOR_SYMBOL = 0;
public const GROUPING_SEPARATOR_SYMBOL = 1;
public const PATTERN_SEPARATOR_SYMBOL = 2;
public const PERCENT_SYMBOL = 3;
public const ZERO_DIGIT_SYMBOL = 4;
public const DIGIT_SYMBOL = 5;
public const MINUS_SIGN_SYMBOL = 6;
public const PLUS_SIGN_SYMBOL = 7;
public const CURRENCY_SYMBOL = 8;
public const INTL_CURRENCY_SYMBOL = 9;
public const MONETARY_SEPARATOR_SYMBOL = 10;
public const EXPONENTIAL_SYMBOL = 11;
public const PERMILL_SYMBOL = 12;
public const PAD_ESCAPE_SYMBOL = 13;
public const INFINITY_SYMBOL = 14;
public const NAN_SYMBOL = 15;
public const SIGNIFICANT_DIGIT_SYMBOL = 16;
public const MONETARY_GROUPING_SEPARATOR_SYMBOL = 17;
/* Rounding mode values used by NumberFormatter::setAttribute() with NumberFormatter::ROUNDING_MODE attribute */
public const ROUND_CEILING = 0;
public const ROUND_FLOOR = 1;
public const ROUND_DOWN = 2;
public const ROUND_UP = 3;
public const ROUND_HALFEVEN = 4;
public const ROUND_HALFDOWN = 5;
public const ROUND_HALFUP = 6;
/* Pad position values used by NumberFormatter::setAttribute() with NumberFormatter::PADDING_POSITION attribute */
public const PAD_BEFORE_PREFIX = 0;
public const PAD_AFTER_PREFIX = 1;
public const PAD_BEFORE_SUFFIX = 2;
public const PAD_AFTER_SUFFIX = 3;
/**
* The error code from the last operation.
*
* @var int
*/
protected $errorCode = Icu::U_ZERO_ERROR;
/**
* The error message from the last operation.
*
* @var string
*/
protected $errorMessage = 'U_ZERO_ERROR';
/**
* @var int
*/
private $style;
/**
* Default values for the en locale.
*/
private $attributes = [
self::FRACTION_DIGITS => 0,
self::GROUPING_USED => 1,
self::ROUNDING_MODE => self::ROUND_HALFEVEN,
];
/**
* Holds the initialized attributes code.
*/
private $initializedAttributes = [];
/**
* The supported styles to the constructor $styles argument.
*/
private static $supportedStyles = [
'CURRENCY' => self::CURRENCY,
'DECIMAL' => self::DECIMAL,
];
/**
* Supported attributes to the setAttribute() $attr argument.
*/
private static $supportedAttributes = [
'FRACTION_DIGITS' => self::FRACTION_DIGITS,
'GROUPING_USED' => self::GROUPING_USED,
'ROUNDING_MODE' => self::ROUNDING_MODE,
];
/**
* The available rounding modes for setAttribute() usage with
* NumberFormatter::ROUNDING_MODE. NumberFormatter::ROUND_DOWN
* and NumberFormatter::ROUND_UP does not have a PHP only equivalent.
*/
private static $roundingModes = [
'ROUND_HALFEVEN' => self::ROUND_HALFEVEN,
'ROUND_HALFDOWN' => self::ROUND_HALFDOWN,
'ROUND_HALFUP' => self::ROUND_HALFUP,
'ROUND_CEILING' => self::ROUND_CEILING,
'ROUND_FLOOR' => self::ROUND_FLOOR,
'ROUND_DOWN' => self::ROUND_DOWN,
'ROUND_UP' => self::ROUND_UP,
];
/**
* The mapping between NumberFormatter rounding modes to the available
* modes in PHP's round() function.
*
* @see https://php.net/round
*/
private static $phpRoundingMap = [
self::ROUND_HALFDOWN => \PHP_ROUND_HALF_DOWN,
self::ROUND_HALFEVEN => \PHP_ROUND_HALF_EVEN,
self::ROUND_HALFUP => \PHP_ROUND_HALF_UP,
];
/**
* The list of supported rounding modes which aren't available modes in
* PHP's round() function, but there's an equivalent. Keys are rounding
* modes, values does not matter.
*/
private static $customRoundingList = [
self::ROUND_CEILING => true,
self::ROUND_FLOOR => true,
self::ROUND_DOWN => true,
self::ROUND_UP => true,
];
/**
* The maximum value of the integer type in 32 bit platforms.
*/
private static $int32Max = 2147483647;
/**
* The maximum value of the integer type in 64 bit platforms.
*
* @var int|float
*/
private static $int64Max = 9223372036854775807;
private static $enSymbols = [
self::DECIMAL => ['.', ',', ';', '%', '0', '#', '-', '+', '¤', '¤¤', '.', 'E', '‰', '*', '∞', 'NaN', '@', ','],
self::CURRENCY => ['.', ',', ';', '%', '0', '#', '-', '+', '¤', '¤¤', '.', 'E', '‰', '*', '∞', 'NaN', '@', ','],
];
private static $enTextAttributes = [
self::DECIMAL => ['', '', '-', '', ' ', 'XXX', ''],
self::CURRENCY => ['¤', '', '-¤', '', ' ', 'XXX'],
];
/**
* @param string|null $locale The locale code. The only currently supported locale is "en" (or null using the default locale, i.e. "en")
* @param int $style Style of the formatting, one of the format style constants.
* The only supported styles are NumberFormatter::DECIMAL
* and NumberFormatter::CURRENCY.
* @param string $pattern Not supported. A pattern string in case $style is NumberFormat::PATTERN_DECIMAL or
* NumberFormat::PATTERN_RULEBASED. It must conform to the syntax
* described in the ICU DecimalFormat or ICU RuleBasedNumberFormat documentation
*
* @see https://php.net/numberformatter.create
* @see https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1DecimalFormat.html#details
* @see https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1RuleBasedNumberFormat.html#details
*
* @throws MethodArgumentValueNotImplementedException When $locale different than "en" or null is passed
* @throws MethodArgumentValueNotImplementedException When the $style is not supported
* @throws MethodArgumentNotImplementedException When the pattern value is different than null
*/
public function __construct(?string $locale = 'en', int $style = null, string $pattern = null)
{
if ('en' !== $locale && null !== $locale) {
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'locale', $locale, 'Only the locale "en" is supported');
}
if (!\in_array($style, self::$supportedStyles)) {
$message = sprintf('The available styles are: %s.', implode(', ', array_keys(self::$supportedStyles)));
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'style', $style, $message);
}
if (null !== $pattern) {
throw new MethodArgumentNotImplementedException(__METHOD__, 'pattern');
}
$this->style = $style;
}
/**
* Static constructor.
*
* @param string|null $locale The locale code. The only supported locale is "en" (or null using the default locale, i.e. "en")
* @param int $style Style of the formatting, one of the format style constants.
* The only currently supported styles are NumberFormatter::DECIMAL
* and NumberFormatter::CURRENCY.
* @param string $pattern Not supported. A pattern string in case $style is NumberFormat::PATTERN_DECIMAL or
* NumberFormat::PATTERN_RULEBASED. It must conform to the syntax
* described in the ICU DecimalFormat or ICU RuleBasedNumberFormat documentation
*
* @return static
*
* @see https://php.net/numberformatter.create
* @see http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details
* @see http://www.icu-project.org/apiref/icu4c/classRuleBasedNumberFormat.html#_details
*
* @throws MethodArgumentValueNotImplementedException When $locale different than "en" or null is passed
* @throws MethodArgumentValueNotImplementedException When the $style is not supported
* @throws MethodArgumentNotImplementedException When the pattern value is different than null
*/
public static function create(?string $locale = 'en', int $style = null, string $pattern = null)
{
return new static($locale, $style, $pattern);
}
/**
* Format a currency value.
*
* @return string The formatted currency value
*
* @see https://php.net/numberformatter.formatcurrency
* @see https://en.wikipedia.org/wiki/ISO_4217#Active_codes
*/
public function formatCurrency(float $amount, string $currency)
{
if (self::DECIMAL === $this->style) {
return $this->format($amount);
}
if (null === $symbol = Currencies::getSymbol($currency)) {
return false;
}
$fractionDigits = Currencies::getFractionDigits($currency);
$amount = $this->roundCurrency($amount, $currency);
$negative = false;
if (0 > $amount) {
$negative = true;
$amount *= -1;
}
$amount = $this->formatNumber($amount, $fractionDigits);
// There's a non-breaking space after the currency code (i.e. CRC 100), but not if the currency has a symbol (i.e. £100).
$ret = $symbol.(mb_strlen($symbol, 'UTF-8') > 2 ? "\xc2\xa0" : '').$amount;
return $negative ? '-'.$ret : $ret;
}
/**
* Format a number.
*
* @param int|float $num The value to format
* @param int $type Type of the formatting, one of the format type constants.
* Only type NumberFormatter::TYPE_DEFAULT is currently supported.
*
* @return bool|string The formatted value or false on error
*
* @see https://php.net/numberformatter.format
*
* @throws NotImplementedException If the method is called with the class $style 'CURRENCY'
* @throws MethodArgumentValueNotImplementedException If the $type is different than TYPE_DEFAULT
*/
public function format($num, int $type = self::TYPE_DEFAULT)
{
// The original NumberFormatter does not support this format type
if (self::TYPE_CURRENCY === $type) {
if (\PHP_VERSION_ID >= 80000) {
throw new \ValueError(sprintf('The format type must be a NumberFormatter::TYPE_* constant (%s given).', $type));
}
trigger_error(__METHOD__.'(): Unsupported format type '.$type, \E_USER_WARNING);
return false;
}
if (self::CURRENCY === $this->style) {
throw new NotImplementedException(sprintf('"%s()" method does not support the formatting of currencies (instance with CURRENCY style). "%s".', __METHOD__, NotImplementedException::INTL_INSTALL_MESSAGE));
}
// Only the default type is supported.
if (self::TYPE_DEFAULT !== $type) {
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'type', $type, 'Only TYPE_DEFAULT is supported');
}
$fractionDigits = $this->getAttribute(self::FRACTION_DIGITS);
$num = $this->round($num, $fractionDigits);
$num = $this->formatNumber($num, $fractionDigits);
// behave like the intl extension
$this->resetError();
return $num;
}
/**
* Returns an attribute value.
*
* @return int|false The attribute value on success or false on error
*
* @see https://php.net/numberformatter.getattribute
*/
public function getAttribute(int $attribute)
{
return $this->attributes[$attribute] ?? null;
}
/**
* Returns formatter's last error code. Always returns the U_ZERO_ERROR class constant value.
*
* @return int The error code from last formatter call
*
* @see https://php.net/numberformatter.geterrorcode
*/
public function getErrorCode()
{
return $this->errorCode;
}
/**
* Returns formatter's last error message. Always returns the U_ZERO_ERROR_MESSAGE class constant value.
*
* @return string The error message from last formatter call
*
* @see https://php.net/numberformatter.geterrormessage
*/
public function getErrorMessage()
{
return $this->errorMessage;
}
/**
* Returns the formatter's locale.
*
* The parameter $type is currently ignored.
*
* @param int $type Not supported. The locale name type to return (Locale::VALID_LOCALE or Locale::ACTUAL_LOCALE)
*
* @return string The locale used to create the formatter. Currently always
* returns "en".
*
* @see https://php.net/numberformatter.getlocale
*/
public function getLocale(int $type = Locale::ACTUAL_LOCALE)
{
return 'en';
}
/**
* Not supported. Returns the formatter's pattern.
*
* @return string|false The pattern string used by the formatter or false on error
*
* @see https://php.net/numberformatter.getpattern
*
* @throws MethodNotImplementedException
*/
public function getPattern()
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Returns a formatter symbol value.
*
* @return string|false The symbol value or false on error
*
* @see https://php.net/numberformatter.getsymbol
*/
public function getSymbol(int $symbol)
{
return \array_key_exists($this->style, self::$enSymbols) && \array_key_exists($symbol, self::$enSymbols[$this->style]) ? self::$enSymbols[$this->style][$symbol] : false;
}
/**
* Not supported. Returns a formatter text attribute value.
*
* @return string|false The attribute value or false on error
*
* @see https://php.net/numberformatter.gettextattribute
*/
public function getTextAttribute(int $attribute)
{
return \array_key_exists($this->style, self::$enTextAttributes) && \array_key_exists($attribute, self::$enTextAttributes[$this->style]) ? self::$enTextAttributes[$this->style][$attribute] : false;
}
/**
* Not supported. Parse a currency number.
*
* @return float|false The parsed numeric value or false on error
*
* @see https://php.net/numberformatter.parsecurrency
*
* @throws MethodNotImplementedException
*/
public function parseCurrency(string $string, &$currency, &$offset = null)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Parse a number.
*
* @return int|float|false The parsed value or false on error
*
* @see https://php.net/numberformatter.parse
*/
public function parse(string $string, int $type = self::TYPE_DOUBLE, &$offset = null)
{
if (self::TYPE_DEFAULT === $type || self::TYPE_CURRENCY === $type) {
if (\PHP_VERSION_ID >= 80000) {
throw new \ValueError(sprintf('The format type must be a NumberFormatter::TYPE_* constant (%d given).', $type));
}
trigger_error(__METHOD__.'(): Unsupported format type '.$type, \E_USER_WARNING);
return false;
}
// Any invalid number at the end of the string is removed.
// Only numbers and the fraction separator is expected in the string.
// If grouping is used, grouping separator also becomes a valid character.
$groupingMatch = $this->getAttribute(self::GROUPING_USED) ? '|(?P<grouping>\d++(,{1}\d+)++(\.\d*+)?)' : '';
if (preg_match("/^-?(?:\.\d++{$groupingMatch}|\d++(\.\d*+)?)/", $string, $matches)) {
$string = $matches[0];
$offset = \strlen($string);
// value is not valid if grouping is used, but digits are not grouped in groups of three
if ($error = isset($matches['grouping']) && !preg_match('/^-?(?:\d{1,3}+)?(?:(?:,\d{3})++|\d*+)(?:\.\d*+)?$/', $string)) {
// the position on error is 0 for positive and 1 for negative numbers
$offset = 0 === strpos($string, '-') ? 1 : 0;
}
} else {
$error = true;
$offset = 0;
}
if ($error) {
Icu::setError(Icu::U_PARSE_ERROR, 'Number parsing failed');
$this->errorCode = Icu::getErrorCode();
$this->errorMessage = Icu::getErrorMessage();
return false;
}
$string = str_replace(',', '', $string);
$string = $this->convertValueDataType($string, $type);
// behave like the intl extension
$this->resetError();
return $string;
}
/**
* Set an attribute.
*
* @param int|float $value
*
* @return bool true on success or false on failure
*
* @see https://php.net/numberformatter.setattribute
*
* @throws MethodArgumentValueNotImplementedException When the $attribute is not supported
* @throws MethodArgumentValueNotImplementedException When the $value is not supported
*/
public function setAttribute(int $attribute, $value)
{
if (!\in_array($attribute, self::$supportedAttributes)) {
$message = sprintf(
'The available attributes are: %s',
implode(', ', array_keys(self::$supportedAttributes))
);
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'attribute', $value, $message);
}
if (self::$supportedAttributes['ROUNDING_MODE'] === $attribute && $this->isInvalidRoundingMode($value)) {
$message = sprintf(
'The supported values for ROUNDING_MODE are: %s',
implode(', ', array_keys(self::$roundingModes))
);
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'attribute', $value, $message);
}
if (self::$supportedAttributes['GROUPING_USED'] === $attribute) {
$value = $this->normalizeGroupingUsedValue($value);
}
if (self::$supportedAttributes['FRACTION_DIGITS'] === $attribute) {
$value = $this->normalizeFractionDigitsValue($value);
if ($value < 0) {
// ignore negative values but do not raise an error
return true;
}
}
$this->attributes[$attribute] = $value;
$this->initializedAttributes[$attribute] = true;
return true;
}
/**
* Not supported. Set the formatter's pattern.
*
* @return bool true on success or false on failure
*
* @see https://php.net/numberformatter.setpattern
* @see http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details
*
* @throws MethodNotImplementedException
*/
public function setPattern(string $pattern)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Set the formatter's symbol.
*
* @return bool true on success or false on failure
*
* @see https://php.net/numberformatter.setsymbol
*
* @throws MethodNotImplementedException
*/
public function setSymbol(int $symbol, string $value)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Not supported. Set a text attribute.
*
* @return bool true on success or false on failure
*
* @see https://php.net/numberformatter.settextattribute
*
* @throws MethodNotImplementedException
*/
public function setTextAttribute(int $attribute, string $value)
{
throw new MethodNotImplementedException(__METHOD__);
}
/**
* Set the error to the default U_ZERO_ERROR.
*/
protected function resetError()
{
Icu::setError(Icu::U_ZERO_ERROR);
$this->errorCode = Icu::getErrorCode();
$this->errorMessage = Icu::getErrorMessage();
}
/**
* Rounds a currency value, applying increment rounding if applicable.
*
* When a currency have a rounding increment, an extra round is made after the first one. The rounding factor is
* determined in the ICU data and is explained as of:
*
* "the rounding increment is given in units of 10^(-fraction_digits)"
*
* The only actual rounding data as of this writing, is CHF.
*
* @see http://en.wikipedia.org/wiki/Swedish_rounding
* @see http://www.docjar.com/html/api/com/ibm/icu/util/Currency.java.html#1007
*/
private function roundCurrency(float $value, string $currency): float
{
$fractionDigits = Currencies::getFractionDigits($currency);
$roundingIncrement = Currencies::getRoundingIncrement($currency);
// Round with the formatter rounding mode
$value = $this->round($value, $fractionDigits);
// Swiss rounding
if (0 < $roundingIncrement && 0 < $fractionDigits) {
$roundingFactor = $roundingIncrement / 10 ** $fractionDigits;
$value = round($value / $roundingFactor) * $roundingFactor;
}
return $value;
}
/**
* Rounds a value.
*
* @param int|float $value The value to round
*
* @return int|float The rounded value
*/
private function round($value, int $precision)
{
$precision = $this->getUninitializedPrecision($value, $precision);
$roundingModeAttribute = $this->getAttribute(self::ROUNDING_MODE);
if (isset(self::$phpRoundingMap[$roundingModeAttribute])) {
$value = round($value, $precision, self::$phpRoundingMap[$roundingModeAttribute]);
} elseif (isset(self::$customRoundingList[$roundingModeAttribute])) {
$roundingCoef = 10 ** $precision;
$value *= $roundingCoef;
$value = (float) (string) $value;
switch ($roundingModeAttribute) {
case self::ROUND_CEILING:
$value = ceil($value);
break;
case self::ROUND_FLOOR:
$value = floor($value);
break;
case self::ROUND_UP:
$value = $value > 0 ? ceil($value) : floor($value);
break;
case self::ROUND_DOWN:
$value = $value > 0 ? floor($value) : ceil($value);
break;
}
$value /= $roundingCoef;
}
return $value;
}
/**
* Formats a number.
*
* @param int|float $value The numeric value to format
*/
private function formatNumber($value, int $precision): string
{
$precision = $this->getUninitializedPrecision($value, $precision);
return number_format($value, $precision, '.', $this->getAttribute(self::GROUPING_USED) ? ',' : '');
}
/**
* Returns the precision value if the DECIMAL style is being used and the FRACTION_DIGITS attribute is uninitialized.
*
* @param int|float $value The value to get the precision from if the FRACTION_DIGITS attribute is uninitialized
*/
private function getUninitializedPrecision($value, int $precision): int
{
if (self::CURRENCY === $this->style) {
return $precision;
}
if (!$this->isInitializedAttribute(self::FRACTION_DIGITS)) {
preg_match('/.*\.(.*)/', (string) $value, $digits);
if (isset($digits[1])) {
$precision = \strlen($digits[1]);
}
}
return $precision;
}
/**
* Check if the attribute is initialized (value set by client code).
*/
private function isInitializedAttribute(string $attr): bool
{
return isset($this->initializedAttributes[$attr]);
}
/**
* Returns the numeric value using the $type to convert to the right data type.
*
* @param mixed $value The value to be converted
*
* @return int|float|false The converted value
*/
private function convertValueDataType($value, int $type)
{
if (self::TYPE_DOUBLE === $type) {
$value = (float) $value;
} elseif (self::TYPE_INT32 === $type) {
$value = $this->getInt32Value($value);
} elseif (self::TYPE_INT64 === $type) {
$value = $this->getInt64Value($value);
}
return $value;
}
/**
* Convert the value data type to int or returns false if the value is out of the integer value range.
*
* @return int|false The converted value
*/
private function getInt32Value($value)
{
if ($value > self::$int32Max || $value < -self::$int32Max - 1) {
return false;
}
return (int) $value;
}
/**
* Convert the value data type to int or returns false if the value is out of the integer value range.
*
* @return int|float|false The converted value
*/
private function getInt64Value($value)
{
if ($value > self::$int64Max || $value < -self::$int64Max - 1) {
return false;
}
if (\PHP_INT_SIZE !== 8 && ($value > self::$int32Max || $value < -self::$int32Max - 1)) {
return (float) $value;
}
return (int) $value;
}
/**
* Check if the rounding mode is invalid.
*/
private function isInvalidRoundingMode(int $value): bool
{
if (\in_array($value, self::$roundingModes, true)) {
return false;
}
return true;
}
/**
* Returns the normalized value for the GROUPING_USED attribute. Any value that can be converted to int will be
* cast to Boolean and then to int again. This way, negative values are converted to 1 and string values to 0.
*/
private function normalizeGroupingUsedValue($value): int
{
return (int) (bool) (int) $value;
}
/**
* Returns the normalized value for the FRACTION_DIGITS attribute.
*/
private function normalizeFractionDigitsValue($value): int
{
return (int) $value;
}
}

View File

@ -0,0 +1,23 @@
Symfony Polyfill / Intl: ICU
============================
This package provides fallback implementations when the
[Intl](https://php.net/intl) extension is not installed.
It is limited to the "en" locale and to:
- [`intl_is_failure()`](https://php.net/intl-is-failure)
- [`intl_get_error_code()`](https://php.net/intl-get-error-code)
- [`intl_get_error_message()`](https://php.net/intl-get-error-message)
- [`intl_error_name()`](https://php.net/intl-error-name)
- [`Collator`](https://php.net/Collator)
- [`NumberFormatter`](https://php.net/NumberFormatter)
- [`Locale`](https://php.net/Locale)
- [`IntlDateFormatter`](https://php.net/IntlDateFormatter)
More information can be found in the
[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md).
License
=======
This library is released under the [MIT license](LICENSE).

View File

@ -0,0 +1,1311 @@
<?php
return array (
'ADP' =>
array (
0 => 'ADP',
1 => 0,
2 => 0,
),
'AED' =>
array (
0 => 'AED',
),
'AFA' =>
array (
0 => 'AFA',
),
'AFN' =>
array (
0 => 'AFN',
1 => 0,
2 => 0,
),
'ALK' =>
array (
0 => 'ALK',
),
'ALL' =>
array (
0 => 'ALL',
1 => 0,
2 => 0,
),
'AMD' =>
array (
0 => 'AMD',
1 => 2,
2 => 0,
),
'ANG' =>
array (
0 => 'ANG',
),
'AOA' =>
array (
0 => 'AOA',
),
'AOK' =>
array (
0 => 'AOK',
),
'AON' =>
array (
0 => 'AON',
),
'AOR' =>
array (
0 => 'AOR',
),
'ARA' =>
array (
0 => 'ARA',
),
'ARL' =>
array (
0 => 'ARL',
),
'ARM' =>
array (
0 => 'ARM',
),
'ARP' =>
array (
0 => 'ARP',
),
'ARS' =>
array (
0 => 'ARS',
),
'ATS' =>
array (
0 => 'ATS',
),
'AUD' =>
array (
0 => 'A$',
),
'AWG' =>
array (
0 => 'AWG',
),
'AZM' =>
array (
0 => 'AZM',
),
'AZN' =>
array (
0 => 'AZN',
),
'BAD' =>
array (
0 => 'BAD',
),
'BAM' =>
array (
0 => 'BAM',
),
'BAN' =>
array (
0 => 'BAN',
),
'BBD' =>
array (
0 => 'BBD',
),
'BDT' =>
array (
0 => 'BDT',
),
'BEC' =>
array (
0 => 'BEC',
),
'BEF' =>
array (
0 => 'BEF',
),
'BEL' =>
array (
0 => 'BEL',
),
'BGL' =>
array (
0 => 'BGL',
),
'BGM' =>
array (
0 => 'BGM',
),
'BGN' =>
array (
0 => 'BGN',
),
'BGO' =>
array (
0 => 'BGO',
),
'BHD' =>
array (
0 => 'BHD',
1 => 3,
2 => 0,
),
'BIF' =>
array (
0 => 'BIF',
1 => 0,
2 => 0,
),
'BMD' =>
array (
0 => 'BMD',
),
'BND' =>
array (
0 => 'BND',
),
'BOB' =>
array (
0 => 'BOB',
),
'BOL' =>
array (
0 => 'BOL',
),
'BOP' =>
array (
0 => 'BOP',
),
'BOV' =>
array (
0 => 'BOV',
),
'BRB' =>
array (
0 => 'BRB',
),
'BRC' =>
array (
0 => 'BRC',
),
'BRE' =>
array (
0 => 'BRE',
),
'BRL' =>
array (
0 => 'R$',
),
'BRN' =>
array (
0 => 'BRN',
),
'BRR' =>
array (
0 => 'BRR',
),
'BRZ' =>
array (
0 => 'BRZ',
),
'BSD' =>
array (
0 => 'BSD',
),
'BTN' =>
array (
0 => 'BTN',
),
'BUK' =>
array (
0 => 'BUK',
),
'BWP' =>
array (
0 => 'BWP',
),
'BYB' =>
array (
0 => 'BYB',
),
'BYN' =>
array (
0 => 'BYN',
1 => 2,
2 => 0,
),
'BYR' =>
array (
0 => 'BYR',
1 => 0,
2 => 0,
),
'BZD' =>
array (
0 => 'BZD',
),
'CAD' =>
array (
0 => 'CA$',
1 => 2,
2 => 0,
),
'CDF' =>
array (
0 => 'CDF',
),
'CHE' =>
array (
0 => 'CHE',
),
'CHF' =>
array (
0 => 'CHF',
1 => 2,
2 => 0,
),
'CHW' =>
array (
0 => 'CHW',
),
'CLE' =>
array (
0 => 'CLE',
),
'CLF' =>
array (
0 => 'CLF',
1 => 4,
2 => 0,
),
'CLP' =>
array (
0 => 'CLP',
1 => 0,
2 => 0,
),
'CNH' =>
array (
0 => 'CNH',
),
'CNX' =>
array (
0 => 'CNX',
),
'CNY' =>
array (
0 => 'CN¥',
),
'COP' =>
array (
0 => 'COP',
1 => 2,
2 => 0,
),
'COU' =>
array (
0 => 'COU',
),
'CRC' =>
array (
0 => 'CRC',
1 => 2,
2 => 0,
),
'CSD' =>
array (
0 => 'CSD',
),
'CSK' =>
array (
0 => 'CSK',
),
'CUC' =>
array (
0 => 'CUC',
),
'CUP' =>
array (
0 => 'CUP',
),
'CVE' =>
array (
0 => 'CVE',
),
'CYP' =>
array (
0 => 'CYP',
),
'CZK' =>
array (
0 => 'CZK',
1 => 2,
2 => 0,
),
'DDM' =>
array (
0 => 'DDM',
),
'DEM' =>
array (
0 => 'DEM',
),
'DJF' =>
array (
0 => 'DJF',
1 => 0,
2 => 0,
),
'DKK' =>
array (
0 => 'DKK',
1 => 2,
2 => 0,
),
'DOP' =>
array (
0 => 'DOP',
),
'DZD' =>
array (
0 => 'DZD',
),
'ECS' =>
array (
0 => 'ECS',
),
'ECV' =>
array (
0 => 'ECV',
),
'EEK' =>
array (
0 => 'EEK',
),
'EGP' =>
array (
0 => 'EGP',
),
'ERN' =>
array (
0 => 'ERN',
),
'ESA' =>
array (
0 => 'ESA',
),
'ESB' =>
array (
0 => 'ESB',
),
'ESP' =>
array (
0 => 'ESP',
1 => 0,
2 => 0,
),
'ETB' =>
array (
0 => 'ETB',
),
'EUR' =>
array (
0 => '€',
),
'FIM' =>
array (
0 => 'FIM',
),
'FJD' =>
array (
0 => 'FJD',
),
'FKP' =>
array (
0 => 'FKP',
),
'FRF' =>
array (
0 => 'FRF',
),
'GBP' =>
array (
0 => '£',
),
'GEK' =>
array (
0 => 'GEK',
),
'GEL' =>
array (
0 => 'GEL',
),
'GHC' =>
array (
0 => 'GHC',
),
'GHS' =>
array (
0 => 'GHS',
),
'GIP' =>
array (
0 => 'GIP',
),
'GMD' =>
array (
0 => 'GMD',
),
'GNF' =>
array (
0 => 'GNF',
1 => 0,
2 => 0,
),
'GNS' =>
array (
0 => 'GNS',
),
'GQE' =>
array (
0 => 'GQE',
),
'GRD' =>
array (
0 => 'GRD',
),
'GTQ' =>
array (
0 => 'GTQ',
),
'GWE' =>
array (
0 => 'GWE',
),
'GWP' =>
array (
0 => 'GWP',
),
'GYD' =>
array (
0 => 'GYD',
1 => 2,
2 => 0,
),
'HKD' =>
array (
0 => 'HK$',
),
'HNL' =>
array (
0 => 'HNL',
),
'HRD' =>
array (
0 => 'HRD',
),
'HRK' =>
array (
0 => 'HRK',
),
'HTG' =>
array (
0 => 'HTG',
),
'HUF' =>
array (
0 => 'HUF',
1 => 2,
2 => 0,
),
'IDR' =>
array (
0 => 'IDR',
1 => 2,
2 => 0,
),
'IEP' =>
array (
0 => 'IEP',
),
'ILP' =>
array (
0 => 'ILP',
),
'ILR' =>
array (
0 => 'ILR',
),
'ILS' =>
array (
0 => '₪',
),
'INR' =>
array (
0 => '₹',
),
'IQD' =>
array (
0 => 'IQD',
1 => 0,
2 => 0,
),
'IRR' =>
array (
0 => 'IRR',
1 => 0,
2 => 0,
),
'ISJ' =>
array (
0 => 'ISJ',
),
'ISK' =>
array (
0 => 'ISK',
1 => 0,
2 => 0,
),
'ITL' =>
array (
0 => 'ITL',
1 => 0,
2 => 0,
),
'JMD' =>
array (
0 => 'JMD',
),
'JOD' =>
array (
0 => 'JOD',
1 => 3,
2 => 0,
),
'JPY' =>
array (
0 => '¥',
1 => 0,
2 => 0,
),
'KES' =>
array (
0 => 'KES',
),
'KGS' =>
array (
0 => 'KGS',
),
'KHR' =>
array (
0 => 'KHR',
),
'KMF' =>
array (
0 => 'KMF',
1 => 0,
2 => 0,
),
'KPW' =>
array (
0 => 'KPW',
1 => 0,
2 => 0,
),
'KRH' =>
array (
0 => 'KRH',
),
'KRO' =>
array (
0 => 'KRO',
),
'KRW' =>
array (
0 => '₩',
1 => 0,
2 => 0,
),
'KWD' =>
array (
0 => 'KWD',
1 => 3,
2 => 0,
),
'KYD' =>
array (
0 => 'KYD',
),
'KZT' =>
array (
0 => 'KZT',
),
'LAK' =>
array (
0 => 'LAK',
1 => 0,
2 => 0,
),
'LBP' =>
array (
0 => 'LBP',
1 => 0,
2 => 0,
),
'LKR' =>
array (
0 => 'LKR',
),
'LRD' =>
array (
0 => 'LRD',
),
'LSL' =>
array (
0 => 'LSL',
),
'LTL' =>
array (
0 => 'LTL',
),
'LTT' =>
array (
0 => 'LTT',
),
'LUC' =>
array (
0 => 'LUC',
),
'LUF' =>
array (
0 => 'LUF',
1 => 0,
2 => 0,
),
'LUL' =>
array (
0 => 'LUL',
),
'LVL' =>
array (
0 => 'LVL',
),
'LVR' =>
array (
0 => 'LVR',
),
'LYD' =>
array (
0 => 'LYD',
1 => 3,
2 => 0,
),
'MAD' =>
array (
0 => 'MAD',
),
'MAF' =>
array (
0 => 'MAF',
),
'MCF' =>
array (
0 => 'MCF',
),
'MDC' =>
array (
0 => 'MDC',
),
'MDL' =>
array (
0 => 'MDL',
),
'MGA' =>
array (
0 => 'MGA',
1 => 0,
2 => 0,
),
'MGF' =>
array (
0 => 'MGF',
1 => 0,
2 => 0,
),
'MKD' =>
array (
0 => 'MKD',
),
'MKN' =>
array (
0 => 'MKN',
),
'MLF' =>
array (
0 => 'MLF',
),
'MMK' =>
array (
0 => 'MMK',
1 => 0,
2 => 0,
),
'MNT' =>
array (
0 => 'MNT',
1 => 2,
2 => 0,
),
'MOP' =>
array (
0 => 'MOP',
),
'MRO' =>
array (
0 => 'MRO',
1 => 0,
2 => 0,
),
'MRU' =>
array (
0 => 'MRU',
),
'MTL' =>
array (
0 => 'MTL',
),
'MTP' =>
array (
0 => 'MTP',
),
'MUR' =>
array (
0 => 'MUR',
1 => 2,
2 => 0,
),
'MVP' =>
array (
0 => 'MVP',
),
'MVR' =>
array (
0 => 'MVR',
),
'MWK' =>
array (
0 => 'MWK',
),
'MXN' =>
array (
0 => 'MX$',
),
'MXP' =>
array (
0 => 'MXP',
),
'MXV' =>
array (
0 => 'MXV',
),
'MYR' =>
array (
0 => 'MYR',
),
'MZE' =>
array (
0 => 'MZE',
),
'MZM' =>
array (
0 => 'MZM',
),
'MZN' =>
array (
0 => 'MZN',
),
'NAD' =>
array (
0 => 'NAD',
),
'NGN' =>
array (
0 => 'NGN',
),
'NIC' =>
array (
0 => 'NIC',
),
'NIO' =>
array (
0 => 'NIO',
),
'NLG' =>
array (
0 => 'NLG',
),
'NOK' =>
array (
0 => 'NOK',
1 => 2,
2 => 0,
),
'NPR' =>
array (
0 => 'NPR',
),
'NZD' =>
array (
0 => 'NZ$',
),
'OMR' =>
array (
0 => 'OMR',
1 => 3,
2 => 0,
),
'PAB' =>
array (
0 => 'PAB',
),
'PEI' =>
array (
0 => 'PEI',
),
'PEN' =>
array (
0 => 'PEN',
),
'PES' =>
array (
0 => 'PES',
),
'PGK' =>
array (
0 => 'PGK',
),
'PHP' =>
array (
0 => '₱',
),
'PKR' =>
array (
0 => 'PKR',
1 => 2,
2 => 0,
),
'PLN' =>
array (
0 => 'PLN',
),
'PLZ' =>
array (
0 => 'PLZ',
),
'PTE' =>
array (
0 => 'PTE',
),
'PYG' =>
array (
0 => 'PYG',
1 => 0,
2 => 0,
),
'QAR' =>
array (
0 => 'QAR',
),
'RHD' =>
array (
0 => 'RHD',
),
'ROL' =>
array (
0 => 'ROL',
),
'RON' =>
array (
0 => 'RON',
),
'RSD' =>
array (
0 => 'RSD',
1 => 0,
2 => 0,
),
'RUB' =>
array (
0 => 'RUB',
),
'RUR' =>
array (
0 => 'RUR',
),
'RWF' =>
array (
0 => 'RWF',
1 => 0,
2 => 0,
),
'SAR' =>
array (
0 => 'SAR',
),
'SBD' =>
array (
0 => 'SBD',
),
'SCR' =>
array (
0 => 'SCR',
),
'SDD' =>
array (
0 => 'SDD',
),
'SDG' =>
array (
0 => 'SDG',
),
'SDP' =>
array (
0 => 'SDP',
),
'SEK' =>
array (
0 => 'SEK',
1 => 2,
2 => 0,
),
'SGD' =>
array (
0 => 'SGD',
),
'SHP' =>
array (
0 => 'SHP',
),
'SIT' =>
array (
0 => 'SIT',
),
'SKK' =>
array (
0 => 'SKK',
),
'SLL' =>
array (
0 => 'SLL',
1 => 0,
2 => 0,
),
'SOS' =>
array (
0 => 'SOS',
1 => 0,
2 => 0,
),
'SRD' =>
array (
0 => 'SRD',
),
'SRG' =>
array (
0 => 'SRG',
),
'SSP' =>
array (
0 => 'SSP',
),
'STD' =>
array (
0 => 'STD',
1 => 0,
2 => 0,
),
'STN' =>
array (
0 => 'STN',
),
'SUR' =>
array (
0 => 'SUR',
),
'SVC' =>
array (
0 => 'SVC',
),
'SYP' =>
array (
0 => 'SYP',
1 => 0,
2 => 0,
),
'SZL' =>
array (
0 => 'SZL',
),
'THB' =>
array (
0 => 'THB',
),
'TJR' =>
array (
0 => 'TJR',
),
'TJS' =>
array (
0 => 'TJS',
),
'TMM' =>
array (
0 => 'TMM',
1 => 0,
2 => 0,
),
'TMT' =>
array (
0 => 'TMT',
),
'TND' =>
array (
0 => 'TND',
1 => 3,
2 => 0,
),
'TOP' =>
array (
0 => 'TOP',
),
'TPE' =>
array (
0 => 'TPE',
),
'TRL' =>
array (
0 => 'TRL',
1 => 0,
2 => 0,
),
'TRY' =>
array (
0 => 'TRY',
),
'TTD' =>
array (
0 => 'TTD',
),
'TWD' =>
array (
0 => 'NT$',
1 => 2,
2 => 0,
),
'TZS' =>
array (
0 => 'TZS',
1 => 2,
2 => 0,
),
'UAH' =>
array (
0 => 'UAH',
),
'UAK' =>
array (
0 => 'UAK',
),
'UGS' =>
array (
0 => 'UGS',
),
'UGX' =>
array (
0 => 'UGX',
1 => 0,
2 => 0,
),
'USD' =>
array (
0 => '$',
),
'USN' =>
array (
0 => 'USN',
),
'USS' =>
array (
0 => 'USS',
),
'UYI' =>
array (
0 => 'UYI',
1 => 0,
2 => 0,
),
'UYP' =>
array (
0 => 'UYP',
),
'UYU' =>
array (
0 => 'UYU',
),
'UYW' =>
array (
0 => 'UYW',
1 => 4,
2 => 0,
),
'UZS' =>
array (
0 => 'UZS',
1 => 2,
2 => 0,
),
'VEB' =>
array (
0 => 'VEB',
),
'VEF' =>
array (
0 => 'VEF',
1 => 2,
2 => 0,
),
'VES' =>
array (
0 => 'VES',
),
'VND' =>
array (
0 => '₫',
1 => 0,
2 => 0,
),
'VNN' =>
array (
0 => 'VNN',
),
'VUV' =>
array (
0 => 'VUV',
1 => 0,
2 => 0,
),
'WST' =>
array (
0 => 'WST',
),
'XAF' =>
array (
0 => 'FCFA',
1 => 0,
2 => 0,
),
'XCD' =>
array (
0 => 'EC$',
),
'XEU' =>
array (
0 => 'XEU',
),
'XFO' =>
array (
0 => 'XFO',
),
'XFU' =>
array (
0 => 'XFU',
),
'XOF' =>
array (
0 => 'FCFA',
1 => 0,
2 => 0,
),
'XPF' =>
array (
0 => 'CFPF',
1 => 0,
2 => 0,
),
'XRE' =>
array (
0 => 'XRE',
),
'YDD' =>
array (
0 => 'YDD',
),
'YER' =>
array (
0 => 'YER',
1 => 0,
2 => 0,
),
'YUD' =>
array (
0 => 'YUD',
),
'YUM' =>
array (
0 => 'YUM',
),
'YUN' =>
array (
0 => 'YUN',
),
'YUR' =>
array (
0 => 'YUR',
),
'ZAL' =>
array (
0 => 'ZAL',
),
'ZAR' =>
array (
0 => 'ZAR',
),
'ZMK' =>
array (
0 => 'ZMK',
1 => 0,
2 => 0,
),
'ZMW' =>
array (
0 => 'ZMW',
),
'ZRN' =>
array (
0 => 'ZRN',
),
'ZRZ' =>
array (
0 => 'ZRZ',
),
'ZWD' =>
array (
0 => 'ZWD',
1 => 0,
2 => 0,
),
'ZWL' =>
array (
0 => 'ZWL',
),
'ZWR' =>
array (
0 => 'ZWR',
),
'DEFAULT' =>
array (
1 => 2,
2 => 0,
),
);

View File

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Intl\Icu\Collator as CollatorPolyfill;
/**
* Stub implementation for the Collator class of the intl extension.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class Collator extends CollatorPolyfill
{
}

View File

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Intl\Icu\IntlDateFormatter as IntlDateFormatterPolyfill;
/**
* Stub implementation for the IntlDateFormatter class of the intl extension.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class IntlDateFormatter extends IntlDateFormatterPolyfill
{
}

View File

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Intl\Icu\Locale as LocalePolyfill;
/**
* Stub implementation for the Locale class of the intl extension.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class Locale extends LocalePolyfill
{
}

View File

@ -0,0 +1,23 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Intl\Icu\NumberFormatter as NumberFormatterPolyfill;
/**
* Stub implementation for the NumberFormatter class of the intl extension.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @see IntlNumberFormatter
*/
class NumberFormatter extends NumberFormatterPolyfill
{
}

View File

@ -0,0 +1,33 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Intl\Icu as p;
if (extension_loaded('intl')) {
return;
}
if (\PHP_VERSION_ID >= 80000) {
return require __DIR__.'/bootstrap80.php';
}
if (!function_exists('intl_is_failure')) {
function intl_is_failure($errorCode) { return p\Icu::isFailure($errorCode); }
}
if (!function_exists('intl_get_error_code')) {
function intl_get_error_code() { return p\Icu::getErrorCode(); }
}
if (!function_exists('intl_get_error_message')) {
function intl_get_error_message() { return p\Icu::getErrorMessage(); }
}
if (!function_exists('intl_error_name')) {
function intl_error_name($errorCode) { return p\Icu::getErrorName($errorCode); }
}

View File

@ -0,0 +1,25 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Polyfill\Intl\Icu as p;
if (!function_exists('intl_is_failure')) {
function intl_is_failure(?int $errorCode): bool { return p\Icu::isFailure((int) $errorCode); }
}
if (!function_exists('intl_get_error_code')) {
function intl_get_error_code(): int { return p\Icu::getErrorCode(); }
}
if (!function_exists('intl_get_error_message')) {
function intl_get_error_message(): string { return p\Icu::getErrorMessage(); }
}
if (!function_exists('intl_error_name')) {
function intl_error_name(?int $errorCode): string { return p\Icu::getErrorName((int) $errorCode); }
}

View File

@ -0,0 +1,42 @@
{
"name": "symfony/polyfill-intl-icu",
"type": "library",
"description": "Symfony polyfill for intl's ICU-related data and classes",
"keywords": ["polyfill", "shim", "compatibility", "portable", "intl", "icu"],
"homepage": "https://symfony.com",
"license": "MIT",
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"require": {
"php": ">=7.1"
},
"autoload": {
"files": [ "bootstrap.php" ],
"psr-4": { "Symfony\\Polyfill\\Intl\\Icu\\": "" },
"classmap": [ "Resources/stubs" ],
"exclude-from-classmap": [
"/Tests/"
]
},
"suggest": {
"ext-intl": "For best performance and support of other locales than \"en\""
},
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
}
}