From e2f7b99f1694331a25012eec10c5bd8cd247bec9 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Mon, 10 Jun 2013 20:22:34 +0300 Subject: [PATCH 01/12] Unit test for "yii\helpers\base\FileHelper" has been created. --- tests/unit/framework/helpers/FileHelperTest.php | 71 +++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 tests/unit/framework/helpers/FileHelperTest.php diff --git a/tests/unit/framework/helpers/FileHelperTest.php b/tests/unit/framework/helpers/FileHelperTest.php new file mode 100644 index 0000000..89a5a02 --- /dev/null +++ b/tests/unit/framework/helpers/FileHelperTest.php @@ -0,0 +1,71 @@ +testFilePath = Yii::getAlias('@yiiunit/runtime') . DIRECTORY_SEPARATOR . get_class($this); + $this->createDir($this->testFilePath); + } + + public function tearDown() + { + $this->removeDir($this->testFilePath); + } + + /** + * Creates directory. + * @param string $dirName directory full name. + */ + protected function createDir($dirName) + { + if (!file_exists($dirName)) { + mkdir($dirName, 0777, true); + } + } + + /** + * Removes directory. + * @param string $dirName directory full name. + */ + protected function removeDir($dirName) + { + if (!empty($dirName) && file_exists($dirName)) { + exec("rm -rf {$dirName}"); + } + } + + // Tests : + + public function testCopyDirectory() { + $basePath = $this->testFilePath; + $srcDirName = $basePath . DIRECTORY_SEPARATOR . 'test_src_dir'; + mkdir($srcDirName, 0777, true); + $files = array( + 'file1.txt' => 'file 1 content', + 'file2.txt' => 'file 2 content', + ); + foreach ($files as $name => $content) { + file_put_contents($srcDirName . DIRECTORY_SEPARATOR . $name, $content); + } + $dstDirName = $basePath . DIRECTORY_SEPARATOR . 'test_dst_dir'; + + FileHelper::copyDirectory($srcDirName, $dstDirName); + + $this->assertTrue(file_exists($dstDirName), 'Destination directory does not exist!'); + foreach ($files as $name => $content) { + $fileName = $dstDirName . DIRECTORY_SEPARATOR . $name; + $this->assertTrue(file_exists($fileName), 'Directory file is missing!'); + $this->assertEquals($content, file_get_contents($fileName), 'Incorrect file content!'); + } + } +} \ No newline at end of file From ef60ec56a3b98a735b3d0ad8cf5e633daec5df88 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Mon, 10 Jun 2013 20:28:26 +0300 Subject: [PATCH 02/12] Method "yii\helpers\base\FileHelper::removeDirectory()" has been added. --- framework/yii/helpers/base/FileHelper.php | 22 +++++++++++++++++++ tests/unit/framework/helpers/FileHelperTest.php | 29 ++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/framework/yii/helpers/base/FileHelper.php b/framework/yii/helpers/base/FileHelper.php index 954c86e..d76ebc7 100644 --- a/framework/yii/helpers/base/FileHelper.php +++ b/framework/yii/helpers/base/FileHelper.php @@ -169,4 +169,26 @@ class FileHelper } closedir($handle); } + + /** + * Removes a directory recursively. + * @param string $directory to be deleted recursively. + */ + public static function removeDirectory($directory) + { + $items = glob($directory . DIRECTORY_SEPARATOR . '{,.}*', GLOB_MARK | GLOB_BRACE); + foreach ($items as $item) { + if (basename($item) == '.' || basename($item) == '..') { + continue; + } + if (substr($item, -1) == DIRECTORY_SEPARATOR) { + self::removeDirectory($item); + } else { + unlink($item); + } + } + if (is_dir($directory)) { + rmdir($directory); + } + } } diff --git a/tests/unit/framework/helpers/FileHelperTest.php b/tests/unit/framework/helpers/FileHelperTest.php index 89a5a02..2027510 100644 --- a/tests/unit/framework/helpers/FileHelperTest.php +++ b/tests/unit/framework/helpers/FileHelperTest.php @@ -9,6 +9,9 @@ use yii\test\TestCase; */ class FileHelperTest extends TestCase { + /** + * @var string test files path. + */ private $testFilePath = ''; public function setUp() @@ -46,7 +49,8 @@ class FileHelperTest extends TestCase // Tests : - public function testCopyDirectory() { + public function testCopyDirectory() + { $basePath = $this->testFilePath; $srcDirName = $basePath . DIRECTORY_SEPARATOR . 'test_src_dir'; mkdir($srcDirName, 0777, true); @@ -68,4 +72,27 @@ class FileHelperTest extends TestCase $this->assertEquals($content, file_get_contents($fileName), 'Incorrect file content!'); } } + + public function testRemoveDirectory() + { + $basePath = $this->testFilePath; + $dirName = $basePath . DIRECTORY_SEPARATOR . 'test_dir_for_remove'; + mkdir($dirName, 0777, true); + $files = array( + 'file1.txt' => 'file 1 content', + 'file2.txt' => 'file 2 content', + ); + foreach ($files as $name => $content) { + file_put_contents($dirName . DIRECTORY_SEPARATOR . $name, $content); + } + $subDirName = $dirName . DIRECTORY_SEPARATOR . 'test_sub_dir'; + mkdir($subDirName, 0777, true); + foreach ($files as $name => $content) { + file_put_contents($subDirName . DIRECTORY_SEPARATOR . $name, $content); + } + + FileHelper::removeDirectory($dirName); + + $this->assertFalse(file_exists($dirName), 'Unable to remove directory!'); + } } \ No newline at end of file From dfda6663f613d98a64ed911f7cfc48e1f7b8085c Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Mon, 10 Jun 2013 20:57:17 +0300 Subject: [PATCH 03/12] Method "yii\helpers\base\FileHelper::findFiles()" has been added. --- framework/yii/helpers/base/FileHelper.php | 97 +++++++++++++++++++++++++ tests/unit/framework/helpers/FileHelperTest.php | 29 ++++++++ 2 files changed, 126 insertions(+) diff --git a/framework/yii/helpers/base/FileHelper.php b/framework/yii/helpers/base/FileHelper.php index d76ebc7..27088a6 100644 --- a/framework/yii/helpers/base/FileHelper.php +++ b/framework/yii/helpers/base/FileHelper.php @@ -191,4 +191,101 @@ class FileHelper rmdir($directory); } } + + /** + * Returns the files found under the specified directory and subdirectories. + * @param string $dir the directory under which the files will be looked for. + * @param array $options options for file searching. Valid options are: + *
    + *
  • fileTypes: array, list of file name suffix (without dot). Only files with these suffixes will be returned.
  • + *
  • exclude: array, list of directory and file exclusions. Each exclusion can be either a name or a path. + * If a file or directory name or path matches the exclusion, it will not be copied. For example, an exclusion of + * '.svn' will exclude all files and directories whose name is '.svn'. And an exclusion of '/a/b' will exclude + * file or directory '$src/a/b'. Note, that '/' should be used as separator regardless of the value of the DIRECTORY_SEPARATOR constant. + *
  • + *
  • level: integer, recursion depth, default=-1. + * Level -1 means searching for all directories and files under the directory; + * Level 0 means searching for only the files DIRECTLY under the directory; + * level N means searching for those directories that are within N levels. + *
  • + *
