If I have the following class method:
class ClassA
{
public function MethodA()
{
trigger_error('An error has occurred.', E_USER_ERROR);
}
}
... then what is the accepted way of documenting that it might "throw" (issue) an error using trigger_error() explicitly? The closest I found was to use either #throws, or #exception. Is it possible this behavior should not be documented at all, or instead included in the method description itself?
The reason I ask this question about trigger_error is because I use a custom error logging class which is called by a custom error handler, hence it is convenient to issue runtime errors and output/save them in an organized fashion. Should I just use #see trigger_error or #see ErrorLog (my custom class) instead?
EDIT:
For those wondering about the script flow if an error is triggered: the script will not terminate execution on E_USER_ERROR.
The script will not terminate execution on E_USER_ERROR.
This may be your bigger issue. PHP's error mechanism is rather... primitive. It basically only knows two modes: notify the developer of potential issues by triggering a notice, warning or stern warning (a.k.a. error), or to stop the script by triggering a fatal error. Triggering a (fatal) error yet continuing with the script execution is not really its purpose.
The function should simply be documented informally with "triggers error if X, Y or Z". This typically means "will kill script if X, Y or Z". If you're overriding that behaviour, that's up to you.
Formally documented errors make sense if those errors can be handled in a standardised manner, which is very much what exceptions are. They have formal documentation standards (#throws) and can be handled in flexible ways (try..catch). You really should use exceptions for the purpose you're trying to use errors here.
Related
I would like to know how throw works in PHP.
For example, does it act like a die() or exit()? How can I know what is done internally?
I am asking this because I saw Kohana using their $this->redirect() method with a throw to terminate the script execution instead of the traditional exit.
throw is not like exit or die at all. Throwing an exception does not automatically terminate the application, a thrown exception can be caught by the application. Only when an exception is not caught will the application be terminated.
try {
throw new Exception;
} catch (Exception $e) {
echo 'caught it';
}
echo 'not dead yet';
Exceptions are a mechanism to signal errors to higher up callers in a more flexible and rigorous manner than simple return false statements would allow. They are not comparable to a simple exit or die.
I don't know what Kohana does exactly, but throwing an exception instead of using a simple exit or die is an abuse of exceptions. Exceptions should be thrown in exceptional error circumstances only.
As already explained, you use throw to throw exceptions that can be caught "further up" in your application.
When you work with objects and object oriented programming you start coding every single object you make as a standalone object that you can give to someone else. The public methods of these are an API, and the phpdoc above each public method details what exceptions the class might throw under certain circumstances.
So, someone has created a standalone object that does something for you, like writing to a disk. You want to use this object, so you look at the docs and see it throws a PermissionsException when the object can't write to the disk because of a permissions issue.
In your code that uses this person's object, you now know that you should catch that exception, log it, and continue however you want your application to work given that circumstance (show a nice error to the user if it's via an AJAX call, for example).
So, knowing this, when you code your own objects, make descriptive exceptions for different circumstances that someone who you give your object to can use and respond to in their own applications.
Both die and exit you don't really want to use in production applications. They're useful for debugging when you do a var_dump() and then want to halt application execution straight afterwards or if you want to completely stop the script from running for some reason.
As for why your specific found piece of code does it this way, you should ask the developer if it isn't documented with good reasoning.
Using "throw" without try/catch will terminate the script with a "catchable fatal error". As far as I know there's no benefit in using "throw" this way. If you want to terminate a script you should use exit(), so you don't need to suppress the error message.
Consider a method which might throw an exception with some descriptive text:
if ($someCondition) {
throw new \Whatever\Exception('dilithium exhausted');
}
And elsewhere in the method is another block that might throw the same exception, but with different text:
if ($anotherCondition) {
throw new \Whatever\Exception('differentialator exploded');
}
While writing unit tests for this class, you create failure cases so that you can verify that these two exceptions get thrown properly. In these failure cases, do you prefer to:
A) Use #exceptionExpected in the test method's docblock to trap the generic \Whatever\Exception class and subsequently ignore the getMessage() text, assuming you got the right one? (Seems like a bad idea.)
or:
B) Use try/catch and then assert that the caught exception's getMessage() text equals the exact descriptive string you're expecting? (More resilient but it means changing your tests whenever you change your error wording.)
or:
C) Create a separate exception for each error case (e.g., \Whatever\DilithiumException and \Whatever\DifferentialatorException) and then use #exceptionExpected for each one.
I'm currently using B but tending toward C. I'm curious what others are doing in this same scenario. Do you have any guidelines that help you determine, "At what point does an error deserve its own exception class versus a more generic shared one?"
All of the above.
A is great, and I use as much as possible because it is simplest. There is another case when A does not work:
/**
* #exceptionExpected FooException
*/
test() {
// code that could throw FooException
...
// purpose of the test that throws of FooException
}
In this case, the test could pass when it should have failed because it didn't even get to what I was testing. A good way to deal with this is to use $this->setExpectedException()
B is great when you might actually use information from the exception. Rather than using the text of the exception message I would prefer to use the code. I have a form validation exception that packages up all the problems encountered in the data into one exception. By extending the exception class it becomes easy to transmit a good deal of information from the internal error state to the external handling code.
C accomplishes the same thing as B, but allows for simplifying the code by relying on more classes. The difference between these two is subtle and I tend to rely on design aesthetic to make the decision.
TL; DR: Use exception codes rather than messages, and design to the use case rather than the unit tests.
PHPUnit also provides #expectedExceptionCode and #expectedExceptionMessage when you need this level of detail. Warning: The latter requires the former.
BTW, I also tend toward A. If I need to express more meaning in the exception, I prefer to create a new exception class. I find the message to be too volatile to be worth testing in most applications.
I am getting my head around PHPUnit, and trying to build a test case for an existing class.
The class is a static configuration class, getting, setting and listing configuration options that will be available in the application.
The class is very strictly built. If I try to set a configuration setting with an incompatible value, or a configuration setting that does not exist, a E_USER_ERROR is thrown, halting the execution of the script. Even if it's not the fine art of error handling, it works fine for the purposes of this class. An error in that class is always the result of a programming error, and never of bad user input.
This has the great advantage that you don't have to worry about how error messages are handled or logged, which keeps the code slim. Output the message (or not, in production), die(), done.
However, with Unit tests, I don't see how I can continue to work with classic PHP errors. I will have to convert the functions to return success flags, or throw exceptions.
Am I correct?
Or is there a way in PHPUnit to expect errors, as there is to expect Exceptions? I can't see any straight away.
From the PHPUnit manual:
By default, PHPUnit converts PHP errors, warnings, and notices that are triggered during the execution of a test to an exception. Using these exceptions, you can, for instance, expect a test to trigger a PHP error as shown in Example 4.8.
class ExpectedErrorTest extends PHPUnit_Framework_TestCase
{
/**
* #expectedException PHPUnit_Framework_Error
*/
public function testFailingInclude()
{
include 'not_existing_file.php';
}
}
Same works for exceptions of course.
Edit: didn't see this was already mentioned in the comments, but I'll leave it here for reference in case someone looks for the same question and doesn't read the comments
If you want to cancel treating warning as Exception, use
PHPUnit_Framework_Error_Warning::$enabled=false; .
I don't know the details of your implementation, but couldn't you raise a normal (custom) exception (which can be tested) and have a global exception handler in your app that will throw the proper E_USER_ERROR based on these (custom) exceptions?
Check set_exception_handler for more information on setting this global exception handler.
Couldn't you define an error handler (via set_error_handler) that will treat any error that cannot be handled by PHPUnit (such as E_USER_ERROR) as a failure of the unit test?
(Inspired by WordPress' tests)
First of all, this question is purely theoretical. Fact is, whether it's possible or not, it would be terribly bad practice. Having said that, here's my question:
PHP offers the possibility to define custom error handlers via the set_error_handler function. By parsing the error message, it's possible to find out what triggered the error.
I'm interested mostly in 'Call to undefined function' errors. I know its possible to parse the error message to uncover the called function, and this got me thinking.
Would it be possible for the error handler, in case of an Undefined Function, to attempt to include a file (say functions.html.php for all functions starting with a html_ prefix), and then attempt to re-execute the function, plus arguments, that initially triggered the error? And, most importantly, return the function's value in case of success?
In brief without using exception handling you won't be able to recover from the error in the way you described.
There is a way to handle this specifically for undefined functions, however that is to say undefined member functions from an object. This is the __call() method. Basically if you call an undefined method from an object __call() then takes that function call and does whatever you put in the method body see http://php.net/manual/en/language.oop5.overloading.phplink text
It's not really possible to restart the execution where the error occured.
However: there is a system to loading classes on demand, using the __autoload function.
From the manual:-
You may define an __autoload function which is automatically called in case you are trying to use a class/interface which hasn't been defined yet. By calling this function the scripting engine is given a last chance to load the class before PHP fails with an error.
There is more in the PHP manual here: http://php.net/manual/en/language.oop5.autoload.php
I understand the significance of the term 'fatal error', but I want to write a test class like this (disgustingly simplified):
class tester {
function execute() {
if( #$this->tryit() === true ) return true;
return false;
}
function tryit() {
$doesntexist = new noobject();
return true;
}
}
actually I'd have a Test parent class, and then classes would extend it and contain a bunch of methods with a bunch of tests. The parent class would define execute and it would just run every method in the child class (excluding execute of course) and collecting data on which functions pass and which fail.
I want to write tests before I actually write part of my code, but instead of using assert I just want to run every test and generate a list of which functions of which test classes fail. But that means if a test fails it means there was an error -- but I also want to handle instances where I forgot to define a class, etc. Is it possible to do that, while not having the entire script die?
I was thinking that the script would just fail up until the function call with the # in front of it, and then continue, but obviously I was wrong. Is there a workaround?
A fatal error is fatal, and there is nothing you can do about it.
Two ideas of solutions could be :
To test if the method exists before trying to call it ; see method_exists
Or, to run each "test" in a separate processus : this way, if there is a Fatal Error caused by one test, only the "child" process corresponding to that test dies, and the "parent" process, the test launcher, can detect this and consider it as a failure.
Actually, the second solution exists in PHPUnit since version 3.4, if I remember correctly ;-)
Fatal errors cannot be stopped, not even with set_error_handler. However, you can often find another way at the expense of writing more code. For the example method tryit, you can write an autoload function that triggers a non-fatal error or (in PHP 5.3.0) throws an exception, or use class_exists to skip the instantiation of a non-existent class.
Yes and No
You cannot write it so that the code picks up where it left off, after the fatal. However, you can use register_shutdown_function() to continue processing php after the fatal error.
I've written code that checks which kind of fatal error it was and then attempt to fix it before redirecting the user back to the same page, or dying.
register_shutdown_function is excellent for redirecting the user to a 500 error page with a contact form prevalued with the error info. This way I could have the users help me out by opening an issue on my github acct.
I'm guessing you would set up an error handler with the set_error_handler() function that calls into your testing class to report an error, but I'm not entirely sure exactly how you'd implement it.
With PHP 7, you can now try/catch a fatal error.
https://www.php.net/manual/en/language.exceptions.php