/**
  * List Directory
  *
  * The final argument, $cb, is a callback that either evaluates to true or
  * false and performs a filter operation, or it can also modify the
  * directory/file names returned.  To achieve the latter effect use as
  * follows:
  *
  * <code>
  * <?php
  * function uc(&$filename) {
  *     $filename = strtoupper($filename);
  *     return true;
  * }
  * $entries = File_Util::listDir('.', FILE_LIST_ALL, FILE_SORT_NONE, 'uc');
  * foreach ($entries as $e) {
  *     echo $e->name, "\n";
  * }
  * ?>
  * </code>
  *
  * @static
  * @access  public
  * @return  array
  * @param   string  $path
  * @param   int     $list
  * @param   int     $sort
  * @param   mixed   $cb
  */
 function listDir($path, $list = FILE_LIST_ALL, $sort = FILE_SORT_NONE, $cb = null)
 {
     if (!strlen($path) || !is_dir($path)) {
         return null;
     }
     $entries = array();
     for ($dir = dir($path); false !== ($entry = $dir->read());) {
         if ($list & FILE_LIST_DOTS || $entry[0] !== '.') {
             $isRef = $entry === '.' || $entry === '..';
             $isDir = $isRef || is_dir($path . '/' . $entry);
             if ((!$isDir && $list & FILE_LIST_FILES || $isDir && $list & FILE_LIST_DIRS) && (!is_callable($cb) || call_user_func_array($cb, array(&$entry)))) {
                 $entries[] = (object) array('name' => $entry, 'size' => $isDir ? null : filesize($path . '/' . $entry), 'date' => filemtime($path . '/' . $entry));
             }
         }
     }
     $dir->close();
     if ($sort) {
         $entries = File_Util::sortFiles($entries, $sort);
     }
     return $entries;
 }