Skip to content

Commit 077cb21

Browse files
Added error list and errors, a batch validator
1 parent 9d8bad0 commit 077cb21

28 files changed

+676
-105
lines changed

BatchServiceDefinitionValidator.php

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
namespace Matthias\SymfonyServiceDefinitionValidator;
4+
5+
use Matthias\SymfonyServiceDefinitionValidator\Error\ValidationErrorFactoryInterface;
6+
use Matthias\SymfonyServiceDefinitionValidator\Exception\DefinitionValidationExceptionInterface;
7+
use Symfony\Component\DependencyInjection\ContainerBuilder;
8+
9+
class BatchServiceDefinitionValidator implements BatchServiceDefinitionValidatorInterface
10+
{
11+
private $serviceDefinitionValidator;
12+
private $validationErrorFactory;
13+
14+
public function __construct(
15+
ServiceDefinitionValidatorInterface $serviceDefinitionValidator,
16+
ValidationErrorFactoryInterface $validationErrorFactory
17+
)
18+
{
19+
$this->serviceDefinitionValidator = $serviceDefinitionValidator;
20+
$this->validationErrorFactory = $validationErrorFactory;
21+
}
22+
23+
public function validate(array $serviceDefinitions)
24+
{
25+
$validationErrorList = $this->validationErrorFactory->createValidationErrorList();
26+
27+
foreach ($serviceDefinitions as $serviceId => $definition) {
28+
try {
29+
$this->serviceDefinitionValidator->validate($definition);
30+
} catch (\Exception $exception) {
31+
if ($exception instanceof DefinitionValidationExceptionInterface) {
32+
$error = $this->validationErrorFactory->createValidationError($serviceId, $definition, $exception);
33+
$validationErrorList->add($error);
34+
} else {
35+
throw $exception;
36+
}
37+
}
38+
}
39+
40+
return $validationErrorList;
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace Matthias\SymfonyServiceDefinitionValidator;
4+
5+
use Matthias\SymfonyServiceDefinitionValidator\Error\ValidationErrorListInterface;
6+
7+
interface BatchServiceDefinitionValidatorInterface
8+
{
9+
/**
10+
* Each key should be a service id, each value should be a Definition object
11+
*
12+
* @param array $serviceDefinitions
13+
* @return ValidationErrorListInterface
14+
*/
15+
public function validate(array $serviceDefinitions);
16+
}

Compiler/ValidateServiceDefinitionsPass.php

+24-10
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,37 @@
22

33
namespace Matthias\SymfonyServiceDefinitionValidator\Compiler;
44

5-
use Matthias\SymfonyServiceDefinitionValidator\ServiceDefinitionValidatorInterface;
5+
use Matthias\SymfonyServiceDefinitionValidator\BatchServiceDefinitionValidator;
6+
use Matthias\SymfonyServiceDefinitionValidator\Error\Printer\SimpleErrorListPrinter;
7+
use Matthias\SymfonyServiceDefinitionValidator\Error\ValidationErrorFactory;
8+
use Matthias\SymfonyServiceDefinitionValidator\Exception\InvalidServiceDefinitionsException;
9+
use Matthias\SymfonyServiceDefinitionValidator\ServiceDefinitionValidatorFactory;
610
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
711
use Symfony\Component\DependencyInjection\ContainerBuilder;
812

913
class ValidateServiceDefinitionsPass implements CompilerPassInterface
1014
{
11-
private $validator;
12-
13-
public function __construct(ServiceDefinitionValidatorInterface $validator)
14-
{
15-
$this->validator = $validator;
16-
}
17-
1815
public function process(ContainerBuilder $container)
1916
{
20-
foreach ($container->getDefinitions() as $definition) {
21-
$this->validator->validate($definition);
17+
$serviceDefinitions = $container->getDefinitions();
18+
19+
$validatorFactory = new ServiceDefinitionValidatorFactory();
20+
$validator = $validatorFactory->create($container);
21+
22+
$batchValidator = new BatchServiceDefinitionValidator(
23+
$validator,
24+
new ValidationErrorFactory()
25+
);
26+
27+
$errorList = $batchValidator->validate($serviceDefinitions);
28+
29+
if (count($errorList) === 0) {
30+
return;
2231
}
32+
33+
$errorListPrinter = new SimpleErrorListPrinter();
34+
$message = $errorListPrinter->printErrorList($errorList);
35+
36+
throw new InvalidServiceDefinitionsException($message);
2337
}
2438
}

ConstructorResolver.php

+46-27
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
namespace Matthias\SymfonyServiceDefinitionValidator;
44

5+
use Matthias\SymfonyServiceDefinitionValidator\Exception\ClassNotFoundException;
56
use Matthias\SymfonyServiceDefinitionValidator\Exception\MethodNotFoundException;
67
use Symfony\Component\DependencyInjection\ContainerBuilder;
78
use Symfony\Component\DependencyInjection\Definition;
8-
use Matthias\SymfonyServiceDefinitionValidator\Exception\ClassNotFoundException;
99

1010
class ConstructorResolver implements ConstructorResolverInterface
1111
{
@@ -24,39 +24,58 @@ public function __construct(
2424
public function resolve(Definition $definition)
2525
{
2626
if ($definition->getFactoryClass() && $definition->getFactoryMethod()) {
27-
$factoryClass = $this->resolvePlaceholders($definition->getFactoryClass());
28-
if (!class_exists($factoryClass)) {
29-
throw new ClassNotFoundException($factoryClass);
30-
}
27+
return $this->resolveFactoryClassWithMethod(
28+
$definition->getFactoryClass(),
29+
$definition->getFactoryMethod()
30+
);
31+
} elseif ($definition->getFactoryService() && $definition->getFactoryMethod()) {
32+
return $this->resolveFactoryServiceWithMethod(
33+
$definition->getFactoryService(),
34+
$definition->getFactoryMethod()
35+
);
36+
} elseif ($definition->getClass()) {
37+
return $this->resolveClassWithConstructor($definition->getClass());
38+
}
3139

32-
$factoryMethod = $this->resolvePlaceholders($definition->getFactoryMethod());
33-
if (!method_exists($factoryClass, $factoryMethod)) {
34-
throw new MethodNotFoundException($factoryClass, $factoryMethod);
35-
}
40+
return null;
41+
}
3642

37-
return new \ReflectionMethod($factoryClass, $factoryMethod);
38-
} elseif ($definition->getFactoryService() && $definition->getFactoryMethod()) {
39-
$factoryServiceId = $this->resolvePlaceholders($definition->getFactoryService());
40-
$factoryDefinition = $this->containerBuilder->findDefinition($factoryServiceId);
43+
private function resolveFactoryClassWithMethod($factoryClass, $factoryMethod)
44+
{
45+
if (!class_exists($factoryClass)) {
46+
throw new ClassNotFoundException($factoryClass);
47+
}
4148

42-
$factoryClass = $this->resultingClassResolver->resolve($factoryDefinition);
49+
if (!method_exists($factoryClass, $factoryMethod)) {
50+
throw new MethodNotFoundException($factoryClass, $factoryMethod);
51+
}
4352

44-
$factoryMethod = $this->resolvePlaceholders($definition->getFactoryMethod());
45-
if (!method_exists($factoryClass, $factoryMethod)) {
46-
throw new MethodNotFoundException($factoryClass, $factoryMethod);
47-
}
53+
return new \ReflectionMethod($factoryClass, $factoryMethod);
54+
}
55+
56+
private function resolveFactoryServiceWithMethod($factoryServiceId, $factoryMethod)
57+
{
58+
$factoryDefinition = $this->containerBuilder->findDefinition($factoryServiceId);
59+
60+
$factoryClass = $this->resultingClassResolver->resolve($factoryDefinition);
4861

49-
return new \ReflectionMethod($factoryClass, $factoryMethod);
50-
} else {
51-
$class = $this->resolvePlaceholders($definition->getClass());
62+
if (!method_exists($factoryClass, $factoryMethod)) {
63+
throw new MethodNotFoundException($factoryClass, $factoryMethod);
64+
}
65+
66+
return new \ReflectionMethod($factoryClass, $factoryMethod);
67+
}
68+
69+
private function resolveClassWithConstructor($class)
70+
{
71+
$class = $this->resolvePlaceholders($class);
5272

53-
$reflectionClass = new \ReflectionClass($class);
73+
$reflectionClass = new \ReflectionClass($class);
5474

55-
if ($reflectionClass->hasMethod('__construct')) {
56-
$constructMethod = $reflectionClass->getMethod('__construct');
57-
if ($constructMethod->isPublic()) {
58-
return $constructMethod;
59-
}
75+
if ($reflectionClass->hasMethod('__construct')) {
76+
$constructMethod = $reflectionClass->getMethod('__construct');
77+
if ($constructMethod->isPublic()) {
78+
return $constructMethod;
6079
}
6180
}
6281

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace Matthias\SymfonyServiceDefinitionValidator\Error\Printer;
4+
5+
use Matthias\SymfonyServiceDefinitionValidator\Error\ValidationErrorListInterface;
6+
7+
interface ErrorListPrinterInterface
8+
{
9+
/**
10+
* @param ValidationErrorListInterface $errorList
11+
* @return string
12+
*/
13+
public function printErrorList(ValidationErrorListInterface $errorList);
14+
}
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace Matthias\SymfonyServiceDefinitionValidator\Error\Printer;
4+
5+
use Matthias\SymfonyServiceDefinitionValidator\Error\ValidationErrorInterface;
6+
use Matthias\SymfonyServiceDefinitionValidator\Error\ValidationErrorListInterface;
7+
8+
class SimpleErrorListPrinter implements ErrorListPrinterInterface
9+
{
10+
public function printErrorList(ValidationErrorListInterface $errorList)
11+
{
12+
$result = '';
13+
$result .= $this->printHeader($errorList);
14+
15+
foreach ($errorList as $error) {
16+
$result .= $this->printError($error);
17+
}
18+
19+
return $result;
20+
}
21+
22+
private function printHeader(ValidationErrorListInterface $errorList)
23+
{
24+
return sprintf(
25+
'Service definition validation errors (%d):',
26+
count($errorList)
27+
);
28+
}
29+
30+
private function printError(ValidationErrorInterface $error)
31+
{
32+
return sprintf(
33+
"\n".'- %s: %s',
34+
$error->getServiceId(),
35+
$error->getException()->getMessage()
36+
);
37+
}
38+
}

Error/ValidationError.php

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace Matthias\SymfonyServiceDefinitionValidator\Error;
4+
5+
use Symfony\Component\DependencyInjection\Definition;
6+
7+
class ValidationError implements ValidationErrorInterface
8+
{
9+
private $serviceId;
10+
private $definition;
11+
private $exception;
12+
13+
public function __construct(
14+
$serviceId,
15+
Definition $definition,
16+
\Exception $exception
17+
)
18+
{
19+
$this->serviceId = $serviceId;
20+
$this->definition = $definition;
21+
$this->exception = $exception;
22+
}
23+
24+
public function getDefinition()
25+
{
26+
return $this->definition;
27+
}
28+
29+
public function getException()
30+
{
31+
return $this->exception;
32+
}
33+
34+
public function getServiceId()
35+
{
36+
return $this->serviceId;
37+
}
38+
}

Error/ValidationErrorFactory.php

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Matthias\SymfonyServiceDefinitionValidator\Error;
4+
5+
use Symfony\Component\DependencyInjection\Definition;
6+
7+
class ValidationErrorFactory implements ValidationErrorFactoryInterface
8+
{
9+
public function createValidationErrorList()
10+
{
11+
return new ValidationErrorList();
12+
}
13+
14+
public function createValidationError($serviceId, Definition $definition, \Exception $exception)
15+
{
16+
return new ValidationError($serviceId, $definition, $exception);
17+
}
18+
}
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Matthias\SymfonyServiceDefinitionValidator\Error;
4+
5+
use Symfony\Component\DependencyInjection\Definition;
6+
7+
interface ValidationErrorFactoryInterface
8+
{
9+
/**
10+
* @return ValidationErrorListInterface
11+
*/
12+
public function createValidationErrorList();
13+
14+
/**
15+
* @return ValidationErrorInterface
16+
*/
17+
public function createValidationError($serviceId, Definition $definition, \Exception $exception);
18+
}

Error/ValidationErrorInterface.php

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Matthias\SymfonyServiceDefinitionValidator\Error;
4+
5+
use Symfony\Component\DependencyInjection\Definition;
6+
7+
interface ValidationErrorInterface
8+
{
9+
/**
10+
* @return Definition
11+
*/
12+
public function getDefinition();
13+
14+
/**
15+
* @return \Exception
16+
*/
17+
public function getException();
18+
19+
/**
20+
* @return string
21+
*/
22+
public function getServiceId();
23+
}

Error/ValidationErrorList.php

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Matthias\SymfonyServiceDefinitionValidator\Error;
4+
5+
class ValidationErrorList implements \IteratorAggregate, ValidationErrorListInterface
6+
{
7+
private $errors = array();
8+
9+
public function add(ValidationErrorInterface $error)
10+
{
11+
$this->errors[] = $error;
12+
}
13+
14+
public function count()
15+
{
16+
return count($this->errors);
17+
}
18+
19+
public function getIterator()
20+
{
21+
return new \ArrayIterator($this->errors);
22+
}
23+
}
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
namespace Matthias\SymfonyServiceDefinitionValidator\Error;
4+
5+
interface ValidationErrorListInterface extends \Countable, \Traversable
6+
{
7+
/**
8+
* @param ValidationErrorInterface $error
9+
* @return null
10+
*/
11+
public function add(ValidationErrorInterface $error);
12+
}

0 commit comments

Comments
 (0)