├── phpunit.xml.dist ├── .gitignore ├── .coveralls.yml ├── tests ├── fixtures │ ├── dummy_file.txt │ ├── override_config_file.php │ ├── deep_config_file.php │ ├── config_file.php │ ├── override_deep_config_file.php │ └── schema_config_file.php ├── bootstrap.php ├── TestCase.php ├── ConfigSchemaTest.php ├── ConfigFactoryTest.php ├── ConfigTraitTest.php └── ConfigTest.php ├── phpcs.xml.dist ├── .github └── workflows │ ├── code-quality.yml │ └── testing.yml ├── .docheader ├── src ├── Exception │ ├── ConfigException.php │ ├── KeyNotFoundException.php │ ├── FailedToLoadConfigException.php │ ├── InvalidConfigException.php │ ├── FailedToProcessConfigException.php │ ├── FailedToInstantiateParentException.php │ ├── FailedToResolveConfigException.php │ └── InvalidConfigurationSourceException.php ├── ConfigValidatorInterface.php ├── ConfigSchemaInterface.php ├── Loader.php ├── Loader │ ├── LoaderInterface.php │ ├── AbstractLoader.php │ ├── JSONLoader.php │ ├── PHPLoader.php │ └── LoaderFactory.php ├── AbstractConfigSchema.php ├── ConfigInterface.php ├── ConfigSchema.php ├── ConfigTrait.php ├── AbstractConfig.php ├── Config.php └── ConfigFactory.php ├── repositories.yaml ├── phpunit.xml ├── LICENSE ├── composer.json ├── CHANGELOG.md └── README.md /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | composer.lock 3 | -------------------------------------------------------------------------------- /.coveralls.yml: -------------------------------------------------------------------------------- 1 | json_path: coveralls-upload.json 2 | -------------------------------------------------------------------------------- /tests/fixtures/dummy_file.txt: -------------------------------------------------------------------------------- 1 | # Readme File to force an exception from Config constructor 2 | 3 | Just some random content. 4 | -------------------------------------------------------------------------------- /phpcs.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | The coding standard for the Bright Nucleus Config package. 4 | 5 | src 6 | tests 7 | 8 | 9 | -------------------------------------------------------------------------------- /.github/workflows/code-quality.yml: -------------------------------------------------------------------------------- 1 | name: Code Quality Checks 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | - master 9 | 10 | jobs: 11 | code-quality: 12 | uses: brightnucleus/.github/.github/workflows/reusable-code-quality.yml@main 13 | -------------------------------------------------------------------------------- /.docheader: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config; 13 | 14 | $test_data = [ 15 | 16 | 'random_string' => 'override_value', 17 | 18 | ]; 19 | 20 | return $test_data; 21 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config\Tests; 13 | 14 | use Yoast\PHPUnitPolyfills\TestCases\TestCase as PolyfilledTestCase; 15 | 16 | class TestCase extends PolyfilledTestCase 17 | { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /tests/fixtures/deep_config_file.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config; 13 | 14 | $test_data = [ 15 | 'section_1' => [ 16 | 'test_key_1' => 'test_value_1', 17 | ], 18 | 'section_2' => [ 19 | 'test_key_2' => 'test_value_2', 20 | ], 21 | ]; 22 | 23 | return ['vendor' => ['package' => $test_data]]; 24 | -------------------------------------------------------------------------------- /tests/fixtures/config_file.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config; 13 | 14 | $test_data = [ 15 | 16 | 'random_string' => 'test_value', 17 | 'positive_integer' => 42, 18 | 'negative_integer' => -256, 19 | 'positive_boolean' => true, 20 | 'negative_boolean' => false, 21 | 22 | ]; 23 | 24 | return $test_data; 25 | -------------------------------------------------------------------------------- /src/Exception/ConfigException.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config\Exception; 13 | 14 | use BrightNucleus\Exception\ExceptionInterface; 15 | 16 | /** 17 | * Interface ConfigException. 18 | * 19 | * @since 0.4.0 20 | * 21 | * @package BrightNucleus\Config\Exception 22 | * @author Alain Schlesser 23 | */ 24 | interface ConfigException extends ExceptionInterface 25 | { 26 | 27 | } 28 | -------------------------------------------------------------------------------- /tests/fixtures/override_deep_config_file.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config; 13 | 14 | $test_data = [ 15 | 'section_1' => [ 16 | 'test_key_3' => 'override_value_3', 17 | ], 18 | 'section_2' => [ 19 | 'test_key_2' => 'override_value_2', 20 | ], 21 | 'section_3' => [ 22 | 'test_key_4' => 'override_value_4', 23 | ], 24 | ]; 25 | 26 | return ['vendor' => ['package' => $test_data]]; 27 | -------------------------------------------------------------------------------- /src/Exception/KeyNotFoundException.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config\Exception; 13 | 14 | use BrightNucleus\Exception\OutOfRangeException; 15 | 16 | /** 17 | * Class KeyNotFoundException. 18 | * 19 | * @since 0.4.0 20 | * 21 | * @package BrightNucleus\Config\Exception 22 | * @author Alain Schlesser 23 | */ 24 | class KeyNotFoundException extends OutOfRangeException implements ConfigException 25 | { 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/Exception/FailedToLoadConfigException.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config\Exception; 13 | 14 | use BrightNucleus\Exception\RuntimeException; 15 | 16 | /** 17 | * Class FailedToLoadConfigException. 18 | * 19 | * @since 0.4.0 20 | * 21 | * @package BrightNucleus\Config\Exception 22 | * @author Alain Schlesser 23 | */ 24 | class FailedToLoadConfigException extends RuntimeException implements ConfigException 25 | { 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/Exception/InvalidConfigException.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config\Exception; 13 | 14 | use BrightNucleus\Exception\UnexpectedValueException; 15 | 16 | /** 17 | * Class FailedToLoadConfigException. 18 | * 19 | * @since 0.4.0 20 | * 21 | * @package BrightNucleus\Config\Exception 22 | * @author Alain Schlesser 23 | */ 24 | class InvalidConfigException extends UnexpectedValueException implements ConfigException 25 | { 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/Exception/FailedToProcessConfigException.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config\Exception; 13 | 14 | use BrightNucleus\Exception\RuntimeException; 15 | 16 | /** 17 | * Class FailedToProcessConfigException. 18 | * 19 | * @since 0.4.0 20 | * 21 | * @package BrightNucleus\Config\Exception 22 | * @author Alain Schlesser 23 | */ 24 | class FailedToProcessConfigException extends RuntimeException implements ConfigException 25 | { 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/Exception/FailedToInstantiateParentException.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config\Exception; 13 | 14 | use BrightNucleus\Exception\RuntimeException; 15 | 16 | /** 17 | * Class FailedToInstantiateParentException. 18 | * 19 | * @since 0.4.0 20 | * 21 | * @package BrightNucleus\Config\Exception 22 | * @author Alain Schlesser 23 | */ 24 | class FailedToInstantiateParentException extends RuntimeException implements ConfigException 25 | { 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/Exception/FailedToResolveConfigException.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config\Exception; 13 | 14 | use BrightNucleus\Exception\UnexpectedValueException; 15 | 16 | /** 17 | * Class FailedToResolveConfigException. 18 | * 19 | * @since 0.4.0 20 | * 21 | * @package BrightNucleus\Config\Exception 22 | * @author Alain Schlesser 23 | */ 24 | class FailedToResolveConfigException extends UnexpectedValueException implements ConfigException 25 | { 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/Exception/InvalidConfigurationSourceException.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config\Exception; 13 | 14 | use BrightNucleus\Exception\InvalidArgumentException; 15 | 16 | /** 17 | * Class InvalidConfigurationSourceException. 18 | * 19 | * @since 0.4.0 20 | * 21 | * @package BrightNucleus\Config\Exception 22 | * @author Alain Schlesser 23 | */ 24 | class InvalidConfigurationSourceException extends InvalidArgumentException implements ConfigException 25 | { 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/ConfigValidatorInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config; 13 | 14 | /** 15 | * Contract to deal with configuration value validation. 16 | * 17 | * @since 0.1.0 18 | * 19 | * @package BrightNucleus\Config 20 | * @author Alain Schlesser 21 | */ 22 | interface ConfigValidatorInterface 23 | { 24 | 25 | /** 26 | * Check whether the passed-in Config is valid. 27 | * 28 | * @since 0.1.0 29 | * 30 | * @param ConfigInterface $config 31 | * 32 | * @return bool 33 | */ 34 | public function isValid(ConfigInterface $config); 35 | } 36 | -------------------------------------------------------------------------------- /tests/fixtures/schema_config_file.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Core; 13 | 14 | $test_schema = [ 15 | 16 | 'random_string' => [ 17 | 'default' => 'default_test_value', 18 | 'required' => true, 19 | ], 20 | 'positive_integer' => [ 21 | 'default' => 99, 22 | 'required' => 'TRUE', 23 | ], 24 | 'negative_integer' => [ 25 | 'required' => 'Yes', 26 | ], 27 | 'positive_boolean' => [ 28 | 'default' => true, 29 | 'required' => false, 30 | ], 31 | 'negative_boolean' => [ 32 | 'required' => 'No', 33 | ], 34 | 35 | ]; 36 | 37 | return $test_schema; 38 | -------------------------------------------------------------------------------- /repositories.yaml: -------------------------------------------------------------------------------- 1 | # BrightNucleus Repository Sync Configuration 2 | # ========================================== 3 | 4 | # GitHub organization name 5 | organization: brightnucleus 6 | 7 | # Repositories to exclude from sync (e.g., the dev repo itself) 8 | exclude_repos: 9 | - brightnucleus.dev 10 | 11 | # Common default branch names to check (in order of preference) 12 | default_branch_names: 13 | - main 14 | - master 15 | - develop 16 | 17 | # Safety checks before updating repositories 18 | safety_checks: 19 | # Check for uncommitted changes before updating 20 | check_uncommitted: true 21 | 22 | # Check for unpushed commits before updating 23 | check_unpushed: true 24 | 25 | # Create backup before updating (not implemented yet) 26 | create_backup: false 27 | 28 | # Additional repository-specific configurations 29 | # You can override settings for specific repositories 30 | repository_overrides: [] 31 | # Example: 32 | # - name: specific-repo 33 | # default_branch: custom-branch 34 | # skip_update: false -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | tests 18 | 19 | 20 | 21 | 23 | 24 | src 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Alain Schlesser, Bright Nucleus 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /src/ConfigSchemaInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config; 13 | 14 | /** 15 | * Contract to deal with configuration requirements. 16 | * 17 | * @since 0.1.0 18 | * 19 | * @package BrightNucleus\Config 20 | * @author Alain Schlesser 21 | */ 22 | interface ConfigSchemaInterface 23 | { 24 | 25 | /** 26 | * Get the set of defined options. 27 | * 28 | * @since 0.1.0 29 | * 30 | * @return array|null 31 | */ 32 | public function getDefinedOptions(); 33 | 34 | /** 35 | * Get the set of default options. 36 | * 37 | * @since 0.1.0 38 | * 39 | * @return array|null 40 | */ 41 | public function getDefaultOptions(); 42 | 43 | /** 44 | * Get the set of required options. 45 | * 46 | * @since 0.1.0 47 | * 48 | * @return array|null 49 | */ 50 | public function getRequiredOptions(); 51 | } 52 | -------------------------------------------------------------------------------- /src/Loader.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config; 13 | 14 | use BrightNucleus\Config\Exception\FailedToLoadConfigException; 15 | use BrightNucleus\Config\Loader\LoaderFactory; 16 | 17 | /** 18 | * Class Loader. 19 | * 20 | * @since 0.4.0 21 | * 22 | * @package BrightNucleus\Config 23 | * @author Alain Schlesser 24 | */ 25 | class Loader 26 | { 27 | 28 | /** 29 | * Static convenience function to load a configuration from an URI. 30 | * 31 | * @since 0.4.0 32 | * 33 | * @param string $uri URI of the resource to load. 34 | * 35 | * @return array|null Parsed data loaded from the resource. 36 | * @throws FailedToLoadConfigException If the configuration could not be loaded. 37 | */ 38 | public static function load($uri) 39 | { 40 | $loader = LoaderFactory::createFromUri($uri); 41 | 42 | return $loader->load($uri); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Loader/LoaderInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config\Loader; 13 | 14 | /** 15 | * Interface LoaderInterface. 16 | * 17 | * @since 0.4.0 18 | * 19 | * @package BrightNucleus\Config\Loader 20 | * @author Alain Schlesser 21 | */ 22 | interface LoaderInterface 23 | { 24 | 25 | /** 26 | * Check whether the loader is able to load a given URI. 27 | * 28 | * @since 0.4.0 29 | * 30 | * @param string $uri URI to check. 31 | * 32 | * @return bool Whether the loader can load the given URI. 33 | */ 34 | public static function canLoad($uri); 35 | 36 | /** 37 | * Load the configuration from an URI. 38 | * 39 | * @since 0.4.0 40 | * 41 | * @param string $uri URI of the resource to load. 42 | * 43 | * @return array|null Data contained within the resource. Null if no data could be loaded/parsed. 44 | * @throws FailedToLoadConfigException If the configuration could not be loaded. 45 | */ 46 | public function load($uri); 47 | } 48 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "brightnucleus/config", 3 | "description": "Minimal, reusable Config component.", 4 | "minimum-stability": "dev", 5 | "prefer-stable": true, 6 | "require-dev": { 7 | "malukenho/docheader": "^1", 8 | "mikey179/vfsstream": "^1.6", 9 | "yoast/phpunit-polyfills": "^3", 10 | "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5 || ^10", 11 | "squizlabs/php_codesniffer": "^3", 12 | "php-parallel-lint/php-parallel-lint": "^1.4" 13 | }, 14 | "license": "MIT", 15 | "authors": [ 16 | { 17 | "name": "Alain Schlesser", 18 | "email": "alain.schlesser@gmail.com" 19 | } 20 | ], 21 | "require": { 22 | "php": ">=8.0", 23 | "symfony/options-resolver": ">=2.8", 24 | "brightnucleus/exceptions": ">=0.4" 25 | }, 26 | "autoload": { 27 | "psr-4": { 28 | "BrightNucleus\\Config\\": "src/" 29 | } 30 | }, 31 | "autoload-dev": { 32 | "psr-4": { 33 | "BrightNucleus\\Config\\Tests\\": "tests/" 34 | } 35 | }, 36 | "scripts": { 37 | "check": [ 38 | "@cs-check", 39 | "@test" 40 | ], 41 | "upload-coverage": "vendor/bin/coveralls -v --coverage_clover clover.xml", 42 | "cs-check": "vendor/bin/phpcs -ps --colors", 43 | "cs-fix": "vendor/bin/phpcbf -ps --colors", 44 | "test": "vendor/bin/phpunit --colors", 45 | "test-coverage": "vendor/bin/phpunit --colors --coverage-clover clover.xml", 46 | "license-check": "vendor/bin/docheader --ansi check src/" 47 | } 48 | } -------------------------------------------------------------------------------- /tests/ConfigSchemaTest.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config\Tests; 13 | 14 | use BrightNucleus\Config\Config; 15 | use BrightNucleus\Config\ConfigSchema; 16 | 17 | class ConfigSchemaTest extends TestCase 18 | { 19 | 20 | /** 21 | * @covers \BrightNucleus\Config\ConfigSchema::__construct 22 | */ 23 | public function testInstantiation() 24 | { 25 | $schema_config = $this->getMockBuilder('\BrightNucleus\Config\ConfigInterface') 26 | ->getMock(); 27 | $schema_config->method('getArrayCopy') 28 | ->willReturn([]); 29 | $schema = new ConfigSchema($schema_config); 30 | $this->assertInstanceOf( 31 | '\BrightNucleus\Config\ConfigSchemaInterface', 32 | $schema 33 | ); 34 | $this->assertInstanceOf( 35 | '\BrightNucleus\Config\AbstractConfigSchema', 36 | $schema 37 | ); 38 | $this->assertInstanceOf('\BrightNucleus\Config\ConfigSchema', $schema); 39 | $this->expectException('BrightNucleus\Exception\InvalidArgumentException'); 40 | $this->expectExceptionMessage('Invalid schema source:'); 41 | new ConfigSchema(25); 42 | } 43 | 44 | /** 45 | * @covers \BrightNucleus\Config\ConfigSchema::parseSchema 46 | * @covers \BrightNucleus\Config\ConfigSchema::parseDefined 47 | * @covers \BrightNucleus\Config\ConfigSchema::parseDefault 48 | * @covers \BrightNucleus\Config\ConfigSchema::parseRequired 49 | * @covers \BrightNucleus\Config\ConfigSchema::isTruthy 50 | */ 51 | public function testParsing() 52 | { 53 | $schema_config = new Config(__DIR__ . '/fixtures/schema_config_file.php'); 54 | $schema = new ConfigSchema($schema_config); 55 | $this->assertInstanceOf( 56 | '\BrightNucleus\Config\ConfigSchemaInterface', 57 | $schema 58 | ); 59 | $this->assertInstanceOf( 60 | '\BrightNucleus\Config\AbstractConfigSchema', 61 | $schema 62 | ); 63 | $this->assertInstanceOf('\BrightNucleus\Config\ConfigSchema', $schema); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/AbstractConfigSchema.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config; 13 | 14 | /** 15 | * Handles basic validation of the config schema. 16 | * 17 | * @since 0.1.0 18 | * 19 | * @package BrightNucleus\Config 20 | * @author Alain Schlesser 21 | */ 22 | abstract class AbstractConfigSchema implements ConfigSchemaInterface 23 | { 24 | 25 | /** 26 | * The defined values that are recognized. 27 | * 28 | * @var ConfigInterface 29 | */ 30 | protected $defined; 31 | 32 | /** 33 | * The default values that can be overwritten. 34 | * 35 | * @var ConfigInterface 36 | */ 37 | protected $defaults; 38 | 39 | /** 40 | * The required values that need to be set. 41 | * 42 | * @var ConfigInterface 43 | */ 44 | protected $required; 45 | 46 | /** 47 | * Get the set of defined options. 48 | * 49 | * @since 0.1.0 50 | * 51 | * @return array|null 52 | */ 53 | public function getDefinedOptions() 54 | { 55 | if (! $this->defined) { 56 | return null; 57 | } 58 | 59 | if ($this->defined instanceof ConfigInterface) { 60 | return $this->defined->getArrayCopy(); 61 | } 62 | 63 | return (array)$this->defined; 64 | } 65 | 66 | /** 67 | * Get the set of default options. 68 | * 69 | * @since 0.1.0 70 | * 71 | * @return array|null 72 | */ 73 | public function getDefaultOptions() 74 | { 75 | if (! $this->defaults) { 76 | return null; 77 | } 78 | 79 | if ($this->defaults instanceof ConfigInterface) { 80 | return $this->defaults->getArrayCopy(); 81 | } 82 | 83 | return (array)$this->defaults; 84 | } 85 | 86 | /** 87 | * Get the set of required options. 88 | * 89 | * @since 0.1.0 90 | * 91 | * @return array|null 92 | */ 93 | public function getRequiredOptions() 94 | { 95 | if (! $this->required) { 96 | return null; 97 | } 98 | 99 | if ($this->required instanceof ConfigInterface) { 100 | return $this->required->getArrayCopy(); 101 | } 102 | 103 | return (array)$this->required; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/Loader/AbstractLoader.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config\Loader; 13 | 14 | use BrightNucleus\Config\Exception\FailedToLoadConfigException; 15 | use Exception; 16 | 17 | /** 18 | * Class AbstractLoader. 19 | * 20 | * @since 0.4.0 21 | * 22 | * @package BrightNucleus\Config\Loader 23 | * @author Alain Schlesser 24 | */ 25 | abstract class AbstractLoader implements LoaderInterface 26 | { 27 | 28 | /** 29 | * Load the configuration from an URI. 30 | * 31 | * @since 0.4.0 32 | * 33 | * @param string $uri URI of the resource to load. 34 | * 35 | * @return array|null Data contained within the resource. Null if no data could be loaded/parsed. 36 | * @throws FailedToLoadConfigException If the configuration could not be loaded. 37 | */ 38 | public function load($uri) 39 | { 40 | try { 41 | $uri = $this->validateUri($uri); 42 | $data = $this->loadUri($uri); 43 | 44 | return $this->parseData($data); 45 | } catch (Exception $exception) { 46 | throw new FailedToLoadConfigException( 47 | sprintf( 48 | _('Could not load resource located at "%1$s". Reason: "%2$s".'), 49 | $uri, 50 | $exception->getMessage() 51 | ), 52 | $exception->getCode(), 53 | $exception 54 | ); 55 | } 56 | } 57 | 58 | /** 59 | * Validate and return the URI. 60 | * 61 | * @since 0.4.0 62 | * 63 | * @param string $uri URI of the resource to load. 64 | * 65 | * @return string Validated URI. 66 | */ 67 | protected function validateUri($uri) 68 | { 69 | return $uri; 70 | } 71 | 72 | /** 73 | * Parse the raw data and return it in parsed form. 74 | * 75 | * @since 0.4.0 76 | * 77 | * @param array|null $data Raw data to be parsed. 78 | * 79 | * @return array|null Data in parsed form. Null if no parsable data found. 80 | */ 81 | protected function parseData($data) 82 | { 83 | return $data; 84 | } 85 | 86 | /** 87 | * Load the contents of an resource identified by an URI. 88 | * 89 | * @since 0.4.0 90 | * 91 | * @param string $uri URI of the resource. 92 | * 93 | * @return array|null Raw data loaded from the resource. Null if no data found. 94 | */ 95 | abstract protected function loadUri($uri); 96 | } 97 | -------------------------------------------------------------------------------- /src/Loader/JSONLoader.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config\Loader; 13 | 14 | use BrightNucleus\Config\Exception\FailedToLoadConfigException; 15 | use Exception; 16 | 17 | /** 18 | * Class JSONLoader. 19 | * 20 | * @since 0.1.0 21 | * 22 | * @package BrightNucleus\Config\Loader 23 | * @author Pascal Knecht 24 | */ 25 | class JSONLoader extends AbstractLoader 26 | { 27 | 28 | /** 29 | * Check whether the loader is able to load a given URI. 30 | * 31 | * @since 0.4.0 32 | * 33 | * @param string $uri URI to check. 34 | * 35 | * @return bool Whether the loader can load the given URI. 36 | */ 37 | public static function canLoad($uri) 38 | { 39 | $path = pathinfo($uri); 40 | 41 | return 'json' === mb_strtolower($path['extension']); 42 | } 43 | 44 | /** 45 | * Load the contents of an resource identified by an URI. 46 | * 47 | * @since 0.4.0 48 | * 49 | * @param string $uri URI of the resource. 50 | * 51 | * @return array|null Raw data loaded from the resource. Null if no data found. 52 | * @throws FailedToLoadConfigException If the resource could not be loaded. 53 | */ 54 | protected function loadUri($uri) 55 | { 56 | try { 57 | ob_start(); 58 | $data = json_decode(file_get_contents($uri), true); 59 | ob_end_clean(); 60 | 61 | return (array)$data; 62 | } catch (Exception $exception) { 63 | throw new FailedToLoadConfigException( 64 | sprintf( 65 | _('Could not include JSON config file "%1$s". Reason: "%2$s".'), 66 | $uri, 67 | $exception->getMessage() 68 | ), 69 | $exception->getCode(), 70 | $exception 71 | ); 72 | } 73 | } 74 | 75 | /** 76 | * Validate and return the URI. 77 | * 78 | * @since 0.4.0 79 | * 80 | * @param string $uri URI of the resource to load. 81 | * 82 | * @return string Validated URI. 83 | * @throws FailedToLoadConfigException If the URI does not exist or is not readable. 84 | */ 85 | protected function validateUri($uri) 86 | { 87 | if (! is_readable($uri)) { 88 | throw new FailedToLoadConfigException( 89 | sprintf( 90 | _('The requested JSON config file "%1$s" does not exist or is not readable.'), 91 | $uri 92 | ) 93 | ); 94 | } 95 | 96 | return $uri; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Loader/PHPLoader.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config\Loader; 13 | 14 | use BrightNucleus\Config\Exception\FailedToLoadConfigException; 15 | use Exception; 16 | 17 | /** 18 | * Class PHPLoader. 19 | * 20 | * @since 0.4.0 21 | * 22 | * @package BrightNucleus\Config\Loader 23 | * @author Alain Schlesser 24 | */ 25 | class PHPLoader extends AbstractLoader 26 | { 27 | 28 | /** 29 | * Check whether the loader is able to load a given URI. 30 | * 31 | * @since 0.4.0 32 | * 33 | * @param string $uri URI to check. 34 | * 35 | * @return bool Whether the loader can load the given URI. 36 | */ 37 | public static function canLoad($uri) 38 | { 39 | $path = pathinfo($uri); 40 | 41 | return 'php' === mb_strtolower($path['extension']); 42 | } 43 | 44 | /** 45 | * Load the contents of an resource identified by an URI. 46 | * 47 | * @since 0.4.0 48 | * 49 | * @param string $uri URI of the resource. 50 | * 51 | * @return array|null Raw data loaded from the resource. Null if no data found. 52 | * @throws FailedToLoadConfigException If the resource could not be loaded. 53 | */ 54 | protected function loadUri($uri) 55 | { 56 | try { 57 | // Try to load the file through PHP's include(). 58 | // Make sure we don't accidentally create output. 59 | ob_start(); 60 | $data = include($uri); 61 | ob_end_clean(); 62 | 63 | return $data; 64 | } catch (Exception $exception) { 65 | throw new FailedToLoadConfigException( 66 | sprintf( 67 | _('Could not include PHP config file "%1$s". Reason: "%2$s".'), 68 | $uri, 69 | $exception->getMessage() 70 | ), 71 | $exception->getCode(), 72 | $exception 73 | ); 74 | } 75 | } 76 | 77 | /** 78 | * Validate and return the URI. 79 | * 80 | * @since 0.4.0 81 | * 82 | * @param string $uri URI of the resource to load. 83 | * 84 | * @return string Validated URI. 85 | * @throws FailedToLoadConfigException If the URI does not exist or is not readable. 86 | */ 87 | protected function validateUri($uri) 88 | { 89 | if (! is_readable($uri)) { 90 | throw new FailedToLoadConfigException( 91 | sprintf( 92 | _('The requested PHP config file "%1$s" does not exist or is not readable.'), 93 | $uri 94 | ) 95 | ); 96 | } 97 | 98 | return $uri; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/ConfigInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config; 13 | 14 | use ArrayAccess; 15 | use Countable; 16 | use IteratorAggregate; 17 | use Serializable; 18 | 19 | /** 20 | * Contract to deal with configuration values. 21 | * 22 | * @since 0.1.0 23 | * 24 | * @package BrightNucleus\Config 25 | * @author Alain Schlesser 26 | */ 27 | interface ConfigInterface extends IteratorAggregate, ArrayAccess, Serializable, Countable 28 | { 29 | 30 | /** 31 | * Creates a copy of the ArrayObject. 32 | * 33 | * Returns a copy of the array. When the ArrayObject refers to an object an 34 | * array of the public properties of that object will be returned. 35 | * This is implemented by \ArrayObject. 36 | * 37 | * @since 0.1.0 38 | * 39 | * @return array Copy of the array. 40 | */ 41 | public function getArrayCopy(); 42 | 43 | /** 44 | * Check whether the Config has a specific key. 45 | * 46 | * To check a value several levels deep, add the keys for each level as a comma-separated list. 47 | * 48 | * @since 0.1.0 49 | * @since 0.1.4 Accepts list of keys. 50 | * 51 | * @param string $_ List of keys. 52 | * 53 | * @return bool 54 | */ 55 | public function hasKey($_); 56 | 57 | /** 58 | * Get the value of a specific key. 59 | * 60 | * To get a value several levels deep, add the keys for each level as a comma-separated list. 61 | * 62 | * @since 0.1.0 63 | * @since 0.1.4 Accepts list of keys. 64 | * 65 | * @param string $_ List of keys. 66 | * 67 | * @return mixed 68 | */ 69 | public function getKey($_); 70 | 71 | /** 72 | * Get a (multi-dimensional) array of all the configuration settings. 73 | * 74 | * @since 0.1.4 75 | * 76 | * @return array 77 | */ 78 | public function getAll(); 79 | 80 | /** 81 | * Get the an array with all the keys 82 | * 83 | * @since 0.1.0 84 | * 85 | * @return mixed 86 | */ 87 | public function getKeys(); 88 | 89 | /** 90 | * Is the Config valid? 91 | * 92 | * @since 0.1.0 93 | * 94 | * @return boolean 95 | */ 96 | public function isValid(); 97 | 98 | /** 99 | * Get a new config at a specific sub-level. 100 | * 101 | * @since 0.1.13 102 | * 103 | * @param string $_ List of keys. 104 | * 105 | * @return ConfigInterface 106 | */ 107 | public function getSubConfig($_); 108 | 109 | /** 110 | * Serialize the config. 111 | * 112 | * @since 0.1.13 113 | * 114 | * @return array 115 | */ 116 | public function __serialize(): array; 117 | 118 | /** 119 | * Unserialize the config. 120 | * 121 | * @since 0.1.13 122 | * 123 | * @param array $data The data to unserialize. 124 | */ 125 | public function __unserialize(array $data): void; 126 | } 127 | -------------------------------------------------------------------------------- /src/Loader/LoaderFactory.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config\Loader; 13 | 14 | use BrightNucleus\Config\Exception\FailedToLoadConfigException; 15 | use Exception; 16 | 17 | /** 18 | * Class LoaderFactory. 19 | * 20 | * @since 0.4.0 21 | * 22 | * @package BrightNucleus\Config\Loader 23 | * @author Alain Schlesser 24 | */ 25 | class LoaderFactory 26 | { 27 | 28 | /** 29 | * Array of fully qualified class names of known loaders. 30 | * 31 | * @var array 32 | * 33 | * @since 0.4.0 34 | */ 35 | protected static $loaders = [ 36 | 'BrightNucleus\Config\Loader\PHPLoader', 37 | 'BrightNucleus\Config\Loader\JSONLoader', 38 | ]; 39 | 40 | /** 41 | * Array of instantiated loaders. 42 | * 43 | * These are lazily instantiated and added as needed. 44 | * 45 | * @var LoaderInterface[] 46 | * 47 | * @since 0.4.0 48 | */ 49 | protected static $loaderInstances = []; 50 | 51 | /** 52 | * Create a new Loader from an URI. 53 | * 54 | * @since 0.4.0 55 | * 56 | * @param string $uri URI of the resource to create a loader for. 57 | * 58 | * @return LoaderInterface Loader that is able to load the given URI. 59 | * @throws FailedToLoadConfigException If no suitable loader was found. 60 | */ 61 | public static function createFromUri($uri) 62 | { 63 | foreach (static::$loaders as $loader) { 64 | if ($loader::canLoad($uri)) { 65 | return static::getLoader($loader); 66 | } 67 | } 68 | 69 | throw new FailedToLoadConfigException( 70 | sprintf( 71 | _('Could not find a suitable loader for URI "%1$s".'), 72 | $uri 73 | ) 74 | ); 75 | } 76 | 77 | /** 78 | * Get an instance of a specific loader. 79 | * 80 | * The loader is lazily instantiated if needed. 81 | * 82 | * @since 0.4.0 83 | * 84 | * @param string $loaderClass Fully qualified class name of the loader to get. 85 | * 86 | * @return LoaderInterface Instance of the requested loader. 87 | * @throws FailedToLoadConfigException If the loader class could not be instantiated. 88 | */ 89 | public static function getLoader($loaderClass) 90 | { 91 | try { 92 | if (! array_key_exists($loaderClass, static::$loaderInstances)) { 93 | static::$loaderInstances[$loaderClass] = new $loaderClass; 94 | } 95 | 96 | return static::$loaderInstances[$loaderClass]; 97 | } catch (Exception $exception) { 98 | throw new FailedToLoadConfigException( 99 | sprintf( 100 | _('Could not instantiate the requested loader class "%1$s".'), 101 | $loaderClass 102 | ) 103 | ); 104 | } 105 | } 106 | 107 | /** 108 | * Register a new loader. 109 | * 110 | * @since 0.4.0 111 | * 112 | * @param string $loader Fully qualified class name of a loader implementing LoaderInterface. 113 | */ 114 | public static function registerLoader($loader) 115 | { 116 | if (in_array($loader, static::$loaders, true)) { 117 | return; 118 | } 119 | 120 | static::$loaders [] = $loader; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/ConfigSchema.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config; 13 | 14 | use BrightNucleus\Exception\InvalidArgumentException; 15 | 16 | /** 17 | * Generic implementation of a configuration requirements check. 18 | * 19 | * @since 0.1.0 20 | * 21 | * @package BrightNucleus\Config 22 | * @author Alain Schlesser 23 | */ 24 | class ConfigSchema extends AbstractConfigSchema 25 | { 26 | 27 | /** 28 | * The key that is used in the schema to define a default value. 29 | */ 30 | const DEFAULT_VALUE = 'default'; 31 | /** 32 | * The key that is used in the schema to define a required value. 33 | */ 34 | const REQUIRED_KEY = 'required'; 35 | 36 | /** 37 | * Instantiate a ConfigSchema object. 38 | * 39 | * @since 0.1.0 40 | * 41 | * @param ConfigInterface|array $schema The schema to parse. 42 | * 43 | * @throws InvalidArgumentException 44 | */ 45 | public function __construct($schema) 46 | { 47 | if ($schema instanceof ConfigInterface) { 48 | $schema = $schema->getArrayCopy(); 49 | } 50 | 51 | if (! is_array($schema)) { 52 | throw new InvalidArgumentException( 53 | sprintf( 54 | _('Invalid schema source: %1$s'), 55 | print_r($schema, true) 56 | ) 57 | ); 58 | } 59 | 60 | array_walk($schema, [$this, 'parseSchema']); 61 | } 62 | 63 | /** 64 | * Parse a single provided schema entry. 65 | * 66 | * @since 0.1.0 67 | * 68 | * @param mixed $data The data associated with the key. 69 | * @param string $key The key of the schema data. 70 | */ 71 | protected function parseSchema($data, $key) 72 | { 73 | $this->parseDefined($key); 74 | 75 | if (array_key_exists(self::REQUIRED_KEY, $data)) { 76 | $this->parseRequired( 77 | $key, 78 | $data[self::REQUIRED_KEY] 79 | ); 80 | } 81 | 82 | if (array_key_exists(self::DEFAULT_VALUE, $data)) { 83 | $this->parseDefault( 84 | $key, 85 | $data[self::DEFAULT_VALUE] 86 | ); 87 | } 88 | } 89 | 90 | /** 91 | * Parse the set of defined values. 92 | * 93 | * @since 0.1.0 94 | * 95 | * @param string $key The key of the schema data. 96 | */ 97 | protected function parseDefined($key) 98 | { 99 | $this->defined[] = $key; 100 | } 101 | 102 | /** 103 | * Parse the set of required values. 104 | * 105 | * @since 0.1.0 106 | * 107 | * @param string $key The key of the schema data. 108 | * @param mixed $data The data associated with the key. 109 | */ 110 | protected function parseRequired($key, $data) 111 | { 112 | if ($this->isTruthy($data)) { 113 | $this->required[] = $key; 114 | } 115 | } 116 | 117 | /** 118 | * Parse the set of default values. 119 | * 120 | * @since 0.1.0 121 | * 122 | * @param string $key The key of the schema data. 123 | * @param mixed $data The data associated with the key. 124 | */ 125 | protected function parseDefault($key, $data) 126 | { 127 | $this->defaults[$key] = $data; 128 | } 129 | 130 | /** 131 | * Return a boolean true or false for an arbitrary set of data. Recognizes 132 | * several different string values that should be valued as true. 133 | * 134 | * @since 0.1.0 135 | * 136 | * @param mixed $data The data to evaluate. 137 | * 138 | * @return bool 139 | */ 140 | protected function isTruthy($data) 141 | { 142 | $truthy_values = [ 143 | true, 144 | 1, 145 | 'true', 146 | 'True', 147 | 'TRUE', 148 | 'y', 149 | 'Y', 150 | 'yes', 151 | 'Yes', 152 | 'YES', 153 | '√', 154 | ]; 155 | 156 | return in_array($data, $truthy_values, true); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/ConfigTrait.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config; 13 | 14 | use BrightNucleus\Config\Exception\FailedToProcessConfigException; 15 | use Exception; 16 | 17 | /** 18 | * Basic config processing that can be included within classes. 19 | * 20 | * @since 0.1.2 21 | * 22 | * @package BrightNucleus\Config 23 | * @author Alain Schlesser 24 | */ 25 | trait ConfigTrait 26 | { 27 | 28 | /** 29 | * Reference to the Config object. 30 | * 31 | * @since 0.1.2 32 | * 33 | * @var ConfigInterface 34 | */ 35 | protected $config; 36 | 37 | /** 38 | * Process the passed-in configuration file. 39 | * 40 | * @since 0.1.2 41 | * 42 | * @param ConfigInterface $config The Config to process. 43 | * @param string ... List of keys. 44 | * 45 | * @throws FailedToProcessConfigException If the arguments could not be parsed into a Config. 46 | */ 47 | protected function processConfig(ConfigInterface $config) 48 | { 49 | if (func_num_args() > 1) { 50 | try { 51 | $keys = func_get_args(); 52 | array_shift($keys); 53 | $config = $config->getSubConfig($keys); 54 | } catch (Exception $exception) { 55 | throw new FailedToProcessConfigException( 56 | sprintf( 57 | _('Could not process the config with the arguments "%1$s".'), 58 | print_r(func_get_args(), true) 59 | ) 60 | ); 61 | } 62 | } 63 | $this->config = $config; 64 | } 65 | 66 | /** 67 | * Check whether the Config has a specific key. 68 | * 69 | * To get a value several levels deep, add the keys for each level as a comma-separated list. 70 | * 71 | * @since 0.1.2 72 | * @since 0.1.5 Accepts list of keys. 73 | * 74 | * @param string|array $_ List of keys. 75 | * 76 | * @return bool Whether the key is known. 77 | */ 78 | protected function hasConfigKey($_) 79 | { 80 | $keys = func_get_args(); 81 | 82 | return $this->config->hasKey($keys); 83 | } 84 | 85 | /** 86 | * Get the Config value for a specific key. 87 | * 88 | * To get a value several levels deep, add the keys for each level as a comma-separated list. 89 | * 90 | * @since 0.1.2 91 | * @since 0.1.5 Accepts list of keys. 92 | * 93 | * @param string|array $_ List of keys. 94 | * 95 | * @return mixed Value of the key. 96 | */ 97 | protected function getConfigKey($_) 98 | { 99 | $keys = func_get_args(); 100 | 101 | return $this->config->getKey($keys); 102 | } 103 | 104 | /** 105 | * Get the callable Config value for a specific key. 106 | * 107 | * If the fetched value is indeed a callable, it will be executed with the provided arguments, and the resultant 108 | * value will be returned instead. 109 | * 110 | * @since 0.4.8 111 | * 112 | * @param string|array $key Key or array of nested keys. 113 | * @param array $args Optional. Array of arguments to pass to the callable. 114 | * 115 | * @return mixed Resultant value of the key's callable. 116 | */ 117 | protected function getConfigCallable($key, array $args = []) 118 | { 119 | $value = $this->config->getKey($key); 120 | 121 | if (is_callable($value)) { 122 | $value = $value(...$args); 123 | } 124 | 125 | return $value; 126 | } 127 | 128 | /** 129 | * Get a (multi-dimensional) array of all the configuration settings. 130 | * 131 | * @since 0.1.4 132 | * 133 | * @return array All the configuration settings. 134 | */ 135 | protected function getConfigArray() 136 | { 137 | return $this->config->getAll(); 138 | } 139 | 140 | /** 141 | * Get an array of all the keys that are known by the Config. 142 | * 143 | * @since 0.1.2 144 | * 145 | * @return array Array of strings containing all the keys. 146 | */ 147 | protected function getConfigKeys() 148 | { 149 | return $this->config->getKeys(); 150 | } 151 | 152 | /** 153 | * Get a default configuration in case none was injected into the constructor. 154 | * 155 | * The name and path of the configuration needs to be set as a const called DEFAULT_CONFIG within the class 156 | * containing the trait. The path needs to be relative to the location of the containing class file. 157 | * 158 | * @since 0.4.2 159 | * 160 | * @return ConfigInterface Configuration settings to use. 161 | */ 162 | protected function fetchDefaultConfig() 163 | { 164 | $configFile = method_exists($this, 'getDefaultConfigFile') 165 | ? $this->getDefaultConfigFile() 166 | : __DIR__ . '/../config/defaults.php'; 167 | 168 | return $this->fetchConfig($configFile); 169 | } 170 | 171 | /** 172 | * Get a configuration from a specified $file. 173 | * 174 | * If file is not accessible or readable, returns an empty Config. 175 | * 176 | * @since 0.4.2 177 | * 178 | * @return ConfigInterface Configuration settings to use. 179 | */ 180 | protected function fetchConfig($configFile) 181 | { 182 | if (is_string($configFile) && ! is_readable($configFile)) { 183 | $configFile = []; 184 | } 185 | 186 | return ConfigFactory::create($configFile); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/AbstractConfig.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config; 13 | 14 | use ArrayObject; 15 | use BrightNucleus\Config\Exception\KeyNotFoundException; 16 | use Exception; 17 | 18 | /** 19 | * Handles basic manipulation of configuration values. 20 | * 21 | * @since 0.1.0 22 | * 23 | * @package BrightNucleus\Config 24 | * @author Alain Schlesser 25 | */ 26 | abstract class AbstractConfig extends ArrayObject implements ConfigInterface 27 | { 28 | 29 | /** 30 | * Array of strings that are used as delimiters to parse configuration keys. 31 | * 32 | * @since 0.1.6 33 | * 34 | * @var array 35 | */ 36 | protected $delimiter = ['\\', '/', '.']; 37 | 38 | /** 39 | * Instantiate the AbstractConfig object. 40 | * 41 | * @since 0.1.0 42 | * @since 0.1.6 Accepts a delimiter to parse configuration keys. 43 | * 44 | * @param array $config Array with settings. 45 | * @param string[]|string|null $delimiter A string or array of strings that are used as delimiters to parse 46 | * configuration keys. Defaults to "\", "/" & ".". 47 | */ 48 | public function __construct(array $config, $delimiter = null) 49 | { 50 | // Make sure the config entries can be accessed as properties. 51 | parent::__construct($config, ArrayObject::ARRAY_AS_PROPS); 52 | 53 | if (null !== $delimiter) { 54 | $this->delimiter = (array)$delimiter; 55 | } 56 | } 57 | 58 | /** 59 | * Get the value of a specific key. 60 | * 61 | * To get a value several levels deep, add the keys for each level as a comma-separated list. 62 | * 63 | * @since 0.1.0 64 | * @since 0.1.4 Accepts list of keys. 65 | * 66 | * @param string|array $_ List of keys. 67 | * 68 | * @return mixed 69 | * @throws KeyNotFoundException If an unknown key is requested. 70 | */ 71 | public function getKey($_) 72 | { 73 | $keys = $this->validateKeys(func_get_args()); 74 | 75 | $keys = array_reverse($keys); 76 | $array = $this->getArrayCopy(); 77 | while (count($keys) > 0) { 78 | $key = array_pop($keys); 79 | $array = $array[$key]; 80 | } 81 | 82 | return $array; 83 | } 84 | 85 | /** 86 | * Check whether the Config has a specific key. 87 | * 88 | * To check a value several levels deep, add the keys for each level as a comma-separated list. 89 | * 90 | * @since 0.1.0 91 | * @since 0.1.4 Accepts list of keys. 92 | * 93 | * @param string|array $_ List of keys. 94 | * 95 | * @return bool 96 | */ 97 | public function hasKey($_) 98 | { 99 | try { 100 | $keys = array_reverse($this->getKeyArguments(func_get_args())); 101 | 102 | $array = $this->getArrayCopy(); 103 | while (count($keys) > 0) { 104 | $key = array_pop($keys); 105 | if (! array_key_exists($key, $array)) { 106 | return false; 107 | } 108 | $array = $array[$key]; 109 | } 110 | } catch (Exception $exception) { 111 | return false; 112 | } 113 | 114 | return true; 115 | } 116 | 117 | /** 118 | * Get a (multi-dimensional) array of all the configuration settings. 119 | * 120 | * @since 0.1.4 121 | * 122 | * @return array 123 | */ 124 | public function getAll() 125 | { 126 | return $this->getArrayCopy(); 127 | } 128 | 129 | /** 130 | * Get the an array with all the keys 131 | * 132 | * @since 0.1.0 133 | * @return array 134 | */ 135 | public function getKeys() 136 | { 137 | return array_keys((array)$this); 138 | } 139 | 140 | /** 141 | * Get a new config at a specific sub-level. 142 | * 143 | * @since 0.1.13 144 | * 145 | * @param string|array $_ List of keys. 146 | * 147 | * @return ConfigInterface 148 | * @throws KeyNotFoundException If an unknown key is requested. 149 | */ 150 | public function getSubConfig($_) 151 | { 152 | $keys = $this->validateKeys(func_get_args()); 153 | 154 | $subConfig = clone $this; 155 | $subConfig->reduceToSubKey($keys); 156 | 157 | return $subConfig; 158 | } 159 | 160 | /** 161 | * Validate a set of keys to make sure they exist. 162 | * 163 | * @since 0.1.13 164 | * 165 | * @param string|array $_ List of keys. 166 | * 167 | * @return array List of keys. 168 | * @throws KeyNotFoundException If an unknown key is requested. 169 | */ 170 | public function validateKeys($_) 171 | { 172 | $keys = $this->getKeyArguments(func_get_args()); 173 | 174 | if (! $this->hasKey($keys)) { 175 | throw new KeyNotFoundException( 176 | sprintf( 177 | _('The configuration key %1$s does not exist.'), 178 | implode('->', $keys) 179 | ) 180 | ); 181 | } 182 | 183 | return $keys; 184 | } 185 | 186 | /** 187 | * Reduce the currently stored config array to a subarray at a specific level. 188 | * 189 | * @since 0.1.13 190 | * 191 | * @param array $keys Array of keys that point to a key down in the hierarchy. 192 | */ 193 | protected function reduceToSubKey(array $keys) 194 | { 195 | $this->exchangeArray($this->getKey($keys)); 196 | } 197 | 198 | /** 199 | * Recursively extract the configuration key arguments from an arbitrary array. 200 | * 201 | * @since 0.1.6 202 | * 203 | * @param array $arguments Array as fetched through get_func_args(). 204 | * 205 | * @return array Array of strings. 206 | */ 207 | protected function getKeyArguments($arguments) 208 | { 209 | $keys = []; 210 | foreach ($arguments as $argument) { 211 | if (is_array($argument)) { 212 | $keys = array_merge($keys, $this->getKeyArguments($argument)); 213 | } 214 | if (is_string($argument)) { 215 | $keys = array_merge($keys, $this->parseKeysString($argument)); 216 | } 217 | } 218 | 219 | return $keys; 220 | } 221 | 222 | /** 223 | * Extract individual keys from a delimited string. 224 | * 225 | * @since 0.1.6 226 | * 227 | * @param string $keyString Delimited string of keys. 228 | * 229 | * @return array Array of key strings. 230 | */ 231 | protected function parseKeysString($keyString) 232 | { 233 | // Replace all of the configured delimiters by the first one, so that we can then use explode(). 234 | $normalizedString = str_replace($this->delimiter, $this->delimiter[0], $keyString); 235 | 236 | return (array)explode($this->delimiter[0], $normalizedString); 237 | } 238 | 239 | /** 240 | * Validate the Config file. 241 | * 242 | * @since 0.1.0 243 | * @return boolean 244 | */ 245 | abstract public function isValid(); 246 | } 247 | -------------------------------------------------------------------------------- /src/Config.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config; 13 | 14 | use BrightNucleus\Config\ConfigSchemaInterface as Schema; 15 | use BrightNucleus\Config\ConfigValidatorInterface as Validator; 16 | use BrightNucleus\Config\Exception\FailedToInstantiateParentException; 17 | use BrightNucleus\Config\Exception\FailedToLoadConfigException; 18 | use BrightNucleus\Config\Exception\FailedToResolveConfigException; 19 | use BrightNucleus\Config\Exception\InvalidConfigException; 20 | use BrightNucleus\Config\Exception\InvalidConfigurationSourceException; 21 | use Exception; 22 | use Symfony\Component\OptionsResolver\OptionsResolver; 23 | 24 | /** 25 | * Generic implementation of a Config object. 26 | * 27 | * @since 0.1.0 28 | * 29 | * @package BrightNucleus\Config 30 | * @author Alain Schlesser 31 | */ 32 | class Config extends AbstractConfig 33 | { 34 | 35 | /** 36 | * The schema of the Config file. 37 | * 38 | * @var Schema 39 | */ 40 | protected $schema; 41 | 42 | /** 43 | * The Validator class that gets asked to do the validation of the config. 44 | * 45 | * @since 0.1.0 46 | * 47 | * @var Validator 48 | */ 49 | protected $validator; 50 | 51 | /** 52 | * Instantiate the Config object. 53 | * 54 | * It accepts either an array with the configuration settings, or a 55 | * filename pointing to a PHP file it can include. 56 | * 57 | * @since 0.1.0 58 | * @since 0.1.6 Accepts a delimiter to parse configuration keys. 59 | * 60 | * @param array|string $config Array with settings or filename for the 61 | * settings file. 62 | * @param Schema|null $schema Optional. Config that contains default 63 | * values that can get overwritten. 64 | * @param Validator|null $validator Optional. Validator class that does the 65 | * actual validation. 66 | * @param string[]|string|null $delimiter A string or array of strings that are used as delimiters to parse 67 | * configuration keys. Defaults to "\", "/" & ".". 68 | * 69 | * @throws InvalidConfigurationSourceException If the config source is not a string or array. 70 | * @throws FailedToInstantiateParentException If the parent class could not be instantiated. 71 | * @throws FailedToLoadConfigException If loading of the config source failed. 72 | * @throws FailedToResolveConfigException If the config file could not be resolved. 73 | * @throws InvalidConfigException If the config file is not valid. 74 | */ 75 | public function __construct( 76 | $config, 77 | ?Schema $schema = null, 78 | ?Validator $validator = null, 79 | $delimiter = null 80 | ) { 81 | $this->schema = $schema; 82 | $this->validator = $validator; 83 | 84 | // Make sure $config is either a string or array. 85 | if (! (is_string($config) || is_array($config))) { 86 | throw new InvalidConfigurationSourceException( 87 | sprintf( 88 | _('Invalid configuration source: %1$s'), 89 | print_r($config, true) 90 | ) 91 | ); 92 | } 93 | 94 | if (is_string($config)) { 95 | $config = Loader::load($config); 96 | } 97 | 98 | // Run the $config through the OptionsResolver. 99 | $config = $this->resolveOptions($config); 100 | 101 | // Instantiate the parent class. 102 | try { 103 | parent::__construct($config, $delimiter); 104 | } catch (Exception $exception) { 105 | throw new FailedToInstantiateParentException( 106 | sprintf( 107 | _('Could not instantiate the configuration through its parent. Reason: %1$s'), 108 | $exception->getMessage() 109 | ) 110 | ); 111 | } 112 | 113 | // Finally, validate the resulting config. 114 | if (! $this->isValid()) { 115 | throw new InvalidConfigException( 116 | sprintf( 117 | _('ConfigInterface file is not valid: %1$s'), 118 | print_r($config, true) 119 | ) 120 | ); 121 | } 122 | } 123 | 124 | /** 125 | * Validate the Config file. 126 | * 127 | * @since 0.1.0 128 | * 129 | * @return boolean 130 | */ 131 | public function isValid() 132 | { 133 | if ($this->validator) { 134 | return $this->validator->isValid($this); 135 | } 136 | 137 | return true; 138 | } 139 | 140 | /** 141 | * Process the passed-in defaults and merge them with the new values, while 142 | * checking that all required options are set. 143 | * 144 | * @since 0.1.0 145 | * 146 | * @param array $config Configuration settings to resolve. 147 | * 148 | * @return array Resolved configuration settings. 149 | * @throws FailedToResolveConfigException If there are errors while resolving the options. 150 | */ 151 | protected function resolveOptions($config) 152 | { 153 | if (! $this->schema) { 154 | return $config; 155 | } 156 | 157 | try { 158 | $resolver = new OptionsResolver(); 159 | if ($this->configureOptions($resolver)) { 160 | $config = $resolver->resolve($config); 161 | } 162 | } catch (Exception $exception) { 163 | throw new FailedToResolveConfigException( 164 | sprintf( 165 | _('Error while resolving config options: %1$s'), 166 | $exception->getMessage() 167 | ) 168 | ); 169 | } 170 | 171 | return $config; 172 | } 173 | 174 | /** 175 | * Configure the possible and required options for the Config. 176 | * 177 | * This should return a bool to let the resolve_options() know whether the 178 | * actual resolving needs to be done or not. 179 | * 180 | * @since 0.1.0 181 | * 182 | * @param OptionsResolver $resolver Reference to the OptionsResolver 183 | * instance. 184 | * 185 | * @return bool Whether to do the resolving. 186 | * @throws FailedToResolveConfigException If there are errors while processing. 187 | */ 188 | protected function configureOptions(OptionsResolver $resolver) 189 | { 190 | $defined = $this->schema->getDefinedOptions(); 191 | $defaults = $this->schema->getDefaultOptions(); 192 | $required = $this->schema->getRequiredOptions(); 193 | 194 | if (! $defined && ! $defaults && ! $required) { 195 | return false; 196 | } 197 | 198 | try { 199 | if ($defined) { 200 | $resolver->setDefined($defined); 201 | } 202 | if ($defaults) { 203 | $resolver->setDefaults($defaults); 204 | } 205 | if ($required) { 206 | $resolver->setRequired($required); 207 | } 208 | } catch (Exception $exception) { 209 | throw new FailedToResolveConfigException( 210 | sprintf( 211 | _('Error while processing config options: %1$s'), 212 | $exception->getMessage() 213 | ) 214 | ); 215 | } 216 | 217 | return true; 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /src/ConfigFactory.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016-2017 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config; 13 | 14 | use Exception; 15 | 16 | /** 17 | * Create new object instances that implement ConfigInterface. 18 | * 19 | * @since 0.3.0 20 | * 21 | * @package BrightNucleus\Config 22 | * @author Alain Schlesser 23 | */ 24 | class ConfigFactory 25 | { 26 | 27 | /** 28 | * Cached contents of the config files. 29 | * 30 | * @since 0.4.3 31 | * 32 | * @var array 33 | */ 34 | protected static $configFilesCache = []; 35 | 36 | /** 37 | * Create a new ConfigInterface object from a file. 38 | * 39 | * If a comma-separated list of files is provided, they are checked in sequence until the first one could be loaded 40 | * successfully. 41 | * 42 | * @since 0.3.0 43 | * 44 | * @param string|array $_ List of files. 45 | * 46 | * @return ConfigInterface Instance of a ConfigInterface implementation. 47 | */ 48 | public static function createFromFile($_) 49 | { 50 | $files = array_reverse(func_get_args()); 51 | 52 | if (is_array($files[0])) { 53 | $files = $files[0]; 54 | } 55 | 56 | while (count($files) > 0) { 57 | try { 58 | $file = array_pop($files); 59 | 60 | if (! is_string($file)) { 61 | continue; 62 | } 63 | 64 | if (! is_readable($file)) { 65 | continue; 66 | } 67 | 68 | $config = static::createFromArray( 69 | static::getFromCache($file, function ($file) { 70 | return Loader::load($file); 71 | }) 72 | ); 73 | 74 | if (null === $config) { 75 | continue; 76 | } 77 | 78 | return $config; 79 | } catch (Exception $exception) { 80 | // Fail silently and try next file. 81 | } 82 | } 83 | 84 | return static::createFromArray([]); 85 | } 86 | 87 | /** 88 | * Create a new ConfigInterface object from an array. 89 | * 90 | * @since 0.3.0 91 | * 92 | * @param array $array Array with configuration values. 93 | * 94 | * @return ConfigInterface Instance of a ConfigInterface implementation. 95 | */ 96 | public static function createFromArray(array $array) 97 | { 98 | try { 99 | return new Config($array); 100 | } catch (Exception $exception) { 101 | // Fail silently and try next file. 102 | } 103 | 104 | return null; 105 | } 106 | 107 | /** 108 | * Create a new ConfigInterface object. 109 | * 110 | * Tries to deduce the correct creation method by inspecting the provided arguments. 111 | * 112 | * @since 0.3.0 113 | * 114 | * @param mixed $_ Array with configuration values. 115 | * 116 | * @return ConfigInterface Instance of a ConfigInterface implementation. 117 | */ 118 | public static function create($_) 119 | { 120 | if (func_num_args() < 1) { 121 | return static::createFromArray([]); 122 | } 123 | 124 | $arguments = func_get_args(); 125 | 126 | if (is_array($arguments[0]) && func_num_args() === 1) { 127 | return static::createFromArray($arguments[0]); 128 | } 129 | 130 | return static::createFromFile($arguments); 131 | } 132 | 133 | /** 134 | * Create a new ConfigInterface object, by merging several files together. 135 | * 136 | * Duplicate keys in later files will override those in earlier files. 137 | * 138 | * @since 0.4.6 139 | * 140 | * @param mixed $_ Array with configuration values. 141 | * 142 | * @return ConfigInterface Instance of a ConfigInterface implementation. 143 | */ 144 | public static function merge($_) 145 | { 146 | if (func_num_args() < 1) { 147 | return static::createFromArray([]); 148 | } 149 | 150 | $arguments = func_get_args(); 151 | 152 | if (is_array($arguments[0]) && func_num_args() === 1) { 153 | return static::createFromArray($arguments[0]); 154 | } 155 | 156 | return static::mergeFromFiles($arguments); 157 | } 158 | 159 | /** 160 | * Create a new ConfigInterface object by merging data from several files. 161 | * 162 | * If a comma-separated list of files is provided, they are loaded in sequence and later files override settings in 163 | * earlier files. 164 | * 165 | * @since 0.4.6 166 | * 167 | * @param string|array $_ List of files. 168 | * 169 | * @return ConfigInterface Instance of a ConfigInterface implementation. 170 | */ 171 | public static function mergeFromFiles($_) 172 | { 173 | $files = array_reverse(func_get_args()); 174 | $data = []; 175 | 176 | if (is_array($files[0])) { 177 | $files = array_reverse($files[0]); 178 | } 179 | 180 | while (count($files) > 0) { 181 | try { 182 | $file = array_pop($files); 183 | 184 | if (! is_readable($file)) { 185 | continue; 186 | } 187 | 188 | $new_data = static::getFromCache($file, function ($file) { 189 | return Loader::load($file); 190 | }); 191 | 192 | if (null === $data) { 193 | continue; 194 | } 195 | 196 | $data = array_replace_recursive($data, $new_data); 197 | } catch (Exception $exception) { 198 | // Fail silently and try next file. 199 | } 200 | } 201 | 202 | return static::createFromArray($data); 203 | } 204 | 205 | /** 206 | * Create a new ConfigInterface object from a file and return a sub-portion of it. 207 | * 208 | * The first argument needs to be the file name to load, and the subsequent arguments will be passed on to 209 | * `Config::getSubConfig()`. 210 | * 211 | * @since 0.4.5 212 | * 213 | * @param mixed $_ File name of the config to load as a string, followed by an array of keys to pass to 214 | * `Config::getSubConfig()`. 215 | * 216 | * @return ConfigInterface Instance of a ConfigInterface implementation. 217 | */ 218 | public static function createSubConfig($_) 219 | { 220 | if (func_num_args() < 2) { 221 | return static::createFromArray([]); 222 | } 223 | 224 | $arguments = func_get_args(); 225 | $file = array_shift($arguments); 226 | 227 | $config = static::createFromFile($file); 228 | 229 | return $config->getSubConfig($arguments); 230 | } 231 | 232 | /** 233 | * Get a config file from the config file cache. 234 | * 235 | * @since 0.4.4 236 | * 237 | * @param string $identifier Identifier to look for in the cache. 238 | * @param mixed $fallback Fallback to use to fill the cache. If $fallback is a callable, it will be executed 239 | * with $identifier as an argument. 240 | * 241 | * @return mixed The latest content of the cache for the given identifier. 242 | */ 243 | protected static function getFromCache($identifier, $fallback) 244 | { 245 | if (! array_key_exists($identifier, static::$configFilesCache)) { 246 | static::$configFilesCache[$identifier] = is_callable($fallback) 247 | ? $fallback($identifier) 248 | : $fallback; 249 | } 250 | 251 | return static::$configFilesCache[$identifier]; 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | This project adheres to [Semantic Versioning](http://semver.org/). 4 | 5 | ## [0.4.12] - 2019-10-16 6 | ### Fixed 7 | - Correct invalid Composer configuration. 8 | - Add Composer lock file to .gitignore. 9 | 10 | ## [0.4.11] - 2019-10-16 11 | ### Added 12 | - Added JSON loader. Props [@pascalknecht](https://github.com/pascalknecht) 13 | - Fixed docblocks for PHP & JSON loader `canLoad()` methods. 14 | - Adapted docblock for `LoaderInterface::load()` method. 15 | 16 | ## [0.4.10] - 2017-02-16 17 | ### Fixed 18 | - Fix Coveralls integration. 19 | 20 | ## [0.4.9] - 2017-02-16 21 | ### Added 22 | - Added several checks that can be run through Composer scripts. 23 | - Added Travis CI configuration. 24 | 25 | ## [0.4.8] - 2016-08-06 26 | ### Added 27 | - Added `ConfigTrait::getConfigCallable()` to get a callable and immmediately execute it to get the resultant value. 28 | - Added corresponding unit test. 29 | 30 | ## [0.4.7] - 2016-07-22 31 | ### Fixed 32 | - Fixed the way arrays were merged recursively. 33 | - Added regression test. 34 | 35 | ## [0.4.6] - 2016-07-22 36 | ### Added 37 | - Added `ConfigFactory::merge()` to merge several Config files into one. 38 | - Added corresponding unit test. 39 | 40 | ## [0.4.5] - 2016-07-05 41 | ### Added 42 | - Added `ConfigFactory::createSubConfig()` to quickly create a new config at a sublevel. 43 | - Added corresponding unit test. 44 | 45 | ## [0.4.4] - 2016-07-05 46 | ### Changed 47 | - Extracted the cache retrieval in `ConfigFactory` into its own method. 48 | 49 | ## [0.4.3] - 2016-07-05 50 | ### Added 51 | - Added file caching to CreateFactory. 52 | - Added unit tests to make sure the caching works. 53 | 54 | ## [0.4.2] - 2016-06-09 55 | ### Added 56 | - Added trait functionality to load a default config file. 57 | - Added corresponding entry in the README.md file. 58 | 59 | ## [0.4.1] - 2016-06-08 60 | ### Removed 61 | - Removed `beberlei/assert` and all assert checks for now, until a better replacement has been found. 62 | 63 | ## [0.4.0] - 2016-04-28 64 | ### Added 65 | - Added extensible loader framework that includes `PHPLoader` and can be extended by other packages. 66 | 67 | ### Changed 68 | - Changed the extensions that are being thrown to more specific ones. They types are extensions of the ones that were previously used, but some exception messages have changed. 69 | - Generic `Config` class now uses the loader framework instead of reading the data on its own. 70 | 71 | ## [0.3.1] - 2016-04-05 72 | ### Changed 73 | - Update Composer dependencies. 74 | 75 | ## [0.3.0] - 2016-04-04 76 | ### Added 77 | - Added `ConfigFactory`. 78 | 79 | ### Changed 80 | - Changed licensing from GPL2 to MIT. 81 | 82 | ## [0.2.8] - 2016-03-28 83 | ### Changed 84 | - Bumped [PHP Composter PHPCS PSR-2](https://github.com/php-composter/php-composter-phpcs-psr2) version. 85 | 86 | ## [0.2.7] - 2016-03-28 87 | ### Changed 88 | - Switched from a custom `pre-commit` script to the [PHP Composter PHPCS PSR-2](https://github.com/php-composter/php-composter-phpcs-psr2) package. 89 | 90 | ## [0.2.6] - 2016-03-22 91 | ### Fixed 92 | - Switch `beberlei/assert` back to official branch. Issue [#138](https://github.com/beberlei/assert/issues/138) has been fixed with v2.5. 93 | 94 | ## [0.2.5] - 2016-03-04 95 | ### Fixed 96 | - Switch `beberlei/assert` to own fork until [#138](https://github.com/beberlei/assert/issues/138) has been fixed. 97 | 98 | ## [0.2.4] - 2016-02-26 99 | ### Added 100 | - Better documentation in `README.md`. 101 | 102 | ### Fixed 103 | - Corrected type hints for variadic methods. 104 | 105 | ## [0.2.3] - 2016-02-19 106 | ### Fixed 107 | - Remove test cases that were not needed anymore due to the addition of the `$_` argument in v0.2.2. 108 | - Remove const array for truthy values and replace by local variable, to avoid PHP5.6+ requirement. 109 | 110 | ## [0.2.2] - 2016-02-18 111 | ### Fixed 112 | - Add dummy variables in methods with a variable number of arguments to fix PHPStorm inspections. 113 | - Fix case in `reduceToSubKey()` method. 114 | 115 | ## [0.2.1] - 2016-02-18 116 | ### Fixed 117 | - Bumped version requirement for `brightnucleus/exceptions` to v0.2+. 118 | 119 | ## [0.2.0] - 2016-02-17 120 | ### Added 121 | - Added `getSubConfig()` to extract the subtree at a specific level & key. 122 | - Added corresponding tests. 123 | 124 | ## [0.1.12] - 2016-02-17 125 | ### Fixed 126 | - Updated `brightnucleus/exceptions` dependency. 127 | 128 | ## [0.1.11] - 2016-02-17 129 | ### Fixed 130 | - Lowered version requirement for `symfony/options-resolver`. 131 | 132 | ## [0.1.10] - 2016-02-16 133 | ### Added 134 | - Added `brightnucleus/exceptions` to have coherentexceptions across all Bright Nucleus packages. 135 | 136 | ## [0.1.9] - 2016-02-16 137 | ### Added 138 | - The `beberlei/assert` package has been added as a development dependency. 139 | - Several assertions have been added to check the arguments passed in to the methods. 140 | - A `pre-commit` hook has been added to run unit tests and validate code standards. 141 | 142 | ### Fixed 143 | - Methods have been rearranged. 144 | - A few code style tweaks have been made to adhere to PHPCS PSR-2. 145 | 146 | ## [0.1.8] - 2016-02-11 147 | ### Added 148 | - The `ConfigTrait::processConfig()` method now accepts one or more additional parameters (can be delimited strings) to start with a Config at a specific level. Useful to include different `vendor\package` levels in a single merged Config file. 149 | 150 | ## [0.1.7] - 2016-02-11 151 | ### Fixed 152 | - The `hasKey()` method doesn't throw an exception, returns `false` instead. 153 | 154 | ## [0.1.6] - 2016-02-11 155 | ### Added 156 | - The `has*` & `get*` methods now support keys with delimiters. The delimiters are set via the Config constructor. 157 | - Tests for the above. 158 | 159 | ## [0.1.5] - 2016-02-11 160 | ### Added 161 | - `ConfigTrait::hasConfigKey()` and `ConfigTrait::getConfigKey()` now support a list of strings to fetch a value from several levels deep. 162 | - Tests for the above. 163 | 164 | ## [0.1.4] - 2016-02-11 165 | ### Added 166 | - `AbstractConfig:getAll()` and `ConfigTrait::getConfigArray()`. 167 | - `hasKey()` and `getKey()` now support a list of strings to fetch a value from several levels deep. 168 | - Tests for the above. 169 | 170 | ### Fixed 171 | - PHP requirement for unit tests was lowered from 5.6 to 5.4. 172 | 173 | ## [0.1.3] - 2016-02-10 174 | ### Added 175 | - Badges in README.md 176 | 177 | ### Fixed 178 | - Update Composer to use PHPUnit 4 to reduce PHP version requirements. 179 | - Fix several minor code quality issues. 180 | 181 | ## [0.1.2] - 2016-02-01 182 | ### Added 183 | - ConfigTrait. 184 | - Tests for ConfigTrait. 185 | 186 | ## [0.1.1] - 2016-01-29 187 | ### Added 188 | - Tests for schema requirements. 189 | 190 | ### Fixed 191 | - Don't instantiate `OptionsResolver` if not needed. 192 | - Formatting tweaks 193 | 194 | ## [0.1.0] - 2016-01-29 195 | ### Added 196 | - Initial release to GitHub. 197 | 198 | [0.4.12]: https://github.com/brightnucleus/config/compare/v0.4.11...v0.4.12 199 | [0.4.11]: https://github.com/brightnucleus/config/compare/v0.4.10...v0.4.11 200 | [0.4.10]: https://github.com/brightnucleus/config/compare/v0.4.9...v0.4.10 201 | [0.4.9]: https://github.com/brightnucleus/config/compare/v0.4.8...v0.4.9 202 | [0.4.8]: https://github.com/brightnucleus/config/compare/v0.4.7...v0.4.8 203 | [0.4.7]: https://github.com/brightnucleus/config/compare/v0.4.6...v0.4.7 204 | [0.4.6]: https://github.com/brightnucleus/config/compare/v0.4.5...v0.4.6 205 | [0.4.5]: https://github.com/brightnucleus/config/compare/v0.4.4...v0.4.5 206 | [0.4.4]: https://github.com/brightnucleus/config/compare/v0.4.3...v0.4.4 207 | [0.4.3]: https://github.com/brightnucleus/config/compare/v0.4.2...v0.4.3 208 | [0.4.2]: https://github.com/brightnucleus/config/compare/v0.4.1...v0.4.2 209 | [0.4.1]: https://github.com/brightnucleus/config/compare/v0.4.0...v0.4.1 210 | [0.4.0]: https://github.com/brightnucleus/config/compare/v0.3.1...v0.4.0 211 | [0.3.1]: https://github.com/brightnucleus/config/compare/v0.3.0...v0.3.1 212 | [0.3.0]: https://github.com/brightnucleus/config/compare/v0.2.8...v0.3.0 213 | [0.2.8]: https://github.com/brightnucleus/config/compare/v0.2.7...v0.2.8 214 | [0.2.7]: https://github.com/brightnucleus/config/compare/v0.2.6...v0.2.7 215 | [0.2.6]: https://github.com/brightnucleus/config/compare/v0.2.5...v0.2.6 216 | [0.2.5]: https://github.com/brightnucleus/config/compare/v0.2.4...v0.2.5 217 | [0.2.4]: https://github.com/brightnucleus/config/compare/v0.2.3...v0.2.4 218 | [0.2.3]: https://github.com/brightnucleus/config/compare/v0.2.2...v0.2.3 219 | [0.2.2]: https://github.com/brightnucleus/config/compare/v0.2.1...v0.2.2 220 | [0.2.1]: https://github.com/brightnucleus/config/compare/v0.2.0...v0.2.1 221 | [0.2.0]: https://github.com/brightnucleus/config/compare/v0.1.12...v0.2.0 222 | [0.1.12]: https://github.com/brightnucleus/config/compare/v0.1.11...v0.1.12 223 | [0.1.11]: https://github.com/brightnucleus/config/compare/v0.1.10...v0.1.11 224 | [0.1.10]: https://github.com/brightnucleus/config/compare/v0.1.9...v0.1.10 225 | [0.1.9]: https://github.com/brightnucleus/config/compare/v0.1.8...v0.1.9 226 | [0.1.8]: https://github.com/brightnucleus/config/compare/v0.1.7...v0.1.8 227 | [0.1.7]: https://github.com/brightnucleus/config/compare/v0.1.6...v0.1.7 228 | [0.1.6]: https://github.com/brightnucleus/config/compare/v0.1.5...v0.1.6 229 | [0.1.5]: https://github.com/brightnucleus/config/compare/v0.1.4...v0.1.5 230 | [0.1.4]: https://github.com/brightnucleus/config/compare/v0.1.3...v0.1.4 231 | [0.1.3]: https://github.com/brightnucleus/config/compare/v0.1.2...v0.1.3 232 | [0.1.2]: https://github.com/brightnucleus/config/compare/v0.1.1...v0.1.2 233 | [0.1.1]: https://github.com/brightnucleus/config/compare/v0.1.0...v0.1.1 234 | [0.1.0]: https://github.com/brightnucleus/config/compare/v0.0.0...v0.1.0 235 | -------------------------------------------------------------------------------- /tests/ConfigFactoryTest.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config\Tests; 13 | 14 | use BrightNucleus\Config\ConfigFactory; 15 | use org\bovigo\vfs\vfsStream; 16 | 17 | /** 18 | * Class ConfigFactoryTest. 19 | * 20 | * @since 0.3.0 21 | * 22 | * @package BrightNucleus\Config 23 | * @author Alain Schlesser 24 | */ 25 | class ConfigFactoryTest extends TestCase 26 | { 27 | 28 | /* 29 | * TODO: There's still lots of work to do to render these tests useful. 30 | */ 31 | 32 | /* 33 | * Don't use an array const to avoid bumping PHP requirement to 5.6. 34 | */ 35 | protected static $test_array = [ 36 | 'random_string' => 'test_value', 37 | 'positive_integer' => 42, 38 | 'negative_integer' => -256, 39 | 'positive_boolean' => true, 40 | 'negative_boolean' => false, 41 | ]; 42 | 43 | protected static $test_multi_array = [ 44 | 'level1' => [ 45 | 'level2' => [ 46 | 'level3' => [ 47 | 'level4_key' => 'level4_value', 48 | ], 49 | ], 50 | ], 51 | ]; 52 | 53 | /** 54 | * Test creation from file. 55 | * 56 | * @covers \BrightNucleus\Config\ConfigFactory::createFromFile 57 | * 58 | * @since 0.3.0 59 | */ 60 | public function testCreateFromFile() 61 | { 62 | $config = ConfigFactory::createFromFile(__DIR__ . '/fixtures/config_file.php'); 63 | 64 | $this->assertInstanceOf('\BrightNucleus\Config\ConfigInterface', $config); 65 | $this->assertInstanceOf('\BrightNucleus\Config\AbstractConfig', $config); 66 | $this->assertInstanceOf('\BrightNucleus\Config\Config', $config); 67 | $this->assertTrue($config->hasKey('random_string')); 68 | 69 | $config2 = ConfigFactory::createFromFile( 70 | 'nonsense_file.php', 71 | 'still_nonsense.txt', 72 | __DIR__ . '/fixtures/config_file.php' 73 | ); 74 | 75 | $this->assertInstanceOf('\BrightNucleus\Config\ConfigInterface', $config2); 76 | $this->assertInstanceOf('\BrightNucleus\Config\AbstractConfig', $config2); 77 | $this->assertInstanceOf('\BrightNucleus\Config\Config', $config2); 78 | $this->assertTrue($config2->hasKey('random_string')); 79 | } 80 | 81 | /** 82 | * Test creation from array. 83 | * 84 | * @covers \BrightNucleus\Config\ConfigFactory::createFromArray 85 | * 86 | * @since 0.3.0 87 | */ 88 | public function testCreateFromArray() 89 | { 90 | $config = ConfigFactory::createFromArray(['some_key' => 'some_value']); 91 | 92 | $this->assertInstanceOf('\BrightNucleus\Config\ConfigInterface', $config); 93 | $this->assertInstanceOf('\BrightNucleus\Config\AbstractConfig', $config); 94 | $this->assertInstanceOf('\BrightNucleus\Config\Config', $config); 95 | $this->assertTrue($config->hasKey('some_key')); 96 | $this->assertEquals('some_value', $config->getKey('some_key')); 97 | } 98 | 99 | /** 100 | * Test creation from best guess using one file. 101 | * 102 | * @covers \BrightNucleus\Config\ConfigFactory::create 103 | * 104 | * @since 0.3.0 105 | */ 106 | public function testCreateFromBestGuessUsingOneFile() 107 | { 108 | $config = ConfigFactory::create(__DIR__ . '/fixtures/config_file.php'); 109 | 110 | $this->assertInstanceOf('\BrightNucleus\Config\ConfigInterface', $config); 111 | $this->assertInstanceOf('\BrightNucleus\Config\AbstractConfig', $config); 112 | $this->assertInstanceOf('\BrightNucleus\Config\Config', $config); 113 | $this->assertTrue($config->hasKey('random_string')); 114 | } 115 | 116 | /** 117 | * Test creation from best guess using several files. 118 | * 119 | * @covers \BrightNucleus\Config\ConfigFactory::create 120 | * 121 | * @since 0.3.0 122 | */ 123 | public function testCreateFromBestGuessUsingSeveralFiles() 124 | { 125 | $config = ConfigFactory::create( 126 | 'nonsense_file.php', 127 | 'still_nonsense.txt', 128 | __DIR__ . '/fixtures/config_file.php' 129 | ); 130 | 131 | $this->assertInstanceOf('\BrightNucleus\Config\ConfigInterface', $config); 132 | $this->assertInstanceOf('\BrightNucleus\Config\AbstractConfig', $config); 133 | $this->assertInstanceOf('\BrightNucleus\Config\Config', $config); 134 | $this->assertTrue($config->hasKey('random_string')); 135 | } 136 | 137 | /** 138 | * Test creation from best guess using an array. 139 | * 140 | * @covers \BrightNucleus\Config\ConfigFactory::create 141 | * 142 | * @since 0.3.0 143 | */ 144 | public function testCreateFromBestGuessUsingAnArray() 145 | { 146 | $config = ConfigFactory::create(['some_key' => 'some_value']); 147 | 148 | $this->assertInstanceOf('\BrightNucleus\Config\ConfigInterface', $config); 149 | $this->assertInstanceOf('\BrightNucleus\Config\AbstractConfig', $config); 150 | $this->assertInstanceOf('\BrightNucleus\Config\Config', $config); 151 | $this->assertTrue($config->hasKey('some_key')); 152 | $this->assertEquals('some_value', $config->getKey('some_key')); 153 | } 154 | 155 | /** 156 | * Test whether the caching system works when loading the same config file several times. 157 | * 158 | * @covers \BrightNucleus\Config\ConfigFactory::create 159 | * 160 | * @since 0.4.3 161 | */ 162 | public function testWhetherCachingWorks() 163 | { 164 | $content = ' "test_value" ];'; 165 | $empty_content = 'withContent($content); 169 | $filesystem->addChild($configFile); 170 | $this->assertTrue($filesystem->hasChild('test_config.php')); 171 | $this->assertEquals($content, $configFile->getContent()); 172 | 173 | $configA = ConfigFactory::create($configFile->url()); 174 | $this->assertTrue($configA->hasKey('test_key')); 175 | 176 | $configFile->setContent($empty_content); 177 | $this->assertEquals($empty_content, $configFile->getContent()); 178 | 179 | $configB = ConfigFactory::create($configFile->url()); 180 | $this->assertTrue($configB->hasKey('test_key')); 181 | } 182 | 183 | /** 184 | * Test whether a subconfig can be immediately created through the ConfigFactory. 185 | * 186 | * @covers \BrightNucleus\Config\ConfigFactory::createSubConfig 187 | * 188 | * @since 0.4.5 189 | */ 190 | public function testCreateSubConfig() 191 | { 192 | $config = ConfigFactory::createSubConfig(__DIR__ . '/fixtures/deep_config_file.php', 'vendor', 'package'); 193 | 194 | $this->assertInstanceOf('\BrightNucleus\Config\ConfigInterface', $config); 195 | $this->assertInstanceOf('\BrightNucleus\Config\AbstractConfig', $config); 196 | $this->assertInstanceOf('\BrightNucleus\Config\Config', $config); 197 | $this->assertTrue($config->hasKey('section_1', 'test_key_1')); 198 | } 199 | 200 | /** 201 | * Test merging using several files. 202 | * 203 | * @covers \BrightNucleus\Config\ConfigFactory::merge 204 | * 205 | * @since 0.3.0 206 | */ 207 | public function testMergeUsingSeveralFiles() 208 | { 209 | $config = ConfigFactory::merge( 210 | __DIR__ . '/fixtures/config_file.php', 211 | __DIR__ . '/fixtures/override_config_file.php' 212 | ); 213 | 214 | $this->assertInstanceOf('\BrightNucleus\Config\ConfigInterface', $config); 215 | $this->assertInstanceOf('\BrightNucleus\Config\AbstractConfig', $config); 216 | $this->assertInstanceOf('\BrightNucleus\Config\Config', $config); 217 | $this->assertTrue($config->hasKey('random_string')); 218 | $this->assertEquals('override_value', $config->getKey('random_string')); 219 | } 220 | 221 | /** 222 | * Test merging using several files. 223 | * 224 | * @covers \BrightNucleus\Config\ConfigFactory::merge 225 | * 226 | * @since 0.3.0 227 | */ 228 | public function testMergeUsingHierarchicalData() 229 | { 230 | $config = ConfigFactory::merge( 231 | __DIR__ . '/fixtures/deep_config_file.php', 232 | __DIR__ . '/fixtures/override_deep_config_file.php' 233 | )->getSubConfig('vendor\package'); 234 | 235 | $this->assertInstanceOf('\BrightNucleus\Config\ConfigInterface', $config); 236 | $this->assertInstanceOf('\BrightNucleus\Config\AbstractConfig', $config); 237 | $this->assertInstanceOf('\BrightNucleus\Config\Config', $config); 238 | $this->assertTrue($config->hasKey('section_1', 'test_key_1')); 239 | $this->assertTrue($config->hasKey('section_2', 'test_key_2')); 240 | $this->assertTrue($config->hasKey('section_1', 'test_key_3')); 241 | $this->assertTrue($config->hasKey('section_3', 'test_key_4')); 242 | $this->assertEquals('test_value_1', $config->getKey('section_1', 'test_key_1')); 243 | $this->assertEquals('override_value_2', $config->getKey('section_2', 'test_key_2')); 244 | $this->assertEquals('override_value_3', $config->getKey('section_1', 'test_key_3')); 245 | $this->assertEquals('override_value_4', $config->getKey('section_3', 'test_key_4')); 246 | } 247 | 248 | /** 249 | * Test merging using invalid files. 250 | * 251 | * @covers \BrightNucleus\Config\ConfigFactory::merge 252 | * 253 | * @since 0.3.0 254 | */ 255 | public function testMergeUsingInvalidFiles() 256 | { 257 | $config = ConfigFactory::merge( 258 | 'nonsense_file.php', 259 | 'still_nonsense.txt', 260 | __DIR__ . '/fixtures/config_file.php' 261 | ); 262 | 263 | $this->assertInstanceOf('\BrightNucleus\Config\ConfigInterface', $config); 264 | $this->assertInstanceOf('\BrightNucleus\Config\AbstractConfig', $config); 265 | $this->assertInstanceOf('\BrightNucleus\Config\Config', $config); 266 | $this->assertTrue($config->hasKey('random_string')); 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bright Nucleus Config Component 2 | 3 | [![PHP Version Require](http://poser.pugx.org/brightnucleus/config/require/php)](https://packagist.org/packages/brightnucleus/config) 4 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/brightnucleus/config/badges/quality-score.png?b=main)](https://scrutinizer-ci.com/g/brightnucleus/config/?branch=main) 5 | [![Code Coverage](https://coveralls.io/repos/github/brightnucleus/config/badge.svg?branch=main)](https://coveralls.io/github/brightnucleus/config?branch=main) 6 | [![Build Status](https://github.com/brightnucleus/config/actions/workflows/testing.yml/badge.svg?branch=main)](https://github.com/brightnucleus/config/actions/workflows/testing.yml) 7 | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/7ac6b6b664f14ebdb660d8b0242dc3f7)](https://app.codacy.com/gh/brightnucleus/config/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) 8 | [![Code Climate](https://codeclimate.com/github/brightnucleus/config/badges/gpa.svg)](https://codeclimate.com/github/brightnucleus/config) 9 | 10 | [![Latest Stable Version](https://poser.pugx.org/brightnucleus/config/v/stable)](https://packagist.org/packages/brightnucleus/config) 11 | [![Total Downloads](https://poser.pugx.org/brightnucleus/config/downloads)](https://packagist.org/packages/brightnucleus/config) 12 | [![Latest Unstable Version](https://poser.pugx.org/brightnucleus/config/v/unstable)](https://packagist.org/packages/brightnucleus/config) 13 | [![License](https://poser.pugx.org/brightnucleus/config/license)](https://packagist.org/packages/brightnucleus/config) 14 | 15 | This is a very lean Config component to help you write reusable code. It only offers basic functionality and is meant to be used in libraries and small projects. If you need a Config component for more complex projects, you should take a look at the [Symfony Config Component](http://symfony.com/doc/current/components/config/index.html). 16 | 17 | ## Table Of Contents 18 | 19 | * [Installation](#installation) 20 | * [Basic Usage](#basic-usage) 21 | * [Working With Config Data](#working-with-config-data) 22 | * [Checking The Existence Of A Key](#checking-the-existence-of-a-key) 23 | * [Getting The Value Of A Key](#getting-the-value-of-a-key) 24 | * [Nested Keys](#nested-keys) 25 | * [Example - Configuration File](#example---configuration-file) 26 | * [Example - Configurable Class](#example---configurable-class) 27 | * [Example - Getting The Config Into The Class](#example---getting-the-config-into-the-class) 28 | * [Example - Class That Loads Default Config Unless Config Was Injected](#example---class-that-loads-default-config-unless-config-was-injected) 29 | * [Example - Merging Several Configs Into One](#example---merging-several-configs-into-one) 30 | * [Config Formats](#config-formats) 31 | * [Advanced Usage](#advanced-usage) 32 | * [Configuration Schema](#configuration-schema) 33 | * [Configuration Validation](#configuration-validation) 34 | * [Custom Implementations](#custom-implementations) 35 | * [Contributing](#contributing) 36 | 37 | ## Installation 38 | 39 | The best way to use this component is through Composer: 40 | 41 | ```BASH 42 | composer require brightnucleus/config 43 | ``` 44 | 45 | ## Basic Usage 46 | 47 | A class that wants to be configurable should accept a `ConfigInterface` in its constructor, so that the Config can be injected. The surrounding code then should inject an instance of an object (for example the generic `Config` that is provided with this component). This way, the class that accepts the Config can be written in a 100% reusable way, while all project-specific stuff will be injected through the Config. 48 | 49 | ### Working With Config Data 50 | 51 | #### Checking The Existence Of A Key 52 | 53 | To check whether the configuration has a certain key, you can use the `ConfigInterface::hasKey($key)` method, or, if you are using the `ConfigTrait` in your class, you can use the convenience method `$this->hasConfigKey($key)`. 54 | 55 | #### Getting The Value Of A Key 56 | 57 | To get the configuration value of a certain key, you can use the `ConfigInterface::getKey($key)` method, or, if you are using the `ConfigTrait` in your class, you can use the convenience method `$this->getConfigKey($key)`. 58 | 59 | If you use closures in your Config file, you can also use the convenience function `$this->getConfigCallable( $key, array $args )` provided by the `ConfigTrait`, which will immediately execute the closure by passing it the provided arguments, and return the resultant value instead. 60 | 61 | #### Nested Keys 62 | 63 | If your keys are nested, you can provide multiple levels of keys in one request. So, whenever you need to provide a key and want to use a nested one, you can either provide a comma-separated list of keys ( `$this->getConfigKey( 'level1', 'level2', 'level3' );` ) or a string that contains the list of keys separated by a delimiter ( `$this->getConfigKey( 'level1/level2/level3' );` ). 64 | 65 | You can freely mix-and-match these two approaches as you like. 66 | 67 | The default delimiters are: `/`, `\` and `.`. You can choose different delimiters by passing an array of delimiters as a fourth argument to the `Config`s constructor. 68 | 69 | ### Example - Configuration File 70 | 71 | The snippet below shows the basic structure of a config file. 72 | 73 | ```PHP 74 | 'test_value', 81 | ]; 82 | 83 | return [ 84 | 'BrightNucleus' => [ 85 | 'Example' => $example, 86 | ], 87 | ]; 88 | ``` 89 | 90 | ### Example - Configurable Class 91 | 92 | Here is an example setup of how you could feed this configuration into a plugin. 93 | 94 | ```PHP 95 | processConfig( $config ); 113 | } 114 | 115 | /** 116 | * Do something. 117 | */ 118 | public function run() { 119 | $key = 'test_key'; 120 | 121 | return sprintf( 122 | _( 'The value of the config key "$1%s" is "$2%s".'), 123 | $key, 124 | $this->getConfigKey( $key ) 125 | ); 126 | } 127 | } 128 | ``` 129 | 130 | ### Example - Getting The Config Into The Class 131 | 132 | You can combine all of your configurations into 1 single file and only pass "Sub-Configurations" to the individual components using the `getSubConfig()` method. This way, you can avoid an additional file access and an additional validation pass for each component. 133 | 134 | To create a new instance of a `ConfigInterface`, you should use the `ConfigFactory`. The basic method `ConfigFactory::create()` can take either an array of values, or one or more file names (with absolute paths) as strings. 135 | 136 | If you provide a comma-separated list of file names, they are processed consecutively until the first one could be loaded successfully. 137 | 138 | There's a convenience function `ConfigFactory::createSubConfig()` to immediately fetch a sub-config from a loaded config file. This allows you to quickly bypass the vendor/package prefixes and only pass in the relevant data into the new object. 139 | 140 | Here's how you can pass the configuration file into the class: 141 | 142 | ```PHP 143 | run(); 155 | } 156 | ``` 157 | 158 | ### Example - Class That Loads Default Config Unless Config Was Injected 159 | 160 | The `ConfigTrait` provides some convenience functionality that lets you write classes that can receive an injected Config, but fall back to a default configuration file if non was injected. 161 | 162 | Here's you can code such a class: 163 | 164 | ```PHP 165 | processConfig( $config ?: $this->fetchDefaultConfig() ); 188 | } 189 | 190 | /** 191 | * Get the default configuration file name. 192 | * 193 | * This is used to override the default location. 194 | * 195 | * @return string Path & filename to the default configuration file. 196 | */ 197 | protected function getDefaultConfigFile() { 198 | return __DIR__ . '/../config/my_default_config.php'; 199 | } 200 | } 201 | ``` 202 | 203 | ### Example - Merging Several Configs Into One 204 | 205 | You can provide a comma-separated list of file names to the `ConfigFactory::merge()` method. They are loaded consecutively and merged into one coherent Config. For each duplicate Config key, the value in the later files will override the value in the earlier files. 206 | 207 | For our example, we'll define a new Config file called `override_settings.php`, that overrides a key that was already set in the default Config file. 208 | 209 | ```PHP 210 | 'override_value', 217 | ]; 218 | 219 | return [ 220 | 'BrightNucleus' => [ 221 | 'Example' => $example, 222 | ], 223 | ]; 224 | ``` 225 | 226 | ```PHP 227 | run(); 241 | } 242 | ``` 243 | 244 | ## Config Formats 245 | 246 | The Bright Nucleus Config component can be extended to load a multitude of different file formats. The base package includes a very minimal `PHPLoader` class. It can load basic PHP files that just `return` an array. 247 | 248 | Additional packages that add other formats like JSON or YAML are planned and will be released soon. 249 | 250 | Custom loaders are lazily instantiated only when needed. 251 | 252 | ## Advanced Usage 253 | 254 | ### Configuration Schema 255 | 256 | > TODO 257 | 258 | ### Configuration Validation 259 | 260 | > TODO 261 | 262 | ### Custom Implementations 263 | 264 | > TODO 265 | 266 | ## Contributing 267 | 268 | All feedback / bug reports / pull requests are welcome. 269 | 270 | ## License 271 | 272 | This code is released under the MIT license. 273 | 274 | For the full copyright and license information, please view the [`LICENSE`](LICENSE) file distributed with this source code. 275 | -------------------------------------------------------------------------------- /tests/ConfigTraitTest.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config\Tests; 13 | 14 | use BrightNucleus\Config\Config; 15 | use BrightNucleus\Config\ConfigTrait; 16 | 17 | class ConfigTraitTest extends TestCase 18 | { 19 | 20 | use ConfigTrait; 21 | 22 | /** 23 | * @covers \BrightNucleus\Config\ConfigTrait::processConfig 24 | */ 25 | public function testProcessConfig() 26 | { 27 | $this->assertNull($this->config); 28 | $this->processConfig(new Config([])); 29 | $this->assertNotNull($this->config); 30 | $this->assertInstanceOf( 31 | 'BrightNucleus\Config\ConfigInterface', 32 | $this->config 33 | ); 34 | unset($this->config); 35 | } 36 | 37 | /** 38 | * @covers \BrightNucleus\Config\ConfigTrait::hasConfigKey 39 | */ 40 | public function testHasConfigKey() 41 | { 42 | $this->processConfig( 43 | new Config( 44 | [ 45 | 'testkey1' => 'testvalue1', 46 | 'testkey2' => 'testvalue2', 47 | ] 48 | ) 49 | ); 50 | $this->assertTrue($this->hasConfigKey('testkey1')); 51 | $this->assertTrue($this->hasConfigKey('testkey2')); 52 | $this->assertFalse($this->hasConfigKey('testkey3')); 53 | } 54 | 55 | /** 56 | * @covers \BrightNucleus\Config\ConfigTrait::hasConfigKey 57 | */ 58 | public function testHasConfigKeyWithMultipleLevels() 59 | { 60 | $this->processConfig( 61 | new Config( 62 | [ 63 | 'level1' => ['level2' => ['level3' => ['level4_key' => 'level4_value'],],], 64 | ] 65 | ) 66 | ); 67 | $this->assertTrue($this->hasConfigKey('level1')); 68 | $this->assertTrue($this->hasConfigKey('level1', 'level2')); 69 | $this->assertTrue($this->hasConfigKey('level1', 'level2', 'level3')); 70 | $this->assertTrue($this->hasConfigKey('level1', 'level2', 'level3', 'level4_key')); 71 | $this->assertTrue($this->hasConfigKey('level1\level2', 'level3', 'level4_key')); 72 | $this->assertTrue($this->hasConfigKey('level1', 'level2/level3', 'level4_key')); 73 | $this->assertTrue($this->hasConfigKey('level1', 'level2', 'level3.level4_key')); 74 | $this->assertTrue($this->hasConfigKey('level1\level2\level3\level4_key')); 75 | $this->assertTrue($this->hasConfigKey('level1/level2/level3/level4_key')); 76 | $this->assertTrue($this->hasConfigKey('level1.level2.level3.level4_key')); 77 | $this->assertTrue($this->hasConfigKey('level1\level2/level3.level4_key')); 78 | $this->assertFalse($this->hasConfigKey('level1', 'level2', 'level4_key')); 79 | $this->assertFalse($this->hasConfigKey('level1', 'level3')); 80 | $this->assertFalse($this->hasConfigKey('level2')); 81 | $this->assertFalse($this->hasConfigKey('some_other_key')); 82 | } 83 | 84 | /** 85 | * @covers \BrightNucleus\Config\ConfigTrait::getConfigKey 86 | */ 87 | public function testGetConfigKey() 88 | { 89 | $this->processConfig( 90 | new Config( 91 | [ 92 | 'testkey1' => 'testvalue1', 93 | 'testkey2' => 'testvalue2', 94 | ] 95 | ) 96 | ); 97 | $this->assertEquals('testvalue1', $this->getConfigKey('testkey1')); 98 | $this->assertEquals('testvalue2', $this->getConfigKey('testkey2')); 99 | $this->expectException('OutOfRangeException'); 100 | $this->getConfigKey('testkey3'); 101 | } 102 | 103 | /** 104 | * @covers \BrightNucleus\Config\ConfigTrait::getConfigKey 105 | */ 106 | public function testGetConfigKeyWithMultipleLevels() 107 | { 108 | $this->processConfig( 109 | new Config( 110 | [ 111 | 'level1' => ['level2' => ['level3' => ['level4_key' => 'level4_value'],],], 112 | ] 113 | ) 114 | ); 115 | $this->assertEquals('level4_value', $this->getConfigKey('level1', 'level2', 'level3', 'level4_key')); 116 | $this->assertEquals('level4_value', $this->getConfigKey('level1\level2', 'level3', 'level4_key')); 117 | $this->assertEquals('level4_value', $this->getConfigKey('level1', 'level2/level3', 'level4_key')); 118 | $this->assertEquals('level4_value', $this->getConfigKey('level1', 'level2', 'level3.level4_key')); 119 | $this->assertEquals('level4_value', $this->getConfigKey('level1\level2\level3\level4_key')); 120 | $this->assertEquals('level4_value', $this->getConfigKey('level1/level2/level3/level4_key')); 121 | $this->assertEquals('level4_value', $this->getConfigKey('level1.level2.level3.level4_key')); 122 | $this->assertEquals('level4_value', $this->getConfigKey('level1\level2/level3.level4_key')); 123 | $this->expectException('BrightNucleus\Config\Exception\KeyNotFoundException'); 124 | $this->expectExceptionMessage('The configuration key level1->level2->level4_key does not exist.'); 125 | $this->getConfigKey('level1', 'level2', 'level4_key'); 126 | } 127 | 128 | /** 129 | * @covers \BrightNucleus\Config\ConfigTrait::getConfigArray 130 | */ 131 | public function testGetConfigArray() 132 | { 133 | $this->processConfig( 134 | new Config( 135 | [ 136 | 'testkey1' => 'testvalue1', 137 | 'testkey2' => 'testvalue2', 138 | ] 139 | ) 140 | ); 141 | $this->assertEquals( 142 | ['testkey1' => 'testvalue1', 'testkey2' => 'testvalue2'], 143 | $this->getConfigArray() 144 | ); 145 | } 146 | 147 | /** 148 | * @covers \BrightNucleus\Config\ConfigTrait::getConfigKeys 149 | */ 150 | public function testGetConfigKeys() 151 | { 152 | $this->processConfig( 153 | new Config( 154 | [ 155 | 'testkey1' => 'testvalue1', 156 | 'testkey2' => 'testvalue2', 157 | ] 158 | ) 159 | ); 160 | $this->assertEquals(['testkey1', 'testkey2'], $this->getConfigKeys()); 161 | } 162 | 163 | /** 164 | * @covers \BrightNucleus\Config\ConfigTrait::processConfig 165 | */ 166 | public function testProcessConfigAllowsPreKeying() 167 | { 168 | $this->processConfig( 169 | new Config( 170 | [ 171 | 'vendor' => [ 172 | 'package' => [ 173 | 'testkey1' => 'testvalue1', 174 | 'testkey2' => 'testvalue2', 175 | ], 176 | ], 177 | ] 178 | ), 179 | 'vendor\package' 180 | ); 181 | $this->assertEquals( 182 | ['testkey1' => 'testvalue1', 'testkey2' => 'testvalue2'], 183 | $this->getConfigArray() 184 | ); 185 | } 186 | 187 | /** 188 | * @covers \BrightNucleus\Config\ConfigTrait::processConfig 189 | */ 190 | public function testProcessConfigThrowsException() 191 | { 192 | $this->expectException('BrightNucleus\Config\Exception\FailedToProcessConfigException'); 193 | $this->expectExceptionMessage('Could not process the config with the arguments'); 194 | $this->processConfig( 195 | new Config( 196 | [ 197 | 'testkey1' => 'testvalue1', 198 | 'testkey2' => 'testvalue2', 199 | ] 200 | ), 201 | 'vendor\package' 202 | ); 203 | } 204 | 205 | /** 206 | * @covers \BrightNucleus\Config\ConfigTrait::fetchConfig 207 | */ 208 | public function testFetchConfigWithExistingFile() 209 | { 210 | $config = $this->fetchConfig(__DIR__ . '/fixtures/config_file.php'); 211 | $this->assertNotNull($config); 212 | $this->assertInstanceOf( 213 | 'BrightNucleus\Config\ConfigInterface', 214 | $config 215 | ); 216 | $this->assertTrue($config->hasKey('random_string')); 217 | $this->assertTrue($config->hasKey('positive_integer')); 218 | $this->assertTrue($config->hasKey('negative_integer')); 219 | $this->assertTrue($config->hasKey('positive_boolean')); 220 | $this->assertTrue($config->hasKey('negative_boolean')); 221 | $this->assertFalse($config->hasKey('some_other_key')); 222 | } 223 | 224 | /** 225 | * @covers \BrightNucleus\Config\ConfigTrait::fetchConfig 226 | */ 227 | public function testFetchConfigWithNonExistingFile() 228 | { 229 | $config = $this->fetchConfig(__DIR__ . '/fixtures/file_does_not_exist.php'); 230 | $this->assertNotNull($config); 231 | $this->assertInstanceOf( 232 | 'BrightNucleus\Config\ConfigInterface', 233 | $config 234 | ); 235 | $this->assertEquals([], $config->getArrayCopy()); 236 | } 237 | 238 | /** 239 | * @covers \BrightNucleus\Config\ConfigTrait::fetchDefaultConfig 240 | */ 241 | public function testFetchDefaultConfig() 242 | { 243 | $config = $this->fetchDefaultConfig(); 244 | $this->assertNotNull($config); 245 | $this->assertInstanceOf( 246 | 'BrightNucleus\Config\ConfigInterface', 247 | $config 248 | ); 249 | $this->assertTrue($config->hasKey('random_string')); 250 | $this->assertTrue($config->hasKey('positive_integer')); 251 | $this->assertTrue($config->hasKey('negative_integer')); 252 | $this->assertTrue($config->hasKey('positive_boolean')); 253 | $this->assertTrue($config->hasKey('negative_boolean')); 254 | $this->assertFalse($config->hasKey('some_other_key')); 255 | } 256 | 257 | // Used to provide a file name for fetchDefaultConfig(). 258 | protected function getDefaultConfigFile() 259 | { 260 | return __DIR__ . '/fixtures/config_file.php'; 261 | } 262 | 263 | /** 264 | * @covers \BrightNucleus\Config\ConfigTrait::getConfigCallable 265 | */ 266 | public function testGetConfigCallable() 267 | { 268 | $this->processConfig( 269 | new Config( 270 | [ 271 | 'testkey1' => function ($a, $b, $c) { 272 | return "{$c}-{$b}-{$a}"; 273 | }, 274 | 'testkey2' => 'testvalue2', 275 | 'nestedlevel0' => [ 276 | 'nestedlevel1' => [ 277 | 'nestedlevel2' => [ 278 | 'testkey3' => function ($a, $b, $c) { 279 | return "{$c}.{$b}.{$a}"; 280 | }, 281 | ], 282 | ], 283 | ], 284 | 'testkey4' => function ($a = null, $b = null, $c = null) { 285 | return "{$c}-{$b}-{$a}"; 286 | }, 287 | ] 288 | ) 289 | ); 290 | 291 | $this->assertEquals('3-2-1', $this->getConfigCallable('testkey1', [1, 2, 3])); 292 | $this->assertEquals('testvalue2', $this->getConfigCallable('testkey2', [1, 2, 3])); 293 | $this->assertEquals( 294 | '3.2.1', 295 | $this->getConfigCallable( 296 | 'nestedlevel0/nestedlevel1\nestedlevel2.testkey3', 297 | [1, 2, 3] 298 | ) 299 | ); 300 | $this->assertEquals( 301 | '3.2.1', 302 | $this->getConfigCallable( 303 | ['nestedlevel0', 'nestedlevel1', 'nestedlevel2\testkey3'], 304 | [1, 2, 3] 305 | ) 306 | ); 307 | $this->assertEquals('--', $this->getConfigCallable('testkey4', [])); 308 | $this->assertEquals('--', $this->getConfigCallable('testkey4')); 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /tests/ConfigTest.php: -------------------------------------------------------------------------------- 1 | 7 | * @license MIT 8 | * @link http://www.brightnucleus.com/ 9 | * @copyright 2016 Alain Schlesser, Bright Nucleus 10 | */ 11 | 12 | namespace BrightNucleus\Config\Tests; 13 | 14 | use BrightNucleus\Config\Config; 15 | use BrightNucleus\Config\ConfigSchema; 16 | 17 | /** 18 | * Class ConfigTest 19 | * 20 | * @since 0.1.0 21 | * 22 | * @package BrightNucleus\Config 23 | * @author Alain Schlesser 24 | */ 25 | class ConfigTest extends TestCase 26 | { 27 | 28 | /* 29 | * TODO: There's still lots of work to do to render these tests useful. 30 | */ 31 | 32 | /* 33 | * Don't use an array const to avoid bumping PHP requirement to 5.6. 34 | */ 35 | protected static $test_array = [ 36 | 'random_string' => 'test_value', 37 | 'positive_integer' => 42, 38 | 'negative_integer' => -256, 39 | 'positive_boolean' => true, 40 | 'negative_boolean' => false, 41 | ]; 42 | 43 | protected static $test_multi_array = [ 44 | 'level1' => [ 45 | 'level2' => [ 46 | 'level3' => [ 47 | 'level4_key' => 'level4_value', 48 | ], 49 | ], 50 | ], 51 | ]; 52 | 53 | /** 54 | * Test creation and value retrieval. 55 | * 56 | * @covers \BrightNucleus\Config\AbstractConfig::__construct 57 | * @covers \BrightNucleus\Config\Config::__construct 58 | * 59 | * @since 1.0.0 60 | */ 61 | public function testConfigFileCreation() 62 | { 63 | $config = new Config(ConfigTest::$test_array); 64 | 65 | $this->assertInstanceOf('\BrightNucleus\Config\ConfigInterface', $config); 66 | $this->assertInstanceOf('\BrightNucleus\Config\AbstractConfig', $config); 67 | $this->assertInstanceOf('\BrightNucleus\Config\Config', $config); 68 | } 69 | 70 | /** 71 | * Test the different error conditions that can throw exceptions in 72 | * __construct(). 73 | * 74 | * @covers BrightNucleus\Config\AbstractConfig::__construct 75 | * @covers BrightNucleus\Config\Config::__construct 76 | * 77 | * @dataProvider configExceptionsDataProvider 78 | * 79 | * @since 1.0.0 80 | * 81 | * @param string $exception Exception class to expect. 82 | * @param string $message Exception message to expect. 83 | * @param mixed $config Configuration source. 84 | * @param mixed $defaults Default values. 85 | * @param mixed $validator Validator object. 86 | */ 87 | public function testConfigExceptions( 88 | $exception, 89 | $message, 90 | $config, 91 | $defaults = null, 92 | $validator = null 93 | ) { 94 | $this->expectException($exception); 95 | $this->expectExceptionMessage($message); 96 | new Config($config, $defaults, $validator); 97 | } 98 | 99 | /** 100 | * Provide testable data to the testFeatureSupport() method. 101 | * 102 | * @since 0.1.0 103 | * 104 | * @return array 105 | */ 106 | public function configExceptionsDataProvider() 107 | { 108 | return [ 109 | // $exception, $message, $config, $defaults, $validator 110 | [ 111 | 'BrightNucleus\Config\Exception\InvalidConfigurationSourceException', 112 | 'Invalid configuration source', 113 | null, 114 | ], 115 | [ 116 | 'BrightNucleus\Config\Exception\FailedToLoadConfigException', 117 | 'Could not load resource located at "/folder/missing_file.php". Reason: ' 118 | . '"The requested PHP config file "/folder/missing_file.php" does not exist or is not readable.".', 119 | '/folder/missing_file.php', 120 | ], 121 | [ 122 | 'BrightNucleus\Config\Exception\FailedToLoadConfigException', 123 | 'Could not find a suitable loader for URI', 124 | __DIR__ . '/fixtures/dummy_file.txt', 125 | ], 126 | ]; 127 | } 128 | 129 | /** 130 | * @covers \BrightNucleus\Config\Config::__construct 131 | * @covers \BrightNucleus\Config\Config::isValid 132 | */ 133 | public function testValidation() 134 | { 135 | $unvalidated_config = new Config(ConfigTest::$test_array, null, null); 136 | $this->assertTrue($unvalidated_config->isValid()); 137 | 138 | $true_validator = $this->getMockBuilder('\BrightNucleus\Config\ConfigValidatorInterface') 139 | ->getMock(); 140 | $true_validator->method('isValid') 141 | ->willReturn(true); 142 | $valid_config = new Config(ConfigTest::$test_array, null, $true_validator); 143 | $this->assertTrue($valid_config->isValid()); 144 | 145 | $false_validator = $this->getMockBuilder('\BrightNucleus\Config\ConfigValidatorInterface') 146 | ->getMock(); 147 | $false_validator->method('isValid') 148 | ->willReturn(false); 149 | $this->expectException('BrightNucleus\Config\Exception\InvalidConfigException'); 150 | $this->expectExceptionMessage('ConfigInterface file is not valid'); 151 | new Config(ConfigTest::$test_array, null, $false_validator); 152 | } 153 | 154 | /** 155 | * @covers \BrightNucleus\Config\AbstractConfig::hasKey 156 | */ 157 | public function testHasKey() 158 | { 159 | $config = new Config(ConfigTest::$test_array); 160 | $this->assertTrue($config->hasKey('random_string')); 161 | $this->assertTrue($config->hasKey('positive_integer')); 162 | $this->assertTrue($config->hasKey('negative_integer')); 163 | $this->assertTrue($config->hasKey('positive_boolean')); 164 | $this->assertTrue($config->hasKey('negative_boolean')); 165 | $this->assertFalse($config->hasKey('some_other_key')); 166 | } 167 | 168 | /** 169 | * @covers \BrightNucleus\Config\AbstractConfig::__construct 170 | * @covers \BrightNucleus\Config\AbstractConfig::hasKey 171 | */ 172 | public function testHasKeyWithMultipleLevels() 173 | { 174 | $config = new Config(ConfigTest::$test_multi_array); 175 | $this->assertTrue($config->hasKey('level1')); 176 | $this->assertTrue($config->hasKey('level1', 'level2')); 177 | $this->assertTrue($config->hasKey('level1', 'level2', 'level3')); 178 | $this->assertTrue($config->hasKey('level1', 'level2', 'level3', 'level4_key')); 179 | $this->assertTrue($config->hasKey(['level1', 'level2', 'level3', 'level4_key'])); 180 | $this->assertTrue($config->hasKey('level1\level2', 'level3', 'level4_key')); 181 | $this->assertTrue($config->hasKey('level1\level2', ['level3', 'level4_key'])); 182 | $this->assertTrue($config->hasKey('level1', 'level2/level3', 'level4_key')); 183 | $this->assertTrue($config->hasKey('level1', 'level2', 'level3.level4_key')); 184 | $this->assertTrue($config->hasKey('level1\level2\level3\level4_key')); 185 | $this->assertTrue($config->hasKey('level1/level2/level3/level4_key')); 186 | $this->assertTrue($config->hasKey('level1.level2.level3.level4_key')); 187 | $this->assertTrue($config->hasKey('level1\level2/level3.level4_key')); 188 | $this->assertTrue($config->hasKey(['level1\level2'], ['level3.level4_key'])); 189 | $this->assertTrue($config->hasKey(['level1\level2/level3.level4_key'])); 190 | $this->assertFalse($config->hasKey('level2')); 191 | $this->assertFalse($config->hasKey('level1', 'level3')); 192 | $this->assertFalse($config->hasKey('level1', 'level2', 'level4_key')); 193 | $this->assertFalse($config->hasKey('level0', 'level1', 'level2', 'level3', 'level4_key')); 194 | $this->assertFalse($config->hasKey('level1.level3')); 195 | $this->assertFalse($config->hasKey('level1.level2.level4_key')); 196 | $this->assertFalse($config->hasKey('level0.level1.level2.level3.level4_key')); 197 | 198 | $config = new Config(ConfigTest::$test_multi_array, null, null, ['@', ':', ';']); 199 | $this->assertTrue($config->hasKey('level1')); 200 | $this->assertTrue($config->hasKey('level1', 'level2')); 201 | $this->assertTrue($config->hasKey('level1', 'level2', 'level3')); 202 | $this->assertTrue($config->hasKey('level1', 'level2', 'level3', 'level4_key')); 203 | $this->assertTrue($config->hasKey('level1@level2', 'level3', 'level4_key')); 204 | $this->assertTrue($config->hasKey('level1', 'level2:level3', 'level4_key')); 205 | $this->assertTrue($config->hasKey('level1', 'level2', 'level3;level4_key')); 206 | $this->assertTrue($config->hasKey('level1@level2@level3@level4_key')); 207 | $this->assertTrue($config->hasKey('level1:level2:level3:level4_key')); 208 | $this->assertTrue($config->hasKey('level1;level2;level3;level4_key')); 209 | $this->assertTrue($config->hasKey('level1@level2:level3;level4_key')); 210 | } 211 | 212 | /** 213 | * @covers \BrightNucleus\Config\AbstractConfig::getKeys 214 | */ 215 | public function testGetKeys() 216 | { 217 | $config = new Config(ConfigTest::$test_array); 218 | $this->assertEquals(array_keys(ConfigTest::$test_array), $config->getKeys()); 219 | } 220 | 221 | /** 222 | * @covers \BrightNucleus\Config\AbstractConfig::getKey 223 | * @covers \BrightNucleus\Config\AbstractConfig::getKeyArguments 224 | */ 225 | public function testGetKey() 226 | { 227 | $config = new Config(ConfigTest::$test_array); 228 | $this->assertEquals('test_value', $config->getKey('random_string')); 229 | $this->assertEquals(42, $config->getKey('positive_integer')); 230 | $this->assertEquals(-256, $config->getKey('negative_integer')); 231 | $this->assertTrue($config->getKey('positive_boolean')); 232 | $this->assertFalse($config->getKey('negative_boolean')); 233 | } 234 | 235 | /** 236 | * @covers \BrightNucleus\Config\AbstractConfig::getKeyArguments 237 | */ 238 | public function testGetKeyThrowsExceptionOnWrongKey() 239 | { 240 | $config = new Config(ConfigTest::$test_array); 241 | $this->expectException('BrightNucleus\Config\Exception\KeyNotFoundException'); 242 | $this->expectExceptionMessage('The configuration key some_other_key does not exist.'); 243 | $config->getKey('some_other_key'); 244 | } 245 | 246 | /** 247 | * @covers \BrightNucleus\Config\AbstractConfig::getKey 248 | * @covers \BrightNucleus\Config\AbstractConfig::getKeyArguments 249 | * @covers \BrightNucleus\Config\AbstractConfig::parseKeysString 250 | */ 251 | public function testGetKeyWithMultipleLevels() 252 | { 253 | $config = new Config(ConfigTest::$test_multi_array); 254 | $this->assertEquals(['level2' => ['level3' => ['level4_key' => 'level4_value']]], $config->getKey('level1')); 255 | $this->assertEquals(['level3' => ['level4_key' => 'level4_value']], $config->getKey('level1', 'level2')); 256 | $this->assertEquals(['level4_key' => 'level4_value'], $config->getKey('level1', 'level2', 'level3')); 257 | $this->assertEquals('level4_value', $config->getKey('level1', 'level2', 'level3', 'level4_key')); 258 | $this->assertEquals('level4_value', $config->getKey('level1\level2', 'level3', 'level4_key')); 259 | $this->assertEquals('level4_value', $config->getKey('level1', 'level2/level3', 'level4_key')); 260 | $this->assertEquals('level4_value', $config->getKey('level1', 'level2', 'level3.level4_key')); 261 | $this->assertEquals('level4_value', $config->getKey('level1\level2\level3\level4_key')); 262 | $this->assertEquals('level4_value', $config->getKey('level1/level2/level3/level4_key')); 263 | $this->assertEquals('level4_value', $config->getKey('level1.level2.level3.level4_key')); 264 | $this->assertEquals('level4_value', $config->getKey('level1\level2/level3.level4_key')); 265 | $this->expectException('BrightNucleus\Config\Exception\KeyNotFoundException'); 266 | $this->expectExceptionMessage('The configuration key level1->level2->level4_key does not exist.'); 267 | $config->getKey('level1', 'level2', 'level4_key'); 268 | } 269 | 270 | /** 271 | * @covers \BrightNucleus\Config\AbstractConfig::getAll 272 | */ 273 | public function testGetAll() 274 | { 275 | $config = new Config(ConfigTest::$test_array); 276 | $this->assertEquals(ConfigTest::$test_array, $config->getAll()); 277 | } 278 | 279 | /** 280 | * @covers \BrightNucleus\Config\Config::__construct 281 | * @covers \BrightNucleus\Config\Config::resolveOptions 282 | * @covers \BrightNucleus\Config\Config::configureOptions 283 | */ 284 | public function testConfigFileWithoutDefaults() 285 | { 286 | $config = new Config(__DIR__ . '/fixtures/config_file.php'); 287 | $this->assertTrue($config->hasKey('random_string')); 288 | $this->assertTrue($config->hasKey('positive_integer')); 289 | $this->assertTrue($config->hasKey('negative_integer')); 290 | $this->assertTrue($config->hasKey('positive_boolean')); 291 | $this->assertTrue($config->hasKey('negative_boolean')); 292 | $this->assertFalse($config->hasKey('some_other_key')); 293 | $this->assertEquals('test_value', $config->getKey('random_string')); 294 | $this->assertEquals(42, $config->getKey('positive_integer')); 295 | $this->assertEquals(-256, $config->getKey('negative_integer')); 296 | $this->assertTrue($config->getKey('positive_boolean')); 297 | $this->assertFalse($config->getKey('negative_boolean')); 298 | $this->expectException('BrightNucleus\Config\Exception\KeyNotFoundException'); 299 | $this->expectExceptionMessage('The configuration key some_other_key does not exist.'); 300 | $this->assertFalse($config->getKey('some_other_key')); 301 | } 302 | 303 | /** 304 | * @covers \BrightNucleus\Config\Config::__construct 305 | * @covers \BrightNucleus\Config\Config::resolveOptions 306 | * @covers \BrightNucleus\Config\Config::configureOptions 307 | */ 308 | public function testConfigFileWithMissingKeys() 309 | { 310 | $schema = new ConfigSchema(new Config(__DIR__ . '/fixtures/schema_config_file.php')); 311 | $this->expectException('BrightNucleus\Config\Exception\FailedToResolveConfigException'); 312 | $this->expectExceptionMessage( 313 | 'Error while resolving config options: The required option "negative_integer" is missing.' 314 | ); 315 | new Config([], $schema); 316 | } 317 | 318 | /** 319 | * @covers \BrightNucleus\Config\Config::__construct 320 | * @covers \BrightNucleus\Config\Config::resolveOptions 321 | * @covers \BrightNucleus\Config\Config::configureOptions 322 | */ 323 | public function testConfigFileWithDefaults() 324 | { 325 | $schema = new ConfigSchema(new Config(__DIR__ . '/fixtures/schema_config_file.php')); 326 | $config = new Config(['negative_integer' => -333], $schema); 327 | $this->assertTrue($config->hasKey('random_string')); 328 | $this->assertTrue($config->hasKey('positive_integer')); 329 | $this->assertTrue($config->hasKey('negative_integer')); 330 | $this->assertTrue($config->hasKey('positive_boolean')); 331 | $this->assertFalse($config->hasKey('negative_boolean')); 332 | $this->assertFalse($config->hasKey('some_other_key')); 333 | $this->assertEquals('default_test_value', $config->getKey('random_string')); 334 | $this->assertEquals(99, $config->getKey('positive_integer')); 335 | $this->assertEquals(-333, $config->getKey('negative_integer')); 336 | $this->assertTrue($config->getKey('positive_boolean')); 337 | $this->expectException('BrightNucleus\Config\Exception\KeyNotFoundException'); 338 | $this->expectExceptionMessage('The configuration key some_other_key does not exist.'); 339 | $this->assertFalse($config->getKey('some_other_key')); 340 | } 341 | 342 | /** 343 | * @covers \BrightNucleus\Config\AbstractConfig::getSubConfig 344 | */ 345 | public function testGetSubConfig() 346 | { 347 | $config = new Config(__DIR__ . '/fixtures/deep_config_file.php'); 348 | $subconfig = $config->getSubConfig('vendor/package'); 349 | $subsection1 = $config->getSubConfig('vendor', 'package', 'section_1'); 350 | $subsection2 = $subconfig->getSubConfig('section_2'); 351 | $this->assertInstanceOf('\BrightNucleus\Config\ConfigInterface', $config); 352 | $this->assertInstanceOf('\BrightNucleus\Config\AbstractConfig', $config); 353 | $this->assertInstanceOf('\BrightNucleus\Config\Config', $config); 354 | $this->assertInstanceOf('\BrightNucleus\Config\ConfigInterface', $subconfig); 355 | $this->assertInstanceOf('\BrightNucleus\Config\AbstractConfig', $subconfig); 356 | $this->assertInstanceOf('\BrightNucleus\Config\Config', $subconfig); 357 | $this->assertInstanceOf('\BrightNucleus\Config\ConfigInterface', $subsection1); 358 | $this->assertInstanceOf('\BrightNucleus\Config\AbstractConfig', $subsection1); 359 | $this->assertInstanceOf('\BrightNucleus\Config\Config', $subsection1); 360 | $this->assertInstanceOf('\BrightNucleus\Config\ConfigInterface', $subsection2); 361 | $this->assertInstanceOf('\BrightNucleus\Config\AbstractConfig', $subsection2); 362 | $this->assertInstanceOf('\BrightNucleus\Config\Config', $subsection2); 363 | $this->assertTrue($config->hasKey('vendor/package')); 364 | $this->assertTrue($config->hasKey('vendor/package/section_1/test_key_1')); 365 | $this->assertTrue($config->hasKey('vendor/package/section_2/test_key_2')); 366 | $this->assertTrue($subconfig->hasKey('section_1/test_key_1')); 367 | $this->assertTrue($subconfig->hasKey('section_2/test_key_2')); 368 | $this->assertTrue($subsection1->hasKey('test_key_1')); 369 | $this->assertTrue($subsection2->hasKey('test_key_2')); 370 | $this->expectException('BrightNucleus\Config\Exception\KeyNotFoundException'); 371 | $this->expectExceptionMessage('The configuration key some_other_key does not exist.'); 372 | $this->assertFalse($config->getSubConfig('some_other_key')); 373 | } 374 | } 375 | --------------------------------------------------------------------------------