Skip to content

Commit d954504

Browse files
committed
Throw a descriptive exception when typeString cannot be parsed
A wildcard or other invalid value in `typeString` caused PHPStan to crash at DI container initialization with a raw `ParserException` and no indication of which config entry was responsible. The new `InvalidTypeStringInConfigException` names the offending `typeString` value, includes the parser's own reason, and appends a wildcard-specific hint when the value contains `*`. Both it and the existing `UnsupportedParamTypeInConfigException` extend the new abstract `InvalidConfigException`, which all factories catch and wrap in `ShouldNotHappenException`.
1 parent f0c9b22 commit d954504

10 files changed

Lines changed: 125 additions & 15 deletions

src/Allowed/AllowedConfigFactory.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44
namespace Spaze\PHPStan\Rules\Disallowed\Allowed;
55

66
use PHPStan\PhpDoc\TypeStringResolver;
7+
use PHPStan\PhpDocParser\Parser\ParserException;
78
use PHPStan\Type\Constant\ConstantBooleanType;
89
use PHPStan\Type\Constant\ConstantIntegerType;
910
use PHPStan\Type\Constant\ConstantStringType;
1011
use PHPStan\Type\NullType;
1112
use PHPStan\Type\VerbosityLevel;
13+
use Spaze\PHPStan\Rules\Disallowed\Exceptions\InvalidConfigException;
14+
use Spaze\PHPStan\Rules\Disallowed\Exceptions\InvalidTypeStringInConfigException;
1215
use Spaze\PHPStan\Rules\Disallowed\Exceptions\UnsupportedParamTypeInConfigException;
1316
use Spaze\PHPStan\Rules\Disallowed\Normalizer\Normalizer;
1417
use Spaze\PHPStan\Rules\Disallowed\Params\ParamValue;
@@ -42,7 +45,7 @@ public function __construct(
4245
* @param array $allowed
4346
* @phpstan-param AllowDirectivesConfig $allowed
4447
* @return AllowedConfig
45-
* @throws UnsupportedParamTypeInConfigException
48+
* @throws InvalidConfigException
4649
*/
4750
public function getConfig(array $allowed): AllowedConfig
4851
{
@@ -147,7 +150,7 @@ public function getConfig(array $allowed): AllowedConfig
147150
* @param int|string $key
148151
* @param int|bool|string|null|array{position:int, value?:int|bool|string, typeString?:string, name?:string} $value
149152
* @return T
150-
* @throws UnsupportedParamTypeInConfigException
153+
* @throws InvalidConfigException
151154
*/
152155
private function paramFactory(string $class, $key, $value): ParamValue
153156
{
@@ -180,7 +183,12 @@ private function paramFactory(string $class, $key, $value): ParamValue
180183
}
181184

182185
if ($typeString) {
183-
$type = $this->typeStringResolver->resolve($typeString);
186+
try {
187+
$type = $this->typeStringResolver->resolve($typeString);
188+
} catch (ParserException $e) {
189+
$hint = str_contains($typeString, '*') ? ' Wildcards are not supported in typeString.' : '';
190+
throw new InvalidTypeStringInConfigException($typeString, $e->getMessage() . $hint, $e);
191+
}
184192
} elseif (is_int($paramValue)) {
185193
$type = new ConstantIntegerType($paramValue);
186194
} elseif (is_bool($paramValue)) {

src/DisallowedCallFactory.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
use PHPStan\ShouldNotHappenException;
77
use Spaze\PHPStan\Rules\Disallowed\Allowed\AllowedConfigFactory;
8-
use Spaze\PHPStan\Rules\Disallowed\Exceptions\UnsupportedParamTypeInConfigException;
8+
use Spaze\PHPStan\Rules\Disallowed\Exceptions\InvalidConfigException;
99
use Spaze\PHPStan\Rules\Disallowed\Formatter\Formatter;
1010
use Spaze\PHPStan\Rules\Disallowed\Normalizer\Normalizer;
1111

@@ -61,7 +61,7 @@ public function createFromConfig(array $config): array
6161
);
6262
$disallowedCalls[$disallowedCall->getKey()] = $disallowedCall;
6363
}
64-
} catch (UnsupportedParamTypeInConfigException $e) {
64+
} catch (InvalidConfigException $e) {
6565
throw new ShouldNotHappenException(sprintf('%s: %s', $this->formatter->formatIdentifier($calls), $e->getMessage()));
6666
}
6767
}

