php directory reading issue - php

I have written this simple script display all the files in a directory as a set of buttons.
This code reads from the upload directory and displays all files inside a submit button in a form.
$handle = opendir("upload");
echo '<form name="form" method="post" action="download.php">';
while($name = readdir($handle)) {
echo '<input type="submit" name="file" value='.$name.' />';
}
echo '</form>';
Now the issue here is; every time I run the script I find two button at the beginning with contents . and ..
I have not been able to figure out what causes this issue.

What you have encountered are two special files used by the file system.
. represents the current directory you are in.1
.. represents the parent directory of the current directory.2
Footnotes:
1. A path such as "/my_dir/././././././file" is equivalent to "/my_dir/file".
2. A path such as "/my_dir/../my_dir/../my_dir/file" is equivalent to "/my_dir/file" since .. will make you move "up" one level.
To get around the issue of showing these two to your user filter the content returned by readdir using something as the below:
while ($name = readdir ($handle)) {
if ($name == '.' || $name == '..')
continue; /* don't echo anything, skip to next read */
echo '<input type="submit" name="file" value='.$name.' />';
}

the directory listing includes . for the current dir and .. for the parent dir.
I usually use this that i got from the PHP manual (http://php.net/manual/en/function.readdir.php)
if ($handle = opendir('.')) {
while (false !== ($entry = readdir($handle))) {
if ($entry != "." && $entry != "..") {
echo "$entry\n";
}
}
closedir($handle);
}
so what you need to do is to exclude the . and .. from the output.

Another solution is to use a FilesystemIterator like this:
foreach(new FilesystemIterator('upload') as $file){
echo $file;
}
It will automatically skip . and .. entries in the filesystem.

Related

How to display images found in a directory and using name info in their filename

Lets say I have 4 jpg files in a directory that will only ever contain jpg files. The number of files may change, the naming convention will always be Name-file.jpg
Bob-file.jpg
Tom-file.jpg
Dave-file.jpg
Douglas-file.jpg
I am trying to come up with a php script that will look at the directory and display each image (as in viewing the picture, not just listing the filename) in the web browser and also add the persons name along side it so I can see who it is.
I have tried playing with arrays and loops but can't get anything remotely close (I'm new to PHP)
Thanks!!
You'll want something roughly along these lines. Using a directory iterator to get all the filenames and then using a regular expression to extract the name from the filename.
foreach ((new DirectoryIterator('./path/to/image/dir')) as $file) {
if (!$file->isDot() && preg_match('#^(.+)\-file\.jpg$#uD', $file->getFilename(), $details) === 1) {
printf('<div><img src="%s" alt="Picture of %s">%s</div>',
htmlentities( '/web/path/to/images/' . $details[0] ),
htmlentities( $details[1] ),
htmlentities( $details[1] )
);
}
}
<?php
$directory_to_read_from = './imgage_directory';
if ($handle = opendir($directory_to_read_from))
{
while (false !== ($entry = readdir($handle)))
{
if ($entry != "." && $entry != "..")
{
$name = explode('-', $entry);
echo '<h1>' . $name[0] . "</h1><img src='$directory_to_read_from/$entry' /><br>";
}
}
closedir($handle);
}
OUTPUT:

Display and delete files from subfolders based on select value

