/    Sign up×
Community /Pin to ProfileBookmark

[RESOLVED] help understanding custom exception class?

Can someone please show me a real world example of using this custom exception handling class? [url]http://php.net/manual/en/language.exceptions.extending.php[/url]

The example they use is not making sense to me because they are creating a test class with all kinds of extra parameters and constructs in it. And then the examples test the test class instead of testing a try catch block on a real piece of code.

I need a real basic example like if I want to put a function in a try catch that trys to make 2 = 3 and I want the custom handler to return the message “ya big dummy! 2 can’t equal 3!”.

I don’t see them using the “throw new” key words anywhere in the examples and another confusing thing is the customFunction(). What is is for and what goes inside it?

Thanks for any help in understanding this.

to post a comment
PHP

14 Comments(s)

Copy linkTweet thisAlerts:
@NogDogSep 03.2011 — The "customFunction" is there to illustrate the idea that by extending the Exception class to a sub-class, that sub-class can then be used to implement additional functionality beyond that supplied by the built-in Exception class.

Here's about as simple an example as I can think of, also making use of set_exception_handler() in order to avoid having to do try/catch:
[code=php]
<?php
/**
* Class we'll use to generate an exception
*/
class Foo {
public function bar($a, $b)
{
if($a != $b) {
throw new FooException("$a does not equal $b.");
}
echo "Yes, $a equals $b.";
}
}

/**
* Class to extend Exception so we can use a custom method
*/
class FooException extends Exception
{
public function myCustomMethod()
{
echo "<p class='error'>Hey, dummy! " . $this->message . "!</p>n";
}
}

/**
* Our custom exception-handler
*/
function myExceptionHandler($exception)
{
if(is_a($exception, 'FooException')) {
$exception->myCustomMethod();
}
else {
echo "<p class='error'>Standard Error: " . $exception->message . "</p>n";
}
}

/**
* Put the exception-handler into effect
*/
set_exception_handler('myExceptionHandler');

/**
* Let's see what happens
*/
$test = new Foo();
$test->bar(3, 3);
$test->bar(2, 3);
[/code]

Output:
<i>
</i>Yes, 3 equals 3.

Hey, dummy! 2 does not equal 3.!
Copy linkTweet thisAlerts:
@iansane6authorSep 03.2011 — oops I typed this one before seeing your example above. Thanks, I'll give it a look and see if I can understand it.

Ok I tried this out and it works
[CODE]
<?php
try {
if(2 != 3){
throw new Exception("ya big dummy! 2 can't equal 3!");
}
} catch (Exception $e) {
echo "Caught my exceptionn", $e;
}
?>
[/CODE]


so I don't understand the purpose of the extending of the exception class. It looks like there would be some benefit to it but why if it's so simple to just do like the code above?

Does it really center around the customFunction doing something when an exception is caught rather than what I was thinking which is that it was to create custom exception messages?
Copy linkTweet thisAlerts:
@iansane6authorSep 03.2011 — Thank you NogDog!

Your use of my silly scenario and message modified to fit in the code makes perfect sence. Especially after understanding the purpose of it better.

I'm actually going to try to apply this to a db class I'm writing for a login script but not sure if there's any reason I would need a custom function to run on exception but maybe I'll think of some use for it just for putting it into practice.

Thanks again!
Copy linkTweet thisAlerts:
@NogDogSep 03.2011 — Like a lot of these things -- especially as you get into OOP PHP -- sometimes you just need to look at it a couple or three different ways until it "clicks" for you. ?
Copy linkTweet thisAlerts:
@iansane6authorSep 04.2011 — I spoke too soon about understanding your example. I think the biggest confusion for me is how to separate this all out into separate script and class files.

I know there are many things wrong with this but am going to post it anyway because it might clarify to you what is so confusing to me.

The main script test.php
[CODE]
<?php
//testing db exception handling

require_once('class_db.php');
set_exception_handler('dbExceptionHandler');
$dbcon = new db();
$dbcon->init_con();

?>
[/CODE]


Database class for connections class_db.php
[CODE]
<?php
require_once('../includes/config.inc.php');
include('class_ehandler_db.php');

class db extends ehandler_db{

function dbExceptionHandler($exception){
if(is_a($exception, 'ehandler_db')){
$exception->customFunction();
}
else{
echo "<p class='error'>Standard Error: " . $exception->message . "</p>n";
}
}



function init_con(){
//initiate the connection
//using constants from config file
try{
$con = mysql_connect('DB_HOST', 'DB_USER', 'DB_PASS');
if(!$con){
throw new ehandler_db("Unable to connect to database");
}
else{
echo "Connection Successful";
}
}
catch (Exception $e){
echo "Exception Caught: " . $e->getMessage();
}
}
?>
[/CODE]



My ehandler specific to the db class class_ehandler_db.php
[CODE]
<?php
/*overrides standard exceptions. Requires php v 5.3.0 +
Code provided by: php.net (http://php.net/manual/en/language.exceptions.extending.php)
Modified by: Ian Simmons
Date: 08-03-11
*/

class ehandler_db extends Exception{
// Redefine the exception so message isn't optional
public function __construct($message, $code = 0, Exception $previous = null) {
// some code

// make sure everything is assigned properly
parent::__construct($message, $code, $previous);
}

// custom string representation of object
public function __toString() {
return __CLASS__ . ": [{$this->code}]: {$this->message}n";
}

public function customFunction() {
echo "<p class='error'>Hey, dummy! " . $this->message . "!</p>n";
}
}

?>
[/CODE]


You'll probably look at this and think "what the heck?!" but I'm hoping this will demonstrate what I want to do and where I am confused transferring the examples from one file into separate files and understanding the logic well enough to follow it.

Also, I'm getting these error messages which looks to me like that missing argument might fix a lot of. It's actually catching my exception and displaying it at the end.

Warning: set_exception_handler() expects the argument (dbExceptionHandler) to be a valid callback in E:shared_www_rootwwwtgprojectswebdesigndemocodeclassesdbexceptiontest.php on line 5 Warning: Missing argument 1 for ehandler_db::__construct(), called in E:shared_www_rootwwwtgprojectswebdesigndemocodeclassesdbexceptiontest.php on line 6 and defined in E:shared_www_rootwwwtgprojectswebdesigndemocodeclassesclass_ehandler_db.php on line 10 Notice: Undefined variable: message in E:shared_www_rootwwwtgprojectswebdesigndemocodeclassesclass_ehandler_db.php on line 14 Warning: mysql_connect(): php_network_getaddresses: getaddrinfo failed: No such host is known. in E:shared_www_rootwwwtgprojectswebdesigndemocodeclassesclass_db.php on line 24 Warning: mysql_connect(): [2002] php_network_getaddresses: getaddrinfo failed: No such host is known. (trying to connect via tcp://DB_HOST:3306) in E:shared_www_rootwwwtgprojectswebdesigndemocodeclassesclass_db.php on line 24 Warning: mysql_connect(): php_network_getaddresses: getaddrinfo failed: No such host is known. in E:shared_www_rootwwwtgprojectswebdesigndemocodeclassesclass_db.php on line 24 Exception Caught: Unable to connect to database


Thanks
Copy linkTweet thisAlerts:
@iansane6authorSep 04.2011 — I caught and fixed the problem where I had quotes around my constants which got rid of those errors.

Now the two things causing problems are:

The missing argument which refers to $message and causes the other errors with the public function and constructor in class_ehandler_db.php

The error for set_exception_hadler() in which case I just don't have a clue where it should go.
Copy linkTweet thisAlerts:
@NogDogSep 04.2011 — I would not recommend that your database class extend an Exception type class, as that is sort of a confusion of responsibilities, and generally you want each class to have a narrow focus. One possibility might be to create sub-classes of Exception if/when desired, using a common method name for your custom exception handler, and check for that method's existence during processing. Your callback function used as a parameter for set_exception_handler() would then either need to be defined separately -- perhaps as part of your general config script -- or as a static method that is part of a utility class. In fact, I might build it into a sub-class of Exception, and then your custom Exception classes would extend that.

CustomException.php:
[code=php]
<?php

abstract class CustomException extends Exception
{
/**
* This method must be defined in each sub-class
* @return string Message to be output by exception handler
*/
public abstract function custom();
/**
* The callback function for set_exception_handler()
*/
public static function handler($e) {
if(is_a($e, 'CustomException')) {
user_error($e->custom());
}
else {
user_error($e->message);
}
}
}
[/code]

DBException.php:
[code=php]
<?php
require_once('path/to/CustomException.php');

/**
* The exception class for our DB class
*/
class DBException extends CustomException
{
public function custom()
{
return ("Hey! You can't do that! Stop it!<br />nERROR: " . $this->message);
}
}
[/code]

DB.php:
[code=php]
<?php
require_once('path/to/CustomException.php');

/**
* The DB class
*/
class DB extends mysqli
{
public function testException()
{
throw new DBException("This is a test.");
}
}
[/code]

sample script:
[code=php]
<?php
require_once('path/to/DB.php'); // will include our Exception classes, too, in this case
// notice use of array() to specify class::method as the callback function:
set_exception_handler(array('CustomException', 'handler'));
$test = new DB('localhost', '####', '####');
$test->testException();
[/code]
Copy linkTweet thisAlerts:
@iansane6authorSep 04.2011 — Thanks for the reply. I don't really understand everything you said.

Actually my goal is to keep the purpose of the classes narrow. I just didn't know what went where with the examples.

Right now I'm getting the same result as I would if I just did
[CODE]
try{
$con = mysql_connect(DB_HOST, 'DB_USER', DB_PASS);
if(!$con){
throw new Exception("Unable to connect to database. This is a test.");
}
else{
echo "Connection Successful";
}
}
catch (Exception $e){
echo "Exception Caught: " . $e->getMessage();
}
[/CODE]


With the intentional 'DB_USER' to make it fail, I get the custom error message I typed into the "throw new Exception()" but I don't need all this code to make that happen. So, back to the purpose of extending Exceptions,

I'm confused by the
[CODE]
public abstract function custom();
[/CODE]


What is its purpose and why I get an error that it can't have a body if I try to add {//any code}. Also, I get the same result with or without that line if I comment it out. Should I be able to put my own function in somewhere that runs when a exception is caught?

This part doesn't seem to be coming into play anywhere.
[CODE]
class ehandler_db extends customException{

public function custom()
{
return ("Hey! You can't do that! Stop it!<br />nERROR: " . $this->message);
}

}
[/CODE]



Here's what I have now, as I understand it.

class_customException.php
[CODE]
<?php
abstract class customException extends Exception{

/**
* This method must be defined in each sub-class
* @return string Message to be output by exception handler
*/

//comented this line out and it made no difference
//tried putting some code in it and get error
public abstract function custom();
/**
* The callback function for set_exception_handler()
*/
public static function handler($e) {
if(is_a($e, 'customException')) {
user_error($e->custom());
}
else {
user_error($e->message);
}
}
}

?>
[/CODE]


class_ehandler_db.php
[CODE]
<?php

require_once('class_customException.php');

class ehandler_db extends customException{

public function custom()
{
return ("Hey! You can't do that! Stop it!<br />nERROR: " . $this->message);
}

}

?>
[/CODE]



class_db.php
[CODE]
<?php
include('../includes/config.inc.php');
require_once('class_ehandler_db.php');

class db{

function init_con(){
//initiate the connection
//using constants from config file

try{
$con = mysql_connect(DB_HOST, 'DB_USER', DB_PASS);
if(!$con){
throw new ehandler_db("Unable to connect to database. This is a test.");
}
else{
echo "Connection Successful";
}
}
catch (Exception $e){
echo "Exception Caught: " . $e->getMessage();
}
}

}
?>
[/CODE]


test.php
[CODE]
<?php
//testing db exception handling
//error_reporting(0);
require_once('class_db.php');
set_exception_handler(array('customException', 'handler'));
$dbcon = new db();
$dbcon->init_con();
?>
[/CODE]


I'm sorry if I'm making this difficult and I really appreciate your time and help.
Copy linkTweet thisAlerts:
@NogDogSep 04.2011 — I created the abstract class CustomException with the idea that it would then be the super-type for any other custom exception class you wanted to define (they would extend CustomException instead of Exception). This way, by defining an abstract custom() method within it (and which, by definition, cannot have a body at that point), all concrete (non-abstract) classes derived from it [i]must[/i] define that method. Therefore, you can know that any object that is_a() CustomException object (has that class as a super-class) will be guaranteed to have a custom() method.

It may seem to be a bit of overkill at this point, but if you think you might ever want to add other specific exception classes that would need to have their own custom() method, you already have everything in place to do so. Certainly it's not the only way (and maybe not even the best), but I figured it was a good illustration of one way to think about it in an OOP way, and follow the dictum to "program to an interface, not to an implementation." ?

Now, after all that philosophy, it may be that you really don't need all that. If all you are concerned about is the error text, you could leave out that abstract class, directly extend the Exception class, and overwrite the __toString() method to get the desired output. ?
Copy linkTweet thisAlerts:
@iansane6authorSep 04.2011 — I've definitely gotten into something that is way over my head.

I don't know the purpose of a super-class really.

I'm wondering what is the purpose of "know that any object that is_a() CustomException object (has that class as a super-class) will be guaranteed to have a custom() method." if the custom() method doesn't do anything.

example of my confusion with this is
[CODE]
class DBException extends CustomException
{
public function custom()
{
return ("Hey! You can't do that! Stop it!<br />nERROR: " . $this->message);
}
}
[/CODE]

I don't see this function being called anywhere. I have it in my code but the output error message I get is not this one. The message I get is the one I put in my "throw new ehandler_db("Cannot connect to database This is a test")" line.

So public function custom() is getting skipped.

I'm also still confused as to the purpose of extending and overriding Exception anyway when I could just make a custom function and call it after the throw line.
Copy linkTweet thisAlerts:
@NogDogSep 05.2011 — Firstly, the only purpose to extend Exception is if you want the new class that extends it to do something that Exception does not already do (or do it in a different way). If there is no such need, then don't bother doing it and just throw an Exception object, instead.

Now, as far as all that other OOP inheritance stuff, the concept is that if we know that cass [b]Foo[/b] has a class variable called [b]$bar[/b] and a method called [b]fubar()[/b], then we know that any class that extends [b]Foo[/b] will include those class properties (along with any new properties they may add. So if class [b]New[/b] extends [b]Foo[/b] -- making [b]New[/b] a sub-class of [b]Foo[/b] (and inversely Foo is a super-class of New) -- then we are guaranteed that [b]New[/b] will include that [b]$bar[/b] variable and [b]fubar()[/b] method (regardless of what else it may have had added to it).

Therefore, we can use type-hinting in a function/method parameter specifying that a parameter is a [b]Foo[/b], and the function will allow an object of [b]Foo[/b] [i]or[/i] [b]New[/b] (since it is a sub-class of Foo) and the function will "know" that that object will include that variable and method. Likewise, the is_a() PHP function returns true if the object supplied to it is of the specified class in its second parameter, or is a sub-type extended from the class (or one of its sub-classes, down any number of levels of inheritance).

PS: I will now make my usual recommendation: get a copy of Matt Zandstras's [i]PHP 5 Objects, Patterns, and Practice[/i], read it once, let it digest a bit, then read it again. (You can skip the "Practice" chapters at the end for now, if you want, as they don't strictly deal with OOP concepts.) That book did wonders for helping me "get it." ?
Copy linkTweet thisAlerts:
@iansane6authorSep 05.2011 — Thank you for recommending that book. I'll go look for it now.

The extent of my past experience with inheritance is that once I made a circle class, ball class, and planet class in C++ where each one inherited parameters from the one above it and then added a couple of it's own parameters to distinguish a planet from a ball from a circle. :-) So I am vaguely aware of how the concept works.

I am also looking for any good video series or learn by examples that are strictly OOP because there is a ton of stuff out there on old school php and I can already write some pretty long and complicated apps doing it old school. I just have to immerse myself into it or lose track of where I'm at since it's all a series of long complicated scripts instead of objects. I've been converting some of them by making classes and using objects such as the database object and page navigation objects but I'm lost on the more complicated stuff.

Oh well, I'll do some more reading and try to find more examples until I "get it"

Thanks :-)
Copy linkTweet thisAlerts:
@iansane6authorSep 06.2011 — Hi NogDog,

I haven't got the book yet but was playing around with the example from php.net today and got it working. It's not using abstract class but I did use your suggestion about not directly extending from the database class.

Now when I pass the constant THROW_CUSTOM as in the php.net example it is throwing my custom message and triggering the custom function which is what I wanted to accomplish. :-)

