Skip to content

Commit a72f6b2

Browse files
authored
You can now allow some items in instanceof listed classes (#306)
Use `allowInInstanceOf`, named after the PHP's `instanceof` operator, if you want to allow a function or a method call, an attribute, a classname, or a namespace in - a class of given name - a class that inherits from a class of given name - a class that implements given interface In case of a namespace, you may also use `allowInUse`, because if you allow a namespace/class `A` in a class `B` using `allowInInstanceOf`, you will also get error on line with `use A;` even if you will not get any error when `A` is used in `B`. Close #302
2 parents bb164b0 + aa05d4d commit a72f6b2

21 files changed

+455
-3
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ Let's say you have disallowed `foo()` with custom rules. But you want to re-allo
5454
- [Allow in methods or functions](docs/allow-in-methods.md)
5555
- [Allow with specified parameters](docs/allow-with-parameters.md)
5656
- [Allow with specified flags](docs/allow-with-flags.md)
57+
- [Allow in classes, child classes, classes implementing an interface](docs/allow-in-instance-of.md) (same as the `instanceof` operator)
5758
- [Allow in class with given attributes](docs/allow-in-class-with-attributes.md)
5859
- [Allow in methods or functions with given attributes](docs/allow-in-methods.md)
5960
- [Allow in class with given attributes on any method](docs/allow-in-class-with-method-attributes.md)

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
},
4242
"scripts": {
4343
"lint": "vendor/bin/parallel-lint --colors src/ tests/",
44-
"lint-7.x": "vendor/bin/parallel-lint --colors src/ tests/ --exclude tests/src/TypesEverywhere.php --exclude tests/src/AttributesEverywhere.php --exclude tests/src/disallowed/functionCallsNamedParams.php --exclude tests/src/disallowed-allow/functionCallsNamedParams.php --exclude tests/src/disallowed/attributeUsages.php --exclude tests/src/disallowed-allow/attributeUsages.php --exclude tests/src/disallowed/constantDynamicUsages.php --exclude tests/src/disallowed-allow/constantDynamicUsages.php --exclude tests/src/Enums.php --exclude tests/src/disallowed/controlStructures.php --exclude tests/src/disallowed-allow/controlStructures.php --exclude tests/src/disallowed/firstClassCallable.php --exclude tests/src/disallowed-allow/firstClassCallable.php --exclude tests/src/disallowed/callableParameters.php --exclude tests/src/disallowed-allow/callableParameters.php",
44+
"lint-7.x": "vendor/bin/parallel-lint --colors src/ tests/ --exclude tests/src/TypesEverywhere.php --exclude tests/src/AttributesEverywhere.php --exclude tests/src/disallowed/functionCallsNamedParams.php --exclude tests/src/disallowed-allow/functionCallsNamedParams.php --exclude tests/src/disallowed/attributeUsages.php --exclude tests/src/disallowed-allow/attributeUsages.php --exclude tests/src/disallowed/constantDynamicUsages.php --exclude tests/src/disallowed-allow/constantDynamicUsages.php --exclude tests/src/Bar.php --exclude tests/src/Enums.php --exclude tests/src/disallowed/controlStructures.php --exclude tests/src/disallowed-allow/controlStructures.php --exclude tests/src/disallowed/firstClassCallable.php --exclude tests/src/disallowed-allow/firstClassCallable.php --exclude tests/src/disallowed/callableParameters.php --exclude tests/src/disallowed-allow/callableParameters.php",
4545
"lint-8.0": "vendor/bin/parallel-lint --colors src/ tests/ --exclude tests/src/TypesEverywhere.php --exclude tests/src/AttributesEverywhere.php --exclude tests/src/disallowed/constantDynamicUsages.php --exclude tests/src/disallowed-allow/constantDynamicUsages.php --exclude tests/src/Enums.php --exclude tests/src/disallowed/firstClassCallable.php --exclude tests/src/disallowed-allow/firstClassCallable.php",
4646
"lint-8.1": "vendor/bin/parallel-lint --colors src/ tests/ --exclude tests/src/AttributesEverywhere.php --exclude tests/src/disallowed/constantDynamicUsages.php --exclude tests/src/disallowed-allow/constantDynamicUsages.php --exclude tests/src/disallowed/firstClassCallable.php --exclude tests/src/disallowed-allow/firstClassCallable.php",
4747
"lint-8.2": "vendor/bin/parallel-lint --colors src/ tests/ --exclude tests/src/disallowed/constantDynamicUsages.php --exclude tests/src/disallowed-allow/constantDynamicUsages.php",

docs/allow-in-instance-of.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
## Allow in classes, child classes, classes implementing an interface
2+
3+
Use `allowInInstanceOf`, named after the PHP's `instanceof` operator, if you want to allow a function or a method call, an attribute, a classname, or a namespace in
4+
- a class of given name
5+
- a class that inherits from a class of given name
6+
- a class that implements given interface
7+
8+
This is useful for example when you want to allow properties or parameters of class `ClassName` in all classes that extend `ParentClass`:
9+
10+
```neon
11+
parameters:
12+
disallowedClasses:
13+
-
14+
class: 'ClassName'
15+
allowInInstanceOf:
16+
- 'ParentClass'
17+
```
18+
Another example could be if you'd like to disallow a `function()` in all classes that implement the `MyInterface` interface.
19+
You can use the `allowExceptInInstanceOf` counterpart (or the `disallowInInstanceOf` alias) for that, like this:
20+
21+
```neon
22+
parameters:
23+
disallowedFunctionCalls:
24+
-
25+
function: 'function()'
26+
disallowInInstanceOf:
27+
- 'MyInterface'
28+
```
29+
30+
### Allow in `use` imports
31+
The `allowInInstanceOf` configuration above will also report an error on the line with the import, if present:
32+
```php
33+
use ClassName;
34+
```
35+
To omit the `use` finding, you can add the `allowInUse` line, like this:
36+
37+
```neon
38+
parameters:
39+
disallowedClasses:
40+
-
41+
class: 'ClassName'
42+
allowInInstanceOf:
43+
- 'ParentClass'
44+
allowInUse: true
45+
```

extension.neon

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ parametersSchema:
2424
?allowIn: listOf(string()),
2525
?allowExceptIn: listOf(string()),
2626
?disallowIn: listOf(string()),
27+
?allowInInstanceOf: listOf(string()),
28+
?allowExceptInInstanceOf: listOf(string()),
29+
?disallowInInstanceOf: listOf(string()),
2730
?allowInClassWithAttributes: listOf(string()),
2831
?allowExceptInClassWithAttributes: listOf(string()),
2932
?disallowInClassWithAttributes: listOf(string()),
@@ -50,6 +53,9 @@ parametersSchema:
5053
?allowIn: listOf(string()),
5154
?allowExceptIn: listOf(string()),
5255
?disallowIn: listOf(string()),
56+
?allowInInstanceOf: listOf(string()),
57+
?allowExceptInInstanceOf: listOf(string()),
58+
?disallowInInstanceOf: listOf(string()),
5359
?allowInClassWithAttributes: listOf(string()),
5460
?allowExceptInClassWithAttributes: listOf(string()),
5561
?disallowInClassWithAttributes: listOf(string()),
@@ -83,6 +89,9 @@ parametersSchema:
8389
?allowExceptInMethods: listOf(string()),
8490
?disallowInFunctions: listOf(string()),
8591
?disallowInMethods: listOf(string()),
92+
?allowInInstanceOf: listOf(string()),
93+
?allowExceptInInstanceOf: listOf(string()),
94+
?disallowInInstanceOf: listOf(string()),
8695
?allowParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())),
8796
?allowParamsInAllowedAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?name: string()])), anyOf(int(), string())),
8897
?allowParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())),
@@ -133,6 +142,9 @@ parametersSchema:
133142
?allowExceptInMethods: listOf(string()),
134143
?disallowInFunctions: listOf(string()),
135144
?disallowInMethods: listOf(string()),
145+
?allowInInstanceOf: listOf(string()),
146+
?allowExceptInInstanceOf: listOf(string()),
147+
?disallowInInstanceOf: listOf(string()),
136148
?allowParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())),
137149
?allowParamsInAllowedAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?name: string()])), anyOf(int(), string())),
138150
?allowParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())),
@@ -183,6 +195,9 @@ parametersSchema:
183195
?allowExceptInMethods: listOf(string()),
184196
?disallowInFunctions: listOf(string()),
185197
?disallowInMethods: listOf(string()),
198+
?allowInInstanceOf: listOf(string()),
199+
?allowExceptInInstanceOf: listOf(string()),
200+
?disallowInInstanceOf: listOf(string()),
186201
?allowParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())),
187202
?allowParamsInAllowedAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?name: string()])), anyOf(int(), string())),
188203
?allowParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())),
@@ -265,6 +280,9 @@ parametersSchema:
265280
?allowExceptInMethods: listOf(string()),
266281
?disallowInFunctions: listOf(string()),
267282
?disallowInMethods: listOf(string()),
283+
?allowInInstanceOf: listOf(string()),
284+
?allowExceptInInstanceOf: listOf(string()),
285+
?disallowInInstanceOf: listOf(string()),
268286
?allowParamsInAllowed: arrayOf(anyOf(int(), string(), bool(), structure([position: int(), ?value: anyOf(int(), string(), bool()), ?typeString: string(), ?name: string()])), anyOf(int(), string())),
269287
?allowParamsInAllowedAnyValue: arrayOf(anyOf(int(), structure([position: int(), ?name: string()])), anyOf(int(), string())),
270288
?allowParamFlagsInAllowed: arrayOf(anyOf(int(), structure([position: int(), ?value: int(), ?typeString: string(), ?name: string()])), anyOf(int(), string())),