My folder structure has 4 layers with my form in the top layer, currently it displays the files in the top layer only, I want to be able to select a subfolder and display the files in it so they can be deleted if necessary.
Produce
Produce/Meat
Produce/Meat/Beef
Produce/Meat/Beef/Portions
Produce/Meat/Beef/Packaged
Produce/Vegtables
Produce/Vegetables/Fresh
Produce/Vegetables/Fresh/Local etc,.
My form displays the contents of the folder it is in with checkboxes, I can then tick boxes and delete files, but I have added a select and want to be able to display the contents of the selected subfolder and delete files. I made two submit buttons and both work, but the delete feature only works if it's in the top folder.
if ($_POST['delete'] == 'Submit')
{
foreach ((array) $_POST['select'] as $file) {
if(file_exists($file)) {
unlink($file);
}
elseif(is_dir($file)) {
rmdir($file);
}
}
}
$files = array();
$dir = opendir('.');
while(false != ($file = readdir($dir))) {
if(($file != ".") and ($file != "..")and ($file != "error_log")) {
$files[] = $file;
}
}
if ($_POST['action'] == 'Change') {
if($_POST['folder'] == 'AAA'){
$files = array();
$dir = opendir('/home/mysite/public_html/Produce/Vegetables/');
while(false != ($file = readdir($dir))) {
if(($file != ".") and ($file != "..")) {
$files[] = $file;
}
}
}
if($_POST['folder'] == 'BBB'){
$files = array();
$dir = opendir('/home/mysite/public_html/Produce/Meat');
while(false != ($file = readdir($dir))) {
if(($file != ".") and ($file != "..")) {
$files[] = $file;
}
}
}
}
natcasesort($files);
?>
<form id="delete" action="" method="POST">
<?php
echo '<table><tr>';
for($i=0; $i<count($files); $i++) {
if ($i%5 == 0) {
echo '</tr>';
echo '<tr>';
}
echo '<td style="width:180px">
<div class="select-all-col"><input name="select[]" type="checkbox" class="select" value="'.$files[$i].'"/>
'.$files[$i].'</div>
<br />
</td>';
}
echo '</table>';
?>
</table>
<br>
Choose a folder:
<select name="folder"><option value="this" selected>This folder</option><option value="BBB">Meat</option><option value="CCC">Meat/Beef</option><option value="DDD">Meat/Beef/Portions</option><option value="EEE">Meat/Beef/Packaged</option><option value="FFF">Vegetables</option><option value="GGG">Vegetables/Fresh</option><option value="HHH">Vegetables/Fresh/Local</option><option value="III">Vegetables/Fresh/Packaged</option></select>
<br>
<input class="button" type="submit" form="delete" name="action" value="Change"><br>
<button type="submit" form="delete" value="Submit">Delete File/s</button>
</form><br>
How can I utilise the selected value to accomplish this?
First, I'd like to address why you are unable to delete files outside of the top folder. You never change the "current working directory" so calling the deleting functions on deep files will never work as intended and could delete the files in the top folder. To correct this, you will either need to include the path with each file/directory to be deleted or call chdir() once so that unlink() and rmdir() are looking in the right place.
I believe your project still has some natural maturing to do including security and UX. I'll provide a generalized/simple snippet for you to consider/compare against your project to hopefully give you a bit more traction in your development.
Your users will be able to make one of two choices on submission: Change Directory & Remove Files/Directories
For the directory change, your program will need to submit two necessary pieces of information:
The action (action="change")
The new folder (newfolder={variable})
For the file/directory deletion, there will be three necessary pieces of information:
The action (action="delete")
The files/directory (files[]={variable})
The directory to access (folder={variable}) * the value in the <select> cannot be trusted, because a user could change the selected value before selecting files in the current directory for deletion. This value must be statically preserved.*Note, you could just add the paths to the filenames in the checkbox values and eliminate the hidden input -- this will be a matter of programming preference.
Purely for demonstration purposes, I'll reference this static array of folders in my code:
$valid_folders=[
'Produce',
'Produce/Meat',
'Produce/Meat/Beef',
'Produce/Meat/Beef/Portions',
'Produce/Meat/Beef/Packaged',
'Produce/Vegetables',
'Produce/Vegetables/Fresh',
'Produce/Vegetables/Fresh/Local',
'Produce/Vegetables/Fresh/Packaged'
];
In reality, you'll probably want to generate an array of valid/permitted/existing folders. I might recommend that you have a look at this link: List all the files and folders in a Directory with PHP recursive function
if(isset($_POST['action'])){ // if there is a submission
if($_POST['action']=="Delete"){ // if delete clicked
if(in_array($_POST['folder'],$valid_folders)){
$folder=$_POST['folder']; // use valid directory
}else{
$folder=$valid_folders[0]; // set a default directory
}
chdir($folder); // set current working directory
//echo "<div>",getcwd(),"</div>"; // confirm directory is correct
foreach($_POST['files'] as $file){ // loop through all files submitted
if(is_dir($file)){ // check if a directory
rmdir($file); // delete it
}else{ // or a file
unlink($file); // delete it
}
}
}elseif($_POST['action']=="Change"){ // if change clicked
if(in_array($_POST['newfolder'],$valid_folders)){ // use valid new directory
$folder=$_POST['newfolder'];
}else{
//echo "Sorry, invalid folder submitted";
$folder=$valid_folders[0]; // set a default directory
}
}
}else{
$folder=$valid_folders[0]; // no submission, set a default directory
}
$dir = opendir("/{$folder}"); // set this to whatever you need it to be -- considering parent directories
//echo "Accessing: /$folder<br>";
while(false!=($file=readdir($dir))){
if(!in_array($file,['.','..','error_log'])){ // deny dots and error_log; you should also consider preventing the deletion of THIS file as well! Alternatively, you could skip this iterated condition and filter the $files array after the loop is finished.
$files[] = $file;
}
}
natcasesort($files);
echo "<form action=\"\" method=\"POST\">";
echo "<select name=\"newfolder\">";
//echo "<option value=\"\">Select a folder</option>"; // this isn't necessary if the neighboring button is descriptive
foreach($valid_folders as $f){
echo "<option",($folder==$f?" selected":""),">{$f}</option>"; // if a previously submitted directory, show it as selected
}
echo "</select> ";
echo "<button name=\"action\" value=\"Change\">Change To Selected Folder</button>";
echo "<br><br>";
echo "Delete one or more files:";
echo "<table><tr>";
for($i=0,$count=sizeof($files); $i<$count; ++$i){
if($i!=0 && $i%5==0){ // see the reason for this change # https://stackoverflow.com/questions/43565075/new-containing-div-after-every-3-records/43566227#43566227
echo "</tr><tr>";
}
echo "<td style=\"width:180px;\">";
echo "<div><input name=\"files[]\" type=\"checkbox\" value=\"{$files[$i]}\">{$files[$i]}</div>";
echo "</td>";
}
echo "</tr></table>";
echo "<input type=\"hidden\" name=\"folder\" value=\"{$folder}\">"; // retain current directory
echo "<button name=\"action\" value=\"Delete\">Delete Checked File(s)</button>";
echo "</form>";
As for form structure, you could implement <input type="submit"> or <button> to submit the form. I won't discuss the caveats for this question.
You see, in the form, $folder is a value that invisibly passed with the submission. This stops the user from moving to an unintended directory when deleting files.
When action=Delete then $folder and $files are used for processing.When action=Change only newfolder is used for processing.
When there is no action a default folder is declared and files will be listed.

