Directory iterator and recursive directory iterator - php

I had to list all files and folders in a directory:
$images = array();
$dirs = array();
$dir = new DirectoryIterator($upload_dir_real);
foreach ($dir as $file) {
if ($file->isDot()) {
continue;
}
if ($file->isDir()) {
// dir
$scanned_dirs[] = $file->getPath();
continue;
} else {
// file
//echo $file->getFilename() . "<br>\n";//DEBUG
$realfile = $file->getFilename() . "<br>\n";
$realpath = $file->getPathname();
echo realpath($realfile);//DEBUG
$file->getFilename();
$images[] = realpath( $realpath );
}
}
This works fine (no errors) but of course counted only the root, so I tried recursive:
$images = array();
$dirs = array();
$dir = new RecursiveDirectoryIterator($upload_dir_real);
foreach ($dir as $file) {
if ($file->isDot()) {
continue;
}
if ($file->isDir()) {
// dir
$scanned_dirs[] = $file->getsubPath();
continue;
} else {
// file
//echo $file->getFilename() . "<br>\n"; //DEBUG
$realfile = $file->getsubFilename() . "<br>\n";
$realpath = $file->getsubPathname();
echo realpath($realfile);//DEBUG
$file->getFilename();
$images[] = realpath( $realpath );
}
}
Basically, I changed the getPath(); with getsubPath() (and equivalent). The problem is that it give me an error:
Fatal error: Call to undefined method SplFileInfo::isDot() in blah blah path
so I searched a while and found this:
Why does isDot() fail on me? (PHP)
This is basically the same problem, but when I try, I get this error:
Fatal error: Class 'FilesystemIterator' not found in in blah blah path
Questions:
1 - why is the method described in the other accepted answer not working for me?
2 - in that same answer, what is the following code:
new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(
$pathToFolder,
FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::CURRENT_AS_SELF));
This actually calls RecursiveIteratorIterator twice? I mean, if it is recursive, it can not be recursive twice :-)
2b - how come FilesystemIterator is not found, even if the PHP manual states (to my understanding) that it is a part of what the recursive iterator is built upon?
(Those questions are because I want to understand better, not to just copy and paste answers).
3 - is there a better way to list all folders and files cross platform?

