2

I have a simple class in a PHP project that uses something like the following in its constructor:

    @set_exception_handler(array($this, 'exception_handler'));

The problem is, it's catching exceptions in global scope, eg: exceptions that are completely not related to the class at all.

Is it possible to limit the scope of exceptions here that are only thrown by instances of this class and/or a specific exception subclass, eg: MyClassException?

randombits
  • 47,058
  • 76
  • 251
  • 433
  • No that is not possible, since Exceptions are global objects. You will have to use normal `try...catch` blocks inside your class methods. – arkascha Feb 03 '16 at 16:19

2 Answers2

2

You can not set exception handler only for own exception. All handlers work in global scope. But, you can create a own exception handler chain and control all exception.

<?php

interface ExceptionHandlerInterface
{
    public function supports(\Exception $e);
    public function handle(\Exception $e);
}

class ExceptionHandler implements ExceptionHandlerInterface
{
    public function supports(\Exception $e)
    {
        return $e instanceof \Exception;
    }

    public function handle(\Exception $e)
    {
        throw $e;
    }
}

class MyExceptionHandler implements ExceptionHandlerInterface
{
    public function supports(\Exception $e)
    {
        return $e instanceof MyException;
    }

    public function handle(\Exception $e)
    {
        exit("Oops, this is a my exception.\n");
    }
}

class ExceptionHandlerChain
{
    private $handlers;

    public function addHandler(ExceptionHandlerInterface $handler, $priority)
    {
        // you should sort all handlers with priority
        $this->handlers[] = $handler;
    }

    public function handle(\Exception $e)
    {
        foreach ($this->handlers as $handler) {
            if ($handler->supports($e)) {
                $handler->handle($e);
            }
        }
    }
}


class MyException extends \Exception
{
}

$chain = new ExceptionHandlerChain();
$chain->addHandler(new MyExceptionHandler(), 0);
$chain->addHandler(new ExceptionHandler(), 1024);

set_exception_handler([$chain, 'handle']);

//throw new RuntimeException();
throw new MyException();
ZhukV
  • 2,892
  • 6
  • 25
  • 36
0

As said in my comment above you cannot somehow limit a global exception handler to specific exception types or events.

You can however use phps magical __call() method to achieve something similar without having to use normal try...catch blocks in all class methods. Consider this simple example:

<?php

class myException extends Exception {}

class myClass
{
  public function __call($funcName, $funcArgs)
  {
    try {
      if (method_exists($this, '_'.$funcName)) {
          $this->_myFunction($funcArgs);
      }
    } catch (myException $e) {
      var_dump($e->getMessage());
    }
  }

  public function _myFunction($args)
  {
      throw new myException('Ooops');
  }
}

$myObj = new myClass;
$myObj->myFunction();

The output here obviously will be:

string(5) "Ooops"

There are three things to point out here:

  1. "normal" methods like _myFunction() do not have to implement a try...catch block
  2. caught exceptions can be limited to certain types and can be handled inside the object scope
  3. no global exception handler is registered, so no global exceptions are caught.

A huge disadvantage with such setup is however, that IDEs fail to support such method chains, so you get no auto completion and no signature help for object methods.

arkascha
  • 41,620
  • 7
  • 58
  • 90
  • The major gotchya here that I did not include my OP is that my class is extending the built-in Redis class. That means using `__call()` won't work here, as the methods exist in the parent call and it'll never get invoked. Otherwise this would have been perfect. – randombits Feb 03 '16 at 18:50