├── .gitignore
├── tests
├── Dflydev
│ └── DotAccessConfiguration
│ │ ├── fixtures
│ │ ├── yamlFileConfigurationBuilderTest-testBuilder-import-level1.yml
│ │ ├── yamlFileConfigurationBuilderTest-testBuilder-import-level0.yml
│ │ └── yamlFileConfigurationBuilderTest-testBuilder.yml
│ │ ├── ConfigurationFactoryTest.php
│ │ ├── PlaceholderResolverFactoryTest.php
│ │ ├── YamlConfigurationBuilderTest.php
│ │ ├── ConfigurationDataSourceTest.php
│ │ ├── YamlFileConfigurationBuilderTest.php
│ │ ├── AbstractConfigurationBuilderTest.php
│ │ └── ConfigurationTest.php
└── bootstrap.php
├── .travis.yml
├── phpcs.xml.dist
├── phpunit.xml.dist
├── src
└── Dflydev
│ └── DotAccessConfiguration
│ ├── ConfigurationBuilderInterface.php
│ ├── ConfigurationFactoryInterface.php
│ ├── ConfigurationFactory.php
│ ├── Configuration.php
│ ├── PlaceholderResolverFactoryInterface.php
│ ├── PlaceholderResolverFactory.php
│ ├── AbstractPlaceholderResolverFactory.php
│ ├── YamlConfigurationBuilder.php
│ ├── ConfigurationDataSource.php
│ ├── YamlFileConfigurationBuilder.php
│ ├── ConfigurationInterface.php
│ ├── AbstractConfigurationBuilder.php
│ └── AbstractConfiguration.php
├── LICENSE
├── .github
├── CONTRIBUTING.md
└── workflows
│ └── tests.yml
├── composer.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .phpunit.result.cache
2 | .phpcs-cache
3 | composer.lock
4 | vendor
5 |
--------------------------------------------------------------------------------
/tests/Dflydev/DotAccessConfiguration/fixtures/yamlFileConfigurationBuilderTest-testBuilder-import-level1.yml:
--------------------------------------------------------------------------------
1 | a1:
2 | b1:
3 | c1: C1
--------------------------------------------------------------------------------
/tests/Dflydev/DotAccessConfiguration/fixtures/yamlFileConfigurationBuilderTest-testBuilder-import-level0.yml:
--------------------------------------------------------------------------------
1 | imports:
2 | - yamlFileConfigurationBuilderTest-testBuilder-import-level1.yml
3 |
4 | a0:
5 | b0:
6 | c0: C0
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.3.3
5 | - 5.3
6 | - 5.4
7 |
8 | before_script:
9 | - wget -nc http://getcomposer.org/composer.phar
10 | - php composer.phar install --dev
11 |
12 | script: phpunit --coverage-text --verbose
13 |
--------------------------------------------------------------------------------
/tests/Dflydev/DotAccessConfiguration/fixtures/yamlFileConfigurationBuilderTest-testBuilder.yml:
--------------------------------------------------------------------------------
1 | imports:
2 | - yamlFileConfigurationBuilderTest-testBuilder-import-level0.yml
3 | - /tmp/testing-this-file-should-not-exist.yml
4 |
5 | a:
6 | b:
7 | c: C
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | src
14 | tests
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ./src/Dflydev/DotAccessConfiguration/
6 |
7 |
8 |
9 |
10 | ./tests/Dflydev/DotAccessConfiguration
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/Dflydev/DotAccessConfiguration/ConfigurationBuilderInterface.php:
--------------------------------------------------------------------------------
1 | importRaw($config);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tests/Dflydev/DotAccessConfiguration/ConfigurationFactoryTest.php:
--------------------------------------------------------------------------------
1 | create();
22 |
23 | $this->assertNotNull($configuration);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Dflydev/DotAccessConfiguration/PlaceholderResolverFactoryInterface.php:
--------------------------------------------------------------------------------
1 | getMockBuilder(\Dflydev\DotAccessConfiguration\Configuration::class)->getMock();
21 | $placeholderResolverFactory = new PlaceholderResolverFactory();
22 | $placeholderResolver = $placeholderResolverFactory->create($configuration);
23 |
24 | $this->assertNotNull($placeholderResolver);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012 Dragonfly Development Inc.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is furnished
8 | to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/src/Dflydev/DotAccessConfiguration/AbstractPlaceholderResolverFactory.php:
--------------------------------------------------------------------------------
1 | createInternal($configuration, new ConfigurationDataSource($configuration));
24 | }
25 |
26 | /**
27 | * Internal create
28 | *
29 | * @param ConfigurationInterface $configuration
30 | * @param DataSourceInterface $dataSource
31 | *
32 | * @return PlaceholderResolverInterface
33 | */
34 | abstract protected function createInternal(ConfigurationInterface $configuration, DataSourceInterface $dataSource);
35 | }
36 |
--------------------------------------------------------------------------------
/tests/Dflydev/DotAccessConfiguration/YamlConfigurationBuilderTest.php:
--------------------------------------------------------------------------------
1 | markTestSkipped('The Symfony2 YAML library is not available');
22 | }
23 |
24 | $configurationBuilder = new YamlConfigurationBuilder();
25 | $configuration = $configurationBuilder->build();
26 |
27 | $this->assertNotNull($configuration);
28 | }
29 |
30 | public function testBuildWithData()
31 | {
32 | if (!class_exists('Symfony\Component\Yaml\Yaml')) {
33 | $this->markTestSkipped('The Symfony2 YAML library is not available');
34 | }
35 |
36 | $configurationBuilder = new YamlConfigurationBuilder('foo: bar');
37 | $configuration = $configurationBuilder->build();
38 |
39 | $this->assertNotNull($configuration);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/tests/Dflydev/DotAccessConfiguration/ConfigurationDataSourceTest.php:
--------------------------------------------------------------------------------
1 | getMockBuilder(\Dflydev\DotAccessConfiguration\Configuration::class)->getMock();
21 |
22 | $configuration
23 | ->expects($this->any())
24 | ->method('getRaw')
25 | ->will($this->returnValueMap(array(
26 | array('foo', 'bar'),
27 | array('foo', null, true),
28 | array('foo', 'bar', false),
29 | )))
30 | ;
31 |
32 | $dataSource = new ConfigurationDataSource($configuration);
33 |
34 | $this->assertEquals('bar', $dataSource->get('foo'));
35 | $this->assertTrue($dataSource->exists('foo'));
36 | $this->assertEquals('bar', $dataSource->get('foo', false));
37 | $this->assertTrue($dataSource->exists('foo', false));
38 | $this->assertNull($dataSource->get('foo', true));
39 | $this->assertFalse($dataSource->exists('foo', true));
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/tests/Dflydev/DotAccessConfiguration/YamlFileConfigurationBuilderTest.php:
--------------------------------------------------------------------------------
1 | markTestSkipped('The Symfony2 YAML library is not available');
23 | }
24 |
25 | $configurationBuilder = new YamlFileConfigurationBuilder(array(__DIR__ . '/fixtures/yamlFileConfigurationBuilderTest-testBuilder.yml'));
26 |
27 | $configuration = $configurationBuilder->build();
28 |
29 | $this->assertEquals('C', $configuration->get('a.b.c'));
30 | $this->assertEquals('C0', $configuration->get('a0.b0.c0'));
31 | $this->assertEquals('C1', $configuration->get('a1.b1.c1'));
32 | $this->assertEquals(array(
33 | 'yamlFileConfigurationBuilderTest-testBuilder-import-level0.yml',
34 | '/tmp/testing-this-file-should-not-exist.yml',
35 | 'yamlFileConfigurationBuilderTest-testBuilder-import-level1.yml',
36 | ), $configuration->get('imports'));
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Contributions are **welcome** and will be fully **credited**. We accept contributions via Pull Requests on [GitHub](https://github.com/dflydev/dflydev-dot-access-configuration).
4 |
5 | ## Pull Requests
6 |
7 | - **[PSR-12 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-12-extended-coding-style-guide.md)** - The easiest way to apply the conventions is to run `./vendor/bin/phpcbf`.
8 |
9 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests.
10 |
11 | - **Tests must pass** - All automated tests, including things like code style checks, but be passing before we'll consider merging the change.
12 |
13 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date.
14 |
15 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option.
16 |
17 | - **Create feature branches** - Don't ask us to pull from your default branch.
18 |
19 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.
20 |
21 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please squash them before submitting.
22 |
23 |
24 | ## Running Tests
25 |
26 | ``` bash
27 | $ composer test
28 | ```
29 |
30 |
31 | **Happy coding**!
32 |
--------------------------------------------------------------------------------
/src/Dflydev/DotAccessConfiguration/YamlConfigurationBuilder.php:
--------------------------------------------------------------------------------
1 | input = $input;
34 | }
35 |
36 | /**
37 | * {@inheritdocs}
38 | */
39 | public function internalBuild(ConfigurationInterface $configuration)
40 | {
41 | if (null !== $this->input) {
42 | try {
43 | $yml = Yaml::parse($this->input, Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE);
44 | } catch (\Exception $e) {
45 | throw new InvalidArgumentException($e->getMessage(), 0, $e);
46 | }
47 | if (is_string($yml)) {
48 | throw(new \InvalidArgumentException('Yaml could not be parsed, parser detected a string.'));
49 | }
50 | $configuration->importRaw($yml);
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dflydev/dot-access-configuration",
3 | "type": "library",
4 | "description": "Given a deep data structure representing a configuration, access configuration by dot notation.",
5 | "homepage": "https://github.com/dflydev/dflydev-dot-access-configuration",
6 | "keywords": ["config", "configuration"],
7 | "license": "MIT",
8 | "authors": [
9 | {
10 | "name": "Dragonfly Development Inc.",
11 | "email": "info@dflydev.com",
12 | "homepage": "http://dflydev.com"
13 | },
14 | {
15 | "name": "Beau Simensen",
16 | "email": "beau@dflydev.com",
17 | "homepage": "http://beausimensen.com"
18 | }
19 | ],
20 | "require": {
21 | "php": ">=7.4",
22 | "dflydev/dot-access-data": "^3",
23 | "dflydev/placeholder-resolver": "1.*"
24 | },
25 | "require-dev": {
26 | "symfony/yaml": "^5.4 || ^6.0",
27 | "phpunit/phpunit": "^9.3",
28 | "squizlabs/php_codesniffer": "^3.5"
29 | },
30 | "suggest": {
31 | "symfony/yaml": "Required for using the YAML Configuration Builders"
32 | },
33 | "autoload": {
34 | "psr-0": {
35 | "Dflydev\\DotAccessConfiguration": "src"
36 | }
37 | },
38 | "scripts": {
39 | "style:fix": "@php ./vendor/bin/phpcsf",
40 | "style:check": "@php ./vendor/bin/phpcs",
41 | "test" : "@php ./vendor/bin/phpunit"
42 | },
43 | "extra": {
44 | "branch-alias": {
45 | "dev-master": "2.0-dev"
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 |
3 | on:
4 | push: ~
5 | pull_request: ~
6 |
7 | jobs:
8 |
9 | phpunit:
10 | name: PHPUnit on ${{ matrix.php }} ${{ matrix.composer-flags }}
11 | runs-on: ubuntu-latest
12 | strategy:
13 | matrix:
14 | php: ['7.4', '8.0', '8.1']
15 |
16 | steps:
17 | - name: Checkout
18 | uses: actions/checkout@v3
19 | with:
20 | fetch-depth: 1
21 |
22 | - name: Setup PHP
23 | uses: shivammathur/setup-php@v2
24 | with:
25 | php-version: ${{ matrix.php }}
26 | extensions: curl
27 | coverage: pcov
28 |
29 | - name: Install Composer dependencies
30 | uses: ramsey/composer-install@v2
31 | with:
32 | composer-options: "--no-progress --prefer-dist --optimize-autoloader"
33 |
34 | - name: Configure matchers
35 | uses: mheap/phpunit-matcher-action@v1
36 |
37 | - name: PHPUnit
38 | run: composer run test -- --coverage-text --teamcity
39 |
40 | phpcs:
41 | name: phpcs
42 | runs-on: ubuntu-latest
43 |
44 | steps:
45 | - name: Checkout
46 | uses: actions/checkout@v3
47 | with:
48 | fetch-depth: 1
49 |
50 | - name: Set up PHP
51 | uses: shivammathur/setup-php@v2
52 | with:
53 | php-version: 8.1
54 | tools: cs2pr
55 |
56 | - name: Install Composer dependencies
57 | uses: ramsey/composer-install@v2
58 | with:
59 | composer-options: "--no-progress --prefer-dist --optimize-autoloader"
60 |
61 | - name: phpcs
62 | run: composer run style:check -- -q --report=checkstyle | cs2pr
63 |
--------------------------------------------------------------------------------
/src/Dflydev/DotAccessConfiguration/ConfigurationDataSource.php:
--------------------------------------------------------------------------------
1 | setConfiguration($configuration);
28 | }
29 |
30 | /**
31 | * {@inheritdoc}
32 | */
33 | public function exists($key, $system = false)
34 | {
35 | if ($system) {
36 | return false;
37 | }
38 |
39 | return null !== $this->configuration->getRaw($key);
40 | }
41 |
42 | /**
43 | * {@inheritdoc}
44 | */
45 | public function get($key, $system = false)
46 | {
47 | if ($system) {
48 | return null;
49 | }
50 |
51 | return $this->configuration->getRaw($key);
52 | }
53 |
54 | /**
55 | * Set Configuration
56 | *
57 | * @param ConfigurationInterface $configuration
58 | *
59 | * @return ConfigurationDataSource
60 | */
61 | public function setConfiguration(ConfigurationInterface $configuration)
62 | {
63 | $this->configuration = $configuration;
64 |
65 | return $this;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/Dflydev/DotAccessConfiguration/YamlFileConfigurationBuilder.php:
--------------------------------------------------------------------------------
1 | yamlConfigurationFilenames = $yamlConfigurationFilenames;
34 | }
35 |
36 | /**
37 | * {@inheritdocs}
38 | */
39 | public function internalBuild(ConfigurationInterface $configuration)
40 | {
41 | $config = array();
42 | $imports = array();
43 | foreach ($this->yamlConfigurationFilenames as $yamlConfigurationFilename) {
44 | if (file_exists($yamlConfigurationFilename)) {
45 | $config = DotAccessDataUtil::mergeAssocArray($config, Yaml::parse(file_get_contents($yamlConfigurationFilename)));
46 | if (isset($config['imports'])) {
47 | foreach ((array) $config['imports'] as $file) {
48 | if (0 === strpos($file, '/')) {
49 | // Absolute path
50 | $imports[] = $file;
51 | } else {
52 | if ($realpath = realpath(dirname($yamlConfigurationFilename) . '/' . $file)) {
53 | $imports[] = $realpath;
54 | }
55 | }
56 | }
57 | }
58 | }
59 | }
60 |
61 | if ($imports) {
62 | $importsBuilder = new static($imports);
63 |
64 | // We want to reconfigure the imports builder to have the
65 | // same basic configuration as this instance.
66 | $this->reconfigure($importsBuilder);
67 |
68 | $configuration->import($importsBuilder->build());
69 |
70 | $internalImports = $configuration->get('imports');
71 | } else {
72 | $internalImports = null;
73 | }
74 |
75 | $configuration->importRaw($config);
76 |
77 | if ($internalImports) {
78 | foreach ((array) $internalImports as $import) {
79 | $configuration->append('imports', $import);
80 | }
81 | }
82 |
83 | return $configuration;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/Dflydev/DotAccessConfiguration/ConfigurationInterface.php:
--------------------------------------------------------------------------------
1 | configurationFactory = $configurationFactory;
29 |
30 | return $this;
31 | }
32 |
33 | /**
34 | * Configuration Factory
35 | *
36 | * @return ConfigurationFactoryInterface
37 | */
38 | protected function configurationFactory()
39 | {
40 | if (null === $this->configurationFactory) {
41 | $this->configurationFactory = new ConfigurationFactory();
42 | }
43 |
44 | return $this->configurationFactory;
45 | }
46 |
47 | /**
48 | * {@inheritdocs}
49 | */
50 | public function build()
51 | {
52 | $configuration = $this->configurationFactory()->create();
53 |
54 | if (null !== $this->placeholderResolverFactory) {
55 | $placeholderResolver = $this->placeholderResolverFactory->create($configuration);
56 | $configuration->setPlaceholderResolver($placeholderResolver);
57 | }
58 |
59 | $this->internalBuild($configuration);
60 |
61 | return $configuration;
62 | }
63 |
64 | /**
65 | * Set Placeholder Resolver Factory
66 | *
67 | * @param PlaceholderResolverFactoryInterface $placeholderResolverFactory
68 | */
69 | public function setPlaceholderResolverFactory(PlaceholderResolverFactoryInterface $placeholderResolverFactory)
70 | {
71 | $this->placeholderResolverFactory = $placeholderResolverFactory;
72 | }
73 |
74 | /**
75 | * Called to reconfigure the specified Configuration Builder to be similar to this instance
76 | *
77 | * @param AbstractConfigurationBuilder $configurationBuilder
78 | */
79 | public function reconfigure(AbstractConfigurationBuilder $configurationBuilder)
80 | {
81 | if (null !== $this->placeholderResolverFactory) {
82 | $configurationBuilder->setPlaceholderResolverFactory($this->placeholderResolverFactory);
83 | }
84 |
85 | $configurationBuilder->setConfigurationFactory($this->configurationFactory());
86 | }
87 |
88 | /**
89 | * Internal build
90 | *
91 | * @param ConfigurationInterface $configuration
92 | */
93 | abstract protected function internalBuild(ConfigurationInterface $configuration);
94 | }
95 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Dot Access Configuration
2 |
3 | Given a deep data structure representing a configuration, access
4 | configuration by dot notation.
5 |
6 | This library combines [dflydev/dot-access-data](https://github.com/dflydev/dflydev-dot-access-data)
7 | and [dflydev/placeholder-resolver](https://github.com/dflydev/dflydev-placeholder-resolver)
8 | to provide a complete configuration solution.
9 |
10 | ## Requirements
11 |
12 | * PHP (5.3+)
13 | * [dflydev/dot-access-data](https://github.com/dflydev/dflydev-dot-access-data) (^3)
14 | * [dflydev/placeholder-resolver](https://github.com/dflydev/dflydev-placeholder-resolver) (1.x)
15 | * [symfony/yaml](https://github.com/symfony/Yaml) (>2,<2.2) *(suggested)*
16 |
17 | ## Usage
18 |
19 | Generally one will use an implementation of `ConfigurationBuilderInterface`
20 | to build `ConfigurationInterface` instances. For example, to build a Configuration
21 | out of a YAML file, one would use the `YamlFileConfigurationBuilder`:
22 |
23 | ```php
24 | use Dflydev\DotAccessConfiguration\YamlFileConfigurationBuilder;
25 |
26 | $configurationBuilder = new YamlFileConfigurationBuilder('config/config.yml');
27 | $configuration = $configurationBuilder->build();
28 | ```
29 |
30 | Once created, the Configuration instance behaves similarly to a Data
31 | instance from [dflydev/dot-access-data](https://github.com/dflydev/dflydev-dot-access-data).
32 |
33 | ```php
34 | $configuration->set('a.b.c', 'ABC');
35 | $configuration->get('a.b.c');
36 | $configuration->set('a.b.e', array('A', 'B', 'C'));
37 | $configuration->append('a.b.e', 'D');
38 | ```
39 |
40 | ## Custom Configurations
41 |
42 | Configuration Builders use Configuration Factories and Placeholder Resolver
43 | Factories behind the scenes in order to build a working configuration.
44 |
45 | Under normal circumstances one should not need to do anything with the
46 | Placeholder Resolver Factory. However, one may wish to extend the
47 | default `Configuration` class or use an entirely different implementation
48 | altogether.
49 |
50 | In order to build instances of custom `ConfigurationInterface` implementations
51 | with the standard builders, one would need to implement
52 | `ConfigurationFactoryInterface` and inject it into any
53 | `ConfigurationBuilderInterface`.
54 |
55 | If a Configuration is declared as follows:
56 | ```php
57 | namespace MyProject;
58 |
59 | use Dflydev\DotAccessConfiguration\Configuration;
60 |
61 | class MyConf extends Configuration
62 | {
63 | public function someSpecialMethod()
64 | {
65 | // Whatever you want here.
66 | }
67 | }
68 | ```
69 |
70 | Create the following factory:
71 | ```php
72 | namespace MyProject;
73 |
74 | use Dflydev\DotAccessConfiguration\ConfigurationFactoryInterface;
75 |
76 | class MyConfFactory implements ConfigurationFactoryInterface
77 | {
78 | /**
79 | * {@inheritdocs}
80 | */
81 | public function create()
82 | {
83 | return new MyConf;
84 | }
85 | }
86 | ```
87 |
88 | To use the factory with any builder, inject it as follows:
89 | ```php
90 | use Dflydev\DotAccessConfiguration\YamlFileConfigurationBuilder;
91 | use MyProject\MyConfFactory;
92 |
93 | $configurationBuilder = new YamlFileConfigurationBuilder('config/config.yml');
94 |
95 | // Inject your custom Configuration Factory
96 | $configurationBuilder->setConfigurationFactory(new MyConfFactory);
97 |
98 | // Will now build instances of MyConfFactory instead of
99 | // the standard Configuration implementation.
100 | $configuration = $configurationBuilder->build();
101 | ```
102 |
103 | ## License
104 |
105 | This library is licensed under the New BSD License - see the LICENSE file
106 | for details.
107 |
108 | ## Community
109 |
110 | If you have questions or want to help out, join us in the
111 | [#dflydev](irc://irc.freenode.net/#dflydev) channel on irc.freenode.net.
112 |
--------------------------------------------------------------------------------
/tests/Dflydev/DotAccessConfiguration/AbstractConfigurationBuilderTest.php:
--------------------------------------------------------------------------------
1 | getMockBuilder(\Dflydev\PlaceholderResolver\PlaceholderResolverInterface::class)->getMock();
21 |
22 | $placeholderResolverFactory = $this->getMockBuilder(\Dflydev\DotAccessConfiguration\PlaceholderResolverFactoryInterface::class)->getMock();
23 | $placeholderResolverFactory
24 | ->expects($this->once())
25 | ->method('create')
26 | ->will($this->returnValue($placeholderResolver))
27 | ;
28 |
29 | $configurationBuilder = $this->getMockForAbstractClass(\Dflydev\DotAccessConfiguration\AbstractConfigurationBuilder::class);
30 | $configurationBuilder
31 | ->expects($this->once())
32 | ->method('internalBuild');
33 |
34 | $configurationBuilder->setPlaceholderResolverFactory($placeholderResolverFactory);
35 | $configurationBuilder->build();
36 | }
37 |
38 | public function testReconfigure()
39 | {
40 | $configuration000 = $this->getMockBuilder(\Dflydev\DotAccessConfiguration\ConfigurationInterface::class)->getMock();
41 |
42 | $configuration000
43 | ->expects($this->exactly(2))
44 | ->method('get')
45 | ->with($this->equalTo('foo'))
46 | ->will($this->returnValue('FOO'))
47 | ;
48 |
49 | $configuration001 = $this->getMockBuilder(\Dflydev\DotAccessConfiguration\ConfigurationInterface::class)->getMock();
50 |
51 | $configuration001
52 | ->expects($this->exactly(2))
53 | ->method('get')
54 | ->with($this->equalTo('bar'))
55 | ->will($this->returnValue('BAR'))
56 | ;
57 |
58 | $placeholderResolver = $this->getMockBuilder(\Dflydev\PlaceholderResolver\PlaceholderResolverInterface::class)->getMock();
59 |
60 | $placeholderResolverFactory = $this->getMockBuilder(\Dflydev\DotAccessConfiguration\PlaceholderResolverFactoryInterface::class)->getMock();
61 | $placeholderResolverFactory
62 | ->expects($this->exactly(2))
63 | ->method('create')
64 | ->will($this->returnValue($placeholderResolver))
65 | ;
66 |
67 | $configurationFactory = $this->getMockBuilder(\Dflydev\DotAccessConfiguration\ConfigurationFactoryInterface::class)->getMock();
68 | $configurationFactory
69 | ->expects($this->exactly(2))
70 | ->method('create')
71 | ->will($this->onConsecutiveCalls($configuration000, $configuration001));
72 | ;
73 |
74 | $configurationBuilder = $this->getMockForAbstractClass('Dflydev\DotAccessConfiguration\AbstractConfigurationBuilder');
75 |
76 | $configurationBuilder->setPlaceholderResolverFactory($placeholderResolverFactory);
77 | $configurationBuilder->setConfigurationFactory($configurationFactory);
78 |
79 | $reconfiguredConfigurationBuilder = $this->getMockForAbstractClass('Dflydev\DotAccessConfiguration\AbstractConfigurationBuilder');
80 | $configurationBuilder->reconfigure($reconfiguredConfigurationBuilder);
81 |
82 | $configurationTest000 = $configurationBuilder->build();
83 | $configurationTest001 = $reconfiguredConfigurationBuilder->build();
84 |
85 | $this->assertEquals('FOO', $configuration000->get('foo'));
86 | $this->assertEquals('FOO', $configurationTest000->get('foo'));
87 | $this->assertEquals('BAR', $configuration001->get('bar'));
88 | $this->assertEquals('BAR', $configurationTest001->get('bar'));
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/Dflydev/DotAccessConfiguration/AbstractConfiguration.php:
--------------------------------------------------------------------------------
1 | data()->get($key);
33 | } catch (MissingPathException $e) {
34 | return null;
35 | }
36 | }
37 |
38 | /**
39 | * {@inheritdocs}
40 | */
41 | public function get($key)
42 | {
43 | $value = $this->getRaw($key);
44 | if (is_object($value)) {
45 | return $value;
46 | }
47 | $this->resolveValues($value);
48 |
49 | return $value;
50 | }
51 |
52 | /**
53 | * {@inheritdocs}
54 | */
55 | public function set($key, $value = null)
56 | {
57 | $this->exportIsDirty = true;
58 |
59 | return $this->data()->set($key, $value);
60 | }
61 |
62 | /**
63 | * {@inheritdocs}
64 | */
65 | public function append($key, $value = null)
66 | {
67 | $this->exportIsDirty = true;
68 |
69 | return $this->data()->append($key, $value);
70 | }
71 |
72 | /**
73 | * {@inheritdocs}
74 | */
75 | public function exportRaw()
76 | {
77 | return $this->data()->export();
78 | }
79 |
80 | /**
81 | * {@inheritdocs}
82 | */
83 | public function export()
84 | {
85 | if ($this->exportIsDirty) {
86 | $this->resolvedExport = $this->data()->export();
87 | $this->resolveValues($this->resolvedExport);
88 | $this->exportIsDirty = false;
89 | }
90 |
91 | return $this->resolvedExport;
92 | }
93 |
94 | /**
95 | * {@inheritdocs}
96 | */
97 | public function exportData()
98 | {
99 | return new Data($this->export());
100 | }
101 |
102 | /**
103 | * {@inheritdocs}
104 | */
105 | public function importRaw($imported = null, $clobber = true)
106 | {
107 | $this->exportIsDirty = true;
108 |
109 | if (null !== $imported) {
110 | $this->data()->import($imported, $clobber);
111 | }
112 | }
113 |
114 | /**
115 | * {@inheritdocs}
116 | */
117 | public function import(ConfigurationInterface $imported, $clobber = true)
118 | {
119 | return $this->importRaw($imported->exportRaw(), $clobber);
120 | }
121 |
122 | /**
123 | * {@inheritdocs}
124 | */
125 | public function resolve($value = null)
126 | {
127 | if (null === $value) {
128 | return null;
129 | }
130 |
131 | return $this->placeholderResolver()->resolvePlaceholder($value);
132 | }
133 |
134 | /**
135 | * {@inheritdocs}
136 | */
137 | public function setPlaceholderResolver(PlaceholderResolverInterface $placeholderResolver)
138 | {
139 | $this->placeholderResolver = $placeholderResolver;
140 |
141 | return $this;
142 | }
143 |
144 | /**
145 | * Resolve values
146 | *
147 | * For objects, do nothing. For strings, resolve placeholder.
148 | * For arrays, call resolveValues() on each item.
149 | *
150 | * @param mixed $input
151 | */
152 | protected function resolveValues(&$input = null)
153 | {
154 | if (is_array($input)) {
155 | foreach ($input as $idx => $value) {
156 | $this->resolveValues($value);
157 | $input[$idx] = $value;
158 | }
159 | } else {
160 | if (!is_object($input)) {
161 | $input = $this->placeholderResolver()->resolvePlaceholder($input);
162 | }
163 | }
164 | }
165 |
166 | /**
167 | * Data
168 | *
169 | * @return Data
170 | */
171 | protected function data()
172 | {
173 | if (null === $this->data) {
174 | $this->data = new Data();
175 | }
176 |
177 | return $this->data;
178 | }
179 |
180 | /**
181 | * Placeholder Resolver
182 | *
183 | * @return PlaceholderResolverInterface
184 | */
185 | protected function placeholderResolver()
186 | {
187 | if (null === $this->placeholderResolver) {
188 | $this->placeholderResolver = new RegexPlaceholderResolver(new ConfigurationDataSource($this), '%', '%');
189 | }
190 |
191 | return $this->placeholderResolver;
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/tests/Dflydev/DotAccessConfiguration/ConfigurationTest.php:
--------------------------------------------------------------------------------
1 | key = $key;
26 | }
27 | };
28 |
29 | return array(
30 | 'a' => array(
31 | 'b' => array(
32 | 'c' => 'ABC',
33 | ),
34 | ),
35 | 'abc' => '%a.b.c%',
36 | 'abcd' => '%a.b.c.d%',
37 | 'some' => array(
38 | 'object' => new $configurationTestObjectClass('some.object'),
39 | 'other' => array(
40 | 'object' => new $configurationTestObjectClass('some.other.object'),
41 | ),
42 | ),
43 | 'object' => new $configurationTestObjectClass('object'),
44 | 'an_array' => array('hello'),
45 | );
46 | }
47 |
48 | protected function runBasicTests($configuration)
49 | {
50 | $this->assertEquals('ABC', $configuration->get('a.b.c'), 'Direct access by dot notation');
51 | $this->assertEquals('ABC', $configuration->get('abc'), 'Resolved access');
52 | $this->assertEquals('%a.b.c.d%', $configuration->get('abcd'), 'Unresolved access');
53 | $this->assertEquals('object', $configuration->get('object')->key);
54 | $this->assertEquals('some.object', $configuration->get('some.object')->key);
55 | $this->assertEquals('some.other.object', $configuration->get('some.other.object')->key);
56 | $this->assertEquals(array('hello'), $configuration->get('an_array'));
57 | $this->assertEquals('This is ABC', $configuration->resolve('This is %a.b.c%'));
58 | $this->assertNull($configuration->resolve());
59 | }
60 |
61 | public function testGet()
62 | {
63 | $configuration = new Configuration($this->getTestData());
64 |
65 | $this->runBasicTests($configuration);
66 | }
67 |
68 | public function testAppend()
69 | {
70 | $configuration = new Configuration($this->getTestData());
71 |
72 | $configuration->append('a.b.c', 'abc');
73 | $configuration->append('an_array', 'world');
74 |
75 | $this->assertEquals(array('ABC', 'abc'), $configuration->get('a.b.c'));
76 | $this->assertEquals(array('hello', 'world'), $configuration->get('an_array'));
77 | }
78 |
79 | public function testExportRaw()
80 | {
81 | $configuration = new Configuration($this->getTestData());
82 |
83 | // Start with "known" expected value.
84 | $expected = $this->getTestData();
85 |
86 | $this->assertEquals($expected, $configuration->exportRaw());
87 |
88 | // Simulate change on an object to ensure that objects
89 | // are being handled correctly.
90 | $expected['object']->key = 'object (modified)';
91 |
92 | // Make the same change in the object that the
93 | // configuration is managing.
94 | $configuration->get('object')->key = 'object (modified)';
95 |
96 | $this->assertEquals($expected, $configuration->exportRaw());
97 | }
98 |
99 | public function testExport()
100 | {
101 | $configuration = new Configuration($this->getTestData());
102 |
103 | // Start with "known" expected value.
104 | $expected = $this->getTestData();
105 |
106 | // We have one replacement that is expected to happen.
107 | // It should be represented in the export as the
108 | // resolved value!
109 | $expected['abc'] = 'ABC';
110 |
111 | $this->assertEquals($expected, $configuration->export());
112 |
113 | // Simulate change on an object to ensure that objects
114 | // are being handled correctly.
115 | $expected['object']->key = 'object (modified)';
116 |
117 | // Make the same change in the object that the
118 | // configuration is managing.
119 | $configuration->get('object')->key = 'object (modified)';
120 |
121 | $this->assertEquals($expected, $configuration->export());
122 |
123 | // Test to make sure that set will result in setting
124 | // a new value and also that export will show this new
125 | // value. (tests "export is dirty" functionality)
126 | $configuration->set('abc', 'ABCD');
127 | $expected['abc'] = 'ABCD';
128 | $this->assertEquals($expected, $configuration->export());
129 | }
130 |
131 | public function testExportData()
132 | {
133 | $configuration = new Configuration($this->getTestData());
134 |
135 | $data = $configuration->exportData();
136 |
137 | // The exportData call should return data filled with
138 | // resolved data.
139 | $this->assertEquals('ABC', $data->get('abc'));
140 | }
141 |
142 | public function testImportRaw()
143 | {
144 | $configuration = new Configuration();
145 |
146 | $configuration->importRaw($this->getTestData());
147 |
148 | $this->runBasicTests($configuration);
149 | }
150 |
151 | public function testImport()
152 | {
153 | $configuration = new Configuration();
154 |
155 | $configuration->import(new Configuration($this->getTestData()));
156 |
157 | $this->runBasicTests($configuration);
158 | }
159 |
160 | public function testSetPlaceholderResolver()
161 | {
162 | $placeholderResolver = $this->getMockBuilder(\Dflydev\PlaceholderResolver\PlaceholderResolverInterface::class)->getMock();
163 |
164 | $placeholderResolver
165 | ->expects($this->once())
166 | ->method('resolvePlaceholder')
167 | ->with($this->equalTo('foo'))
168 | ->will($this->returnValue('bar'))
169 | ;
170 |
171 | $configuration = new Configuration();
172 |
173 | $configuration->setPlaceholderResolver($placeholderResolver);
174 |
175 | $this->assertEquals('bar', $configuration->resolve('foo'));
176 | }
177 | }
178 |
--------------------------------------------------------------------------------