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];