PHP chaining error handling - php

Is a good idea throw exceptions on chained methods?
For example:
class Mailer(){
private $attachment;
public function addAttachment($attachment){
if($this->validateAttachment($attachment)){
$this->attachment = $attachment;
}
throw new \InvalidArgumentException('Invalid attachment');
}
public function send(){
[...]
}
private function validateAttachment($attachment){
if($attachment === 'test'){
return true;
}
return false;
}
}
$mailer = new Mailer();
$mailer->addAttachment('invalid')->send();
Of course, this will fail and cause a fatal error unless we use try / catch.
Otherwise, if we don't thrown an error when addAttachment fails, use will not notice if anything went wrong.
And if the send can work without attachments, we cannot return an error on that method neither.
So, there is any good practice on error logging / handling when using chained methods?

You should throw an Exception anywhere you want to interrupt the flow of the program. It doesn't matter if it's chained or not. In your case, if the adding of the attachment fails, you want to stop it before it reaches send(). That's a perfectly fine use of an Exception.
Obviously, you need to make sure you wrap the whole execution in try/catch or you'll get a Fatal error (where all PHP stops).

Related

PHP get an object from inside a callback

I want to get an errors object from a callback function. here is what i have done so far.
$errors = null;
set_exception_handler(function($exception) use (&$errors) {
$errors = $exception;
var_dump($exception); // works fine
});
var_dump($errors); // give null ????
throw new Exception("blablblablablabla", 0);
I am sure with an anonymous function and a variable would work very fine !
But in this case with a callback and object I am not sure what's going on ? i can't get my head a round. any help explanation would be really appreciated.
set_exception_handler does not stop execution. It just defines what will happen if/when an exception occurs, so you can var_dump($errors); errors after setting it. $errors will still be null at that point because you have set it to null, and nothing has happened yet to change that.
The thing that does stop execution is when an exception actually occurs and the anonymous function defined in set_exception_handler is called. At that point, the var_dump($exception); within that function is called, but any other code in the main program after the point where the exception occurred will not be executed.
Storing the exception in a global variable from within the exception handler doesn't make really sense, because if the exception handler is called, no more code in the main program will be executed, so you won't be able to use that variable anyway.
You are assigning the anonymous function as an exception handler with set_exception_handler().
Yet you do not throw an exception in your code. Therefore the anonymous function is never called, because there was no event to handle, which means that $error was never set.
If you add a try catch block your function will also not be called. This can be seen by the fact, that the line var_dump($exception) from within your handler is not called either.
If you want to store the last called exception in your code, you may need to use a wrapper function around that code, that uses try catch to overwrite a static resource with the last exception encountered.
Note, that the exception within the given function is never fully thrown, because it was catched by the wrapper!
class Catcher {
private static $_EXCEPTION = null;
public static function GET_EXCEPTION () {
return self::$_EXCEPTION;
}
public static function run($func) {
try {
return $func();
} catch (Exception $e) {
self::$_EXCEPTION = $e;
}
}
}
var_dump(Catcher::GET_EXCEPTION()); //dumps NULL
Catcher::run(function(){
throw new Exception('I was called');
});
var_dump(Catcher::GET_EXCEPTION()); //dumps the last thrown exception

Exceptions don't bubble through instances in PHP?

