Skip to content

Commit 7324231

Browse files
author
Florian Krämer
committed
Enhance documentation and introduce new rules for architectural constraints
- Updated README.md to mark the Dependency Constraints Rule as deprecated and introduced the new Forbidden Dependencies Rule, which enforces dependency constraints between namespaces. - Added detailed documentation for the Forbidden Dependencies Rule and the Forbidden Static Methods Rule, outlining their configurations and use cases. - Updated Rules.md to include descriptions and examples for the new rules, ensuring clarity on their functionalities. These changes improve the clarity of the rules and provide guidance for users on enforcing architectural boundaries in their code.
1 parent 390c96a commit 7324231

4 files changed

Lines changed: 314 additions & 1 deletion

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@ See individual rule documentation for detailed configuration examples. A [full c
2525
- [Class Must Be Readonly Rule](docs/rules/Class-Must-Be-Readonly-Rule.md)
2626
- [Class Must Have Specification Docblock Rule](docs/rules/Class-Must-Have-Specification-Docblock-Rule.md)
2727
- [Classname Must Match Pattern Rule](docs/rules/Classname-Must-Match-Pattern-Rule.md)
28-
- [Dependency Constraints Rule](docs/rules/Dependency-Constraints-Rule.md)
28+
- [Dependency Constraints Rule](docs/rules/Dependency-Constraints-Rule.md) *(deprecated, use Forbidden Dependencies Rule)*
2929
- [Forbidden Accessors Rule](docs/rules/Forbidden-Accessors-Rule.md)
30+
- [Forbidden Dependencies Rule](docs/rules/Forbidden-Dependencies-Rule.md)
3031
- [Forbidden Namespaces Rule](docs/rules/Forbidden-Namespaces-Rule.md)
32+
- [Forbidden Static Methods Rule](docs/rules/Forbidden-Static-Methods-Rule.md)
3133
- [Method Must Return Type Rule](docs/rules/Method-Must-Return-Type-Rule.md)
3234
- [Method Signature Must Match Rule](docs/rules/Method-Signature-Must-Match-Rule.md)
3335
- [Methods Returning Bool Must Follow Naming Convention Rule](docs/rules/Methods-Returning-Bool-Must-Follow-Naming-Convention-Rule.md)

docs/Rules.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@ Forbids public and/or protected getters and setters on classes matching specifie
2222

2323
See [Forbidden Accessors Rule documentation](rules/Forbidden-Accessors-Rule.md) for detailed information.
2424

25+
## Forbidden Dependencies Rule
26+
27+
Enforces dependency constraints between namespaces by checking `use` statements and optionally fully qualified class names (FQCNs). This rule prevents classes in one namespace from depending on classes in another, helping enforce architectural boundaries like layer separation.
28+
29+
See [Forbidden Dependencies Rule documentation](rules/Forbidden-Dependencies-Rule.md) for detailed information.
30+
31+
## Forbidden Static Methods Rule
32+
33+
Forbids specific static method calls matching regex patterns. Supports namespace-level, class-level, and method-level granularity. The rule resolves `self`, `static`, and `parent` keywords to actual class names.
34+
35+
See [Forbidden Static Methods Rule documentation](rules/Forbidden-Static-Methods-Rule.md) for detailed information.
36+
2537
## Property Must Match Rule
2638

2739
Ensures that classes matching specified patterns have properties with expected names, types, and visibility scopes. Can optionally enforce that matching classes must have certain properties.
@@ -224,6 +236,16 @@ services:
224236
tags:
225237
- phpstan.rules.rule
226238

239+
# Forbid specific static method calls
240+
-
241+
class: Phauthentic\PHPStanRules\Architecture\ForbiddenStaticMethodsRule
242+
arguments:
243+
forbiddenStaticMethods:
244+
- '/^App\\Legacy\\.*::.*/'
245+
- '/^DateTime::createFromFormat$/'
246+
tags:
247+
- phpstan.rules.rule
248+
227249
# Forbid accessors on domain entities
228250
-
229251
class: Phauthentic\PHPStanRules\Architecture\ForbiddenAccessorsRule
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# Forbidden Dependencies Rule
2+
3+
Enforces dependency constraints between namespaces by checking `use` statements and optionally fully qualified class names (FQCNs). This rule prevents classes in one namespace from depending on classes in another, helping enforce architectural boundaries like layer separation.
4+
5+
> **Note:** This rule replaces the deprecated `DependencyConstraintsRule`. See [Dependency Constraints Rule](Dependency-Constraints-Rule.md) for migration details.
6+
7+
## Configuration Example
8+
9+
### Basic Usage (Use Statements Only)
10+
11+
```neon
12+
-
13+
class: Phauthentic\PHPStanRules\Architecture\ForbiddenDependenciesRule
14+
arguments:
15+
forbiddenDependencies: [
16+
'/^App\\Domain(?:\\\w+)*$/': ['/^App\\Controller\\/']
17+
]
18+
tags:
19+
- phpstan.rules.rule
20+
```
21+
22+
### With FQCN Checking Enabled
23+
24+
```neon
25+
-
26+
class: Phauthentic\PHPStanRules\Architecture\ForbiddenDependenciesRule
27+
arguments:
28+
forbiddenDependencies: [
29+
'/^App\\Capability(?:\\\w+)*$/': [
30+
'/^DateTime$/',
31+
'/^DateTimeImmutable$/'
32+
]
33+
]
34+
checkFqcn: true
35+
tags:
36+
- phpstan.rules.rule
37+
```
38+
39+
### With Selective Reference Types
40+
41+
```neon
42+
-
43+
class: Phauthentic\PHPStanRules\Architecture\ForbiddenDependenciesRule
44+
arguments:
45+
forbiddenDependencies: [
46+
'/^App\\Capability(?:\\\w+)*$/': [
47+
'/^DateTime$/',
48+
'/^DateTimeImmutable$/'
49+
]
50+
]
51+
checkFqcn: true
52+
fqcnReferenceTypes: ['new', 'param', 'return', 'property']
53+
tags:
54+
- phpstan.rules.rule
55+
```
56+
57+
## Parameters
58+
59+
- `forbiddenDependencies`: Array where keys are regex patterns for source namespaces and values are arrays of regex patterns for disallowed dependency namespaces.
60+
- `checkFqcn` (optional, default: `false`): Enable checking of fully qualified class names in addition to use statements.
61+
- `fqcnReferenceTypes` (optional, default: all types): Array of reference types to check when `checkFqcn` is enabled.
62+
- `allowedDependencies` (optional, default: `[]`): Whitelist that overrides forbidden dependencies. If a dependency matches both a forbidden pattern and an allowed pattern, it will be allowed.
63+
64+
## FQCN Reference Types
65+
66+
When `checkFqcn` is enabled, the following reference types can be checked:
67+
68+
- `new` - Class instantiations (e.g., `new \DateTime()`)
69+
- `param` - Parameter type hints (e.g., `function foo(\DateTime $date)`)
70+
- `return` - Return type hints (e.g., `function foo(): \DateTime`)
71+
- `property` - Property type hints (e.g., `private \DateTime $date`)
72+
- `static_call` - Static method calls (e.g., `\DateTime::createFromFormat()`)
73+
- `static_property` - Static property access (e.g., `\DateTime::ATOM`)
74+
- `class_const` - Class constant (e.g., `\DateTime::class`)
75+
- `instanceof` - instanceof checks (e.g., `$x instanceof \DateTime`)
76+
- `catch` - catch blocks (e.g., `catch (\Exception $e)`)
77+
- `extends` - class inheritance (e.g., `class Foo extends \DateTime`)
78+
- `implements` - interface implementation (e.g., `class Foo implements \DateTimeInterface`)
79+
80+
## Use Cases
81+
82+
### Enforcing Layer Boundaries
83+
84+
Prevent domain classes from depending on infrastructure or presentation layers:
85+
86+
```neon
87+
-
88+
class: Phauthentic\PHPStanRules\Architecture\ForbiddenDependenciesRule
89+
arguments:
90+
forbiddenDependencies:
91+
'/^App\\Capability\\.*\\Domain/':
92+
- '/^App\\Capability\\.*\\Application/'
93+
- '/^App\\Capability\\.*\\Infrastructure/'
94+
- '/^App\\Capability\\.*\\Presentation/'
95+
'/^App\\Capability\\.*\\Application/':
96+
- '/^App\\Capability\\.*\\Infrastructure/'
97+
- '/^App\\Capability\\.*\\Presentation/'
98+
tags:
99+
- phpstan.rules.rule
100+
```
101+
102+
### Preventing DateTime Usage in Domain Layer
103+
104+
Encourage the use of domain-specific date/time objects instead of PHP's built-in classes:
105+
106+
```neon
107+
-
108+
class: Phauthentic\PHPStanRules\Architecture\ForbiddenDependenciesRule
109+
arguments:
110+
forbiddenDependencies: [
111+
'/^App\\Capability(?:\\\w+)*$/': [
112+
'/^DateTime$/',
113+
'/^DateTimeImmutable$/'
114+
]
115+
]
116+
checkFqcn: true
117+
tags:
118+
- phpstan.rules.rule
119+
```
120+
121+
This will catch:
122+
123+
- `use DateTime;` (use statement)
124+
- `new \DateTime()` (instantiation)
125+
- `function foo(\DateTime $date)` (parameter type)
126+
- `function bar(): \DateTime` (return type)
127+
- `private \DateTime $date` (property type)
128+
- And all other reference types listed above
129+
130+
### Whitelist with allowedDependencies
131+
132+
The `allowedDependencies` parameter lets you create a "forbid everything except X" pattern. Dependencies matching both forbidden and allowed patterns will be allowed.
133+
134+
```neon
135+
-
136+
class: Phauthentic\PHPStanRules\Architecture\ForbiddenDependenciesRule
137+
arguments:
138+
forbiddenDependencies: [
139+
'/^App\\Capability\\.*\\Domain$/': [
140+
'/.*\\\\.*/'
141+
]
142+
]
143+
checkFqcn: true
144+
allowedDependencies: [
145+
'/^App\\Capability\\.*\\Domain$/': [
146+
'/^App\\Shared\\/',
147+
'/^App\\Capability\\/',
148+
'/^Psr\\/'
149+
]
150+
]
151+
tags:
152+
- phpstan.rules.rule
153+
```
154+
155+
This will:
156+
157+
- **Allow**: `App\Shared\ValueObject\Money`, `App\Capability\Billing\Invoice`, `Psr\Log\LoggerInterface`
158+
- **Forbid**: `Doctrine\ORM\EntityManager`, `Symfony\Component\HttpFoundation\Request`
159+
160+
## Diagram
161+
162+
```mermaid
163+
flowchart TD
164+
A[Check Dependency] --> B{Matches forbidden pattern?}
165+
B -->|No| C[Allow]
166+
B -->|Yes| D{Matches allowed pattern?}
167+
D -->|Yes| E[Allow - Override]
168+
D -->|No| F[Report Error]
169+
```
170+
171+
## Backward Compatibility
172+
173+
By default, `checkFqcn` is `false` and `allowedDependencies` is empty, so existing configurations will continue to work exactly as before, checking only `use` statements. The new FQCN checking and allowedDependencies features must be explicitly enabled.
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Forbidden Static Methods Rule
2+
3+
Forbids specific static method calls matching regex patterns. This rule checks static method calls against a configurable list of forbidden patterns and supports namespace-level, class-level, and method-level granularity.
4+
5+
The rule resolves `self`, `static`, and `parent` keywords to the actual class name before matching, so forbidden patterns work correctly even when these keywords are used.
6+
7+
## Configuration Example
8+
9+
```neon
10+
-
11+
class: Phauthentic\PHPStanRules\Architecture\ForbiddenStaticMethodsRule
12+
arguments:
13+
forbiddenStaticMethods:
14+
- '/^App\\Legacy\\.*::.*/'
15+
- '/^App\\Utils\\StaticHelper::.*/'
16+
- '/^DateTime::createFromFormat$/'
17+
tags:
18+
- phpstan.rules.rule
19+
```
20+
21+
## Parameters
22+
23+
- `forbiddenStaticMethods`: Array of regex patterns to match against static method calls. Patterns are matched against the format `FQCN::methodName`.
24+
25+
## Pattern Granularity
26+
27+
Patterns match against the fully qualified class name followed by `::` and the method name. This allows you to forbid static calls at different levels of granularity:
28+
29+
### Namespace-level
30+
31+
Forbid all static calls to any class in a namespace:
32+
33+
```neon
34+
forbiddenStaticMethods:
35+
- '/^App\\Legacy\\.*::.*/'
36+
```
37+
38+
This forbids calls like `App\Legacy\LegacyHelper::doSomething()` and `App\Legacy\OldService::run()`.
39+
40+
### Class-level
41+
42+
Forbid all static calls on a specific class:
43+
44+
```neon
45+
forbiddenStaticMethods:
46+
- '/^App\\Utils\\StaticHelper::.*/'
47+
```
48+
49+
This forbids all static method calls on `App\Utils\StaticHelper`, regardless of the method name.
50+
51+
### Method-level
52+
53+
Forbid a specific static method on a specific class:
54+
55+
```neon
56+
forbiddenStaticMethods:
57+
- '/^DateTime::createFromFormat$/'
58+
```
59+
60+
This forbids only `DateTime::createFromFormat()` while allowing other static methods like `DateTime::getLastErrors()`.
61+
62+
## Use Cases
63+
64+
### Forbid Legacy Static Helpers
65+
66+
Prevent usage of legacy static helper classes to encourage dependency injection:
67+
68+
```neon
69+
-
70+
class: Phauthentic\PHPStanRules\Architecture\ForbiddenStaticMethodsRule
71+
arguments:
72+
forbiddenStaticMethods:
73+
- '/^App\\Legacy\\.*::.*/'
74+
- '/^App\\Helpers\\.*::.*/'
75+
tags:
76+
- phpstan.rules.rule
77+
```
78+
79+
### Forbid Specific Factory Methods
80+
81+
Forbid using static factory methods on certain classes while allowing other static methods:
82+
83+
```neon
84+
-
85+
class: Phauthentic\PHPStanRules\Architecture\ForbiddenStaticMethodsRule
86+
arguments:
87+
forbiddenStaticMethods:
88+
- '/^DateTime::createFromFormat$/'
89+
- '/^DateTime::createFromTimestamp$/'
90+
tags:
91+
- phpstan.rules.rule
92+
```
93+
94+
### Forbid All Static Calls in Domain Layer
95+
96+
Combine with a broad pattern to forbid all static calls from specific namespaces:
97+
98+
```neon
99+
-
100+
class: Phauthentic\PHPStanRules\Architecture\ForbiddenStaticMethodsRule
101+
arguments:
102+
forbiddenStaticMethods:
103+
- '/^Illuminate\\Support\\Facades\\.*::.*/'
104+
tags:
105+
- phpstan.rules.rule
106+
```
107+
108+
## Handling of self, static, and parent
109+
110+
The rule resolves the keywords `self`, `static`, and `parent` to the actual fully qualified class name before matching against the forbidden patterns. This means:
111+
112+
- `self::create()` inside `App\Service\MyService` is matched as `App\Service\MyService::create`
113+
- `static::create()` inside `App\Service\MyService` is matched as `App\Service\MyService::create`
114+
- `parent::create()` inside a child class is matched against the parent class name
115+
116+
Dynamic class names (e.g., `$class::method()`) and dynamic method names (e.g., `DateTime::$method()`) are skipped.

0 commit comments

Comments
 (0)