Menu

Exception Type Resolver

PHPStan can enforce checked and unchecked exceptions. By default, which exceptions are checked is decided by the DefaultExceptionTypeResolver based on configuration parameters like exceptions.uncheckedExceptionClasses.

If you need custom logic to decide whether an exception is checked or unchecked — for example, based on the scope where the exception is thrown — you can implement a custom exception type resolver.

This is the interface your extension needs to implement:

namespace PHPStan\Rules\Exceptions;

use PHPStan\Analyser\Scope;

interface ExceptionTypeResolver
{

	public function isCheckedException(string $className, Scope $scope): bool;

}

The isCheckedException() method receives the exception class name and the current scope. Because the interface accepts a Scope, you can make decisions based on the file, namespace, or class where the exception is being thrown.

You can delegate to DefaultExceptionTypeResolver for the cases you don’t care about by injecting it into your constructor:

namespace App\PHPStan;

use PHPStan\Analyser\Scope;
use PHPStan\Rules\Exceptions\DefaultExceptionTypeResolver;
use PHPStan\Rules\Exceptions\ExceptionTypeResolver;

class MyExceptionTypeResolver implements ExceptionTypeResolver
{

	public function __construct(
		private DefaultExceptionTypeResolver $defaultResolver,
	)
	{
	}

	public function isCheckedException(string $className, Scope $scope): bool
	{
		// In tests, all exceptions are unchecked
		if ($scope->isInClass()
			&& $scope->getClassReflection()->is(\PHPUnit\Framework\TestCase::class)
		) {
			return false;
		}

		// Delegate to default resolver for everything else
		return $this->defaultResolver->isCheckedException($className, $scope);
	}

}

With this extension and exceptions.check.missingCheckedExceptionInThrows enabled, test methods can throw any exception without declaring @throws:

class MyTest extends \PHPUnit\Framework\TestCase
{
	public function testSomething(): void
	{
		// No error - all exceptions are unchecked in tests
		throw new \App\CheckedException();
	}
}

class MyService
{
	// Error: Missing @throws App\CheckedException
	public function doSomething(): void
	{
		throw new \App\CheckedException();
	}
}

The implementation needs to be registered as a service override in your configuration file:

services:
	exceptionTypeResolver!:
		class: App\PHPStan\MyExceptionTypeResolver

Note the ! after exceptionTypeResolver — this overrides the default service with the same name.

Learn more about checked exceptions »

Theme
A
© 2026 PHPStan s.r.o.