From 5a9f344da8258d77188750989b5ab4c41e9e6e21 Mon Sep 17 00:00:00 2001 From: TimWolla Date: Fri, 15 Jul 2011 18:48:26 +0200 Subject: [PATCH] Added DirectoryUtil DirectoryUtil provides easier handling of directories like: - Scanning through the SPL-Iterators - Easier deleting (with pattern) - Already implemented Size-calculation --- .../files/lib/util/DirectoryUtil.class.php | 355 ++++++++++++++++++ 1 file changed, 355 insertions(+) create mode 100644 wcfsetup/install/files/lib/util/DirectoryUtil.class.php diff --git a/wcfsetup/install/files/lib/util/DirectoryUtil.class.php b/wcfsetup/install/files/lib/util/DirectoryUtil.class.php new file mode 100644 index 0000000000..6c5686bbf1 --- /dev/null +++ b/wcfsetup/install/files/lib/util/DirectoryUtil.class.php @@ -0,0 +1,355 @@ + +* @package com.woltlab.wcf +* @subpackage util +* @category Community Framework +*/ +class DirectoryUtil { + /** + * @var DirectoryIterator | RecursiveDirectoryIterator + */ + protected $obj = null; + + /** + * Stores all files with full path + * + * @var array + */ + protected $files = array(); + + /** + * Stores all files with filename as key and DirectoryIterator object as value + * + * @var array + */ + protected $filesObj = array(); + + /** + * Directory-size in bytes + * + * @var integer + */ + protected $size = 0; + + /** + * Directory path + * + * @var string + */ + protected $directory = ''; + + /** + * Determines wether scan should be recursive + * + * @var boolean + */ + protected $recursive = true; + + /** + * No sorting + * + * @var integer + */ + const SORT_NONE = -1; + + /** + * All recursive and non-recursive instances + * + * @var array + */ + protected static $instances = array( + true => array(), // recursive instances + false => array() // non-recursive instances + ); + + /** + * Creates a new instance of DirectoryUtil + * + * @param string $directory directory path + * @param boolean $recursive created a recursive directory iterator + * @see DirectoryUtil::getInstance() + */ + protected function __construct($directory, $recursive = true) { + $this->directory = $directory; + $this->recursive = $recursive; + + // handle iterator type + if ($this->recursive) { + $this->obj = new \RecursiveDirectoryIterator($directory); + } + else { + $this->obj = new \DirectoryIterator($directory); + } + } + + /** + * @see DirectoryUtil::getInstance() + */ + private final function __clone() {} + + /** + * Clears an instance + * + * @param string $directory directory path + * @param boolean $recursive destroy a recursive instance + * @return boolean success + */ + public static function destroy($directory, $recursive = true) { + $directory = realpath(FileUtil::unifyDirSeperator($directory)); + if (!isset(static::$instances[$recursive][$directory])) { + return false; + } + + unset (static::$instances[$recursive][$directory]); + return true; + } + + /** + * Returns an instance of DirectoryUtil (or child) + * + * @param string $directory Path + * @param boolean $recursive Walk through sub-directories too + * @return static + */ + public static function getInstance($tmpDirectory, $recursive = true) { + $directory = realpath(FileUtil::unifyDirSeperator($tmpDirectory)); + // realpath returns false if the directory does not exist + if ($directory === false) { + throw new SystemException("Unknown directory '".$tmpDirectory."'"); + } + + if (!isset(static::$instances[$recursive][$directory])) { + static::$instances[$recursive][$directory] = new static($directory, $recursive); + } + + return static::$instances[$recursive][$directory]; + } + + /** + * Executes a callback on each file + * + * @param callback $callback Valid callback + * @param string $pattern Apply callback only to files matching the given pattern + * @return boolean Returns false if callback is missing or no files available + */ + public function executeCallback($callback, $pattern = '') { + if (!is_callable($callback) || empty($this->files)) return false; + + $files = $this->getFiles(); + // check for pattern only once -> faster + if (empty($pattern)) { + foreach ($files as $filename) { + call_user_func($callback, $filename); + } + } + else { + foreach ($files as $filename) { + if (!preg_match($pattern, $filename)) continue; + + call_user_func($callback, $filename); + } + } + + return true; + } + + /** + * Returns a sorted list of files + * + * @param integer $order sort-order + * @return array + */ + public function getFiles($order = SORT_ASC) { + // scan the folder + $this->scanFiles(); + $files = $this->files; + + if ($order == SORT_DESC) { + krsort($files, $order); + } + else if ($order == SORT_ASC) { + ksort($files, $order); + } + else if ($order == self::SORT_NONE) { + // nothing to do here :) + } + else { + throw new SystemException('The given sorting is not supported'); + } + + return $files; + } + + /** + * Returns a sorted list of files, with DirectoryIterator object as value + * + * @param integer $order sort-order + * @return array + */ + public function getFilesObj($order = SORT_ASC) { + // scan the folder + $this->scanFilesObj(); + $objects = $this->filesObj; + + if ($order == SORT_DESC) { + krsort($objects, $order); + } + else if ($order == SORT_ASC) { + ksort($objects, $order); + } + else if ($order == self::SORT_NONE) { + // nothing to do here :) + } + else { + throw new SystemException('The given sorting is not supported'); + } + + return $objects; + } + + /** + * Fills the list of available files + */ + protected function scanFiles() { + // value is cached + if (!empty($this->files)) return; + + if ($this->recursive) { + $it = new \RecursiveIteratorIterator($this->obj, \RecursiveIteratorIterator::CHILD_FIRST); + + foreach ($it as $filename => $obj) { + // ignore . and .. + if ($it->isDot()) continue; + + $this->files[$filename] = $filename; + } + } + else { + foreach ($this->obj as $filename => $obj) { + // ignore . and .. + if ($this->obj->isDot()) continue; + + $this->files[$obj->getFilename()] = $obj->getFilename(); + } + } + } + + /** + * Fills the list of available files, with DirectoryIterator object as value + */ + protected function scanFilesObj() { + // value is cached + if (!empty($this->filesObj)) return; + + if ($this->recursive) { + $it = new \RecursiveIteratorIterator($this->obj, \RecursiveIteratorIterator::CHILD_FIRST); + + foreach ($it as $filename => $obj) { + // ignore . and .. + if ($it->isDot()) continue; + + $this->filesObj[$filename] = $obj; + } + } + else { + foreach ($this->obj as $filename => $obj) { + // ignore . and .. + if ($this->obj->isDot()) continue; + + $this->filesObj[$obj->getFilename()] = $obj; + } + } + } + + /** + * Recursive remove of directory + */ + public function removeAll() { + if (!$this->recursive) throw new SystemException('Removing of directory only works in recursive mode'); + + $files = $this->getFilesObj(self::SORT_NONE); + foreach ($files as $filename => $obj) { + if (!is_writable($obj->getPath())) { + throw new SystemException("Could not remove directory: '".$obj->getPath()."' is not writable"); + } + + if ($obj->isDir()) { + rmdir($filename); + } + else if ($obj->isFile()) { + unlink($filename); + } + } + + rmdir($this->directory); + // clear cache + $this->filesObj = array(); + $this->scanFilesObj(); + + $this->files = array(); + $this->scanFiles(); + + // destroy cached instance + unset(static::$instances[$this->recursive][$this->directory]); + } + + /** + * Removes all files that match the pattern + * + * @param string $pattern regex pattern + */ + public function removePattern($pattern) { + if (!$this->recursive) throw new SystemException('Removing of files only works in recursive mode'); + + $files = $this->getFilesObj(self::SORT_NONE); + foreach ($files as $filename => $obj) { + if (!preg_match($pattern, $filename)) continue; + + if (!is_writable($obj->getPath())) { + throw new SystemException("Could not remove directory: '".$obj->getPath()."' is not writable"); + } + + if ($obj->isDir()) { + rmdir($filename); + } + else if ($obj->isFile()) { + unlink($filename); + } + } + + // clear cache + $this->filesObj = array(); + $this->scanFilesObj(); + + $this->files = array(); + $this->scanFiles(); + } + + /** + * Calculates the size of the directory. + * + * @return integer Directorysize in bytes + */ + public function getSize() { + if (!$this->recursive) throw new SystemException('Calculating of size only works in recursive mode'); + + // read cached value first + if ($this->size) return $this->size; + + $files = $this->getFilesObj(self::SORT_NONE); + foreach ($files as $filename => $obj) { + $this->size += $obj->getSize(); + } + + return $this->size; + } +} +?> \ No newline at end of file -- 2.20.1