phpstan.neon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ parameters:
88
CallParamFlagAnyValueConfig: 'array<int|string, int|array{position:int, value?:int, name?:string}>'
99
AllowParamDirectives: 'allowParamsInAllowed?:CallParamConfig, allowParamsInAllowedAnyValue?:CallParamAnyValueConfig, allowParamFlagsInAllowed?:CallParamFlagAnyValueConfig, allowParamsAnywhere?:CallParamConfig, allowParamsAnywhereAnyValue?:CallParamAnyValueConfig, allowParamFlagsAnywhere?:CallParamFlagAnyValueConfig, allowExceptParamsInAllowed?:CallParamConfig, allowExceptParamFlagsInAllowed?:CallParamFlagAnyValueConfig, disallowParamFlagsInAllowed?:CallParamFlagAnyValueConfig, disallowParamsInAllowed?:CallParamConfig, allowExceptParams?:CallParamConfig, disallowParams?:CallParamConfig, allowExceptParamsAnyValue?:CallParamAnyValueConfig, disallowParamsAnyValue?:CallParamAnyValueConfig, allowExceptParamFlags?:CallParamFlagAnyValueConfig, disallowParamFlags?:CallParamFlagAnyValueConfig, allowExceptCaseInsensitiveParams?:CallParamConfig, disallowCaseInsensitiveParams?:CallParamConfig'
1010
AllowAttributesDirectives: 'allowInClassWithAttributes?:list<string>, allowExceptInClassWithAttributes?:list<string>, disallowInClassWithAttributes?:list<string>, allowInFunctionsWithAttributes?:list<string>, allowInMethodsWithAttributes?:list<string>, allowExceptInFunctionsWithAttributes?:list<string>, allowExceptInMethodsWithAttributes?:list<string>, disallowInFunctionsWithAttributes?:list<string>, disallowInMethodsWithAttributes?:list<string>, allowInClassWithMethodAttributes?:list<string>, allowExceptInClassWithMethodAttributes?:list<string>, disallowInClassWithMethodAttributes?:list<string>'
11-
AllowDirectives: 'allowIn?:list<string>, allowExceptIn?:list<string>, disallowIn?:list<string>, allowInFunctions?:list<string>, allowInMethods?:list<string>, allowExceptInFunctions?:list<string>, allowExceptInMethods?:list<string>, disallowInFunctions?:list<string>, disallowInMethods?:list<string>, %typeAliases.AllowParamDirectives%, %typeAliases.AllowAttributesDirectives%'
11+
AllowDirectives: 'allowIn?:list<string>, allowExceptIn?:list<string>, disallowIn?:list<string>, allowInFunctions?:list<string>, allowInMethods?:list<string>, allowExceptInFunctions?:list<string>, allowExceptInMethods?:list<string>, disallowInFunctions?:list<string>, disallowInMethods?:list<string>, allowInInstanceOf?:list<string>, allowExceptInInstanceOf?:list<string>, disallowInInstanceOf?:list<string>, %typeAliases.AllowParamDirectives%, %typeAliases.AllowAttributesDirectives%'
1212
ForbiddenCallsConfig: 'array<array{function?:string|list<string>, method?:string|list<string>, exclude?:string|list<string>, definedIn?:string|list<string>, message?:string, %typeAliases.AllowDirectives%, errorIdentifier?:string, errorTip?:string}>'
1313
DisallowedAttributesConfig: 'array<array{attribute:string|list<string>, exclude?:string|list<string>, message?:string, %typeAliases.AllowDirectives%, errorIdentifier?:string, errorTip?:string}>'
1414
AllowDirectivesConfig: 'array{%typeAliases.AllowDirectives%}'

