Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit of all SPL examples.

  • Loading branch information...
commit c1ae90b63a3cf8da9a41d203efef252cd4bd7363 0 parents
@cdugd cdugd authored
Showing with 2,191 additions and 0 deletions.
  1. 0  README.md
  2. +88 −0 caching-iterator.php
  3. 0  directory-iterator-example/directory-iterator-recursive/file1.txt
  4. 0  directory-iterator-example/directory-iterator-recursive/file2
  5. 0  directory-iterator-example/directory-iterator-recursive/file3
  6. 0  directory-iterator-example/directory-iterator-recursive/hello-world
  7. 0  directory-iterator-example/file1.txt
  8. 0  directory-iterator-example/file2.php
  9. 0  directory-iterator-example/file2.txt
  10. 0  directory-iterator-example/file3
  11. 0  directory-iterator-example/file4.txt
  12. 0  directory-iterator-example/file5
  13. 0  directory-iterator-example/second-directory/newfile.php
  14. 0  directory-iterator-example/second-directory/third-directory/you_found_me
  15. 0  directory-iterator-example/test.php
  16. +239 −0 directory-iterator-pt2.php
  17. +15 −0 directory-iterator.php
  18. +33 −0 directory-iterator/directory-match.php
  19. +51 −0 directory-iterator/directory-tree.php
  20. +40 −0 directory-iterator/filter-dots.php
  21. +60 −0 directory-iterator/filter-extension.php
  22. +43 −0 directory-iterator/filter-key.php
  23. +91 −0 filter-iterator.php
  24. +30 −0 iterator-callback.php
  25. +169 −0 limit-iterator.php
  26. +193 −0 observable.php
  27. +194 −0 observer-exception-handler.php
  28. +408 −0 observer-subject-events.php
  29. +149 −0 pdo-iterator.php
  30. +199 −0 recursive-caching-iterator.php
  31. +17 −0 recursive-directory-iterator.php
  32. +31 −0 recursive-regex-iterator.php
  33. +29 −0 regex-iterator.php
  34. +112 −0 simplexml-iterator.php
