diff --git a/.github/workflows/continuous-integration-perf.yml b/.github/workflows/continuous-integration-perf.yml new file mode 100644 index 000000000..26041eb11 --- /dev/null +++ b/.github/workflows/continuous-integration-perf.yml @@ -0,0 +1,88 @@ +name: Continuous Integration (perf) + +on: + push: + paths-ignore: + - 'bin/**' + - 'docs/**' + pull_request: + paths-ignore: + - 'bin/**' + - 'docs/**' + workflow_dispatch: + inputs: + baseline: + description: 'Baseline mode. latest: compare against latest benchmarks; rebaseline: store new baseline.' + type: choice + default: 'latest' + options: + - 'latest' + - 'rebaseline' + +jobs: + tests: + name: Benchmarks + runs-on: ubuntu-latest + continue-on-error: true # This job is experimental + permissions: + contents: write + pull-requests: write + + strategy: + matrix: + php-version: + - "8.5" + + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + persist-credentials: true + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: xdebug + + - name: Install Dependencies + run: composer install --prefer-dist ${{ matrix.composer-extra-arguments }} + + - name: Fetch Benchmarks + run: | + git fetch origin benchmarks + mkdir -p .phpbench + git checkout origin/benchmarks -- .phpbench || echo "No previous benchmarks found" + + - name: Run Benchmarks + run: | + # Baseline does not exist or rebaseline requested. Generate it. + if [ -z "$(ls -A .phpbench)" ] || [ "${{ github.event.inputs.baseline || 'latest' }}" = "rebaseline" ]; then + vendor/bin/phpbench run --report=aggregate --progress=plain --store --tag=${GITHUB_SHA} + + # On main branch push, update baseline with tolerance for failures. + elif [ "${GITHUB_REF}" = "refs/heads/main" ] && [ "${GITHUB_EVENT_NAME}" = "push" ]; then + vendor/bin/phpbench run --report=aggregate --progress=plain --store --tag=${GITHUB_SHA} --ref=latest --tolerate-failure + + # On other branches, compare against latest baseline, fails if worse. + else + vendor/bin/phpbench run --report=aggregate --progress=plain --store --tag=${GITHUB_SHA} --ref=latest + fi + + # Generate report for human consumption + vendor/bin/phpbench report --report=aggregate --ref=latest | + tail -n+2 | head -n-2 | tr '+' '|' > report.md + + cat report.md + + - name: Commit Benchmark Results + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + run: | + set -euo pipefail + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git checkout benchmarks + mv -f report.md latest.md + git add .phpbench latest.md + git commit -m "Store benchmark results [skip ci]" || echo "No changes to commit" + git push origin benchmarks diff --git a/tests/benchmark/ValidatorBench.php b/tests/benchmark/ValidatorBench.php index a52ff05b2..d16a9fa4d 100644 --- a/tests/benchmark/ValidatorBench.php +++ b/tests/benchmark/ValidatorBench.php @@ -18,13 +18,26 @@ class ValidatorBench use SmokeTestProvider; /** @param array $params */ + #[Bench\ParamProviders(['provideValidatorInput'])] + #[Bench\BeforeMethods('setFileUploadMock')] #[Bench\Iterations(10)] - #[Bench\RetryThreshold(10)] + #[Bench\RetryThreshold(5)] #[Bench\Revs(5)] - #[Bench\ParamProviders(['provideValidatorInput'])] - public function benchValidate(array $params): void + #[Bench\Warmup(1)] + #[Bench\Assert('mode(variant.time.avg) < mode(baseline.time.avg) +/- 10%')] + #[Bench\Assert('mode(variant.time.net) < mode(baseline.time.net) +/- 10%')] + #[Bench\Assert('mode(variant.mem.peak) < mode(baseline.mem.peak) +/- 10%')] + #[Bench\Assert('mode(variant.mem.real) < mode(baseline.mem.real) +/- 10%')] + #[Bench\Assert('mode(variant.mem.final) < mode(baseline.mem.final) +/- 10%')] + #[Bench\Subject] + public function evaluate(array $params): void { [$v, $input] = $params; - $v->validate($input); + $v->evaluate($input); + } + + public function setFileUploadMock(): void + { + set_mock_is_uploaded_file_return(true); } } diff --git a/tests/feature/SerializableTest.php b/tests/feature/SerializableTest.php index b399115cc..a46a95d19 100644 --- a/tests/feature/SerializableTest.php +++ b/tests/feature/SerializableTest.php @@ -12,7 +12,7 @@ test('Can be serialized and unserialized', function ($validator, $input): void { set_mock_is_uploaded_file_return(true); expect( - unserialize(serialize($validator))->validate($input)->isValid(), + unserialize(serialize($validator))->evaluate($input)->hasPassed, )->toBeTrue(); })->with(fn(): Generator => (new class { use SmokeTestProvider { diff --git a/tests/library/SmokeTestProvider.php b/tests/library/SmokeTestProvider.php index 13e9c3e9b..bac7cd924 100644 --- a/tests/library/SmokeTestProvider.php +++ b/tests/library/SmokeTestProvider.php @@ -10,8 +10,8 @@ namespace Respect\Validation\Test; use Generator; -use Respect\Validation\Mixins\Chain; -use Respect\Validation\ValidatorBuilder as v; +use Respect\Validation\Validator; +use Respect\Validation\Validators as vs; use stdClass; use function fopen; @@ -23,167 +23,167 @@ trait SmokeTestProvider { public static function provideValidatorInput(): Generator { - yield 'All' => [v::all(v::intVal()), [1, 2, 3]]; - yield 'AllOf' => [v::allOf(v::intVal(), v::greaterThan(0)), 5]; - yield 'Alnum' => [v::alnum(), 'abc123']; - yield 'Alpha' => [v::alpha(), 'abc']; - yield 'AlwaysInvalid' => [v::not(v::alwaysInvalid()), 'whatever']; - yield 'AlwaysValid' => [v::alwaysValid(), 'whatever']; - yield 'AnyOf' => [v::anyOf(v::intVal(), v::stringVal()), 5]; - yield 'ArrayType' => [v::arrayType(), []]; - yield 'ArrayVal' => [v::arrayVal(), []]; - yield 'Attributes' => [v::attributes(), (object) ['required' => true]]; - yield 'Base' => [v::base(2), '001001']; - yield 'Base64' => [v::base64(), 'U29tZSBCYXNlNjQgU3RyaW5n']; - yield 'Between' => [v::between(1, 10), 5]; - yield 'BetweenExclusive' => [v::betweenExclusive(1, 10), 5]; - yield 'Blank' => [v::blank(), '']; - yield 'BoolType' => [v::boolType(), true]; - yield 'BoolVal' => [v::boolVal(), true]; - yield 'Bsn' => [v::bsn(), '612890053']; - yield 'Call' => [v::call('array_keys', v::each(v::stringType())), ['a' => 'b']]; - yield 'CallableType' => [v::callableType(), [static::class, 'callableTarget']]; - yield 'Callback' => [v::callback('is_string'), 'valid']; - yield 'Charset' => [v::charset('UTF-8'), 'example']; - yield 'Circuit' => [v::circuit(v::intVal(), v::greaterThan(0)), 5]; - yield 'Cnh' => [v::cnh(), '02650306461']; - yield 'Cnpj' => [v::cnpj(), '11444777000161']; - yield 'Consonant' => [v::consonant(), 'bcdf']; - yield 'Contains' => [v::contains('needle'), 'haystack needle']; - yield 'ContainsAny' => [v::containsAny(['a', 'b']), 'abc']; - yield 'ContainsCount' => [v::containsCount('foo', 2), 'foo bar foo']; - yield 'Control' => [v::control(), "\n\r"]; - yield 'Countable' => [v::countable(), []]; - yield 'CountryCode' => [v::countryCode(), 'US']; - yield 'Cpf' => [v::cpf(), '11598647644']; - yield 'CreditCard' => [v::creditCard(), '4111111111111111']; - yield 'CurrencyCode' => [v::currencyCode(), 'USD']; - yield 'Date' => [v::date(), '2020-01-01']; - yield 'DateTime' => [v::dateTime(), '2020-01-01 12:00:00']; - yield 'DateTimeDiff' => [v::dateTimeDiff('years', v::greaterThan(18), 'd/m/Y'), '09/12/1990']; - yield 'Decimal' => [v::decimal(2), '1.23']; - yield 'Digit' => [v::digit(), '7']; - yield 'Directory' => [v::directory(), 'tests/fixtures']; - yield 'Domain' => [v::domain(), 'example.com']; - yield 'Each' => [v::each(v::stringType()), ['a', 'b']]; - yield 'Email' => [v::email(), 'bob@example.com']; - yield 'Emoji' => [v::emoji(), '😀']; - yield 'EndsWith' => [v::endsWith('.com'), 'example.com']; - yield 'Equals' => [v::equals('x'), 'x']; - yield 'Equivalent' => [v::equivalent(123), 123.0]; - yield 'Even' => [v::even(), 2]; - yield 'Executable' => [v::executable(), 'tests/fixtures/executable']; - yield 'Exists' => [v::exists(), 'tests/fixtures/valid-image.png']; - yield 'Extension' => [v::extension('png'), 'image.png']; - yield 'Factor' => [v::factor(0), 36]; - yield 'FalseVal' => [v::falseVal(), false]; - yield 'Falsy' => [v::falsy(), 0]; - yield 'Fibonacci' => [v::fibonacci(), 13]; - yield 'File' => [v::file(), __FILE__]; - yield 'FilterVar' => [v::filterVar(FILTER_VALIDATE_EMAIL), 'bob@example.com']; - yield 'Finite' => [v::finite(), 1.23]; - yield 'FloatType' => [v::floatType(), 1.23]; - yield 'FloatVal' => [v::floatVal(), 1.23]; - yield 'Graph' => [v::graph(), 'abc123!@#']; - yield 'GreaterThan' => [v::greaterThan(0), 1]; - yield 'GreaterThanOrEqual' => [v::greaterThanOrEqual(1), 1]; - yield 'Hetu' => [v::hetu(), '010106A9012']; - yield 'HexRgbColor' => [v::hexRgbColor(), '#FFAABB']; - yield 'Iban' => [v::iban(), 'SE35 5000 0000 0549 1000 0003']; - yield 'Identical' => [v::identical(123), 123]; - yield 'Image' => [v::image(), 'tests/fixtures/valid-image.png']; - yield 'Imei' => [v::imei(), '490154203237518']; - yield 'In' => [v::in(['a', 'b']), 'a']; - yield 'Infinite' => [v::infinite(), INF]; - yield 'Instance' => [v::instance(stdClass::class), new stdClass()]; - yield 'IntType' => [v::intType(), 123]; - yield 'IntVal' => [v::intVal(), 123]; - yield 'Ip' => [v::ip(), '127.0.0.1']; - yield 'Isbn' => [v::isbn(), '9783161484100']; - yield 'IterableType' => [v::iterableType(), []]; - yield 'IterableVal' => [v::iterableVal(), []]; - yield 'Json' => [v::json(), '{"key":"value"}']; - yield 'Key' => [v::key('name', v::stringType()), ['name' => 'value']]; - yield 'KeyExists' => [v::keyExists('name'), ['name' => 'value']]; - yield 'KeyOptional' => [v::keyOptional('missing', v::stringType()), ['name' => 'value']]; - yield 'KeySet' => [v::keySet(v::key('name', v::stringType())), ['name' => 'value']]; - yield 'LanguageCode' => [v::languageCode(), 'en']; - yield 'Lazy' => [v::lazy([static::class, 'callableLazy']), 123]; - yield 'LeapDate' => [v::leapDate('Y-m-d'), '2020-02-29']; - yield 'LeapYear' => [v::leapYear(), 2020]; - yield 'Length' => [v::length(v::equals(4)), 'abcd']; - yield 'LessThan' => [v::lessThan(10), 5]; - yield 'LessThanOrEqual' => [v::lessThanOrEqual(10), 10]; - yield 'Lowercase' => [v::lowercase(), 'abc']; - yield 'Luhn' => [v::luhn(), '2222400041240011']; - yield 'MacAddress' => [v::macAddress(), '00:11:22:33:44:55']; - yield 'Max' => [v::max(v::equals(30)), [10, 20, 30]]; - yield 'Min' => [v::min(v::equals(10)), [10, 20, 30]]; - yield 'Mimetype' => [v::mimetype('image/png'), 'tests/fixtures/valid-image.png']; - yield 'Multiple' => [v::multiple(3), 9]; - yield 'Named' => [v::named('MyValidator', v::intVal()), 123]; - yield 'Negative' => [v::negative(), -1]; - yield 'NfeAccessKey' => [v::nfeAccessKey(), '52060433009911002506550120000007800267301615']; - yield 'Nif' => [v::nif(), '12345678Z']; - yield 'Nip' => [v::nip(), '1645865777']; - yield 'NoneOf' => [v::noneOf(v::intVal(), v::floatVal()), 'foo']; - yield 'Not' => [v::not(v::trueVal()), false]; - yield 'NullOr' => [v::nullOr(v::intVal()), null]; - yield 'Number' => [v::number(), '123']; - yield 'NullType' => [v::nullType(), null]; - yield 'NumericVal' => [v::numericVal(), '123']; - yield 'ObjectType' => [v::objectType(), new stdClass()]; - yield 'Odd' => [v::odd(), 3]; - yield 'OneOf' => [v::oneOf(v::digit(), v::alpha()), 'AB']; - yield 'PerfectSquare' => [v::perfectSquare(), 16]; - yield 'Pesel' => [v::pesel(), '21120209256']; - yield 'Phone' => [v::phone(), '+1 650 253 00 00']; - yield 'PhpLabel' => [v::phpLabel(), 'valid_label']; - yield 'Pis' => [v::pis(), '120.0340.678-8']; - yield 'PolishIdCard' => [v::polishIdCard(), 'AYW036733']; - yield 'PortugueseNif' => [v::portugueseNif(), '123456789']; - yield 'Positive' => [v::positive(), 1]; - yield 'PostalCode' => [v::postalCode('US'), '12345']; - yield 'PrimeNumber' => [v::primeNumber(), 7]; - yield 'Printable' => [v::printable(), 'abc123!@#']; - yield 'Property' => [v::property('email', v::endsWith('@example.com')), (object) ['email' => 'a@example.com']]; - yield 'PropertyExists' => [v::propertyExists('email'), (object) ['email' => 'a@example.com']]; - yield 'PropertyOptional' => [v::propertyOptional('missing', v::email()), (object) ['email' => 'a@example.com']]; - yield 'PublicDomainSuffix' => [v::publicDomainSuffix(), 'co.uk']; - yield 'Punct' => [v::punct(), '!@#']; - yield 'Readable' => [v::readable(), 'tests/fixtures/valid-image.png']; - yield 'Regex' => [v::regex('/^[a-z]+$/'), 'abc']; - yield 'ResourceType' => [v::resourceType(), fopen('php://temp', 'r')]; - yield 'Roman' => [v::roman(), 'XIV']; - yield 'ScalarVal' => [v::scalarVal(), 'example']; - yield 'Size' => [v::size('KB', v::between(1, 1000)), 'tests/fixtures/valid-image.png']; - yield 'Slug' => [v::slug(), 'a-valid-slug']; - yield 'Sorted' => [v::sorted('ASC'), [1, 2, 3]]; - yield 'Space' => [v::space(), " \t\n"]; - yield 'Spaced' => [v::spaced(), 'a b c']; - yield 'StartsWith' => [v::startsWith('ex'), 'example']; - yield 'StringType' => [v::stringType(), 'example']; - yield 'StringVal' => [v::stringVal(), 'example']; - yield 'SubdivisionCode' => [v::subdivisionCode('US'), 'CA']; - yield 'Subset' => [v::subset(['a', 'b', 'c']), ['a', 'b']]; - yield 'SymbolicLink' => [v::symbolicLink(), 'tests/fixtures/symbolic-link']; - yield 'Templated' => [v::templated('Foo', v::stringVal()), 'foo']; - yield 'Time' => [v::time(), '12:34:56']; - yield 'Tld' => [v::tld(), 'com']; - yield 'TrueVal' => [v::trueVal(), true]; - yield 'Undef' => [v::undef(), null]; - yield 'UndefOr' => [v::undefOr(v::intVal()), null]; - yield 'Unique' => [v::unique(), [1, 2, 3]]; - yield 'Uploaded' => [v::uploaded(), 'tests/fixtures/valid-image.png']; - yield 'Uppercase' => [v::uppercase(), 'ABC']; - yield 'Url' => [v::url(), 'https://example.com']; - yield 'Uuid' => [v::uuid(), '123e4567-e89b-12d3-a456-426655440000']; - yield 'Version' => [v::version(), '1.2.3']; - yield 'VideoUrl' => [v::videoUrl(), 'https://www.youtube.com/watch?v=dQw4w9WgXcQ']; - yield 'Vowel' => [v::vowel(), 'aeiou']; - yield 'When' => [v::when(v::intVal(), v::alwaysValid(), v::alwaysInvalid()), 5]; - yield 'Writable' => [v::writable(), 'tests/fixtures/valid-image.png']; - yield 'Xdigit' => [v::xdigit(), 'AF']; + yield 'All' => [new vs\All(new vs\IntVal()), [1, 2, 3]]; + yield 'AllOf' => [new vs\AllOf(new vs\IntVal(), new vs\GreaterThan(0)), 5]; + yield 'Alnum' => [new vs\Alnum(), 'abc123']; + yield 'Alpha' => [new vs\Alpha(), 'abc']; + yield 'AlwaysInvalid' => [new vs\Not(new vs\AlwaysInvalid()), 'whatever']; + yield 'AlwaysValid' => [new vs\AlwaysValid(), 'whatever']; + yield 'AnyOf' => [new vs\AnyOf(new vs\IntVal(), new vs\StringVal()), 5]; + yield 'ArrayType' => [new vs\ArrayType(), []]; + yield 'ArrayVal' => [new vs\ArrayVal(), []]; + yield 'Attributes' => [new vs\Attributes(), (object) ['required' => true]]; + yield 'Base' => [new vs\Base(2), '001001']; + yield 'Base64' => [new vs\Base64(), 'U29tZSBCYXNlNjQgU3RyaW5n']; + yield 'Between' => [new vs\Between(1, 10), 5]; + yield 'BetweenExclusive' => [new vs\BetweenExclusive(1, 10), 5]; + yield 'Blank' => [new vs\Blank(), '']; + yield 'BoolType' => [new vs\BoolType(), true]; + yield 'BoolVal' => [new vs\BoolVal(), true]; + yield 'Bsn' => [new vs\Bsn(), '612890053']; + yield 'Call' => [new vs\Call('array_keys', new vs\Each(new vs\StringType())), ['a' => 'b']]; + yield 'CallableType' => [new vs\CallableType(), [static::class, 'callableTarget']]; + yield 'Callback' => [new vs\Callback('is_string'), 'valid']; + yield 'Charset' => [new vs\Charset('UTF-8'), 'example']; + yield 'Circuit' => [new vs\Circuit(new vs\IntVal(), new vs\GreaterThan(0)), 5]; + yield 'Cnh' => [new vs\Cnh(), '02650306461']; + yield 'Cnpj' => [new vs\Cnpj(), '11444777000161']; + yield 'Consonant' => [new vs\Consonant(), 'bcdf']; + yield 'Contains' => [new vs\Contains('needle'), 'haystack needle']; + yield 'ContainsAny' => [new vs\ContainsAny(['a', 'b']), 'abc']; + yield 'ContainsCount' => [new vs\ContainsCount('foo', 2), 'foo bar foo']; + yield 'Control' => [new vs\Control(), "\n\r"]; + yield 'Countable' => [new vs\Countable(), []]; + yield 'CountryCode' => [new vs\CountryCode(), 'US']; + yield 'Cpf' => [new vs\Cpf(), '11598647644']; + yield 'CreditCard' => [new vs\CreditCard(), '4111111111111111']; + yield 'CurrencyCode' => [new vs\CurrencyCode(), 'USD']; + yield 'Date' => [new vs\Date(), '2020-01-01']; + yield 'DateTime' => [new vs\DateTime(), '2020-01-01 12:00:00']; + yield 'DateTimeDiff' => [new vs\DateTimeDiff('years', new vs\GreaterThan(18), 'd/m/Y'), '09/12/1990']; + yield 'Decimal' => [new vs\Decimal(2), '1.23']; + yield 'Digit' => [new vs\Digit(), '7']; + yield 'Directory' => [new vs\Directory(), 'tests/fixtures']; + yield 'Domain' => [new vs\Domain(), 'example.com']; + yield 'Each' => [new vs\Each(new vs\StringType()), ['a', 'b']]; + yield 'Email' => [new vs\Email(), 'bob@example.com']; + yield 'Emoji' => [new vs\Emoji(), '😀']; + yield 'EndsWith' => [new vs\EndsWith('.com'), 'example.com']; + yield 'Equals' => [new vs\Equals('x'), 'x']; + yield 'Equivalent' => [new vs\Equivalent(123), 123.0]; + yield 'Even' => [new vs\Even(), 2]; + yield 'Executable' => [new vs\Executable(), 'tests/fixtures/executable']; + yield 'Exists' => [new vs\Exists(), 'tests/fixtures/valid-image.png']; + yield 'Extension' => [new vs\Extension('png'), 'image.png']; + yield 'Factor' => [new vs\Factor(0), 36]; + yield 'FalseVal' => [new vs\FalseVal(), false]; + yield 'Falsy' => [new vs\Falsy(), 0]; + yield 'Fibonacci' => [new vs\Fibonacci(), 13]; + yield 'File' => [new vs\File(), __FILE__]; + yield 'FilterVar' => [new vs\FilterVar(FILTER_VALIDATE_EMAIL), 'bob@example.com']; + yield 'Finite' => [new vs\Finite(), 1.23]; + yield 'FloatType' => [new vs\FloatType(), 1.23]; + yield 'FloatVal' => [new vs\FloatVal(), 1.23]; + yield 'Graph' => [new vs\Graph(), 'abc123!@#']; + yield 'GreaterThan' => [new vs\GreaterThan(0), 1]; + yield 'GreaterThanOrEqual' => [new vs\GreaterThanOrEqual(1), 1]; + yield 'Hetu' => [new vs\Hetu(), '010106A9012']; + yield 'HexRgbColor' => [new vs\HexRgbColor(), '#FFAABB']; + yield 'Iban' => [new vs\Iban(), 'SE35 5000 0000 0549 1000 0003']; + yield 'Identical' => [new vs\Identical(123), 123]; + yield 'Image' => [new vs\Image(), 'tests/fixtures/valid-image.png']; + yield 'Imei' => [new vs\Imei(), '490154203237518']; + yield 'In' => [new vs\In(['a', 'b']), 'a']; + yield 'Infinite' => [new vs\Infinite(), INF]; + yield 'Instance' => [new vs\Instance(stdClass::class), new stdClass()]; + yield 'IntType' => [new vs\IntType(), 123]; + yield 'IntVal' => [new vs\IntVal(), 123]; + yield 'Ip' => [new vs\Ip(), '127.0.0.1']; + yield 'Isbn' => [new vs\Isbn(), '9783161484100']; + yield 'IterableType' => [new vs\IterableType(), []]; + yield 'IterableVal' => [new vs\IterableVal(), []]; + yield 'Json' => [new vs\Json(), '{"key":"value"}']; + yield 'Key' => [new vs\Key('name', new vs\StringType()), ['name' => 'value']]; + yield 'KeyExists' => [new vs\KeyExists('name'), ['name' => 'value']]; + yield 'KeyOptional' => [new vs\KeyOptional('missing', new vs\StringType()), ['name' => 'value']]; + yield 'KeySet' => [new vs\KeySet(new vs\Key('name', new vs\StringType())), ['name' => 'value']]; + yield 'LanguageCode' => [new vs\LanguageCode(), 'en']; + yield 'Lazy' => [new vs\Lazy([static::class, 'callableLazy']), 123]; + yield 'LeapDate' => [new vs\LeapDate('Y-m-d'), '2020-02-29']; + yield 'LeapYear' => [new vs\LeapYear(), 2020]; + yield 'Length' => [new vs\Length(new vs\Equals(4)), 'abcd']; + yield 'LessThan' => [new vs\LessThan(10), 5]; + yield 'LessThanOrEqual' => [new vs\LessThanOrEqual(10), 10]; + yield 'Lowercase' => [new vs\Lowercase(), 'abc']; + yield 'Luhn' => [new vs\Luhn(), '2222400041240011']; + yield 'MacAddress' => [new vs\MacAddress(), '00:11:22:33:44:55']; + yield 'Max' => [new vs\Max(new vs\Equals(30)), [10, 20, 30]]; + yield 'Min' => [new vs\Min(new vs\Equals(10)), [10, 20, 30]]; + yield 'Mimetype' => [new vs\Mimetype('image/png'), 'tests/fixtures/valid-image.png']; + yield 'Multiple' => [new vs\Multiple(3), 9]; + yield 'Named' => [new vs\Named('MyValidator', new vs\IntVal()), 123]; + yield 'Negative' => [new vs\Negative(), -1]; + yield 'NfeAccessKey' => [new vs\NfeAccessKey(), '52060433009911002506550120000007800267301615']; + yield 'Nif' => [new vs\Nif(), '12345678Z']; + yield 'Nip' => [new vs\Nip(), '1645865777']; + yield 'NoneOf' => [new vs\NoneOf(new vs\IntVal(), new vs\FloatVal()), 'foo']; + yield 'Not' => [new vs\Not(new vs\TrueVal()), false]; + yield 'NullOr' => [new vs\NullOr(new vs\IntVal()), null]; + yield 'Number' => [new vs\Number(), '123']; + yield 'NullType' => [new vs\NullType(), null]; + yield 'NumericVal' => [new vs\NumericVal(), '123']; + yield 'ObjectType' => [new vs\ObjectType(), new stdClass()]; + yield 'Odd' => [new vs\Odd(), 3]; + yield 'OneOf' => [new vs\OneOf(new vs\Digit(), new vs\Alpha()), 'AB']; + yield 'PerfectSquare' => [new vs\PerfectSquare(), 16]; + yield 'Pesel' => [new vs\Pesel(), '21120209256']; + yield 'Phone' => [new vs\Phone(), '+1 650 253 00 00']; + yield 'PhpLabel' => [new vs\PhpLabel(), 'valid_label']; + yield 'Pis' => [new vs\Pis(), '120.0340.678-8']; + yield 'PolishIdCard' => [new vs\PolishIdCard(), 'AYW036733']; + yield 'PortugueseNif' => [new vs\PortugueseNif(), '123456789']; + yield 'Positive' => [new vs\Positive(), 1]; + yield 'PostalCode' => [new vs\PostalCode('US'), '12345']; + yield 'PrimeNumber' => [new vs\PrimeNumber(), 7]; + yield 'Printable' => [new vs\Printable(), 'abc123!@#']; + yield 'Property' => [new vs\Property('age', new vs\IntVal()), (object) ['age' => 18]]; + yield 'PropertyExists' => [new vs\PropertyExists('age'), (object) ['age' => 18]]; + yield 'PropertyOptional' => [new vs\PropertyOptional('missing', new vs\Email()), (object) []]; + yield 'PublicDomainSuffix' => [new vs\PublicDomainSuffix(), 'co.uk']; + yield 'Punct' => [new vs\Punct(), '!@#']; + yield 'Readable' => [new vs\Readable(), 'tests/fixtures/valid-image.png']; + yield 'Regex' => [new vs\Regex('/^[a-z]+$/'), 'abc']; + yield 'ResourceType' => [new vs\ResourceType(), fopen('php://temp', 'r')]; + yield 'Roman' => [new vs\Roman(), 'XIV']; + yield 'ScalarVal' => [new vs\ScalarVal(), 'example']; + yield 'Size' => [new vs\Size('KB', new vs\Between(1, 1000)), 'tests/fixtures/valid-image.png']; + yield 'Slug' => [new vs\Slug(), 'a-valid-slug']; + yield 'Sorted' => [new vs\Sorted('ASC'), [1, 2, 3]]; + yield 'Space' => [new vs\Space(), " \t\n"]; + yield 'Spaced' => [new vs\Spaced(), 'a b c']; + yield 'StartsWith' => [new vs\StartsWith('ex'), 'example']; + yield 'StringType' => [new vs\StringType(), 'example']; + yield 'StringVal' => [new vs\StringVal(), 'example']; + yield 'SubdivisionCode' => [new vs\SubdivisionCode('US'), 'CA']; + yield 'Subset' => [new vs\Subset(['a', 'b', 'c']), ['a', 'b']]; + yield 'SymbolicLink' => [new vs\SymbolicLink(), 'tests/fixtures/symbolic-link']; + yield 'Templated' => [new vs\Templated('Foo', new vs\StringVal()), 'foo']; + yield 'Time' => [new vs\Time(), '12:34:56']; + yield 'Tld' => [new vs\Tld(), 'com']; + yield 'TrueVal' => [new vs\TrueVal(), true]; + yield 'Undef' => [new vs\Undef(), null]; + yield 'UndefOr' => [new vs\UndefOr(new vs\IntVal()), null]; + yield 'Unique' => [new vs\Unique(), [1, 2, 3]]; + yield 'Uploaded' => [new vs\Uploaded(), 'tests/fixtures/valid-image.png']; + yield 'Uppercase' => [new vs\Uppercase(), 'ABC']; + yield 'Url' => [new vs\Url(), 'https://example.com']; + yield 'Uuid' => [new vs\Uuid(), '123e4567-e89b-12d3-a456-426655440000']; + yield 'Version' => [new vs\Version(), '1.2.3']; + yield 'VideoUrl' => [new vs\VideoUrl(), 'https://www.youtube.com/watch?v=dQw4w9WgXcQ']; + yield 'Vowel' => [new vs\Vowel(), 'aeiou']; + yield 'When' => [new vs\When(new vs\IntVal(), new vs\AlwaysValid(), new vs\AlwaysInvalid()), 5]; + yield 'Writable' => [new vs\Writable(), 'tests/fixtures/valid-image.png']; + yield 'Xdigit' => [new vs\Xdigit(), 'AF']; } public static function callableTarget(): true @@ -191,8 +191,8 @@ public static function callableTarget(): true return true; } - public static function callableLazy(): v|Chain + public static function callableLazy(): Validator { - return v::intVal(); + return new vs\IntVal(); } }