Saturday, March 12, 2011

PHP: Your exception classes

Exceptions

This article is the second part of PHP Exception articles. In the previous one, I've written about Exceptions, I've showed you the basic usage and I've provided some examples. It won't be different this time, but I'm going to be a little more advanced. This article is about how to write your own classes.

Reminder: Exception is a special condition that changes the normal flow of the code.


Extending exceptions

You can extend the core exception class in PHP to have your own class which will suit for the given tasks ahead. It's a good way for categorisation of errors. What I mean is, you can make input exceptions, error exceptions, email exceptions. Another example is if you use the MVC programming pattern, you can make classes related to each layer as follows: ModelException, ViewException, ControllerException. After this, you can extend more classes depending on the occuring errors in each layer.

But let's return from programming patterns to the example code below. Take a look at it. :)

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<?php

 class InputException extends Exception {};

 class SomethingBadException extends Exception {};

 class SomethingReallyBadException extends Exception {};


 // ...

 class foo {

  // ...

  function let_it_happen($input) {

   if( !validate_input($input) ) {

    throw new InputException('Invalid input given.');

   }


   if( !calculate_something($input) ) {

    throw new SomethingBadException('Something bad happenned.');

   } else
   {

    if( !generate_something($input) ) {

     throw new SomethingReallyBadException('Something really terrible happened.');

    }

   }

   // ...

  }

  // ...

 }


 // later in the code...

 $bar = new foo();

 try {


  $bar->let_it_happen();


 }
 catch (InputException $e) {

  echo 'Input error: '.$e->getMessage();

  // do something here, like logging
  log('Error: '.$e->getMessage());

 }
 catch (SomethingBadExcpetion $e) {

  echo 'Error: '.$e->getMessage();

  log('Error: '.$e->getMessage());

  handle_some_stuff();


 }
 catch (SomethingReallyBadException $e) {

  echo 'Fatal error: '.$e->getMessage();

  log('Fatal error: '.$e->getMessage());

  $email->notify_admin('www.site.com - problem',"The following thing happened: ".$e->getMessage()."\n Log in immediately.");

 }


?>

I've defined three new exceptions, and I've also written a class where these exceptions appear. Notice the multiple catch blocks. You have to catch all kind of exceptions by making catch blocks for each of the classes. If an exception occurs you can make the necessary steps in each of the blocks to set variables, warn the user, log or notify someone.

You can also add your custom properties and methods to an extended class. But be careful with this! I say this because if you add a method to your exception class that uses an object - for example email - and you throw this exception in a context where that object is not available or currently not loaded, you're shot and you can start debugging and hacking. Please, prevent this and design your application correctly. The code:

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php  
 
 class BadBadException extends Exception {
  
  private $__variable;
  
  function set_variable($in) {
   
   $this->__variable = $in;
   
  }
  
  function get_variable() {
   
   return $this->__variable;
   
  }
  
  function handleBadException() {
   
   log($this->getMessage());
   
   do_something_necessary();
   
  }
  
 }
 
 // ...
 
 try {
  
  throw new BadBadException('bad bad message');
  
 } 
 catch (BadBadException $e) {
  
  echo $e->getMessage();
  
  $e->handleBadException();
  
 }
 

?>

Final words

Deciding to use exceptions is a good start to make your application more logical, more understandable and what is much more important, more flexible.

Zsolt Szivák

Monday, March 7, 2011

Javascript: validating object interfaces

The interface is one of the most used tool in object-oriented programming. Based on the Gang of Four’s Design Patterns book, it is the first principle of reusable OOP. Unfortunately Javascript can’t implement any interfaces. At least not the usual way. And that’s where my interface class comes in. :)

I got the basic idea from the book Pro Javascript Design Patterns [Apress]. The authors have mentioned three types of emulating an interface in Javascript.

  1. Comments:
    "The easiest and least effective way of emulating an interface is with comments. Mimicking the style of other object-oriented languages, the "interface" and "implements" keywords are used but are commented out so they do not cause syntax errors."
  2. Attribute checking:
    A bit better, but it can be easily tricked out by a careless programmer. In this method when creating a class, it is left to the programmer's conscience to decide which interface is implemented. The interface name is stored in an array. Then, when an interface check is required, a function is validating the content of this array. If it says the class is implementing the given interface, the function believes it and the program flow continues.
    Personally, I don’t really like this method.
  3. Duck typing:
    This is where I have had my inspiration :) There is a line in the book, I'm always smiling when I'm reading it: "Duck typing was named after the saying, 'If it walks like a duck and quacks like a duck, it's a duck.' " :)
    Well, I was quite satisfied with this solution. However the example, that the authors were providing is only checking, if the object has the given methods defined.

My improved interface class:

So I decided to improve this Duck typing method, and add some additional functionality. With my interface class, instead of just checking the methods of the class (or object), we can:
  • check variable and method existence
  • check method’s parameters
  • check method return value
  • check variable value
  • define callback error handling function
  • and we can even define own interface functions, to validate the internal state of the object.
It's quite exciting :)

Here is a brief explanation, of how is this class used:
1.) Define an interface:
1
2
3
4
5
var exampleInterface = new Interface("example", { 
   /* requirements*/
}, {
   /* functions called on error */
})

2.) Create your object

3.) Validate the object's state: by passing the validated object and the interface objects as parameters
1
Interface.check( /* object to validate */, /* interface object */[...]);


Let's see some comment-explained code. First, here are some example interfaces defined:

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// defined interfaces to implement
// checks all these variables. They have to be defined.
var InterfaceVariables = new Interface('InterfaceVariables', {

   // checks, if it is true
   isBooleanVal: true, 

   // just check the variable if it is defined
   isNullVal: null,

   // this string has to contain this value
   stringVal: 'required string value',

   // variable check with interface function. In this example, a variable has to be greater than one. It must always have a bool return.
   isGreaterThanOne: function(obj) { 
      return obj.isGreaterThanOne > 1;
   },

   // array check. here are other items not allowed
   allowJustThese: ['item1', 'item2', 'item3'],

   // array check. other items are allowed
   theseAreRequired_butOthersAllowedToo: ['item1', 'item2', 'item3', ".."],


}, {
   // this object contains the error handling functions

 
   // this runs only if the isBooleanVal is not true
   isBooleanVal: function (intf, obj) {alert("isBooleanVal is not true. Required interface: "+intf.name+".");},

   // and this runs only if the stringVal value is not the required one.
   stringVal: function (intf, obj) {alert("stringVal value is not valid. Required interface: "+intf.name+".");}

});

var InterfaceMethods = new Interface('InterfaceMethods', {

   // checks if method has only these two params. if others are also enabled, put ".." in the end
   // ret: boolean      checks method's return value. Params: (expected return [, arg1, arg2, ...] )
   methodParamCheckAndReturnCheck: {
      args: ['num1', 'num2'], 
      ret: [12, 2, 3] 
   },

   // i think this is clear
   // method must not have parameters
   // ret: true      expected return value is 6
 methodNoParamAndReturnsSix: { 
      args: [],
      ret: 6
   },

   // checks if this is a function plus the parameters
   checkMethodParams: ['arg1', 'arg2', '..'],

   // just check function existence
   isFunctionDefined: null,

   // no parameters allowed
   isFunctionWithNoParams: [] 

},{
   methodNoParamAndReturnsSix: function (intf, obj) { alert("Error with method: methodNoParamAndReturnsSix. Interface: "+intf.name+"."); }
});


For example in the first interface I defined only variable's, and in the second one the methods' rules. Staying at the example, here is a class which "implements" these interfaces:

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// class that has to implement the interface
var classToImplementInterface = (function() {

   // this variable should be true, so the interface callback function will be called
   this.isBooleanVal = false;
   this.isNullVal = null;
   this.stringVal = "required string value";
   this.isGreaterThanOne = 2;

   var privateVar = 'private'; // just for fun... (a class' interface is its public variables and methods, if you wouldn't know that :) )
   var privateMultiplyer = 2;

   this.methodParamCheckAndReturnCheck = function(num1, num2) {
      return num1 * num2 * privateMultiplyer;
   }

   // this method should return 6. So this will generate an error.
   this.methodNoParamAndReturnsSix = function() {
      return 5;
   }

   this.allowJustThese = Array('item1', 'item2', 'item3');
   this.theseAreRequired_butOthersAllowedToo = Array('item1', 'item2', 'item3', "item4", "item5");

   this.checkMethodParams = function (arg1, arg2, arg3) {}

   this.isFunctionDefined = function ( can, have, params ) {}

   this.isFunctionWithNoParams = function () {} // must not have params


 
})


And here is, how you can evaluate the rules:

1
2
3
4
5
6
7
/////////////////////////////////////////////////////////////////////
//       CONCRETE OBJECT to IMPLEMENT INTERFACES
/////////////////////////////////////////////////////////////////////
var concreteObject = new classToImplementInterface();

// before doing something validate object's interface
Interface.check(concreteObject, InterfaceVariables, InterfaceMethods);


A concrete example:

Let's say, there is a car class. A method is called which is responsible for moving objects on the canvas, and the car object is passed in as a parameter. However, to move that car, there are some object requirements. For example, is someone driving it? Or is the door closed?. These can be solved with variable checking. Then…has the given car object all the methods which are used later on? Or is the body state higher than 10%? This can be solved with an anonym function. These requirements must be satisfied for error-free run.


Well that's it. I'm quite satisfied with the results. It's small and simple. Probably it will be used only in development phase, so I haven't done any speed optimization.

If you are interested in further development, please let me know!

Source files:

  • index.html
  • interface.js
  • interface.min.js
  • functions.js

Download:




I'm very enthusiastic and appreciate every donation. I will spend the full amount on books, to improve my skills and create more projects like this (and even better).

Thanks,
Phil



Changelog


  • 1.1:
    • Added callback functionality
    • Callback parameters: ( interface object, checked object )
  • 1.0:
    • variable and method existence
    • method's parameters
    • method return value
    • variable value
    • and we can even define own interface functions, to validate the internal state of the object.