Skip to content

Commit

Permalink
[Bugfix/Enhancement] Ability to provide named parameters for formatte…
Browse files Browse the repository at this point in the history
…r configuration (#22)

* Added failing Unit Test for named arguments in Formatter configuration

* Added ability to pass named parameters within formatter configuration

* Added PHP 7.1 to `.travis.yml`
  • Loading branch information
boesing authored and abacaphiliac committed May 19, 2017
1 parent 533d72c commit 51822b1
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 69 deletions.
6 changes: 5 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ matrix:
env: DEPS=lowest
- php: 7.0
env: DEPS=latest
- php: 7.1
env: DEPS=lowest
- php: 7.1
env: DEPS=latest

install:
- if [[ $DEPS == 'latest' ]]; then travis_retry composer update --no-interaction --prefer-source ; fi
Expand All @@ -32,4 +36,4 @@ script:
- vendor/bin/phpunit --coverage-text

notifications:
email: false
email: false
190 changes: 122 additions & 68 deletions src/EnliteMonolog/Service/MonologServiceFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use Closure;
use Exception;
use Interop\Container\ContainerInterface;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\HandlerInterface;
use Monolog\Logger;
use Monolog\Formatter\FormatterInterface;
Expand Down Expand Up @@ -83,68 +82,50 @@ public function createHandler($container, MonologOptions $options, $handler)
{
if (is_string($handler) && $container->has($handler)) {
return $container->get($handler);
} else {
if (!isset($handler['name'])) {
throw new RuntimeException('Cannot create logger handler');
}

if (!class_exists($handler['name'])) {
throw new RuntimeException('Cannot create logger handler (' . $handler['name'] . ')');
}
}

if (isset($handler['args'])) {
if (!is_array($handler['args'])) {
throw new RuntimeException('Arguments of handler(' . $handler['name'] . ') must be array');
}

$reflection = new \ReflectionClass($handler['name']);
if (!isset($handler['name'])) {
throw new RuntimeException('Cannot create logger handler');
}

if (isset($handler['args']['handler'])) {
foreach ($options->getHandlers() as $key => $option) {
if ($handler['args']['handler'] == $key) {
$handler['args']['handler'] = $this->createHandler($container, $options, $option);
break;
}
}
}
$handlerClassName = $handler['name'];

$parameters = array();
$handlerOptions = $handler['args'];
if (!class_exists($handlerClassName)) {
throw new RuntimeException('Cannot create logger handler (' . $handlerClassName . ')');
}

$requiredArgsCount = $reflection->getConstructor()->getNumberOfRequiredParameters();
$arguments = array_key_exists('args', $handler) ? $handler['args'] : array();

if ($requiredArgsCount > count($handlerOptions)) {
throw new RuntimeException(sprintf('Handler(%s) requires at least %d params. Only %d passed.', $handler['name'], $requiredArgsCount, count($handlerOptions)));
}
if (!is_array($arguments)) {
throw new RuntimeException('Arguments of handler(' . $handlerClassName . ') must be array');
}

foreach($reflection->getConstructor()->getParameters() as $parameter) {
if (!$parameter->isOptional() && !isset($handlerOptions[$parameter->getName()])) {
$argumentValue = array_shift($handlerOptions);
} elseif (isset($handlerOptions[$parameter->getName()])) {
$argumentValue = $handlerOptions[$parameter->getName()];
unset($handlerOptions[$parameter->getName()]);
} else {
$argumentValue = $parameter->getDefaultValue();
}
$parameters[$parameter->getPosition()] = $argumentValue;
if (isset($arguments['handler'])) {
foreach ($options->getHandlers() as $key => $option) {
if ($arguments['handler'] == $key) {
$arguments['handler'] = $this->createHandler($container, $options, $option);
break;
}

/** @var HandlerInterface $instance */
$instance = $reflection->newInstanceArgs($parameters);
} else {
$class = $handler['name'];

/** @var HandlerInterface $instance */
$instance = new $class();
}
}

if (isset($handler['formatter'])) {
$formatter = $this->createFormatter($container, $handler['formatter']);
$instance->setFormatter($formatter);
}
try {
/** @var HandlerInterface $instance */
$instance = $this->createInstanceFromArguments($handlerClassName, $arguments);
} catch (\InvalidArgumentException $exception) {
throw new RuntimeException(sprintf(
'Handler(%s) has an invalid argument configuration',
$handlerClassName
), 0, $exception);
}

return $instance;
if (isset($handler['formatter'])) {
$formatter = $this->createFormatter($container, $handler['formatter']);
$instance->setFormatter($formatter);
}

return $instance;
}

/**
Expand All @@ -159,29 +140,35 @@ public function createFormatter($container, $formatter)
{
if (is_string($formatter) && $container->has($formatter)) {
return $container->get($formatter);
} else {
if (!isset($formatter['name'])) {
throw new RuntimeException('Cannot create logger formatter');
}
}

if (!isset($formatter['name'])) {
throw new RuntimeException('Cannot create logger formatter');
}

if (!class_exists($formatter['name'])) {
throw new RuntimeException('Cannot create logger formatter (' . $formatter['name'] . ')');
}
$formatterClassName = $formatter['name'];

if (isset($formatter['args'])) {
if (!is_array($formatter['args'])) {
throw new RuntimeException('Arguments of formatter(' . $formatter['name'] . ') must be array');
}
if (!class_exists($formatter['name'])) {
throw new RuntimeException('Cannot create logger formatter (' . $formatterClassName . ')');
}

$reflection = new \ReflectionClass($formatter['name']);
$arguments = array_key_exists('args', $formatter) ? $formatter['args'] : array();

return call_user_func_array(array($reflection, 'newInstance'), $formatter['args']);
}
if (!is_array($arguments)) {
throw new RuntimeException('Arguments of formatter(' . $formatterClassName . ') must be array');
}

$class = $formatter['name'];
try {
/** @var FormatterInterface $instance */
$instance = $this->createInstanceFromArguments($formatterClassName, $arguments);
} catch (\InvalidArgumentException $exception) {
throw new RuntimeException(sprintf(
'Formatter(%s) has an invalid argument configuration',
$formatterClassName
), 0, $exception);
}

return new $class();
}
return $instance;
}

