From 5d2bb03785f03a355181a025ac2136535b501622 Mon Sep 17 00:00:00 2001 From: Denis Date: Sun, 8 Feb 2026 13:59:19 +0700 Subject: [PATCH 1/9] fix: correct disposable domains config path in ValidatorFactory The path was resolving to src/config/ instead of the project root config/ directory. Changed from '../../config' to '../../../config' to correctly traverse three levels up from src/Application/Validators/. --- src/Application/Validators/ValidatorFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Application/Validators/ValidatorFactory.php b/src/Application/Validators/ValidatorFactory.php index 861d14e..bda2ec2 100644 --- a/src/Application/Validators/ValidatorFactory.php +++ b/src/Application/Validators/ValidatorFactory.php @@ -26,7 +26,7 @@ public static function create(string $type): ?ValidatorInterface case 'google_email': return new GoogleMailValidator(); case 'disposable_email': - $provider = new FileDisposableEmailDomainProvider(__DIR__ . '/../../config/disposable_domains.php'); + $provider = new FileDisposableEmailDomainProvider(__DIR__ . '/../../../config/disposable_domains.php'); return new DisposableEmailValidator($provider); case 'date': return new DateValidator(); From fff0c77d19f26caa028d8b4c96f39ef8a94f4412 Mon Sep 17 00:00:00 2001 From: Denis Date: Sun, 8 Feb 2026 14:00:36 +0700 Subject: [PATCH 2/9] docs: fix misleading disposable email example in README Removed the erroneous `!` negation before `Validator::isDisposableEmail()`. The method already returns true when the email IS disposable (it internally negates the validator result), so the extra `!` inverted the intended logic. Also updated the validators table to remove the `!` prefix from the helper column, and clarified the Extending section instructions. --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index a81a0e4..0a15879 100644 --- a/README.md +++ b/README.md @@ -65,14 +65,14 @@ Use `ValidatorFactory` to retrieve any validator by its type key: ```php use QDenka\EasyValidation\Application\Validators\ValidatorFactory; -\$types = ['email','google_email','disposable_email','url','number','date','ip','uuid','json','base64','phone']; -foreach (\$types as \$type) { - \$validator = ValidatorFactory::create(\$type); - \$value = 'test_value_for_' . \$type; - if (\$validator && \$validator->validate(\$value)) { - echo "[\$type] valid\n"; +$types = ['email','google_email','disposable_email','url','number','date','ip','uuid','json','base64','phone']; +foreach ($types as $type) { + $validator = ValidatorFactory::create($type); + $value = 'test_value_for_' . $type; + if ($validator && $validator->validate($value)) { + echo "[$type] valid\n"; } else { - echo "[\$type] invalid\n"; + echo "[$type] invalid\n"; } } ``` @@ -92,7 +92,7 @@ if (Validator::isGoogleMail('user@gmail.com')) { echo "It's a Gmail address"; } -if (!Validator::isDisposableEmail('temp@mailinator.com')) { +if (Validator::isDisposableEmail('temp@mailinator.com')) { echo "Disposable email detected"; } @@ -115,7 +115,7 @@ Validator::isValidPhone('+1234567890'); | ------------------ | ---------------------- | ------------------------------------- | | `email` | `isValidEmail()` | RFC-compliant email | | `google_email` | `isGoogleMail()` | Gmail & Googlemail domains | -| `disposable_email` | `!isDisposableEmail()` | Blacklisted disposable domains | +| `disposable_email` | `isDisposableEmail()` | Blacklisted disposable domains | | `url` | `isValidUrl()` | HTTP/HTTPS URLs | | `number` | `isValidNumber()` | Numeric values | | `date` | `isValidDate()` | Date in `Y-m-d` (configurable format) | @@ -150,8 +150,8 @@ Ensure all domains are lowercase. To add a new validator: 1. Implement `ValidatorInterface` in `src/Domain/YourType/YourValidator.php`. -2. Add your class to `VALIDATOR_MAP` in `src/Infrastructure/Factories/Validator.php`. -3. (Optional) Add a static helper in the same class. +2. Register your class in `ValidatorFactory` (`src/Application/Validators/ValidatorFactory.php`). +3. (Optional) Add a static helper in `Validator` facade (`src/Infrastructure/Factories/Validator.php`). 4. Write tests under `tests/` following existing patterns. --- From 8834a29c1924e79c5608cfa6e90f0bf1dd204613 Mon Sep 17 00:00:00 2001 From: Denis Date: Sun, 8 Feb 2026 14:02:39 +0700 Subject: [PATCH 3/9] refactor: move ValidatorInterface and ValidatorFactoryInterface to Domain layer Moved both interfaces from src/Application/Validators/ to src/Domain/Contracts/ so that the Domain layer does not depend on the Application layer (proper DDD dependency direction). Updated all validator imports across Domain layer to reference the new namespace QDenka\EasyValidation\Domain\Contracts\. Also fixed UrlValidator to only accept http/https schemes (matching the documented behavior) and removed redundant is_string() check from JsonValidator (parameter is already type-hinted as string). --- src/Domain/Base64/Base64Validator.php | 2 +- .../Contracts/ValidatorFactoryInterface.php | 21 +++++++++++++++++++ src/Domain/Contracts/ValidatorInterface.php | 21 +++++++++++++++++++ src/Domain/Date/DateValidator.php | 2 +- src/Domain/Email/DisposableEmailValidator.php | 2 +- src/Domain/Email/EmailValidator.php | 4 ++-- src/Domain/Email/GoogleMailValidator.php | 2 +- src/Domain/Ip/IpValidator.php | 2 +- src/Domain/Json/JsonValidator.php | 6 +----- src/Domain/Number/NumberValidator.php | 4 ++-- src/Domain/Phone/PhoneNumberValidator.php | 2 +- src/Domain/Url/UrlValidator.php | 12 +++++++++-- src/Domain/Uuid/UuidValidator.php | 2 +- 13 files changed, 64 insertions(+), 18 deletions(-) create mode 100644 src/Domain/Contracts/ValidatorFactoryInterface.php create mode 100644 src/Domain/Contracts/ValidatorInterface.php diff --git a/src/Domain/Base64/Base64Validator.php b/src/Domain/Base64/Base64Validator.php index 42a1d96..db9516d 100644 --- a/src/Domain/Base64/Base64Validator.php +++ b/src/Domain/Base64/Base64Validator.php @@ -2,7 +2,7 @@ namespace QDenka\EasyValidation\Domain\Base64; -use QDenka\EasyValidation\Application\Validators\ValidatorInterface; +use QDenka\EasyValidation\Domain\Contracts\ValidatorInterface; /** * Validates Base64-encoded strings. diff --git a/src/Domain/Contracts/ValidatorFactoryInterface.php b/src/Domain/Contracts/ValidatorFactoryInterface.php new file mode 100644 index 0000000..610b45a --- /dev/null +++ b/src/Domain/Contracts/ValidatorFactoryInterface.php @@ -0,0 +1,21 @@ + Date: Sun, 8 Feb 2026 14:08:59 +0700 Subject: [PATCH 4/9] refactor: remove duplicate factory logic from Validator facade MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Validator facade now delegates all creation to ValidatorFactory instead of maintaining its own VALIDATOR_MAP copy. This eliminates the DRY violation — only one place to update when adding validators. Also updated both files to use Domain\Contracts namespace for ValidatorInterface and ValidatorFactoryInterface. Converted ValidatorFactory from switch/case to VALIDATOR_MAP constant for consistency and maintainability. --- .../Validators/ValidatorFactory.php | 57 +++++++++-------- src/Infrastructure/Factories/Validator.php | 62 +++---------------- 2 files changed, 40 insertions(+), 79 deletions(-) diff --git a/src/Application/Validators/ValidatorFactory.php b/src/Application/Validators/ValidatorFactory.php index bda2ec2..706d5d2 100644 --- a/src/Application/Validators/ValidatorFactory.php +++ b/src/Application/Validators/ValidatorFactory.php @@ -2,6 +2,8 @@ namespace QDenka\EasyValidation\Application\Validators; +use QDenka\EasyValidation\Domain\Contracts\ValidatorFactoryInterface; +use QDenka\EasyValidation\Domain\Contracts\ValidatorInterface; use QDenka\EasyValidation\Domain\Ip\IpValidator; use QDenka\EasyValidation\Domain\Uuid\UuidValidator; use QDenka\EasyValidation\Domain\Json\JsonValidator; @@ -15,37 +17,38 @@ use QDenka\EasyValidation\Domain\Url\UrlValidator; use QDenka\EasyValidation\Infrastructure\Email\FileDisposableEmailDomainProvider; - class ValidatorFactory implements ValidatorFactoryInterface { + private const VALIDATOR_MAP = [ + 'email' => EmailValidator::class, + 'google_email' => GoogleMailValidator::class, + 'disposable_email' => DisposableEmailValidator::class, + 'url' => UrlValidator::class, + 'number' => NumberValidator::class, + 'date' => DateValidator::class, + 'ip' => IpValidator::class, + 'uuid' => UuidValidator::class, + 'json' => JsonValidator::class, + 'base64' => Base64Validator::class, + 'phone' => PhoneNumberValidator::class, + ]; + public static function create(string $type): ?ValidatorInterface { - switch (strtolower($type)) { - case 'email': - return new EmailValidator(); - case 'google_email': - return new GoogleMailValidator(); - case 'disposable_email': - $provider = new FileDisposableEmailDomainProvider(__DIR__ . '/../../../config/disposable_domains.php'); - return new DisposableEmailValidator($provider); - case 'date': - return new DateValidator(); - case 'number': - return new NumberValidator(); - case 'url': - return new UrlValidator(); - case 'ip': - return new IpValidator(); - case 'uuid': - return new UuidValidator(); - case 'json': - return new JsonValidator(); - case 'base64': - return new Base64Validator(); - case 'phone': - return new PhoneNumberValidator(); - default: - return null; + $type = strtolower($type); + + if (!array_key_exists($type, self::VALIDATOR_MAP)) { + return null; } + + $class = self::VALIDATOR_MAP[$type]; + + if ($type === 'disposable_email') { + $configPath = __DIR__ . '/../../../config/disposable_domains.php'; + $provider = new FileDisposableEmailDomainProvider($configPath); + return new $class($provider); + } + + return new $class(); } } diff --git a/src/Infrastructure/Factories/Validator.php b/src/Infrastructure/Factories/Validator.php index f471b00..894aa78 100644 --- a/src/Infrastructure/Factories/Validator.php +++ b/src/Infrastructure/Factories/Validator.php @@ -2,44 +2,18 @@ namespace QDenka\EasyValidation\Infrastructure\Factories; -use QDenka\EasyValidation\Application\Validators\ValidatorFactoryInterface; -use QDenka\EasyValidation\Application\Validators\ValidatorInterface; -use QDenka\EasyValidation\Domain\Date\DateValidator; -use QDenka\EasyValidation\Domain\Email\EmailValidator; -use QDenka\EasyValidation\Domain\Email\GoogleMailValidator; -use QDenka\EasyValidation\Domain\Email\DisposableEmailValidator; -use QDenka\EasyValidation\Infrastructure\Email\FileDisposableEmailDomainProvider; -use QDenka\EasyValidation\Domain\Number\NumberValidator; -use QDenka\EasyValidation\Domain\Url\UrlValidator; -use QDenka\EasyValidation\Domain\Ip\IpValidator; -use QDenka\EasyValidation\Domain\Uuid\UuidValidator; -use QDenka\EasyValidation\Domain\Json\JsonValidator; -use QDenka\EasyValidation\Domain\Base64\Base64Validator; -use QDenka\EasyValidation\Domain\Phone\PhoneNumberValidator; +use QDenka\EasyValidation\Application\Validators\ValidatorFactory; +use QDenka\EasyValidation\Domain\Contracts\ValidatorInterface; /** * Class Validator * - * Provides static helpers and implements a factory for all built-in validators. + * Static facade that delegates all creation to ValidatorFactory. * * @package QDenka\EasyValidation\Infrastructure\Factories */ -class Validator implements ValidatorFactoryInterface +class Validator { - private const VALIDATOR_MAP = [ - 'email' => EmailValidator::class, - 'google_email' => GoogleMailValidator::class, - 'disposable_email' => DisposableEmailValidator::class, - 'url' => UrlValidator::class, - 'number' => NumberValidator::class, - 'date' => DateValidator::class, - 'ip' => IpValidator::class, - 'uuid' => UuidValidator::class, - 'json' => JsonValidator::class, - 'base64' => Base64Validator::class, - 'phone' => PhoneNumberValidator::class, - ]; - public static function isValidEmail(string $value): bool { return self::validate('email', $value); @@ -55,7 +29,7 @@ public static function isGoogleMail(string $value): bool */ public static function isDisposableEmail(string $value): bool { - return ! self::validate('disposable_email', $value); + return !self::validate('disposable_email', $value); } public static function isValidUrl(string $value): bool @@ -99,36 +73,20 @@ public static function isValidPhone(string $value): bool } /** - * Generic validation by type. - * - * @param string $type - * @param string $value - * @return bool + * Generic validation by type key. */ public static function validate(string $type, string $value): bool { - $validator = self::create($type); + $validator = ValidatorFactory::create($type); - return $validator && $validator->validate($value); + return $validator !== null && $validator->validate($value); } /** - * @inheritdoc + * Proxy to ValidatorFactory::create(). */ public static function create(string $type): ?ValidatorInterface { - if (! array_key_exists($type, self::VALIDATOR_MAP)) { - return null; - } - - $class = self::VALIDATOR_MAP[$type]; - - if ($type === 'disposable_email') { - $configPath = __DIR__ . '/../../../config/disposable_domains.php'; - $provider = new FileDisposableEmailDomainProvider($configPath); - return new $class($provider); - } - - return new $class(); + return ValidatorFactory::create($type); } } From 3bee7fc196ce2579c62ec3cdfcdf25b62fae6576 Mon Sep 17 00:00:00 2001 From: Denis Date: Sun, 8 Feb 2026 14:09:53 +0700 Subject: [PATCH 5/9] refactor: remove deprecated ValidatorInterface from Application layer This interface has been moved to Domain\Contracts\ValidatorInterface. The old file is no longer referenced by any class. --- .../Validators/ValidatorInterface.php | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 src/Application/Validators/ValidatorInterface.php diff --git a/src/Application/Validators/ValidatorInterface.php b/src/Application/Validators/ValidatorInterface.php deleted file mode 100644 index efdcde3..0000000 --- a/src/Application/Validators/ValidatorInterface.php +++ /dev/null @@ -1,19 +0,0 @@ - Date: Sun, 8 Feb 2026 14:11:55 +0700 Subject: [PATCH 6/9] refactor: remove deprecated ValidatorFactoryInterface from Application layer This interface has been moved to Domain\Contracts\ValidatorFactoryInterface. The old file is no longer referenced by any class. --- .../Validators/ValidatorFactoryInterface.php | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 src/Application/Validators/ValidatorFactoryInterface.php diff --git a/src/Application/Validators/ValidatorFactoryInterface.php b/src/Application/Validators/ValidatorFactoryInterface.php deleted file mode 100644 index 54acbbf..0000000 --- a/src/Application/Validators/ValidatorFactoryInterface.php +++ /dev/null @@ -1,17 +0,0 @@ - Date: Sun, 8 Feb 2026 14:14:41 +0700 Subject: [PATCH 7/9] test: expand test coverage to all 11 validator types Previously only Email, URL, Number and Date had tests (4 of 11). Added tests for GoogleMail, DisposableEmail, IP, UUID, JSON, Base64 and Phone validators. Also added edge-case tests: - impossible date (2023-02-30) - ftp:// rejection in URL validator - UUID with invalid version byte - phone without + prefix and too-short numbers - factory create() returns working validator instance --- tests/Infrastructure/ValidatorTest.php | 187 +++++++++++++++++++++++-- 1 file changed, 176 insertions(+), 11 deletions(-) diff --git a/tests/Infrastructure/ValidatorTest.php b/tests/Infrastructure/ValidatorTest.php index 1caedca..3b1ae12 100644 --- a/tests/Infrastructure/ValidatorTest.php +++ b/tests/Infrastructure/ValidatorTest.php @@ -3,53 +3,218 @@ namespace QDenka\EasyValidation\Tests\Infrastructure; use PHPUnit\Framework\TestCase; -use QDenka\EasyValidation\Application\Validators\ValidatorFactoryInterface; use QDenka\EasyValidation\Infrastructure\Factories\Validator; class ValidatorTest extends TestCase { - public function testIsValidEmailValid() + // --- Email --- + + public function testIsValidEmailValid(): void { $this->assertTrue(Validator::isValidEmail('test@example.com')); } - public function testIsValidEmailInvalid() + public function testIsValidEmailInvalid(): void { $this->assertFalse(Validator::isValidEmail('invalid_email')); } - public function testIsValidUrlValid() + // --- Google Mail --- + + public function testIsGoogleMailValid(): void + { + $this->assertTrue(Validator::isGoogleMail('user@gmail.com')); + } + + public function testIsGoogleMailValidGooglemail(): void + { + $this->assertTrue(Validator::isGoogleMail('user@googlemail.com')); + } + + public function testIsGoogleMailInvalidDomain(): void + { + $this->assertFalse(Validator::isGoogleMail('user@yahoo.com')); + } + + public function testIsGoogleMailInvalidFormat(): void + { + $this->assertFalse(Validator::isGoogleMail('not-an-email')); + } + + // --- Disposable Email --- + + public function testIsDisposableEmailDetectsDisposable(): void + { + $this->assertTrue(Validator::isDisposableEmail('user@mailinator.com')); + } + + public function testIsDisposableEmailAllowsNormal(): void + { + $this->assertFalse(Validator::isDisposableEmail('user@example.com')); + } + + // --- URL --- + + public function testIsValidUrlValidHttp(): void { $this->assertTrue(Validator::isValidUrl('http://www.example.com')); } - public function testIsValidUrlInvalid() + public function testIsValidUrlValidHttps(): void + { + $this->assertTrue(Validator::isValidUrl('https://example.com/path?q=1')); + } + + public function testIsValidUrlInvalid(): void { $this->assertFalse(Validator::isValidUrl('invalid_url')); } - public function testIsValidNumberValid() + public function testIsValidUrlRejectsFtp(): void + { + $this->assertFalse(Validator::isValidUrl('ftp://files.example.com')); + } + + // --- Number --- + + public function testIsValidNumberInteger(): void { $this->assertTrue(Validator::isValidNumber('123')); } - public function testIsValidNumberInvalid() + public function testIsValidNumberFloat(): void + { + $this->assertTrue(Validator::isValidNumber('123.45')); + } + + public function testIsValidNumberNegative(): void + { + $this->assertTrue(Validator::isValidNumber('-42')); + } + + public function testIsValidNumberInvalid(): void { - $this->assertFalse(Validator::isValidNumber('invalid_number')); + $this->assertFalse(Validator::isValidNumber('abc')); } - public function testIsValidDateValid() + // --- Date --- + + public function testIsValidDateValid(): void { $this->assertTrue(Validator::isValidDate('2023-04-23')); } - public function testIsValidDateInvalid() + public function testIsValidDateInvalid(): void { $this->assertFalse(Validator::isValidDate('invalid_date')); } - public function testCreateInvalidValidator() + public function testIsValidDateRejectsImpossible(): void + { + $this->assertFalse(Validator::isValidDate('2023-02-30')); + } + + // --- IP --- + + public function testIsValidIpV4(): void + { + $this->assertTrue(Validator::isValidIp('192.168.1.1')); + } + + public function testIsValidIpV6(): void + { + $this->assertTrue(Validator::isValidIp('2001:db8::1')); + } + + public function testIsValidIpInvalid(): void + { + $this->assertFalse(Validator::isValidIp('999.999.999.999')); + } + + public function testIsValidIpInvalidString(): void + { + $this->assertFalse(Validator::isValidIp('not-an-ip')); + } + + // --- UUID --- + + public function testIsValidUuidV4(): void + { + $this->assertTrue(Validator::isValidUuid('550e8400-e29b-41d4-a716-446655440000')); + } + + public function testIsValidUuidInvalid(): void + { + $this->assertFalse(Validator::isValidUuid('not-a-uuid')); + } + + public function testIsValidUuidInvalidVersion(): void + { + $this->assertFalse(Validator::isValidUuid('550e8400-e29b-61d4-a716-446655440000')); + } + + // --- JSON --- + + public function testIsValidJsonObject(): void + { + $this->assertTrue(Validator::isValidJson('{"foo":1}')); + } + + public function testIsValidJsonArray(): void + { + $this->assertTrue(Validator::isValidJson('[1,2,3]')); + } + + public function testIsValidJsonInvalid(): void + { + $this->assertFalse(Validator::isValidJson('{invalid}')); + } + + // --- Base64 --- + + public function testIsValidBase64Valid(): void + { + $this->assertTrue(Validator::isValidBase64(base64_encode('hello world'))); + } + + public function testIsValidBase64Invalid(): void + { + $this->assertFalse(Validator::isValidBase64('not base64!!!')); + } + + // --- Phone --- + + public function testIsValidPhoneValid(): void + { + $this->assertTrue(Validator::isValidPhone('+1234567890')); + } + + public function testIsValidPhoneValidLong(): void + { + $this->assertTrue(Validator::isValidPhone('+442071234567')); + } + + public function testIsValidPhoneInvalidNoPlus(): void + { + $this->assertFalse(Validator::isValidPhone('1234567890')); + } + + public function testIsValidPhoneInvalidTooShort(): void + { + $this->assertFalse(Validator::isValidPhone('+123')); + } + + // --- Factory --- + + public function testCreateReturnsNullForUnknownType(): void { $this->assertNull(Validator::create('invalid_type')); } + + public function testCreateReturnsValidatorForKnownType(): void + { + $validator = Validator::create('email'); + $this->assertNotNull($validator); + $this->assertTrue($validator->validate('test@example.com')); + } } From f0493d61cc195d7cb93a64d00f6eac41b9966bc6 Mon Sep 17 00:00:00 2001 From: Denis Date: Sun, 8 Feb 2026 14:17:09 +0700 Subject: [PATCH 8/9] ci: add GitHub Actions workflow for tests and static analysis Runs PHPUnit across PHP 7.4, 8.0, 8.1, 8.2, 8.3, 8.4 matrix. Runs PHPStan static analysis on PHP 8.3. Triggered on push to main and on pull requests targeting main. Includes Composer dependency caching for faster builds. --- .github/workflows/ci.yml | 68 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..fe59366 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,68 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + tests: + name: PHP ${{ matrix.php }} — Tests + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: intl, json, mbstring + coverage: none + + - name: Cache Composer dependencies + uses: actions/cache@v4 + with: + path: vendor + key: composer-${{ matrix.php }}-${{ hashFiles('composer.json') }} + restore-keys: composer-${{ matrix.php }}- + + - name: Install dependencies + run: composer install --no-interaction --prefer-dist + + - name: Run PHPUnit + run: vendor/bin/phpunit --colors + + static-analysis: + name: PHPStan + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + extensions: intl, json, mbstring + coverage: none + + - name: Cache Composer dependencies + uses: actions/cache@v4 + with: + path: vendor + key: composer-8.3-${{ hashFiles('composer.json') }} + restore-keys: composer-8.3- + + - name: Install dependencies + run: composer install --no-interaction --prefer-dist + + - name: Run PHPStan + run: vendor/bin/phpstan analyse src tests --no-progress From 857392a78eeb9b328f96f5f3536ce3d596d9f5a3 Mon Sep 17 00:00:00 2001 From: Denis Date: Sun, 8 Feb 2026 14:18:33 +0700 Subject: [PATCH 9/9] fix: use explicit instantiation in ValidatorFactory for PHPStan compatibility PHPStan cannot resolve constructor signatures through dynamic `new $class()` calls. Replaced VALIDATOR_MAP + dynamic instantiation with explicit switch/case so PHPStan can statically verify each constructor call. Extracted config path to a class constant. --- .../Validators/ValidatorFactory.php | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/Application/Validators/ValidatorFactory.php b/src/Application/Validators/ValidatorFactory.php index 706d5d2..12e5f75 100644 --- a/src/Application/Validators/ValidatorFactory.php +++ b/src/Application/Validators/ValidatorFactory.php @@ -4,51 +4,51 @@ use QDenka\EasyValidation\Domain\Contracts\ValidatorFactoryInterface; use QDenka\EasyValidation\Domain\Contracts\ValidatorInterface; -use QDenka\EasyValidation\Domain\Ip\IpValidator; -use QDenka\EasyValidation\Domain\Uuid\UuidValidator; -use QDenka\EasyValidation\Domain\Json\JsonValidator; use QDenka\EasyValidation\Domain\Base64\Base64Validator; -use QDenka\EasyValidation\Domain\Phone\PhoneNumberValidator; +use QDenka\EasyValidation\Domain\Date\DateValidator; +use QDenka\EasyValidation\Domain\Email\DisposableEmailValidator; use QDenka\EasyValidation\Domain\Email\EmailValidator; use QDenka\EasyValidation\Domain\Email\GoogleMailValidator; -use QDenka\EasyValidation\Domain\Email\DisposableEmailValidator; -use QDenka\EasyValidation\Domain\Date\DateValidator; +use QDenka\EasyValidation\Domain\Ip\IpValidator; +use QDenka\EasyValidation\Domain\Json\JsonValidator; use QDenka\EasyValidation\Domain\Number\NumberValidator; +use QDenka\EasyValidation\Domain\Phone\PhoneNumberValidator; use QDenka\EasyValidation\Domain\Url\UrlValidator; +use QDenka\EasyValidation\Domain\Uuid\UuidValidator; use QDenka\EasyValidation\Infrastructure\Email\FileDisposableEmailDomainProvider; class ValidatorFactory implements ValidatorFactoryInterface { - private const VALIDATOR_MAP = [ - 'email' => EmailValidator::class, - 'google_email' => GoogleMailValidator::class, - 'disposable_email' => DisposableEmailValidator::class, - 'url' => UrlValidator::class, - 'number' => NumberValidator::class, - 'date' => DateValidator::class, - 'ip' => IpValidator::class, - 'uuid' => UuidValidator::class, - 'json' => JsonValidator::class, - 'base64' => Base64Validator::class, - 'phone' => PhoneNumberValidator::class, - ]; + private const CONFIG_PATH = __DIR__ . '/../../../config/disposable_domains.php'; public static function create(string $type): ?ValidatorInterface { - $type = strtolower($type); - - if (!array_key_exists($type, self::VALIDATOR_MAP)) { - return null; + switch (strtolower($type)) { + case 'email': + return new EmailValidator(); + case 'google_email': + return new GoogleMailValidator(); + case 'disposable_email': + $provider = new FileDisposableEmailDomainProvider(self::CONFIG_PATH); + return new DisposableEmailValidator($provider); + case 'url': + return new UrlValidator(); + case 'number': + return new NumberValidator(); + case 'date': + return new DateValidator(); + case 'ip': + return new IpValidator(); + case 'uuid': + return new UuidValidator(); + case 'json': + return new JsonValidator(); + case 'base64': + return new Base64Validator(); + case 'phone': + return new PhoneNumberValidator(); + default: + return null; } - - $class = self::VALIDATOR_MAP[$type]; - - if ($type === 'disposable_email') { - $configPath = __DIR__ . '/../../../config/disposable_domains.php'; - $provider = new FileDisposableEmailDomainProvider($configPath); - return new $class($provider); - } - - return new $class(); } }