So I have a code snippet that reads a directory and performs certain actions on the files inside. I have an array of filenames to exclude. My code looks like this:
$excluded = array(".","..","thumbs.db");
if($fh = #opendir($dir))
{
while(false !== ($file = #readdir($fh)))
{
if(in_array(strtolower($file),$excluded))
{
continue;
}
//do processing here...
Now, I want it that zip files also should be excluded. Since I do not know what name they might exist in, I will need to skip them based on extension.
Now I know I can split the filename and look at the last element to see if it zip etc, but what I wanted to ask is, is there a way to achieve it within the constraints of what is coded already - like adding it like this, and then tweaking the loop to handle it...
$excluded = array(".","..","thumbs.db","*.zip");
This should do the trick:
$excluded = array(".","..","thumbs.db");
$excludedExtensions = array(".zip",".rar");
if($fh = #opendir($dir))
{
while(false !== ($file = #readdir($fh)))
{
if(in_array(strtolower($file),$excluded) ||
in_array(strtolower(substr($file, -4)), $excludedExtensions) )
{
continue;
}
//do processing here...
It's not exactly what you're looking for, but i don't think it's possible to do the way you wanted it to :(
-------------------------------------------------------------------------------------
EDIT
I wanted to make a more reliable way to do this, since there is some files which have 4 or even 5 letters in their extension. After looking though the PHP manual, i found this:
$excluded = array(".","..","thumbs.db");
$excludedExtensions = array(".zip",".rar", ".7z", ".jpeg", ".phtml");
if($fh = #opendir($dir))
{
while(false !== ($file = #readdir($fh)))
{
$path_parts = pathinfo($file);
if(in_array(strtolower($file),$excluded) ||
in_array(strtolower($path_parts['extension'])) )
{
continue;
}
//do processing here...
See more here: PHP manual: pathinfo
Related
Let's say I have a folder containing 99tf.txt, 40.txt, 65.txt , in any order
If the current script var is 40.txt: I would like it to delete 65.txt (or the next file)
I was looking into something like that:
$file='40.txt';
if ($handle = opendir('./log/')) {
$entry= readdir($handle);
//move pointer to 40.txt
while ($file != $entry && $entry !== false) {
$entry = readdir($handle)
}
//go to the next file
$entry = readdir($handle)
if(is_file('./log/'.$entry)){
unlink('./log/'.$entry);
}
}
But I would like to avoid to go into a loop each time since there could be a lot of files in the folder.
So is there a way to change the $handle pointer to the '$file' directly and delete the next file?
If you don't mind using scandir, then this should work better for you.
$file = '40.txt';
$contents = scandir('./log/');
// Should already be sorted, but do again for safe measure
sort($contents);
// Make sure the file is in there.
if (false !== $index = array_search($file, $contents)) {
// If the file is at the end, do nothing.
if ($index !== count($contents)) {
// Remove the next index
unlink('./log/' . $contents[$index + 1]);
}
}
In regard to the order not mattering, you don't need to sort it. Worth noting however, is that your method, takes longer, but uses less memory, while this method is the reverse, much faster, but potentially more memory consumption.
<?php
$file = '40.txt';
$scan_folder = scandir('./log/');
$num_files = count($scan_folder);
if ($num_files > 1) {
$file_key = array_search($file, $scan_folder) +1;
unlink('./log/'.$file_key);
} else {
// here you will preserve only 1 file all others can be removed every time this script is executed
}
?>
i'm looking for away to see if the query 'id' has a folder with the same id as name in the file system, i did it but it will slow down the drive in the future with lots of files
$query = Model::all();
if(Input::get('field') == 'true'){
$filenames = scandir('img/folders');
$query->whereIn('id', $filenames);
}
as you can see this will scan and get names of all folders inside the 'folders' directory and create an array with it, now my app is going to have hundreds of thousands of folders in the future and i would like to resolve it before it happens, thanks for further help
ps: other propositions to do it differently are welcome
Do you have good reason to believe that scandir on a directory with a large number of folders will actually slow you down?
You can do your query like this:
if(Input::has('field')){
$filenames = scandir('img/folders');
$query = Model::whereIn('id', $filenames)->get();
}
Edit 1
You may find these links useful:
PHP: scandir() is too slow
Get the Files inside a directory
Edit 2
There are some really good suggestions in the links which you should be able to use for guidance to make your own implementation. As I see it, based on the links included from the first edit I made, your options are use DirectoryIterator, readdir or chunking with scandir.
This is a very basic way of doing it but I guess you could do something with readdir like this:
$ids = Model::lists('id');
$matches = [];
if($handle = opendir('path/to/folders'))
{
while (($entry = readdir($handle)) !== false)
{
if(count($ids) === 0)
{
break;
}
if ($entry != "." && $entry != "..")
{
foreach ($ids as $key => $value)
{
if($value === $entry)
{
$matches[] = $entry;
unset($ids[$key]);
}
}
}
}
closedir($handle);
}
return $matches;
I'm building a file browser, and I need to know if a directory has children (but not how many or what type).
What's the most efficient way to find if a directory has children? glob()? scandir() it? Check its tax records?
Edit
It seems I was misunderstood, although I thought I was pretty clear. I'll try to restate my question.
What is the most efficient way to know if a directory is not empty? I'm basically looking for a boolean answer - NOT EMPTY or EMPTY.
I don't need to know:
how many files are in the directory
what the files are
when they were modified
etc.
I do need to know:
does the directory have any files in it at all
efficiently.
I think this is very efficient:
function dir_contains_children($dir) {
$result = false;
if($dh = opendir($dir)) {
while(!$result && ($file = readdir($dh)) !== false) {
$result = $file !== "." && $file !== "..";
}
closedir($dh);
}
return $result;
}
It stops the listing of the directories contents as soon as there is a file or directory found (not including the . and ..).
You could use 'find' to list all empty directories in one step:
exec("find '$dir' -maxdepth 1 -empty -type d",$out,$ret);
print_r($out);
Its not "pure" php but its simple and fast.
This should do, easy, quick and effective.
<?php
function dir_is_empty($dir) {
$dirItems = count(scandir($dir));
if($dirItems > 2) return false;
else return true;
}
?>
Unfortunately, each solution so far has lacked the brevity and elegance necessary to shine above the rest.
So, I was forced to homebrew a solution myself, which I'll be implementing until something better pops up:
if(count(glob($dir."/*")) {
echo "NOT EMPTY";
}
Still not sure of the efficiency of this compared to other methods, which was the original question.
I wanted to expand vstm's answer - Check only for child directories (and not files):
/**
* Check if directory contains child directories.
*/
function dir_contains_children_dirs($dir) {
$result = false;
if($dh = opendir($dir)) {
while (!$result && ($file = readdir($dh))) {
$result = $file !== "." && $file !== ".." && is_dir($dir.'/'.$file);
}
closedir($dh);
}
return $result;
}
Is there a better way to check if a dir is empty than parsing it?
Don't think so. Shortest/quickest way I can think of is the following, which should work as far as I can see.
function dir_is_empty($path)
{
$empty = true;
$dir = opendir($path);
while($file = readdir($dir))
{
if($file != '.' && $file != '..')
{
$empty = false;
break;
}
}
closedir($dir);
return $empty;
}
This should only go through a maximum of 3 files. The two . and .. and potentially whatever comes next. If something comes next, it's not empty, and if not, well then it's empty.
Not really, but you can try to delete it. If it fails, its not empty (or you just can't delete it ;))
function dirIsEmpty ($dir) {
return rmdir($dir) && mkdir($dir);
}
Update:
It seems, that the answer, that takes the condition "without parsing" into account, doesn't find much friends ;)
function dirIsEmpty ($dir) {
return count(glob("$dir/**/*")) === 0:
}
Note, that this assumes, that the directory and every subdirectory doesn't contain any hidden file (starting with a single .).
I'm coding a simple web report system for my company. I wrote a script for index.php that gets a list of files in the "reports" directory and creates a link to that report automatically. It works fine, but my problem here is that readdir( ) keeps returning the . and .. directory pointers in addition to the directory's contents. Is there any way to prevent this OTHER THAN looping through the returned array and stripping them manually?
Here is the relevant code for the curious:
//Open the "reports" directory
$reportDir = opendir('reports');
//Loop through each file
while (false !== ($report = readdir($reportDir)))
{
//Convert the filename to a proper title format
$reportTitle = str_replace(array('_', '.php'), array(' ', ''), $report);
$reportTitle = strtolower($reportTitle);
$reportTitle = ucwords($reportTitle);
//Output link
echo "$reportTitle<br />";
}
//Close the directory
closedir($reportDir);
In your above code, you could append as a first line in the while loop:
if ($report == '.' or $report == '..') continue;
array_diff(scandir($reportDir), array('.', '..'))
or even better:
foreach(glob($dir.'*.php') as $file) {
# do your thing
}
No, those files belong to a directory and readdir should thus return them. I’d consider every other behaviour to be broken.
Anyway, just skip them:
while (false !== ($report = readdir($reportDir)))
{
if (($report == ".") || ($report == ".."))
{
continue;
}
...
}
I would not know another way, as "." and ".." are proper directories as well. As you're looping anyway to form the proper report URL, you might just put in a little if that ignores . and .. for further processing.
EDIT
Paul Lammertsma was a bit faster than me. That's the solution you want ;-)
I wanted to check for the "." and the ".." directories as well as any files that might not be valid based on what I was storing in the directory so I used:
while (false !== ($report = readdir($reportDir)))
{
if (strlen($report) < 8) continue;
// do processing
}