56

I have a server with a lot of files inside various folders, sub-folders, and sub-sub-folders.

I'm trying to make a search.php page that would be used to search the whole server for a specific file. If the file is found, then return the location path to display a download link.

Here's what i have so far:

$root = $_SERVER['DOCUMENT_ROOT']; $search = "test.zip"; $found_files = glob("$root/*/test.zip"); $downloadlink = str_replace("$root/", "", $found_files[0]); if (!empty($downloadlink)) { echo "<a href=\"http://www.example.com/$downloadlink\">$search</a>"; } 

The script is working perfectly if the file is inside the root of my domain name... Now i'm trying to find a way to make it also scan sub-folders and sub-sub-folders but i'm stuck here.

4
  • stackoverflow.com/questions/8870731/… Commented Jun 18, 2013 at 4:48
  • You mind have better luck using the file_exists() function. php.net/manual/en/function.file-exists.php (or a mix of). Commented Jun 18, 2013 at 4:58
  • doesn't tells me how to scan all sub-folders and sub-sobfolders for the file... Commented Jun 18, 2013 at 5:04
  • True. Have you had a look at the link messi fan put up? Seems promising. I'm dabbling with it now, and it's showing me all files in starting folder and sub-folders, but not working the way you want it to. Plus, I've got both eyes in the same socket right; needing some sleep, very soon. Commented Jun 18, 2013 at 5:13

4 Answers 4

96

There are 2 ways.

Use glob to do recursive search:

<?php // Does not support flag GLOB_BRACE function rglob($pattern, $flags = 0) { $files = glob($pattern, $flags); foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir) { $files = array_merge( [], ...[$files, rglob($dir . "/" . basename($pattern), $flags)] ); } return $files; } // usage: to find the test.zip file recursively $result = rglob($_SERVER['DOCUMENT_ROOT'] . '/test.zip'); var_dump($result); // to find the all files that names ends with test.zip $result = rglob($_SERVER['DOCUMENT_ROOT'] . '/*test.zip'); ?> 

Use RecursiveDirectoryIterator

<?php // $regPattern should be using regular expression function rsearch($folder, $regPattern) { $dir = new RecursiveDirectoryIterator($folder); $ite = new RecursiveIteratorIterator($dir); $files = new RegexIterator($ite, $regPattern, RegexIterator::GET_MATCH); $fileList = array(); foreach($files as $file) { $fileList = array_merge($fileList, $file); } return $fileList; } // usage: to find the test.zip file recursively $result = rsearch($_SERVER['DOCUMENT_ROOT'], '/.*\/test\.zip/')); var_dump($result); ?> 

RecursiveDirectoryIterator comes with PHP5 while glob is from PHP4. Both can do the job, it's up to you.

Sign up to request clarification or add additional context in comments.

17 Comments

ok but how can i use it to search for a specific file within folders/subfolders/subsubfolders and return the file's path ?
rsearch: var_dump(rsearch('/folder/.../', '/.*zip/')); rglob: var_dump(rglob('/folder/*/test.zip')); it returns an array of matched files.
I did some anecdotal tests on a deep directory structure and the rsearch function was an order of magnitude faster...
@JasonRDalton, retested, with PHP 7.1 ("anecdotally", too :) ), on a 60MB project tree (with two mid-size git worktrees and lots of other small files etc.), and got the exact opposite. Had a priming run for both right before the measurement, and I pretty consistently got numbers like: rglob: 0.02864, rsearch: 0.12413. Which is a lot more plausible, actually, than the other way around, I'd say.
I suggest you edit your answer to include rsearch($root, '/.*\/test.zip/')); to save newbies a lot of wasted time scanning through comments that are not visible by default. Apart from that, this is a nice answer.
|
48

I want to provide another simple alternative for cases where you can predict a max depth. You can use a pattern with braces listing all possible subfolder depths.

This example allows 0-3 arbitrary subfolders:

glob("$root/{,*/,*/*/,*/*/*/}test_*.zip", GLOB_BRACE); 

Of course the braced pattern could be procedurally generated.

3 Comments

Just be aware that GLOB_BRACE isn't available on all platforms. I only discovered that when my code failed in an automated pipeline.
Or for multiple file types(for example .pdf,.mp4 and .mp3): glob("$root/{.pdf,*/.pdf,/*/.pdf,/*/*/.pdf,.mp4,*/.mp4,/*/.mp4,/*/*/.mp4,.mp3,*/.mp3,/*/.mp3,/*/*/.mp3}", GLOB_BRACE)
@HosseinNedaee Multiple types would be expressed with a brace pattern as well: "$root/{,*/,*/*/,*/*/*/}test_*.{zip,gz,tgz}"
11

This returns fullpath to the file

function rsearch($folder, $pattern) { $iti = new RecursiveDirectoryIterator($folder); foreach(new RecursiveIteratorIterator($iti) as $file){ if(strpos($file , $pattern) !== false){ return $file; } } return false; } 

call the function:

$filepath = rsearch('/home/directory/thisdir/', "/findthisfile.jpg"); 

And this is returns like:

/home/directory/thisdir/subdir/findthisfile.jpg

You can improve this function to find several files like all jpeg file:

function rsearch($folder, $pattern_array) { $return = array(); $iti = new RecursiveDirectoryIterator($folder); foreach(new RecursiveIteratorIterator($iti) as $file){ if (in_array(strtolower(array_pop(explode('.', $file))), $pattern_array)){ $return[] = $file; } } return $return; } 

This can call as:

$filepaths = rsearch('/home/directory/thisdir/', array('jpeg', 'jpg') ); 

Ref: https://stackoverflow.com/a/1860417/219112

3 Comments

Probably should use $file->getExtension () rather than array_pop(explode('.', $file)) to avoid "PHP Notice: Only variables should be passed by reference in ...".
@Sadee Thanks for the function it's working well for my project. The only thing i would add is a die in case the folder path doesn't exist so it don't bother moving forward.-
You may want use yield instead of build a complete a $return array. This will produce a generator and improve performances a lot.
9

As a full solution for your problem (this was also my problem):

function rsearch($folder, $pattern) { $dir = new RecursiveDirectoryIterator($folder); $ite = new RecursiveIteratorIterator($dir); $files = new RegexIterator($ite, $pattern, RegexIterator::MATCH); foreach($files as $file) { yield $file->getPathName(); } } 

Will get you the full path of the items that you wish to find.

Edit: Thanks to Rousseau Alexandre for pointing out , $pattern must be regular expression.

3 Comments

The pattern must be a regular expression
Can you give a brief example of how to call it and iterate through the results?
Example pattern for getting all files with .html extension: $pattern = '#^.*\.html$#'

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.