Commit graph

2,294 commits

Author SHA1 Message Date
Henrique Moody
aafa204307
Fix input overwrite not propagating to adjacent results
Adjacent results are results that treat the same input. When overwriting
the input of a result, we should also overwrite the input of its adjacent
result to maintain consistency. Currently, there are no cases where this
has caused issues, but this change prevents potential problems.

Assisted-by: Claude Code (Opus 4.5)
2026-01-30 20:40:12 +01:00
Alexandre Gomes Gaigalas
ec16b3d2df Refactor case sensitiveness support
This is a mid-size refactor that affects several validators.

Most prominently, the ones that had an `$identical` parameter
to deal with case sensitiveness.

This parameter was confusing, effectively making validators such
as `Contains` behave very differently for arrays versus strings.

In arrays, `$identical` meant "the same type", while it in strings
it meant "case sensitive".

That parameter was removed, and the default behavior is now to
always compare **case sensitive** and strict typing.

A document explaining how to combine other validators in order
to achieve case _insensitive_ comparisons was added.

Additionally, the `Call` validator was refactored back to be
suitable to take on the task of being a fast, quick composable
validator.

With the introduction of `Circuit`, we can shift the responsibility
of dealing with possible mismatches to the user. This kind of type
handling is demonstrated in how I refactored `Tld` to account for
the type mismatch without setting error handlers.
2026-01-30 17:11:13 +00:00
Alexandre Gomes Gaigalas
47f8f82d7f Remove mathematical, niche and deprecated validators
This commit removes validators described in #1642, refactoring
to clean up after their removal.

 - Url was refactored to use the function `filter_var` instead.
 - tests/bootstrap.php is no longer needed and was removed.
 - Updated migration guide with recommendations for replacements.
2026-01-30 16:08:27 +00:00
Alexandre Gomes Gaigalas
7876576f08 Change CI Perf to run on schedule, no runs on PRs
See #1635

The goal is to collect data for a while. Until we have enough
benchmarks, the PR check is not very useful, so it was disabled.

We added `--tolerate-failure` to all runs, so this check will
never fail because of a missed performance assertion, but it will
report it and archive it.
2026-01-30 11:45:06 +00:00
Alexandre Gomes Gaigalas
b603ef7b24 Containerize PhoneNumberUtil instance for Phone rule
Similar to d8e31db (commit that containerized iso code dbs), but
this time for PhoneNumberUtil.

This makes the optional dependency testable.

PhoneNumberUtil doesn't have a public constructor, so a factory
was declared instead.
2026-01-29 21:42:32 +00:00
Alexandre Gomes Gaigalas
d8e31dbc3a Containerize sokil databases
The main focus of this change is to make those optional dependencies
more testable.

Unfortunately, some phpstan-ignores had to be included, since ::set
is not a PsrContainer method. We're only using it on tests though,
so it's fine. It targets our php-di container for testing purposes
only. The real implementation only relies on ::get.

This change also has the side effect of improving the performance
of those validators by not instantiating their databases each time
a iso validator is built, achieving massive improvements in those
scenarios. A small benchmark with no assertions was added to track
that improvement.
2026-01-29 19:29:51 +00:00
Alexandre Gomes Gaigalas
7f4a4c2035 Fix misplaced Outer/Inner names when using named() on Each
The Each validator was using "Outer" names (the name applied to
the parent) in child results and vice-versa.

This commit fixes it, and also streamlines the Result class,
introducing helper `mapChildren` and `mapAdjacent` methods and
removing unecessary recursive array_maps.

Named children now also display their names, allowing users to
create more meaningful messages that do not spam nested
`.0`...`.0`...etc numeric keys which could be confusing. If the
user does not name them, the previous behavior is kept.
2026-01-29 18:24:50 +00:00
Alexandre Gomes Gaigalas
03817185b2 Remove now unecessary fallback to json_decode
Since PHP 8.3, we don't need that fallback anymore. Now that the
library is set to a minimum PHP 8.5, we can safely remove it.
2026-01-29 11:14:40 +00:00
Alexandre Gomes Gaigalas
bee97ae6e1 Fixes git workspace after checking out benchmarks
With the introduction of composite actions in the GitHub workflows,
the ci-perf.yml workflow broke.

This happens because the setup-action configures a Post Run (a hook
to be executed after the workflow).

Since the benchmarks checkout a different orphan branch, when that
Post Run executes, it cannot find the action anymore.