I'll get that book and keep reading and trying examples. Here's the example applied to my db connection.

script.php
[CODE]
<?php
require_once('class_db.php');
$sql = new db();
$sql->init_con();
?>
[/CODE]


class_db.php
[CODE]
<?php
require_once('class_eHandler_db.php');
error_reporting(0);
class db{
function init_con(){
try {
$con = mysql_connect('localhost', 'user', 'pass');
if(!$con){
throw new eHandler_db(eHandler_db::THROW_CUSTOM);
}

} catch (customException $e) { // Will be caught
echo "Caught db connection exceptionn", $e;
$e->customFunction();
}

}
}
?>
[/CODE]


class_eHandler_db.php
[CODE]
<?php
/**
* Create a class to test the exception
*/

require_once('class_customException.php');

class eHandler_db{
public $var;

const THROW_NONE = 0;
const THROW_CUSTOM = 1;
const THROW_DEFAULT = 2;

function __construct($avalue = self::THROW_NONE) {

switch ($avalue) {
case self::THROW_CUSTOM:
// throw custom exception
throw new customException('custom message about db connection', 5);
break;

case self::THROW_DEFAULT:
// throw default one.
throw new Exception('default message', 6);
break;

default:
// No exception, object will be created.
$this->var = $avalue;
break;
}
}
}