src/DisallowedConstantFactory.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
use PHPStan\ShouldNotHappenException;
77
use Spaze\PHPStan\Rules\Disallowed\Allowed\AllowedConfigFactory;
8-
use Spaze\PHPStan\Rules\Disallowed\Exceptions\UnsupportedParamTypeInConfigException;
8+
use Spaze\PHPStan\Rules\Disallowed\Exceptions\InvalidConfigException;
99
use Spaze\PHPStan\Rules\Disallowed\Formatter\Formatter;
1010
use Spaze\PHPStan\Rules\Disallowed\Normalizer\Normalizer;
1111

@@ -54,7 +54,7 @@ public function createFromConfig(array $config): array
5454
);
5555
$disallowedConstants[$disallowedConstant->getConstant()] = $disallowedConstant;
5656
}
57-
} catch (UnsupportedParamTypeInConfigException $e) {
57+
} catch (InvalidConfigException $e) {
5858
throw new ShouldNotHappenException(sprintf('%s: %s', $this->formatter->formatIdentifier($constants), $e->getMessage()));
5959
}
6060
}

src/DisallowedKeywordFactory.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
use PHPStan\ShouldNotHappenException;
77
use Spaze\PHPStan\Rules\Disallowed\Allowed\AllowedConfigFactory;
8-
use Spaze\PHPStan\Rules\Disallowed\Exceptions\UnsupportedParamTypeInConfigException;
8+
use Spaze\PHPStan\Rules\Disallowed\Exceptions\InvalidConfigException;
99
use Spaze\PHPStan\Rules\Disallowed\Formatter\Formatter;
1010

1111
class DisallowedKeywordFactory
@@ -84,7 +84,7 @@ public function getDisallowedKeywords(array $config): array
8484
);
8585
$disallowedKeywords[$disallowedKeyword->getKeyword()] = $disallowedKeyword;
8686
}
87-
} catch (UnsupportedParamTypeInConfigException $e) {
87+
} catch (InvalidConfigException $e) {
8888
throw new ShouldNotHappenException(sprintf('%s: %s', $this->formatter->formatIdentifier($keywords), $e->getMessage()));
8989
}
9090
}

src/DisallowedNamespaceFactory.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
use PHPStan\ShouldNotHappenException;
77
use Spaze\PHPStan\Rules\Disallowed\Allowed\AllowedConfigFactory;
8-
use Spaze\PHPStan\Rules\Disallowed\Exceptions\UnsupportedParamTypeInConfigException;
8+
use Spaze\PHPStan\Rules\Disallowed\Exceptions\InvalidConfigException;
99
use Spaze\PHPStan\Rules\Disallowed\Formatter\Formatter;
1010
use Spaze\PHPStan\Rules\Disallowed\Normalizer\Normalizer;
1111

@@ -64,7 +64,7 @@ public function createFromConfig(array $config): array
6464
);
6565
$disallowedNamespaces[$disallowedNamespace->getNamespace()] = $disallowedNamespace;
6666
}
67-
} catch (UnsupportedParamTypeInConfigException $e) {
67+
} catch (InvalidConfigException $e) {
6868
throw new ShouldNotHappenException(sprintf('%s: %s', $this->formatter->formatIdentifier($namespaces), $e->getMessage()));
6969
}
7070
}

src/DisallowedSuperglobalFactory.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
use PHPStan\ShouldNotHappenException;
77
use Spaze\PHPStan\Rules\Disallowed\Allowed\AllowedConfigFactory;
8-
use Spaze\PHPStan\Rules\Disallowed\Exceptions\UnsupportedParamTypeInConfigException;
8+
use Spaze\PHPStan\Rules\Disallowed\Exceptions\InvalidConfigException;
99
use Spaze\PHPStan\Rules\Disallowed\Formatter\Formatter;
1010

