diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a725465
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+vendor/
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..2af9d0e
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,9 @@
+# Change Log
+All notable changes to this project will be documented in this file.
+This project adheres to [Semantic Versioning](http://semver.org/).
+
+## [0.1.0] - 2016-01-13
+### Added
+- Initial release to GitHub.
+
+[0.1.0]: https://github.com/brightnucleus/phpfeature/compare/v0.0.0...v0.1.0
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..cde3db7
--- /dev/null
+++ b/README.md
@@ -0,0 +1,21 @@
+# Bright Nucleus Config Component
+
+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).
+
+## Installation
+
+The best way to use this component is through Composer:
+
+```BASH
+composer require brightnucleus/config
+```
+
+## Usage
+
+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.
+
+See [link to post coming soon] for more details.
+
+## Contributing
+
+All feedback / bug report / pull request is welcome.
\ No newline at end of file
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..8417097
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,23 @@
+{
+ "name": "brightnucleus/config",
+ "description": "Minimal, reusable Config component.",
+ "version": "0.1.0",
+ "require-dev": {
+ "phpunit/phpunit": "~5.1"
+ },
+ "license": "GPL-2.0+",
+ "authors": [
+ {
+ "name": "Alain Schlesser",
+ "email": "alain.schlesser@gmail.com"
+ }
+ ],
+ "require": {
+ "symfony/options-resolver": "~3.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "BrightNucleus\\Config\\": "src/"
+ }
+ }
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..8472c6e
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,1110 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "This file is @generated automatically"
+ ],
+ "hash": "d5caf7fb32d69f232c373e9dd02b957b",
+ "packages": [
+ {
+ "name": "symfony/options-resolver",
+ "version": "v3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/options-resolver.git",
+ "reference": "8e68c053a39e26559357cc742f01a7182ce40785"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/options-resolver/zipball/8e68c053a39e26559357cc742f01a7182ce40785",
+ "reference": "8e68c053a39e26559357cc742f01a7182ce40785",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.5.9"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\OptionsResolver\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony OptionsResolver Component",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "config",
+ "configuration",
+ "options"
+ ],
+ "time": "2015-11-18 13:48:51"
+ }
+ ],
+ "packages-dev": [
+ {
+ "name": "doctrine/instantiator",
+ "version": "1.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/instantiator.git",
+ "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d",
+ "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3,<8.0-DEV"
+ },
+ "require-dev": {
+ "athletic/athletic": "~0.1.8",
+ "ext-pdo": "*",
+ "ext-phar": "*",
+ "phpunit/phpunit": "~4.0",
+ "squizlabs/php_codesniffer": "~2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Marco Pivetta",
+ "email": "ocramius@gmail.com",
+ "homepage": "http://ocramius.github.com/"
+ }
+ ],
+ "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
+ "homepage": "https://github.com/doctrine/instantiator",
+ "keywords": [
+ "constructor",
+ "instantiate"
+ ],
+ "time": "2015-06-14 21:17:01"
+ },
+ {
+ "name": "myclabs/deep-copy",
+ "version": "1.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/DeepCopy.git",
+ "reference": "e3abefcd7f106677fd352cd7c187d6c969aa9ddc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/e3abefcd7f106677fd352cd7c187d6c969aa9ddc",
+ "reference": "e3abefcd7f106677fd352cd7c187d6c969aa9ddc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "doctrine/collections": "1.*",
+ "phpunit/phpunit": "~4.1"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "DeepCopy\\": "src/DeepCopy/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Create deep copies (clones) of your objects",
+ "homepage": "https://github.com/myclabs/DeepCopy",
+ "keywords": [
+ "clone",
+ "copy",
+ "duplicate",
+ "object",
+ "object graph"
+ ],
+ "time": "2015-11-07 22:20:37"
+ },
+ {
+ "name": "phpdocumentor/reflection-docblock",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
+ "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8",
+ "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.0"
+ },
+ "suggest": {
+ "dflydev/markdown": "~1.0",
+ "erusev/parsedown": "~1.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "phpDocumentor": [
+ "src/"
+ ]
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mike van Riel",
+ "email": "mike.vanriel@naenius.com"
+ }
+ ],
+ "time": "2015-02-03 12:10:50"
+ },
+ {
+ "name": "phpspec/prophecy",
+ "version": "v1.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpspec/prophecy.git",
+ "reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4745ded9307786b730d7a60df5cb5a6c43cf95f7",
+ "reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/instantiator": "^1.0.2",
+ "phpdocumentor/reflection-docblock": "~2.0",
+ "sebastian/comparator": "~1.1"
+ },
+ "require-dev": {
+ "phpspec/phpspec": "~2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.4.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "Prophecy\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Konstantin Kudryashov",
+ "email": "ever.zet@gmail.com",
+ "homepage": "http://everzet.com"
+ },
+ {
+ "name": "Marcello Duarte",
+ "email": "marcello.duarte@gmail.com"
+ }
+ ],
+ "description": "Highly opinionated mocking framework for PHP 5.3+",
+ "homepage": "https://github.com/phpspec/prophecy",
+ "keywords": [
+ "Double",
+ "Dummy",
+ "fake",
+ "mock",
+ "spy",
+ "stub"
+ ],
+ "time": "2015-08-13 10:07:40"
+ },
+ {
+ "name": "phpunit/php-code-coverage",
+ "version": "3.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "64d40a593fc31a8abf4ce3d200147ddf8ca64e52"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/64d40a593fc31a8abf4ce3d200147ddf8ca64e52",
+ "reference": "64d40a593fc31a8abf4ce3d200147ddf8ca64e52",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6",
+ "phpunit/php-file-iterator": "~1.3",
+ "phpunit/php-text-template": "~1.2",
+ "phpunit/php-token-stream": "~1.3",
+ "sebastian/environment": "^1.3.2",
+ "sebastian/version": "~1.0"
+ },
+ "require-dev": {
+ "ext-xdebug": ">=2.1.4",
+ "phpunit/phpunit": "~5"
+ },
+ "suggest": {
+ "ext-dom": "*",
+ "ext-xdebug": ">=2.2.1",
+ "ext-xmlwriter": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.1.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sb@sebastian-bergmann.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+ "keywords": [
+ "coverage",
+ "testing",
+ "xunit"
+ ],
+ "time": "2016-01-11 10:05:34"
+ },
+ {
+ "name": "phpunit/php-file-iterator",
+ "version": "1.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+ "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0",
+ "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.4.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sb@sebastian-bergmann.de",
+ "role": "lead"
+ }
+ ],
+ "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+ "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+ "keywords": [
+ "filesystem",
+ "iterator"
+ ],
+ "time": "2015-06-21 13:08:43"
+ },
+ {
+ "name": "phpunit/php-text-template",
+ "version": "1.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
+ "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
+ "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Simple template engine.",
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+ "keywords": [
+ "template"
+ ],
+ "time": "2015-06-21 13:50:34"
+ },
+ {
+ "name": "phpunit/php-timer",
+ "version": "1.0.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
+ "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3e82f4e9fc92665fafd9157568e4dcb01d014e5b",
+ "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sb@sebastian-bergmann.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Utility class for timing",
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
+ "keywords": [
+ "timer"
+ ],
+ "time": "2015-06-21 08:01:12"
+ },
+ {
+ "name": "phpunit/php-token-stream",
+ "version": "1.4.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-token-stream.git",
+ "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da",
+ "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da",
+ "shasum": ""
+ },
+ "require": {
+ "ext-tokenizer": "*",
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.4-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Wrapper around PHP's tokenizer extension.",
+ "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
+ "keywords": [
+ "tokenizer"
+ ],
+ "time": "2015-09-15 10:49:45"
+ },
+ {
+ "name": "phpunit/phpunit",
+ "version": "5.1.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "72e92139712e868c3888ed4dd4d3c3eb365b1bb7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/72e92139712e868c3888ed4dd4d3c3eb365b1bb7",
+ "reference": "72e92139712e868c3888ed4dd4d3c3eb365b1bb7",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-json": "*",
+ "ext-pcre": "*",
+ "ext-reflection": "*",
+ "ext-spl": "*",
+ "myclabs/deep-copy": "~1.3",
+ "php": ">=5.6",
+ "phpspec/prophecy": "^1.3.1",
+ "phpunit/php-code-coverage": "~3.0",
+ "phpunit/php-file-iterator": "~1.4",
+ "phpunit/php-text-template": "~1.2",
+ "phpunit/php-timer": ">=1.0.6",
+ "phpunit/phpunit-mock-objects": ">=3.0.5",
+ "sebastian/comparator": "~1.1",
+ "sebastian/diff": "~1.2",
+ "sebastian/environment": "~1.3",
+ "sebastian/exporter": "~1.2",
+ "sebastian/global-state": "~1.0",
+ "sebastian/resource-operations": "~1.0",
+ "sebastian/version": "~1.0",
+ "symfony/yaml": "~2.1|~3.0"
+ },
+ "suggest": {
+ "phpunit/php-invoker": "~1.1"
+ },
+ "bin": [
+ "phpunit"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.1.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "The PHP Unit Testing framework.",
+ "homepage": "https://phpunit.de/",
+ "keywords": [
+ "phpunit",
+ "testing",
+ "xunit"
+ ],
+ "time": "2016-01-29 09:19:36"
+ },
+ {
+ "name": "phpunit/phpunit-mock-objects",
+ "version": "3.0.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
+ "reference": "49bc700750196c04dd6bc2c4c99cb632b893836b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/49bc700750196c04dd6bc2c4c99cb632b893836b",
+ "reference": "49bc700750196c04dd6bc2c4c99cb632b893836b",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/instantiator": "^1.0.2",
+ "php": ">=5.6",
+ "phpunit/php-text-template": "~1.2",
+ "sebastian/exporter": "~1.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~5"
+ },
+ "suggest": {
+ "ext-soap": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sb@sebastian-bergmann.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Mock Object library for PHPUnit",
+ "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
+ "keywords": [
+ "mock",
+ "xunit"
+ ],
+ "time": "2015-12-08 08:47:06"
+ },
+ {
+ "name": "sebastian/comparator",
+ "version": "1.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/comparator.git",
+ "reference": "937efb279bd37a375bcadf584dec0726f84dbf22"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22",
+ "reference": "937efb279bd37a375bcadf584dec0726f84dbf22",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3",
+ "sebastian/diff": "~1.2",
+ "sebastian/exporter": "~1.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.4"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.2.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@2bepublished.at"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides the functionality to compare PHP values for equality",
+ "homepage": "http://www.github.com/sebastianbergmann/comparator",
+ "keywords": [
+ "comparator",
+ "compare",
+ "equality"
+ ],
+ "time": "2015-07-26 15:48:44"
+ },
+ {
+ "name": "sebastian/diff",
+ "version": "1.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/diff.git",
+ "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e",
+ "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.4-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Kore Nordmann",
+ "email": "mail@kore-nordmann.de"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Diff implementation",
+ "homepage": "https://github.com/sebastianbergmann/diff",
+ "keywords": [
+ "diff"
+ ],
+ "time": "2015-12-08 07:14:41"
+ },
+ {
+ "name": "sebastian/environment",
+ "version": "1.3.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/environment.git",
+ "reference": "6e7133793a8e5a5714a551a8324337374be209df"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6e7133793a8e5a5714a551a8324337374be209df",
+ "reference": "6e7133793a8e5a5714a551a8324337374be209df",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.4"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.3.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides functionality to handle HHVM/PHP environments",
+ "homepage": "http://www.github.com/sebastianbergmann/environment",
+ "keywords": [
+ "Xdebug",
+ "environment",
+ "hhvm"
+ ],
+ "time": "2015-12-02 08:37:27"
+ },
+ {
+ "name": "sebastian/exporter",
+ "version": "1.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/exporter.git",
+ "reference": "7ae5513327cb536431847bcc0c10edba2701064e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/7ae5513327cb536431847bcc0c10edba2701064e",
+ "reference": "7ae5513327cb536431847bcc0c10edba2701064e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3",
+ "sebastian/recursion-context": "~1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.4"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.2.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@2bepublished.at"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ }
+ ],
+ "description": "Provides the functionality to export PHP variables for visualization",
+ "homepage": "http://www.github.com/sebastianbergmann/exporter",
+ "keywords": [
+ "export",
+ "exporter"
+ ],
+ "time": "2015-06-21 07:55:53"
+ },
+ {
+ "name": "sebastian/global-state",
+ "version": "1.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/global-state.git",
+ "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4",
+ "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.2"
+ },
+ "suggest": {
+ "ext-uopz": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Snapshotting of global state",
+ "homepage": "http://www.github.com/sebastianbergmann/global-state",
+ "keywords": [
+ "global state"
+ ],
+ "time": "2015-10-12 03:26:01"
+ },
+ {
+ "name": "sebastian/recursion-context",
+ "version": "1.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
+ "reference": "913401df809e99e4f47b27cdd781f4a258d58791"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/913401df809e99e4f47b27cdd781f4a258d58791",
+ "reference": "913401df809e99e4f47b27cdd781f4a258d58791",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.4"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ }
+ ],
+ "description": "Provides functionality to recursively process PHP variables",
+ "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
+ "time": "2015-11-11 19:50:13"
+ },
+ {
+ "name": "sebastian/resource-operations",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/resource-operations.git",
+ "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
+ "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Provides a list of PHP built-in functions that operate on resources",
+ "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
+ "time": "2015-07-28 20:34:47"
+ },
+ {
+ "name": "sebastian/version",
+ "version": "1.0.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/version.git",
+ "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6",
+ "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6",
+ "shasum": ""
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+ "homepage": "https://github.com/sebastianbergmann/version",
+ "time": "2015-06-21 13:59:46"
+ },
+ {
+ "name": "symfony/yaml",
+ "version": "v3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/yaml.git",
+ "reference": "3df409958a646dad2bc5046c3fb671ee24a1a691"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/3df409958a646dad2bc5046c3fb671ee24a1a691",
+ "reference": "3df409958a646dad2bc5046c3fb671ee24a1a691",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.5.9"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Yaml\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Yaml Component",
+ "homepage": "https://symfony.com",
+ "time": "2015-12-26 13:39:53"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 0000000..af23108
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,20 @@
+
+
+
+ tests/ConfigTest.php
+
+
+
+
+ src
+
+
+
\ No newline at end of file
diff --git a/src/AbstractConfig.php b/src/AbstractConfig.php
new file mode 100644
index 0000000..6ba8947
--- /dev/null
+++ b/src/AbstractConfig.php
@@ -0,0 +1,93 @@
+
+ * @license GPL-2.0+
+ * @link http://www.brightnucleus.com/
+ * @copyright 2016 Alain Schlesser, Bright Nucleus
+ */
+
+namespace BrightNucleus\Config;
+
+use ArrayObject;
+use OutOfRangeException;
+use BadMethodCallException;
+use BrightNucleus\Config\ConfigInterface;
+
+/**
+ * Config loader used to load config PHP files as objects.
+ *
+ * @since 0.1.0
+ *
+ * @package BrightNucleus\Config
+ * @author Alain Schlesser
+ */
+abstract class AbstractConfig extends ArrayObject implements ConfigInterface
+{
+
+ /**
+ * Instantiate the AbstractConfig object.
+ *
+ * @since 0.1.0
+ *
+ * @param array $config Array with settings.
+ */
+ public function __construct(array $config)
+ {
+ // Make sure the config entries can be accessed as properties.
+ parent::__construct($config, ArrayObject::ARRAY_AS_PROPS);
+ }
+
+ /**
+ * Check whether the Config has a specific key.
+ *
+ * @since 0.1.0
+ *
+ * @param string $key The key to check the existence for.
+ * @return bool
+ */
+ public function hasKey($key)
+ {
+ return array_key_exists($key, (array)$this);
+ }
+
+ /**
+ * Get the value of a specific key.
+ *
+ * @since 0.1.0
+ *
+ * @param string $key The key to get the value for.
+ * @return mixed
+ * @throws OutOfRangeException If an unknown key is requested.
+ */
+ public function getKey($key)
+ {
+ if ( ! $this->hasKey($key)) {
+ throw new OutOfRangeException(sprintf(_('The configuration key %1$s does not exist.'),
+ (string)$key));
+ }
+
+ return $this[$key];
+ }
+
+ /**
+ * Get the an array with all the keys
+ *
+ * @since 0.1.0
+ * @return mixed
+ */
+ public function getKeys()
+ {
+ return array_keys((array)$this);
+ }
+
+ /**
+ * Validate the Config file.
+ *
+ * @since 0.1.0
+ * @return boolean
+ */
+ abstract public function isValid();
+}
diff --git a/src/AbstractConfigSchema.php b/src/AbstractConfigSchema.php
new file mode 100644
index 0000000..326a84b
--- /dev/null
+++ b/src/AbstractConfigSchema.php
@@ -0,0 +1,107 @@
+
+ * @license GPL-2.0+
+ * @link http://www.brightnucleus.com/
+ * @copyright 2016 Alain Schlesser, Bright Nucleus
+ */
+
+namespace BrightNucleus\Config;
+
+use BrightNucleus\Config\ConfigSchemaInterface;
+
+/**
+ * Class AbstractConfigSchema
+ *
+ * @since 0.1.0
+ *
+ * @package BrightNucleus\Config
+ * @author Alain Schlesser
+ */
+abstract class AbstractConfigSchema implements ConfigSchemaInterface
+{
+
+ /**
+ * The defined values that are recognized.
+ *
+ * @var ConfigInterface
+ */
+ protected $defined;
+
+ /**
+ * The default values that can be overwritten.
+ *
+ * @var ConfigInterface
+ */
+ protected $defaults;
+
+ /**
+ * The required values that need to be set.
+ *
+ * @var ConfigInterface
+ */
+ protected $required;
+
+ /**
+ * Get the set of defined options.
+ *
+ * @since 0.1.0
+ *
+ * @return array|null
+ */
+ public function getDefinedOptions()
+ {
+ if ( ! $this->defined) {
+ return null;
+ }
+
+ if ($this->defined instanceof ConfigInterface) {
+ return $this->defined->getArrayCopy();
+ }
+
+ return (array)$this->defined;
+ }
+
+ /**
+ * Get the set of default options.
+ *
+ * @since 0.1.0
+ *
+ * @return array|null
+ */
+ public function getDefaultOptions()
+ {
+ if ( ! $this->defaults) {
+ return null;
+ }
+
+ if ($this->defaults instanceof ConfigInterface) {
+ return $this->defaults->getArrayCopy();
+ }
+
+ return (array)$this->defaults;
+ }
+
+ /**
+ * Get the set of required options.
+ *
+ * @since 0.1.0
+ *
+ * @return array|null
+ */
+ public function getRequiredOptions()
+ {
+ if ( ! $this->required) {
+ return null;
+ }
+
+ if ($this->required instanceof ConfigInterface) {
+ return $this->required->getArrayCopy();
+ }
+
+ return (array)$this->required;
+ }
+}
diff --git a/src/Config.php b/src/Config.php
new file mode 100644
index 0000000..7eb051a
--- /dev/null
+++ b/src/Config.php
@@ -0,0 +1,244 @@
+
+ * @license GPL-2.0+
+ * @link http://www.brightnucleus.com/
+ * @copyright 2016 Alain Schlesser, Bright Nucleus
+ */
+
+namespace BrightNucleus\Config;
+
+use Exception;
+use RuntimeException;
+use InvalidArgumentException;
+use UnexpectedValueException;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+use BrightNucleus\Config\ConfigSchemaInterface as Schema;
+use BrightNucleus\Config\ConfigValidatorInterface as Validator;
+
+/**
+ * Class Config
+ *
+ * @since 0.1.0
+ *
+ * @package BrightNucleus\Config
+ * @author Alain Schlesser
+ */
+class Config extends AbstractConfig
+{
+
+ /**
+ * The schema of the Config file.
+ *
+ * @var Schema
+ */
+ protected $schema;
+
+ /**
+ * The Validator class that gets asked to do the validation of the config.
+ *
+ * @since 0.1.0
+ *
+ * @var Validator
+ */
+ protected $validator;
+
+ /**
+ * Instantiate the Config object.
+ *
+ * It accepts either an array with the configuration settings, or a
+ * filename pointing to a PHP file it can include.
+ *
+ * @since 0.1.0
+ *
+ * @param array|string $config Array with settings or filename for the
+ * settings file.
+ * @param Schema|null $schema Optional. Config that contains default
+ * values that can get overwritten.
+ * @param Validator|null $validator Optional. Validator class that does the
+ * actual validation.
+ *
+ * @throws InvalidArgumentException If the config source is not a string or
+ * array.
+ * @throws RuntimeException If loading of the config source failed.
+ * @throws UnexpectedValueException If the config file is not valid.
+ */
+ public function __construct(
+ $config,
+ Schema $schema = null,
+ Validator $validator = null
+ ) {
+
+ $this->schema = $schema;
+ $this->validator = $validator;
+
+ // Make sure $config is either a string or array.
+ if ( ! (is_string($config) || is_array($config))) {
+ throw new InvalidArgumentException(sprintf(
+ _('Invalid configuration source: %1$s'),
+ print_r($config, true)
+ ));
+ }
+
+ if (is_string($config)) {
+ $config = $this->fetchArrayData($config);
+ }
+
+ $config = $this->resolveOptions($config);
+
+ parent::__construct($config);
+
+ // Finally, validate the resulting config.
+ if ( ! $this->isValid()) {
+ throw new UnexpectedValueException(sprintf(
+ _('ConfigInterface file is not valid: %1$s'),
+ print_r($config, true)
+ ));
+ }
+ }
+
+ /**
+ * Fetch array data from a string pointing to a file.
+ *
+ * @since 0.1.0
+ *
+ * @param string $config Filename for the settings file.
+ * @return array Array with configuration settings.
+ * @throws RuntimeException If the config source is a non-existing
+ * file.
+ * @throws RuntimeException If loading of the config source failed.
+ */
+ protected function fetchArrayData($config)
+ {
+
+ if (is_string($config) && ! ('' === $config)) {
+
+ // $config is a valid string, make sure it is an existing file.
+ if ( ! file_exists($config)) {
+ throw new RuntimeException(sprintf(
+ _('Non-existing configuration source: %1$s'),
+ (string)$config
+ ));
+ }
+
+ // Try to load the file through PHP's include().
+ $configString = $config;
+ try {
+ // Make sure we don't accidentally create output.
+ ob_get_contents();
+ $config = include($configString);
+ ob_clean();
+
+ // The included should return an array.
+ if ( ! is_array($config)) {
+ throw new RuntimeException(_('File inclusion did not return an array.'));
+ }
+ } catch (Exception $exception) {
+ throw new RuntimeException(sprintf(
+ _('Loading from configuration source %1$s failed. Reason: %2$s'),
+ (string)$configString,
+ (string)$exception->getMessage()
+ ));
+ }
+ }
+
+ return (array)$config;
+ }
+
+ /**
+ * Process the passed-in defaults and merge them with the new values, while
+ * checking that all required options are set.
+ *
+ * @since 0.1.0
+ *
+ * @param array $config Configuration settings to resolve.
+ * @return array Resolved configuration settings.
+ * @throws UnexpectedValueException If there are errors while resolving the
+ * options.
+ */
+ protected function resolveOptions($config)
+ {
+
+ try {
+ $resolver = new OptionsResolver();
+ if ($this->configureOptions($resolver)) {
+ $config = $resolver->resolve($config);
+ }
+ } catch (Exception $exception) {
+ throw new UnexpectedValueException(sprintf(
+ _('Error while resolving config options: %1$s'),
+ $exception->getMessage()
+ ));
+ }
+
+ return $config;
+ }
+
+ /**
+ * Configure the possible and required options for the Config.
+ *
+ * This should return a bool to let the resolve_options() know whether the
+ * actual resolving needs to be done or not.
+ *
+ * @since 0.1.0
+ *
+ * @param OptionsResolver $resolver Reference to the OptionsResolver
+ * instance.
+ * @return bool Whether to do the resolving.
+ * @throws UnexpectedValueException If there are errors while processing.
+ */
+ protected function configureOptions(OptionsResolver $resolver)
+ {
+
+ if ( ! $this->schema) {
+ return false;
+ }
+
+ $defined = $this->schema->getDefinedOptions();
+ $defaults = $this->schema->getDefaultOptions();
+ $required = $this->schema->getRequiredOptions();
+
+ if ( ! $defined && ! $defaults && ! $required) {
+ return false;
+ }
+
+ try {
+ if ($defined) {
+ $resolver->setDefined($defined);
+ }
+ if ($defaults) {
+ $resolver->setDefaults($defaults);
+ }
+ if ($required) {
+ $resolver->setRequired($required);
+ }
+ } catch (Exception $exception) {
+ throw new UnexpectedValueException(sprintf(
+ _('Error while processing config options: %1$s'),
+ $exception->getMessage()
+ ));
+ }
+
+ return true;
+ }
+
+ /**
+ * Validate the Config file.
+ *
+ * @since 0.1.0
+ *
+ * @return boolean
+ */
+ public function isValid()
+ {
+
+ if ($this->validator) {
+ return $this->validator->isValid($this);
+ }
+
+ return true;
+ }
+}
diff --git a/src/ConfigInterface.php b/src/ConfigInterface.php
new file mode 100644
index 0000000..0f89d19
--- /dev/null
+++ b/src/ConfigInterface.php
@@ -0,0 +1,82 @@
+
+ * @license GPL-2.0+
+ * @link http://www.brightnucleus.com/
+ * @copyright 2016 Alain Schlesser, Bright Nucleus
+ */
+
+namespace BrightNucleus\Config;
+
+use Countable;
+use ArrayAccess;
+use Serializable;
+use IteratorAggregate;
+
+/**
+ * Config loader used to load config PHP files as objects.
+ *
+ * @since 0.1.0
+ *
+ * @package BrightNucleus\Config
+ * @author Alain Schlesser
+ */
+interface ConfigInterface extends IteratorAggregate, ArrayAccess, Serializable, Countable
+{
+
+ /**
+ * Creates a copy of the ArrayObject.
+ *
+ * Returns a copy of the array. When the ArrayObject refers to an object an
+ * array of the public properties of that object will be returned.
+ * This is implemented by \ArrayObject.
+ *
+ * @since 0.1.0
+ *
+ * @return array Copy of the array.
+ */
+ public function getArrayCopy();
+
+ /**
+ * Check whether the Config has a specific key.
+ *
+ * @since 0.1.0
+ *
+ * @param string $key The key to check the existence for.
+ *
+ * @return bool
+ */
+ public function hasKey($key);
+
+ /**
+ * Get the value of a specific key.
+ *
+ * @since 0.1.0
+ *
+ * @param string $key The key to get the value for.
+ *
+ * @return mixed
+ */
+ public function getKey($key);
+
+ /**
+ * Get the an array with all the keys
+ *
+ * @since 0.1.0
+ *
+ * @return mixed
+ */
+ public function getKeys();
+
+ /**
+ * Is the Config valid?
+ *
+ * @since 0.1.0
+ *
+ * @return boolean
+ */
+ public function isValid();
+}
diff --git a/src/ConfigSchema.php b/src/ConfigSchema.php
new file mode 100644
index 0000000..34bd05e
--- /dev/null
+++ b/src/ConfigSchema.php
@@ -0,0 +1,156 @@
+
+ * @license GPL-2.0+
+ * @link http://www.brightnucleus.com/
+ * @copyright 2016 Alain Schlesser, Bright Nucleus
+ */
+
+namespace BrightNucleus\Config;
+
+use InvalidArgumentException;
+use BrightNucleus\Config\ConfigInterface;
+
+/**
+ * Class ConfigSchema
+ *
+ * @since 0.1.0
+ *
+ * @package BrightNucleus\Config
+ * @author Alain Schlesser
+ */
+class ConfigSchema extends AbstractConfigSchema
+{
+
+ /**
+ * The key that is used in the schema to define a required value.
+ */
+ const REQUIRED_KEY = 'required';
+
+ /**
+ * The key that is used in the schema to define a default value.
+ */
+ const DEFAULT_VALUE = 'default';
+
+ /**
+ * The list of values that are recognized as true in the schema.
+ */
+ const TRUTHY_VALUES = [
+ true,
+ 1,
+ 'true',
+ 'True',
+ 'TRUE',
+ 'y',
+ 'Y',
+ 'yes',
+ 'Yes',
+ 'YES',
+ '√',
+ ];
+
+ /**
+ * Instantiate a ConfigSchema object.
+ *
+ * @since 0.1.0
+ *
+ * @param ConfigInterface|array $schema The schema to parse.
+ * @throws InvalidArgumentException
+ */
+ public function __construct($schema)
+ {
+ if ($schema instanceof ConfigInterface) {
+ $schema = $schema->getArrayCopy();
+ }
+
+ if ( ! is_array($schema)) {
+ throw new InvalidArgumentException(sprintf(
+ _('Invalid schema source: %1$s'),
+ print_r($schema, true)
+ ));
+ }
+
+ array_walk($schema, [$this, 'parseSchema']);
+ }
+
+ /**
+ * Parse a single provided schema entry.
+ *
+ * @since 0.1.0
+ *
+ * @param mixed $data The data associated with the key.
+ * @param string $key The key of the schema data.
+ */
+ protected function parseSchema($data, $key)
+ {
+ $this->parseDefined($key, $data);
+
+ if (array_key_exists(ConfigSchema::REQUIRED_KEY, $data)) {
+ $this->parseRequired($key,
+ $data[ConfigSchema::REQUIRED_KEY]);
+ }
+
+ if (array_key_exists(ConfigSchema::DEFAULT_VALUE, $data)) {
+ $this->parseDefault($key,
+ $data[ConfigSchema::DEFAULT_VALUE]);
+ }
+ }
+
+ /**
+ * Parse the set of defined values.
+ *
+ * @since 0.1.0
+ *
+ * @param string $key The key of the schema data.
+ * @param mixed $data The data associated with the key.
+ */
+ protected function parseDefined($key, $data)
+ {
+ $this->defined[] = $key;
+ }
+
+ /**
+ * Parse the set of required values.
+ *
+ * @since 0.1.0
+ *
+ * @param string $key The key of the schema data.
+ * @param mixed $data The data associated with the key.
+ */
+ protected function parseRequired($key, $data)
+ {
+ if ($this->isTruthy($data)) {
+ $this->required[] = $key;
+ }
+ }
+
+ /**
+ * Parse the set of default values.
+ *
+ * @since 0.1.0
+ *
+ * @param string $key The key of the schema data.
+ * @param mixed $data The data associated with the key.
+ */
+ protected function parseDefault($key, $data)
+ {
+ $this->defaults[$key] = $data;
+ }
+
+ /**
+ * Return a boolean true or false for an arbitrary set of data. Recognizes
+ * several different string values that should be valued as true.
+ *
+ * @since 0.1.0
+ *
+ * @param mixed $data The data to evaluate.
+ * @return bool
+ */
+ protected function isTruthy($data)
+ {
+ return in_array($data, ConfigSchema::TRUTHY_VALUES, true);
+ }
+}
diff --git a/src/ConfigSchemaInterface.php b/src/ConfigSchemaInterface.php
new file mode 100644
index 0000000..d261367
--- /dev/null
+++ b/src/ConfigSchemaInterface.php
@@ -0,0 +1,51 @@
+
+ * @license GPL-2.0+
+ * @link http://www.brightnucleus.com/
+ * @copyright 2016 Alain Schlesser, Bright Nucleus
+ */
+
+namespace BrightNucleus\Config;
+
+/**
+ * Interface ConfigSchemaInterface
+ *
+ * @since 0.1.0
+ *
+ * @package BrightNucleus\Config
+ * @author Alain Schlesser
+ */
+interface ConfigSchemaInterface
+{
+
+ /**
+ * Get the set of defined options.
+ *
+ * @since 0.1.0
+ *
+ * @return array|null
+ */
+ public function getDefinedOptions();
+
+ /**
+ * Get the set of default options.
+ *
+ * @since 0.1.0
+ *
+ * @return array|null
+ */
+ public function getDefaultOptions();
+
+ /**
+ * Get the set of required options.
+ *
+ * @since 0.1.0
+ *
+ * @return array|null
+ */
+ public function getRequiredOptions();
+}
diff --git a/src/ConfigValidatorInterface.php b/src/ConfigValidatorInterface.php
new file mode 100644
index 0000000..d761edd
--- /dev/null
+++ b/src/ConfigValidatorInterface.php
@@ -0,0 +1,37 @@
+
+ * @license GPL-2.0+
+ * @link http://www.brightnucleus.com/
+ * @copyright 2016 Alain Schlesser, Bright Nucleus
+ */
+
+namespace BrightNucleus\Config;
+
+use BrightNucleus\Config\ConfigInterface;
+
+/**
+ * Interface ConfigValidatorInterface
+ *
+ * @since 0.1.0
+ *
+ * @package BrightNucleus\Config
+ * @author Alain Schlesser
+ */
+interface ConfigValidatorInterface
+{
+
+ /**
+ * Check whether the passed-in Config is valid.
+ *
+ * @since 0.1.0
+ *
+ * @param ConfigInterface $config
+ *
+ * @return bool
+ */
+ public function isValid(ConfigInterface $config);
+}
diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php
new file mode 100644
index 0000000..b3b9484
--- /dev/null
+++ b/tests/ConfigTest.php
@@ -0,0 +1,248 @@
+
+ * @license GPL-2.0+
+ * @link http://www.brightnucleus.com/
+ * @copyright 2016 Alain Schlesser, Bright Nucleus
+ */
+
+namespace BrightNucleus\Config;
+
+use BrightNucleus\Config\Config;
+
+/**
+ * Class ConfigTest
+ *
+ * @since 0.1.0
+ *
+ * @package BrightNucleus\Config
+ * @author Alain Schlesser
+ */
+class ConfigTest extends \PHPUnit_Framework_TestCase
+{
+
+ /*
+ * TODO: There's still lots of work to do to render these tests useful.
+ */
+
+ const TEST_ARRAY = [
+ 'random_string' => 'test_value',
+ 'positive_integer' => 42,
+ 'negative_integer' => -256,
+ 'positive_boolean' => true,
+ 'negative_boolean' => false,
+ ];
+
+ const TEST_SCHEMA = [
+ 'random_string' => [
+ 'default' => 'default_test_value',
+ 'required' => true,
+ ],
+ 'positive_integer' => [
+ 'default' => 'default_test_value',
+ 'required' => 'true',
+ ],
+ 'negative_integer' => [
+ 'default' => 'default_test_value',
+ 'required' => 'Yes',
+ ],
+ 'positive_boolean' => ['default' => 'default_test_value'],
+ 'negative_boolean' => ['default' => 'default_test_value'],
+ ];
+
+ /**
+ * Test creation and value retrieval.
+ *
+ * @covers BrightNucleus\Config\AbstractConfig::__construct
+ * @covers BrightNucleus\Config\Config::__construct
+ *
+ * @since 1.0.0
+ */
+ public function testConfigFileCreation()
+ {
+
+ $config = new Config(ConfigTest::TEST_ARRAY);
+
+ $this->assertInstanceOf('\BrightNucleus\Config\ConfigInterface',
+ $config);
+ $this->assertInstanceOf('\BrightNucleus\Config\AbstractConfig',
+ $config);
+ $this->assertInstanceOf('\BrightNucleus\Config\Config', $config);
+ }
+
+ /**
+ * Test the different error conditions that can throw exceptions in
+ * __construct().
+ *
+ * @covers BrightNucleus\Config\AbstractConfig::__construct
+ * @covers BrightNucleus\Config\Config::__construct
+ * @covers BrightNucleus\Config\Config::fetchArrayData
+ *
+ * @dataProvider configExceptionsDataProvider
+ *
+ * @since 1.0.0
+ *
+ * @param string $exception Exception class to expect.
+ * @param string $message Exception message to expect.
+ * @param mixed $config Configuration source.
+ * @param mixed $defaults Default values.
+ * @param mixed $validator Validator object.
+ */
+ public function testConfigExceptions(
+ $exception,
+ $message,
+ $config,
+ $defaults = null,
+ $validator = null
+ ) {
+
+ $this->setExpectedException($exception, $message);
+ $config = new Config($config, $defaults, $validator);
+ }
+
+ /**
+ * Provide testable data to the testFeatureSupport() method.
+ *
+ * @since 0.1.0
+ *
+ * @return array
+ */
+ public function configExceptionsDataProvider()
+ {
+ return [
+ // $exception, $message, $config, $defaults, $validator
+ ['InvalidArgumentException', 'Invalid configuration source', null],
+ [
+ 'RuntimeException',
+ 'Non-existing configuration source',
+ '/folder/missing_file.php',
+ ],
+ [
+ 'RuntimeException',
+ 'File inclusion did not return an array.',
+ __DIR__ . '/dummy_file.txt',
+ ],
+ ];
+ }
+
+ /**
+ * @covers BrightNucleus\Config\Config::__construct
+ * @covers BrightNucleus\Config\Config::isValid
+ */
+ public function testValidation()
+ {
+ $unvalidated_config = new Config(ConfigTest::TEST_ARRAY, null, null);
+ $this->assertTrue($unvalidated_config->isValid());
+
+ $true_validator = $this->getMockBuilder('\BrightNucleus\Config\ConfigValidatorInterface')
+ ->getMock();
+ $true_validator->method('isValid')
+ ->willReturn(true);
+ $valid_config = new Config(ConfigTest::TEST_ARRAY, null,
+ $true_validator);
+ $this->assertTrue($valid_config->isValid());
+
+ $false_validator = $this->getMockBuilder('\BrightNucleus\Config\ConfigValidatorInterface')
+ ->getMock();
+ $false_validator->method('isValid')
+ ->willReturn(false);
+ $this->setExpectedException('UnexpectedValueException',
+ 'ConfigInterface file is not valid');
+ $invalid_config = new Config(ConfigTest::TEST_ARRAY, null,
+ $false_validator);
+ }
+
+ /**
+ * @covers BrightNucleus\Config\AbstractConfig::hasKey
+ */
+ public function testHasKey()
+ {
+ $config = new Config(ConfigTest::TEST_ARRAY);
+ $this->assertTrue($config->hasKey('random_string'));
+ $this->assertTrue($config->hasKey('positive_integer'));
+ $this->assertTrue($config->hasKey('negative_integer'));
+ $this->assertTrue($config->hasKey('positive_boolean'));
+ $this->assertTrue($config->hasKey('negative_boolean'));
+ $this->assertFalse($config->hasKey('some_other_key'));
+ }
+
+ /**
+ * @covers BrightNucleus\Config\AbstractConfig::getKeys
+ */
+ public function testGetKeys()
+ {
+ $config = new Config(ConfigTest::TEST_ARRAY);
+ $this->assertEquals(array_keys(ConfigTest::TEST_ARRAY),
+ $config->getKeys());
+ }
+
+ /**
+ * @covers BrightNucleus\Config\AbstractConfig::getKey
+ */
+ public function testGetKey()
+ {
+ $config = new Config(ConfigTest::TEST_ARRAY);
+ $this->assertEquals('test_value', $config->getKey('random_string'));
+ $this->assertEquals(42, $config->getKey('positive_integer'));
+ $this->assertEquals(-256, $config->getKey('negative_integer'));
+ $this->assertTrue($config->getKey('positive_boolean'));
+ $this->assertFalse($config->getKey('negative_boolean'));
+ $this->setExpectedException('OutOfRangeException',
+ 'The configuration key some_other_key does not exist.');
+ $this->assertFalse($config->getKey('some_other_key'));
+ }
+
+ /**
+ * @covers BrightNucleus\Config\Config::__construct
+ * @covers BrightNucleus\Config\Config::fetchArrayData
+ * @covers BrightNucleus\Config\Config::resolveOptions
+ * @covers BrightNucleus\Config\Config::configureOptions
+ */
+ public function testConfigFileWithoutDefaults()
+ {
+ $config = new Config(__DIR__ . '/config_file.php');
+ $this->assertTrue($config->hasKey('random_string'));
+ $this->assertTrue($config->hasKey('positive_integer'));
+ $this->assertTrue($config->hasKey('negative_integer'));
+ $this->assertTrue($config->hasKey('positive_boolean'));
+ $this->assertTrue($config->hasKey('negative_boolean'));
+ $this->assertFalse($config->hasKey('some_other_key'));
+ $this->assertEquals('test_value', $config->getKey('random_string'));
+ $this->assertEquals(42, $config->getKey('positive_integer'));
+ $this->assertEquals(-256, $config->getKey('negative_integer'));
+ $this->assertTrue($config->getKey('positive_boolean'));
+ $this->assertFalse($config->getKey('negative_boolean'));
+ $this->setExpectedException('OutOfRangeException',
+ 'The configuration key some_other_key does not exist.');
+ $this->assertFalse($config->getKey('some_other_key'));
+ }
+
+ /**
+ * @covers BrightNucleus\Config\Config::__construct
+ * @covers BrightNucleus\Config\Config::fetchArrayData
+ * @covers BrightNucleus\Config\Config::resolveOptions
+ * @covers BrightNucleus\Config\Config::configureOptions
+ */
+ public function testConfigFileWithDefaults()
+ {
+ $schema = new ConfigSchema(ConfigTest::TEST_SCHEMA);
+ $config = new Config(__DIR__ . '/config_file.php', $schema);
+ $this->assertTrue($config->hasKey('random_string'));
+ $this->assertTrue($config->hasKey('positive_integer'));
+ $this->assertTrue($config->hasKey('negative_integer'));
+ $this->assertTrue($config->hasKey('positive_boolean'));
+ $this->assertTrue($config->hasKey('negative_boolean'));
+ $this->assertFalse($config->hasKey('some_other_key'));
+ $this->assertEquals('test_value', $config->getKey('random_string'));
+ $this->assertEquals(42, $config->getKey('positive_integer'));
+ $this->assertEquals(-256, $config->getKey('negative_integer'));
+ $this->assertTrue($config->getKey('positive_boolean'));
+ $this->assertFalse($config->getKey('negative_boolean'));
+ $this->setExpectedException('OutOfRangeException',
+ 'The configuration key some_other_key does not exist.');
+ $this->assertFalse($config->getKey('some_other_key'));
+ }
+}
diff --git a/tests/config_file.php b/tests/config_file.php
new file mode 100644
index 0000000..e713995
--- /dev/null
+++ b/tests/config_file.php
@@ -0,0 +1,24 @@
+
+ * @license GPL-2.0+
+ * @link http://www.brightnucleus.com/
+ * @copyright 2015-2016 Alain Schlesser, Bright Nucleus
+ */
+
+namespace BrightNucleus\Core;
+
+$test_data = [
+
+ 'random_string' => 'test_value',
+ 'positive_integer' => 42,
+ 'negative_integer' => -256,
+ 'positive_boolean' => true,
+ 'negative_boolean' => false,
+
+];
+
+return $test_data;
\ No newline at end of file
diff --git a/tests/dummy_file.txt b/tests/dummy_file.txt
new file mode 100644
index 0000000..5d56665
--- /dev/null
+++ b/tests/dummy_file.txt
@@ -0,0 +1,3 @@
+# Readme File to force an exception from Config constructor
+
+Just some random content.