This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
php scandir --> search for files/directories
I have a folder, inside this folder, have many subfolders, but I would like to scan all the subfolders, and scan all the .m file... How can I do so??
Here is the file:
/MyFilePath/
/myPath.m
/myPath2.m
/myPath3.m
/MyClasses/
/my.m
/my1.m
/my2.m
/my3.m
/Utilities/
/u1.m
/u2.m
/External/
/a.m
/b.m
/c.m
/Internal/
/d.m
/e.m
/f.m
/Views/
/a_v.m
/b_v.m
/c_v.m
/Controllers/
/a_vc.m
/b_vc.m
/c_vc.m
/AnotherClasses/
/anmy.m
/anmy1.m
/anmy2.m
/anmy3.m
/Networking/
/net1.m
/net2.m
/net3.m
/External/
/Internal/
/Views/
/Controllers/
You could also use some of the SPL's iterators. A quick and basic example would look like:
$directories = new RecursiveDirectoryIterator('path/to/search');
$flattened = new RecursiveIteratorIterator($directories);
$filter = new RegexIterator($flattened, '/\.in$/');
foreach ($filter as $file) {
echo $file, PHP_EOL;
}
More infos (mostly incomplete):
http://php.net/recursivedirectoryiterator
http://php.net/recursiveiteratoriterator
http://php.net/regexiterator
You can use a recursive function like this:
function searchFiles($dir,$pattern,$recursive=false)
{
$matches = array();
$d = dir($dir);
while (false !== ($entry = $d->read()))
{
if (is_dir($d->path.$entry) && $recursive)
{
$subdir = $d->path.$entry;
$matches = array_merge($matches,searchFiles($dir,$pattern,$recursive));
}
elseif (is_file($d->path.$entry) && preg_match($pattern,$entry))
{
$matches[] = $d->path.$entry;
}
}
$d->close();
return $matches;
}
Usage:
$matches = searchFiles("/mypath/","'[.]m$'i",true);
function ScanForMFiles($dir){
$return = array();
if ($handle = opendir($dir)) {
while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != "..") {
if(is_dir($dir.$file)){
$return = array_merge($return, ScanForMFiles($dir.$file."/"));
}
else {
if(substr($file, -2) == '.m')
$return[] = $file;
}
}
}
closedir($handle);
}
return $return;
}
var_dump(ScanForMFiles('./'));
You'll want to look to the PHP docs for details on this: http://php.net/manual/en/function.readdir.php
Here's an example that should do what you you want. It will return an array of all .m files in the sub directories. You can then loop through each file and read the contents if that is what you are interested in.
<?php
function get_m_files($root = '.'){
$files = array();
$directories = array();
$last_letter = $root[strlen($root)-1];
$root = ($last_letter == '\\' || $last_letter == '/') ? $root : $root.DIRECTORY_SEPARATOR;
$directories[] = $root;
while (sizeof($directories)) {
$dir = array_pop($directories);
if ($handle = opendir($dir)) {
while (false !== ($file = readdir($handle))) {
if ($file == '.' || $file == '..') {
continue;
}
$file = $dir.$file;
if (is_dir($file)) {
$directory_path = $file.DIRECTORY_SEPARATOR;
array_push($directories, $directory_path);
} elseif (is_file($file)) {
if (substr( $file, -strlen( ".m" ) ) == ".m") {
$files[] = $file;
}
}
}
closedir($handle);
}
}
return $files;
}
?>
Related
I took over this site for management. the former developer used opendir() which opens only one level before getting the files in the folder. I would like to create multi-level folders before the final files. I created the sub-folders on the server but I need to modify the code to dynamically recognise the sub-folders as folders not file.
if ($handle = opendir("parentfolder/".$pageid.'/')) {
$list = array();
while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != "..") {
$list[] = "$file\n";
}
}
rsort($list);
$clength = count($list);
for($x = 0; $x <$clength; $x++){
$pubFolders .= "<a href='".$maindomain."/reports/".$list[$x]."' class='imagefolders'><img src='".$maindomain."/images/icons/image.png' alt=''/><br>".$list[$x]."</a>";
}
$data = $data.$pubFolders;
closedir($handle);
}
Use glob() with GLOB_ONLYDIR; some example functions are as follows:
function findDirectories($rootPath) {
$directories = array();
foreach (glob($rootPath . "/*", GLOB_ONLYDIR) as $directory) {
$directories[] = $directory;
}
return $directories;
}
function findFiles($rootPath, $extension) {
$files = array();
foreach (glob($rootPath . "/*.$extension") as $file) {
$files[] = $file;
}
return $files;
}
function findFilesRecursive($rootPath,$extension) {
$files = findFiles($rootPath,$extension);
$directories = findDirectories($rootPath);
if (!empty($directories)) {
foreach ($directories as $key=>$directory) {
$foundFiles = findFilesRecursive($directory,$extension);
foreach ($foundFiles as $foundFile) {
$files[] = $foundFile;
}
}
}
return $files;
}
If you don't care about defining specific extensions, just pass in * as the $extension parameter.
I was looking at RecursiveDirectoryIterator and glob to say
"return me a list of files (in an array) based on the extension (for example) .less. Oh and look in all child, grandchild and so on and so forth, excluding . and .. until you find all files matching."
But I am not sure the best approach to create a recursive function that keeps going well beyond the grand child.
What I have is a mess, its worked for two years - but now I need to refactor and change it up:
public function get_directory_of_files($path, $filename, $extension) {
if (!is_dir($path)) {
throw new AisisCore_FileHandling_FileException("Could not find said path: " . $path);
}
if (file_exists($filename)) {
$handler = opendir($path);
while ($file = readdir($handler)) {
if ($file != "." && $file != "..") {
$this->package_files [] = $file;
$count = count($this->package_files);
for ($i = 0; $i < $count; $i++) {
if (substr(strrchr($this->package_files [$i], '.'), 1) == $extension) {
if ($this->package_files [$i] == $filename) {
$this->files_got_back = $this->package_files [$i];
}
}
}
}
}
}
return $this->_files_got_back;
}
This requires a file name to be passed in and thats not really my thing to do any more. So how can I re-write this function to do the above "pseudo code"
This function recursively finds files with a matching ending string
function getDirectoryContents($directory, $extension)
{
$extension = strtolower($extension);
$files = array();
$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory));
while($it->valid())
{
if (!$it->isDot() && endsWith(strtolower($it->key()), $extension))
{
array_push($files, $it->key());
}
$it->next();
}
return $files;
}
function endsWith($haystack, $needle)
{
return $needle === "" || substr($haystack, -strlen($needle)) === $needle;
}
Used like so
print_r(getDirectoryContents('folder/', '.php'));
It converts the extension to lowercase to compare against
Take a look at this code:
<?php
class ex{
private function get_files_array($path,$ext, &$results){ //&to ensure it's a reference... but in php obj are passed by ref.
if (!is_dir($path)) {
//throw new AisisCore_FileHandling_FileException("Could not find said path: " . $path);
}
if ($dir = opendir($path)) {
$extLength = strlen($ext);
while (false !== ($file = readdir($dir))) {
if ($file != '.' && $file != '..'){
if (is_file($path.'/'.$file) && substr($file,-$extLength) == $ext){
$results[] = $path . '/' . $file; //it's a file and the correct extension
}
elseif (is_dir($path . '/'. $file)){
$this->get_files_array($path.'/'.$file, $ext, $results); //it's a dir
}
}
}
}else{
//unable to open dir
}
}
public function get_files_deep($path,$ext){
$results = array();
$this->get_files_array($path,$ext,$results);
return $results;
}
}
$ex = new ex();
var_dump($ex->get_files_deep('_some_path','.less'));
?>
It will retrieve all the files with the matching extension in the path and it's sub directories.
I hope it's what you need.
I would like to ask what I have to add to make this function to show not only the files on top dir but also the files in subdirs..
private function _populateFileList()
{
$dir_handle = opendir($this->_files_dir);
if (! $dir_handle)
{
return false;
}
while (($file = readdir($dir_handle)) !== false)
{
if (in_array($file, $this->_hidden_files))
{
continue;
}
if (filetype($this->_files_dir . '/' . $file) == 'file')
{
$this->_file_list[] = $file;
}
}
closedir($dir_handle);
return true;
}
Thank you in advance!
You could implement the recursion yourself, or you could use the existing iterator classes to handle the recursion and filesystem traversal for you:
$dirIterator = new RecursiveDirectoryIterator('.', FilesystemIterator::SKIP_DOTS);
$recursiveIterator = new RecursiveIteratorIterator($dirIterator);
$filterIterator = new CallbackFilterIterator($recursiveIterator, function ($file) {
// adjust as needed
static $badFiles = ['foo', 'bar', 'baz'];
return !in_array($file, $badFiles);
});
$files = iterator_to_array($filterIterator);
var_dump($files);
By this you can get all subdir content
customerdel('FolderPath');
function customerdel($dirname=null){
if($dirname!=null){
if (is_dir($dirname))
$dir_handle = opendir($dirname);
if (!$dir_handle)
return false;
while($file = readdir($dir_handle)) {
if ($file != "." && $file != "..") {
if (!is_dir($dirname."/".$file))
echo $dirname."/".$file.'<br>';
else{
echo $dirname.'/'.$file.'<br> ';
customerdel($dirname.'/'.$file);
}
}
}
closedir($dir_handle);
}
}
Here is how you can get a recursive array of all files in a directory and its subdirectories.
The returned array is like: array( [fileName] => [filePath] )
EDIT: I've included a small check if there are filenames in the subdirectories with the same name. If so, an underscore and counter is added to the key-name in the returned array:
array( [fileName]_[COUNTER] => [filePath] )
private function getFileList($directory) {
$fileList = array();
$handle = opendir($directory);
if ($handle) {
while ($entry = readdir($handle)) {
if ($entry !== '.' and $entry !== '..') {
if (is_dir($directory . $entry)) {
$fileList = array_merge($fileList, $this->getFileList($directory . $entry . '/'));
} else {
$i = 0;
$_entry = $entry;
// Check if filename is allready in use
while (array_key_exists($_entry, $fileList)) {
$i++;
$_entry = $entry . "_$i";
}
$fileList[$_entry] = $directory . $entry;
}
}
}
closedir($handle);
}
return $fileList;
}
I have a function which returns an array of files in a folder recursive.
protected function getFiles($base) {
$files = array();
if(!is_dir($base)) return $files;
if (($handle = opendir($base)) != false) {
while (false !== ($file = readdir($handle))) {
if ($file == "." || $file == "..") continue;
if(is_dir("$base/$file")) {
$subfiles = $this->getFiles("$base/$file");
$files = array_merge($files, $subfiles);
} else {
if(File::type($file,false) == "xml")
$files[] = "$base/$file";
}
}
closedir($handle);
}
return $files;
}
How can I modify this so that it always lists the root files first before any sub folders? At the moment by default the folders always come first.
You may want to take a look at the PHP SPL DirectoryIterator Class. You can instantiate the object and then quickly iterate over to segment out directories vs. files vs. links and get the full SplFileInfo object for each (which makes it really easy to get whatever info you want about the files).
$directory = '/path/to/directory';
$iterator = new DirectoryIterator($directory);
$dirs = array();
$files = array();
$links = array();
foreach($iterator as $obj) {
if($obj->isFile()) {
$files[] = $obj;
} else if ($obj->isDir()) {
$dirs[] = $obj;
} else if ($obj->isLink()) {
$links[] = $obj;
}
}
Sorry just realized you wanted to do it recursively. Well for that use RecursiveDirectoryIterator , but concept is much the same.
Just create an array of sub-directories while you loop the directory pointer, and iterate the array of directories at the end:
protected function getFiles($base) {
$files = $dirs = array();
if(!is_dir($base)) return $files;
if (($handle = opendir($base)) != false) {
while (false !== ($file = readdir($handle))) {
if ($file == "." || $file == "..") continue;
if(is_dir("$base/$file")) {
$dirs[] = "$base/$file";
} else {
if(File::type($file,false) == "xml")
$files[] = "$base/$file";
}
}
closedir($handle);
foreach ($dirs as $dir) {
$subfiles = $this->getFiles($dir);
$files = array_merge($files, $subfiles);
}
}
return $files;
}
Use two arrays one for the files in the current folder and one for the those in subfolders then merge them.
protected function getFiles($base) {
$files = array();
$subFiles = array();
if(!is_dir($base)) return $files;
if (($handle = opendir($base)) != false) {
while (false !== ($file = readdir($handle))) {
if ($file == "." || $file == "..") continue;
if(is_dir("$base/$file")) {
$subFiles = array_merge($subFiles, $this->getFiles("$base/$file"));
} else {
if(File::type($file,false) == "xml")
$files[] = "$base/$file";
}
}
closedir($handle);
}
return array_merge($files, $subFiles);
}
The function below returns an array of XML files from a given directory including all subdirectories. How can I modify this function by passing a second optional parameter which excludes a directory.
E.g:
getDirXmlFiles($config_dir, "example2");
Directory/Files
/file1.xml
/file2.xml
/examples/file3.xml
/examples/file4.xml
/example2/file5.xml
/example2/file5.xml
In the above case the function would return all files except files in the example2 directory.
function getDirXmlFiles($base) {
$files = array();
if(!is_dir($base)) return $files;
if ($handle = opendir($base)) {
while (false !== ($file = readdir($handle))) {
if ($file == "." || $file == "..") continue;
if(is_dir("$base/$file")) {
$subfiles = $this->getDirXmlFiles("$base/$file");
$files = array_merge($files, $subfiles);
} else {
if(Cms_File::type($file,false) == "xml")
$files[] = "$base/$file";
}
}
closedir($handle);
}
return $files;
}
Try this:
function getDirXmlFiles($base, $exclude = NULL) {
$files = array();
if(!is_dir($base)) return $files;
if ($handle = opendir($base)) {
while (false !== ($file = readdir($handle))) {
if ($file == "." || $file == ".." || "$base/$file" == $exclude) continue;
if(is_dir("$base/$file")) {
$subfiles = $this->getDirXmlFiles("$base/$file",$exclude);
$files = array_merge($files, $subfiles);
} else {
if(Cms_File::type($file,false) == "xml")
$files[] = "$base/$file";
}
}
closedir($handle);
}
return $files;
}
Redefine the function inputs:
function getDirXmlFiles($base, $excludeDir) {
Then change this line:
if(is_dir("$base/$file")) {
to this:
if(is_dir("$base/$file") && "$base/$file" != "$base/$excludeDir") {
You can just slightly modify the function to check and see if the current directory is the one you want to exclude or not
function getDirXmlFiles($base,$exclude) {
$files = array();
if(!is_dir($base)) return $files;
if ($handle = opendir($base)) {
while (false !== ($file = readdir($handle))) {
if ($file == "." || $file == "..") continue;
if(is_dir("$base/$file") && $file != $exclude) {
$subfiles = $this->getDirXmlFiles("$base/$file",$exclude);
$files = array_merge($files, $subfiles);
} else {
if(Cms_File::type($file,false) == "xml")
$files[] = "$base/$file";
}
}
closedir($handle);
}
return $files;
}
Smth of the sort ( tested only the syntax validation there might be needed some small debugging ) ( this will allow you to exclude multiple directory names )
function getDirXmlFiles($base, $excludeArray = array()) {
if (!is_array($excludeArray))
throw new Exception(__CLASS__ . "->" . __METHOD__ . " expects second argument to be a valid array");
$files = array();
if(!is_dir($base)) return $files;
if ($handle = opendir($base)) {
while (false !== ($file = readdir($handle))) {
$path = $base . "/" . $file;
$isDir = is_dir($path);
if (
$file == "."
|| $file == ".."
|| (
$isDir
&& count($excludeArray) > 0
&& in_array($file, $excludeArray)
)
)
continue;
if($isDir) {
$subfiles = $this->getDirXmlFiles($path, $excludeArray);
$files = array_merge($files, $subfiles);
} else {
if(Cms_File::type($file,false) == "xml")
$files[] = $path;
}
}
closedir($handle);
}
return $files;
}