Commit graph

190 commits

Author SHA1 Message Date
Henrique Moody
16c2a75d00
Enable validating inputs without throwing exceptions
Currently, the only way to handle when a validation fails is by
`assert()`, which either throws an exception or doesn't. That means that
every time a user wants to handle the results, they must use try-catch
blocks, which may add some overhead.

This commit introduces the `ResultQuery` class that wraps a `Result`
object, providing an alternative to exception-based validation. This
allows users to handle results directly without try-catch blocks.

I’m taking a risky move here using the old method `validate()`, but I
can’t think of a better name for this method.
2025-12-22 20:37:11 +01:00
Henrique Moody
5b00d69766
Update how we handle templates
Currently, the templates that a user provides when running `assert()`
can significantly impact how the message is displayed. Because of this,
the formatters become complex as they all need to handle similar
conditions to format results.

This commit changes this behaviour, letting only the
`InterpolationRenderer` handle the templates. This makes the code
simpler and allows people to use the `InterpolationRenderer` directly,
without needing to figure out how to handle templates. Thinking about it
further, I believe handling templates is a concern for the `Renderer`
anyway, and this will open the way to other improvements using the
renderer.

I also removed the exception that is thrown when the template is not a
string, because I think that after validation has failed, we should not
throw any other exceptions, as that could cause unexpected errors for
users.
2025-12-22 14:05:55 +01:00
Henrique Moody
644ecb5190
Change the contract of the formatters
Both `ArrayFormatter` and `StringFormatter` accept an instance of the
`Translator`. Thinking about it a bit better, I realised that a
formatter might not always need a `Translator`, but it will surely need
a `Renderer`.

Besides, the `InterpolationRenderer` needs to take translation into
account, so it seems more natural to me that this is the one that will
get an instance of the `Translator`, as other implementations of the
`Renderer` might not even deal with translations.
2025-12-22 13:43:59 +01:00
Henrique Moody
fd7438f9c7
Use a DI container to create instances of Validator
The `ValidatorDefaults` is cumbersome, and customising it can be
annoying. Most projects use some sort of dependency injection container,
and by integrating the creation of the `Validator` with the PSR-11, we
allow users to easily customise how they create validators.

Some tasks, like overwriting the `Translator`, become a bit more
verbose, if the user is not already using a PSR-11 container, but I
think that’s a good tradeoff.
2025-12-22 13:12:04 +01:00
Henrique Moody
bf9b970e24
Move important value objects to the root namespace
The `Id`, `Name`, and `Path` value objects are not only message-related
concerns, they're part of the core of the library, hence it makes sense
to place them at the root namespace.
2025-12-21 11:14:47 +01:00
Henrique Moody
137c74c5b3
Change how we trace the path of results
Currently, we’re using scalar values to trace paths. The problem with
that approach is that we can’t create a reliable hierarchy with them, as
we can’t know for sure when a path is the same for different rules. By
using an object, we can easily compare and create a parent-child
relationship with it.

While making these changes, I deemed it necessary to also create objects
to handle Name and Id, which makes the code simpler and more robust. By
having Name and Path, we can create specific stringifiers that allow us
to customise how we render those values.

I didn’t manage to make those changes atomically, which is why this
commit makes so many changes. I found myself moving back and forth, and
making all those changes at once was the best solution I found.
2025-12-20 22:19:17 +01:00
Henrique Moody
82e0722443
Split the Formatter into different formatters
I've noticed that the `StandardFormatter` was quite bloated, which made
it difficult to maintain. Understanding what each method was doing was
quite complicated. Besides, the name "Standard" doesn't mean anything,
because it doesn't say what the implementation does.

I split the `Formatter` into two different interfaces: `StringFormatter`
and `ArrayFormatter`, and I moved some code around:

*  `StandardFormatter::main()` -> `FirstResultStringFormatter`
* `StandardFormatter::full()` -> `NestedListStringFormatter`
* `StandardFormatter::array()` -> `NestedArrayFormatter`

That opens up new ways of handling error messages, potentially
introducing features like `JsonStringFormatter` or `FlatArrayFormatter`
in the future.

While working on this, I removed a significant amount of unnecessary
code, which also improved my overall understanding of those formatters.

I'm not very happy with all the methods in `ValidatorDefaults`, but I
will refactor that later.
2025-12-19 16:20:28 +01:00
Henrique Moody
cfeb01e89e
Bump respect/coding-standard from 4 to 5 2025-12-18 19:03:39 +01:00
Henrique Moody
0066786fa7
Remove deprecated methods check() and validate()
We want to release version 3.0 as fresh as possible, without having to
maintain backward compatibility with the previous versions.

