I’ve been strongly considering adding the following function to my current project.
/**
* Extend the root trigger error function to allow objects to
* pass a reference to themselves as the third parameter that
* their status might be logged and analysed.
*
* @param string $msg
* @param integer $code
* @param object $obj
*/
function trigger_error($msg, $code, $obj = null) {
if (!is_null($obj)) {
// logging the object status here.
}
return \ rigger_error($msg, $code);
}
What this does is allow classes to provide themselves to the error report.
public function __get ($var) {
if (isset($this->$var)) {
return $this->$var;
} else {
trigger_error("Class::__get {$var} does not exist.", E_USER_NOTICE, $this);
}
}
The idea is that the root trigger_error function will ignore the third parameter. If the namespace version is loaded it will catch the class and log it. My main worry is this is a violation of the principle that functions shouldn’t reference classes - even statically - or have an internal state. I think the benefits to debugging and testing elsewhere outweigh this concern. Thoughts?
Is there any reason you’re using trigger_error rather than exceptions? It seems better to me to be using try/catch that way you can catch errors higher up if needed.
Exceptions do not allow the code to continue after the point they occur. Trigger error remains useful for logging bad practice actions by outside code against the object that aren’t necessarily fatal to code execution. For example
public function __get( $var ) {
if(isset($this->$var) {
return $this->$var;
} else {
trigger_error( 'Classname::__get: Accessed property '.$var.' does not exist.', E_USER_NOTICE );
return null;
}
}
The notice itself isn’t important enough to kill the script on the spot, but we do want to make note of it because it could be useful in diagnosing other, more serious errors. The reason an exception isn’t used here is because, frankly, I don’t want to wrap every access to the object in a try catch block, nor do I expect anyone using the framework to want to wrap object accesses in try/catch. In normal code execution the error should never be raised - it will only come up while writing the code. But when writing code others might use your idea of normal isn’t going to be the same as the other persons, so flexibility is better here.
Now obviously I could write it that way, and if I was going to do that I would use exceptions. But I want the framework code to be somewhat forgiving of small mistakes just as PHP is forgiving.
Using trigger_error to create notices and warnings is similar in some respects to assert. Assert is used when the code should NEVER miss the assertion during normal operation, and so (presumably) you can turn the check off when you move into production after fully testing the code. Trigger_error is used when the code SHOULD never hit the given result, but there exists a default action that should be safe (in this case, returning null) for the execution of most code. Still to be safe, we raise a notice or warning so that the mistake isn’t forgotten about. Exceptions are used when the code can hit the error either due to bad user input or misbehavior on the part of outside programs the script is trying to interface with.
Also take the time to truly understand the difference between errors and exceptions.
echo "In this script we demonstrate the difference between triggering errors and throwing exceptions<br>";
echo "This line is immediately prior to an error<br/>";
trigger_error("This is the error", E_USER_NOTICE);
echo "<br/><br/>And the program continues to this line and beyond.<br/>";
echo "Meanwhile, if we hit an exception in a try catch block code after the exception within the try block doesn't execute.<br/>";
try {
echo "Before Exception.<br />";
throw new Exception("Our Exception Method.");
echo "And now note that nothing in this echo statement will get sent to the browser.<br />";
echo "Nor anything here. This code is dead. It can't execute because it follows an exception.<br />";
} catch (Exception $e) {
echo "This code, in the catch block, executes instead.<br />";
}
echo "And this code executes as well since it follows the try/catch.<br />";
echo "But if we throw an exception now without a try, the program will end with an uncaught exception.<br />";
throw new Exception( "This is fatal as we will not be caught" );
As I said, “Exceptions do not allow the code to continue after the point they occur.” This is entirely true. There is a reason why Java has a finally clause and that PHP sorely needs such a clause before its exception handling can be considered complete. Issuing a throw statement takes your code out of whatever it is doing and moves up until it either gets caught or goes uncaught and causes the program to die. Even if you use a global exception handler there is no way to return to the next line after the original throw statement and continue code execution. You just can’t.
trigger_error does allow for this. If the error is handled by the error handler the PHP interpreter resumes at the very next line if it is allowed to by the chosen error code (warning, notice, and deprecated allow code to continue).
There’s a time and place for exceptions and try/catch blocks. They are a very powerful way to manage program logic. However, to use them correctly requires that you understand how they work.
Michael I understand, and agreed with what you said in your original post. I simply felt it should have been stated as “Exceptions do not allow the code to continue after the point they occur within the try block” Uncaught being a given of course. But you didn’t really go into that much detail of when it stopped when answering Tom is all. No biggie. Quit Hatin’ on me
well i like the idea but as you said it does bring concerns to mind. But it seems as if your trying to make recoverable errors witch is cool, But instead of using trigger error it would probably be a better solution to write your own handler for such cases so you can handle the object properly and not let it slip threw the cracks.
I tried that. But a final exception handler cannot jump back to the point of the error and allow the code to resume. Indeed, if you reach a final exception handler you might as well log the error and send a graceful custom 500 message to the browser - not much else that can be done.
Trust me, I love OOP and I avoided trigger error up until the point I realized it’s the only way to do notices and warnings that allow the code to continue.
Is this a good thing? Well, it depends on your aims and strictness. PNL is being designed as an instructional framework more than a deployment one. As such, I want it to be forgiving of simple mistakes like attempts to access non-existing members of an object. With the StdClass this results in the property being created, null returned and notice raised.
If I wanted to harden the framework to force correct inputs I could by throwing an exception. And while this would force the user to correct these small errors, it would also make learning the basics that much more difficult (there’s a reason C isn’t most people’s first language).