Skip to content

Commit

Permalink
ExceptionTypeResolver as an interface allowing for custom implementat…
Browse files Browse the repository at this point in the history
…ions
  • Loading branch information
ondrejmirtes committed May 20, 2021
1 parent fcb5c12 commit a125304
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 112 deletions.
10 changes: 9 additions & 1 deletion conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -750,12 +750,14 @@ services:
treatPhpDocTypesAsCertain: %treatPhpDocTypesAsCertain%

-
class: PHPStan\Rules\Exceptions\ExceptionTypeResolver
class: PHPStan\Rules\Exceptions\DefaultExceptionTypeResolver
arguments:
uncheckedExceptionRegexes: %exceptions.uncheckedExceptionRegexes%
uncheckedExceptionClasses: %exceptions.uncheckedExceptionClasses%
checkedExceptionRegexes: %exceptions.checkedExceptionRegexes%
checkedExceptionClasses: %exceptions.checkedExceptionClasses%
autowired:
- PHPStan\Rules\Exceptions\DefaultExceptionTypeResolver

-
class: PHPStan\Rules\Exceptions\MissingCheckedExceptionInFunctionThrowsRule
Expand All @@ -765,6 +767,8 @@ services:

-
class: PHPStan\Rules\Exceptions\MissingCheckedExceptionInThrowsCheck
arguments:
exceptionTypeResolver: @exceptionTypeResolver

-
class: PHPStan\Rules\Exceptions\TooWideFunctionThrowTypeRule
Expand Down Expand Up @@ -1355,6 +1359,10 @@ services:
- phpstan.broker.dynamicMethodReturnTypeExtension
- phpstan.broker.dynamicStaticMethodReturnTypeExtension

exceptionTypeResolver:
class: PHPStan\Rules\Exceptions\ExceptionTypeResolver
factory: @PHPStan\Rules\Exceptions\DefaultExceptionTypeResolver

typeSpecifier:
class: PHPStan\Analyser\TypeSpecifier
factory: @typeSpecifierFactory::create
Expand Down
115 changes: 115 additions & 0 deletions src/Rules/Exceptions/DefaultExceptionTypeResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Exceptions;

use Nette\Utils\Strings;
use PHPStan\Reflection\ReflectionProvider;

class DefaultExceptionTypeResolver implements ExceptionTypeResolver
{

private ReflectionProvider $reflectionProvider;

/** @var string[] */
private array $uncheckedExceptionRegexes;

/** @var string[] */
private array $uncheckedExceptionClasses;

/** @var string[] */
private array $checkedExceptionRegexes;

/** @var string[] */
private array $checkedExceptionClasses;

/**
* @param ReflectionProvider $reflectionProvider
* @param string[] $uncheckedExceptionRegexes
* @param string[] $uncheckedExceptionClasses
* @param string[] $checkedExceptionRegexes
* @param string[] $checkedExceptionClasses
*/
public function __construct(
ReflectionProvider $reflectionProvider,
array $uncheckedExceptionRegexes,
array $uncheckedExceptionClasses,
array $checkedExceptionRegexes,
array $checkedExceptionClasses
)
{
$this->reflectionProvider = $reflectionProvider;
$this->uncheckedExceptionRegexes = $uncheckedExceptionRegexes;
$this->uncheckedExceptionClasses = $uncheckedExceptionClasses;
$this->checkedExceptionRegexes = $checkedExceptionRegexes;
$this->checkedExceptionClasses = $checkedExceptionClasses;
}

public function isCheckedException(string $className): bool
{
foreach ($this->uncheckedExceptionRegexes as $regex) {
if (Strings::match($className, $regex) !== null) {
return false;
}
}

foreach ($this->uncheckedExceptionClasses as $uncheckedExceptionClass) {
if ($className === $uncheckedExceptionClass) {
return false;
}
}

if (!$this->reflectionProvider->hasClass($className)) {
return $this->isCheckedExceptionInternal($className);
}

$classReflection = $this->reflectionProvider->getClass($className);
foreach ($this->uncheckedExceptionClasses as $uncheckedExceptionClass) {
if ($classReflection->getName() === $uncheckedExceptionClass) {
return false;
}

if (!$classReflection->isSubclassOf($uncheckedExceptionClass)) {
continue;
}

return false;
}

return $this->isCheckedExceptionInternal($className);
}

private function isCheckedExceptionInternal(string $className): bool
{
foreach ($this->checkedExceptionRegexes as $regex) {
if (Strings::match($className, $regex) !== null) {
return true;
}
}

foreach ($this->checkedExceptionClasses as $checkedExceptionClass) {
if ($className === $checkedExceptionClass) {
return true;
}
}

if (!$this->reflectionProvider->hasClass($className)) {
return count($this->checkedExceptionRegexes) === 0 && count($this->checkedExceptionClasses) === 0;
}

$classReflection = $this->reflectionProvider->getClass($className);
foreach ($this->checkedExceptionClasses as $checkedExceptionClass) {
if ($classReflection->getName() === $checkedExceptionClass) {
return true;
}

if (!$classReflection->isSubclassOf($checkedExceptionClass)) {
continue;
}

return true;
}

return count($this->checkedExceptionRegexes) === 0 && count($this->checkedExceptionClasses) === 0;
}

}
109 changes: 2 additions & 107 deletions src/Rules/Exceptions/ExceptionTypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,114 +2,9 @@

