├── .coveralls.yml
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── composer.json
├── examples
├── dependency_config.yml
├── dependency_logger.php
├── logger_config.json
├── logger_config.yml
├── multiple_loggers.php
└── single_logger.php
├── phpunit.xml.dist
├── src
├── Cascade.php
├── Config.php
├── Config
│ ├── ConfigLoader.php
│ └── Loader
│ │ ├── ClassLoader.php
│ │ ├── ClassLoader
│ │ ├── FormatterLoader.php
│ │ ├── HandlerLoader.php
│ │ ├── LoggerLoader.php
│ │ ├── ProcessorLoader.php
│ │ └── Resolver
│ │ │ ├── ConstructorResolver.php
│ │ │ └── ExtraOptionsResolver.php
│ │ ├── FileLoader
│ │ ├── FileLoaderAbstract.php
│ │ ├── Json.php
│ │ ├── PhpArray.php
│ │ └── Yaml.php
│ │ └── PhpArray.php
└── Util.php
└── tests
├── CascadeTest.php
├── Config
├── ConfigLoaderTest.php
└── Loader
│ ├── ClassLoader
│ ├── FormatterLoaderTest.php
│ ├── HandlerLoaderTest.php
│ ├── LoggerLoaderTest.php
│ ├── ProcessorLoaderTest.php
│ └── Resolver
│ │ ├── ConstructorResolverTest.php
│ │ └── ExtraOptionsResolverTest.php
│ ├── ClassLoaderTest.php
│ ├── FileLoader
│ ├── FileLoaderAbstractTest.php
│ ├── JsonTest.php
│ ├── PhpArrayTest.php
│ └── YamlTest.php
│ └── PhpArrayTest.php
├── ConfigTest.php
├── Fixtures.php
├── Fixtures
├── DependentClass.php
├── SampleClass.php
├── fixture_config.json
├── fixture_config.php
├── fixture_config.yml
├── fixture_invalid_config.php
├── fixture_sample.json
└── fixture_sample.yml
└── UtilTest.php
/.coveralls.yml:
--------------------------------------------------------------------------------
1 | coverage_clover: clover.xml
2 | json_path: coveralls-upload.json
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /composer.lock
3 | /composer.phar
4 | /phpunit.xml
5 | /vendor
6 | /.idea
7 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | env:
4 | global:
5 | - COVERALLS=0
6 | - PHPCS=0
7 |
8 | matrix:
9 | include:
10 | - php: 5.3
11 | dist: precise
12 | env: COMPOSER_MEMORY_LIMIT=-1
13 | - php: 5.4
14 | dist: trusty
15 | - php: 5.5
16 | dist: trusty
17 | - php: 5.6
18 | env: COVERALLS=1 PHPCS=1
19 | - php: 7
20 |
21 | allow_failures:
22 | # allow failure for Php < 5.6
23 | - php: 5.3
24 | - php: 5.4
25 | - php: 5.5
26 | fast_finish: true
27 |
28 | install:
29 | composer install --prefer-source
30 |
31 | script:
32 | - sh -c "if [ '$COVERALLS' = '1' ]; then ./vendor/bin/phpunit --coverage-clover clover.xml ; else ./vendor/bin/phpunit ; fi"
33 | - sh -c "if [ '$PHPCS' = '1' ]; then vendor/bin/phpcs -p --extensions=php --standard=PSR2 ./src ./tests ; fi"
34 |
35 | after_script:
36 | - sh -c "if [ '$COVERALLS' = '1' ]; then vendor/bin/coveralls -vvv ; fi"
37 |
38 |
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Raphaël Antonmattei
4 | Copyright (c) 2015 The Orchard
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Monolog Cascade [](https://travis-ci.org/theorchard/monolog-cascade) [](https://coveralls.io/r/theorchard/monolog-cascade?branch=master)
2 | ===============
3 |
4 | What is Monolog Cascade?
5 | ------------------------
6 |
7 | Monolog Cascade is a [Monolog](https://github.com/Seldaek/monolog) extension that allows you to set up and configure multiple loggers and handlers from a single config file.
8 |
9 | It's been inspired by the [`logging.config`](https://docs.python.org/3.4/library/logging.config.html?highlight=fileconfig#module-logging.config) Python module.
10 |
11 |
12 | ------------
13 |
14 |
15 | Installation
16 | ------------
17 |
18 | Add `monolog-cascade` as a requirement in your `composer.json` file or run
19 | ```sh
20 | $ composer require theorchard/monolog-cascade
21 | ```
22 |
23 | Note: Monolog Cascade requires PHP 5.3.9 or higher.
24 |
25 | Usage
26 | -----
27 |
28 | ```php
29 | info('Well, that works!');
44 | Cascade::getLogger('myLogger')->error('Maybe not...');
45 | ```
46 |
47 | Configuring your loggers
48 | ------------------------
49 | Monolog Cascade supports the following config formats:
50 | - Yaml
51 | - JSON
52 | - PHP File returning Array
53 | - PHP Array
54 |
55 | ### Configuration structure
56 | Here is a sample Yaml config file:
57 | ```yaml
58 |
59 | formatters:
60 | dashed:
61 | class: Monolog\Formatter\LineFormatter
62 | format: "%datetime%-%channel%.%level_name% - %message%\n"
63 | handlers:
64 | console:
65 | class: Monolog\Handler\StreamHandler
66 | level: DEBUG
67 | formatter: dashed
68 | processors: [memory_processor]
69 | stream: php://stdout
70 | info_file_handler:
71 | class: Monolog\Handler\StreamHandler
72 | level: INFO
73 | formatter: dashed
74 | stream: ./example_info.log
75 | processors:
76 | web_processor:
77 | class: Monolog\Processor\WebProcessor
78 | memory_processor:
79 | class: Monolog\Processor\MemoryUsageProcessor
80 | loggers:
81 | myLogger:
82 | handlers: [console, info_file_handler]
83 | processors: [web_processor]
84 | ```
85 |
86 | Here is a sample PHP config file:
87 | ```php
88 | 1,
92 |
93 | 'formatters' => array(
94 | 'spaced' => array(
95 | 'format' => "%datetime% %channel%.%level_name% %message%\n",
96 | 'include_stacktraces' => true
97 | ),
98 | 'dashed' => array(
99 | 'format' => "%datetime%-%channel%.%level_name% - %message%\n"
100 | ),
101 | ),
102 | 'handlers' => array(
103 | 'console' => array(
104 | 'class' => 'Monolog\Handler\StreamHandler',
105 | 'level' => 'DEBUG',
106 | 'formatter' => 'spaced',
107 | 'stream' => 'php://stdout'
108 | ),
109 |
110 | 'info_file_handler' => array(
111 | 'class' => 'Monolog\Handler\StreamHandler',
112 | 'level' => 'INFO',
113 | 'formatter' => 'dashed',
114 | 'stream' => './demo_info.log'
115 | ),
116 |
117 | 'error_file_handler' => array(
118 | 'class' => 'Monolog\Handler\StreamHandler',
119 | 'level' => 'ERROR',
120 | 'stream' => './demo_error.log',
121 | 'formatter' => 'spaced'
122 | )
123 | ),
124 | 'processors' => array(
125 | 'tag_processor' => array(
126 | 'class' => 'Monolog\Processor\TagProcessor'
127 | )
128 | ),
129 | 'loggers' => array(
130 | 'my_logger' => array(
131 | 'handlers' => array('console', 'info_file_handler')
132 | )
133 | )
134 | );
135 | ```
136 |
137 | More information on how the Cascade config parser loads and reads the parameters:
138 |
139 | Only the `loggers` key is required. If `formatters` and/or `handlers` are ommitted, Monolog's default will be used. `processors` is optional and if ommitted, no processors will be used. (See the "Optional Keys" section further below).
140 |
141 | Other keys are optional and would be interpreted as described below:
142 |
143 | - **_formatters_** - the derived associative array (from the Yaml or JSON) in which each key is the formatter identifier holds keys/values to configure your formatters.
144 | The only _reserved_ key is `class` and it should contain the classname of the formatter you would like to use. Other parameters will be interpreted as constructor parameters for that class and passed in when the formatter object is instanced by the Cascade config loader.
145 | If some parameters are not present in the constructor, they will be treated as extra parameters and Cascade will try to interpret them should they match any custom handler functions that are able to use them. (see [Extra Parameters](#user-content-extra-parameters-other-than-constructors) section below)
146 |
147 | If `class` is not provided Cascade will default to `Monolog\Formatter\LineFormatter`
148 |
149 | - **_handlers_** - the derived associative array (from the Yaml or JSON) in which each key is the handler identifier holds keys/values to configure your handlers.
The following keys are _reserved_:
150 | - `class` (optional): classname of the handler you would like to use
151 | - `formatter` (optional): formatter identifier that you have defined
152 | - `processors` (optional): array of processor identifiers that you have defined
153 | - `handlers` (optional): array of handler identifiers that you have defined
154 | - `handler` (optional): single handler identifier that you have defined
155 |
156 | Other parameters will be interpreted as constructor parameters for that Handler class and passed in when the handler object is instantiated by the Cascade config loader.
157 | If some parameters are not present in the constructor, they will be interpreted as extra parameters and Cascade will try to interpret them should they match any custom handler functions that are able to use them. (see [Extra Parameters](#user-content-extra-parameters-other-than-constructors) section below)
158 |
159 | If class is not provided Cascade will default to `Monolog\Handler\StreamHandler`
160 |
161 | - **_processors_** - the derived associative array (from the Yaml or JSON) in which each key is the processor identifier holds keys/values to configure your processors.
The following key is _reserved_:
162 | - `class` (required): classname of the processor you would like to use
163 |
164 | - **_loggers_** - the derived array (from the Yaml or JSON) in which each key is the logger identifier may contain only a `handlers` key and/or a `processors` key. You can decide what handler(s) and/or processor(s) you would like your logger to use.
165 |
166 | **Note**: If you would like to use objects as parameters for your handlers, you can pass a class name (using the `class` option) with the corresponding arguments just like you would configure your handler. Cascade recursively instantiates and loads those objects as it parses the config file. See [this sample config file](https://github.com/theorchard/monolog-cascade/blob/master/examples/dependency_config.yml).
167 |
168 | #### Parameter case
169 | You can use either _underscored_ or _camelCased_ style in your config files, it does not matter. However, it is important that they match the names of the arguments from the constructor method.
170 |
171 | ```php
172 | public function __construct($level = Logger::ERROR, $bubble = true, $appName = null)
173 | ```
174 |
175 | Using a Yaml file:
176 | ```yaml
177 | level: ERROR,
178 | bubble: true,
179 | app_name: "some app that I wrote"
180 | ```
181 |
182 | Cascade will _camelCase_ all the names of your parameters internally prior to be passed to the constructors.
183 |
184 | #### Optional keys
185 | `formatters`, `handlers` and `processors` keys are optional. If ommitted Cascade will default to Monolog's default formatter and handler: `Monolog\Formatter\LineFormatter` and `Monolog\Handler\StreamHandler` to `stderr`. If `processors` is ommitted, your logger(s) won't use any.
186 |
187 | #### Default parameters
188 | If a constructor method provides default value(s) in their declaration, Cascade will look it up and identify those parameters as optional with their default values. It can therefore be ommitted in your config file.
189 |
190 | #### Order of sections and params
191 | Order of the sections within the config file has no impact as long as they are formatted properly.
192 |
Order of parameters does not matter either.
193 |
194 | #### Extra parameters (other than constructor's)
195 | You may want to have your Formatters and/or Handlers consume values other than via the constructor. Some methods may be called to do additional set up when configuring your loggers. Cascade interprets those extra params 3 different ways and will try do so in that order:
196 |
197 | 1. _Instance method_
198 |
Your Formatter or Handler has a defined method that takes a param as input. In that case you can write it as follow in your config file:
199 |
200 | ```yaml
201 | formatters:
202 | spaced:
203 | class: Monolog\Formatter\LineFormatter
204 | format: "%datetime% %channel%.%level_name% %message%\n"
205 | include_stacktraces: true
206 | ```
207 | In this example, the `LineFormatter` class has an `includeStacktraces` method that takes a boolean. This method will be called upon instantiation.
208 |
209 | 2. _Public member_
210 |
Your Formatter or Handler has a public member that can be set.
211 |
212 | ```yaml
213 | formatters:
214 | spaced:
215 | class: Monolog\Formatter\SomeFormatter
216 | some_public_member: "some value"
217 | ```
218 | In this example, the public member will be set to the passed in value upon instantiation.
219 |
220 | 3. _Custom handler function_
221 |
See `FormatterLoader::initExtraOptionsHandlers` and `HandlerLoader::initExtraOptionsHandlers`. Those methods hold closures that can call instance methods if needed. The closure takes the instance and the parameter value as input.
222 |
223 | ```php
224 | self::$extraOptionHandlers = array(
225 | 'Monolog\Formatter\LineFormatter' => array(
226 | 'includeStacktraces' => function ($instance, $include) {
227 | $instance->includeStacktraces($include);
228 | }
229 | )
230 | );
231 | ```
232 | You can add handlers at runtime if needed. (i.e. if you write your logger handler for instance)
233 |
234 | Running Tests
235 | -------------
236 |
237 | Just run Phpunit:
238 | ```sh
239 | $ phpunit tests/
240 | ```
241 |
242 | Contributing
243 | ------------
244 |
245 | This extension is open source. Feel free to contribute and send a pull request!
246 |
247 | Make sure your code follows the [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) standards, is documented and has unit tests.
248 |
249 |
250 | What's next?
251 | ------------
252 | - add support for `.ini` config files
253 | - add support for namespaced Loggers with message propagation (through handler inheritance) so children loggers log messages using parent's handlers
254 | - add more custom function handlers to cover all the possible options of the current Monolog Formatters and Handlers
255 | - ~~add support for Processors (DONE)~~
256 | - ~~add support for DB/Store and other handlers requiring injection into the constructor ([issue #30](https://github.com/theorchard/monolog-cascade/issues/30)) (DONE)~~
257 | - other suggestions?
258 |
259 |
260 | Symfony Users
261 | -------------
262 | You may want to use [MonologBundle](https://github.com/symfony/MonologBundle) as it integrates directly with your favorite framework.
263 |
264 |
265 | Under The Hood
266 | --------------
267 | Here is a [Medium post](https://medium.com/orchard-technology/enhancing-monolog-699efff1051d#.dw6qu1c2p) if you want to know more about the implementation.
268 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "theorchard/monolog-cascade",
3 | "license": "MIT",
4 | "type": "library",
5 | "description": "Monolog extension to configure multiple loggers in the blink of an eye and access them from anywhere",
6 | "keywords": ["monolog", "cascade", "monolog extension", "monolog plugin", "monolog utils", "logger", "log"],
7 | "authors": [
8 | {
9 | "name": "Raphaël Antonmattei",
10 | "email": "rantonmattei@theorchard.com",
11 | "homepage": "https://github.com/rantonmattei"
12 | }
13 | ],
14 | "homepage": "https://github.com/theorchard/monolog-cascade",
15 | "require": {
16 | "php": ">=7.2",
17 | "symfony/config": "^2.7 || ^3.0 || ^4.0 || ^5.0",
18 | "symfony/options-resolver": "^2.7 || ^3.0 || ^4.0 || ^5.0",
19 | "symfony/serializer": "^2.7 || ^3.0 || ^4.0 || ^5.0",
20 | "symfony/yaml": "^2.7 || ^3.0 || ^4.0 || ^5.0",
21 | "monolog/monolog": "^1.13 || ^2.0"
22 | },
23 | "autoload": {
24 | "psr-4": {
25 | "Cascade\\": "src/"
26 | }
27 | },
28 | "autoload-dev": {
29 | "psr-4": {
30 | "Cascade\\Tests\\": "tests/"
31 | }
32 | },
33 | "require-dev": {
34 | "mikey179/vfsstream": "^1.6",
35 | "phpunit/phpcov": "^6.0",
36 | "phpunit/phpunit": "^8.5",
37 | "php-coveralls/php-coveralls": "^1.0",
38 | "squizlabs/php_codesniffer": "^2.5"
39 | },
40 | "prefer-stable": true,
41 | "extra": {
42 | "branch-alias": {
43 | "dev-master": "0.5.x-dev"
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/examples/dependency_config.yml:
--------------------------------------------------------------------------------
1 | ---
2 | version: 1
3 |
4 | disable_existing_loggers: false
5 |
6 | handlers:
7 | sentry:
8 | class: Monolog\Handler\RavenHandler
9 | level: DEBUG
10 | raven_client:
11 | class: Raven_Client
12 | options_or_dsn: https://something:dummy@getsentry.com/1
13 | redis:
14 | class: Monolog\Handler\RedisHandler
15 | level: DEBUG
16 | key: cascade
17 | redis:
18 | class: Redis
19 | connect: ['127.0.0.1', 6379]
20 | loggers:
21 | dependency:
22 | handlers: [sentry, redis]
23 |
--------------------------------------------------------------------------------
/examples/dependency_logger.php:
--------------------------------------------------------------------------------
1 | info('Well, that works!');
13 | Cascade::getLogger('dependency')->error('Maybe not...');
14 |
--------------------------------------------------------------------------------
/examples/logger_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "disable_existing_loggers": false,
4 | "formatters": {
5 | "spaced": {
6 | "class": "Monolog\\Formatter\\LineFormatter",
7 | "format": "%datetime% %channel%.%level_name% %message%\n",
8 | "include_stacktraces": true
9 | },
10 | "dashed": {
11 | "format": "%datetime%-%channel%.%level_name% - %message%\n"
12 | }
13 | },
14 | "handlers": {
15 | "console": {
16 | "class": "Monolog\\Handler\\StreamHandler",
17 | "level": "DEBUG",
18 | "formatter": "spaced",
19 | "stream": "php://stdout"
20 | },
21 | "info_file_handler": {
22 | "class": "Monolog\\Handler\\StreamHandler",
23 | "level": "INFO",
24 | "formatter": "dashed",
25 | "stream": "./example_info.log"
26 | },
27 | "error_file_handler": {
28 | "class": "Monolog\\Handler\\StreamHandler",
29 | "level": "ERROR",
30 | "formatter": "dashed",
31 | "stream": "./example_error.log"
32 | }
33 | },
34 | "loggers": {
35 | "loggerA": {
36 | "handlers": [
37 | "console",
38 | "info_file_handler"
39 | ]
40 | },
41 | "loggerB": {
42 | "handlers": [
43 | "console",
44 | "error_file_handler"
45 | ]
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/examples/logger_config.yml:
--------------------------------------------------------------------------------
1 | ---
2 | version: 1
3 |
4 | disable_existing_loggers: false
5 |
6 | formatters:
7 | spaced:
8 | # class is not needed here as LineFormatter is the default Formatter when none is provided
9 | class: Monolog\Formatter\LineFormatter
10 | format: "%datetime% %channel%.%level_name% %message%\n"
11 | include_stacktraces: true
12 | dashed:
13 | format: "%datetime%-%channel%.%level_name% - %message%\n"
14 |
15 | processors:
16 | web_processor:
17 | class: Monolog\Processor\WebProcessor
18 |
19 | handlers:
20 | console:
21 | class: Monolog\Handler\StreamHandler
22 | level: DEBUG
23 | formatter: spaced
24 | stream: php://stdout
25 | info_file_handler:
26 | class: Monolog\Handler\StreamHandler
27 | level: INFO
28 | formatter: dashed
29 | stream: ./example_info.log
30 | error_file_handler:
31 | class: Monolog\Handler\StreamHandler
32 | level: ERROR
33 | formatter: dashed
34 | stream: ./example_error.log
35 |
36 | loggers:
37 | loggerA:
38 | handlers: [console, info_file_handler]
39 | loggerB:
40 | handlers: [console, error_file_handler]
41 | processors: [web_processor]
42 |
--------------------------------------------------------------------------------
/examples/multiple_loggers.php:
--------------------------------------------------------------------------------
1 | info('Well, that works!');
13 | Cascade::getLogger('loggerB')->error('Maybe not...');
14 |
15 | // This should log into 2 different log files depending on the level: 'example_info.log' and 'example_error.log'
--------------------------------------------------------------------------------
/examples/single_logger.php:
--------------------------------------------------------------------------------
1 | pushHandler(new Monolog\Handler\StreamHandler('php://stdout'));
8 | $logger->info('Hellooooo World!');
9 |
10 | // you should see the following in the stdout:
11 | // [YYYY-mm-dd hh:mm:ss] some_logger.INFO: Hellooooo World!
12 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | tests/
7 |
8 |
9 |
10 |
11 |
12 | src/
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/Cascade.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade;
12 |
13 | use Monolog\Handler\HandlerInterface;
14 | use Monolog\Logger;
15 | use Monolog\Registry;
16 |
17 | use Cascade\Config\ConfigLoader;
18 |
19 | /**
20 | * Module class that manages Monolog Logger object
21 | * @see Logger
22 | * @see Registry
23 | *
24 | * @author Raphael Antonmattei
25 | */
26 | class Cascade
27 | {
28 | /**
29 | * Config class that holds options for all registered loggers
30 | * This is optional, you can set up your loggers programmatically
31 | * @var Config
32 | */
33 | protected static $config = null;
34 |
35 | /**
36 | * Create a new Logger object and push it to the registry
37 | * @see Logger::__construct
38 | *
39 | * @throws \InvalidArgumentException if no name is given
40 | *
41 | * @param string $name The logging channel
42 | * @param HandlerInterface[] $handlers Optional stack of handlers, the first
43 | * one in the array is called first, etc.
44 | * @param callable[] $processors Optional array of processors
45 | *
46 | * @return Logger Newly created Logger
47 | */
48 | public static function createLogger(
49 | $name,
50 | array $handlers = array(),
51 | array $processors = array()
52 | ) {
53 |
54 | if (empty($name)) {
55 | throw new \InvalidArgumentException('Logger name is required.');
56 | }
57 |
58 | $logger = new Logger($name, $handlers, $processors);
59 | Registry::addLogger($logger);
60 |
61 | return $logger;
62 | }
63 |
64 | /**
65 | * Get a Logger instance by name. Creates a new one if a Logger with the
66 | * provided name does not exist
67 | *
68 | * @param string $name Name of the requested Logger instance
69 | *
70 | * @return Logger Requested instance of Logger or new instance
71 | */
72 | public static function getLogger($name)
73 | {
74 | return Registry::hasLogger($name) ? Registry::getInstance($name) : self::createLogger($name);
75 | }
76 |
77 | /**
78 | * Alias of getLogger
79 | * @see getLogger
80 | *
81 | * @param string $name Name of the requested Logger instance
82 | *
83 | * @return Logger Requested instance of Logger or new instance
84 | */
85 | public static function logger($name)
86 | {
87 | return self::getLogger($name);
88 | }
89 |
90 | /**
91 | * Checks if a logger with given name already exists.
92 | *
93 | * @param string $name Name of the requested Logger instance
94 | *
95 | * @return bool true - Logger already exists; false - Logger does not exist
96 | */
97 | public static function hasLogger($name)
98 | {
99 | return Registry::hasLogger($name);
100 | }
101 |
102 | /**
103 | * Return the config options
104 | *
105 | * @return Config Array with configuration options
106 | */
107 | public static function getConfig()
108 | {
109 | return self::$config;
110 | }
111 |
112 | /**
113 | * Load configuration options from a file, a JSON or Yaml string or an array.
114 | *
115 | * @param string|array $resource Path to config file or configuration as string or array
116 | */
117 | public static function fileConfig($resource)
118 | {
119 | self::$config = new Config($resource, new ConfigLoader());
120 | self::$config->load();
121 | self::$config->configure();
122 | }
123 |
124 | /**
125 | * Load configuration options from a JSON or Yaml string. Alias of fileConfig.
126 | * @see fileConfig
127 | *
128 | * @param string $configString Configuration in string form
129 | */
130 | public static function loadConfigFromString($configString)
131 | {
132 | self::fileConfig($configString);
133 | }
134 |
135 | /**
136 | * Load configuration options from an array. Alias of fileConfig.
137 | * @see fileConfig
138 | *
139 | * @param array $configArray Configuration in array form
140 | */
141 | public static function loadConfigFromArray($configArray)
142 | {
143 | self::fileConfig($configArray);
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/src/Config.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade;
12 |
13 | use Monolog;
14 |
15 | use Cascade\Config\ConfigLoader;
16 | use Cascade\Config\Loader\ClassLoader\FormatterLoader;
17 | use Cascade\Config\Loader\ClassLoader\HandlerLoader;
18 | use Cascade\Config\Loader\ClassLoader\LoggerLoader;
19 | use Cascade\Config\Loader\ClassLoader\ProcessorLoader;
20 |
21 | /**
22 | * Config class that takes a config resource (file, JSON, Yaml, etc.) and configure Loggers with
23 | * all the required options (Formatters, Handlers, etc.)
24 | *
25 | * @author Raphael Antonmattei
26 | */
27 | class Config
28 | {
29 | /**
30 | * Input from user. This is either a file path, a string or an array
31 | * @var string|array
32 | */
33 | protected $input = null;
34 |
35 | /**
36 | * Array of logger configuration options: (logger attributes, formatters, handlers, etc.)
37 | * @var array
38 | */
39 | protected $options = array();
40 |
41 | /**
42 | * Array of Formatter objects
43 | * @var Monolog\Formatter\FormatterInterface[]
44 | */
45 | protected $formatters = array();
46 |
47 | /**
48 | * Array of Handler objects
49 | * @var Monolog\Handler\HandlerInterface[]
50 | */
51 | protected $handlers = array();
52 |
53 | /**
54 | * Array of Processor objects
55 | * @var callable[]
56 | */
57 | protected $processors = array();
58 |
59 | /**
60 | * Array of logger objects
61 | * @var Monolog\Logger[]
62 | */
63 | protected $loggers = array();
64 |
65 | /**
66 | * Config loader
67 | * @var ConfigLoader
68 | */
69 | protected $loader = null;
70 |
71 | /**
72 | * Instantiate a Config object
73 | *
74 | * @param string|array $input User input
75 | * @param ConfigLoader $loader Config loader object
76 | */
77 | public function __construct($input, ConfigLoader $loader)
78 | {
79 | $this->input = $input;
80 | $this->loader = $loader;
81 | }
82 |
83 | /**
84 | * Load config options into the options array using the injected loader
85 | */
86 | public function load()
87 | {
88 | $this->options = $this->loader->load($this->input);
89 | }
90 |
91 | /**
92 | * Configure and register Logger(s) according to the options passed in
93 | */
94 | public function configure()
95 | {
96 | if (!isset($this->options['disable_existing_loggers'])) {
97 | // We disable any existing loggers by default
98 | $this->options['disable_existing_loggers'] = true;
99 | }
100 |
101 | if ($this->options['disable_existing_loggers']) {
102 | Monolog\Registry::clear();
103 | }
104 |
105 | if (isset($this->options['formatters'])) {
106 | $this->configureFormatters($this->options['formatters']);
107 | }
108 |
109 | if (isset($this->options['processors'])) {
110 | $this->configureProcessors($this->options['processors']);
111 | }
112 |
113 | if (isset($this->options['handlers'])) {
114 | $this->configureHandlers($this->options['handlers']);
115 | }
116 |
117 | if (isset($this->options['loggers'])) {
118 | $this->configureLoggers($this->options['loggers']);
119 | } else {
120 | throw new \RuntimeException(
121 | 'Cannot configure loggers. No logger configuration options provided.'
122 | );
123 | }
124 | }
125 |
126 | /**
127 | * Configure the formatters
128 | *
129 | * @param array $formatters Array of formatter options
130 | */
131 | protected function configureFormatters(array $formatters = array())
132 | {
133 | foreach ($formatters as $formatterId => $formatterOptions) {
134 | $formatterLoader = new FormatterLoader($formatterOptions);
135 | $this->formatters[$formatterId] = $formatterLoader->load();
136 | }
137 | }
138 |
139 | /**
140 | * Configure the handlers
141 | *
142 | * @param array $handlers Array of handler options
143 | */
144 | protected function configureHandlers(array $handlers)
145 | {
146 | foreach ($handlers as $handlerId => $handlerOptions) {
147 | $handlerLoader = new HandlerLoader($handlerOptions, $this->formatters, $this->processors, $this->handlers);
148 | $this->handlers[$handlerId] = $handlerLoader->load();
149 | }
150 | }
151 |
152 | /**
153 | * Configure the processors
154 | *
155 | * @param array $processors Array of processor options
156 | */
157 | protected function configureProcessors(array $processors)
158 | {
159 | foreach ($processors as $processorName => $processorOptions) {
160 | $processorLoader = new ProcessorLoader($processorOptions, $this->processors);
161 | $this->processors[$processorName] = $processorLoader->load();
162 | }
163 | }
164 |
165 | /**
166 | * Configure the loggers
167 | *
168 | * @param array $loggers Array of logger options
169 | */
170 | protected function configureLoggers(array $loggers)
171 | {
172 | foreach ($loggers as $loggerName => $loggerOptions) {
173 | $loggerLoader = new LoggerLoader($loggerName, $loggerOptions, $this->handlers, $this->processors);
174 | $this->loggers[$loggerName] = $loggerLoader->load();
175 | }
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/src/Config/ConfigLoader.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Config;
12 |
13 | use Symfony\Component\Config\FileLocator;
14 | use Symfony\Component\Config\Loader\DelegatingLoader;
15 | use Symfony\Component\Config\Loader\LoaderResolver;
16 |
17 | use Cascade\Config\Loader\FileLoader\Json as JsonLoader;
18 | use Cascade\Config\Loader\FileLoader\PhpArray as ArrayFromFileLoader;
19 | use Cascade\Config\Loader\FileLoader\Yaml as YamlLoader;
20 | use Cascade\Config\Loader\PhpArray as ArrayLoader;
21 |
22 | /**
23 | * Loader class that loads Yaml, JSON and array from various resources (file, php array, string)
24 | * @see DelegatingLoader
25 | *
26 | * @author Raphael Antonmattei
27 | */
28 | class ConfigLoader extends DelegatingLoader
29 | {
30 | /**
31 | * Locator
32 | * @var FileLocator
33 | */
34 | protected $locator = null;
35 |
36 | /**
37 | * Instantiate a Loader object
38 | * @todo have the locator passed to the constructor so we can load more than one file
39 | */
40 | public function __construct()
41 | {
42 | $this->locator = new FileLocator();
43 |
44 | $loaderResolver = new LoaderResolver(array(
45 | // Do not change that order, it does matter as the resolver returns the first loader
46 | // that meets the requirements of the "supports" method for each of those loaders
47 | new ArrayLoader(),
48 | new ArrayFromFileLoader($this->locator),
49 | new JsonLoader($this->locator),
50 | new YamlLoader($this->locator)
51 | ));
52 |
53 | parent::__construct($loaderResolver);
54 | }
55 |
56 | /**
57 | * Loads a configuration resource: file, array, string
58 | *
59 | * @param mixed $resource Resource to load
60 | * @param string|null $type Not used
61 | *
62 | * @return array Array of config options
63 | */
64 | public function load($resource, $type = null)
65 | {
66 | return parent::load($resource);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/Config/Loader/ClassLoader.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Config\Loader;
12 |
13 | use Cascade\Config\Loader\ClassLoader\Resolver\ConstructorResolver;
14 | use Cascade\Config\Loader\ClassLoader\Resolver\ExtraOptionsResolver;
15 | use Cascade\Util;
16 |
17 | /**
18 | * Class Loader. Instantiate an object given a set of options. The option might look like:
19 | * array(
20 | * 'class' => 'Some\Class'
21 | * 'some_contruct_param' => 'abc',
22 | * 'some_param' => 'def',
23 | * 'some_other_param' => 'sdsad'
24 | * )
25 | *
26 | * Some of them are applicable to the constructor, other are applicable to other handlers.
27 | * For the latter you need to make sure there is a handler defined for that option
28 | *
29 | * @author Raphael Antonmattei
30 | * @author Dom Morgan
31 | */
32 | class ClassLoader
33 | {
34 | /**
35 | * Default class to use if none is provided in the option array
36 | */
37 | const DEFAULT_CLASS = '\stdClass';
38 |
39 | /**
40 | * Array of Closures indexed by class.
41 | * array(
42 | * '\Full\Absolute\Namespace\ClassName' => array(
43 | * 'myOption' => Closure
44 | * ), ...
45 | * )
46 | * @var array
47 | */
48 | public static $extraOptionHandlers = array();
49 |
50 | /**
51 | * Name of the class you want to load
52 | * @var String
53 | */
54 | public $class = null;
55 |
56 | /**
57 | * Reflected object of the class passed in
58 | * @var \ReflectionClass
59 | */
60 | protected $reflected = null;
61 |
62 | /**
63 | * Array of options. This is a raw copy of the options passed in.
64 | * @var array
65 | */
66 | protected $rawOptions = array();
67 |
68 | /**
69 | * Constructor
70 | *
71 | * @param array $options Array of options
72 | * The option array might look like:
73 | * array(
74 | * 'class' => 'Some\Class',
75 | * 'some_contruct_param' => 'abc',
76 | * 'some_param' => 'def',
77 | * 'some_other_param' => 'sdsad'
78 | * )
79 | */
80 | public function __construct(array $options)
81 | {
82 | $this->rawOptions = self::optionsToCamelCase($options);
83 | $this->setClass();
84 | $this->reflected = new \ReflectionClass($this->class);
85 | }
86 |
87 | /**
88 | * Set the class you want to load from the raw option array
89 | */
90 | protected function setClass()
91 | {
92 | if (!isset($this->rawOptions['class'])) {
93 | $this->rawOptions['class'] = static::DEFAULT_CLASS;
94 | }
95 |
96 | $this->class = $this->rawOptions['class'];
97 | unset($this->rawOptions['class']);
98 | }
99 |
100 | /**
101 | * Recursively loads objects into any of the rawOptions that represent
102 | * a class
103 | */
104 | protected function loadChildClasses()
105 | {
106 | foreach ($this->rawOptions as &$option) {
107 | if (is_array($option)
108 | && array_key_exists('class', $option)
109 | && class_exists($option['class'])
110 | ) {
111 | $classLoader = new ClassLoader($option);
112 | $option = $classLoader->load();
113 | }
114 | }
115 | }
116 |
117 | /**
118 | * Return option values indexed by name using camelCased keys
119 | *
120 | * @param array $options Array of options
121 | *
122 | * @return mixed[] Array of options indexed by (camelCased) name
123 | */
124 | public static function optionsToCamelCase(array $options)
125 | {
126 | $optionsByName = array();
127 |
128 | if (count($options)) {
129 | foreach ($options as $name => $value) {
130 | $optionsByName[Util::snakeToCamelCase($name)] = $value;
131 | }
132 | }
133 |
134 | return $optionsByName;
135 | }
136 |
137 | /**
138 | * Resolve options and returns them into 2 buckets:
139 | * - constructor options and
140 | * - extra options
141 | * Extra options are those that are not in the contructor. The constructor arguments determine
142 | * what goes into which bucket
143 | *
144 | * @return array Array of constructorOptions and extraOptions
145 | */
146 | private function resolveOptions()
147 | {
148 | $constructorResolver = new ConstructorResolver($this->reflected);
149 |
150 | // Contructor options are only the ones matching the contructor args' names
151 | $constructorOptions = array_intersect_key(
152 | $this->rawOptions,
153 | $constructorResolver->getConstructorArgs()
154 | );
155 |
156 | // Extra options are everything else than contructor options
157 | $extraOptions = array_diff_key(
158 | $this->rawOptions,
159 | $constructorOptions
160 | );
161 |
162 | $extraOptionsResolver = new ExtraOptionsResolver(
163 | $this->reflected,
164 | array_keys($extraOptions)
165 | );
166 |
167 | return array(
168 | $constructorResolver->resolve($constructorOptions),
169 | $extraOptionsResolver->resolve($extraOptions, $this)
170 | );
171 | }
172 |
173 | /**
174 | * Instantiate the reflected object using the parsed contructor args and set
175 | * extra options if any
176 | *
177 | * @return mixed Instance of the reflected object
178 | */
179 | public function load()
180 | {
181 | $this->loadChildClasses();
182 |
183 | list($constructorResolvedOptions, $extraResolvedOptions) = $this->resolveOptions();
184 | $instance = $this->reflected->newInstanceArgs($constructorResolvedOptions);
185 |
186 | $this->loadExtraOptions($extraResolvedOptions, $instance);
187 |
188 | return $instance;
189 | }
190 |
191 | /**
192 | * Indicates whether or not an option is supported by the loader
193 | *
194 | * @param string $extraOptionName Option name
195 | *
196 | * @return boolean Whether or not an option is supported by the loader
197 | */
198 | public function canHandle($extraOptionName)
199 | {
200 | return
201 | isset(self::$extraOptionHandlers['*'][$extraOptionName]) ||
202 | isset(self::$extraOptionHandlers[$this->class][$extraOptionName]);
203 | }
204 |
205 | /**
206 | * Get the corresponding handler for a given option
207 | *
208 | * @param string $extraOptionName Option name
209 | *
210 | * @return \Closure|null Corresponding Closure object or null if not found
211 | */
212 | public function getExtraOptionsHandler($extraOptionName)
213 | {
214 | // Check extraOption handlers that are valid for all classes
215 | if (isset(self::$extraOptionHandlers['*'][$extraOptionName])) {
216 | return self::$extraOptionHandlers['*'][$extraOptionName];
217 | }
218 |
219 | // Check extraOption handlers that are valid for the given class
220 | if (isset(self::$extraOptionHandlers[$this->class][$extraOptionName])) {
221 | return self::$extraOptionHandlers[$this->class][$extraOptionName];
222 | }
223 |
224 | return null;
225 | }
226 |
227 | /**
228 | * Set extra options if any were requested
229 | *
230 | * @param array $extraOptions Array of extra options (key => value)
231 | * @param mixed $instance Instance you want to set options for
232 | */
233 | public function loadExtraOptions($extraOptions, $instance)
234 | {
235 | foreach ($extraOptions as $name => $value) {
236 | if ($this->reflected->hasMethod($name)) {
237 | // There is a method to handle this option
238 | call_user_func_array(
239 | array($instance, $name),
240 | is_array($value) ? $value : array($value)
241 | );
242 | continue;
243 | }
244 | if ($this->reflected->hasProperty($name) &&
245 | $this->reflected->getProperty($name)->isPublic()
246 | ) {
247 | // There is a public member we can set for this option
248 | $instance->$name = $value;
249 | continue;
250 | }
251 |
252 | if ($this->canHandle($name)) {
253 | // There is a custom handler for that option
254 | $closure = $this->getExtraOptionsHandler($name);
255 | $closure($instance, $value);
256 | }
257 | }
258 | }
259 | }
260 |
--------------------------------------------------------------------------------
/src/Config/Loader/ClassLoader/FormatterLoader.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Config\Loader\ClassLoader;
12 |
13 | use Monolog;
14 |
15 | use Cascade\Config\Loader\ClassLoader;
16 |
17 | /**
18 | * Formatter Loader. Loads the Formatter options, validate them and instantiates
19 | * a Formatter object (implementing Monolog\Formatter\FormatterInterface) with all
20 | * the corresponding options
21 | * @see ClassLoader
22 | *
23 | * @author Raphael Antonmattei
24 | */
25 | class FormatterLoader extends ClassLoader
26 | {
27 | /**
28 | * Default formatter class to use if none is provided in the option array
29 | */
30 | const DEFAULT_CLASS = 'Monolog\Formatter\LineFormatter';
31 |
32 | /**
33 | * Constructor
34 | * @see ClassLoader::__construct
35 | * @see Monolog\Formatter classes for formatter options
36 | *
37 | * @param array $formatterOptions Formatter options
38 | */
39 | public function __construct(array $formatterOptions)
40 | {
41 | parent::__construct($formatterOptions);
42 |
43 | self::initExtraOptionsHandlers();
44 | }
45 |
46 | /**
47 | * Loads the closures as option handlers. Add handlers to this function if
48 | * you want to support additional custom options.
49 | *
50 | * The syntax is the following:
51 | * array(
52 | * '\Full\Absolute\Namespace\ClassName' => array(
53 | * 'myOption' => Closure
54 | * ), ...
55 | * )
56 | *
57 | * You can use the '*' wildcard if you want to set up an option for all
58 | * Formatter classes
59 | *
60 | * @todo add handlers to handle extra options for all known Monolog formatters
61 | */
62 | public static function initExtraOptionsHandlers()
63 | {
64 | self::$extraOptionHandlers = array(
65 | 'Monolog\Formatter\LineFormatter' => array(
66 | 'includeStacktraces' => function (Monolog\Formatter\LineFormatter $instance, $include) {
67 | $instance->includeStacktraces($include);
68 | }
69 | )
70 | );
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/Config/Loader/ClassLoader/HandlerLoader.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Config\Loader\ClassLoader;
12 |
13 | use Monolog\Formatter\FormatterInterface;
14 | use Monolog\Handler\HandlerInterface;
15 | use Monolog\Handler\LogglyHandler;
16 |
17 | use Cascade\Config\Loader\ClassLoader;
18 |
19 | /**
20 | * Handler Loader. Loads the Handler options, validate them and instantiates
21 | * a Handler object (implementing Monolog\Handler\HandlerInterface) with all
22 | * the corresponding options
23 | * @see ClassLoader
24 | *
25 | * @author Raphael Antonmattei
26 | */
27 | class HandlerLoader extends ClassLoader
28 | {
29 | /**
30 | * Default handler class to use if none is provided in the option array
31 | */
32 | const DEFAULT_CLASS = 'Monolog\Handler\StreamHandler';
33 |
34 | /**
35 | * Constructor
36 | * @see ClassLoader::__construct
37 | * @see \Monolog\Handler classes for handler options
38 | *
39 | * @param array $handlerOptions Handler options
40 | * @param FormatterInterface[] $formatters Array of formatter to pick from
41 | * @param callable[] $processors Array of processors to pick from
42 | * @param callable[] $handlers Array of handlers to pick from
43 | */
44 | public function __construct(
45 | array &$handlerOptions,
46 | array $formatters = array(),
47 | array $processors = array(),
48 | array $handlers = array()
49 | ) {
50 | $this->populateFormatters($handlerOptions, $formatters);
51 | $this->populateProcessors($handlerOptions, $processors);
52 | $this->populateHandlers($handlerOptions, $handlers);
53 | parent::__construct($handlerOptions);
54 |
55 | self::initExtraOptionsHandlers();
56 | }
57 |
58 | /**
59 | * Replace the formatter name in the option array with the corresponding object from the
60 | * formatter array passed in if it exists.
61 | *
62 | * If no formatter is specified in the options, Monolog will use its default formatter for the
63 | * handler
64 | *
65 | * @throws \InvalidArgumentException
66 | *
67 | * @param array &$handlerOptions Handler options
68 | * @param FormatterInterface[] $formatters Array of formatter to pick from
69 | */
70 | private function populateFormatters(array &$handlerOptions, array $formatters)
71 | {
72 | if (isset($handlerOptions['formatter'])) {
73 | if (isset($formatters[$handlerOptions['formatter']])) {
74 | $handlerOptions['formatter'] = $formatters[$handlerOptions['formatter']];
75 | } else {
76 | throw new \InvalidArgumentException(
77 | sprintf(
78 | 'Formatter %s not found in the configured formatters',
79 | $handlerOptions['formatter']
80 | )
81 | );
82 | }
83 | }
84 | }
85 |
86 | /**
87 | * Replace the processors in the option array with the corresponding callable from the
88 | * array of loaded and callable processors, if it exists.
89 | *
90 | * @throws \InvalidArgumentException
91 | *
92 | * @param array &$handlerOptions Handler options
93 | * @param callable[] $processors Array of processors to pick from
94 | */
95 | private function populateProcessors(array &$handlerOptions, array $processors)
96 | {
97 | $processorArray = array();
98 |
99 | if (isset($handlerOptions['processors'])) {
100 | foreach ($handlerOptions['processors'] as $processorId) {
101 | if (isset($processors[$processorId])) {
102 | $processorArray[] = $processors[$processorId];
103 | } else {
104 | throw new \InvalidArgumentException(
105 | sprintf(
106 | 'Cannot add processor "%s" to the handler. Processor not found.',
107 | $processorId
108 | )
109 | );
110 | }
111 | }
112 |
113 | $handlerOptions['processors'] = $processorArray;
114 | }
115 | }
116 |
117 | /**
118 | * Replace the handler or handlers in the option array with the corresponding callable(s) from the
119 | * array of loaded and callable handlers, if they exist.
120 | *
121 | * @throws \InvalidArgumentException
122 | *
123 | * @param array &$handlerOptions Handler options
124 | * @param callable[] $handlers Array of handlers to pick from
125 | */
126 | private function populateHandlers(array &$handlerOptions, array $handlers)
127 | {
128 | $handlerArray = array();
129 |
130 | if (isset($handlerOptions['handlers'])) {
131 | foreach ($handlerOptions['handlers'] as $handlerId) {
132 | if (isset($handlers[$handlerId])) {
133 | $handlerArray[] = $handlers[$handlerId];
134 | } else {
135 | throw new \InvalidArgumentException(
136 | sprintf(
137 | 'Cannot add handler "%s" to the handler. Handler not found.',
138 | $handlerId
139 | )
140 | );
141 | }
142 | }
143 |
144 | $handlerOptions['handlers'] = $handlerArray;
145 | }
146 |
147 | if (isset($handlerOptions['handler'])) {
148 | $handlerId = $handlerOptions['handler'];
149 |
150 | if (isset($handlers[$handlerId])) {
151 | $handlerOptions['handler'] = $handlers[$handlerId];
152 | } else {
153 | throw new \InvalidArgumentException(
154 | sprintf(
155 | 'Cannot add handler "%s" to the handler. Handler not found.',
156 | $handlerId
157 | )
158 | );
159 | }
160 | }
161 | }
162 |
163 | /**
164 | * Loads the closures as option handlers. Add handlers to this function if
165 | * you want to support additional custom options.
166 | *
167 | * The syntax is the following:
168 | * array(
169 | * '\Full\Absolute\Namespace\ClassName' => array(
170 | * 'myOption' => Closure
171 | * ), ...
172 | * )
173 | *
174 | * You can use the '*' wildcard if you want to set up an option for all
175 | * Handler classes
176 | */
177 | public static function initExtraOptionsHandlers()
178 | {
179 | self::$extraOptionHandlers = array(
180 | '*' => array(
181 | 'formatter' => function (HandlerInterface $instance, FormatterInterface $formatter) {
182 | $instance->setFormatter($formatter);
183 | },
184 | 'processors' => function (HandlerInterface $instance, array $processors) {
185 | // We need to reverse the array because Monolog "pushes" processors to top of the stack
186 | foreach (array_reverse($processors) as $processor) {
187 | $instance->pushProcessor($processor);
188 | }
189 | }
190 | ),
191 | 'Monolog\Handler\LogglyHandler' => array(
192 | 'tags' => function (LogglyHandler $instance, $tags) {
193 | $instance->setTag($tags);
194 | }
195 | )
196 | );
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/src/Config/Loader/ClassLoader/LoggerLoader.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Config\Loader\ClassLoader;
12 |
13 | use Cascade\Cascade;
14 |
15 | use Monolog;
16 |
17 | /**
18 | * Logger Loader. Instantiate a Logger and set passed in handlers and processors if any
19 | * @see ClassLoader
20 | *
21 | * @author Raphael Antonmattei
22 | */
23 | class LoggerLoader
24 | {
25 | /**
26 | * Array of options
27 | * @var array
28 | */
29 | protected $loggerOptions = array();
30 |
31 | /**
32 | * Array of handlers
33 | * @var Monolog\Handler\HandlerInterface[]
34 | */
35 | protected $handlers = array();
36 |
37 | /**
38 | * Array of processors
39 | * @var callable[]
40 | */
41 | protected $processors = array();
42 |
43 | /**
44 | * Logger
45 | * @var Monolog\Logger
46 | */
47 | protected $logger = null;
48 |
49 | /**
50 | * Constructor
51 | *
52 | * @param string $loggerName Name of the logger
53 | * @param array $loggerOptions Array of logger options
54 | * @param Monolog\Handler\HandlerInterface[] $handlers Array of Monolog handlers
55 | * @param callable[] $processors Array of processors
56 | */
57 | public function __construct(
58 | $loggerName,
59 | array $loggerOptions = array(),
60 | array $handlers = array(),
61 | array $processors = array()
62 | ) {
63 | $this->loggerOptions = $loggerOptions;
64 | $this->handlers = $handlers;
65 | $this->processors = $processors;
66 |
67 | // This instantiates a Logger object and set it to the Registry
68 | $this->logger = Cascade::getLogger($loggerName);
69 | }
70 |
71 | /**
72 | * Resolve handlers for that Logger (if any provided) against an array of previously set
73 | * up handlers. Returns an array of valid handlers.
74 | *
75 | * @throws \InvalidArgumentException if a requested handler is not available in $handlers
76 | *
77 | * @param array $loggerOptions Array of logger options
78 | * @param Monolog\Handler\HandlerInterface[] $handlers Available Handlers to resolve against
79 | *
80 | * @return Monolog\Handler\HandlerInterface[] Array of Monolog handlers
81 | */
82 | public function resolveHandlers(array $loggerOptions, array $handlers)
83 | {
84 | $handlerArray = array();
85 |
86 | if (isset($loggerOptions['handlers'])) {
87 | // If handlers have been specified and, they do exist in the provided handlers array
88 | // We return an array of handler objects
89 | foreach ($loggerOptions['handlers'] as $handlerId) {
90 | if (isset($handlers[$handlerId])) {
91 | $handlerArray[] = $handlers[$handlerId];
92 | } else {
93 | throw new \InvalidArgumentException(
94 | sprintf(
95 | 'Cannot add handler "%s" to the logger "%s". Handler not found.',
96 | $handlerId,
97 | $this->logger->getName()
98 | )
99 | );
100 | }
101 | }
102 | }
103 |
104 | // If nothing is set there is nothing to resolve, Handlers will be Monolog's default
105 |
106 | return $handlerArray;
107 | }
108 |
109 | /**
110 | * Resolve processors for that Logger (if any provided) against an array of previously set
111 | * up processors.
112 | *
113 | * @throws \InvalidArgumentException if a requested processor is not available in $processors
114 | *
115 | * @param array $loggerOptions Array of logger options
116 | * @param callable[] $processors Available Processors to resolve against
117 | *
118 | * @return callable[] Array of Monolog processors
119 | */
120 | public function resolveProcessors(array $loggerOptions, $processors)
121 | {
122 | $processorArray = array();
123 |
124 | if (isset($loggerOptions['processors'])) {
125 | // If processors have been specified and, they do exist in the provided processors array
126 | // We return an array of processor objects
127 | foreach ($loggerOptions['processors'] as $processorId) {
128 | if (isset($processors[$processorId])) {
129 | $processorArray[] = $processors[$processorId];
130 | } else {
131 | throw new \InvalidArgumentException(
132 | sprintf(
133 | 'Cannot add processor "%s" to the logger "%s". Processor not found.',
134 | $processorId,
135 | $this->logger->getName()
136 | )
137 | );
138 | }
139 | }
140 | }
141 |
142 | // If nothing is set there is nothing to resolve, Processors will be Monolog's default
143 |
144 | return $processorArray;
145 | }
146 |
147 | /**
148 | * Add handlers to the Logger
149 | *
150 | * @param Monolog\Handler\HandlerInterface[] Array of Monolog handlers
151 | */
152 | private function addHandlers(array $handlers)
153 | {
154 | // We need to reverse the array because Monolog "pushes" handlers to top of the stack
155 | foreach (array_reverse($handlers) as $handler) {
156 | $this->logger->pushHandler($handler);
157 | }
158 | }
159 |
160 | /**
161 | * Add processors to the Logger
162 | *
163 | * @param callable[] Array of Monolog processors
164 | */
165 | private function addProcessors(array $processors)
166 | {
167 | // We need to reverse the array because Monolog "pushes" processors to top of the stack
168 | foreach (array_reverse($processors) as $processor) {
169 | $this->logger->pushProcessor($processor);
170 | }
171 | }
172 |
173 | /**
174 | * Return the instantiated Logger object based on its name
175 | *
176 | * @return Monolog\Logger Logger object
177 | */
178 | public function load()
179 | {
180 | $this->addHandlers($this->resolveHandlers($this->loggerOptions, $this->handlers));
181 | $this->addProcessors($this->resolveProcessors($this->loggerOptions, $this->processors));
182 |
183 | return $this->logger;
184 | }
185 | }
186 |
--------------------------------------------------------------------------------
/src/Config/Loader/ClassLoader/ProcessorLoader.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Config\Loader\ClassLoader;
12 |
13 | use Cascade\Config\Loader\ClassLoader;
14 |
15 | use Monolog;
16 |
17 | /**
18 | * Processor Loader. Loads the Processor options, validate them and instantiates
19 | * a Processor object (implementing Monolog\Processor\ProcessorInterface) with all
20 | * the corresponding options
21 | * @see ClassLoader
22 | *
23 | * @author Kate Burdon
24 | * @author Raphael Antonmattei
25 | */
26 | class ProcessorLoader extends ClassLoader
27 | {
28 | /**
29 | * Constructor
30 | * @see ClassLoader::__construct
31 | * @see Monolog\Handler classes for handler options
32 | *
33 | * @param array $processorOptions Processor options
34 | * @param Monolog\Processor\ProcessorInterface[] $processors Array of processors to pick from
35 | */
36 | public function __construct(array &$processorOptions, array $processors = array())
37 | {
38 | parent::__construct($processorOptions);
39 |
40 | // @todo add additional options later? Is the "tags" option needed in this implementation?
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Config/Loader/ClassLoader/Resolver/ConstructorResolver.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Config\Loader\ClassLoader\Resolver;
12 |
13 | use Cascade\Util;
14 | use Symfony\Component\OptionsResolver\OptionsResolver;
15 |
16 | /**
17 | * Constructor Resolver. Pull args from the contructor and set up an option
18 | * resolver against args requirements
19 | *
20 | * @author Raphael Antonmattei
21 | */
22 | class ConstructorResolver
23 | {
24 | /**
25 | * Reflection class for which you want to resolve constructor options
26 | * @var \ReflectionClass
27 | */
28 | protected $reflected = null;
29 |
30 | /**
31 | * Registry of resolvers
32 | * @var array
33 | */
34 | private static $resolvers = array();
35 |
36 | /**
37 | * Associative array of contructor args to resolve against
38 | * @var \ReflectionParameter[]
39 | */
40 | protected $constructorArgs = array();
41 |
42 | /**
43 | * Contructor
44 | *
45 | * @param \ReflectionClass $reflected Reflection class for which you want to resolve
46 | * constructor options
47 | */
48 | public function __construct(\ReflectionClass $reflected)
49 | {
50 | $this->reflected = $reflected;
51 | $this->initConstructorArgs();
52 | }
53 |
54 | /**
55 | * Fetches constructor args (array of ReflectionParameter) from the reflected class
56 | * and set them as an associative array
57 | *
58 | * Convert the parameter names to camelCase for classes that have contructor
59 | * params defined in snake_case for consistency with the options
60 | */
61 | public function initConstructorArgs()
62 | {
63 | $constructor = $this->reflected->getConstructor();
64 |
65 | if (!is_null($constructor)) {
66 | // Index parameters by their names
67 | foreach ($constructor->getParameters() as $param) {
68 | $name = Util::snakeToCamelCase($param->getName());
69 | $this->constructorArgs[$name] = $param;
70 | }
71 | }
72 | }
73 |
74 | /**
75 | * Returns the contructor args as an associative array
76 | *
77 | * @return array Contructor args
78 | */
79 | public function getConstructorArgs()
80 | {
81 | return $this->constructorArgs;
82 | }
83 |
84 | /**
85 | * Returns the reflected object
86 | *
87 | * @return \ReflectionClass
88 | */
89 | public function getReflected()
90 | {
91 | return $this->reflected;
92 | }
93 |
94 | /**
95 | * Configure options for the provided OptionResolver to match contructor args requirements
96 | *
97 | * @param OptionsResolver $optionsResolver OptionResolver to configure
98 | */
99 | protected function configureOptions(OptionsResolver $optionsResolver)
100 | {
101 | foreach ($this->constructorArgs as $name => $param) {
102 | if ($param->isOptional() && $param->isDefaultValueAvailable()) {
103 | $optionsResolver->setDefault($name, $param->getDefaultValue());
104 | } else {
105 | $optionsResolver->setRequired($name);
106 | }
107 | }
108 | }
109 |
110 | /**
111 | * Loops through constructor args and buid an ordered array of args using
112 | * the option values passed in. We assume the passed in array has been resolved already.
113 | * i.e. That the arg name has an entry in the option array.
114 | *
115 | * @param array $hashOfOptions Array of options
116 | *
117 | * @return array Array of ordered args
118 | */
119 | public function hashToArgsArray($hashOfOptions)
120 | {
121 | $optionsArray = new \SplFixedArray(count($hashOfOptions));
122 |
123 | foreach ($this->constructorArgs as $name => $param) {
124 | $optionsArray[$param->getPosition()] = $hashOfOptions[$name];
125 | }
126 |
127 | return $optionsArray->toArray();
128 | }
129 |
130 | /**
131 | * Resolve options against constructor args
132 | *
133 | * @param array $options Array of option values. Expected array looks like:
134 | * array(
135 | * 'someParam' => 'def',
136 | * 'someOtherParam' => 'sdsad'
137 | * )
138 | *
139 | * @return array Array of resolved ordered args
140 | */
141 | public function resolve(array $options)
142 | {
143 | $reflectedClassName = $this->reflected->getName();
144 |
145 | // We check if that constructor has been configured before and is in the registry
146 | if (!isset(self::$resolvers[$reflectedClassName])) {
147 | self::$resolvers[$reflectedClassName] = new OptionsResolver();
148 |
149 | $this->configureOptions(self::$resolvers[$reflectedClassName]);
150 | }
151 |
152 | return $this->hashToArgsArray(
153 | self::$resolvers[$reflectedClassName]->resolve($options)
154 | );
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/src/Config/Loader/ClassLoader/Resolver/ExtraOptionsResolver.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Config\Loader\ClassLoader\Resolver;
12 |
13 | use Symfony\Component\OptionsResolver\OptionsResolver;
14 |
15 | use Cascade\Config\Loader\ClassLoader;
16 |
17 | /**
18 | * Extra options resolver. Set up an option resolver for the passed in params and
19 | * apply validation rules if any
20 | *
21 | * @author Raphael Antonmattei
22 | */
23 | class ExtraOptionsResolver
24 | {
25 | /**
26 | * Reflection class for which you want to resolve extra options
27 | * @var \ReflectionClass
28 | */
29 | protected $reflected = null;
30 |
31 | /**
32 | * Registry of resolvers
33 | * @var OptionsResolver[]
34 | */
35 | private static $resolvers = array();
36 |
37 | /**
38 | * Associative array of parameters to resolve against
39 | * @var array
40 | */
41 | protected $params = array();
42 |
43 | /**
44 | * Constructor
45 | *
46 | * @param \ReflectionClass $reflected Reflection class for which you want to resolve
47 | * extra options
48 | * @param array $params Associative array of extra parameters we want to resolve against
49 | */
50 | public function __construct(\ReflectionClass $reflected, array $params = array())
51 | {
52 | $this->reflected = $reflected;
53 | $this->setParams($params);
54 | }
55 |
56 | /**
57 | * Set the parameters we want to resolve against
58 | *
59 | * @param array $params Associative array of extra parameters we want to resolve against
60 | */
61 | public function setParams(array $params = array())
62 | {
63 | $this->params = $params;
64 | }
65 |
66 | /**
67 | * Get the parameters we want to resolve against
68 | *
69 | * @return array $params Associative array of parameters
70 | */
71 | public function getParams()
72 | {
73 | return $this->params;
74 | }
75 |
76 | /**
77 | * Returns the reflected object
78 | *
79 | * @return \ReflectionClass
80 | */
81 | public function getReflected()
82 | {
83 | return $this->reflected;
84 | }
85 |
86 | /**
87 | * Generate a unique hash based on the keys of the extra params
88 | *
89 | * @param array $params: array of parameters
90 | *
91 | * @return string Unique MD5 hash
92 | */
93 | public static function generateParamsHashKey($params)
94 | {
95 | return md5(serialize($params));
96 | }
97 |
98 | /**
99 | * Configure options for the provided OptionResolver to match extra params requirements
100 | *
101 | * @param OptionsResolver $resolver OptionResolver to configure
102 | * @param ClassLoader|null $classLoader Optional class loader if you want to use custom
103 | * handlers for some of the extra options
104 | */
105 | protected function configureOptions(OptionsResolver $resolver, ClassLoader $classLoader = null)
106 | {
107 | foreach ($this->params as $name) {
108 | if ($this->reflected->hasMethod($name)) {
109 | // There is a method to handle this option
110 | $resolver->setDefined($name);
111 | continue;
112 | }
113 | if ($this->reflected->hasProperty($name) &&
114 | $this->reflected->getProperty($name)->isPublic()
115 | ) {
116 | // There is a public member we can set to handle this option
117 | $resolver->setDefined($name);
118 | continue;
119 | }
120 |
121 | // Option that cannot be handled by a regular setter but
122 | // requires specific pre-processing and/or handling to be set
123 | // e.g. like LogglyHandler::addTag for instance
124 | if (!is_null($classLoader) && $classLoader->canHandle($name)) {
125 | $resolver->setDefined($name);
126 | }
127 | }
128 | }
129 |
130 | /**
131 | * Resolve options against extra params requirements
132 | *
133 | * @param array $options Array of option values
134 | * @param ClassLoader|null $classLoader Optional class loader if you want to use custom
135 | * handlers to resolve the extra options
136 | *
137 | * @return array Array of resolved options
138 | */
139 | public function resolve($options, ClassLoader $classLoader = null)
140 | {
141 | $hashKey = self::generateParamsHashKey($this->params);
142 |
143 | // Was configureOptions() executed before for this class?
144 | if (!isset(self::$resolvers[$hashKey])) {
145 | self::$resolvers[$hashKey] = new OptionsResolver();
146 | $this->configureOptions(self::$resolvers[$hashKey], $classLoader);
147 | }
148 |
149 | return self::$resolvers[$hashKey]->resolve($options);
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/src/Config/Loader/FileLoader/FileLoaderAbstract.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Config\Loader\FileLoader;
12 |
13 | use Symfony\Component\Config\Loader\FileLoader;
14 |
15 | /**
16 | * Abstract class that reads input from various sources: file, string or array
17 | *
18 | * @author Raphael Antonmattei
19 | */
20 | abstract class FileLoaderAbstract extends FileLoader
21 | {
22 | public static $validExtensions = array();
23 |
24 | /**
25 | * Read from a file or string
26 | *
27 | * @throws \RuntimeException if the file is not readable
28 | *
29 | * @param string $input Filepath or string
30 | *
31 | * @return string Return a string from read file or directly from $input
32 | */
33 | public function readFrom($input)
34 | {
35 | if ($this->isFile($input)) {
36 | if (is_readable($input) === false) {
37 | throw new \RuntimeException(
38 | sprintf('Unable to parse "%s" as the file is not readable.', $input)
39 | );
40 | }
41 |
42 | // $input is a filepath, so we load that file
43 | $input = file_get_contents($input);
44 | }
45 |
46 | return trim($input);
47 | }
48 |
49 | /**
50 | * Test if a given resource is a file name or a file path
51 | *
52 | * @param string $resource Plain string or file path
53 | *
54 | * @return boolean Whether or not the resource is a file
55 | */
56 | public function isFile($resource)
57 | {
58 | return (strpos($resource, "\n") === false) && is_file($resource);
59 | }
60 |
61 | /**
62 | * Validate a file extension against a list of provided valid extensions
63 | *
64 | * @param string $filepath file path of the file we want to check
65 | *
66 | * @return boolean Whether or not the extension is valid
67 | */
68 | public function validateExtension($filepath)
69 | {
70 | return in_array(pathinfo($filepath, PATHINFO_EXTENSION), static::$validExtensions, true);
71 | }
72 |
73 | /**
74 | * Return a section of an array based on the key passed in
75 | *
76 | * @param array $array Array we want the section from
77 | * @param string $section Section name (key)
78 | *
79 | * @return array|mixed Return the section of an array or just a value
80 | */
81 | public function getSectionOf($array, $section = '')
82 | {
83 | if (!empty($section) && array_key_exists($section, $array)) {
84 | return $array[$section];
85 | }
86 |
87 | return $array;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/Config/Loader/FileLoader/Json.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Config\Loader\FileLoader;
12 |
13 | /**
14 | * JSON loader class. It can load a JSON string or a Yaml file
15 | * @see FileLoaderAbstract
16 | *
17 | * @author Raphael Antonmattei
18 | */
19 | class Json extends FileLoaderAbstract
20 | {
21 | /**
22 | * Valid file extensions for this loader
23 | * @var array
24 | */
25 | public static $validExtensions = array('json');
26 |
27 | /**
28 | * Load a JSON string/file
29 | *
30 | * @param string $resource JSON string or file path to a JSON file
31 | * @param string|null $type Not used.
32 | *
33 | * @return array Array containing data from the parsed JSON string or file
34 | */
35 | public function load($resource, $type = null)
36 | {
37 | return json_decode($this->readFrom($resource), true);
38 | }
39 |
40 | /**
41 | * Determine whether a given string is supposed to be a Json string
42 | * This is a very simplified validation to avoid calling
43 | * json_decode (which is much more expensive). If the json is invalid, it will throw an
44 | * exception when we actually load it.
45 | *
46 | * @param string $string String to evaluate
47 | *
48 | * @return boolean Whether or not the passed string is meant to be a JSON string
49 | */
50 | private function isJson($string)
51 | {
52 | return (
53 | !empty($string) &&
54 | ($string[0] === '[' || $string[0] === '{')
55 | );
56 | }
57 |
58 | /**
59 | * Return whether or not the passed in resource is supported by this loader
60 | *
61 | * @param string $resource Plain string or filepath
62 | * @param string $type Not used
63 | *
64 | * @return boolean Whether or not the passed in resource is supported by this loader
65 | */
66 | public function supports($resource, $type = null)
67 | {
68 | if (is_string($resource)) {
69 | if ($this->isFile($resource)) {
70 | return $this->validateExtension($resource);
71 | } else {
72 | return $this->isJson($resource);
73 | }
74 | }
75 |
76 | return false;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/Config/Loader/FileLoader/PhpArray.php:
--------------------------------------------------------------------------------
1 | isFile($resource) && $this->validateExtension($resource);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Config/Loader/FileLoader/Yaml.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Config\Loader\FileLoader;
12 |
13 | use Symfony\Component\Yaml\Yaml as YamlParser;
14 |
15 | /**
16 | * Yaml loader class. It can load a Yaml string or a Yaml file
17 | * @see FileLoaderAbstract
18 | *
19 | * @author Raphael Antonmattei
20 | */
21 | class Yaml extends FileLoaderAbstract
22 | {
23 | /**
24 | * Valid file extensions for this loader
25 | * @var array
26 | */
27 | public static $validExtensions = array(
28 | 'yaml', // official extension
29 | 'yml' // but everybody uses that one
30 | );
31 |
32 | /**
33 | * Load a Yaml string/file
34 | *
35 | * @param string $resource Yaml string or file path to a Yaml file
36 | * @param string|null $type Not used
37 | *
38 | * @return array Array containing data from the parse Yaml string or file
39 | */
40 | public function load($resource, $type = null)
41 | {
42 | return YamlParser::parse($this->readFrom($resource));
43 | }
44 |
45 | /**
46 | * Return whether or not the resource passed in is supported by this loader
47 | * /!\ This does not validate Yaml content. The parser will raise an exception in that case
48 | *
49 | * @param string $resource Plain string or filepath
50 | * @param string $type Not used
51 | *
52 | * @return boolean Whether or not the passed in resrouce is supported by this loader
53 | */
54 | public function supports($resource, $type = null)
55 | {
56 | if (!is_string($resource)) {
57 | return false;
58 | }
59 |
60 | if ($this->isFile($resource)) {
61 | return $this->validateExtension($resource);
62 | }
63 |
64 | return true;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Config/Loader/PhpArray.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Config\Loader;
12 |
13 | use Symfony\Component\Config\Loader\Loader;
14 |
15 | /**
16 | * Array loader class. It loads a php array
17 | * @see Loader
18 | *
19 | * @author Raphael Antonmattei
20 | */
21 | class PhpArray extends Loader
22 | {
23 | /**
24 | * Loads an array
25 | *
26 | * @param array $array Array to load
27 | * @param string|null $type Not used
28 | *
29 | * @return array The passed in array
30 | */
31 | public function load($array, $type = null)
32 | {
33 | return $array;
34 | }
35 |
36 | /**
37 | * Return whether or not the passed in resource is supported by this loader
38 | *
39 | * @param string $resource Plain string or filepath
40 | * @param string|null $type Not used
41 | *
42 | * @return boolean Whether or not the passed in resource is supported by this loader
43 | */
44 | public function supports($resource, $type = null)
45 | {
46 | return is_array($resource);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Util.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Tests;
12 |
13 | use Monolog\Logger;
14 | use Monolog\Registry;
15 |
16 | use Cascade\Cascade;
17 | use PHPUnit\Framework\TestCase;
18 |
19 | /**
20 | * Class CascadeTest
21 | *
22 | * @author Raphael Antonmattei
23 | */
24 | class CascadeTest extends TestCase
25 | {
26 | public function teardown(): void
27 | {
28 | Registry::clear();
29 | parent::teardown();
30 | }
31 |
32 | public function testCreateLogger()
33 | {
34 | $logger = Cascade::createLogger('test');
35 |
36 | $this->assertTrue($logger instanceof Logger);
37 | $this->assertEquals('test', $logger->getName());
38 | $this->assertTrue(Registry::hasLogger('test'));
39 | }
40 |
41 | public function testRegistry()
42 | {
43 | // Creates the logger and push it to the registry
44 | $logger = Cascade::logger('test');
45 |
46 | // We should get the logger from the registry this time
47 | $logger2 = Cascade::logger('test');
48 | $this->assertSame($logger, $logger2);
49 | }
50 |
51 | public function testRegistryWithInvalidName()
52 | {
53 | $this->expectException(\InvalidArgumentException::class);
54 |
55 | Cascade::getLogger(null);
56 | }
57 |
58 | public function testFileConfig()
59 | {
60 | $filePath = Fixtures::getPhpArrayConfigFile();
61 | Cascade::fileConfig($filePath);
62 | $this->assertInstanceOf('Cascade\Config', Cascade::getConfig());
63 | }
64 |
65 | public function testLoadConfigFromArray()
66 | {
67 | $options = Fixtures::getPhpArrayConfig();
68 | Cascade::loadConfigFromArray($options);
69 | $this->assertInstanceOf('Cascade\Config', Cascade::getConfig());
70 | }
71 |
72 | public function testLoadConfigFromStringWithJson()
73 | {
74 | $jsonConfig = Fixtures::getJsonConfig();
75 | Cascade::loadConfigFromString($jsonConfig);
76 | $this->assertInstanceOf('Cascade\Config', Cascade::getConfig());
77 | }
78 |
79 | public function testLoadConfigFromStringWithYaml()
80 | {
81 | $yamlConfig = Fixtures::getYamlConfig();
82 | Cascade::loadConfigFromString($yamlConfig);
83 | $this->assertInstanceOf('Cascade\Config', Cascade::getConfig());
84 | }
85 |
86 | public function testHasLogger()
87 | {
88 | // implicitly create logger "existing"
89 | Cascade::logger('existing');
90 | $this->assertFalse(Cascade::hasLogger('not_existing'));
91 | $this->assertTrue(Cascade::hasLogger('existing'));
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/tests/Config/ConfigLoaderTest.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Tests\Config;
12 |
13 | use Cascade\Config\ConfigLoader;
14 | use Cascade\Tests\Fixtures;
15 | use PHPUnit\Framework\TestCase;
16 |
17 | /**
18 | * Class ConfigLoaderTest
19 | *
20 | * @author Raphael Antonmattei
21 | */
22 | class ConfigLoaderTest extends TestCase
23 | {
24 | /**
25 | * Loader to test against
26 | * @var ConfigLoader
27 | */
28 | protected $loader = null;
29 |
30 | public function setUp(): void
31 | {
32 | parent::setup();
33 | $this->loader = new ConfigLoader();
34 | }
35 |
36 | public function tearDown(): void
37 | {
38 | $this->loader = null;
39 | parent::tearDown();
40 | }
41 |
42 | public function testLoader()
43 | {
44 | $this->assertInstanceOf(
45 | 'Symfony\Component\Config\Loader\DelegatingLoader',
46 | $this->loader
47 | );
48 |
49 | $this->assertInstanceOf(
50 | 'Symfony\Component\Config\Loader\LoaderResolver',
51 | $this->loader->getResolver()
52 | );
53 |
54 | $configLoaders = $this->loader->getResolver()->getLoaders();
55 | $this->assertCount(4, $configLoaders);
56 |
57 | // Checking the order of thr loaders
58 | $this->assertInstanceOf(
59 | 'Cascade\Config\Loader\PhpArray',
60 | $configLoaders[0]
61 | );
62 | $this->assertInstanceOf(
63 | 'Cascade\Config\Loader\FileLoader\PhpArray',
64 | $configLoaders[1]
65 | );
66 | $this->assertInstanceOf(
67 | 'Cascade\Config\Loader\FileLoader\Json',
68 | $configLoaders[2]
69 | );
70 | $this->assertInstanceOf(
71 | 'Cascade\Config\Loader\FileLoader\Yaml',
72 | $configLoaders[3]
73 | );
74 | }
75 |
76 | public function testLoad()
77 | {
78 | $json = Fixtures::getSampleJsonString();
79 | $this->assertEquals(json_decode($json, true), $this->loader->load($json));
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/tests/Config/Loader/ClassLoader/FormatterLoaderTest.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Tests\Config\Loader\ClassLoader;
12 |
13 | use Cascade\Config\Loader\ClassLoader\FormatterLoader;
14 | use PHPUnit\Framework\TestCase;
15 |
16 | /**
17 | * Class FormatterLoaderTest
18 | *
19 | * @author Raphael Antonmattei
20 | */
21 | class FormatterLoaderTest extends TestCase
22 | {
23 | /**
24 | * Set up function
25 | */
26 | public function setUp(): void
27 | {
28 | parent::setUp();
29 | new FormatterLoader(array());
30 | }
31 |
32 | /**
33 | * Tear down function
34 | */
35 | public function tearDown(): void
36 | {
37 | FormatterLoader::$extraOptionHandlers = array();
38 | parent::tearDown();
39 | }
40 |
41 | /**
42 | * Check if the handler exists for a given class and option
43 | * Also checks that it a callable and return it
44 | *
45 | * @param string $class Class name the handler applies to
46 | * @param string $optionName Option name
47 | * @return \Closure Closure
48 | * @throws \Exception
49 | */
50 | private function getHandler($class, $optionName)
51 | {
52 | if (isset(FormatterLoader::$extraOptionHandlers[$class][$optionName])) {
53 | // Get the closure
54 | $closure = FormatterLoader::$extraOptionHandlers[$class][$optionName];
55 |
56 | $this->assertTrue(is_callable($closure));
57 |
58 | return $closure;
59 | } else {
60 | throw new \Exception(
61 | sprintf(
62 | 'Handler %s is not defined for class %s',
63 | $optionName,
64 | $class
65 | )
66 | );
67 | }
68 | }
69 |
70 | /**
71 | * Tests that calling the given Closure will trigger a method call with the given param
72 | * in the given class
73 | *
74 | * @param string $class Class name
75 | * @param string $methodName Method name
76 | * @param mixed $methodArg Parameter passed to the closure
77 | * @param \Closure $closure Closure to call
78 | */
79 | private function doTestMethodCalledInHandler($class, $methodName, $methodArg, \Closure $closure)
80 | {
81 | // Setup mock and expectations
82 | $mock = $this->getMockBuilder($class)
83 | ->setMethods(array($methodName))
84 | ->getMock();
85 |
86 | $mock->expects($this->once())
87 | ->method($methodName)
88 | ->with($methodArg);
89 |
90 | // Calling the handler
91 | $closure($mock, $methodArg);
92 | }
93 |
94 |
95 | /**
96 | * Test that handlers exist
97 | */
98 | public function testHandlersExist()
99 | {
100 | $this->assertTrue(count(FormatterLoader::$extraOptionHandlers) > 0);
101 | }
102 |
103 | /**
104 | * Data provider for testHandlers
105 | * /!\ Important note: just add values to this array if you need to test a newly added handler
106 | * If one of your handlers calls more than one method you can add more than one entries
107 | *
108 | * @return array of array of args for testHandlers
109 | */
110 | public function handlerParamsProvider()
111 | {
112 | return array(
113 | array(
114 | 'Monolog\Formatter\LineFormatter', // Class name
115 | 'includeStacktraces', // Option name
116 | true, // Option test value
117 | 'includeStacktraces' // Name of the method called by your handler
118 | )
119 | );
120 | }
121 |
122 | /**
123 | * Test the extra option handlers
124 | * @see doTestMethodCalledInHandler
125 | *
126 | * @param string $class Class name
127 | * @param string $optionName Option name
128 | * @param mixed $optionValue Option value
129 | * @param string $calledMethodName Expected called method name
130 | * @dataProvider handlerParamsProvider
131 | */
132 | public function testHandlers($class, $optionName, $optionValue, $calledMethodName)
133 | {
134 | // Test if handler exists and return it
135 | $closure = $this->getHandler($class, $optionName);
136 |
137 | $this->doTestMethodCalledInHandler($class, $calledMethodName, $optionValue, $closure);
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/tests/Config/Loader/ClassLoader/HandlerLoaderTest.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Tests\Config\Loader\ClassLoader;
12 |
13 | use Monolog\Formatter\LineFormatter;
14 |
15 | use Cascade\Config\Loader\ClassLoader\HandlerLoader;
16 | use PHPUnit\Framework\TestCase;
17 |
18 | /**
19 | * Class HandlerLoaderTest
20 | *
21 | * @author Raphael Antonmattei
22 | */
23 | class HandlerLoaderTest extends TestCase
24 | {
25 | public function testHandlerLoader()
26 | {
27 | $dummyClosure = function () {
28 | // Empty function
29 | };
30 | $original = $options = array(
31 | 'class' => 'Monolog\Handler\TestHandler',
32 | 'level' => 'DEBUG',
33 | 'formatter' => 'test_formatter',
34 | 'processors' => array('test_processor_1', 'test_processor_2')
35 | );
36 | $formatters = array('test_formatter' => new LineFormatter());
37 | $processors = array(
38 | 'test_processor_1' => $dummyClosure,
39 | 'test_processor_2' => $dummyClosure
40 | );
41 | $loader = new HandlerLoader($options, $formatters, $processors);
42 |
43 | $this->assertNotEquals($original, $options);
44 | $this->assertSame($formatters['test_formatter'], $options['formatter']);
45 | $this->assertSame($processors['test_processor_1'], $options['processors'][0]);
46 | $this->assertSame($processors['test_processor_2'], $options['processors'][1]);
47 | }
48 |
49 | public function testHandlerLoaderWithNoOptions()
50 | {
51 | $original = $options = array();
52 | $loader = new HandlerLoader($options);
53 |
54 | $this->assertEquals($original, $options);
55 | }
56 |
57 | public function testHandlerLoaderWithInvalidFormatter()
58 | {
59 | $this->expectException(\InvalidArgumentException::class);
60 |
61 | $options = array(
62 | 'formatter' => 'test_formatter'
63 | );
64 |
65 | $formatters = array('test_formatterXYZ' => new LineFormatter());
66 | $loader = new HandlerLoader($options, $formatters);
67 | }
68 |
69 | public function testHandlerLoaderWithInvalidProcessor()
70 | {
71 | $this->expectException(\InvalidArgumentException::class);
72 |
73 | $dummyClosure = function () {
74 | // Empty function
75 | };
76 | $options = array(
77 | 'processors' => array('test_processor_1')
78 | );
79 |
80 | $formatters = array();
81 | $processors = array('test_processorXYZ' => $dummyClosure);
82 | $loader = new HandlerLoader($options, $formatters, $processors);
83 | }
84 |
85 | public function testHandlerLoaderWithInvalidHandler()
86 | {
87 | $this->expectException(\InvalidArgumentException::class);
88 |
89 | $dummyClosure = function () {
90 | // Empty function
91 | };
92 | $options = array(
93 | 'handler' => 'test_handler'
94 | );
95 |
96 | $formatters = array();
97 | $processors = array();
98 | $handlers = array('test_handlerXYZ' => $dummyClosure);
99 | $loader = new HandlerLoader($options, $formatters, $processors, $handlers);
100 | }
101 |
102 | public function testHandlerLoaderWithInvalidHandlers()
103 | {
104 | $this->expectException(\InvalidArgumentException::class);
105 |
106 | $dummyClosure = function () {
107 | // Empty function
108 | };
109 | $options = array(
110 | 'handlers' => array('test_handler_1', 'test_handler_2')
111 | );
112 |
113 | $formatters = array();
114 | $processors = array();
115 | $handlers = array(
116 | 'test_handler_1' => $dummyClosure,
117 | 'test_handlerXYZ' => $dummyClosure
118 | );
119 | $loader = new HandlerLoader($options, $formatters, $processors, $handlers);
120 | }
121 |
122 | /**
123 | * Check if the handler exists for a given class and option
124 | * Also checks that it a callable and return it
125 | *
126 | * @param string $class Class name the handler applies to
127 | * @param string $optionName Option name
128 | * @return \Closure Closure
129 | * @throws \Exception
130 | */
131 | private function getHandler($class, $optionName)
132 | {
133 | if (isset(HandlerLoader::$extraOptionHandlers[$class][$optionName])) {
134 | // Get the closure
135 | $closure = HandlerLoader::$extraOptionHandlers[$class][$optionName];
136 |
137 | $this->assertTrue(is_callable($closure));
138 |
139 | return $closure;
140 | } else {
141 | throw new \Exception(
142 | sprintf(
143 | 'Custom handler %s is not defined for class %s',
144 | $optionName,
145 | $class
146 | )
147 | );
148 | }
149 | }
150 |
151 | /**
152 | * Tests that calling the given Closure will trigger a method call with the given param
153 | * in the given class
154 | *
155 | * @param string $class Class name
156 | * @param string $methodName Method name
157 | * @param mixed $methodArg Parameter passed to the closure
158 | * @param \Closure $closure Closure to call
159 | */
160 | private function doTestMethodCalledInHandler($class, $methodName, $methodArg, \Closure $closure)
161 | {
162 | // Setup mock and expectations
163 | $mock = $this->getMockBuilder($class)
164 | ->disableOriginalConstructor()
165 | ->setMethods(array($methodName))
166 | ->getMock();
167 |
168 | $mock->expects($this->once())
169 | ->method($methodName)
170 | ->with($methodArg);
171 |
172 | // Calling the handler
173 | $closure($mock, $methodArg);
174 | }
175 |
176 |
177 | /**
178 | * Test that handlers exist
179 | */
180 | public function testHandlersExist()
181 | {
182 | $options = array();
183 | new HandlerLoader($options);
184 | $this->assertTrue(count(HandlerLoader::$extraOptionHandlers) > 0);
185 | }
186 |
187 | /**
188 | * Data provider for testHandlers
189 | * /!\ Important note:
190 | * Just add values to this array if you need to test a newly added handler
191 | *
192 | * If one of your handlers calls more than one method you can add more than one entries
193 | *
194 | * @return array of array of args for testHandlers
195 | */
196 | public function handlerParamsProvider()
197 | {
198 | return array(
199 | array(
200 | '*', // Class name
201 | 'formatter', // Option name
202 | new LineFormatter(), // Option test value
203 | 'setFormatter' // Name of the method called by your handler
204 | ),
205 | array(
206 | 'Monolog\Handler\LogglyHandler', // Class name
207 | 'tags', // Option name
208 | array('some_tag'), // Option test value
209 | 'setTag' // Name of the method called by your handler
210 | )
211 | );
212 | }
213 |
214 | /**
215 | * Test the extra option handlers
216 | *
217 | * @param string $class Class name
218 | * @param string $optionName Option name
219 | * @param mixed $optionValue Option value
220 | * @param string $calledMethodName Expected called method name
221 | * @dataProvider handlerParamsProvider
222 | */
223 | public function testHandlers($class, $optionName, $optionValue, $calledMethodName)
224 | {
225 | $options = array();
226 | new HandlerLoader($options);
227 | // Test if handler exists and return it
228 | $closure = $this->getHandler($class, $optionName);
229 |
230 | if ($class == '*') {
231 | $class = 'Monolog\Handler\TestHandler';
232 | }
233 |
234 | $this->doTestMethodCalledInHandler($class, $calledMethodName, $optionValue, $closure);
235 | }
236 |
237 | /**
238 | * Test extra option processor handler
239 | */
240 | public function testHandlerForProcessor()
241 | {
242 | $options = array();
243 |
244 | $mockProcessor1 = function($record) {
245 | $record['extra']['dummy'] = 'Hello world 1!';
246 | return $record;
247 | };
248 | $mockProcessor2 = function($record) {
249 | $record['extra']['dummy'] = 'Hello world 1!';
250 | return $record;
251 | };
252 | $processorsArray = array($mockProcessor1, $mockProcessor2);
253 |
254 | // Setup mock and expectations
255 | $mockHandler = $this->getMockBuilder('Monolog\Handler\TestHandler')
256 | ->disableOriginalConstructor()
257 | ->setMethods(array('pushProcessor'))
258 | ->getMock();
259 |
260 | $mockHandler->expects($this->exactly(sizeof($processorsArray)))
261 | ->method('pushProcessor')
262 | ->withConsecutive(array($mockProcessor2), array($mockProcessor1));
263 |
264 | new HandlerLoader($options);
265 | $closure = $this->getHandler('*', 'processors');
266 | $closure($mockHandler, $processorsArray);
267 | }
268 |
269 | public function testReplacesHandlerNamesInOptionsArrayWithLoadedCallable()
270 | {
271 | $options = array(
272 | 'handlers' => array(
273 | 'foo',
274 | 'bar',
275 | ),
276 | 'handler' => 'baz'
277 | );
278 |
279 | $formatters = array();
280 | $processors = array();
281 | $handlers = array(
282 | 'foo' => function () {
283 | return 'foo';
284 | },
285 | 'bar' => function () {
286 | return 'bar';
287 | },
288 | 'baz' => function () {
289 | return 'baz';
290 | },
291 | );
292 |
293 | $loader = new HandlerLoader($options, $formatters, $processors, $handlers);
294 |
295 | $this->assertSame($handlers['foo'], $options['handlers'][0]);
296 | $this->assertSame($handlers['bar'], $options['handlers'][1]);
297 | $this->assertSame($handlers['baz'], $options['handler']);
298 | }
299 | }
300 |
--------------------------------------------------------------------------------
/tests/Config/Loader/ClassLoader/LoggerLoaderTest.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Tests\Config\Loader\ClassLoader;
12 |
13 | use Monolog\Handler\TestHandler;
14 | use Monolog\Logger;
15 | use Monolog\Registry;
16 |
17 | use Cascade\Config\Loader\ClassLoader\LoggerLoader;
18 | use PHPUnit\Framework\TestCase;
19 |
20 | /**
21 | * Class LoggerLoaderTest
22 | *
23 | * @author Raphael Antonmattei
24 | */
25 | class LoggerLoaderTest extends TestCase
26 | {
27 | /**
28 | * Tear down function
29 | */
30 | public function tearDown(): void
31 | {
32 | parent::tearDown();
33 | Registry::clear();
34 | }
35 |
36 | public function testConstructor()
37 | {
38 | $loader = new LoggerLoader('testLogger');
39 |
40 | $this->assertTrue(Registry::hasLogger('testLogger'));
41 | }
42 |
43 | public function testResolveHandlers()
44 | {
45 | $options = array(
46 | 'handlers' => array('test_handler_1', 'test_handler_2')
47 | );
48 | $handlers = array(
49 | 'test_handler_1' => new TestHandler(),
50 | 'test_handler_2' => new TestHandler()
51 | );
52 | $loader = new LoggerLoader('testLogger', $options, $handlers);
53 |
54 | $this->assertEquals(
55 | array_values($handlers),
56 | $loader->resolveHandlers($options, $handlers)
57 | );
58 | }
59 |
60 | public function testResolveHandlersWithMismatch()
61 | {
62 | $this->expectException(\InvalidArgumentException::class);
63 |
64 | $options = array(
65 | 'handlers' => array('unexisting_handler', 'test_handler_2')
66 | );
67 | $handlers = array(
68 | 'test_handler_1' => new TestHandler(),
69 | 'test_handler_2' => new TestHandler()
70 | );
71 | $loader = new LoggerLoader('testLogger', $options, $handlers);
72 |
73 | // This should throw an InvalidArgumentException
74 | $loader->resolveHandlers($options, $handlers);
75 | }
76 |
77 | public function testResolveProcessors()
78 | {
79 | $dummyClosure = function () {
80 | // Empty function
81 | };
82 | $options = array(
83 | 'processors' => array('test_processor_1', 'test_processor_2')
84 | );
85 | $processors = array(
86 | 'test_processor_1' => $dummyClosure,
87 | 'test_processor_2' => $dummyClosure
88 | );
89 |
90 | $loader = new LoggerLoader('testLogger', $options, array(), $processors);
91 |
92 | $this->assertEquals(
93 | array_values($processors),
94 | $loader->resolveProcessors($options, $processors)
95 | );
96 | }
97 |
98 | public function testResolveProcessorsWithMismatch()
99 | {
100 | $this->expectException(\InvalidArgumentException::class);
101 |
102 | $dummyClosure = function () {
103 | // Empty function
104 | };
105 | $options = array(
106 | 'processors' => array('unexisting_processor', 'test_processor_2')
107 | );
108 | $processors = array(
109 | 'test_processor_1' => $dummyClosure,
110 | 'test_processor_2' => $dummyClosure
111 | );
112 |
113 | $loader = new LoggerLoader('testLogger', $options, array(), $processors);
114 |
115 | // This should throw an InvalidArgumentException
116 | $loader->resolveProcessors($options, $processors);
117 | }
118 |
119 | public function testLoad()
120 | {
121 | $options = array(
122 | 'handlers' => array('test_handler_1', 'test_handler_2'),
123 | 'processors' => array('test_processor_1', 'test_processor_2')
124 | );
125 | $handlers = array(
126 | 'test_handler_1' => new TestHandler(),
127 | 'test_handler_2' => new TestHandler()
128 | );
129 | $dummyClosure = function () {
130 | // Empty function
131 | };
132 | $processors = array(
133 | 'test_processor_1' => $dummyClosure,
134 | 'test_processor_2' => $dummyClosure
135 | );
136 |
137 | $loader = new LoggerLoader('testLogger', $options, $handlers, $processors);
138 | $logger = $loader->load();
139 |
140 | $this->assertTrue($logger instanceof Logger);
141 | $this->assertEquals(array_values($handlers), $logger->getHandlers());
142 | $this->assertEquals(array_values($processors), $logger->getProcessors());
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/tests/Config/Loader/ClassLoader/ProcessorLoaderTest.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Tests\Config\Loader\ClassLoader;
12 |
13 | use Monolog\Processor\WebProcessor;
14 |
15 | use Cascade\Config\Loader\ClassLoader\ProcessorLoader;
16 | use PHPUnit\Framework\TestCase;
17 |
18 | /**
19 | * Class ProcessorLoaderTest
20 | *
21 | * @author Kate Burdon
22 | */
23 | class ProcessorLoaderTest extends TestCase
24 | {
25 | public function testProcessorLoader()
26 | {
27 | $options = array(
28 | 'class' => 'Monolog\Processor\WebProcessor'
29 | );
30 | $processors = array(new WebProcessor());
31 | $loader = new ProcessorLoader($options, $processors);
32 |
33 | $this->assertEquals($loader->class, $options['class']);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tests/Config/Loader/ClassLoader/Resolver/ConstructorResolverTest.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Tests\Config\Loader\ClassLoader\Resolver;
12 |
13 | use Cascade\Util;
14 | use Cascade\Config\Loader\ClassLoader\Resolver\ConstructorResolver;
15 |
16 | use PHPUnit\Framework\TestCase;
17 | use Symfony;
18 | use Symfony\Component\OptionsResolver\Exception\MissingOptionsException;
19 | use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException;
20 |
21 | /**
22 | * Class ConstructorResolverTest
23 | *
24 | * @author Raphael Antonmattei
25 | */
26 | class ConstructorResolverTest extends TestCase
27 | {
28 | /**
29 | * Reflection class for which you want to resolve extra options
30 | * @var \ReflectionClass
31 | */
32 | protected $reflected = null;
33 |
34 | /**
35 | * Constructor Resolver
36 | * @var ConstructorResolver
37 | */
38 | protected $resolver = null;
39 |
40 | /**
41 | * Set up function
42 | */
43 | public function setUp(): void
44 | {
45 | $this->class = 'Cascade\Tests\Fixtures\SampleClass';
46 | $this->resolver = new ConstructorResolver(new \ReflectionClass($this->class));
47 | parent::setUp();
48 | }
49 |
50 | /**
51 | * Tear down function
52 | */
53 | public function tearDown(): void
54 | {
55 | $this->resolver = null;
56 | $this->class = null;
57 | parent::tearDown();
58 | }
59 |
60 | /**
61 | * Return the contructor args of the reflected class
62 | *
63 | * @return \ReflectionParameter[] array of params
64 | */
65 | protected function getConstructorArgs()
66 | {
67 | return $this->resolver->getReflected()->getConstructor()->getParameters();
68 | }
69 |
70 | /**
71 | * Test the resolver contructor
72 | */
73 | public function testConstructor()
74 | {
75 | $this->assertEquals($this->class, $this->resolver->getReflected()->getName());
76 | }
77 |
78 | /**
79 | * Test that constructor args were pulled properly
80 | *
81 | * Note that we need to deuplicate the CamelCase conversion here for old
82 | * fashioned classes
83 | */
84 | public function testInitConstructorArgs()
85 | {
86 | $expectedConstructorArgs = array();
87 |
88 | foreach ($this->getConstructorArgs() as $param) {
89 | $expectedConstructorArgs[Util::snakeToCamelCase($param->getName())] = $param;
90 | }
91 | $this->assertEquals($expectedConstructorArgs, $this->resolver->getConstructorArgs());
92 | }
93 |
94 | /**
95 | * Test the hashToArgsArray function
96 | */
97 | public function testHashToArgsArray()
98 | {
99 | $this->assertEquals(
100 | array('someValue', 'hello', 'there', 'slither'),
101 | $this->resolver->hashToArgsArray(
102 | array( // Not properly ordered on purpose
103 | 'optionalB' => 'there',
104 | 'optionalA' => 'hello',
105 | 'optionalSnake' => 'slither',
106 | 'mandatory' => 'someValue',
107 | )
108 | )
109 | );
110 | }
111 |
112 | /**
113 | * Data provider for testResolve
114 | *
115 | * The order of the input options does not matter and is somewhat random. The resolution
116 | * should reconcile those options and match them up with the contructor param position
117 | *
118 | * @return array of arrays with expected resolved values and options used as input
119 | */
120 | public function optionsProvider()
121 | {
122 | return array(
123 | array(
124 | array('someValue', 'hello', 'there', 'slither'), // Expected resolved options
125 | array( // Options (order should not matter, part of resolution)
126 | 'optionalB' => 'there',
127 | 'optionalA' => 'hello',
128 | 'mandatory' => 'someValue',
129 | 'optionalSnake' => 'slither',
130 | )
131 | ),
132 | array(
133 | array('someValue', 'hello', 'BBB', 'snake'),
134 | array(
135 | 'mandatory' => 'someValue',
136 | 'optionalA' => 'hello',
137 | )
138 | ),
139 | array(
140 | array('someValue', 'AAA', 'BBB', 'snake'),
141 | array('mandatory' => 'someValue')
142 | )
143 | );
144 | }
145 |
146 | /**
147 | * Test resolving with valid options
148 | *
149 | * @param array $expectedResolvedOptions Array of expected resolved options
150 | * (i.e. parsed and validated)
151 | * @param array $options Array of raw options
152 | * @dataProvider optionsProvider
153 | */
154 | public function testResolve(array $expectedResolvedOptions, array $options)
155 | {
156 | $this->assertEquals($expectedResolvedOptions, $this->resolver->resolve($options));
157 | }
158 |
159 | /**
160 | * Data provider for testResolveWithInvalidOptions.
161 | *
162 | * The order of the input options does not matter and is somewhat random. The resolution
163 | * should reconcile those options and match them up with the contructor param position
164 | *
165 | * @return array of arrays with expected resolved values and options used as input
166 | */
167 | public function missingOptionsProvider()
168 | {
169 | return array(
170 | array(
171 | array( // No values
172 | ),
173 | array( // Missing a mandatory value
174 | 'optionalB' => 'BBB'
175 | ),
176 | array( // Still missing a mandatory value
177 | 'optionalB' => 'there',
178 | 'optionalA' => 'hello'
179 | )
180 | )
181 | );
182 | }
183 |
184 | /**
185 | * Test resolving with missing/incomplete options. It should throw an exception.
186 | *
187 | * @param array $incompleteOptions Array of invalid options
188 | * @dataProvider missingOptionsProvider
189 | */
190 | public function testResolveWithMissingOptions(array $incompleteOptions)
191 | {
192 | $this->expectException(MissingOptionsException::class);
193 |
194 | $this->resolver->resolve($incompleteOptions);
195 | }
196 |
197 | /**
198 | * Data provider for testResolveWithInvalidOptions
199 | *
200 | * The order of the input options does not matter and is somewhat random. The resolution
201 | * should reconcile those options and match them up with the contructor param position
202 | *
203 | * @return array of arrays with expected resolved values and options used as input
204 | */
205 | public function invalidOptionsProvider()
206 | {
207 | return array(
208 | array(
209 | array('ABC'),
210 | array( // All invalid
211 | 'someInvalidOptionA' => 'abc',
212 | 'someInvalidOptionB' => 'def'
213 | ),
214 | array( // Some invalid
215 | 'optionalB' => 'there',
216 | 'optionalA' => 'hello',
217 | 'mandatory' => 'dsadsa',
218 | 'additionalInvalid' => 'some unknow param'
219 | )
220 | )
221 | );
222 | }
223 |
224 | /**
225 | * Test resolving with invalid options. It should throw an exception.
226 | *
227 | * @param array $invalidOptions Array of invalid options
228 | * @dataProvider invalidOptionsProvider
229 | */
230 | public function testResolveWithInvalidOptions($invalidOptions)
231 | {
232 | $this->expectException(UndefinedOptionsException::class);
233 |
234 | $this->resolver->resolve($invalidOptions);
235 | }
236 | }
237 |
--------------------------------------------------------------------------------
/tests/Config/Loader/ClassLoader/Resolver/ExtraOptionsResolverTest.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Tests\Config\Loader\ClassLoader\Resolver;
12 |
13 | use Cascade\Config\Loader\ClassLoader\Resolver\ExtraOptionsResolver;
14 |
15 | use PHPUnit\Framework\TestCase;
16 | use Symfony;
17 | use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException;
18 |
19 | /**
20 | * Class ExtraOptionsResolverTest
21 | *
22 | * @author Raphael Antonmattei
23 | */
24 | class ExtraOptionsResolverTest extends TestCase
25 | {
26 | /**
27 | * Reflection class for which you want to resolve extra options
28 | * @var \ReflectionClass
29 | */
30 | protected $reflected = null;
31 |
32 | /**
33 | * ExtraOptions Resolver
34 | * @var ExtraOptionsResolver
35 | */
36 | protected $resolver = null;
37 |
38 | /**
39 | * Set up function
40 | */
41 | public function setUp(): void
42 | {
43 | $this->class = 'Cascade\Tests\Fixtures\SampleClass';
44 | $this->params = array('optionalA', 'optionalB');
45 | $this->resolver = new ExtraOptionsResolver(
46 | new \ReflectionClass($this->class),
47 | $this->params
48 | );
49 | parent::setUp();
50 | }
51 |
52 | /**
53 | * Tear down function
54 | */
55 | public function tearDown(): void
56 | {
57 | $this->resolver = null;
58 | $this->class = null;
59 | parent::tearDown();
60 | }
61 |
62 | /**
63 | * Test the hsah key generation
64 | */
65 | public function testGenerateParamsHashKey()
66 | {
67 | $a = array('optionA', 'optionB', 'optionC');
68 | $b = array('optionA', 'optionB', 'optionC');
69 |
70 | $this->assertEquals(
71 | ExtraOptionsResolver::generateParamsHashKey($a),
72 | ExtraOptionsResolver::generateParamsHashKey($b)
73 | );
74 | }
75 |
76 | /**
77 | * Test the resolver contructor
78 | */
79 | public function testConstructor()
80 | {
81 | $this->assertEquals($this->class, $this->resolver->getReflected()->getName());
82 | $this->assertEquals($this->params, $this->resolver->getParams());
83 | }
84 |
85 | /**
86 | * Test resolving with valid options
87 | */
88 | public function testResolve()
89 | {
90 | $this->assertEquals(
91 | array_combine($this->params, array('hello', 'there')),
92 | $this->resolver->resolve(array('optionalB' => 'there', 'optionalA' => 'hello'))
93 | );
94 |
95 | // Resolve an empty array (edge case)
96 | $this->assertEquals(array(), $this->resolver->resolve(array()));
97 | }
98 |
99 | /**
100 | * Data provider for testResolveWithInvalidOptions
101 | *
102 | * The order of the input options does not matter and is somewhat random. The resolution
103 | * should reconcile those options and match them up with the closure param position
104 | *
105 | * @return array of arrays with expected resolved values and options used as input
106 | */
107 | public function optionsProvider()
108 | {
109 | return array(
110 | array(
111 | array('optionalA', 'optionalB', 'mandatory'),
112 | $this->getMockBuilder('Cascade\Config\Loader\ClassLoader')
113 | ->disableOriginalConstructor()
114 | ->getMock()->method('canHandle')
115 | ->willReturn(true)
116 | )
117 | );
118 | }
119 |
120 | /**
121 | * Test resolving with valid options
122 | */
123 | public function testResolveWithCustomOptionHandler()
124 | {
125 | $this->params = array('optionalA', 'optionalB', 'mandatory');
126 | $this->resolver = new ExtraOptionsResolver(
127 | new \ReflectionClass($this->class),
128 | $this->params
129 | );
130 |
131 | // Create a stub for the SomeClass class.
132 | $stub = $this->getMockBuilder('Cascade\Config\Loader\ClassLoader')
133 | ->disableOriginalConstructor()
134 | ->getMock();
135 |
136 | $stub->method('canHandle')
137 | ->willReturn(true);
138 |
139 | // Resolve an empty array (edge case)
140 | $this->assertEquals(array('mandatory' => 'abc'), $this->resolver->resolve(array('mandatory' => 'abc'), $stub));
141 | }
142 |
143 | /**
144 | * Data provider for testResolveWithInvalidOptions
145 | *
146 | * The order of the input options does not matter and is somewhat random. The resolution
147 | * should reconcile those options and match them up with the closure param position
148 | *
149 | * @return array of arrays with expected resolved values and options used as input
150 | */
151 | public function invalidOptionsProvider()
152 | {
153 | return array(
154 | array(
155 | array( // Some invalid
156 | 'optionalB' => 'there',
157 | 'optionalA' => 'hello',
158 | 'additionalInvalid' => 'some unknow param'
159 | ),
160 | array( // All invalid
161 | 'someInvalidOptionA' => 'abc',
162 | 'someInvalidOptionB' => 'def'
163 | )
164 | )
165 | );
166 | }
167 |
168 | /**
169 | * Test resolving with invalid options. It should throw an exception.
170 | *
171 | * @param array $invalidOptions Array of invalid options
172 | * @dataProvider invalidOptionsProvider
173 | */
174 | public function testResolveWithInvalidOptions($invalidOptions)
175 | {
176 | $this->expectException(UndefinedOptionsException::class);
177 |
178 | $this->resolver->resolve($invalidOptions);
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/tests/Config/Loader/ClassLoaderTest.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Tests\Config\Loader;
12 |
13 | use Cascade\Config\Loader\ClassLoader;
14 | use Cascade\Tests\Fixtures\DependentClass;
15 | use Cascade\Tests\Fixtures\SampleClass;
16 | use PHPUnit\Framework\TestCase;
17 |
18 | /**
19 | * Class ClassLoaderTest
20 | *
21 | * @author Raphael Antonmattei
22 | * @author Dom Morgan
23 | */
24 | class ClassLoaderTest extends TestCase
25 | {
26 | /**
27 | * Set up function
28 | */
29 | public function setUp(): void
30 | {
31 | parent::setUp();
32 | }
33 |
34 | /**
35 | * Tear down function
36 | */
37 | public function tearDown(): void
38 | {
39 | ClassLoader::$extraOptionHandlers = array();
40 | parent::tearDown();
41 | }
42 |
43 | /**
44 | * Provides options with and without a class param
45 | * @return array of args
46 | */
47 | public function dataFortestSetClass()
48 | {
49 | return array(
50 | array(
51 | array(
52 | 'class' => 'Cascade\Tests\Fixtures\SampleClass',
53 | 'some_param' => 'abc'
54 | ),
55 | 'Cascade\Tests\Fixtures\SampleClass'
56 | ),
57 | array(
58 | array(
59 | 'some_param' => 'abc'
60 | ),
61 | '\stdClass'
62 | )
63 | );
64 | }
65 |
66 | /**
67 | * Testing the setClass method
68 | *
69 | * @param array $options Array of options
70 | * @param string $expectedClass Expected classname of the instantiated object
71 | * @dataProvider dataFortestSetClass
72 | */
73 | public function testSetClass($options, $expectedClass)
74 | {
75 | $loader = new ClassLoader($options);
76 |
77 | $this->assertEquals($expectedClass, $loader->class);
78 | }
79 |
80 | public function testOptionsToCamelCase()
81 | {
82 | $array = array('hello_there' => 'Hello', 'bye_bye' => 'Bye');
83 |
84 | $this->assertEquals(
85 | array('helloThere' => 'Hello', 'byeBye' => 'Bye'),
86 | ClassLoader::optionsToCamelCase($array)
87 | );
88 | }
89 |
90 | public function testGetExtraOptionsHandler()
91 | {
92 | ClassLoader::$extraOptionHandlers = array(
93 | '*' => array(
94 | 'hello' => function ($instance, $value) {
95 | $instance->setHello(strtoupper($value));
96 | }
97 | ),
98 | 'Cascade\Tests\Fixtures\SampleClass' => array(
99 | 'there' => function ($instance, $value) {
100 | $instance->setThere(strtoupper($value).'!!!');
101 | }
102 | )
103 | );
104 |
105 | $loader = new ClassLoader(array());
106 | $existingHandler = $loader->getExtraOptionsHandler('hello');
107 | $this->assertNotNull($existingHandler);
108 | $this->assertTrue(is_callable($existingHandler));
109 |
110 | $this->assertNull($loader->getExtraOptionsHandler('nohandler'));
111 | }
112 |
113 | public function testLoad()
114 | {
115 | $options = array(
116 | 'class' => 'Cascade\Tests\Fixtures\SampleClass',
117 | 'mandatory' => 'someValue',
118 | 'optional_X' => 'testing some stuff',
119 | 'optional_Y' => 'testing other stuff',
120 | 'hello' => 'hello',
121 | 'there' => 'there',
122 | );
123 |
124 | ClassLoader::$extraOptionHandlers = array(
125 | '*' => array(
126 | 'hello' => function ($instance, $value) {
127 | $instance->setHello(strtoupper($value));
128 | }
129 | ),
130 | 'Cascade\Tests\Fixtures\SampleClass' => array(
131 | 'there' => function ($instance, $value) {
132 | $instance->setThere(strtoupper($value).'!!!');
133 | }
134 | )
135 | );
136 |
137 | $loader = new ClassLoader($options);
138 | $instance = $loader->load();
139 |
140 | $expectedInstance = new SampleClass('someValue');
141 | $expectedInstance->optionalX('testing some stuff');
142 | $expectedInstance->optionalY = 'testing other stuff';
143 | $expectedInstance->setHello('HELLO');
144 | $expectedInstance->setThere('THERE!!!');
145 |
146 | $this->assertEquals($expectedInstance, $instance);
147 | }
148 |
149 | /**
150 | * Test a nested class to load
151 | */
152 | public function testLoadDependency()
153 | {
154 | $options = array(
155 | 'class' => 'Cascade\Tests\Fixtures\DependentClass',
156 | 'dependency' => array(
157 | 'class' => 'Cascade\Tests\Fixtures\SampleClass',
158 | 'mandatory' => 'someValue',
159 | )
160 | );
161 |
162 | $loader = new ClassLoader($options);
163 | $instance = $loader->load();
164 |
165 | $expectedInstance = new DependentClass(
166 | new SampleClass('someValue')
167 | );
168 |
169 | $this->assertEquals($expectedInstance, $instance);
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/tests/Config/Loader/FileLoader/FileLoaderAbstractTest.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Tests\Config\Loader\FileLoader;
12 |
13 | use PHPUnit\Framework\MockObject\MockObject;
14 | use PHPUnit\Framework\TestCase;
15 | use Symfony\Component\Config\FileLocator;
16 | use Symfony\Component\Config\FileLocatorInterface;
17 | use org\bovigo\vfs\vfsStream;
18 |
19 | use Cascade\Tests\Fixtures;
20 |
21 | /**
22 | * Class FileLoaderAbstractTest
23 | *
24 | * @author Raphael Antonmattei
25 | */
26 | class FileLoaderAbstractTest extends TestCase
27 | {
28 | /**
29 | * Mock of extending Cascade\Config\Loader\FileLoader\FileLoaderAbstract
30 | * @var MockObject
31 | */
32 | protected $mock = null;
33 |
34 | public function setUp(): void
35 | {
36 | parent::setUp();
37 |
38 | $fileLocatorMock = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')
39 | ->getMock();
40 |
41 | $this->mock = $this->getMockForAbstractClass(
42 | 'Cascade\Config\Loader\FileLoader\FileLoaderAbstract',
43 | array($fileLocatorMock),
44 | 'FileLoaderAbstractMockClass' // mock class name
45 | );
46 |
47 | // Setting valid extensions for tests
48 | \FileLoaderAbstractMockClass::$validExtensions = array('test', 'php');
49 | }
50 |
51 | public function tearDown(): void
52 | {
53 | $this->mock = null;
54 | parent::tearDown();
55 | }
56 |
57 | /**
58 | * Test loading config from a valid file
59 | */
60 | public function testReadFrom()
61 | {
62 | $this->assertEquals(
63 | Fixtures::getSampleYamlString(),
64 | $this->mock->readFrom(Fixtures::getSampleYamlFile())
65 | );
66 | }
67 |
68 | /**
69 | * Test loading config from a valid file
70 | */
71 | public function testLoadFileFromString()
72 | {
73 | $this->assertEquals(
74 | trim(Fixtures::getSampleString()),
75 | $this->mock->readFrom(Fixtures::getSampleString())
76 | );
77 | }
78 |
79 | /**
80 | * Data provider for testGetSectionOf
81 | *
82 | * @return array array with original value, section and expected value
83 | */
84 | public function extensionsDataProvider()
85 | {
86 | return array(
87 | array(true, 'hello/world.test'),
88 | array(true, 'hello/world.php'),
89 | array(false, 'hello/world.jpeg'),
90 | array(false, 'hello/world'),
91 | array(false, '')
92 | );
93 | }
94 |
95 | /**
96 | * Test validating the extension
97 | *
98 | * @param boolean $expected Expected boolean value
99 | * @param string $filepath Filepath to validate
100 | * @dataProvider extensionsDataProvider
101 | */
102 | public function testValidateExtension($expected, $filepath)
103 | {
104 | if ($expected) {
105 | $this->assertTrue($this->mock->validateExtension($filepath));
106 | } else {
107 | $this->assertFalse($this->mock->validateExtension($filepath));
108 | }
109 | }
110 |
111 | /**
112 | * Data provider for testGetSectionOf
113 | *
114 | * @return array array wit original value, section and expected value
115 | */
116 | public function arrayDataProvider()
117 | {
118 | return array(
119 | array(
120 | array(
121 | 'a' => array('aa' => 'AA', 'ab' => 'AB'),
122 | 'b' => array('ba' => 'BA', 'bb' => 'BB')
123 | ),
124 | 'b',
125 | array('ba' => 'BA', 'bb' => 'BB')
126 | ),
127 | array(
128 | array('a' => 'A', 'b' => 'B'),
129 | 'c',
130 | array('a' => 'A', 'b' => 'B'),
131 | ),
132 | array(
133 | array('a' => 'A', 'b' => 'B'),
134 | '',
135 | array('a' => 'A', 'b' => 'B'),
136 | )
137 | );
138 | }
139 |
140 | /**
141 | * Test the getSectionOf function
142 | *
143 | * @param array $array Array of options
144 | * @param string $section Section key
145 | * @param array $expected Expected array for the given section
146 | * @dataProvider arrayDataProvider
147 | */
148 | public function testGetSectionOf(array $array, $section, array $expected)
149 | {
150 | $this->assertSame($expected, $this->mock->getSectionOf($array, $section));
151 | }
152 |
153 | /**
154 | * Test loading an invalid file
155 | */
156 | public function testloadFileFromInvalidFile()
157 | {
158 | $this->expectException(\RuntimeException::class);
159 |
160 | // mocking the file system from a 'config_dir' base dir
161 | $root = vfsStream::setup('config_dir');
162 |
163 | // Adding an unreadable file (chmod 0000)
164 | vfsStream::newFile('config.yml', 0000)
165 | ->withContent(
166 | "---\n".
167 | "hidden_config: true"
168 | )->at($root);
169 |
170 | // This will throw an exception because the file is not readable
171 | $this->mock->readFrom(vfsStream::url('config_dir/config.yml'));
172 |
173 | stream_wrapper_unregister(vfsStream::SCHEME);
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/tests/Config/Loader/FileLoader/JsonTest.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Tests\Config\Loader\FileLoader;
12 |
13 | use Cascade\Tests\Fixtures;
14 | use PHPUnit\Framework\MockObject\MockBuilder;
15 | use PHPUnit\Framework\TestCase;
16 |
17 | /**
18 | * Class JsonTest
19 | *
20 | * @author Raphael Antonmattei
21 | */
22 | class JsonTest extends TestCase
23 | {
24 | /**
25 | * JSON loader mock builder
26 | * @var MockBuilder
27 | */
28 | protected $jsonLoader = null;
29 |
30 | public function setUp(): void
31 | {
32 | parent::setUp();
33 |
34 | $fileLocatorMock = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')
35 | ->getMock();
36 |
37 | $this->jsonLoader = $this->getMockBuilder(
38 | 'Cascade\Config\Loader\FileLoader\Json'
39 | )
40 | ->setConstructorArgs(array($fileLocatorMock))
41 | ->setMethods(array('readFrom', 'isFile', 'validateExtension'))
42 | ->getMock();
43 | }
44 |
45 | public function tearDown(): void
46 | {
47 | $this->jsonLoader = null;
48 | parent::tearDown();
49 | }
50 |
51 | /**
52 | * Test loading a JSON string
53 | */
54 | public function testLoad()
55 | {
56 | $json = Fixtures::getSampleJsonString();
57 |
58 | $this->jsonLoader->expects($this->once())
59 | ->method('readFrom')
60 | ->willReturn($json);
61 |
62 | $this->assertEquals(
63 | json_decode($json, true),
64 | $this->jsonLoader->load($json)
65 | );
66 | }
67 |
68 | /**
69 | * Data provider for testSupportsWithInvalidResource
70 | *
71 | * @return array array non-string values
72 | */
73 | public function notStringDataProvider()
74 | {
75 | return array(
76 | array(array()),
77 | array(true),
78 | array(123),
79 | array(123.456),
80 | array(null),
81 | array(new \stdClass),
82 | array(function () {
83 | })
84 | );
85 | }
86 |
87 | /**
88 | * Test loading resources supported by the JsonLoader
89 | *
90 | * @param mixed $invalidResource Invalid resource value
91 | * @dataProvider notStringDataProvider
92 | */
93 | public function testSupportsWithInvalidResource($invalidResource)
94 | {
95 | $this->assertFalse($this->jsonLoader->supports($invalidResource));
96 | }
97 |
98 | /**
99 | * Test loading a JSON string
100 | */
101 | public function testSupportsWithJsonString()
102 | {
103 | $this->jsonLoader->expects($this->once())
104 | ->method('isFile')
105 | ->willReturn(false);
106 |
107 | $json = Fixtures::getSampleJsonString();
108 |
109 | $this->assertTrue($this->jsonLoader->supports($json));
110 | }
111 |
112 | /**
113 | * Test loading a JSON file
114 | * Note that this function tests isJson with a valid Json string
115 | */
116 | public function testSupportsWithJsonFile()
117 | {
118 | $this->jsonLoader->expects($this->once())
119 | ->method('isFile')
120 | ->willReturn(true);
121 |
122 | $this->jsonLoader->expects($this->once())
123 | ->method('validateExtension')
124 | ->willReturn(true);
125 |
126 | $jsonFile = Fixtures::getSampleJsonFile();
127 |
128 | $this->assertTrue($this->jsonLoader->supports($jsonFile));
129 | }
130 |
131 | /**
132 | * Test isJson method with invalid JSON string.
133 | * Valid scenario is tested by the method above
134 | */
135 | public function testSupportsWithNonJsonString()
136 | {
137 | $this->jsonLoader->expects($this->once())
138 | ->method('isFile')
139 | ->willReturn(false);
140 |
141 | $someString = Fixtures::getSampleString();
142 |
143 | $this->assertFalse($this->jsonLoader->supports($someString));
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/tests/Config/Loader/FileLoader/PhpArrayTest.php:
--------------------------------------------------------------------------------
1 | loader = new ArrayLoader(new FileLocator());
28 | }
29 |
30 | protected function tearDown(): void
31 | {
32 | $this->loader = null;
33 | }
34 |
35 | public function testSupportsPhpFile()
36 | {
37 | $this->assertTrue($this->loader->supports(__DIR__.'/../../../Fixtures/fixture_config.php'));
38 | }
39 |
40 | public function testDoesNotSupportNonPhpFiles()
41 | {
42 | $this->assertFalse($this->loader->supports('foo'));
43 | $this->assertFalse($this->loader->supports(__DIR__.'/../../../Fixtures/fixture_config.json'));
44 | }
45 |
46 | public function testThrowsExceptionWhenLoadingFileIfDoesNotReturnValidPhpArray()
47 | {
48 | $this->expectException(\InvalidArgumentException::class);
49 |
50 | $this->loader->load(__DIR__.'/../../../Fixtures/fixture_invalid_config.php');
51 | }
52 |
53 | public function testLoadsPhpArrayConfigFromFile()
54 | {
55 | $this->assertSame(
56 | include __DIR__.'/../../../Fixtures/fixture_config.php',
57 | $this->loader->load(__DIR__.'/../../../Fixtures/fixture_config.php')
58 | );
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/tests/Config/Loader/FileLoader/YamlTest.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Tests\Config\Loader\FileLoader;
12 |
13 | use PHPUnit\Framework\MockObject\MockBuilder;
14 | use PHPUnit\Framework\TestCase;
15 | use Symfony\Component\Yaml\Yaml as YamlParser;
16 |
17 | use Cascade\Tests\Fixtures;
18 |
19 | /**
20 | * Class YamlTest
21 | *
22 | * @author Raphael Antonmattei
23 | */
24 | class YamlTest extends TestCase
25 | {
26 | /**
27 | * Yaml loader mock builder
28 | * @var MockBuilder
29 | */
30 | protected $yamlLoader = null;
31 |
32 | public function setUp(): void
33 | {
34 | parent::setUp();
35 |
36 | $fileLocatorMock = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')
37 | ->getMock();
38 |
39 | $this->yamlLoader = $this->getMockBuilder(
40 | 'Cascade\Config\Loader\FileLoader\Yaml'
41 | )
42 | ->setConstructorArgs(array($fileLocatorMock))
43 | ->setMethods(array('readFrom', 'isFile', 'validateExtension'))
44 | ->getMock();
45 | }
46 |
47 | public function tearDown(): void
48 | {
49 | $this->yamlLoader = null;
50 | parent::tearDown();
51 | }
52 |
53 | /**
54 | * Test loading a Yaml string
55 | */
56 | public function testLoad()
57 | {
58 | $yaml = Fixtures::getSampleYamlString();
59 |
60 | $this->yamlLoader->expects($this->once())
61 | ->method('readFrom')
62 | ->willReturn($yaml);
63 |
64 | $this->assertEquals(
65 | YamlParser::parse($yaml),
66 | $this->yamlLoader->load($yaml)
67 | );
68 | }
69 |
70 | /**
71 | * Data provider for testSupportsWithInvalidResource
72 | * @return array array non-string values
73 | */
74 | public function notStringDataProvider()
75 | {
76 | return array(
77 | array(array()),
78 | array(true),
79 | array(123),
80 | array(123.456),
81 | array(null),
82 | array(new \stdClass),
83 | // array(function () {
84 | // })
85 | // cannot test Closure type because of PhpUnit
86 | // @see https://github.com/sebastianbergmann/phpunit/issues/451
87 | );
88 | }
89 |
90 | /**
91 | * Test loading resources supported by the YamlLoader
92 | *
93 | * @param mixed $invalidResource Invalid resource value
94 | * @dataProvider notStringDataProvider
95 | */
96 | public function testSupportsWithInvalidResource($invalidResource)
97 | {
98 | $this->assertFalse($this->yamlLoader->supports($invalidResource));
99 | }
100 |
101 | /**
102 | * Test loading a Yaml string
103 | */
104 | public function testSupportsWithYamlString()
105 | {
106 | $this->yamlLoader->expects($this->once())
107 | ->method('isFile')
108 | ->willReturn(false);
109 |
110 | $yaml = Fixtures::getSampleYamlString();
111 |
112 | $this->assertTrue($this->yamlLoader->supports($yaml));
113 | }
114 |
115 | /**
116 | * Test loading a Yaml file
117 | */
118 | public function testSupportsWithYamlFile()
119 | {
120 | $this->yamlLoader->expects($this->once())
121 | ->method('isFile')
122 | ->willReturn(true);
123 |
124 | $this->yamlLoader->expects($this->once())
125 | ->method('validateExtension')
126 | ->willReturn(true);
127 |
128 | $yamlFile = Fixtures::getSampleYamlFile();
129 |
130 | $this->assertTrue($this->yamlLoader->supports($yamlFile));
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/tests/Config/Loader/PhpArrayTest.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Tests\Config\Loader;
12 |
13 | use Cascade\Config\Loader\PhpArray as ArrayLoader;
14 | use Cascade\Tests\Fixtures;
15 | use PHPUnit\Framework\TestCase;
16 |
17 | /**
18 | * Class PhpArrayTest
19 | *
20 | * @author Raphael Antonmattei
21 | */
22 | class PhpArrayTest extends TestCase
23 | {
24 | /**
25 | * Array loader object
26 | * @var ArrayLoader
27 | */
28 | protected $arrayLoader = null;
29 |
30 | public function setUp(): void
31 | {
32 | parent::setUp();
33 |
34 | $this->arrayLoader = new ArrayLoader();
35 | }
36 |
37 | public function tearDown(): void
38 | {
39 | $this->arrayLoader = null;
40 | parent::tearDown();
41 | }
42 |
43 | /**
44 | * Test loading a Php array
45 | */
46 | public function testLoad()
47 | {
48 | $array = Fixtures::getSamplePhpArray();
49 | $this->assertSame($array, $this->arrayLoader->load($array));
50 | }
51 |
52 | /**
53 | * Data provider for testSupportsWithInvalidResource
54 | *
55 | * @return array array of non-array values
56 | */
57 | public function notStringDataProvider()
58 | {
59 | return array(
60 | array('Some string'),
61 | array(true),
62 | array(123),
63 | array(123.456),
64 | array(null),
65 | array(new \stdClass),
66 | array(function () {
67 | })
68 | );
69 | }
70 |
71 | /**
72 | * Test loading resources supported by the YamlLoader
73 | *
74 | * @param mixed $invalidResource Invalid resource value
75 | * @dataProvider notStringDataProvider
76 | */
77 | public function testSupportsWithInvalidResource($invalidResource)
78 | {
79 | $this->assertFalse($this->arrayLoader->supports($invalidResource));
80 | }
81 |
82 | /**
83 | * Test supports with a valid array
84 | */
85 | public function testSupports()
86 | {
87 | $array = Fixtures::getSamplePhpArray();
88 | $this->assertTrue($this->arrayLoader->supports($array));
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/tests/ConfigTest.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Tests;
12 |
13 | use Monolog\Registry;
14 |
15 | use Cascade\Config;
16 | use Cascade\Tests\Fixtures;
17 | use PHPUnit\Framework\TestCase;
18 |
19 | /**
20 | * Class ConfigTest
21 | *
22 | * @author Raphael Antonmattei
23 | */
24 | class ConfigTest extends TestCase
25 | {
26 | /**
27 | * Testing contructor and load functions
28 | */
29 | public function testLoad()
30 | {
31 | $mock = $this->getMockBuilder('Cascade\Config\ConfigLoader')
32 | ->disableOriginalConstructor()
33 | ->setMethods(array('load'))
34 | ->getMock();
35 |
36 | $array = Fixtures::getSamplePhpArray();
37 |
38 | $mock->expects($this->once())
39 | ->method('load')
40 | ->willReturn($array);
41 |
42 | $config = new Config(array(''), $mock);
43 | $config->load();
44 | }
45 |
46 | public function testConfigure()
47 | {
48 | $options = Fixtures::getPhpArrayConfig();
49 |
50 | // Mocking the ConfigLoader with the load method
51 | $configLoader = $this->getMockBuilder('Cascade\Config\ConfigLoader')
52 | ->disableOriginalConstructor()
53 | ->setMethods(array('load'))
54 | ->getMock();
55 |
56 | $configLoader->method('load')->willReturn($options);
57 |
58 | // Mocking the config object and set expectations for the configure methods
59 | $config = $this->getMockBuilder('Cascade\Config')
60 | ->setConstructorArgs(array($options, $configLoader))
61 | ->setMethods(array(
62 | 'configureFormatters',
63 | 'configureProcessors',
64 | 'configureHandlers',
65 | 'configureLoggers'
66 | ))
67 | ->getMock();
68 |
69 | $config->expects($this->once())->method('configureFormatters');
70 | $config->expects($this->once())->method('configureProcessors');
71 | $config->expects($this->once())->method('configureHandlers');
72 | $config->expects($this->once())->method('configureLoggers');
73 |
74 | $config->load();
75 | $config->configure();
76 | }
77 |
78 | /**
79 | * Test configure throwing an exception due to missing 'loggers' key
80 | */
81 | public function testConfigureWithNoLoggers()
82 | {
83 | $this->expectException(\RuntimeException::class);
84 |
85 | $options = array();
86 |
87 | // Mocking the ConfigLoader with the load method
88 | $configLoader = $this->getMockBuilder('Cascade\Config\ConfigLoader')
89 | ->disableOriginalConstructor()
90 | ->setMethods(array('load'))
91 | ->getMock();
92 |
93 | $configLoader->method('load')->willReturn($options);
94 |
95 | // Mocking the config object
96 | $config = $this->getMockBuilder('Cascade\Config')
97 | ->setConstructorArgs(array($options, $configLoader))
98 | ->setMethods(null)
99 | ->getMock();
100 |
101 | $config->load();
102 |
103 | // This should trigger an exception because there is no 'loggers' key in
104 | // the options passed in
105 | $config->configure();
106 | }
107 |
108 | public function testLoggersConfigured()
109 | {
110 | $options = Fixtures::getPhpArrayConfig();
111 |
112 | // Mocking the ConfigLoader with the load method
113 | $configLoader = $this->getMockBuilder('Cascade\Config\ConfigLoader')
114 | ->disableOriginalConstructor()
115 | ->setMethods(array('load'))
116 | ->getMock();
117 |
118 | $configLoader->method('load')->willReturn($options);
119 |
120 | $config = new Config($options, $configLoader);
121 |
122 | $config->load();
123 | $config->configure();
124 |
125 | $this->assertTrue(Registry::hasLogger('my_logger'));
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/tests/Fixtures.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Tests;
12 |
13 | class Fixtures
14 | {
15 | /**
16 | * Return the fixture directory
17 | *
18 | * @return string Fixture directory
19 | */
20 | public static function fixtureDir()
21 | {
22 | return realpath(__DIR__.'/Fixtures');
23 | }
24 |
25 | /**
26 | * Return a path to a non existing file
27 | *
28 | * @return string Wrong file path
29 | */
30 | public static function getInvalidFile()
31 | {
32 | return 'some/non/existing/file.txt';
33 | }
34 |
35 | /**
36 | * Return the fixture Yaml config file
37 | *
38 | * @return string Path to yaml config file
39 | */
40 | public static function getYamlConfigFile()
41 | {
42 | return self::fixtureDir().'/fixture_config.yml';
43 | }
44 |
45 | /**
46 | * Return a config as Yaml
47 | *
48 | * @return string Yaml config
49 | */
50 | public static function getYamlConfig()
51 | {
52 | return file_get_contents(self::fixtureDir().'/fixture_config.yml');
53 | }
54 |
55 | /**
56 | * Return the fixture sample Yaml file
57 | *
58 | * @return string Path to a sample yaml file
59 | */
60 | public static function getSampleYamlFile()
61 | {
62 | return self::fixtureDir().'/fixture_sample.yml';
63 | }
64 |
65 | /**
66 | * Return the fixture sample Yaml string
67 | *
68 | * @return string Sample yaml string
69 | */
70 | public static function getSampleYamlString()
71 | {
72 | return trim(
73 | '---'."\n".
74 | 'greeting: "hello"'."\n".
75 | 'to: "you"'."\n"
76 | );
77 | }
78 |
79 | /**
80 | * Return the fixture JSON config file
81 | *
82 | * @return string Path to JSON config file
83 | */
84 | public static function getJsonConfigFile()
85 | {
86 | return self::fixtureDir().'/fixture_config.json';
87 | }
88 |
89 | /**
90 | * Return a config as JSON
91 | *
92 | * @return string JSON config
93 | */
94 | public static function getJsonConfig()
95 | {
96 | return file_get_contents(self::fixtureDir().'/fixture_config.json');
97 | }
98 |
99 | /**
100 | * Return the fixture sample JSON file
101 | *
102 | * @return string Path to a sample JSON file
103 | */
104 | public static function getSampleJsonFile()
105 | {
106 | return self::fixtureDir().'/fixture_sample.json';
107 | }
108 |
109 | /**
110 | * Return the fixture sample JSON string
111 | *
112 | * @return string Sample JSON string
113 | */
114 | public static function getSampleJsonString()
115 | {
116 | return trim(
117 | '{'."\n".
118 | ' "greeting": "hello",'."\n".
119 | ' "to": "you"'."\n".
120 | '}'."\n"
121 | );
122 | }
123 |
124 | /**
125 | * Return a sample string
126 | *
127 | * @return string Sample string
128 | */
129 | public static function getSampleString()
130 | {
131 | return " some string with new \n\n lines and white spaces \n\n";
132 | }
133 |
134 | /**
135 | * Return the fixture PHP array config file
136 | *
137 | * @return string Path to PHP array config file
138 | */
139 | public static function getPhpArrayConfigFile()
140 | {
141 | return self::fixtureDir().'/fixture_config.php';
142 | }
143 |
144 | /**
145 | * Return a config array
146 | *
147 | * @return array Config array
148 | */
149 | public static function getPhpArrayConfig()
150 | {
151 | return require self::fixtureDir().'/fixture_config.php';
152 | }
153 |
154 | /**
155 | * Return a sample array
156 | *
157 | * @return array Sample array
158 | */
159 | public static function getSamplePhpArray()
160 | {
161 | return array(
162 | 'greeting' => 'hello',
163 | 'to' => 'you'
164 | );
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/tests/Fixtures/DependentClass.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Tests\Fixtures;
12 |
13 | /**
14 | * Class DependentClass
15 | *
16 | * @author Raphael Antonmattei
17 | * @author Dom Morgan
18 | */
19 | class DependentClass
20 | {
21 | /**
22 | * An object dependency
23 | * @var SampleClass
24 | */
25 | private $dependency;
26 |
27 | /**
28 | * Constructor
29 | *
30 | * @param SampleClass $dependency Some sample object
31 | */
32 | public function __construct(SampleClass $dependency)
33 | {
34 | $this->setDependency($dependency);
35 | }
36 |
37 | /**
38 | * Set the object dependency
39 | *
40 | * @param SampleClass $dependency Some sample object
41 | */
42 | public function setDependency(SampleClass $dependency)
43 | {
44 | $this->dependency = $dependency;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/tests/Fixtures/SampleClass.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | namespace Cascade\Tests\Fixtures;
12 |
13 | /**
14 | * Class SampleClass
15 | *
16 | * @author Raphael Antonmattei
17 | */
18 | class SampleClass
19 | {
20 | /**
21 | * Some mandatory member
22 | * @var mixed
23 | */
24 | private $mandatory;
25 |
26 | /**
27 | * Optional member A
28 | * @var mixed
29 | */
30 | private $optionalA;
31 |
32 | /**
33 | * Optional member A
34 | * @var mixed
35 | */
36 | public $optionalB;
37 |
38 | /**
39 | * Optional member X
40 | * @var mixed
41 | */
42 | private $optionalX;
43 |
44 | /**
45 | * Optional member Y
46 | * @var mixed
47 | */
48 | public $optionalY;
49 |
50 | /**
51 | * Hello member
52 | * @var mixed
53 | */
54 | private $hello;
55 |
56 | /**
57 | * There member
58 | * @var mixed
59 | */
60 | private $there;
61 |
62 | /**
63 | * Constructor
64 | *
65 | * @param mixed $mandatory Some mandatory param
66 | * @param string $optionalA Some optional param
67 | * @param string $optionalB Some other optional param
68 | * @param string $optional_snake Some optional snake param
69 | */
70 | public function __construct(
71 | $mandatory,
72 | $optionalA = 'AAA',
73 | $optionalB = 'BBB',
74 | $optional_snake = 'snake'
75 | ) {
76 | $this->setMandatory($mandatory);
77 | }
78 |
79 | /**
80 | * Set the mandatory property
81 | *
82 | * @param mixed $mandatory Some value
83 | */
84 | public function setMandatory($mandatory)
85 | {
86 | $this->mandatory = $mandatory;
87 | }
88 |
89 | /**
90 | * Function that sets the optionalA member
91 | *
92 | * @param mixed $value Some value
93 | */
94 | public function optionalA($value)
95 | {
96 | $this->optionalA = $value;
97 | }
98 |
99 | /**
100 | * Function that sets the optionalX member
101 | *
102 | * @param mixed $value Some value
103 | */
104 | public function optionalX($value)
105 | {
106 | $this->optionalX = $value;
107 | }
108 |
109 | /**
110 | * Function that sets the hello member
111 | *
112 | * @param mixed $value Some value
113 | */
114 | public function setHello($value)
115 | {
116 | $this->hello = $value;
117 | }
118 |
119 | /**
120 | * Function that sets the there member
121 | *
122 | * @param mixed $value Some value
123 | */
124 | public function setThere($value)
125 | {
126 | $this->there = $value;
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/tests/Fixtures/fixture_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "disable_existing_loggers": false,
4 | "formatters": {
5 | "simple": {
6 | "class": "Monolog\\Formatter\\LineFormatter",
7 | "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
8 | }
9 | },
10 | "handlers": {
11 | "console": {
12 | "class": "Monolog\\Handler\\StreamHandler",
13 | "level": "DEBUG",
14 | "formatter": "simple",
15 | "stream": "php://stdout"
16 | },
17 | "info_file_handler": {
18 | "class": "Monolog\\Handler\\StreamHandler",
19 | "level": "INFO",
20 | "formatter": "simple",
21 | "stream": "./info.log"
22 | },
23 | "error_file_handler": {
24 | "class": "Monolog\\Handler\\StreamHandler",
25 | "level": "ERROR",
26 | "formatter": "simple",
27 | "stream": "./error.log"
28 | }
29 | },
30 | "processors": {
31 | "tag_processor": {
32 | "class": "Monolog\\Processor\\TagProcessor"
33 | }
34 | },
35 | "loggers": {
36 | "my_logger": {
37 | "level": "ERROR",
38 | "handlers": [
39 | "console"
40 | ],
41 | "propagate": "no"
42 | }
43 | },
44 | "root": {
45 | "level": "INFO",
46 | "handlers": [
47 | "console",
48 | "info_file_handler",
49 | "error_file_handler"
50 | ]
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/tests/Fixtures/fixture_config.php:
--------------------------------------------------------------------------------
1 |
6 | * (c) The Orchard
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 | return array(
12 | 'version' => 1,
13 |
14 | 'formatters' => array(
15 | 'spaced' => array(
16 | 'format' => "%datetime% %channel%.%level_name% %message%\n",
17 | 'include_stacktraces' => true
18 | ),
19 | 'dashed' => array(
20 | 'format' => "%datetime%-%channel%.%level_name% - %message%\n"
21 | ),
22 | ),
23 | 'handlers' => array(
24 | 'console' => array(
25 | 'class' => 'Monolog\Handler\StreamHandler',
26 | 'level' => 'DEBUG',
27 | 'formatter' => 'spaced',
28 | 'stream' => 'php://stdout'
29 | ),
30 |
31 | 'info_file_handler' => array(
32 | 'class' => 'Monolog\Handler\StreamHandler',
33 | 'level' => 'INFO',
34 | 'formatter' => 'dashed',
35 | 'stream' => './demo_info.log'
36 | ),
37 |
38 | 'error_file_handler' => array(
39 | 'class' => 'Monolog\Handler\StreamHandler',
40 | 'level' => 'ERROR',
41 | 'stream' => './demo_error.log',
42 | 'formatter' => 'spaced'
43 | ),
44 |
45 | 'group_handler' => array(
46 | 'class' => 'Monolog\Handler\GroupHandler',
47 | 'handlers' => array(
48 | 'console',
49 | 'info_file_handler',
50 | ),
51 | ),
52 |
53 | 'fingers_crossed_handler' => array(
54 | 'class' => 'Monolog\Handler\FingersCrossedHandler',
55 | 'handler' => 'group_handler',
56 | ),
57 | ),
58 | 'processors' => array(
59 | 'tag_processor' => array(
60 | 'class' => 'Monolog\Processor\TagProcessor'
61 | )
62 | ),
63 | 'loggers' => array(
64 | 'my_logger' => array(
65 | 'handlers' => array('console', 'info_file_handler')
66 | )
67 | )
68 | );
69 |
--------------------------------------------------------------------------------
/tests/Fixtures/fixture_config.yml:
--------------------------------------------------------------------------------
1 | ---
2 | version: 1
3 |
4 | disable_existing_loggers: false
5 |
6 | formatters:
7 | spaced:
8 | format: "%datetime% %channel%.%level_name% %message%\n"
9 | include_stacktraces: true
10 | dashed:
11 | format: "%datetime%-%channel%.%level_name% - %message%\n"
12 |
13 | processors:
14 | tag_processor:
15 | class: Monolog\Processor\TagProcessor
16 |
17 | handlers:
18 | console:
19 | class: Monolog\Handler\StreamHandler
20 | level: DEBUG
21 | formatter: spaced
22 | stream: php://stdout
23 |
24 | info_file_handler:
25 | class: Monolog\Handler\StreamHandler
26 | level: INFO
27 | formatter: dashed
28 | stream: ./demo_info.log
29 |
30 | error_file_handler:
31 | class: Monolog\Handler\StreamHandler
32 | level: ERROR
33 | stream: ./demo_error.log
34 | formatter: spaced
35 |
36 | loggers:
37 | my_logger:
38 | handlers: [console, info_file_handler]
39 |
--------------------------------------------------------------------------------
/tests/Fixtures/fixture_invalid_config.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | class UtilTest extends TestCase
14 | {
15 | public function testSnakeToCamelCase()
16 | {
17 | // non-strings
18 | $this->assertSame(null, Util::snakeToCamelCase(null));
19 | $this->assertSame(null, Util::snakeToCamelCase(array()));
20 | $this->assertSame(null, Util::snakeToCamelCase(1));
21 |
22 | // strings
23 | $this->assertSame('', Util::snakeToCamelCase(''));
24 | $this->assertSame('foo', Util::snakeToCamelCase('foo'));
25 | $this->assertSame('fooBar', Util::snakeToCamelCase('foo_bar'));
26 | $this->assertSame('fooBarBaz', Util::snakeToCamelCase('foo_bar_baz'));
27 |
28 | // weird strings
29 | $this->assertSame('_', Util::snakeToCamelCase('_'));
30 | $this->assertSame('__', Util::snakeToCamelCase('___'));
31 | $this->assertSame('_ _', Util::snakeToCamelCase('_ _'));
32 | $this->assertSame('x_', Util::snakeToCamelCase('X__'));
33 | $this->assertSame('_X', Util::snakeToCamelCase('__X'));
34 | }
35 | }
36 |
--------------------------------------------------------------------------------