I was getting an error about an exception thrown in a __toString() method in a class of mine, even though there was a try...catch() in there. I did some tracking down and it turns out an object within my instance was throwing an exception in its __toString(), which meant that it wasn't being caught by the containing class' catch!
I wrote some test code as a demonstration:
class test {
public function __toString() {
try {
$b = new b();
echo (string)$b;
} catch (exception $e) {
return (string)$e;
}
}
}
class b {
public function __toString() {
throw new Exception ("test");
}
}
$test = new test();
echo $test;
I had thought that exceptions always "bubbled up" through the code until they were caught or made it all the way out.
Is there any workaround for this? The instance within my class is from a library; I don't know if I can maintainably modify it to have a catch in its own __toString().
Per PHP's __toString() section in the magic methods docs:
Warning
You cannot throw an exception from within a __toString() method. Doing
so will result in a fatal error.
When executing the code snippet you gave you get a message that says the same thing:
PHP Fatal error: Method b::__toString() must not throw an exception
There's a feature request here to support this, which states that in current state of things it would be hard to implement.
Having said all that, the proper way to fix this is to submit a patch to your upstream library to not throw exceptions from that method.
If that's not possible, the workaround for you is instead of:
echo (string)$b;
write
echo $b->__toString();
In which case you can catch the exception as you expect.

How to point all notices and warnings to exceptions?

Is it possible to point all notices and warnings inside try/catch in exceptions. But do it only in try, the rest of the code should use default error handling. The main problem is that the try is in the while loop and can be called several times so i think it isnt a good idea to use set_error_handler and restore_error_handler several times. Maybe php have something like in java?
The only way is to use error handler. But you can configure the error handler to throw or not to throw exceptions:
class ErrorHandler {
public static $throw = false;
public static function handler() {
if(self::$throw) throw new Exception();
}
}
set_error_handler(array('ErrorHandler', 'handler'));
ErrorHandler::$throw = true;

test the return value of a method that triggers an error with PHPUnit