namespace PHPStan\Rules\Exceptions;

use Nette\Utils\Strings;
use PHPStan\Reflection\ReflectionProvider;

class ExceptionTypeResolver
interface ExceptionTypeResolver
{

private ReflectionProvider $reflectionProvider;

/** @var string[] */
private array $uncheckedExceptionRegexes;

/** @var string[] */
private array $uncheckedExceptionClasses;

/** @var string[] */
private array $checkedExceptionRegexes;

/** @var string[] */
private array $checkedExceptionClasses;

/**
* @param ReflectionProvider $reflectionProvider
* @param string[] $uncheckedExceptionRegexes
* @param string[] $uncheckedExceptionClasses
* @param string[] $checkedExceptionRegexes
* @param string[] $checkedExceptionClasses
*/
public function __construct(
ReflectionProvider $reflectionProvider,
array $uncheckedExceptionRegexes,
array $uncheckedExceptionClasses,
array $checkedExceptionRegexes,
array $checkedExceptionClasses
)
{
$this->reflectionProvider = $reflectionProvider;
$this->uncheckedExceptionRegexes = $uncheckedExceptionRegexes;
$this->uncheckedExceptionClasses = $uncheckedExceptionClasses;
$this->checkedExceptionRegexes = $checkedExceptionRegexes;
$this->checkedExceptionClasses = $checkedExceptionClasses;
}

public function isCheckedException(string $className): bool
{
foreach ($this->uncheckedExceptionRegexes as $regex) {
if (Strings::match($className, $regex) !== null) {
return false;
}
}

foreach ($this->uncheckedExceptionClasses as $uncheckedExceptionClass) {
if ($className === $uncheckedExceptionClass) {
return false;
}
}

if (!$this->reflectionProvider->hasClass($className)) {
return $this->isCheckedExceptionInternal($className);
}

$classReflection = $this->reflectionProvider->getClass($className);
foreach ($this->uncheckedExceptionClasses as $uncheckedExceptionClass) {
if ($classReflection->getName() === $uncheckedExceptionClass) {
return false;
}

if (!$classReflection->isSubclassOf($uncheckedExceptionClass)) {
continue;
}

return false;
}

return $this->isCheckedExceptionInternal($className);
}

private function isCheckedExceptionInternal(string $className): bool
{
foreach ($this->checkedExceptionRegexes as $regex) {
if (Strings::match($className, $regex) !== null) {
return true;
}
}

foreach ($this->checkedExceptionClasses as $checkedExceptionClass) {
if ($className === $checkedExceptionClass) {
return true;
}
}

if (!$this->reflectionProvider->hasClass($className)) {
return count($this->checkedExceptionRegexes) === 0 && count($this->checkedExceptionClasses) === 0;
}

$classReflection = $this->reflectionProvider->getClass($className);
foreach ($this->checkedExceptionClasses as $checkedExceptionClass) {
if ($classReflection->getName() === $checkedExceptionClass) {
return true;
}

if (!$classReflection->isSubclassOf($checkedExceptionClass)) {
continue;
}

return true;
}

return count($this->checkedExceptionRegexes) === 0 && count($this->checkedExceptionClasses) === 0;
}
public function isCheckedException(string $className): bool;

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use PHPStan\Testing\TestCase;

class ExceptionTypeResolverTest extends TestCase
class DefaultExceptionTypeResolverTest extends TestCase
{

public function dataIsCheckedException(): array
Expand Down Expand Up @@ -139,7 +139,7 @@ public function testIsCheckedException(
bool $expectedResult
): void
{
$resolver = new ExceptionTypeResolver($this->createBroker(), $uncheckedExceptionRegexes, $uncheckedExceptionClasses, $checkedExceptionRegexes, $checkedExceptionClasses);
$resolver = new DefaultExceptionTypeResolver($this->createBroker(), $uncheckedExceptionRegexes, $uncheckedExceptionClasses, $checkedExceptionRegexes, $checkedExceptionClasses);
$this->assertSame($expectedResult, $resolver->isCheckedException($className));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class MissingCheckedExceptionInFunctionThrowsRuleTest extends RuleTestCase
protected function getRule(): Rule
{
return new MissingCheckedExceptionInFunctionThrowsRule(
new MissingCheckedExceptionInThrowsCheck(new ExceptionTypeResolver(
new MissingCheckedExceptionInThrowsCheck(new DefaultExceptionTypeResolver(
$this->createReflectionProvider(),
[],
[\PHPStan\ShouldNotHappenException::class],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class MissingCheckedExceptionInMethodThrowsRuleTest extends RuleTestCase
protected function getRule(): Rule
{
return new MissingCheckedExceptionInMethodThrowsRule(
new MissingCheckedExceptionInThrowsCheck(new ExceptionTypeResolver(
new MissingCheckedExceptionInThrowsCheck(new DefaultExceptionTypeResolver(
$this->createReflectionProvider(),
[],
[\PHPStan\ShouldNotHappenException::class],
Expand Down

0 comments on commit a125304

Please sign in to comment.