Skip all folders when using scandir - php

I am gathering list of files in given folder, where there might be files and folders.
I can manually skip folder when I want, but how can I skip ALL folders automatically and store only files in the array?
$moduleArray = array_diff(scandir('module/', 1), array('..', '.', 'admin'));
Well, I got this... But any way to do it while searching using scandir?
foreach($moduleArray as $module) {
$end = explode(".", $module);
if(end($end) == "php") {
$name = substr($module, 0, -8);
echo " $name <br />";
}
}

Alternatively, you could use SPL DirectoryIterator's ->isDir() too:
$moduleArray = array();
foreach (new DirectoryIterator('module/') as $file) {
if($file->isDir()) continue;
$moduleArray[] = $file->getPathname(); // or getFilename()
}

You Can Search Array And Remove Folders;
This Is The Best Way That I Know.:-)
for($i=0;$i<count($moduleArray);$i++)
{
if(is_dir($moduleArray[$i])
{
//Skip It
}
}

$moduleArray = array_diff(scandir('module/', 1), array('..', '.'));
$fileListArray = array();
foreach ($moduleArray as $module){
if (!is_dir($module)){
$fileListArray[] = $module;
}
}

Related

Why is substr($fileinfo->getFilename(),'-3-sml') !== false not working?

My goal is to move the first file containing -3-sml in the filename from temp to b2. What my code does is move every file to b2. Why is that and what to change?
$name = "";
$dname = "";
$dir = new DirectoryIterator('temp/');
foreach ($dir as $fileinfo)
{
if (!$fileinfo->isDot())
{
if (substr($fileinfo->getFilename(),'-3-sml') !== false)
{
rename("temp/".$fileinfo->getFilename(), "b2/".$fileinfo->getFilename());
$name = $fileinfo->getFilename();
$dname = "http://domain.com/b2/".$fileinfo->getFilename();
break;
}
}
}
EDIT: I tried
if (substr($fileinfo->getFilename(),'-3-sml') !== false)
{
echo $fileinfo->getFilename()."<br>";
continue;
rename("temp/".$fileinfo->getFilename(), "b2/".$fileinfo->getFilename());
$name = $fileinfo->getFilename();
$dname = "http://oneitis.mygrabrede.de/b2/".$fileinfo->getFilename();
break;
}
and it seems that substr($fileinfo->getFilename(),'-3-sml') !== false doesn't filter anything, so every filename goes through. Why?
EDIT: Solution is: it has to be strpos not substr.
If you need to move only the first matching file, you can use glob, and access the first item on the array, i.e.:
$origin_path = "/somepath/temp";
$dest_path = "/somepath/b2";
$files = glob("$origin_path/*-3-sml*");
$fileName = basename($files[0]);
if(!is_dir("$dest_path")){
mkdir("$dest_path", 0777);
rename($files[0],"$dest_path/$fileName" );
}else{
rename($files[0],"$dest_path/$fileName" );
}
If you need to search inside multiple sub-folders, use this recursive glob function:
function rglob($pattern='*', $flags = 0, $path='')
{
$paths=glob($path.'*', GLOB_MARK|GLOB_ONLYDIR|GLOB_NOSORT);
$files=glob($path.$pattern, $flags);
foreach ($paths as $path) { $files=array_merge($files,rglob($pattern, $flags, $path)); }
return $files;
}
and change from
$files = glob("$origin_path/*-3-sml*");
to
$files = rglob("$origin_path/*-3-sml*");
Change substr() to strpos()
Substr for find out substring
Strpos will return position of specific string

Recursive function scanning a folder and storing it in an array

I am trying to make a recursive function to go through all of the folder path that I have given it in the parameters.
What I am trying to do is to store the folder tree into an array for example I have Folder1 and this folder contains 4 text files and another folder and I want the structure to be a multidimensional array like the following
Array 1 = Folder one
Array 1 = text.text.....So on so forth
I have the following function that I build but its not working as I want it too. I know that I need to check whether it is in the root directory or not but when it becomes recursive it becoems harder
function displayAllFolders($root)
{
$foldersArray = array();
$listFolderFile = scandir($root);
foreach($listFolderFile as $row)
{
if($row == "." || $row == "..")
{
continue;
}
elseif(is_dir("$root/$row") == true)
{
$foldersArray["$root/$row"] = "$row";
$folder = "$root/$row";
#$foldersArray[] = displayAllFolders("$root/$row");
}
else
{
$foldersArray[]= array("$root/$row") ;
}
}
var_dump($foldersArray);
}
Using RecursiveDirectoryIterator with RecursiveIteratorIterator this becomes rather easy, e.g.:
$it = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(
// root dir
'.',
// ignore dots
RecursiveDirectoryIterator::SKIP_DOTS
),
// include directories
RecursiveIteratorIterator::SELF_FIRST
// default is:
// RecursiveIteratorIterator::LEAVES_ONLY
//
// which would only list files
);
foreach ($it as $entry) {
/* #var $entry \SplFileInfo */
echo $entry->getPathname(), "\n";
}
Your approach isn't recursive at all.
It would be recursive if you called the same function again in case of a directory. You only make one sweep.
Have a look here:
http://php.net/manual/en/function.scandir.php
A few solutions are posted. I would advise you to start with the usercomment by mmda dot nl.
(function is named dirToArray, exactly what you are tryting to do.)
In case it will be removed, I pasted it here:
function dirToArray($dir) {
$result = array();
$cdir = scandir($dir);
foreach ($cdir as $key => $value) {
if (!in_array($value,array(".",".."))) {
if (is_dir($dir . DIRECTORY_SEPARATOR . $value)) {
$result[$value] = dirToArray($dir . DIRECTORY_SEPARATOR . $value);
}
else {
$result[] = $value;
}
}
}
return $result;
}
Why not using PHP itself? Just have a look at the RecursiveDirectoryIterator of the standard php library (SPL).
$folders = [];
$iterator = new RecursiveDirectoryIterator(new RecursiveDirectoryIterator($directory));
iterator_apply($iterator, 'scanFolders', array($iterator, $folders));
function scanFolders($iterator, $folders) {
while ($iterator->valid()) {
if ($iterator->hasChildren()) {
scanFolders($iterator->getChildren(), $folders);
} else {
$folders[] = $iterator->current();
}
$iterator->next();
}
}

