diff --git a/README.md b/README.md index d897eb2..fa38fec 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# CLDR [![Build Status](https://travis-ci.org/ICanBoogie/CLDR.svg?branch=master)](https://travis-ci.org/ICanBoogie/CLDR) +# CLDR + +[![Build Status][]](https://travis-ci.org/ICanBoogie/CLDR) [![Code Quality][]](https://scrutinizer-ci.com/g/ICanBoogie/CLDR/?branch=master) The __CLDR__ package provides means to internationalize your application by leveraging the data and conventions defined by the [Unicode Common Locale Data Repository](http://cldr.unicode.org/) (CLDR). @@ -54,14 +56,14 @@ provide a client instance. =1.0" }, - "require-dev": { - "predis/predis": "~1.0" - }, - "autoload": { "psr-4": { "ICanBoogie\\CLDR\\": "lib" diff --git a/lib/Calendar.php b/lib/Calendar.php index 193967a..bc00b73 100644 --- a/lib/Calendar.php +++ b/lib/Calendar.php @@ -57,6 +57,15 @@ class Calendar extends \ArrayObject use AccessorTrait; use LocalePropertyTrait; + static private $era_translation = [ + + 'abbreviated' => 'eraAbbr', + 'narrow' => 'eraNarrow', + 'short' => 'eraAbbr', + 'wide' => 'eraNames' + + ]; + /** * @return DateTimeFormatter */ @@ -90,15 +99,6 @@ public function __construct(Locale $locale, array $data) public function __get($property) { - static $era_translation = [ - - 'abbreviated' => 'eraAbbr', - 'narrow' => 'eraNarrow', - 'short' => 'eraAbbr', - 'wide' => 'eraNames' - - ]; - if (preg_match('#^(standalone_)?(abbreviated|narrow|short|wide)_(days|eras|months|quarters)$#', $property, $matches)) { list(, $standalone, $width, $type) = $matches; @@ -107,19 +107,17 @@ public function __get($property) if ($type == 'eras') { - return $this->$property = $data[$era_translation[$width]]; + return $this->$property = $data[self::$era_translation[$width]]; } - else - { - $data = $data[$standalone ? 'stand-alone' : 'format']; - if ($width == 'short' && empty($data[$width])) - { - $width = 'abbreviated'; - } + $data = $data[$standalone ? 'stand-alone' : 'format']; - return $this->$property = $data[$width]; + if ($width == 'short' && empty($data[$width])) + { + $width = 'abbreviated'; } + + return $this->$property = $data[$width]; } return $this->__object_get($property); diff --git a/lib/DateFormatter.php b/lib/DateFormatter.php index 20bbe97..e799473 100644 --- a/lib/DateFormatter.php +++ b/lib/DateFormatter.php @@ -41,19 +41,10 @@ class DateFormatter extends DateTimeFormatter /** * Resolves widths defined in `dateFormats` (full, long, medium, short) into a pattern. * - * @param string $pattern_or_width_or_skeleton - * - * @return string + * @inheritdoc */ protected function resolve_pattern($pattern_or_width_or_skeleton) { - static $widths = [ 'full', 'long', 'medium', 'short' ]; - - if (in_array($pattern_or_width_or_skeleton, $widths)) - { - return $this->calendar['dateFormats'][$pattern_or_width_or_skeleton]; - } - - return parent::resolve_pattern($pattern_or_width_or_skeleton); + return parent::resolve_pattern($this->resolve_width($pattern_or_width_or_skeleton, 'dateFormats')); } } diff --git a/lib/DateTimeFormatter.php b/lib/DateTimeFormatter.php index 237633f..1036045 100644 --- a/lib/DateTimeFormatter.php +++ b/lib/DateTimeFormatter.php @@ -253,6 +253,26 @@ protected function resolve_pattern($pattern_or_width_or_skeleton) return $pattern; } + /** + * Resolves widths (full, long, medium, short) into a pattern. + * + * @param string $pattern_or_width_or_skeleton + * @param string $from Width Source e.g. "timeFormats". + * + * @return string + */ + protected function resolve_width($pattern_or_width_or_skeleton, $from) + { + static $widths = [ 'full', 'long', 'medium', 'short' ]; + + if (in_array($pattern_or_width_or_skeleton, $widths)) + { + return $this->calendar[$from][$pattern_or_width_or_skeleton]; + } + + return $pattern_or_width_or_skeleton; + } + /* * era (G) */ diff --git a/lib/LocalizedDateTime.php b/lib/LocalizedDateTime.php index 1a3dd7b..14da375 100644 --- a/lib/LocalizedDateTime.php +++ b/lib/LocalizedDateTime.php @@ -44,6 +44,8 @@ */ class LocalizedDateTime extends LocalizedObjectWithFormatter { + static private $format_widths = [ 'full', 'long', 'medium', 'short' ]; + /** * Returns the formatter. * @@ -56,13 +58,9 @@ protected function lazy_get_formatter() public function __get($property) { - switch ($property) + if (strpos($property, 'as_') === 0 && in_array($width = substr($property, 3), self::$format_widths)) { - case 'as_full': - case 'as_long': - case 'as_medium': - case 'as_short': - return call_user_func([ $this, 'format_' . $property ]); + return $this->{ 'format_as_' . $width }(); } try @@ -82,13 +80,9 @@ public function __set($property, $value) public function __call($method, $arguments) { - switch ($method) + if (strpos($method, 'format_as_') === 0 && in_array($width = substr($method, 10), self::$format_widths)) { - case 'format_as_full': - case 'format_as_long': - case 'format_as_medium': - case 'format_as_short': - return $this->format(substr($method, 10)); + return $this->format($width); } return call_user_func_array([ $this->target, $method ], $arguments); @@ -106,7 +100,7 @@ public function __toString() * * @return string */ - public function format($pattern=null) + public function format($pattern = null) { return $this->formatter->format($this->target, $pattern); } diff --git a/lib/NumberFormatter.php b/lib/NumberFormatter.php index a30b8e8..7fdcdd2 100644 --- a/lib/NumberFormatter.php +++ b/lib/NumberFormatter.php @@ -21,8 +21,19 @@ class NumberFormatter use AccessorTrait; use RepositoryPropertyTrait; + static private $default_symbols = [ + + 'decimal' => ".", + 'group' => ",", + 'percentSign' => "%", + 'plusSign' => "+", + 'minusSign' => "-", + 'perMille' => "‰" + + ]; + /** - * Return the precision of a number. + * Returns the precision of a number. * * @param $number * @@ -96,78 +107,27 @@ public function __construct(Repository $repository=null) * * @return string The formatted number. */ - public function format($number, $pattern, array $symbols=[]) + public function format($number, $pattern, array $symbols = []) { if (!($pattern instanceof NumberPattern)) { $pattern = NumberPattern::from($pattern); } - $symbols += [ - - 'decimal' => ".", - 'group' => ",", - 'percentSign' => "%", - 'plusSign' => "+", - 'minusSign' => "-", - 'perMille' => "‰" - - ]; - - # - - $negative = $number < 0; - $number = abs($number * $pattern->multiplier); - - if ($pattern->max_decimal_digits >= 0) - { - $number = round($number, $pattern->max_decimal_digits); - } - - $number = "$number"; - - if (($pos = strpos($number, '.')) !== false) - { - $integer = substr($number, 0, $pos); - $decimal = substr($number, $pos + 1); - } - else - { - $integer = $number; - $decimal = ''; - } - - if ($pattern->decimal_digits > strlen($decimal)) - { - $decimal = str_pad($decimal, $pattern->decimal_digits, '0'); - } + $symbols += self::$default_symbols; - if (strlen($decimal)) - { - $decimal = $symbols['decimal'] . $decimal; - } - - $integer = str_pad($integer, $pattern->integer_digits, '0', STR_PAD_LEFT); - $group_size1 = $pattern->group_size1; + list($integer, $decimal) = $pattern->parse_number($number); - if ($group_size1 > 0 && strlen($integer) > $pattern->group_size1) - { - $group_size2 = $pattern->group_size2; - - $str1 = substr($integer, 0, -$group_size1); - $str2 = substr($integer, -$group_size1); - $size = $group_size2 > 0 ? $group_size2 : $group_size1; - $str1 = str_pad($str1, (int) ((strlen($str1) + $size - 1) / $size) * $size, ' ', STR_PAD_LEFT); - $integer = ltrim(implode($symbols['group'], str_split($str1, $size))) . $symbols['group'] . $str2; - } + $formatted_integer = $pattern->format_integer_with_group($integer, $symbols['group']); + $formatted_number = $pattern->format_integer_with_decimal($formatted_integer, $decimal, $symbols['decimal']); - if ($negative) + if ($number < 0) { - $number = $pattern->negative_prefix . $integer . $decimal . $pattern->negative_suffix; + $number = $pattern->negative_prefix . $formatted_number . $pattern->negative_suffix; } else { - $number = $pattern->positive_prefix . $integer . $decimal . $pattern->positive_suffix; + $number = $pattern->positive_prefix . $formatted_number . $pattern->positive_suffix; } return strtr($number, [ diff --git a/lib/NumberPattern.php b/lib/NumberPattern.php index 0e7e709..612c23e 100644 --- a/lib/NumberPattern.php +++ b/lib/NumberPattern.php @@ -207,4 +207,83 @@ public function __toString() { return $this->pattern; } + + /** + * Parse a number according to the pattern and return its integer and decimal parts. + * + * @param number $number + * + * @return array An array made with the integer and decimal parts of the number. + */ + public function parse_number($number) + { + $number = abs($number * $this->multiplier); + + if ($this->max_decimal_digits >= 0) + { + $number = round($number, $this->max_decimal_digits); + } + + $number = "$number"; + + if (($pos = strpos($number, '.')) !== false) + { + return [ substr($number, 0, $pos), substr($number, $pos + 1) ]; + } + + return [ $number, '' ]; + } + + /** + * Formats integer according to group pattern. + * + * @param int $integer + * @param string $group_symbol + * + * @return string + */ + public function format_integer_with_group($integer, $group_symbol) + { + $integer = str_pad($integer, $this->integer_digits, '0', STR_PAD_LEFT); + $group_size1 = $this->group_size1; + + if ($group_size1 < 1 || strlen($integer) <= $this->group_size1) + { + return $integer; + } + + $group_size2 = $this->group_size2; + + $str1 = substr($integer, 0, -$group_size1); + $str2 = substr($integer, -$group_size1); + $size = $group_size2 > 0 ? $group_size2 : $group_size1; + $str1 = str_pad($str1, (int) ((strlen($str1) + $size - 1) / $size) * $size, ' ', STR_PAD_LEFT); + + return ltrim(implode($group_symbol, str_split($str1, $size))) . $group_symbol . $str2; + } + + /** + * Formats an integer with a decimal. + * + * @param string|int $integer An integer, or a formatted integer as returned by + * {@link format_integer_with_group}. + * @param string $decimal + * @param string $decimal_symbol + * + * @return string + */ + public function format_integer_with_decimal($integer, $decimal, $decimal_symbol) + { + if ($this->decimal_digits > strlen($decimal)) + { + $decimal = str_pad($decimal, $this->decimal_digits, '0'); + } + + if (strlen($decimal)) + { + $decimal = $decimal_symbol . $decimal; + } + + return "$integer" . $decimal; + } } diff --git a/lib/PredisProvider.php b/lib/RedisProvider.php similarity index 73% rename from lib/PredisProvider.php rename to lib/RedisProvider.php index 43d18f8..d373e91 100644 --- a/lib/PredisProvider.php +++ b/lib/RedisProvider.php @@ -11,31 +11,26 @@ namespace ICanBoogie\CLDR; -use Predis\Client as Client; - /** * Provides CLDR data from a Redis client, and falls back to a specified provider when the data * is not available. * * @package ICanBoogie\CLDR */ -class PredisProvider implements ProviderInterface, CacheInterface +class RedisProvider implements ProviderInterface, CacheInterface { use ProviderChainTrait; - /** - * @var Client - */ - private $client; + private $redis; /** * @param ProviderInterface $provider Fallback provider. - * @param Client $client + * @param object $redis */ - public function __construct(ProviderInterface $provider, Client $client) + public function __construct(ProviderInterface $provider, $redis) { $this->provider = $provider; - $this->client = $client; + $this->redis = $redis; } /** @@ -59,7 +54,7 @@ private function path_to_key($path) */ public function exists($key) { - return $this->client->exists($this->path_to_key($key)); + return $this->redis->exists($this->path_to_key($key)); } /** @@ -76,7 +71,7 @@ public function retrieve($key) return null; } - return json_decode($this->client->get($this->path_to_key($key)), true); + return json_decode($this->redis->get($this->path_to_key($key)), true); } /** @@ -87,6 +82,6 @@ public function retrieve($key) */ public function store($key, $data) { - $this->client->set($this->path_to_key($key), json_encode($data)); + $this->redis->set($this->path_to_key($key), json_encode($data)); } } diff --git a/lib/Territory.php b/lib/Territory.php index 98a55f6..61516b2 100644 --- a/lib/Territory.php +++ b/lib/Territory.php @@ -64,14 +64,18 @@ public function __get($property) } /** - * Return the `territoryContainment` data. + * Retrieve a territory section from supplemental. * - * @return array + * @param string $section + * + * @return mixed + * + * @throws NoTerritoryData in attempt to retrieve data that is not defined for a territory. */ - protected function lazy_get_containment() + private function retrieve_from_supplemental($section) { $code = $this->code; - $data = $this->repository->supplemental['territoryContainment']; + $data = $this->repository->supplemental[$section]; if (empty($data[$code])) { @@ -81,6 +85,16 @@ protected function lazy_get_containment() return $data[$code]; } + /** + * Return the `territoryContainment` data. + * + * @return array + */ + protected function lazy_get_containment() + { + return $this->retrieve_from_supplemental('territoryContainment'); + } + /** * Returns the currencies used throughout the history of the territory. * @@ -114,28 +128,39 @@ protected function lazy_get_currency() * * @return Currency */ - public function currency_at($date=null) + public function currency_at($date = null) { - if (!$date) + $code = $this->find_currency_at($this->currencies, DateTime::from($date ?: 'now')->as_date); + + if (!$code) { - $date = 'now'; + return null; } - $date = DateTime::from($date)->as_date; - $currencies = $this->currencies; + return new Currency($this->repository, $code); + } + + /** + * Return the currency in a list used at a point in time. + * + * @param array $currencies + * @param string $normalized_date + * + * @return string + */ + private function find_currency_at(array $currencies, $normalized_date) + { $rc = false; foreach ($currencies as $currency) { $name = key($currency); $interval = current($currency); + $_from = null; + $_to = null; + extract($interval); - if (isset($interval['_from']) && $interval['_from'] > $date) - { - continue; - } - - if (isset($interval['_to']) && $interval['_to'] < $date) + if (($_from && $_from > $normalized_date) || ($_to && $_to < $normalized_date)) { continue; } @@ -143,12 +168,7 @@ public function currency_at($date=null) $rc = $name; } - if (!$rc) - { - return $rc; - } - - return new Currency($this->repository, $rc); + return $rc; } /* @@ -207,15 +227,7 @@ protected function get_weekend_end() */ protected function lazy_get_info() { - $code = $this->code; - $data = $this->repository->supplemental['territoryInfo']; - - if (empty($data[$code])) - { - throw new NoTerritoryData; - } - - return $data[$code]; + return $this->retrieve_from_supplemental('territoryInfo'); } /** diff --git a/lib/TimeFormatter.php b/lib/TimeFormatter.php index 65c1b11..6e1330b 100644 --- a/lib/TimeFormatter.php +++ b/lib/TimeFormatter.php @@ -41,19 +41,10 @@ class TimeFormatter extends DateTimeFormatter /** * Resolves widths defined in `timeFormats` (full, long, medium, short) into a pattern. * - * @param string $pattern_or_width_or_skeleton - * - * @return string + * @inheritdoc */ protected function resolve_pattern($pattern_or_width_or_skeleton) { - static $widths = [ 'full', 'long', 'medium', 'short' ]; - - if (in_array($pattern_or_width_or_skeleton, $widths)) - { - return $this->calendar['timeFormats'][$pattern_or_width_or_skeleton]; - } - - return parent::resolve_pattern($pattern_or_width_or_skeleton); + return parent::resolve_pattern($this->resolve_width($pattern_or_width_or_skeleton, 'timeFormats')); } } diff --git a/lib/WebProvider.php b/lib/WebProvider.php index d96f5a4..2488a66 100644 --- a/lib/WebProvider.php +++ b/lib/WebProvider.php @@ -16,7 +16,8 @@ */ class WebProvider implements ProviderInterface { - protected $origin; + private $origin; + private $connection; /** * Initializes the {@link $origin} property. @@ -28,6 +29,30 @@ public function __construct($origin="http://www.unicode.org/repos/cldr-aux/json/ $this->origin = $origin; } + /** + * Returns a reusable cURL connection. + * + * @return resource + */ + private function obtain_connection() + { + if ($this->connection) + { + return $this->connection; + } + + $connection = curl_init(); + + curl_setopt_array($connection, [ + + CURLOPT_FAILONERROR => true, + CURLOPT_RETURNTRANSFER => 1 + + ]); + + return $this->connection = $connection; + } + /** * The section path, following the pattern "/
". * @@ -39,20 +64,13 @@ public function __construct($origin="http://www.unicode.org/repos/cldr-aux/json/ */ public function provide($path) { - $ch = curl_init($this->origin . $path . '.json'); - - curl_setopt_array($ch, [ - - CURLOPT_FAILONERROR => true, - CURLOPT_RETURNTRANSFER => 1 - - ]); + $connection = $this->obtain_connection(); - $rc = curl_exec($ch); + curl_setopt($connection, CURLOPT_URL, $this->origin . $path . '.json'); - $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $rc = curl_exec($connection); - curl_close($ch); + $http_code = curl_getinfo($connection, CURLINFO_HTTP_CODE); if ($http_code != 200) {