1111
class DisallowedSuperglobalFactory
@@ -67,7 +67,7 @@ public function getDisallowedVariables(array $config): array
6767
);
6868
$disallowedSuperglobals[$disallowedSuperglobal->getVariable()] = $disallowedSuperglobal;
6969
}
70-
} catch (UnsupportedParamTypeInConfigException $e) {
70+
} catch (InvalidConfigException $e) {
7171
throw new ShouldNotHappenException(sprintf('%s: %s', $this->formatter->formatIdentifier($superglobals), $e->getMessage()));
7272
}
7373
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
declare(strict_types = 1);
3+
4+
namespace Spaze\PHPStan\Rules\Disallowed\Exceptions;
5+
6+
use Exception;
7+
8+
abstract class InvalidConfigException extends Exception
9+
{
10+
11+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
declare(strict_types = 1);
3+
4+
namespace Spaze\PHPStan\Rules\Disallowed\Exceptions;
5+
6+
use Throwable;
7+
8+
class InvalidTypeStringInConfigException extends InvalidConfigException
9+
{
10+
11+
public function __construct(string $typeString, string $reason, ?Throwable $previous = null)
12+
{
13+
parent::__construct(sprintf("Invalid typeString '%s': %s", $typeString, $reason), 0, $previous);
14+
}
15+
16+
}

src/Exceptions/UnsupportedParamTypeInConfigException.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@
33

44
namespace Spaze\PHPStan\Rules\Disallowed\Exceptions;
55

6-
use Exception;
76
use Throwable;
87

9-
class UnsupportedParamTypeInConfigException extends Exception
8+
class UnsupportedParamTypeInConfigException extends InvalidConfigException
109
{
1110

1211
public function __construct(?int $position, ?string $name, string $type, int $code = 0, ?Throwable $previous = null)
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
declare(strict_types = 1);
3+
4+
namespace Spaze\PHPStan\Rules\Disallowed\Calls;
5+
6+
use PHPStan\ShouldNotHappenException;
7+
use PHPStan\Testing\PHPStanTestCase;
8+
use Spaze\PHPStan\Rules\Disallowed\DisallowedCallFactory;
9+
use Spaze\PHPStan\Rules\Disallowed\RuleErrors\DisallowedCallableParameterRuleErrors;
10+
use Spaze\PHPStan\Rules\Disallowed\RuleErrors\DisallowedFunctionRuleErrors;
11+
12+
class FunctionCallsInvalidTypeStringConfigTest extends PHPStanTestCase
13+
{
14+
15+
/**
16+
* @throws ShouldNotHappenException
17+
*/
18+
public function testInvalidTypeStringWithWildcard(): void
19+
{
20+
$this->expectException(ShouldNotHappenException::class);
21+
$this->expectExceptionMessage('Wildcards are not supported in typeString.');
22+
$container = self::getContainer();
23+
new FunctionCalls(
24+
$container->getByType(DisallowedFunctionRuleErrors::class),
25+
$container->getByType(DisallowedCallableParameterRuleErrors::class),
26+
$container->getByType(DisallowedCallFactory::class),
27+
[
28+
[
29+
'function' => 'foo()',
30+
'disallowParamsInAllowed' => [
31+
1 => [
32+
'position' => 1,
33+
'typeString' => 'Foo\*',
34+
],
35+
],
36+
],
37+
]
38+
);
39+
}
40+
41+
42+
/**
43+
* @throws ShouldNotHappenException
44+
*/
45+
public function testInvalidTypeStringWithoutWildcard(): void
46+
{
47+
$this->expectException(ShouldNotHappenException::class);
48+
$this->expectExceptionMessage("Invalid typeString 'array<int':");
49+
$container = self::getContainer();
50+
new FunctionCalls(
51+
$container->getByType(DisallowedFunctionRuleErrors::class),
52+
$container->getByType(DisallowedCallableParameterRuleErrors::class),
53+
$container->getByType(DisallowedCallFactory::class),
54+
[
55+
[
56+
'function' => 'foo()',
57+
'disallowParamsInAllowed' => [
58+
1 => [
59+
'position' => 1,
60+
'typeString' => 'array<int',
61+
],
62+
],
63+
],
64+
]
65+
);
66+
}
67+
68+
69+
public static function getAdditionalConfigFiles(): array
70+
{
71+
return [
72+
__DIR__ . '/../../extension.neon',
73+
];
74+
}
75+
76+
}

0 commit comments

Comments
 (0)