Acked-by: Alexandre Gomes Gaigalas <alganet@gmail.com>
2025-12-18 17:29:02 +01:00
Fabio Ribeiro
0e8ac1817b
Rename Result->isValid method 2025-03-31 21:29:37 +02:00
Henrique Moody
a3c197f600
Handle names via the Named rule
Because of how the validation engine works now [1], there's no reason to
keep adding names to each rule. Instead, create a single rule that
handles naming rules with a few other accessories. This change is not
necessarily simple, but it shrinks the `Rule` interface, and it's more
aligned with how the library works right now.

Personally, I think this API is much more straightforward than the
`setName()` method, as it's way more explicit about which rule we're
naming. Because of this change, the behaviour changed slightly, but it's
for the best.

Because of this change, I managed to remove a lot of code, but
unfortunately, it's quite a big-bang commit. It would be too complicated
to make it atomic since names are an intrinsic part of the library.

[1]: 238f2d506a
2024-12-26 23:10:19 +01:00
Henrique Moody
9dac855c9e
Customize overwriting file and line in ValidationException
I've already changed the `ValidationException` so as not to let the file
and line from the Validator.php [1]. However, one could go even further
when creating more customizations on top of this library, and allowing
to customize the line could be very useful.

What motivated me making this change because it will be handy when I get
back to work on [Assertion][].

[1]: 75a9b8e94f
[Assertion]: https://github.com/Respect/Assertion
2024-12-26 20:37:54 +01:00
Henrique Moody
1d1da7f099
Create "Templated" rule
Because of how the validation engine works, there's no reason to keep
adding templates to each rule. Instead, creating a single rule that
handles templating rules will simplify the library greatly and shrink
the `Rule` interface.

Personally, I think this API is much more straightforward than the
`setTemplate()` method, as it's way more explicit which rule is being
templated.
2024-12-26 14:40:09 +01:00
Henrique Moody
74c018bced
Make mixins smarter and with a shorter name
To make PHPStan recognize methods when we call Validator with static and
non-static rule names, I added a few methods from `Validator` to the
`ChainedValidator` interface[1]. However, this didn't work so well
because there could have been more methods from `Validator`.

