From 8f416a242b776857fbd9640eea0e4f1e5fd44152 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Thu, 8 Sep 2022 17:53:22 -0700 Subject: [PATCH] findFiles() - Add suppport for $maxDepth option --- CRM/Utils/File.php | 38 +++++++++++++++++++++++----- tests/phpunit/CRM/Utils/FileTest.php | 33 ++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/CRM/Utils/File.php b/CRM/Utils/File.php index c2299ec490..4dbc7e47e3 100644 --- a/CRM/Utils/File.php +++ b/CRM/Utils/File.php @@ -744,9 +744,13 @@ HTACCESS; * glob pattern, eg "*.txt". * @param bool $relative * TRUE if paths should be made relative to $dir + * @param int|null $maxDepth + * Maximum depth of subdirs to check. + * For no limit, use NULL. + * * @return array(string) */ - public static function findFiles($dir, $pattern, $relative = FALSE) { + public static function findFiles($dir, $pattern, $relative = FALSE, ?int $maxDepth = NULL) { if (!is_dir($dir) || !is_readable($dir)) { return []; } @@ -757,7 +761,8 @@ HTACCESS; ? constant('CIVICRM_EXCLUDE_DIRS_PATTERN') : '@' . preg_quote(DIRECTORY_SEPARATOR) . '\.@'; - $dir = rtrim($dir, '/'); + $dir = rtrim($dir, '/' . DIRECTORY_SEPARATOR); + $baseDepth = static::findPathDepth($dir); $todos = [$dir]; $result = []; while (!empty($todos)) { @@ -771,15 +776,36 @@ HTACCESS; } } // Find subdirs to recurse into. - $subdirs = glob("$subdir/*", GLOB_ONLYDIR); - if (!empty($excludeDirsPattern)) { - $subdirs = preg_grep($excludeDirsPattern, $subdirs, PREG_GREP_INVERT); + $depth = static::findPathDepth($subdir) - $baseDepth + 1; + if ($maxDepth === NULL || $depth <= $maxDepth) { + $subdirs = glob("$subdir/*", GLOB_ONLYDIR); + if (!empty($excludeDirsPattern)) { + $subdirs = preg_grep($excludeDirsPattern, $subdirs, PREG_GREP_INVERT); + } + $todos = array_merge($todos, $subdirs); } - $todos = array_merge($todos, $subdirs); } return $result; } + /** + * Determine the absolute depth of a path expression. + * + * @param string $path + * Ex: '/var/www/foo' + * @return int + * Ex: 3 + */ + private static function findPathDepth(string $path): int { + // Both PHP-Unix and PHP-Windows support '/'s. Additionally, PHP-Windows also supports '\'s. + // They are roughly equivalent. (The differences are described by a secret book hidden in the tower of Mordor.) + $depth = substr_count($path, '/'); + if (DIRECTORY_SEPARATOR !== '/') { + $depth += substr_count($path, DIRECTORY_SEPARATOR); + } + return $depth; + } + /** * Determine if $child is a sub-directory of $parent * diff --git a/tests/phpunit/CRM/Utils/FileTest.php b/tests/phpunit/CRM/Utils/FileTest.php index 3661430847..6bf8af002c 100644 --- a/tests/phpunit/CRM/Utils/FileTest.php +++ b/tests/phpunit/CRM/Utils/FileTest.php @@ -538,4 +538,37 @@ class CRM_Utils_FileTest extends CiviUnitTestCase { } } + public function trueOrFalse(): array { + return [ + 'TRUE' => [TRUE], + 'FALSE' => [FALSE], + ]; + } + + /** + * @param bool $isRelative + * @dataProvider trueOrFalse + */ + public function testFindFilesDepth(bool $isRelative) { + $CRM = Civi::paths()->getPath('[civicrm.root]/CRM'); + $depthResults[0] = CRM_Utils_File::findFiles($CRM, 'Contact.php', $isRelative, 0); + $depthResults[1] = CRM_Utils_File::findFiles($CRM, 'Contact.php', $isRelative, 1); + $depthResults[2] = CRM_Utils_File::findFiles($CRM, 'Contact.php', $isRelative, 2); + $depthResults[3] = CRM_Utils_File::findFiles($CRM, 'Contact.php', $isRelative, 3); + $depthResults[NULL] = CRM_Utils_File::findFiles($CRM, 'Contact.php', $isRelative); + + $expectPrefix = $isRelative ? '' : $CRM . '/'; + + $expectFiles['Contact/BAO/Contact.php'] = [0 => FALSE, 1 => FALSE, 2 => TRUE, 3 => TRUE, NULL => TRUE]; + $expectFiles['Contact/Import/Parser/Contact.php'] = [0 => FALSE, 1 => FALSE, 2 => FALSE, 3 => TRUE, NULL => TRUE]; + + foreach ($expectFiles as $expectFile => $expectMatches) { + $actualMatches = []; + foreach ($expectMatches as $depth => $expectMatch) { + $actualMatches[$depth] = in_array($expectPrefix . $expectFile, $depthResults[$depth]); + } + $this->assertEquals($expectMatches, $actualMatches, "The file $expectFile should be found as follows:"); + } + } + } -- 2.25.1