src/Allowed/Allowed.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ public function isAllowed(?Node $node, Scope $scope, ?array $args, Disallowed $d
8181
}
8282
return true;
8383
}
84+
if ($disallowed->getAllowInInstanceOf()) {
85+
return $this->isInstanceOf($scope, $disallowed->getAllowInInstanceOf());
86+
}
87+
if ($disallowed->getAllowExceptInInstanceOf()) {
88+
return !$this->isInstanceOf($scope, $disallowed->getAllowExceptInInstanceOf());
89+
}
8490
if ($hasParams && $disallowed->getAllowExceptParams()) {
8591
return $this->hasAllowedParams($scope, $args, $disallowed->getAllowExceptParams(), false);
8692
}
@@ -122,6 +128,22 @@ private function callMatches(Scope $scope, string $call): bool
122128
}
123129

124130

131+
/**
132+
* @param Scope $scope
133+
* @param list<string> $allowConfig
134+
* @return bool
135+
*/
136+
private function isInstanceOf(Scope $scope, array $allowConfig): bool
137+
{
138+
foreach ($allowConfig as $allowInstanceOf) {
139+
if ($scope->isInClass() && $scope->getClassReflection()->is($allowInstanceOf)) {
140+
return true;
141+
}
142+
}
143+
return false;
144+
}
145+
146+
125147
/**
126148
* @param Scope $scope
127149
* @param array<Arg>|null $args

src/Allowed/AllowedConfig.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ class AllowedConfig
2020
/** @var list<string> */
2121
private array $allowExceptInCalls;
2222

