Here is my attempt at a PHP class to be used as a base class for typed collections. My goals with this are:
- to be able to access collection members with foreach
- to be able to access collection members using indices (array-like)
- to ensure that all collection members are objects of the same type
I have implemented the appropriate interfaces. Here is the code:
<?php
/**
* A class to hold a collection of objects of the same type.
*
* There are tow purposes:
* - to ensure that all elements are of the same type
* - to be enumerable by the foreach loop
*/
abstract class TypedCollection implements Countable, Iterator, ArrayAccess
{
/**
* Child classes pass the collection type to this constructor
*
* @param string $typeName The name of the underlying class
* @throws InvalidArgumentException If the class can't be found by reflection
*/
public function __construct($typeName)
{
if(class_exists($typeName))
{
$this->_type = new ReflectionClass($typeName);
}
else
{
throw new InvalidArgumentException(
"The class $typeName does not exist.");
}
}
/* Implement Countable */
/**
* Returns the count of the elements in the collection
* @return int
*/
public function count()
{
return count($this->_innerArray);
}
/* Implement Iterator */
/**
* Returns the current element of the collection
* @return Object
*/
public function current()
{
return current($this->_innerArray);
}
/**
* Return the key of the current element
* @return int
*/
public function key()
{
return key($this->_innerArray);
}
/**
* Move forward to next element
*/
public function next()
{
next($this->_innerArray);
}
/**
* Rewind to its first element
*/
public function rewind()
{
reset($this->_innerArray);
}
/**
* Checks if the current position is valid
* @return bool
*/
public function valid()
{
return $this->current() !== false;
}
/* Implement ArrayAccess */
/**
* Whether an offset exists
* @param int $offset
* @return bool
*/
public function offsetExists($offset)
{
return isset($this->_innerArray[$offset]);
}
/**
* Returns the value at the specified offset
* @param int $offset
* @return int
*/
public function offsetGet($offset)
{
if(isset($this->_innerArray[$offset]))
{
return $this->_innerArray[$offset];
}
else
{
return null;
}
}
/**
* Assigns a value to the specified offset
* @param int $offset
* @param mixed $object
* @throws InvalidArgumentException If $object is not of the underlying type
*/
public function offsetSet($offset, $object)
{
if ($object instanceof $this->_type->name)
{
if (is_null($offset))
{
$this->_innerArray[] = $object;
}
else
{
$this->_innerArray[$offset] = $object;
}
}
else
{
throw new InvalidArgumentException(
"Object needs to be a $this->_type->name instance.");
}
}
/**
* Unsets an offset
* @param int $offset
*/
public function offsetUnset($offset)
{
unset($this->_innerArray[$offset]);
$this->_innerArray = array_values($this->_innerArray);
}
/* TypedCollection functions */
/**
* Adds an object to the collection.
* @param mixed $object The object to add to the collection
*/
public function add($object)
{
$this->offsetSet(null, $object);
}
/**
* Removes all elements from the collection
*/
public function clear()
{
$this->_innerArray = array();
}
/**
* Checks if an object belongs to the collection
* @param mixed $object
* @return bool
*/
public function contains($object)
{
return in_array($object, $this->_innerArray, true);
}
/**
* Return an object index
* @param mixed $object
* @return int
*/
public function indexOf($object)
{
return array_search($object);
}
/**
* Inserts an object at the specified offset in the collection
* @param int $offset
* @param mixed $object
* @throws InvalidArgumentException If the object is not of the underlying type
* @throws OutOfRangeException If the offset does not exist
*/
public function insert($offset, $object)
{
if(array_key_exists($offset, $this->_innerArray))
{
if($object instanceof $this->_type->name)
{
$tempArray = array($object, $this->offsetGet($offset));
array_splice($this->_innerArray, $offset, 1, $tempArray);
$this->_innerArray = array_values($this->_innerArray);
}
else
{
throw new InvalidArgumentException(
"Object needs to be a $this->_type->name instance.");
}
}
else
{
throw new OutOfRangeException(
"The index $offset does not exist in the collection.");
}
}
/**
* Removes the specified object from the collection
* @param mixed $object
*/
public function remove($object)
{
if($this->contains($object))
{
$this->offsetUnset(array_search($object, $this->_innerArray));
$this->_innerArray = array_values($this->_innerArray);
}
}
/**
* Removes the object at the specified offset in the collection
* @param int $offset
*/
public function removeAt($offset)
{
$this->offsetUnset($offset);
$this->_innerArray = array_values($this->_innerArray);
}
/**
* The array that contains collection elements
* @var array
*/
protected $_innerArray = array();
/**
* The collection type
* @var ReflectionClass
*/
protected $_type;
}
?>
A child class:
<?php
include_once 'TypedCollection.php';
include_once 'Statement.php';
class StatementCollection extends TypedCollection
{
public function __construct()
{
parent::__construct('Statement');
}
}
?>
How I use it:
$for2->Statements[0]->TrueStatements->add($break);
$for2->Statements->add($echo1);
Any thoughts on the code are welcome.