Re: My Experience with PHP5 Error Handling (and possible
| From: | Greg Beaver | Date: | Thu, 26 Aug 2004 06:54:03 +0000 |
| Subject: | Re: My Experience with PHP5 Error Handling (and possible | ||
| References: | 1 2 3 4 5 6 7 8 | Groups: | php.pear.dev |
| Request: | Send a blank email to pear-dev+get-32956@lists.php.net to get a copy of this message | ||
Davey wrote:
That was exactly the idea. I haven't tested it in php4, but I did this exact thing in PEAR_Server, so you can see the basic principal if you look in PEAR/Server/Exception.php. It is basically lots of empty classes that extend the parent class, so that you can catch() hierarchies. I'm imagining that error stack would take all the stuff that is currently in errorstack instances, and instead become wholly static as you suggest by using the singleton array to store instance data. Callbacks would process based on the class of the object instead of the package. This has some MAJOR technical hurdles I have not worked out, I'm mainly thinking out loud here about vaporware before I dive in headfirst.using Exception class names instead. This is because the class name can replace both the package and error code fields, simplifying the API substantially, and allowing easy promotion of a warning.How will this work in PHP4? Also, I don't see how the class name can replace the error code unless you have a seperate exception class for every error.
I have my exceptions setup like this:
abstract Crtx_Exception {
const CRTX_EXCEPTION_ERROR = 1;
const CRTX_EXCEPTION_WARNING = 2;
const CRTX_EXCEPTION_NOTICE = 3;
These are not really error codes you are using, at least not as PEAR_ErrorStack was designed, these are really integer representations of error levels. An error code is intended to define the kind of specific error condition. From the package/error code, you can (without looking at the error message) determine that there was an invalid package name while parsing, or that there was a database connection error. This is why class name could replace the code/package combo. In other words, classname obsoletes the error code and package entirely. A PEAR_Server_ExceptionNotLocalChannel class tells you this is a PEAR_Server exception, and the error type is "Not a local channel" (hypothetical exception). This replaces 'package' => 'PEAR_Server', 'code' => PEAR_SERVER_ERROR_NOTLOCALCHANNEL
I think so - but I don't follow the concerns completely. Basically, it sounds like you do support the RFC to me. You're saying that you want your php5-only stuff to use throw(). What you're also saying is that you need php4 stuff to move to PEAR_ErrorStack so that it can be properly handled in a mixed environment with try/catch and PEAR_Error, so that error handling can be deferred. Right? Bad: <?php try { $something5->blah(); if (PEAR::isError($something4->blah()) {public __construct($msg, $code) { parent::__construct($this->getMsg($msg), $code); }protected function getMsg($msg, $code) { // an array of messages like: $msg[self::CRTX_EXCEPTION_ERROR] = "Fatal Error: $msg"; return $msg[$code]; }// Added for use with my afforementioned Crtx_ErrorStack public function getLevel($code) { // an array of levels like: $level[self::CRTX_EXCEPTION_ERROR] = 'error'; return $level[$code]; }} I then extend for each package, adding extra constants and overriding getMsg/getLevel as necessary (i.e. Crtx_SOAP_Exception or Crtx_XML_XSLT_Exception). The only way I can see to get the code, is $exception->getCode() - is this what you mean? Like I've tried to impress on certain people, If you're writing a PHP 5 only package you will do: class Some_Package5 { public function __construct() {if ($something_wrong) { throw new PEAR_Exception(...); }} } If you're writing a PHP4 packagte, you will do: class Some_Package4 {function Some_Package() { if ($something_wrong) { return PEAR::raiseError(...); /* On PHP4 this will return a PEAR_Error, and place it on the ErrorStack On PHP5 this will return null, and place it on the stack (this is completely PHP4 compatible and won't require an eval('throw foo'); */ } }} // if the user is creating PHP5 only code he can do: try {$obj = new Some_Package5; $obj2 = new Some_Package4;} catch (PEAR_Exception $e) {// handle it} // he can also do the below :) /* if the user is creating code for PHP5 that uses PHP4 packages (note you CANNOT use PHP5 classes in PHP4, thats not the point of my idea!) he will do: */ $obj = new Some_Package5; $obj2 = new Some_Package4; if (PEAR_ErrorStack::staticHasErrors('Some_Package5') || PEAR_ErrorStack::staticHasErros('Some_Package4')) {// handle it} /* if the user is creating code for PHP4 only, he can't use Some_Package5 */ $obj = new Some_Package4; if (PEAR::isError($obj)) {// handle it} OR if (PEAR_ErrorStack::staticHasErrors('Some_Package4')) {// handle it} So you see, the user only needs to compromise exceptions when either: a) Working with PHP4 only b) using PHP4 packages in PHP5 If he is doing: c) using PHP5 packages in PHP4... it won't work or d) Using PHP5 packages in PHP5 only app, he will just use try...catch IF HE WANTS I hope this FINALLY explains things...
// handle right here
// ... more lines of code
} catch (Exception $e) {
// defer handling of exceptions to here
}
?>
Good?:
<?php
try {
$something5->blah();
$something4->blah();
// ... more lines of code
} catch (Exception $e) {
// defer handling of exceptions to here
}
if (PEAR_ErrorStack::staticHasError('something4')) {
// defer handling of php4 re-caught PEAR_Errors to here
}
?>
Am I totally off the mark here? What we're looking at is try/catch simulation for php4, used in tandem with actual try/catch in php5. PEAR_Error does not allow deferred catch without a custom and somewhat complex callback. PEAR_ErrorStack is designed to allow deferred catch, just like exceptions, but with one difference - you aren't forced to handle errors sent to PEAR_ErrorStack. For this reason, it really is only suited to converting PEAR_Errors, raising and handling warnings, and then possibly for logging exceptions (something PEAR_Exception does on its own).
Greg