-
-
Save UziTech/3b65b2543cee57cd6d2ecfcccf846f20 to your computer and use it in GitHub Desktop.
/* | |
* License: DWTFYW | |
*/ | |
/** | |
* Search recusively for files in a base directory matching a glob pattern. | |
* The `GLOB_NOCHECK` flag has no effect. | |
* | |
* @param string $base Directory to search | |
* @param string $pattern Glob pattern to match files | |
* @param int $flags Glob flags from https://www.php.net/manual/function.glob.php | |
* @return string[] Array of files matching the pattern | |
*/ | |
function glob_recursive($base, $pattern, $flags = 0) { | |
$flags = $flags & ~GLOB_NOCHECK; | |
if (substr($base, -1) !== DIRECTORY_SEPARATOR) { | |
$base .= DIRECTORY_SEPARATOR; | |
} | |
$files = glob($base.$pattern, $flags); | |
if (!is_array($files)) { | |
$files = []; | |
} | |
$dirs = glob($base.'*', GLOB_ONLYDIR|GLOB_NOSORT|GLOB_MARK); | |
if (!is_array($dirs)) { | |
return $files; | |
} | |
foreach ($dirs as $dir) { | |
$dirFiles = glob_recursive($dir, $pattern, $flags); | |
$files = array_merge($files, $dirFiles); | |
} | |
return $files; | |
} |
@UziTech flag GLOB_NOCHECK
grant return pattern if cant found paths by pattern
$this->rglob(
- if not
is_dir($path)
then it's file - no need additional check - change pattern for search in sub-directories
- ignore flags
established BC practice - check native *_recursive() functions
I was going for usefulness not consistency with native functions.
Especially as far as GLOB_NOCHECK
goes. I don't get why that would be useful. The user is sending the pattern why would they need it back? They can just check if the array is empty and use the pattern they sent to the function.
@UziTech "useful for me" not same to "useful for anybody". if function name is glob_recursive()
then it's should be glob()
called recursively, without any other changes.
If that is what people want they can use your implementation.
then it's should be glob() called recursively, without any other changes.
That isn't always the most useful. For instance if glob
fails it returns false, so consistency would say that if glob_recursive
fails in any of the subfolders it should return false. You implementation returns the current list of files instead of false. (Which I would argue is better anyway.)
Also yours will return the string pattern many times when using GLOB_NOCHECK
if the pattern is not found in a subfolder. Which is consistent but not very useful.
// dir1
// file1
// dir2
// file2
// dir3
// file3
glob_recursive("/", "file1", GLOB_NOCHECK);
// Your return:
// ["dir1/file1", "file1", "file1"]
// Most likely expected return:
// ["dir1/file1"]
@WinterSilence actually it looks like yours would just return ["/file1"]
for the above example because it doesn't looks through directories that don't match the pattern.
Also GLOB_NOCHECK
still returns an array with the pattern as a string not just the string pattern.
glob("*.txt", GLOB_NOCHECK)
// returns ["*.txt"] if no files found not "*.txt"
Here is a version that works with GLOB_NOCHECK
function glob_recursive($base, $pattern, $flags = 0) {
$glob_nocheck = $flags & GLOB_NOCHECK;
$flags = $flags & ~GLOB_NOCHECK;
function check_folder($base, $pattern, $flags) {
if (substr($base, -1) !== DIRECTORY_SEPARATOR) {
$base .= DIRECTORY_SEPARATOR;
}
$files = glob($base.$pattern, $flags);
if (!is_array($files)) {
$files = [];
}
$dirs = glob($base.'*', GLOB_ONLYDIR|GLOB_NOSORT|GLOB_MARK);
if (!is_array($dirs)) {
return $files;
}
foreach ($dirs as $dir) {
$dirFiles = check_folder($dir, $pattern, $flags);
$files = array_merge($files, $dirFiles);
}
return $files;
}
$files = check_folder($base, $pattern, $flags);
if ($glob_nocheck && count($files) === 0) {
return [$pattern];
}
return $files;
}
Hi there, I used your marvelous second function glob_recursive, but, I need to say there is an issue when we want using the function glob_recursive twice or more per php run execution.
The issue lead to a FATAL ERROR: Fatal error: Cannot redeclare check_folder() (previously declared in ....
SO... I found where is the issue:
you used a declaration of a sub-function in your second version of glob_recursive(), then unlike class methods, functions (or sub-functions are exported to a global namespace (OR) custom local and of course you certainly know function are not class).
sub-function (I call them like that but it's just a function that will be declared once the main parent function will be called).. cause an issue because once we call ONE time glob_recursive() the function is registered in the PHP execution... (meant : declared!)
finally if we call a second time glob_recursive() it lead to a force re-declare check_folder() function as it is a part of glob_recursive().
The fix is easy, (and I think you just forgot it, as your function is amazing):
Before line:
function check_folder($base, $pattern, $flags) {
Add just:
if (!function_exists('check_folder')) {
And we now must close the newly added "if" statement, so ...
Before line:
$files = check_folder($base, $pattern, $flags);
Add just an another :
}
And now, the glob_recursive() function can be called infinite amount of attempts !
By the way, thanks for the function !!
Thanks for this function! Do you have a package with it for composer?
@UziTech I agree - the reason I put my (slightly flawed) function in the mix is I just wanted a simple flat array of all the files (recursed) under a parent dir.