To fix it, I introduce a new step that restores the git workspace,
making it available for that hook.
2026-01-29 11:06:19 +00:00
Alexandre Gomes Gaigalas
b69beb1db7 Refactor CI Workflows
- Added a composite action for common setup tasks.
 - Shorter names that fit better GitHub runner displays.
 - Changed ci-perf to only run if src or tests change.
 - Removed redundant step names when they're obvious.
2026-01-28 17:31:37 +00:00
Alexandre Gomes Gaigalas
2a7f345e32 Streamline validators.md index
Makes it so the index looks more like a cheatsheet, condensing
information instead of making long lists that require lots of
scrolling to explore.

Additionally, the happy path for each validator was also
added, providing a quick reference use for comparison.

The direct markdown links were replaced by titled markdown
references, offering mouse-over tooltips over links that
display the validator one-line description.

To ensure a proper source of truth for these new index
goodies, the AssertionMessageLinter was modified to
verify that the first assertion in each doc is a
single-line validator that passes (a happy path), further
making our documentation conventions more solid.
2026-01-28 12:47:08 +00:00
Henrique Moody
68ed5d20d1
Create migration guide from v2 to v3
As we approach the release of version 3.0, it is important to provide our users
with a clear guide to migrating from version 2.x to 3.0. This commit provides
guidance by creating a document that will remain in the repository, so users can
always access it.
2026-01-27 14:40:28 +01:00
Henrique Moody
00e7f2a6ff
Fix ResultQuery::findByPath() for nested paths
The `findByPath()` method was failing to return results when using nested
dot-notation paths such as `user.email` or `items.1`. However, it’s returning
`null` instead of the expected result in some cases.

The root cause was a mismatch between how paths are stored vs searched:

- Storage: Validators like Key and Each create results where the path is stored
  as a linked list. For `user.email`, the "email" result has `path="email"` with
  `parent="user"`.

- Search (old): The method expected a tree structure where it would find a child
  with `path="user"`, then search that child for `path="email"`. But no child
  had `path="user"` - only "email" (with "user" as its parent).

The fix computes each result's full path by walking up the parent chain and
compares it against the search path. Also converts numeric strings to integers
when parsing paths (e.g., `items.1` → `['items', 1]`) since array indices are
stored as integers.

While working on this fix, I also realised that to expose the result's status,
it’s best to use `hasFailed()` instead of `isValid()` in `ResultQuery`, since
users will mostly use results when validation failed, not when it passed.

Assisted-by: Claude Code (Opus 4.5)
2026-01-27 13:25:40 +01:00
Henrique Moody
4390e4feb6
Simplify how we load and save files in data/
We had different ways of saving and loading files from `data/`, so I decided to
unify them to simplify things. I repurposed the `DomainInfo` class and named it
`DataLoader`, so we can use the same class to load anything from the `data/`
directory.
2026-01-26 20:28:29 +01:00
Henrique Moody
819d734a00
Check for mismatches in the mixin classes
When we change the contract of a validator, or create a new one, we need to
ensure that the mixin for the validator is present and matches the validator's
constructor.

This commit changes the current class that generates those mixin classes,
converting it into a linter so we can run it in the GitHub workflow to check for
missing changes.
2026-01-26 20:14:09 +01:00
Henrique Moody
0190f3e109
Group lint-related commands together
Since we have so many lint-related commands now, it makes sense to group
them together to it's easier to spot them.
2026-01-26 20:14:09 +01:00
Alexandre Gomes Gaigalas
bd48bdcda4 Lint Changelog format in validator docs
Introduces a Markdown linter for checking the Changelog format.

"See Also" was transformed into a section to make it easier to
handle it with the `Content` class. The "Related" linter was
simplified to reflect that change too.

An additional "alignment" parameter was added to markdown table
generators, allowing the padding and headers to be explicitly
marked with a specific left (-1), middle (0) or right(1)
alignment.

Existing files were fixed using the `fix` option after the
changes.
2026-01-26 19:11:00 +00:00
Alexandre Gomes Gaigalas
a91517108e Lint SPDX conventions
The `reuse lint` command only checks for REUSE compliance, which
will accept all sorts of SPDX headers.

In this project, however, we have also other conventions. For
example, we require all PHP and docs files from the project
to have a specific license (not just any license) and also a
specific File Copyright Text (not just any copyright).

This commit introduces a command to solve this problem, validating
the headers more thoroughly.