/**
Expand Down Expand Up @@ -218,4 +205,71 @@ public function createProcessor($container, $processor)

throw new RuntimeException('Unknown processor type, must be a Closure or the FQCN of an invokable class');
}

/**
* Handles the constructor arguments and if they're named, just sort them to fit constructor ordering.
*
* @param string $className
* @param array $arguments
*
* @return object
* @throws \InvalidArgumentException If given arguments are not valid for provided className constructor.
*/
private function createInstanceFromArguments($className, array $arguments)
{
$reflection = new \ReflectionClass($className);
$constructor = $reflection->getConstructor();

// There is no or at least a non-accessible constructor for provided class name,
// therefore there is no need to handle arguments anyway
if ($constructor === null) {
return $reflection->newInstanceArgs($arguments);
}

if (!$constructor->isPublic()) {
throw new \InvalidArgumentException(sprintf(
'%s::__construct is not accessible',
$className
));
}

$requiredArgsCount = $constructor->getNumberOfRequiredParameters();
$argumentCount = count($arguments);

if ($requiredArgsCount > $argumentCount) {
throw new \InvalidArgumentException(sprintf(
'%s::__construct() requires at least %d arguments; %d given',
$className,
$requiredArgsCount,
$argumentCount
));
}

// Arguments supposed to be ordered
if (isset($arguments[0])) {
return $reflection->newInstanceArgs($arguments);
}

$parameters = array();

foreach($constructor->getParameters() as $parameter) {
$parameterName = $parameter->getName();

if (array_key_exists($parameterName, $arguments)) {
$parameters[$parameter->getPosition()] = $arguments[$parameterName];
continue;
}

if (!$parameter->isOptional()) {
throw new \InvalidArgumentException(sprintf(
'Missing at least one required parameters `%s`',
$parameterName
));
}

$parameters[$parameter->getPosition()] = $parameter->getDefaultValue();
}

return $reflection->newInstanceArgs($parameters);
}
}
21 changes: 21 additions & 0 deletions test/EnliteMonologTest/Service/MonologServiceFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,27 @@ public function testCreateFormatterWithoutArguments()
self::assertInstanceOf('\Monolog\Formatter\LineFormatter', $actual);
}

public function testCreateFormatterWithNamedArguments()
{
$serviceManager = new ServiceManager();
$factory = new MonologServiceFactory();

$dateFormat = 'Y-m-d\TH:i:sZ';
$actual = $factory->createFormatter($serviceManager, array(
'name' => '\Monolog\Formatter\LineFormatter',
'args' => array(
'dateFormat' => $dateFormat,
),
));

self::assertInstanceOf('\Monolog\Formatter\LineFormatter', $actual);
$reflection = new \ReflectionClass($actual);
$property = $reflection->getProperty('dateFormat');
$property->setAccessible(true);
self::assertSame($dateFormat, $property->getValue($actual), 'Unable to set arguments by name');
}


public function testCreateLoggerWithProcessor()
{
$options = new MonologOptions();
Expand Down

0 comments on commit 51822b1

Please sign in to comment.