Skip to content

Implementation of DefaultThrowTypeExtension which could override @thr… #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,21 @@ parameters:
- RuntimeException
```

If some third-party code defines wrong throw types, you could override definitions like this:

```neon
parameters:
exceptionRules:
methodThrowTypeDeclarations:
FooProject\SomeService:
sendMessage:
- FooProject\ConnectionTimeoutException
methodWithoutException: []
functionThrowTypeDeclarations:
myFooFunction:
- FooException
```

## Extensibility

`Dynamic throw type extensions` - If the throw type is not always the same, but depends on an argument passed to the method. (Similar feature as [Dynamic return type extensions](https://github.com/phpstan/phpstan#dynamic-return-type-extensions))
Expand Down
9 changes: 9 additions & 0 deletions extension.neon
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ parameters:
exceptionRules:
reportMaybes: true
checkedExceptions: []
methodThrowTypeDeclarations: []
functionThrowTypeDeclarations: []

extensions:
exceptionRules: Pepakriz\PHPStanExceptionRules\DI\ExceptionRulesExtension
Expand All @@ -10,6 +12,13 @@ services:
-
class: Pepakriz\PHPStanExceptionRules\CheckedExceptionService(%exceptionRules.checkedExceptions%)

-
class: Pepakriz\PHPStanExceptionRules\DefaultThrowTypeExtension(%exceptionRules.methodThrowTypeDeclarations%, %exceptionRules.functionThrowTypeDeclarations%)
tags:
- exceptionRules.dynamicMethodThrowTypeExtension
- exceptionRules.dynamicStaticMethodThrowTypeExtension
- exceptionRules.dynamicFunctionThrowTypeExtension

-
class: Pepakriz\PHPStanExceptionRules\Rules\ThrowsPhpDocRule
tags: [phpstan.rules.rule]
Expand Down
3 changes: 2 additions & 1 deletion phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ parameters:
- %rootDir%/../../../tests/*/data/*

exceptionRules:
checkedExceptions: []
checkedExceptions:
- Pepakriz\PHPStanExceptionRules\RuntimeException
123 changes: 123 additions & 0 deletions src/DefaultThrowTypeExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?php declare(strict_types = 1);

namespace Pepakriz\PHPStanExceptionRules;

use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use function array_map;

class DefaultThrowTypeExtension implements DynamicFunctionThrowTypeExtension, DynamicMethodThrowTypeExtension, DynamicStaticMethodThrowTypeExtension
{

/**
* @var string[][][]
*/
private $methodThrowTypes = [];

/**
* @var string[][]
*/
private $functionThrowTypes = [];

/**
* @param string[][][] $methodThrowTypes
* @param string[][] $functionThrowTypes
*/
public function __construct(
array $methodThrowTypes,
array $functionThrowTypes
)
{
foreach ($methodThrowTypes as $className => $methods) {
foreach ($methods as $methodName => $throwTypes) {
$this->methodThrowTypes[$className][$methodName] = [];
foreach ($throwTypes as $throwType) {
$this->addMethodThrowType($className, $methodName, $throwType);
}
}
}

foreach ($functionThrowTypes as $functionName => $throwTypes) {
$this->functionThrowTypes[$functionName] = [];
foreach ($throwTypes as $throwType) {
$this->addFunctionThrowType($functionName, $throwType);
}
}
}

private function addMethodThrowType(string $className, string $methodName, string $throwType): void
{
$this->methodThrowTypes[$className][$methodName][] = $throwType;
}

private function addFunctionThrowType(string $functionName, string $throwType): void
{
$this->functionThrowTypes[$functionName][] = $throwType;
}

/**
* @throws UnsupportedFunctionException
*/
public function getThrowTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): Type
{
$functionName = $functionReflection->getName();
if (!isset($this->functionThrowTypes[$functionName])) {
throw new UnsupportedFunctionException();
}

$throwTypes = array_map(function (string $className): ObjectType {
return new ObjectType($className);
}, $this->functionThrowTypes[$functionName]);

return TypeCombinator::union(...$throwTypes);
}

/**
* @throws UnsupportedClassException
* @throws UnsupportedFunctionException
*/
public function getThrowTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
{
return $this->getMethodThrowType($methodReflection);
}

/**
* @throws UnsupportedClassException
* @throws UnsupportedFunctionException
*/
public function getThrowTypeFromStaticMethodCall(MethodReflection $methodReflection, StaticCall $methodCall, Scope $scope): Type
{
return $this->getMethodThrowType($methodReflection);
}

/**
* @throws UnsupportedClassException
* @throws UnsupportedFunctionException
*/
private function getMethodThrowType(MethodReflection $methodReflection): Type
{
$className = $methodReflection->getDeclaringClass()->getName();
if (!isset($this->methodThrowTypes[$className])) {
throw new UnsupportedClassException();
}

if (!isset($this->methodThrowTypes[$className][$methodReflection->getName()])) {
throw new UnsupportedFunctionException();
}

$throwTypeClasses = $this->methodThrowTypes[$className][$methodReflection->getName()];
$throwTypes = array_map(function (string $className): ObjectType {
return new ObjectType($className);
}, $throwTypeClasses);

return TypeCombinator::union(...$throwTypes);
}

}
10 changes: 10 additions & 0 deletions src/RuntimeException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php declare(strict_types = 1);

namespace Pepakriz\PHPStanExceptionRules;

use RuntimeException as CoreRuntimeException;

abstract class RuntimeException extends CoreRuntimeException
{

}
2 changes: 0 additions & 2 deletions src/UnsupportedClassException.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

namespace Pepakriz\PHPStanExceptionRules;

use RuntimeException;

class UnsupportedClassException extends RuntimeException
{

Expand Down
2 changes: 0 additions & 2 deletions src/UnsupportedFunctionException.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

namespace Pepakriz\PHPStanExceptionRules;

use RuntimeException;

class UnsupportedFunctionException extends RuntimeException
{

Expand Down