Optimizing a file navigator

I've come up with a basic file navigator that accepts user input to jump to different directories. The only problem I have with it is that I'm basically looping over the data three times:
Get a valid list of all directories for comparison against user input
Build a "sorted" list of directories and files
Output final list
Any tips on optimizing or improving this code?
define('ROOT', '/path/to/somewhere');
// get a list of valid paths
$valid = array();
$dir = new RecursiveDirectoryIterator(ROOT);
$dir->setFlags(RecursiveDirectoryIterator::SKIP_DOTS);
$iter = new ParentIterator($dir);
foreach(new RecursiveIteratorIterator($iter, RecursiveIteratorIterator::SELF_FIRST) as $file) {
$path = str_replace(ROOT, '', $file->getPathname());
$valid[] = $path;
}
// user input
$subpath = isset($_GET['path']) && in_array($_GET['path'], $valid) ? $_GET['path'] : NULL;
$cwd = isset($subpath) ? ROOT.$subpath : ROOT;
// build and sort directory tree
$files = array();
foreach(new DirectoryIterator($cwd) as $file) {
if($file->isDot()) {
continue;
}
if($file->isDir()) {
$path = str_replace(ROOT, '', $file->getPathname());
$count = iterator_count(new RecursiveDirectoryIterator($file->getRealPath(), FilesystemIterator::SKIP_DOTS));
$files[$path]['name'] = $file->getFilename();
$files[$path]['count'] = $count;
} else {
$files[] = $file->getFilename();
}
asort($files);
}
// output directory tree
if(!empty($files)) {
foreach($files as $key=>$value) {
if(is_array($value)) {
echo "{$value['name']} ({$value['count']})<br />";
} else {
echo "$value<br />";
}
}
}
How often is the directory structure going to change? Can it be cached and the whitelist be re-generated only when there are changes instead of on each request? This would be my approach depending on requirements and load factors.Beyond that probably lies the realm of micro-optimizations.

PHP Array Blocklist