23+
/** @var list<string> */
24+
private array $allowInInstanceOf;
25+
26+
/** @var list<string> */
27+
private array $allowExceptInInstanceOf;
28+
2329
/** @var list<string> */
2430
private array $allowInClassWithAttributes;
2531

@@ -56,6 +62,8 @@ class AllowedConfig
5662
* @param list<string> $allowExceptIn
5763
* @param list<string> $allowInCalls
5864
* @param list<string> $allowExceptInCalls
65+
* @param list<string> $allowInInstanceOf
66+
* @param list<string> $allowExceptInInstanceOf
5967
* @param list<string> $allowInClassWithAttributes
6068
* @param list<string> $allowExceptInClassWithAttributes
6169
* @param list<string> $allowInCallsWithAttributes
@@ -72,6 +80,8 @@ public function __construct(
7280
array $allowExceptIn,
7381
array $allowInCalls,
7482
array $allowExceptInCalls,
83+
array $allowInInstanceOf,
84+
array $allowExceptInInstanceOf,
7585
array $allowInClassWithAttributes,
7686
array $allowExceptInClassWithAttributes,
7787
array $allowInCallsWithAttributes,
@@ -87,6 +97,8 @@ public function __construct(
8797
$this->allowExceptIn = $allowExceptIn;
8898
$this->allowInCalls = $allowInCalls;
8999
$this->allowExceptInCalls = $allowExceptInCalls;
100+
$this->allowInInstanceOf = $allowInInstanceOf;
101+
$this->allowExceptInInstanceOf = $allowExceptInInstanceOf;
90102
$this->allowInClassWithAttributes = $allowInClassWithAttributes;
91103
$this->allowExceptInClassWithAttributes = $allowExceptInClassWithAttributes;
92104
$this->allowInCallsWithAttributes = $allowInCallsWithAttributes;
@@ -136,6 +148,24 @@ public function getAllowExceptInCalls(): array
136148
}
137149

138150

151+
/**
152+
* @return list<string>
153+
*/
154+
public function getAllowInInstanceOf(): array
155+
{
156+
return $this->allowInInstanceOf;
157+
}
158+
159+
160+
/**
161+
* @return list<string>
162+
*/
163+
public function getAllowExceptInInstancesOf(): array
164+
{
165+
return $this->allowExceptInInstanceOf;
166+
}
167+
168+
139169
/**
140170
* @return list<string>
141171
*/

src/Allowed/AllowedConfigFactory.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public function __construct(
4646
*/
4747
public function getConfig(array $allowed): AllowedConfig
4848
{
49-
$allowInCalls = $allowExceptInCalls = $allowInClassWithAttributes = $allowExceptInClassWithAttributes = [];
49+
$allowInCalls = $allowExceptInCalls = $allowInInstanceOf = $allowExceptInInstanceOf = $allowInClassWithAttributes = $allowExceptInClassWithAttributes = [];
5050
$allowInCallsWithAttributes = $allowExceptInCallsWithAttributes = $allowInClassWithMethodAttributes = $allowExceptInClassWithMethodAttributes = [];
5151
$allowParamsInAllowed = $allowParamsAnywhere = $allowExceptParamsInAllowed = $allowExceptParams = [];
5252

@@ -56,6 +56,12 @@ public function getConfig(array $allowed): AllowedConfig
5656
foreach ($allowed['allowExceptInFunctions'] ?? $allowed['allowExceptInMethods'] ?? $allowed['disallowInFunctions'] ?? $allowed['disallowInMethods'] ?? [] as $disallowedCall) {
5757
$allowExceptInCalls[] = $this->normalizer->normalizeCall($disallowedCall);
5858
}
59+
foreach ($allowed['allowInInstanceOf'] ?? [] as $allowedInstanceOf) {
60+
$allowInInstanceOf[] = $this->normalizer->normalizeNamespace($allowedInstanceOf);
61+
}
62+
foreach ($allowed['allowExceptInInstanceOf'] ?? $allowed['disallowInInstanceOf'] ?? [] as $disallowedInstanceOf) {
63+
$allowExceptInInstanceOf[] = $this->normalizer->normalizeNamespace($disallowedInstanceOf);
64+
}
5965
foreach ($allowed['allowInClassWithAttributes'] ?? [] as $allowInClassAttribute) {
6066
$allowInClassWithAttributes[] = $this->normalizer->normalizeAttribute($allowInClassAttribute);
6167
}
@@ -115,6 +121,8 @@ public function getConfig(array $allowed): AllowedConfig
115121
$allowed['allowExceptIn'] ?? $allowed['disallowIn'] ?? [],
116122
$allowInCalls,
117123
$allowExceptInCalls,
124+
$allowInInstanceOf,
125+
$allowExceptInInstanceOf,
118126
$allowInClassWithAttributes,
119127
$allowExceptInClassWithAttributes,
120128
$allowInCallsWithAttributes,

src/Disallowed.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,18 @@ public function getAllowInCalls(): array;
3030
public function getAllowExceptInCalls(): array;
3131

3232

33+
/**
34+
* @return list<string>
35+
*/
36+
public function getAllowInInstanceOf(): array;
37+
38+
39+
/**
40+
* @return list<string>
41+
*/
42+
public function getAllowExceptInInstanceOf(): array;
43+
44+
3345
/**
3446
* @return list<string>
3547
*/

src/DisallowedAttribute.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,18 @@ public function getAllowExceptInCalls(): array
9494
}
9595

9696

97+
public function getAllowInInstanceOf(): array
98+
{
99+
return $this->allowedConfig->getAllowInInstanceOf();
100+
}
101+
102+
103+
public function getAllowExceptInInstanceOf(): array
104+
{
105+
return $this->allowedConfig->getAllowExceptInInstancesOf();
106+
}
107+
108+
97109
public function getAllowParamsInAllowed(): array
98110
{
99111
return $this->allowedConfig->getAllowParamsInAllowed();

0 commit comments

Comments
 (0)