Commit 040db44f by Qiang Xue

refactored FileHelper.

parent 2a30e329
...@@ -134,12 +134,21 @@ class FileHelper ...@@ -134,12 +134,21 @@ class FileHelper
* *
* - dirMode: integer, the permission to be set for newly copied directories. Defaults to 0777. * - dirMode: integer, the permission to be set for newly copied directories. Defaults to 0777.
* - fileMode: integer, the permission to be set for newly copied files. Defaults to the current environment setting. * - fileMode: integer, the permission to be set for newly copied files. Defaults to the current environment setting.
* - beforeCopy: callback, a PHP callback that is called before copying each sub-directory or file. * - filter: callback, a PHP callback that is called for each sub-directory or file.
* If the callback returns false, the copy operation for the sub-directory or file will be cancelled. * If the callback returns false, the the sub-directory or file will not be copied.
* The signature of the callback should be: `function ($path)`, where `$path` refers the full path to be copied.
* - fileTypes: array, list of file name suffix (without dot). Only files with these suffixes will be copied.
* - only: array, list of patterns that the files or directories should match if they want to be copied.
* A path matches a pattern if it contains the pattern string at its end. For example,
* '/a/b' will match all files and directories ending with '/a/b'; and the '.svn' will match all files and
* directories whose name ends with '.svn'. Note, the '/' characters in a pattern matches both '/' and '\'.
* If a file/directory matches both a name in "only" and "except", it will NOT be copied.
* - except: array, list of patterns that the files or directories should NOT match if they want to be copied.
* For more details on how to specify the patterns, please refer to the "only" option.
* - recursive: boolean, whether the files under the subdirectories should also be copied. Defaults to true.
* - afterCopy: callback, a PHP callback that is called after each sub-directory or file is successfully copied.
* The signature of the callback should be: `function ($from, $to)`, where `$from` is the sub-directory or * The signature of the callback should be: `function ($from, $to)`, where `$from` is the sub-directory or
* file to be copied from, while `$to` is the copy target. * file copied from, while `$to` is the copy target.
* - afterCopy: callback, a PHP callback that is called after a sub-directory or file is successfully copied.
* The signature of the callback is similar to that of `beforeCopy`.
*/ */
public static function copyDirectory($src, $dst, $options = array()) public static function copyDirectory($src, $dst, $options = array())
{ {
...@@ -154,7 +163,7 @@ class FileHelper ...@@ -154,7 +163,7 @@ class FileHelper
} }
$from = $src . DIRECTORY_SEPARATOR . $file; $from = $src . DIRECTORY_SEPARATOR . $file;
$to = $dst . DIRECTORY_SEPARATOR . $file; $to = $dst . DIRECTORY_SEPARATOR . $file;
if (!isset($options['beforeCopy']) || call_user_func($options['beforeCopy'], $from, $to)) { if (static::filterPath($from, $options)) {
if (is_file($from)) { if (is_file($from)) {
copy($from, $to); copy($from, $to);
if (isset($options['fileMode'])) { if (isset($options['fileMode'])) {
...@@ -177,21 +186,22 @@ class FileHelper ...@@ -177,21 +186,22 @@ class FileHelper
*/ */
public static function removeDirectory($dir) public static function removeDirectory($dir)
{ {
$items = glob($dir . DIRECTORY_SEPARATOR . '{,.}*', GLOB_MARK | GLOB_BRACE); if (!is_dir($dir) || !($handle = opendir($dir))) {
foreach ($items as $item) { return;
$itemBaseName = basename($item); }
if ($itemBaseName === '.' || $itemBaseName === '..') { while (($file = readdir($handle)) !== false) {
if ($file === '.' || $file === '..') {
continue; continue;
} }
if (StringHelper::substr($item, -1, 1) == DIRECTORY_SEPARATOR) { $path = $dir . DIRECTORY_SEPARATOR . $file;
static::removeDirectory($item); if (is_file($path)) {
unlink($path);
} else { } else {
unlink($item); static::removeDirectory($path);
} }
} }
if (is_dir($dir)) { closedir($handle);
rmdir($dir); rmdir($dir);
}
} }
/** /**
...@@ -200,18 +210,17 @@ class FileHelper ...@@ -200,18 +210,17 @@ class FileHelper
* @param array $options options for file searching. Valid options are: * @param array $options options for file searching. Valid options are:
* *
* - filter: callback, a PHP callback that is called for each sub-directory or file. * - filter: callback, a PHP callback that is called for each sub-directory or file.
* If the callback returns false, the the sub-directory or file will not be placed to the result set. * If the callback returns false, the the sub-directory or file will be excluded from the returning result.
* The signature of the callback should be: `function ($base, $name, $isFile)`, where `$base` is the name of directory, * The signature of the callback should be: `function ($path)`, where `$path` refers the full path to be filtered.
* which contains file or sub-directory, `$name` file or sub-directory name, `$isFile` indicates if object is a file or a directory.
* - fileTypes: array, list of file name suffix (without dot). Only files with these suffixes will be returned. * - fileTypes: array, list of file name suffix (without dot). Only files with these suffixes will be returned.
* - only: array, list of path names that the files or directories should match if they want to be put in the result set. * - only: array, list of patterns that the files or directories should match if they want to be returned.
* The matching is done in a partial manner. For example, the '.svn' will match all files and directories whose name ends with '.svn'. * A path matches a pattern if it contains the pattern string at its end. For example,
* And the name '/a/b' will match all files and directories ending with '/a/b'. * '/a/b' will match all files and directories ending with '/a/b'; and the '.svn' will match all files and
* Note, that '/' should be used as separator regardless of the value of the DIRECTORY_SEPARATOR constant. * directories whose name ends with '.svn'. Note, the '/' characters in a pattern matches both '/' and '\'.
* If a file/directory matches both a name in "only" and "except", it will NOT be put in the result set. * If a file/directory matches both a name in "only" and "except", it will NOT be returned.
* - except: array, list of path names that the files or directories should NOT match if they want to be put in the result set. * - except: array, list of patterns that the files or directories should NOT match if they want to be returned.
* - recursive: boolean, whether the files should be looked recursively under all subdirectories. * For more details on how to specify the patterns, please refer to the "only" option.
* Defaults to true. * - recursive: boolean, whether the files under the subdirectories should also be lookied for. Defaults to true.
* @return array files found under the directory. The file list is sorted. * @return array files found under the directory. The file list is sorted.
*/ */
public static function findFiles($dir, $options = array()) public static function findFiles($dir, $options = array())
...@@ -223,7 +232,7 @@ class FileHelper ...@@ -223,7 +232,7 @@ class FileHelper
continue; continue;
} }
$path = $dir . DIRECTORY_SEPARATOR . $file; $path = $dir . DIRECTORY_SEPARATOR . $file;
if (static::validatePath($path, $options)) { if (static::filterPath($path, $options)) {
if (is_file($path)) { if (is_file($path)) {
$list[] = $path; $list[] = $path;
} elseif (!isset($options['recursive']) || $options['recursive']) { } elseif (!isset($options['recursive']) || $options['recursive']) {
...@@ -236,62 +245,61 @@ class FileHelper ...@@ -236,62 +245,61 @@ class FileHelper
} }
/** /**
* Validates a file or directory, checking if it match given conditions. * Checks if the given file path satisfies the filtering options.
* @param string $path the path of the file or directory to be validated * @param string $path the path of the file or directory to be checked
* @param array $options options for file searching. * @param array $options the filtering options. See [[findFiles()]] for explanations of
* @return boolean whether the file or directory is valid * the supported options.
* @return boolean whether the file or directory satisfies the filtering options.
*/ */
protected static function validatePath($path, $options) protected static function filterPath($path, $options)
{ {
if (isset($options['filter']) && !call_user_func($options['filter'], $path)) { if (isset($options['filter']) && !call_user_func($options['filter'], $path)) {
return false; return false;
} }
$path = str_replace('\\', '/', $path); $path = str_replace('\\', '/', $path);
$n = strlen($path); $n = StringHelper::strlen($path);
if (!empty($options['except'])) { if (!empty($options['except'])) {
foreach ($options['except'] as $name) { foreach ($options['except'] as $name) {
if (strrpos($path, $name) === $n - strlen($name)) { if (StringHelper::substr($path, -StringHelper::strlen($name), $n) === $name) {
return false; return false;
} }
} }
} }
if (!empty($options['only'])) { if (!empty($options['only'])) {
foreach ($options['only'] as $name) { foreach ($options['only'] as $name) {
if (strrpos($path, $name) !== $n - strlen($name)) { if (StringHelper::substr($path, -StringHelper::strlen($name), $n) !== $name) {
return false; return false;
} }
} }
} }
if (!empty($options['fileTypes'])) { if (!empty($options['fileTypes']) && is_file($path)) {
if (!is_file($path)) { return in_array(pathinfo($path, PATHINFO_EXTENSION), $options['fileTypes']);
return true; } else {
} return true;
if (($type = pathinfo($path, PATHINFO_EXTENSION)) !== '') {
return in_array($type, $options['fileTypes']);
} else {
return false;
}
} }
return true;
} }
/** /**
* Shared environment safe version of mkdir. Supports recursive creation. * Makes directory.
* For avoidance of umask side-effects chmod is used. *
* This method is similar to the PHP `mkdir()` function except that
* it uses `chmod()` to set the permission of the created directory
* in order to avoid the impact of the `umask` setting.
* *
* @param string $path path to be created. * @param string $path path to be created.
* @param integer $mode the permission to be set for created directory. If not set 0777 will be used. * @param integer $mode the permission to be set for created directory.
* @param boolean $recursive whether to create directory structure recursive if parent dirs do not exist. * @param boolean $recursive whether to create parent directories if they do not exist.
* @return boolean result of mkdir. * @return boolean whether the directory is created successfully
* @see mkdir
*/ */
public static function mkdir($path, $mode = null, $recursive = false) public static function mkdir($path, $mode = 0777, $recursive = true)
{ {
$prevDir = dirname($path); if (is_dir($path)) {
if ($recursive && !is_dir($path) && !is_dir($prevDir)) { return true;
static::mkdir(dirname($path), $mode, true); }
$parentDir = dirname($path);
if ($recursive && !is_dir($parentDir)) {
static::mkdir($parentDir, $mode, true);
} }
$mode = isset($mode) ? $mode : 0777;
$result = mkdir($path, $mode); $result = mkdir($path, $mode);
chmod($path, $mode); chmod($path, $mode);
return $result; return $result;
......
...@@ -45,7 +45,7 @@ class FileHelperTest extends TestCase ...@@ -45,7 +45,7 @@ class FileHelperTest extends TestCase
*/ */
protected function removeDir($dirName) protected function removeDir($dirName)
{ {
if (!empty($dirName) && file_exists($dirName)) { if (!empty($dirName) && is_dir($dirName)) {
if ($handle = opendir($dirName)) { if ($handle = opendir($dirName)) {
while (false !== ($entry = readdir($handle))) { while (false !== ($entry = readdir($handle))) {
if ($entry != '.' && $entry != '..') { if ($entry != '.' && $entry != '..') {
...@@ -235,8 +235,8 @@ class FileHelperTest extends TestCase ...@@ -235,8 +235,8 @@ class FileHelperTest extends TestCase
$dirName = $basePath . DIRECTORY_SEPARATOR . $dirName; $dirName = $basePath . DIRECTORY_SEPARATOR . $dirName;
$options = array( $options = array(
'filter' => function($base, $name, $isFile) use ($passedFileName) { 'filter' => function($path) use ($passedFileName) {
return ($passedFileName == $name); return $passedFileName == basename($path);
} }
); );
$foundFiles = FileHelper::findFiles($dirName, $options); $foundFiles = FileHelper::findFiles($dirName, $options);
...@@ -261,7 +261,7 @@ class FileHelperTest extends TestCase ...@@ -261,7 +261,7 @@ class FileHelperTest extends TestCase
$dirName = $basePath . DIRECTORY_SEPARATOR . $dirName; $dirName = $basePath . DIRECTORY_SEPARATOR . $dirName;
$options = array( $options = array(
'exclude' => array($excludeFileName), 'except' => array($excludeFileName),
); );
$foundFiles = FileHelper::findFiles($dirName, $options); $foundFiles = FileHelper::findFiles($dirName, $options);
$this->assertEquals(array($dirName . DIRECTORY_SEPARATOR . $fileName), $foundFiles); $this->assertEquals(array($dirName . DIRECTORY_SEPARATOR . $fileName), $foundFiles);
...@@ -320,4 +320,4 @@ class FileHelperTest extends TestCase ...@@ -320,4 +320,4 @@ class FileHelperTest extends TestCase
$this->assertEquals($mimeType, FileHelper::getMimeTypeByExtension($fileName, $magicFile)); $this->assertEquals($mimeType, FileHelper::getMimeTypeByExtension($fileName, $magicFile));
} }
} }
} }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment