_checkCallback(); //_checkCallback is defined a little later if($key) { if(isset($this->_members[$key])) { throw new KeyInUseException("Key \"$key\" already in use!"); } else { $this->_members[$key] = $obj; } } else { $this->_members[] = $obj; } } /** * Remove an item from the Collection * * @param mixed $key */ public function removeItem($key) { $this->_checkCallback(); if(isset($this->_members[$key])) unset($this->_members[$key]); else throw new KeyInvalidException("Invalid key \"$key\"!"); } /** * Get an item from the Collection * * @param mixed $key * @return mixed */ public function getItem($key) { $this->_checkCallback(); if(isset($this->_members[$key])) return $this->_members[$key]; else throw new KeyInvalidException("Invalid key \"$key\"!"); } /** * Returns an array keys used in this Collection * * @return array */ public function keys() { $this->_checkCallback(); return array_keys($this->_members); } /** * Get the size of this collection (the number of items) * * @return integer */ public function length() { $this->_checkCallback(); return sizeof($this->_members); } /** * Check whether or not an item exists with the specified key * * @param mixed $key * @return boolean */ public function exists($key) { $this->_checkCallback(); return (isset($this->_members[$key])); } /** * Use this method to define a function to be * invoked prior to accessing the collection. * The function should take a collection as a * its sole parameter. * * @param string $functionName * @param string[optional] $objOrClass * @return boolaen */ public function setLoadCallback($functionName, $objOrClass = null) { if($objOrClass) $callback = array($objOrClass, $functionName); else $callback = $functionName; //make sure the function/method is valid if(!is_callable($callback, false, $callableName)) { throw new Exception("$callableName is not callable " . "as a parameter to onload"); return false; } $this->_onload = $callback; } /** * Check to see if a callback has been defined and if so, * whether or not it has already been called. If not, * invoke the callback function */ private function _checkCallback() { if(isset($this->_onload) && !$this->_isLoaded) { $this->_isLoaded = true; call_user_func($this->_onload, $this); } } /** * Get a CollectionIterator for this Collection * * @return CollectionIterator */ public function getIterator() { $this->_checkCallback(); return new CollectionIterator($this); } /** * Returns whether or not this collection is paginatable * In other words, whether it has both per_page and page_number * specified * * @return boolean */ public function canPaginate() { if ($this->per_page && $this->page_number) return true; else return false; } /** * Sets up this collection to be separted into different pages * * @param integer $per_page * @param integer $page_number */ public function paginate($per_page, $page_number) { $this->per_page = $per_page; $this->page_number = $page_number; if (!$page_number || $page_number < 1) $page_number = 1; elseif ($page_number > $this->num_pages()) $page_number = $this->num_pages(); $this->page_number = $page_number; } /** * Returns the number of items there should be per page * * @return integer */ public function per_page() { return $this->per_page; } /** * Returns the current page number * * @return integer */ public function page_number() { return $this->page_number; } /** * Returns the number of pages of items that this Collection * can display, or returns zero (0) if pagination has not * been set * * @return integer */ public function num_pages() { if ($this->canPaginate()) return ceil($this->length() / $this->per_page); else return 0; } /** * Creates and prints a simple HTML menu, listing off page numbers as links * * @param string $page The URL of the page, including querystring data */ public function pageMenu($page) { if ($this->canPaginate()) { // strip out any previous occurances of &p=<#> from the page url $page = preg_replace("(&p=[0-9]+)", "", $page); $output = ""; for($i = 1; $i <= $this->num_pages(); $i++) { $this_page = $i == $this->page_number; $output .= ($this_page ? "" : "") . "{$i}" . ($this_page ? "" : "") . " "; } print "

Pages: " . substr($output, 0, -1) . "

"; } } } class CollectionIterator implements Iterator { /** * The pointer to the collection that is being iterated * * @var Collection */ private $_collection; /** * The current index position through the iterator * * @var integer */ private $_currIndex = 0; /** * The array of keys used in the collection * * @var array */ private $_keys; /** * The current position used by pagination * * @var integer */ private $_currPos = 0; /** * Construct a new CollectionIterator * * @param Collection $objCol */ function __construct(Collection $objCol) { $this->_collection = $objCol; $this->_keys = $this->_collection->keys(); } /** * Go to the page specified by fast-forwarding (CollectionIterator::next()) * to the current position * * @param integer $page The number of the page that should be fast forwarded to */ function goToPage($page) { for ($i = 0; $i < ($page - 1) * $this->_collection->per_page() ; $i++) $this->next(true); } /** * Go back the beginning of this Iterator * */ function rewind() { $this->_currPos = 0; if ($this->_collection->canPaginate()) $this->goToPage($this->_collection->page_number()); else $this->_currIndex = 0; } /** * Returns whether or not you are at the end of the Iterator * * @return boolean */ function valid() { return $this->_currIndex < $this->_collection->length(); } /** * Returns the key for the current Iterator item * * @return mixed */ function key() { return $this->_keys[$this->_currIndex]; } /** * Returns the current item * * @return GenericObject */ function current() { return $this->_collection->getItem($this->_keys[$this->_currIndex]); } /** * Go to the next item in the Iterator * * @param boolean[optional] $ignore Species whether or not to ignore keeping track of items-per-page with pagination */ function next($ignore = false) { // check if it can paginate and it's not being ignored if (!$ignore && $this->_collection->canPaginate()) { // increment the current position on the page $this->_currPos++; if ($this->_currPos >= $this->_collection->per_page()) { // it's past the current page position $this->_currIndex = $this->_collection->length() + 1; return; } } $this->_currIndex++; } } ?>