diff --git a/framework/yii/helpers/base/FileHelper.php b/framework/yii/helpers/base/FileHelper.php index 0f6afca..8cbf1b2 100644 --- a/framework/yii/helpers/base/FileHelper.php +++ b/framework/yii/helpers/base/FileHelper.php @@ -134,12 +134,21 @@ class FileHelper * * - 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. - * - beforeCopy: callback, a PHP callback that is called before copying each sub-directory or file. - * If the callback returns false, the copy operation for the sub-directory or file will be cancelled. + * - 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 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 - * file to be 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`. + * file copied from, while `$to` is the copy target. */ public static function copyDirectory($src, $dst, $options = array()) { @@ -154,7 +163,7 @@ class FileHelper } $from = $src . 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)) { copy($from, $to); if (isset($options['fileMode'])) { @@ -177,21 +186,22 @@ class FileHelper */ public static function removeDirectory($dir) { - $items = glob($dir . DIRECTORY_SEPARATOR . '{,.}*', GLOB_MARK | GLOB_BRACE); - foreach ($items as $item) { - $itemBaseName = basename($item); - if ($itemBaseName === '.' || $itemBaseName === '..') { + if (!is_dir($dir) || !($handle = opendir($dir))) { + return; + } + while (($file = readdir($handle)) !== false) { + if ($file === '.' || $file === '..') { continue; } - if (StringHelper::substr($item, -1, 1) == DIRECTORY_SEPARATOR) { - static::removeDirectory($item); + $path = $dir . DIRECTORY_SEPARATOR . $file; + if (is_file($path)) { + unlink($path); } else { - unlink($item); + static::removeDirectory($path); } } - if (is_dir($dir)) { - rmdir($dir); - } + closedir($handle); + rmdir($dir); } /** @@ -200,18 +210,17 @@ class FileHelper * @param array $options options for file searching. Valid options are: * * - 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. - * The signature of the callback should be: `function ($base, $name, $isFile)`, where `$base` is the name of directory, - * which contains file or sub-directory, `$name` file or sub-directory name, `$isFile` indicates if object is a file or a directory. + * 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 ($path)`, where `$path` refers the full path to be filtered. * - 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. - * The matching is done in a partial manner. For example, the '.svn' will match all files and directories whose name ends with '.svn'. - * And the name '/a/b' will match all files and directories ending with '/a/b'. - * Note, that '/' should be used as separator regardless of the value of the DIRECTORY_SEPARATOR constant. - * If a file/directory matches both a name in "only" and "except", it will NOT be put in the result set. - * - except: array, list of path names that the files or directories should NOT match if they want to be put in the result set. - * - recursive: boolean, whether the files should be looked recursively under all subdirectories. - * Defaults to true. + * - only: array, list of patterns that the files or directories should match if they want to be returned. + * 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 returned. + * - except: array, list of patterns that the files or directories should NOT match if they want to be returned. + * 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 lookied for. Defaults to true. * @return array files found under the directory. The file list is sorted. */ public static function findFiles($dir, $options = array()) @@ -223,7 +232,7 @@ class FileHelper continue; } $path = $dir . DIRECTORY_SEPARATOR . $file; - if (static::validatePath($path, $options)) { + if (static::filterPath($path, $options)) { if (is_file($path)) { $list[] = $path; } elseif (!isset($options['recursive']) || $options['recursive']) { @@ -236,62 +245,61 @@ class FileHelper } /** - * Validates a file or directory, checking if it match given conditions. - * @param string $path the path of the file or directory to be validated - * @param array $options options for file searching. - * @return boolean whether the file or directory is valid + * Checks if the given file path satisfies the filtering options. + * @param string $path the path of the file or directory to be checked + * @param array $options the filtering options. See [[findFiles()]] for explanations of + * 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)) { return false; } $path = str_replace('\\', '/', $path); - $n = strlen($path); + $n = StringHelper::strlen($path); if (!empty($options['except'])) { foreach ($options['except'] as $name) { - if (strrpos($path, $name) === $n - strlen($name)) { + if (StringHelper::substr($path, -StringHelper::strlen($name), $n) === $name) { return false; } } } if (!empty($options['only'])) { foreach ($options['only'] as $name) { - if (strrpos($path, $name) !== $n - strlen($name)) { + if (StringHelper::substr($path, -StringHelper::strlen($name), $n) !== $name) { return false; } } } - if (!empty($options['fileTypes'])) { - if (!is_file($path)) { - return true; - } - if (($type = pathinfo($path, PATHINFO_EXTENSION)) !== '') { - return in_array($type, $options['fileTypes']); - } else { - return false; - } + if (!empty($options['fileTypes']) && is_file($path)) { + return in_array(pathinfo($path, PATHINFO_EXTENSION), $options['fileTypes']); + } else { + return true; } - return true; } /** - * Shared environment safe version of mkdir. Supports recursive creation. - * For avoidance of umask side-effects chmod is used. + * Makes directory. + * + * 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 integer $mode the permission to be set for created directory. If not set 0777 will be used. - * @param boolean $recursive whether to create directory structure recursive if parent dirs do not exist. - * @return boolean result of mkdir. - * @see mkdir + * @param integer $mode the permission to be set for created directory. + * @param boolean $recursive whether to create parent directories if they do not exist. + * @return boolean whether the directory is created successfully */ - public static function mkdir($path, $mode = null, $recursive = false) + public static function mkdir($path, $mode = 0777, $recursive = true) { - $prevDir = dirname($path); - if ($recursive && !is_dir($path) && !is_dir($prevDir)) { - static::mkdir(dirname($path), $mode, true); + if (is_dir($path)) { + return true; + } + $parentDir = dirname($path); + if ($recursive && !is_dir($parentDir)) { + static::mkdir($parentDir, $mode, true); } - $mode = isset($mode) ? $mode : 0777; $result = mkdir($path, $mode); chmod($path, $mode); return $result; diff --git a/tests/unit/framework/helpers/FileHelperTest.php b/tests/unit/framework/helpers/FileHelperTest.php index 781812d..5ff4099 100644 --- a/tests/unit/framework/helpers/FileHelperTest.php +++ b/tests/unit/framework/helpers/FileHelperTest.php @@ -45,7 +45,7 @@ class FileHelperTest extends TestCase */ protected function removeDir($dirName) { - if (!empty($dirName) && file_exists($dirName)) { + if (!empty($dirName) && is_dir($dirName)) { if ($handle = opendir($dirName)) { while (false !== ($entry = readdir($handle))) { if ($entry != '.' && $entry != '..') { @@ -235,8 +235,8 @@ class FileHelperTest extends TestCase $dirName = $basePath . DIRECTORY_SEPARATOR . $dirName; $options = array( - 'filter' => function($base, $name, $isFile) use ($passedFileName) { - return ($passedFileName == $name); + 'filter' => function($path) use ($passedFileName) { + return $passedFileName == basename($path); } ); $foundFiles = FileHelper::findFiles($dirName, $options); @@ -261,7 +261,7 @@ class FileHelperTest extends TestCase $dirName = $basePath . DIRECTORY_SEPARATOR . $dirName; $options = array( - 'exclude' => array($excludeFileName), + 'except' => array($excludeFileName), ); $foundFiles = FileHelper::findFiles($dirName, $options); $this->assertEquals(array($dirName . DIRECTORY_SEPARATOR . $fileName), $foundFiles); @@ -320,4 +320,4 @@ class FileHelperTest extends TestCase $this->assertEquals($mimeType, FileHelper::getMimeTypeByExtension($fileName, $magicFile)); } } -} \ No newline at end of file +}