?>
[/CODE]


class_customException.php
[CODE]
<?php
/**
* Define a custom exception class
*/
class customException extends Exception
{
// Redefine the exception so message isn't optional
public function __construct($message, $code = 0, Exception $previous = null) {
// some code

// make sure everything is assigned properly
parent::__construct($message, $code, $previous);
}

// custom string representation of object
public function __toString() {
return __CLASS__ . ": [{$this->code}]: {$this->message}n";
}

public function customFunction() {
echo "<p>An error has occured and a message is being sent to the site administrator</p>";
//code here to collect and send message
}
}

?>
[/CODE]


And the resulting exception message?
[CODE]
Caught db connection exception customException: [5]: custom message about db connection
An error has occured and a message is being sent to the site administrator
[/CODE]


The only issue I'm seeing now is that i'm still calling the custom function from the top class (customException) which is where the code is so it still limits the ability for different eHandlers--as I'm calling them-- to have different actions from the customFunction. But, now that I think about it, since the custom function is called when the exception is caught, I can create a list of custom functions for different purposes and put them in the main customException class and then provide that list in documentation.
Copy linkTweet thisAlerts:
@iansane6authorSep 11.2011 — I think I have finally got this figured out. The "THROW_CUSTOM" parameter in the example on php.net was not even necessary. After starting over many times and writing the code step by step(file by file) several times I was finally able to get the abstract class thing working.

It may not be the best place to use this but it is an example of using abstract class to provide and control a list of functions available to different classes for custom exception messages and functions. The parameters in the constructor of the abstract class can be overridden for custom message and custom error code but the parameters of the custom function are controlled by the list of abstract functions.

Here's my solution for anyone else who needs to see it. Feel free to tell me if I'm still doing this wrong but it is working well for what I was trying to do.

[CODE]
main script index.php in this case
/*****************************************************************************/
<?php
require_once('classes/class_db.php');

$sql = new db();
$sql->init_con();

?>




database object class_db.php
/*****************************************************************************/
<?php
require_once('class_dbException.php');

error_reporting(0);
class db{
function init_con(){
try {
$con = mysql_connect('localhost', 'user', 'pass');
if(!$con){
throw new dbException('Could not connect with the specified data', 5);
}

} catch(ac_customException $e){
echo "Caught db connection exceptionn", $e;
$e->dbNotify($e);
}

}
}
?>


custom database exception class class_dbException.php
/*******************************************************************************/
<?php
require_once('ac_customException.php');

class dbException extends ac_customException{

/*db exception special functions below this line****************************/

public function dbNotify($e){
//dbNotify emails web master with exception info

$host = $_SERVER['SERVER_NAME'];
$remoteIP = $_SERVER['REMOTE_ADDR'];

//testing but in reality would use code to email the info to the web master
//and log to specified log file
echo "<p>Caught db connection exception -- " . $e . "</p>";
echo "<p>From remote IP " . $remoteIP . " on server: " . $host . "</p>";
}

}
?>



abstract custom exception class ac_customException.php
/***************************************************************************/
<?php
abstract class ac_customException extends Exception{

public function __construct($message, $code = 0, Exception $previous = null) {

// make sure everything is assigned properly
parent::__construct($message, $code, $previous);
}

// custom string representation of object
public function __toString() {
return __CLASS__ . ": [{$this->code}]: {$this->message}n";
}

/*list abstract classes for use by sub classes below this line*********/

abstract public function dbNotify($e); //notify web master
abstract public function navNotify($e); //notify web master

}

[/CODE]


Now I can use the same abstract class for example, a navigation class and it contains a navNotify function like the dbNotify function above. Both functions are limited to recieving the exception message for use in the notification. At this point my function can do what ever it wants as long as the abstract function exists in the abstract class.

Thanks to NogDog for getting me going in the right direction :-)
×

Success!

Help @iansane6 spread the word by sharing this article on Twitter...

Tweet This
Sign in
Forgot password?
Sign in with TwitchSign in with GithubCreate Account
about: ({
version: 0.1.9 BETA 5.6,
whats_new: community page,
up_next: more Davinci•003 tasks,
coming_soon: events calendar,
social: @webDeveloperHQ
});

legal: ({
terms: of use,
privacy: policy
});
changelog: (
version: 0.1.9,
notes: added community page

version: 0.1.8,
notes: added Davinci•003

version: 0.1.7,
notes: upvote answers to bounties

version: 0.1.6,
notes: article editor refresh
)...
recent_tips: (
tipper: @AriseFacilitySolutions09,
tipped: article
amount: 1000 SATS,

tipper: @Yussuf4331,
tipped: article
amount: 1000 SATS,

tipper: @darkwebsites540,
tipped: article
amount: 10 SATS,
)...