├── README.md ├── caching-iterator.php ├── directory-iterator-example ├── directory-iterator-recursive │ ├── file1.txt │ ├── file2 │ ├── file3 │ └── hello-world ├── file1.txt ├── file2.php ├── file2.txt ├── file3 ├── file4.txt ├── file5 ├── second-directory │ ├── newfile.php │ └── third-directory │ │ └── you_found_me └── test.php ├── directory-iterator-pt2.php ├── directory-iterator.php ├── directory-iterator ├── directory-match.php ├── directory-tree.php ├── filter-dots.php ├── filter-extension.php └── filter-key.php ├── filter-iterator.php ├── iterator-callback.php ├── limit-iterator.php ├── observable.php ├── observer-exception-handler.php ├── observer-subject-events.php ├── pdo-iterator.php ├── recursive-caching-iterator.php ├── recursive-directory-iterator.php ├── recursive-regex-iterator.php ├── regex-iterator.php └── simplexml-iterator.php /README.md: -------------------------------------------------------------------------------- 1 | About this Repository 2 | ===================== 3 | 4 | This repository aims to be a community wiki (a la git pull requests) of useful PHP Standard Template Library (SPL) examples. The php.net website lacks user submitted examples in the SPL department, so Queen City PHP (http://qcphp.org) is taking the initiative to provide examples and spread the word. 5 | 6 | All of the base level PHP files are working examples of their SPL iterator counterparts. Documentation will be added to the readme in the future. 7 | 8 | Regarding the Examples 9 | ------------------------- 10 | 11 | Each example can be run from the command line, i.e.: 12 | 13 | ```` 14 | php -f caching-iterator.php 15 | ```` 16 | 17 | Which will produce example output in your console/terminal. 18 | 19 | 20 | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/cballou/php-spl-iterator-interface-examples/trend.png)](https://bitdeli.com/free "Bitdeli Badge") 21 | 22 | -------------------------------------------------------------------------------- /caching-iterator.php: -------------------------------------------------------------------------------- 1 | '/home', 15 | 'Products' => '/products', 16 | 'Company' => '/company', 17 | 'Privacy Policy' => '/privacy-policy' 18 | ); 19 | 20 | // storage of output 21 | $output = new ArrayIterator(); 22 | 23 | try { 24 | 25 | // create the caching iterator of the nav array 26 | $it = new CachingIterator(new ArrayIterator($nav)); 27 | foreach ($it as $name => $url) { 28 | if ($it->hasNext()) { 29 | $output->append('
  • ' . $name . '
  • '); 30 | } else { 31 | $output->append('
  • ' . $name . '
  • '); 32 | } 33 | } 34 | 35 | // if we have values, output the unordered list 36 | if ($output->count()) { 37 | echo ''; 38 | } 39 | 40 | } catch (Exception $e) { 41 | die($e->getMessage()); 42 | } 43 | 44 | /** 45 | * Below is the same example, but prettified in a nice, extensible class 46 | * allowing you to reuse it for nav, subnav, or any time you need to 47 | * determine the last element of an array. 48 | */ 49 | class NavBuilder extends CachingIterator { 50 | 51 | /** 52 | * Override the current() method to modify the return value 53 | * for the given index. 54 | * 55 | * @access public 56 | * @return string 57 | */ 58 | public function current() 59 | { 60 | // get the name and url of the nav item 61 | $name = parent::key(); 62 | $url = parent::current(); 63 | 64 | // determine if we're on the last element 65 | if ($this->hasNext()) { 66 | return '
  • ' . $name . '
  • '; 67 | } else { 68 | return '
  • ' . $name . '
  • '; 69 | } 70 | } 71 | 72 | /** 73 | * Outputs the navigation. 74 | */ 75 | public function generate() 76 | { 77 | $inner = $this->getInnerIterator(); 78 | var_dump(get_class_methods($inner)); 79 | } 80 | 81 | } 82 | 83 | try { 84 | $it = new NavBuilder(new ArrayIterator($nav)); 85 | echo $it->generate(); 86 | } catch (Exception $e) { 87 | var_dump($e); die; 88 | } -------------------------------------------------------------------------------- /directory-iterator-example/directory-iterator-recursive/file1.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cballou/PHP-SPL-Iterator-Interface-Examples/bf04445fd0ebc8639d9cac8be7dfd6183abfa7d8/directory-iterator-example/directory-iterator-recursive/file1.txt -------------------------------------------------------------------------------- /directory-iterator-example/directory-iterator-recursive/file2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cballou/PHP-SPL-Iterator-Interface-Examples/bf04445fd0ebc8639d9cac8be7dfd6183abfa7d8/directory-iterator-example/directory-iterator-recursive/file2 -------------------------------------------------------------------------------- /directory-iterator-example/directory-iterator-recursive/file3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cballou/PHP-SPL-Iterator-Interface-Examples/bf04445fd0ebc8639d9cac8be7dfd6183abfa7d8/directory-iterator-example/directory-iterator-recursive/file3 -------------------------------------------------------------------------------- /directory-iterator-example/directory-iterator-recursive/hello-world: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cballou/PHP-SPL-Iterator-Interface-Examples/bf04445fd0ebc8639d9cac8be7dfd6183abfa7d8/directory-iterator-example/directory-iterator-recursive/hello-world -------------------------------------------------------------------------------- /directory-iterator-example/file1.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cballou/PHP-SPL-Iterator-Interface-Examples/bf04445fd0ebc8639d9cac8be7dfd6183abfa7d8/directory-iterator-example/file1.txt -------------------------------------------------------------------------------- /directory-iterator-example/file2.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cballou/PHP-SPL-Iterator-Interface-Examples/bf04445fd0ebc8639d9cac8be7dfd6183abfa7d8/directory-iterator-example/file2.php -------------------------------------------------------------------------------- /directory-iterator-example/file2.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cballou/PHP-SPL-Iterator-Interface-Examples/bf04445fd0ebc8639d9cac8be7dfd6183abfa7d8/directory-iterator-example/file2.txt -------------------------------------------------------------------------------- /directory-iterator-example/file3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cballou/PHP-SPL-Iterator-Interface-Examples/bf04445fd0ebc8639d9cac8be7dfd6183abfa7d8/directory-iterator-example/file3 -------------------------------------------------------------------------------- /directory-iterator-example/file4.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cballou/PHP-SPL-Iterator-Interface-Examples/bf04445fd0ebc8639d9cac8be7dfd6183abfa7d8/directory-iterator-example/file4.txt -------------------------------------------------------------------------------- /directory-iterator-example/file5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cballou/PHP-SPL-Iterator-Interface-Examples/bf04445fd0ebc8639d9cac8be7dfd6183abfa7d8/directory-iterator-example/file5 -------------------------------------------------------------------------------- /directory-iterator-example/second-directory/newfile.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cballou/PHP-SPL-Iterator-Interface-Examples/bf04445fd0ebc8639d9cac8be7dfd6183abfa7d8/directory-iterator-example/second-directory/newfile.php -------------------------------------------------------------------------------- /directory-iterator-example/second-directory/third-directory/you_found_me: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cballou/PHP-SPL-Iterator-Interface-Examples/bf04445fd0ebc8639d9cac8be7dfd6183abfa7d8/directory-iterator-example/second-directory/third-directory/you_found_me -------------------------------------------------------------------------------- /directory-iterator-example/test.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cballou/PHP-SPL-Iterator-Interface-Examples/bf04445fd0ebc8639d9cac8be7dfd6183abfa7d8/directory-iterator-example/test.php -------------------------------------------------------------------------------- /directory-iterator-pt2.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class DirectoryUsage 18 | { 19 | /** 20 | * Recursively list the contents of a directory. Second parameter allows you 21 | * to specify whether you'd like to only return a list of directories or files. 22 | * 23 | * @param string $dir 24 | * @param string $type ['file'|'dir'] 25 | * @return array 26 | */ 27 | public function dirListByType($dir, $type = 'file') 28 | { 29 | $output = array(); 30 | 31 | $it = new RecursiveDirectoryIterator($dir); 32 | $it = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST); 33 | foreach ($it as $file) { 34 | if ($file->getType() == $type) { 35 | $output[] = $file->getPathname(); 36 | } 37 | } 38 | 39 | return $output; 40 | } 41 | 42 | /** 43 | * Example of using SPL to clean up directories by removing specific filenames. 44 | * 45 | * @access public 46 | * @param string $directory 47 | * @param array $filter 48 | */ 49 | public function cleanDir($directory, $filter = array('_vti_cnf', '_vti_private', '_vti_txt', '_private', '_themes', 'msupdate', 'vti_pvt', 'vti_script', '_vti_log', '_template','Thumbs.db')) 50 | { 51 | $it = new RecursiveDirectoryIterator($directory); 52 | $it = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST); 53 | foreach ($it as $file) { 54 | 55 | // remove empty dirs 56 | if (sizeof($file->getSize()) == 0) { 57 | unlink($file->getPath()); 58 | } 59 | 60 | // remove instances of Thumbs.db 61 | if ($file->getFileName() == 'Thumbs.db') { 62 | unlink($file->getPath() . DIRECTORY_SEPARATOR . $file->getFilename()); 63 | } 64 | 65 | // if paths match filter, delete directory recursively 66 | $parts = explode(DIRECTORY_SEPARATOR, $file->getPath()); 67 | if(in_array(end($parts), $filter)) { 68 | $this->deleteDir($file->getPath()); 69 | } 70 | 71 | } 72 | } 73 | 74 | /** 75 | * Method to get information about all the files in a directory (recursive) 76 | * 77 | * @param string $directory 78 | * @param array $filter 79 | * @return array 80 | */ 81 | public function fileInfo($directory, $filter = array('php', 'xsl', 'xml', 'htm', 'html','css')) 82 | { 83 | $count_directories = 0; 84 | $count_files = 0; 85 | $count_lines = 0; 86 | $count_bytes = 0; 87 | 88 | $it = new RecursiveDirectoryIterator($directory); 89 | $it = new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::CHILD_FIRST); 90 | 91 | foreach ($it as $file) { 92 | if (false === $file->isDir()) { 93 | // get the file extension 94 | $ext = $file->getExtension(); 95 | if (in_array($ext, $filter)) { 96 | $count_files++; 97 | $count_bytes += $file->getSize(); 98 | $count_lines += sizeof(explode("n", file_get_contents($file->getPathName()))); 99 | } 100 | } else if(false === strpos($file->getPathname(), 'CVS') && $file->isDir()) { 101 | $count_directories++; 102 | } 103 | } 104 | 105 | return array( 106 | 'bytes' => $count_bytes, 107 | 'files' => $count_files, 108 | 'lines' => $count_lines, 109 | 'directories' => $count_directories 110 | ); 111 | } 112 | 113 | /** 114 | * Recursively delete a directory and all subdirectories. 115 | * 116 | * @access public 117 | * @param string $dir 118 | * @return void 119 | */ 120 | public function deleteDir($dir) 121 | { 122 | $it = new RecursiveDirectoryIterator($dir); 123 | $it = new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::CHILD_FIRST); 124 | foreach ($it as $file) { 125 | if ($file->isDir()) { 126 | rmdir($file->getPathname()); 127 | } else { 128 | unlink($file->getPathname()); 129 | @rmdir($dir); 130 | } 131 | } 132 | @rmdir($dir); 133 | } 134 | 135 | /** 136 | * Find a file by regex in a given directory. 137 | * 138 | * @param string $path 139 | * @param string $regex 140 | * @return array 141 | */ 142 | public function fileFinder($path, $regex) 143 | { 144 | $matches = array(); 145 | 146 | $fileList = new DirMatch($path, $regex); 147 | foreach ($fileList as $file) { 148 | $matches[] = $file; 149 | } 150 | 151 | return $matches; 152 | } 153 | 154 | /** 155 | * List files in a given directory. 156 | * 157 | * @param string $dir 158 | * @return array 159 | */ 160 | public function fileLister($dir) 161 | { 162 | $files = array(); 163 | 164 | $filtered = new DirectoryFilterDots($dir); 165 | foreach ($filtered as $file) { 166 | if ($file->isDir()) { 167 | continue; 168 | } 169 | $files[] = $file->getFilename(); 170 | } 171 | 172 | return $files; 173 | } 174 | 175 | } 176 | 177 | // generate the directory path to the example dir 178 | $dir = __DIR__ . DIRECTORY_SEPARATOR . 'directory-iterator-example'; 179 | 180 | // load up the class 181 | $DirectoryUsage = new DirectoryUsage(); 182 | 183 | echo '==================================' . PHP_EOL; 184 | echo 'Recursively show all files in a directory.' . PHP_EOL; 185 | echo '==================================' . PHP_EOL; 186 | 187 | $files = $DirectoryUsage->dirListByType($dir, 'file'); 188 | foreach ($files as $f) { 189 | echo $f . PHP_EOL; 190 | } 191 | 192 | echo '==================================' . PHP_EOL; 193 | echo 'Recursively show all directories in a directory.' . PHP_EOL; 194 | echo '==================================' . PHP_EOL; 195 | 196 | $dirs = $DirectoryUsage->dirListByType($dir, 'dir'); 197 | foreach ($dirs as $d) { 198 | echo $d . PHP_EOL; 199 | } 200 | 201 | echo '==================================' . PHP_EOL; 202 | echo 'Recursively iterate over all files in a directory.' . PHP_EOL; 203 | echo '==================================' . PHP_EOL; 204 | 205 | // recursively generate a tree representation 206 | $files = new DirectoryTreeIterator($dir); 207 | foreach ($files as $f) { 208 | echo $f . PHP_EOL; 209 | } 210 | 211 | echo '==================================' . PHP_EOL; 212 | echo 'Iterate over a all files in a directory, filtering out dots.' . PHP_EOL; 213 | echo '==================================' . PHP_EOL; 214 | 215 | // recursively generate a tree representation 216 | $files = new DirectoryFilterDots($dir); 217 | foreach ($files as $f) { 218 | echo $f . PHP_EOL; 219 | } 220 | 221 | echo '==================================' . PHP_EOL; 222 | echo 'Find all files with a PHP extension.' . PHP_EOL; 223 | echo '==================================' . PHP_EOL; 224 | 225 | // filter by PHP file extension 226 | $phpFiles = new ExtensionFilter(new DirectoryIterator($dir), 'php', $whitelist=true); 227 | foreach ($phpFiles as $f) { 228 | echo $f->getPathName() . PHP_EOL; 229 | } 230 | 231 | echo '==================================' . PHP_EOL; 232 | echo 'Find all files without a PHP extension.' . PHP_EOL; 233 | echo '==================================' . PHP_EOL; 234 | 235 | // filter by PHP file extension 236 | $phpFiles = new ExtensionFilter(new DirectoryIterator($dir), 'php', $whitelist=false); 237 | foreach ($phpFiles as $f) { 238 | echo $f->getPathName() . PHP_EOL; 239 | } -------------------------------------------------------------------------------- /directory-iterator.php: -------------------------------------------------------------------------------- 1 | isDot()) { 12 | // example of the 13 | echo $file->getRealPath() . PHP_EOL; 14 | } 15 | } -------------------------------------------------------------------------------- /directory-iterator/directory-match.php: -------------------------------------------------------------------------------- 1 | getMessage()); 23 | } 24 | } 25 | 26 | /** 27 | * Skip over elements with children, returning keys. 28 | * 29 | * @access public 30 | * @return string 31 | */ 32 | public function current() 33 | { 34 | if ($this->hasChildren()) { 35 | $this->next(); 36 | } 37 | return $this->getInnerIterator()->current()->getPath() . DIRECTORY_SEPARATOR . $this->key(); 38 | } 39 | 40 | /** 41 | * An aggregate of the inner iterator. 42 | * 43 | * @access public 44 | * @param string $func 45 | * @param mixed $params 46 | */ 47 | public function __call($func, $params) 48 | { 49 | return call_user_func_array(array($this->getSubIterator(), $func), $params); 50 | } 51 | } -------------------------------------------------------------------------------- /directory-iterator/filter-dots.php: -------------------------------------------------------------------------------- 1 | getInnerIterator()->isDot(); 28 | } 29 | 30 | /** 31 | * Override the key method to return the path name. 32 | * 33 | * @access public 34 | * @return string The current entries path name 35 | */ 36 | public function key() 37 | { 38 | return $this->getInnerIterator()->getPathname(); 39 | } 40 | } -------------------------------------------------------------------------------- /directory-iterator/filter-extension.php: -------------------------------------------------------------------------------- 1 | _it = $it; 21 | $this->_ext = $ext; 22 | $this->_whitelisted = $whitelisted; 23 | } 24 | 25 | /** 26 | * Given the current iterator position, check the filename against 27 | * the extension and filter accordingly. 28 | * 29 | * @access public 30 | * @return bool 31 | */ 32 | public function accept() 33 | { 34 | $return = true; 35 | 36 | // skip dots 37 | if ($this->_it->isDot()) return false; 38 | 39 | // pop off the extension for non-directories and try to match 40 | if (!$this->_it->isDir()) { 41 | $ext = $this->_it->getExtension(); 42 | 43 | if ($this->_whitelisted) { 44 | if (is_array($this->_ext)) { 45 | $return = in_array($ext, $this->_ext); 46 | } else { 47 | $return = $ext === $this->_ext; 48 | } 49 | } else { 50 | if (is_array($this->_ext)) { 51 | $return = !in_array($ext, $this->_ext); 52 | } else { 53 | $return = $ext !== $this->_ext; 54 | } 55 | } 56 | } 57 | 58 | return $return; 59 | } 60 | } -------------------------------------------------------------------------------- /directory-iterator/filter-key.php: -------------------------------------------------------------------------------- 1 | _regex = $regex; 20 | } 21 | 22 | /** 23 | * Provide the required accept() method for filtering keys by 24 | * a regular expression. 25 | * 26 | * @access public 27 | * @return int|bool 28 | */ 29 | public function accept() 30 | { 31 | return preg_match($this->_regex, $this->getInnerIterator()->key()); 32 | } 33 | 34 | /** 35 | * Override the cloning method. 36 | * 37 | * @access protected 38 | * @return bool 39 | */ 40 | protected function __clone() { 41 | return false; 42 | } 43 | } -------------------------------------------------------------------------------- /filter-iterator.php: -------------------------------------------------------------------------------- 1 | _filterKey = $filterKey; 28 | $this->_filterVal = $filterVal; 29 | } 30 | 31 | /** 32 | * The accept method is required, as FilterExample 33 | * extends FilterIterator with abstract method accept(). 34 | * 35 | * @access public 36 | * @accept Only allow values that are not ___ 37 | * @return string 38 | */ 39 | public function accept() 40 | { 41 | $object = $this->getInnerIterator()->current(); 42 | // base case 43 | if (!isset($object[$this->_filterKey])) return true; 44 | // if the key and value match the filter 45 | if (strcasecmp($object[$this->_filterKey], $this->_filterVal) == 0) { 46 | return false; 47 | } 48 | return true; 49 | } 50 | } 51 | 52 | // load up an append iterator 53 | $it = new AppendIterator(); 54 | 55 | // create some example users as ArrayIterators 56 | $user1 = new ArrayIterator(array('id' => 1, 'name' => 'George')); 57 | $user2 = new ArrayIterator(array('id' => 2, 'name' => 'John')); 58 | $user3 = new ArrayIterator(array('id' => 3, 'name' => 'Eric')); 59 | $user4 = new ArrayIterator(array('id' => 4, 'name' => 'Jason')); 60 | $user5 = new ArrayIterator(array('id' => 5, 'name' => 'Emanuel')); 61 | 62 | // filter and append the ArrayIterators 63 | $it->append(new ObjectFilter($user1, 'name', 'Eric')); 64 | $it->append(new ObjectFilter($user2, 'name', 'Eric')); 65 | $it->append(new ObjectFilter($user3, 'name', 'Eric')); 66 | $it->append(new ObjectFilter($user4, 'name', 'Eric')); 67 | $it->append(new ObjectFilter($user5, 'name', 'Eric')); 68 | 69 | // show the example filtered output 70 | foreach($it as $key => $val) { 71 | echo $key . ' = ' . $val . PHP_EOL; 72 | } 73 | 74 | 75 | /** 76 | * This is the same example, but utilising ArrayObject 77 | * instead of AppendIterator and ArrayIterator. 78 | */ 79 | $users = array( 80 | array('id' => 1, 'name' => 'George'), 81 | array('id' => 2, 'name' => 'John'), 82 | array('id' => 3, 'name' => 'Eric'), 83 | array('id' => 4, 'name' => 'Jason'), 84 | array('id' => 5, 'name' => 'Emanuel') 85 | ); 86 | 87 | // convert users to ArrayObject 88 | $users = new ArrayObject($users); 89 | 90 | // filter out all user's with the name "John" 91 | $it = new ObjectFilter($users->getIterator(), 'name', 'john'); -------------------------------------------------------------------------------- /iterator-callback.php: -------------------------------------------------------------------------------- 1 | key()] = $prefix . '_' . $it->current(); 14 | echo PHP_EOL; 15 | return true; 16 | } 17 | 18 | // example array of table names to prefix 19 | $array = array('users', 'roles', 'users_roles', 'users_profile'); 20 | 21 | try { 22 | 23 | // apply the callback function to the iterator 24 | $it = new ArrayIterator($array); 25 | $prefix = 'example'; 26 | iterator_apply($it, 'addDbPrefix', array($it, $prefix)); 27 | 28 | } catch(Exception $e) { 29 | die($e->getMessage()); 30 | } 31 | -------------------------------------------------------------------------------- /limit-iterator.php: -------------------------------------------------------------------------------- 1 | _it = $it; 37 | $this->_count = $it->count(); 38 | 39 | $this->setCurrentPage($page); 40 | $this->setItemsPerPage($limit); 41 | } 42 | 43 | /** 44 | * Set the number of items to display per page. 45 | * 46 | * @access public 47 | * @param int $count 48 | */ 49 | public function setItemsPerPage($count = 10) 50 | { 51 | $this->_itemsPerPage = (int) $count; 52 | $this->_totalPages = ($this->_count > $this->_itemsPerPage) ? ceil($this->_count / $this->_itemsPerPage) : 1; 53 | } 54 | 55 | /** 56 | * Set the current page (offset). 57 | * 58 | * @access public 59 | * @param int $page 60 | */ 61 | public function setCurrentPage($page = 1) 62 | { 63 | $this->_currentPage = (int) $page; 64 | } 65 | 66 | /** 67 | * Returns the current page. 68 | * 69 | * @access public 70 | * @return int 71 | */ 72 | public function getCurrentPage() 73 | { 74 | return $this->_currentPage; 75 | } 76 | 77 | /** 78 | * Determines if another page exists. 79 | * 80 | * @access public 81 | * @return bool 82 | */ 83 | public function hasNextPage() 84 | { 85 | return $this->_currentPage < $this->_totalPages; 86 | } 87 | 88 | /** 89 | * Determines if a previous page exists. 90 | * 91 | * @access public 92 | * @return bool 93 | */ 94 | public function hasPreviousPage() 95 | { 96 | return $this->_currentPage > 1; 97 | } 98 | 99 | /** 100 | * Returns (fake render) the items matching the specific requirements. 101 | * 102 | * @access public 103 | * @param mixed $page 104 | * @param mixed $limit 105 | * @return mixed 106 | */ 107 | public function render($page = NULL, $limit = NULL) 108 | { 109 | if (!empty($page)) { 110 | $this->setCurrentPage($page); 111 | } 112 | 113 | if (!empty($limit)) { 114 | $this->setItemsPerPage($limit); 115 | } 116 | 117 | // quickly calculate the offset based on the page 118 | if ($page > 0) $page -= 1; 119 | $offset = $page * $this->_itemsPerPage; 120 | 121 | // return the limit iterator 122 | return new LimitIterator($this->_it, $offset, $this->_itemsPerPage); 123 | } 124 | 125 | } 126 | 127 | // generate an example of page items to iterate over 128 | $items = array( 129 | array('id' => 1, 'name' => 'Item 1', 'desc' => 'Description', 'price' => 4.99), 130 | array('id' => 2, 'name' => 'Item 2', 'desc' => 'Description', 'price' => 4.99), 131 | array('id' => 3, 'name' => 'Item 3', 'desc' => 'Description', 'price' => 4.99), 132 | array('id' => 4, 'name' => 'Item 4', 'desc' => 'Description', 'price' => 4.99), 133 | array('id' => 5, 'name' => 'Item 5', 'desc' => 'Description', 'price' => 4.99), 134 | array('id' => 6, 'name' => 'Item 6', 'desc' => 'Description', 'price' => 4.99), 135 | array('id' => 7, 'name' => 'Item 7', 'desc' => 'Description', 'price' => 4.99), 136 | array('id' => 8, 'name' => 'Item 8', 'desc' => 'Description', 'price' => 4.99), 137 | array('id' => 9, 'name' => 'Item 9', 'desc' => 'Description', 'price' => 4.99), 138 | array('id' => 10, 'name' => 'Item 10', 'desc' => 'Description', 'price' => 4.99), 139 | array('id' => 11, 'name' => 'Item 11', 'desc' => 'Description', 'price' => 4.99), 140 | array('id' => 12, 'name' => 'Item 12', 'desc' => 'Description', 'price' => 4.99), 141 | array('id' => 13, 'name' => 'Item 13', 'desc' => 'Description', 'price' => 4.99), 142 | array('id' => 14, 'name' => 'Item 14', 'desc' => 'Description', 'price' => 4.99), 143 | array('id' => 15, 'name' => 'Item 15', 'desc' => 'Description', 'price' => 4.99), 144 | array('id' => 16, 'name' => 'Item 16', 'desc' => 'Description', 'price' => 4.99), 145 | array('id' => 17, 'name' => 'Item 17', 'desc' => 'Description', 'price' => 4.99), 146 | array('id' => 18, 'name' => 'Item 18', 'desc' => 'Description', 'price' => 4.99), 147 | array('id' => 19, 'name' => 'Item 19', 'desc' => 'Description', 'price' => 4.99), 148 | array('id' => 20, 'name' => 'Item 20', 'desc' => 'Description', 'price' => 4.99), 149 | array('id' => 21, 'name' => 'Item 21', 'desc' => 'Description', 'price' => 4.99) 150 | ); 151 | 152 | // load the paginator 153 | $Paginator = new Paginator(new ArrayIterator($items)); 154 | 155 | // displays the initial set (page 1, limit 10) 156 | $results = $Paginator->render(); 157 | foreach ($results as $r) { 158 | var_dump($r); 159 | } 160 | 161 | // check for another page 162 | if ($Paginator->hasNextPage()) { 163 | echo 'DISPLAYING THE NEXT SET OF RESULTS AS AN EXAMPLE' . PHP_EOL; 164 | // displays the next page results as an example 165 | $results = $Paginator->render($Paginator->getCurrentPage() + 1); 166 | foreach ($results as $r) { 167 | var_dump($r); 168 | } 169 | } -------------------------------------------------------------------------------- /observable.php: -------------------------------------------------------------------------------- 1 | containsObserver($observer)) { 47 | $this->_observers[] = $observer; 48 | } 49 | } 50 | 51 | /** 52 | * Deletes an observer from the set of observers contained within 53 | * this particular observable. 54 | * 55 | * @access public 56 | * @param Observer $observer the observer to be deleted. 57 | * @return void 58 | */ 59 | public function deleteObserver(SplObserver $observer) 60 | { 61 | if ($this->containsObserver($observer)) { 62 | $this->observers = array_diff($this->_observers, array($observer)); 63 | } 64 | } 65 | 66 | /** 67 | * Clears the observer list so that this object no longer has any observers. 68 | * 69 | * @access public 70 | * @return void 71 | */ 72 | public function deleteObservers() 73 | { 74 | unset($this->_observers); 75 | $this->_observers = array(); 76 | } 77 | 78 | /** 79 | * If this object has changed, as indicated by the hasChanged method, then 80 | * notify all of its observers and then call the clearChanged method to 81 | * indicate that this object has no longer changed. 82 | * 83 | * @access public 84 | * @return void 85 | */ 86 | public function notifyObservers() 87 | { 88 | foreach ($this->_observers as $observer) { 89 | $observer->update($this); 90 | } 91 | } 92 | 93 | /** 94 | * Returns the number of observers of this Observable object. 95 | * 96 | * @access public 97 | * @return int the number of observers of this object. 98 | */ 99 | public function countObservers() 100 | { 101 | return count($this->_observers); 102 | } 103 | 104 | /** 105 | * Check if observer already exists in the list. 106 | * 107 | * @param SplObserver $observer 108 | * @return bool 109 | */ 110 | public function containsObserver(SplObserver $observer) 111 | { 112 | return in_array($observer, $this->_observers); 113 | } 114 | 115 | /** 116 | * Add an observer. 117 | * 118 | * @access public 119 | * @param SplObserver $observer 120 | * @return void 121 | */ 122 | 123 | public function attach(SplObserver $observer) 124 | { 125 | $this->addObserver($observer); 126 | } 127 | 128 | /** 129 | * Remove an observer. 130 | * 131 | * @access public 132 | * @param SplObserver $observer 133 | * @return void 134 | */ 135 | public function detach(SplObserver $observer) 136 | { 137 | $this->deleteObserver($observer); 138 | } 139 | 140 | /** 141 | * Notify observers of a change. 142 | * 143 | * @access public 144 | * @return void 145 | */ 146 | public function notify() 147 | { 148 | $this->notifyObservers(); 149 | } 150 | 151 | } 152 | 153 | 154 | //============================ 155 | // example usage of Observable 156 | //============================ 157 | 158 | class KillBot implements SplObserver 159 | { 160 | public function update(SplSubject $subject) 161 | { 162 | echo __CLASS__ . " says kill all humans." . PHP_EOL; 163 | } 164 | } 165 | 166 | class LoveBot implements SplObserver 167 | { 168 | public function update(SplSubject $subject) 169 | { 170 | echo __CLASS__ . " says kiss all humans." . PHP_EOL; 171 | } 172 | } 173 | 174 | // load the observable (SPLSubject) 175 | $robots = new Observable(); 176 | 177 | // load some observers 178 | $killbot = new KillBot(); 179 | $lovebot = new LoveBot(); 180 | 181 | // add the observers to the observable 182 | $robots->addObserver($killbot); 183 | $robots->addObserver($lovebot); 184 | 185 | // notify the observers of an event 186 | $robots->notify(); 187 | 188 | /* 189 | Observers output: 190 | 191 | KillBot says kill all humans 192 | LoveBot says kiss all humans 193 | */ 194 | -------------------------------------------------------------------------------- /observer-exception-handler.php: -------------------------------------------------------------------------------- 1 | _exception; 44 | } 45 | 46 | /** 47 | * Attaches an SplObserver to the ExceptionHandler to be notified when an 48 | * uncaught Exception is thrown. 49 | * 50 | * @access public 51 | * @param SplObserver The observer to attach 52 | * @return void 53 | */ 54 | public function attach(SplObserver $obs) 55 | { 56 | $id = spl_object_hash($obs); 57 | $this->_observers[$id] = $obs; 58 | } 59 | 60 | /** 61 | * Detaches the SplObserver from the ExceptionHandler, so it will no longer 62 | * be notified when an uncaught Exception is thrown. 63 | * 64 | * @access public 65 | * @param SplObserver The observer to detach 66 | * @return void 67 | */ 68 | public function detach(SplObserver $obs) 69 | { 70 | $id = spl_object_hash($obs); 71 | unset($this->_observers[$id]); 72 | } 73 | 74 | /** 75 | * Notify all observers of the uncaught Exception so they can handle it as 76 | * needed. 77 | * 78 | * @access public 79 | * @return void 80 | */ 81 | public function notify() 82 | { 83 | foreach($this->_observers as $obs) { 84 | $obs->update($this); 85 | } 86 | } 87 | 88 | /** 89 | * This is the method that should be set as the default Exception handler by 90 | * the calling code. 91 | * 92 | * @access public 93 | * @return void 94 | */ 95 | public function handle(Exception $e) 96 | { 97 | $this->_exception = $e; 98 | $this->notify(); 99 | } 100 | 101 | } 102 | 103 | /** 104 | * The Logger exception handler is responsible for logging uncaught 105 | * exceptions to a file for debugging. It is an extension of what 106 | * would be your actual Logger class. 107 | */ 108 | class ExceptionLogger extends Logger implements SplObserver 109 | { 110 | /** 111 | * Update the error_log with information about the Exception. 112 | * 113 | * @param SplSubject $subject The ExceptionHandler 114 | * @return bool 115 | */ 116 | public function update(SplSubject $subject) 117 | { 118 | $exception = $subject->getException(); 119 | 120 | $output = 'File: ' . $exception->getFile() . PHP_EOL; 121 | $output .= 'Line: ' . $exception->getLine() . PHP_EOL; 122 | $output .= 'Message: ' . PHP_EOL . $exception->getMessage() . PHP_EOL; 123 | $output .= 'Stack Trace:' . PHP_EOL . $exception->getTraceAsString() . PHP_EOL; 124 | 125 | echo "\n\nThe following message was sent to your default PHP error log:\n\n"; 126 | echo $output; 127 | 128 | return error_log($output); 129 | } 130 | } 131 | 132 | /** 133 | * The Mailer exception handler is responsible for mailing uncaught 134 | * exceptions to an administrator for notifications. It is an extension 135 | * of what would be your actual Mailer class. 136 | */ 137 | class ExceptionMailer extends Mailer implements SplObserver 138 | { 139 | 140 | /** 141 | * Mail the sysadmin with Exception information. 142 | * 143 | * @param SplSubject $subject The ExceptionHandler 144 | * @return bool 145 | */ 146 | public function update(SplSubject $subject) 147 | { 148 | $exception = $subject->getException(); 149 | 150 | // perhaps emailer also would like to know the server in question 151 | $output = 'Server: ' . $_SERVER['HOSTNAME'] . PHP_EOL; 152 | $output .= 'File: ' . $exception->getFile() . PHP_EOL; 153 | $output .= 'Line: ' . $exception->getLine() . PHP_EOL; 154 | $output .= 'Message: ' . PHP_EOL . $exception->getMessage() . PHP_EOL; 155 | $output .= 'Stack Trace:' . PHP_EOL . $exception->getTraceAsString() . PHP_EOL; 156 | 157 | $headers = 'From: webmaster@yourdomain.com' . "\r\n" . 158 | 'Reply-To: webmaster@yourdomain.com' . "\r\n" . 159 | 'X-Mailer: PHP/' . phpversion(); 160 | 161 | echo "\n\nThe following email (would be) sent to your webmaster@yourdomain.com:\n\n"; 162 | echo $output; 163 | 164 | //return mail('webmaster@yourdomain.com', 'Exception Thrown', $output, $headers); 165 | } 166 | 167 | } 168 | 169 | /** 170 | * Assume this Mailer class is your actual mailer class (i.e. SwiftMailer). 171 | */ 172 | class Mailer { } 173 | 174 | /** 175 | * Assume this Logger class is your actual logger class. 176 | */ 177 | class Logger { } 178 | 179 | //==================================== 180 | // BELOW THIS LINE RUNS THE ABOVE CODE 181 | //==================================== 182 | 183 | // Create the ExceptionHandler 184 | $handler = new ExceptionHandler(); 185 | 186 | // Attach an Exception Logger and Mailer 187 | $handler->attach(new ExceptionLogger()); 188 | $handler->attach(new ExceptionMailer()); 189 | 190 | // Set ExceptionHandler::handle() as the default 191 | set_exception_handler(array($handler, 'handle')); 192 | 193 | // throw an exception for handling 194 | throw new Exception("This is a test of the emergency broadcast system\n", 0); 195 | -------------------------------------------------------------------------------- /observer-subject-events.php: -------------------------------------------------------------------------------- 1 | _events); 44 | } 45 | 46 | /** 47 | * Add a new event by name. 48 | * 49 | * @access public 50 | * @param string $name 51 | * @param mixed $triggersMethod 52 | * @return Event 53 | */ 54 | public function add($name, $triggersMethod = NULL) 55 | { 56 | if (!isset($this->_events[$name])) { 57 | $this->_events[$name] = new Event($triggersMethod); 58 | } 59 | return $this->_events[$name]; 60 | } 61 | 62 | /** 63 | * Retrieve an event by name. If one does not exist, it will be created 64 | * on the fly. 65 | * 66 | * @access public 67 | * @param string $name 68 | * @return Event 69 | */ 70 | public function get($name) 71 | { 72 | if (!isset($this->_events[$name])) { 73 | return $this->add($name); 74 | } 75 | return $this->_events[$name]; 76 | } 77 | 78 | /** 79 | * Retrieves all events. 80 | * 81 | * @access public 82 | * @return array 83 | */ 84 | public function getAll() 85 | { 86 | return $this->_events; 87 | } 88 | 89 | /** 90 | * Trigger an event. Returns the event for monitoring status. 91 | * 92 | * @access public 93 | * @param string $name 94 | * @param mixed $data The data to pass to the triggered event(s) 95 | * @return void 96 | */ 97 | public function trigger($name, $data) 98 | { 99 | $this->get($name)->notify($data); 100 | } 101 | 102 | /** 103 | * Remove an event by name. 104 | * 105 | * @access public 106 | * @param string $name 107 | * @return bool 108 | */ 109 | public function remove($name) 110 | { 111 | if (isset($this->_events[$name])) { 112 | unset($this->_events[$name]); 113 | return true; 114 | } 115 | return false; 116 | } 117 | 118 | /** 119 | * Retrieve the names of all current events. 120 | * 121 | * @access public 122 | * @return array 123 | */ 124 | public function getNames() 125 | { 126 | return array_keys($this->_events); 127 | } 128 | 129 | /** 130 | * Magic __get method for the lazy who don't wish to use the 131 | * add() or get() methods. It will add an event if it doesn't exist, 132 | * or simply return an existing event. 133 | * 134 | * @access public 135 | * @return Event 136 | */ 137 | public function __get($name) 138 | { 139 | return $this->add($name); 140 | } 141 | 142 | } 143 | 144 | /** 145 | * Attach event handlers to an event to be notified 146 | * @author Thomas RAMBAUD 147 | * @version 1.0 148 | * @access public 149 | */ 150 | class Event implements SplSubject { 151 | 152 | // stores all attached observers 153 | private $_observers; 154 | 155 | /** 156 | * Default constructor to initialize the observers. 157 | * 158 | * @access public 159 | * @return void 160 | */ 161 | public function __construct() 162 | { 163 | $this->_observers = new SplObjectStorage(); 164 | } 165 | 166 | /** 167 | * Wrapper for the attach method, allowing for the addition 168 | * of a method name to call within the observer. 169 | * 170 | * @access public 171 | * @param SplObserver $event 172 | * @param mixed $triggersMethod 173 | * @return Event 174 | */ 175 | public function bind(SplObserver $event, $triggersMethod = NULL) 176 | { 177 | $this->_observers->attach($event, $triggersMethod); 178 | return $this; 179 | } 180 | 181 | /** 182 | * Attach a new observer for the particular event. 183 | * 184 | * @access public 185 | * @param SplObserver $event 186 | * @return Event 187 | */ 188 | public function attach(SplObserver $event) 189 | { 190 | $this->_observers->attach($event); 191 | return $this; 192 | } 193 | 194 | /** 195 | * Detach an existing observer from the particular event. 196 | * 197 | * @access public 198 | * @param SplObserver $event 199 | * @return Event 200 | */ 201 | public function detach(SplObserver $event) 202 | { 203 | $this->_observers->detach($event); 204 | return $this; 205 | } 206 | 207 | /** 208 | * Notify all event observers that the event was triggered. 209 | * 210 | * @access public 211 | * @param mixed &$args 212 | */ 213 | public function notify(&$args = null) 214 | { 215 | $this->_observers->rewind(); 216 | while ($this->_observers->valid()) { 217 | $triggersMethod = $this->_observers->getInfo(); 218 | $observer = $this->_observers->current(); 219 | $observer->update($this, $triggersMethod, $args); 220 | 221 | // on to the next observer for notification 222 | $this->_observers->next(); 223 | } 224 | } 225 | 226 | /** 227 | * Retrieves all observers. 228 | * 229 | * @access public 230 | * @return SplObjectStorage 231 | */ 232 | public function getHandlers() 233 | { 234 | return $this->_observers; 235 | } 236 | 237 | } 238 | 239 | /** 240 | * You can attach an EventListener to an event to be notified when a specific 241 | * event has occured. Although unused, you can use 242 | * 243 | * @author Thomas RAMBAUD 244 | * @version 1.0 245 | * @access public 246 | */ 247 | abstract class EventListener implements SplObserver { 248 | 249 | // holds all states 250 | private $_states = array(); 251 | 252 | /** 253 | * Returns all states. 254 | * 255 | * @access public 256 | * @return void 257 | */ 258 | public function getStates() 259 | { 260 | return $this->_states; 261 | } 262 | 263 | /** 264 | * Adds a new state. 265 | * 266 | * @access public 267 | * @param mixed $state 268 | * @param int $stateValue 269 | * @return void 270 | */ 271 | public function addState($state, $stateValue = 1) 272 | { 273 | $this->_states[$state] = $stateValue; 274 | } 275 | 276 | /** 277 | * @Removes a state. 278 | * 279 | * @access public 280 | * @param mixed $state 281 | * @return bool 282 | */ 283 | public function removeState($state) 284 | { 285 | if ($this->hasState($state)){ 286 | unset($this->_states[$state]); 287 | return TRUE; 288 | } 289 | return FALSE; 290 | } 291 | 292 | /** 293 | * Checks if a given state exists. 294 | * 295 | * @access public 296 | * @param mixed $state 297 | * @return bool 298 | */ 299 | public function hasState($state) 300 | { 301 | return isset($this->_states[$state]); 302 | } 303 | 304 | /** 305 | * Implementation of SplObserver::update(). 306 | * 307 | * @access public 308 | * @param SplSubject $subject 309 | * @param mixed $triggersMethod 310 | * @param mixed &$arg Any passed in arguments 311 | */ 312 | public function update(SplSubject $subject, $triggersMethod = NULL, &$arg = NULL) { 313 | if ($triggersMethod) { 314 | if (method_exists($this, $triggersMethod)) { 315 | $this->{$triggersMethod}($arg); 316 | } else { 317 | throw new Exception('The specified event method ' . get_called_class() . '::' . $triggersMethod . ' does not exist.'); 318 | } 319 | } else { 320 | throw new Exception('The specified event method ' . get_called_class() . '::' . 'update() does not exist.'); 321 | } 322 | } 323 | 324 | } 325 | 326 | /** 327 | * An example of creating an email notification event that gets triggered 328 | * when a new comment is added. 329 | */ 330 | class EmailNotification extends EventListener { 331 | 332 | public function notify(&$comment) 333 | { 334 | $recipients = array('dude@domain.com', 'lady@organization.org'); 335 | foreach ($recipients as $email) { 336 | echo 'Notifying recipient ' . $email . PHP_EOL; 337 | echo 'Comment: ' . print_r($comment, true) . PHP_EOL; 338 | //mail($email, 'Comment added', $comment['body']); 339 | } 340 | } 341 | 342 | } 343 | 344 | /** 345 | * An example of creating a new comment logger that gets triggered when a 346 | * new comment is added. 347 | */ 348 | class CommentLogger extends EventListener { 349 | 350 | public function comment(&$comment) 351 | { 352 | echo 'Logging the comment:' . PHP_EOL; 353 | echo print_r($comment, true) . PHP_EOL; 354 | //error_log('notice', $comment) 355 | } 356 | 357 | } 358 | 359 | //=================================================== 360 | // EXAMPLE USAGE OF THE ABOVE CLASSES BELOW THIS LINE 361 | //=================================================== 362 | 363 | /** 364 | * Quick example function of adding a comment. 365 | */ 366 | function add_comment($comment_info, EventDispatcher $EventDispatcher) 367 | { 368 | // insert the comment into the database 369 | $sql = sprintf('INSERT INTO comments SET created_by = %d, comment = %s, created_ts = %s', 370 | $comment_info['created_by'], 371 | '"' . mysql_real_escape_string($comment_info['comment']) . '"', 372 | '"' . time() . '"'); 373 | 374 | // myqsl_query($sql); 375 | 376 | // notify any event listeners of onCommentAdded 377 | $EventDispatcher->onCommentAdded->notify($comment_info); 378 | } 379 | 380 | // load up an instance of the event handler 381 | $EventDispatcher = new EventDispatcher(); 382 | 383 | // watch for comment being added and attach notification and logging 384 | $EventDispatcher->onCommentAdded->bind(new EmailNotification(), 'notify'); 385 | $EventDispatcher->onCommentAdded->bind(new CommentLogger(), 'comment'); 386 | 387 | // trigger the bound events for add_comment 388 | add_comment( 389 | array( 390 | 'created_by' => 1, 391 | 'comment' => 'Lorem ipsum dolor sir amet.' 392 | ), 393 | $EventDispatcher 394 | ); 395 | 396 | /* 397 | You can perform the same thing above by doing the following: 398 | 399 | // add a new event 400 | $Events->add('onCommentAdded'); 401 | 402 | // bind some event handlers to the event 403 | $Events->get('onCommentAdded')->attach(new EmailNotification()); 404 | $Events->get('onCommentAdded')->attach(new CommentLogger()); 405 | 406 | This avoids using the magic method __get(), which is particularly slow. 407 | It really depends on if you want to decrease readability. 408 | */ -------------------------------------------------------------------------------- /pdo-iterator.php: -------------------------------------------------------------------------------- 1 | _it = $it; 22 | $this->_dsn = $dsn; 23 | $this->_table = $table; 24 | 25 | parent::__construct($this->_it, self::LEAVES_ONLY); 26 | } 27 | 28 | /** 29 | * Generates a table header based on the metadata. 30 | * 31 | * @access public 32 | * @return string 33 | */ 34 | public function getHeader() 35 | { 36 | $output = '' . PHP_EOL; 37 | $output .= '' . PHP_EOL; 38 | $output .= ''; 39 | 40 | $results = $this->_dsn->query(sprintf('SHOW COLUMNS FROM %s', $this->_table)); 41 | foreach ($results as $r) { 42 | $output .= ''; 43 | } 44 | 45 | $output .= '' . PHP_EOL; 46 | $output .= '' . PHP_EOL; 47 | $output .= '' . PHP_EOL; 48 | echo $output; 49 | } 50 | 51 | /** 52 | * Get the body. 53 | * 54 | * @access public 55 | * @return string 56 | */ 57 | public function getBody() 58 | { 59 | $output = ''; 60 | while ($this->valid()) { 61 | echo ''; 62 | $this->next(); 63 | } 64 | } 65 | 66 | /** 67 | * Generates the table footer. 68 | * 69 | * @access public 70 | * @return string 71 | */ 72 | public function getFooter() 73 | { 74 | echo '
    ' . $r['Field'] . '
    ' . $this->current() . '
    '; 75 | } 76 | 77 | /** 78 | * Create a new table row. 79 | * 80 | * @access public 81 | * @return string 82 | */ 83 | public function beginChildren() { 84 | echo ''; 85 | } 86 | 87 | /** 88 | * Close a table row. 89 | * 90 | * @access public 91 | * @return string 92 | */ 93 | public function endChildren() { 94 | echo '' . PHP_EOL; 95 | } 96 | 97 | } 98 | 99 | try { 100 | 101 | // load the database via PDO 102 | $dsn = new PDO('mysql:dbname=testdb;host=127.0.0.1'); 103 | 104 | // the result only implements Traversable 105 | $stmt = $dsn->prepare('SELECT * FROM test'); 106 | 107 | // exceute the query 108 | $stmt->execute(); 109 | $stmt->setFetchMode(PDO::FETCH_ASSOC); 110 | 111 | // get the results 112 | $results = $stmt->fetchAll(); 113 | 114 | // generate the recursive iterator 115 | $it = new RecursiveArrayIterator($results); 116 | $TableRows = new TableRows($it, $dsn, 'test'); 117 | 118 | // output the table 119 | $TableRows->getHeader(); 120 | $TableRows->getBody(); 121 | $TableRows->getFooter(); 122 | 123 | } catch (PDOException $e) { 124 | die($e->getMessage()); 125 | } 126 | 127 | /* 128 | CREATE DATABASE testdb; 129 | USE testdb; 130 | 131 | CREATE TABLE test ( 132 | `id` int(11) unsigned auto_increment primary key, 133 | `name` varchar(32), 134 | `description` varchar(255), 135 | `created` int(11) unsigned 136 | ); 137 | 138 | INSERT INTO test (id, name, description, created) VALUES 139 | (1, 'Entry 1', 'Description', UNIX_TIMESTAMP(NOW())), 140 | (2, 'Entry 1', 'Description', UNIX_TIMESTAMP(NOW())), 141 | (3, 'Entry 1', 'Description', UNIX_TIMESTAMP(NOW())), 142 | (4, 'Entry 1', 'Description', UNIX_TIMESTAMP(NOW())), 143 | (5, 'Entry 1', 'Description', UNIX_TIMESTAMP(NOW())), 144 | (6, 'Entry 1', 'Description', UNIX_TIMESTAMP(NOW())), 145 | (7, 'Entry 1', 'Description', UNIX_TIMESTAMP(NOW())), 146 | (8, 'Entry 1', 'Description', UNIX_TIMESTAMP(NOW())), 147 | (9, 'Entry 1', 'Description', UNIX_TIMESTAMP(NOW())), 148 | (10, 'Entry 1', 'Description', UNIX_TIMESTAMP(NOW())); 149 | */ 150 | -------------------------------------------------------------------------------- /recursive-caching-iterator.php: -------------------------------------------------------------------------------- 1 | '/home', 17 | 'Fake' => array( 18 | 'Double Fake' => array( 19 | 'Nested Double Fake' => '/fake/double/nested', 20 | 'Doubly Nested Double Fake' => '/fake/double/doubly' 21 | ), 22 | 'Triple Fake' => '/fake/tripe' 23 | ), 24 | 'Products' => array( 25 | 'Product 1' => '/products/1', 26 | 'Product 2' => '/products/2', 27 | 'Product 3' => '/products/3', 28 | 'Nested Product' => array( 29 | 'Nested 1' => '/products/nested/1', 30 | 'Nested 2' => '/products/nested/2' 31 | ) 32 | ), 33 | 'Company' => '/company', 34 | 'Privacy Policy' => '/privacy-policy' 35 | ); 36 | 37 | // storage of output 38 | $output = new ArrayIterator(); 39 | 40 | try { 41 | 42 | // create the caching iterator of the nav array 43 | $it = new RecursiveIteratorIterator( 44 | new RecursiveCachingIterator( 45 | new RecursiveArrayIterator($nav) 46 | ), 47 | RecursiveIteratorIterator::SELF_FIRST 48 | ); 49 | 50 | // child flag 51 | $depth = 0; 52 | 53 | // generate the nav 54 | foreach ($it as $name => $url) { 55 | 56 | // set the current depth 57 | $curDepth = $it->getDepth(); 58 | 59 | // store the difference in depths 60 | $diff = abs($curDepth - $depth); 61 | 62 | // close previous nested levels 63 | if ($curDepth < $depth) { 64 | $output->append(str_repeat('', $diff)); 65 | } 66 | 67 | // check if we have the last nav item 68 | if ($it->hasNext()) { 69 | $output->append('
  • ' . $name . ''); 70 | } else { 71 | $output->append('
  • ' . $name . ''); 72 | } 73 | 74 | // either add a subnav or close the list item 75 | if ($it->hasChildren()) { 76 | $output->append('
  • ', $diff); 153 | } 154 | 155 | // check if we have the last nav item 156 | if ($this->hasNext()) { 157 | $output .= '
  • ' . $name . ''; 158 | } else { 159 | $output .= '
  • ' . $name . ''; 160 | } 161 | 162 | // either add a subnav or close the list item 163 | if ($this->hasChildren()) { 164 | $output .= '