diff --git a/src/Model/Fields/Field.php b/src/Model/Fields/Field.php index d720c22..b50b5cd 100644 --- a/src/Model/Fields/Field.php +++ b/src/Model/Fields/Field.php @@ -36,12 +36,16 @@ class Field implements FieldTypes /** @var FieldValidator */ protected $FieldValidator; + /** @var array */ + protected $customValidators = []; + /** @var array */ private $errors = []; public function __construct() { $this->FieldValidator = new FieldValidator(); + $this->initValidators(); } public function getValue() @@ -61,6 +65,11 @@ public function getName() return $this->name; } + public function addValidator(callable $callback) + { + $this->customValidators[] = $callback; + } + public function setValue($value) { $this->checkMaximum($value); @@ -70,6 +79,9 @@ public function setValue($value) if (!is_null($value)) { $this->checkValue($value); } + if (!empty($this->customValidators)) { + $this->checkCustomValidators($value); + } if (!empty($this->errors)) { throw new InvalidArgumentException(print_r($this->errors, true)); } @@ -136,4 +148,27 @@ public function checkValue($value) ); } } + + protected function initValidators() {} + + private function checkCustomValidators($value) + { + foreach ($this->customValidators as $validator) { + $result = $validator($value, $this); + + if (!$result instanceof FieldValidationResultInterface) { + throw new InvalidArgumentException( + 'Custom validator must return an instance of FieldValidationResultInterface.' + ); + } + + if (!$result->isValid()) { + $this->errors[] = sprintf( + 'Validation failed for field %s: %s', + $this->name, + (string) $result->getMessage() + ); + } + } + } } diff --git a/src/Model/Fields/FieldValidationResult.php b/src/Model/Fields/FieldValidationResult.php new file mode 100644 index 0000000..acd3906 --- /dev/null +++ b/src/Model/Fields/FieldValidationResult.php @@ -0,0 +1,30 @@ +valid = $valid; + $this->message = $message; + } + + /** @return bool */ + public function isValid() + { + return $this->valid; + } + + /** @return null|string */ + public function getMessage() + { + return $this->message; + } +} diff --git a/src/Model/Fields/FieldValidationResultInterface.php b/src/Model/Fields/FieldValidationResultInterface.php new file mode 100644 index 0000000..ae5321c --- /dev/null +++ b/src/Model/Fields/FieldValidationResultInterface.php @@ -0,0 +1,12 @@ + $maxLength); + return (bool) (strlen((string) $value) > $maxLength); } /** @@ -23,7 +23,7 @@ public function isTooLong($maxLength, $value) */ public function isTooShort($minLength, $value) { - return (bool) (strlen($value) < $minLength); + return (bool) (strlen((string) $value) < $minLength); } /** diff --git a/src/Model/Fields/Payer/IP.php b/src/Model/Fields/Payer/IP.php index 2b5e81a..eab1e0f 100644 --- a/src/Model/Fields/Payer/IP.php +++ b/src/Model/Fields/Payer/IP.php @@ -3,6 +3,7 @@ namespace Tpay\OpenApi\Model\Fields\Payer; use Tpay\OpenApi\Model\Fields\Field; +use Tpay\OpenApi\Model\Fields\FieldValidationResult; /** * @method getValue(): string @@ -13,5 +14,15 @@ class IP extends Field protected $type = self::STRING; protected $minLength = 3; protected $maxLength = 255; - protected $pattern = '^([0-9]{1,3}\.){3}[0-9]{1,3}$'; + + protected function initValidators() + { + $this->addValidator(function ($value) { + if (false === filter_var($value, FILTER_VALIDATE_IP)) { + return new FieldValidationResult(false, 'Invalid IP address.'); + } + + return new FieldValidationResult(true); + }); + } } diff --git a/tests/Model/Fields/Payer/IpTest.php b/tests/Model/Fields/Payer/IpTest.php new file mode 100644 index 0000000..e36f3db --- /dev/null +++ b/tests/Model/Fields/Payer/IpTest.php @@ -0,0 +1,58 @@ +setValue('127.0.0.1'); + + $this->assertEquals('127.0.0.1', $ip->getValue()); + } + + public function testIpv6() + { + $ip = new Ip(); + $ip->setValue('2001:db8::1'); + + $this->assertEquals('2001:db8::1', $ip->getValue()); + } + + public function testInvalidIp() + { + $ip = new Ip(); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Validation failed for field Tpay\OpenApi\Model\Fields\Payer\IP: Invalid IP address.'); + + $ip->setValue('TEST123'); + } + + public function testNull() + { + $ip = new Ip(); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Value of field Tpay\OpenApi\Model\Fields\Payer\IP is too short. Min required 3'); + $this->expectExceptionMessage('Validation failed for field Tpay\OpenApi\Model\Fields\Payer\IP: Invalid IP address.'); + + $ip->setValue(null); + } + + public function testWrongType() + { + $ip = new Ip(); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Value type of field Tpay\OpenApi\Model\Fields\Payer\IP is invalid. Should be string type'); + $this->expectExceptionMessage('Validation failed for field Tpay\OpenApi\Model\Fields\Payer\IP: Invalid IP address.'); + + $ip->setValue(123212312321); + } +} diff --git a/tests/Model/ModelsTest.php b/tests/Model/ModelsTest.php index 9d5274f..193d383 100644 --- a/tests/Model/ModelsTest.php +++ b/tests/Model/ModelsTest.php @@ -6,6 +6,7 @@ use RecursiveDirectoryIterator; use RecursiveIteratorIterator; use SplFileInfo; +use Tpay\OpenApi\Model\Fields\FieldValidationResult; class ModelsTest extends TestCase { @@ -42,6 +43,10 @@ public static function dataModel() ); $className = str_replace('/', '\\', $className); + if (FieldValidationResult::class === $className) { + continue; + } + yield $className => [$className]; } }