-
jLinux The Linux Dude
  
-
Posts: 157
Threads: 35
Joined: Jun 2015
Reputation:
2
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?
-
musheertm Junior Member
 
-
Posts: 10
Threads: 0
Joined: Sep 2015
Reputation:
0
-
mwhitney Posting Freak
    
-
Posts: 1,101
Threads: 4
Joined: Nov 2014
Reputation:
95
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.
-
jLinux The Linux Dude
  
-
Posts: 157
Threads: 35
Joined: Jun 2015
Reputation:
2
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.
-
includebeer CodeIgniter Team
-
Posts: 1,018
Threads: 18
Joined: Oct 2014
Reputation:
40
(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?
-
jLinux The Linux Dude
  
-
Posts: 157
Threads: 35
Joined: Jun 2015
Reputation:
2
09-21-2015, 04:21 PM
(This post was last modified: 09-21-2015, 04:29 PM by jLinux.)
(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($log) log_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)
-
mwhitney Posting Freak
    
-
Posts: 1,101
Threads: 4
Joined: Nov 2014
Reputation:
95
(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.
-
mwhitney Posting Freak
    
-
Posts: 1,101
Threads: 4
Joined: Nov 2014
Reputation:
95
(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.
-
jLinux The Linux Dude
  
-
Posts: 157
Threads: 35
Joined: Jun 2015
Reputation:
2
09-22-2015, 10:03 AM
(This post was last modified: 09-22-2015, 10:10 AM by jLinux.)
(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
-
includebeer CodeIgniter Team
-
Posts: 1,018
Threads: 18
Joined: Oct 2014
Reputation:
40
(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 !
|