+ * @return array files found under the directory. The file list is sorted. + */ + public static function findFiles($dir, array $options = array()) + { + $fileTypes = array(); + $exclude = array(); + $level = -1; + extract($options); + $list = static::findFilesRecursive($dir, '', $fileTypes, $exclude, $level); + sort($list); + return $list; + } + + /** + * Returns the files found under the specified directory and subdirectories. + * This method is mainly used by [[findFiles]]. + * @param string $dir the source directory. + * @param string $base the path relative to the original source directory. + * @param array $fileTypes list of file name suffix (without dot). Only files with these suffixes will be returned. + * @param array $exclude list of directory and file exclusions. Each exclusion can be either a name or a path. + * If a file or directory name or path matches the exclusion, it will not be copied. For example, an exclusion of + * '.svn' will exclude all files and directories whose name is '.svn'. And an exclusion of '/a/b' will exclude + * file or directory '$src/a/b'. Note, that '/' should be used as separator regardless of the value of the DIRECTORY_SEPARATOR constant. + * @param integer $level recursion depth. It defaults to -1. + * Level -1 means searching for all directories and files under the directory; + * Level 0 means searching for only the files DIRECTLY under the directory; + * level N means searching for those directories that are within N levels. + * @return array files found under the directory. + */ + protected static function findFilesRecursive($dir, $base, $fileTypes, $exclude, $level) + { + $list = array(); + $handle = opendir($dir); + while (($file = readdir($handle)) !== false) { + if ($file === '.' || $file === '..') { + continue; + } + $path = $dir . DIRECTORY_SEPARATOR . $file; + $isFile = is_file($path); + if (static::validatePath($base, $file, $isFile, $fileTypes, $exclude)) { + if ($isFile) { + $list[] = $path; + } elseif ($level) { + $list = array_merge($list, static::findFilesRecursive($path, $base . DIRECTORY_SEPARATOR . $file, $fileTypes, $exclude, $level-1)); + } + } + } + closedir($handle); + return $list; + } + + /** + * Validates a file or directory, checking if it match given conditions. + * @param string $base the path relative to the original source directory + * @param string $file the file or directory name + * @param boolean $isFile whether this is a file + * @param array $fileTypes list of valid file name suffixes (without dot). + * @param array $exclude list of directory and file exclusions. Each exclusion can be either a name or a path. + * If a file or directory name or path matches the exclusion, false will be returned. For example, an exclusion of + * '.svn' will return false for all files and directories whose name is '.svn'. And an exclusion of '/a/b' will return false for + * file or directory '$src/a/b'. Note, that '/' should be used as separator regardless of the value of the DIRECTORY_SEPARATOR constant. + * @return boolean whether the file or directory is valid + */ + protected static function validatePath($base, $file, $isFile, $fileTypes, $exclude) + { + foreach ($exclude as $e) { + if ($file === $e || strpos($base . DIRECTORY_SEPARATOR . $file, $e) === 0) { + return false; + } + } + if (!$isFile || empty($fileTypes)) { + return true; + } + if (($type = pathinfo($file, PATHINFO_EXTENSION)) !== '') { + return in_array($type, $fileTypes); + } else { + return false; + } + } } diff --git a/tests/unit/framework/helpers/FileHelperTest.php b/tests/unit/framework/helpers/FileHelperTest.php index 2027510..262c3be 100644 --- a/tests/unit/framework/helpers/FileHelperTest.php +++ b/tests/unit/framework/helpers/FileHelperTest.php @@ -95,4 +95,33 @@ class FileHelperTest extends TestCase $this->assertFalse(file_exists($dirName), 'Unable to remove directory!'); } + + public function testFindFiles() + { + $basePath = $this->testFilePath; + $expectedFiles = array(); + + $dirName = $basePath . DIRECTORY_SEPARATOR . 'test_dir_for_remove'; + mkdir($dirName, 0777, true); + $files = array( + 'file1.txt' => 'file 1 content', + 'file2.txt' => 'file 2 content', + ); + foreach ($files as $name => $content) { + $fileName = $dirName . DIRECTORY_SEPARATOR . $name; + file_put_contents($fileName, $content); + $expectedFiles[] = $fileName; + } + $subDirName = $dirName . DIRECTORY_SEPARATOR . 'test_sub_dir'; + mkdir($subDirName, 0777, true); + foreach ($files as $name => $content) { + $fileName = $subDirName . DIRECTORY_SEPARATOR . $name; + file_put_contents($fileName, $content); + $expectedFiles[] = $fileName; + } + + $foundFiles = FileHelper::findFiles($dirName); + sort($expectedFiles); + $this->assertEquals($expectedFiles, $foundFiles); + } } \ No newline at end of file From 539864f435933117710e482ae256786613969789 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Mon, 10 Jun 2013 21:33:53 +0300 Subject: [PATCH 04/12] "FileHelperTest" has been refactored. --- tests/unit/framework/helpers/FileHelperTest.php | 97 +++++++++++++++---------- 1 file changed, 58 insertions(+), 39 deletions(-) diff --git a/tests/unit/framework/helpers/FileHelperTest.php b/tests/unit/framework/helpers/FileHelperTest.php index 262c3be..14afa68 100644 --- a/tests/unit/framework/helpers/FileHelperTest.php +++ b/tests/unit/framework/helpers/FileHelperTest.php @@ -47,20 +47,42 @@ class FileHelperTest extends TestCase } } + /** + * Creates test files structure, + * @param array $items file system objects to be created in format: objectName => objectContent + * Arrays specifies directories, other values - files. + * @param string $basePath structure base file path. + */ + protected function createFileStructure(array $items, $basePath = '') { + if (empty($basePath)) { + $basePath = $this->testFilePath; + } + foreach ($items as $name => $content) { + $itemName = $basePath . DIRECTORY_SEPARATOR . $name; + if (is_array($content)) { + mkdir($itemName, 0777, true); + $this->createFileStructure($content, $itemName); + } else { + file_put_contents($itemName, $content); + } + } + } + // Tests : public function testCopyDirectory() { - $basePath = $this->testFilePath; - $srcDirName = $basePath . DIRECTORY_SEPARATOR . 'test_src_dir'; - mkdir($srcDirName, 0777, true); + $srcDirName = 'test_src_dir'; $files = array( 'file1.txt' => 'file 1 content', 'file2.txt' => 'file 2 content', ); - foreach ($files as $name => $content) { - file_put_contents($srcDirName . DIRECTORY_SEPARATOR . $name, $content); - } + $this->createFileStructure(array( + $srcDirName => $files + )); + + $basePath = $this->testFilePath; + $srcDirName = $basePath . DIRECTORY_SEPARATOR . $srcDirName; $dstDirName = $basePath . DIRECTORY_SEPARATOR . 'test_dst_dir'; FileHelper::copyDirectory($srcDirName, $dstDirName); @@ -75,21 +97,20 @@ class FileHelperTest extends TestCase public function testRemoveDirectory() { + $dirName = 'test_dir_for_remove'; + $this->createFileStructure(array( + $dirName => array( + 'file1.txt' => 'file 1 content', + 'file2.txt' => 'file 2 content', + 'test_sub_dir' => array( + 'sub_dir_file_1.txt' => 'sub dir file 1 content', + 'sub_dir_file_2.txt' => 'sub dir file 2 content', + ), + ), + )); + $basePath = $this->testFilePath; - $dirName = $basePath . DIRECTORY_SEPARATOR . 'test_dir_for_remove'; - mkdir($dirName, 0777, true); - $files = array( - 'file1.txt' => 'file 1 content', - 'file2.txt' => 'file 2 content', - ); - foreach ($files as $name => $content) { - file_put_contents($dirName . DIRECTORY_SEPARATOR . $name, $content); - } - $subDirName = $dirName . DIRECTORY_SEPARATOR . 'test_sub_dir'; - mkdir($subDirName, 0777, true); - foreach ($files as $name => $content) { - file_put_contents($subDirName . DIRECTORY_SEPARATOR . $name, $content); - } + $dirName = $basePath . DIRECTORY_SEPARATOR . $dirName; FileHelper::removeDirectory($dirName); @@ -98,27 +119,25 @@ class FileHelperTest extends TestCase public function testFindFiles() { + $dirName = 'test_dir'; + $this->createFileStructure(array( + $dirName => array( + 'file_1.txt' => 'file 1 content', + 'file_2.txt' => 'file 2 content', + 'test_sub_dir' => array( + 'file_1_1.txt' => 'sub dir file 1 content', + 'file_1_2.txt' => 'sub dir file 2 content', + ), + ), + )); $basePath = $this->testFilePath; - $expectedFiles = array(); - - $dirName = $basePath . DIRECTORY_SEPARATOR . 'test_dir_for_remove'; - mkdir($dirName, 0777, true); - $files = array( - 'file1.txt' => 'file 1 content', - 'file2.txt' => 'file 2 content', + $dirName = $basePath . DIRECTORY_SEPARATOR . $dirName; + $expectedFiles = array( + $dirName . DIRECTORY_SEPARATOR . 'file_1.txt', + $dirName . DIRECTORY_SEPARATOR . 'file_2.txt', + $dirName . DIRECTORY_SEPARATOR . 'test_sub_dir' . DIRECTORY_SEPARATOR . 'file_1_1.txt', + $dirName . DIRECTORY_SEPARATOR . 'test_sub_dir' . DIRECTORY_SEPARATOR . 'file_1_2.txt', ); - foreach ($files as $name => $content) { - $fileName = $dirName . DIRECTORY_SEPARATOR . $name; - file_put_contents($fileName, $content); - $expectedFiles[] = $fileName; - } - $subDirName = $dirName . DIRECTORY_SEPARATOR . 'test_sub_dir'; - mkdir($subDirName, 0777, true); - foreach ($files as $name => $content) { - $fileName = $subDirName . DIRECTORY_SEPARATOR . $name; - file_put_contents($fileName, $content); - $expectedFiles[] = $fileName; - } $foundFiles = FileHelper::findFiles($dirName); sort($expectedFiles); From f416a3664887c72b01ea985240421281701a8c3c Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Mon, 10 Jun 2013 22:07:19 +0300 Subject: [PATCH 05/12] "FileHelperTest" has been advanced. --- tests/unit/framework/helpers/FileHelperTest.php | 127 +++++++++++++++++++++++- 1 file changed, 125 insertions(+), 2 deletions(-) diff --git a/tests/unit/framework/helpers/FileHelperTest.php b/tests/unit/framework/helpers/FileHelperTest.php index 14afa68..8de45c4 100644 --- a/tests/unit/framework/helpers/FileHelperTest.php +++ b/tests/unit/framework/helpers/FileHelperTest.php @@ -18,6 +18,9 @@ class FileHelperTest extends TestCase { $this->testFilePath = Yii::getAlias('@yiiunit/runtime') . DIRECTORY_SEPARATOR . get_class($this); $this->createDir($this->testFilePath); + if (!file_exists($this->testFilePath)) { + $this->markTestIncomplete('Unit tests runtime directory should have writable permissions!'); + } } public function tearDown() @@ -43,17 +46,40 @@ class FileHelperTest extends TestCase protected function removeDir($dirName) { if (!empty($dirName) && file_exists($dirName)) { - exec("rm -rf {$dirName}"); + if ($handle = opendir($dirName)) { + while (false !== ($entry = readdir($handle))) { + if ($entry != '.' && $entry != '..') { + if (is_dir($dirName . DIRECTORY_SEPARATOR . $entry) === true) { + $this->removeDir($dirName . DIRECTORY_SEPARATOR . $entry); + } else { + unlink($dirName . DIRECTORY_SEPARATOR . $entry); + } + } + } + closedir($handle); + rmdir($dirName); + } } } /** + * Get file permission mode. + * @param string $file file name. + * @return string permission mode. + */ + protected function getMode($file) + { + return substr(sprintf('%o', fileperms($file)), -4); + } + + /** * Creates test files structure, * @param array $items file system objects to be created in format: objectName => objectContent * Arrays specifies directories, other values - files. * @param string $basePath structure base file path. */ - protected function createFileStructure(array $items, $basePath = '') { + protected function createFileStructure(array $items, $basePath = '') + { if (empty($basePath)) { $basePath = $this->testFilePath; } @@ -68,6 +94,18 @@ class FileHelperTest extends TestCase } } + /** + * Asserts that file has specific permission mode. + * @param integer $expectedMode expected file permission mode. + * @param string $fileName file name. + * @param string $message error message + */ + protected function assertFileMode($expectedMode, $fileName, $message='') + { + $expectedMode = sprintf('%o', $expectedMode); + $this->assertEquals($expectedMode, $this->getMode($fileName), $message); + } + // Tests : public function testCopyDirectory() @@ -95,6 +133,42 @@ class FileHelperTest extends TestCase } } + /** + * @depends testCopyDirectory + */ + public function testCopyDirectoryPermissions() + { + if (substr(PHP_OS, 0, 3) == 'WIN') { + $this->markTestSkipped("Can't reliably test it on Windows because fileperms() always return 0777."); + } + + $srcDirName = 'test_src_dir'; + $subDirName = 'test_sub_dir'; + $fileName = 'test_file.txt'; + $this->createFileStructure(array( + $srcDirName => array( + $subDirName => array(), + $fileName => 'test file content', + ), + )); + + $basePath = $this->testFilePath; + $srcDirName = $basePath . DIRECTORY_SEPARATOR . $srcDirName; + $dstDirName = $basePath . DIRECTORY_SEPARATOR . 'test_dst_dir'; + + $dirMode = 0755; + $fileMode = 0755; + $options = array( + 'dirMode' => $dirMode, + 'fileMode' => $fileMode, + ); + FileHelper::copyDirectory($srcDirName, $dstDirName, $options); + + $this->assertFileMode($dirMode, $dstDirName, 'Destination directory has wrong mode!'); + $this->assertFileMode($dirMode, $dstDirName . DIRECTORY_SEPARATOR . $subDirName, 'Copied sub directory has wrong mode!'); + $this->assertFileMode($fileMode, $dstDirName . DIRECTORY_SEPARATOR . $fileName, 'Copied file has wrong mode!'); + } + public function testRemoveDirectory() { $dirName = 'test_dir_for_remove'; @@ -143,4 +217,53 @@ class FileHelperTest extends TestCase sort($expectedFiles); $this->assertEquals($expectedFiles, $foundFiles); } + + /** + * @depends testFindFiles + */ + public function testFindFilesExclude() + { + $dirName = 'test_dir'; + $fileName = 'test_file.txt'; + $excludeFileName = 'exclude_file.txt'; + $this->createFileStructure(array( + $dirName => array( + $fileName => 'file content', + $excludeFileName => 'exclude file content', + ), + )); + $basePath = $this->testFilePath; + $dirName = $basePath . DIRECTORY_SEPARATOR . $dirName; + + $options = array( + 'exclude' => array($excludeFileName), + ); + $foundFiles = FileHelper::findFiles($dirName, $options); + $this->assertEquals(array($dirName . DIRECTORY_SEPARATOR . $fileName), $foundFiles); + } + + /** + * @depends testFindFiles + */ + public function testFindFilesFileType() + { + $dirName = 'test_dir'; + $fileType = 'dat'; + $fileName = 'test_file.' . $fileType; + $excludeFileName = 'exclude_file.txt'; + $this->createFileStructure(array( + $dirName => array( + $fileName => 'file content', + $excludeFileName => 'exclude file content', + ), + )); + $basePath = $this->testFilePath; + $dirName = $basePath . DIRECTORY_SEPARATOR . $dirName; + + $options = array( + 'fileTypes' => array($fileType), + ); + $foundFiles = FileHelper::findFiles($dirName, $options); + $this->assertEquals(array($dirName . DIRECTORY_SEPARATOR . $fileName), $foundFiles); + } } \ No newline at end of file From 8f7a783757f41b4ae1ea555a7502ebedbb4a83c3 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Mon, 10 Jun 2013 22:15:03 +0300 Subject: [PATCH 06/12] Method "yii\helpers\base\FileHelper::mkdir()" has been added. --- framework/yii/helpers/base/FileHelper.php | 22 ++++++++++++++++++++++ tests/unit/framework/helpers/FileHelperTest.php | 12 ++++++++++++ 2 files changed, 34 insertions(+) diff --git a/framework/yii/helpers/base/FileHelper.php b/framework/yii/helpers/base/FileHelper.php index 27088a6..7fa87d5 100644 --- a/framework/yii/helpers/base/FileHelper.php +++ b/framework/yii/helpers/base/FileHelper.php @@ -288,4 +288,26 @@ class FileHelper return false; } } + + /** + * Shared environment safe version of mkdir. Supports recursive creation. + * For avoidance of umask side-effects chmod is used. + * + * @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 + */ + public static function mkdir($path, $mode = null, $recursive = false) + { + $prevDir = dirname($path); + if ($recursive && !is_dir($path) && !is_dir($prevDir)) { + static::mkdir(dirname($path), $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 8de45c4..e3bd343 100644 --- a/tests/unit/framework/helpers/FileHelperTest.php +++ b/tests/unit/framework/helpers/FileHelperTest.php @@ -266,4 +266,16 @@ class FileHelperTest extends TestCase $foundFiles = FileHelper::findFiles($dirName, $options); $this->assertEquals(array($dirName . DIRECTORY_SEPARATOR . $fileName), $foundFiles); } + + public function testMkdir() { + $basePath = $this->testFilePath; + + $dirName = $basePath . DIRECTORY_SEPARATOR . 'test_dir'; + FileHelper::mkdir($dirName); + $this->assertTrue(file_exists($dirName), 'Unable to create directory!'); + + $dirName = $basePath . DIRECTORY_SEPARATOR . 'test_dir_level_1' . DIRECTORY_SEPARATOR . 'test_dir_level_2'; + FileHelper::mkdir($dirName, null, true); + $this->assertTrue(file_exists($dirName), 'Unable to create directory recursively!'); + } } \ No newline at end of file From a5480de8521c7377a47e5ce4ad963d247646e2ae Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Mon, 10 Jun 2013 22:22:44 +0300 Subject: [PATCH 07/12] Test case "FileHelperTest::testGetMimeTypeByExtension()" has been added. --- tests/unit/framework/helpers/FileHelperTest.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/unit/framework/helpers/FileHelperTest.php b/tests/unit/framework/helpers/FileHelperTest.php index e3bd343..c74f20c 100644 --- a/tests/unit/framework/helpers/FileHelperTest.php +++ b/tests/unit/framework/helpers/FileHelperTest.php @@ -278,4 +278,21 @@ class FileHelperTest extends TestCase FileHelper::mkdir($dirName, null, true); $this->assertTrue(file_exists($dirName), 'Unable to create directory recursively!'); } + + public function testGetMimeTypeByExtension() + { + $magicFile = $this->testFilePath . DIRECTORY_SEPARATOR . 'mime_type.php'; + $mimeTypeMap = array( + 'txa' => 'application/json', + 'txb' => 'another/mime', + ); + $magicFileContent = ' $mimeType) { + $fileName = 'test.' . $extension; + $this->assertNull(FileHelper::getMimeTypeByExtension($fileName)); + $this->assertEquals($mimeType, FileHelper::getMimeTypeByExtension($fileName, $magicFile)); + } + } } \ No newline at end of file From 3acd77b8b951bb51349acea5052eb6b6ae4a6ea2 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Mon, 10 Jun 2013 22:24:32 +0300 Subject: [PATCH 08/12] "self" replaced by "static" at "yii\helpers\base\FileHelper::mkdir()". --- framework/yii/helpers/base/FileHelper.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/framework/yii/helpers/base/FileHelper.php b/framework/yii/helpers/base/FileHelper.php index 7fa87d5..406225c 100644 --- a/framework/yii/helpers/base/FileHelper.php +++ b/framework/yii/helpers/base/FileHelper.php @@ -95,7 +95,7 @@ class FileHelper } } - return $checkExtension ? self::getMimeTypeByExtension($file) : null; + return $checkExtension ? static::getMimeTypeByExtension($file) : null; } /** @@ -172,23 +172,23 @@ class FileHelper /** * Removes a directory recursively. - * @param string $directory to be deleted recursively. + * @param string $dir to be deleted recursively. */ - public static function removeDirectory($directory) + public static function removeDirectory($dir) { - $items = glob($directory . DIRECTORY_SEPARATOR . '{,.}*', GLOB_MARK | GLOB_BRACE); + $items = glob($dir . DIRECTORY_SEPARATOR . '{,.}*', GLOB_MARK | GLOB_BRACE); foreach ($items as $item) { if (basename($item) == '.' || basename($item) == '..') { continue; } if (substr($item, -1) == DIRECTORY_SEPARATOR) { - self::removeDirectory($item); + static::removeDirectory($item); } else { unlink($item); } } - if (is_dir($directory)) { - rmdir($directory); + if (is_dir($dir)) { + rmdir($dir); } } From 12d94c4a35dcbd9b6433843afa2928395e0ed105 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Mon, 10 Jun 2013 22:37:28 +0300 Subject: [PATCH 09/12] Doc comments at "yii\helpers\base\FileHelper::findFiles()" have been recomposed using markdown format. --- framework/yii/helpers/base/FileHelper.php | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/framework/yii/helpers/base/FileHelper.php b/framework/yii/helpers/base/FileHelper.php index 406225c..c5b8250 100644 --- a/framework/yii/helpers/base/FileHelper.php +++ b/framework/yii/helpers/base/FileHelper.php @@ -196,19 +196,15 @@ class FileHelper * Returns the files found under the specified directory and subdirectories. * @param string $dir the directory under which the files will be looked for. * @param array $options options for file searching. Valid options are: - *
    - *
  • fileTypes: array, list of file name suffix (without dot). Only files with these suffixes will be returned.
  • - *
  • exclude: array, list of directory and file exclusions. Each exclusion can be either a name or a path. - * If a file or directory name or path matches the exclusion, it will not be copied. For example, an exclusion of - * '.svn' will exclude all files and directories whose name is '.svn'. And an exclusion of '/a/b' will exclude - * file or directory '$src/a/b'. Note, that '/' should be used as separator regardless of the value of the DIRECTORY_SEPARATOR constant. - *
  • - *
  • level: integer, recursion depth, default=-1. - * Level -1 means searching for all directories and files under the directory; - * Level 0 means searching for only the files DIRECTLY under the directory; - * level N means searching for those directories that are within N levels. - *
  • - *
