├── .gitignore
├── .php_cs
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── UPGRADE.md
├── composer.json
├── phpunit.xml
├── src
├── ApplicationConfig.php
├── ApplicationConfigIterator.php
├── Configurator.php
├── ContainerAdapter.php
├── ContainerAdapterFactory.php
├── Exception
│ ├── EntryDoesNotExistException.php
│ ├── Exception.php
│ ├── InvalidConfigException.php
│ ├── MissingDependencyException.php
│ ├── NoMatchingFilesException.php
│ ├── NotClassDefinitionException.php
│ ├── NotContainerAdapterException.php
│ ├── ReadOnlyException.php
│ ├── UnknownContainerException.php
│ ├── UnknownFileTypeException.php
│ └── UnknownSettingException.php
├── FileReader
│ ├── FileLocator.php
│ ├── FileReader.php
│ ├── JSONFileReader.php
│ ├── PHPFileReader.php
│ ├── ReaderFactory.php
│ └── YAMLFileReader.php
├── InflectorConfig.php
├── InflectorDefinition.php
├── League
│ ├── ApplicationConfigServiceProvider.php
│ ├── InflectorServiceProvider.php
│ ├── LeagueContainerAdapter.php
│ └── ServiceServiceProvider.php
├── Pimple
│ └── PimpleContainerAdapter.php
├── ServiceConfig.php
└── ServiceDefinition.php
└── tests
├── acceptance
├── AbstractContainerAdapterTest.php
├── LeagueContainerAdapterTest.php
├── PimpleContainerAdapterTest.php
├── PimpleContainerWrapper.php
├── SupportsApplicationConfig.php
├── SupportsInflectorConfig.php
└── SupportsServiceConfig.php
├── mocks
├── BootableServiceProvider.php
├── ExampleClass.php
├── ExampleClassWithArgs.php
├── ExampleContainer.php
├── ExampleContainerAdapter.php
├── ExampleExtendedContainer.php
├── ExampleFactory.php
├── ExampleInterface.php
├── FileReader
│ └── CustomFileReader.php
└── NotContainerAdapter.php
├── support
└── TestFileCreator.php
└── unit
├── ApplicationConfigIteratorTest.php
├── ApplicationConfigTest.php
├── ConfiguratorTest.php
├── ContainerAdapterFactoryTest.php
├── Exception
├── EntryDoesNotExistExceptionTest.php
├── InvalidConfigExceptionTest.php
├── MissingDependencyExceptionTest.php
├── NoMatchingFilesExceptionTest.php
├── NotClassDefinitionExceptionTest.php
├── NotContainerAdapterExceptionTest.php
├── ReadOnlyExceptionTest.php
├── UnknownContainerExceptionTest.php
├── UnknownFileTypeExceptionTest.php
└── UnknownSettingExceptionTest.php
├── FileReader
├── FileLocatorTest.php
├── JSONFileReaderTest.php
├── PHPFileReaderTest.php
├── ReaderFactoryTest.php
└── YAMLFileReaderTest.php
├── InflectorConfigTest.php
├── InflectorDefinitionTest.php
├── ServiceConfigTest.php
└── ServiceDefinitionTest.php
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 | tests/.test-config
3 | .php_cs.cache
4 | composer.lock
5 |
--------------------------------------------------------------------------------
/.php_cs:
--------------------------------------------------------------------------------
1 | in(__DIR__);
4 |
5 | return PhpCsFixer\Config::create()
6 | ->setRules([
7 | '@PSR2' => true,
8 | 'array_syntax' => [
9 | 'syntax' => 'short',
10 | ],
11 | 'binary_operator_spaces' => [
12 | 'align_double_arrow' => true,
13 | 'align_equals' => true,
14 | ],
15 | 'concat_space' => [
16 | 'spacing' => 'one',
17 | ],
18 | 'function_typehint_space' => true,
19 | 'hash_to_slash_comment' => true,
20 | 'include' => true,
21 | 'lowercase_cast' => true,
22 | 'method_separation' => true,
23 | 'native_function_casing' => true,
24 | 'new_with_braces' => true,
25 | 'no_alias_functions' => true,
26 | 'no_blank_lines_after_class_opening' => true,
27 | 'no_blank_lines_after_phpdoc' => true,
28 | 'no_empty_comment' => true,
29 | 'no_empty_phpdoc' => true,
30 | 'no_empty_statement' => true,
31 | 'no_extra_consecutive_blank_lines' => true,
32 | 'no_leading_import_slash' => true,
33 | 'no_leading_namespace_whitespace' => true,
34 | 'no_multiline_whitespace_around_double_arrow' => true,
35 | 'no_multiline_whitespace_before_semicolons' => true,
36 | 'no_short_bool_cast' => true,
37 | 'no_singleline_whitespace_before_semicolons' => true,
38 | 'no_spaces_around_offset' => true,
39 | 'no_trailing_comma_in_list_call' => true,
40 | 'no_trailing_comma_in_singleline_array' => true,
41 | 'no_unreachable_default_argument_value' => true,
42 | 'no_unused_imports' => true,
43 | 'no_useless_else' => true,
44 | 'no_useless_return' => true,
45 | 'no_whitespace_before_comma_in_array' => true,
46 | 'object_operator_without_whitespace' => true,
47 | 'ordered_imports' => true,
48 | 'phpdoc_align' => true,
49 | 'phpdoc_indent' => true,
50 | 'phpdoc_inline_tag' => true,
51 | 'phpdoc_no_access' => true,
52 | 'phpdoc_no_alias_tag' => [
53 | 'type' => 'var',
54 | ],
55 | 'phpdoc_no_package' => true,
56 | 'phpdoc_order' => true,
57 | 'phpdoc_scalar' => true,
58 | 'phpdoc_separation' => true,
59 | 'phpdoc_single_line_var_spacing' => true,
60 | 'phpdoc_summary' => true,
61 | 'phpdoc_to_comment' => true,
62 | 'phpdoc_trim' => true,
63 | 'phpdoc_types' => true,
64 | 'self_accessor' => true,
65 | 'short_scalar_cast' => true,
66 | 'single_blank_line_before_namespace' => true,
67 | 'single_quote' => true,
68 | 'space_after_semicolon' => true,
69 | 'standardize_not_equals' => true,
70 | 'trailing_comma_in_multiline_array' => true,
71 | 'trim_array_spaces' => true,
72 | 'unary_operator_spaces' => true,
73 | 'whitespace_after_comma_in_array' => true,
74 | ])
75 | ->setRiskyAllowed(true)
76 | ->setUsingCache(true)
77 | ->setFinder($finder);
78 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.6
5 | - 7.0
6 | - 7.1
7 | - hhvm
8 |
9 | cache:
10 | directories:
11 | - $HOME/.composer/cache
12 |
13 | before_install:
14 | - composer self-update
15 | - composer validate
16 |
17 | install:
18 | - composer install --prefer-dist
19 |
20 | script:
21 | - composer test
22 |
23 | matrix:
24 | fast_finish: true
25 | allow_failures:
26 | - php: hhvm
27 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
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 | ## 1.0.0 - 2015-19-31
6 | ### Added
7 | * Inflector support for Pimple.
8 | * Custom file readers with `withFileReader(string $extension, string $readerClass);`.
9 | * Custom container adapters with `withContainerAdapter(string $containerName, string $adapterName);`.
10 |
11 | ## 0.5.2 - 2016-09-30
12 | ### Added
13 | * Service aliases.
14 | * Container injection using `Configurator::container()`.
15 |
16 | ### Fixed
17 | * Non-string arguments can be provided to Pimple services.
18 |
19 | ## 0.5.1 - 2016-09-18
20 | ### Added
21 | * `TomPHP\ContainerConfigurator\FileReader\YAMLFileReader` for reading
22 | YAML files (requires `symfony/yaml` to be installed).
23 | * Service factories.
24 |
25 | ## 0.5.0 - 2016-09-13
26 | ### Added
27 | * `TomPHP\ContainerConfigurator\Configurator` as the main API.
28 | * If `class` is left out of the config for a service, then the service name
29 | is assumed to be the name of the class.
30 | * Services can be set as singleton by default.
31 | * All exceptions implement `TomPHP\ContainerConfigurator\Exception\Exception`.
32 | * Support for Pimple.
33 |
34 | ### Changed
35 | * The composer package has been renamed to `container-configurator`.
36 | * The package namespace has changed to `TomPHP\ContainerConfigurator`.
37 | * Minimum supported PHP version is now `5.6`.
38 | * Exception base-classes have been updated.
39 | * File reader classes have moved to `TomPHP\ContainerConfigurator\FileReader`.
40 |
41 | ### Removed
42 | * `TomPHP\ConfigServiceProvider\ConfigServiceProvider`
43 | * Custom configurable service providers (`TomPHP\ConfigServiceProvider\ConfigurableServiceProvider`).
44 | * Custom sub-providers.
45 | * `TomPHP\ConfigServiceProvider\Exception\RuntimeException`
46 |
47 | ## [0.4.0] - 2016-05-25
48 | ### Added
49 | * Exception thrown when no files are found when using the `fromFiles`
50 | constructor
51 |
52 | ### Changed
53 | * Config containing class names will remain as strings and not be converted to
54 | instances
55 |
56 | ## [0.3.3] - 2015-10-10
57 | ### Added
58 | * Configuring DI via the config
59 |
60 | ## [0.3.2] - 2015-09-24
61 | ### Added
62 | * Reading JSON config files via the `fromFiles` constructor
63 | * Support from braces in file globbing patterns
64 |
65 | ## [0.3.1] - 2015-09-23
66 | ### Added
67 | * Reading config files (PHP and JSON)
68 |
69 | ## [0.3.0] - 2015-09-23
70 | ### Added
71 | * `ConfigServiceProvider::fromConfig()` factory method
72 | * Sub providers
73 |
74 | ### Changed
75 | * `TomPHP\ConfigServiceProvider\InflectorConfigServiceProvider` is
76 | now a sub provider
77 | * Provider classes are marked as final
78 |
79 | ### Removed
80 | * `TomPHP\ConfigServiceProvider\Config` static factory
81 |
82 | ## [0.2.1] - 2015-09-21
83 | ### Added
84 | * Support to set up inflectors via configuration
85 | * `TomPHP\ConfigServiceProvider\Config` - a static class to enable easy setup.
86 |
87 | ## [0.2.0] - 2015-09-03
88 | ### Changed
89 | * Now depends on League Container `^2.0.2`
90 | * `TomPHP\ConfigServiceProvider\ConfigServiceProvider` now extends
91 | `League\Container\ServiceProvider\AbstractServiceProvider`
92 |
93 | ## [0.1.2] - 2014-04-12
94 | ### Added
95 | * Contributing guidelines
96 | * `composer test` to run test suite
97 | * Make sub-arrays accessible directly
98 |
99 | ### Changed
100 | * Make League Container dependency stricter (use `^` version)
101 |
102 | ## [0.1.1] - 2014-04-12
103 | ### Added
104 | * CHANGELOG.md
105 | * Homepage field to composer.json
106 |
107 | ### Fixed
108 | * Typo in README.md
109 |
110 | ## [0.1.0] - 2015-04-12
111 | ### Added
112 | * Service provider for `league/container`
113 | * Support for multiple levels of config
114 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Thank you for considering to contribute to this project.
4 |
5 | ## Submitting Pull Requests
6 |
7 | Please submit your pull requests to the `master` branch.
8 |
9 | ## Tests
10 |
11 | All new features or bug fixes should include supporting tests. To run the test
12 | suite you need to first install the dependencies using composer:
13 |
14 | ```
15 | $ composer install
16 | ```
17 |
18 | Then the tests can be run using the following command:
19 |
20 | ```
21 | $ composer test
22 | ```
23 |
24 | This will also check for the PSR-2 Coding Standard compliance.
25 |
26 | ## Travis
27 |
28 | Once you have submitted a pull request, Travis CI will automatically run the
29 | tests. The tests **must** pass for the PR to be accepted.
30 |
31 | ## Coding Standard
32 |
33 | Please stick PSR-1 and PSR-2 standards - this will be verified by Travis CI:
34 |
35 | * https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md
36 | * https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md
37 |
38 | Also, keep the code tidy and well refactored - don't let methods get too long
39 | or there be too many levels of indentation.
40 |
41 | Happy coding and thank you for contributing.
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Tom Oram
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Container Configurator
2 |
3 | [](https://api.travis-ci.org/tomphp/container-configurator)
4 | [](https://scrutinizer-ci.com/g/tomphp/container-configurator/?branch=master)
5 | [](https://packagist.org/packages/tomphp/container-configurator)
6 | [](https://packagist.org/packages/tomphp/container-configurator)
7 | [](https://packagist.org/packages/tomphp/container-configurator)
8 | [](https://packagist.org/packages/tomphp/container-configurator)
9 |
10 | This package enables you to configure your application and the Dependency
11 | Injection Container (DIC) via config arrays or files. Currently, supported
12 | containers are:
13 |
14 | * [League Of Extraordinary Packages' Container](https://github.com/thephpleague/container)
15 | * [Pimple](http://pimple.sensiolabs.org/)
16 |
17 | ## Installation
18 |
19 | Installation can be done easily using composer:
20 |
21 | ```
22 | $ composer require tomphp/container-configurator
23 | ```
24 |
25 | ## Example Usage
26 |
27 | ```php
28 | [
35 | 'name' => 'example_db',
36 | 'username' => 'dbuser',
37 | 'password' => 'dbpass',
38 | ],
39 | 'di' => [
40 | 'services' => [
41 | 'database_connection' => [
42 | 'class' => DatabaseConnection::class,
43 | 'arguments' => [
44 | 'config.db.name',
45 | 'config.db.username',
46 | 'config.db.password',
47 | ],
48 | ],
49 | ],
50 | ],
51 | ];
52 |
53 | $container = new Container();
54 | Configurator::apply()->configFromArray($config)->to($container);
55 |
56 | $db = $container->get('database_connection');
57 | ```
58 |
59 | ## Reading Files From Disk
60 |
61 | Instead of providing the config as an array, you can also provide a list of
62 | file pattern matches to the `fromFiles` function.
63 |
64 | ```php
65 | Configurator::apply()
66 | ->configFromFile('config_dir/config.global.php')
67 | ->configFromFiles('json_dir/*.json')
68 | ->configFromFiles('config_dir/*.local.php')
69 | ->to($container);
70 | ```
71 |
72 | `configFromFile(string $filename)` reads config in from a single file.
73 |
74 | `configFromFiles(string $pattern)` reads config from multiple files using
75 | globbing patterns.
76 |
77 | ### Merging
78 |
79 | The reader matches files in the order they are specified. As files are
80 | read their config is merged in; overwriting any matching keys.
81 |
82 | ### Supported Formats
83 |
84 | Currently `.php` and `.json` files are supported out of the box. PHP
85 | config files **must** return a PHP array.
86 |
87 | `.yaml` and `.yml` files can be read when the package `symfony/yaml` is
88 | available. Run
89 |
90 | ```
91 | composer require symfony/yaml
92 | ```
93 |
94 | to install it.
95 |
96 | ## Application Configuration
97 |
98 | All values in the config array are made accessible via the DIC with the keys
99 | separated by a separator (default: `.`) and prefixed with constant string (default:
100 | `config`).
101 |
102 | #### Example
103 |
104 | ```php
105 | $config = [
106 | 'db' => [
107 | 'name' => 'example_db',
108 | 'username' => 'dbuser',
109 | 'password' => 'dbpass',
110 | ],
111 | ];
112 |
113 | $container = new Container();
114 | Configurator::apply()->configFromArray($config)->to($container);
115 |
116 | var_dump($container->get('config.db.name'));
117 | /*
118 | * OUTPUT:
119 | * string(10) "example_db"
120 | */
121 | ```
122 |
123 | ### Accessing A Whole Sub-Array
124 |
125 | Whole sub-arrays are also made available for cases where you want them instead
126 | of individual values.
127 |
128 | #### Example
129 |
130 | ```php
131 | $config = [
132 | 'db' => [
133 | 'name' => 'example_db',
134 | 'username' => 'dbuser',
135 | 'password' => 'dbpass',
136 | ],
137 | ];
138 |
139 | $container = new Container();
140 | Configurator::apply()->configFromArray($config)->to($container);
141 |
142 | var_dump($container->get('config.db'));
143 | /*
144 | * OUTPUT:
145 | * array(3) {
146 | * ["name"]=>
147 | * string(10) "example_db"
148 | * ["username"]=>
149 | * string(6) "dbuser"
150 | * ["password"]=>
151 | * string(6) "dbpass"
152 | * }
153 | */
154 | ```
155 |
156 | ## Configuring Services
157 |
158 | Another feature is the ability to add services to your container via the
159 | config. By default, this is done by adding a `services` key under a `di` key in
160 | the config in the following format:
161 |
162 | ```php
163 | $config = [
164 | 'di' => [
165 | 'services' => [
166 | 'logger' => [
167 | 'class' => Logger::class,
168 | 'singleton' => true,
169 | 'arguments' => [
170 | StdoutLogger::class,
171 | ],
172 | 'methods' => [
173 | 'setLogLevel' => [ 'info' ],
174 | ],
175 | ],
176 | StdoutLogger::class => [],
177 | ],
178 | ],
179 | ];
180 |
181 | $container = new Container();
182 | Configurator::apply()->configFromArray($config)->to($container);
183 |
184 | $logger = $container->get('logger'));
185 | ```
186 |
187 | ### Service Aliases
188 |
189 | You can create an alias to another service by using the `service` keyword
190 | instead of `class`:
191 |
192 | ```php
193 | $config = [
194 | 'database' => [ /* ... */ ],
195 | 'di' => [
196 | 'services' => [
197 | DatabaseConnection::class => [
198 | 'service' => MySQLDatabaseConnection::class,
199 | ],
200 | MySQLDatabaseConnection::class => [
201 | 'arguments' => [
202 | 'config.database.host',
203 | 'config.database.username',
204 | 'config.database.password',
205 | 'config.database.dbname',
206 | ],
207 | ],
208 | ],
209 | ],
210 | ];
211 | ```
212 |
213 | ### Service Factories
214 |
215 | If you require some addition additional logic when creating a service, you can
216 | define a Service Factory. A service factory is simply an invokable class which
217 | can take a list of arguments and returns the service instance.
218 |
219 | Services are added to the container by using the `factory` key instead of the
220 | `class` key.
221 |
222 | #### Example Config
223 | ```php
224 | $appConfig = [
225 | 'db' => [
226 | 'host' => 'localhost',
227 | 'database' => 'example_db',
228 | 'username' => 'example_user',
229 | 'password' => 'example_password',
230 | ],
231 | 'di' => [
232 | 'services' => [
233 | 'database' => [
234 | 'factory' => MySQLPDOFactory::class,
235 | 'singleton' => true,
236 | 'arguments' => [
237 | 'config.db.host',
238 | 'config.db.database',
239 | 'config.db.username',
240 | 'config.db.password',
241 | ],
242 | ],
243 | ],
244 | ],
245 | ];
246 | ```
247 |
248 | #### Example Service Factory
249 | ```php
250 | setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
259 |
260 | return $pdo;
261 | }
262 | }
263 | ```
264 |
265 | ### Injecting The Container
266 |
267 | In the rare case that you want to inject the container in as a dependency to
268 | one of your services, you can use `Configurator::container()` as the name
269 | of the injected dependency. This will only work in PHP config files, it's not
270 | available with YAML or JSON.
271 |
272 | ```php
273 | $config = [
274 | 'di' => [
275 | 'services' => [
276 | ContainerAwareService::class => [
277 | 'arguments' => [Configurator::container()],
278 | ],
279 | ],
280 | ],
281 | ];
282 | ```
283 |
284 | ### Configuring Inflectors
285 |
286 | It is also possible to set up
287 | [Inflectors](http://container.thephpleague.com/inflectors/) by adding an
288 | `inflectors` key to the `di` section of the config.
289 |
290 | ```php
291 | $appConfig = [
292 | 'di' => [
293 | 'inflectors' => [
294 | LoggerAwareInterface::class => [
295 | 'setLogger' => ['Some\Logger']
296 | ],
297 | ],
298 | ],
299 | ];
300 | ```
301 |
302 | ## Extra Settings
303 |
304 | The behaviour of the `Configurator` can be adjusted by using the
305 | `withSetting(string $name, $value` method:
306 |
307 | ```php
308 | Configurator::apply()
309 | ->configFromFiles('*.cfg.php'),
310 | ->withSetting(Configurator::SETTING_PREFIX, 'settings')
311 | ->withSetting(Configurator::SETTING_SEPARATOR, '/')
312 | ->to($container);
313 | ```
314 |
315 | Available settings are:
316 |
317 | | Name | Description | Default |
318 | |------------------------------------|-------------------------------------------------|-----------------|
319 | | SETTING_PREFIX | Sets prefix name for config value keys. | `config` |
320 | | SETTING_SEPARATOR | Sets the separator for config key. | `.` |
321 | | SETTING_SERVICES_KEY | Where the config for the services is. | `di.services` |
322 | | SETTING_INFLECTORS_KEY | Where the config for the inflectors is. | `di.inflectors` |
323 | | SETTING_DEFAULT_SINGLETON_SERVICES | Sets whether services are singleton by default. | `false` |
324 |
325 | ## Advanced Customisation
326 |
327 | ### Adding A Custom File Reader
328 |
329 | You can create your own custom file reader by implementing the
330 | `TomPHP\ContainerConfigurator\FileReader\FileReader` interface. Once you have
331 | created it, you can use the
332 | `withFileReader(string $extension, string $readerClassName)` method to enable
333 | the it.
334 |
335 | **IMPORTANT**: `withFileReader()` must be called before calling
336 | `configFromFile()` or `configFromFiles()`!
337 |
338 | ```php
339 | Configurator::apply()
340 | ->withFileReader('.xml', MyCustomXMLFileReader::class)
341 | ->configFromFile('config.xml'),
342 | ->to($container);
343 | ```
344 |
345 | ### Adding A Custom Container Adapter
346 |
347 | You can create your own container adapter so that you can configure other
348 | containers. This is done by implementing the
349 | `TomPHP\ContainerConfigurator\FileReader\ContainerAdapter` interface. Once you
350 | have created your adapter, you can use the
351 | `withContainerAdapter(string $containerName, string $adapterName)` method to
352 | enable the it:
353 |
354 | ```php
355 | Configurator::apply()
356 | ->withContainerAdapter(MyContainer::class, MyContainerAdapter::class)
357 | ->configFromArray($appConfig),
358 | ->to($container);
359 | ```
360 |
--------------------------------------------------------------------------------
/UPGRADE.md:
--------------------------------------------------------------------------------
1 | # Upgrade Instructions
2 |
3 | ## v0.4.\* -> v0.5.\*
4 |
5 | There are three major changes which occurred in this update:
6 |
7 | ### Package Renamed
8 |
9 | This package is now called **Container Configurator**. It's not installed with
10 | the command:
11 |
12 | ```
13 | composer require tomphp/container-configurator
14 | ```
15 |
16 | For existing projects, you can fix this by running:
17 |
18 | ```
19 | composer remove tomphp/config-service-provider && composer require tomphp/container-configurator
20 | ```
21 |
22 | To require it in your project you need to:
23 |
24 | ```
25 | use TomPHP\ContainerConfigurator\Configurator;
26 | ```
27 |
28 | ### New API
29 |
30 | The API had a major rewrite in v0.5.0. Rather than creating a service provider
31 | like this:
32 |
33 | ```php
34 | $container->addServiceProvider(ConfigServiceProvider::fromFile(['config.php']);
35 | ```
36 |
37 | You now configure the container like this:
38 |
39 | ```php
40 | Configurator::apply()
41 | ->configFromFiles('config/*.inc.php')
42 | ->to($container);
43 | ```
44 |
45 | If you had multiple file pattern matches, you can chain more `configFromFiles`
46 | calls like so:
47 |
48 | ```php
49 | Configurator::apply()
50 | ->configFromFiles('*.global.php')
51 | ->configFromFiles('*.local.php')
52 | ->to($container);
53 | ```
54 |
55 | Also:
56 |
57 | ```php
58 | $container->addServiceProvider(ConfigServiceProvider::fromConfig($config);
59 | ```
60 |
61 | Would now be replaced with:
62 |
63 | ```php
64 | Configurator::apply()
65 | ->configFromArray($config)
66 | ->to($container);
67 | ```
68 |
69 | ### Default DI Config Has Changed Structure
70 |
71 | There's been some changes to where the service and inflector config go by
72 | default. Before v0.5.0 the config would look like this:
73 |
74 | ```php
75 | $config = [
76 | 'di' => [
77 | // Config for services goes here
78 | ],
79 | 'inflectors' => [
80 | // Config for inflectors goes here
81 | ],
82 | ];
83 | ```
84 |
85 | By default, v0.5.0 now has this format:
86 |
87 | ```php
88 | $config = [
89 | 'di' => [
90 | 'services' => [
91 | // Config for services goes here
92 | ],
93 | 'inflectors' => [
94 | // Config for inflectors goes here
95 | ],
96 | ],
97 | ];
98 | ```
99 |
100 | If you don't wish to adopt this new format, you can set the Configurator up to
101 | behave in the old way by using the following settings:
102 |
103 | ```php
104 | Configurator::apply()
105 | ->configFromArray($config)
106 | ->withSetting(Configurator::SETTING_SERVICES_KEY, 'di')
107 | ->withSetting(Configurator::SETTING_INFLECTORS_KEY, 'inflectors')
108 | ->to($container);
109 | ```
110 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tomphp/container-configurator",
3 | "description": "Configure your application and the Dependency Injection Container (DIC) via config arrays or config files.",
4 | "license": "MIT",
5 | "type": "library",
6 | "homepage": "https://github.com/tomphp/config-service-provider",
7 | "keywords": ["di", "dependency injection", "container", "league"],
8 | "authors": [
9 | {
10 | "name": "Tom Oram",
11 | "email": "tom@x2k.co.uk",
12 | "homepage": "https://github.com/tomphp",
13 | "role": "Developer"
14 | }
15 | ],
16 | "suggest": {
17 | "league/container": "Small but powerful dependency injection container http://container.thephpleague.com",
18 | "pimple/pimple": "A small PHP 5.3 dependency injection container http://pimple.sensiolabs.org",
19 | "symfony/yaml": "For reading configuration from YAML files"
20 | },
21 | "require": {
22 | "php": "^5.6|^7.0",
23 | "beberlei/assert": "^2.6",
24 | "tomphp/exception-constructor-tools": "^1.0.0"
25 | },
26 | "require-dev": {
27 | "friendsofphp/php-cs-fixer": "^2.1.0",
28 | "league/container": "^2.0.2",
29 | "phpunit/phpunit": "^5.5.4",
30 | "pimple/pimple": "^3.0.0",
31 | "squizlabs/php_codesniffer": "^2.8.0",
32 | "symfony/yaml": "^3.1.4"
33 | },
34 | "autoload": {
35 | "psr-4": {
36 | "TomPHP\\ContainerConfigurator\\": "src/"
37 | }
38 | },
39 | "autoload-dev": {
40 | "psr-4": {
41 | "tests\\unit\\TomPHP\\ContainerConfigurator\\": "tests/unit/",
42 | "tests\\acceptance\\": "tests/acceptance/",
43 | "tests\\support\\": "tests/support/",
44 | "tests\\mocks\\": "tests/mocks/"
45 | },
46 | "files": [
47 | "vendor/phpunit/phpunit/src/Framework/Assert/Functions.php"
48 | ]
49 | },
50 | "config": {
51 | "sort-packages": true
52 | },
53 | "scripts": {
54 | "cs:fix": [
55 | "phpcbf --standard=psr2 src tests; exit 0",
56 | "php-cs-fixer fix --verbose; exit 0"
57 | ],
58 | "cs:check": [
59 | "phpcs --standard=psr2 src tests",
60 | "php-cs-fixer fix --dry-run --verbose"
61 | ],
62 | "test": [
63 | "@cs:check",
64 | "phpunit --colors=always"
65 | ]
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 | tests/unit
22 |
23 |
24 | tests/acceptance
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/ApplicationConfig.php:
--------------------------------------------------------------------------------
1 | string()->notEmpty();
35 |
36 | $this->config = $config;
37 | $this->separator = $separator;
38 | }
39 |
40 | public function merge(array $config)
41 | {
42 | $this->config = array_replace_recursive($this->config, $config);
43 | }
44 |
45 | /**
46 | * @param string $separator
47 | *
48 | * @throws InvalidArgumentException
49 | *
50 | * @return void
51 | */
52 | public function setSeparator($separator)
53 | {
54 | \Assert\that($separator)->string()->notEmpty();
55 |
56 | $this->separator = $separator;
57 | }
58 |
59 | public function getIterator()
60 | {
61 | return new ApplicationConfigIterator($this);
62 | }
63 |
64 | /**
65 | * @return array
66 | */
67 | public function getKeys()
68 | {
69 | return array_keys(iterator_to_array(new ApplicationConfigIterator($this)));
70 | }
71 |
72 | public function offsetExists($offset)
73 | {
74 | try {
75 | $this->traverseConfig($this->getPath($offset));
76 | } catch (EntryDoesNotExistException $e) {
77 | return false;
78 | }
79 |
80 | return true;
81 | }
82 |
83 | /**
84 | * @param mixed $offset
85 | *
86 | * @throws EntryDoesNotExistException
87 | *
88 | * @return mixed
89 | */
90 | public function offsetGet($offset)
91 | {
92 | return $this->traverseConfig($this->getPath($offset));
93 | }
94 |
95 | /**
96 | * @param mixed $offset
97 | * @param mixed $value
98 | *
99 | * @throws ReadOnlyException
100 | */
101 | public function offsetSet($offset, $value)
102 | {
103 | throw ReadOnlyException::fromClassName(__CLASS__);
104 | }
105 |
106 | /**
107 | * @param mixed $offset
108 | *
109 | * @throws ReadOnlyException
110 | */
111 | public function offsetUnset($offset)
112 | {
113 | throw ReadOnlyException::fromClassName(__CLASS__);
114 | }
115 |
116 | /**
117 | * @return array
118 | */
119 | public function asArray()
120 | {
121 | return $this->config;
122 | }
123 |
124 | /**
125 | * @return string
126 | */
127 | public function getSeparator()
128 | {
129 | return $this->separator;
130 | }
131 |
132 | /**
133 | * @param string $offset
134 | *
135 | * @return array
136 | */
137 | private function getPath($offset)
138 | {
139 | return explode($this->separator, $offset);
140 | }
141 |
142 | /**
143 | * @param array $path
144 | *
145 | * @throws EntryDoesNotExistException
146 | *
147 | * @return mixed
148 | */
149 | private function traverseConfig(array $path)
150 | {
151 | $pointer = &$this->config;
152 |
153 | foreach ($path as $node) {
154 | if (!is_array($pointer) || !array_key_exists($node, $pointer)) {
155 | throw EntryDoesNotExistException::fromKey(implode($this->separator, $path));
156 | }
157 |
158 | $pointer = &$pointer[$node];
159 | }
160 |
161 | return $pointer;
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/src/ApplicationConfigIterator.php:
--------------------------------------------------------------------------------
1 | asArray()),
30 | RecursiveIteratorIterator::SELF_FIRST
31 | );
32 | $this->separator = $config->getSeparator();
33 | }
34 |
35 | public function key()
36 | {
37 | return implode($this->separator, array_merge($this->path, [parent::key()]));
38 | }
39 |
40 | public function next()
41 | {
42 | if ($this->callHasChildren()) {
43 | array_push($this->path, parent::key());
44 | }
45 |
46 | parent::next();
47 | }
48 |
49 | public function rewind()
50 | {
51 | $this->path = [];
52 |
53 | parent::rewind();
54 | }
55 |
56 | public function endChildren()
57 | {
58 | array_pop($this->path);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Configurator.php:
--------------------------------------------------------------------------------
1 | FileReader\JSONFileReader::class,
20 | '.php' => FileReader\PHPFileReader::class,
21 | '.yaml' => FileReader\YAMLFileReader::class,
22 | '.yml' => FileReader\YAMLFileReader::class,
23 | ];
24 |
25 | const CONTAINER_ADAPTERS = [
26 | \League\Container\Container::class => League\LeagueContainerAdapter::class,
27 | \Pimple\Container::class => Pimple\PimpleContainerAdapter::class,
28 | ];
29 |
30 | /**
31 | * @var ApplicationConfig
32 | */
33 | private $config;
34 |
35 | /**
36 | * @var FileReader\ReaderFactory
37 | */
38 | private $readerFactory;
39 |
40 | /**
41 | * @var mixed[]
42 | */
43 | private $settings = [
44 | self::SETTING_PREFIX => 'config',
45 | self::SETTING_SEPARATOR => '.',
46 | self::SETTING_SERVICES_KEY => 'di.services',
47 | self::SETTING_INFLECTORS_KEY => 'di.inflectors',
48 | self::SETTING_DEFAULT_SINGLETON_SERVICES => false,
49 | ];
50 |
51 | /**
52 | * @var string[]
53 | */
54 | private $fileReaders = self::FILE_READERS;
55 |
56 | /**
57 | * @var string[]
58 | */
59 | private $containerAdapters = self::CONTAINER_ADAPTERS;
60 |
61 | /**
62 | * @var string
63 | */
64 | private static $containerIdentifier;
65 |
66 | /**
67 | * @return Configurator
68 | */
69 | public static function apply()
70 | {
71 | return new self();
72 | }
73 |
74 | private function __construct()
75 | {
76 | $this->config = new ApplicationConfig([]);
77 | }
78 |
79 | /**
80 | * @return string
81 | */
82 | public static function container()
83 | {
84 | if (!self::$containerIdentifier) {
85 | self::$containerIdentifier = uniqid(__CLASS__ . '::CONTAINER_ID::');
86 | }
87 |
88 | return self::$containerIdentifier;
89 | }
90 |
91 | /**
92 | * @param array $config
93 | *
94 | * @return $this
95 | */
96 | public function configFromArray(array $config)
97 | {
98 | $this->config->merge($config);
99 |
100 | return $this;
101 | }
102 |
103 | /**
104 | * @param string $filename
105 | *
106 | * @throws InvalidArgumentException
107 | *
108 | * @return $this
109 | */
110 | public function configFromFile($filename)
111 | {
112 | Assertion::file($filename);
113 |
114 | $this->readFileAndMergeConfig($filename);
115 |
116 | return $this;
117 | }
118 |
119 | /**
120 | * @param string $pattern
121 | *
122 | * @throws NoMatchingFilesException
123 | * @throws InvalidArgumentException
124 | *
125 | * @return $this
126 | */
127 | public function configFromFiles($pattern)
128 | {
129 | Assertion::string($pattern);
130 |
131 | $locator = new FileReader\FileLocator();
132 |
133 | $files = $locator->locate($pattern);
134 |
135 | if (count($files) === 0) {
136 | throw NoMatchingFilesException::fromPattern($pattern);
137 | }
138 |
139 | foreach ($files as $filename) {
140 | $this->readFileAndMergeConfig($filename);
141 | }
142 |
143 | return $this;
144 | }
145 |
146 | /**
147 | * @param string $name
148 | * @param mixed $value
149 | *
150 | * @throws UnknownSettingException
151 | * @throws InvalidArgumentException
152 | *
153 | * @return $this
154 | */
155 | public function withSetting($name, $value)
156 | {
157 | Assertion::string($name);
158 | Assertion::scalar($value);
159 |
160 | if (!array_key_exists($name, $this->settings)) {
161 | throw UnknownSettingException::fromSetting($name, array_keys($this->settings));
162 | }
163 |
164 | $this->settings[$name] = $value;
165 |
166 | return $this;
167 | }
168 |
169 | /**
170 | * @param string $extension
171 | * @param string $className
172 | *
173 | * @return $this
174 | */
175 | public function withFileReader($extension, $className)
176 | {
177 | $this->fileReaders[$extension] = $className;
178 |
179 | return $this;
180 | }
181 |
182 | /**
183 | * @param string $containerName
184 | * @param string $adapterName
185 | *
186 | * @return $this
187 | */
188 | public function withContainerAdapter($containerName, $adapterName)
189 | {
190 | $this->containerAdapters[$containerName] = $adapterName;
191 |
192 | return $this;
193 | }
194 |
195 | /**
196 | * @param object $container
197 | *
198 | * @return void
199 | */
200 | public function to($container)
201 | {
202 | $this->config->setSeparator($this->settings[self::SETTING_SEPARATOR]);
203 |
204 | $factory = new ContainerAdapterFactory($this->containerAdapters);
205 |
206 | $configurator = $factory->create($container);
207 |
208 | $configurator->addApplicationConfig($this->config, $this->settings[self::SETTING_PREFIX]);
209 |
210 | if (isset($this->config[$this->settings[self::SETTING_SERVICES_KEY]])) {
211 | $configurator->addServiceConfig(new ServiceConfig(
212 | $this->config[$this->settings[self::SETTING_SERVICES_KEY]],
213 | $this->settings[self::SETTING_DEFAULT_SINGLETON_SERVICES]
214 | ));
215 | }
216 |
217 | if (isset($this->config[$this->settings[self::SETTING_INFLECTORS_KEY]])) {
218 | $configurator->addInflectorConfig(new InflectorConfig(
219 | $this->config[$this->settings[self::SETTING_INFLECTORS_KEY]]
220 | ));
221 | }
222 | }
223 |
224 | /**
225 | * @param string $filename
226 | *
227 | * @return void
228 | */
229 | private function readFileAndMergeConfig($filename)
230 | {
231 | $reader = $this->getReaderFor($filename);
232 |
233 | $this->config->merge($reader->read($filename));
234 | }
235 |
236 | /**
237 | * @param string $filename
238 | *
239 | * @return FileReader\FileReader
240 | */
241 | private function getReaderFor($filename)
242 | {
243 | if (!$this->readerFactory) {
244 | $this->readerFactory = new FileReader\ReaderFactory($this->fileReaders);
245 | }
246 |
247 | return $this->readerFactory->create($filename);
248 | }
249 | }
250 |
--------------------------------------------------------------------------------
/src/ContainerAdapter.php:
--------------------------------------------------------------------------------
1 | config = $config;
21 | }
22 |
23 | /**
24 | * @param object $container
25 | *
26 | * @throws UnknownContainerException
27 | * @throws NotContainerAdapterException
28 | *
29 | * @return ContainerAdapter
30 | */
31 | public function create($container)
32 | {
33 | $class = '';
34 |
35 | foreach ($this->config as $containerClass => $configuratorClass) {
36 | if ($container instanceof $containerClass) {
37 | $class = $configuratorClass;
38 | break;
39 | }
40 | }
41 |
42 | if (!$class) {
43 | throw UnknownContainerException::fromContainerName(
44 | get_class($container),
45 | array_keys($this->config)
46 | );
47 | }
48 |
49 | $instance = new $class();
50 |
51 | if (!$instance instanceof ContainerAdapter) {
52 | throw NotContainerAdapterException::fromClassName($class);
53 | }
54 |
55 | $instance->setContainer($container);
56 |
57 | return $instance;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Exception/EntryDoesNotExistException.php:
--------------------------------------------------------------------------------
1 | null,
15 | JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
16 | JSON_ERROR_STATE_MISMATCH => 'Underflow or the modes mismatch',
17 | JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
18 | JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
19 | JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded',
20 | ];
21 |
22 | /**
23 | * @var string
24 | */
25 | private $filename;
26 |
27 | public function read($filename)
28 | {
29 | Assertion::file($filename);
30 |
31 | $this->filename = $filename;
32 |
33 | $config = json_decode(file_get_contents($filename), true);
34 |
35 | if (json_last_error() !== JSON_ERROR_NONE) {
36 | throw InvalidConfigException::fromJSONFileError($filename, $this->getJsonError());
37 | }
38 |
39 | return $config;
40 | }
41 |
42 | /**
43 | * @return string
44 | */
45 | private function getJsonError()
46 | {
47 | if (function_exists('json_last_error_msg')) {
48 | return json_last_error_msg();
49 | }
50 |
51 | return self::JSON_ERRORS[json_last_error()];
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/FileReader/PHPFileReader.php:
--------------------------------------------------------------------------------
1 | filename = $filename;
20 |
21 | $config = include $this->filename;
22 |
23 | $this->assertConfigIsValid($config);
24 |
25 | return $config;
26 | }
27 |
28 | private function assertConfigIsValid($config)
29 | {
30 | if (!is_array($config)) {
31 | throw InvalidConfigException::fromPHPFileError($this->filename);
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/FileReader/ReaderFactory.php:
--------------------------------------------------------------------------------
1 | config = $config;
30 | }
31 |
32 | /**
33 | * @param string $filename
34 | *
35 | * @throws InvalidArgumentException
36 | *
37 | * @return FileReader
38 | */
39 | public function create($filename)
40 | {
41 | Assertion::file($filename);
42 |
43 | $readerClass = $this->getReaderClass($filename);
44 |
45 | if (!isset($this->readers[$readerClass])) {
46 | $this->readers[$readerClass] = new $readerClass();
47 | }
48 |
49 | return $this->readers[$readerClass];
50 | }
51 |
52 | /**
53 | * @param string $filename
54 | *
55 | * @throws UnknownFileTypeException
56 | *
57 | * @return string
58 | */
59 | private function getReaderClass($filename)
60 | {
61 | $readerClass = null;
62 |
63 | foreach ($this->config as $extension => $className) {
64 | if ($this->endsWith($filename, $extension)) {
65 | $readerClass = $className;
66 | break;
67 | }
68 | }
69 |
70 | if ($readerClass === null) {
71 | throw UnknownFileTypeException::fromFileExtension(
72 | $filename,
73 | array_keys($this->config)
74 | );
75 | }
76 |
77 | return $readerClass;
78 | }
79 |
80 | /**
81 | * @param string $haystack
82 | * @param string $needle
83 | *
84 | * @return bool
85 | */
86 | private function endsWith($haystack, $needle)
87 | {
88 | return $needle === substr($haystack, -strlen($needle));
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/FileReader/YAMLFileReader.php:
--------------------------------------------------------------------------------
1 | getMessage());
33 | }
34 |
35 | return $config;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/InflectorConfig.php:
--------------------------------------------------------------------------------
1 | inflectors = [];
24 |
25 | foreach ($config as $interfaceName => $methods) {
26 | $this->inflectors[] = new InflectorDefinition(
27 | $interfaceName,
28 | $methods
29 | );
30 | }
31 | }
32 |
33 | public function getIterator()
34 | {
35 | return new ArrayIterator($this->inflectors);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/InflectorDefinition.php:
--------------------------------------------------------------------------------
1 | interface = $interface;
27 | $this->methods = $methods;
28 | }
29 |
30 | /**
31 | * @return string
32 | */
33 | public function getInterface()
34 | {
35 | return $this->interface;
36 | }
37 |
38 | /**
39 | * @return array
40 | */
41 | public function getMethods()
42 | {
43 | return $this->methods;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/League/ApplicationConfigServiceProvider.php:
--------------------------------------------------------------------------------
1 | prefix = $prefix;
36 | $this->config = $config;
37 | $this->provides = array_map(
38 | function ($key) {
39 | return $this->keyPrefix() . $key;
40 | },
41 | $config->getKeys()
42 | );
43 | }
44 |
45 | public function register()
46 | {
47 | $prefix = $this->keyPrefix();
48 |
49 | foreach ($this->config as $key => $value) {
50 | $this->container->share($prefix . $key, function () use ($value) {
51 | return $value;
52 | });
53 | }
54 | }
55 |
56 | /**
57 | * @return string
58 | */
59 | private function keyPrefix()
60 | {
61 | if (empty($this->prefix)) {
62 | return '';
63 | }
64 |
65 | return $this->prefix . $this->config->getSeparator();
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/League/InflectorServiceProvider.php:
--------------------------------------------------------------------------------
1 | config = $config;
26 | }
27 |
28 | public function register()
29 | {
30 | }
31 |
32 | public function boot()
33 | {
34 | foreach ($this->config as $definition) {
35 | $this->configureInterface($definition);
36 | }
37 | }
38 |
39 | /**
40 | * @param InflectorDefinition $definition
41 | */
42 | private function configureInterface(InflectorDefinition $definition)
43 | {
44 | foreach ($definition->getMethods() as $method => $args) {
45 | $this->addInflectorMethod(
46 | $definition->getInterface(),
47 | $method,
48 | $args
49 | );
50 | }
51 | }
52 |
53 | /**
54 | * @param string $interface
55 | * @param string $method
56 | * @param array $args
57 | */
58 | private function addInflectorMethod($interface, $method, array $args)
59 | {
60 | $this->getContainer()
61 | ->inflector($interface)
62 | ->invokeMethod($method, $args);
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/League/LeagueContainerAdapter.php:
--------------------------------------------------------------------------------
1 | container = $container;
28 | }
29 |
30 | public function addApplicationConfig(ApplicationConfig $config, $prefix = 'config')
31 | {
32 | Assertion::string($prefix);
33 |
34 | $this->container->addServiceProvider(new ApplicationConfigServiceProvider($config, $prefix));
35 | }
36 |
37 | public function addServiceConfig(ServiceConfig $config)
38 | {
39 | $this->container->addServiceProvider(new ServiceServiceProvider($config));
40 | }
41 |
42 | public function addInflectorConfig(InflectorConfig $config)
43 | {
44 | $this->container->addServiceProvider(new InflectorServiceProvider($config));
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/League/ServiceServiceProvider.php:
--------------------------------------------------------------------------------
1 | config = $config;
28 | $this->provides = $config->getKeys();
29 | }
30 |
31 | public function register()
32 | {
33 | foreach ($this->config as $config) {
34 | $this->registerService($config);
35 | }
36 | }
37 |
38 | /**
39 | * @param ServiceDefinition $definition
40 | *
41 | * @throws NotClassDefinitionException
42 | *
43 | * @return void
44 | */
45 | private function registerService(ServiceDefinition $definition)
46 | {
47 | if ($definition->isFactory()) {
48 | $this->getContainer()->add(
49 | $definition->getName(),
50 | $this->createFactoryFactory($definition),
51 | $definition->isSingleton()
52 | );
53 |
54 | return;
55 | }
56 |
57 | if ($definition->isAlias()) {
58 | $this->getContainer()->add(
59 | $definition->getName(),
60 | $this->createAliasFactory($definition)
61 | );
62 |
63 | return;
64 | }
65 |
66 | $service = $this->getContainer()->add(
67 | $definition->getName(),
68 | $definition->getClass(),
69 | $definition->isSingleton()
70 | );
71 |
72 | if (!$service instanceof ClassDefinition) {
73 | throw NotClassDefinitionException::fromServiceName($definition->getName());
74 | }
75 |
76 | $service->withArguments($this->injectContainer($definition->getArguments()));
77 | $this->addMethodCalls($service, $definition);
78 | }
79 |
80 | /**
81 | * @param ClassDefinition $service
82 | * @param ServiceDefinition $definition
83 | */
84 | private function addMethodCalls(ClassDefinition $service, ServiceDefinition $definition)
85 | {
86 | foreach ($definition->getMethods() as $method => $args) {
87 | $service->withMethodCall($method, $this->injectContainer($args));
88 | }
89 | }
90 |
91 | /**
92 | * @param ServiceDefinition $definition
93 | *
94 | * @return \Closure
95 | */
96 | private function createAliasFactory(ServiceDefinition $definition)
97 | {
98 | return function () use ($definition) {
99 | return $this->getContainer()->get($definition->getClass());
100 | };
101 | }
102 |
103 | /**
104 | * @param ServiceDefinition $definition
105 | *
106 | * @return \Closure
107 | */
108 | private function createFactoryFactory(ServiceDefinition $definition)
109 | {
110 | return function () use ($definition) {
111 | $className = $definition->getClass();
112 | $factory = new $className();
113 |
114 | return $factory(...$this->resolveArguments($definition->getArguments()));
115 | };
116 | }
117 |
118 | /**
119 | * @param array $arguments
120 | *
121 | * @return array
122 | */
123 | private function injectContainer(array $arguments)
124 | {
125 | return array_map(
126 | function ($argument) {
127 | return ($argument === Configurator::container())
128 | ? $this->container
129 | : $argument;
130 | },
131 | $arguments
132 | );
133 | }
134 |
135 | /**
136 | * @param array $arguments
137 | *
138 | * @return array
139 | */
140 | private function resolveArguments(array $arguments)
141 | {
142 | return array_map(
143 | function ($argument) {
144 | if ($argument === Configurator::container()) {
145 | return $this->container;
146 | }
147 |
148 | if ($this->container->has($argument)) {
149 | return $this->container->get($argument);
150 | }
151 |
152 | return $argument;
153 | },
154 | $arguments
155 | );
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/src/Pimple/PimpleContainerAdapter.php:
--------------------------------------------------------------------------------
1 | container = $container;
37 | }
38 |
39 | public function addApplicationConfig(ApplicationConfig $config, $prefix = 'config')
40 | {
41 | Assertion::string($prefix);
42 |
43 | if (!empty($prefix)) {
44 | $prefix .= $config->getSeparator();
45 | }
46 |
47 | foreach ($config as $key => $value) {
48 | $this->container[$prefix . $key] = $value;
49 | }
50 | }
51 |
52 | public function addServiceConfig(ServiceConfig $config)
53 | {
54 | foreach ($config as $definition) {
55 | $this->addServiceToContainer($definition);
56 | }
57 | }
58 |
59 | public function addInflectorConfig(InflectorConfig $config)
60 | {
61 | foreach ($config as $definition) {
62 | $this->inflectors[$definition->getInterface()] = $this->createInflector($definition);
63 | }
64 | }
65 |
66 | private function addServiceToContainer(ServiceDefinition $definition)
67 | {
68 | $factory = $this->createFactory($definition);
69 |
70 | if (!$definition->isSingleton()) {
71 | $factory = $this->container->factory($factory);
72 | }
73 |
74 | $this->container[$definition->getName()] = $factory;
75 | }
76 |
77 | /**
78 | * @param ServiceDefinition $definition
79 | *
80 | * @return Closure
81 | */
82 | private function createFactory(ServiceDefinition $definition)
83 | {
84 | if ($definition->isFactory()) {
85 | return $this->applyInflectors($this->createFactoryFactory($definition));
86 | }
87 |
88 | if ($definition->isAlias()) {
89 | return $this->createAliasFactory($definition);
90 | }
91 |
92 | return $this->applyInflectors($this->createInstanceFactory($definition));
93 | }
94 |
95 | /**
96 | * @param ServiceDefinition $definition
97 | *
98 | * @return Closure
99 | */
100 | private function createFactoryFactory(ServiceDefinition $definition)
101 | {
102 | return function () use ($definition) {
103 | $className = $definition->getClass();
104 | $factory = new $className();
105 |
106 | return $factory(...$this->resolveArguments($definition->getArguments()));
107 | };
108 | }
109 |
110 | /**
111 | * @param ServiceDefinition $definition
112 | *
113 | * @return Closure
114 | */
115 | private function createAliasFactory(ServiceDefinition $definition)
116 | {
117 | return function () use ($definition) {
118 | return $this->container[$definition->getClass()];
119 | };
120 | }
121 |
122 | /**
123 | * @param ServiceDefinition $definition
124 | *
125 | * @return Closure
126 | */
127 | private function createInstanceFactory(ServiceDefinition $definition)
128 | {
129 | return function () use ($definition) {
130 | $className = $definition->getClass();
131 | $instance = new $className(...$this->resolveArguments($definition->getArguments()));
132 |
133 | foreach ($definition->getMethods() as $name => $args) {
134 | $instance->$name(...$this->resolveArguments($args));
135 | }
136 |
137 | return $instance;
138 | };
139 | }
140 |
141 | /**
142 | * @param InflectorDefinition $definition
143 | *
144 | * @return Closure
145 | */
146 | private function createInflector(InflectorDefinition $definition)
147 | {
148 | return function ($subject) use ($definition) {
149 | foreach ($definition->getMethods() as $method => $arguments) {
150 | $subject->$method(...$this->resolveArguments($arguments));
151 | }
152 | };
153 | }
154 |
155 | /**
156 | * @param Closure $factory
157 | *
158 | * @return Closure
159 | */
160 | private function applyInflectors(Closure $factory)
161 | {
162 | return function () use ($factory) {
163 | $instance = $factory();
164 |
165 | foreach ($this->inflectors as $interface => $inflector) {
166 | if ($instance instanceof $interface) {
167 | $inflector($instance);
168 | }
169 | }
170 |
171 | return $instance;
172 | };
173 | }
174 |
175 | /**
176 | * @param array $arguments
177 | *
178 | * @return array
179 | */
180 | private function resolveArguments(array $arguments)
181 | {
182 | return array_map(
183 | function ($argument) {
184 | if (!is_string($argument)) {
185 | return $argument;
186 | }
187 |
188 | if ($argument === Configurator::container()) {
189 | return $this->container;
190 | }
191 |
192 | if (isset($this->container[$argument])) {
193 | return $this->container[$argument];
194 | }
195 |
196 | return $argument;
197 | },
198 | $arguments
199 | );
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/src/ServiceConfig.php:
--------------------------------------------------------------------------------
1 | $serviceConfig) {
31 | $this->config[] = new ServiceDefinition($key, $serviceConfig, $singletonDefault);
32 | }
33 | }
34 |
35 | /**
36 | * @return array
37 | */
38 | public function getKeys()
39 | {
40 | return array_map(
41 | function (ServiceDefinition $definition) {
42 | return $definition->getName();
43 | },
44 | $this->config
45 | );
46 | }
47 |
48 | public function getIterator()
49 | {
50 | return new ArrayIterator($this->config);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/ServiceDefinition.php:
--------------------------------------------------------------------------------
1 | name = $name;
63 | $this->class = $this->className($name, $config);
64 | $this->isSingleton = isset($config['singleton']) ? $config['singleton'] : $singletonDefault;
65 | $this->isFactory = isset($config['factory']);
66 | $this->isAlias = isset($config['service']);
67 | $this->arguments = isset($config['arguments']) ? $config['arguments'] : [];
68 | $this->methods = isset($config['methods']) ? $config['methods'] : [];
69 | }
70 |
71 | /**
72 | * @return string
73 | */
74 | public function getName()
75 | {
76 | return $this->name;
77 | }
78 |
79 | /**
80 | * @return string
81 | */
82 | public function getClass()
83 | {
84 | return $this->class;
85 | }
86 |
87 | /**
88 | * @return bool
89 | */
90 | public function isSingleton()
91 | {
92 | return $this->isSingleton;
93 | }
94 |
95 | /**
96 | * @return bool
97 | */
98 | public function isFactory()
99 | {
100 | return $this->isFactory;
101 | }
102 |
103 | /**
104 | * @return bool
105 | */
106 | public function isAlias()
107 | {
108 | return $this->isAlias;
109 | }
110 |
111 | /**
112 | * @return array
113 | */
114 | public function getArguments()
115 | {
116 | return $this->arguments;
117 | }
118 |
119 | /**
120 | * @return array
121 | */
122 | public function getMethods()
123 | {
124 | return $this->methods;
125 | }
126 |
127 | /**
128 | * @param string $name
129 | * @param array $config
130 | *
131 | * @throws InvalidConfigException
132 | *
133 | * @return string
134 | */
135 | private function className($name, array $config)
136 | {
137 | if (isset($config['class']) && isset($config['factory'])) {
138 | throw InvalidConfigException::fromNameWhenClassAndFactorySpecified($name);
139 | }
140 |
141 | if (isset($config['class']) && isset($config['service'])) {
142 | throw InvalidConfigException::fromNameWhenClassAndServiceSpecified($name);
143 | }
144 |
145 | if (isset($config['factory']) && isset($config['service'])) {
146 | throw InvalidConfigException::fromNameWhenFactoryAndServiceSpecified($name);
147 | }
148 |
149 | if (isset($config['service'])) {
150 | return $config['service'];
151 | }
152 |
153 | if (isset($config['class'])) {
154 | return $config['class'];
155 | }
156 |
157 | if (isset($config['factory'])) {
158 | return $config['factory'];
159 | }
160 |
161 | return $name;
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/tests/acceptance/AbstractContainerAdapterTest.php:
--------------------------------------------------------------------------------
1 | 'example-value'];
25 |
26 | $this->createJSONConfigFile('config.json', $config);
27 |
28 | Configurator::apply()
29 | ->configFromFile($this->getTestPath('config.json'))
30 | ->to($this->container);
31 |
32 | assertSame('example-value', $this->container->get('config.example-key'));
33 | }
34 |
35 | public function testItCanBeConfiguredFromFiles()
36 | {
37 | $config = ['example-key' => 'example-value'];
38 |
39 | $this->createJSONConfigFile('config.json', $config);
40 |
41 | Configurator::apply()
42 | ->configFromFiles($this->getTestPath('*'))
43 | ->to($this->container);
44 |
45 | assertSame('example-value', $this->container->get('config.example-key'));
46 | }
47 |
48 | public function testItAddToConfigUsingFiles()
49 | {
50 | $config = ['keyB' => 'valueB'];
51 |
52 | $this->createJSONConfigFile('config.json', $config);
53 |
54 | Configurator::apply()
55 | ->configFromArray(['keyA' => 'valueA', 'keyB' => 'valueX'])
56 | ->configFromFiles($this->getTestPath('*'))
57 | ->to($this->container);
58 |
59 | assertSame('valueA', $this->container->get('config.keyA'));
60 | assertSame('valueB', $this->container->get('config.keyB'));
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/tests/acceptance/LeagueContainerAdapterTest.php:
--------------------------------------------------------------------------------
1 | container = new Container();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/tests/acceptance/PimpleContainerAdapterTest.php:
--------------------------------------------------------------------------------
1 | container = new PimpleContainerWrapper();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/tests/acceptance/PimpleContainerWrapper.php:
--------------------------------------------------------------------------------
1 | 'valueA'];
12 |
13 | Configurator::apply()
14 | ->configFromArray($config)
15 | ->to($this->container);
16 |
17 | assertEquals('valueA', $this->container->get('config.keyA'));
18 | }
19 |
20 | public function testItCascadeAddsConfigToTheContainer()
21 | {
22 | Configurator::apply()
23 | ->configFromArray(['keyA' => 'valueA', 'keyB' => 'valueX'])
24 | ->configFromArray(['keyB' => 'valueB'])
25 | ->to($this->container);
26 |
27 | assertEquals('valueA', $this->container->get('config.keyA'));
28 | }
29 |
30 | public function testItAddsGroupedConfigToTheContainer()
31 | {
32 | Configurator::apply()
33 | ->configFromArray(['group1' => ['keyA' => 'valueA']])
34 | ->to($this->container);
35 |
36 | assertEquals(['keyA' => 'valueA'], $this->container->get('config.group1'));
37 | assertEquals('valueA', $this->container->get('config.group1.keyA'));
38 | }
39 |
40 | public function testItAddsConfigToTheContainerWithAnAlternativeSeparator()
41 | {
42 | Configurator::apply()
43 | ->configFromArray(['keyA' => 'valueA'])
44 | ->withSetting(Configurator::SETTING_SEPARATOR, '/')
45 | ->to($this->container);
46 |
47 | assertEquals('valueA', $this->container->get('config/keyA'));
48 | }
49 |
50 | public function testItAddsConfigToTheContainerWithAnAlternativePrefix()
51 | {
52 | Configurator::apply()
53 | ->configFromArray(['keyA' => 'valueA'])
54 | ->withSetting(Configurator::SETTING_PREFIX, 'settings')
55 | ->to($this->container);
56 |
57 | assertEquals('valueA', $this->container->get('settings.keyA'));
58 | }
59 |
60 | public function testItAddsConfigToTheContainerWithNoPrefix()
61 | {
62 | Configurator::apply()
63 | ->configFromArray(['keyA' => 'valueA'])
64 | ->withSetting(Configurator::SETTING_PREFIX, '')
65 | ->to($this->container);
66 |
67 | assertEquals('valueA', $this->container->get('keyA'));
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/tests/acceptance/SupportsInflectorConfig.php:
--------------------------------------------------------------------------------
1 | [
16 | 'services' => [
17 | 'example' => [
18 | 'class' => ExampleClass::class,
19 | ],
20 | ],
21 | 'inflectors' => [
22 | ExampleInterface::class => [
23 | 'setValue' => ['test_value'],
24 | ],
25 | ],
26 | ],
27 | ];
28 |
29 | Configurator::apply()
30 | ->configFromArray($config)
31 | ->to($this->container);
32 |
33 | assertEquals(
34 | 'test_value',
35 | $this->container->get('example')->getValue()
36 | );
37 | }
38 |
39 | public function testItSetsUpAnInflectorForServiceFactory()
40 | {
41 | $config = [
42 | 'class_name' => ExampleClass::class,
43 | 'di' => [
44 | 'services' => [
45 | 'example' => [
46 | 'factory' => ExampleFactory::class,
47 | 'arguments' => [
48 | 'config.class_name',
49 | ],
50 | ],
51 | ],
52 | 'inflectors' => [
53 | ExampleInterface::class => [
54 | 'setValue' => ['test_value'],
55 | ],
56 | ],
57 | ],
58 | ];
59 |
60 | Configurator::apply()
61 | ->configFromArray($config)
62 | ->to($this->container);
63 |
64 | assertEquals(
65 | 'test_value',
66 | $this->container->get('example')->getValue()
67 | );
68 | }
69 |
70 | public function testItResolvesInflectorArguments()
71 | {
72 | $config = [
73 | 'argument' => 'test_value',
74 | 'di' => [
75 | 'services' => [
76 | 'example' => [
77 | 'class' => ExampleClass::class,
78 | ],
79 | ],
80 | 'inflectors' => [
81 | ExampleInterface::class => [
82 | 'setValue' => ['config.argument'],
83 | ],
84 | ],
85 | ],
86 | ];
87 |
88 | Configurator::apply()
89 | ->configFromArray($config)
90 | ->to($this->container);
91 |
92 | assertEquals(
93 | 'test_value',
94 | $this->container->get('example')->getValue()
95 | );
96 | }
97 |
98 | public function testItSetsUpAnInflectorUsingCustomInflectorsKey()
99 | {
100 | $config = [
101 | 'di' => [
102 | 'services' => [
103 | 'example' => [
104 | 'class' => ExampleClass::class,
105 | ],
106 | ],
107 | ],
108 | 'inflectors' => [
109 | ExampleInterface::class => [
110 | 'setValue' => ['test_value'],
111 | ],
112 | ],
113 | ];
114 |
115 | Configurator::apply()
116 | ->configFromArray($config)
117 | ->withSetting(Configurator::SETTING_INFLECTORS_KEY, 'inflectors')
118 | ->to($this->container);
119 |
120 | assertEquals(
121 | 'test_value',
122 | $this->container->get('example')->getValue()
123 | );
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/tests/acceptance/SupportsServiceConfig.php:
--------------------------------------------------------------------------------
1 | [
16 | 'services' => [
17 | 'example_class' => [
18 | 'class' => ExampleClass::class,
19 | ],
20 | ],
21 | ],
22 | ];
23 |
24 | Configurator::apply()
25 | ->configFromArray($config)
26 | ->to($this->container);
27 |
28 | assertInstanceOf(ExampleClass::class, $this->container->get('example_class'));
29 | }
30 |
31 | public function testItAddsServicesToTheContainerForADifferentConfigKey()
32 | {
33 | $config = [
34 | 'di' => [
35 | 'example_class' => [
36 | 'class' => ExampleClass::class,
37 | ],
38 | ],
39 | ];
40 |
41 | Configurator::apply()
42 | ->configFromArray($config)
43 | ->withSetting(Configurator::SETTING_SERVICES_KEY, 'di')
44 | ->to($this->container);
45 |
46 | assertInstanceOf(ExampleClass::class, $this->container->get('example_class'));
47 | }
48 |
49 | public function testItCreatesUniqueServiceInstancesByDefault()
50 | {
51 | $config = [
52 | 'di' => [
53 | 'services' => [
54 | 'example_class' => [
55 | 'class' => ExampleClass::class,
56 | 'singleton' => false,
57 | ],
58 | ],
59 | ],
60 | ];
61 |
62 | Configurator::apply()
63 | ->configFromArray($config)
64 | ->to($this->container);
65 |
66 | $instance1 = $this->container->get('example_class');
67 | $instance2 = $this->container->get('example_class');
68 |
69 | assertNotSame($instance1, $instance2);
70 | }
71 |
72 | public function testItCanCreateSingletonServiceInstances()
73 | {
74 | $config = [
75 | 'di' => [
76 | 'services' => [
77 | 'example_class' => [
78 | 'class' => ExampleClass::class,
79 | 'singleton' => true,
80 | ],
81 | ],
82 | ],
83 | ];
84 |
85 | Configurator::apply()
86 | ->configFromArray($config)
87 | ->to($this->container);
88 |
89 | $instance1 = $this->container->get('example_class');
90 | $instance2 = $this->container->get('example_class');
91 |
92 | assertSame($instance1, $instance2);
93 | }
94 |
95 | public function testItCanCreateSingletonServiceInstancesByDefault()
96 | {
97 | $config = [
98 | 'di' => [
99 | 'services' => [
100 | 'example_class' => [
101 | 'class' => ExampleClass::class,
102 | ],
103 | ],
104 | ],
105 | ];
106 |
107 | Configurator::apply()
108 | ->configFromArray($config)
109 | ->withSetting(Configurator::SETTING_DEFAULT_SINGLETON_SERVICES, true)
110 | ->to($this->container);
111 |
112 | $instance1 = $this->container->get('example_class');
113 | $instance2 = $this->container->get('example_class');
114 |
115 | assertSame($instance1, $instance2);
116 | }
117 |
118 | public function testItCanCreateUniqueServiceInstancesWhenSingletonIsDefault()
119 | {
120 | $config = [
121 | 'di' => [
122 | 'services' => [
123 | 'example_class' => [
124 | 'class' => ExampleClass::class,
125 | 'singleton' => false,
126 | ],
127 | ],
128 | ],
129 | ];
130 |
131 | Configurator::apply()
132 | ->configFromArray($config)
133 | ->withSetting(Configurator::SETTING_DEFAULT_SINGLETON_SERVICES, true)
134 | ->to($this->container);
135 |
136 | $instance1 = $this->container->get('example_class');
137 | $instance2 = $this->container->get('example_class');
138 |
139 | assertNotSame($instance1, $instance2);
140 | }
141 |
142 | public function testItAddsConstructorArguments()
143 | {
144 | $config = [
145 | 'di' => [
146 | 'services' => [
147 | 'example_class' => [
148 | 'class' => ExampleClassWithArgs::class,
149 | 'arguments' => [
150 | 'arg1',
151 | 'arg2',
152 | ],
153 | ],
154 | ],
155 | ],
156 | ];
157 |
158 | Configurator::apply()
159 | ->configFromArray($config)
160 | ->to($this->container);
161 |
162 | $instance = $this->container->get('example_class');
163 |
164 | assertEquals(['arg1', 'arg2'], $instance->getConstructorArgs());
165 | }
166 |
167 | public function testItResolvesConstructorArgumentsIfTheyAreServiceNames()
168 | {
169 | $config = [
170 | 'arg1' => 'value1',
171 | 'arg2' => 'value2',
172 | 'di' => [
173 | 'services' => [
174 | 'example_class' => [
175 | 'class' => ExampleClassWithArgs::class,
176 | 'arguments' => [
177 | 'config.arg1',
178 | 'config.arg2',
179 | ],
180 | ],
181 | ],
182 | ],
183 | ];
184 |
185 | Configurator::apply()
186 | ->configFromArray($config)
187 | ->to($this->container);
188 |
189 | $instance = $this->container->get('example_class');
190 |
191 | assertEquals(['value1', 'value2'], $instance->getConstructorArgs());
192 | }
193 |
194 | public function testItUsesTheStringIfConstructorArgumentsAreClassNames()
195 | {
196 | $config = [
197 | 'di' => [
198 | 'services' => [
199 | 'example_class' => [
200 | 'class' => ExampleClassWithArgs::class,
201 | 'arguments' => [
202 | ExampleClass::class,
203 | 'arg2',
204 | ],
205 | ],
206 | ],
207 | ],
208 | ];
209 |
210 | Configurator::apply()
211 | ->configFromArray($config)
212 | ->to($this->container);
213 |
214 | $instance = $this->container->get('example_class');
215 |
216 | assertEquals([ExampleClass::class, 'arg2'], $instance->getConstructorArgs());
217 | }
218 |
219 | public function testItUsesComplexConstructorArguments()
220 | {
221 | $config = [
222 | 'di' => [
223 | 'services' => [
224 | 'example_class' => [
225 | 'class' => ExampleClassWithArgs::class,
226 | 'arguments' => [
227 | ['example_array'],
228 | new \stdClass(),
229 | ],
230 | ],
231 | ],
232 | ],
233 | ];
234 |
235 | Configurator::apply()
236 | ->configFromArray($config)
237 | ->to($this->container);
238 |
239 | $instance = $this->container->get('example_class');
240 |
241 | assertEquals([['example_array'], new \stdClass()], $instance->getConstructorArgs());
242 | }
243 |
244 | public function testItCallsSetterMethods()
245 | {
246 | $config = [
247 | 'di' => [
248 | 'services' => [
249 | 'example_class' => [
250 | 'class' => ExampleClass::class,
251 | 'methods' => [
252 | 'setValue' => ['the value'],
253 | ],
254 | ],
255 | ],
256 | ],
257 | ];
258 |
259 | Configurator::apply()
260 | ->configFromArray($config)
261 | ->to($this->container);
262 |
263 | $instance = $this->container->get('example_class');
264 |
265 | assertEquals('the value', $instance->getValue());
266 | }
267 |
268 | public function testItResolvesSetterMethodArgumentsIfTheyAreServiceNames()
269 | {
270 | $config = [
271 | 'arg' => 'value',
272 | 'di' => [
273 | 'services' => [
274 | 'example_class' => [
275 | 'class' => ExampleClass::class,
276 | 'methods' => [
277 | 'setValue' => ['config.arg'],
278 | ],
279 | ],
280 | ],
281 | ],
282 | ];
283 |
284 | Configurator::apply()
285 | ->configFromArray($config)
286 | ->to($this->container);
287 |
288 | $instance = $this->container->get('example_class');
289 |
290 | assertEquals('value', $instance->getValue());
291 | }
292 |
293 | public function testItUsesTheStringIffSetterMethodArgumentsAreClassNames()
294 | {
295 | $config = [
296 | 'di' => [
297 | 'services' => [
298 | 'example_class' => [
299 | 'class' => ExampleClass::class,
300 | 'methods' => [
301 | 'setValue' => [ExampleClass::class],
302 | ],
303 | ],
304 | ],
305 | ],
306 | ];
307 |
308 | Configurator::apply()
309 | ->configFromArray($config)
310 | ->to($this->container);
311 |
312 | $instance = $this->container->get('example_class');
313 |
314 | assertSame(ExampleClass::class, $instance->getValue());
315 | }
316 |
317 | public function testIsCreatesAServiceThroughAFactoryClass()
318 | {
319 | $config = [
320 | 'class_name' => ExampleClassWithArgs::class,
321 | 'di' => [
322 | 'services' => [
323 | 'example_service' => [
324 | 'factory' => ExampleFactory::class,
325 | 'arguments' => [
326 | 'config.class_name',
327 | 'example_argument',
328 | ],
329 | ],
330 | ],
331 | ],
332 | ];
333 |
334 | Configurator::apply()
335 | ->configFromArray($config)
336 | ->to($this->container);
337 |
338 | $instance = $this->container->get('example_service');
339 |
340 | assertInstanceOf(ExampleClassWithArgs::class, $instance);
341 | assertSame(['example_argument'], $instance->getConstructorArgs());
342 | }
343 |
344 | public function testItCanCreateAServiceAlias()
345 | {
346 | $config = [
347 | 'di' => [
348 | 'services' => [
349 | 'example_class' => [
350 | 'class' => ExampleClass::class,
351 | 'singleton' => true,
352 | ],
353 | 'example_alias' => [
354 | 'service' => 'example_class',
355 | ],
356 | ],
357 | ],
358 | ];
359 |
360 | Configurator::apply()
361 | ->configFromArray($config)
362 | ->to($this->container);
363 |
364 | assertSame($this->container->get('example_class'), $this->container->get('example_alias'));
365 | }
366 |
367 | public function testItInjectsTheContainerAsAConstructorDependency()
368 | {
369 | $config = [
370 | 'di' => [
371 | 'services' => [
372 | 'example_service' => [
373 | 'class' => ExampleClassWithArgs::class,
374 | 'arguments' => [Configurator::container()],
375 | ],
376 | ],
377 | ],
378 | ];
379 |
380 | Configurator::apply()
381 | ->configFromArray($config)
382 | ->to($this->container);
383 |
384 | $instance = $this->container->get('example_service');
385 |
386 | assertSame([$this->container], $instance->getConstructorArgs());
387 | }
388 |
389 | public function testItInjectsTheContainerAsAMethodDependency()
390 | {
391 | $config = [
392 | 'di' => [
393 | 'services' => [
394 | 'example_service' => [
395 | 'class' => ExampleClass::class,
396 | 'methods' => [
397 | 'setValue' => [Configurator::container()],
398 | ],
399 | ],
400 | ],
401 | ],
402 | ];
403 |
404 | Configurator::apply()
405 | ->configFromArray($config)
406 | ->to($this->container);
407 |
408 | $instance = $this->container->get('example_service');
409 |
410 | assertSame($this->container, $instance->getValue());
411 | }
412 |
413 | public function testItInjectsTheContainerAsFactoryDependency()
414 | {
415 | $config = [
416 | 'class_name' => ExampleClassWithArgs::class,
417 | 'di' => [
418 | 'services' => [
419 | 'example_service' => [
420 | 'factory' => ExampleFactory::class,
421 | 'arguments' => [
422 | 'config.class_name',
423 | Configurator::container(),
424 | ],
425 | ],
426 | ],
427 | ],
428 | ];
429 |
430 | Configurator::apply()
431 | ->configFromArray($config)
432 | ->to($this->container);
433 |
434 | $instance = $this->container->get('example_service');
435 |
436 | assertSame([$this->container], $instance->getConstructorArgs());
437 | }
438 | }
439 |
--------------------------------------------------------------------------------
/tests/mocks/BootableServiceProvider.php:
--------------------------------------------------------------------------------
1 | value = $value;
12 | }
13 |
14 | public function getValue()
15 | {
16 | return $this->value;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/tests/mocks/ExampleClassWithArgs.php:
--------------------------------------------------------------------------------
1 | constructorArgs = $constructorArgs;
12 | }
13 |
14 | public function getConstructorArgs()
15 | {
16 | return $this->constructorArgs;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/tests/mocks/ExampleContainer.php:
--------------------------------------------------------------------------------
1 | container = $container;
34 | }
35 |
36 | public function getContainer()
37 | {
38 | return $this->container;
39 | }
40 |
41 | public function addApplicationConfig(ApplicationConfig $config, $prefix = 'config')
42 | {
43 | }
44 |
45 | public function addServiceConfig(ServiceConfig $config)
46 | {
47 | }
48 |
49 | public function addInflectorConfig(InflectorConfig $config)
50 | {
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/tests/mocks/ExampleExtendedContainer.php:
--------------------------------------------------------------------------------
1 | deleteTestFiles();
15 | }
16 |
17 | /**
18 | * @param string $name
19 | *
20 | * @return string
21 | */
22 | protected function getTestPath($name)
23 | {
24 | $this->ensurePathExists();
25 |
26 | return "{$this->configFilePath}/$name";
27 | }
28 |
29 | /**
30 | * @param string $filename
31 | * @param array $config
32 | */
33 | protected function createPHPConfigFile($filename, array $config)
34 | {
35 | $code = 'createTestFile($filename, $code);
38 | }
39 |
40 | /**
41 | * @param string $filename
42 | * @param array $config
43 | */
44 | protected function createJSONConfigFile($filename, array $config)
45 | {
46 | $code = json_encode($config);
47 |
48 | $this->createTestFile($filename, $code);
49 | }
50 |
51 | /**
52 | * @param string $name
53 | * @param string $content
54 | */
55 | protected function createTestFile($name, $content = 'test content')
56 | {
57 | $this->ensurePathExists();
58 |
59 | file_put_contents("{$this->configFilePath}/$name", $content);
60 | }
61 |
62 | private function deleteTestFiles()
63 | {
64 | $this->ensurePathExists();
65 |
66 | // Test for safety!
67 | if (strpos($this->configFilePath, __DIR__) !== 0) {
68 | throw new \Exception('DANGER!!! - Config file is not local to this project');
69 | }
70 |
71 | $files = glob("{$this->configFilePath}/*");
72 |
73 | foreach ($files as $file) {
74 | unlink($file);
75 | }
76 | }
77 |
78 | private function ensurePathExists()
79 | {
80 | $this->configFilePath = __DIR__ . '/../.test-config';
81 |
82 | if (!file_exists($this->configFilePath)) {
83 | mkdir($this->configFilePath);
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/tests/unit/ApplicationConfigIteratorTest.php:
--------------------------------------------------------------------------------
1 | 'valueA',
14 | 'keyB' => 'valueB',
15 | ]);
16 |
17 | assertEquals(
18 | [
19 | 'keyA' => 'valueA',
20 | 'keyB' => 'valueB',
21 | ],
22 | iterator_to_array($iterator)
23 | );
24 | }
25 |
26 | public function testItIteratesRecursively()
27 | {
28 | $iterator = new ApplicationConfig([
29 | 'group1' => [
30 | 'keyA' => 'valueA',
31 | ],
32 | 'group2' => [
33 | 'keyB' => 'valueB',
34 | ],
35 | ]);
36 |
37 | assertEquals(
38 | [
39 | 'group1' => [
40 | 'keyA' => 'valueA',
41 | ],
42 | 'group1.keyA' => 'valueA',
43 | 'group2' => [
44 | 'keyB' => 'valueB',
45 | ],
46 | 'group2.keyB' => 'valueB',
47 | ],
48 | iterator_to_array($iterator)
49 | );
50 | }
51 |
52 | public function testItGoesMultipleLevels()
53 | {
54 | $iterator = new ApplicationConfig([
55 | 'group1' => [
56 | 'keyA' => 'valueA',
57 | 'group2' => [
58 | 'keyB' => 'valueB',
59 | ],
60 | ],
61 | ]);
62 |
63 | assertEquals(
64 | [
65 | 'group1' => [
66 | 'keyA' => 'valueA',
67 | 'group2' => [
68 | 'keyB' => 'valueB',
69 | ],
70 | ],
71 | 'group1.keyA' => 'valueA',
72 | 'group1.group2' => [
73 | 'keyB' => 'valueB',
74 | ],
75 | 'group1.group2.keyB' => 'valueB',
76 | ],
77 | iterator_to_array($iterator)
78 | );
79 | }
80 |
81 | public function testItRewinds()
82 | {
83 | $iterator = new ApplicationConfig([
84 | 'group1' => [
85 | 'keyA' => 'valueA',
86 | 'keyB' => 'valueB',
87 | 'keyC' => 'valueC',
88 | ],
89 | ]);
90 |
91 | next($iterator);
92 | next($iterator);
93 | next($iterator);
94 |
95 | assertEquals(
96 | [
97 | 'group1' => [
98 | 'keyA' => 'valueA',
99 | 'keyB' => 'valueB',
100 | 'keyC' => 'valueC',
101 | ],
102 | 'group1.keyA' => 'valueA',
103 | 'group1.keyB' => 'valueB',
104 | 'group1.keyC' => 'valueC',
105 | ],
106 | iterator_to_array($iterator)
107 | );
108 | }
109 |
110 | public function testItUsesADifferentSeparator()
111 | {
112 | $iterator = new ApplicationConfig([
113 | 'group1' => [
114 | 'keyA' => 'valueA',
115 | ],
116 | ], '->');
117 |
118 | assertEquals(
119 | [
120 | 'group1' => [
121 | 'keyA' => 'valueA',
122 | ],
123 | 'group1->keyA' => 'valueA',
124 | ],
125 | iterator_to_array($iterator)
126 | );
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/tests/unit/ApplicationConfigTest.php:
--------------------------------------------------------------------------------
1 | config = new ApplicationConfig([
23 | 'keyA' => 'valueA',
24 | 'group1' => [
25 | 'keyB' => 'valueB',
26 | 'null' => null,
27 | ],
28 | ]);
29 | }
30 |
31 | public function testItProvidesAccessToSimpleScalarValues()
32 | {
33 | assertEquals('valueA', $this->config['keyA']);
34 | }
35 |
36 | public function testItProvidesAccessToArrayValues()
37 | {
38 | assertEquals(['keyB' => 'valueB', 'null' => null], $this->config['group1']);
39 | }
40 |
41 | public function testItProvidesToSubValuesUsingDotNotation()
42 | {
43 | assertEquals('valueB', $this->config['group1.keyB']);
44 | }
45 |
46 | public function testItSaysIfAnEntryIsSet()
47 | {
48 | assertTrue(isset($this->config['group1.keyB']));
49 | }
50 |
51 | public function testItSaysIfAnEntryIsNotSet()
52 | {
53 | assertFalse(isset($this->config['bad.entry']));
54 | }
55 |
56 | public function testItSaysIfAnEntryIsSetIfItIsFalsey()
57 | {
58 | assertTrue(isset($this->config['group1.null']));
59 | }
60 |
61 | public function testItReturnsAllItsKeys()
62 | {
63 | assertEquals(
64 | [
65 | 'keyA',
66 | 'group1',
67 | 'group1.keyB',
68 | 'group1.null',
69 | ],
70 | $this->config->getKeys()
71 | );
72 | }
73 |
74 | public function testItCanBeConvertedToAnArray()
75 | {
76 | assertEquals(
77 | [
78 | 'keyA' => 'valueA',
79 | 'group1' => [
80 | 'keyB' => 'valueB',
81 | 'null' => null,
82 | ],
83 | ],
84 | $this->config->asArray()
85 | );
86 | }
87 |
88 | public function testItWorksWithADifferentSeperator()
89 | {
90 | $this->config = new ApplicationConfig([
91 | 'group1' => [
92 | 'keyA' => 'valueA',
93 | ],
94 | ], '->');
95 | assertEquals('valueA', $this->config['group1->keyA']);
96 | }
97 |
98 | public function testItThrowsForAnEmptySeparatorOnConstruction()
99 | {
100 | $this->expectException(InvalidArgumentException::class);
101 |
102 | $this->config = new ApplicationConfig([], '');
103 | }
104 |
105 | public function testItCannotHaveAValueSet()
106 | {
107 | $this->expectException(ReadOnlyException::class);
108 |
109 | $this->config['key'] = 'value';
110 | }
111 |
112 | public function testItCannotHaveAValueRemoved()
113 | {
114 | $this->expectException(ReadOnlyException::class);
115 |
116 | unset($this->config['keyA']);
117 | }
118 |
119 | public function testItMergesInNewConfig()
120 | {
121 | $config = new ApplicationConfig([
122 | 'group' => [
123 | 'keyA' => 'valueA',
124 | 'keyB' => 'valueX',
125 | ],
126 | ]);
127 |
128 | $config->merge(['group' => ['keyB' => 'valueB']]);
129 |
130 | assertSame('valueA', $config['group.keyA']);
131 | assertSame('valueB', $config['group.keyB']);
132 | }
133 |
134 | public function testItUpdatesTheSeparator()
135 | {
136 | $config = new ApplicationConfig([
137 | 'group' => [
138 | 'keyA' => 'valueA',
139 | ],
140 | ]);
141 |
142 | $config->setSeparator('/');
143 |
144 | assertSame('valueA', $config['group/keyA']);
145 | }
146 |
147 | public function testItThrowsForAnEmptySeparatorWhenSettingSeparator()
148 | {
149 | $this->expectException(InvalidArgumentException::class);
150 |
151 | $this->config = new ApplicationConfig([]);
152 | $this->config->setSeparator('');
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/tests/unit/ConfiguratorTest.php:
--------------------------------------------------------------------------------
1 | expectException(InvalidArgumentException::class);
23 |
24 | Configurator::apply()->configFromFile($this->getTestPath('config.php'));
25 | }
26 |
27 | public function testItThrowsAnExceptionWhenNoFilesAreNotFound()
28 | {
29 | $this->expectException(NoMatchingFilesException::class);
30 |
31 | Configurator::apply()->configFromFiles($this->getTestPath('config.php'));
32 | }
33 |
34 | public function testItThrowsWhenAnUnknownSettingIsSet()
35 | {
36 | $this->expectException(UnknownSettingException::class);
37 |
38 | Configurator::apply()->withSetting('unknown_setting', 'value');
39 | }
40 |
41 | public function testTheContainerIdentifierStringIsAlwaysTheSame()
42 | {
43 | assertSame(Configurator::container(), Configurator::container());
44 | }
45 |
46 | public function testItCanAcceptADifferentFileReader()
47 | {
48 | $container = new Container();
49 | $this->createTestFile('custom.xxx');
50 | CustomFileReader::reset();
51 |
52 | $configFile = $this->getTestPath('custom.xxx');
53 | Configurator::apply()
54 | ->withFileReader('.xxx', CustomFileReader::class)
55 | ->configFromFile($configFile)
56 | ->to($container);
57 |
58 | assertSame([$configFile], CustomFileReader::getReads());
59 | }
60 |
61 | public function testItCanUseDifferentContainerAdapters()
62 | {
63 | $container = new ExampleContainer();
64 | ExampleContainerAdapter::reset();
65 |
66 | Configurator::apply()
67 | ->withContainerAdapter(ExampleContainer::class, ExampleContainerAdapter::class)
68 | ->configFromArray([])
69 | ->to($container);
70 |
71 | assertSame(1, ExampleContainerAdapter::getNumberOfInstances());
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/tests/unit/ContainerAdapterFactoryTest.php:
--------------------------------------------------------------------------------
1 | subject = new ContainerAdapterFactory([
24 | ExampleContainer::class => ExampleContainerAdapter::class,
25 | ]);
26 | }
27 |
28 | public function testItCreatesAnInstanceOfTheContainerAdapter()
29 | {
30 | assertInstanceOf(
31 | ExampleContainerAdapter::class,
32 | $this->subject->create(new ExampleContainer())
33 | );
34 | }
35 |
36 | public function testItCreatesAnInstanceOfTheConfiguratorForSubclassedContainer()
37 | {
38 | assertInstanceOf(
39 | ExampleContainerAdapter::class,
40 | $this->subject->create(new ExampleExtendedContainer())
41 | );
42 | }
43 |
44 | public function testItThrowsIfContainerIsNotKnown()
45 | {
46 | $this->expectException(UnknownContainerException::class);
47 |
48 | $this->subject->create(new \stdClass());
49 | }
50 |
51 | public function testItThrowsIfNotAContainerAdapter()
52 | {
53 | $this->subject = new ContainerAdapterFactory([
54 | ExampleContainer::class => NotContainerAdapter::class,
55 | ]);
56 |
57 | $this->expectException(NotContainerAdapterException::class);
58 |
59 | $this->subject->create(new ExampleContainer());
60 | }
61 |
62 | public function testItSetsTheContainerOnTheConfigurator()
63 | {
64 | $container = new ExampleContainer();
65 | $configurator = $this->subject->create($container);
66 |
67 | assertSame($container, $configurator->getContainer());
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/tests/unit/Exception/EntryDoesNotExistExceptionTest.php:
--------------------------------------------------------------------------------
1 | getMessage()
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/tests/unit/Exception/InvalidConfigExceptionTest.php:
--------------------------------------------------------------------------------
1 | getMessage()
27 | );
28 | }
29 |
30 | public function testItCanBeCreatedWithAJSONFileError()
31 | {
32 | assertSame(
33 | 'Invalid JSON in "example.json": JSON Error Message',
34 | InvalidConfigException::fromJSONFileError('example.json', 'JSON Error Message')->getMessage()
35 | );
36 | }
37 |
38 | public function testItCanBeCreatedFromYAMLFileError()
39 | {
40 | assertSame(
41 | 'Invalid YAML in "example.yml": YAML Error Message',
42 | InvalidConfigException::fromYAMLFileError('example.yml', 'YAML Error Message')->getMessage()
43 | );
44 | }
45 |
46 | public function testItCanBeCreatedFromNameWhenClassAndFactoryAreSpecified()
47 | {
48 | assertSame(
49 | 'Both "class" and "factory" are specified for service "example"; these cannot be used together.',
50 | InvalidConfigException::fromNameWhenClassAndFactorySpecified('example')->getMessage()
51 | );
52 | }
53 |
54 | public function testItCanBeCreatedFromNameWhenClassAndServiceAreSpecified()
55 | {
56 | assertSame(
57 | 'Both "class" and "service" are specified for service "example"; these cannot be used together.',
58 | InvalidConfigException::fromNameWhenClassAndServiceSpecified('example')->getMessage()
59 | );
60 | }
61 |
62 | public function testItCanBeCreatedFromNameWhenFactoryAndServiceAreSpecified()
63 | {
64 | assertSame(
65 | 'Both "factory" and "service" are specified for service "example"; these cannot be used together.',
66 | InvalidConfigException::fromNameWhenFactoryAndServiceSpecified('example')->getMessage()
67 | );
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/tests/unit/Exception/MissingDependencyExceptionTest.php:
--------------------------------------------------------------------------------
1 | getMessage()
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/tests/unit/Exception/NoMatchingFilesExceptionTest.php:
--------------------------------------------------------------------------------
1 | getMessage()
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/tests/unit/Exception/NotClassDefinitionExceptionTest.php:
--------------------------------------------------------------------------------
1 | getMessage()
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/tests/unit/Exception/NotContainerAdapterExceptionTest.php:
--------------------------------------------------------------------------------
1 | getMessage()
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/tests/unit/Exception/ReadOnlyExceptionTest.php:
--------------------------------------------------------------------------------
1 | getMessage()
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/tests/unit/Exception/UnknownContainerExceptionTest.php:
--------------------------------------------------------------------------------
1 | getMessage()
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tests/unit/Exception/UnknownFileTypeExceptionTest.php:
--------------------------------------------------------------------------------
1 | getMessage()
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tests/unit/Exception/UnknownSettingExceptionTest.php:
--------------------------------------------------------------------------------
1 | getMessage()
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tests/unit/FileReader/FileLocatorTest.php:
--------------------------------------------------------------------------------
1 | locator = new FileLocator();
21 | }
22 |
23 | public function testItFindsFilesByGlobbing()
24 | {
25 | $this->createTestFile('config1.php');
26 | $this->createTestFile('config2.php');
27 | $this->createTestFile('config.json');
28 |
29 | $files = $this->locator->locate($this->getTestPath('*.php'));
30 |
31 | assertEquals([
32 | $this->getTestPath('config1.php'),
33 | $this->getTestPath('config2.php'),
34 | ], $files);
35 | }
36 |
37 | public function testItFindsFindsFilesByGlobbingWithBraces()
38 | {
39 | $this->createTestFile('global.php');
40 | $this->createTestFile('database.local.php');
41 | $this->createTestFile('nothing.php');
42 | $this->createTestFile('nothing.php.dist');
43 |
44 | $files = $this->locator->locate($this->getTestPath('{,*.}{global,local}.php'));
45 |
46 | assertEquals([
47 | $this->getTestPath('global.php'),
48 | $this->getTestPath('database.local.php'),
49 | ], $files);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/tests/unit/FileReader/JSONFileReaderTest.php:
--------------------------------------------------------------------------------
1 | reader = new JSONFileReader();
24 | }
25 |
26 | public function testItIsAFileReader()
27 | {
28 | assertInstanceOf(FileReader::class, $this->reader);
29 | }
30 |
31 | public function testItThrowsIfFileDoesNotExist()
32 | {
33 | $this->expectException(InvalidArgumentException::class);
34 |
35 | $this->reader->read('file-which-does-not-exist');
36 | }
37 |
38 | public function testReadsAPHPConfigFile()
39 | {
40 | $config = ['key' => 'value', 'sub' => ['key' => 'value']];
41 |
42 | $this->createTestFile('config.json', json_encode($config));
43 |
44 | assertEquals($config, $this->reader->read($this->getTestPath('config.json')));
45 | }
46 |
47 | public function testItThrowsIfTheConfigIsInvalid()
48 | {
49 | $this->expectException(InvalidConfigException::class);
50 |
51 | $this->createTestFile('config.json', 'not json');
52 |
53 | $this->reader->read($this->getTestPath('config.json'));
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/tests/unit/FileReader/PHPFileReaderTest.php:
--------------------------------------------------------------------------------
1 | reader = new PHPFileReader();
24 | }
25 |
26 | public function testItIsAFileReader()
27 | {
28 | assertInstanceOf(FileReader::class, $this->reader);
29 | }
30 |
31 | public function testItThrowsIfFileDoesNotExist()
32 | {
33 | $this->expectException(InvalidArgumentException::class);
34 |
35 | $this->reader->read('file-which-does-not-exist');
36 | }
37 |
38 | public function testReadsAPHPConfigFile()
39 | {
40 | $config = ['key' => 'value'];
41 | $code = 'createTestFile('config.php', $code);
44 |
45 | assertEquals($config, $this->reader->read($this->getTestPath('config.php')));
46 | }
47 |
48 | public function testItThrowsIfTheConfigIsInvalid()
49 | {
50 | $this->expectException(InvalidConfigException::class);
51 |
52 | $code = 'createTestFile('config.php', $code);
54 |
55 | $this->reader->read($this->getTestPath('config.php'));
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/tests/unit/FileReader/ReaderFactoryTest.php:
--------------------------------------------------------------------------------
1 | factory = new ReaderFactory([
26 | '.php' => PHPFileReader::class,
27 | '.json' => JSONFileReader::class,
28 | '.yaml' => YAMLFileReader::class,
29 | '.yml' => YAMLFileReader::class,
30 | ]);
31 | }
32 |
33 | /**
34 | * @dataProvider providerCreatesAppropriateFileReader
35 | *
36 | * @param string $extension
37 | * @param string $fileReaderClass
38 | */
39 | public function testCreatesAppropriateFileReader($extension, $fileReaderClass)
40 | {
41 | $filename = 'test' . $extension;
42 |
43 | $this->createTestFile($filename);
44 |
45 | $reader = $this->factory->create($this->getTestPath($filename));
46 |
47 | assertInstanceOf($fileReaderClass, $reader);
48 | }
49 |
50 | /**
51 | * @return \Generator
52 | */
53 | public function providerCreatesAppropriateFileReader()
54 | {
55 | $extensions = [
56 | '.json' => JSONFileReader::class,
57 | '.php' => PHPFileReader::class,
58 | '.yaml' => YAMLFileReader::class,
59 | '.yml' => YAMLFileReader::class,
60 | ];
61 |
62 | foreach ($extensions as $extension => $fileReaderClass) {
63 | yield [
64 | $extension,
65 | $fileReaderClass,
66 | ];
67 | }
68 | }
69 |
70 | public function testReturnsTheSameReaderForTheSameFileType()
71 | {
72 | $this->createTestFile('test1.php');
73 | $this->createTestFile('test2.php');
74 |
75 | $reader1 = $this->factory->create($this->getTestPath('test1.php'));
76 | $reader2 = $this->factory->create($this->getTestPath('test2.php'));
77 |
78 | assertSame($reader1, $reader2);
79 | }
80 |
81 | public function testItThrowsIfTheArgumentIsNotAFileName()
82 | {
83 | $this->expectException(InvalidArgumentException::class);
84 |
85 | $this->factory->create('missing-file.xxx');
86 | }
87 |
88 | public function testItThrowsIfThereIsNoRegisteredReaderForGivenFileType()
89 | {
90 | $this->createTestFile('test.unknown');
91 |
92 | $this->expectException(UnknownFileTypeException::class);
93 |
94 | $this->factory->create($this->getTestPath('test.unknown'));
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/tests/unit/FileReader/YAMLFileReaderTest.php:
--------------------------------------------------------------------------------
1 | reader = new YAMLFileReader();
25 | }
26 |
27 | public function testItIsAFileReader()
28 | {
29 | assertInstanceOf(FileReader::class, $this->reader);
30 | }
31 |
32 | public function testItThrowsIfFileDoesNotExist()
33 | {
34 | $this->expectException(InvalidArgumentException::class);
35 |
36 | $this->reader->read('file-which-does-not-exist');
37 | }
38 |
39 | public function testReadsAYAMLConfigFile()
40 | {
41 | $config = ['key' => 'value', 'sub' => ['key' => 'value']];
42 |
43 | $this->createTestFile('config.yml', Yaml\Yaml::dump($config));
44 |
45 | assertEquals($config, $this->reader->read($this->getTestPath('config.yml')));
46 | }
47 |
48 | public function testItThrowsIfTheConfigIsInvalid()
49 | {
50 | $this->expectException(InvalidConfigException::class);
51 |
52 | $this->createTestFile('config.yml', '[not yaml;');
53 |
54 | $this->reader->read($this->getTestPath('config.yml'));
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/tests/unit/InflectorConfigTest.php:
--------------------------------------------------------------------------------
1 | ['arg1', 'arg2']];
15 |
16 | $subject = new InflectorConfig([$interface => $methods]);
17 |
18 | assertEquals(
19 | [new InflectorDefinition($interface, $methods)],
20 | iterator_to_array($subject)
21 | );
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/tests/unit/InflectorDefinitionTest.php:
--------------------------------------------------------------------------------
1 | subject = new InflectorDefinition(
18 | 'interface_name',
19 | ['method1' => ['arg1', 'arg2']]
20 | );
21 | }
22 |
23 | public function testGetInterfaceReturnsTheInterfaceName()
24 | {
25 | assertEquals('interface_name', $this->subject->getInterface());
26 | }
27 |
28 | public function testGetMethodsReturnsTheMethods()
29 | {
30 | assertEquals(
31 | ['method1' => ['arg1', 'arg2']],
32 | $this->subject->getMethods()
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tests/unit/ServiceConfigTest.php:
--------------------------------------------------------------------------------
1 | __CLASS__,
15 | 'singleton' => false,
16 | 'arguments' => ['argument1', 'argument2'],
17 | 'method' => ['setSomething' => ['value']],
18 | ];
19 |
20 | $config = new ServiceConfig(['service_name' => $serviceConfig]);
21 |
22 | assertEquals(
23 | [new ServiceDefinition('service_name', $serviceConfig)],
24 | iterator_to_array($config)
25 | );
26 | }
27 |
28 | public function testItProvidesAListOfKeys()
29 | {
30 | $serviceConfig = [
31 | 'class' => __CLASS__,
32 | 'singleton' => false,
33 | 'arguments' => ['argument1', 'argument2'],
34 | 'method' => ['setSomething' => ['value']],
35 | ];
36 |
37 | $config = new ServiceConfig([
38 | 'service1' => $serviceConfig,
39 | 'service2' => $serviceConfig,
40 | ]);
41 |
42 | assertEquals(['service1', 'service2'], $config->getKeys());
43 | }
44 |
45 | public function testDefaultValueForSingletonCanBeSetToTrue()
46 | {
47 | $serviceConfig = ['class' => __CLASS__];
48 |
49 | $config = new ServiceConfig(['service_name' => $serviceConfig], true);
50 |
51 | assertTrue(iterator_to_array($config)[0]->isSingleton());
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/tests/unit/ServiceDefinitionTest.php:
--------------------------------------------------------------------------------
1 | __CLASS__,
15 | 'singleton' => false,
16 | 'arguments' => ['argument1', 'argument2'],
17 | 'methods' => ['setSomething' => ['value']],
18 | ];
19 |
20 | $definition = new ServiceDefinition('service_name', $config);
21 |
22 | assertEquals('service_name', $definition->getName());
23 | assertEquals(__CLASS__, $definition->getClass());
24 | assertFalse($definition->isFactory());
25 | assertFalse($definition->isSingleton());
26 | assertEquals(['argument1', 'argument2'], $definition->getArguments());
27 | assertEquals(['setSomething' => ['value']], $definition->getMethods());
28 | }
29 |
30 | public function testClassDefaultsToKey()
31 | {
32 | $definition = new ServiceDefinition('service_name', []);
33 |
34 | assertEquals('service_name', $definition->getClass());
35 | }
36 |
37 | public function testSingletonDefaultsToFalse()
38 | {
39 | $definition = new ServiceDefinition('service_name', []);
40 |
41 | assertFalse($definition->isSingleton());
42 | }
43 |
44 | public function testSingletonDefaultCanBeSetToToTrue()
45 | {
46 | $definition = new ServiceDefinition('service_name', [], true);
47 |
48 | assertTrue($definition->isSingleton());
49 | }
50 |
51 | public function testArgumentsDefaultToAnEmptyList()
52 | {
53 | $definition = new ServiceDefinition('service_name', []);
54 |
55 | assertEquals([], $definition->getArguments());
56 | }
57 |
58 | public function testMethodsDefaultToAnEmptyList()
59 | {
60 | $definition = new ServiceDefinition('service_name', []);
61 |
62 | assertEquals([], $definition->getMethods());
63 | }
64 |
65 | public function testServiceFactoryDefinition()
66 | {
67 | $definition = new ServiceDefinition('service_name', ['factory' => __CLASS__]);
68 |
69 | assertTrue($definition->isFactory());
70 | assertFalse($definition->isAlias());
71 | assertSame(__CLASS__, $definition->getClass());
72 | }
73 |
74 | public function testServiceAliasDefinition()
75 | {
76 | $definition = new ServiceDefinition('service_name', ['service' => __CLASS__]);
77 |
78 | assertTrue($definition->isAlias());
79 | assertFalse($definition->isFactory());
80 | assertSame(__CLASS__, $definition->getClass());
81 | }
82 |
83 | public function testItThrowIfClassAndFactoryAreDefined()
84 | {
85 | $this->expectException(InvalidConfigException::class);
86 |
87 | new ServiceDefinition('service_name', ['class' => __CLASS__, 'factory' => __CLASS__]);
88 | }
89 |
90 | public function testItThrowIfClassAndServiceAreDefined()
91 | {
92 | $this->expectException(InvalidConfigException::class);
93 |
94 | new ServiceDefinition('service_name', ['class' => __CLASS__, 'service' => __CLASS__]);
95 | }
96 |
97 | public function testItThrowIfFactoryAndServiceAreDefined()
98 | {
99 | $this->expectException(InvalidConfigException::class);
100 |
101 | new ServiceDefinition('service_name', ['factory' => __CLASS__, 'service' => __CLASS__]);
102 | }
103 | }
104 |
--------------------------------------------------------------------------------