1 - why is the method described in the other accepted answer not working for me ??`
As far as i can tell . the code works perfectly but your implementation is wrong you are using the following
Code
$dir = new RecursiveDirectoryIterator($upload_dir_real);
Instead of
$dir = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($upload_dir_real));
In that same answer actually calls RecursiveIteratorIterator twice ?? I mean, if it is recursive , it can not be recursive twice ... :-))
No it does not its different
RecursiveIteratorIterator != RecursiveDirectoryIterator != FilesystemIterator
^ ^
how come FilesystemIterator is not found , even if the php manual states (to my understanding) that it is a part of what the recursive iterator is built upon??
You already answered that your self in your comment you are using PHP version 5.2.9 which is no longer supported or recommended
3 - Is there a better way to list all folder and files cross platform ??
Since that is resolved all you need is FilesystemIterator::SKIP_DOTS you don't have to call $file->isDot()
Example
$fullPath = __DIR__;
$dirs = $files = array();
$directory = new RecursiveDirectoryIterator($fullPath, FilesystemIterator::SKIP_DOTS);
foreach (new RecursiveIteratorIterator($directory, RecursiveIteratorIterator::SELF_FIRST) as $path ) {
$path->isDir() ? $dirs[] = $path->__toString() : $files[] = realpath($path->__toString());
}
var_dump($files, $dirs);

Here is another method, utilizing setFlags:
<?php
$o_dir = new RecursiveDirectoryIterator('.');
$o_dir->setFlags(RecursiveDirectoryIterator::SKIP_DOTS);
$o_iter = new RecursiveIteratorIterator($o_dir);
foreach ($o_iter as $o_info) {
echo $o_info->getPathname(), "\n";
}
https://php.net/filesystemiterator.setflags

Related

How do I remove the dot using isDot() using RecursiveDirectoryIterator?

I have this simple below code to help me get the contents of all files and sub folder. I took this code from the PHP cook book. it says that I can use the isDot() but it doesn't explain how to ..
I tried to put a if just before array but it doesn't work...
if (! $file->isDot())
I don't know how to put both getPathname and isDot when adding to the array.
I've read the PHP net website but don't understand how to fit in.
code:
$dir = new RecursiveDirectoryIterator('/usr/local');
$dircontent = array();
foreach (new RecursiveIteratorIterator($dir) as $file) {
$dircontent [] = $file -> getPathname();
}
print_r($dircontent);
Use the FilesystemIterator::SKIP_DOTS flag in your RecursiveDirectoryIterator to get rid of dot files:
$dir = '/usr/local';
$result = array();
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(
$dir,
FilesystemIterator::SKIP_DOTS
)
);
foreach ($files as $file) {
$result[] = $file->getPathname();
}
print_r($result);
The reason isDot() is failing is that your $file variable holds a SplFileInfo object, not a DirectoryIterator.

php glob - scan in subfolders for a file

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 "$search";
}
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.
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.
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.
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
As a full solution for your problem (this was also my problem):
<?php
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.

unable to skip unreadable directories with RecursiveDirectoryIterator

I want to get a list of all the subdirectories and my below code works except when I have readonly permissions on certain folders.
In the below question it shows how to skip a directory with RecursiveDirectoryIterator
Can I make RecursiveDirectoryIterator skip unreadable directories? however my code is slightly different here and I am not able to get around the problem.
$path = 'www/';
foreach (new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path,RecursiveDirectoryIterator::KEY_AS_PATHNAME),
RecursiveIteratorIterator::CHILD_FIRST) as $file => $info)
{
if ($info->isDir())
{
echo $file . '<br>';
}
}
I get the error
Uncaught exception 'UnexpectedValueException' with message 'RecursiveDirectoryIterator::__construct(../../www/special): failed to open dir: Permission denied'
I have tried replacing it with the accepted answer in the other question.
new RecursiveIteratorIterator(
new RecursiveDirectoryIterator("."),
RecursiveIteratorIterator::LEAVES_ONLY,
RecursiveIteratorIterator::CATCH_GET_CHILD);
However this code will not give me a list of all the directories inside of www like I want, where am I going wrong here?
Introduction
The main issue with your code is using CHILD_FIRST
FROM PHP DOC
Optional mode. Possible values are
RecursiveIteratorIterator::LEAVES_ONLY - The default. Lists only leaves in iteration.
RecursiveIteratorIterator::SELF_FIRST - Lists leaves and parents in iteration with parents coming first.
RecursiveIteratorIterator::CHILD_FIRST - Lists leaves and parents in iteration with leaves coming first.
What you should use is SELF_FIRST so that the current directory is included. You also forgot to add optional parameters RecursiveIteratorIterator::CATCH_GET_CHILD
FROM PHP DOC
Optional flag. Possible values are RecursiveIteratorIterator::CATCH_GET_CHILD which will then ignore exceptions thrown in calls to RecursiveIteratorIterator::getChildren().
Your CODE Revisited
foreach (new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path,RecursiveDirectoryIterator::KEY_AS_PATHNAME),
RecursiveIteratorIterator::SELF_FIRST, RecursiveIteratorIterator::CATCH_GET_CHILD) as $file => $info)
{
if ($info->isDir())
{
echo $file . '<br>';
}
}
You really want CHILD_FIRST
If you really want to maintain the CHILD_FIRST structure then i suggest you use ReadableDirectoryIterator
Example
foreach ( new RecursiveIteratorIterator(
new ReadableDirectoryIterator($path),RecursiveIteratorIterator::CHILD_FIRST) as $file ) {
echo $file . '<br>';
}
Class Used
class ReadableDirectoryIterator extends RecursiveFilterIterator {
function __construct($path) {
if (!$path instanceof RecursiveDirectoryIterator) {
if (! is_readable($path) || ! is_dir($path))
throw new InvalidArgumentException("$path is not a valid directory or not readable");
$path = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
}
parent::__construct($path);
}
public function accept() {
return $this->current()->isReadable() && $this->current()->isDir();
}
}
function dirScan($dir, $fullpath = false){
$ignore = array(".","..");
if (isset($dir) && is_readable($dir)){
$dlist = array();
$dir = realpath($dir);
$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir,RecursiveDirectoryIterator::KEY_AS_PATHNAME),RecursiveIteratorIterator::SELF_FIRST, RecursiveIteratorIterator::CATCH_GET_CHILD);
foreach($objects as $entry){
if(!in_array(basename($entry), $ignore)){
if (!$fullpath){
$entry = str_replace($dir, '', $entry);
}
$dlist[] = $entry;
}
}
return $dlist;
}
}
This code works 100%...
You can simply use this function in order to scan for files and folders in your desired directory or drive. You just need to pass the path of the desired directory into the function. The second parameter of the function is to show full-path of the scanned files and folder. False value of the second parameter means not to show full-path.
The array $ignore is used to exclude any desired filename or foldername from the listing.
The function returns the array containing list of files and folders.
This function skips the files and folders that are unreadable while recursion.
I've set up the following directory structure:
/
test.php <-- the test script
www/
test1/ <-- permissions = 000
file1
test2/
file2
file3
I ran the following code (I've added the SKIP_DOTS flag to skip . and .. btw):
$i = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator("www", FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::LEAVES_ONLY,
RecursiveIteratorIterator::CATCH_GET_CHILD
);
print_r(iterator_to_array($i));
It outputs the following:
Array
(
[www/test2/file2] => SplFileInfo Object
(
[pathName:SplFileInfo:private] => www/test2/file2
[fileName:SplFileInfo:private] => file2
)
[www/file3] => SplFileInfo Object
(
[pathName:SplFileInfo:private] => www/file3
[fileName:SplFileInfo:private] => file3
)
)
This works as expected.
Update
Added the flags you've had in your original example (although I believe those are default anyway):
foreach (new RecursiveIteratorIterator(
new RecursiveDirectoryIterator("www", FilesystemIterator::SKIP_DOTS | FilesystemIterator::KEY_AS_PATHNAME),
RecursiveIteratorIterator::LEAVES_ONLY,
RecursiveIteratorIterator::CATCH_GET_CHILD | RecursiveIteratorIterator::CHILD_FIRST
) as $file => $info) {
echo $file, "\n";
print_r($info);
if ($info->isDir()) {
echo $file . '<br>';
}
}
Output:
www/test2/file2
SplFileInfo Object
(
[pathName:SplFileInfo:private] => www/test2/file2
[fileName:SplFileInfo:private] => file2
)
www/file3
SplFileInfo Object
(
[pathName:SplFileInfo:private] => www/file3
[fileName:SplFileInfo:private] => file3
)
<?php
$path = "D:/Movies";
$directory_iterator = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::KEY_AS_PATHNAME);
$files = new RecursiveIteratorIterator($directory_iterator,
RecursiveIteratorIterator::SELF_FIRST,
RecursiveIteratorIterator::CATCH_GET_CHILD);
try {
foreach( $files as $fullFileName => $file) {
$path_parts = pathinfo($fullFileName);
if(is_file($fullFileName)){
$path_parts = pathinfo($fullFileName);
$fileName[] = $path_parts['filename'];
$extensionName[] = $path_parts['extension'];
$dirName[] = $path_parts['dirname'];
$baseName[] = $path_parts['basename'];
$fullpath[] = $fullFileName;
}
}
foreach ($fullpath as $filles){
echo $filles;
echo "</br>";
}
}
catch (UnexpectedValueException $e) {
printf("Directory [%s] contained a directory we can not recurse into", $directory);
}
?>
The glob function skips read errors automatically and should simplify your code a bit as well.
If you are getting unhandled exceptions, why don't you put that code in a try block, with an exception catch block to catch errors when it can't read directories? Just a simple suggestion by looking at your code and your problem. There is probably a neater way to do it in PHP.
You need to use SELF_FIRST constant if you want to return the unreadable directory name.
When you're doing CHILD_FIRST, it attempt to get into the directory, fails, and the current directory name is not included.
$path = 'testing';
$directory_iterator = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::KEY_AS_PATHNAME);
$iterator = new RecursiveIteratorIterator($directory_iterator,
RecursiveIteratorIterator::SELF_FIRST,
RecursiveIteratorIterator::CATCH_GET_CHILD);
foreach ($iterator as $file => $info) {
if ($info->isDir()) {
echo $file . "\n";
}
}
What about try catch the UnexpectedValueException. Maybe there is even an unique exception code for that error you can check. Otherwise you can evil parse exception message for "permission denied".
I would suggest to examine the http://php.net/manual/de/class.unexpectedvalueexception.php

Check directory exists and if it doesnt choose an image

I have this code so far which perfectly but relies on there being a directory in place:
$path = '/home/sites/therealbeercompany.co.uk/public_html/public/themes/trbc/images/backgrounds/'.$this->slug;
$bgimagearray = array();
$iterator = new DirectoryIterator($path);
foreach ($iterator as $fileinfo) {
if ($fileinfo->isFile() && !preg_match('\.jpg$/', $fileinfo->getFilename())) {
$bgimagearray[] = "'" . $fileinfo->getFilename() . "'";
}
}
I need to work in a bit at the top so that if the directory doesnt exist it defaults to the images sat in the root of the background directory...
Any help would be appreciated.
You want is_dir. Test your slug directory, and if it doesn't exist, use the root background directory instead.
Use is_dir to see if the dir is there, and if not, set $path to the current path (where the script is running from)
if (!is_dir($path)) {
$path = $_SERVER["PATH_TRANSLATED"];
}
I very much dangerously assumed that $path is not going to be used anywhere else :)
(is_dir is better, thanks!)
DirectoryIterator will throw an UnexpectedValueException when the path cannot be opened, so you can wrap the call into a try/catch block and then fallback to the root path. In a function:
function getBackgroundImages($path, $rootPath = NULL)
{
$bgImages = array();
try {
$iterator = new DirectoryIterator($path);
// foreach code
} catch(UnexpectedValueException $e) {
if($rootPath === NULL) {
throw $e;
}
$bgImages = getBackgroundImages($rootPath);
}
return $bgImages;
}
But of course file_exists or is_dir are a valid options too.
You could also use the file_exists function to check for the directory.

Recursive File Search (PHP)

I'm trying to return the files in a specified directory using a recursive search.
I successfully achieved this, however I want to add a few lines of code that will allow me to specify certain extensions that I want to be returned.
For example return only .jpg files in the directory.
Here's my code,
<?php
$it = new RecursiveDirectoryIterator("L:\folder\folder\folder");
foreach(new RecursiveIteratorIterator($it) as $file) {
echo $file . "<br/> \n";
}
?>
please let me know what I can add to the above code to achieve this, thanks
<?php
$it = new RecursiveDirectoryIterator("L:\folder\folder\folder");
$display = Array ( 'jpeg', 'jpg' );
foreach(new RecursiveIteratorIterator($it) as $file)
{
if (in_array(strtolower(array_pop(explode('.', $file))), $display))
echo $file . "<br/> \n";
}
?>
You should create a filter:
class JpegOnlyFilter extends RecursiveFilterIterator
{
public function __construct($iterator)
{
parent::__construct($iterator);
}
public function accept()
{
return $this->current()->isFile() && preg_match("/\.jpe?g$/ui", $this->getFilename());
}
public function __toString()
{
return $this->current()->getFilename();
}
}
$it = new RecursiveDirectoryIterator("L:\folder\folder\folder");
$it = new JpegOnlyFilter($it);
$it = new RecursiveIteratorIterator($it);
foreach ($it as $file)
...
Try this, it uses an array of allowed file types and only echos out the file if the file extension exists within the array.
<?php
$it = new RecursiveDirectoryIterator("L:\folder\folder\folder");
$allowed=array("pdf","txt");
foreach(new RecursiveIteratorIterator($it) as $file) {
if(in_array(substr($file, strrpos($file, '.') + 1),$allowed)) {
echo $file . "<br/> \n";
}
}
?>
You may also find that you could pass an array of allowed file types to your RecursiveDirectoryIterator class and only return files that match.
Let PHP do the job:
$directory = new RecursiveDirectoryIterator('path/to/directory/');
$iterator = new RecursiveIteratorIterator($directory);
$regex = new RegexIterator($iterator, '/\.jpe?g$/i', RecursiveRegexIterator::GET_MATCH);
echo '<pre>';
print_r($regex);
Regarding the top voted answer: I created this code which is using fewer functions, only 3, isset(), array_flip(), explode() instead of 4 functions. I tested the top voted answer and it was slower than mine. I suggest giving mine a try:
$it = new RecursiveDirectoryIterator("L:\folder\folder\folder");
foreach(new RecursiveIteratorIterator($it) as $file) {
$FILE = array_flip(explode('.', $file));
if (isset($FILE['php']) || isset($FILE['jpg'])) {
echo $file. "<br />";
}
}
None of these worked for my case. So i wrote this function, not sure about efficiency, I just wanted to remove some duplicate photos quickly. Hope it helps someone else.
function rglob($dir, $pattern, $matches=array())
{
$dir_list = glob($dir . '*/');
$pattern_match = glob($dir . $pattern);
$matches = array_merge($matches, $pattern_match);
foreach($dir_list as $directory)
{
$matches = rglob($directory, $pattern, $matches);
}
return $matches;
}
$matches = rglob("C:/Bridge/", '*(2).ARW');
Only slight improvement that could be made is that currently for it to work you have to have a trailing forward slash on the start directory.
In this sample code;
You can set any path for $directory variable, for example ./, 'L:\folder\folder\folder' or anythings...
And also you can set a pattern file name in $pattern optional, You can put '/\.jpg$/' for every file with .jpg extension, and also if you want to find all files, can just use '//' for this variable.
$directory = 'L:\folder\folder\folder';
$pattern = '/\.jpg$/'; //use "//" for all files
$directoryIterator = new RecursiveDirectoryIterator($directory);
$iteratorIterator = new RecursiveIteratorIterator($directoryIterator);
$regexIterator = new RegexIterator($iteratorIterator, $pattern);
foreach ($regexIterator as $file) {
if (is_dir($file)) continue;
echo "$file\n";
}
here is the correct way to search in folders and sub folders
$it = new RecursiveDirectoryIterator(__DIR__);
$findExts = Array ( 'jpeg', 'jpg' );
foreach(new RecursiveIteratorIterator($it) as $file)
{
$ext = pathinfo($file, PATHINFO_EXTENSION);
if(in_array($ext, $findExts)){
echo $file.PHP_EOL;
}
}
You might want to check out this page about using glob() for a similar search:
http://www.electrictoolbox.com/php-glob-find-files/

Categories