Skip to content

Commit

Permalink
Merge pull request #31 from dmnc/master
Browse files Browse the repository at this point in the history
Add support for handlers where constructor params are objects to fix #30
  • Loading branch information
rantonmattei committed Aug 24, 2015
2 parents 02b1051 + 58677db commit 4da5092
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 14 deletions.
22 changes: 22 additions & 0 deletions examples/dependency_config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
version: 1

disable_existing_loggers: false

handlers:
sentry:
class: Monolog\Handler\RavenHandler
level: DEBUG
raven_client:
class: Raven_Client
options_or_dsn: https://something:[email protected]/1
redis:
class: Monolog\Handler\RedisHandler
level: DEBUG
key: cascade
redis:
class: Redis
connect: ['127.0.0.1', 6379]
loggers:
dependency:
handlers: [sentry, redis]
13 changes: 13 additions & 0 deletions examples/dependency_logger.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php
require_once(realpath(__DIR__.'/../vendor/autoload.php'));

use Cascade\Cascade;

// For these to work you will need php-redis and raven/raven

// You will want to update this file with a valid dsn
$loggerConfigFile = realpath(__DIR__.'/dependency_config.yml');

Cascade::fileConfig($loggerConfigFile);
Cascade::getLogger('dependency')->info('Well, that works!');
Cascade::getLogger('dependency')->error('Maybe not...');
21 changes: 21 additions & 0 deletions src/Config/Loader/ClassLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,25 @@ protected function setClass()
unset($this->rawOptions['class']);
}

/**
* Recursively loads objects into any of the rawOptions that represent
* a class
*
* @author Dom Morgan <[email protected]>
*/
protected function loadChildClasses()
{
foreach ($this->rawOptions as &$option) {
if (is_array($option)
&& array_key_exists('class', $option)
&& class_exists($option['class'])
) {
$classLoader = new ClassLoader($option);
$option = $classLoader->load();
}
}
}