This question is specific to using PHPUnit.
PHPUnit automatically converts php errors to exceptions. Is there a way to test the return value of a method that happens to trigger a php error (either built-in errors or user generated errors via trigger_error)?
Example of code to test:
function load_file ($file)
{
if (! file_exists($file)) {
trigger_error("file {$file} does not exist", E_USER_WARNING);
return false;
}
return file_get_contents($file);
}
This is the type of test I want to write:
public function testLoadFile ()
{
$this->assertFalse(load_file('/some/non-existent/file'));
}
The problem I am having is that the triggered error causes my unit test to fail (as it should). But if I try to catch it, or set an expected exception any code that after the error is triggered never executes so I have no way of testing the return value of the method.
This example doesn't work:
public function testLoadFile ()
{
$this->setExpectedException('Exception');
$result = load_file('/some/non-existent/file');
// code after this point never gets executed
$this->assertFalse($result);
}
Any ideas how I could achieve this?
There is no way to do this within one unit test. It is possible if you break up testing the return value, and the notice into two different tests.
PHPUnit's error handler catches PHP errors and notices and converts them into Exceptions--which by definition stops program execution. The function you are testing never returns at all. You can, however, temporarily disable the conversion of errors into exceptions, even at runtime.
This is probably easier with an example, so, here's what the two tests should look like:
public function testLoadFileTriggersErrorWhenFileNotFound()
{
$this->setExpectedException('PHPUnit_Framework_Error_Warning'); // Or whichever exception it is
$result = load_file('/some/non-existent/file');
}
public function testLoadFileRetunsFalseWhenFileNotFound()
{
PHPUnit_Framework_Error_Warning::$enabled = FALSE;
$result = load_file('/some/non-existent/file');
$this->assertFalse($result);
}
This also has the added bonus of making your tests clearer, cleaner and self documenting.
Re: Comment:
That's a great question, and I had no idea until I ran a couple of tests. It looks as if it will not restore the default/original value, at least as of PHPUnit 3.3.17 (the current stable release right now).
So, I would actually amend the above to look like so:
public function testLoadFileRetunsFalseWhenFileNotFound()
{
$warningEnabledOrig = PHPUnit_Framework_Error_Warning::$enabled;
PHPUnit_Framework_Error_Warning::$enabled = false;
$result = load_file('/some/non-existent/file');
$this->assertFalse($result);
PHPUnit_Framework_Error_Warning::$enabled = $warningEnabledOrig;
}
Re: Second Comment:
That's not completely true. I'm looking at PHPUnit's error handler, and it works as follows:
If it is an E_WARNING, use PHPUnit_Framework_Error_Warning as an exception class.
If it is an E_NOTICE or E_STRICT error, use PHPUnit_Framework_Error_Notice
Else, use PHPUnit_Framework_Error as the exception class.
So, yes, errors of the E_USER_* are not turned into PHPUnit's *_Warning or *_Notice class, they are still transformed into a generic PHPUnit_Framework_Error exception.
Further Thoughts
While it depends exactly on how the function is used, I'd probably switch to throwing an actual exception instead of triggering an error, if it were me. Yes, this would change the logic flow of the method, and the code that uses the method... right now the execution does not stop when it cannot read a file. But that's up to you to decide whether the requested file not existing is truly exceptional behaviour. I tend to use exceptions way more than errors/warnings/notices, because they are easier to handle, test and work into your application flow. I usually reserve the notices for things like depreciated method calls, etc.
Use a phpunit.xml configuration file and disable the notice/warning/error to Exception conversion. More details in the manual. It's basically something like this:
<phpunit convertErrorsToExceptions="false"
convertNoticesToExceptions="false"
convertWarningsToExceptions="false">
</phpunit>
Instead of expecting a generic "Exception", what about expecting a "PHPUnit_Framework_Error" ?
Something like this might do :
/**
* #expectedException PHPUnit_Framework_Error
*/
public function testFailingInclude()
{
include 'not_existing_file.php';
}
Which, I suppose, might also be written as :
public function testLoadFile ()
{
$this->setExpectedException('PHPUnit_Framework_Error');
$result = load_file('/some/non-existent/file');
// code after this point never gets executed
$this->assertFalse($result);
}
For more informations, see Testing PHP Errors
Especially, it says (quoting) :
PHPUnit_Framework_Error_Notice and
PHPUnit_Framework_Error_Warning represent
PHP notices and warning, respectively.
Looking at the /usr/share/php/PHPUnit/TextUI/TestRunner.php file I have on my system, I see this (line 198 and following) :
if (!$arguments['convertNoticesToExceptions']) {
PHPUnit_Framework_Error_Notice::$enabled = FALSE;
}
if (!$arguments['convertWarningsToExceptions']) {
PHPUnit_Framework_Error_Warning::$enabled = FALSE;
}
So maybe you'll have to pass some kind of parameter to activate that behaviour ? But it seems to be enabled by default...
Actually there is a way to test both the return value and the exception thrown (in this case an error converted by PHPUnit).
You just have to do the following:
public function testLoadFileTriggersErrorWhenFileNotFound()
{
$this->assertFalse(#load_file('/some/non-existent/file'));
$this->setExpectedException('PHPUnit_Framework_Error_Warning'); // Or whichever exception it is
load_file('/some/non-existent/file');
}
Notice that to test for the return value you have to use the error suppression operator on the function call (the # before the function name). This way no exception will be thrown and the execution will continue. You then have to set the expected exception as usual to test the error.
What you cannot do is test multiple exceptions within a unit test.
This answer is a bit late to the party, but anyhow:
You can use Netsilik/BaseTestCase (MIT License) to test directly for triggered Notices/Warnings, without ignoring them or converting them to Exceptions. Because the notices/warnings they are not converted to an Exception, the execution is not halted.
composer require netsilik/base-test-case
Testing for an E_USER_NOTICE:
<?php
namespace Tests;
class MyTestCase extends \Netsilik\Testing\BaseTestCase
{
public function test_whenNoticeTriggered_weCanTestForIt()
{
$foo = new Foo();
$foo->bar();
self::assertErrorTriggered(E_USER_NOTICE, 'The notice message');
}
}
Hope this helps someone in the future.

organizing technical and user-facing error messages in php

I'm having a little trouble organizing my error messages for two interacting classes. One object has states that are 'error-ish', where something went wrong, or happened unexpectedly, but the situation is still salvageable. I don't want to use exceptions, 1. because they only have a single string for the message, and 2. because I want to access the object after the error. At the very least, I want to use some of its get() methods to construct a useful error message after the exception!
Ultimately I have two messages I want to communicate: one to myself as the coder, that something went wrong. This string would have the technical details of the file, line, function/method, arguments, and results. Obviously, I don't want to show this to the user, so there is another string of an error message that I want to show the user ("That email address was not found" kind of thing ).
So the thought occurs to me to build an error message array, which could use the error code from exceptions, or a status code, as keys for various messages. ( Though if I do this, where do I store the array of messages? ) Another option might be to make an error status object.
Is there anything like "error patterns", similar to design patterns?
Exceptions really are your best option, they do everything you asked. Even multiple messages are possible, since exceptions are just classes that you can extend. You could just pass the object that causes the exception to said exception.
<?php
class ExampleException extends Exception {
private $secondMessage;
private $objectThatCausedIt;
public function __construct( $secondMessage, $objectThatCausedIt ) {
parent::__construct(
"Descriptive message for developer",
// error code for this type of error
1000 );
$this->secondMessage = $secondMessage;
$this->objectThatCausedIt = $objectThatCausedIt;
}
public function getSecondMessage() {
return $this->secondMessage;
}
public function getObjectThatCausedIt() {
return $this->objectThatCausedIt;
}
}
class Example {
public function causeException() {
throw new ExampleException( "Second Message", $this );
}
}
Now you just use the class and wrap the call that might throw the exception in a try-catch block.
<?php
$example = new Example();
try {
$example->causeException();
}
catch ( ExampleException $e ) {
// kind of pointless here, just an illustration
// get object that caused it and do something with it.
dump_and_log_function( $e->getObjectThatCausedIt() );
// or just use $example, which is still "alive"
// and references the same instance
dump_and_log_function( $example );
}
Extending Exception has the benefit that you also get a stack backtrace. The backtrace contains information like in what file, line and function the exception occured. You can also access the error code, the message and more. I suggest you read the PHP documentation on Exceptions (http://php.net/manual/en/class.exception.php) for more information.
Regarding the error messages and logging, I usually use a singleton class for that. A singleton class has only one instance of itself (in case you didn't know.) Here is an extremely simple example:
<?php
class Log {
private $logFile;
private static $instance;
/* Log instances may only be constructed in Log::getInstance */
private function __construct() {
$this->logFile = fopen( "path/to/log/file", "a" );
}
public logMessage( $message ) {
fwrite( $this->logFile, $message );
}
public static getInstance() {
if ( !self::$instance ) self::$instance = new self();
return self::$instance;
}
}
Now going back to the exception handling throw-catch block, you can change it to something like this:
<?php
$example = new Example();
try {
$example->causeException();
}
catch ( ExampleException $e ) {
// log developer message and backtrace
Log::getInstance()->logMessage( $e->getMessage() );
Log::getInstance()->logMessage( $e->getTraceAsString() );
// or more compact by casting to string
Log::getInstance()->logMessage( (string)$e );
// and now print error for users
echo "<p>An Error has occured: {$e->getSecondMessage()}</p>";
}
Instead of echoing the error message right away, you could make the Log class have a property with an array of messages. Then you could list them later in a view script all at once. You could also make the logMessage method store the messages in the session so they could be displayed after a refresh (just don't forget to clear the messages from the session or they will be displayed over and over again ;-).
I don't want to use exceptions, 1.
because they only have a single string
for the message, and 2. because I want
to access the object after the error.
At the very least, I want to use some
of its get() methods to construct a
useful error message after the
exception!
You can create your own exception classes and you can extend PHP's exception classes, adding the support you need. However, possibly you can have two settings, developer and client, where client errors goto the display and developer errors don't instead going to a log file or something.
Which, translates into two custom exception types (though you could have many more, I'm saying two distinct base classes).

Categories