The introduced command also does some dogfooding, using validators
from the library itself to perform some of its tasks, namely: Call,
Each, Contains, Templated and Named, showcasing potential different
use cases for the project.
2026-01-26 16:07:09 +00:00
Henrique Moody
a107b8cb9f
Prevent validators from generating validatorBuilder Ids
When calling `v::validatorName()`, a `ValidatorBuilder` is instantiated
to manage the validation chain. However, users generally expect the ID
to reflect the specific validator invoked rather than the internal
builder class.

This commit updates `Id::fromValidator()` to resolve the ID from the
underlying validator when it is the sole member of the
`ValidatorBuilder`, ensuring more intuitive identification.
2026-01-26 13:57:39 +01:00
Alexandre Gomes Gaigalas
e83e73b771 Adhere to GitHub Community Standards
Makes the project more friendly to GitHub users by providing
conventional files.

Those changes will improve our score under the GitHub
community tab:

https://github.com/Respect/Validation/community

 - The CHANGELOG.md file was removed. It was outdated and
   not being maintained.
 - Minimalistic .github/PULL_REQUEST_TEMPLATE.md
 - Minimalistic .github/SECURITY.md
 - Minimalistic .github/CODE_OF_CONDUCT.md
 - Minimalistic .github/ISSUE_TEMPLATE
2026-01-26 12:00:42 +00:00
Henrique Moody
d1d8980ff9
Use awesome-pages to customize the menus
We tried using `mkdocs-nav-weight` but it turned out quiet limited. Not
only we have to add specific frontmatter to the Markdown files, but we
could also not hide and sort directories.

This commit introduces awesome-pages, which allows us to customize the
order of pages and not list the content of the "validators" directory in
the left menu.
2026-01-26 12:26:06 +01:00
Henrique Moody
aee9123eac
Simplify placeholder pipes documentation
Reference StringFormatter project for modifier details instead of
duplicating documentation.

Assisted-by: Claude Code (Opus 4.5)
2026-01-26 12:00:47 +01:00
Henrique Moody
316200d8ce
Update message placeholder conversion documentation
This documentation had become outdated after significant changes to the
codebase, including the replacement of the template rendering code with
Respect\StringFormatter.

Assisted-by: Claude Code (Opus 4.5)
2026-01-26 12:00:39 +01:00
Henrique Moody
9e22fb02d7
Update "Message Translation" documentation
After many changes to the codebase, this documentation was outdated.

Assisted-by: Claude Code (Opus 4.5)
2026-01-26 12:00:30 +01:00
Henrique Moody
f85d34db3d
Update "Handling Exceptions" documentation
After many changes in the codebase, the documentation was outdated.
2026-01-26 12:00:17 +01:00
Henrique Moody
a12cabb1a9
Remove "license.md" file
Since we have the license in several different places, we might as well
remove the license from the documentation.
2026-01-26 10:34:40 +01:00
Henrique Moody
16b9240044
Allow passing definitions to the ContainerRegistry
This makes it slightly easier to define custom definitions to the
container before creating it.
2026-01-26 10:13:48 +01:00
Henrique Moody
140bd36aa3
Rename library/ to src/
We've always considered renaming this directory, as it's not a common
standard to name `library` the directory where the source code of a
library it. Having it as `src/` is a common pattern we find in several
PHP libraries these days.

Acked-by: Alexandre Gomes Gaigalas <alganet@gmail.com>
2026-01-22 13:13:15 +01:00
Henrique Moody
a372a0b90b
Replace template rendering code with Respect\StringFormatter
I've moved almost all the code for placeholder replacement and parameter
modifiers into an external library called Respect\StringFormatter. This approach
allows us to evolve the template capabilities without making major changes to the
Validation's code.

This commit will introduce another dependency, `respect/string-formatter`, and
will upgrade the version of `respect/string-formatter`, which simplifies our
internal API greatly.

While making this change, I also updated how we generate exceptions. Instead of
rendering the full message and the array of messages, we delegate that creation
to the `ResultQuery`, which improves performance because we don’t need to render
those big messages unless the user actually needs them.
2026-01-21 17:29:15 +01:00
Alexandre Gomes Gaigalas
9862963d06 Setup Continuous Performance
A new workflow, continuous-integration-perf.yml was introduced. It:

 - Checks out the `benchmarks` branch locally.
 - Runs the benchmarks, accounting for non-existant baselines
   and target (main/PR).
 - Stores the .phpbench storage folder and a human-readable
   report in the `benchmarks` branch.
 - Does not make a PR fail, and never reports a failure
   when merging to main.
 - Allows workflow_dispatch for quick re-runs, and has an
   option to reset the baseline in case something changes
   (GitHub runner setup gets faster/slower, major refactors,
   etc).