Unexplained GET errors

I have code that reads the images directory for a user (user 38 below) and returns an array of the file names, skipping the . and .. references.
// $dir = 38/images
$dirHandle = opendir($dir)$dirHandle = opendir($dir)
while (false !== ($fileName = readdir($dirHandle))) {
if ($fileName == "." || $fileName == "..")
continue;
-- Put file on array which gets returned to ajax load call at end --
}
This works fine but it seems to generate the access errors shown below:
Am I doing something fundamentally wrong?
Thanks
Unless you have an index.php file in your 38 and 38/images folders, you are issuing a get over a folder, over which you don't have permissions enough.
Check your script path, and your JS code in order to fix it.
I got to the bottom of this. It happens when a directory of images is being prefetched to the page:
while($fileName = readdir($dirHandle)) {
$filepath = $dir . $fileName;
echo ("<img class='galleryThumb' src='$filepath' >");
}
The trouble occurs when $fileName is "." or "..". The <img class='galleryThumb' src='$filepath' > echoed down with Ajax then has trouble evaluating a src attribute that's a directory rather than a file. I fixed it by adding a check for "." and ".." :
while($fileName = readdir($dirHandle)) {
if ($fileName == "." || $fileName == "..") {
continue;
}
$filepath = $dir . $fileName;
echo ("<img class='galleryThumb' src='$filepath' >");
}
Since you see 403 errors from network panel of javascript debugger, it is javascript, who is accesing these paths. The php code you posted has almost nothing to do with that.

