Skip to content

Commit 32fc270

Browse files
authored
Merge pull request #10 from programmatordev/feature/symfony-8-1-upgrade
Symfony 8.1 upgrade
2 parents 854ada6 + c8b47c1 commit 32fc270

13 files changed

Lines changed: 282 additions & 32 deletions

README.md

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ offering an easy-to-use and intuitive API to validate user input or other data i
2121
## Table of Contents
2222

2323
- [Installation](#installation)
24+
- [When to use it](#when-to-use-it)
2425
- [Usage](#usage)
2526
- [Constraints](#constraints)
2627
- [Methods](#methods)
@@ -30,6 +31,7 @@ offering an easy-to-use and intuitive API to validate user input or other data i
3031
- [toArray](#toarray)
3132
- [addNamespace](#addnamespace)
3233
- [setTranslator](#settranslator)
34+
- [reset](#reset)
3335
- [Custom Constraints](#custom-constraints)
3436
- [Translations](#translations)
3537

@@ -45,6 +47,13 @@ Install via [Composer](https://getcomposer.org/):
4547
composer require programmatordev/fluent-validator
4648
```
4749

50+
## When to use it
51+
52+
Use Fluent Validator when you want Symfony Validator constraints for raw values without setting up object metadata, attributes, forms, or a larger validation layer.
53+
It is useful for small input checks, command arguments, request fragments, webhook payload values, configuration values, and library code.
54+
55+
This package does not replace Symfony Validator. It wraps Symfony Validator and keeps its constraints, violation objects, groups, translations, and custom constraint model.
56+
4857
## Usage
4958

5059
Simple usage example:
@@ -63,7 +72,33 @@ if ($errors->count() > 0) {
6372
}
6473
```
6574

75+
Use `assert` when invalid values should stop the current flow:
76+
77+
```php
78+
use ProgrammatorDev\FluentValidator\Exception\ValidationFailedException;
79+
use ProgrammatorDev\FluentValidator\Validator;
80+
81+
try {
82+
Validator::notBlank()->email()->assert($email, 'email');
83+
}
84+
catch (ValidationFailedException $exception) {
85+
$message = $exception->getMessage();
86+
// "email: This value is not a valid email address."
87+
}
88+
```
89+
90+
Use `isValid` when you only need a boolean:
91+
92+
```php
93+
use ProgrammatorDev\FluentValidator\Validator;
94+
95+
if (!Validator::url()->isValid($website)) {
96+
// handle invalid URL
97+
}
98+
```
99+
66100
Constraint autocompletion is available in IDEs like PhpStorm.
101+
The suggested methods are generated from the installed Symfony Validator constraints.
67102
The method names match Symfony constraints but with a lowercase first letter:
68103

69104
- `NotBlank` => `notBlank`
@@ -77,6 +112,20 @@ For all available methods, check the [Methods](#methods) section.
77112

78113
There is also a section for [Custom Constraints](#custom-constraints) and [Translations](#translations).
79114

115+
### Groups
116+
117+
Validation groups work the same way as in Symfony Validator:
118+
119+
```php
120+
use ProgrammatorDev\FluentValidator\Validator;
121+
122+
$validator = Validator::notBlank(groups: ['Default'])
123+
->email(groups: ['registration']);
124+
125+
$validator->isValid('invalid-email', groups: ['Default']); // true
126+
$validator->isValid('invalid-email', groups: ['registration']); // false
127+
```
128+
80129
## Constraints
81130

82131
All available constraints can be found on the [Symfony Validator documentation](https://symfony.com/doc/current/validation.html#constraints).
@@ -209,6 +258,15 @@ Used to add a translator for validation error message translations.
209258

210259
Check the [Translations](#translations) section.
211260

261+
### `reset`
262+
263+
```php
264+
reset(): void
265+
```
266+
267+
Clears globally registered custom constraint namespaces and translator configuration.
268+
Useful when changing global validator configuration in tests, workers, or other long-running PHP processes.
269+
212270
## Custom Constraints
213271

214272
If you need a custom constraint, follow the Symfony Validator documentation: [Creating Custom Constraints](https://symfony.com/doc/current/validation/custom_constraint.html).
@@ -291,4 +349,4 @@ Make sure to open a pull request or issue.
291349
## License
292350

293351
This project is licensed under the MIT license.
294-
Please see the [LICENSE](LICENSE) file distributed with this source code for further information regarding copyright and licensing.
352+
Please see the [LICENSE](LICENSE) file distributed with this source code for further information regarding copyright and licensing.

composer.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@
1313
],
1414
"require": {
1515
"php": ">=8.4",
16-
"symfony/config": "^8.0",
17-
"symfony/translation": "^8.0",
18-
"symfony/validator": "^8.0"
16+
"symfony/config": "^8.1",
17+
"symfony/translation": "^8.1",
18+
"symfony/validator": "^8.1"
1919
},
2020
"require-dev": {
2121
"phpunit/phpunit": "^11.5",
22-
"symfony/console": "^8.0",
23-
"symfony/var-dumper": "^8.0",
22+
"symfony/console": "^8.1",
23+
"symfony/var-dumper": "^8.1",
2424
"wyrihaximus/list-classes-in-directory": "^1.7"
2525
},
2626
"autoload": {

src/ChainedValidatorInterface.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,17 @@ public function wordCount(
742742
mixed $payload = null,
743743
): ChainedValidatorInterface&Validator;
744744

745+
public function xml(
746+
string $formatMessage = 'This value is not valid XML.',
747+
string $schemaMessage = 'This value does not conform to the expected XSD schema.',
748+
string $tooLargeMessage = 'This XML payload is too large ({{ size }} bytes): it exceeds the limit of {{ limit }} bytes.',
749+
?string $schemaPath = null,
750+
int $schemaFlags = 0,
751+
int $maxSize = 5242880,
752+
?array $groups = null,
753+
mixed $payload = null,
754+
): ChainedValidatorInterface&Validator;
755+
745756
public function yaml(
746757
string $message = 'This value is not valid YAML.',
747758
int $flags = 0,
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\FluentValidator\Exception;
4+
5+
class NoSuchTranslationException extends \RuntimeException
6+
{
7+
public function __construct(string $locale)
8+
{
9+
$message = sprintf('Translation for locale "%s" was not found.', $locale);
10+
11+
parent::__construct($message);
12+
}
13+
}

src/StaticValidatorInterface.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,17 @@ public static function wordCount(
742742
mixed $payload = null,
743743
): ChainedValidatorInterface&Validator;
744744

745+
public static function xml(
746+
string $formatMessage = 'This value is not valid XML.',
747+
string $schemaMessage = 'This value does not conform to the expected XSD schema.',
748+
string $tooLargeMessage = 'This XML payload is too large ({{ size }} bytes): it exceeds the limit of {{ limit }} bytes.',
749+
?string $schemaPath = null,
750+
int $schemaFlags = 0,
751+
int $maxSize = 5242880,
752+
?array $groups = null,
753+
mixed $payload = null,
754+
): ChainedValidatorInterface&Validator;
755+
745756
public static function yaml(
746757
string $message = 'This value is not valid YAML.',
747758
int $flags = 0,

src/Translator/Translator.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace ProgrammatorDev\FluentValidator\Translator;
44

55
use Composer\InstalledVersions;
6+
use ProgrammatorDev\FluentValidator\Exception\NoSuchTranslationException;
67
use Symfony\Component\Translation\Loader\XliffFileLoader;
78
use Symfony\Contracts\Translation\TranslatorInterface;
89

@@ -16,6 +17,10 @@ public function __construct(private string $locale)
1617
$packagePath = InstalledVersions::getInstallPath('symfony/validator');
1718
$resourcePath = sprintf('%s/Resources/translations/validators.%s.xlf', $packagePath, $this->locale);
1819

20+
if (!is_file($resourcePath)) {
21+
throw new NoSuchTranslationException($this->locale);
22+
}
23+
1924
$this->translator = new \Symfony\Component\Translation\Translator($this->locale);
2025
$this->translator->addLoader('xlf', new XliffFileLoader());
2126
$this->translator->addResource('xlf', $resourcePath, $this->locale);
@@ -30,4 +35,4 @@ public function getLocale(): string
3035
{
3136
return $this->locale;
3237
}
33-
}
38+
}

src/Validator.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
class Validator
1515
{
1616
/** @var Constraint[] */
17-
private array $constraints;
17+
private array $constraints = [];
1818

1919
/** @var string[] */
2020
private static array $namespaces = [];
@@ -99,11 +99,21 @@ private function addConstraint(Constraint $constraint): void
9999

100100
public static function addNamespace(string $namespace): void
101101
{
102+
if (in_array($namespace, self::$namespaces, true)) {
103+
return;
104+
}
105+
102106
self::$namespaces[] = $namespace;
103107
}
104108

105109
public static function setTranslator(?TranslatorInterface $translator): void
106110
{
107111
self::$translator = $translator;
108112
}
109-
}
113+
114+
public static function reset(): void
115+
{
116+
self::$namespaces = [];
117+
self::$translator = null;
118+
}
119+
}

src/Writer/InterfaceWriter.php

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,14 @@ class InterfaceWriter
66
{
77
private \SplFileObject $file;
88

9-
public function __construct(private readonly string $interfaceName)
9+
public function __construct(
10+
private readonly string $interfaceName,
11+
?string $outputDirectory = null,
12+
)
1013
{
11-
$filename = sprintf('src/%s.php', $this->interfaceName);
14+
$outputDirectory ??= dirname(__DIR__);
15+
$filename = sprintf('%s/%s.php', rtrim($outputDirectory, '/'), $this->interfaceName);
16+
1217
$this->file = new \SplFileObject($filename, 'w');
1318
}
1419

@@ -100,10 +105,6 @@ private function formatType(string $type): string
100105

101106
private function formatValue(mixed $value): string
102107
{
103-
if (is_string($value)) {
104-
return sprintf("'%s'", $value);
105-
}
106-
107108
if ($value === []) {
108109
return '[]';
109110
}
@@ -112,14 +113,6 @@ private function formatValue(mixed $value): string
112113
return 'null';
113114
}
114115

115-
if ($value === false) {
116-
return 'false';
117-
}
118-
119-
if ($value === true) {
120-
return 'true';
121-
}
122-
123-
return (string) $value;
116+
return var_export($value, true);
124117
}
125-
}
118+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44

55
use PHPUnit\Framework\TestCase;
66

7-
class AbstractTestCase extends TestCase {}
7+
class AbstractTestCase extends TestCase {}

src/Test/Constraint/ContainsAlphanumeric.php renamed to tests/Fixtures/Constraint/ContainsAlphanumeric.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace ProgrammatorDev\FluentValidator\Test\Constraint;
3+
namespace ProgrammatorDev\FluentValidator\Test\Fixtures\Constraint;
44

55
use Symfony\Component\Validator\Constraint;
66

@@ -24,4 +24,4 @@ public function __sleep(): array
2424
{
2525
return array_merge(parent::__sleep(), ['mode']);
2626
}
27-
}
27+
}

0 commit comments

Comments
 (0)