Assessment-Ben/src/DomainObjects/Validation/AbstractValidator.php

181 lines
4.8 KiB
PHP
Raw Normal View History

2025-12-17 21:38:05 +01:00
<?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:
}}}*/