PHP Reading Directories

So I'm going through reading and writing to files in PHP via PHP Docs and there's an example I didn't quite understand:
http://php.net/manual/en/function.readdir.php
if toward the end it shows an example like this:
<?php
if ($handle = opendir('.')) {
while (false !== ($entry = readdir($handle))) {
if ($entry != "." && $entry != "..") {
echo "$entry\n";
}
}
closedir($handle);
}
?>
In what case would . or .. ever be read?
The readdir API call iterates over all of the directories. So assuming you loop over the current directory (denoted by ".") then you get into an endless loop. Also, iterating over the parent directory (denoted by "..") is avoided to restrict the list to the current directory and beneath.
Hope that helps.
If you want to read directories using PHP, I would recommend you use the scandir function. Below is a demonstration of scandir
$path = __DIR__.'/images';
$contents = scandir($path);
foreach($contents as $current){
if($current === '.' || $current === '..') continue ;
if(is_dir("$path/$current")){
echo 'I am a directory';
} elseif($path[0] == '.'){
echo "I am a file with a name starting with dot";
} else {
echo 'I am a file';
}
}
Because in a UNIX filesystem, . and .. are like signposts, as far as I know. Certainly to this PHP function, anyway.
Keep them in there, you'll get some weird results (like endless loops, etc.) otherwise!
In *nix . is the present working directory and .. is the directory parent. However any file or directory preceded by a '.' is considered hidden so I prefer something like the following:
...
if ($entry[0] !== '.') {
echo "$entry\n";
}
...
This ensures that you don't parse "up" the directory tree, that you don't endlessly loop the present directory, and that any hidden files/folders are ignored.

Dynamic link created based on new file upload?

Every 72 hours I upload a new PHP file to my server. (well actually it is an xml file transformed on the server with php) Is there a method to create a link on an html page that links to the "new" PHP doc automatically everytime a new file is uploaded?
I don't want to manually change the link every 72 hours. I would ultimately like to have an html page with a list of links to every new doc that is uploaded. I found this for images but I need someting like this but for PHP files and links.
http://net.tutsplus.com/articles/news/scanning-folders-with-php/
Any help would be very appreciated.
I found a solution that add links to the xml files. Now I just need to figure out how to add a link to reference the xslt sheet for each new xml file that is upload AUTOMATICALLY. I am not sure how to do this but any help would be very helpful. Thanks for everyones help.
<?php
$count = 0;
if ($handle = opendir('.')) {
while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != "..") {$count++;
print("".$file."<br />\n");
}
}
echo '<br /><br />Return';
closedir($handle);
}
?>
To read in a directory of files and then sort them by upload time you can just use:
$files = glob("files/*.xml");
$files = array_combine($files, array_map("filemtime", $files));
arsort($files);
print "link: " . current($files); // make that an actual <a href=
You can do that pretty easily with PHP function readdir:
http://php.net/manual/en/function.readdir.php
Simply loop through the files in the directory where you upload files and have php output a link for each.
ie:
<?php
if ($handle = opendir('/path/to/upload_dir')) {
while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != "..") {
echo '' . $file . '<br />';
}
}
closedir($handle);
}
?>
You'll need to edit the http:// URL on the href to point to the correct download URL for your server, as well as the server path for opendir.
Hope that helps.
list by filetype
<?php
if ($handle = opendir('/path/to/dir')) {
while (false !== ($file = readdir($handle))) {
if (strpos($file, '.php',1)||strpos($file, '.xml',1) ) {
echo "<p>$file</p>";
}
}
closedir($handle);
}

Categories