From 653a761a5e8b4c50d3a8d9cb8df7677236b66d34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20K=C4=85kol?= Date: Fri, 31 Oct 2025 12:02:11 +0100 Subject: [PATCH 1/3] Validate ipv6 --- src/Model/Fields/Field.php | 35 ++++++++++++ src/Model/Fields/FieldValidationResult.php | 34 +++++++++++ .../Fields/FieldValidationResultInterface.php | 16 ++++++ src/Model/Fields/FieldValidator.php | 4 +- src/Model/Fields/Payer/IP.php | 12 +++- tests/Model/Fields/Payer/IpTest.php | 57 +++++++++++++++++++ tests/Model/ModelsTest.php | 5 ++ 7 files changed, 160 insertions(+), 3 deletions(-) create mode 100644 src/Model/Fields/FieldValidationResult.php create mode 100644 src/Model/Fields/FieldValidationResultInterface.php create mode 100644 tests/Model/Fields/Payer/IpTest.php diff --git a/src/Model/Fields/Field.php b/src/Model/Fields/Field.php index d720c22..7b540a8 100644 --- a/src/Model/Fields/Field.php +++ b/src/Model/Fields/Field.php @@ -39,9 +39,13 @@ class Field implements FieldTypes /** @var array */ private $errors = []; + /** @var array */ + protected $customValidators = []; + 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..652faef --- /dev/null +++ b/src/Model/Fields/FieldValidationResult.php @@ -0,0 +1,34 @@ +valid = $valid; + $this->message = $message; + } + + /** + * @return bool + */ + public function isValid() + { + return $this->valid; + } + + /** + * @return string|null + */ + public function getMessage() + { + return $this->message; + } +} \ No newline at end of file diff --git a/src/Model/Fields/FieldValidationResultInterface.php b/src/Model/Fields/FieldValidationResultInterface.php new file mode 100644 index 0000000..8cb6938 --- /dev/null +++ b/src/Model/Fields/FieldValidationResultInterface.php @@ -0,0 +1,16 @@ + $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..ac862cb 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,14 @@ 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 (filter_var($value, FILTER_VALIDATE_IP) === false) { + 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..67e4108 --- /dev/null +++ b/tests/Model/Fields/Payer/IpTest.php @@ -0,0 +1,57 @@ +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); + } +} \ No newline at end of file diff --git a/tests/Model/ModelsTest.php b/tests/Model/ModelsTest.php index 9d5274f..ba64692 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 ($className === FieldValidationResult::class) { + continue; + } + yield $className => [$className]; } } From 122b6eb31d8db1cec210dc037679a783756fba6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20K=C4=85kol?= Date: Fri, 31 Oct 2025 12:08:43 +0100 Subject: [PATCH 2/3] fixer --- src/Model/Fields/Field.php | 6 +++--- src/Model/Fields/FieldValidationResult.php | 12 ++++-------- .../Fields/FieldValidationResultInterface.php | 8 ++------ src/Model/Fields/Payer/IP.php | 3 ++- tests/Model/Fields/Payer/IpTest.php | 19 ++++++++++--------- tests/Model/ModelsTest.php | 2 +- 6 files changed, 22 insertions(+), 28 deletions(-) diff --git a/src/Model/Fields/Field.php b/src/Model/Fields/Field.php index 7b540a8..b50b5cd 100644 --- a/src/Model/Fields/Field.php +++ b/src/Model/Fields/Field.php @@ -36,12 +36,12 @@ class Field implements FieldTypes /** @var FieldValidator */ protected $FieldValidator; - /** @var array */ - private $errors = []; - /** @var array */ protected $customValidators = []; + /** @var array */ + private $errors = []; + public function __construct() { $this->FieldValidator = new FieldValidator(); diff --git a/src/Model/Fields/FieldValidationResult.php b/src/Model/Fields/FieldValidationResult.php index 652faef..9afa1a7 100644 --- a/src/Model/Fields/FieldValidationResult.php +++ b/src/Model/Fields/FieldValidationResult.php @@ -7,7 +7,7 @@ class FieldValidationResult implements FieldValidationResultInterface /** @var bool */ private $valid; - /** @var string|null */ + /** @var null|string */ private $message; public function __construct($valid, $message = null) @@ -16,19 +16,15 @@ public function __construct($valid, $message = null) $this->message = $message; } - /** - * @return bool - */ + /** @return bool */ public function isValid() { return $this->valid; } - /** - * @return string|null - */ + /** @return string|null */ public function getMessage() { return $this->message; } -} \ No newline at end of file +} diff --git a/src/Model/Fields/FieldValidationResultInterface.php b/src/Model/Fields/FieldValidationResultInterface.php index 8cb6938..1a8ca18 100644 --- a/src/Model/Fields/FieldValidationResultInterface.php +++ b/src/Model/Fields/FieldValidationResultInterface.php @@ -4,13 +4,9 @@ interface FieldValidationResultInterface { - /** - * @return bool - */ + /**@return bool */ public function isValid(); - /** - * @return string|null - */ + /** @return null|string */ public function getMessage(); } \ No newline at end of file diff --git a/src/Model/Fields/Payer/IP.php b/src/Model/Fields/Payer/IP.php index ac862cb..eab1e0f 100644 --- a/src/Model/Fields/Payer/IP.php +++ b/src/Model/Fields/Payer/IP.php @@ -18,9 +18,10 @@ class IP extends Field protected function initValidators() { $this->addValidator(function ($value) { - if (filter_var($value, FILTER_VALIDATE_IP) === false) { + 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 index 67e4108..e36f3db 100644 --- a/tests/Model/Fields/Payer/IpTest.php +++ b/tests/Model/Fields/Payer/IpTest.php @@ -2,6 +2,7 @@ namespace Model\Fields\Payer; +use InvalidArgumentException; use PHPUnit\Framework\TestCase; use Tpay\OpenApi\Model\Fields\Payer\IP; @@ -10,34 +11,34 @@ class IpTest extends TestCase public function testIpv4() { $ip = new Ip(); - $ip->setValue("127.0.0.1"); + $ip->setValue('127.0.0.1'); - $this->assertEquals("127.0.0.1", $ip->getValue()); + $this->assertEquals('127.0.0.1', $ip->getValue()); } public function testIpv6() { $ip = new Ip(); - $ip->setValue("2001:db8::1"); + $ip->setValue('2001:db8::1'); - $this->assertEquals("2001:db8::1", $ip->getValue()); + $this->assertEquals('2001:db8::1', $ip->getValue()); } public function testInvalidIp() { $ip = new Ip(); - $this->expectException(\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Validation failed for field Tpay\OpenApi\Model\Fields\Payer\IP: Invalid IP address.'); - $ip->setValue("TEST123"); + $ip->setValue('TEST123'); } public function testNull() { $ip = new Ip(); - $this->expectException(\InvalidArgumentException::class); + $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.'); @@ -48,10 +49,10 @@ public function testWrongType() { $ip = new Ip(); - $this->expectException(\InvalidArgumentException::class); + $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); } -} \ No newline at end of file +} diff --git a/tests/Model/ModelsTest.php b/tests/Model/ModelsTest.php index ba64692..193d383 100644 --- a/tests/Model/ModelsTest.php +++ b/tests/Model/ModelsTest.php @@ -43,7 +43,7 @@ public static function dataModel() ); $className = str_replace('/', '\\', $className); - if ($className === FieldValidationResult::class) { + if (FieldValidationResult::class === $className) { continue; } From 0dc698755ae7eac9d029671a94f43ce79dedecc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksander=20K=C4=85kol?= Date: Fri, 31 Oct 2025 12:10:03 +0100 Subject: [PATCH 3/3] fixer --- src/Model/Fields/FieldValidationResult.php | 4 ++-- src/Model/Fields/FieldValidationResultInterface.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Model/Fields/FieldValidationResult.php b/src/Model/Fields/FieldValidationResult.php index 9afa1a7..acd3906 100644 --- a/src/Model/Fields/FieldValidationResult.php +++ b/src/Model/Fields/FieldValidationResult.php @@ -7,7 +7,7 @@ class FieldValidationResult implements FieldValidationResultInterface /** @var bool */ private $valid; - /** @var null|string */ + /** @var null|string */ private $message; public function __construct($valid, $message = null) @@ -22,7 +22,7 @@ public function isValid() return $this->valid; } - /** @return string|null */ + /** @return null|string */ public function getMessage() { return $this->message; diff --git a/src/Model/Fields/FieldValidationResultInterface.php b/src/Model/Fields/FieldValidationResultInterface.php index 1a8ca18..ae5321c 100644 --- a/src/Model/Fields/FieldValidationResultInterface.php +++ b/src/Model/Fields/FieldValidationResultInterface.php @@ -4,9 +4,9 @@ interface FieldValidationResultInterface { - /**@return bool */ + /** @return bool */ public function isValid(); /** @return null|string */ public function getMessage(); -} \ No newline at end of file +}