181 lines
4.8 KiB
PHP
181 lines
4.8 KiB
PHP
|
|
<?php declare(strict_types=1);
|
||
|
|
|
||
|
|
namespace VeruA\DomainObjects\Validation;
|
||
|
|
|
||
|
|
use VeruA\Log;
|
||
|
|
use VeruA\DomainObjects\DomainObject;
|
||
|
|
use VeruA\DomainObjects\ValueObjects\Value;
|
||
|
|
use VeruA\DomainObjects\Validation\ResultCollection;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Validator is a singleton base class for classes validating DomainObjects.
|
||
|
|
*/
|
||
|
|
abstract class AbstractValidator implements Validator
|
||
|
|
{
|
||
|
|
use Log;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Needs to be declared in every childclass
|
||
|
|
* @var callable[] A list of tests used to validate domainObjects dataTypes, indexed by fieldname
|
||
|
|
*/
|
||
|
|
protected $validators = [];
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Stores the single instance of Validator.
|
||
|
|
* @var Validator
|
||
|
|
* @abstract Needs to be redeclared in every child class
|
||
|
|
*/
|
||
|
|
protected static AbstractValidator $instance;
|
||
|
|
|
||
|
|
private bool $validatorsLoaded = false;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Implement constructor in childclasses and assign the validator functions to using setValidators()
|
||
|
|
* All sub-classes should have protected constructors.
|
||
|
|
*/
|
||
|
|
protected abstract function __construct();
|
||
|
|
|
||
|
|
// getValidators() {{{
|
||
|
|
/**
|
||
|
|
* Returns the validator functions.
|
||
|
|
*/
|
||
|
|
public function getValidators()
|
||
|
|
{
|
||
|
|
return $this->validators;
|
||
|
|
} // }}}
|
||
|
|
|
||
|
|
// setValidators() {{{
|
||
|
|
/**
|
||
|
|
* Sets the validator functions.
|
||
|
|
*/
|
||
|
|
protected function setValidators($tests)
|
||
|
|
{
|
||
|
|
$parentClass = get_parent_class($this);
|
||
|
|
$parentTests = [];
|
||
|
|
|
||
|
|
if ($parentClass !== false && $parentClass !== AbstractValidator::class)
|
||
|
|
{
|
||
|
|
$parentTests = $parentClass::getInstance()->getValidators();
|
||
|
|
}
|
||
|
|
$this->validators = array_merge($parentTests, $tests);
|
||
|
|
} // }}}
|
||
|
|
|
||
|
|
// addValidator() {{{
|
||
|
|
/**
|
||
|
|
* Dynamically add a validator function
|
||
|
|
* @param string $field The DomainObject Field to validate
|
||
|
|
* @param callable $validator The Validator function to use
|
||
|
|
* @param string $validatorIndex The internal index used to store the validator function. Use this to
|
||
|
|
* remove or overwrite the validator
|
||
|
|
*/
|
||
|
|
public function addValidator(string $field, callable $validator, ?string $validatorIndex=null)
|
||
|
|
{
|
||
|
|
if (!isset($this->validators[$field])) $this->validators[$field] = [];
|
||
|
|
$this->validators[$field][$validatorIndex] = $validator;
|
||
|
|
} // }}}
|
||
|
|
|
||
|
|
// removeValidator() {{{
|
||
|
|
/**
|
||
|
|
* Dynamiocally remove a validator function
|
||
|
|
*/
|
||
|
|
public function removeValidator(string $field, $validatorIndex=null)
|
||
|
|
{
|
||
|
|
if (isset($validatorIndex))
|
||
|
|
{
|
||
|
|
unset($this->validators[$field][$validatorIndex]);
|
||
|
|
} else
|
||
|
|
{
|
||
|
|
unset($this->validators[$field]);
|
||
|
|
}
|
||
|
|
} // }}}
|
||
|
|
|
||
|
|
// getInstance() {{{
|
||
|
|
/**
|
||
|
|
* Returnst the instances of the validator, instantiates a new one if there isn't one already.
|
||
|
|
* @return Validator
|
||
|
|
*/
|
||
|
|
public static function getInstance(): Validator
|
||
|
|
{
|
||
|
|
if (!isset(static::$instance)) {
|
||
|
|
static::$instance = new static();
|
||
|
|
}
|
||
|
|
|
||
|
|
assert(
|
||
|
|
array_column(
|
||
|
|
(new \ReflectionClass(static::$instance))
|
||
|
|
->getProperties(\ReflectionProperty::IS_STATIC | \ReflectionProperty::IS_PROTECTED),
|
||
|
|
'class', 'name'
|
||
|
|
)['instance'] === get_class(static::$instance),
|
||
|
|
new \Exception('$instance has to be redeclared in '.get_class(static::$instance))
|
||
|
|
);
|
||
|
|
|
||
|
|
return static::$instance;
|
||
|
|
} // }}}
|
||
|
|
|
||
|
|
// validate() {{{
|
||
|
|
/**
|
||
|
|
* Validates a DomainObject.
|
||
|
|
* @param DomainObject $domainObject
|
||
|
|
* @return ResultCollection|bool returns true if validations passed a ResultCollection otherwise
|
||
|
|
*/
|
||
|
|
public function validate(Validatable $domainObject)
|
||
|
|
{
|
||
|
|
$this->loadDefaultValidators($domainObject);
|
||
|
|
|
||
|
|
$rc = new ResultCollection((new \ReflectionClass($domainObject))->getShortName());
|
||
|
|
$passed = true;
|
||
|
|
|
||
|
|
// Validate properties
|
||
|
|
foreach ($this->validators as $col => $validators)
|
||
|
|
{
|
||
|
|
if (!isset($domainObject->$col)) {
|
||
|
|
$this->log()->notice('DomainObject '.get_class($domainObject)." has no field '$col'");
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
try {
|
||
|
|
$valueObject = $domainObject->getValueObjectOf($col);
|
||
|
|
foreach ($validators as $test)
|
||
|
|
{
|
||
|
|
if (true !== ($result = $test($valueObject))) {
|
||
|
|
$rc->$col = $result;
|
||
|
|
$passed = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch (\OutOfRangeException $e) {
|
||
|
|
self::log()->notice('Field not set', ['name' => $col]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return $passed ?: $rc;
|
||
|
|
} // }}}
|
||
|
|
|
||
|
|
// loadDefaultValidators() {{{
|
||
|
|
/**
|
||
|
|
* load vaildators defined in ValueObjects.
|
||
|
|
* Defined as Default and as such meaning always used. An example is the EAN13 ValueObject
|
||
|
|
* which always validates checksum and length
|
||
|
|
*/
|
||
|
|
private function loadDefaultValidators(DomainObject $domainObject)
|
||
|
|
{
|
||
|
|
if (! $this->validatorsLoaded)
|
||
|
|
{
|
||
|
|
foreach ($domainObject->dataTypes() as $col => $dt)
|
||
|
|
{
|
||
|
|
if (is_subclass_of($dt, Value::class) && !empty($validators = ($dt)::defaultValidators()))
|
||
|
|
{
|
||
|
|
if (!isset($this->validators[$col])) $this->validators[$col] = [];
|
||
|
|
|
||
|
|
$this->validators[$col] = array_merge($this->validators[$col], $validators);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
self::log()->debug('Registered Validators', $this->validators);
|
||
|
|
|
||
|
|
$this->validatorsLoaded = true;
|
||
|
|
}
|
||
|
|
} // }}}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* jEdit buffer local properties {{{
|
||
|
|
* :folding=explicit:collapseFolds=1:
|
||
|
|
}}}*/
|