+ * - fileTypes: array, list of file name suffix (without dot). Only files with these suffixes will be returned. + * - exclude: array, list of directory and file exclusions. Each exclusion can be either a name or a path. + * If a file or directory name or path matches the exclusion, it will not be copied. For example, an exclusion of + * '.svn' will exclude all files and directories whose name is '.svn'. And an exclusion of '/a/b' will exclude + * file or directory '$src/a/b'. Note, that '/' should be used as separator regardless of the value of the DIRECTORY_SEPARATOR constant. + * - level: integer, recursion depth, default=-1. + * Level -1 means searching for all directories and files under the directory; + * Level 0 means searching for only the files DIRECTLY under the directory; + * level N means searching for those directories that are within N levels. * @return array files found under the directory. The file list is sorted. */ public static function findFiles($dir, array $options = array()) From 7697f21592c9dc40a05a4d0e3fb190901995b557 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Mon, 10 Jun 2013 23:36:22 +0300 Subject: [PATCH 10/12] "filter" callback option has been added to "yii\helpers\base\FileHelper::findFiles()". --- framework/yii/helpers/base/FileHelper.php | 65 +++++++++++++++---------- tests/unit/framework/helpers/FileHelperTest.php | 25 ++++++++++ 2 files changed, 63 insertions(+), 27 deletions(-) diff --git a/framework/yii/helpers/base/FileHelper.php b/framework/yii/helpers/base/FileHelper.php index c5b8250..2f6d559 100644 --- a/framework/yii/helpers/base/FileHelper.php +++ b/framework/yii/helpers/base/FileHelper.php @@ -196,6 +196,10 @@ class FileHelper * Returns the files found under the specified directory and subdirectories. * @param string $dir the directory under which the files will be looked for. * @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 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. * - fileTypes: array, list of file name suffix (without dot). Only files with these suffixes will be returned. * - exclude: array, list of directory and file exclusions. Each exclusion can be either a name or a path. * If a file or directory name or path matches the exclusion, it will not be copied. For example, an exclusion of @@ -209,11 +213,9 @@ class FileHelper */ public static function findFiles($dir, array $options = array()) { - $fileTypes = array(); - $exclude = array(); - $level = -1; - extract($options); - $list = static::findFilesRecursive($dir, '', $fileTypes, $exclude, $level); + $level = array_key_exists('level', $options) ? $options['level'] : -1; + $filterOptions = $options; + $list = static::findFilesRecursive($dir, '', $filterOptions, $level); sort($list); return $list; } @@ -223,18 +225,17 @@ class FileHelper * This method is mainly used by [[findFiles]]. * @param string $dir the source directory. * @param string $base the path relative to the original source directory. - * @param array $fileTypes list of file name suffix (without dot). Only files with these suffixes will be returned. - * @param array $exclude list of directory and file exclusions. Each exclusion can be either a name or a path. - * If a file or directory name or path matches the exclusion, it will not be copied. For example, an exclusion of - * '.svn' will exclude all files and directories whose name is '.svn'. And an exclusion of '/a/b' will exclude - * file or directory '$src/a/b'. Note, that '/' should be used as separator regardless of the value of the DIRECTORY_SEPARATOR constant. + * @param array $filterOptions list of filter options. + * - filter: a PHP callback, which results indicates if file will be returned. + * - fileTypes: list of file name suffix (without dot). Only files with these suffixes will be returned. + * - exclude: list of directory and file exclusions. Each exclusion can be either a name or a path. * @param integer $level recursion depth. It defaults to -1. * Level -1 means searching for all directories and files under the directory; * Level 0 means searching for only the files DIRECTLY under the directory; * level N means searching for those directories that are within N levels. * @return array files found under the directory. */ - protected static function findFilesRecursive($dir, $base, $fileTypes, $exclude, $level) + protected static function findFilesRecursive($dir, $base, array $filterOptions, $level) { $list = array(); $handle = opendir($dir); @@ -244,11 +245,11 @@ class FileHelper } $path = $dir . DIRECTORY_SEPARATOR . $file; $isFile = is_file($path); - if (static::validatePath($base, $file, $isFile, $fileTypes, $exclude)) { + if (static::validatePath($base, $file, $isFile, $filterOptions)) { if ($isFile) { $list[] = $path; } elseif ($level) { - $list = array_merge($list, static::findFilesRecursive($path, $base . DIRECTORY_SEPARATOR . $file, $fileTypes, $exclude, $level-1)); + $list = array_merge($list, static::findFilesRecursive($path, $base . DIRECTORY_SEPARATOR . $file, $filterOptions, $level-1)); } } } @@ -259,30 +260,40 @@ class FileHelper /** * Validates a file or directory, checking if it match given conditions. * @param string $base the path relative to the original source directory - * @param string $file the file or directory name + * @param string $name the file or directory name * @param boolean $isFile whether this is a file - * @param array $fileTypes list of valid file name suffixes (without dot). - * @param array $exclude list of directory and file exclusions. Each exclusion can be either a name or a path. + * @param array $filterOptions list of filter options. + * - filter: a PHP callback, which results indicates if file will be returned. + * - fileTypes: list of file name suffix (without dot). Only files with these suffixes will be returned. + * - exclude: list of directory and file exclusions. Each exclusion can be either a name or a path. * If a file or directory name or path matches the exclusion, false will be returned. For example, an exclusion of * '.svn' will return false for all files and directories whose name is '.svn'. And an exclusion of '/a/b' will return false for * file or directory '$src/a/b'. Note, that '/' should be used as separator regardless of the value of the DIRECTORY_SEPARATOR constant. * @return boolean whether the file or directory is valid */ - protected static function validatePath($base, $file, $isFile, $fileTypes, $exclude) + protected static function validatePath($base, $name, $isFile, array $filterOptions) { - foreach ($exclude as $e) { - if ($file === $e || strpos($base . DIRECTORY_SEPARATOR . $file, $e) === 0) { - return false; - } + if (isset($filterOptions['filter']) && !call_user_func($filterOptions['filter'], $base, $name, $isFile)) { + return false; } - if (!$isFile || empty($fileTypes)) { - return true; + if (!empty($filterOptions['exclude'])) { + foreach ($filterOptions['exclude'] as $e) { + if ($name === $e || strpos($base . DIRECTORY_SEPARATOR . $name, $e) === 0) { + return false; + } + } } - if (($type = pathinfo($file, PATHINFO_EXTENSION)) !== '') { - return in_array($type, $fileTypes); - } else { - return false; + if (!empty($filterOptions['fileTypes'])) { + if (!$isFile) { + return true; + } + if (($type = pathinfo($name, PATHINFO_EXTENSION)) !== '') { + return in_array($type, $filterOptions['fileTypes']); + } else { + return false; + } } + return true; } /** diff --git a/tests/unit/framework/helpers/FileHelperTest.php b/tests/unit/framework/helpers/FileHelperTest.php index c74f20c..781812d 100644 --- a/tests/unit/framework/helpers/FileHelperTest.php +++ b/tests/unit/framework/helpers/FileHelperTest.php @@ -221,6 +221,31 @@ class FileHelperTest extends TestCase /** * @depends testFindFiles */ + public function testFindFileFilter() + { + $dirName = 'test_dir'; + $passedFileName = 'passed.txt'; + $this->createFileStructure(array( + $dirName => array( + $passedFileName => 'passed file content', + 'declined.txt' => 'declined file content', + ), + )); + $basePath = $this->testFilePath; + $dirName = $basePath . DIRECTORY_SEPARATOR . $dirName; + + $options = array( + 'filter' => function($base, $name, $isFile) use ($passedFileName) { + return ($passedFileName == $name); + } + ); + $foundFiles = FileHelper::findFiles($dirName, $options); + $this->assertEquals(array($dirName . DIRECTORY_SEPARATOR . $passedFileName), $foundFiles); + } + + /** + * @depends testFindFiles + */ public function testFindFilesExclude() { $dirName = 'test_dir'; From 40a005d543b3c47beaf03c53c74767592c726816 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Mon, 10 Jun 2013 23:50:47 +0300 Subject: [PATCH 11/12] "yii\helpers\StringHelper" usage has been added to "yii\helpers\base\FileHelper". --- framework/yii/helpers/base/FileHelper.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/framework/yii/helpers/base/FileHelper.php b/framework/yii/helpers/base/FileHelper.php index 2f6d559..72f6d18 100644 --- a/framework/yii/helpers/base/FileHelper.php +++ b/framework/yii/helpers/base/FileHelper.php @@ -10,6 +10,7 @@ namespace yii\helpers\base; use Yii; +use yii\helpers\StringHelper; /** * Filesystem helper @@ -66,7 +67,7 @@ class FileHelper if ($language === $sourceLanguage) { return $file; } - $desiredFile = dirname($file) . DIRECTORY_SEPARATOR . $sourceLanguage . DIRECTORY_SEPARATOR . basename($file); + $desiredFile = dirname($file) . DIRECTORY_SEPARATOR . $sourceLanguage . DIRECTORY_SEPARATOR . StringHelper::basename($file); return is_file($desiredFile) ? $desiredFile : $file; } @@ -178,10 +179,11 @@ class FileHelper { $items = glob($dir . DIRECTORY_SEPARATOR . '{,.}*', GLOB_MARK | GLOB_BRACE); foreach ($items as $item) { - if (basename($item) == '.' || basename($item) == '..') { + $itemBaseName = StringHelper::basename($item); + if ($itemBaseName === '.' || $itemBaseName === '..') { continue; } - if (substr($item, -1) == DIRECTORY_SEPARATOR) { + if (StringHelper::substr($item, -1, 1) == DIRECTORY_SEPARATOR) { static::removeDirectory($item); } else { unlink($item); From dc8c71f409ff8d680ec0fed277e65fc620d65c0b Mon Sep 17 00:00:00 2001 From: Paul Klimov Date: Tue, 11 Jun 2013 10:52:31 +0300 Subject: [PATCH 12/12] "StringHelper::basename()" has been replaced by plain "basename" at "FileHelper". --- framework/yii/helpers/base/FileHelper.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/yii/helpers/base/FileHelper.php b/framework/yii/helpers/base/FileHelper.php index 72f6d18..dc8aca6 100644 --- a/framework/yii/helpers/base/FileHelper.php +++ b/framework/yii/helpers/base/FileHelper.php @@ -67,7 +67,7 @@ class FileHelper if ($language === $sourceLanguage) { return $file; } - $desiredFile = dirname($file) . DIRECTORY_SEPARATOR . $sourceLanguage . DIRECTORY_SEPARATOR . StringHelper::basename($file); + $desiredFile = dirname($file) . DIRECTORY_SEPARATOR . $sourceLanguage . DIRECTORY_SEPARATOR . basename($file); return is_file($desiredFile) ? $desiredFile : $file; } @@ -179,7 +179,7 @@ class FileHelper { $items = glob($dir . DIRECTORY_SEPARATOR . '{,.}*', GLOB_MARK | GLOB_BRACE); foreach ($items as $item) { - $itemBaseName = StringHelper::basename($item); + $itemBaseName = basename($item); if ($itemBaseName === '.' || $itemBaseName === '..') { continue; }