Thus, it keeps a historical record of all benchmark results.

These results can be viewed by exploring GitHub via the web
interface and seeing the changes in `latest.md` (the human
file commited).

Additionally, one can clone the `benchmarks` branch and run
`phpbench log` to explore the history in more detail.

Some adjustments to previously added benchmarks were made:

 - Assertions were included in order to track time and memory
   tresholds.
 - The benchmarks are now more surgical, and address the
   concrete validators instead of the whole chain validate.

These changes were made to make benchmarks more isolated, with
the intention of adding chain-related benchmarks separately
in the future.
2026-01-21 06:31:37 +00:00
Alexandre Gomes Gaigalas
d9cdc118b2 Introduce REUSE compliance
This commit introduces REUSE compliance by annotating all files
with SPDX information and placing the reused licences in the
LICENSES folder.

We additionally removed the docheader tool which is made obsolete
by this change.

The main LICENSE and copyright text of the project is now not under
my personal name anymore, and it belongs to "The Respect Project
Contributors" instead.

This change restores author names to several files, giving the
appropriate attribution for contributions.
2026-01-21 06:28:11 +00:00
Alexandre Gomes Gaigalas
3270c1f72c Make all remaining validators serializable
This commit concludes the effort to make all current validators
serializable by fixing the remaining ones.

The ability to use `finfo` instances on some filesystem validators
was removed. `Image` was refactored to be a readonly class.

A command was added to the main `composer qa` flow that checks
if all validators are covered by smoke tests (which are currently
used by benchmarks and serialization tests). Therefore, this commit
also ensures that every validator has a benchmark.
2026-01-19 11:04:35 +00:00
Henrique Moody
66a92cdd91
Update "Feature guide" documentation
The documentation was quite outdated after the many changes I made in
the main branch. This commit will make sure it's up-to-date.

I'm also creating a "Placeholder pipes" documentation, so there's not
too much content in the "Feature guide"

Assisted-by: Zed (devstral-2:123b)
2026-01-19 10:47:37 +01:00
Alexandre Gomes Gaigalas
0090191aaa Introduce tests for serialization of validators
This commit introduces a new feature test: SerializableTest, that
tests several validators for their ability to be serialized and
unserialized.

It also makes it so that the same list of validators can be used
by both simple benchmarks and "smoke tests" of all kinds, including
this serialize/unserialize one.

Additionally, the FilterVar validator was modified. Previously, due
to the use of Callback, it was not serializable, but now it is.
2026-01-19 09:24:38 +00:00
Henrique Moody
9e768ccae3
Fix wrong links in the list of validators
The file changed directory and I forgot to rename it. This commit fixes
that.
2026-01-18 18:15:43 +01:00
Henrique Moody
f820f89b56
Rename InvalidRuleConstructorException to InvalidValidatorException
After renaming rules to validatores, it doesn't make sense to keep on
having that exception. I renamed it to a more cleaner name, not
mentioning the constructor because I think that if the constructor is
not valid, the validator is not valid, hence the name I chose.
2026-01-18 16:56:34 +01:00
Alexandre Gomes Gaigalas
8e5938a059 Introduce ContainsCount validator
This validator is similar to Contains, but also checks how many
times the needle appears.

Additionally, the Domain validator was changed to use it instead
of relying on an unserializable callback, thus, making it
serializable.
2026-01-15 13:39:46 +00:00
Alexandre Gomes Gaigalas
4618996b52 Fix factory when validator has no constructor
This change introduces a less misleading exception when trying to
construct an instance and failing due to mismatched arguments
coming from ReflectionExceptions.
2026-01-15 05:51:00 +00:00
Henrique Moody
d2198dfd01
Replace isValid() calls with assert()
There's more value on showing how `assert()` displays the validation
messages than simply showing if `isValid()` returns `true` or `false`.

However, that increases the chances of having outdated documentation, so
I created a doc linter that updates the Markdown files with the
correct message.
2026-01-13 23:37:06 -07:00
Henrique Moody
98150c7065
Add doc linter to check outdated templates
We don't often change the tempaltes of validators, but when we do it's
extremely important that the documentation of the validators match the
exact template the validator has.
2026-01-13 23:37:06 -07:00
Henrique Moody
7aef3763f2
Add doc linter to check related validators
This commit ensures that if validator A has a direct link to validator
B, validator B will have a direct link to validator A too.
2026-01-13 23:37:06 -07:00
Henrique Moody
d38736d167
Add doc linter to check validator constructors
When we make changes to the code, renaming variables, or adding
parameters to a validator, it's easy to forget to update the
documentation.

