Limit PHP error handler to specific namespace(s)

2019-04-05 17:52发布

问题:

is there any way in PHP to set error handler only for specific namespace(s)? I'm building a small framework and I'd like to be able to try-catch all error/warning/notice messages within it's namespace by setting custom error handler and throwing exceptions with it. Errors triggered outside this specific namespace should behave in a regular way.

Can it be done with PHP?

Thank you.

回答1:

Apologies in advance for not actually trying this out before posting:

The fifth (optional) parameter which PHP passes to your error handler method (and which is typically ignored) is an array $errcontext that points to the current PHP symbol table. My thought is that if you can extract the namespace from the backtrace of an Exception as proposed by Rudie then it may well also be possible to extract namespace information in a similar way from $errcontext. If that's true then your error handler can check its own namespace and "fall through" by returning false if the current namespace doesn't match the one for which the error handler is designed.

Also, error handlers can be "stacked" which means that at least in principle (if my suggestion to "mine" $errcontext actually works) you could set a separate error handler for each namespace.

I'm not claiming that this approach is any more "elegant" than the solutions proposed by Josh and Rudie, but it does seem to be in line with what you're trying to do - which is to impose a sort of scoping constraint on your error handler(s).

Good luck!



回答2:

I have not tried to do this before so I apologize in advance if this does not work (at the very least it may get you thinking), but this is how I would try to accomplish this:

Since your namespace is going to be apart of an overall larger framework, I would have all of the classes in the framework extend a base class (for this lets call it BaseClass). Inside that class I would create a method called errorHandler(). Inside this function you would do whatever you want to do for your exception handling (perhaps even throwing an exception yourself).

Now that you have this functionality, we have to figure out how to get this function called. Since all the objects in your namespace extend BaseClass, they all have access to this errorHandler() method. Now inside your code you can use the normal try / catch blocks to capture exceptions that happen and instead of using the standard exception model, you would instead call $this->errorHander() (Now that I think about it you may want to put some parameters in here -- possibly the exception you get from the catch statement). This should give you what you need for the parts of the code that you expect problems to occur.

The next part we need to figure out is to handle the exceptions that you do not expect and how you plan to route those through this error handler. This one is a little more tricky because this solution relies on a try / catch block somewhere. Since this is a framework, I am going to assume that everything runs through index.php or some other bootstrap file (something like Zend Framework or the like). If this is the case then I would put your try / catch around wherever the exection of the framework begins. Based on the exception that you get in the catch block you could decide if you want to run it through your errorHandler() method. I have to admin this portion somehow feels a little dirty and that there should be a better way to do this (perhaps once you get farther along a better solution will present itself).

Hopefully this helps you get farther along in your process. If someone has an idea how to get the last part not feel so dirty that would be great.



回答3:

Can it be done with PHP?

It can be done quite easily I'd say. Without actually writing the code, this is what I'd do:

  1. Create a try/catch block
  2. Catch all exceptions (\Exception)
  3. Find the last called class (somewhere in $exception->getTrace())
  4. That class will have its namespace in its name: some\name\space\Class
  5. Use dirname($namespace) to remove the class name ("Class" in this case)
  6. Do whatever you like with the result OR rethrow the exception: throw $exception;

edit
Even closures have namespaces:

namespace oele\boele;

$fn = function() {
  throw new \Exception;
};

try {
  $fn();
}
catch ( \Exception $ex ) {
  print_r($ex);
}

$ex->getTrace()[0]['function'] will be oele\boele\{closure}

edit
Too bad the trace array doesn't have a key 'namespace' for every element.