Skip to content

Commit 6969c14

Browse files
committed
Add more benchmarks, smoke tests, make FilterVar serializable
1 parent 8e5938a commit 6969c14

5 files changed

Lines changed: 247 additions & 110 deletions

File tree

library/Validators/FilterVar.php

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,11 @@
1212
use Attribute;
1313
use Respect\Validation\Exceptions\InvalidRuleConstructorException;
1414
use Respect\Validation\Message\Template;
15-
use Respect\Validation\Validators\Core\Envelope;
15+
use Respect\Validation\Result;
16+
use Respect\Validation\Validator;
1617

1718
use function array_key_exists;
1819
use function filter_var;
19-
use function is_array;
20-
use function is_int;
2120

2221
use const FILTER_VALIDATE_BOOLEAN;
2322
use const FILTER_VALIDATE_DOMAIN;
@@ -33,7 +32,7 @@
3332
'{{subject}} must be valid',
3433
'{{subject}} must not be valid',
3534
)]
36-
final class FilterVar extends Envelope
35+
final class FilterVar implements Validator
3736
{
3837
private const array ALLOWED_FILTERS = [
3938
FILTER_VALIDATE_BOOLEAN => 'is_bool',
@@ -46,21 +45,22 @@ final class FilterVar extends Envelope
4645
FILTER_VALIDATE_URL => 'is_string',
4746
];
4847

49-
public function __construct(int $filter, mixed $options = null)
48+
public function __construct(private int $filter, private mixed $options = null)
5049
{
5150
if (!array_key_exists($filter, self::ALLOWED_FILTERS)) {
5251
throw new InvalidRuleConstructorException('Cannot accept the given filter');
5352
}
53+
}
5454

55-
$arguments = [$filter];
56-
if (is_array($options) || is_int($options)) {
57-
$arguments[] = $options;
58-
}
59-
60-
parent::__construct(new Callback(static function ($input) use ($filter, $arguments) {
61-
return (self::ALLOWED_FILTERS[$filter])(
62-
filter_var($input, ...$arguments),
63-
);
64-
}));
55+
public function evaluate(mixed $input): Result
56+
{
57+
return Result::of(
58+
(self::ALLOWED_FILTERS[$this->filter])(match ($this->options) {
59+
null => filter_var($input, $this->filter),
60+
default => filter_var($input, $this->filter, $this->options),
61+
}),
62+
$input,
63+
$this,
64+
);
6565
}
6666
}

phpbench.json.dist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"$schema": "./vendor/phpbench/phpbench/phpbench.schema.json",
3-
"runner.bootstrap": "vendor/autoload.php",
3+
"runner.bootstrap": "tests/bootstrap.php",
44
"runner.path": "tests/benchmark",
55
"runner.file_pattern": "*Bench.php"
66
}

tests/benchmark/ValidatorBench.php

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

3-
declare(strict_types=1);
4-
5-
namespace Respect\Validation\Benchmarks;
6-
73
/*
84
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
95
* SPDX-License-Identifier: MIT
106
*/
117

12-
use Generator;
8+
declare(strict_types=1);
9+
10+
namespace Respect\Validation\Benchmarks;
11+
1312
use PhpBench\Attributes as Bench;
13+
use Respect\Validation\Test\SmokeTestProvider;
1414
use Respect\Validation\Validator;
15-
use Respect\Validation\ValidatorBuilder as v;
1615