0  README.md
No changes.
88 caching-iterator.php
@@ -0,0 +1,88 @@
+<?php
+/**
+ * An example of using the caching iterator to perform a look-ahead for the last element
+ * in a single dimension "navigation" array so we can accurately set classes for "last".
+ *
+ * Please note:
+ * No safety measures have been taken to sanitize the output.
+ *
+ * @author Corey Ballou
+ */
+
+// example navigation array
+$nav = array(
+ 'Home' => '/home',
+ 'Products' => '/products',
+ 'Company' => '/company',
+ 'Privacy Policy' => '/privacy-policy'
+);
+
+// storage of output
+$output = new ArrayIterator();
+
+try {
+
+ // create the caching iterator of the nav array
+ $it = new CachingIterator(new ArrayIterator($nav));
+ foreach ($it as $name => $url) {
+ if ($it->hasNext()) {
+ $output->append('<li><a href="' . $url . '">' . $name . '</a></li>');
+ } else {
+ $output->append('<li class="last"><a href="' . $url . '">' . $name . '</a></li>');
+ }
+ }
+
+ // if we have values, output the unordered list
+ if ($output->count()) {
+ echo '<ul id="nav">' . "\n" . implode("\n", (array) $output) . "\n" . '</ul>';
+ }
+
+} catch (Exception $e) {
+ die($e->getMessage());
+}
+
+/**
+ * Below is the same example, but prettified in a nice, extensible class
+ * allowing you to reuse it for nav, subnav, or any time you need to
+ * determine the last element of an array.
+ */
+class NavBuilder extends CachingIterator {
+
+ /**
+ * Override the current() method to modify the return value
+ * for the given index.
+ *
+ * @access public
+ * @return string
+ */
+ public function current()
+ {
+ // get the name and url of the nav item
+ $name = parent::key();
+ $url = parent::current();
+
+ // determine if we're on the last element
+ if ($this->hasNext()) {
+ return '<li><a href="' . $url . '">' . $name . '</a></li>';
+ } else {
+ return '<li class="last"><a href="' . $url . '">' . $name . '</a></li>';
+ }
+ }
+
+ /**
+ * Outputs the navigation.
+ */
+ public function generate()
+ {
+ $inner = $this->getInnerIterator();
+ var_dump(get_class_methods($inner));
+ }
+
+}
+
+try {
+ $it = new NavBuilder(new ArrayIterator($nav));
+ echo $it->generate();
+} catch (Exception $e) {
+ var_dump($e); die;
+}
0  directory-iterator-example/directory-iterator-recursive/file1.txt
No changes.
0  directory-iterator-example/directory-iterator-recursive/file2
No changes.
0  directory-iterator-example/directory-iterator-recursive/file3
No changes.
0  directory-iterator-example/directory-iterator-recursive/hello-world
No changes.
0  directory-iterator-example/file1.txt
No changes.
0  directory-iterator-example/file2.php
No changes.
0  directory-iterator-example/file2.txt
No changes.
0  directory-iterator-example/file3
No changes.
0  directory-iterator-example/file4.txt
No changes.
0  directory-iterator-example/file5
No changes.
0  directory-iterator-example/second-directory/newfile.php
No changes.
0  directory-iterator-example/second-directory/third-directory/you_found_me
No changes.
0  directory-iterator-example/test.php
No changes.
239 directory-iterator-pt2.php
@@ -0,0 +1,239 @@
+<?php
+require_once(__DIR__ . DIRECTORY_SEPARATOR . 'directory-iterator' . DIRECTORY_SEPARATOR . 'filter-dots.php');
+require_once(__DIR__ . DIRECTORY_SEPARATOR . 'directory-iterator' . DIRECTORY_SEPARATOR . 'filter-extension.php');
+require_once(__DIR__ . DIRECTORY_SEPARATOR . 'directory-iterator' . DIRECTORY_SEPARATOR . 'filter-key.php');
+require_once(__DIR__ . DIRECTORY_SEPARATOR . 'directory-iterator' . DIRECTORY_SEPARATOR . 'directory-tree.php');
+require_once(__DIR__ . DIRECTORY_SEPARATOR . 'directory-iterator' . DIRECTORY_SEPARATOR . 'directory-graph.php');
+require_once(__DIR__ . DIRECTORY_SEPARATOR . 'directory-iterator' . DIRECTORY_SEPARATOR . 'directory-match.php');
+
+/**
+ * This class contains example usage for the entire directory-iterator sub
+ * directory.
+ *
+ * http://www.phpclasses.org/package/4389-PHP-Retrieve-directory-listings-with-SPL-iterators.html
+ *
+ * @author Paul Scott <pscott@uwc.ac.za>
+ */
+class DirectoryUsage
+{
+ /**
+ * Recursively list the contents of a directory. Second parameter allows you
+ * to specify whether you'd like to only return a list of directories or files.
+ *
+ * @param string $dir
+ * @param string $type ['file'|'dir']
+ * @return array
+ */
+ public function dirListByType($dir, $type = 'file')
+ {
+ $output = array();
+
+ $it = new RecursiveDirectoryIterator($dir);
+ $it = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
+ foreach ($it as $file) {
+ if ($file->getType() == $type) {
+ $output[] = $file->getPathname();
+ }
+ }
+
+ return $output;
+ }
+
+ /**
+ * Example of using SPL to clean up directories by removing specific filenames.
+ *
+ * @access public
+ * @param string $directory
+ * @param array $filter
+ */
+ public function cleanDir($directory, $filter = array('_vti_cnf', '_vti_private', '_vti_txt', '_private', '_themes', 'msupdate', 'vti_pvt', 'vti_script', '_vti_log', '_template','Thumbs.db'))
+ {
+ $it = new RecursiveDirectoryIterator($directory);
+ $it = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
+ foreach ($it as $file) {
+
+ // remove empty dirs
+ if (sizeof($file->getSize()) == 0) {
+ unlink($file->getPath());
+ }
+
+ // remove instances of Thumbs.db
+ if ($file->getFileName() == 'Thumbs.db') {
+ unlink($file->getPath() . DIRECTORY_SEPARATOR . $file->getFilename());
+ }
+
+ // if paths match filter, delete directory recursively
+ $parts = explode(DIRECTORY_SEPARATOR, $file->getPath());
+ if(in_array(end($parts), $filter)) {
+ $this->deleteDir($file->getPath());
+ }
+
+ }
+ }
+
+ /**
+ * Method to get information about all the files in a directory (recursive)
+ *
+ * @param string $directory
+ * @param array $filter
+ * @return array
+ */
+ public function fileInfo($directory, $filter = array('php', 'xsl', 'xml', 'htm', 'html','css'))
+ {
+ $count_directories = 0;
+ $count_files = 0;
+ $count_lines = 0;
+ $count_bytes = 0;
+
+ $it = new RecursiveDirectoryIterator($directory);
+ $it = new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::CHILD_FIRST);
+
+ foreach ($it as $file) {
+ if (false === $file->isDir()) {
+ // get the file extension
+ $ext = $file->getExtension();
+ if (in_array($ext, $filter)) {
+ $count_files++;
+ $count_bytes += $file->getSize();
+ $count_lines += sizeof(explode("n", file_get_contents($file->getPathName())));
+ }
+ } else if(false === strpos($file->getPathname(), 'CVS') && $file->isDir()) {
+ $count_directories++;
+ }
+ }
+
+ return array(
+ 'bytes' => $count_bytes,
+ 'files' => $count_files,
+ 'lines' => $count_lines,
+ 'directories' => $count_directories
+ );
+ }
+
+ /**
+ * Recursively delete a directory and all subdirectories.
+ *
+ * @access public
+ * @param string $dir
+ * @return void
+ */
+ public function deleteDir($dir)
+ {
+ $it = new RecursiveDirectoryIterator($dir);
+ $it = new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::CHILD_FIRST);
+ foreach ($it as $file) {
+ if ($file->isDir()) {
+ rmdir($file->getPathname());
+ } else {
+ unlink($file->getPathname());
+ @rmdir($dir);
+ }
+ }
+ @rmdir($dir);
+ }
+
+ /**
+ * Find a file by regex in a given directory.
+ *
+ * @param string $path
+ * @param string $regex
+ * @return array
+ */
+ public function fileFinder($path, $regex)
+ {
+ $matches = array();
+
+ $fileList = new DirMatch($path, $regex);
+ foreach ($fileList as $file) {
+ $matches[] = $file;
+ }
+
+ return $matches;
+ }
+
+ /**
+ * List files in a given directory.
+ *
+ * @param string $dir
+ * @return array
+ */
+ public function fileLister($dir)
+ {
+ $files = array();
+
+ $filtered = new DirectoryFilterDots($dir);
+ foreach ($filtered as $file) {
+ if ($file->isDir()) {
+ continue;
+ }
+ $files[] = $file->getFilename();
+ }
+
+ return $files;
+ }
+
+}
+
+// generate the directory path to the example dir
+$dir = __DIR__ . DIRECTORY_SEPARATOR . 'directory-iterator-example';
+
+// load up the class
+$DirectoryUsage = new DirectoryUsage();
+
+echo '==================================' . PHP_EOL;
+echo 'Recursively show all files in a directory.' . PHP_EOL;
+echo '==================================' . PHP_EOL;
+
+$files = $DirectoryUsage->dirListByType($dir, 'file');
+foreach ($files as $f) {
+ echo $f . PHP_EOL;
+}
+
+echo '==================================' . PHP_EOL;
+echo 'Recursively show all directories in a directory.' . PHP_EOL;
+echo '==================================' . PHP_EOL;
+
+$dirs = $DirectoryUsage->dirListByType($dir, 'dir');
+foreach ($dirs as $d) {
+ echo $d . PHP_EOL;
+}
+
+echo '==================================' . PHP_EOL;
+echo 'Recursively iterate over all files in a directory.' . PHP_EOL;
+echo '==================================' . PHP_EOL;
+
+// recursively generate a tree representation
+$files = new DirectoryTreeIterator($dir);
+foreach ($files as $f) {
+ echo $f . PHP_EOL;
+}
+
+echo '==================================' . PHP_EOL;
+echo 'Iterate over a all files in a directory, filtering out dots.' . PHP_EOL;
+echo '==================================' . PHP_EOL;
+
+// recursively generate a tree representation
+$files = new DirectoryFilterDots($dir);
+foreach ($files as $f) {
+ echo $f . PHP_EOL;
+}
+
+echo '==================================' . PHP_EOL;
+echo 'Find all files with a PHP extension.' . PHP_EOL;
+echo '==================================' . PHP_EOL;
+
+// filter by PHP file extension
+$phpFiles = new ExtensionFilter(new DirectoryIterator($dir), 'php', $whitelist=true);
+foreach ($phpFiles as $f) {
+ echo $f->getPathName() . PHP_EOL;
+}
+
+echo '==================================' . PHP_EOL;
+echo 'Find all files without a PHP extension.' . PHP_EOL;
+echo '==================================' . PHP_EOL;
+
+// filter by PHP file extension
+$phpFiles = new ExtensionFilter(new DirectoryIterator($dir), 'php', $whitelist=false);
+foreach ($phpFiles as $f) {
+ echo $f->getPathName() . PHP_EOL;
+}
15 directory-iterator.php
@@ -0,0 +1,15 @@
+<?php
+/**
+ * The below example is the simplest case of iterating over all direct children of a directory,
+ * without recursing into sub-directories. The "." and ".." references are skipped.
+ */
+
+// iterate over all files from the child directory "directory-iterator-example"
+$files = new DirectoryIterator(__DIR__ . DIRECTORY_SEPARATOR . 'directory-iterator-example');
+foreach ($files as $file) {
+ // skip over dots ("." and "..")
+ if (!$file->isDot()) {
+ // example of the
+ echo $file->getRealPath() . PHP_EOL;
+ }
+}
33 directory-iterator/directory-match.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Handle matching and filtering directories by a regular expression.
+ */
+class DirectoryMatch extends KeyFilter
+{
+ public function __construct($path , $regex)
+ {
+ parent::__construct(new DirTreeIterator($path), $regex);
+ }
+
+ /**
+ * Override the current element to simply return the key.
+ *
+ * @access public
+ * @return string
+ */
+ public function current()
+ {
+ return parent::key();
+ }
+
+ /**
+ * Override the key element to simply return the key.
+ *
+ * @access public
+ * @return string
+ */
+ public function key()
+ {
+ return parent::key();
+ }
+}
51 directory-iterator/directory-tree.php
@@ -0,0 +1,51 @@
+<?php
+class DirectoryTreeIterator extends RecursiveIteratorIterator
+{
+ /**
+ * Construct from a path.
+ * @param $path directory to iterate
+ */
+ public function __construct($path)
+ {
+ try {
+ parent::__construct(
+ new RecursiveCachingIterator(
+ new RecursiveDirectoryIterator(
+ $path,
+ RecursiveDirectoryIterator::KEY_AS_FILENAME
+ ),
+ CachingIterator::CALL_TOSTRING|CachingIterator::CATCH_GET_CHILD
+ ),
+ parent::SELF_FIRST
+ );
+ } catch(Exception $e) {
+ die($e->getMessage());
+ }
+ }
+
+ /**
+ * Skip over elements with children, returning keys.
+ *
+ * @access public
+ * @return string
+ */
+ public function current()
+ {
+ if ($this->hasChildren()) {
+ $this->next();
+ }
+ return $this->getInnerIterator()->current()->getPath() . DIRECTORY_SEPARATOR . $this->key();
+ }
+
+ /**
+ * An aggregate of the inner iterator.
+ *
+ * @access public
+ * @param string $func
+ * @param mixed $params
+ */
+ public function __call($func, $params)
+ {
+ return call_user_func_array(array($this->getSubIterator(), $func), $params);
+ }
+}
40 directory-iterator/filter-dots.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * Directory iterator class - filters out the . and .. directories
+ *
+ */
+class DirectoryFilterDots extends RecursiveFilterIterator
+{
+ /**
+ * Init with a recursive directory iterator.
+ *
+ * @access public
+ * @param RecursiveDirectoryIterator $path The directory to iterate
+ */
+ public function __construct($path)
+ {
+ parent::__construct(new RecursiveDirectoryIterator($path));
+ }
+
+ /**
+ * Filter out both kinds of dots in a directory structure.
+ *
+ * @access public
+ * @return bool Whether the current entry is neither '.' nor '..'
+ */
+ public function accept()
+ {
+ return !$this->getInnerIterator()->isDot();
+ }
+
+ /**
+ * Override the key method to return the path name.
+ *
+ * @access public
+ * @return string The current entries path name
+ */
+ public function key()
+ {
+ return $this->getInnerIterator()->getPathname();
+ }
+}
60 directory-iterator/filter-extension.php
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Filters out files with specified extensions.
+ */
+class ExtensionFilter extends FilterIterator {
+
+ private $_ext;
+ private $_it;
+ private $_whitelisted;
+
+ /**
+ * Takes both a directory iterator and a file extension and only returns
+ * results matching the particular extension.
+ *
+ * @access public
+ */
+ public function __construct(DirectoryIterator $it, $ext, $whitelisted = false)
+ {
+ parent::__construct($it);
+ $this->_it = $it;
+ $this->_ext = $ext;
+ $this->_whitelisted = $whitelisted;
+ }
+
+ /**
+ * Given the current iterator position, check the filename against
+ * the extension and filter accordingly.
+ *
+ * @access public
+ * @return bool
+ */
+ public function accept()
+ {
+ $return = true;
+
+ // skip dots
+ if ($this->_it->isDot()) return false;
+
+ // pop off the extension for non-directories and try to match
+ if (!$this->_it->isDir()) {
+ $ext = $this->_it->getExtension();
+
+ if ($this->_whitelisted) {
+ if (is_array($this->_ext)) {
+ $return = in_array($ext, $this->_ext);
+ } else {
+ $return = $ext === $this->_ext;
+ }
+ } else {
+ if (is_array($this->_ext)) {
+ $return = !in_array($ext, $this->_ext);
+ } else {
+ $return = $ext !== $this->_ext;
+ }
+ }
+ }
+
+ return $return;
+ }
+}
43 directory-iterator/filter-key.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Filter an array of string results based on a given regular expression.
+ */
+class KeyFilter extends FilterIterator
+{
+ private $_regex;
+
+ /**
+ * The key filter takes in an iterator and a regular expression pattern
+ * to filter the iterator keys against.
+ *
+ * @access public
+ * @return void
+ */
+ public function __construct(Iterator $it, $regex)
+ {
+ parent::__construct($it);
+ $this->_regex = $regex;
+ }
+
+ /**
+ * Provide the required accept() method for filtering keys by
+ * a regular expression.
+ *
+ * @access public
+ * @return int|bool
+ */
+ public function accept()
+ {
+ return preg_match($this->_regex, $this->getInnerIterator()->key());
+ }
+
+ /**
+ * Override the cloning method.
+ *
+ * @access protected
+ * @return bool
+ */
+ protected function __clone() {
+ return false;
+ }
+}
91 filter-iterator.php
@@ -0,0 +1,91 @@
+<?php
+/**
+ * This class demonstrates a "real world" example of filtering users out from a
+ * mock array. Pretend the data came from your model and you only need to match a subset
+ * of the returned results. This class is an example of filtering any array
+ * by either key or value with a few minor adjustments.
+ *
+ * @Class FilterExample
+ */
+class ObjectFilter extends FilterIterator
+{
+ protected $_filterKey;
+ protected $_filterVal;
+
+ /**
+ * Calls the parent FilterIterator constructor.
+ *
+ * @param Iterator $it An iterator object
+ * @param mixed $filterKey
+ * @param mixed $filterVal
+ *
+ */
+ public function __construct(Iterator $it, $filterKey, $filterVal)
+ {
+ parent::__construct($it);
+
+ $this->_filterKey = $filterKey;
+ $this->_filterVal = $filterVal;
+ }
+
+ /**
+ * The accept method is required, as FilterExample
+ * extends FilterIterator with abstract method accept().
+ *
+ * @access public
+ * @accept Only allow values that are not ___
+ * @return string
+ */
+ public function accept()
+ {
+ $object = $this->getInnerIterator()->current();
+ // base case
+ if (!isset($object[$this->_filterKey])) return true;
+ // if the key and value match the filter
+ if (strcasecmp($object[$this->_filterKey], $this->_filterVal) == 0) {
+ return false;
+ }
+ return true;
+ }
+}
+
+// load up an append iterator
+$it = new AppendIterator();
+
+// create some example users as ArrayIterators
+$user1 = new ArrayIterator(array('id' => 1, 'name' => 'George'));
+$user2 = new ArrayIterator(array('id' => 2, 'name' => 'John'));
+$user3 = new ArrayIterator(array('id' => 3, 'name' => 'Eric'));
+$user4 = new ArrayIterator(array('id' => 4, 'name' => 'Jason'));
+$user5 = new ArrayIterator(array('id' => 5, 'name' => 'Emanuel'));
+
+// filter and append the ArrayIterators
+$it->append(new ObjectFilter($user1, 'name', 'Eric'));
+$it->append(new ObjectFilter($user2, 'name', 'Eric'));
+$it->append(new ObjectFilter($user3, 'name', 'Eric'));
+$it->append(new ObjectFilter($user4, 'name', 'Eric'));
+$it->append(new ObjectFilter($user5, 'name', 'Eric'));
+
+// show the example filtered output
+foreach($it as $key => $val) {
+ echo $key . ' = ' . $val . PHP_EOL;
+}
+
+
+/**
+ * This is the same example, but utilising ArrayObject
+ * instead of AppendIterator and ArrayIterator.
+ */
+$users = array(
+ array('id' => 1, 'name' => 'George'),
+ array('id' => 2, 'name' => 'John'),
+ array('id' => 3, 'name' => 'Eric'),
+ array('id' => 4, 'name' => 'Jason'),
+ array('id' => 5, 'name' => 'Emanuel')
+);
+
+// convert users to ArrayObject
+$users = new ArrayObject($users);
+
+// filter out all user's with the name "John"
+$it = new ObjectFilter($users->getIterator(), 'name', 'john');
30 iterator-callback.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * Example of applying a callback to capitalize the first letter.
+ *
+ * Note that you must return TRUE from the callback function to continue
+ * iterating. This can be useful if you wish to stop iteration under
+ * certain conditions.
+ *
+ * @param Iterator $it
+ * @return bool
+ */
+function addDbPrefix(Traversable $it, $prefix = 'test') {
+ echo $it[$it->key()] = $prefix . '_' . $it->current();
+ echo PHP_EOL;
+ return true;
+}
+
+// example array of table names to prefix
+$array = array('users', 'roles', 'users_roles', 'users_profile');
+
+try {
+
+ // apply the callback function to the iterator
+ $it = new ArrayIterator($array);
+ $prefix = 'example';
+ iterator_apply($it, 'addDbPrefix', array($it, $prefix));
+
+} catch(Exception $e) {
+ die($e->getMessage());
+}
169 limit-iterator.php
@@ -0,0 +1,169 @@
+<?php
+/**
+ * The main use case of a limit iterator is pagination. Below is an example
+ * Pagination class which takes an array (or object) iterator as an argument
+ * and allows pagination on the set.
+ *
+ * @author Corey Ballou
+ */
+
+class Paginator extends LimitIterator {
+
+ // stores the array iterator
+ protected $_it;
+
+ // stores the current page
+ protected $_currentPage;
+
+ // stores the max number of items to display per page
+ protected $_limit;
+
+ // stores the number of array items
+ protected $_count;
+
+ // stores the total pages in the resultset
+ protected $_totalPages;
+
+ /**
+ * Default constructor to load the iterator. Override the parent
+ * LimitIterator as we don't want to return
+ *
+ * @access public
+ * @param ArrayIterator $it
+ */
+ public function __construct(ArrayIterator $it, $page = 1, $limit = 10)
+ {
+ $this->_it = $it;
+ $this->_count = $it->count();
+
+ $this->setCurrentPage($page);
+ $this->setItemsPerPage($limit);
+ }
+
+ /**
+ * Set the number of items to display per page.
+ *
+ * @access public
+ * @param int $count
+ */
+ public function setItemsPerPage($count = 10)
+ {
+ $this->_itemsPerPage = (int) $count;
+ $this->_totalPages = ($this->_count > $this->_itemsPerPage) ? ceil($this->_count / $this->_itemsPerPage) : 1;
+ }
+
+ /**
+ * Set the current page (offset).
+ *
+ * @access public
+ * @param int $page
+ */
+ public function setCurrentPage($page = 1)
+ {
+ $this->_currentPage = (int) $page;
+ }
+
+ /**
+ * Returns the current page.
+ *
+ * @access public
+ * @return int
+ */
+ public function getCurrentPage()
+ {
+ return $this->_currentPage;
+ }
+
+ /**
+ * Determines if another page exists.
+ *
+ * @access public
+ * @return bool
+ */
+ public function hasNextPage()
+ {
+ return $this->_currentPage < $this->_totalPages;
+ }
+
+ /**
+ * Determines if a previous page exists.
+ *
+ * @access public
+ * @return bool
+ */
+ public function hasPreviousPage()
+ {
+ return $this->_currentPage > 1;
+ }
+
+ /**
+ * Returns (fake render) the items matching the specific requirements.
+ *
+ * @access public
+ * @param mixed $page
+ * @param mixed $limit
+ * @return mixed
+ */
+ public function render($page = NULL, $limit = NULL)
+ {
+ if (!empty($page)) {
+ $this->setCurrentPage($page);
+ }
+
+ if (!empty($limit)) {
+ $this->setItemsPerPage($limit);
+ }
+
+ // quickly calculate the offset based on the page
+ if ($page > 0) $page -= 1;
+ $offset = $page * $this->_itemsPerPage;
+
+ // return the limit iterator
+ return new LimitIterator($this->_it, $offset, $this->_itemsPerPage);
+ }
+
+}
+
+// generate an example of page items to iterate over
+$items = array(
+ array('id' => 1, 'name' => 'Item 1', 'desc' => 'Description', 'price' => 4.99),
+ array('id' => 2, 'name' => 'Item 2', 'desc' => 'Description', 'price' => 4.99),
+ array('id' => 3, 'name' => 'Item 3', 'desc' => 'Description', 'price' => 4.99),
+ array('id' => 4, 'name' => 'Item 4', 'desc' => 'Description', 'price' => 4.99),
+ array('id' => 5, 'name' => 'Item 5', 'desc' => 'Description', 'price' => 4.99),
+ array('id' => 6, 'name' => 'Item 6', 'desc' => 'Description', 'price' => 4.99),
+ array('id' => 7, 'name' => 'Item 7', 'desc' => 'Description', 'price' => 4.99),
+ array('id' => 8, 'name' => 'Item 8', 'desc' => 'Description', 'price' => 4.99),
+ array('id' => 9, 'name' => 'Item 9', 'desc' => 'Description', 'price' => 4.99),
+ array('id' => 10, 'name' => 'Item 10', 'desc' => 'Description', 'price' => 4.99),
+ array('id' => 11, 'name' => 'Item 11', 'desc' => 'Description', 'price' => 4.99),
+ array('id' => 12, 'name' => 'Item 12', 'desc' => 'Description', 'price' => 4.99),
+ array('id' => 13, 'name' => 'Item 13', 'desc' => 'Description', 'price' => 4.99),
+ array('id' => 14, 'name' => 'Item 14', 'desc' => 'Description', 'price' => 4.99),
+ array('id' => 15, 'name' => 'Item 15', 'desc' => 'Description', 'price' => 4.99),
+ array('id' => 16, 'name' => 'Item 16', 'desc' => 'Description', 'price' => 4.99),
+ array('id' => 17, 'name' => 'Item 17', 'desc' => 'Description', 'price' => 4.99),
+ array('id' => 18, 'name' => 'Item 18', 'desc' => 'Description', 'price' => 4.99),
+ array('id' => 19, 'name' => 'Item 19', 'desc' => 'Description', 'price' => 4.99),
+ array('id' => 20, 'name' => 'Item 20', 'desc' => 'Description', 'price' => 4.99),
+ array('id' => 21, 'name' => 'Item 21', 'desc' => 'Description', 'price' => 4.99)
+);
+
+// load the paginator
+$Paginator = new Paginator(new ArrayIterator($items));
+
+// displays the initial set (page 1, limit 10)
+$results = $Paginator->render();
+foreach ($results as $r) {
+ var_dump($r);
+}
+
+// check for another page
+if ($Paginator->hasNextPage()) {
+ echo 'DISPLAYING THE NEXT SET OF RESULTS AS AN EXAMPLE' . PHP_EOL;
+ // displays the next page results as an example
+ $results = $Paginator->render($Paginator->getCurrentPage() + 1);
+ foreach ($results as $r) {
+ var_dump($r);
+ }
+}
193 observable.php
@@ -0,0 +1,193 @@
+<?php
+/**
+ * A class can implement the Observer interface when it wants to be notified of
+ * changes in "observable" objects. The SPL library has contained the SplObserver
+ * interface and SplSubject class since PHP 5.1.
+ *
+ * This class represents an observable object, or "data" in the model-view paradigm.
+ * It can be subclassed to represent an object that the application wants to have
+ * observed. An observable object can have one or more observers. An observer may
+ * be any object that implements interface Observer. After an observable instance
+ * changes, an application calling the Observable's notifyObservers method causes
+ * all of its observers to be notified of the change by a call to their update method.
+ *
+ * The order in which notifications will be delivered is unspecified. The default
+ * implementation provided in the Observerable class will notify Observers in the
+ * order in which they registered interest, but subclasses may change this order,
+ * use no guaranteed order, deliver notifications on separate threads, or may
+ * guarantee that their subclass follows this order, as they choose.
+ */
+class Observable implements SplSubject
+{
+
+ /**
+ * @var array $observers list of observer objects
+ */
+ protected $_observers = array();
+
+ /**
+ * Construct an Observable with zero Observers.
+ *
+ * @access public
+ * @return void
+ */
+ public function __construct() { }
+
+ /**
+ * Adds an observer to the set of observers for this object, provided that it is not the same as some observer already in the set.
+ *
+ * @access public
+ * @param SplObserver $observer an observer to be added.
+ * @return void
+ */
+ public function addObserver(SplObserver $observer)
+ {
+ // ensure the observer doesn't already exist
+ if (!$this->containsObserver($observer)) {
+ $this->_observers[] = $observer;
+ }
+ }
+
+ /**
+ * Deletes an observer from the set of observers contained within
+ * this particular observable.
+ *
+ * @access public
+ * @param Observer $observer the observer to be deleted.
+ * @return void
+ */
+ public function deleteObserver(SplObserver $observer)
+ {
+ if ($this->containsObserver($observer)) {
+ $this->observers = array_diff($this->_observers, array($observer));
+ }
+ }
+
+ /**
+ * Clears the observer list so that this object no longer has any observers.
+ *
+ * @access public
+ * @return void
+ */
+ public function deleteObservers()
+ {
+ unset($this->_observers);
+ $this->_observers = array();
+ }
+
+ /**
+ * If this object has changed, as indicated by the hasChanged method, then
+ * notify all of its observers and then call the clearChanged method to
+ * indicate that this object has no longer changed.
+ *
+ * @access public
+ * @return void
+ */
+ public function notifyObservers()
+ {
+ foreach ($this->_observers as $observer) {
+ $observer->update($this);
+ }
+ }
+
+ /**
+ * Returns the number of observers of this Observable object.
+ *
+ * @access public
+ * @return int the number of observers of this object.
+ */
+ public function countObservers()
+ {
+ return count($this->_observers);
+ }
+
+ /**
+ * Check if observer already exists in the list.
+ *
+ * @param SplObserver $observer
+ * @return bool
+ */
+ public function containsObserver(SplObserver $observer)
+ {
+ return in_array($observer, $this->_observers);
+ }
+
+ /**
+ * Add an observer.
+ *
+ * @access public
+ * @param SplObserver $observer
+ * @return void
+ */
+
+ public function attach(SplObserver $observer)
+ {
+ $this->addObserver($observer);
+ }
+
+ /**
+ * Remove an observer.
+ *
+ * @access public
+ * @param SplObserver $observer
+ * @return void
+ */
+ public function detach(SplObserver $observer)
+ {
+ $this->deleteObserver($observer);
+ }
+
+ /**
+ * Notify observers of a change.
+ *
+ * @access public
+ * @return void
+ */
+ public function notify()
+ {
+ $this->notifyObservers();
+ }
+
+}
+
+
+//============================
+// example usage of Observable
+//============================
+
+class KillBot implements SplObserver
+{
+ public function update(SplSubject $subject)
+ {
+ echo __CLASS__ . " says kill all humans." . PHP_EOL;
+ }
+}
+
+class LoveBot implements SplObserver
+{
+ public function update(SplSubject $subject)
+ {
+ echo __CLASS__ . " says kiss all humans." . PHP_EOL;
+ }
+}
+
+// load the observable (SPLSubject)
+$robots = new Observable();
+
+// load some observers
+$killbot = new KillBot();
+$lovebot = new LoveBot();
+
+// add the observers to the observable
+$robots->addObserver($killbot);
+$robots->addObserver($lovebot);
+
+// notify the observers of an event
+$robots->notify();
+
+/*
+Observers output:
+
+KillBot says kill all humans
+LoveBot says kiss all humans
+*/
194 observer-exception-handler.php
@@ -0,0 +1,194 @@
+<?php
+/**
+ * This is an example of using the observer pattern to handle exceptions. You
+ * can attach an arbitrary number of observers to the ExceptionHandler
+ * for handling exceptions in different ways. This could be extensible by
+ * adding more observers, or only having observers act on distinct types of
+ * exceptions.
+ *
+ * The ExceptionHandler class sends uncaught exception messages to the proper
+ * handlers. This is done using SplObserver/SplSubject.
+*/
+class ExceptionHandler implements SplSubject
+{
+ /**
+ * An array of SplObserver objects to notify of Exceptions.
+ *
+ * @var array
+ */
+ private $_observers = array();
+
+ /**
+ * The uncaught Exception that needs to be handled.
+ *
+ * @var Exception
+ */
+ protected $_exception;
+
+ /**
+ * Constructor method for ExceptionHandler.
+ *
+ * @return ExceptionHandler
+ */
+ function __construct() { }
+
+ /**
+ * A custom method for returning the exception.
+ *
+ * @access public
+ * @return Exception
+ */
+ public function getException()
+ {
+ return $this->_exception;
+ }
+
+ /**
+ * Attaches an SplObserver to the ExceptionHandler to be notified when an
+ * uncaught Exception is thrown.
+ *
+ * @access public
+ * @param SplObserver The observer to attach
+ * @return void
+ */
+ public function attach(SplObserver $obs)
+ {
+ $id = spl_object_hash($obs);
+ $this->_observers[$id] = $obs;
+ }
+
+ /**
+ * Detaches the SplObserver from the ExceptionHandler, so it will no longer
+ * be notified when an uncaught Exception is thrown.
+ *
+ * @access public
+ * @param SplObserver The observer to detach
+ * @return void
+ */
+ public function detach(SplObserver $obs)
+ {
+ $id = spl_object_hash($obs);
+ unset($this->_observers[$id]);
+ }
+
+ /**
+ * Notify all observers of the uncaught Exception so they can handle it as
+ * needed.
+ *
+ * @access public
+ * @return void
+ */
+ public function notify()
+ {
+ foreach($this->_observers as $obs) {
+ $obs->update($this);
+ }
+ }
+
+ /**
+ * This is the method that should be set as the default Exception handler by
+ * the calling code.
+ *
+ * @access public
+ * @return void
+ */
+ public function handle(Exception $e)
+ {
+ $this->_exception = $e;
+ $this->notify();
+ }
+
+}
+
+/**
+ * The Logger exception handler is responsible for logging uncaught
+ * exceptions to a file for debugging. It is an extension of what
+ * would be your actual Logger class.
+ */
+class ExceptionLogger extends Logger implements SplObserver
+{
+ /**
+ * Update the error_log with information about the Exception.
+ *
+ * @param SplSubject $subject The ExceptionHandler
+ * @return bool
+ */
+ public function update(SplSubject $subject)
+ {
+ $exception = $subject->getException();
+
+ $output = 'File: ' . $exception->getFile() . PHP_EOL;
+ $output .= 'Line: ' . $exception->getLine() . PHP_EOL;
+ $output .= 'Message: ' . PHP_EOL . $exception->getMessage() . PHP_EOL;
+ $output .= 'Stack Trace:' . PHP_EOL . $exception->getTraceAsString() . PHP_EOL;
+
+ echo "\n\nThe following message was sent to your default PHP error log:\n\n";
+ echo $output;
+
+ return error_log($output);
+ }
+}
+
+/**
+ * The Mailer exception handler is responsible for mailing uncaught
+ * exceptions to an administrator for notifications. It is an extension
+ * of what would be your actual Mailer class.
+ */
+class ExceptionMailer extends Mailer implements SplObserver
+{
+
+ /**
+ * Mail the sysadmin with Exception information.
+ *
+ * @param SplSubject $subject The ExceptionHandler
+ * @return bool
+ */
+ public function update(SplSubject $subject)
+ {
+ $exception = $subject->getException();
+
+ // perhaps emailer also would like to know the server in question
+ $output = 'Server: ' . $_SERVER['HOSTNAME'] . PHP_EOL;
+ $output .= 'File: ' . $exception->getFile() . PHP_EOL;
+ $output .= 'Line: ' . $exception->getLine() . PHP_EOL;
+ $output .= 'Message: ' . PHP_EOL . $exception->getMessage() . PHP_EOL;
+ $output .= 'Stack Trace:' . PHP_EOL . $exception->getTraceAsString() . PHP_EOL;
+
+ $headers = 'From: webmaster@yourdomain.com' . "\r\n" .
+ 'Reply-To: webmaster@yourdomain.com' . "\r\n" .
+ 'X-Mailer: PHP/' . phpversion();
+
+ echo "\n\nThe following email (would be) sent to your webmaster@yourdomain.com:\n\n";
+ echo $output;
+
+ //return mail('webmaster@yourdomain.com', 'Exception Thrown', $output, $headers);
+ }
+
+}
+
+/**
+ * Assume this Mailer class is your actual mailer class (i.e. SwiftMailer).
+ */
+class Mailer { }
+
+/**
+ * Assume this Logger class is your actual logger class.
+ */
+class Logger { }
+
+//====================================
+// BELOW THIS LINE RUNS THE ABOVE CODE
+//====================================
+
+// Create the ExceptionHandler
+$handler = new ExceptionHandler();
+
+// Attach an Exception Logger and Mailer
+$handler->attach(new ExceptionLogger());
+$handler->attach(new ExceptionMailer());
+
+// Set ExceptionHandler::handle() as the default
+set_exception_handler(array($handler, 'handle'));
+
+// throw an exception for handling
+throw new Exception("This is a test of the emergency broadcast system\n", 0);
408 observer-subject-events.php
@@ -0,0 +1,408 @@
+<?php
+/**
+ * The EventDispatcher class provides a container for storing and dispatching
+ * events. Modifications have been added to trigger specific methods (events)
+ * by name as opposed to forcing the usage of update(). The singleton pattern
+ * was also removed in addition to adding methods to override the default usage
+ * of __call().
+ *
+ * Based on the original code:
+ * http://forrst.com/posts/PHP_Event_handling-5Ke
+ *
+ * Ideas for scaling in the cloud:
+ * http://www.slideshare.net/beberlei/towards-the-cloud-eventdriven-architectures-in-php
+ *
+ * @author Thomas RAMBAUD
+ * @author Corey Ballou
+ * @version 1.1
+ * @access public
+ */
+class EventDispatcher {
+
+ // stores all created events
+ private $_events = array();
+
+ /**
+ * Default constructor.
+ *
+ * @access public
+ * @return void
+ */
+ public function __construct() {
+ // do nothing
+ }
+
+ /**
+ * Determine the total number of events.
+ *
+ * @access public
+ * @return int
+ */
+ public function count()
+ {
+ return count($this->_events);
+ }
+
+ /**
+ * Add a new event by name.
+ *
+ * @access public
+ * @param string $name
+ * @param mixed $triggersMethod
+ * @return Event
+ */
+ public function add($name, $triggersMethod = NULL)
+ {
+ if (!isset($this->_events[$name])) {
+ $this->_events[$name] = new Event($triggersMethod);
+ }
+ return $this->_events[$name];
+ }
+
+ /**
+ * Retrieve an event by name. If one does not exist, it will be created
+ * on the fly.
+ *
+ * @access public
+ * @param string $name
+ * @return Event
+ */
+ public function get($name)
+ {
+ if (!isset($this->_events[$name])) {
+ return $this->add($name);
+ }
+ return $this->_events[$name];
+ }
+
+ /**
+ * Retrieves all events.
+ *
+ * @access public
+ * @return array
+ */
+ public function getAll()
+ {
+ return $this->_events;
+ }
+
+ /**
+ * Trigger an event. Returns the event for monitoring status.
+ *
+ * @access public
+ * @param string $name
+ * @param mixed $data The data to pass to the triggered event(s)
+ * @return void
+ */
+ public function trigger($name, $data)
+ {
+ $this->get($name)->notify($data);
+ }
+
+ /**
+ * Remove an event by name.
+ *
+ * @access public
+ * @param string $name
+ * @return bool
+ */
+ public function remove($name)
+ {
+ if (isset($this->_events[$name])) {
+ unset($this->_events[$name]);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Retrieve the names of all current events.
+ *
+ * @access public
+ * @return array
+ */
+ public function getNames()
+ {
+ return array_keys($this->_events);
+ }
+
+ /**
+ * Magic __get method for the lazy who don't wish to use the
+ * add() or get() methods. It will add an event if it doesn't exist,
+ * or simply return an existing event.
+ *
+ * @access public
+ * @return Event
+ */
+ public function __get($name)
+ {
+ return $this->add($name);
+ }
+
+}
+
+/**
+ * Attach event handlers to an event to be notified
+ * @author Thomas RAMBAUD
+ * @version 1.0
+ * @access public
+ */
+class Event implements SplSubject {
+
+ // stores all attached observers
+ private $_observers;
+
+ /**
+ * Default constructor to initialize the observers.
+ *
+ * @access public
+ * @return void
+ */
+ public function __construct()
+ {
+ $this->_observers = new SplObjectStorage();
+ }
+
+ /**
+ * Wrapper for the attach method, allowing for the addition
+ * of a method name to call within the observer.
+ *
+ * @access public
+ * @param SplObserver $event
+ * @param mixed $triggersMethod
+ * @return Event
+ */
+ public function bind(SplObserver $event, $triggersMethod = NULL)
+ {
+ $this->_observers->attach($event, $triggersMethod);
+ return $this;
+ }
+
+ /**
+ * Attach a new observer for the particular event.
+ *
+ * @access public
+ * @param SplObserver $event
+ * @return Event
+ */
+ public function attach(SplObserver $event)
+ {
+ $this->_observers->attach($event);
+ return $this;
+ }
+
+ /**
+ * Detach an existing observer from the particular event.
+ *
+ * @access public
+ * @param SplObserver $event
+ * @return Event
+ */
+ public function detach(SplObserver $event)
+ {
+ $this->_observers->detach($event);
+ return $this;
+ }
+
+ /**
+ * Notify all event observers that the event was triggered.
+ *
+ * @access public
+ * @param mixed &$args
+ */
+ public function notify(&$args = null)
+ {
+ $this->_observers->rewind();
+ while ($this->_observers->valid()) {
+ $triggersMethod = $this->_observers->getInfo();
+ $observer = $this->_observers->current();
+ $observer->update($this, $triggersMethod, $args);
+
+ // on to the next observer for notification
+ $this->_observers->next();
+ }
+ }
+
+ /**
+ * Retrieves all observers.
+ *
+ * @access public
+ * @return SplObjectStorage
+ */
+ public function getHandlers()
+ {
+ return $this->_observers;
+ }
+
+}
+
+/**
+ * You can attach an EventListener to an event to be notified when a specific
+ * event has occured. Although unused, you can use
+ *
+ * @author Thomas RAMBAUD
+ * @version 1.0
+ * @access public
+ */
+abstract class EventListener implements SplObserver {
+
+ // holds all states
+ private $_states = array();
+
+ /**
+ * Returns all states.
+ *
+ * @access public
+ * @return void
+ */
+ public function getStates()
+ {
+ return $this->_states;
+ }
+
+ /**
+ * Adds a new state.
+ *
+ * @access public
+ * @param mixed $state
+ * @param int $stateValue
+ * @return void
+ */
+ public function addState($state, $stateValue = 1)
+ {
+ $this->_states[$state] = $stateValue;
+ }
+
+ /**
+ * @Removes a state.
+ *
+ * @access public
+ * @param mixed $state
+ * @return bool
+ */
+ public function removeState($state)
+ {
+ if ($this->hasState($state)){
+ unset($this->_states[$state]);
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ /**
+ * Checks if a given state exists.
+ *
+ * @access public
+ * @param mixed $state
+ * @return bool
+ */
+ public function hasState($state)
+ {
+ return isset($this->_states[$state]);
+ }
+
+ /**
+ * Implementation of SplObserver::update().
+ *
+ * @access public
+ * @param SplSubject $subject
+ * @param mixed $triggersMethod
+ * @param mixed &$arg Any passed in arguments
+ */
+ public function update(SplSubject $subject, $triggersMethod = NULL, &$arg = NULL) {
+ if ($triggersMethod) {
+ if (method_exists($this, $triggersMethod)) {
+ $this->{$triggersMethod}($arg);
+ } else {
+ throw new Exception('The specified event method ' . get_called_class() . '::' . $triggersMethod . ' does not exist.');
+ }
+ } else {
+ throw new Exception('The specified event method ' . get_called_class() . '::' . 'update() does not exist.');
+ }
+ }
+
+}
+
+/**
+ * An example of creating an email notification event that gets triggered
+ * when a new comment is added.
+ */
+class EmailNotification extends EventListener {
+
+ public function notify(&$comment)
+ {
+ $recipients = array('dude@domain.com', 'lady@organization.org');
+ foreach ($recipients as $email) {
+ echo 'Notifying recipient ' . $email . PHP_EOL;
+ echo 'Comment: ' . print_r($comment, true) . PHP_EOL;
+ //mail($email, 'Comment added', $comment['body']);
+ }
+ }
+
+}
+
+/**
+ * An example of creating a new comment logger that gets triggered when a
+ * new comment is added.
+ */
+class CommentLogger extends EventListener {
+
+ public function comment(&$comment)
+ {
+ echo 'Logging the comment:' . PHP_EOL;
+ echo print_r($comment, true) . PHP_EOL;
+ //error_log('notice', $comment)
+ }
+
+}
+
+//===================================================
+// EXAMPLE USAGE OF THE ABOVE CLASSES BELOW THIS LINE
+//===================================================
+
+/**
+ * Quick example function of adding a comment.
+ */
+function add_comment($comment_info, EventDispatcher $EventDispatcher)
+{
+ // insert the comment into the database
+ $sql = sprintf('INSERT INTO comments SET created_by = %d, comment = %s, created_ts = %s',
+ $comment_info['created_by'],
+ '"' . mysql_real_escape_string($comment_info['comment']) . '"',
+ '"' . time() . '"');
+
+ // myqsl_query($sql);
+
+ // notify any event listeners of onCommentAdded
+ $EventDispatcher->onCommentAdded->notify($comment_info);
+}
+
+// load up an instance of the event handler
+$EventDispatcher = new EventDispatcher();
+
+// watch for comment being added and attach notification and logging
+$EventDispatcher->onCommentAdded->bind(new EmailNotification(), 'notify');
+$EventDispatcher->onCommentAdded->bind(new CommentLogger(), 'comment');
+
+// trigger the bound events for add_comment
+add_comment(
+ array(
+ 'created_by' => 1,
+ 'comment' => 'Lorem ipsum dolor sir amet.'
+ ),
+ $EventDispatcher
+);
+
+/*
+You can perform the same thing above by doing the following:
+
+// add a new event
+$Events->add('onCommentAdded');
+
+// bind some event handlers to the event
+$Events->get('onCommentAdded')->attach(new EmailNotification());
+$Events->get('onCommentAdded')->attach(new CommentLogger());
+
+This avoids using the magic method __get(), which is particularly slow.
+It really depends on if you want to decrease readability.
+*/
149 pdo-iterator.php
@@ -0,0 +1,149 @@
+<?php
+/**
+ * This example demonstrates conversion of a PDO resultset into a tabular view.
+ * It does not currently support
+ */
+class TableRows extends RecursiveIteratorIterator {
+
+ // store the iterator
+ protected $_it;
+
+ // the PDO
+ protected $_dsn;
+
+ // teh table name
+ protected $_table;
+
+ /**
+ * Load the iterator.
+ */
+ public function __construct($it, $dsn, $table) {
+ $this->_it = $it;
+ $this->_dsn = $dsn;
+ $this->_table = $table;
+
+ parent::__construct($this->_it, self::LEAVES_ONLY);
+ }
+
+ /**
+ * Generates a table header based on the metadata.
+ *
+ * @access public
+ * @return string
+ */
+ public function getHeader()
+ {
+ $output = '<table cellpadding="0" cellspacing="0" style="width:100%;border:1px solid #000; padding: 4px;">' . PHP_EOL;
+ $output .= '<thead>' . PHP_EOL;
+ $output .= '<tr>';
+
+ $results = $this->_dsn->query(sprintf('SHOW COLUMNS FROM %s', $this->_table));
+ foreach ($results as $r) {
+ $output .= '<th>' . $r['Field'] . '</th>';
+ }
+
+ $output .= '</tr>' . PHP_EOL;
+ $output .= '</thead>' . PHP_EOL;
+ $output .= '<tbody>' . PHP_EOL;
+ echo $output;
+ }
+
+ /**
+ * Get the body.
+ *
+ * @access public
+ * @return string
+ */
+ public function getBody()
+ {
+ $output = '';
+ while ($this->valid()) {
+ echo '<td>' . $this->current() . '</td>';
+ $this->next();
+ }
+ }
+
+ /**
+ * Generates the table footer.
+ *
+ * @access public
+ * @return string
+ */
+ public function getFooter()
+ {
+ echo '</tbody></table>';
+ }
+
+ /**
+ * Create a new table row.
+ *
+ * @access public
+ * @return string
+ */
+ public function beginChildren() {
+ echo '<tr>';
+ }
+
+ /**
+ * Close a table row.
+ *
+ * @access public
+ * @return string
+ */
+ public function endChildren() {
+ echo '</tr>' . PHP_EOL;
+ }
+
+}
+
+try {
+
+ // load the database via PDO
+ $dsn = new PDO('mysql:dbname=testdb;host=127.0.0.1');
+
+ // the result only implements Traversable
+ $stmt = $dsn->prepare('SELECT * FROM test');
+
+ // exceute the query
+ $stmt->execute();
+ $stmt->setFetchMode(PDO::FETCH_ASSOC);
+
+ // get the results
+ $results = $stmt->fetchAll();
+
+ // generate the recursive iterator
+ $it = new RecursiveArrayIterator($results);
+ $TableRows = new TableRows($it, $dsn, 'test');
+
+ // output the table
+ $TableRows->getHeader();
+ $TableRows->getBody();
+ $TableRows->getFooter();
+
+} catch (PDOException $e) {
+ die($e->getMessage());
+}
+
+/*
+CREATE DATABASE testdb;
+USE testdb;
+
+CREATE TABLE test (
+ `id` int(11) unsigned auto_increment primary key,
+ `name` varchar(32),
+ `description` varchar(255),
+ `created` int(11) unsigned
+);
+
+INSERT INTO test (id, name, description, created) VALUES
+(1, 'Entry 1', 'Description', UNIX_TIMESTAMP(NOW())),
+(2, 'Entry 1', 'Description', UNIX_TIMESTAMP(NOW())),
+(3, 'Entry 1', 'Description', UNIX_TIMESTAMP(NOW())),
+(4, 'Entry 1', 'Description', UNIX_TIMESTAMP(NOW())),
+(5, 'Entry 1', 'Description', UNIX_TIMESTAMP(NOW())),
+(6, 'Entry 1', 'Description', UNIX_TIMESTAMP(NOW())),
+(7, 'Entry 1', 'Description', UNIX_TIMESTAMP(NOW())),
+(8, 'Entry 1', 'Description', UNIX_TIMESTAMP(NOW())),
+(9, 'Entry 1', 'Description', UNIX_TIMESTAMP(NOW())),
+(10, 'Entry 1', 'Description', UNIX_TIMESTAMP(NOW()));
+*/
199 recursive-caching-iterator.php
@@ -0,0 +1,199 @@
+<?php
+/**
+ * This is an example of using the recursive caching iterator to do a lookahead
+ * for the last element in a multi-dimensional "navigation" array so we can
+ * accurately set classes for "last". The other functionality of generating
+ * a full unordered list is just a bonus.
+ *
+ * Please note:
+ * No safety measures have been taken to sanitize the output.
+ *
+ * @author Corey Ballou
+ */
+
+// example navigation array
+$nav = array(
+ 'Home' => '/home',
+ 'Fake' => array(
+ 'Double Fake' => array(
+ 'Nested Double Fake' => '/fake/double/nested',
+ 'Doubly Nested Double Fake' => '/fake/double/doubly'
+ ),
+ 'Triple Fake' => '/fake/tripe'
+ ),
+ 'Products' => array(
+ 'Product 1' => '/products/1',
+ 'Product 2' => '/products/2',
+ 'Product 3' => '/products/3',
+ 'Nested Product' => array(
+ 'Nested 1' => '/products/nested/1',
+ 'Nested 2' => '/products/nested/2'
+ )
+ ),
+ 'Company' => '/company',
+ 'Privacy Policy' => '/privacy-policy'
+);
+
+// storage of output
+$output = new ArrayIterator();
+
+try {
+
+ // create the caching iterator of the nav array
+ $it = new RecursiveIteratorIterator(
+ new RecursiveCachingIterator(
+ new RecursiveArrayIterator($nav)
+ ),
+ RecursiveIteratorIterator::SELF_FIRST
+ );
+
+ // child flag
+ $depth = 0;
+
+ // generate the nav
+ foreach ($it as $name => $url) {
+
+ // set the current depth
+ $curDepth = $it->getDepth();
+
+ // store the difference in depths
+ $diff = abs($curDepth - $depth);
+
+ // close previous nested levels
+ if ($curDepth < $depth) {
+ $output->append(str_repeat('</ul></li>', $diff));
+ }
+
+ // check if we have the last nav item
+ if ($it->hasNext()) {
+ $output->append('<li><a href="' . $url . '">' . $name . '</a>');
+ } else {
+ $output->append('<li class="last"><a href="' . $url . '">' . $name . '</a>');
+ }
+
+ // either add a subnav or close the list item
+ if ($it->hasChildren()) {
+ $output->append('<ul>');
+ } else {
+ $output->append('</li>');
+ }
+
+ // cache the depth
+ $depth = $curDepth;
+ }
+
+ // if we have values, output the unordered list
+ if ($output->count()) {
+ echo '<ul id="nav">' . "\n" . implode("\n", (array) $output) . "\n" . '</ul>';
+ }
+
+} catch (Exception $e) {
+ die($e->getMessage());
+}
+
+
+echo PHP_EOL . PHP_EOL . 'CLASS EXAMPLE' . PHP_EOL . PHP_EOL;
+
+
+/**
+ * Below is the same example, but prettified in a nice, extensible class
+ * allowing you to reuse it for nav, subnav, or any time you need to
+ * determine the last element of an array.
+ */
+class NavBuilder extends RecursiveIteratorIterator {
+
+ // stores the previous depth
+ private $_depth = 0;
+
+ // stores the current iteration's depth
+ private $_curDepth = 0;
+
+ // store the iterator
+ protected $_it;
+
+ /**
+ * Constructor.
+ *
+ * @access public
+ * @param Traversable $it
+ * @param int $mode
+ * @param int $flags
+ */
+ public function __construct(Traversable $it, $mode = RecursiveIteratorIterator::SELF_FIRST, $flags = 0)
+ {
+ parent::__construct($it, $mode, $flags);
+
+ // store the caching iterator
+ $this->_it = $it;
+ }
+
+ /**
+ * Override the return values.
+ *
+ * @access public
+ */
+ public function current()
+ {
+ // the return output string
+ $output = '';
+
+ // set the current depth
+ $this->_curDepth = parent::getDepth();
+
+ // store the difference in depths
+ $diff = abs($this->_curDepth - $this->_depth);
+
+ // get the name and url of the nav item
+ $name = parent::key();
+ $url = parent::current();
+
+ // close previous nested levels
+ if ($this->_curDepth < $this->_depth) {
+ $output .= str_repeat('</ul></li>', $diff);
+ }
+
+ // check if we have the last nav item
+ if ($this->hasNext()) {
+ $output .= '<li><a href="' . $url . '">' . $name . '</a>';
+ } else {
+ $output .= '<li class="last"><a href="' . $url . '">' . $name . '</a>';
+ }
+
+ // either add a subnav or close the list item
+ if ($this->hasChildren()) {
+ $output .= '<ul>';
+ } else {
+ $output .= '</li>';
+ }
+
+ // cache the depth
+ $this->_depth = $this->_curDepth;
+
+ // return the output ( we could've also overridden current())
+ return $output;
+ }
+
+}
+
+//======
+// usage
+//======
+
+try {
+
+ // generate the recursive caching iterator
+ $it = new RecursiveCachingIterator(new RecursiveArrayIterator($nav));
+
+ // build the navigation with the iterator
+ $it = new NavBuilder($it, RecursiveIteratorIterator::SELF_FIRST);
+
+ // display the resulting navigation
+ echo '<ul id="nav">' . PHP_EOL;
+ foreach ($it as $value) {
+ echo $value . "\n";
+ }
+ echo '</ul>' . PHP_EOL;
+
+} catch (Exception $e) {
+ var_dump($e); die;
+}
17 recursive-directory-iterator.php
@@ -0,0 +1,17 @@
+<?php
+/**
+ * This is a basic example of recursively iterating over a directory,
+ * skipping both "." and "..".
+ */
+$files = new RecursiveIteratorIterator(
+ new RecursiveDirectoryIterator(__DIR__ . DIRECTORY_SEPARATOR . 'directory-iterator-example')
+);
+
+// iterate over all files from the child directory "directory-iterator-example"
+foreach ($files as $file) {
+ // skip over dots ("." and "..")
+ if (!$file->isDot()) {
+ // example of the
+ echo $file->getRealPath() . PHP_EOL;
+ }
+}
31 recursive-regex-iterator.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * This example recursively finds all PHP files from a given starting directory.
+ * The regular expression must also match directory names (i.e. no file extension)
+ * as it's part of the filtering process.
+ */
+
+// the starting directory
+$directory = __DIR__ . DIRECTORY_SEPARATOR . 'directory-iterator-example';
+
+$dir = new RecursiveIteratorIterator(
+ new RecursiveRegexIterator(
+ new RecursiveDirectoryIterator(
+ $directory,
+ RecursiveDirectoryIterator::FOLLOW_SYMLINKS
+ ),
+ // match both php file extensions and directories
+ '#(?<!/)\.php$|^[^\.]*$#i'
+ ),
+ true
+);
+
+// output all matches
+echo PHP_EOL . 'PHP files contained in directory ' . $directory . PHP_EOL . PHP_EOL;
+if (!empty($dir)) {
+ foreach ($dir as $d) {
+ if (strpos($d->getFilename(), '.php') !== FALSE) {
+ echo $d->getPath() . DIRECTORY_SEPARATOR . $d->getFilename() . PHP_EOL;
+ }
+ }
+}
29 regex-iterator.php
@@ -0,0 +1,29 @@
+<?php
+/**
+ * This is a very basic example of using the regex iterator to perform a
+ * replacement. In this case, we are merely performing a swap of the
+ * pattern matches (test) and (0-9+).
+ *
+ * You can specify any of the following modes:
+ *
+ * RegexIterator::MATCH Only execute match (filter) for the current entry.
+ * RegexIterator::GET_MATCH Return the first match for the current entry.
+ * RegexIterator::ALL_MATCHES Return all matches for the current entry.
+ * RegexIterator::SPLIT Returns the split values for the current entry.
+ * RegexIterator::REPLACE Replace the current entry.
+ * RegexIterator::USE_KEY Special flag: Match the entry key instead of the entry value.
+ */
+$a = new ArrayIterator(array('test1', 'test2', 'test3'));
+$i = new RegexIterator($a, '/^(test)(\d+)/', RegexIterator::REPLACE);
+$i->replacement = '$2:$1';
+
+print_r(iterator_to_array($i));
+/*
+Array
+(
+ [0] => 1:test
+ [1] => 2:test
+ [2] => 3:test
+)
+*/
+
112 simplexml-iterator.php
@@ -0,0 +1,112 @@
+<?php
+/**
+ * This example demonstrates recursively iterating over an XML file
+ * to any particular path.
+ */
+
+$xmlstring = <<<XML
+<?xml version = "1.0" encoding="UTF-8" standalone="yes"?>
+<document>
+ <animal>
+ <category id="26">
+ <species>Phascolarctidae</species>
+ <type>koala</type>
+ <name>Bruce</name>
+ </category>
+ </animal>
+ <animal>
+ <category id="27">
+ <species>macropod</species>
+ <type>kangaroo</type>
+ <name>Bruce</name>
+ </category>
+ </animal>
+ <animal>
+ <category id="28">
+ <species>diprotodon</species>
+ <type>wombat</type>
+ <name>Bruce</name>
+ </category>
+ </animal>
+ <animal>
+ <category id="31">
+ <species>macropod</species>
+ <type>wallaby</type>
+ <name>Bruce</name>
+ </category>
+ </animal>
+ <animal>
+ <category id="21">
+ <species>dromaius</species>
+ <type>emu</type>
+ <name>Bruce</name>
+ </category>
+ </animal>
+ <animal>
+ <category id=