There are all these resources for recursively looping through sub directories, but I haven't found ONE that shows how to do the opposite.
This is what I want to do...
<?php
// get the current working directory
// start a loop
// check if a certain file exists in the current directory
// if not, set current directory as parent directory
// end loop
So, in other words, I'm searching for a VERY specific file in the current directory, if it doesn't exist there, check it's parent, then it's parent, etc.
Everything I've tried just feels ugly to me. Hopefully someone has an elegant solution to this.
Thanks!
Try to create a recursive function like this
function getSomeFile($path) {
if(file_exists($path) {
return file_get_contents($path);
}
else {
return getSomeFile("../" . $path);
}
}
The easiest way to do this would be using ../ this will move you to the folder above. You can then get a file/folder list for that directory. Don't forget that if you check children of the directory above you then you're checking your siblings. If you just want to go straight up the tree then you can simply keep stepping up a directory until you hit root or as far as you are permitted to go.
<?php
$dir = '.';
while ($dir != '/'){
if (file_exists($dir.'/'. $filename)) {
echo 'found it!';
break;
} else {
echo 'Changing directory' . "\n";
$dir = chdir('..');
}
}
?>
Modified mavili 's code:
function findParentDirWithFile( $file = false ) {
if ( empty($file) ) { return false; }
$dir = '.';
while ($dir != '/') {
if (file_exists($dir.'/'. $file)) {
echo 'found it!';
return $dir . '/' . $file;
break;
} else {
echo 'Changing directory' . "\n";
chdir('..');
$dir = getcwd();
}
}
}
Related
I'm trying to create a script for including (through require_once) multiple files, but I'm expecting from it following behavior:
all file names of required files are defined as values in array
script check if all files from array exist in given directory
if yes, require them and continue (only if each of them exist)
if no, terminate script and show error message (if any file is missing)
UPDATE
After taking a closer look at my original script I found why it didn't work. Second IF statement ($countMissing == 0) was inside FOR loop and it produced empty arrays for files which were found. Taking that IF statement out of the loop sorted the problem.
WORKING VERSION (with few tiny modifications):
// Array with required file names
$files = array('some_file', 'other_file', 'another_file');
// Count how many files is in the array
$count = count($files);
// Eampty array for catching missing files
$missingFiles = array();
for ($i=0; $i < $count; $i++) {
// If filename is in the array and file exist in directory...
if (in_array($files[$i], $files) && file_exists(LIBRARIES . $files[$i] . '.php')) {
// ...update array value with full path to file
$files[$i] = LIBRARIES . $files[$i] . '.php';
} else {
// Add missing file(s) to array
$missingFiles[] = LIBRARIES . $files[$i] . '.php';
}
}
// Count errors
$countMissing = count($missingFiles);
// If there was no missing files...
if ($countMissing == 0) {
foreach ($files as $file) {
// ...include all files
require_once ($file);
}
} else {
// ...otherwise show error message with names of missing files
echo "File(s): " . implode(", ", $missingFiles) . " wasn't found.";
}
If this thread won't be deleted I hope it will help somebody.
Try this:
$files = array(
'some_file',
'other_file',
'another_file',
);
// create full paths
$files = array_map(function ($file) {
return ROOT_DIR . $file . '.php')
}, $files);
// find missing files
$missing = array_filter($files, function ($file) {
return !file_exists($file);
});
if (0 === count($missing)) {
array_walk($files, function ($file) {
require_once $file;
});
} else {
array_walk($missing, function ($file) {
echo "File: " . $file " wasn't found.";
});
}
For reference, see:
http://php.net/manual/en/function.array-map.php
http://php.net/manual/en/function.array-filter.php
http://php.net/manual/en/function.array-walk.php
Try this, prevent loop inside the loop.
for ($i=0; $i < $count; $i++) {
// If filename is in the array but file not exist in directory...
if (in_array($files[$i], $files) && !file_exists(ROOT_DIR . $files[$i] . '.php')) {
// ...add name of missing file to error array
$errors[] = $files[$i];
}
else{
require_once (ROOT_DIR . $file[$i] . '.php');
}
}
The code from localheinz and jp might be fine, but I wouldn't code things like that because it makes things complicated. Assuming you don't want a list of missing files (which would be slightly different) I'd do it like this:
$filesOK=true;
foreach($files as $file)
{
$path = ROOT_DIR . $file . ".php";
if(!file_exists($path ))
{
$filesOK=false; // we have a MIA
break; // quit the loop, one failure is enough
}
}
if($filesOK)
foreach($files as $file)
require_once($file);
else
echo "We have a problem";
For me this is much easier to see at a glance. Easier to debug, and the CPU is going to do the same job one way or anther. Probably not much difference in execution speed - if that even mattered.
If you need the list of missing files, then:
$filesOK=true;
foreach($files as $file)
{
$path = ROOT_DIR . $file . ".php";
if(!file_exists($path)) // assume each file is given with proper path
{
$filesOK=false; // we have a MIA
$mia[]=$path; // or $file if you just want the name
}
}
if($filesOK)
foreach($files as $file)
require_once($file);
else
{
if(is_array(#$mia)) // I always make sure foreach is protected
foreach($mia as $badfile) // even if it seems obvious that its ok
echo "Missing in action: $badfile<br>";
}
I want to be able to list all the directories, subdirectories and files in the "./" folder ie the project folder called fileSystem which contains this php file scanDir.php.
You can view the directory system I've got here:
At the minute it will only return the subdirectory folders/files in the root of the mkdir directory but not any folders inside that subdirectory.
How do I modify the code so that it demonstrates all the files, directories, subdirectories and their files and subdirectories within the fileSystem folder given that the php file being run is called scanDir.php and the code for that is provided below.
Here is the php code:
$path = "./";
if(is_dir($path))
{
$dir_handle = opendir($path);
//extra check to see if it's a directory handle.
//loop round one directory and read all it's content.
//readdir takes optional parameter of directory handle.
//if you only scan one single directory then no need to passs in argument.
//if you are then going to scan into sub-directories the argument needs
//to be passed into readdir.
while (($dir = readdir($dir_handle))!== false)
{
if(is_dir($dir))
{
echo "is dir: " . $dir . "<br>";
if($dir == "mkdir")
{
$sub_dir_handle = opendir($dir);
while(($sub_dir = readdir($sub_dir_handle))!== false)
{
echo "--> --> contents=$sub_dir <br>";
}
}
}
elseif(is_file($dir))
{
echo "is file: " . $dir . "<br>" ;
}
}
closedir($dir_handle); //will close the automatically open dir.
}
else {
echo "is not a directory";
}
Use scandir to see all stuff in the directory and is_file to check if the item is file or next directory, if it is directory, repeat the same thing over and over.
So, this is completely new code.
function listIt($path) {
$items = scandir($path);
foreach($items as $item) {
// Ignore the . and .. folders
if($item != "." AND $item != "..") {
if (is_file($path . $item)) {
// this is the file
echo "-> " . $item . "<br>";
} else {
// this is the directory
// do the list it again!
echo "---> " . $item;
echo "<div style='padding-left: 10px'>";
listIt($path . $item . "/");
echo "</div>";
}
}
}
}
echo "<div style='padding-left: 10px'>";
listIt("/");
echo "</div>";
You can see the live demo here in my webserver, btw, I will keep this link just for a second
When you see the "->" it's an file and "-->" is a directory
The pure code with no HTML:
function listIt($path) {
$items = scandir($path);
foreach($items as $item) {
// Ignore the . and .. folders
if($item != "." AND $item != "..") {
if (is_file($path . $item)) {
// this is the file
// Code for file
} else {
// this is the directory
// do the list it again!
// Code for directory
listIt($path . $item . "/");
}
}
}
}
listIt("/");
the demo can take a while to load, it's a lot of items.
There are some powerful builtin functions for PHP to find files and folders, personally I like the recursiveIterator family of classes.
$startfolder=$_SERVER['DOCUMENT_ROOT'];
$files=array();
foreach( new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $startfolder, RecursiveDirectoryIterator::KEY_AS_PATHNAME ), RecursiveIteratorIterator::CHILD_FIRST ) as $file => $info ) {
if( $info->isFile() && $info->isReadable() ){
$files[]=array('filename'=>$info->getFilename(),'path'=>realpath( $info->getPathname() ) );
}
}
echo '<pre>',print_r($files,true),'</pre>';
I have one main directory "Images" and many subdirectories - inside them are files (in main folder too). Example tree:
+Images
-first.jpg
-second.jpg
++FOLDER1
--image1.jpg
++FOLDER2
--image2.jpg
I found a script there:
function ListFiles($dir) {
if($dh = opendir($dir)) {
$files = Array();
$inner_files = Array();
while($file = readdir($dh)) {
if($file != "." && $file != ".." && $file[0] != '.') {
if(is_dir($dir . "/" . $file)) {
$inner_files = ListFiles($dir . "/" . $file);
if(is_array($inner_files)) $files = array_merge($files, $inner_files);
} else {
array_push($files, $dir . "/" . $file);
}
}
}
closedir($dh);
return $files;
}
}
$list = ListFiles('../upload/_thumbs/Images');
sort($list);
foreach ($list as $key=>$file){
echo '<option value="">'.$file.'</option>';
}
This script works fine but I can sort my results by folders (first main content, then rest)... I get i my select:
../upload/_thumbs/Images/first.jpg
../upload/_thumbs/Images/FOLDER1/image1.jpg
../upload/_thumbs/Images/FOLDER2/image2.jpg
../upload/_thumbs/Images/second.jpg
Expection:
../upload/_thumbs/Images/first.jpg
../upload/_thumbs/Images/second.jpg
../upload/_thumbs/Images/FOLDER1/image1.jpg
../upload/_thumbs/Images/FOLDER2/image2.jpg
I would be glad if you will help me! Thanks!
Well it probably has something to do with the sort function you call somewhere in the last lines
sort($list);
It's causing the array to sort alphabetical. You could try to leave it out, or you could try to use natsort.
natsort($list);
This is happening because you are sorting with the full path. Instead of sort, I would use uasort which is similar to sort, except you use a callback function to define which goes first.
alternatively, you could get by prepending the file name to the path and using the key on insert. In other words:
array_push($files, $dir . "/" . $file);
becomes:
$files[$file . $dir] = $dir . "/" . $file;
then you sort using ksort
The code below is able list down nicely all the folders, sub folder and their files.
But I need create this in as a directory.html which will hyperlink to the file. it will be open at a computer so the linking how do I do it, as of now I only can display the content but no hyperlink.
Thanks for helping. !
<?php
$base_dir = 'C:\Users\baoky\Dropbox\SS133A\FINAL PHASE\Documents To Submit';
if (is_dir($base_dir))
scan_directory($base_dir);
else
echo 'Invalid base directory. Please check your setting.';
// recursive function to check all dir
function scan_directory($path) {
if (is_dir($path)) {
if ($dir_handle = opendir($path)) {
echo '<ul>';
while (($file = readdir($dir_handle)) !== false) {
if ($file != '.' && $file != '..') {
if (is_dir($path . '/' . $file)) {
echo '<li>';
echo ''.$file.'';
scan_directory($path . '/' . $file);
echo '</li>';
}
else
echo "<li>{$file}</li>";
}
}
echo '</ul>';
}
}
}
?>
the issue is with the recursive is not able to get the full path. I did a href but if a folder is inside 1 folder, then in another sub folder , then another sub folder which is folder1/folder2/folder3/file.txt then its display only file.txt which is wrong.
echo "<a href='$file'>$file</a>" will be the basics of it, though you'd need to build the FULL path leading up the file, since you're doing a recursive dump and only dealing with the "local" sub-path.
I have a basic PHP script that displays the file contents of a directory. Here is the script:
<?php
$Dept = "deptTemplate";
if(isset($_REQUEST['dir'])) {
$current_dir = $_REQUEST['dir'];
} else {
$current_dir = 'docs';
}
if ($handle = opendir($current_dir)) {
while (false !== ($file_or_dir = readdir($handle))) {
if(in_array($file_or_dir, array('.', '..'))) continue;
$path = $current_dir.'/'.$file_or_dir;
if(is_file($path)) {
echo '`'.$file_or_dir.' - [Delete button/link]<br/>`';
} else {
echo '``'.$file_or_dir."\n`` - [Delete button/link]`<br/>`";
}
}
closedir($handle);
}
?>
I am trying to create a delete link/button that displays next to each file and when clicked, the corresponding file will be deleted. Would you know how to do this?
Use the built-in unlink($filepath) function.
Sure, you'd have to use unlink() and rmdir(), and you'd need a recursive directory removal function because rmdir() doesn't work on directories with files in them. You'd also want to make sure that the deletion script is really secure to stop people from just deleting everything.
Something like this for the recursive function:
function Remove_Dir($dir)
{
$error = array();
if(is_dir($dir))
{
$files = scandir($dir); //scandir() returns an array of all files/directories in the directory
foreach($files as $file)
{
$fullpath = $dir . "/" . $file;
if($file == '..' || $file == '.')
{
continue; //Skip if ".." or "."
}
elseif(is_dir($fullpath))
{
Remove_Dir($fullpath); //recursively remove nested directories if directory
}
elseif(is_file($fullpath))
{
unlink($fullpath); //Delete file otherwise
}
else
{
$error[] = 'Error on ' . $fullpath . '. Not Directory or File.' //Should be impossible error, because everything in a directory should be a file or directory, or . or .., and thus should be covered.
}
}
$files = scandir($dir); //Check directory again
if(count($files) > 2) //if $files contains more than . and ..
{
Remove_Dir($dir);
}
else
{
rmdir($dir); //Remove directory once all files/directories are removed from within it.
}
if(count($error) != 0)
{return $error;}
else
{return true;}
}
}
Then you just need to pass the file or directory to be deleted through GET or something to the script, probably require urlencode() or something for that, make sure that it's an authorized user with permissions to delete trying to delete the stuff, and unlink() if it's a file, and Remove_Dir() if it's a directory.
You should have to prepend the full path to the directory or file to the directory/file in the script before removing the directory/file.
Some things you'll want for security is firstly making sure that the deletion is taking place in the place it's supposed to, so someone can't do ?dir=/ or something and attempt to delete the entire filesystem from root, which can probably be circumvented by prepending the appropriate path onto the input with something like $dir = '/home/user/public_html/directories/' . $_GET['dir'];, of course then they can potentially delete everything in that path, which means that you need to make sure that the user is authorized to do so.
Need to keep periodic backups of files just in case.
Something like this? Not tested...
<?php
echo '`'.$file_or_dir.' - [Delete button/link]<br/>`';
?>
<?php
if ($_GET['del'] == 1 && isset($_GET['file_or_dir']){
unlink ("path/".$_GET['file_or_dir']);
}
?>
I've worked it out:
I added this delete link on the end of each listed file in the original script:
- < a href="delete.php?file='.$file_or_dir.'&dir=' . $dir . '"> Delete< /a>< br/>';
This link takes me to the download script page, which looked like this:
<?php
ob_start();
$file = $_GET["file"];
$getDir = $_GET["dir"];
$dir = 'docs/' . $getDir . '';
$isFile = ($dir == "") ? 'docs/' . $file . '' : '' . $dir . '/' . $file . '';
if (is_file($isFile)){
if ($dir == "")
unlink('docs/' . $file . '');
else
unlink('' . $dir . '/' . $file . '');
echo '' . $file . ' deleted';
echo ' from ' . $dir . '';
}
else{
rmdir('' . $dir . '/' . $file . '');
echo '' . $dir . '/' . $file . ' deleted';}
header("Location: indexer.php?p=" . $getDir . "");
ob_flush();
?>
It all works brilliantly now, thank you all for your help and suggestions :)