I've created this code to cycle through the folders in the current directory and echo out a link to the folder, it all works fine. How would I go about using the $blacklist array as an array to hold the directory names of directories I dont want to show?
$blacklist = array('dropdown');
$results = array();
$dir = opendir("./");
while($file = readdir($dir)) {
if($file != "." && $file != "..") {
$results[] = $file;
}
}
closedir($dir);
foreach($results as $file) {
if($blocked != true) {
$fileUrl = $file;
$fileExplodedName = explode("_", $file);
$fileName = "";
$fileNameCount = count($fileExplodedName);
echo "<a href='".$fileUrl."'>";
$i = 1;
foreach($fileExplodedName as $name) {
$fileName .= $name." ";
}
echo trim($fileName);
echo "</a><br/>";
}
}
array_diff is the best tool for this job -- it's the shortest to write, very clear to read, and I would expect also the fastest.
$filesToShow = array_diff($results, $blacklist);
foreach($filesToShow as $file) {
// display the file
}
Use in_array for this.
$blocked = in_array($file, $blacklist);
Note that this is rather expensive. The runtime complexity of in_array is O(n) so don't make a large blacklist. This is actually faster, but with more "clumsy" code:
$blacklist = array('dropdown' => true);
/* ... */
$blocked = isset($blacklist[$file]);
The runtime complexity of the block check is then reduced to O(1) since the array (hashmap) is constant time on key lookup.

PHP scandir results: sort by folder-file, then alphabetically

PHP manual for scandir: By default, the sorted order is alphabetical in ascending order.
I'm building a file browser (in Windows), so I want the addresses to be returned sorted by folder/file, then alphabetically in those subsets.
Example: Right now, I scan and output
Aardvark.txt
BarDir
BazDir
Dante.pdf
FooDir
and I want
BarDir
BazDir
FooDir
Aardvark.txt
Dante.pdf
Other than a usort and is_dir() solution (which I can figure out myself), is there a quick and efficient way to do this?
The ninja who wrote this comment is on the right track - is that the best way?
Does this give you what you want?
function readDir($path) {
// Make sure we have a trailing slash and asterix
$path = rtrim($path, '/') . '/*';
$dirs = glob($path, GLOB_ONLYDIR);
$files = glob($path);
return array_unique(array_merge($dirs, $files));
}
$path = '/path/to/dir/';
readDir($path);
Note that you can't glob('*.*') for files because it picks up folders named like.this.
Please try this. A simple function to sort the PHP scandir results by files and folders (directories):
function sort_dir_files($dir)
{
$sortedData = array();
foreach(scandir($dir) as $file)
{
if(is_file($dir.'/'.$file))
array_push($sortedData, $file);
else
array_unshift($sortedData, $file);
}
return $sortedData;
}
I'm late to the party but I like to offer my solution with readdir() rather than with glob(). What I was missing from the solution is a recursive version of your solution. But with readdir it's faster than with glob.
So with glob it would look like this:
function myglobdir($path, $level = 0) {
$dirs = glob($path.'/*', GLOB_ONLYDIR);
$files = glob($path.'/*');
$all2 = array_unique(array_merge($dirs, $files));
$filter = array($path.'/Thumbs.db');
$all = array_diff($all2,$filter);
foreach ($all as $target){
echo "$target<br />";
if(is_dir("$target")){
myglobdir($target, ($level+1));
}
}
}
And this one is with readdir but has basically the same output:
function myreaddir($target, $level = 0){
$ignore = array("cgi-bin", ".", "..", "Thumbs.db");
$dirs = array();
$files = array();
if(is_dir($target)){
if($dir = opendir($target)){
while (($file = readdir($dir)) !== false){
if(!in_array($file, $ignore)){
if(is_dir("$target/$file")){
array_push($dirs, "$target/$file");
}
else{
array_push($files, "$target/$file");
}
}
}
//Sort
sort($dirs);
sort($files);
$all = array_unique(array_merge($dirs, $files));
foreach ($all as $value){
echo "$value<br />";
if(is_dir($value)){
myreaddir($value, ($level+1));
}
}
}
closedir($dir);
}
}
I hope someone might find this useful.

Categories