With this change, we avoid having a disparity between the documentation
and the code.
2026-01-13 23:37:05 -07:00
Henrique Moody
098c973a2a
Add GitHub action to lint documentation files
When we make changes to the category of a validator, it's easy to forget
to update overall list of validators. This commit a GitHub actions that
will run a console command to check if the documentation it up-to-date.

The job will fail when we need to change the document, but the console
command will fix the issues, so there isn't a lot of friction there.
2026-01-13 23:37:05 -07:00
Alexandre Gomes Gaigalas
abdc73bd56 Introduce phpbench benchmarks and profiles
This commit is the first step in setting up *Continuous Performance*
for the repository.

 - Adds phpbench/phpbench to dev dependencies.
 - Adds an initial `ValidatorBench.php` file with validate benchmarks
   for several validators.
 - Adds `composer bench` script to run benchmarks.
 - Adds `composer bench:profile` script to generate profiles.
2026-01-12 23:44:38 +00:00
Alexandre Gomes Gaigalas
cd96d01364 Fix message overriding bug in NestedArrayFormatter
This commit resolves an issue where validation messages would overwrite
each other when multiple validators failed on the same path or key
(e.g., within an `Each` or `Key` validator).

Changes to `NestedArrayFormatter`:
- Implemented a merge strategy: Key collisions now result in a list of
  messages instead of the last message winning.
- Improved handling of mixed key types: When both numeric and string
  keys are present (common in composite validators), numeric keys are now
  replaced by the validator's ID (e.g., `arrayType`, `equals`) to provide
  meaningful, distinct keys.
- Preserved list behavior: Purely numeric key sets are treated as lists,
  maintaining their sequence without re-keying logic.
- Refactored the class to use smaller, single-purpose methods and
  `array_reduce` for clarity.

Tests:
- Updated feature tests (`EachTest`, `AttributesTest`, etc.) to expect the
  full set of validation errors.
- Enhanced `NestedArrayFormatterTest` with scenarios for key collisions,
  mixed keys, and ID substitution.
2026-01-12 10:42:11 +00:00
Henrique Moody
4cc45b55a8
Create a "Getting started" page
That allows users to quickly start playing with Validation.
2026-01-10 15:43:31 +01:00
Henrique Moody
6022914cf0
Remove Yes and No rules
I created those validators to make it easy to parse parameters or
console command inputs that were answers to questions one might ask.

One of the biggest problems is that it depends on the machine's locale,
which can be a bit troublesome, rather than receiving a locale in the
constructor. That doesn’t allow for a lot of flexibility when someone
has a multi-lingual application. Additionally, these validators rely on
the regex from `nl_langinfo()`, which is very permissive, resulting in
false positives.

I have a working version of a console command that retrieves data from
the Unicode Common Locale Data Repository (CLDR) and updates a list of
`yesstr` and `nostr` strings from the main XML file of each language.
However, I came to realise that the whole thing is not worth it.

The validators Yes and No can be replaced by using rules like `Regex`
and `In`. They won’t have the ease of multilingual support, but I don’t
think those validators are used a lot. So, I decided I would just remove
them, and if users really ask for it in the next major version, I’d be
happy to revive my branch.
2026-01-10 05:47:28 +01:00
Henrique Moody
35ea95c6f0
Remove number prefixes from Markdown files
We used to have those to preserve the order of the pages when generating
the documentation with MkDocs. This commit introduces the
`mkdocs-nav-weight`, that allows us to make that order without having
those prefixes.
2026-01-07 14:46:06 +01:00
Henrique Moody
f635cc748f
Update regional information
I manually run the commands that update the data we use in `Tld`,
`PostalCode`, and `PublicDomainSuffix`.
2026-01-07 09:48:50 +01:00
Henrique Moody
7892a7c902
Port Bash scripts to PHP
It makes more sense to use PHP to generate PHP code than to use Bash. I
love writing Bash scripts, but I know it's not for everyone, and they
can become quite complex. Porting them to PHP code also lowers the
barrier for people to change them.

While I was making those changes, I also noticed a problem with how we
save the domain suffixes. We're converting all of them to ASCII, so we
are not preserving languages such as Chinese, Thai, and Hebrew, which
use non-ASCII characters.
2026-01-06 10:06:22 +01:00