This commit will rename the mixins to better names, but it will also
make the `Chain` (old `ChainedValidator` to have a `@mixin` on itself of
the `Validator` class.

[1]: a974c0c834
2024-12-18 19:54:44 +01:00
Henrique Moody
2485d54226
Create internal "Reducer" rule
When dealing with rules inside another rule, there are cases in which we
want to validate one or more rules but reduce the number of nested
results.
2024-12-12 18:11:04 +01:00
Henrique Moody
19db9cb07a
Create internal "Binder" rule
There are several cases in which we need to bind the name and template
of a rule to another. We need to do that because results need
information from rules to be created, and because results come from
rules, in some cases, it's not ideal (or possible) to change the
information after the result is created.
2024-12-12 16:20:08 +01:00
Henrique Moody
4a16ad3d09
Allow templates to be callables
There are a few use cases in which you would like to have a custom
exception but, at the same time, reuse the message or exception that
validation might give you.

This commit creates a new feature that allows users to define a callable
that will generate an exception when it fails.
2024-12-11 17:50:47 +01:00
Henrique Moody
e6af762fe4
Rename "Validatable" to "Rule"
Besides the interface's name, everything already calls this type "Rule",
not "Validatable." This commit puts a stone on it and renames the
interface for better naming.
2024-12-05 19:32:14 +01:00
Henrique Moody
d356696af9
Upgrade translation mechanism
Currently, defining translations is quite cumbersome, and the translator
callback is passed to the constructor of multiple classes, which makes
it quite ugly and could make translations inconsistent.

This commit completely changes how translations are done in Validation.
Instead of using a callback, it uses a specific class, and `Validator`
will pass that object through the objects that render the messages.
2024-12-05 15:42:40 +01:00
Henrique Moody
2ae1df177a
Allow to customise messages while asserting
Because we now have a single "assert()" method, we have more freedom to
add more customizations to it. This specific one is handy if someone
wants to use the library to validate but wants to use their own
exceptions.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-03-26 15:04:04 +01:00
Henrique Moody
fefe905e0b
Include "__root__" when getting message as an array
When converting an object into an array, we exclude the message root
message from it. Since we're using a convention to template those
messages as an array, we could also use the same convention to return
those messages.

While working on it, I noticed that the name "__self__" wasn't
reflecting what that really meant, so I renamed it "__root__" because it
better reflects the meaning of those messages/templates.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-03-25 22:38:19 +01:00
Henrique Moody
66faefd695
Remove previous validation engine
After many refactorings, no rules use the previous validation engine.
That means we can remove the unused code from the repository and switch
from the previous to the new validation engine everywhere.

This commit will also soft deprecate the methods "validate()", and
"check()" in all the rules and the "assert()" in all rules but the
Validator itself. That means using those methods will still be allowed,
but static analysis tools might complain.

This is a big step toward releasing the next major version, as the code
is pretty much the way it should be when I release the next version.
There's some documentation to be updated, and I would like to change the
behavior of a couple of rules.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-03-25 12:28:25 +01:00
Henrique Moody
97b243daa1
Allow building rules using prefixes
Although helpful, the changes in the Min, Max, and Length rules made
using those rules more verbose. This commit will simplify their use by
allowing users to use them as prefixes.

Because I was creating prefixes for those rules, I made other cool
prefixes. Doing that is scary because it will generate more code to
support, and I would have liked to avoid that. However, that's a
valuable addition, and it's worth the risk.

I might reconsider that in the future, but for now, that looks like a
good idea.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-03-24 16:58:24 +01:00
Henrique Moody
fdd8c5a978
Remove unused code from the old validation engine
Because we've changed the validation engine, those classes are not
necessary anymore. This is a major step for the new validation engine.

The NestedValidationException was one of the most important classes of
the project, the one that formatted nested messages -- and it was also
one of the buggiest and most complicated ones. With the new validation
engine, it became obsolete.

These changes are a significant step toward refactoring the whole
codebase. The changes in the Factory are there to ensure it doesn't
break for the time being.

Thank you NestedValidationException; you did an excellent job for a long
time. I can't say I will miss you, but seeing you go is weird.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-03-07 00:59:45 +01:00
Henrique Moody
c946f16f60
Move mixin classes to the "Mixin" namespace
Just to make the root directory cleaner.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-03-03 16:52:29 +01:00
Henrique Moody
238f2d506a
Update validation engine
There are a few "problems" with the current engine:

- Allowing each rule to execute assert() and check() means duplication
  in some cases.

- Because we use exceptions to assert/check, we can only invert a
  validation (with Not) if there are errors. That means that we have
  limited granularity control.

- There is a lot of logic in the exceptions. That means that even after
  it throws an exception, something could still happen. We're stable on
  that front, but I want to simplify them. Besides, debugging exception
  code is painful because the stack trace does not go beyond the
  exception.

Apart from that, there are many limitations with templating, and working
that out in the current implementation makes it much harder.

These changes will improve the library in many aspects, but they will
also change the behavior and break backward compatibility. However,
that's a price I'm willing to pay for the improvements we'll have.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-02-22 16:54:44 +01:00
Henrique Moody
7199c5e440
Use convention to idenfity custom templates
With this convention, it's much simpler to identify whether an exception
has a custom template or if that template came from the rule itself.
This commit is a preparation for further changes.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-02-14 18:12:18 +01:00
Henrique Moody
02b70bf1cb
Move Template to the Message namespace
That way, everything related to messages would stay in the same
namespace.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-02-09 19:50:25 +01:00
Henrique Moody
84f1d3296e
Remove "@throws" annotations
I don't find much use for those, and it's hard to be consistent with all
places that could throw an exception.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-01-29 23:51:42 +01:00
Henrique Moody
e4f2c8154a
Use PHP attributes to define templates
Creating a specific exception for each rule adds a painful overhead. If
you want to make a custom message for your rule, you will need to create
an exception and then register that exception namespace to be able to
use it—all that is just for customizing the message of your rule.

Having different namespaces also implies that you need to fetch the
exception of the rule from another directory to change it. As Uncle Bob
said, "Classes that change together belong together. Classes that are
not reused together should not be grouped."

This commit will drastically change this library, moving all the
templates from the exceptions to the rules. Consequently, the Factory
becomes much simpler, and the library gets a bit smaller, too.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-01-29 23:43:57 +01:00
Henrique Moody
9a13c9fb03
Update coding standards
This change will bring many breaking changes. The good thing is that we
can finally use more modern resources available in PHP.

I can imagine that's not a popular change since it will bring many
breaking changes to users, but we shouldn't be stuck in time because of
that. Using some of those features will make it easier to contribute to
the project. At least, I hope so.

There are still some useless doc-blocks, and we're not using "readonly"
properties when we could. I aim to send those changes soon.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2024-01-28 00:22:41 +01:00
Alexandre Gomes Gaigalas
ab3732f91f Use SPDX IDs for licensing
SPDX IDs are shorter than licensing notes previously used, and
adhere better to FOSS standards. It is also machine-readable.
2023-02-19 00:19:10 -03:00
Alexandre Gomes Gaigalas
15f148da24 Dusting off. See CHANGELOG.md for more details on this commit 2023-02-13 03:59:11 -03:00
Henrique Moody
d532e942a5
Create mixin with rules names as methods
One of the reasons for this change is to spare some space on the
Validator class, leaving only real code there. However, the biggest
reason is that IDEs can auto-complete rules after Validator creates the
first rule.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2020-10-04 13:04:20 +02:00
Henrique Moody
72dd88144e
Update version of "respect/coding-standard"
Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2020-08-27 16:21:02 +02:00
Henrique Moody
3145426472
Update version of "respect/coding-standard"
With that update, we will be fully following PSR-12.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2020-07-21 22:54:41 +02:00
Pierre-Antoine Guillaume
4d706b0845
Update docblock from "when" rule
The variable name in dynamic method when does not match variable name in When object.
2020-04-16 17:24:58 +02:00
Henrique Moody
ef8a8f4b27
Turn LICENSE file into plain/text
There is no need for that file to be a Markdown, and it can be a plain
text file.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2019-05-23 16:21:34 +02:00
Henrique Moody
c522f6235c
Remove "IdentityCard" in favor of "PolishIdCard"
The only Identity Card we have is the Polish one, that said it makes
more sense to have a specific rule that only validates that, rather than
having a "IdentityCard" rule that only accepts one value.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2019-05-12 00:22:45 +02:00
Henrique Moody
f23b3a522a
Remove "Vatin" in favor of "Nip"
There are not other VATINs besides the one from Poland. That said, it
makes more sense to have a specific rule that only validates NIP.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2019-05-11 23:44:10 +02:00
Henrique Moody
9c0f8dcfcc
Rename rule "Cntrl" to "Control"
This commit will rename the rule by removing the abbreviation to make it
a bit easier to understand what it does and much easier to find.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2019-05-11 19:35:14 +02:00
Henrique Moody
6040ddee42
Fix the case of the "@inheritDoc" tag
According to the official documentation [1] the correct way of writing
the "inheritDoc" tag is with the uppercase "D".

[1]: https://docs.phpdoc.org/guides/inheritance.html

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2019-04-05 19:02:12 +02:00
Henrique Moody
1f6c821fb6
Refactor "Sorted" rule
The sorted rule accepts a callback on its constructor that may be used
to filter values from inside the input. However, with the "Call" rule
one can archive almost the same result. Besides that particular
characteristic, its constructor accepts a boolean value to determine
whether the sorting is ascending or descending.

This commit will remove the callback from the constructor and replace
the boolean by a string which can be "ASC" or "DESC."

Along with those changes, this change will make a few more improvements:

- Make the exception message specific about the sorting direction;

- Allow the rule to validate also strings;

- Update documentation.

Co-authored-by: Danilo Correa <danilosilva87@gmail.com>
Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2019-03-10 18:31:03 +01:00
Henrique Moody
c60ca7937b
Update the Coding Standards
This commit will add a couple of rules that we already use but also
introduce new ones and make sure all the code is in accordance with the
new coding standards.

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2019-02-17 19:52:26 +01:00
Danilo Correa
9f9b33a36c
Apply contribution guidelines to "NfeAccessKey" rule
Co-authored-by: Henrique Moody <henriquemoody@gmail.com>
2019-02-17 19:21:14 +01:00
Mazen Touati
6d3eb6afed
Create "Iban" rule
The validation procedure is adhering to what is described in Wikipedia.

Link: https://en.wikipedia.org/wiki/International_Bank_Account_Number#Validating_the_IBAN
Co-authored-by: Henrique Moody <henriquemoody@gmail.com>
2019-02-10 12:54:54 +01:00
Henrique Moody
99b912ff87
Apply "SlevomatCodingStandard.ControlStructures.DisallowYodaComparison"
Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2019-02-09 14:19:22 +01:00
Henrique Moody
c30603759e
Apply "SlevomatCodingStandard.TypeHints.TypeHintDeclaration"
Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2019-02-09 14:09:28 +01:00
Henrique Moody
b007284857
Increate PHPStan level to 3
Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
2019-02-04 00:44:08 +01:00
Mazen Touati
9c7550aa43
Create "NotEmoji" rule
The data in this commit has been gathered, scrapped, organized and
treated based on the Emoji Unicode V11 specification [1].

[1]: https://unicode.org/Public/emoji/11.0/emoji-test.txt

Co-authored-by: Henrique Moody <henriquemoody@gmail.com>
2019-02-03 17:51:55 +01:00