Repository navigation
Speed up validation of large wildcard payloads - #21
Merged
Merged
Conversation
Validating a product with 180 variants (5,956 keys after wildcard expansion) took ~950 ms in the CLI and ~2.6 s with Xdebug on. It now takes ~58 ms and ~88 ms. Three causes, all fixed: - RuleParser parsed every expanded wildcard key separately. Each rule entry is now parsed once; later keys get shallow clones, so each key still owns its instances (parse: 577 ms -> 17 ms). - Validator re-ran every rule: validate() ran passes() twice, and a repeat passes()/fails() duplicated errors. The outcome is now stored and reused; set_value() and sometimes() (when it adds rules) reset it (validated() after passes(): 198 ms -> 27 ms). - Every rule check re-resolved default messages for all of the rule's constraints, each lookup rebuilding the MessagesBag table. MessagesBag::get() now reads the table built once, and sync_custom_messages() skips the work when no custom messages are in play (passes: 177 ms -> 14 ms). Also fixes MessagesBag merging config/messages.php shallowly: overriding one validator message wiped every other default in the group. It now merges recursively. Adds tests for wildcard parsing, run lifecycle, custom messages and the messages bag; a public GET /validation-stress route in example/ to exercise the validator; and the OpenSpec specs for validation-rule-parsing, validation-run-lifecycle and validation-messages. Timings: CLI, PHP 8.4, Xdebug off unless stated, mean of 2 runs, main measured at 15f25bd. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ahamed
force-pushed
the
feature/validator-performance
branch
from
November 13, 2026 11:21
0ed3973 to
0ba675f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Validating a product with 180 variants (5,956 keys after wildcard expansion, the
example/stress payload) drops from ~950 ms to ~58 ms in the CLI, and from ~2.5 s to ~88 ms with Xdebug on. Three hot paths were fixed:set_value()andsometimes()(when it adds rules) clear the stored outcome, so the next call validates again.MessagesBagnow mergesconfig/messages.phprecursively. Overridingvalidator.required.defaultused to wipe out every other validator default.example/gets a publicGET /wp-json/framework/v1/validation-stress?variants=180[&invalid=1]route for exercising the validator end to end.validation-rule-parsing,validation-run-lifecycle,validation-messages. The two OpenSpec changes are archived.Evidence
Stress payload (180 variants, 5,956 keys), CLI, PHP 8.4.
mainwas measured in a worktree at15f25bd; each figure is the mean of 2 runs:main, Xdebug offmain, Xdebug onValidator::make)passes()validated()afterpasses()On
main,validated()runs every rule again; on this branch it only builds the result. The playground web request (1,238 ms inpasses()before) has not been re-measured.Tests that failed before and pass now:
Tests that already passed were added to lock in the existing message and parsing behaviour: custom messages (flat, nested, placeholder, closure, one wildcard key, another field, rule reused across validators),
Rule::whenper validator, closures per key, and per-key errors.composer test:unit: OK (980 tests, 2314 assertions).composer phpcs: 0 errors. The 3 line-length warnings it reports were already there (TemplateEngine.php,ValidationRule.php).Merge Danger
Door: two-way
No public signatures change, and reverting the commit restores the old behaviour.
Blast Radius: validation
What consumers could notice:
get_rules()returns separate (cloned) instances for each expanded key. A rule object passed for a wildcard key used to be shared by all keys.passes()/fails()returns the stored outcome. Code that writes$this->dataor$this->rulesdirectly in aValidatorsubclass must callreset_outcome().$this->name,$this->valueor$this->datamust overridesync_custom_messages(). No rule insrc/does this.🤖 Generated with Claude Code