From 8051e9ad81483dd4bb59d7357d57aa0d325b7661 Mon Sep 17 00:00:00 2001 From: Eriksen Costa Date: Mon, 24 Jan 2011 22:48:04 -0200 Subject: [PATCH 01/32] [Locale] refactored Locale class The Locale component class now encapsulates the PHP's Locale static methods. If the intl extension is not loaded, these methods will throw a RuntimeException. More refactorings will be needed to remove the intl extension dependency. --- src/Symfony/Component/Locale/Locale.php | 262 +++++++++++++++++- .../Tests/Component/Locale/LocaleTest.php | 126 +++++++++ 2 files changed, 386 insertions(+), 2 deletions(-) create mode 100644 tests/Symfony/Tests/Component/Locale/LocaleTest.php diff --git a/src/Symfony/Component/Locale/Locale.php b/src/Symfony/Component/Locale/Locale.php index 3b2c4ec83bbcd..ff5f1d31d01b2 100644 --- a/src/Symfony/Component/Locale/Locale.php +++ b/src/Symfony/Component/Locale/Locale.php @@ -11,7 +11,7 @@ namespace Symfony\Component\Locale; -class Locale extends \Locale +class Locale { /** * Caches the countries in different locales @@ -164,4 +164,262 @@ public static function getLocales() { return array_keys(self::getDisplayLocales(self::getDefault())); } -} \ No newline at end of file + + /** + * Returns the best available locale based on HTTP "Accept-Language" header according to RFC 2616 + * + * @param string $header The string containing the "Accept-Language" header value + * @return string The corresponding locale code + * @see http://www.php.net/manual/en/locale.acceptfromhttp.php + * @throws RuntimeException When the intl extension is not loaded + */ + public static function acceptFromHttp($header) + { + self::isIntlExtensionAvailable(); + return \Locale::acceptFromHttp($header); + } + + /** + * Returns a correctly ordered and delimited locale code + * + * @param array $subtags A keyed array where the keys identify the particular locale code subtag + * @return string The corresponding locale code + * @see http://www.php.net/manual/en/locale.composelocale.php + * @throws RuntimeException When the intl extension is not loaded + */ + public static function composeLocale(array $subtags) + { + self::isIntlExtensionAvailable(); + return \Locale::composeLocale($subtags); + } + + /** + * Checks if a language tag filter matches with locale + * + * @param string $langtag The language tag to check + * @param string $locale The language range to check against + * @return string The corresponding locale code + * @see http://www.php.net/manual/en/locale.filtermatches.php + * @throws RuntimeException When the intl extension is not loaded + */ + public static function filterMatches($langtag, $locale, $canonicalize = false) + { + self::isIntlExtensionAvailable(); + return \Locale::filterMatches($langtag, $locale, $canonicalize); + } + + /** + * Returns the variants for the input locale + * + * @param string $locale The locale to extract the variants from + * @return array The locale variants + * @see http://www.php.net/manual/en/locale.getallvariants.php + * @throws RuntimeException When the intl extension is not loaded + */ + public static function getAllVariants($locale) + { + self::isIntlExtensionAvailable(); + return \Locale::getAllVariants($locale); + } + + /** + * Returns the default locale + * + * @return string The default locale code + * @see http://www.php.net/manual/en/locale.getdefault.php + * @throws RuntimeException When the intl extension is not loaded + */ + public static function getDefault() + { + self::isIntlExtensionAvailable(); + return \Locale::getDefault(); + } + + /** + * Returns the localized display name for the locale language + * + * @param string $locale The locale code to return the display language from + * @param string $inLocale Optional format locale code to use to display the language name + * @return string The localized language display name + * @see http://www.php.net/manual/en/locale.getdisplaylanguage.php + * @throws RuntimeException When the intl extension is not loaded + */ + public static function getDisplayLanguage($locale, $inLocale = null) + { + self::isIntlExtensionAvailable(); + return \Locale::getDisplayLanguage($locale, $inLocale); + } + + /** + * Returns the localized display name for the locale + * + * @param string $locale The locale code to return the display locale name from + * @param string $inLocale Optional format locale code to use to display the locale name + * @return string The localized locale display name + * @see http://www.php.net/manual/en/locale.getdisplayname.php + * @throws RuntimeException When the intl extension is not loaded + */ + public static function getDisplayName($locale, $inLocale = null) + { + self::isIntlExtensionAvailable(); + return \Locale::getDisplayName($locale, $inLocale); + } + + /** + * Returns the localized display name for the locale region + * + * @param string $locale The locale code to return the display region from + * @param string $inLocale Optional format locale code to use to display the region name + * @return string The localized region display name + * @see http://www.php.net/manual/en/locale.getdisplayregion.php + * @throws RuntimeException When the intl extension is not loaded + */ + public static function getDisplayRegion($locale, $inLocale = null) + { + self::isIntlExtensionAvailable(); + return \Locale::getDisplayRegion($locale, $inLocale); + } + + /** + * Returns the localized display name for the locale script + * + * @param string $locale The locale code to return the display scrit from + * @param string $inLocale Optional format locale code to use to display the script name + * @return string The localized script display name + * @see http://www.php.net/manual/en/locale.getdisplayscript.php + * @throws RuntimeException When the intl extension is not loaded + */ + public static function getDisplayScript($locale, $inLocale = null) + { + self::isIntlExtensionAvailable(); + return \Locale::getDisplayScript($locale, $inLocale); + } + + /** + * Returns the localized display name for the locale variant + * + * @param string $locale The locale code to return the display variant from + * @param string $inLocale Optional format locale code to use to display the variant name + * @return string The localized variant display name + * @see http://www.php.net/manual/en/locale.getdisplayvariant.php + * @throws RuntimeException When the intl extension is not loaded + */ + public static function getDisplayVariant($locale, $inLocale = null) + { + self::isIntlExtensionAvailable(); + return \Locale::getDisplayVariant($locale, $inLocale); + } + + /** + * Returns the keywords for the locale + * + * @param string $locale The locale code to extract the keywords from + * @return array Associative array with the extracted variants + * @see http://www.php.net/manual/en/locale.getkeywords.php + * @throws RuntimeException When the intl extension is not loaded + */ + public static function getKeywords($locale) + { + self::isIntlExtensionAvailable(); + return \Locale::getKeywords($locale); + } + + /** + * Returns the primary language for the locale + * + * @param string $locale The locale code to extract the language code from + * @return string|null The extracted language code or null in case of error + * @see http://www.php.net/manual/en/locale.getprimarylanguage.php + * @throws RuntimeException When the intl extension is not loaded + */ + public static function getPrimaryLanguage($locale) + { + self::isIntlExtensionAvailable(); + return \Locale::getPrimaryLanguage($locale); + } + + /** + * Returns the region for the locale + * + * @param string $locale The locale code to extract the region code from + * @return string|null The extracted region code or null if not present + * @see http://www.php.net/manual/en/locale.getregion.php + * @throws RuntimeException When the intl extension is not loaded + */ + public static function getRegion($locale) + { + self::isIntlExtensionAvailable(); + return \Locale::getRegion($locale); + } + + /** + * Returns the script for the locale + * + * @param string $locale The locale code to extract the script code from + * @return string|null The extracted script code or null if not present + * @see http://www.php.net/manual/en/locale.getscript.php + * @throws RuntimeException When the intl extension is not loaded + */ + public static function getScript($locale) + { + self::isIntlExtensionAvailable(); + return \Locale::getScript($locale); + } + + /** + * Returns the closest language tag for the locale + * + * @param array $langtag A list of the language tags to compare to locale + * @param string $locale The locale to use as the language range when matching + * @param bool $canonicalize If true, the arguments will be converted to canonical form before matching + * @param string $default The locale to use if no match is found + * @see http://www.php.net/manual/en/locale.lookup.php + * @throws RuntimeException When the intl extension is not loaded + */ + public static function lookup(array $langtag, $locale, $canonicalize = false, $default = null) + { + self::isIntlExtensionAvailable(); + return \Locale::lookup($langtag, $locale, $canonicalize, $default); + } + + /** + * Returns an associative array of locale identifier subtags + * + * @param string $locale The locale code to extract the subtag array from + * @return array Associative arrat with the extracted subtags + * @see http://www.php.net/manual/en/locale.parselocale.php + * @throws RuntimeException When the intl extension is not loaded + */ + public static function parseLocale($locale) + { + self::isIntlExtensionAvailable(); + return \Locale::parseLocale($locale); + } + + /** + * Sets the default runtime locale + * + * @param string $locale The locale code + * @see http://www.php.net/manual/en/locale.parselocale.php + * @throws RuntimeException When the intl extension is not loaded + */ + public static function setDefault($locale) + { + self::isIntlExtensionAvailable(); + return \Locale::setDefault($locale); + } + + /** + * Check if the intl extension is loaded. + * + * @throws RuntimeException If the intl extension is not loaded + */ + private static function isIntlExtensionAvailable() + { + if (!extension_loaded('intl')) { + throw new \RuntimeException('The intl extension is not available.'); + } + + return true; + } +} diff --git a/tests/Symfony/Tests/Component/Locale/LocaleTest.php b/tests/Symfony/Tests/Component/Locale/LocaleTest.php new file mode 100644 index 0000000000000..c292bf2e38483 --- /dev/null +++ b/tests/Symfony/Tests/Component/Locale/LocaleTest.php @@ -0,0 +1,126 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +class LocaleTest extends \PHPUnit_Framework_TestCase +{ + public function setUp() + { + if (!extension_loaded('intl')) { + $this->markTestSkipped('The intl extension is not available.'); + } + } + + public function testAcceptFromHttp() + { + $this->assertEquals('pt_BR', Locale::acceptFromHttp('pt-br,en-us;q=0.7,en;q=0.5')); + } + + public function testComposeLocale() + { + $subtags = array( + 'language' => 'pt', + 'script' => 'Latn', + 'region' => 'BR' + ); + $this->assertEquals('pt_Latn_BR', Locale::composeLocale($subtags)); + } + + public function testFilterMatches() + { + $this->assertTrue(Locale::filterMatches('pt-BR', 'pt-BR')); + } + + public function testGetAllVariants() + { + $this->assertEquals(array('LATN'), Locale::getAllVariants('pt_BR_Latn')); + } + + /** + * @covers Symfony\Component\Locale\Locale::getDefault + * @covers Symfony\Component\Locale\Locale::setDefault + */ + public function testGetDefault() + { + Locale::setDefault('en_US'); + $this->assertEquals('en_US', Locale::getDefault()); + } + + public function testGetDisplayLanguage() + { + $this->assertEquals('Portuguese', Locale::getDisplayLanguage('pt-Latn-BR', 'en')); + } + + public function testGetDisplayName() + { + $this->assertEquals('Portuguese (Latin, Brazil)', Locale::getDisplayName('pt-Latn-BR', 'en')); + } + + public function testGetDisplayRegion() + { + $this->assertEquals('Brazil', Locale::getDisplayRegion('pt-Latn-BR', 'en')); + } + + public function testGetDisplayScript() + { + $this->assertEquals('Latin', Locale::getDisplayScript('pt-Latn-BR', 'en')); + } + + public function testGetDisplayVariant() + { + $this->assertEmpty(Locale::getDisplayVariant('pt-Latn-BR', 'en')); + } + + public function testGetKeywords() + { + $this->assertEquals( + array('currency' => 'BRL'), + Locale::getKeywords('pt-BR@currency=BRL') + ); + } + + public function testGetPrimaryLanguage() + { + $this->assertEquals('pt', Locale::getPrimaryLanguage('pt-Latn-BR')); + } + + public function testGetRegion() + { + $this->assertEquals('BR', Locale::getRegion('pt-Latn-BR')); + } + + public function testGetScript() + { + $this->assertEquals('Latn', Locale::getScript('pt-Latn-BR')); + } + + public function testLookup() + { + $langtag = array( + 'pt-Latn-BR', + 'pt-BR' + ); + $this->assertEquals('pt-BR', Locale::lookup($langtag, 'pt-BR-x-priv1')); + } + + public function testParseLocale() + { + $expected = array( + 'language' => 'pt', + 'script' => 'Latn', + 'region' => 'BR' + ); + $this->assertEquals($expected, Locale::parseLocale('pt-Latn-BR')); + } +} From 2850851d5ec8bc39a44eccb6c4bf10653783e5f9 Mon Sep 17 00:00:00 2001 From: Eriksen Costa Date: Tue, 25 Jan 2011 14:50:51 -0200 Subject: [PATCH 02/32] [Locale] renamed and simplified Locale::isIntlExtensionAvailable() --- src/Symfony/Component/Locale/Locale.php | 41 ++++++++++++------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/Symfony/Component/Locale/Locale.php b/src/Symfony/Component/Locale/Locale.php index ff5f1d31d01b2..f9b1f9908bdc7 100644 --- a/src/Symfony/Component/Locale/Locale.php +++ b/src/Symfony/Component/Locale/Locale.php @@ -175,7 +175,7 @@ public static function getLocales() */ public static function acceptFromHttp($header) { - self::isIntlExtensionAvailable(); + self::assertIntlExtensionAvailability(); return \Locale::acceptFromHttp($header); } @@ -189,7 +189,7 @@ public static function acceptFromHttp($header) */ public static function composeLocale(array $subtags) { - self::isIntlExtensionAvailable(); + self::assertIntlExtensionAvailability(); return \Locale::composeLocale($subtags); } @@ -204,7 +204,7 @@ public static function composeLocale(array $subtags) */ public static function filterMatches($langtag, $locale, $canonicalize = false) { - self::isIntlExtensionAvailable(); + self::assertIntlExtensionAvailability(); return \Locale::filterMatches($langtag, $locale, $canonicalize); } @@ -218,7 +218,7 @@ public static function filterMatches($langtag, $locale, $canonicalize = false) */ public static function getAllVariants($locale) { - self::isIntlExtensionAvailable(); + self::assertIntlExtensionAvailability(); return \Locale::getAllVariants($locale); } @@ -231,7 +231,7 @@ public static function getAllVariants($locale) */ public static function getDefault() { - self::isIntlExtensionAvailable(); + self::assertIntlExtensionAvailability(); return \Locale::getDefault(); } @@ -246,7 +246,7 @@ public static function getDefault() */ public static function getDisplayLanguage($locale, $inLocale = null) { - self::isIntlExtensionAvailable(); + self::assertIntlExtensionAvailability(); return \Locale::getDisplayLanguage($locale, $inLocale); } @@ -261,7 +261,7 @@ public static function getDisplayLanguage($locale, $inLocale = null) */ public static function getDisplayName($locale, $inLocale = null) { - self::isIntlExtensionAvailable(); + self::assertIntlExtensionAvailability(); return \Locale::getDisplayName($locale, $inLocale); } @@ -276,7 +276,7 @@ public static function getDisplayName($locale, $inLocale = null) */ public static function getDisplayRegion($locale, $inLocale = null) { - self::isIntlExtensionAvailable(); + self::assertIntlExtensionAvailability(); return \Locale::getDisplayRegion($locale, $inLocale); } @@ -291,7 +291,7 @@ public static function getDisplayRegion($locale, $inLocale = null) */ public static function getDisplayScript($locale, $inLocale = null) { - self::isIntlExtensionAvailable(); + self::assertIntlExtensionAvailability(); return \Locale::getDisplayScript($locale, $inLocale); } @@ -306,7 +306,7 @@ public static function getDisplayScript($locale, $inLocale = null) */ public static function getDisplayVariant($locale, $inLocale = null) { - self::isIntlExtensionAvailable(); + self::assertIntlExtensionAvailability(); return \Locale::getDisplayVariant($locale, $inLocale); } @@ -320,7 +320,7 @@ public static function getDisplayVariant($locale, $inLocale = null) */ public static function getKeywords($locale) { - self::isIntlExtensionAvailable(); + self::assertIntlExtensionAvailability(); return \Locale::getKeywords($locale); } @@ -334,7 +334,7 @@ public static function getKeywords($locale) */ public static function getPrimaryLanguage($locale) { - self::isIntlExtensionAvailable(); + self::assertIntlExtensionAvailability(); return \Locale::getPrimaryLanguage($locale); } @@ -348,7 +348,7 @@ public static function getPrimaryLanguage($locale) */ public static function getRegion($locale) { - self::isIntlExtensionAvailable(); + self::assertIntlExtensionAvailability(); return \Locale::getRegion($locale); } @@ -362,7 +362,7 @@ public static function getRegion($locale) */ public static function getScript($locale) { - self::isIntlExtensionAvailable(); + self::assertIntlExtensionAvailability(); return \Locale::getScript($locale); } @@ -378,7 +378,7 @@ public static function getScript($locale) */ public static function lookup(array $langtag, $locale, $canonicalize = false, $default = null) { - self::isIntlExtensionAvailable(); + self::assertIntlExtensionAvailability(); return \Locale::lookup($langtag, $locale, $canonicalize, $default); } @@ -392,7 +392,7 @@ public static function lookup(array $langtag, $locale, $canonicalize = false, $d */ public static function parseLocale($locale) { - self::isIntlExtensionAvailable(); + self::assertIntlExtensionAvailability(); return \Locale::parseLocale($locale); } @@ -400,26 +400,25 @@ public static function parseLocale($locale) * Sets the default runtime locale * * @param string $locale The locale code + * @return bool true on success or false on failure * @see http://www.php.net/manual/en/locale.parselocale.php * @throws RuntimeException When the intl extension is not loaded */ public static function setDefault($locale) { - self::isIntlExtensionAvailable(); + self::assertIntlExtensionAvailability(); return \Locale::setDefault($locale); } /** * Check if the intl extension is loaded. * - * @throws RuntimeException If the intl extension is not loaded + * @throws RuntimeException When the intl extension is not loaded */ - private static function isIntlExtensionAvailable() + private static function assertIntlExtensionAvailability() { if (!extension_loaded('intl')) { throw new \RuntimeException('The intl extension is not available.'); } - - return true; } } From 4c1ce759758e99f53b76192c9e9f7e54b43cb686 Mon Sep 17 00:00:00 2001 From: Eriksen Costa Date: Tue, 25 Jan 2011 21:04:24 -0200 Subject: [PATCH 03/32] [Locale] added intl's \Locale constants to the Locale class --- src/Symfony/Component/Locale/Locale.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Symfony/Component/Locale/Locale.php b/src/Symfony/Component/Locale/Locale.php index f9b1f9908bdc7..a033c21b87590 100644 --- a/src/Symfony/Component/Locale/Locale.php +++ b/src/Symfony/Component/Locale/Locale.php @@ -13,6 +13,21 @@ class Locale { + const DEFAULT_LOCALE = null; + + /** Locale method constants */ + const ACTUAL_LOCALE = 0; + const VALID_LOCALE = 1; + + /** Language tags constants */ + const LANG_TAG = 'language'; + const EXTLANG_TAG = 'extlang'; + const SCRIPT_TAG = 'script'; + const REGION_TAG = 'region'; + const VARIANT_TAG = 'variant'; + const GRANDFATHERED_LANG_TAG = 'grandfathered'; + const PRIVATE_TAG = 'private'; + /** * Caches the countries in different locales * @var array From 2333ee3892c317b31c7c2ae3bfd5e7c62adb9789 Mon Sep 17 00:00:00 2001 From: Eriksen Costa Date: Tue, 25 Jan 2011 17:29:08 -0200 Subject: [PATCH 04/32] [Locale] added NumberFormatterInterface --- .../Locale/NumberFormatterInterface.php | 243 ++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 src/Symfony/Component/Locale/NumberFormatterInterface.php diff --git a/src/Symfony/Component/Locale/NumberFormatterInterface.php b/src/Symfony/Component/Locale/NumberFormatterInterface.php new file mode 100644 index 0000000000000..1d2e7b79fe895 --- /dev/null +++ b/src/Symfony/Component/Locale/NumberFormatterInterface.php @@ -0,0 +1,243 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Locale; + +interface NumberFormatterInterface +{ + /** Format style constants */ + const PATTERN_DECIMAL = 0; + const DECIMAL = 1; + const CURRENCY = 2; + const PERCENT = 3; + const SCIENTIFIC = 4; + const SPELLOUT = 5; + const ORDINAL = 6; + const DURATION = 7; + const PATTERN_RULEBASED = 9; + const IGNORE = 0; + const DEFAULT_STYLE = 1; + + /** Format type constants */ + const TYPE_DEFAULT = 0; + const TYPE_INT32 = 1; + const TYPE_INT64 = 2; + const TYPE_DOUBLE = 3; + const TYPE_CURRENCY = 4; + + /** Numeric attribute constants */ + const PARSE_INT_ONLY = 0; + const GROUPING_USED = 1; + const DECIMAL_ALWAYS_SHOWN = 2; + const MAX_INTEGER_DIGITS = 3; + const MIN_INTEGER_DIGITS = 4; + const INTEGER_DIGITS = 5; + const MAX_FRACTION_DIGITS = 6; + const MIN_FRACTION_DIGITS = 7; + const FRACTION_DIGITS = 8; + const MULTIPLIER = 9; + const GROUPING_SIZE = 10; + const ROUNDING_MODE = 11; + const ROUNDING_INCREMENT = 12; + const FORMAT_WIDTH = 13; + const PADDING_POSITION = 14; + const SECONDARY_GROUPING_SIZE = 15; + const SIGNIFICANT_DIGITS_USED = 16; + const MIN_SIGNIFICANT_DIGITS = 17; + const MAX_SIGNIFICANT_DIGITS = 18; + const LENIENT_PARSE = 19; + + /** Text attribute constants */ + const POSITIVE_PREFIX = 0; + const POSITIVE_SUFFIX = 1; + const NEGATIVE_PREFIX = 2; + const NEGATIVE_SUFFIX = 3; + const PADDING_CHARACTER = 4; + const CURRENCY_CODE = 5; + const DEFAULT_RULESET = 6; + const PUBLIC_RULESETS = 7; + + /** Format symbol constants */ + const DECIMAL_SEPARATOR_SYMBOL = 0; + const GROUPING_SEPARATOR_SYMBOL = 1; + const PATTERN_SEPARATOR_SYMBOL = 2; + const PERCENT_SYMBOL = 3; + const ZERO_DIGIT_SYMBOL = 4; + const DIGIT_SYMBOL = 5; + const MINUS_SIGN_SYMBOL = 6; + const PLUS_SIGN_SYMBOL = 7; + const CURRENCY_SYMBOL = 8; + const INTL_CURRENCY_SYMBOL = 9; + const MONETARY_SEPARATOR_SYMBOL = 10; + const EXPONENTIAL_SYMBOL = 11; + const PERMILL_SYMBOL = 12; + const PAD_ESCAPE_SYMBOL = 13; + const INFINITY_SYMBOL = 14; + const NAN_SYMBOL = 15; + const SIGNIFICANT_DIGIT_SYMBOL = 16; + const MONETARY_GROUPING_SEPARATOR_SYMBOL = 17; + + /** Rounding mode values used by NumberFormatter::setAttribute() with NumberFormatter::ROUNDING_MODE attribute */ + const ROUND_CEILING = 0; + const ROUND_FLOOR = 1; + const ROUND_DOWN = 2; + const ROUND_UP = 3; + const ROUND_HALFEVEN = 4; + const ROUND_HALFDOWN = 5; + const ROUND_HALFUP = 6; + + /** Pad position values used by NumberFormatter::setAttribute() with NumberFormatter::PADDING_POSITION attribute */ + const PAD_BEFORE_PREFIX = 0; + const PAD_AFTER_PREFIX = 1; + const PAD_BEFORE_SUFFIX = 2; + const PAD_AFTER_SUFFIX = 3; + + /** + * Constructor + * + * @param string $locale The locale code + * @param int $style Style of the formatting, one of the format style constants + * @param string $pattern 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 http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details + * @see http://www.icu-project.org/apiref/icu4c/classRuleBasedNumberFormat.html#_details + */ + function __construct($locale, $style, $pattern = null); + + /** + * Format a currency value + * + * @param float $value The numeric currency value + * @param string $currency The 3-letter ISO 4217 currency code indicating the currency to use + * @return string The formatted currency value + * @see http://www.iso.org/iso/support/faqs/faqs_widely_used_standards/widely_used_standards_other/currency_codes/currency_codes_list-1.htm + */ + function formatCurrency($value, $currency); + + /** + * Format a number + * + * @param number $value The value to format + * @param int $type Type of the formatting, one of the format type constants + * @return bool|string The formatted value or false on error + */ + function format($value, $type); + + /** + * Returns an attribute value + * + * @param int $attr An attribute specifier, one of the numeric attribute constants + * @return bool|int The attribute value on success or false on error + */ + function getAttribute($attr); + + /** + * Returns formatter's last error code + * + * @return int The error code from last formatter call + */ + function getErrorCode(); + + /** + * Returns formatter's last error message + * + * @return string The error message from last formatter call + */ + function getErrorMessage(); + + /** + * Returns the formatter's locale + * + * @param int $type The locale name type to return between valid or actual (Locale::VALID_LOCALE or Locale::ACTUAL_LOCALE, respectively) + * @return string The locale name used to create the formatter + */ + function getLocale($type); + + /** + * Returns the formatter's pattern + * + * @return bool|string The pattern string used by the formatter or false on error + */ + function getPattern(); + + /** + * Returns a formatter symbol value + * + * @param int $attr A symbol specifier, one of the format symbol constants + * @return bool|string The symbol value or false on error + */ + function getSymbol($attr); + + /** + * Returns a formatter text attribute value + * + * @param int $attr An attribute specifier, one of the text attribute constants + * @return bool|string The attribute value or false on error + */ + function getTextAttribute($attr); + + /** + * Parse a currency number + * + * @param string $value The value to parse + * @param string $currency Parameter to receive the currency name (reference) + * @param int $position Offset to begin the parsing on return this value will hold the offset at which the parsing ended + * @return bool|string The parsed numeric value of false on error + */ + function parseCurrency($value, &$currency, &$position = null); + + /** + * Parse a number + * + * @param string $value The value to parse + * @param string $type Type of the formatting, one of the format type constants. NumberFormatter::TYPE_DOUBLE by default + * @param int $position Offset to begin the parsing on return this value will hold the offset at which the parsing ended + * @return bool|string The parsed value of false on error + */ + function parse($value, $type = self::TYPE_DOUBLE, &$position = null); + + /** + * Set an attribute + * + * @param int $attr An attribute specifier, one of the numeric attribute constants + * @param int $value The attribute value + * @return bool true on success or false on failure + */ + function setAttribute($attr, $value); + + /** + * Set the formatter's pattern + * + * @param string $pattern A pattern string in conformance with the ICU DecimalFormat documentation + * @return bool true on success or false on failure + * @see http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details + */ + function setPattern($attr, $value); + + /** + * Set the formatter's symbol + * + * @param int $attr A symbol specifier, one of the format symbol constants + * @param string $value The value for the symbol + * @return bool true on success or false on failure + */ + function setSymbol($attr, $value); + + /** + * Set a text attribute + * + * @param int $attr An attribute specifier, one of the text attribute constants + * @param int $value The attribute value + * @return bool true on success or false on failure + */ + function setTextAttribute($attr, $value); +} From 8f17d18e66a98b6db7f0e85d9568e19932572985 Mon Sep 17 00:00:00 2001 From: Eriksen Costa Date: Tue, 25 Jan 2011 20:00:38 -0200 Subject: [PATCH 05/32] [Locale] added NumberFormatter class This class simply encapsulates intl's NumberFormatter class. --- .../Component/Locale/NumberFormatter.php | 150 +++++++++++++++++ .../Locale/NumberFormatterInterface.php | 4 +- .../Component/Locale/NumberFormatterTest.php | 151 ++++++++++++++++++ 3 files changed, 303 insertions(+), 2 deletions(-) create mode 100644 src/Symfony/Component/Locale/NumberFormatter.php create mode 100644 tests/Symfony/Tests/Component/Locale/NumberFormatterTest.php diff --git a/src/Symfony/Component/Locale/NumberFormatter.php b/src/Symfony/Component/Locale/NumberFormatter.php new file mode 100644 index 0000000000000..5a0b53996533d --- /dev/null +++ b/src/Symfony/Component/Locale/NumberFormatter.php @@ -0,0 +1,150 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Locale; + +use Symfony\Component\Locale\NumberFormatterInterface; + +/** + * Provides a NumberFormatter using the related intl class capabilities. + */ +class NumberFormatter implements NumberFormatterInterface +{ + private $formatter = null; + + /** + * @{inheritDoc} + */ + public function __construct($locale, $style, $pattern = null) + { + $this->formatter = new \NumberFormatter($locale, $style, $pattern); + } + + /** + * @{inheritDoc} + */ + public function formatCurrency($value, $currency) + { + return $this->formatter->formatCurrency($value, $currency); + } + + /** + * @{inheritDoc} + */ + public function format($value, $type = null) + { + return $this->formatter->format($value, $type); + } + + /** + * @{inheritDoc} + */ + public function getAttribute($attr) + { + return $this->formatter->getAttribute($attr); + } + + /** + * @{inheritDoc} + */ + public function getErrorCode() + { + return $this->formatter->getErrorCode(); + } + + /** + * @{inheritDoc} + */ + public function getErrorMessage() + { + return $this->formatter->getErrorMessage(); + } + + /** + * @{inheritDoc} + */ + public function getLocale($type) + { + return $this->formatter->getLocale($type); + } + + /** + * @{inheritDoc} + */ + public function getPattern() + { + return $this->formatter->getPattern(); + } + + /** + * @{inheritDoc} + */ + public function getSymbol($attr) + { + return $this->formatter->getSymbol($attr); + } + + /** + * @{inheritDoc} + */ + public function getTextAttribute($attr) + { + return $this->formatter->getTextAttribute($attr); + } + + /** + * @{inheritDoc} + */ + public function parseCurrency($value, &$currency, &$position = null) + { + return $this->formatter->parseCurrency($value, $currency, $position); + } + + /** + * @{inheritDoc} + */ + public function parse($value, $type = self::TYPE_DOUBLE, &$position = null) + { + return $this->formatter->parse($value, $type, $position); + } + + /** + * @{inheritDoc} + */ + public function setAttribute($attr, $value) + { + return $this->formatter->setAttribute($attr, $value); + } + + /** + * @{inheritDoc} + */ + public function setPattern($pattern) + { + return $this->formatter->setPattern($pattern); + } + + /** + * @{inheritDoc} + */ + public function setSymbol($attr, $value) + { + return $this->formatter->setSymbol($attr, $value); + } + + /** + * @{inheritDoc} + */ + public function setTextAttribute($attr, $value) + { + return $this->formatter->setTextAttribute($attr, $value); + } +} diff --git a/src/Symfony/Component/Locale/NumberFormatterInterface.php b/src/Symfony/Component/Locale/NumberFormatterInterface.php index 1d2e7b79fe895..5ff2d6b62107e 100644 --- a/src/Symfony/Component/Locale/NumberFormatterInterface.php +++ b/src/Symfony/Component/Locale/NumberFormatterInterface.php @@ -130,7 +130,7 @@ function formatCurrency($value, $currency); * @param int $type Type of the formatting, one of the format type constants * @return bool|string The formatted value or false on error */ - function format($value, $type); + function format($value, $type = null); /** * Returns an attribute value @@ -221,7 +221,7 @@ function setAttribute($attr, $value); * @return bool true on success or false on failure * @see http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details */ - function setPattern($attr, $value); + function setPattern($pattern); /** * Set the formatter's symbol diff --git a/tests/Symfony/Tests/Component/Locale/NumberFormatterTest.php b/tests/Symfony/Tests/Component/Locale/NumberFormatterTest.php new file mode 100644 index 0000000000000..977910e109d79 --- /dev/null +++ b/tests/Symfony/Tests/Component/Locale/NumberFormatterTest.php @@ -0,0 +1,151 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Tests\Component\Locale; + +use Symfony\Component\Locale\NumberFormatter; + +class NumberFormatterTest extends \PHPUnit_Framework_TestCase +{ + public function setUp() + { + if (!extension_loaded('intl')) { + $this->markTestSkipped('The intl extension is not available.'); + } + } + + public function testConstructor() + { + $formatter = $this->createFormatter(); + $this->assertInstanceOf('Symfony\Component\Locale\NumberFormatter', $formatter); + } + + public function testFormatCurrency() + { + $formatter = $this->createFormatter(NumberFormatter::CURRENCY); + $this->assertEquals('R$1.000,00', $formatter->formatCurrency(1000, 'BRL')); + } + + public function testFormat() + { + $formatter = $this->createFormatter(); + $this->assertEquals('1.000', $formatter->format(1000)); + } + + public function testGetAttribute() + { + $formatter = $this->createFormatter(); + $this->assertEquals(3, $formatter->getAttribute(NumberFormatter::MAX_FRACTION_DIGITS)); + } + + public function testGetErrorCode() + { + $formatter = $this->createFormatter(); + $this->assertInternalType('int', $formatter->getErrorCode()); + + // It's strange but as NumberFormat::DEFAULT_STYLE have the same value + // that NumberFormat::DECIMAL, this warning is triggered. The same + // applies to getErrorMessage(). + // http://icu-project.org/apiref/icu4c/unum_8h.html + $this->assertEquals(-127, $formatter->getErrorCode()); + } + + public function testGetErrorMessage() + { + $formatter = $this->createFormatter(); + $this->assertInternalType('string', $formatter->getErrorMessage()); + $this->assertEquals('U_USING_DEFAULT_WARNING', $formatter->getErrorMessage()); + } + + /** + * @todo Update Locale class used (use the class from Locale component) + */ + public function testGetLocale() + { + $formatter = $this->createFormatter(); + $this->assertEquals('pt', $formatter->getLocale(\Locale::ACTUAL_LOCALE)); + $this->assertEquals('pt_BR', $formatter->getLocale(\Locale::VALID_LOCALE)); + } + + public function testGetPattern() + { + $formatter = $this->createFormatter(); + $this->assertEquals('#,##0.###', $formatter->getPattern()); + } + + public function testGetSymbol() + { + $formatter = $this->createFormatter(); + $this->assertEquals('R$', $formatter->getSymbol(NumberFormatter::CURRENCY_SYMBOL)); + } + + public function testGetTextAttribute() + { + $formatter = $this->createFormatter(); + $this->assertEquals('-', $formatter->getTextAttribute(NumberFormatter::NEGATIVE_PREFIX)); + } + + public function testParseCurrency() + { + $formatter = $this->createFormatter(NumberFormatter::CURRENCY); + + $position = 0; + $value = $formatter->parseCurrency('(US$1.000,00)', $currency, $position); + + $this->assertEquals(-1000, $value); + $this->assertEquals('USD', $currency); + $this->assertEquals(13, $position); + } + + public function testParse() + { + $formatter = $this->createFormatter(); + + $position = 0; + $value = $formatter->parse('1.000,00', NumberFormatter::TYPE_DOUBLE, $position); + + $this->assertEquals(1000.00, $value); + $this->assertEquals(8, $position); + } + + public function testSetAttribute() + { + $formatter = $this->createFormatter(); + $this->assertTrue($formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 2)); + $this->assertEquals(2, $formatter->getAttribute(NumberFormatter::MAX_FRACTION_DIGITS)); + } + + public function testSetPattern() + { + $formatter = $this->createFormatter(); + $this->assertTrue($formatter->setPattern('#,##0.###')); + $this->assertEquals('#,##0.###', $formatter->getPattern()); + } + + public function testSetSymbol() + { + $formatter = $this->createFormatter(); + $this->assertTrue($formatter->setSymbol(NumberFormatter::CURRENCY_SYMBOL, 'BRL')); + $this->assertEquals('BRL', $formatter->getSymbol(NumberFormatter::CURRENCY_SYMBOL)); + } + + public function testSetTextAttribute() + { + $formatter = $this->createFormatter(); + $this->assertTrue($formatter->setTextAttribute(NumberFormatter::POSITIVE_PREFIX, '+')); + $this->assertEquals('+', $formatter->getTextAttribute(NumberFormatter::POSITIVE_PREFIX)); + } + + private function createFormatter($style = NumberFormatter::DECIMAL) + { + return new NumberFormatter('pt_BR', $style); + } +} From 5a51450cfec7147709841998bf2fd497e2d27181 Mon Sep 17 00:00:00 2001 From: Eriksen Costa Date: Tue, 25 Jan 2011 21:13:09 -0200 Subject: [PATCH 06/32] [Locale] changed NumberFormatInterface::getLocale() to use Locale::ACTUAL_LOCALE constant --- src/Symfony/Component/Locale/NumberFormatter.php | 3 ++- src/Symfony/Component/Locale/NumberFormatterInterface.php | 4 +++- tests/Symfony/Tests/Component/Locale/NumberFormatterTest.php | 5 +++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Locale/NumberFormatter.php b/src/Symfony/Component/Locale/NumberFormatter.php index 5a0b53996533d..59ba7315104e9 100644 --- a/src/Symfony/Component/Locale/NumberFormatter.php +++ b/src/Symfony/Component/Locale/NumberFormatter.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Locale; +use Symfony\Component\Locale\Locale; use Symfony\Component\Locale\NumberFormatterInterface; /** @@ -71,7 +72,7 @@ public function getErrorMessage() /** * @{inheritDoc} */ - public function getLocale($type) + public function getLocale($type = Locale::ACTUAL_LOCALE) { return $this->formatter->getLocale($type); } diff --git a/src/Symfony/Component/Locale/NumberFormatterInterface.php b/src/Symfony/Component/Locale/NumberFormatterInterface.php index 5ff2d6b62107e..06e4f3ded1d46 100644 --- a/src/Symfony/Component/Locale/NumberFormatterInterface.php +++ b/src/Symfony/Component/Locale/NumberFormatterInterface.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Locale; +use Symfony\Component\Locale\Locale; + interface NumberFormatterInterface { /** Format style constants */ @@ -160,7 +162,7 @@ function getErrorMessage(); * @param int $type The locale name type to return between valid or actual (Locale::VALID_LOCALE or Locale::ACTUAL_LOCALE, respectively) * @return string The locale name used to create the formatter */ - function getLocale($type); + function getLocale($type = Locale::ACTUAL_LOCALE); /** * Returns the formatter's pattern diff --git a/tests/Symfony/Tests/Component/Locale/NumberFormatterTest.php b/tests/Symfony/Tests/Component/Locale/NumberFormatterTest.php index 977910e109d79..d01ffef890551 100644 --- a/tests/Symfony/Tests/Component/Locale/NumberFormatterTest.php +++ b/tests/Symfony/Tests/Component/Locale/NumberFormatterTest.php @@ -11,6 +11,7 @@ namespace Symfony\Tests\Component\Locale; +use Symfony\Component\Locale\Locale; use Symfony\Component\Locale\NumberFormatter; class NumberFormatterTest extends \PHPUnit_Framework_TestCase @@ -71,8 +72,8 @@ public function testGetErrorMessage() public function testGetLocale() { $formatter = $this->createFormatter(); - $this->assertEquals('pt', $formatter->getLocale(\Locale::ACTUAL_LOCALE)); - $this->assertEquals('pt_BR', $formatter->getLocale(\Locale::VALID_LOCALE)); + $this->assertEquals('pt', $formatter->getLocale()); + $this->assertEquals('pt_BR', $formatter->getLocale(Locale::VALID_LOCALE)); } public function testGetPattern() From 6672c3901ae0fef09e30593847a326d453d6cac3 Mon Sep 17 00:00:00 2001 From: Eriksen Costa Date: Wed, 26 Jan 2011 01:10:36 -0200 Subject: [PATCH 07/32] [Locale] added SimpleNumberFormatter class (not finished) --- .../Locale/SimpleNumberFormatter.php | 170 ++++++++++++++++++ .../Locale/SimpleNumberFormatterTest.php | 49 +++++ 2 files changed, 219 insertions(+) create mode 100644 src/Symfony/Component/Locale/SimpleNumberFormatter.php create mode 100644 tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php diff --git a/src/Symfony/Component/Locale/SimpleNumberFormatter.php b/src/Symfony/Component/Locale/SimpleNumberFormatter.php new file mode 100644 index 0000000000000..548e6d65dadae --- /dev/null +++ b/src/Symfony/Component/Locale/SimpleNumberFormatter.php @@ -0,0 +1,170 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Locale; + +use Symfony\Component\Locale\Locale; +use Symfony\Component\Locale\NumberFormatterInterface; + +/** + * Provides a simple NumberFormatter for the 'en' locale. + */ +class SimpleNumberFormatter implements NumberFormatterInterface +{ + private $formatter = null; + + /** + * @see http://source.icu-project.org/repos/icu/icu/trunk/source/data/curr/en.txt + */ + private $currencies = array( + 'ALL' => array('0x410x4c0x4c', '%.0f'), + 'BRL' => array('0x520x24', '%.2f'), + 'CRC' => array('0xe20x820xa1', '%.0f') + ); + + /** + * @{inheritDoc} + */ + public function __construct($locale = 'en', $style = null, $pattern = null) + { + } + + /** + * @{inheritDoc} + */ + public function formatCurrency($value, $currency) + { + $symbol = ''; + $hexSymbol = $this->currencies[$currency][0]; + $format = $this->currencies[$currency][1]; + + $hex = explode('0x', $hexSymbol); + unset($hex[0]); + + foreach ($hex as $h) { + $symbol .= chr(hexdec($h)); + } + + return sprintf('%s'.$format, $symbol, $value); + } + + /** + * @{inheritDoc} + */ + public function format($value, $type = null) + { + + } + + /** + * @{inheritDoc} + */ + public function getAttribute($attr) + { + + } + + /** + * @{inheritDoc} + */ + public function getErrorCode() + { + + } + + /** + * @{inheritDoc} + */ + public function getErrorMessage() + { + + } + + /** + * @{inheritDoc} + */ + public function getLocale($type = Locale::ACTUAL_LOCALE) + { + + } + + /** + * @{inheritDoc} + */ + public function getPattern() + { + + } + + /** + * @{inheritDoc} + */ + public function getSymbol($attr) + { + + } + + /** + * @{inheritDoc} + */ + public function getTextAttribute($attr) + { + + } + + /** + * @{inheritDoc} + */ + public function parseCurrency($value, &$currency, &$position = null) + { + + } + + /** + * @{inheritDoc} + */ + public function parse($value, $type = self::TYPE_DOUBLE, &$position = null) + { + + } + + /** + * @{inheritDoc} + */ + public function setAttribute($attr, $value) + { + + } + + /** + * @{inheritDoc} + */ + public function setPattern($pattern) + { + + } + + /** + * @{inheritDoc} + */ + public function setSymbol($attr, $value) + { + + } + + /** + * @{inheritDoc} + */ + public function setTextAttribute($attr, $value) + { + + } +} diff --git a/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php b/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php new file mode 100644 index 0000000000000..12e55973b7093 --- /dev/null +++ b/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Tests\Component\Locale; + +use Symfony\Component\Locale\Locale; +use Symfony\Component\Locale\SimpleNumberFormatter; + +class SimpleNumberFormatterTest extends \PHPUnit_Framework_TestCase +{ + private $formatter = null; + + public function setUp() + { + $this->formatter = new SimpleNumberFormatter(); + } + + /** + * @dataProvider formatCurrencyProvider + */ + public function testFormatCurrency($value, $currency, $expected) + { + // just for testing purposes + $f = new \NumberFormatter('en', \NumberFormatter::CURRENCY); + + $this->assertEquals( + //$expected, + $f->formatCurrency($value, $currency), + $this->formatter->formatCurrency($value, $currency) + ); + } + + public function formatCurrencyProvider() + { + return array( + array(100, 'ALL', 'ALL100'), + array(100, 'BRL', 'R$100.00'), + array(100, 'CRC', '₡100') + ); + } +} From d245fb8edc575173dc16aee162f60f2fc2bf5d5f Mon Sep 17 00:00:00 2001 From: Eriksen Costa Date: Sat, 29 Jan 2011 21:56:54 -0200 Subject: [PATCH 08/32] [Locale] partialy implemented \NumberFormatter's format() and formatCurrency() methods in PHP --- .../Locale/SimpleNumberFormatter.php | 226 ++++++++++++++++-- .../Locale/SimpleNumberFormatterTest.php | 106 +++++++- 2 files changed, 305 insertions(+), 27 deletions(-) diff --git a/src/Symfony/Component/Locale/SimpleNumberFormatter.php b/src/Symfony/Component/Locale/SimpleNumberFormatter.php index 548e6d65dadae..6917b2f0aed69 100644 --- a/src/Symfony/Component/Locale/SimpleNumberFormatter.php +++ b/src/Symfony/Component/Locale/SimpleNumberFormatter.php @@ -19,15 +19,75 @@ */ class SimpleNumberFormatter implements NumberFormatterInterface { - private $formatter = null; + /** + * Default values for the en locale. + */ + private $attributes = array( + self::FRACTION_DIGITS => 0, + self::GROUPING_USED => 1, + self::ROUNDING_MODE => self::ROUND_HALFEVEN + ); + + /** + * The supported styles to the constructor $styles argument. + */ + private static $supportedStyles = array( + 'CURRENCY' => self::CURRENCY, + 'DECIMAL' => self::DECIMAL + ); + + /** + * Supported attributes to the setAttribute() $attr argument. + */ + private static $supportedAttributes = array( + 'FRACTION_DIGITS' => self::FRACTION_DIGITS, + 'GROUPING_USED' => self::GROUPING_USED, + 'ROUNDING_MODE' => self::ROUNDING_MODE + ); /** - * @see http://source.icu-project.org/repos/icu/icu/trunk/source/data/curr/en.txt + * The available rounding modes for setAttribute() usage with + * SimpleNumberFormatter::ROUNDING_MODE. SimpleNumberFormatter::ROUND_DOWN + * and SimpleNumberFormatter::ROUND_UP does not have a PHP only equivalent. + */ + private static $roundingModes = array( + 'ROUND_CEILING' => self::ROUND_CEILING, + 'ROUND_FLOOR' => self::ROUND_FLOOR, + 'ROUND_HALFEVEN' => self::ROUND_HALFEVEN, + 'ROUND_HALFDOWN' => self::ROUND_HALFDOWN, + 'ROUND_HALFUP' => self::ROUND_HALFUP + ); + + /** + * The available values for setAttribute() usage with + * SimpleNumberFormatter::GROUPING_USED. + */ + private static $groupingUsedValues = array(0, 1); + + /** + * The mapping between \NumberFormatter rounding modes to the available + * modes in PHP's round() function. + * + * @see http://www.php.net/manual/en/function.round.php + */ + private static $phpRoundingMap = array( + self::ROUND_HALFDOWN => \PHP_ROUND_HALF_DOWN, + self::ROUND_HALFEVEN => \PHP_ROUND_HALF_EVEN, + self::ROUND_HALFUP => \PHP_ROUND_HALF_UP + ); + + /** + * The currencies symbols. Each array have the symbol definition in + * hexadecimal and the decimal digits. + * + * @see http://source.icu-project.org/repos/icu/icu/trunk/source/data/curr/en.txt + * @todo Move this to Resources/data and use \ResourceBundle to load the data. + * @todo Search in the icu data where the currency subunits (usage of cents) are defined */ private $currencies = array( - 'ALL' => array('0x410x4c0x4c', '%.0f'), - 'BRL' => array('0x520x24', '%.2f'), - 'CRC' => array('0xe20x820xa1', '%.0f') + 'ALL' => array('0x410x4c0x4c', 0), + 'BRL' => array('0x520x24', 2), + 'CRC' => array('0xe20x820xa1', 0) ); /** @@ -35,33 +95,58 @@ class SimpleNumberFormatter implements NumberFormatterInterface */ public function __construct($locale = 'en', $style = null, $pattern = null) { + if ('en' != $locale) { + throw new \InvalidArgumentException('Unsupported $locale value. Only the \'en\' locale is supported. Install the intl extension for full localization capabilities.'); + } + + if (!in_array($style, self::$supportedStyles)) { + throw new \InvalidArgumentException(sprintf( + 'Unsupported $style value. The available styles are: %s. Install the intl extension for full localization capabilities.', + implode(', ', array_keys(self::$supportedStyles)) + )); + } + + if (!is_null($pattern)) { + throw new \InvalidArgumentException('The $pattern value must be null. Install the intl extension for full localization capabilities.'); + } } /** * @{inheritDoc} + * @todo With the default rounding mode (ROUND_HALFEVEN), the currency value + * seems to be correctly rounded. However, since ROUND_CEILING is + * mapping to the ceil() function, the value is being returned. This + * is wrong. */ public function formatCurrency($value, $currency) { - $symbol = ''; - $hexSymbol = $this->currencies[$currency][0]; - $format = $this->currencies[$currency][1]; - - $hex = explode('0x', $hexSymbol); - unset($hex[0]); + $symbol = $this->getCurrencySymbol($currency); + $value = $this->round($value, $this->currencies[$currency][1]); - foreach ($hex as $h) { - $symbol .= chr(hexdec($h)); + $negative = false; + if (0 > $value) { + $negative = true; + $value *= -1; } - return sprintf('%s'.$format, $symbol, $value); + $value = $this->formatNumber($value, $this->currencies[$currency][1]); + + $ret = $symbol.$value; + return $negative ? '('.$ret.')' : $ret; } /** * @{inheritDoc} */ - public function format($value, $type = null) + public function format($value, $type = self::TYPE_DEFAULT) { + if (0 > ($fractionDigits = $this->getAttribute(self::FRACTION_DIGITS))) { + $fractionDigits = 0; + } + // Rounding + $value = $this->round($value, $fractionDigits); + return $this->formatNumber($value, $fractionDigits); } /** @@ -69,7 +154,9 @@ public function format($value, $type = null) */ public function getAttribute($attr) { - + if (isset($this->attributes[$attr])) { + return $this->attributes[$attr]; + } } /** @@ -138,10 +225,30 @@ public function parse($value, $type = self::TYPE_DOUBLE, &$position = null) /** * @{inheritDoc} + * @todo Decide between throwing an exception if ROUDING_MODE or GROUPING_USED are invalid. + * In \NumberFormatter, a true is returned and the format/parse() methods have undefined values + * in these cases. + * @throws InvalidArgumentException When the $attr is not supported */ public function setAttribute($attr, $value) { + if (!in_array($attr, self::$supportedAttributes)) { + throw new \InvalidArgumentException(sprintf( + 'Unsupported $attr value. The available attributes are: %s. Install the intl extension for full localization capabilities.', + implode(', ', array_keys(self::$supportedAttributes)) + )); + } + + if (self::$supportedAttributes['ROUNDING_MODE'] == $attr && $this->isInvalidRoundingMode($value)) { + return false; + } + if (self::$supportedAttributes['GROUPING_USED'] == $attr && $this->isInvalidGroupingUsedValue($value)) { + return false; + } + + $this->attributes[$attr] = $value; + return true; } /** @@ -167,4 +274,91 @@ public function setTextAttribute($attr, $value) { } + + /** + * Returns the currency symbol. + * + * @param string $currency The 3-letter ISO 4217 currency code indicating the currency to use + * @return string The currency symbol + */ + private function getCurrencySymbol($currency) + { + $symbol = ''; + $hexSymbol = $this->currencies[$currency][0]; + $hex = explode('0x', $hexSymbol); + unset($hex[0]); + + foreach ($hex as $h) { + $symbol .= chr(hexdec($h)); + } + + return $symbol; + } + + /** + * Rounds a value. + * + * @param numeric $value The value to round + * @param int $precision The number of decimal digits to round to + * @return numeric The rounded value + */ + private function round($value, $precision) + { + switch ($this->getAttribute(self::ROUNDING_MODE)): + case self::ROUND_CEILING: + $value = ceil($value); + break; + case self::ROUND_FLOOR: + $value = floor($value); + break; + default: + $roundingMode = self::$phpRoundingMap[$this->getAttribute(self::ROUNDING_MODE)]; + $value = round($value, $precision, $roundingMode); + break; + endswitch; + + return $value; + } + + /** + * Formats a number. + * + * @param numeric $value The numeric value to format + * @param int $precision The number of decimal digits to use + * @return string The formatted number + */ + private function formatNumber($value, $precision) + { + return number_format($value, $precision, '.', $this->getAttribute(self::GROUPING_USED) ? ',' : ''); + } + + /** + * Check if the rounding mode is invalid. + * + * @param int $value The rounding mode value to check + * @return bool true if the rounding mode is invalid, false otherwise + */ + private function isInvalidRoundingMode($value) + { + if (in_array($value, self::$roundingModes, true)) { + return false; + } + + return true; + } + + /** + * Check if the grouping value is invalid. + * + * @param int $value The grouping value to check + * @return bool true if the grouping value is invalid, false otherwise + */ + private function isInvalidGroupingUsedValue($value) + { + if (in_array($value, self::$groupingUsedValues, true)) { + return false; + } + + return true; + } } diff --git a/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php b/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php index 12e55973b7093..2da7f6e081d29 100644 --- a/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php +++ b/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php @@ -16,26 +16,100 @@ class SimpleNumberFormatterTest extends \PHPUnit_Framework_TestCase { - private $formatter = null; + /** + * @expectedException InvalidArgumentException + */ + public function testConstructorWithUnsupportedLocale() + { + $formatter = new SimpleNumberFormatter('pt_BR'); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testConstructorWithUnsupportedStyle() + { + $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::PATTERN_DECIMAL); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testConstructorWithPatternDifferentThanNull() + { + $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL, ''); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testSetAttributeWithUnsupportedAttribute() + { + $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $formatter->setAttribute(SimpleNumberFormatter::LENIENT_PARSE, null); + } - public function setUp() + public function testSetAttributeInvalidRoundingMode() { - $this->formatter = new SimpleNumberFormatter(); + $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + + $ret = $formatter->setAttribute(SimpleNumberFormatter::ROUNDING_MODE, null); + $roundingMode = $formatter->getAttribute(SimpleNumberFormatter::ROUNDING_MODE); + + $this->assertFalse($ret); + $this->assertEquals(SimpleNumberFormatter::ROUND_HALFEVEN, $roundingMode); + } + + public function testSetAttributeInvalidGroupingUsedValue() + { + $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + + $ret = $formatter->setAttribute(SimpleNumberFormatter::GROUPING_USED, null); + $groupingUsed = $formatter->getAttribute(SimpleNumberFormatter::GROUPING_USED); + + $this->assertFalse($ret); + $this->assertEquals(SimpleNumberFormatter::GROUPING_USED, $groupingUsed); + } + + public function testFormat() + { + $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + + // Rounds to the next highest integer + $formatter->setAttribute(SimpleNumberFormatter::ROUNDING_MODE, SimpleNumberFormatter::ROUND_CEILING); + $this->assertSame('10', $formatter->format(9.5)); + + // Use the defined fraction digits + $formatter->setAttribute(SimpleNumberFormatter::FRACTION_DIGITS, 2); + $this->assertSame('10.00', $formatter->format(9.5)); + $formatter->setAttribute(SimpleNumberFormatter::FRACTION_DIGITS, -1); + $this->assertSame('10', $formatter->format(9.5)); + + // Set the grouping size + $formatter->setAttribute(SimpleNumberFormatter::ROUNDING_MODE, SimpleNumberFormatter::ROUND_HALFEVEN); + $formatter->setAttribute(SimpleNumberFormatter::FRACTION_DIGITS, 2); + $this->assertSame('9.56', $formatter->format(9.555)); + $this->assertSame('1,000,000.12', $formatter->format(1000000.123)); } /** * @dataProvider formatCurrencyProvider + * @see SimpleNumberFormatter::formatCurrency() + * @todo Test with ROUND_CEILING and ROUND_FLOOR modes */ public function testFormatCurrency($value, $currency, $expected) { - // just for testing purposes - $f = new \NumberFormatter('en', \NumberFormatter::CURRENCY); + $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $this->assertEquals($expected, $formatter->formatCurrency($value, $currency)); - $this->assertEquals( - //$expected, - $f->formatCurrency($value, $currency), - $this->formatter->formatCurrency($value, $currency) - ); + if (extension_loaded('intl')) { + $numberFormatter = new \NumberFormatter('en', \NumberFormatter::CURRENCY); + + $this->assertEquals( + $numberFormatter->formatCurrency($value, $currency), + $formatter->formatCurrency($value, $currency) + ); + } } public function formatCurrencyProvider() @@ -43,7 +117,17 @@ public function formatCurrencyProvider() return array( array(100, 'ALL', 'ALL100'), array(100, 'BRL', 'R$100.00'), - array(100, 'CRC', '₡100') + array(100, 'CRC', '₡100'), + array(-100, 'ALL', '(ALL100)'), + array(-100, 'BRL', '(R$100.00)'), + array(-100, 'CRC', '(₡100)'), + array(1000.12, 'ALL', 'ALL1,000'), + array(1000.12, 'BRL', 'R$1,000.12'), + array(1000.12, 'CRC', '₡1,000'), + // Test with other rounding modes + // array(1000.127, 'ALL', 'ALL1,000'), + // array(1000.127, 'BRL', 'R$1,000.12'), + // array(1000.127, 'CRC', '₡1,000'), ); } } From af499eefff37eb4bbfc30b24825ede9383d6607d Mon Sep 17 00:00:00 2001 From: Eriksen Costa Date: Sun, 30 Jan 2011 03:17:25 -0200 Subject: [PATCH 09/32] [Locale] partial impl of \NumberFormatter::parse() --- .../Locale/SimpleNumberFormatter.php | 57 ++++++++ .../Locale/SimpleNumberFormatterTest.php | 127 +++++++++++++++--- 2 files changed, 163 insertions(+), 21 deletions(-) diff --git a/src/Symfony/Component/Locale/SimpleNumberFormatter.php b/src/Symfony/Component/Locale/SimpleNumberFormatter.php index 6917b2f0aed69..d80b1098bd901 100644 --- a/src/Symfony/Component/Locale/SimpleNumberFormatter.php +++ b/src/Symfony/Component/Locale/SimpleNumberFormatter.php @@ -220,7 +220,64 @@ public function parseCurrency($value, &$currency, &$position = null) */ public function parse($value, $type = self::TYPE_DOUBLE, &$position = null) { + preg_match('/^([^0-9\-]{0,})(.*)/', $value, $matches); + // Any string before the numeric value causes error in the parsing + if (isset($matches[1]) && !empty($matches[1])) { + return false; + } + + // Remove everything that is not number or dot (.) + $value = preg_replace('/[^0-9\.\-]/', '', $value); + + // int 32 + $int32UpperBound = 2147483647; + $int32LowerBound = ($int32UpperBound * -1) - 1; + + // int 64 + $int64UpperBound = 9223372036854775807; + $int64LowerBound = ($int64UpperBound * -1) - 1; + + if ($type == self::TYPE_DEFAULT) { + if (strstr($value, '.') || is_float($value + 0) || is_float($value - 0)) { + $type = self::TYPE_DOUBLE; + } + elseif ($value <= $int32UpperBound && $value >= $int32LowerBound) { + $type = self::TYPE_INT32; + } + elseif ($value <= $int64UpperBound && $value >= $int64LowerBound) { + $type = self::TYPE_INT64; + } + else { + $type = self::TYPE_DOUBLE; + } + } + + if ($type == self::TYPE_DOUBLE) { + $value = (float) $value; + } + elseif ($type == self::TYPE_INT32) { + if ($value > $int32UpperBound) { + $value = $int32UpperBound; + } + elseif ($value < $int32LowerBound) { + $value = $int32LowerBound; + } + + $value = (int) $value; + } + elseif ($type == self::TYPE_INT64) { + if ($value > $int64UpperBound) { + $value = $int64UpperBound; + } + elseif ($value < $int64LowerBound) { + $value = $int64LowerBound; + } + + $value = (int) $value; + } + + return $value; } /** diff --git a/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php b/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php index 2da7f6e081d29..0e506f6aced2c 100644 --- a/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php +++ b/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php @@ -71,27 +71,6 @@ public function testSetAttributeInvalidGroupingUsedValue() $this->assertEquals(SimpleNumberFormatter::GROUPING_USED, $groupingUsed); } - public function testFormat() - { - $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); - - // Rounds to the next highest integer - $formatter->setAttribute(SimpleNumberFormatter::ROUNDING_MODE, SimpleNumberFormatter::ROUND_CEILING); - $this->assertSame('10', $formatter->format(9.5)); - - // Use the defined fraction digits - $formatter->setAttribute(SimpleNumberFormatter::FRACTION_DIGITS, 2); - $this->assertSame('10.00', $formatter->format(9.5)); - $formatter->setAttribute(SimpleNumberFormatter::FRACTION_DIGITS, -1); - $this->assertSame('10', $formatter->format(9.5)); - - // Set the grouping size - $formatter->setAttribute(SimpleNumberFormatter::ROUNDING_MODE, SimpleNumberFormatter::ROUND_HALFEVEN); - $formatter->setAttribute(SimpleNumberFormatter::FRACTION_DIGITS, 2); - $this->assertSame('9.56', $formatter->format(9.555)); - $this->assertSame('1,000,000.12', $formatter->format(1000000.123)); - } - /** * @dataProvider formatCurrencyProvider * @see SimpleNumberFormatter::formatCurrency() @@ -130,4 +109,110 @@ public function formatCurrencyProvider() // array(1000.127, 'CRC', '₡1,000'), ); } + + public function testFormat() + { + $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + + // Rounds to the next highest integer + $formatter->setAttribute(SimpleNumberFormatter::ROUNDING_MODE, SimpleNumberFormatter::ROUND_CEILING); + $this->assertSame('10', $formatter->format(9.5)); + + // Use the defined fraction digits + $formatter->setAttribute(SimpleNumberFormatter::FRACTION_DIGITS, 2); + $this->assertSame('10.00', $formatter->format(9.5)); + $formatter->setAttribute(SimpleNumberFormatter::FRACTION_DIGITS, -1); + $this->assertSame('10', $formatter->format(9.5)); + + // Rounds to the nearest even number + $formatter->setAttribute(SimpleNumberFormatter::ROUNDING_MODE, SimpleNumberFormatter::ROUND_HALFEVEN); + $formatter->setAttribute(SimpleNumberFormatter::FRACTION_DIGITS, 2); + $this->assertSame('9.56', $formatter->format(9.555)); + $this->assertSame('1,000,000.12', $formatter->format(1000000.123)); + + // Don't use number grouping + $formatter->setAttribute(SimpleNumberFormatter::GROUPING_USED, 0); + $this->assertSame('1000000.12', $formatter->format(1000000.123)); + } + + public function testParseValueWithStringInTheBeginning() + { + $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $value = $formatter->parse('R$1,234,567.89', SimpleNumberFormatter::TYPE_DOUBLE); + $this->assertFalse($value); + + $formatter = new \NumberFormatter('en', \NumberFormatter::DECIMAL); + $value = $formatter->parse('R$1,234,567.89', \NumberFormatter::TYPE_DOUBLE); + $this->assertFalse($value); + } + + public function testParseValueWithStringAtTheEnd() + { + $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $value = $formatter->parse('1,234,567.89', SimpleNumberFormatter::TYPE_DOUBLE); + $this->assertEquals(1234567.89, $value); + + $formatter = new \NumberFormatter('en', \NumberFormatter::DECIMAL); + $value = $formatter->parse('1,234,567.89R$', \NumberFormatter::TYPE_DOUBLE); + $this->assertEquals(1234567.89, $value); + } + + public function testParse() + { + $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + + $value = $formatter->parse('9,223,372,036,854,775,808', SimpleNumberFormatter::TYPE_DOUBLE); + $this->assertSame(9223372036854775808, $value); + + $value = $formatter->parse('2,147,483,648', SimpleNumberFormatter::TYPE_INT32); + $this->assertSame(2147483647, $value); + + $value = $formatter->parse('-2,147,483,649', SimpleNumberFormatter::TYPE_INT32); + $this->assertSame(-2147483648, $value); + + $value = $formatter->parse('9,223,372,036,854,775,808', SimpleNumberFormatter::TYPE_INT64); + $this->assertSame(9223372036854775807, $value); + + $value = $formatter->parse('-9,223,372,036,854,775,809', SimpleNumberFormatter::TYPE_INT64); + // Strangely, using -9223372036854775808 directly in code make PHP type juggle the value to float + $this->assertSame(-9223372036854775807 - 1, $value); + } + + public function testParseDetectType() + { + $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $value = $formatter->parse('1', SimpleNumberFormatter::TYPE_DEFAULT); + $this->assertInternalType('integer', $value); + + $value = $formatter->parse('1.1', SimpleNumberFormatter::TYPE_DEFAULT); + $this->assertInternalType('float', $value); + + // int 64 overflow + $value = $formatter->parse('9,223,372,036,854,775,808', SimpleNumberFormatter::TYPE_DEFAULT); + $this->assertInternalType('float', $value); + $this->assertEquals(9223372036854775808, $value); + + $value = $formatter->parse('-9,223,372,036,854,775,809', SimpleNumberFormatter::TYPE_DEFAULT); + $this->assertInternalType('float', $value); + $this->assertEquals(-9223372036854775809, $value); + + // int 32 + $value = $formatter->parse('2,147,483,647', SimpleNumberFormatter::TYPE_DEFAULT); + $this->assertInternalType('integer', $value); + $this->assertSame(2147483647, $value); + + $value = $formatter->parse('-2,147,483,648', SimpleNumberFormatter::TYPE_DEFAULT); + $this->assertInternalType('integer', $value); + $this->assertSame(-2147483648, $value); + + // int 64 + $value = $formatter->parse('9,223,372,036,854,775,807', SimpleNumberFormatter::TYPE_DEFAULT); + $this->assertInternalType('integer', $value); + $this->assertSame(9223372036854775807, $value); + + $value = $formatter->parse('-9,223,372,036,854,775,808', SimpleNumberFormatter::TYPE_DEFAULT); + $this->assertInternalType('integer', $value); + // Strangely, using -9223372036854775808 directly in code make PHP type juggle the value to float + $this->assertSame(-9223372036854775807 - 1, $value); + } } From 8b99817b2482cba8d164e6236a95ff363a43a6d6 Mon Sep 17 00:00:00 2001 From: Eriksen Costa Date: Tue, 1 Feb 2011 22:36:13 -0200 Subject: [PATCH 10/32] [Locale] extracted method --- .../Locale/SimpleNumberFormatter.php | 101 +++++++++--------- 1 file changed, 53 insertions(+), 48 deletions(-) diff --git a/src/Symfony/Component/Locale/SimpleNumberFormatter.php b/src/Symfony/Component/Locale/SimpleNumberFormatter.php index d80b1098bd901..99bc70c38e4cc 100644 --- a/src/Symfony/Component/Locale/SimpleNumberFormatter.php +++ b/src/Symfony/Component/Locale/SimpleNumberFormatter.php @@ -230,54 +230,7 @@ public function parse($value, $type = self::TYPE_DOUBLE, &$position = null) // Remove everything that is not number or dot (.) $value = preg_replace('/[^0-9\.\-]/', '', $value); - // int 32 - $int32UpperBound = 2147483647; - $int32LowerBound = ($int32UpperBound * -1) - 1; - - // int 64 - $int64UpperBound = 9223372036854775807; - $int64LowerBound = ($int64UpperBound * -1) - 1; - - if ($type == self::TYPE_DEFAULT) { - if (strstr($value, '.') || is_float($value + 0) || is_float($value - 0)) { - $type = self::TYPE_DOUBLE; - } - elseif ($value <= $int32UpperBound && $value >= $int32LowerBound) { - $type = self::TYPE_INT32; - } - elseif ($value <= $int64UpperBound && $value >= $int64LowerBound) { - $type = self::TYPE_INT64; - } - else { - $type = self::TYPE_DOUBLE; - } - } - - if ($type == self::TYPE_DOUBLE) { - $value = (float) $value; - } - elseif ($type == self::TYPE_INT32) { - if ($value > $int32UpperBound) { - $value = $int32UpperBound; - } - elseif ($value < $int32LowerBound) { - $value = $int32LowerBound; - } - - $value = (int) $value; - } - elseif ($type == self::TYPE_INT64) { - if ($value > $int64UpperBound) { - $value = $int64UpperBound; - } - elseif ($value < $int64LowerBound) { - $value = $int64LowerBound; - } - - $value = (int) $value; - } - - return $value; + return $this->getNumericValue($value, $type); } /** @@ -418,4 +371,56 @@ private function isInvalidGroupingUsedValue($value) return true; } + + private function getNumericValue($value, $type) + { + // int 32 + $int32UpperBound = 2147483647; + $int32LowerBound = ($int32UpperBound * -1) - 1; + + // int 64 + $int64UpperBound = 9223372036854775807; + $int64LowerBound = ($int64UpperBound * -1) - 1; + + if ($type == self::TYPE_DEFAULT) { + if (strstr($value, '.') || is_float($value + 0) || is_float($value - 0)) { + $type = self::TYPE_DOUBLE; + } + elseif ($value <= $int32UpperBound && $value >= $int32LowerBound) { + $type = self::TYPE_INT32; + } + elseif ($value <= $int64UpperBound && $value >= $int64LowerBound) { + $type = self::TYPE_INT64; + } + else { + $type = self::TYPE_DOUBLE; + } + } + + if ($type == self::TYPE_DOUBLE) { + $value = (float) $value; + } + elseif ($type == self::TYPE_INT32) { + if ($value > $int32UpperBound) { + $value = $int32UpperBound; + } + elseif ($value < $int32LowerBound) { + $value = $int32LowerBound; + } + + $value = (int) $value; + } + elseif ($type == self::TYPE_INT64) { + if ($value > $int64UpperBound) { + $value = $int64UpperBound; + } + elseif ($value < $int64LowerBound) { + $value = $int64LowerBound; + } + + $value = (int) $value; + } + + return $value; + } } From 75b55cab3001e2d45f8e7124ae2135e84dd88e16 Mon Sep 17 00:00:00 2001 From: Eriksen Costa Date: Tue, 1 Feb 2011 23:38:15 -0200 Subject: [PATCH 11/32] [Locale] extract method refactoring --- .../Locale/SimpleNumberFormatter.php | 106 +++++++++++------- 1 file changed, 68 insertions(+), 38 deletions(-) diff --git a/src/Symfony/Component/Locale/SimpleNumberFormatter.php b/src/Symfony/Component/Locale/SimpleNumberFormatter.php index 99bc70c38e4cc..4df71df3b5625 100644 --- a/src/Symfony/Component/Locale/SimpleNumberFormatter.php +++ b/src/Symfony/Component/Locale/SimpleNumberFormatter.php @@ -90,6 +90,20 @@ class SimpleNumberFormatter implements NumberFormatterInterface 'CRC' => array('0xe20x820xa1', 0) ); + /** + * The maximum values of the integer type in 32 and 64 bit platforms. + */ + private static $intValues = array( + self::TYPE_INT32 => array( + 'positive' => 2147483647, + 'negative' => -2147483648 + ), + self::TYPE_INT64 => array( + 'positive' => 9223372036854775807, + 'negative' => -9223372036854775808 + ) + ); + /** * @{inheritDoc} */ @@ -374,53 +388,69 @@ private function isInvalidGroupingUsedValue($value) private function getNumericValue($value, $type) { - // int 32 - $int32UpperBound = 2147483647; - $int32LowerBound = ($int32UpperBound * -1) - 1; - - // int 64 - $int64UpperBound = 9223372036854775807; - $int64LowerBound = ($int64UpperBound * -1) - 1; - - if ($type == self::TYPE_DEFAULT) { - if (strstr($value, '.') || is_float($value + 0) || is_float($value - 0)) { - $type = self::TYPE_DOUBLE; - } - elseif ($value <= $int32UpperBound && $value >= $int32LowerBound) { - $type = self::TYPE_INT32; - } - elseif ($value <= $int64UpperBound && $value >= $int64LowerBound) { - $type = self::TYPE_INT64; - } - else { - $type = self::TYPE_DOUBLE; - } - } + $type = $this->detectNumberType($value, $type); if ($type == self::TYPE_DOUBLE) { $value = (float) $value; } elseif ($type == self::TYPE_INT32) { - if ($value > $int32UpperBound) { - $value = $int32UpperBound; - } - elseif ($value < $int32LowerBound) { - $value = $int32LowerBound; - } - - $value = (int) $value; + $value = $this->getIntValue($type, $value); } elseif ($type == self::TYPE_INT64) { - if ($value > $int64UpperBound) { - $value = $int64UpperBound; - } - elseif ($value < $int64LowerBound) { - $value = $int64LowerBound; - } - - $value = (int) $value; + $value = $this->getIntValue($type, $value); } return $value; } + + private function detectNumberType($value, $type) + { + if ($type != self::TYPE_DEFAULT) { + return $type; + } + + if ($this->isFloat($value)) { + $type = self::TYPE_DOUBLE; + } + elseif ($this->isInt32($value)) { + $type = self::TYPE_INT32; + } + elseif ($this->isInt64($value)) { + $type = self::TYPE_INT64; + } + + return $type; + } + + private function isFloat($value) + { + return strstr($value, '.') || is_float($value + 0) || is_float($value - 0); + } + + private function isInt32($value) + { + return $this->isIntType(self::TYPE_INT32, $value); + } + + private function isInt64($value) + { + return $this->isIntType(self::TYPE_INT64, $value); + } + + private function isIntType($type, $value) + { + return $value <= self::$intValues[$type]['positive'] && $value >= self::$intValues[$type]['negative']; + } + + private function getIntValue($int, $value) + { + if ($value > self::$intValues[$int]['positive']) { + $value = self::$intValues[$int]['positive']; + } + elseif ($value < self::$intValues[$int]['negative']) { + $value = self::$intValues[$int]['negative']; + } + + return (int) $value; + } } From 1b0c671458d7175cb5c8eb82f09bc32fd60a138e Mon Sep 17 00:00:00 2001 From: Eriksen Costa Date: Wed, 2 Feb 2011 00:50:40 -0200 Subject: [PATCH 12/32] [Locale] method throws exception for unsupported argument value --- src/Symfony/Component/Locale/SimpleNumberFormatter.php | 4 ++++ .../Component/Locale/SimpleNumberFormatterTest.php | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/Symfony/Component/Locale/SimpleNumberFormatter.php b/src/Symfony/Component/Locale/SimpleNumberFormatter.php index 4df71df3b5625..bd6788ea1327d 100644 --- a/src/Symfony/Component/Locale/SimpleNumberFormatter.php +++ b/src/Symfony/Component/Locale/SimpleNumberFormatter.php @@ -234,6 +234,10 @@ public function parseCurrency($value, &$currency, &$position = null) */ public function parse($value, $type = self::TYPE_DOUBLE, &$position = null) { + if (!is_null($position)) { + throw new \RuntimeException('$position should be null. Processing based on the $position value is not supported. Install the intl extension for full localization capabilities.'); + } + preg_match('/^([^0-9\-]{0,})(.*)/', $value, $matches); // Any string before the numeric value causes error in the parsing diff --git a/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php b/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php index 0e506f6aced2c..114c261984695 100644 --- a/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php +++ b/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php @@ -215,4 +215,14 @@ public function testParseDetectType() // Strangely, using -9223372036854775808 directly in code make PHP type juggle the value to float $this->assertSame(-9223372036854775807 - 1, $value); } + + /** + * @expectedException RuntimeException + */ + public function testParseWithPositionValue() + { + $position = 1; + $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $formatter->parse('123', SimpleNumberFormatter::TYPE_DEFAULT, $position); + } } From b198a9e0622eb11dce2085527de6b59379138265 Mon Sep 17 00:00:00 2001 From: Eriksen Costa Date: Wed, 2 Feb 2011 00:52:19 -0200 Subject: [PATCH 13/32] [Locale] refactored test code --- .../Locale/SimpleNumberFormatterTest.php | 67 ++++++++++--------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php b/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php index 114c261984695..e9ce61326728e 100644 --- a/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php +++ b/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php @@ -16,6 +16,15 @@ class SimpleNumberFormatterTest extends \PHPUnit_Framework_TestCase { + private static $int64Upper = 9223372036854775807; + + /** + * Strangely, using -9223372036854775808 directly in code make PHP type + * juggle the value to float. Then, use this value with an explicit typecast + * to int, e.g.: (int) self::$int64Lower. + */ + private static $int64Lower = -9223372036854775808; + /** * @expectedException InvalidArgumentException */ @@ -164,56 +173,50 @@ public function testParse() $value = $formatter->parse('9,223,372,036,854,775,808', SimpleNumberFormatter::TYPE_DOUBLE); $this->assertSame(9223372036854775808, $value); + // int 32 $value = $formatter->parse('2,147,483,648', SimpleNumberFormatter::TYPE_INT32); $this->assertSame(2147483647, $value); $value = $formatter->parse('-2,147,483,649', SimpleNumberFormatter::TYPE_INT32); $this->assertSame(-2147483648, $value); + // int 64 $value = $formatter->parse('9,223,372,036,854,775,808', SimpleNumberFormatter::TYPE_INT64); $this->assertSame(9223372036854775807, $value); $value = $formatter->parse('-9,223,372,036,854,775,809', SimpleNumberFormatter::TYPE_INT64); - // Strangely, using -9223372036854775808 directly in code make PHP type juggle the value to float - $this->assertSame(-9223372036854775807 - 1, $value); + $this->assertSame((int) self::$int64Lower, $value); } - public function testParseDetectType() + /** + * @dataProvider parseDetectTypeProvider + */ + public function testParseDetectType($parseValue, $expectedType, $expectedValue) { $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); - $value = $formatter->parse('1', SimpleNumberFormatter::TYPE_DEFAULT); - $this->assertInternalType('integer', $value); - - $value = $formatter->parse('1.1', SimpleNumberFormatter::TYPE_DEFAULT); - $this->assertInternalType('float', $value); - - // int 64 overflow - $value = $formatter->parse('9,223,372,036,854,775,808', SimpleNumberFormatter::TYPE_DEFAULT); - $this->assertInternalType('float', $value); - $this->assertEquals(9223372036854775808, $value); - - $value = $formatter->parse('-9,223,372,036,854,775,809', SimpleNumberFormatter::TYPE_DEFAULT); - $this->assertInternalType('float', $value); - $this->assertEquals(-9223372036854775809, $value); + $value = $formatter->parse($parseValue, SimpleNumberFormatter::TYPE_DEFAULT); + $this->assertInternalType($expectedType, $value); + $this->assertSame($expectedValue, $value); + } - // int 32 - $value = $formatter->parse('2,147,483,647', SimpleNumberFormatter::TYPE_DEFAULT); - $this->assertInternalType('integer', $value); - $this->assertSame(2147483647, $value); + public function parseDetectTypeProvider() + { + return array( + array('1', 'integer', 1), + array('1.1', 'float', 1.1), - $value = $formatter->parse('-2,147,483,648', SimpleNumberFormatter::TYPE_DEFAULT); - $this->assertInternalType('integer', $value); - $this->assertSame(-2147483648, $value); + // int 32 + array('2,147,483,647', 'integer', 2147483647), + array('-2,147,483,648', 'integer', -2147483648), - // int 64 - $value = $formatter->parse('9,223,372,036,854,775,807', SimpleNumberFormatter::TYPE_DEFAULT); - $this->assertInternalType('integer', $value); - $this->assertSame(9223372036854775807, $value); + // int 64 + array('9,223,372,036,854,775,807', 'integer', self::$int64Upper), + array('-9,223,372,036,854,775,808', 'integer', (int) self::$int64Lower), - $value = $formatter->parse('-9,223,372,036,854,775,808', SimpleNumberFormatter::TYPE_DEFAULT); - $this->assertInternalType('integer', $value); - // Strangely, using -9223372036854775808 directly in code make PHP type juggle the value to float - $this->assertSame(-9223372036854775807 - 1, $value); + // int 64 overflow + array('9,223,372,036,854,775,808', 'float', 9223372036854775808), + array('-9,223,372,036,854,775,809', 'float', -9223372036854775809) + ); } /** From ff6083fe8dd89fa88deb283e95d3f64300b99635 Mon Sep 17 00:00:00 2001 From: Eriksen Costa Date: Wed, 2 Feb 2011 01:23:10 -0200 Subject: [PATCH 14/32] [Locale] removed support for ceil and floor rounding modes as them does not have a precise equivalent in PHP --- .../Locale/SimpleNumberFormatter.php | 21 ++----------------- .../Locale/SimpleNumberFormatterTest.php | 15 ++++--------- 2 files changed, 6 insertions(+), 30 deletions(-) diff --git a/src/Symfony/Component/Locale/SimpleNumberFormatter.php b/src/Symfony/Component/Locale/SimpleNumberFormatter.php index bd6788ea1327d..6367a7911c093 100644 --- a/src/Symfony/Component/Locale/SimpleNumberFormatter.php +++ b/src/Symfony/Component/Locale/SimpleNumberFormatter.php @@ -51,8 +51,6 @@ class SimpleNumberFormatter implements NumberFormatterInterface * and SimpleNumberFormatter::ROUND_UP does not have a PHP only equivalent. */ private static $roundingModes = array( - 'ROUND_CEILING' => self::ROUND_CEILING, - 'ROUND_FLOOR' => self::ROUND_FLOOR, 'ROUND_HALFEVEN' => self::ROUND_HALFEVEN, 'ROUND_HALFDOWN' => self::ROUND_HALFDOWN, 'ROUND_HALFUP' => self::ROUND_HALFUP @@ -127,10 +125,6 @@ public function __construct($locale = 'en', $style = null, $pattern = null) /** * @{inheritDoc} - * @todo With the default rounding mode (ROUND_HALFEVEN), the currency value - * seems to be correctly rounded. However, since ROUND_CEILING is - * mapping to the ceil() function, the value is being returned. This - * is wrong. */ public function formatCurrency($value, $currency) { @@ -158,7 +152,6 @@ public function format($value, $type = self::TYPE_DEFAULT) $fractionDigits = 0; } - // Rounding $value = $this->round($value, $fractionDigits); return $this->formatNumber($value, $fractionDigits); } @@ -332,18 +325,8 @@ private function getCurrencySymbol($currency) */ private function round($value, $precision) { - switch ($this->getAttribute(self::ROUNDING_MODE)): - case self::ROUND_CEILING: - $value = ceil($value); - break; - case self::ROUND_FLOOR: - $value = floor($value); - break; - default: - $roundingMode = self::$phpRoundingMap[$this->getAttribute(self::ROUNDING_MODE)]; - $value = round($value, $precision, $roundingMode); - break; - endswitch; + $roundingMode = self::$phpRoundingMap[$this->getAttribute(self::ROUNDING_MODE)]; + $value = round($value, $precision, $roundingMode); return $value; } diff --git a/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php b/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php index e9ce61326728e..998878e46400d 100644 --- a/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php +++ b/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php @@ -123,23 +123,16 @@ public function testFormat() { $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); - // Rounds to the next highest integer - $formatter->setAttribute(SimpleNumberFormatter::ROUNDING_MODE, SimpleNumberFormatter::ROUND_CEILING); - $this->assertSame('10', $formatter->format(9.5)); - // Use the defined fraction digits $formatter->setAttribute(SimpleNumberFormatter::FRACTION_DIGITS, 2); - $this->assertSame('10.00', $formatter->format(9.5)); - $formatter->setAttribute(SimpleNumberFormatter::FRACTION_DIGITS, -1); - $this->assertSame('10', $formatter->format(9.5)); - - // Rounds to the nearest even number - $formatter->setAttribute(SimpleNumberFormatter::ROUNDING_MODE, SimpleNumberFormatter::ROUND_HALFEVEN); - $formatter->setAttribute(SimpleNumberFormatter::FRACTION_DIGITS, 2); $this->assertSame('9.56', $formatter->format(9.555)); $this->assertSame('1,000,000.12', $formatter->format(1000000.123)); + $formatter->setAttribute(SimpleNumberFormatter::FRACTION_DIGITS, -1); + $this->assertSame('10', $formatter->format(9.5)); + // Don't use number grouping + $formatter->setAttribute(SimpleNumberFormatter::FRACTION_DIGITS, 2); $formatter->setAttribute(SimpleNumberFormatter::GROUPING_USED, 0); $this->assertSame('1000000.12', $formatter->format(1000000.123)); } From 353418ede78a9697bc8af23b484a73fdb192f8f4 Mon Sep 17 00:00:00 2001 From: Eriksen Costa Date: Wed, 2 Feb 2011 01:38:34 -0200 Subject: [PATCH 15/32] [Locale] updated docblock --- src/Symfony/Component/Locale/SimpleNumberFormatter.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Symfony/Component/Locale/SimpleNumberFormatter.php b/src/Symfony/Component/Locale/SimpleNumberFormatter.php index 6367a7911c093..2ffe50341d28c 100644 --- a/src/Symfony/Component/Locale/SimpleNumberFormatter.php +++ b/src/Symfony/Component/Locale/SimpleNumberFormatter.php @@ -79,6 +79,8 @@ class SimpleNumberFormatter implements NumberFormatterInterface * hexadecimal and the decimal digits. * * @see http://source.icu-project.org/repos/icu/icu/trunk/source/data/curr/en.txt + * @see SimpleNumberFormatter::getCurrencySymbol() + * @see SimpleNumberFormatter::formatCurrency() * @todo Move this to Resources/data and use \ResourceBundle to load the data. * @todo Search in the icu data where the currency subunits (usage of cents) are defined */ From 15595570ebfed4a50b8e12ed4b8720046d6f4ba5 Mon Sep 17 00:00:00 2001 From: Eriksen Costa Date: Wed, 2 Feb 2011 01:47:00 -0200 Subject: [PATCH 16/32] [Locale] added implementation to getErrorCode and getErrorMessage methos in SimpleNumberFormatter --- .../Component/Locale/SimpleNumberFormatter.php | 7 +++++-- .../Component/Locale/SimpleNumberFormatterTest.php | 12 ++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Locale/SimpleNumberFormatter.php b/src/Symfony/Component/Locale/SimpleNumberFormatter.php index 2ffe50341d28c..95f363e7bdb1a 100644 --- a/src/Symfony/Component/Locale/SimpleNumberFormatter.php +++ b/src/Symfony/Component/Locale/SimpleNumberFormatter.php @@ -19,6 +19,9 @@ */ class SimpleNumberFormatter implements NumberFormatterInterface { + const U_ZERO_ERROR = 0; + const U_ZERO_ERROR_MESSAGE = 'U_ZERO_ERROR'; + /** * Default values for the en locale. */ @@ -173,7 +176,7 @@ public function getAttribute($attr) */ public function getErrorCode() { - + return self::U_ZERO_ERROR; } /** @@ -181,7 +184,7 @@ public function getErrorCode() */ public function getErrorMessage() { - + return self::U_ZERO_ERROR_MESSAGE; } /** diff --git a/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php b/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php index 998878e46400d..c8a0d085fb75b 100644 --- a/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php +++ b/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php @@ -137,6 +137,18 @@ public function testFormat() $this->assertSame('1000000.12', $formatter->format(1000000.123)); } + public function testGetErrorCode() + { + $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $this->assertEquals(SimpleNumberFormatter::U_ZERO_ERROR, $formatter->getErrorCode()); + } + + public function testGetErrorMessage() + { + $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $this->assertEquals(SimpleNumberFormatter::U_ZERO_ERROR_MESSAGE, $formatter->getErrorMessage()); + } + public function testParseValueWithStringInTheBeginning() { $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); From 33bb0c0b596ab5614ce1e27066eba6976cd09320 Mon Sep 17 00:00:00 2001 From: Eriksen Costa Date: Wed, 2 Feb 2011 02:02:20 -0200 Subject: [PATCH 17/32] [Locale] not implemented methods throws RuntimeException --- .../Locale/SimpleNumberFormatter.php | 22 ++++-- .../Locale/SimpleNumberFormatterTest.php | 75 +++++++++++++++++++ 2 files changed, 89 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Component/Locale/SimpleNumberFormatter.php b/src/Symfony/Component/Locale/SimpleNumberFormatter.php index 95f363e7bdb1a..35ca602fbedab 100644 --- a/src/Symfony/Component/Locale/SimpleNumberFormatter.php +++ b/src/Symfony/Component/Locale/SimpleNumberFormatter.php @@ -192,7 +192,7 @@ public function getErrorMessage() */ public function getLocale($type = Locale::ACTUAL_LOCALE) { - + $this->throwMethodNotImplementException(__METHOD__); } /** @@ -200,7 +200,7 @@ public function getLocale($type = Locale::ACTUAL_LOCALE) */ public function getPattern() { - + $this->throwMethodNotImplementException(__METHOD__); } /** @@ -208,7 +208,7 @@ public function getPattern() */ public function getSymbol($attr) { - + $this->throwMethodNotImplementException(__METHOD__); } /** @@ -216,7 +216,7 @@ public function getSymbol($attr) */ public function getTextAttribute($attr) { - + $this->throwMethodNotImplementException(__METHOD__); } /** @@ -224,7 +224,7 @@ public function getTextAttribute($attr) */ public function parseCurrency($value, &$currency, &$position = null) { - + $this->throwMethodNotImplementException(__METHOD__); } /** @@ -282,7 +282,7 @@ public function setAttribute($attr, $value) */ public function setPattern($pattern) { - + $this->throwMethodNotImplementException(__METHOD__); } /** @@ -290,7 +290,7 @@ public function setPattern($pattern) */ public function setSymbol($attr, $value) { - + $this->throwMethodNotImplementException(__METHOD__); } /** @@ -298,7 +298,7 @@ public function setSymbol($attr, $value) */ public function setTextAttribute($attr, $value) { - + $this->throwMethodNotImplementException(__METHOD__); } /** @@ -445,4 +445,10 @@ private function getIntValue($int, $value) return (int) $value; } + + private function throwMethodNotImplementException($methodName) + { + $message = sprintf('The %s::%s() is not implemented. Install the intl extension for full localization capabilities.', __CLASS__, $methodName); + throw new \RuntimeException($message); + } } diff --git a/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php b/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php index c8a0d085fb75b..eac606f41f794 100644 --- a/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php +++ b/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php @@ -58,6 +58,9 @@ public function testSetAttributeWithUnsupportedAttribute() $formatter->setAttribute(SimpleNumberFormatter::LENIENT_PARSE, null); } + /** + * @covers Symfony\Component\Locale\SimpleNumberFormatter::getAttribute + */ public function testSetAttributeInvalidRoundingMode() { $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); @@ -149,6 +152,51 @@ public function testGetErrorMessage() $this->assertEquals(SimpleNumberFormatter::U_ZERO_ERROR_MESSAGE, $formatter->getErrorMessage()); } + /** + * @expectedException RuntimeException + */ + public function testGetLocale() + { + $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $formatter->getLocale(); + } + + /** + * @expectedException RuntimeException + */ + public function testGetPattern() + { + $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $formatter->getPattern(); + } + + /** + * @expectedException RuntimeException + */ + public function testGetSymbol() + { + $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $formatter->getSymbol(null); + } + + /** + * @expectedException RuntimeException + */ + public function testGetTextAttribute() + { + $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $formatter->getTextAttribute(null); + } + + /** + * @expectedException RuntimeException + */ + public function testParseCurrency() + { + $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $formatter->parseCurrency(null, $currency); + } + public function testParseValueWithStringInTheBeginning() { $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); @@ -233,4 +281,31 @@ public function testParseWithPositionValue() $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); $formatter->parse('123', SimpleNumberFormatter::TYPE_DEFAULT, $position); } + + /** + * @expectedException RuntimeException + */ + public function testSetPattern() + { + $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $formatter->setPattern(null); + } + + /** + * @expectedException RuntimeException + */ + public function testSetSymbol() + { + $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $formatter->setSymbol(null, null); + } + + /** + * @expectedException RuntimeException + */ + public function testSetTextAttribute() + { + $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $formatter->setTextAttribute(null, null); + } } From cceeefd1840366880f019e08eb9136953b1a31c1 Mon Sep 17 00:00:00 2001 From: Eriksen Costa Date: Sat, 5 Feb 2011 12:20:57 -0200 Subject: [PATCH 18/32] [Locale] Removed NumberFormatterInterface and NumberFormatter wrapper class. --- .../Component/Locale/NumberFormatter.php | 151 ----------- .../Locale/NumberFormatterInterface.php | 245 ------------------ .../Locale/SimpleNumberFormatter.php | 90 ++++++- .../Component/Locale/NumberFormatterTest.php | 152 ----------- 4 files changed, 88 insertions(+), 550 deletions(-) delete mode 100644 src/Symfony/Component/Locale/NumberFormatter.php delete mode 100644 src/Symfony/Component/Locale/NumberFormatterInterface.php delete mode 100644 tests/Symfony/Tests/Component/Locale/NumberFormatterTest.php diff --git a/src/Symfony/Component/Locale/NumberFormatter.php b/src/Symfony/Component/Locale/NumberFormatter.php deleted file mode 100644 index 59ba7315104e9..0000000000000 --- a/src/Symfony/Component/Locale/NumberFormatter.php +++ /dev/null @@ -1,151 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Locale; - -use Symfony\Component\Locale\Locale; -use Symfony\Component\Locale\NumberFormatterInterface; - -/** - * Provides a NumberFormatter using the related intl class capabilities. - */ -class NumberFormatter implements NumberFormatterInterface -{ - private $formatter = null; - - /** - * @{inheritDoc} - */ - public function __construct($locale, $style, $pattern = null) - { - $this->formatter = new \NumberFormatter($locale, $style, $pattern); - } - - /** - * @{inheritDoc} - */ - public function formatCurrency($value, $currency) - { - return $this->formatter->formatCurrency($value, $currency); - } - - /** - * @{inheritDoc} - */ - public function format($value, $type = null) - { - return $this->formatter->format($value, $type); - } - - /** - * @{inheritDoc} - */ - public function getAttribute($attr) - { - return $this->formatter->getAttribute($attr); - } - - /** - * @{inheritDoc} - */ - public function getErrorCode() - { - return $this->formatter->getErrorCode(); - } - - /** - * @{inheritDoc} - */ - public function getErrorMessage() - { - return $this->formatter->getErrorMessage(); - } - - /** - * @{inheritDoc} - */ - public function getLocale($type = Locale::ACTUAL_LOCALE) - { - return $this->formatter->getLocale($type); - } - - /** - * @{inheritDoc} - */ - public function getPattern() - { - return $this->formatter->getPattern(); - } - - /** - * @{inheritDoc} - */ - public function getSymbol($attr) - { - return $this->formatter->getSymbol($attr); - } - - /** - * @{inheritDoc} - */ - public function getTextAttribute($attr) - { - return $this->formatter->getTextAttribute($attr); - } - - /** - * @{inheritDoc} - */ - public function parseCurrency($value, &$currency, &$position = null) - { - return $this->formatter->parseCurrency($value, $currency, $position); - } - - /** - * @{inheritDoc} - */ - public function parse($value, $type = self::TYPE_DOUBLE, &$position = null) - { - return $this->formatter->parse($value, $type, $position); - } - - /** - * @{inheritDoc} - */ - public function setAttribute($attr, $value) - { - return $this->formatter->setAttribute($attr, $value); - } - - /** - * @{inheritDoc} - */ - public function setPattern($pattern) - { - return $this->formatter->setPattern($pattern); - } - - /** - * @{inheritDoc} - */ - public function setSymbol($attr, $value) - { - return $this->formatter->setSymbol($attr, $value); - } - - /** - * @{inheritDoc} - */ - public function setTextAttribute($attr, $value) - { - return $this->formatter->setTextAttribute($attr, $value); - } -} diff --git a/src/Symfony/Component/Locale/NumberFormatterInterface.php b/src/Symfony/Component/Locale/NumberFormatterInterface.php deleted file mode 100644 index 06e4f3ded1d46..0000000000000 --- a/src/Symfony/Component/Locale/NumberFormatterInterface.php +++ /dev/null @@ -1,245 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Locale; - -use Symfony\Component\Locale\Locale; - -interface NumberFormatterInterface -{ - /** Format style constants */ - const PATTERN_DECIMAL = 0; - const DECIMAL = 1; - const CURRENCY = 2; - const PERCENT = 3; - const SCIENTIFIC = 4; - const SPELLOUT = 5; - const ORDINAL = 6; - const DURATION = 7; - const PATTERN_RULEBASED = 9; - const IGNORE = 0; - const DEFAULT_STYLE = 1; - - /** Format type constants */ - const TYPE_DEFAULT = 0; - const TYPE_INT32 = 1; - const TYPE_INT64 = 2; - const TYPE_DOUBLE = 3; - const TYPE_CURRENCY = 4; - - /** Numeric attribute constants */ - const PARSE_INT_ONLY = 0; - const GROUPING_USED = 1; - const DECIMAL_ALWAYS_SHOWN = 2; - const MAX_INTEGER_DIGITS = 3; - const MIN_INTEGER_DIGITS = 4; - const INTEGER_DIGITS = 5; - const MAX_FRACTION_DIGITS = 6; - const MIN_FRACTION_DIGITS = 7; - const FRACTION_DIGITS = 8; - const MULTIPLIER = 9; - const GROUPING_SIZE = 10; - const ROUNDING_MODE = 11; - const ROUNDING_INCREMENT = 12; - const FORMAT_WIDTH = 13; - const PADDING_POSITION = 14; - const SECONDARY_GROUPING_SIZE = 15; - const SIGNIFICANT_DIGITS_USED = 16; - const MIN_SIGNIFICANT_DIGITS = 17; - const MAX_SIGNIFICANT_DIGITS = 18; - const LENIENT_PARSE = 19; - - /** Text attribute constants */ - const POSITIVE_PREFIX = 0; - const POSITIVE_SUFFIX = 1; - const NEGATIVE_PREFIX = 2; - const NEGATIVE_SUFFIX = 3; - const PADDING_CHARACTER = 4; - const CURRENCY_CODE = 5; - const DEFAULT_RULESET = 6; - const PUBLIC_RULESETS = 7; - - /** Format symbol constants */ - const DECIMAL_SEPARATOR_SYMBOL = 0; - const GROUPING_SEPARATOR_SYMBOL = 1; - const PATTERN_SEPARATOR_SYMBOL = 2; - const PERCENT_SYMBOL = 3; - const ZERO_DIGIT_SYMBOL = 4; - const DIGIT_SYMBOL = 5; - const MINUS_SIGN_SYMBOL = 6; - const PLUS_SIGN_SYMBOL = 7; - const CURRENCY_SYMBOL = 8; - const INTL_CURRENCY_SYMBOL = 9; - const MONETARY_SEPARATOR_SYMBOL = 10; - const EXPONENTIAL_SYMBOL = 11; - const PERMILL_SYMBOL = 12; - const PAD_ESCAPE_SYMBOL = 13; - const INFINITY_SYMBOL = 14; - const NAN_SYMBOL = 15; - const SIGNIFICANT_DIGIT_SYMBOL = 16; - const MONETARY_GROUPING_SEPARATOR_SYMBOL = 17; - - /** Rounding mode values used by NumberFormatter::setAttribute() with NumberFormatter::ROUNDING_MODE attribute */ - const ROUND_CEILING = 0; - const ROUND_FLOOR = 1; - const ROUND_DOWN = 2; - const ROUND_UP = 3; - const ROUND_HALFEVEN = 4; - const ROUND_HALFDOWN = 5; - const ROUND_HALFUP = 6; - - /** Pad position values used by NumberFormatter::setAttribute() with NumberFormatter::PADDING_POSITION attribute */ - const PAD_BEFORE_PREFIX = 0; - const PAD_AFTER_PREFIX = 1; - const PAD_BEFORE_SUFFIX = 2; - const PAD_AFTER_SUFFIX = 3; - - /** - * Constructor - * - * @param string $locale The locale code - * @param int $style Style of the formatting, one of the format style constants - * @param string $pattern 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 http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details - * @see http://www.icu-project.org/apiref/icu4c/classRuleBasedNumberFormat.html#_details - */ - function __construct($locale, $style, $pattern = null); - - /** - * Format a currency value - * - * @param float $value The numeric currency value - * @param string $currency The 3-letter ISO 4217 currency code indicating the currency to use - * @return string The formatted currency value - * @see http://www.iso.org/iso/support/faqs/faqs_widely_used_standards/widely_used_standards_other/currency_codes/currency_codes_list-1.htm - */ - function formatCurrency($value, $currency); - - /** - * Format a number - * - * @param number $value The value to format - * @param int $type Type of the formatting, one of the format type constants - * @return bool|string The formatted value or false on error - */ - function format($value, $type = null); - - /** - * Returns an attribute value - * - * @param int $attr An attribute specifier, one of the numeric attribute constants - * @return bool|int The attribute value on success or false on error - */ - function getAttribute($attr); - - /** - * Returns formatter's last error code - * - * @return int The error code from last formatter call - */ - function getErrorCode(); - - /** - * Returns formatter's last error message - * - * @return string The error message from last formatter call - */ - function getErrorMessage(); - - /** - * Returns the formatter's locale - * - * @param int $type The locale name type to return between valid or actual (Locale::VALID_LOCALE or Locale::ACTUAL_LOCALE, respectively) - * @return string The locale name used to create the formatter - */ - function getLocale($type = Locale::ACTUAL_LOCALE); - - /** - * Returns the formatter's pattern - * - * @return bool|string The pattern string used by the formatter or false on error - */ - function getPattern(); - - /** - * Returns a formatter symbol value - * - * @param int $attr A symbol specifier, one of the format symbol constants - * @return bool|string The symbol value or false on error - */ - function getSymbol($attr); - - /** - * Returns a formatter text attribute value - * - * @param int $attr An attribute specifier, one of the text attribute constants - * @return bool|string The attribute value or false on error - */ - function getTextAttribute($attr); - - /** - * Parse a currency number - * - * @param string $value The value to parse - * @param string $currency Parameter to receive the currency name (reference) - * @param int $position Offset to begin the parsing on return this value will hold the offset at which the parsing ended - * @return bool|string The parsed numeric value of false on error - */ - function parseCurrency($value, &$currency, &$position = null); - - /** - * Parse a number - * - * @param string $value The value to parse - * @param string $type Type of the formatting, one of the format type constants. NumberFormatter::TYPE_DOUBLE by default - * @param int $position Offset to begin the parsing on return this value will hold the offset at which the parsing ended - * @return bool|string The parsed value of false on error - */ - function parse($value, $type = self::TYPE_DOUBLE, &$position = null); - - /** - * Set an attribute - * - * @param int $attr An attribute specifier, one of the numeric attribute constants - * @param int $value The attribute value - * @return bool true on success or false on failure - */ - function setAttribute($attr, $value); - - /** - * Set the formatter's pattern - * - * @param string $pattern A pattern string in conformance with the ICU DecimalFormat documentation - * @return bool true on success or false on failure - * @see http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details - */ - function setPattern($pattern); - - /** - * Set the formatter's symbol - * - * @param int $attr A symbol specifier, one of the format symbol constants - * @param string $value The value for the symbol - * @return bool true on success or false on failure - */ - function setSymbol($attr, $value); - - /** - * Set a text attribute - * - * @param int $attr An attribute specifier, one of the text attribute constants - * @param int $value The attribute value - * @return bool true on success or false on failure - */ - function setTextAttribute($attr, $value); -} diff --git a/src/Symfony/Component/Locale/SimpleNumberFormatter.php b/src/Symfony/Component/Locale/SimpleNumberFormatter.php index 35ca602fbedab..fd0bc2151b8ac 100644 --- a/src/Symfony/Component/Locale/SimpleNumberFormatter.php +++ b/src/Symfony/Component/Locale/SimpleNumberFormatter.php @@ -12,16 +12,102 @@ namespace Symfony\Component\Locale; use Symfony\Component\Locale\Locale; -use Symfony\Component\Locale\NumberFormatterInterface; /** * Provides a simple NumberFormatter for the 'en' locale. */ -class SimpleNumberFormatter implements NumberFormatterInterface +class SimpleNumberFormatter { const U_ZERO_ERROR = 0; const U_ZERO_ERROR_MESSAGE = 'U_ZERO_ERROR'; + /** Format style constants */ + const PATTERN_DECIMAL = 0; + const DECIMAL = 1; + const CURRENCY = 2; + const PERCENT = 3; + const SCIENTIFIC = 4; + const SPELLOUT = 5; + const ORDINAL = 6; + const DURATION = 7; + const PATTERN_RULEBASED = 9; + const IGNORE = 0; + const DEFAULT_STYLE = 1; + + /** Format type constants */ + const TYPE_DEFAULT = 0; + const TYPE_INT32 = 1; + const TYPE_INT64 = 2; + const TYPE_DOUBLE = 3; + const TYPE_CURRENCY = 4; + + /** Numeric attribute constants */ + const PARSE_INT_ONLY = 0; + const GROUPING_USED = 1; + const DECIMAL_ALWAYS_SHOWN = 2; + const MAX_INTEGER_DIGITS = 3; + const MIN_INTEGER_DIGITS = 4; + const INTEGER_DIGITS = 5; + const MAX_FRACTION_DIGITS = 6; + const MIN_FRACTION_DIGITS = 7; + const FRACTION_DIGITS = 8; + const MULTIPLIER = 9; + const GROUPING_SIZE = 10; + const ROUNDING_MODE = 11; + const ROUNDING_INCREMENT = 12; + const FORMAT_WIDTH = 13; + const PADDING_POSITION = 14; + const SECONDARY_GROUPING_SIZE = 15; + const SIGNIFICANT_DIGITS_USED = 16; + const MIN_SIGNIFICANT_DIGITS = 17; + const MAX_SIGNIFICANT_DIGITS = 18; + const LENIENT_PARSE = 19; + + /** Text attribute constants */ + const POSITIVE_PREFIX = 0; + const POSITIVE_SUFFIX = 1; + const NEGATIVE_PREFIX = 2; + const NEGATIVE_SUFFIX = 3; + const PADDING_CHARACTER = 4; + const CURRENCY_CODE = 5; + const DEFAULT_RULESET = 6; + const PUBLIC_RULESETS = 7; + + /** Format symbol constants */ + const DECIMAL_SEPARATOR_SYMBOL = 0; + const GROUPING_SEPARATOR_SYMBOL = 1; + const PATTERN_SEPARATOR_SYMBOL = 2; + const PERCENT_SYMBOL = 3; + const ZERO_DIGIT_SYMBOL = 4; + const DIGIT_SYMBOL = 5; + const MINUS_SIGN_SYMBOL = 6; + const PLUS_SIGN_SYMBOL = 7; + const CURRENCY_SYMBOL = 8; + const INTL_CURRENCY_SYMBOL = 9; + const MONETARY_SEPARATOR_SYMBOL = 10; + const EXPONENTIAL_SYMBOL = 11; + const PERMILL_SYMBOL = 12; + const PAD_ESCAPE_SYMBOL = 13; + const INFINITY_SYMBOL = 14; + const NAN_SYMBOL = 15; + const SIGNIFICANT_DIGIT_SYMBOL = 16; + const MONETARY_GROUPING_SEPARATOR_SYMBOL = 17; + + /** Rounding mode values used by NumberFormatter::setAttribute() with NumberFormatter::ROUNDING_MODE attribute */ + const ROUND_CEILING = 0; + const ROUND_FLOOR = 1; + const ROUND_DOWN = 2; + const ROUND_UP = 3; + const ROUND_HALFEVEN = 4; + const ROUND_HALFDOWN = 5; + const ROUND_HALFUP = 6; + + /** Pad position values used by NumberFormatter::setAttribute() with NumberFormatter::PADDING_POSITION attribute */ + const PAD_BEFORE_PREFIX = 0; + const PAD_AFTER_PREFIX = 1; + const PAD_BEFORE_SUFFIX = 2; + const PAD_AFTER_SUFFIX = 3; + /** * Default values for the en locale. */ diff --git a/tests/Symfony/Tests/Component/Locale/NumberFormatterTest.php b/tests/Symfony/Tests/Component/Locale/NumberFormatterTest.php deleted file mode 100644 index d01ffef890551..0000000000000 --- a/tests/Symfony/Tests/Component/Locale/NumberFormatterTest.php +++ /dev/null @@ -1,152 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Tests\Component\Locale; - -use Symfony\Component\Locale\Locale; -use Symfony\Component\Locale\NumberFormatter; - -class NumberFormatterTest extends \PHPUnit_Framework_TestCase -{ - public function setUp() - { - if (!extension_loaded('intl')) { - $this->markTestSkipped('The intl extension is not available.'); - } - } - - public function testConstructor() - { - $formatter = $this->createFormatter(); - $this->assertInstanceOf('Symfony\Component\Locale\NumberFormatter', $formatter); - } - - public function testFormatCurrency() - { - $formatter = $this->createFormatter(NumberFormatter::CURRENCY); - $this->assertEquals('R$1.000,00', $formatter->formatCurrency(1000, 'BRL')); - } - - public function testFormat() - { - $formatter = $this->createFormatter(); - $this->assertEquals('1.000', $formatter->format(1000)); - } - - public function testGetAttribute() - { - $formatter = $this->createFormatter(); - $this->assertEquals(3, $formatter->getAttribute(NumberFormatter::MAX_FRACTION_DIGITS)); - } - - public function testGetErrorCode() - { - $formatter = $this->createFormatter(); - $this->assertInternalType('int', $formatter->getErrorCode()); - - // It's strange but as NumberFormat::DEFAULT_STYLE have the same value - // that NumberFormat::DECIMAL, this warning is triggered. The same - // applies to getErrorMessage(). - // http://icu-project.org/apiref/icu4c/unum_8h.html - $this->assertEquals(-127, $formatter->getErrorCode()); - } - - public function testGetErrorMessage() - { - $formatter = $this->createFormatter(); - $this->assertInternalType('string', $formatter->getErrorMessage()); - $this->assertEquals('U_USING_DEFAULT_WARNING', $formatter->getErrorMessage()); - } - - /** - * @todo Update Locale class used (use the class from Locale component) - */ - public function testGetLocale() - { - $formatter = $this->createFormatter(); - $this->assertEquals('pt', $formatter->getLocale()); - $this->assertEquals('pt_BR', $formatter->getLocale(Locale::VALID_LOCALE)); - } - - public function testGetPattern() - { - $formatter = $this->createFormatter(); - $this->assertEquals('#,##0.###', $formatter->getPattern()); - } - - public function testGetSymbol() - { - $formatter = $this->createFormatter(); - $this->assertEquals('R$', $formatter->getSymbol(NumberFormatter::CURRENCY_SYMBOL)); - } - - public function testGetTextAttribute() - { - $formatter = $this->createFormatter(); - $this->assertEquals('-', $formatter->getTextAttribute(NumberFormatter::NEGATIVE_PREFIX)); - } - - public function testParseCurrency() - { - $formatter = $this->createFormatter(NumberFormatter::CURRENCY); - - $position = 0; - $value = $formatter->parseCurrency('(US$1.000,00)', $currency, $position); - - $this->assertEquals(-1000, $value); - $this->assertEquals('USD', $currency); - $this->assertEquals(13, $position); - } - - public function testParse() - { - $formatter = $this->createFormatter(); - - $position = 0; - $value = $formatter->parse('1.000,00', NumberFormatter::TYPE_DOUBLE, $position); - - $this->assertEquals(1000.00, $value); - $this->assertEquals(8, $position); - } - - public function testSetAttribute() - { - $formatter = $this->createFormatter(); - $this->assertTrue($formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 2)); - $this->assertEquals(2, $formatter->getAttribute(NumberFormatter::MAX_FRACTION_DIGITS)); - } - - public function testSetPattern() - { - $formatter = $this->createFormatter(); - $this->assertTrue($formatter->setPattern('#,##0.###')); - $this->assertEquals('#,##0.###', $formatter->getPattern()); - } - - public function testSetSymbol() - { - $formatter = $this->createFormatter(); - $this->assertTrue($formatter->setSymbol(NumberFormatter::CURRENCY_SYMBOL, 'BRL')); - $this->assertEquals('BRL', $formatter->getSymbol(NumberFormatter::CURRENCY_SYMBOL)); - } - - public function testSetTextAttribute() - { - $formatter = $this->createFormatter(); - $this->assertTrue($formatter->setTextAttribute(NumberFormatter::POSITIVE_PREFIX, '+')); - $this->assertEquals('+', $formatter->getTextAttribute(NumberFormatter::POSITIVE_PREFIX)); - } - - private function createFormatter($style = NumberFormatter::DECIMAL) - { - return new NumberFormatter('pt_BR', $style); - } -} From 4f024e39950976550ebba328387e0c4e810ab263 Mon Sep 17 00:00:00 2001 From: Eriksen Costa Date: Sat, 5 Feb 2011 12:29:20 -0200 Subject: [PATCH 19/32] [Locale] moved and renamed SimpleNumberFormatter to StubNumberFormatter --- .../StubNumberFormatter.php} | 6 +- .../StubNumberFormatterTest.php} | 100 +++++++++--------- 2 files changed, 53 insertions(+), 53 deletions(-) rename src/Symfony/Component/Locale/{SimpleNumberFormatter.php => Stub/StubNumberFormatter.php} (99%) rename tests/Symfony/Tests/Component/Locale/{SimpleNumberFormatterTest.php => Stub/StubNumberFormatterTest.php} (63%) diff --git a/src/Symfony/Component/Locale/SimpleNumberFormatter.php b/src/Symfony/Component/Locale/Stub/StubNumberFormatter.php similarity index 99% rename from src/Symfony/Component/Locale/SimpleNumberFormatter.php rename to src/Symfony/Component/Locale/Stub/StubNumberFormatter.php index fd0bc2151b8ac..9fd297d3e08a9 100644 --- a/src/Symfony/Component/Locale/SimpleNumberFormatter.php +++ b/src/Symfony/Component/Locale/Stub/StubNumberFormatter.php @@ -9,14 +9,14 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Locale; +namespace Symfony\Component\Locale\Stub; use Symfony\Component\Locale\Locale; /** - * Provides a simple NumberFormatter for the 'en' locale. + * Provides a stub NumberFormatter for the 'en' locale. */ -class SimpleNumberFormatter +class StubNumberFormatter { const U_ZERO_ERROR = 0; const U_ZERO_ERROR_MESSAGE = 'U_ZERO_ERROR'; diff --git a/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php b/tests/Symfony/Tests/Component/Locale/Stub/StubNumberFormatterTest.php similarity index 63% rename from tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php rename to tests/Symfony/Tests/Component/Locale/Stub/StubNumberFormatterTest.php index eac606f41f794..02f288a28c84f 100644 --- a/tests/Symfony/Tests/Component/Locale/SimpleNumberFormatterTest.php +++ b/tests/Symfony/Tests/Component/Locale/Stub/StubNumberFormatterTest.php @@ -9,12 +9,12 @@ * file that was distributed with this source code. */ -namespace Symfony\Tests\Component\Locale; +namespace Symfony\Tests\Component\Locale\Stub; use Symfony\Component\Locale\Locale; -use Symfony\Component\Locale\SimpleNumberFormatter; +use Symfony\Component\Locale\Stub\StubNumberFormatter; -class SimpleNumberFormatterTest extends \PHPUnit_Framework_TestCase +class StubNumberFormatterTest extends \PHPUnit_Framework_TestCase { private static $int64Upper = 9223372036854775807; @@ -30,7 +30,7 @@ class SimpleNumberFormatterTest extends \PHPUnit_Framework_TestCase */ public function testConstructorWithUnsupportedLocale() { - $formatter = new SimpleNumberFormatter('pt_BR'); + $formatter = new StubNumberFormatter('pt_BR'); } /** @@ -38,7 +38,7 @@ public function testConstructorWithUnsupportedLocale() */ public function testConstructorWithUnsupportedStyle() { - $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::PATTERN_DECIMAL); + $formatter = new StubNumberFormatter('en', StubNumberFormatter::PATTERN_DECIMAL); } /** @@ -46,7 +46,7 @@ public function testConstructorWithUnsupportedStyle() */ public function testConstructorWithPatternDifferentThanNull() { - $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL, ''); + $formatter = new StubNumberFormatter('en', StubNumberFormatter::DECIMAL, ''); } /** @@ -54,43 +54,43 @@ public function testConstructorWithPatternDifferentThanNull() */ public function testSetAttributeWithUnsupportedAttribute() { - $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); - $formatter->setAttribute(SimpleNumberFormatter::LENIENT_PARSE, null); + $formatter = new StubNumberFormatter('en', StubNumberFormatter::DECIMAL); + $formatter->setAttribute(StubNumberFormatter::LENIENT_PARSE, null); } /** - * @covers Symfony\Component\Locale\SimpleNumberFormatter::getAttribute + * @covers Symfony\Component\Locale\StubNumberFormatter::getAttribute */ public function testSetAttributeInvalidRoundingMode() { - $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $formatter = new StubNumberFormatter('en', StubNumberFormatter::DECIMAL); - $ret = $formatter->setAttribute(SimpleNumberFormatter::ROUNDING_MODE, null); - $roundingMode = $formatter->getAttribute(SimpleNumberFormatter::ROUNDING_MODE); + $ret = $formatter->setAttribute(StubNumberFormatter::ROUNDING_MODE, null); + $roundingMode = $formatter->getAttribute(StubNumberFormatter::ROUNDING_MODE); $this->assertFalse($ret); - $this->assertEquals(SimpleNumberFormatter::ROUND_HALFEVEN, $roundingMode); + $this->assertEquals(StubNumberFormatter::ROUND_HALFEVEN, $roundingMode); } public function testSetAttributeInvalidGroupingUsedValue() { - $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $formatter = new StubNumberFormatter('en', StubNumberFormatter::DECIMAL); - $ret = $formatter->setAttribute(SimpleNumberFormatter::GROUPING_USED, null); - $groupingUsed = $formatter->getAttribute(SimpleNumberFormatter::GROUPING_USED); + $ret = $formatter->setAttribute(StubNumberFormatter::GROUPING_USED, null); + $groupingUsed = $formatter->getAttribute(StubNumberFormatter::GROUPING_USED); $this->assertFalse($ret); - $this->assertEquals(SimpleNumberFormatter::GROUPING_USED, $groupingUsed); + $this->assertEquals(StubNumberFormatter::GROUPING_USED, $groupingUsed); } /** * @dataProvider formatCurrencyProvider - * @see SimpleNumberFormatter::formatCurrency() + * @see StubNumberFormatter::formatCurrency() * @todo Test with ROUND_CEILING and ROUND_FLOOR modes */ public function testFormatCurrency($value, $currency, $expected) { - $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $formatter = new StubNumberFormatter('en', StubNumberFormatter::DECIMAL); $this->assertEquals($expected, $formatter->formatCurrency($value, $currency)); if (extension_loaded('intl')) { @@ -124,32 +124,32 @@ public function formatCurrencyProvider() public function testFormat() { - $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $formatter = new StubNumberFormatter('en', StubNumberFormatter::DECIMAL); // Use the defined fraction digits - $formatter->setAttribute(SimpleNumberFormatter::FRACTION_DIGITS, 2); + $formatter->setAttribute(StubNumberFormatter::FRACTION_DIGITS, 2); $this->assertSame('9.56', $formatter->format(9.555)); $this->assertSame('1,000,000.12', $formatter->format(1000000.123)); - $formatter->setAttribute(SimpleNumberFormatter::FRACTION_DIGITS, -1); + $formatter->setAttribute(StubNumberFormatter::FRACTION_DIGITS, -1); $this->assertSame('10', $formatter->format(9.5)); // Don't use number grouping - $formatter->setAttribute(SimpleNumberFormatter::FRACTION_DIGITS, 2); - $formatter->setAttribute(SimpleNumberFormatter::GROUPING_USED, 0); + $formatter->setAttribute(StubNumberFormatter::FRACTION_DIGITS, 2); + $formatter->setAttribute(StubNumberFormatter::GROUPING_USED, 0); $this->assertSame('1000000.12', $formatter->format(1000000.123)); } public function testGetErrorCode() { - $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); - $this->assertEquals(SimpleNumberFormatter::U_ZERO_ERROR, $formatter->getErrorCode()); + $formatter = new StubNumberFormatter('en', StubNumberFormatter::DECIMAL); + $this->assertEquals(StubNumberFormatter::U_ZERO_ERROR, $formatter->getErrorCode()); } public function testGetErrorMessage() { - $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); - $this->assertEquals(SimpleNumberFormatter::U_ZERO_ERROR_MESSAGE, $formatter->getErrorMessage()); + $formatter = new StubNumberFormatter('en', StubNumberFormatter::DECIMAL); + $this->assertEquals(StubNumberFormatter::U_ZERO_ERROR_MESSAGE, $formatter->getErrorMessage()); } /** @@ -157,7 +157,7 @@ public function testGetErrorMessage() */ public function testGetLocale() { - $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $formatter = new StubNumberFormatter('en', StubNumberFormatter::DECIMAL); $formatter->getLocale(); } @@ -166,7 +166,7 @@ public function testGetLocale() */ public function testGetPattern() { - $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $formatter = new StubNumberFormatter('en', StubNumberFormatter::DECIMAL); $formatter->getPattern(); } @@ -175,7 +175,7 @@ public function testGetPattern() */ public function testGetSymbol() { - $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $formatter = new StubNumberFormatter('en', StubNumberFormatter::DECIMAL); $formatter->getSymbol(null); } @@ -184,7 +184,7 @@ public function testGetSymbol() */ public function testGetTextAttribute() { - $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $formatter = new StubNumberFormatter('en', StubNumberFormatter::DECIMAL); $formatter->getTextAttribute(null); } @@ -193,14 +193,14 @@ public function testGetTextAttribute() */ public function testParseCurrency() { - $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $formatter = new StubNumberFormatter('en', StubNumberFormatter::DECIMAL); $formatter->parseCurrency(null, $currency); } public function testParseValueWithStringInTheBeginning() { - $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); - $value = $formatter->parse('R$1,234,567.89', SimpleNumberFormatter::TYPE_DOUBLE); + $formatter = new StubNumberFormatter('en', StubNumberFormatter::DECIMAL); + $value = $formatter->parse('R$1,234,567.89', StubNumberFormatter::TYPE_DOUBLE); $this->assertFalse($value); $formatter = new \NumberFormatter('en', \NumberFormatter::DECIMAL); @@ -210,8 +210,8 @@ public function testParseValueWithStringInTheBeginning() public function testParseValueWithStringAtTheEnd() { - $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); - $value = $formatter->parse('1,234,567.89', SimpleNumberFormatter::TYPE_DOUBLE); + $formatter = new StubNumberFormatter('en', StubNumberFormatter::DECIMAL); + $value = $formatter->parse('1,234,567.89', StubNumberFormatter::TYPE_DOUBLE); $this->assertEquals(1234567.89, $value); $formatter = new \NumberFormatter('en', \NumberFormatter::DECIMAL); @@ -221,23 +221,23 @@ public function testParseValueWithStringAtTheEnd() public function testParse() { - $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $formatter = new StubNumberFormatter('en', StubNumberFormatter::DECIMAL); - $value = $formatter->parse('9,223,372,036,854,775,808', SimpleNumberFormatter::TYPE_DOUBLE); + $value = $formatter->parse('9,223,372,036,854,775,808', StubNumberFormatter::TYPE_DOUBLE); $this->assertSame(9223372036854775808, $value); // int 32 - $value = $formatter->parse('2,147,483,648', SimpleNumberFormatter::TYPE_INT32); + $value = $formatter->parse('2,147,483,648', StubNumberFormatter::TYPE_INT32); $this->assertSame(2147483647, $value); - $value = $formatter->parse('-2,147,483,649', SimpleNumberFormatter::TYPE_INT32); + $value = $formatter->parse('-2,147,483,649', StubNumberFormatter::TYPE_INT32); $this->assertSame(-2147483648, $value); // int 64 - $value = $formatter->parse('9,223,372,036,854,775,808', SimpleNumberFormatter::TYPE_INT64); + $value = $formatter->parse('9,223,372,036,854,775,808', StubNumberFormatter::TYPE_INT64); $this->assertSame(9223372036854775807, $value); - $value = $formatter->parse('-9,223,372,036,854,775,809', SimpleNumberFormatter::TYPE_INT64); + $value = $formatter->parse('-9,223,372,036,854,775,809', StubNumberFormatter::TYPE_INT64); $this->assertSame((int) self::$int64Lower, $value); } @@ -246,8 +246,8 @@ public function testParse() */ public function testParseDetectType($parseValue, $expectedType, $expectedValue) { - $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); - $value = $formatter->parse($parseValue, SimpleNumberFormatter::TYPE_DEFAULT); + $formatter = new StubNumberFormatter('en', StubNumberFormatter::DECIMAL); + $value = $formatter->parse($parseValue, StubNumberFormatter::TYPE_DEFAULT); $this->assertInternalType($expectedType, $value); $this->assertSame($expectedValue, $value); } @@ -278,8 +278,8 @@ public function parseDetectTypeProvider() public function testParseWithPositionValue() { $position = 1; - $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); - $formatter->parse('123', SimpleNumberFormatter::TYPE_DEFAULT, $position); + $formatter = new StubNumberFormatter('en', StubNumberFormatter::DECIMAL); + $formatter->parse('123', StubNumberFormatter::TYPE_DEFAULT, $position); } /** @@ -287,7 +287,7 @@ public function testParseWithPositionValue() */ public function testSetPattern() { - $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $formatter = new StubNumberFormatter('en', StubNumberFormatter::DECIMAL); $formatter->setPattern(null); } @@ -296,7 +296,7 @@ public function testSetPattern() */ public function testSetSymbol() { - $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $formatter = new StubNumberFormatter('en', StubNumberFormatter::DECIMAL); $formatter->setSymbol(null, null); } @@ -305,7 +305,7 @@ public function testSetSymbol() */ public function testSetTextAttribute() { - $formatter = new SimpleNumberFormatter('en', SimpleNumberFormatter::DECIMAL); + $formatter = new StubNumberFormatter('en', StubNumberFormatter::DECIMAL); $formatter->setTextAttribute(null, null); } } From 0aaf7b655e5c882ce6910fb1d525ce06cbc090c9 Mon Sep 17 00:00:00 2001 From: Eriksen Costa Date: Sat, 5 Feb 2011 15:07:42 -0200 Subject: [PATCH 20/32] [Locale] added learning tests for the \NumberFormatter class --- .../Stub/Learning/NumberFormatterTest.php | 307 ++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100644 tests/Symfony/Tests/Component/Locale/Stub/Learning/NumberFormatterTest.php diff --git a/tests/Symfony/Tests/Component/Locale/Stub/Learning/NumberFormatterTest.php b/tests/Symfony/Tests/Component/Locale/Stub/Learning/NumberFormatterTest.php new file mode 100644 index 0000000000000..d0dfe40d99476 --- /dev/null +++ b/tests/Symfony/Tests/Component/Locale/Stub/Learning/NumberFormatterTest.php @@ -0,0 +1,307 @@ +formatter = $this->getDecimalFormatter(); + } + + private function getDecimalFormatter() + { + return new \NumberFormatter('en', \NumberFormatter::DECIMAL); + } + + private function getCurrencyFormatter() + { + $formatter = new \NumberFormatter('en', \NumberFormatter::CURRENCY); + $formatter->setSymbol(\NumberFormatter::CURRENCY_SYMBOL, 'SFD'); + return $formatter; + } + + /** + * @dataProvider formatCurrencyProvider + */ + public function testFormatCurrency(\NumberFormatter $formatter, $value, $currency, $expectedValue) + { + $formattedCurrency = $formatter->formatCurrency($value, $currency); + $this->assertEquals($expectedValue, $formattedCurrency); + } + + public function formatCurrencyProvider() + { + $df = $this->getDecimalFormatter(); + $cf = $this->getCurrencyFormatter(); + + return array( + array($df, 100, 'BRL', '100'), + array($df, 100.1, 'BRL', '100.1'), + array($cf, 100, 'BRL', 'R$100.00'), + array($cf, 100.1, 'BRL', 'R$100.10') + ); + } + + /** + * @dataProvider formatProvider + */ + public function testFormat($formatter, $value, $expectedValue) + { + $formattedValue = $formatter->format($value); + $this->assertEquals($expectedValue, $formattedValue); + } + + public function formatProvider() + { + $df = $this->getDecimalFormatter(); + $cf = $this->getCurrencyFormatter(); + + return array( + array($df, 1, '1'), + array($df, 1.1, '1.1'), + array($cf, 1, 'SFD1.00'), + array($cf, 1.1, 'SFD1.10') + ); + } + + /** + * @dataProvider formatTypeDefaultProvider + */ + public function testFormatTypeDefault($formatter, $value, $expectedValue) + { + $formattedValue = $formatter->format($value, \NumberFormatter::TYPE_DEFAULT); + $this->assertEquals($expectedValue, $formattedValue); + } + + public function formatTypeDefaultProvider() + { + $df = $this->getDecimalFormatter(); + $cf = $this->getCurrencyFormatter(); + + return array( + array($df, 1, '1'), + array($df, 1.1, '1.1'), + array($cf, 1, 'SFD1.00'), + array($cf, 1.1, 'SFD1.10') + ); + } + + /** + * @dataProvider formatTypeInt32Provider + */ + public function testFormatTypeInt32($formatter, $value, $expectedValue, $message = '') + { + $formattedValue = $formatter->format($value, \NumberFormatter::TYPE_INT32); + $this->assertEquals($expectedValue, $formattedValue, $message); + } + + public function formatTypeInt32Provider() + { + $df = $this->getDecimalFormatter(); + $cf = $this->getCurrencyFormatter(); + + $message = '->format() TYPE_INT32 formats incosistencily an integer if out of 32 bit range.'; + + return array( + array($df, 1, '1'), + array($df, 1.1, '1'), + array($df, 2147483648, '-2,147,483,648', $message), + array($df, -2147483649, '2,147,483,647', $message), + array($cf, 1, 'SFD1.00'), + array($cf, 1.1, 'SFD1.00'), + array($cf, 2147483648, '(SFD2,147,483,648.00)', $message), + array($cf, -2147483649, 'SFD2,147,483,647.00', $message) + ); + } + + /** + * The parse() method works differently with integer out of the 32 bit range. format() works fine. + * @dataProvider formatTypeInt64Provider + */ + public function testFormatTypeInt64($formatter, $value, $expectedValue) + { + $formattedValue = $formatter->format($value, \NumberFormatter::TYPE_INT64); + $this->assertEquals($expectedValue, $formattedValue); + } + + public function formatTypeInt64Provider() + { + $df = $this->getDecimalFormatter(); + $cf = $this->getCurrencyFormatter(); + + return array( + array($df, 1, '1'), + array($df, 1.1, '1'), + array($df, 2147483648, '2,147,483,648'), + array($df, -2147483649, '-2,147,483,649'), + array($cf, 1, 'SFD1.00'), + array($cf, 1.1, 'SFD1.00'), + array($cf, 2147483648, 'SFD2,147,483,648.00'), + array($cf, -2147483649, '(SFD2,147,483,649.00)') + ); + } + + /** + * @dataProvider formatTypeDoubleProvider + */ + public function testFormatTypeDouble($formatter, $value, $expectedValue) + { + $formattedValue = $formatter->format($value, \NumberFormatter::TYPE_DOUBLE); + $this->assertEquals($expectedValue, $formattedValue); + } + + public function formatTypeDoubleProvider() + { + $df = $this->getDecimalFormatter(); + $cf = $this->getCurrencyFormatter(); + + return array( + array($df, 1, '1'), + array($df, 1.1, '1.1'), + array($cf, 1, 'SFD1.00'), + array($cf, 1.1, 'SFD1.10'), + ); + } + + /** + * @dataProvider formatTypeCurrencyProvider + * @expectedException PHPUnit_Framework_Error_Warning + */ + public function testFormatTypeCurrency($formatter, $value) + { + $formattedValue = $formatter->format($value, \NumberFormatter::TYPE_CURRENCY); + } + + public function formatTypeCurrencyProvider() + { + $df = $this->getDecimalFormatter(); + $cf = $this->getCurrencyFormatter(); + + return array( + array($df, 1), + array($df, 1), + ); + } + + /** + * @dataProvider parseCurrencyProvider + */ + public function testParseCurrency($formatter, $value, $expectedValue, $expectedCurrency) + { + $currency = ''; + $parsedValue = $formatter->parseCurrency($value, $currency); + $this->assertEquals($expectedValue, $parsedValue); + $this->assertEquals($expectedCurrency, $currency); + } + + public function parseCurrencyProvider() + { + $df = $this->getDecimalFormatter(); + $cf = $this->getCurrencyFormatter(); + + return array( + array($df, 1, 1, ''), + array($df, 1.1, 1.1, ''), + array($cf, '$1.00', 1, 'USD'), + array($cf, '€1.00', 1, 'EUR'), + array($cf, 'R$1.00', 1, 'BRL') + ); + } + + public function testParse() + { + $parsedValue = $this->formatter->parse('1'); + $this->assertInternalType('float', $parsedValue, '->parse() as double by default.'); + $this->assertEquals(1, $parsedValue); + + $parsedValue = $this->formatter->parse('1', \NumberFormatter::TYPE_DOUBLE, $position); + $this->assertNull($position, '->parse() returns null to the $position reference if it doesn\'t had a defined value.'); + + $position = 0; + $parsedValue = $this->formatter->parse('1', \NumberFormatter::TYPE_DOUBLE, $position); + $this->assertEquals(1, $position); + + $parsedValue = $this->formatter->parse('prefix1', \NumberFormatter::TYPE_DOUBLE); + $this->assertFalse($parsedValue, '->parse() does not parse a number with a string prefix.'); + + $parsedValue = $this->formatter->parse('1suffix', \NumberFormatter::TYPE_DOUBLE); + $this->assertEquals(1, $parsedValue, '->parse() parses a number with a string suffix.'); + + $position = 0; + $parsedValue = $this->formatter->parse('1suffix', \NumberFormatter::TYPE_DOUBLE, $position); + $this->assertEquals(1, $parsedValue); + $this->assertEquals(1, $position, '->parse() ignores anything not a number before the number.'); + } + + /** + * @expectedException PHPUnit_Framework_Error_Warning + */ + public function testParseTypeDefault() + { + $this->formatter->parse('1', \NumberFormatter::TYPE_DEFAULT); + } + + public function testParseTypeInt32() + { + $parsedValue = $this->formatter->parse('1', \NumberFormatter::TYPE_INT32); + $this->assertInternalType('integer', $parsedValue); + $this->assertEquals(1, $parsedValue); + + $parsedValue = $this->formatter->parse('1.1', \NumberFormatter::TYPE_INT32); + $this->assertInternalType('integer', $parsedValue); + $this->assertEquals(1, $parsedValue, '->parse() TYPE_INT32 ignores the decimal part of a number and uses only the integer one.'); + + // int 32 out of range + $parsedValue = $this->formatter->parse('2,147,483,648', \NumberFormatter::TYPE_INT32); + $this->assertFalse($parsedValue, '->parse() TYPE_INT32 returns false if the value is out of range.'); + $parsedValue = $this->formatter->parse('-2,147,483,649', \NumberFormatter::TYPE_INT32); + $this->assertFalse($parsedValue, '->parse() TYPE_INT32 returns false if the value is out of range.'); + } + + public function testParseInt64() + { + // int 64 parsing + $parsedValue = $this->formatter->parse('2,147,483,647', \NumberFormatter::TYPE_INT64); + $this->assertInternalType('integer', $parsedValue); + $this->assertEquals(2147483647, $parsedValue); + + $parsedValue = $this->formatter->parse('-2,147,483,648', \NumberFormatter::TYPE_INT64); + $this->assertInternalType('integer', $parsedValue); + $this->assertEquals(-2147483648, $parsedValue); + + // int 64 using only 32 bit range strangeness + $parsedValue = $this->formatter->parse('2,147,483,648', \NumberFormatter::TYPE_INT64); + $this->assertInternalType('integer', $parsedValue); + $this->assertEquals(-2147483648, $parsedValue, '->parse() TYPE_INT64 does not use true 64 bit integers, using only the 32 bit range.'); + + $parsedValue = $this->formatter->parse('-2,147,483,649', \NumberFormatter::TYPE_INT64); + $this->assertInternalType('integer', $parsedValue); + $this->assertEquals(2147483647, $parsedValue, '->parse() TYPE_INT64 does not use true 64 bit integers, using only the 32 bit range.'); + } + + public function testParseTypeDouble() + { + $parsedValue = $this->formatter->parse('1', \NumberFormatter::TYPE_DOUBLE); + $this->assertInternalType('float', $parsedValue); + $this->assertEquals(1, $parsedValue); + + $parsedValue = $this->formatter->parse('1.1'); + $this->assertInternalType('float', $parsedValue); + $this->assertEquals(1.1, $parsedValue); + + $parsedValue = $this->formatter->parse('1,1'); + $this->assertInternalType('float', $parsedValue); + $this->assertEquals(11, $parsedValue); + } + + /** + * @expectedException PHPUnit_Framework_Error_Warning + */ + public function testParseTypeCurrency() + { + $this->formatter->parse('1', \NumberFormatter::TYPE_CURRENCY); + } +} From 59a0f72202cfec087b3475da80ad0630b899d312 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sat, 5 Feb 2011 21:29:40 +0100 Subject: [PATCH 21/32] [Locale] first implementation of StubIntlDateFormatter --- .../Locale/Stub/StubIntlDateFormatter.php | 111 ++++++++++++++++++ .../Locale/Stub/StubIntlDateFormatterTest.php | 84 +++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php create mode 100644 tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php diff --git a/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php b/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php new file mode 100644 index 0000000000000..d09b5e3cbd098 --- /dev/null +++ b/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Locale\Stub; + +use Symfony\Component\Locale\Locale; + +/** + * Provides a stub IntlDateFormatter for the 'en' locale. + */ +class StubIntlDateFormatter +{ + /* formats */ + const NONE = -1; + const FULL = 0; + const LONG = 1; + const MEDIUM = 2; + const SHORT = 3; + + /* formats */ + const TRADITIONAL = 0; + const GREGORIAN = 1; + + public function __construct($locale, $datetype, $timetype, $timezone = null, $calendar = null, $pattern = null) + { + if ('en' != $locale) { + throw new \InvalidArgumentException('Unsupported $locale value. Only the \'en\' locale is supported. Install the intl extension for full localization capabilities.'); + } + + $this->setPattern($pattern); + } + + public function format($timestamp) + { + $callback = function($matches) use ($timestamp) { + $pattern = $matches[0]; + $length = strlen($pattern); + + switch ($pattern[0]) { + case 'M': + $matchLengthMap = array( + 1 => 'n', + 2 => 'm', + 3 => 'M', + 4 => 'F', + ); + + if (isset($matchLengthMap[$length])) { + return gmdate($matchLengthMap[$length], $timestamp); + } else if (5 == $length) { + return substr(gmdate('M', $timestamp), 0, 1); + } else { + return str_pad(gmdate('m', $timestamp), $length, '0', STR_PAD_LEFT); + } + break; + + case 'y': + $matchLengthMap = array( + 1 => 'Y', + 2 => 'y', + 3 => 'Y', + 4 => 'Y', + ); + + if (isset($matchLengthMap[$length])) { + return gmdate($matchLengthMap[$length], $timestamp); + } else { + return str_pad(gmdate('Y', $timestamp), $length, '0', STR_PAD_LEFT); + } + break; + + case 'd': + return str_pad(gmdate('j', $timestamp), $length, '0', STR_PAD_LEFT); + break; + } + }; + + $formatted = preg_replace_callback('/(M+|y+|d+)/', $callback, $this->getPattern()); + + return $formatted; + } + + public function getPattern() + { + return $this->pattern; + } + + public function getCalendar() + { + $this->throwMethodNotImplementException(__METHOD__); + } + + public function setPattern($pattern) + { + $this->pattern = $pattern; + } + + private function throwMethodNotImplementException($methodName) + { + $message = sprintf('The %s::%s() is not implemented. Install the intl extension for full localization capabilities.', __CLASS__, $methodName); + throw new \RuntimeException($message); + } +} diff --git a/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php b/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php new file mode 100644 index 0000000000000..924ee5006ba12 --- /dev/null +++ b/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Tests\Component\Locale\Stub; + +use Symfony\Component\Locale\Locale; +use Symfony\Component\Locale\Stub\StubIntlDateFormatter; + +class StubIntlDateFormatterTest extends \PHPUnit_Framework_TestCase +{ + public function formatProvider() + { + return array( + array('y-M-d', 0, '1970-1-1'), + + /* months */ + array('M', 0, '1'), + array('MM', 0, '01'), + array('MMM', 0, 'Jan'), + array('MMMM', 0, 'January'), + array('MMMMM', 0, 'J'), + /* this is stupid */ + array('MMMMMM', 0, '00001'), + + /* years */ + array('y', 0, '1970'), + array('yy', 0, '70'), + array('yyy', 0, '1970'), + array('yyyy', 0, '1970'), + array('yyyyy', 0, '01970'), + array('yyyyyy', 0, '001970'), + + /* day */ + array('d', 0, '1'), + array('dd', 0, '01'), + array('ddd', 0, '001'), + ); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testConstructorWithUnsupportedLocale() + { + $formatter = new StubIntlDateFormatter('pt_BR', StubIntlDateFormatter::MEDIUM, StubIntlDateFormatter::SHORT); + } + + public function testConstructor() + { + $formatter = new StubIntlDateFormatter('en', StubIntlDateFormatter::MEDIUM, StubIntlDateFormatter::SHORT, 'UTC', StubIntlDateFormatter::GREGORIAN, 'Y-M-d'); + $this->assertEquals('Y-M-d', $formatter->getPattern()); + } + + /** + * @dataProvider formatProvider + */ + public function testFormat($pattern, $timestamp, $expected) + { + $formatter = new StubIntlDateFormatter('en', StubIntlDateFormatter::MEDIUM, StubIntlDateFormatter::SHORT, 'UTC', StubIntlDateFormatter::GREGORIAN, $pattern); + $this->assertEquals($expected, $formatter->format($timestamp)); + + if (extension_loaded('intl')) { + $formatter = new \IntlDateFormatter('en', \IntlDateFormatter::MEDIUM, \IntlDateFormatter::SHORT, 'UTC', \IntlDateFormatter::GREGORIAN, $pattern); + $this->assertEquals($expected, $formatter->format($timestamp)); + } + } + + /** + * @expectedException RuntimeException + */ + public function testGetCalendar() + { + $formatter = new StubIntlDateFormatter('en', StubIntlDateFormatter::MEDIUM, StubIntlDateFormatter::SHORT); + $formatter->getCalendar(); + } +} From 3da43071da3fffc1baf44103dba34ee6847037ce Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sun, 6 Feb 2011 16:57:45 +0100 Subject: [PATCH 22/32] [Locale] add support for escaping, give specifics on implementation used in tests --- .../Component/Locale/Stub/StubIntlDateFormatter.php | 8 +++++++- .../Locale/Stub/StubIntlDateFormatterTest.php | 12 ++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php b/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php index d09b5e3cbd098..335d2f992dd77 100644 --- a/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php +++ b/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php @@ -40,10 +40,16 @@ public function __construct($locale, $datetype, $timetype, $timezone = null, $ca public function format($timestamp) { + $regExp = "/('(M+|y+|d+|[^Myd])|M+|y+|d+)/"; + $callback = function($matches) use ($timestamp) { $pattern = $matches[0]; $length = strlen($pattern); + if ("'" === $pattern[0]) { + return substr($pattern, 1); + } + switch ($pattern[0]) { case 'M': $matchLengthMap = array( @@ -83,7 +89,7 @@ public function format($timestamp) } }; - $formatted = preg_replace_callback('/(M+|y+|d+)/', $callback, $this->getPattern()); + $formatted = preg_replace_callback($regExp, $callback, $this->getPattern()); return $formatted; } diff --git a/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php b/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php index 924ee5006ba12..50baa5d723aa0 100644 --- a/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php +++ b/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php @@ -21,6 +21,14 @@ public function formatProvider() return array( array('y-M-d', 0, '1970-1-1'), + /* escaping */ + array("'M", 0, 'M'), + array("'y-'M-'d", 0, 'y-M-d'), + array("'yy", 0, 'yy'), + array("'''yy", 0, "'yy"), + array("''y", 0, "'1970"), + array("''yy", 0, "'70"), + /* months */ array('M', 0, '1'), array('MM', 0, '01'), @@ -65,11 +73,11 @@ public function testConstructor() public function testFormat($pattern, $timestamp, $expected) { $formatter = new StubIntlDateFormatter('en', StubIntlDateFormatter::MEDIUM, StubIntlDateFormatter::SHORT, 'UTC', StubIntlDateFormatter::GREGORIAN, $pattern); - $this->assertEquals($expected, $formatter->format($timestamp)); + $this->assertEquals($expected, $formatter->format($timestamp), 'Check date format with stub implementation.'); if (extension_loaded('intl')) { $formatter = new \IntlDateFormatter('en', \IntlDateFormatter::MEDIUM, \IntlDateFormatter::SHORT, 'UTC', \IntlDateFormatter::GREGORIAN, $pattern); - $this->assertEquals($expected, $formatter->format($timestamp)); + $this->assertEquals($expected, $formatter->format($timestamp), 'Check date format with intl extension.'); } } From 09b24fa7fab8c6c31f8f8c05f577878ffb693610 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sun, 6 Feb 2011 18:05:26 +0100 Subject: [PATCH 23/32] [Locale] move intl bugs to skipped tests --- .../Locale/Stub/StubIntlDateFormatterTest.php | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php b/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php index 50baa5d723aa0..35994dbcc0404 100644 --- a/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php +++ b/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php @@ -23,7 +23,6 @@ public function formatProvider() /* escaping */ array("'M", 0, 'M'), - array("'y-'M-'d", 0, 'y-M-d'), array("'yy", 0, 'yy'), array("'''yy", 0, "'yy"), array("''y", 0, "'1970"), @@ -53,6 +52,19 @@ public function formatProvider() ); } + /** + * provides data for cases that are broken in icu/intl + */ + public function brokenFormatProvider() + { + return array( + /* escaping */ + array("'y-'M-'d", 0, 'y-M-d'), + array("WTF 'y-'M", 0, '0T1 y-M'), + array("n-'M", 0, 'n-M'), + ); + } + /** * @expectedException InvalidArgumentException */ @@ -81,6 +93,22 @@ public function testFormat($pattern, $timestamp, $expected) } } + /** + * @dataProvider brokenFormatProvider + */ + public function testBrokenFormat($pattern, $timestamp, $expected) + { + $this->markTestSkipped('icu/intl has some bugs, thus skipping.'); + + $formatter = new StubIntlDateFormatter('en', StubIntlDateFormatter::MEDIUM, StubIntlDateFormatter::SHORT, 'UTC', StubIntlDateFormatter::GREGORIAN, $pattern); + $this->assertEquals($expected, $formatter->format($timestamp), 'Check date format with stub implementation.'); + + if (extension_loaded('intl')) { + $formatter = new \IntlDateFormatter('en', \IntlDateFormatter::MEDIUM, \IntlDateFormatter::SHORT, 'UTC', \IntlDateFormatter::GREGORIAN, $pattern); + $this->assertEquals($expected, $formatter->format($timestamp), 'Check date format with intl extension.'); + } + } + /** * @expectedException RuntimeException */ From 43794ba4dc34f98c4df81f4d1779d49a9744b726 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sun, 6 Feb 2011 18:53:20 +0100 Subject: [PATCH 24/32] [Locale] support for G and Q placeholders in StubIntlDateFormatter::format --- .../Locale/Stub/StubIntlDateFormatter.php | 26 +++++++++++++++++- .../Locale/Stub/StubIntlDateFormatterTest.php | 27 ++++++++++++++++++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php b/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php index 335d2f992dd77..fecc81d7f61f3 100644 --- a/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php +++ b/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php @@ -40,7 +40,7 @@ public function __construct($locale, $datetype, $timetype, $timezone = null, $ca public function format($timestamp) { - $regExp = "/('(M+|y+|d+|[^Myd])|M+|y+|d+)/"; + $regExp = "/('(M+|y+|d+|G+|Q+|q+|[^MydGQq])|M+|y+|d+|G+|Q+|q+)/"; $callback = function($matches) use ($timestamp) { $pattern = $matches[0]; @@ -86,6 +86,30 @@ public function format($timestamp) case 'd': return str_pad(gmdate('j', $timestamp), $length, '0', STR_PAD_LEFT); break; + + case 'G': + $year = (int) gmdate('Y', $timestamp); + return $year >= 0 ? 'AD' : 'BC'; + break; + + case 'q': + case 'Q': + $month = (int) gmdate('n', $timestamp); + $quarter = (int) floor(($month - 1) / 3) + 1; + switch ($length) { + case 1: + case 2: + return str_pad($quarter, $length, '0', STR_PAD_LEFT); + break; + case 3: + return 'Q' . $quarter; + break; + default: + $map = array(1 => '1st quarter', 2 => '2nd quarter', 3 => '3rd quarter', 4 => '4th quarter'); + return $map[$quarter]; + break; + } + break; } }; diff --git a/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php b/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php index 35994dbcc0404..5c22a7f14ccec 100644 --- a/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php +++ b/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php @@ -34,7 +34,6 @@ public function formatProvider() array('MMM', 0, 'Jan'), array('MMMM', 0, 'January'), array('MMMMM', 0, 'J'), - /* this is stupid */ array('MMMMMM', 0, '00001'), /* years */ @@ -49,6 +48,32 @@ public function formatProvider() array('d', 0, '1'), array('dd', 0, '01'), array('ddd', 0, '001'), + + /* era */ + array('G', 0, 'AD'), + array('G', -62167222800, 'BC'), + + /* quarter */ + array('Q', 0, '1'), + array('QQ', 0, '01'), + array('QQQ', 0, 'Q1'), + array('QQQQ', 0, '1st quarter'), + array('QQQQQ', 0, '1st quarter'), + + array('q', 0, '1'), + array('qq', 0, '01'), + array('qqq', 0, 'Q1'), + array('qqqq', 0, '1st quarter'), + array('qqqqq', 0, '1st quarter'), + + array('Q', 7776000, '2'), + array('QQ', 7776000, '02'), + array('QQQ', 7776000, 'Q2'), + array('QQQQ', 7776000, '2nd quarter'), + + array('QQQQ', 15638400, '3rd quarter'), + + array('QQQQ', 23587200, '4th quarter'), ); } From af6fae63075b5cddfc14204f51ef9f279fc4f476 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sun, 6 Feb 2011 19:00:29 +0100 Subject: [PATCH 25/32] [Locale] add support for L, which is the same as M --- .../Component/Locale/Stub/StubIntlDateFormatter.php | 3 ++- .../Component/Locale/Stub/StubIntlDateFormatterTest.php | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php b/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php index fecc81d7f61f3..d692e8cc9e679 100644 --- a/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php +++ b/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php @@ -40,7 +40,7 @@ public function __construct($locale, $datetype, $timetype, $timezone = null, $ca public function format($timestamp) { - $regExp = "/('(M+|y+|d+|G+|Q+|q+|[^MydGQq])|M+|y+|d+|G+|Q+|q+)/"; + $regExp = "/('(M+|L+|y+|d+|G+|Q+|q+|[^MLydGQq])|M+|L+|y+|d+|G+|Q+|q+)/"; $callback = function($matches) use ($timestamp) { $pattern = $matches[0]; @@ -52,6 +52,7 @@ public function format($timestamp) switch ($pattern[0]) { case 'M': + case 'L': $matchLengthMap = array( 1 => 'n', 2 => 'm', diff --git a/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php b/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php index 5c22a7f14ccec..ea14cc28e00f0 100644 --- a/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php +++ b/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php @@ -36,6 +36,13 @@ public function formatProvider() array('MMMMM', 0, 'J'), array('MMMMMM', 0, '00001'), + array('L', 0, '1'), + array('LL', 0, '01'), + array('LLL', 0, 'Jan'), + array('LLLL', 0, 'January'), + array('LLLLL', 0, 'J'), + array('LLLLLL', 0, '00001'), + /* years */ array('y', 0, '1970'), array('yy', 0, '70'), From a91c970a07b1da72e20b118538c84a6a520bfef7 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sun, 6 Feb 2011 20:21:25 +0100 Subject: [PATCH 26/32] [Locale] use assertSame instead of assertEquals --- .../Component/Locale/Stub/StubIntlDateFormatterTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php b/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php index ea14cc28e00f0..815418edb8113 100644 --- a/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php +++ b/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php @@ -117,11 +117,11 @@ public function testConstructor() public function testFormat($pattern, $timestamp, $expected) { $formatter = new StubIntlDateFormatter('en', StubIntlDateFormatter::MEDIUM, StubIntlDateFormatter::SHORT, 'UTC', StubIntlDateFormatter::GREGORIAN, $pattern); - $this->assertEquals($expected, $formatter->format($timestamp), 'Check date format with stub implementation.'); + $this->assertSame($expected, $formatter->format($timestamp), 'Check date format with stub implementation.'); if (extension_loaded('intl')) { $formatter = new \IntlDateFormatter('en', \IntlDateFormatter::MEDIUM, \IntlDateFormatter::SHORT, 'UTC', \IntlDateFormatter::GREGORIAN, $pattern); - $this->assertEquals($expected, $formatter->format($timestamp), 'Check date format with intl extension.'); + $this->assertSame($expected, $formatter->format($timestamp), 'Check date format with intl extension.'); } } @@ -133,11 +133,11 @@ public function testBrokenFormat($pattern, $timestamp, $expected) $this->markTestSkipped('icu/intl has some bugs, thus skipping.'); $formatter = new StubIntlDateFormatter('en', StubIntlDateFormatter::MEDIUM, StubIntlDateFormatter::SHORT, 'UTC', StubIntlDateFormatter::GREGORIAN, $pattern); - $this->assertEquals($expected, $formatter->format($timestamp), 'Check date format with stub implementation.'); + $this->assertSame($expected, $formatter->format($timestamp), 'Check date format with stub implementation.'); if (extension_loaded('intl')) { $formatter = new \IntlDateFormatter('en', \IntlDateFormatter::MEDIUM, \IntlDateFormatter::SHORT, 'UTC', \IntlDateFormatter::GREGORIAN, $pattern); - $this->assertEquals($expected, $formatter->format($timestamp), 'Check date format with intl extension.'); + $this->assertSame($expected, $formatter->format($timestamp), 'Check date format with intl extension.'); } } From c1aa2746fa0e780e919fae9c3fe9375f7a2c2c3f Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sun, 6 Feb 2011 20:21:41 +0100 Subject: [PATCH 27/32] [Locale] add support for h --- .../Locale/Stub/StubIntlDateFormatter.php | 7 ++++++- .../Locale/Stub/StubIntlDateFormatterTest.php | 18 ++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php b/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php index d692e8cc9e679..59300d99424cc 100644 --- a/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php +++ b/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php @@ -40,7 +40,7 @@ public function __construct($locale, $datetype, $timetype, $timezone = null, $ca public function format($timestamp) { - $regExp = "/('(M+|L+|y+|d+|G+|Q+|q+|[^MLydGQq])|M+|L+|y+|d+|G+|Q+|q+)/"; + $regExp = "/('(M+|L+|y+|d+|G+|Q+|q+|h+|[^MLydGQqh])|M+|L+|y+|d+|G+|Q+|q+|h+)/"; $callback = function($matches) use ($timestamp) { $pattern = $matches[0]; @@ -111,6 +111,11 @@ public function format($timestamp) break; } break; + + case 'h': + $hours = gmdate('g', $timestamp); + return str_pad($hours, $length, '0', STR_PAD_LEFT); + break; } }; diff --git a/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php b/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php index 815418edb8113..bdf4f90c522e2 100644 --- a/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php +++ b/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php @@ -28,22 +28,22 @@ public function formatProvider() array("''y", 0, "'1970"), array("''yy", 0, "'70"), - /* months */ + /* month */ array('M', 0, '1'), array('MM', 0, '01'), array('MMM', 0, 'Jan'), array('MMMM', 0, 'January'), array('MMMMM', 0, 'J'), - array('MMMMMM', 0, '00001'), + array('MMMMMM', 0, '000001'), array('L', 0, '1'), array('LL', 0, '01'), array('LLL', 0, 'Jan'), array('LLLL', 0, 'January'), array('LLLLL', 0, 'J'), - array('LLLLLL', 0, '00001'), + array('LLLLLL', 0, '000001'), - /* years */ + /* year */ array('y', 0, '1970'), array('yy', 0, '70'), array('yyy', 0, '1970'), @@ -81,6 +81,14 @@ public function formatProvider() array('QQQQ', 15638400, '3rd quarter'), array('QQQQ', 23587200, '4th quarter'), + + /* hour */ + array('h', 0, '12'), + array('hh', 0, '12'), + array('hhh', 0, '012'), + + array('h', 1, '12'), + array('h', 3600, '1'), ); } @@ -92,6 +100,8 @@ public function brokenFormatProvider() return array( /* escaping */ array("'y-'M-'d", 0, 'y-M-d'), + + /* weird bugs */ array("WTF 'y-'M", 0, '0T1 y-M'), array("n-'M", 0, 'n-M'), ); From fec50dc7569cb9cc6475879c84b181a1e5e447cc Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sun, 6 Feb 2011 20:30:29 +0100 Subject: [PATCH 28/32] [Locale] support for D (day of year) --- .../Component/Locale/Stub/StubIntlDateFormatter.php | 10 +++++++--- .../Locale/Stub/StubIntlDateFormatterTest.php | 9 +++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php b/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php index 59300d99424cc..60532dfcbfefc 100644 --- a/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php +++ b/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php @@ -40,7 +40,7 @@ public function __construct($locale, $datetype, $timetype, $timezone = null, $ca public function format($timestamp) { - $regExp = "/('(M+|L+|y+|d+|G+|Q+|q+|h+|[^MLydGQqh])|M+|L+|y+|d+|G+|Q+|q+|h+)/"; + $regExp = "/('(M+|L+|y+|d+|G+|Q+|q+|h+|D+|[^MLydGQqhD])|M+|L+|y+|d+|G+|Q+|q+|h+|D+)/"; $callback = function($matches) use ($timestamp) { $pattern = $matches[0]; @@ -113,8 +113,12 @@ public function format($timestamp) break; case 'h': - $hours = gmdate('g', $timestamp); - return str_pad($hours, $length, '0', STR_PAD_LEFT); + return str_pad(gmdate('g', $timestamp), $length, '0', STR_PAD_LEFT); + break; + + case 'D': + $dayOfYear = gmdate('z', $timestamp) + 1; + return str_pad($dayOfYear, $length, '0', STR_PAD_LEFT); break; } }; diff --git a/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php b/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php index bdf4f90c522e2..3eb9f2847a099 100644 --- a/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php +++ b/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php @@ -73,13 +73,16 @@ public function formatProvider() array('qqqq', 0, '1st quarter'), array('qqqqq', 0, '1st quarter'), + // 4 months array('Q', 7776000, '2'), array('QQ', 7776000, '02'), array('QQQ', 7776000, 'Q2'), array('QQQQ', 7776000, '2nd quarter'), + // 7 months array('QQQQ', 15638400, '3rd quarter'), + // 10 months array('QQQQ', 23587200, '4th quarter'), /* hour */ @@ -89,6 +92,12 @@ public function formatProvider() array('h', 1, '12'), array('h', 3600, '1'), + + /* day of year */ + array('D', 0, '1'), + array('D', 86400, '2'), // 1 day + array('D', 31536000, '1'), // 1 year + array('D', 31622400, '2'), // 1 year + 1 day ); } From b7f42f098dc6cde308cdfefc74d2a209eff397d8 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sun, 6 Feb 2011 20:42:30 +0100 Subject: [PATCH 29/32] [Locale] support for E (day of week) --- .../Locale/Stub/StubIntlDateFormatter.php | 16 +++++++++++++++- .../Locale/Stub/StubIntlDateFormatterTest.php | 10 ++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php b/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php index 60532dfcbfefc..44e5fddac6091 100644 --- a/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php +++ b/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php @@ -40,7 +40,7 @@ public function __construct($locale, $datetype, $timetype, $timezone = null, $ca public function format($timestamp) { - $regExp = "/('(M+|L+|y+|d+|G+|Q+|q+|h+|D+|[^MLydGQqhD])|M+|L+|y+|d+|G+|Q+|q+|h+|D+)/"; + $regExp = "/('(M+|L+|y+|d+|G+|Q+|q+|h+|D+|E+|[^MLydGQqhDE])|M+|L+|y+|d+|G+|Q+|q+|h+|D+|E+)/"; $callback = function($matches) use ($timestamp) { $pattern = $matches[0]; @@ -120,6 +120,20 @@ public function format($timestamp) $dayOfYear = gmdate('z', $timestamp) + 1; return str_pad($dayOfYear, $length, '0', STR_PAD_LEFT); break; + + case 'E': + $dayOfWeek = gmdate('l', $timestamp); + switch ($length) { + case 4: + return $dayOfWeek; + break; + case 5: + return $dayOfWeek[0]; + break; + default: + return substr($dayOfWeek, 0, 3); + } + break; } }; diff --git a/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php b/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php index 3eb9f2847a099..33ba5ba62648b 100644 --- a/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php +++ b/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php @@ -98,6 +98,16 @@ public function formatProvider() array('D', 86400, '2'), // 1 day array('D', 31536000, '1'), // 1 year array('D', 31622400, '2'), // 1 year + 1 day + + /* day of week */ + array('E', 0, 'Thu'), + array('EE', 0, 'Thu'), + array('EEE', 0, 'Thu'), + array('EEEE', 0, 'Thursday'), + array('EEEEE', 0, 'T'), + array('EEEEEE', 0, 'Thu'), + + array('E', 1296950400, 'Sun'), ); } From d3e24d698cffc451489bfe3198cef674c01bf350 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sun, 6 Feb 2011 21:00:22 +0100 Subject: [PATCH 30/32] [Locale] support for a (AM/PM) --- .../Locale/Stub/StubIntlDateFormatter.php | 8 ++++++-- .../Locale/Stub/StubIntlDateFormatterTest.php | 15 ++++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php b/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php index 44e5fddac6091..d6a84f71febf8 100644 --- a/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php +++ b/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php @@ -40,7 +40,7 @@ public function __construct($locale, $datetype, $timetype, $timezone = null, $ca public function format($timestamp) { - $regExp = "/('(M+|L+|y+|d+|G+|Q+|q+|h+|D+|E+|[^MLydGQqhDE])|M+|L+|y+|d+|G+|Q+|q+|h+|D+|E+)/"; + $regExp = "/('(M+|L+|y+|d+|G+|Q+|q+|h+|D+|E+|a+|[^MLydGQqhDEa])|M+|L+|y+|d+|G+|Q+|q+|h+|D+|E+|a+)/"; $callback = function($matches) use ($timestamp) { $pattern = $matches[0]; @@ -134,7 +134,11 @@ public function format($timestamp) return substr($dayOfWeek, 0, 3); } break; - } + + case 'a': + return gmdate('A', $timestamp); + break; + } }; $formatted = preg_replace_callback($regExp, $callback, $this->getPattern()); diff --git a/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php b/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php index 33ba5ba62648b..ed2e2b36aacf5 100644 --- a/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php +++ b/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php @@ -107,7 +107,20 @@ public function formatProvider() array('EEEEE', 0, 'T'), array('EEEEEE', 0, 'Thu'), - array('E', 1296950400, 'Sun'), + array('E', 1296540000, 'Tue'), // 2011-02-01 + array('E', 1296950400, 'Sun'), // 2011-02-06 + + /* am/pm marker */ + array('a', 0, 'AM'), + array('aa', 0, 'AM'), + array('aaa', 0, 'AM'), + array('aaaa', 0, 'AM'), + + // 12 hours + array('a', 43200, 'PM'), + array('aa', 43200, 'PM'), + array('aaa', 43200, 'PM'), + array('aaaa', 43200, 'PM'), ); } From 85d05c81acdd5d098761302b5d77aed35d99c1a9 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sun, 6 Feb 2011 21:06:48 +0100 Subject: [PATCH 31/32] [Locale] support for H (24 hour) --- .../Component/Locale/Stub/StubIntlDateFormatter.php | 6 +++++- .../Locale/Stub/StubIntlDateFormatterTest.php | 13 ++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php b/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php index d6a84f71febf8..1632018ec1179 100644 --- a/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php +++ b/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php @@ -40,7 +40,7 @@ public function __construct($locale, $datetype, $timetype, $timezone = null, $ca public function format($timestamp) { - $regExp = "/('(M+|L+|y+|d+|G+|Q+|q+|h+|D+|E+|a+|[^MLydGQqhDEa])|M+|L+|y+|d+|G+|Q+|q+|h+|D+|E+|a+)/"; + $regExp = "/('(M+|L+|y+|d+|G+|Q+|q+|h+|D+|E+|a+|H+|[^MLydGQqhDEaH])|M+|L+|y+|d+|G+|Q+|q+|h+|D+|E+|a+|H+)/"; $callback = function($matches) use ($timestamp) { $pattern = $matches[0]; @@ -138,6 +138,10 @@ public function format($timestamp) case 'a': return gmdate('A', $timestamp); break; + + case 'H': + return str_pad(gmdate('G', $timestamp), $length, '0', STR_PAD_LEFT); + break; } }; diff --git a/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php b/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php index ed2e2b36aacf5..fe3a776e6b318 100644 --- a/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php +++ b/tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php @@ -85,13 +85,14 @@ public function formatProvider() // 10 months array('QQQQ', 23587200, '4th quarter'), - /* hour */ + /* 12-hour */ array('h', 0, '12'), array('hh', 0, '12'), array('hhh', 0, '012'), array('h', 1, '12'), array('h', 3600, '1'), + array('h', 43200, '12'), // 12 hours /* day of year */ array('D', 0, '1'), @@ -121,6 +122,16 @@ public function formatProvider() array('aa', 43200, 'PM'), array('aaa', 43200, 'PM'), array('aaaa', 43200, 'PM'), + + /* 24-hour */ + array('H', 0, '0'), + array('HH', 0, '00'), + array('HHH', 0, '000'), + + array('H', 1, '0'), + array('H', 3600, '1'), + array('H', 43200, '12'), + array('H', 46800, '13'), ); } From 0396dab473dd3eb2d6880858b0cc330b2f3ba9c5 Mon Sep 17 00:00:00 2001 From: Igor Wiedler Date: Sun, 6 Feb 2011 21:20:57 +0100 Subject: [PATCH 32/32] [Locale] refactor IntlDateFormatter::format to build regExp dynamically --- .../Component/Locale/Stub/StubIntlDateFormatter.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php b/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php index 1632018ec1179..ced9126847231 100644 --- a/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php +++ b/src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php @@ -40,7 +40,12 @@ public function __construct($locale, $datetype, $timetype, $timezone = null, $ca public function format($timestamp) { - $regExp = "/('(M+|L+|y+|d+|G+|Q+|q+|h+|D+|E+|a+|H+|[^MLydGQqhDEaH])|M+|L+|y+|d+|G+|Q+|q+|h+|D+|E+|a+|H+)/"; + $specialChars = 'MLydGQqhDEaH'; + $specialCharsArray = str_split($specialChars); + $specialCharsMatch = implode('|', array_map(function($char) { + return $char . '+'; + }, $specialCharsArray)); + $regExp = "/('($specialCharsMatch|[^$specialChars])|$specialCharsMatch)/"; $callback = function($matches) use ($timestamp) { $pattern = $matches[0]; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy