William Graham’s blog

May 21, 2007

Typesafe arrays for PHP

Filed under: PHP,Programming — liamgraham @ 6:16 pm

PHP is not a strongly typed language. This makes for very convenient and quick coding, but it can also lead to bugs and confusion. I noticed recently that, most likely due to my own paranoia (and Java background), I was checking the types of array elements every time I accessed them, just to make sure I was dealing with ints when I thought I was dealing with ints, etc. I decided that I’d rather just find a way to make some typesafe array classes to use … so that’s what I did.

There is an ArrayObject class in PHP5 which can be subclassed (not sure about PHP4, since I’m new to PHP and jumped straight into v5.x). If you have an ArrayObject $myArray, there are two ways in which you can append a new element:

$myArray->append($foo);

and

$myArray[] = $foo;
//or, e.g., $myArray[3] = $foo, if you want to alter the 4th element of the array

Affecting the behavior of the first one is pretty obvious: override the append method in the subclass, adding type checking. The second one is a little more obscure, and I had to search for a while to find out how to affect the [] operations. I was looking for C++-style operator overloading, where I could somehow define what ‘[]’ meant in my class. PHP doesn’t do this, except for, I believe, in a PECL extension. Finally I found a bulletin board post mentioning that the offsetSet and offsetGet methods of ArrayObject actually control what happens with the square bracket method of array access, so I was set to go.

Initially I wrote four subclasses of ArrayObject: IntArray, FloatArray, StringArray, and BooleanArray, each of which overrode the append and offsetSet methods from the parent class. However, I then did some refactoring, realizing that there was code that could be factored to a base class. So I defined an abstract base class, TypeSafeArray, which looks like this:

————————————————————————-

/**
* @desc Abstract class extending ArrayObject. Used as base class for
* type-specific array subclasses. Each subclass must implement
* the abstract method typeCheck to define its appropriate type
* checking behavior
*
* @package php_wrappers.arrays
* @author Bill Graham, 20070514
*
*/

abstract class TypeSafeArray extends ArrayObject
{
/**
* @desc Overriding ArrayObject::append to add typechecking
* @param mixed $val – a value to append to the array
*/
public function append($val)
{
$this->typeCheck($val);
parent::append($val);
}

/**
* @desc Overriding ArrayObject::offsetSet to add typechecking; this method is called when values are set using the []
* @param int $idx – the index of the array element to set
* @param mixed $val – the value to set
*/
public function offsetSet($idx, $val)
{
$this->typeCheck($val);
parent::offsetSet($idx, $val);
}

/**
* @desc abstract method must be implemented by subclasses to define type-checking behavior
* @param mixed $val – the value to be type-checked
*/
abstract protected function typeCheck($val);
}

————————————————————————-
By doing this, the type-specific arrays can simply extend the TypeSafeArray class, and provide an implementation of the abstract typeCheck method. As an example, here’s my IntArray class:

————————————————————————-

/**
* @desc Array class to specifically hold integer values
*
* @package php_wrappers.arrays
* @author Bill Graham, 20070510
*/

require_once(‘TypeSafeArray.php’);

class IntArray extends TypeSafeArray
{
/**
* @desc implementation of abstract method from TypeSafeArray to define type-checking behavior
* @param mixed $val – the value to be type-checked
*/
protected function typeCheck($val)
{
if (!is_int($val))
{
throw new Exception(“Non-integer value ‘” . $val . “‘ supplied to IntArray”);
}

return true;
}
}

————————————————————————-

Any other type-specific array classes will be similar … simply provide an implementation of the typeCheck method which does whatever is needed to insure that only arguments of the correct type are appended to the array. Below is a simple test script showing the classes in action:

————————————————————————-

require_once(‘IntArray.php’);

$set = new IntArray();

try
{
//legal operations. these should succeed …
$set->append(1);
$set[] = 2;
$set[2] = 3;

//illegal operations. each will throw an exception. uncomment any line you want to try …
$set->append(1.2);
//$set[] = “foo”;
//$set[2] = 3.2;
}
catch (Exception $e)
{
print “Exception encountered appending to ” . get_class($set) . “:\n”;
print “\t” . $e->getMessage() . “\n”;
}

foreach($set as $val)
{
print $val . “\n”;
}

————————————————————————-

Okay, there we go. I find these classes to be very useful. I may be in the minority, but I really do want type safety as an option for my PHP development.

Finally, I certainly don’t claim to be the only person, or the first person who has thought of this. I didn’t run across any others in my searches, although I did find a few similar things more focused on ‘collection’ classes. I’d love to know of other solutions or implementations, so email me or post a comment. Thanks!

-William

Blog at WordPress.com.