From 16e63f6de29ae5b26be501105a668dbf1fd4fbce Mon Sep 17 00:00:00 2001 From: Arsen Date: Sat, 3 Feb 2018 19:00:59 +0300 Subject: [PATCH] Fixes #13465: Added `yii\helpers\FileHelper::findDirectory()` method --- framework/CHANGELOG.md | 1 + framework/helpers/BaseFileHelper.php | 93 +++++++++++++++++++--- tests/framework/helpers/FileHelperTest.php | 40 ++++++++++ 3 files changed, 121 insertions(+), 13 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 9c6c0ff337..32864bb80a 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -4,6 +4,7 @@ Yii Framework 2 Change Log 2.0.14 under development ------------------------ +- Enh #13465: Added `yii\helpers\FileHelper::findDirectory()` method (ArsSirek, developeruz) - Enh #8527: Added `yii\i18n\Locale` component having `getCurrencySymbol()` method (amarox, samdark) - Enh #14546: Added `dataDirectory` property into `BaseActiveFixture` (leandrogehlen) - Bug #15522: Fixed `yii\db\ActiveRecord::refresh()` method does not use an alias in the condition (vladis84) diff --git a/framework/helpers/BaseFileHelper.php b/framework/helpers/BaseFileHelper.php index e7e10ad030..944b7ba1eb 100644 --- a/framework/helpers/BaseFileHelper.php +++ b/framework/helpers/BaseFileHelper.php @@ -459,20 +459,10 @@ class BaseFileHelper */ public static function findFiles($dir, $options = []) { - if (!is_dir($dir)) { - throw new InvalidParamException("The dir argument must be a directory: $dir"); - } - $dir = rtrim($dir, DIRECTORY_SEPARATOR); - if (!isset($options['basePath'])) { - // this should be done only once - $options['basePath'] = realpath($dir); - $options = static::normalizeOptions($options); - } + $dir = self::clearDir($dir); + $options = self::setBasePath($dir, $options); $list = []; - $handle = opendir($dir); - if ($handle === false) { - throw new InvalidParamException("Unable to open directory: $dir"); - } + $handle = self::openDir($dir); while (($file = readdir($handle)) !== false) { if ($file === '.' || $file === '..') { continue; @@ -491,6 +481,83 @@ class BaseFileHelper return $list; } + /** + * Returns the directories 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 directory searching. Valid options are: + * + * - `filter`: callback, a PHP callback that is called for each directory or file. + * The signature of the callback should be: `function ($path)`, where `$path` refers the full path to be filtered. + * The callback can return one of the following values: + * + * * `true`: the directory will be returned + * * `false`: the directory will NOT be returned + * + * - `recursive`: boolean, whether the files under the subdirectories should also be looked for. Defaults to `true`. + * @return array directories found under the directory, in no particular order. Ordering depends on the files system used. + * @throws InvalidParamException if the dir is invalid. + * @since 2.0.14 + */ + public static function findDirectory($dir, $options = []) + { + $dir = self::clearDir($dir); + $options = self::setBasePath($dir, $options); + $list = []; + $handle = self::openDir($dir); + while (($file = readdir($handle)) !== false) { + if ($file === '.' || $file === '..') { + continue; + } + $path = $dir . DIRECTORY_SEPARATOR . $file; + if (is_dir($path) && static::filterPath($path, $options)) { + $list[] = $path; + if (!isset($options['recursive']) || $options['recursive']) { + $list = array_merge($list, static::findDirectory($path, $options)); + } + } + } + closedir($handle); + + return $list; + } + + /* + * @param string $dir + */ + private static function setBasePath($dir, $options) + { + if (!isset($options['basePath'])) { + // this should be done only once + $options['basePath'] = realpath($dir); + $options = static::normalizeOptions($options); + } + + return $options; + } + + /* + * @param string $dir + */ + private static function openDir($dir) + { + $handle = opendir($dir); + if ($handle === false) { + throw new InvalidParamException("Unable to open directory: $dir"); + } + return $handle; + } + + /* + * @param string $dir + */ + private static function clearDir($dir) + { + if (!is_dir($dir)) { + throw new InvalidParamException("The dir argument must be a directory: $dir"); + } + return rtrim($dir, DIRECTORY_SEPARATOR); + } + /** * Checks if the given file path satisfies the filtering options. * @param string $path the path of the file or directory to be checked diff --git a/tests/framework/helpers/FileHelperTest.php b/tests/framework/helpers/FileHelperTest.php index d69c8a86b0..6c873dd8de 100644 --- a/tests/framework/helpers/FileHelperTest.php +++ b/tests/framework/helpers/FileHelperTest.php @@ -882,4 +882,44 @@ class FileHelperTest extends TestCase $this->assertFileNotExists($dstDirName . DIRECTORY_SEPARATOR . 'dir2'); $this->assertFileNotExists($dstDirName . DIRECTORY_SEPARATOR . 'dir3'); } + + public function testFindDirectory() + { + $dirName = 'test_dir'; + $this->createFileStructure([ + $dirName => [ + 'test_sub_dir' => [ + 'file_1.txt' => 'sub dir file 1 content', + ], + 'second_sub_dir' => [ + 'file_1.txt' => 'sub dir file 2 content', + ], + ], + ]); + $basePath = $this->testFilePath; + $dirName = $basePath . DIRECTORY_SEPARATOR . $dirName; + $expectedFiles = [ + $dirName . DIRECTORY_SEPARATOR . 'test_sub_dir', + $dirName . DIRECTORY_SEPARATOR . 'second_sub_dir' + ]; + + $foundFiles = FileHelper::findDirectory($dirName); + sort($expectedFiles); + sort($foundFiles); + $this->assertEquals($expectedFiles, $foundFiles); + + $expectedFiles = [ + $dirName . DIRECTORY_SEPARATOR . 'second_sub_dir' + ]; + $options = [ + 'filter' => function ($path) { + return 'second_sub_dir' == basename($path); + }, + ]; + $foundFiles = FileHelper::findDirectory($dirName, $options); + sort($expectedFiles); + sort($foundFiles); + $this->assertEquals($expectedFiles, $foundFiles); + + } }