11---
2- description : Pre-output checklist, naming, typing, comparisons , and PHPDoc rules for all PHP files in libraries.
2+ description : Pre-output checklist, naming, typing, complexity , and PHPDoc rules for all PHP files in libraries.
33paths :
4- - " src/**/*.php"
5- - " tests/**/*.php"
4+ - " src/**/*.php"
5+ - " tests/**/*.php"
66---
77
88# Code style
99
1010Semantic code rules for all PHP files. Formatting rules (PSR-1, PSR-4, PSR-12, line length) are enforced by ` phpcs.xml `
11- and are not repeated here. Refer to ` rules/domain .md` for domain modeling rules.
11+ and are not repeated here. Refer to ` php-library-modeling .md` for library modeling rules.
1212
1313## Pre-output checklist
1414
@@ -29,19 +29,27 @@ Verify every item before producing any PHP code. If any item fails, revise befor
29298 . No generic identifiers exist. Use domain-specific names instead:
3030 ` $data ` → ` $payload ` , ` $value ` → ` $totalAmount ` , ` $item ` → ` $element ` ,
3131 ` $info ` → ` $currencyDetails ` , ` $result ` → ` $conversionOutcome ` .
32- 9 . No raw arrays exist where a typed collection or value object is available. Use ` tiny-blocks/collection `
33- (` Collection ` , ` Collectible ` ) instead of raw ` array ` for any list of domain objects. Raw arrays are acceptable
34- only for primitive configuration data, variadic pass-through, or interop at system boundaries.
32+ 9 . No raw arrays exist where a typed collection or value object is available. Use the ` tiny-blocks/collection `
33+ fluent API (` Collection ` , ` Collectible ` ) when data is ` Collectible ` . Use ` createLazyFrom ` when elements are
34+ consumed once. Raw arrays are acceptable only for primitive configuration data, variadic pass-through, and
35+ interop at system boundaries. See "Collection usage" below for the full rule and example.
353610 . No private methods exist except private constructors for factory patterns. Inline trivial logic at the call site
3637 or extract it to a collaborator or value object.
373811 . Members are ordered: constants first, then constructor, then static methods, then instance methods. Within each
3839 group, order by body size ascending (number of lines between ` { ` and ` } ` ). Constants and enum cases, which have
3940 no body, are ordered by name length ascending.
404112 . Constructor parameters are ordered by parameter name length ascending (count the name only, without ` $ ` or type),
4142 except when parameters have an implicit semantic order (e.g., ` $start/$end ` , ` $from/$to ` , ` $startAt/$endAt ` ),
42- which takes precedence. The same rule applies to named arguments at call sites.
43+ which takes precedence. Parameters with default values go last, regardless of name length. The same rule
44+ applies to named arguments at call sites.
4345 Example: ` $id ` (2) → ` $value ` (5) → ` $status ` (6) → ` $precision ` (9).
44- 13 . No O(N²) or worse complexity exists.
46+ 13 . Time and space complexity are first-class design concerns.
47+ - No ` O(N²) ` or worse time complexity exists unless the problem inherently requires it and the cost is
48+ documented in PHPDoc on the interface method.
49+ - Space complexity is kept minimal: prefer lazy/streaming pipelines (` createLazyFrom ` ) over materializing
50+ intermediate collections.
51+ - Never re-iterate the same source; fuse stages when possible.
52+ - Public interface methods document time and space complexity in Big O form (see "PHPDoc" section).
455314 . No logic is duplicated across two or more places (DRY).
465415 . No abstraction exists without real duplication or isolation need (KISS).
475516 . All identifiers, comments, and documentation are written in American English.
@@ -59,8 +67,31 @@ Verify every item before producing any PHP code. If any item fails, revise befor
596723 . No vertical alignment of types in parameter lists or property declarations. Use a single space between
6068 type and variable name. Never pad with extra spaces to align columns:
6169 ` public OrderId $id ` — not ` public OrderId $id ` .
62- 24 . Opening brace ` { ` goes on the same line as the closing parenthesis ` ) ` for constructors, methods, and
63- closures: ` ): ReturnType { ` — not ` ): ReturnType\n { ` . Parameters with default values go last.
70+ 24 . Opening brace ` { ` follows PSR-12: on a ** new line** for classes, interfaces, traits, enums, and methods
71+ (including constructors); on the ** same line** for closures and control structures (` if ` , ` for ` , ` foreach ` ,
72+ ` while ` , ` switch ` , ` match ` , ` try ` ).
73+ 25 . Never pass an argument whose value equals the parameter's default. Omit the argument entirely.
74+ Example — ` toArray(KeyPreservation $keyPreservation = KeyPreservation::PRESERVE) ` :
75+ ` $collection->toArray(keyPreservation: KeyPreservation::PRESERVE) ` → ` $collection->toArray() ` .
76+ Only pass the argument when the value differs from the default.
77+ 26 . No trailing comma in any multi-line list. This applies to parameter lists (constructors, methods,
78+ closures), argument lists at call sites, array literals, match arms, and any other comma-separated
79+ multi-line structure. The last element never has a comma after it. PHP accepts trailing commas in
80+ parameter lists, but this project prohibits them for visual consistency.
81+ Example — correct:
82+ ```
83+ new Precision(
84+ value: 2,
85+ rounding: RoundingMode::HALF_UP
86+ );
87+ ```
88+ Example — prohibited:
89+ ```
90+ new Precision(
91+ value: 2,
92+ rounding: RoundingMode::HALF_UP,
93+ );
94+ ```
6495
6596## Casing conventions
6697
@@ -70,9 +101,7 @@ Verify every item before producing any PHP code. If any item fails, revise befor
70101## Naming
71102
72103- Names describe **what** in domain terms, not **how** technically: `$monthlyRevenue` instead of `$calculatedValue`.
73- - Generic technical verbs (` process ` , ` handle ` , ` execute ` , ` mark ` , ` enforce ` , ` manage ` , ` ensure ` , ` validate ` ,
74- ` check ` , ` verify ` , ` assert ` , ` transform ` , ` parse ` , ` compute ` , ` sanitize ` , ` normalize ` ) ** should be avoided** .
75- Prefer names that describe the domain operation.
104+ - Generic technical verbs are avoided. See `php-library-modeling.md` — Nomenclature.
76105- Booleans use predicate form: `isActive`, `hasPermission`, `wasProcessed`.
77106- Collections are always plural: `$orders`, `$lines`.
78107- Methods returning bool use prefixes: `is`, `has`, `can`, `was`, `should`.
@@ -93,12 +122,19 @@ All identifiers, enum values, comments, and error codes use American English spe
93122
94123## PHPDoc
95124
96- - PHPDoc is restricted to interfaces only, documenting obligations and ` @throws ` .
125+ - PHPDoc is restricted to interfaces only, documenting obligations, `@throws`, and complexity .
97126- Never add PHPDoc to concrete classes.
127+ - Document `@throws` for every exception the method may raise.
128+ - Document time and space complexity in Big O form. When a method participates in a fused pipeline (e.g., collection
129+ pipelines), express cost as a two-part form: call-site cost + fused-pass contribution. Include a legend defining
130+ variables (e.g., `N` for input size, `K` for number of stages).
98131
99132## Collection usage
100133
101- When a property or parameter is ` Collectible ` , use its fluent API. Never break out to raw array functions.
134+ When a property or parameter is `Collectible`, use its fluent API. Never break out to raw array functions such as
135+ `array_map`, `array_filter`, `iterator_to_array`, or `foreach` + accumulation. The same applies to `filter()`,
136+ `reduce()`, `each()`, and all other `Collectible` operations. Chain them fluently. Never materialize with
137+ `iterator_to_array` to then pass into a raw `array_*` function.
102138
103139**Prohibited — `array_map` + `iterator_to_array` on a Collectible:**
104140
@@ -116,6 +152,3 @@ $names = $collection
116152 ->map(transformations: static fn(Element $element): string => $element->name())
117153 ->toArray(keyPreservation: KeyPreservation::DISCARD);
118154```
119-
120- The same applies to ` filter() ` , ` reduce() ` , ` each() ` , and all other ` Collectible ` operations. Chain them
121- fluently. Never materialize with ` iterator_to_array ` to then pass into a raw ` array_* ` function.
0 commit comments