/**
* Return option values indexed by name using camelCased keys
*
Expand Down Expand Up @@ -163,6 +182,8 @@ private function resolveOptions()
*/
public function load()
{
$this->loadChildClasses();

list($constructorResolvedOptions, $extraResolvedOptions) = $this->resolveOptions();
$instance = $this->reflected->newInstanceArgs($constructorResolvedOptions);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
namespace Cascade\Config\Loader\ClassLoader\Resolver;

use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;

/**
* Constructor Resolver. Pull args from the contructor and set up an option
Expand Down Expand Up @@ -55,15 +56,19 @@ public function __construct(\ReflectionClass $reflected)
/**
* Fetches constructor args (array of ReflectionParameter) from the reflected class
* and set them as an associative array
*
* Convert the parameter names to camelCase for classes that have contructor
* params defined in snake_case for consistency with the options
*/
public function initConstructorArgs()
{
$constructor = $this->reflected->getConstructor();
$nameConverter = new CamelCaseToSnakeCaseNameConverter();

if (!is_null($constructor)) {
// Index parameters by their names
foreach ($constructor->getParameters() as $param) {
$this->constructorArgs[$param->getName()] = $param;
$this->constructorArgs[$nameConverter->denormalize($param->getName())] = $param;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
use Cascade\Config\Loader\ClassLoader\Resolver\ConstructorResolver;
use Cascade\Tests\Fixtures\SampleClass;

use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;

/**
* Class ConstructorResolverTest
*
Expand Down Expand Up @@ -74,13 +76,17 @@ public function testConstructor()

/**
* Test that constructor args were pulled properly
*
* Notie that we need to deuplicate the CamelCase conversion here for old
* fashioned classes
*/
public function testInitConstructorArgs()
{
$expectedConstructorArgs = array();
$nameConverter = new CamelCaseToSnakeCaseNameConverter();

foreach ($this->getConstructorArgs() as $param) {
$expectedConstructorArgs[$param->getName()] = $param;
$expectedConstructorArgs[$nameConverter->denormalize($param->getName())] = $param;
}
$this->assertEquals($expectedConstructorArgs, $this->resolver->getConstructorArgs());
}
Expand All @@ -91,12 +97,13 @@ public function testInitConstructorArgs()
public function testHashToArgsArray()
{
$this->assertEquals(
array('someValue', 'hello', 'there'),
array('someValue', 'hello', 'there', 'slither'),
$this->resolver->hashToArgsArray(
array( // Not properly ordered on purpose
'optionalB' => 'there',
'optionalA' => 'hello',
'mandatory' => 'someValue'
'optionalB' => 'there',
'optionalA' => 'hello',
'optionalSnake' => 'slither',
'mandatory' => 'someValue',
)
)
);
Expand All @@ -113,22 +120,23 @@ public function optionsProvider()
{
return array(
array(
array('someValue', 'hello', 'there'), // Expected resolved options
array('someValue', 'hello', 'there', 'slither'), // Expected resolved options
array( // Options (order should not matter, part of resolution)
'optionalB' => 'there',
'optionalA' => 'hello',
'mandatory' => 'someValue'
'optionalB' => 'there',
'optionalA' => 'hello',
'mandatory' => 'someValue',
'optionalSnake' => 'slither',
)
),
array(
array('someValue', 'hello', 'BBB'),
array('someValue', 'hello', 'BBB', 'snake'),
array(
'mandatory' => 'someValue',
'optionalA' => 'hello'
'optionalA' => 'hello',
)
),
array(
array('someValue', 'AAA', 'BBB'),
array('someValue', 'AAA', 'BBB', 'snake'),
array('mandatory' => 'someValue')
)
);
Expand Down
26 changes: 26 additions & 0 deletions tests/Config/Loader/ClassLoaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

use Cascade\Config\Loader\ClassLoader;
use Cascade\Tests\Fixtures\SampleClass;
use Cascade\Tests\Fixtures\DependentClass;

/**
* Class ClassLoaderTest
Expand Down Expand Up @@ -145,4 +146,29 @@ public function testLoad()

$this->assertEquals($expectedInstance, $instance);
}

/**
* Test a nested class to load
*
* @author Dom Morgan <[email protected]>
*/
public function testLoadDependency()
{
$options = array(
'class' => 'Cascade\Tests\Fixtures\DependentClass',
'dependency' => array(
'class' => 'Cascade\Tests\Fixtures\SampleClass',
'mandatory' => 'someValue',
)
);

$loader = new ClassLoader($options);
$instance = $loader->load();

$expectedInstance = new DependentClass(
new SampleClass('someValue')
);

$this->assertEquals($expectedInstance, $instance);
}
}
47 changes: 47 additions & 0 deletions tests/Fixtures/DependentClass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php
/**
* This file is part of the Monolog Cascade package.
*
* (c) Raphael Antonmattei <[email protected]>
* (c) The Orchard
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Cascade\Tests\Fixtures;

/**
* Class SampleClass
*
* @author Raphael Antonmattei <[email protected]>
*/
class DependentClass
{
/**
* An object dependency
* @var Cascade\Tests\Fixtures\SampleClass
*/
private $dependency;

/**
* Constructor
*
* @param mixed $mandatory Some mandatory param
* @param string $optionalA Some optional param
*/
public function __construct(
SampleClass $dependency
) {
$this->setDependency($dependency);
}

/**
* Set the object dependency
*
* @param Cascade\Tests\Fixtures\SampleClass $dependency Some value
*/
public function setDependency($dependency)
{
$this->dependency = $dependency;
}
}
3 changes: 2 additions & 1 deletion tests/Fixtures/SampleClass.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ class SampleClass
public function __construct(
$mandatory,
$optionalA = 'AAA',
$optionalB = 'BBB'
$optionalB = 'BBB',
$optional_snake = 'snake'
) {
$this->setMandatory($mandatory);
}
Expand Down

0 comments on commit 4da5092

Please sign in to comment.