I think that injecting logger into the CustomException is not right, cause (as you pointed) it breaks SRP and increase the complexity of your exceptions classes.
I'll suggest you to separate Exception from ExceptionHandler. Exception class should only contain information about "what (and where) went wrong". ExceptionHandler is responsible for logging exception (and doing some other work with exception if needed).
So you can setup one global ExceptionHandler
(using set_exception_handler and set_error_handler or some framework-based exception handling mechanism like symfony's ExceptionListener), that will catch all unhandled exceptions.
<?php
class ExceptionHandler {
/**
* @var Logger
*/
private $logger;
public function __construct(Logger $logger)
{
$this->logger = $logger;
}
public function handle(Throwable $e)
{
$this->logger->logException($e);
}
}
In application code you can still throw and catch exceptions. I think there are 4 common situations.
Fully recoverable exceptions
This is general way for handling recoverable exceptions – such situations when you don't want to fail at all, but you need to do something when such exception occurs.
<?php
try {
$methodThatThrowsException();
}
catch (DoesNotMatterException $e) {
// do some stuff and continue the execution
// note, that this exception won't be logged
}
Recoverable logged exception
The same as previous, but you want to log this exception.
<?php
try {
$methodThatThrowsException();
}
catch (NonCriticalExceptionThatShouldBeLogged $e) {
$this->exceptionHandler->handle($e); // log exception
// do some stuff and continue the execution
}
Non-recoverable exceptions with "finalizer"
You want to execute some specific business logic and then fail. You can catch the exception, process it and then throw it again. Global exception handler will handle this exception and log it.
<?php
try {
$methodThatThrowsException();
}
catch (CriticalException $e) {
// do some stuff like cleanup/transaction rollback
throw $e;
}
Non-recoverable exceptions
If you want to just log the exception and fail, you can just throw this exception and global exception handler will catch and log it.
<?php
$methodThatThrowsException();
// ExceptionHandler::handle will be executed