2014-05-07 05:51:54 +02:00
|
|
|
<?php
|
|
|
|
|
2015-06-08 17:09:25 +02:00
|
|
|
/*
|
|
|
|
* This file is part of Respect/Validation.
|
|
|
|
*
|
|
|
|
* (c) Alexandre Gomes Gaigalas <alexandre@gaigalas.net>
|
|
|
|
*
|
|
|
|
* For the full copyright and license information, please view the "LICENSE.md"
|
|
|
|
* file that was distributed with this source code.
|
|
|
|
*/
|
|
|
|
|
2017-11-19 19:01:31 +01:00
|
|
|
declare(strict_types=1);
|
|
|
|
|
2014-05-07 05:51:54 +02:00
|
|
|
namespace Respect\Validation;
|
|
|
|
|
2018-02-04 00:59:18 +01:00
|
|
|
use ReflectionClass;
|
|
|
|
use ReflectionObject;
|
|
|
|
use Respect\Validation\Exceptions\ComponentException;
|
|
|
|
use Respect\Validation\Exceptions\InvalidClassException;
|
|
|
|
use Respect\Validation\Exceptions\ValidationException;
|
2018-01-06 14:49:38 +01:00
|
|
|
use function array_map;
|
|
|
|
use function array_merge;
|
|
|
|
use function array_unique;
|
|
|
|
use function class_exists;
|
|
|
|
use function Respect\Stringifier\stringify;
|
2014-05-07 05:51:54 +02:00
|
|
|
|
2018-01-06 14:49:38 +01:00
|
|
|
/**
|
|
|
|
* Factory of objects.
|
|
|
|
*
|
|
|
|
* @author Henrique Moody <henriquemoody@gmail.com>
|
|
|
|
*/
|
|
|
|
final class Factory
|
2014-05-07 05:51:54 +02:00
|
|
|
{
|
2018-01-06 14:49:38 +01:00
|
|
|
private const DEFAULT_RULES_NAMESPACES = [
|
|
|
|
'Respect\\Validation\\Rules',
|
|
|
|
'Respect\\Validation\\Rules\\Locale',
|
|
|
|
];
|
2014-05-07 05:51:54 +02:00
|
|
|
|
2018-01-06 14:49:38 +01:00
|
|
|
private const DEFAULT_EXCEPTIONS_NAMESPACES = [
|
|
|
|
'Respect\\Validation\\Exceptions',
|
|
|
|
'Respect\\Validation\\Exceptions\\Locale',
|
|
|
|
];
|
2014-05-07 05:51:54 +02:00
|
|
|
|
2018-01-06 14:49:38 +01:00
|
|
|
/**
|
|
|
|
* Default instance of the Factory.
|
|
|
|
*
|
|
|
|
* @var Factory
|
|
|
|
*/
|
|
|
|
private static $defaultInstance;
|
2016-02-16 05:21:17 +01:00
|
|
|
|
2018-01-06 14:49:38 +01:00
|
|
|
/**
|
|
|
|
* @var string[]
|
|
|
|
*/
|
|
|
|
private $rulesNamespaces = [];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var string[]
|
|
|
|
*/
|
|
|
|
private $exceptionsNamespaces = [];
|
2016-02-16 05:21:17 +01:00
|
|
|
|
2018-01-06 14:49:38 +01:00
|
|
|
/**
|
|
|
|
* Initializes the factory with the defined namespaces.
|
|
|
|
*
|
|
|
|
* If the default namespace is not in the array, it will be add to the end
|
|
|
|
* of the array.
|
|
|
|
*
|
|
|
|
* @param string[] $rulesNamespaces
|
|
|
|
* @param string[] $exceptionsNamespaces
|
|
|
|
*/
|
|
|
|
public function __construct(array $rulesNamespaces, array $exceptionsNamespaces)
|
2014-05-07 05:51:54 +02:00
|
|
|
{
|
2018-01-06 14:49:38 +01:00
|
|
|
$this->rulesNamespaces = $this->filterNamespaces($rulesNamespaces, self::DEFAULT_RULES_NAMESPACES);
|
|
|
|
$this->exceptionsNamespaces = $this->filterNamespaces($exceptionsNamespaces, self::DEFAULT_EXCEPTIONS_NAMESPACES);
|
2014-05-07 05:51:54 +02:00
|
|
|
}
|
|
|
|
|
2018-01-06 14:49:38 +01:00
|
|
|
/**
|
|
|
|
* Define the default instance of the Factory.
|
|
|
|
*
|
|
|
|
* @param Factory $defaultInstance
|
|
|
|
*/
|
|
|
|
public static function setDefaultInstance(self $defaultInstance): void
|
2014-05-07 05:51:54 +02:00
|
|
|
{
|
2018-01-06 14:49:38 +01:00
|
|
|
self::$defaultInstance = $defaultInstance;
|
2014-05-07 05:51:54 +02:00
|
|
|
}
|
|
|
|
|
2018-01-06 14:49:38 +01:00
|
|
|
/**
|
|
|
|
* Returns the default instance of the Factory.
|
|
|
|
*
|
|
|
|
* @return Factory
|
|
|
|
*/
|
|
|
|
public static function getDefaultInstance(): self
|
2014-05-07 05:51:54 +02:00
|
|
|
{
|
2018-01-06 14:49:38 +01:00
|
|
|
if (null === self::$defaultInstance) {
|
|
|
|
self::$defaultInstance = new self(self::DEFAULT_RULES_NAMESPACES, self::DEFAULT_EXCEPTIONS_NAMESPACES);
|
2014-05-07 05:51:54 +02:00
|
|
|
}
|
|
|
|
|
2018-01-06 14:49:38 +01:00
|
|
|
return self::$defaultInstance;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a rule.
|
|
|
|
*
|
|
|
|
* @param string $ruleName
|
|
|
|
* @param array $arguments
|
|
|
|
*
|
|
|
|
* @throws ComponentException
|
|
|
|
*
|
|
|
|
* @return Validatable
|
|
|
|
*/
|
|
|
|
public function rule(string $ruleName, array $arguments = []): Validatable
|
|
|
|
{
|
|
|
|
foreach ($this->rulesNamespaces as $namespace) {
|
|
|
|
$className = sprintf('%s\\%s', $namespace, ucfirst($ruleName));
|
2015-06-08 16:47:14 +02:00
|
|
|
if (!class_exists($className)) {
|
2014-05-07 05:51:54 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-01-06 14:49:38 +01:00
|
|
|
return $this->createReflectionClass($className, Validatable::class)->newInstanceArgs($arguments);
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new ComponentException(sprintf('"%s" is not a valid rule name', $ruleName));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates an exception.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* @param Validatable $validatable
|
|
|
|
* @param mixed $input
|
|
|
|
* @param array $extraParams
|
|
|
|
*
|
|
|
|
* @throws ComponentException
|
|
|
|
*
|
|
|
|
* @return ValidationException
|
|
|
|
*/
|
|
|
|
public function exception(Validatable $validatable, $input, array $extraParams = []): ValidationException
|
|
|
|
{
|
|
|
|
$reflection = new ReflectionObject($validatable);
|
|
|
|
$ruleName = $reflection->getShortName();
|
2018-03-22 19:28:07 +01:00
|
|
|
$name = $validatable->getName() ?: stringify($input);
|
|
|
|
$params = ['input' => $input] + $extraParams + $this->extractPropertiesValues($validatable, $reflection);
|
2018-01-06 14:49:38 +01:00
|
|
|
foreach ($this->exceptionsNamespaces as $namespace) {
|
|
|
|
$exceptionName = sprintf('%s\\%sException', $namespace, $ruleName);
|
|
|
|
if (!class_exists($exceptionName)) {
|
|
|
|
continue;
|
2014-05-07 05:51:54 +02:00
|
|
|
}
|
|
|
|
|
2018-01-06 14:49:38 +01:00
|
|
|
return $this->createValidationException($exceptionName, $name, $params);
|
2014-05-07 05:51:54 +02:00
|
|
|
}
|
|
|
|
|
2018-03-22 19:28:07 +01:00
|
|
|
return $this->createValidationException(ValidationException::class, $name, $params);
|
2018-01-06 14:49:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a reflection based on class name.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* @param string $name
|
|
|
|
* @param string $parentName
|
|
|
|
*
|
|
|
|
* @throws InvalidClassException
|
|
|
|
*
|
|
|
|
* @return ReflectionClass
|
|
|
|
*/
|
|
|
|
private function createReflectionClass(string $name, string $parentName): ReflectionClass
|
|
|
|
{
|
|
|
|
$reflection = new ReflectionClass($name);
|
2018-03-22 19:28:07 +01:00
|
|
|
if (!$reflection->isSubclassOf($parentName) && $parentName !== $name) {
|
2018-01-06 14:49:38 +01:00
|
|
|
throw new InvalidClassException(sprintf('"%s" must be an instance of "%s"', $name, $parentName));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$reflection->isInstantiable()) {
|
|
|
|
throw new InvalidClassException(sprintf('"%s" must be instantiable', $name));
|
|
|
|
}
|
|
|
|
|
|
|
|
return $reflection;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Filters namespaces.
|
|
|
|
*
|
|
|
|
* Ensure namespaces are in the right format and contain the default namespaces.
|
|
|
|
*
|
|
|
|
* @param array $namespaces
|
|
|
|
* @param array $defaultNamespaces
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
private function filterNamespaces(array $namespaces, array $defaultNamespaces): array
|
|
|
|
{
|
|
|
|
$filter = function (string $namespace): string {
|
|
|
|
return trim($namespace, '\\');
|
|
|
|
};
|
|
|
|
|
|
|
|
return array_unique(
|
|
|
|
array_merge(
|
|
|
|
array_map($filter, $namespaces),
|
|
|
|
array_map($filter, $defaultNamespaces)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a Validation exception.
|
|
|
|
*
|
|
|
|
* @param string $exceptionName
|
|
|
|
* @param mixed $name
|
|
|
|
* @param array $params
|
|
|
|
*
|
|
|
|
* @return ValidationException
|
|
|
|
*/
|
|
|
|
private function createValidationException(string $exceptionName, $name, array $params): ValidationException
|
|
|
|
{
|
|
|
|
$exception = $this->createReflectionClass($exceptionName, ValidationException::class)->newInstance();
|
|
|
|
$exception->configure($name, $params);
|
|
|
|
if (isset($params['template'])) {
|
|
|
|
$exception->setTemplate($params['template']);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $exception;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param Validatable $validatable
|
2018-01-23 21:59:49 +01:00
|
|
|
* @param ReflectionClass $reflection
|
2018-01-06 14:49:38 +01:00
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
2018-01-23 21:59:49 +01:00
|
|
|
private function extractPropertiesValues(Validatable $validatable, ReflectionClass $reflection): array
|
2018-01-06 14:49:38 +01:00
|
|
|
{
|
|
|
|
$values = [];
|
|
|
|
foreach ($reflection->getProperties() as $property) {
|
|
|
|
$property->setAccessible(true);
|
|
|
|
|
|
|
|
$values[$property->getName()] = $property->getValue($validatable);
|
|
|
|
}
|
|
|
|
|
2018-01-23 21:59:49 +01:00
|
|
|
if (($parentReflection = $reflection->getParentClass())) {
|
|
|
|
return $values + $this->extractPropertiesValues($validatable, $parentReflection);
|
|
|
|
}
|
|
|
|
|
2018-01-06 14:49:38 +01:00
|
|
|
return $values;
|
2014-05-07 05:51:54 +02:00
|
|
|
}
|
|
|
|
}
|