1716
class ValidatorBench
1817
{
19-
/** @param array<Validator, mixed> $params */ #[Bench\Iterations(10)]
18+
use SmokeTestProvider;
19+
20+
/** @param array<Validator, mixed> $params */
21+
#[Bench\Iterations(10)]
2022
#[Bench\RetryThreshold(10)]
2123
#[Bench\Revs(5)]
2224
#[Bench\ParamProviders(['provideValidatorInput'])]
@@ -25,91 +27,4 @@ public function benchValidate(array $params): void
2527
[$v, $input] = $params;
2628
$v->validate($input);
2729
}
28-
29-
public function provideValidatorInput(): Generator
30-
{
31-
yield 'AllOf' => [v::allOf(v::intVal(), v::greaterThan(0)), 5];
32-
yield 'Alnum' => [v::alnum(), 'abc123'];
33-
yield 'Alpha' => [v::alpha(), 'abc'];
34-
yield 'AnyOf' => [v::anyOf(v::intVal(), v::stringVal()), 5];
35-
yield 'ArrayType' => [v::arrayType(), []];
36-
yield 'ArrayVal' => [v::arrayVal(), []];
37-
yield 'Between' => [v::between(1, 10), 5];
38-
yield 'BetweenExclusive' => [v::betweenExclusive(1, 10), 5];
39-
yield 'BoolType' => [v::boolType(), true];
40-
yield 'BoolVal' => [v::boolVal(), true];
41-
yield 'Bsn' => [v::bsn(), '612890053'];
42-
yield 'Call' => [v::call('array_keys', v::each(v::stringType())), ['a' => 'b']];
43-
yield 'Charset' => [v::charset('UTF-8'), 'example'];
44-
yield 'Circuit' => [v::circuit(v::intVal(), v::trueVal()), 123];
45-
yield 'Cnpj' => [v::cnpj(), '11444777000161'];
46-
yield 'Consonant' => [v::consonant(), 'bcdf'];
47-
yield 'Contains' => [v::contains('needle'), 'haystack needle'];
48-
yield 'ContainsAny' => [v::containsAny(['a', 'b']), 'abc'];
49-
yield 'ContainsCount' => [v::containsCount('a', 3), 'banana'];
50-
yield 'Control' => [v::control(), "\n\r"];
51-
yield 'Countable' => [v::countable(), []];
52-
yield 'CountryCode' => [v::countryCode(), 'US'];
53-
yield 'Cpf' => [v::cpf(), '11598647644'];
54-
yield 'CurrencyCode' => [v::currencyCode(), 'USD'];
55-
yield 'Date' => [v::date(), '2020-01-01'];
56-
yield 'DateTime' => [v::dateTime(), '2020-01-01 12:00:00'];
57-
yield 'Decimal' => [v::decimal(2), '1.23'];
58-
yield 'Digit' => [v::digit(), '7'];
59-
yield 'Domain' => [v::domain(), 'example.com'];
60-
yield 'Each' => [v::each(v::stringType()), ['a', 'b']];
61-
yield 'Email' => [v::email(), 'bob@example.com'];
62-
yield 'EndsWith' => [v::endsWith('.com'), 'example.com'];
63-
yield 'Equals' => [v::equals('x'), 'x'];
64-
yield 'Even' => [v::even(), 2];
65-
yield 'Executable' => [v::executable(), 'tests/fixtures/executable'];
66-
yield 'Exists' => [v::exists(), 'tests/fixtures/valid-image.png'];
67-
yield 'FalseVal' => [v::falseVal(), false];
68-
yield 'Fibonacci' => [v::fibonacci(), 13];
69-
yield 'File' => [v::file(), __FILE__];
70-
yield 'FloatType' => [v::floatType(), 1.23];
71-
yield 'FloatVal' => [v::floatVal(), 1.23];
72-
yield 'GreaterThan' => [v::greaterThan(0), 1];
73-
yield 'GreaterThanOrEqual' => [v::greaterThanOrEqual(1), 1];
74-
yield 'Hetu' => [v::hetu(), '010106A9012'];
75-
yield 'Iban' => [v::iban(), 'SE35 5000 0000 0549 1000 0003'];
76-
yield 'Identical' => [v::identical(123), 123];
77-
yield 'In' => [v::in(['a', 'b']), 'a'];
78-
yield 'IntType' => [v::intType(), 123];
79-
yield 'IntVal' => [v::intVal(), 123];
80-
yield 'Ip' => [v::ip(), '127.0.0.1'];
81-
yield 'IterableVal' => [v::iterableVal(), []];
82-
yield 'LanguageCode' => [v::languageCode(), 'en'];
83-
yield 'LessThan' => [v::lessThan(10), 5];
84-
yield 'LessThanOrEqual' => [v::lessThanOrEqual(10), 10];
85-
yield 'Lowercase' => [v::lowercase(), 'abc'];
86-
yield 'Luhn' => [v::luhn(), '2222400041240011'];
87-
yield 'MacAddress' => [v::macAddress(), '00:11:22:33:44:55'];
88-
yield 'Negative' => [v::negative(), -1];
89-
yield 'Nip' => [v::nip(), '1645865777'];
90-
yield 'Not' => [v::not(v::trueVal()), false];
91-
yield 'NullType' => [v::nullType(), null];
92-
yield 'NumericVal' => [v::numericVal(), '123'];
93-
yield 'Odd' => [v::odd(), 3];
94-
yield 'PerfectSquare' => [v::perfectSquare(), 16];
95-
yield 'Pesel' => [v::pesel(), '21120209256'];
96-
yield 'Property' => [v::property('email', v::endsWith('@example.com')), (object) ['email' => 'a@example.com']];
97-
yield 'PropertyExists' => [v::propertyExists('email'), (object) ['email' => 'a@example.com']];
98-
yield 'PropertyOptional' => [v::propertyOptional('missing', v::email()), (object) ['email' => 'a@example.com']];
99-
yield 'Readable' => [v::readable(), 'tests/fixtures/valid-image.png'];
100-
yield 'ScalarVal' => [v::scalarVal(), 'example'];
101-
yield 'Slug' => [v::slug(), 'a-valid-slug'];
102-
yield 'StartsWith' => [v::startsWith('ex'), 'example'];
103-
yield 'StringType' => [v::stringType(), 'example'];
104-
yield 'StringVal' => [v::stringVal(), 'example'];
105-
yield 'SymbolicLink' => [v::symbolicLink(), 'tests/fixtures/symbolic-link'];
106-
yield 'Time' => [v::time(), '12:34:56'];
107-
yield 'TrueVal' => [v::trueVal(), true];
108-
yield 'Unique' => [v::unique(), [1, 2, 3]];
109-
yield 'Uppercase' => [v::uppercase(), 'ABC'];
110-
yield 'Uuid' => [v::uuid(), '123e4567-e89b-12d3-a456-426655440000'];
111-
yield 'When' => [v::when(v::trueVal(), v::intVal()), 123];
112-
yield 'Writable' => [v::writable(), 'tests/fixtures/valid-image.png'];
113-
yield 'Xdigit' => [v::xdigit(), 'AF'];
114-
}
11530
}
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
<?php
2+
3+
/*
4+
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
5+
* SPDX-License-Identifier: MIT
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Respect\Validation\Test;
11+
12+
use Generator;
13+
use Respect\Validation\Mixins\Chain;
14+
use Respect\Validation\ValidatorBuilder as v;
15+
use stdClass;
16+
17+
use function fopen;
18+
19+
use const FILTER_VALIDATE_EMAIL;
20+
use const INF;
21+
22+
trait SmokeTestProvider
23+
{
24+
public static function provideValidatorInput(): Generator
25+
{
26+
yield 'All' => [v::all(v::intVal()), [1, 2, 3]];
27+
yield 'AllOf' => [v::allOf(v::intVal(), v::greaterThan(0)), 5];
28+
yield 'Alnum' => [v::alnum(), 'abc123'];
29+
yield 'Alpha' => [v::alpha(), 'abc'];
30+
yield 'AlwaysInvalid' => [v::not(v::alwaysInvalid()), 'whatever'];
31+
yield 'AlwaysValid' => [v::alwaysValid(), 'whatever'];
32+
yield 'AnyOf' => [v::anyOf(v::intVal(), v::stringVal()), 5];
33+
yield 'ArrayType' => [v::arrayType(), []];
34+
yield 'ArrayVal' => [v::arrayVal(), []];
35+
yield 'Attributes' => [v::attributes(), (object) ['required' => true]];
36+
yield 'Base' => [v::base(2), '001001'];
37+
yield 'Base64' => [v::base64(), 'U29tZSBCYXNlNjQgU3RyaW5n'];
38+
yield 'Between' => [v::between(1, 10), 5];
39+
yield 'BetweenExclusive' => [v::betweenExclusive(1, 10), 5];
40+
yield 'Blank' => [v::blank(), ''];
41+
yield 'BoolType' => [v::boolType(), true];
42+
yield 'BoolVal' => [v::boolVal(), true];
43+
yield 'Bsn' => [v::bsn(), '612890053'];
44+
yield 'Call' => [v::call('array_keys', v::each(v::stringType())), ['a' => 'b']];
45+
yield 'CallableType' => [v::callableType(), [static::class, 'callableTarget']];
46+
yield 'Callback' => [v::callback('is_string'), 'valid'];
47+
yield 'Charset' => [v::charset('UTF-8'), 'example'];
48+
yield 'Circuit' => [v::circuit(v::intVal(), v::greaterThan(0)), 5];
49+
yield 'Cnh' => [v::cnh(), '02650306461'];
50+
yield 'Cnpj' => [v::cnpj(), '11444777000161'];
51+
yield 'Consonant' => [v::consonant(), 'bcdf'];
52+
yield 'Contains' => [v::contains('needle'), 'haystack needle'];
53+
yield 'ContainsAny' => [v::containsAny(['a', 'b']), 'abc'];
54+
yield 'ContainsCount' => [v::containsCount('foo', 2), 'foo bar foo'];
55+
yield 'Control' => [v::control(), "\n\r"];
56+
yield 'Countable' => [v::countable(), []];
57+
yield 'CountryCode' => [v::countryCode(), 'US'];
58+
yield 'Cpf' => [v::cpf(), '11598647644'];
59+
yield 'CreditCard' => [v::creditCard(), '4111111111111111'];
60+
yield 'CurrencyCode' => [v::currencyCode(), 'USD'];
61+
yield 'Date' => [v::date(), '2020-01-01'];
62+
yield 'DateTime' => [v::dateTime(), '2020-01-01 12:00:00'];
63+
yield 'DateTimeDiff' => [v::dateTimeDiff('years', v::greaterThan(18), 'd/m/Y'), '09/12/1990'];
64+
yield 'Decimal' => [v::decimal(2), '1.23'];
65+
yield 'Digit' => [v::digit(), '7'];
66+
yield 'Directory' => [v::directory(), 'tests/fixtures'];
67+
yield 'Domain' => [v::domain(), 'example.com'];
68+
yield 'Each' => [v::each(v::stringType()), ['a', 'b']];
69+
yield 'Email' => [v::email(), 'bob@example.com'];
70+
yield 'Emoji' => [v::emoji(), '😀'];
71+
yield 'EndsWith' => [v::endsWith('.com'), 'example.com'];
72+
yield 'Equals' => [v::equals('x'), 'x'];
73+
yield 'Equivalent' => [v::equivalent(123), 123.0];
74+
yield 'Even' => [v::even(), 2];
75+
yield 'Executable' => [v::executable(), 'tests/fixtures/executable'];
76+
yield 'Exists' => [v::exists(), 'tests/fixtures/valid-image.png'];
77+
yield 'Extension' => [v::extension('png'), 'image.png'];
78+
yield 'Factor' => [v::factor(0), 36];
79+
yield 'FalseVal' => [v::falseVal(), false];
80+
yield 'Falsy' => [v::falsy(), 0];
81+
yield 'Fibonacci' => [v::fibonacci(), 13];
82+
yield 'File' => [v::file(), __FILE__];
83+
yield 'FilterVar' => [v::filterVar(FILTER_VALIDATE_EMAIL), 'bob@example.com'];
84+
yield 'Finite' => [v::finite(), 1.23];
85+
yield 'FloatType' => [v::floatType(), 1.23];
86+
yield 'FloatVal' => [v::floatVal(), 1.23];
87+
yield 'Graph' => [v::graph(), 'abc123!@#'];
88+
yield 'GreaterThan' => [v::greaterThan(0), 1];
89+
yield 'GreaterThanOrEqual' => [v::greaterThanOrEqual(1), 1];
90+
yield 'Hetu' => [v::hetu(), '010106A9012'];
91+
yield 'HexRGBColor' => [v::hexRgbColor(), '#FFAABB'];
92+
yield 'Iban' => [v::iban(), 'SE35 5000 0000 0549 1000 0003'];
93+
yield 'Identical' => [v::identical(123), 123];
94+
yield 'Imei' => [v::imei(), '490154203237518'];
95+
yield 'In' => [v::in(['a', 'b']), 'a'];
96+
yield 'Infinite' => [v::infinite(), INF];
97+
yield 'Instance' => [v::instance(stdClass::class), new stdClass()];
98+
yield 'IntType' => [v::intType(), 123];
99+
yield 'IntVal' => [v::intVal(), 123];
100+
yield 'Ip' => [v::ip(), '127.0.0.1'];
101+
yield 'Isbn' => [v::isbn(), '9783161484100'];
102+
yield 'IterableType' => [v::iterableType(), []];
103+
yield 'IterableVal' => [v::iterableVal(), []];
104+
yield 'Json' => [v::json(), '{"key":"value"}'];
105+
yield 'Key' => [v::key('name', v::stringType()), ['name' => 'value']];
106+
yield 'KeyExists' => [v::keyExists('name'), ['name' => 'value']];
107+
yield 'KeyOptional' => [v::keyOptional('missing', v::stringType()), ['name' => 'value']];
108+
yield 'KeySet' => [v::keySet(v::key('name', v::stringType())), ['name' => 'value']];
109+
yield 'LanguageCode' => [v::languageCode(), 'en'];
110+
yield 'Lazy' => [v::lazy([static::class, 'callableLazy']), 123];
111+
yield 'LeapDate' => [v::leapDate('Y-m-d'), '2020-02-29'];
112+
yield 'LeapYear' => [v::leapYear(), 2020];
113+
yield 'Length' => [v::length(v::equals(4)), 'abcd'];
114+
yield 'LessThan' => [v::lessThan(10), 5];
115+
yield 'LessThanOrEqual' => [v::lessThanOrEqual(10), 10];
116+
yield 'Lowercase' => [v::lowercase(), 'abc'];
117+
yield 'Luhn' => [v::luhn(), '2222400041240011'];
118+
yield 'MacAddress' => [v::macAddress(), '00:11:22:33:44:55'];
119+
yield 'Max' => [v::max(v::equals(30)), [10, 20, 30]];
120+
yield 'Min' => [v::min(v::equals(10)), [10, 20, 30]];
121+
yield 'Multiple' => [v::multiple(3), 9];
122+
yield 'Named' => [v::named('MyValidator', v::intVal()), 123];
123+
yield 'Negative' => [v::negative(), -1];
124+
yield 'NfeAccessKey' => [v::nfeAccessKey(), '52060433009911002506550120000007800267301615'];
125+
yield 'Nif' => [v::nif(), '12345678Z'];
126+
yield 'Nip' => [v::nip(), '1645865777'];
127+
yield 'Not' => [v::not(v::trueVal()), false];
128+
yield 'NullOr' => [v::nullOr(v::intVal()), null];
129+
yield 'Number' => [v::number(), '123'];
130+
yield 'NullType' => [v::nullType(), null];
131+
yield 'NumericVal' => [v::numericVal(), '123'];
132+
yield 'Odd' => [v::odd(), 3];
133+
yield 'OneOf' => [v::oneOf(v::digit(), v::alpha()), 'AB'];
134+
yield 'PerfectSquare' => [v::perfectSquare(), 16];
135+
yield 'Pesel' => [v::pesel(), '21120209256'];
136+
yield 'Phone' => [v::phone(), '+1 650 253 00 00'];
137+
yield 'PhpLabel' => [v::phpLabel(), 'valid_label'];
138+
yield 'Pis' => [v::pis(), '120.0340.678-8'];
139+
yield 'PolishIdCard' => [v::polishIdCard(), 'AYW036733'];
140+
yield 'PortugueseNif' => [v::portugueseNif(), '123456789'];
141+
yield 'Positive' => [v::positive(), 1];
142+
yield 'PostalCode' => [v::postalCode('US'), '12345'];
143+
yield 'PrimeNumber' => [v::primeNumber(), 7];
144+
yield 'Printable' => [v::printable(), 'abc123!@#'];
145+
yield 'Property' => [v::property('email', v::endsWith('@example.com')), (object) ['email' => 'a@example.com']];
146+
yield 'PropertyExists' => [v::propertyExists('email'), (object) ['email' => 'a@example.com']];
147+
yield 'PropertyOptional' => [v::propertyOptional('missing', v::email()), (object) ['email' => 'a@example.com']];
148+
yield 'PublicDomainSuffix' => [v::publicDomainSuffix(), 'co.uk'];
149+
yield 'Punct' => [v::punct(), '!@#'];
150+
yield 'Readable' => [v::readable(), 'tests/fixtures/valid-image.png'];
151+
yield 'Regex' => [v::regex('/^[a-z]+$/'), 'abc'];
152+
yield 'ResourceType' => [v::resourceType(), fopen('php://temp', 'r')];
153+
yield 'Roman' => [v::roman(), 'XIV'];
154+
yield 'ScalarVal' => [v::scalarVal(), 'example'];
155+
yield 'Slug' => [v::slug(), 'a-valid-slug'];
156+
yield 'Sorted' => [v::sorted('ASC'), [1, 2, 3]];
157+
yield 'Space' => [v::space(), " \t\n"];
158+
yield 'Spaced' => [v::spaced(), 'a b c'];
159+
yield 'StartsWith' => [v::startsWith('ex'), 'example'];
160+
yield 'StringType' => [v::stringType(), 'example'];
161+
yield 'StringVal' => [v::stringVal(), 'example'];
162+
yield 'SubdivisionCode' => [v::subdivisionCode('US'), 'CA'];
163+
yield 'Subset' => [v::subset(['a', 'b', 'c']), ['a', 'b']];
164+
yield 'SymbolicLink' => [v::symbolicLink(), 'tests/fixtures/symbolic-link'];
165+
yield 'Time' => [v::time(), '12:34:56'];
166+
yield 'Tld' => [v::tld(), 'com'];
167+
yield 'TrueVal' => [v::trueVal(), true];
168+
yield 'Undef' => [v::undef(), null];
169+
yield 'UndefOr' => [v::undefOr(v::intVal()), null];
170+
yield 'Unique' => [v::unique(), [1, 2, 3]];
171+
yield 'Uppercase' => [v::uppercase(), 'ABC'];
172+
yield 'Uuid' => [v::uuid(), '123e4567-e89b-12d3-a456-426655440000'];
173+
yield 'Version' => [v::version(), '1.2.3'];
174+
yield 'VideoUrl' => [v::videoUrl(), 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'];
175+
yield 'Vowel' => [v::vowel(), 'aeiou'];
176+
yield 'When' => [v::when(v::intVal(), v::alwaysValid(), v::alwaysInvalid()), 5];
177+
yield 'Writable' => [v::writable(), 'tests/fixtures/valid-image.png'];
178+
yield 'Xdigit' => [v::xdigit(), 'AF'];
179+
}
180+
181+
public static function callableTarget(): true
182+
{
183+
return true;
184+
}
185+
186+
public static function callableLazy(): v|Chain
187+
{
188+
return v::intVal();
189+
}
190+
}

0 commit comments

Comments
 (0)