├── .gitignore ├── src ├── tests │ ├── bootstrap.php │ └── Herrera │ │ └── Wise │ │ └── Tests │ │ ├── Exception │ │ ├── Exception.php │ │ └── AbstractExceptionTest.php │ │ ├── NeverSupportedProcessor.php │ │ ├── Loader │ │ ├── ExampleFileLoader.php │ │ ├── IniFileLoaderTest.php │ │ ├── YamlFileLoaderTest.php │ │ ├── PhpFileLoaderTest.php │ │ ├── JsonFileLoaderTest.php │ │ ├── XmlFileLoaderTest.php │ │ ├── LoaderResolverTest.php │ │ └── AbstractFileLoaderTest.php │ │ ├── BasicProcessor.php │ │ ├── Processor │ │ ├── ExampleProcessor.php │ │ ├── TestProcessor.php │ │ ├── AbstractProcessorTest.php │ │ ├── ProcessorResolverTest.php │ │ └── DelegatingProcessorTest.php │ │ ├── Resource │ │ └── ResourceCollectorTest.php │ │ ├── Util │ │ └── ArrayUtilTest.php │ │ └── WiseTest.php └── lib │ └── Herrera │ └── Wise │ ├── Exception │ ├── LogicException.php │ ├── ExceptionInterface.php │ ├── ImportException.php │ ├── LoaderException.php │ ├── ProcessorException.php │ ├── InvalidArgumentException.php │ ├── InvalidReferenceException.php │ └── AbstractException.php │ ├── WiseAwareInterface.php │ ├── Processor │ ├── ProcessorResolverInterface.php │ ├── AbstractProcessor.php │ ├── ProcessorInterface.php │ ├── DelegatingProcessor.php │ └── ProcessorResolver.php │ ├── Resource │ ├── ResourceAwareInterface.php │ ├── ResourceCollectorInterface.php │ └── ResourceCollector.php │ ├── Loader │ ├── IniFileLoader.php │ ├── PhpFileLoader.php │ ├── YamlFileLoader.php │ ├── JsonFileLoader.php │ ├── LoaderResolver.php │ ├── XmlFileLoader.php │ └── AbstractFileLoader.php │ ├── Util │ └── ArrayUtil.php │ └── Wise.php ├── .travis.yml ├── doc ├── 00-Installing.md ├── 03-HowToCreateAProcessor.md ├── 02-HowToCreateALoader.md └── 01-Usage.md ├── phpunit.xml.dist ├── README.md ├── LICENSE ├── composer.json └── res └── schema.xsd /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | /bin/ 3 | /coverage/ 4 | /src/vendors/ 5 | 6 | /composer.lock -------------------------------------------------------------------------------- /src/tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | add(null, __DIR__); 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.3 5 | - 5.4 6 | - 5.5 7 | 8 | before_script: 9 | - composer self-update 10 | - composer install --dev --no-interaction --prefer-source 11 | 12 | script: bin/phpunit -------------------------------------------------------------------------------- /src/tests/Herrera/Wise/Tests/Exception/Exception.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class LogicException extends AbstractException 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Exception/ExceptionInterface.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | interface ExceptionInterface 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Exception/ImportException.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class ImportException extends AbstractException 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Exception/LoaderException.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class LoaderException extends AbstractException 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Exception/ProcessorException.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class ProcessorException extends AbstractException 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Exception/InvalidArgumentException.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class InvalidArgumentException extends AbstractException 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Exception/InvalidReferenceException.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class InvalidReferenceException extends AbstractException 11 | { 12 | } 13 | -------------------------------------------------------------------------------- /src/tests/Herrera/Wise/Tests/NeverSupportedProcessor.php: -------------------------------------------------------------------------------- 1 | assertEquals( 12 | 'Test message.', 13 | Exception::format('%s message.', 'Test')->getMessage() 14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/tests/Herrera/Wise/Tests/Loader/ExampleFileLoader.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | interface WiseAwareInterface 11 | { 12 | /** 13 | * Returns the Wise instance. 14 | * 15 | * @return Wise The instance. 16 | */ 17 | public function getWise(); 18 | 19 | /** 20 | * Sets a Wise instance. 21 | * 22 | * @param Wise $wise An instance. 23 | */ 24 | public function setWise(Wise $wise); 25 | } 26 | -------------------------------------------------------------------------------- /doc/00-Installing.md: -------------------------------------------------------------------------------- 1 | Installing 2 | ========== 3 | 4 | Composer 5 | -------- 6 | 7 | The easiest way to install Wise is by using [Composer][]: 8 | 9 | $ composer require herrera-io/wise=~1.0 10 | 11 | You may then load it by requiring the Composer autoloader: 12 | 13 | ```php 14 | require 'vendor/autoload.php'; 15 | ``` 16 | 17 | PSR-0 18 | ----- 19 | 20 | You may use any class loader that supports [PSR-0][]. 21 | 22 | ```php 23 | $loader = new SplClassLoader(); 24 | $loader->add('Herrera\\Wise', 'src/lib'); 25 | ``` 26 | 27 | [Composer]: http://getcomposer.org/ 28 | [PSR-0]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Processor/ProcessorResolverInterface.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | interface ProcessorResolverInterface 11 | { 12 | /** 13 | * Returns the processor able to handle the resource. 14 | * 15 | * @param mixed $resource The resource. 16 | * @param string $type The resource type. 17 | * 18 | * @return ProcessorInterface|boolean The processor, or FALSE if none. 19 | */ 20 | public function resolve($resource, $type = null); 21 | } 22 | -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Resource/ResourceAwareInterface.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | interface ResourceAwareInterface 11 | { 12 | /** 13 | * Returns the resource collector. 14 | * 15 | * @return ResourceCollectorInterface The collector. 16 | */ 17 | public function getResourceCollector(); 18 | 19 | /** 20 | * Sets the resource collector. 21 | * 22 | * @param ResourceCollectorInterface $collector The collector. 23 | */ 24 | public function setResourceCollector(ResourceCollectorInterface $collector); 25 | } 26 | -------------------------------------------------------------------------------- /src/tests/Herrera/Wise/Tests/BasicProcessor.php: -------------------------------------------------------------------------------- 1 | root('root'); 14 | 15 | $root->children() 16 | ->booleanNode('enabled') 17 | ->defaultFalse() 18 | ->end() 19 | ->integerNode('number')->end() 20 | ->end(); 21 | 22 | return $builder; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Loader/IniFileLoader.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class IniFileLoader extends AbstractFileLoader 11 | { 12 | /** 13 | * {@inheritDoc} 14 | */ 15 | public function supports($resource, $type = null) 16 | { 17 | return (is_string($resource) 18 | && ('ini' === strtolower(pathinfo($resource, PATHINFO_EXTENSION)))) 19 | && ((null === $type) || ('ini' === $type)); 20 | } 21 | 22 | /** 23 | * @override 24 | */ 25 | protected function doLoad($file) 26 | { 27 | return parse_ini_file($file, true); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | src/lib/ 15 | 16 | 17 | 18 | 19 | src/tests/ 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Loader/PhpFileLoader.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class PhpFileLoader extends AbstractFileLoader 11 | { 12 | /** 13 | * {@inheritDoc} 14 | */ 15 | public function supports($resource, $type = null) 16 | { 17 | return (is_string($resource) 18 | && ('php' === strtolower(pathinfo($resource, PATHINFO_EXTENSION)))) 19 | && ((null === $type) || ('php' === $type)); 20 | } 21 | 22 | /** 23 | * @override 24 | */ 25 | protected function doLoad($file) 26 | { 27 | /** @noinspection PhpIncludeInspection */ 28 | return require $file; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Loader/YamlFileLoader.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class YamlFileLoader extends AbstractFileLoader 13 | { 14 | /** 15 | * {@inheritDoc} 16 | */ 17 | public function supports($resource, $type = null) 18 | { 19 | return (is_string($resource) 20 | && ('yml' === strtolower(pathinfo($resource, PATHINFO_EXTENSION)))) 21 | && ((null === $type) || ('yaml' === $type)); 22 | } 23 | 24 | /** 25 | * @override 26 | */ 27 | protected function doLoad($file) 28 | { 29 | return Parser::parse($file); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/tests/Herrera/Wise/Tests/Processor/ExampleProcessor.php: -------------------------------------------------------------------------------- 1 | root('example'); 14 | 15 | $root->children() 16 | ->booleanNode('enabled') 17 | ->defaultFalse() 18 | ->end() 19 | ->end(); 20 | 21 | return $builder; 22 | } 23 | 24 | public function supports($resource, $type = null) 25 | { 26 | return is_array($resource) 27 | && ('example' === $type); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/tests/Herrera/Wise/Tests/Processor/TestProcessor.php: -------------------------------------------------------------------------------- 1 | root('root'); 14 | 15 | $root->children() 16 | ->booleanNode('enabled') 17 | ->defaultFalse() 18 | ->end() 19 | ->integerNode('number')->end() 20 | ->end(); 21 | 22 | return $builder; 23 | } 24 | 25 | public function supports($resource, $type = null) 26 | { 27 | return ((null === $type) || ('php' === $type)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Exception/AbstractException.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | abstract class AbstractException extends Exception implements ExceptionInterface 13 | { 14 | /** 15 | * Creates an exception using a formatted message. 16 | * 17 | * @param string $format The message format. 18 | * @param mixed $value,... A value. 19 | * 20 | * @return static The exception. 21 | */ 22 | public static function format($format, $value = null) 23 | { 24 | if (1 < func_num_args()) { 25 | $format = vsprintf($format, array_slice(func_get_args(), 1)); 26 | } 27 | 28 | return new static($format); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Resource/ResourceCollectorInterface.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | interface ResourceCollectorInterface 13 | { 14 | /** 15 | * Adds a resource to the collection. 16 | * 17 | * @param ResourceInterface $resource A resource. 18 | */ 19 | public function addResource(ResourceInterface $resource); 20 | 21 | /** 22 | * Removes all resources in the collection. 23 | */ 24 | public function clearResources(); 25 | 26 | /** 27 | * Returns all of the resources in the collection. 28 | * 29 | * @return ResourceInterface[] The resources. 30 | */ 31 | public function getResources(); 32 | } 33 | -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Resource/ResourceCollector.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class ResourceCollector implements ResourceCollectorInterface 13 | { 14 | /** 15 | * The collection of resources. 16 | * 17 | * @var ResourceInterface[] 18 | */ 19 | private $resources = array(); 20 | 21 | /** 22 | * {@inheritDoc} 23 | */ 24 | public function addResource(ResourceInterface $resource) 25 | { 26 | $this->resources[] = $resource; 27 | } 28 | 29 | /** 30 | * {@inheritDoc} 31 | */ 32 | public function clearResources() 33 | { 34 | $this->resources = array(); 35 | } 36 | 37 | /** 38 | * {@inheritDoc} 39 | */ 40 | public function getResources() 41 | { 42 | return $this->resources; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Wise 2 | ==== 3 | 4 | [![Build Status]](http://travis-ci.org/herrera-io/php-wise) 5 | 6 | Wise is a configuration manager built on Symfony [Config][]. It supports data 7 | formats such as INI, JSON, PHP (native), XML, and YAML. You can also normalize 8 | and validate configuration data. 9 | 10 | ```php 11 | $wise = Herrera\Wise\Wise::create('/path/to/config'); 12 | $data = $wise->load('example.yml'); 13 | ``` 14 | 15 | Forks 16 | ----- 17 | 18 | As this project is no longer maintained, please see the [Wiki page for maintained forks](https://github.com/kherge-abandoned/php-wise/wiki/Forks). 19 | 20 | Documentation 21 | ------------- 22 | 23 | - [Installing][] 24 | - [Usage][] 25 | - [How to Create a Loader][] 26 | 27 | [Build Status]: https://secure.travis-ci.org/herrera-io/php-wise.png?branch=master 28 | [Config]: http://symfony.com/doc/current/components/config/index.html 29 | [How to Create a Loader]: doc/02-HowToCreateALoader.md 30 | [Installing]: doc/00-Installing.md 31 | [Usage]: doc/01-Usage.md 32 | -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Processor/AbstractProcessor.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | abstract class AbstractProcessor implements ProcessorInterface 13 | { 14 | /** 15 | * The processor resolver. 16 | * 17 | * @var ProcessorResolverInterface 18 | */ 19 | private $resolver; 20 | 21 | /** 22 | * {@inheritDoc} 23 | */ 24 | public function getResolver() 25 | { 26 | return $this->resolver; 27 | } 28 | 29 | /** 30 | * {@inheritDoc} 31 | */ 32 | public function process(array $data) 33 | { 34 | $processor = new Processor(); 35 | 36 | return $processor->processConfiguration($this, $data); 37 | } 38 | 39 | /** 40 | * {@inheritDoc} 41 | */ 42 | public function setResolver(ProcessorResolverInterface $resolver) 43 | { 44 | $this->resolver = $resolver; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Kevin Herrera 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Loader/JsonFileLoader.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class JsonFileLoader extends AbstractFileLoader 14 | { 15 | /** 16 | * The JSON parser. 17 | * 18 | * @var Parser 19 | */ 20 | private $json; 21 | 22 | /** 23 | * @override 24 | */ 25 | public function __construct(FileLocatorInterface $locator) 26 | { 27 | parent::__construct($locator); 28 | 29 | $this->json = new Parser(); 30 | } 31 | 32 | /** 33 | * {@inheritDoc} 34 | */ 35 | public function supports($resource, $type = null) 36 | { 37 | return (is_string($resource) 38 | && ('json' === strtolower(pathinfo($resource, PATHINFO_EXTENSION)))) 39 | && ((null === $type) || ('json' === $type)); 40 | } 41 | 42 | /** 43 | * @override 44 | */ 45 | protected function doLoad($file) 46 | { 47 | return $this->json->decodeFile($file, true); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "herrera-io/wise", 3 | "description": "Symfony Config for everyone else.", 4 | "keywords": ["config"], 5 | "homepage": "http://github.com/herrera-io/php-wise", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Kevin Herrera", 10 | "email": "kevin@herrera.io", 11 | "homepage": "http://kevin.herrera.io" 12 | } 13 | ], 14 | "support": { 15 | "issues": "https://github.com/herrera-io/php-wise/issues" 16 | }, 17 | "require": { 18 | "php": ">=5.3.3", 19 | "symfony/config": "~2.2" 20 | }, 21 | "require-dev": { 22 | "herrera-io/json": "~1.0", 23 | "herrera-io/phpunit-test-case": "1.*", 24 | "phpunit/phpunit": "3.7.*", 25 | "symfony/yaml": "~2.2" 26 | }, 27 | "suggest": { 28 | "ext-dom": "For parsing XML files.", 29 | "herrera-io/json": "For parsing JSON files.", 30 | "symfony/yaml": "For parsing YAML files." 31 | }, 32 | "autoload": { 33 | "psr-0": { 34 | "Herrera\\Wise": "src/lib" 35 | } 36 | }, 37 | "config": { 38 | "bin-dir": "bin", 39 | "vendor-dir": "src/vendors" 40 | }, 41 | "extra": { 42 | "branch-alias": { 43 | "dev-master": "1.0-dev" 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Processor/ProcessorInterface.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | interface ProcessorInterface extends ConfigurationInterface 13 | { 14 | /** 15 | * Returns the processor resolver. 16 | * 17 | * @return ProcessorResolverInterface The resolver. 18 | */ 19 | public function getResolver(); 20 | 21 | /** 22 | * Processes the configuration data. 23 | * 24 | * @param array $data The data. 25 | * 26 | * @return array The processed data. 27 | */ 28 | public function process(array $data); 29 | 30 | /** 31 | * Sets the processor resolver. 32 | * 33 | * @param ProcessorResolverInterface $resolver The resolver. 34 | */ 35 | public function setResolver(ProcessorResolverInterface $resolver); 36 | 37 | /** 38 | * Checks if a resource is supported by this processor. 39 | * 40 | * @param mixed $resource A resource. 41 | * @param string $type The resource type. 42 | * 43 | * @return boolean TRUE if it is supported, FALSE if not. 44 | */ 45 | public function supports($resource, $type = null); 46 | } 47 | -------------------------------------------------------------------------------- /src/tests/Herrera/Wise/Tests/Processor/AbstractProcessorTest.php: -------------------------------------------------------------------------------- 1 | setPropertyValue($this->processor, 'resolver', $this->resolver); 24 | 25 | $this->assertSame($this->resolver, $this->processor->getResolver()); 26 | } 27 | 28 | public function testProcess() 29 | { 30 | $this->assertSame( 31 | array('enabled' => false), 32 | $this->processor->process(array()) 33 | ); 34 | } 35 | 36 | /** 37 | * @depends testGetResolver 38 | */ 39 | public function testSetResolver() 40 | { 41 | $this->processor->setResolver($this->resolver); 42 | 43 | $this->assertSame($this->resolver, $this->processor->getResolver()); 44 | } 45 | 46 | protected function setUp() 47 | { 48 | $this->processor = new ExampleProcessor(); 49 | $this->resolver = new ProcessorResolver(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Processor/DelegatingProcessor.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class DelegatingProcessor extends AbstractProcessor 13 | { 14 | /** 15 | * The last processor returned by the resolver. 16 | * 17 | * @var ProcessorInterface 18 | */ 19 | private $last; 20 | 21 | /** 22 | * Sets the processor resolver. 23 | * 24 | * @param ProcessorResolverInterface $resolver The resolver. 25 | */ 26 | public function __construct(ProcessorResolverInterface $resolver) 27 | { 28 | $this->setResolver($resolver); 29 | } 30 | 31 | /** 32 | * {@inheritDoc} 33 | * 34 | * @throws ProcessorException If no processor is set. 35 | */ 36 | public function getConfigTreeBuilder() 37 | { 38 | if (null === $this->last) { 39 | throw new ProcessorException( 40 | 'The support() method did not find a processor.' 41 | ); 42 | } 43 | 44 | return $this->last->getConfigTreeBuilder(); 45 | } 46 | 47 | /** 48 | * {@inheritDoc} 49 | */ 50 | public function supports($resource, $type = null) 51 | { 52 | $this->last = $this->getResolver()->resolve($resource, $type) ?: null; 53 | 54 | return (null !== $this->last); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Processor/ProcessorResolver.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class ProcessorResolver implements ProcessorResolverInterface 11 | { 12 | /** 13 | * The collection of processors. 14 | * 15 | * @var ProcessorInterface[] 16 | */ 17 | private $processors = array(); 18 | 19 | /** 20 | * Sets processors in the resolver. 21 | * 22 | * @param ProcessorInterface[] $processors The processors. 23 | */ 24 | public function __construct(array $processors = array()) 25 | { 26 | foreach ($processors as $processor) { 27 | $this->addProcessor($processor); 28 | } 29 | } 30 | 31 | /** 32 | * Adds a processor to the resolve. 33 | * 34 | * @param ProcessorInterface $processor A processor. 35 | */ 36 | public function addProcessor(ProcessorInterface $processor) 37 | { 38 | $this->processors[] = $processor; 39 | } 40 | 41 | /** 42 | * Returns the processors in the resolve. 43 | * 44 | * @return ProcessorInterface[] The processors. 45 | */ 46 | public function getProcessors() 47 | { 48 | return $this->processors; 49 | } 50 | 51 | /** 52 | * {@inheritDoc} 53 | */ 54 | public function resolve($resource, $type = null) 55 | { 56 | foreach ($this->processors as $processor) { 57 | if ($processor->supports($resource, $type)) { 58 | return $processor; 59 | } 60 | } 61 | 62 | return false; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/tests/Herrera/Wise/Tests/Resource/ResourceCollectorTest.php: -------------------------------------------------------------------------------- 1 | collector->addResource($this->resource); 24 | 25 | $this->assertSame( 26 | array($this->resource), 27 | $this->getPropertyValue($this->collector, 'resources') 28 | ); 29 | } 30 | 31 | /** 32 | * @depends testAddResource 33 | */ 34 | public function testClearResources() 35 | { 36 | $this->collector->addResource($this->resource); 37 | $this->collector->clearResources(); 38 | 39 | $this->assertSame( 40 | array(), 41 | $this->getPropertyValue($this->collector, 'resources') 42 | ); 43 | } 44 | 45 | /** 46 | * @depends testAddResource 47 | */ 48 | public function testGetResources() 49 | { 50 | $this->collector->addResource($this->resource); 51 | 52 | $this->assertSame( 53 | array($this->resource), 54 | $this->collector->getResources() 55 | ); 56 | } 57 | 58 | protected function setUp() 59 | { 60 | $this->collector = new ResourceCollector(); 61 | $this->resource = new FileResource(__FILE__); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Util/ArrayUtil.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class ArrayUtil 11 | { 12 | /** 13 | * Flattens an associative array. 14 | * 15 | * @param array $array An array. 16 | * @param string $prefix A key prefix. 17 | * @param string $join The key join character. 18 | * 19 | * @return array The flattened array. 20 | */ 21 | public static function flatten(array $array, $prefix = '', $join = '.') 22 | { 23 | $flat = array(); 24 | 25 | foreach ($array as $key => $value) { 26 | $key = $prefix ? $prefix . $join . $key : $key; 27 | 28 | if (is_array($value)) { 29 | $flat = array_merge( 30 | $flat, 31 | self::flatten($value, $key, $join) 32 | ); 33 | } else { 34 | $flat[$key] = $value; 35 | } 36 | } 37 | 38 | return $flat; 39 | } 40 | 41 | /** 42 | * Similar to `array_walk_recursive()`, but passes the current array too. 43 | * 44 | * @param array &$array An array. 45 | * @param callable $callback The callable. 46 | * @param mixed $data The user data. 47 | */ 48 | public static function walkRecursive(&$array, $callback, $data = null) 49 | { 50 | foreach ($array as $key => &$value) { 51 | if (is_array($value)) { 52 | self::walkRecursive($value, $callback, $data); 53 | } else { 54 | $callback($value, $key, $array, $data); 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/tests/Herrera/Wise/Tests/Processor/ProcessorResolverTest.php: -------------------------------------------------------------------------------- 1 | assertSame( 23 | array($this->processor), 24 | $this->getPropertyValue($this->resolver, 'processors') 25 | ); 26 | } 27 | 28 | public function testAddProcessor() 29 | { 30 | $resolver = new ProcessorResolver(); 31 | $resolver->addProcessor($this->processor); 32 | 33 | $this->assertSame( 34 | array($this->processor), 35 | $this->getPropertyValue($resolver, 'processors') 36 | ); 37 | } 38 | 39 | /** 40 | * @depends testConstruct 41 | */ 42 | public function testGetProcessors() 43 | { 44 | $this->assertSame( 45 | array($this->processor), 46 | $this->resolver->getProcessors() 47 | ); 48 | } 49 | 50 | /** 51 | * @depends testConstruct 52 | */ 53 | public function testResolve() 54 | { 55 | $this->assertFalse($this->resolver->resolve('test')); 56 | $this->assertSame( 57 | $this->processor, 58 | $this->resolver->resolve(array(), 'example') 59 | ); 60 | } 61 | 62 | protected function setUp() 63 | { 64 | $this->processor = new ExampleProcessor(); 65 | $this->resolver = new ProcessorResolver(array($this->processor)); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/tests/Herrera/Wise/Tests/Loader/IniFileLoaderTest.php: -------------------------------------------------------------------------------- 1 | assertTrue($this->loader->supports('test.ini')); 21 | $this->assertTrue($this->loader->supports('test.ini', 'ini')); 22 | } 23 | 24 | public function testDoLoad() 25 | { 26 | file_put_contents( 27 | "{$this->dir}/test.ini", 28 | <<dir}/import.ini", 40 | <<assertSame( 47 | array( 48 | 'imported' => array( 49 | 'value' => 'imported value' 50 | ), 51 | 'imports' => array( 52 | array('resource' => 'import.ini') 53 | ), 54 | 'root' => array( 55 | 'number' => '123', 56 | 'imported' => 'imported value' 57 | ), 58 | ), 59 | $this->loader->load('test.ini') 60 | ); 61 | } 62 | 63 | protected function setUp() 64 | { 65 | $this->dir = $this->createDir(); 66 | $this->loader = new IniFileLoader(new FileLocator($this->dir)); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/tests/Herrera/Wise/Tests/Loader/YamlFileLoaderTest.php: -------------------------------------------------------------------------------- 1 | assertTrue($this->loader->supports('test.yml')); 21 | $this->assertTrue($this->loader->supports('test.yml', 'yaml')); 22 | } 23 | 24 | public function testDoLoad() 25 | { 26 | file_put_contents( 27 | "{$this->dir}/test.yml", 28 | <<dir}/import.yml", 40 | <<assertSame( 47 | array( 48 | 'imported' => array( 49 | 'value' => 'imported value' 50 | ), 51 | 'imports' => array( 52 | array('resource' => 'import.yml') 53 | ), 54 | 'root' => array( 55 | 'number' => 123, 56 | 'imported' => 'imported value' 57 | ), 58 | ), 59 | $this->loader->load('test.yml') 60 | ); 61 | } 62 | 63 | protected function setUp() 64 | { 65 | $this->dir = $this->createDir(); 66 | $this->loader = new YamlFileLoader(new FileLocator($this->dir)); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/tests/Herrera/Wise/Tests/Util/ArrayUtilTest.php: -------------------------------------------------------------------------------- 1 | assertEquals( 13 | array( 14 | 'one' => 1, 15 | 'sub.two' => 2, 16 | 'sub.sub.three' => 3 17 | ), 18 | ArrayUtil::flatten( 19 | array( 20 | 'one' => 1, 21 | 'sub' => array( 22 | 'two' => 2, 23 | 'sub' => array( 24 | 'three' => 3 25 | ) 26 | ) 27 | ) 28 | ) 29 | ); 30 | } 31 | 32 | public function testWalkRecursive() 33 | { 34 | $expected = $actual = array( 35 | 'one' => array( 36 | 'two' => array( 37 | 'three' => array( 38 | 'four' => 'eight', 39 | 'twelve' => 'thirteen', 40 | ), 41 | 'five' => 'nine', 42 | ), 43 | 'six' => 'ten', 44 | ), 45 | 'seven' => 'eleven', 46 | ); 47 | 48 | ArrayUtil::walkRecursive( 49 | $actual, 50 | function (&$value, $key, &$array) { 51 | if ('four' === $key) { 52 | unset($array[$key]); 53 | 54 | $array['changed'] = $value; 55 | } 56 | } 57 | ); 58 | 59 | unset($expected['one']['two']['three']['four']); 60 | 61 | $expected['one']['two']['three']['changed'] = 'eight'; 62 | 63 | $this->assertSame($expected, $actual); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /res/schema.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/tests/Herrera/Wise/Tests/Processor/DelegatingProcessorTest.php: -------------------------------------------------------------------------------- 1 | assertNotNull( 19 | $this->getPropertyValue($this->processor, 'resolver') 20 | ); 21 | 22 | $this->assertSame( 23 | $this->processor->getResolver(), 24 | $this->getPropertyValue($this->processor, 'resolver') 25 | ); 26 | } 27 | 28 | /** 29 | * @expectedException \Herrera\Wise\Exception\ProcessorException 30 | * @expectedExceptionMessage The support() method did not find a processor. 31 | */ 32 | public function testGetConfigTreeBuilderNoneAvailable() 33 | { 34 | $this->processor->getConfigTreeBuilder(); 35 | } 36 | 37 | public function testGetConfigTreeBuilder() 38 | { 39 | $this->setPropertyValue( 40 | $this->processor, 41 | 'last', 42 | new ExampleProcessor() 43 | ); 44 | 45 | $this->assertInstanceOf( 46 | 'Symfony\\Component\\Config\\Definition\\Builder\\TreeBuilder', 47 | $this->processor->getConfigTreeBuilder() 48 | ); 49 | } 50 | 51 | public function testSupports() 52 | { 53 | $this->assertFalse($this->processor->supports('test')); 54 | $this->assertTrue($this->processor->supports(array(), 'example')); 55 | } 56 | 57 | protected function setUp() 58 | { 59 | $this->processor = new DelegatingProcessor( 60 | new ProcessorResolver(array(new ExampleProcessor())) 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/tests/Herrera/Wise/Tests/Loader/PhpFileLoaderTest.php: -------------------------------------------------------------------------------- 1 | assertTrue($this->loader->supports('test.php')); 21 | $this->assertTrue($this->loader->supports('test.php', 'php')); 22 | } 23 | 24 | public function testDoLoad() 25 | { 26 | file_put_contents( 27 | "{$this->dir}/test.php", 28 | << array( 31 | array('resource' => 'import.php') 32 | ), 33 | 'root' => array( 34 | 'number' => 123, 35 | 'imported' => '%imported.value%' 36 | ) 37 | ); 38 | DATA 39 | ); 40 | 41 | file_put_contents( 42 | "{$this->dir}/import.php", 43 | << array( 46 | 'value' => 'imported value' 47 | ) 48 | ); 49 | DATA 50 | ); 51 | 52 | $this->assertSame( 53 | array( 54 | 'imported' => array( 55 | 'value' => 'imported value' 56 | ), 57 | 'imports' => array( 58 | array('resource' => 'import.php') 59 | ), 60 | 'root' => array( 61 | 'number' => 123, 62 | 'imported' => 'imported value' 63 | ), 64 | ), 65 | $this->loader->load('test.php') 66 | ); 67 | } 68 | 69 | protected function setUp() 70 | { 71 | $this->dir = $this->createDir(); 72 | $this->loader = new PhpFileLoader(new FileLocator($this->dir)); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/tests/Herrera/Wise/Tests/Loader/JsonFileLoaderTest.php: -------------------------------------------------------------------------------- 1 | assertTrue($this->loader->supports('test.json')); 21 | $this->assertTrue($this->loader->supports('test.json', 'json')); 22 | } 23 | 24 | public function testDoLoad() 25 | { 26 | file_put_contents( 27 | "{$this->dir}/test.json", 28 | <<dir}/import.json", 46 | <<assertSame( 56 | array( 57 | 'imported' => array( 58 | 'value' => 'imported value' 59 | ), 60 | 'imports' => array( 61 | array('resource' => 'import.json') 62 | ), 63 | 'root' => array( 64 | 'number' => 123, 65 | 'imported' => 'imported value' 66 | ), 67 | ), 68 | $this->loader->load('test.json') 69 | ); 70 | } 71 | 72 | protected function setUp() 73 | { 74 | $this->dir = $this->createDir(); 75 | $this->loader = new JsonFileLoader(new FileLocator($this->dir)); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/tests/Herrera/Wise/Tests/Loader/XmlFileLoaderTest.php: -------------------------------------------------------------------------------- 1 | assertTrue($this->loader->supports('test.xml')); 21 | $this->assertTrue($this->loader->supports('test.xml', 'xml')); 22 | } 23 | 24 | public function testDoLoad() 25 | { 26 | file_put_contents( 27 | "{$this->dir}/test.xml", 28 | << 30 | 31 | 32 | import.xml 33 | 34 | 35 | 36 | 123 37 | %imported.value% 38 | 1 39 | 1.23 40 | 41 | 42 | DATA 43 | ); 44 | 45 | file_put_contents( 46 | "{$this->dir}/import.xml", 47 | << 49 | 50 | imported value 51 | 52 | 53 | DATA 54 | ); 55 | 56 | $this->assertSame( 57 | array( 58 | 'imported' => array( 59 | 'value' => 'imported value' 60 | ), 61 | 'imports' => array( 62 | array('resource' => 'import.xml') 63 | ), 64 | 'root' => array( 65 | 'number' => 123, 66 | 'imported' => 'imported value', 67 | 'enabled' => true, 68 | 'unit' => 1.23 69 | ), 70 | ), 71 | $this->loader->load('test.xml') 72 | ); 73 | } 74 | 75 | protected function setUp() 76 | { 77 | $this->dir = $this->createDir(); 78 | $this->loader = new XmlFileLoader(new FileLocator($this->dir)); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /doc/03-HowToCreateAProcessor.md: -------------------------------------------------------------------------------- 1 | How to Create a Processor 2 | ========================= 3 | 4 | To normalize and validate configuration data, you will need to create a 5 | processor that provides a [configuration definition][]. You must have a solid 6 | understanding on how the [Config][] component generates those definitions in 7 | order to create a processor. 8 | 9 | Creating a Processor 10 | -------------------- 11 | 12 | To create a processing class, you will need to extend the `AbstractProcessor` 13 | class and implement the `getConfigTreeBuilder()` method: 14 | 15 | ```php 16 | class MyProcessor implement Herrera\Wise\Processor\AbstractProcessor 17 | { 18 | public function getConfigTreeBuilder() 19 | { 20 | // return definition tree builder 21 | } 22 | 23 | public function supports($resource, $type = null) 24 | { 25 | // returns true if $resource and $type are supported 26 | } 27 | } 28 | ``` 29 | 30 | You will then need to register it with `Wise`: 31 | 32 | ```php 33 | $wise->setProcessor(new MyProcessor()); 34 | ``` 35 | 36 | Any configuration resource that is loaded, and is supported by the processor, 37 | will be normalized and validated according to the definition you have provided. 38 | 39 | Using Multiple Processors 40 | ------------------------- 41 | 42 | To use more than one processor, you will need to use both a delegating processor 43 | and a processor resolver. The delegating processor will use the processor 44 | resolver to find the correct processor to use: 45 | 46 | ```php 47 | $resolver = new Herrera\Wise\Processor\ProcessorResolver(); 48 | 49 | $wise->setProcessor( 50 | new Herrera\Wise\Processor\DelegatingProcessor($resolver) 51 | ); 52 | ``` 53 | 54 | With the processor registered with `Wise`, you can then add all processors you 55 | need using the resolver's `addPrococessor()` method: 56 | 57 | ```php 58 | $resolver->addProcessor(new MyProcessorOne()); 59 | $resolver->addProcessor(new MyProcessorTwo()); 60 | $resolver->addProcessor(new MyProcessorThree()); 61 | // ... snip ... 62 | ``` 63 | 64 | [Config]: http://symfony.com/doc/current/components/config/index.html 65 | [configuration definition]: http://symfony.com/doc/current/components/config/definition.html -------------------------------------------------------------------------------- /doc/02-HowToCreateALoader.md: -------------------------------------------------------------------------------- 1 | How to Create a Loader 2 | ====================== 3 | 4 | Creating a loader is nearly identical to that of the Symfony [Config][] 5 | component, with the difference being added support for Wise features. 6 | 7 | - [File Based Loader](#FileBasedLoader) 8 | - [Other Loader](#OtherLoader) 9 | 10 | --- 11 | 12 | # File Based Loader 13 | 14 | To create a file-based loader, you need to extend the `AbstractFileLoader` 15 | class. The `AbstractFileLoader` class enabled support for global parameters 16 | and importing from other configuration resources. The following is an example 17 | simple loader for CSV files: 18 | 19 | ```php 20 | class CsvFileLoader extends Herrera\Wise\Loader\AbstractFileLoader 21 | { 22 | public function supports($resource, $type = null) 23 | { 24 | return (is_string($resource) 25 | && ('csv' === strtolower(pathinfo($resource, PATHINFO_EXTENSION)))) 26 | && ((null === $type) || ('csv' === $type)); 27 | } 28 | 29 | protected function doLoad($resource, $type = null) 30 | { 31 | $fp = fopen($resource, 'r'); 32 | $data = array(); 33 | 34 | while (false !== ($row = fgetcsv($fp))) { 35 | if ((1 === count($row)) && (null === $row[0])) { 36 | continue; 37 | } 38 | 39 | $data[$row[0]] = $row[1]; 40 | } 41 | 42 | fcloses($fp); 43 | 44 | return $data; 45 | } 46 | } 47 | ``` 48 | 49 | # Other Loader 50 | 51 | For resources that are not files, you will need to implement these interfaces: 52 | 53 | - `Herrera\Wise\Resource\ResourceAwareInterface` - For caching support 54 | - `Herrera\Wise\WiseAwareInterface` - For global parameters support 55 | - `Symfony\Component\Config\Loader\LoaderInterface` - For basic loading 56 | 57 | Implementing the first two interfaces flags your class as having support for 58 | caching and global parameters. Your class will actually need to make use of 59 | the resource collector and `Wise` instance, which will be set when your class 60 | is set as the `Wise` loader. In the `AbstractFileLoader` class provided by the 61 | library, this is already handled for you. 62 | 63 | [Config]: http://symfony.com/doc/current/components/config/resources.html#resource-loaders -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Loader/LoaderResolver.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class LoaderResolver extends Base implements 18 | ResourceAwareInterface, 19 | WiseAwareInterface 20 | { 21 | /** 22 | * The resource collector. 23 | * 24 | * @var ResourceCollectorInterface 25 | */ 26 | private $collector; 27 | 28 | /** 29 | * The Wise instance. 30 | * 31 | * @var Wise 32 | */ 33 | private $wise; 34 | 35 | /** 36 | * @override 37 | */ 38 | public function addLoader(LoaderInterface $loader) 39 | { 40 | if ($this->collector && ($loader instanceof ResourceAwareInterface)) { 41 | $loader->setResourceCollector($this->collector); 42 | } 43 | 44 | if ($this->wise && ($loader instanceof WiseAwareInterface)) { 45 | $loader->setWise($this->wise); 46 | } 47 | 48 | parent::addLoader($loader); 49 | } 50 | 51 | /** 52 | * {@inheritDoc} 53 | */ 54 | public function getResourceCollector() 55 | { 56 | return $this->collector; 57 | } 58 | 59 | /** 60 | * {@inheritDoc} 61 | */ 62 | public function getWise() 63 | { 64 | return $this->wise; 65 | } 66 | 67 | /** 68 | * {@inheritDoc} 69 | */ 70 | public function setResourceCollector(ResourceCollectorInterface $collector) 71 | { 72 | $this->collector = $collector; 73 | 74 | foreach ($this->getLoaders() as $loader) { 75 | if ($loader instanceof ResourceAwareInterface) { 76 | $loader->setResourceCollector($collector); 77 | } 78 | } 79 | } 80 | 81 | /** 82 | * {@inheritDoc} 83 | */ 84 | public function setWise(Wise $wise) 85 | { 86 | $this->wise = $wise; 87 | 88 | foreach ($this->getLoaders() as $loader) { 89 | if ($loader instanceof WiseAwareInterface) { 90 | $loader->setWise($wise); 91 | } 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Loader/XmlFileLoader.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class XmlFileLoader extends AbstractFileLoader 14 | { 15 | /** 16 | * {@inheritDoc} 17 | */ 18 | public function supports($resource, $type = null) 19 | { 20 | return (is_string($resource) 21 | && ('xml' === strtolower(pathinfo($resource, PATHINFO_EXTENSION)))) 22 | && ((null === $type) || ('xml' === $type)); 23 | } 24 | 25 | /** 26 | * @override 27 | */ 28 | protected function doLoad($file) 29 | { 30 | $doc = new DOMDocument(); 31 | $doc->preserveWhiteSpace = false; 32 | 33 | $doc->load($file); 34 | $doc->schemaValidate(__DIR__ . '/../../../../../res/schema.xsd'); 35 | 36 | return $this->toArray($doc->documentElement); 37 | } 38 | 39 | /** 40 | * Converts a DOM elements to native PHP values. 41 | * 42 | * @param DOMElement $node The node. 43 | * 44 | * @return mixed The result. 45 | */ 46 | private function toArray(DOMElement $node) 47 | { 48 | $value = null; 49 | 50 | switch ($node->nodeName) { 51 | case 'array': 52 | $value = array(); 53 | 54 | if ($node->hasChildNodes()) { 55 | for ($i = 0; $i < $node->childNodes->length; $i++) { 56 | /** @var $child DOMElement */ 57 | $child = $node->childNodes->item($i); 58 | 59 | if ($child->hasAttribute('key')) { 60 | $value[$child->getAttribute('key')] = $this->toArray($child); 61 | } else { 62 | $value[] = $this->toArray($child); 63 | } 64 | } 65 | } 66 | break; 67 | case 'bool': 68 | $value = (bool) $node->textContent; 69 | break; 70 | case 'float': 71 | $value = (float) $node->textContent; 72 | break; 73 | case 'int': 74 | $value = (int) $node->textContent; 75 | break; 76 | case 'str': 77 | $value = $node->textContent; 78 | break; 79 | } 80 | 81 | return $value; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/tests/Herrera/Wise/Tests/Loader/LoaderResolverTest.php: -------------------------------------------------------------------------------- 1 | setPropertyValue($this->resolver, 'collector', $this->collector); 32 | $this->setPropertyValue($this->resolver, 'wise', $this->wise); 33 | 34 | $loader = new ExampleFileLoader(new FileLocator()); 35 | 36 | $this->resolver->addLoader($loader); 37 | 38 | $this->assertSame($this->collector, $loader->getResourceCollector()); 39 | $this->assertSame($this->wise, $loader->getWise()); 40 | } 41 | 42 | public function testGetResourceCollector() 43 | { 44 | $this->setPropertyValue($this->resolver, 'collector', $this->collector); 45 | 46 | $this->assertSame( 47 | $this->collector, 48 | $this->resolver->getResourceCollector() 49 | ); 50 | } 51 | 52 | public function testGetWise() 53 | { 54 | $this->setPropertyValue($this->resolver, 'wise', $this->wise); 55 | 56 | $this->assertSame($this->wise, $this->resolver->getWise()); 57 | } 58 | 59 | public function testSetResourceCollector() 60 | { 61 | $loader = new ExampleFileLoader(new FileLocator()); 62 | 63 | $this->resolver->addLoader($loader); 64 | $this->resolver->setResourceCollector($this->collector); 65 | 66 | $this->assertSame($this->collector, $loader->getResourceCollector()); 67 | } 68 | 69 | public function testSetWise() 70 | { 71 | $loader = new ExampleFileLoader(new FileLocator()); 72 | 73 | $this->resolver->addLoader($loader); 74 | $this->resolver->setWise($this->wise); 75 | 76 | $this->assertSame($this->wise, $loader->getWise()); 77 | } 78 | 79 | protected function setUp() 80 | { 81 | $this->collector = new ResourceCollector(); 82 | $this->resolver = new LoaderResolver(); 83 | $this->wise = new Wise(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /doc/01-Usage.md: -------------------------------------------------------------------------------- 1 | Usage 2 | ===== 3 | 4 | > **Notice:** The documentation was designed to be read from top to bottom. 5 | > Some > tasks are shared with other topics. For example, the `$locator` 6 | > instance > in the **Single Loader** topic is needed in the **Multiple Loader** 7 | > topic, but the process of creating it is not covered again. 8 | 9 | - [All-In-One](#all-in-one) 10 | - [Cherry Pick](#cherry-pick) 11 | - [Single Loader](#single-loader) 12 | - [Multiple Loaders](#multiple-loaders) 13 | - [Caching](#caching) 14 | - [Processing](#processing) 15 | - [Loading Data](#loading-data) 16 | 17 | There are two ways of using Wise. 18 | 19 | All-In-One 20 | ---------- 21 | 22 | The easiest way to start using Wise is by calling the `create()` method: 23 | 24 | ```php 25 | use Herrera\Wise\Wise; 26 | 27 | $wise = Wise::create('/path/to/config'); 28 | ``` 29 | 30 | Using this approach requires that you install all of Wise's suggested Composer 31 | dependencies, but grants you the ability to load INI, JSON, PHP, XML, and YAML 32 | configuration files. 33 | 34 | The `create()` method will accept a single directory path, or an array of 35 | directory paths. You may also specify a cache directory path, and the debug 36 | mode, if necessary: 37 | 38 | ```php 39 | $wise = Wise::create( 40 | array( // multiple directory paths 41 | '/path/1', 42 | '/path/2', 43 | '/path/3', 44 | '/path/4', 45 | ), 46 | '/path/to/cache/dir', // cache directory path 47 | true // enables debugging 48 | ); 49 | ``` 50 | 51 | Cherry Pick 52 | ----------- 53 | 54 | Your project may not make use of one or more configuration file types, so 55 | registering all of the available loaders may be wasteful. Instead of using 56 | the `create()` method, you will want to create a new instance of `Wise`: 57 | 58 | ```php 59 | $wise = new Wise(); 60 | ``` 61 | 62 | ### Single Loader 63 | 64 | If you are using only one file type for your project, setting up a loader 65 | is fairly straightforward. First, you will need to create a `FileLocator` 66 | instance, which is used by all loaders to find configuration files: 67 | 68 | ```php 69 | use Symfony\Component\Config\FileLocator; 70 | 71 | $locator = new FileLocator('/path/to/config'); 72 | ``` 73 | 74 | If your configuration files are located in multiple directories, you may 75 | provide an array of directory paths. 76 | 77 | > You might be scratching your head and wondering why you are creating an 78 | > instance of a class from a different library. The Wise library is built on 79 | > top of Symfony's Config library, which shares many of the same classes 80 | > with Wise. 81 | 82 | Once you have created your locator, you can now instantiate your desired 83 | loader with the new `FileLocator` object, which will then be registered 84 | with `$wise`: 85 | 86 | ```php 87 | use Herrera\Wise\Loader\PhpFileLoader; 88 | 89 | $loader = new PhpFileLoader($locator); 90 | 91 | $wise->setLoader($loader); 92 | ``` 93 | 94 | This example demonstrates how to support only PHP scripts as configuration 95 | files. You may also use any of the loaders that have been bundled with the 96 | library: 97 | 98 | - `Herrera\Wise\Loader\IniFileLoader` 99 | - `Herrera\Wise\Loader\JsonFileLoader` — Requires `herrera-io/json`. 100 | - `Herrera\Wise\Loader\PhpFileLoader` 101 | - `Herrera\Wise\Loader\XmlFileLoader` — Requires the `dom` extension. 102 | - `Herrera\Wise\Loader\YamlFileLoader` — Requires `symfony/yaml`. 103 | 104 | You may also [create your own loader][] and use that instead. 105 | 106 | ### Multiple Loaders 107 | 108 | To use multiple loaders, you do not need to create multiple instances of Wise. 109 | Instead, what you will need is a delegating loader. This delegating loader is 110 | used in place of an actual loader. To use a delegating loader, you must first 111 | create its resolver: 112 | 113 | ```php 114 | use Herrera\Wise\Loader\LoaderResolver; 115 | use Herrera\Wise\Loader\IniFileLoader; 116 | use Herrera\Wise\Loader\PhpFileLoader; 117 | 118 | $resolver = new LoaderResolver( 119 | array( 120 | new IniFileLoader($locator), 121 | new PhpFileLoader($locator), 122 | ) 123 | ); 124 | ``` 125 | 126 | In this example, the INI and PHP file loaders have been registered with the 127 | resolver. This will allow the delegating loader to use the INI and PHP loaders 128 | when an attempt is made to load either file type. Before that can be done, you 129 | must create the delegating loader using your resolver, and then registering 130 | the loader with Wise: 131 | 132 | ```php 133 | use Herrera\Wise\Loader\DelegatingLoader; 134 | 135 | $delegator = new DelegatingLoader($resolver); 136 | 137 | $wise->setLoader($delegator); 138 | ``` 139 | 140 | #### Caching 141 | 142 | Now that you have registered your loader(s), you may want to cache the result 143 | to improve performance. To do so, you must register a resource collector, and 144 | then set the cache directory path: 145 | 146 | ```php 147 | use Herrera\Wise\Resource\ResourceCollector; 148 | 149 | $wise->setCacheDir('/path/to/cache/dir'); 150 | $wise->setCollector(new ResourceCollector()); 151 | ``` 152 | 153 | > The purpose of the `ResourceCollector` instance is so that `imports` can be 154 | > properly tracked when they are loaded. Without the resource collector, any 155 | > changes made to imported files will not be considered when the cache needs 156 | > to be refreshed, resulting in stale cache data. 157 | 158 | Processing 159 | ---------- 160 | 161 | Regardless of whether you chose the **Single Loader** or **Multiple Loaders** 162 | approach, you may want to normalize and/or validate the configuration data that 163 | you are loading. In Wise, this is called processing. For this part, **you must 164 | known** how to create your own definitions, which is covered by Symfony's 165 | own [documentation][]. You must have a solid understanding of that 166 | documentation before you will be able to proceed. 167 | 168 | *intermission, Jeopardy theme song* 169 | 170 | You should now continue on to [How to Create a Processor][]. 171 | 172 | Loading Data 173 | ------------ 174 | 175 | After you have set up Wise, you may now load your configuration files: 176 | 177 | ```php 178 | $config = $wise->load('config.ini'); 179 | ``` 180 | 181 | If you want a flattened version of your configuration data, delimited by ".", 182 | you may instead use the `loadFlat()` method: 183 | 184 | ```php 185 | $flatConfig = $wise->loadFlat('config.ini'); 186 | ``` 187 | 188 | This will turn this data: 189 | 190 | ```php 191 | array( 192 | 'one' => array( 193 | 'two' => array( 194 | 'three' => 123 195 | ) 196 | ), 197 | 'two' => 2 198 | ) 199 | ``` 200 | 201 | into: 202 | 203 | ```php 204 | array( 205 | 'one.two.three' => 123, 206 | 'two' => 2 207 | ) 208 | ``` 209 | 210 | [create your own loader]: 02-HowToCreateALoader.md 211 | [documentation]: http://symfony.com/doc/current/components/config/definition.html 212 | [How to Create a Processor]: 03-HowToCreateAProcessor.md 213 | -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Loader/AbstractFileLoader.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | abstract class AbstractFileLoader extends FileLoader implements ResourceAwareInterface, WiseAwareInterface 22 | { 23 | /** 24 | * The resource collector. 25 | * 26 | * @var ResourceCollectorInterface 27 | */ 28 | private $collector; 29 | 30 | /** 31 | * The Wise instance. 32 | * 33 | * @var Wise 34 | */ 35 | private $wise; 36 | 37 | /** 38 | * Replace the placeholder value(s). 39 | * 40 | * @param mixed $input The input. 41 | * @param array $data The data the input is from. 42 | * @param array $global The global values. 43 | * 44 | * @return mixed The result. 45 | * 46 | * @throws InvalidReferenceException If an invalid reference is used. 47 | */ 48 | public function doReplace($input, $data, $global) 49 | { 50 | preg_match_all( 51 | '/%(?P[^%]+)%/', 52 | $input, 53 | $matches 54 | ); 55 | 56 | if (false === empty($matches['reference'])) { 57 | foreach ($matches['reference'] as $reference) { 58 | try { 59 | $ref = $this->resolveReference($reference, $data); 60 | } catch (InvalidReferenceException $exception) { 61 | if (empty($global)) { 62 | throw $exception; 63 | } 64 | 65 | $ref = $this->resolveReference($reference, $global); 66 | } 67 | 68 | if ((false === is_null($ref)) 69 | && (false === is_scalar($ref)) 70 | && (false == preg_match('/^%(?:[^%]+)%$/', $input))) { 71 | throw InvalidReferenceException::format( 72 | 'The non-scalar reference "%s" cannot be used inline.', 73 | "%$reference%" 74 | ); 75 | } 76 | 77 | if ("%$reference%" === $input) { 78 | $input = $ref; 79 | } else { 80 | $input = str_replace("%$reference%", $ref, $input); 81 | } 82 | } 83 | } 84 | 85 | return $input; 86 | } 87 | 88 | /** 89 | * {@inheritDoc} 90 | */ 91 | public function getResourceCollector() 92 | { 93 | return $this->collector; 94 | } 95 | 96 | /** 97 | * {@inheritDoc} 98 | */ 99 | public function getWise() 100 | { 101 | return $this->wise; 102 | } 103 | 104 | /** 105 | * {@inheritDoc} 106 | */ 107 | public function load($resource, $type = null) 108 | { 109 | $file = $this->locator->locate($resource, $type); 110 | 111 | if ($this->collector) { 112 | $this->collector->addResource(new FileResource($file)); 113 | } 114 | 115 | $data = $this->doLoad($file); 116 | 117 | return $this->process($data, $resource); 118 | } 119 | 120 | /** 121 | * Imports other configuration files and resolves references. 122 | * 123 | * @param array $data The data. 124 | * @param string $file The file source. 125 | * 126 | * @return array The processed data. 127 | * 128 | * @throws ImportException If "imports" is invalid. 129 | * @throws InvalidReferenceException If an invalid reference is used. 130 | */ 131 | public function process($data, $file) 132 | { 133 | if (empty($data)) { 134 | return array(); 135 | } 136 | 137 | if (isset($data['imports'])) { 138 | if (false === is_array($data['imports'])) { 139 | throw ImportException::format( 140 | 'The "imports" value is not valid in "%s".', 141 | $file 142 | ); 143 | } 144 | 145 | $dir = dirname($file); 146 | 147 | foreach ($data['imports'] as $i => $import) { 148 | if (false === is_array($import)) { 149 | throw ImportException::format( 150 | 'One of the "imports" values (#%d) is not valid in "%s".', 151 | $i, 152 | $file 153 | ); 154 | } 155 | 156 | if (false === isset($import['resource'])) { 157 | throw ImportException::format( 158 | 'A resource was not defined for an import in "%s".', 159 | $file 160 | ); 161 | } 162 | 163 | $this->setCurrentDir($dir); 164 | 165 | $data = array_replace_recursive( 166 | $this->import( 167 | $import['resource'], 168 | null, 169 | isset($import['ignore_errors']) ? (bool) $import['ignore_errors'] : false 170 | ), 171 | $data 172 | ); 173 | } 174 | } 175 | 176 | $global = $this->wise ? $this->wise->getGlobalParameters() : array(); 177 | $_this = $this; 178 | 179 | ArrayUtil::walkRecursive( 180 | $data, 181 | function (&$value, $key, &$array) use (&$data, $global, $_this) { 182 | $value = $_this->doReplace($value, $data, $global); 183 | 184 | if (false !== strpos($key, '%')) { 185 | unset($array[$key]); 186 | 187 | $key = $_this->doReplace($key, $data, $global); 188 | 189 | $array[$key] = $value; 190 | } 191 | } 192 | ); 193 | 194 | return $data; 195 | } 196 | 197 | /** 198 | * Resolves the reference and returns its value. 199 | * 200 | * @param string $reference A reference. 201 | * @param array|ArrayAccess $values A list of values. 202 | * 203 | * @return mixed The referenced value. 204 | * 205 | * @throws InvalidReferenceException If the reference is not valid. 206 | */ 207 | public function resolveReference($reference, $values) 208 | { 209 | foreach (explode('.', $reference) as $leaf) { 210 | if ((!is_array($values) && !($values instanceof ArrayAccess)) 211 | || (is_array($values) && !array_key_exists($leaf, $values)) 212 | || (($values instanceof ArrayAccess) && !$values->offsetExists($leaf))) { 213 | throw InvalidReferenceException::format( 214 | 'The reference "%s" could not be resolved (failed at "%s").', 215 | "%$reference%", 216 | $leaf 217 | ); 218 | } 219 | 220 | $values = $values[$leaf]; 221 | } 222 | 223 | return $values; 224 | } 225 | 226 | /** 227 | * {@inheritDoc} 228 | */ 229 | public function setResourceCollector(ResourceCollectorInterface $collector) 230 | { 231 | $this->collector = $collector; 232 | } 233 | 234 | /** 235 | * {@inheritDoc} 236 | */ 237 | public function setWise(Wise $wise) 238 | { 239 | $this->wise = $wise; 240 | } 241 | 242 | /** 243 | * Returns the parsed data of the file. 244 | * 245 | * @param string $file The file path. 246 | * 247 | * @return array The parsed data. 248 | */ 249 | abstract protected function doLoad($file); 250 | } 251 | -------------------------------------------------------------------------------- /src/tests/Herrera/Wise/Tests/Loader/AbstractFileLoaderTest.php: -------------------------------------------------------------------------------- 1 | setPropertyValue($this->loader, 'collector', $this->collector); 30 | 31 | $this->assertSame( 32 | $this->collector, 33 | $this->loader->getResourceCollector() 34 | ); 35 | } 36 | 37 | public function testGetWise() 38 | { 39 | $wise = new Wise(); 40 | 41 | $this->setPropertyValue($this->loader, 'wise', $wise); 42 | 43 | $this->assertSame($wise, $this->loader->getWise()); 44 | } 45 | 46 | public function testLoad() 47 | { 48 | $data = array('rand' => rand()); 49 | 50 | file_put_contents( 51 | "{$this->dir}/test.php", 52 | 'setPropertyValue($this->loader, 'collector', $this->collector); 56 | $this->assertSame($data, $this->loader->load('test.php')); 57 | 58 | $resources = $this->collector->getResources(); 59 | 60 | $this->assertCount(1, $resources); 61 | $this->assertInstanceOf( 62 | 'Symfony\\Component\\Config\\Resource\\FileResource', 63 | $resources[0] 64 | ); 65 | } 66 | 67 | public function testProcessEmpty() 68 | { 69 | $this->assertSame(array(), $this->loader->process(null, 'test.file')); 70 | } 71 | 72 | /** 73 | * @expectedException \Herrera\Wise\Exception\ImportException 74 | * @expectedExceptionMessage The "imports" value is not valid in "test.file". 75 | */ 76 | public function testProcessInvalidImports() 77 | { 78 | $this->loader->process(array('imports' => 123), 'test.file'); 79 | } 80 | 81 | /** 82 | * @expectedException \Herrera\Wise\Exception\ImportException 83 | * @expectedExceptionMessage One of the "imports" values (#0) is not valid in "test.file". 84 | */ 85 | public function testProcessInvalidImport() 86 | { 87 | $this->loader->process( 88 | array('imports' => array(123)), 89 | 'test.file' 90 | ); 91 | } 92 | 93 | /** 94 | * @expectedException \Herrera\Wise\Exception\ImportException 95 | * @expectedExceptionMessage A resource was not defined for an import in "test.file". 96 | */ 97 | public function testProcessInvalidImportMissingResource() 98 | { 99 | $this->loader->process( 100 | array('imports' => array(array())), 101 | 'test.file' 102 | ); 103 | } 104 | 105 | public function testProcess() 106 | { 107 | $wise = new Wise(); 108 | $wise->setGlobalParameters( 109 | array( 110 | 'global' => array( 111 | 'value' => 999 112 | ) 113 | ) 114 | ); 115 | 116 | $this->setPropertyValue($this->loader, 'wise', $wise); 117 | 118 | file_put_contents( 119 | "{$this->dir}/one.php", 120 | ' array( 123 | array('resource' => 'two.php') 124 | ), 125 | 'global' => '%global.value%', 126 | 'placeholder' => '%imported.list%', 127 | 'sub' => array( 128 | 'inline_placeholder' => 'rand: %imported.list.null%%imported.value%', 129 | ), 130 | '%imported.key%' => 'a value' 131 | ), 132 | true 133 | ) . ';' 134 | ); 135 | 136 | file_put_contents( 137 | "{$this->dir}/two.php", 138 | ' array( 141 | 'key' => 'replaced_key', 142 | 'list' => array( 143 | 'null' => null, 144 | 'value' => 123 145 | ), 146 | 'value' => $rand = rand() 147 | ) 148 | ), 149 | true 150 | ) . ';' 151 | ); 152 | 153 | $this->assertEquals( 154 | array( 155 | 'imports' => array( 156 | array( 157 | 'resource' => 'two.php' 158 | ) 159 | ), 160 | 'global' => 999, 161 | 'placeholder' => array( 162 | 'null' => null, 163 | 'value' => 123 164 | ), 165 | 'sub' => array( 166 | 'inline_placeholder' => 'rand: ' . $rand, 167 | ), 168 | 'replaced_key' => 'a value', 169 | 'imported' => array( 170 | 'key' => 'replaced_key', 171 | 'list' => array( 172 | 'null' => null, 173 | 'value' => 123 174 | ), 175 | 'value' => $rand 176 | ) 177 | ), 178 | $this->loader->load('one.php') 179 | ); 180 | } 181 | 182 | /** 183 | * @expectedException \Herrera\Wise\Exception\InvalidReferenceException 184 | * @expectedExceptionMessage The reference "%test.reference%" could not be resolved (failed at "test"). 185 | */ 186 | public function testProcessInvalidReference() 187 | { 188 | $this->loader->process( 189 | array( 190 | 'bad_reference' => '%test.reference%' 191 | ), 192 | 'test.php' 193 | ); 194 | } 195 | 196 | /** 197 | * @expectedException \Herrera\Wise\Exception\InvalidReferenceException 198 | * @expectedExceptionMessage The non-scalar reference "%test.reference%" cannot be used inline. 199 | */ 200 | public function testProcessNonScalarReference() 201 | { 202 | $this->loader->process( 203 | array( 204 | 'bad_reference' => 'bad: %test.reference%', 205 | 'test' => array( 206 | 'reference' => array( 207 | 'value' => 123 208 | ) 209 | ) 210 | ), 211 | 'test.php' 212 | ); 213 | } 214 | 215 | /** 216 | * @expectedException \Herrera\Wise\Exception\InvalidReferenceException 217 | * @expectedExceptionMessage The reference "%a.b.c.d%" could not be resolved (failed at "a"). 218 | */ 219 | public function testResolveReferenceInvalid() 220 | { 221 | $this->loader->resolveReference('a.b.c.d', array()); 222 | } 223 | 224 | public function testResolveReference() 225 | { 226 | $array = array( 227 | 'a' => array( 228 | 'b' => array( 229 | 'c' => array( 230 | 'd' => 123 231 | ) 232 | ) 233 | ) 234 | ); 235 | 236 | $object = new ArrayObject($array); 237 | 238 | $this->assertEquals( 239 | 123, 240 | $this->loader->resolveReference('a.b.c.d', $array) 241 | ); 242 | 243 | $this->assertEquals( 244 | 123, 245 | $this->loader->resolveReference('a.b.c.d', $object) 246 | ); 247 | } 248 | 249 | /** 250 | * @depends testGetResourceCollector 251 | */ 252 | public function testSetResourceCollector() 253 | { 254 | $this->loader->setResourceCollector($this->collector); 255 | 256 | $this->assertSame( 257 | $this->collector, 258 | $this->loader->getResourceCollector() 259 | ); 260 | } 261 | 262 | /** 263 | * @depends testGetWise 264 | */ 265 | public function testSetWise() 266 | { 267 | $wise = new Wise(); 268 | 269 | $this->loader->setWise($wise); 270 | 271 | $this->assertSame($wise, $this->loader->getWise()); 272 | } 273 | 274 | protected function setUp() 275 | { 276 | $this->dir = $this->createDir(); 277 | $this->collector = new ResourceCollector(); 278 | $this->loader = new ExampleFileLoader(new FileLocator($this->dir)); 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /src/lib/Herrera/Wise/Wise.php: -------------------------------------------------------------------------------- 1 | 26 | */ 27 | class Wise 28 | { 29 | /** 30 | * The cache directory path. 31 | * 32 | * @var string 33 | */ 34 | private $cacheDir; 35 | 36 | /** 37 | * The resource collector. 38 | * 39 | * @var ResourceCollectorInterface 40 | */ 41 | private $collector; 42 | 43 | /** 44 | * The debug mode flag. 45 | * 46 | * @var boolean 47 | */ 48 | private $debug; 49 | 50 | /** 51 | * The configuration loader. 52 | * 53 | * @var LoaderInterface 54 | */ 55 | private $loader; 56 | 57 | /** 58 | * The list of global parameters. 59 | * 60 | * @var array 61 | */ 62 | private $parameters = array(); 63 | 64 | /** 65 | * The configuration processor. 66 | * 67 | * @var ProcessorInterface 68 | */ 69 | private $processor; 70 | 71 | /** 72 | * Sets the debugging mode. 73 | * 74 | * @param boolean $debug Enable debugging? 75 | */ 76 | public function __construct($debug = false) 77 | { 78 | $this->debug = $debug; 79 | } 80 | 81 | /** 82 | * Creates a pre-configured instance of Wise. 83 | * 84 | * @param array|string $paths The configuration directory path(s). 85 | * @param string $cache The cache directory path. 86 | * @param boolean $debug Enable debugging? 87 | * 88 | * @return Wise The instance. 89 | */ 90 | public static function create($paths, $cache = null, $debug = false) 91 | { 92 | $wise = new self($debug); 93 | 94 | if ($cache) { 95 | $wise->setCacheDir($cache); 96 | } 97 | 98 | $locator = new FileLocator($paths); 99 | $resolver = new LoaderResolver( 100 | array( 101 | new Loader\IniFileLoader($locator), 102 | new Loader\JsonFileLoader($locator), 103 | new Loader\PhpFileLoader($locator), 104 | new Loader\XmlFileLoader($locator), 105 | new Loader\YamlFileLoader($locator), 106 | ) 107 | ); 108 | 109 | $wise->setCollector(new Resource\ResourceCollector()); 110 | $wise->setLoader(new DelegatingLoader($resolver)); 111 | 112 | $resolver->setResourceCollector($wise->getCollector()); 113 | $resolver->setWise($wise); 114 | 115 | return $wise; 116 | } 117 | 118 | /** 119 | * Returns the cache directory path. 120 | * 121 | * @return string The path. 122 | */ 123 | public function getCacheDir() 124 | { 125 | return $this->cacheDir; 126 | } 127 | 128 | /** 129 | * Returns the resource collector. 130 | * 131 | * @return ResourceCollectorInterface The collector. 132 | */ 133 | public function getCollector() 134 | { 135 | return $this->collector; 136 | } 137 | 138 | /** 139 | * Returns the list of global parameters. 140 | * 141 | * @return array The parameters. 142 | */ 143 | public function getGlobalParameters() 144 | { 145 | return $this->parameters; 146 | } 147 | 148 | /** 149 | * Returns the configuration loader. 150 | * 151 | * @return LoaderInterface The loader. 152 | */ 153 | public function getLoader() 154 | { 155 | return $this->loader; 156 | } 157 | 158 | /** 159 | * Returns the configuration processor. 160 | * 161 | * @return ProcessorInterface The processor. 162 | */ 163 | public function getProcessor() 164 | { 165 | return $this->processor; 166 | } 167 | 168 | /** 169 | * Checks if debugging is enabled. 170 | * 171 | * @return boolean TRUE if it is enabled, FALSE if not. 172 | */ 173 | public function isDebugEnabled() 174 | { 175 | return $this->debug; 176 | } 177 | 178 | /** 179 | * Loads the configuration data from a resource. 180 | * 181 | * @param mixed $resource A resource. 182 | * @param string $type The resource type. 183 | * @param boolean $require Require processing? 184 | * 185 | * @return array The data. 186 | * 187 | * @throws LoaderException If the loader could not be used. 188 | * @throws LogicException If no loader has been configured. 189 | */ 190 | public function load($resource, $type = null, $require = false) 191 | { 192 | if (null === $this->loader) { 193 | throw new LogicException('No loader has been configured.'); 194 | } 195 | 196 | if (false === $this->loader->supports($resource, $type)) { 197 | throw LoaderException::format( 198 | 'The resource "%s"%s is not supported by the loader.', 199 | is_scalar($resource) ? $resource : gettype($resource), 200 | $type ? " ($type)" : '' 201 | ); 202 | } 203 | 204 | if ($this->cacheDir 205 | && $this->collector 206 | && is_string($resource) 207 | && (false === strpos("\n", $resource)) 208 | && (false === strpos("\r", $resource))) { 209 | $cache = new ConfigCache( 210 | $this->cacheDir . DIRECTORY_SEPARATOR . basename($resource) . '.cache', 211 | $this->debug 212 | ); 213 | 214 | if ($cache->isFresh()) { 215 | /** @noinspection PhpIncludeInspection */ 216 | return require $cache; 217 | } 218 | } 219 | 220 | if ($this->collector) { 221 | $this->collector->clearResources(); 222 | } 223 | 224 | $data = $this->process( 225 | $this->loader->load($resource, $type), 226 | $resource, 227 | $type, 228 | $require 229 | ); 230 | 231 | if (isset($cache)) { 232 | $cache->write( 233 | 'collector->getResources() 235 | ); 236 | } 237 | 238 | return $data; 239 | } 240 | 241 | /** 242 | * Loads the configuration data from a resource and returns it flattened. 243 | * 244 | * @param mixed $resource A resource. 245 | * @param string $type The resource type. 246 | * @param boolean $require Require processing? 247 | * 248 | * @return array The data. 249 | */ 250 | public function loadFlat($resource, $type = null, $require = false) 251 | { 252 | return ArrayUtil::flatten($this->load($resource, $type, $require)); 253 | } 254 | 255 | /** 256 | * Sets the cache directory path. 257 | * 258 | * @param string $path The path. 259 | */ 260 | public function setCacheDir($path) 261 | { 262 | $this->cacheDir = $path; 263 | } 264 | 265 | /** 266 | * Sets the resource collector. 267 | * 268 | * @param ResourceCollectorInterface $collector The collector. 269 | */ 270 | public function setCollector(ResourceCollectorInterface $collector) 271 | { 272 | $this->collector = $collector; 273 | 274 | if ($this->loader) { 275 | if ($this->loader instanceof ResourceAwareInterface) { 276 | $this->loader->setResourceCollector($collector); 277 | } 278 | 279 | if ($this->loader instanceof DelegatingLoader) { 280 | $resolver = $this->loader->getResolver(); 281 | 282 | if ($resolver instanceof ResourceAwareInterface) { 283 | $resolver->setResourceCollector($collector); 284 | } 285 | } 286 | } 287 | } 288 | 289 | /** 290 | * Sets a list of global parameters. 291 | * 292 | * @param array|ArrayAccess $parameters The parameters. 293 | * 294 | * @throws InvalidArgumentException If $parameters is invalid. 295 | */ 296 | public function setGlobalParameters($parameters) 297 | { 298 | if (!is_array($parameters) && !($parameters instanceof ArrayAccess)) { 299 | throw new InvalidArgumentException( 300 | 'The $parameters argument must be an array or array accessible object.' 301 | ); 302 | } 303 | 304 | $this->parameters = $parameters; 305 | } 306 | 307 | /** 308 | * Sets a configuration loader. 309 | * 310 | * @param LoaderInterface $loader A loader. 311 | */ 312 | public function setLoader(LoaderInterface $loader) 313 | { 314 | $this->loader = $loader; 315 | 316 | if ($this->collector && ($loader instanceof ResourceAwareInterface)) { 317 | $loader->setResourceCollector($this->collector); 318 | } 319 | 320 | if ($loader instanceof WiseAwareInterface) { 321 | $loader->setWise($this); 322 | } 323 | 324 | if ($loader instanceof DelegatingLoader) { 325 | $resolver = $loader->getResolver(); 326 | 327 | if ($this->collector && ($resolver instanceof ResourceAwareInterface)) { 328 | $resolver->setResourceCollector($this->collector); 329 | } 330 | 331 | if ($resolver instanceof WiseAwareInterface) { 332 | $resolver->setWise($this); 333 | } 334 | 335 | } 336 | } 337 | 338 | /** 339 | * Sets a configuration processor. 340 | * 341 | * @param ConfigurationInterface $processor A processor. 342 | */ 343 | public function setProcessor(ConfigurationInterface $processor) 344 | { 345 | $this->processor = $processor; 346 | } 347 | 348 | /** 349 | * Processes the configuration definition. 350 | * 351 | * @param array $data The configuration data. 352 | * @param mixed $resource A resource. 353 | * @param string $type The resource type. 354 | * @param boolean $require Require processing? 355 | * 356 | * @return array The processed configuration data. 357 | * 358 | * @throws ProcessorException If the processor could not be used and it is 359 | * require that one be used. 360 | */ 361 | private function process(array $data, $resource, $type, $require) 362 | { 363 | if ($this->processor) { 364 | if ($this->processor instanceof ProcessorInterface) { 365 | if ($this->processor->supports($resource, $type)) { 366 | $data = $this->processor->process($data); 367 | } elseif ($require) { 368 | throw ProcessorException::format( 369 | 'The resource "%s"%s is not supported by the processor.', 370 | is_string($resource) ? $resource : gettype($resource), 371 | $type ? " ($type)" : '' 372 | ); 373 | } 374 | } else { 375 | $processor = new Processor(); 376 | $data = $processor->processConfiguration( 377 | $this->processor, 378 | $data 379 | ); 380 | } 381 | } elseif ($require) { 382 | throw ProcessorException::format( 383 | 'No processor registered to handle any resource.' 384 | ); 385 | } 386 | 387 | return $data; 388 | } 389 | } 390 | -------------------------------------------------------------------------------- /src/tests/Herrera/Wise/Tests/WiseTest.php: -------------------------------------------------------------------------------- 1 | assertTrue($this->getPropertyValue($this->wise, 'debug')); 49 | 50 | $wise = new Wise(); 51 | 52 | $this->assertFalse($this->getPropertyValue($wise, 'debug')); 53 | } 54 | 55 | public function testCreate() 56 | { 57 | file_put_contents( 58 | $this->dir . '/test.php', 59 | << array( 62 | 'number' => 123 63 | ) 64 | ); 65 | PHP 66 | ); 67 | 68 | $wise = Wise::create($this->dir, $this->cache, true); 69 | $expected = array( 70 | 'root' => array( 71 | 'number' => 123 72 | ) 73 | ); 74 | 75 | $this->assertEquals($expected, $wise->load('test.php', 'php')); 76 | $this->assertFileExists($this->cache . '/test.php.cache'); 77 | $this->assertFileExists($this->cache . '/test.php.cache.meta'); 78 | 79 | /** @var $delegator \Symfony\Component\Config\Loader\DelegatingLoader */ 80 | $delegator = $this->getPropertyValue($wise, 'loader'); 81 | 82 | /** @var $loaders \Herrera\Wise\Loader\LoaderResolver */ 83 | $resolver = $delegator->getResolver(); 84 | 85 | /** @var $loader \Herrera\Wise\Loader\AbstractFileLoader */ 86 | foreach ($resolver->getLoaders() as $loader) { 87 | $this->assertSame( 88 | $wise->getCollector(), 89 | $loader->getResourceCollector() 90 | ); 91 | $this->assertSame($wise, $loader->getWise()); 92 | } 93 | } 94 | 95 | public function testGetCacheDir() 96 | { 97 | $this->setPropertyValue($this->wise, 'cacheDir', $this->cache); 98 | 99 | $this->assertEquals($this->cache, $this->wise->getCacheDir()); 100 | } 101 | 102 | public function testGetCollector() 103 | { 104 | $this->setPropertyValue($this->wise, 'collector', $this->collector); 105 | 106 | $this->assertSame($this->collector, $this->wise->getCollector()); 107 | } 108 | 109 | public function testGetGlobalParameters() 110 | { 111 | $this->setPropertyValue( 112 | $this->wise, 113 | 'parameters', 114 | array('value' => 123) 115 | ); 116 | 117 | $this->assertEquals( 118 | array('value' => 123), 119 | $this->wise->getGlobalParameters() 120 | ); 121 | } 122 | 123 | public function testGetLoader() 124 | { 125 | $this->setPropertyValue($this->wise, 'loader', $this->loader); 126 | 127 | $this->assertSame($this->loader, $this->wise->getLoader()); 128 | } 129 | 130 | public function testGetProcessor() 131 | { 132 | $this->setPropertyValue($this->wise, 'processor', $this->processor); 133 | 134 | $this->assertSame($this->processor, $this->wise->getProcessor()); 135 | } 136 | 137 | /** 138 | * @depends testConstruct 139 | */ 140 | public function testIsDebugEnabled() 141 | { 142 | $this->assertTrue($this->wise->isDebugEnabled()); 143 | } 144 | 145 | /** 146 | * @expectedException \Herrera\Wise\Exception\LogicException 147 | * @expectedExceptionMessage No loader has been configured. 148 | */ 149 | public function testLoadLoaderNotSet() 150 | { 151 | $this->wise->load('test'); 152 | } 153 | 154 | /** 155 | * @expectedException \Herrera\Wise\Exception\LoaderException 156 | * @expectedExceptionMessage The resource "123" (test) is not supported by the loader. 157 | */ 158 | public function testLoadLoaderNotSupported() 159 | { 160 | $this->setPropertyValue($this->wise, 'loader', $this->loader); 161 | 162 | $this->wise->load(123, 'test'); 163 | } 164 | 165 | public function testLoad() 166 | { 167 | file_put_contents( 168 | $this->dir . '/test.php', 169 | << array( 172 | 'number' => 123 173 | ) 174 | ); 175 | PHP 176 | ); 177 | 178 | $expected = array( 179 | 'enabled' => false, 180 | 'number' => '123' 181 | ); 182 | 183 | $this->setPropertyValue($this->wise, 'loader', $this->loader); 184 | 185 | $this->assertEquals( 186 | array( 187 | 'root' => array( 188 | 'number' => 123 189 | ) 190 | ), 191 | $this->wise->load('test.php', 'php') 192 | ); 193 | 194 | $this->setPropertyValue($this->wise, 'cacheDir', $this->cache); 195 | $this->setPropertyValue($this->wise, 'collector', $this->collector); 196 | $this->setPropertyValue($this->wise, 'debug', true); 197 | $this->setPropertyValue($this->wise, 'processor', $this->processor); 198 | 199 | $this->assertEquals($expected, $this->wise->load('test.php', 'php')); 200 | $this->assertFileExists($this->cache . '/test.php.cache'); 201 | $this->assertFileExists($this->cache . '/test.php.cache.meta'); 202 | 203 | /** @noinspection PhpIncludeInspection */ 204 | $this->assertEquals($expected, require $this->cache . '/test.php.cache'); 205 | 206 | $meta = unserialize( 207 | file_get_contents( 208 | $this->cache . '/test.php.cache.meta' 209 | ) 210 | ); 211 | 212 | $this->assertCount(1, $meta); 213 | $this->assertInstanceOf( 214 | 'Symfony\\Component\\Config\\Resource\\FileResource', 215 | $meta[0] 216 | ); 217 | 218 | file_put_contents($this->dir . '/test.php', ''); 219 | touch( 220 | $this->dir . '/test.php', 221 | filemtime($this->cache . '/test.php.cache') - 1000 222 | ); 223 | 224 | $this->assertEquals($expected, $this->wise->load('test.php', 'php')); 225 | } 226 | 227 | /** 228 | * @depends testLoad 229 | */ 230 | public function testLoadFlat() 231 | { 232 | file_put_contents( 233 | $this->dir . '/test.php', 234 | << array( 237 | 'number' => 123 238 | ) 239 | ); 240 | PHP 241 | ); 242 | 243 | $this->setPropertyValue($this->wise, 'loader', $this->loader); 244 | 245 | $this->assertEquals( 246 | array( 247 | 'root.number' => 123 248 | ), 249 | $this->wise->loadFlat('test.php', 'php') 250 | ); 251 | } 252 | 253 | public function testLoadWithBasicProcessor() 254 | { 255 | file_put_contents( 256 | $this->dir . '/test.php', 257 | << array( 260 | 'number' => 123 261 | ) 262 | ); 263 | PHP 264 | ); 265 | 266 | $this->setPropertyValue($this->wise, 'loader', $this->loader); 267 | $this->setPropertyValue($this->wise, 'processor', new BasicProcessor()); 268 | 269 | $this->assertEquals( 270 | array( 271 | 'enabled' => false, 272 | 'number' => 123 273 | ), 274 | $this->wise->load('test.php', 'php') 275 | ); 276 | } 277 | 278 | public function testLoadNoProcessorSupported() 279 | { 280 | file_put_contents( 281 | $this->dir . '/test.php', 282 | << array( 285 | 'number' => 123 286 | ) 287 | ); 288 | PHP 289 | ); 290 | 291 | $this->setPropertyValue($this->wise, 'loader', $this->loader); 292 | $this->setPropertyValue( 293 | $this->wise, 294 | 'processor', 295 | new NeverSupportedProcessor() 296 | ); 297 | 298 | $this->setExpectedException( 299 | 'Herrera\\Wise\\Exception\\ProcessorException', 300 | 'The resource "test.php" (php) is not supported by the processor.' 301 | ); 302 | 303 | $this->wise->load('test.php', 'php', true); 304 | } 305 | 306 | public function testLoadNoProcessorRegistered() 307 | { 308 | file_put_contents( 309 | $this->dir . '/test.php', 310 | << array( 313 | 'number' => 123 314 | ) 315 | ); 316 | PHP 317 | ); 318 | 319 | $this->setPropertyValue($this->wise, 'loader', $this->loader); 320 | 321 | $this->setExpectedException( 322 | 'Herrera\\Wise\\Exception\\ProcessorException', 323 | 'No processor registered to handle any resource.' 324 | ); 325 | 326 | $this->wise->load('test.php', 'php', true); 327 | } 328 | 329 | /** 330 | * @depends testGetCacheDir 331 | */ 332 | public function testSetCacheDir() 333 | { 334 | $this->wise->setCacheDir($this->cache); 335 | 336 | $this->assertEquals($this->cache, $this->wise->getCacheDir()); 337 | } 338 | 339 | /** 340 | * @depends testGetCollector 341 | */ 342 | public function testSetCollector() 343 | { 344 | $this->setPropertyValue($this->wise, 'loader', $this->loader); 345 | 346 | $this->wise->setCollector($this->collector); 347 | 348 | $this->assertSame($this->collector, $this->wise->getCollector()); 349 | $this->assertSame( 350 | $this->collector, 351 | $this->loader->getResourceCollector() 352 | ); 353 | } 354 | 355 | public function testSetCollectorDelegator() 356 | { 357 | $resolver = new LoaderResolver(); 358 | $loader = new DelegatingLoader($resolver); 359 | 360 | $this->setPropertyValue($this->wise, 'loader', $loader); 361 | 362 | $this->wise->setCollector($this->collector); 363 | 364 | $this->assertSame($this->collector, $resolver->getResourceCollector()); 365 | } 366 | 367 | /** 368 | * @depends testGetGlobalParameters 369 | */ 370 | public function testSetGlobalParameters() 371 | { 372 | $this->wise->setGlobalParameters(array('value' => 123)); 373 | 374 | $this->assertEquals( 375 | array('value' => 123), 376 | $this->wise->getGlobalParameters() 377 | ); 378 | } 379 | 380 | public function testSetGlobalParametersInvalid() 381 | { 382 | $this->setExpectedException( 383 | 'Herrera\\Wise\\Exception\\InvalidArgumentException', 384 | 'The $parameters argument must be an array or array accessible object.' 385 | ); 386 | 387 | $this->wise->setGlobalParameters(true); 388 | } 389 | 390 | /** 391 | * @depends testGetLoader 392 | */ 393 | public function testSetLoader() 394 | { 395 | $this->setPropertyValue($this->wise, 'collector', $this->collector); 396 | 397 | $this->wise->setLoader($this->loader); 398 | 399 | $this->assertSame($this->loader, $this->wise->getLoader()); 400 | $this->assertSame( 401 | $this->collector, 402 | $this->loader->getResourceCollector() 403 | ); 404 | $this->assertSame( 405 | $this->wise, 406 | $this->loader->getWise() 407 | ); 408 | } 409 | 410 | public function setSetLoaderDelegator() 411 | { 412 | $this->setPropertyValue($this->wise, 'collector', $this->collector); 413 | 414 | $resolver = new LoaderResolver(); 415 | $loader = new DelegatingLoader($resolver); 416 | 417 | $this->wise->setLoader($loader); 418 | 419 | $this->assertSame($this->collector, $resolver->getResourceCollector()); 420 | $this->assertSame($this->wise, $resolver->getWise()); 421 | } 422 | 423 | /** 424 | * @depends testGetProcessor 425 | */ 426 | public function testSetProcessor() 427 | { 428 | $this->wise->setProcessor($this->processor); 429 | 430 | $this->assertSame($this->processor, $this->wise->getProcessor()); 431 | 432 | $processor = new BasicProcessor(); 433 | 434 | $this->wise->setProcessor($processor); 435 | 436 | $this->assertSame($processor, $this->wise->getProcessor()); 437 | } 438 | 439 | protected function setUp() 440 | { 441 | $this->cache = $this->createDir(); 442 | $this->dir = $this->createDir(); 443 | 444 | $this->collector = new ResourceCollector(); 445 | $this->loader = new PhpFileLoader(new FileLocator($this->dir)); 446 | $this->processor = new TestProcessor(); 447 | $this->wise = new Wise(true); 448 | 449 | $this->loader->setResourceCollector($this->collector); 450 | } 451 | } 452 | --------------------------------------------------------------------------------