• 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Handing errors in my code using Exceptions

#1
I was wondering how many of you use PHP ErrorException (or just Exception) to manage errors in your scripts. The way im managing Errors/Notifications/Logging is just really messy, and I was looking for an efficient way to clean it up.

I was thinking that maybe I could just throw exceptions from the Models or Libraries if an error is encountered, as opposed to just returning false.

Just to give an idea of what I mean by using the exceptions with notifications..
PHP Code:
<?php
class App_lib {
    private static function 
_display_notification($type$message)
    {
        echo 
strtoupper($type) . ": {$message}\n";
    }

    public static function 
add_notification($message$type 'info')
    {
        if(
$message instanceof ErrorException)
        {
            
self::_display_notification('error',"{$message->getMessage()}; Code: {$message->getCode()}; Severity: {$message->getSeverity()}print_r($message->getTrace(), TRUE) );
        }
        elseif(
$message instanceof Exception)
        {
            
self::_display_notification('info'$message->getMessage());
        }
        else
        {
            
self::_display_notification($type$message);
        }
    }

    public static function 
something($a)
    {
        if( ! 
is_numeric($a))
        {
            throw new 
ErrorException("The value $a is NOT numeric..",123,456);
        }

        if(
$a 2)
        {
            throw new 
Exception("The value $a was greater than 2");
        }

        if(
$a === 2)
        {
            return 
FALSE;
        }

        return 
TRUE;
    }
}

$val 'a';

try {
    if(
App_lib::something($val))
        echo 
"Value was apparently 1!\n";
    else
        echo 
"Value was 2\n";
}
catch(
Exception $e) {
    
App_lib::add_notification($e);
}

/*
 * Result..
 * $var = 'ABC';
 *      ERROR: The value ABC is NOT numeric..; Code: 123; Severity: 456
 *
 * $var = 3;
 *      INFO: The value 3 was greater than 2
 *
 * $var = 2;
 *      Value was 2
 *
 * $var = 1;
 *      Value was apparently 1!
 */

// Creating errors without using exceptions
App_lib::add_notification('info','Info notification');

App_lib::add_notification('error','error notification'); 

Anyone use them like this? If not, why not?
Reply

#2
wil try.
Reply

#3
I don't have a lot of experience using Exceptions in CodeIgniter, but, coming from other environments (primarily other programming languages), the actions I'm willing to take once an exception has occurred are usually pretty limited, because it indicates that the system/application is in an unexpected state. The fact that the "finally" clause was only added in PHP 5.5 just adds to the mess, because it is even less likely that you can sensibly continue execution after an exception without it.

Additionally, though I'm sure it's just a simplified example, I'd really expect an InvalidArgumentException for the ! is_numeric($a) check and maybe a RangeException for the $a > 2 check. While it may be valid in your case, ErrorException appears to be intended to handle the conversion of errors into Exceptions, especially in cases where there may not be a more appropriate type of Exception available.
Reply

#4
Perhaps I can create my own Exception type? (http://php.net/manual/en/language.exceptions.php#91159)

I just thought it would be easy to use exceptions to throw errors from the methods, opposed to just true/false, and without having to require whatever is using the class to handle errors the same way.
Reply

#5
(09-21-2015, 07:30 AM)mwhitney Wrote: I don't have a lot of experience using Exceptions in CodeIgniter, but, coming from other environments (primarily other programming languages), the actions I'm willing to take once an exception has occurred are usually pretty limited, because it indicates that the system/application is in an unexpected state. The fact that the "finally" clause was only added in PHP 5.5 just adds to the mess, because it is even less likely that you can sensibly continue execution after an exception without it.

I always use exceptions in my models and libraries. The controllers do the try/catch and return the errors to the ajax calls to be displayed to the user. I usually do that for fatal errors but I also use this technique for failed validation.

I never understand the purpose of "finally". From php.net : "Code within the finally block will always be executed after the try and catch blocks, regardless of whether an exception has been thrown, and before normal execution resumes." If it's always executed, why not just put the code after the try/catch? What difference does it make if the code is in the finally clause instead of after the try/catch?
Test your translation files with Translation Tester
Reply

#6
(09-21-2015, 03:55 PM)includebeer Wrote: I always use exceptions in my models and libraries. The controllers do the try/catch and return the errors ...

Exactly what Im thinking about doing

Right now, this is the class for notifications
PHP Code:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class 
App_lib
{
 
   /**
     * @var string $notifications   Array of notifications
     */
 
   private static $notifications = array();

 
   private static $CI;

 
   public static $instance;

 
   // ------------------------------------------------------------------------

 
   public function __construct()
 
   {
 
       self::$CI =& get_instance();

 
       static::$instance $this;

 
       $this->_check_flash_notifications();
 
   }
 
   
    
// ------------------------------------------------------------------------

 
   public static function keep_flash_notifications()
 
   {
 
       self::$CI->session->keep_flashdata('notifications');
 
   }

 
   // ------------------------------------------------------------------------

 
   private static function set_flash_notifications()
 
   {
 
       self::$CI->session->set_flashdata('notifications'self::$notifications);
 
   }

 
   // ------------------------------------------------------------------------

 
   private function _check_flash_notifications()
 
   {
 
       $flash self::$CI->session->flashdata('notifications');

 
       if($flash)
 
       {
 
           if(empty(self::$notifications))
 
           {
 
               self::$notifications $flash;
 
           }
 
           else
            
{
 
               self::$notifications array_merge(self::$notifications$flash);
 
           }
 
       }
 
   }

 
   // ------------------------------------------------------------------------

 
   /**
     * Add Notification
     *
     * Add a notification to the private $notifications array
     *
     * @param   string     $type       Type of notification (error, success, info, warning)
     * @param   string     $message    Notification message value
     * @param   bool    $log        If TRUE, then log_message($type, $message) will ensue
     *
     */
 
   public static function add_notification($type 'info'$title NULL$message$log FALSE)
 
   {

 
       if( ! in_array($type, ['error','info','success','warning']))
 
       {
 
           log_message('error',"Unknown notification type '{$type}' for message: {$message}; Changing type to info");

 
           $type 'info';
 
       }

 
       self::$notifications[$type][] = array($title$message);

 
       if($loglog_message($type$message);

 
       self::set_flash_notifications();
 
   }

 
   // ------------------------------------------------------------------------

 
   /**
     * Clear Notifications
     *
     * Clears private $notifications array
     *
     */
 
   public static function clear_notifications()
 
   {
 
       self::$notifications = array();
 
   }

 
   // ------------------------------------------------------------------------

 
   /**
     * Get Notifications
     *
     * Returns local private $notifications array
     *
     * @since   0.1.0
     * @return  arr         Array of notifications
     *
     */
 
   public static function get_notifications()
 
   {
 
       log_message('error','Executed: get_notifications');

 
       $return self::$notifications;

 
       self::clear_notifications();

 
       return $return;
 
   }

 
   // ------------------------------------------------------------------------

 
   /**
     * Get Latest Error
     *
     * This will retrieve all errors using the self::get_notifications function, meaning it will
     * clear out all notifications, but return only one...
     *
     * @since   0.1.0
     * @return str
     */
 
   public static function get_latest_errors()
 
   {
 
       log_message('error','Executed: get_latest_errors');

 
       if( ! $notifications self::get_notifications())
 
       {
 
           return FALSE;
 
       }

 
       //die('<pre>' . print_r($notifications, TRUE));

 
       if(@count($notifications['error']) > 1)
 
       {
 
           $return = array();

 
           foreach($notifications['error'] as $n)
 
           {
 
               // Push just the error string to the array
 
               array_push($return$n[1]);
 
           }

 
           return implode('; ',$return);
 
       }
 
       elseif(@count($notifications['error']) === 1)
 
       {
 
           // Return just the error string
 
           return $notifications['error'][0][1];
 
       }
 
       elseif( ! @isset($notifications['error'][0]))
 
       {
 
           return FALSE;
 
       }

 
       // Just incase
 
       return FALSE;
 
   }

 
   // ------------------------------------------------------------------------

 
   /**
     * Check Errors
     *
     * Checks for any errors in private $notifications array
     *
     * @since   0.1.0
     * @return  bool        TRUE of self::$notifications['error']
     *
     */
 
   public static function check_errors()
 
   {
 
       if(isset(self::$notifications['error']))
 
       {
 
           return TRUE;
 
       }

 
       return FALSE;
 
   }

 
   // ------------------------------------------------------------------------

 
   /**
     * Check Notifications
     *
     * Check for any created notifications have been added to $notifications
     *
     * @since   0.1.0
     * @return  bool        TRUE if self::$notifications isnt empty
     *
     */
 
   public static function check_notifications()
 
   {
 
       return (self::$notifications TRUE FALSE);
 
   }

 
   // ------------------------------------------------------------------------

 
   /**
     * Generate Notifications
     *
     * Uses functions in the template helper to generate the output for any
     * notifications
     *
     * @since   0.1.0
     * @return  string        String containing HTML for notifications
     *
     */
 
   public static function generate_notifications()
 
   {
 
       $return '';

 
       foreach(self::get_notifications() as $type => $n)
 
       {
 
           if(function_exists('notification_' $type))
 
           {
 
               $f 'notification_' $type;

 
               if(is_array($n))
 
               {
 
                   foreach($n as $n2)
 
                   {
 
                       $return .= $f($n2[0], $n2[1]);
 
                   }
 
               }
 
               else
                
{
 
                   $return .= $f($n[0], $n[1]);
 
               }
 
           }
 
       }

 
       return (empty($return) ? NULL $return);
 
   }


I know, its terrible... use kind words.

And this is how the travesty its used..
PHP Code:
// Create a notification/alert
App_lib::add_notification('error','Title','Some text');

if( ! 
somefunction() AND ! App_lib::check_errors()){
 
   App_lib::add_notification('error','Title','Something failed, not sure what though, since no notification/error/alert was generated');
}

// Show em
echo "Errors: " App_lib::generate_notifications(); 

Id much rather just do something like..
PHP Code:
class App_lib {
 
   private static function _display_notification($type$message)
 
   {
 
       echo strtoupper($type) . ": {$message}\n";
 
   }

 
   public static function add_notification($message$type 'info')
 
   {
 
       if($message instanceof ErrorException)
 
       {
 
           self::_display_notification('error',"{$message->getMessage()}; Code: {$message->getCode()}; Severity: {$message->getSeverity()}print_r($message->getTrace(), TRUE) );
 
       }
 
       elseif($message instanceof Exception)
 
       {
 
           self::_display_notification('info'$message->getMessage());
 
       }
 
       else
        
{
 
           self::_display_notification($type$message);
 
       }
 
   }

 
   public static function something($a)
 
   {
 
       if( ! is_numeric($a))
 
       {
 
           throw new ErrorException("The value $a is NOT numeric..",123,456);
 
       }

 
       if($a 2)
 
       {
 
           throw new Exception("The value $a was greater than 2");
 
       }

 
       if($a === 2)
 
       {
 
           return FALSE;
 
       }

 
       return TRUE;
 
   }
}

$val 'a';

try {
 
   if(App_lib::something($val))
 
       echo "Value was apparently 1!\n";
 
   else
        echo 
"Value was 2\n";
}
catch(
Exception $e) {
 
   App_lib::add_notification($e);

(Thats just me playing around with the idea..)

If ErrorException or Exception shouldn't be used, I can create my own (http://php.net/manual/en/language.exceptions.php#91159)
Reply

#7
(09-21-2015, 03:55 PM)includebeer Wrote: I never understand the purpose of "finally". From php.net : "Code within the finally block will always be executed after the try and catch blocks, regardless of whether an exception has been thrown, and before normal execution resumes." If it's always executed, why not just put the code after the try/catch? What difference does it make if the code is in the finally clause instead of after the try/catch?

If the exception isn't caught, none of the code after the try/catch block is likely to be executed, but the code in a finally block will be executed. If the exception is caught, but the catch block prevents the code after the try/catch from being executed (by throwing another exception or returning, for example), the finally block will still be executed (unless you use exit/die in the try/catch block).

Most of the "standard" examples for this are less useful with decent garbage collection, but there are still plenty of cases in which you want to free up any resources used inside the try block which will no longer be needed, regardless of whether the code was executed successfully or threw an exception.

If the finally block uses a return or similar statement, it can essentially suppress an exception thrown in the try/catch blocks, which is certainly something that can't be done by code after the try/catch blocks.

The benefits are somewhat limited by the fact that finally blocks aren't executed if you use exit/die, but I tend to avoid using those anyway.
Reply

#8
(09-21-2015, 04:21 PM)jLinux Wrote: If ErrorException or Exception shouldn't be used, I can create my own (http://php.net/manual/en/language.exceptions.php#91159)

Sorry for posting this separately from the response to includebeer above, but I missed this part before I posted that response.

You can certainly use those as much as you'd like, I just think it's more useful to use the available Exceptions for their specified purpose, especially if other people ever have to read your code (and, in my case, that other person is sometimes myself, 2 years later, wondering what I was thinking when I wrote that code). Who knows, you might even want to write an exception handler someday which handles specific types of exceptions in specific ways (though this requires slightly more effort in PHP than in some other languages, since PHP allows only one catch per try).

My primary point here is that the type of the Exception is usually more important than the message encapsulated in that Exception. If I catch an InvalidArgumentException, I know that I probably passed bad data to the function/method, so I probably don't want to continue using that data the same way (if the function has multiple arguments, I may not know which argument was invalid without some additional checks, but I know where to start if I want to check). I might even decide to only catch a subset of Exceptions and throw the rest so they can be handled by higher-level code or the framework's exception handler.
Reply

#9
(09-22-2015, 07:59 AM)mwhitney Wrote:
(09-21-2015, 04:21 PM)jLinux Wrote: If ErrorException or Exception shouldn't be used, I can create my own (http://php.net/manual/en/language.exceptions.php#91159)

Sorry for posting this separately from the response to includebeer above, but I missed this part before I posted that response.

You can certainly use those as much as you'd like, I just think it's more useful to use the available Exceptions for their specified purpose, especially if other people ever have to read your code (and, in my case, that other person is sometimes myself, 2 years later, wondering what I was thinking when I wrote that code). Who knows, you might even want to write an exception handler someday which handles specific types of exceptions in specific ways (though this requires slightly more effort in PHP than in some other languages, since PHP allows only one catch per try).

My primary point here is that the type of the Exception is usually more important than the message encapsulated in that Exception. If I catch an InvalidArgumentException, I know that I probably passed bad data to the function/method, so I probably don't want to continue using that data the same way (if the function has multiple arguments, I may not know which argument was invalid without some additional checks, but I know where to start if I want to check). I might even decide to only catch a subset of Exceptions and throw the rest so they can be handled by higher-level code or the framework's exception handler.

I think you think that I plan on using exceptions to basically generate every error message or notification (Which if thats how I came across, then I apologize). I really only plan on using it for serious issues, such as when an error in a lib/model was encountered that should terminate the execution of the model.

Small example..
PHP Code:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class 
Computer_model extends CI_Model
{
 
   public function create($name$os)
 
   {
 
       if($this->computer_exists($name))
 
       {
 
           throw new ErrorException("The computer name {$name} already exists")
 
       }
 
       
        if
( ! $this->os_exists($os))
 
       {
 
           if( ! $this->create_os($os))
 
           {
 
               // In this case, the exception should be thrown from create_os
 
               return FALSE;
 
           }
 
           else
            
{
 
               log_message('info',"The OS {$os} doesn't exist, so I created it");
 
           }
 
       }
 
       
        
// Try to create computer
 
       if( ! $this->db->insert('computers',[ /* Data... */ ]))
 
       {
 
           // If it failed, throw an exception with the error, the SQL Query error code, and use the "severity" as an error category..
 
           throw new ErrorException("Failed to create the new computer {$name}" App_lib::ERROR_CODES['sql_query'], App_lib::ERROR_CATEGORIES['computers'])
 
       }
 
       else
        
{
 
           return TRUE;
 
       }
 
   }
}

// ... Then in the controller

$name $this->input->post('name');
$os $this->input->post('os');

try {
 
   if($this->Computer_model->create($name$os))
 
       echo "Computer was created successfully";
 
   else
        echo 
"There was an error creating the computer..";
}
catch(
Exception $e) {
 
   // Something serious happened, interpret the exception
 
   App_lib::add_notification($e);



Edit: Hm, that does mean that I would have to catch the exception in the models as well, if its relying on a method that throws exceptions
Reply

#10
(09-22-2015, 07:34 AM)mwhitney Wrote: If the exception isn't caught, none of the code after the try/catch block is likely to be executed, but the code in a finally block will be executed. If the exception is caught, but the catch block prevents the code after the try/catch from being executed (by throwing another exception or returning, for example), the finally block will still be executed (unless you use exit/die in the try/catch block).

Most of the "standard" examples for this are less useful with decent garbage collection, but there are still plenty of cases in which you want to free up any resources used inside the try block which will no longer be needed, regardless of whether the code was executed successfully or threw an exception.

If the finally block uses a return or similar statement, it can essentially suppress an exception thrown in the try/catch blocks, which is certainly something that can't be done by code after the try/catch blocks.

Thanks for the explanation !
Test your translation files with Translation Tester
Reply


Digg   Delicious   Reddit   Facebook   Twitter   StumbleUpon  


  Theme © 2014 iAndrew  
Powered By MyBB, © 2002-2020 MyBB Group.