diff --git a/README.md b/README.md index 604c204..8317553 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,7 @@ The __CLDR__ package provides means to internationalize your application by leve data and conventions defined by the [Unicode Common Locale Data Repository](http://cldr.unicode.org/) (CLDR). The package targets the [CLDR version 26](http://cldr.unicode.org/index/downloads/cldr-26), from -which data is retrieved when required. A stack of cache can be used to store and retrieve these -data. +which data is retrieved when required. @@ -13,25 +12,28 @@ data. ## Instantiating the repository -The CLDR is represented by a [Repository][] instance, from which you can access -the available locales or supplemental data. When necessary, the repository fetches the required -data through a provider. You can use the default provider, or define your own. +The CLDR is represented by a [Repository][] instance, from which data is accessed. When required, +data is retrieved through a provider, and in order to avoid hitting the web with every request, +a stack of them is used. -The following example demonstrates how such a repository is instantiated and how the -available locales and supplemental data are accessed: +The following example demonstrates how a repository can be instantiated with a nice stack of +providers. One fetches the data from the web, the other from the filesystem, and the last one +from the runtime memory: ```php locales['en']; $french_locale = $repository->locales['fr']; diff --git a/lib/FileCache.php b/lib/FileCache.php deleted file mode 100644 index 90d9794..0000000 --- a/lib/FileCache.php +++ /dev/null @@ -1,67 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace ICanBoogie\CLDR; - -/** - * A cache that persists using files. - * - *
- * - */ -class FileCache implements CacheInterface -{ - protected $root; - - static private function path_to_key($path) - { - return str_replace('/', '--', $path); - } - - public function __construct($root) - { - $this->root = rtrim($root, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; - } - - public function exists($path) - { - $key = self::path_to_key($path); - $filename = $this->root . $key; - - return file_exists($filename); - } - - public function retrieve($path) - { - $key = self::path_to_key($path); - $filename = $this->root . $key; - - if (!file_exists($filename)) - { - return; - } - - return file_get_contents($filename); - } - - public function store($path, $data) - { - $key = self::path_to_key($path); - $filename = $this->root . $key; - - file_put_contents($filename, $data); - } -} diff --git a/lib/FileProvider.php b/lib/FileProvider.php new file mode 100644 index 0000000..785c382 --- /dev/null +++ b/lib/FileProvider.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ICanBoogie\CLDR; + +/** + * Provides CLDR data from the filesystem, and falls back to a specified provider when the data + * is not available. + */ +class FileProvider implements ProviderInterface, CacheInterface +{ + /** + * Create a store key from a CLDR path. + * + * @param string $path A CLDR path. + * + * @return string A store key. + */ + static private function path_to_key($path) + { + return str_replace('/', '--', $path); + } + + /** + * The directory where files are stored. + * + * @var string + */ + protected $root; + + /** + * @var ProviderInterface + */ + protected $provider; + + /** + * @param ProviderInterface $provider Fallback provider. + * @param string $directory Path to the directory where cached files are stored. + */ + public function __construct(ProviderInterface $provider, $directory) + { + $this->root = rtrim($directory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; + $this->provider = $provider; + } + + public function exists($path) + { + $key = self::path_to_key($path); + $filename = $this->root . $key; + + return file_exists($filename); + } + + public function retrieve($path) + { + $key = self::path_to_key($path); + $filename = $this->root . $key; + + if (!file_exists($filename)) + { + return; + } + + return json_decode(file_get_contents($filename), true); + } + + public function store($path, $data) + { + $key = self::path_to_key($path); + $filename = $this->root . $key; + + file_put_contents($filename, json_encode($data)); + } + + /** + * The section path, following the pattern "* - * @property-read Provider $provider A CLDR provider. + * @property-read ProviderInterface $provider A CLDR provider. * @property-read LocaleCollection $locales Locale collection. * @property-read Supplemental $supplemental Representation of the "supplemental" section. * @property-read TerritoryCollection $territories Territory collection. @@ -107,9 +107,9 @@ protected function get_currencies() /** * Initializes the {@link $provider} property. * - * @param Provider $provider + * @param ProviderInterface $provider */ - public function __construct(Provider $provider) + public function __construct(ProviderInterface $provider) { $this->provider = $provider; } @@ -129,7 +129,7 @@ public function __get($property) /** * Fetches the data available at the specified path. * - * Note: The method is forwarded to {@link Provider::fetch}. + * Note: The method is forwarded to {@link Provider::provide}. * * @param string $path * @@ -137,6 +137,6 @@ public function __get($property) */ public function fetch($path) { - return $this->provider->fetch($path); + return $this->provider->provide($path); } } diff --git a/lib/RunTimeCache.php b/lib/RunTimeCache.php deleted file mode 100644 index 033fb30..0000000 --- a/lib/RunTimeCache.php +++ /dev/null @@ -1,59 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace ICanBoogie\CLDR; - -/** - * A cache that only persists during the run time. - * - * This cache is meant to be used as a wrapper to more persistent cache: - * - */ ". + * + * @param string $path + * + * @throws ResourceNotFound when the specified path does not exists on the CLDR source. + * + * @return string + */ + public function provide($path) + { + if ($this->exists($path)) + { + return $this->retrieve($path); + } + + $data = $this->provider->provide($path); + $this->store($path, $data); + + return $data; + } +} diff --git a/lib/Locale.php b/lib/Locale.php index 15fdb1f..26517ff 100644 --- a/lib/Locale.php +++ b/lib/Locale.php @@ -189,7 +189,7 @@ public function offsetGet($offset) throw new OffsetNotDefined(array($offset, $this)); } - $data = $this->repository->provider->fetch("main/{$this->code}/{$offset}"); + $data = $this->repository->fetch("main/{$this->code}/{$offset}"); $path = "main/{$this->code}/" . self::$available_sections[$offset]; $path_parts = explode('/', $path); diff --git a/lib/Provider.php b/lib/Provider.php deleted file mode 100644 index 861b1ce..0000000 --- a/lib/Provider.php +++ /dev/null @@ -1,59 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace ICanBoogie\CLDR; - -/** - * CLDR data provide. - * - * @package ICanBoogie\CLDR - */ -class Provider -{ - protected $cache; - protected $retriever; - - public function __construct(CacheInterface $cache, $retriever) - { - $this->cache = $cache; - $this->retriever = $retriever; - } - - /** - * Fetches the data available at the specified path. - * - * @param string $path - * - * @return array - */ - public function fetch($path) - { - $json = $this->cache->retrieve($path); - - if (!$json) - { - $retriever = $this->retriever; - $json = $retriever($path); - - if ($json) - { - $this->cache->store($path, $json); - } - } - - return json_decode($json, true); - } - - public function __invoke($path) - { - return $this->fetch($path); - } -} diff --git a/lib/ProviderInterface.php b/lib/ProviderInterface.php new file mode 100644 index 0000000..896a4db --- /dev/null +++ b/lib/ProviderInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ICanBoogie\CLDR; + +/** + * An interface for classes that can provide CLDR data. + * + * @package ICanBoogie\CLDR + */ +interface ProviderInterface +{ + /** + * The section path, following the pattern " / ". + * + * @param string $path + * + * @throws ResourceNotFound when the specified path does not exists on the CLDR source. + * + * @return string + */ + public function provide($path); +} diff --git a/lib/Repository.php b/lib/Repository.php index ef98c38..e2aff36 100644 --- a/lib/Repository.php +++ b/lib/Repository.php @@ -27,7 +27,7 @@ * var_dump($repository->territories['FR']); *
- * - */ -class RunTimeCache implements CacheInterface -{ - private $cache = array(); - private $static_cache; - - public function __construct(CacheInterface $static_cache) - { - $this->static_cache = $static_cache; - } - - public function exists($path) - { - return array_key_exists($path, $this->cache); - } - - public function retrieve($path) - { - if (array_key_exists($path, $this->cache)) - { - return $this->cache[$path]; - } - - return $this->cache[$path] = $this->static_cache->retrieve($path); - } - - public function store($path, $data) - { - $this->cache[$path] = $data; - - $this->static_cache->store($path, $data); - } -} diff --git a/lib/RunTimeProvider.php b/lib/RunTimeProvider.php new file mode 100644 index 0000000..796d290 --- /dev/null +++ b/lib/RunTimeProvider.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ICanBoogie\CLDR; + +/** + * Provides CLDR data from an array, and falls back to a specified provider when the data + * is not available. + */ +class RunTimeProvider implements ProviderInterface, CacheInterface +{ + private $store = array(); + private $provider; + + /** + * @param ProviderInterface $provider Fallback provider. + */ + public function __construct(ProviderInterface $provider) + { + $this->provider = $provider; + } + + public function exists($path) + { + return array_key_exists($path, $this->store); + } + + public function retrieve($path) + { + if (!$this->exists($path)) + { + return; + } + + return $this->store[ $path ]; + } + + public function store($path, $data) + { + $this->store[$path] = $data; + } + + /** + * The section path, following the pattern "/ ". + * + * @param string $path + * + * @throws ResourceNotFound when the specified path does not exists on the CLDR source. + * + * @return string + */ + public function provide($path) + { + if ($this->exists($path)) + { + return $this->retrieve($path); + } + + $data = $this->provider->provide($path); + $this->store($path, $data); + + return $data; + } +} diff --git a/lib/Supplemental.php b/lib/Supplemental.php index 90268e4..3cb069f 100644 --- a/lib/Supplemental.php +++ b/lib/Supplemental.php @@ -96,7 +96,7 @@ public function offsetGet($offset) throw new OffsetNotDefined(array($offset, $this)); } - $data = $this->repository->provider->fetch("supplemental/{$offset}"); + $data = $this->repository->fetch("supplemental/{$offset}"); $path = 'supplemental/' . self::$available_sections[$offset]; $path_parts = explode('/', $path); diff --git a/lib/Retriever.php b/lib/WebProvider.php similarity index 75% rename from lib/Retriever.php rename to lib/WebProvider.php index 5dcab99..cb6b809 100644 --- a/lib/Retriever.php +++ b/lib/WebProvider.php @@ -14,21 +14,18 @@ /** * Retrieves sections from the CLDR source. */ -class Retriever +class WebProvider implements ProviderInterface { - protected $origin = 'http://www.unicode.org/repos/cldr-aux/json/26/'; + protected $origin; /** - * Initializes the {@link $origin} property if the `$origin` param is specified. + * Initializes the {@link $origin} property. * * @param string $origin */ - public function __construct($origin=null) + public function __construct($origin="http://www.unicode.org/repos/cldr-aux/json/26/") { - if ($origin) - { - $this->origin = $origin; - } + $this->origin = $origin; } /** @@ -40,7 +37,7 @@ public function __construct($origin=null) * * @return string */ - public function __invoke($path) + public function provide($path) { $ch = curl_init($this->origin . $path . '.json'); @@ -64,6 +61,6 @@ public function __invoke($path) throw new ResourceNotFound($path); } - return $rc; + return json_decode($rc, true); } } diff --git a/tests/FileProviderTest.php b/tests/FileProviderTest.php new file mode 100644 index 0000000..61c2ab5 --- /dev/null +++ b/tests/FileProviderTest.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ICanBoogie\CLDR; + +class FileProviderTest extends \PHPUnit_Framework_TestCase +{ + static private $directory; + + static public function setupBeforeClass() + { + self::$directory = __DIR__ . DIRECTORY_SEPARATOR . 'repository' . DIRECTORY_SEPARATOR; + } + + public function test_provider() + { + $path = 'some/path'; + $root = self::$directory; + + if (file_exists($root . 'some--path')) + { + unlink($root . 'some--path'); + } + + $expected = array('from http' => true ); + + $stub = $this->getMockBuilder('ICanBoogie\CLDR\WebProvider') + ->getMock(); + + $stub + ->expects($this->once()) + ->method('provide') + ->willReturn($expected); + + $provider = new FileProvider($stub, $root); + + for ($i = 0 ; $i < 5 ; $i++) + { + $data = $provider->provide($path); + $this->assertSame($expected, $data); + $this->assertTrue($provider->exists($path)); + } + } +} diff --git a/tests/ProviderTest.php b/tests/ProviderTest.php deleted file mode 100644 index 49c8e56..0000000 --- a/tests/ProviderTest.php +++ /dev/null @@ -1,37 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace ICanBoogie\CLDR; - -class ProviderTest extends \PHPUnit_Framework_TestCase -{ - static private $provider; - - static public function setupBeforeClass() - { - self::$provider = new Provider(new RunTimeCache(new FileCache(__DIR__ . '/repository')), new Retriever); - } - - public function test_sections() - { - $data = self::$provider->fetch('main/fr/ca-gregorian'); - - $this->assertInternalType('array', $data); - $this->assertArrayHasKey('main', $data); - } - - public function test_supplement() - { - $data = self::$provider->fetch('supplemental/calendarPreferenceData'); - $this->assertInternalType('array', $data); - $this->assertArrayHasKey('supplemental', $data); - } -} diff --git a/tests/RepositoryTest.php b/tests/RepositoryTest.php index 21de0a9..88d370f 100644 --- a/tests/RepositoryTest.php +++ b/tests/RepositoryTest.php @@ -34,7 +34,7 @@ public function provide_test_properties_type() array( 'currencies', 'ICanBoogie\CLDR\CurrencyCollection' ), array( 'locales', 'ICanBoogie\CLDR\LocaleCollection' ), - array( 'provider', 'ICanBoogie\CLDR\Provider' ), + array( 'provider', 'ICanBoogie\CLDR\ProviderInterface' ), array( 'supplemental', 'ICanBoogie\CLDR\Supplemental' ), array( 'territories', 'ICanBoogie\CLDR\TerritoryCollection' ), diff --git a/tests/RunTimeProviderTest.php b/tests/RunTimeProviderTest.php new file mode 100644 index 0000000..560fdde --- /dev/null +++ b/tests/RunTimeProviderTest.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ICanBoogie\CLDR; + +class RunTimeProviderTest extends \PHPUnit_Framework_TestCase +{ + public function test_provider() + { + $path = 'some/path'; + $expected = array('from http' => true ); + + $stub = $this->getMockBuilder('ICanBoogie\CLDR\WebProvider') + ->getMock(); + + $stub + ->expects($this->once()) + ->method('provide') + ->willReturn($expected); + + $provider = new RunTimeProvider($stub); + + for ($i = 0 ; $i < 5 ; $i++) + { + $data = $provider->provide($path); + $this->assertSame($expected, $data); + $this->assertTrue($provider->exists($path)); + } + } +} diff --git a/tests/RetrieverTest.php b/tests/WebProviderTest.php similarity index 59% rename from tests/RetrieverTest.php rename to tests/WebProviderTest.php index 3d3f0f5..d5ae738 100644 --- a/tests/RetrieverTest.php +++ b/tests/WebProviderTest.php @@ -11,16 +11,14 @@ namespace ICanBoogie\CLDR; -class RetrieverTest extends \PHPUnit_Framework_TestCase +class WebProviderTest extends \PHPUnit_Framework_TestCase { - public function test_retrieve_ok() + public function test_provide_ok() { - $r = new Retriever; + $provider = new WebProvider; - $json = $r('main/fr/characters'); - $this->assertStringStartsWith('{', $json); - - $data = json_decode($json, true); + $data = $provider->provide('main/fr/characters'); + $this->assertInternalType('array', $data); $this->assertArrayHasKey('main', $data); } @@ -29,7 +27,8 @@ public function test_retrieve_ok() */ public function test_retrieve_failure() { - $r = new Retriever; - $r('undefined_locale/characters'); + $provider = new WebProvider; + + $provider->provide('undefined_locale/characters'); } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 82ebd52..b4061b8 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -27,8 +27,7 @@ function get_repository() if (!$repository) { - $provider = new Provider(new RunTimeCache(new FileCache(__DIR__ . '/repository')), new Retriever); - $repository = new Repository($provider); + $repository = new Repository(new RunTimeProvider(new FileProvider(new WebProvider, __DIR__ . '/repository'))); } return $repository;