Skip to content

Add #[Generate] and the refined string and array shape types - #164

Merged
rasuvaeff merged 4 commits into
masterfrom
feat/generate-attribute-and-refined-types
Oct 2, 2026
Merged

rasuvaeff merged 4 commits into
masterfrom
feat/generate-attribute-and-refined-types

Conversation

@rasuvaeff

Copy link
Copy Markdown
Owner

Fixes #156
Fixes #160

#160 — #[Generate]

  • Rasuvaeff\PropertyTesting\Generate (@api, TARGET_PARAMETER, not repeatable, no constructor defaults). It takes an arbitrary built with new, or a reference to a static factory returning one: 'method' (declaring class first), 'Class::method', [Class::class, 'method'], or an invokable. The reference covers everything an attribute's constant expression cannot hold on PHP 8.3/8.4 (map, filter, flatMap, composite, email, url, json, regex, recursive).
  • Read by ParameterGenerators::forSignature() in this order: override, then the attribute, then @param, then the native type. Both Gen::forParameters() and Gen::forClass() use it. The attribute is exact: it wins over a disagreeing or unreadable docblock, a ?T parameter is not wrapped in nullable, and on a variadic it counts as the override.
  • Gen::forParameters(..., derive: false) reads only overrides and attributes, and refuses a parameter that has neither. The adapters call this without auto (follow-up PRs: Read #[Generate] on property parameters, with and without auto property-testing-testo#68, Read #[Generate] on closure parameters, with and without auto() property-testing-phpunit#61).
  • Every broken form is a refusal that names the parameter: rejected constructor arguments, a repeated attribute, a non-static method, a non-callable, a factory that returns a non-arbitrary or throws.
  • Parameter refusals now end with pass an override or #[Generate]. Both adapters assert only the pass an override substring.

#156 — refined strings and shapes

What the issue listed did not match the code. non-empty-list/non-empty-array and keyed array<K, V> already worked. Array shapes were not supported at all, which is a bigger gap than the "optional keys" the issue mentions. Also, 0x1A is not numeric in PHP 8.

  • Added numeric-string, non-falsy-string/truthy-string, lowercase-string and non-empty-lowercase-string.
  • Added shapes: array{a: T, b?: U}, quoted and integer keys, positional array{T, U} / list{T, U}, array{}, and shapes nested in collections and unions. An optional key is sometimes absent and shrinks towards absent; absent is not null. Unsealed shapes, double-quoted keys and keyed list{} are still refused.
  • A @param written across several lines is now read. Without this, a shape in its usual multi-line form was refused.
  • I kept the hand-written parser instead of adding phpstan/phpdoc-parser. The work is in the generators, not the parsing, and a dependency risks constraint conflicts for consumers.

Also

  • RedisDsn.php: rector (RemoveAlwaysTrueIfConditionRector) was already red on master, which blocked release-check.
  • Path-version pins in the adapter contract job and docs/.api-workspace are raised to 1.2.0. The adapters' current ^1.0 accepts it, so the job stays green before and after they move to ^1.2.
  • Docs: both READMEs, llms.txt, SKILL.md, the from-a-class guide page and the regenerated API pages are updated.

Verification

  • composer build: green.
  • rector: green.
  • roave: the same 5 SKIPPED enum-default entries as v1.1.0, nothing new.
  • Mutation: MSI 97% (gate 97), no escaped mutants in the changed files.
  • Docs build: integrity and anchor checks pass.
  • Both adapter suites run green against this checkout. Their branches pass validate, cs, psalm, tests, require-checker and rector against core 1.2.0.

#[Generate] puts a parameter's generator on the parameter itself: an
arbitrary built with `new`, or a reference to a static factory returning
one. Gen::forParameters() and Gen::forClass() read it after an override and
before the docblock, so both adapters' signature-driven modes get it from
one change. Gen::forParameters(derive: false) reads only overrides and the
attribute — the engine half of an adapter's mode without auto.

The docblock reader now understands numeric-string, non-falsy-string,
lowercase-string, non-empty-lowercase-string and array shapes with optional,
quoted, integer and positional keys, and reads a @PARAM written across lines.

Fixes #156
Fixes #160
@coderabbitai

coderabbitai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 95c08b80-09f4-4701-bbb5-c85bbb174e82

  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@rasuvaeff
rasuvaeff merged commit 2b5fda1 into master Oct 2, 2026
19 checks passed
@rasuvaeff
rasuvaeff deleted the feat/generate-attribute-and-refined-types branch October 3, 2026 16:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant