readdir() in php not seeming to work - php

I'm working on the following piece of code:
<?php
$dir=opendir("docs/recipes");
$files=array();
while (($file=readdir($dir)) !== false)
{
if ($file != "." and $file != ".." and $file != "index.php")
{
array_push($files, $file);
}
}
closedir($dir);
sort($files,SORT_STRING | SORT_FLAG_CASE);
print "<div class=\"blocktext\">";
foreach ($files as $file)
print "$file<br>";
print "</div>";
?>
I had this working on my raspberry pi web server, but I moved my server to arch linux, and it doesn't seem to be working. When I load the page, it spins but then the list of files is empty.
I have checked that httpd is running with systemd and because my webpages load. I know php is working because my phpinfo test page works.
In the folder containing this file, I have a symbolic link called docs, and I know the path is correct. I have all the files in the destination readable. This file is executable.
Is there something else I am missing?

Not necessarily a solution, but some help with good practices and a way to find what is happening on your script
<?php
// initialize variables first
$dir = false;
$files = array();
// assign the handler
$dir = opendir( "docs/recipes" );
// work only if necessary
if ( $dir !== false ) {
while ( ( $file = readdir( $dir ) ) !== false ) {
if ($file != "." and $file != ".." and $file != "index.php") {
array_push($files, $file);
}
}
closedir( $dir );
sort( $files,SORT_STRING | SORT_FLAG_CASE );
print "<div class=\"blocktext\">";
foreach ( $files as $file ) {
print "$file<br>";
}
print "</div>";
} else {
// get informed that something is wrong, print anything that helps you
echo( 'problems opening' );
exit( __FILE__.' '.__LINE__ );
}
?>

from php.net/readdir comes a warning: This function may return Boolean FALSE, but may also return a non-Boolean value which evaluates to FALSE.
you have not specified which version of PHP you are using, nor if it's installed/compiled locally. it would help. my guess is that you don't have all errors turned on, and readdir is catching one of those corner cases.
try this:
$cycle = true;
while ( $cycle !== false ) {
$file = readdir( $dir );
print ( "GOT a FILe or a FALSE:". $file ."br>" );
// do your stuff here
$cycle = $file;
}
then in php.ini set the errors to E_ALL. if using PHP >= 5.4 (i guess you are, since you're on arch :) you can fire up the built-in server, so all errors/logs/messages go straight to STDERR. just do a command from the folder where your index.php is located:
$ php -S <local ip>:<some port>
open as usual in the browser (or a curl, or whatever). output goes to browser, errors go straight to console, no middle-man to cover up any message.
this should enable you to catch the real problem. and/or, you can wrap it all up in a try-catch and see if something pops up.

Related

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.

is_dir() fails while scanning a directory in php

I'm trying to build a <ul> list with a <li> for each directory in a main folder.
here's my code :
<?php
$dir = opendir(getEnv("DOCUMENT_ROOT") . "/folder");
while(($file = readdir($dir)) !== false ){
if( is_dir($file) && $file !== "." && $file !== ".." ){
echo "<li>" . $file . "</li>";
}
}
closedir($dir);
?>
there are two directories in /home/example/folder but there are not recognized as folders (for sure they are !)
if I "echo" the files in the while loop they are printed (they well exist for the script no trouble on that side).
If I try to "filetype" them, a lstat failed error is thrown, I searched on internet the meaning of it and I end up with nothing but technically support that I pain to understand.
Your problem is that inside "folder" directory you can have two directories (a and b),then the reading retrieve "a" and "b" as file names, while is_dir receive a full path filename or a relative filename, you have two options:
pass the full path to filename to the is_dir function (see example code).
pass the relative path (depends on where you put your script).
if( is_dir($dir . "/" . $file) && $file !== "." && $file !== ".." ){ ......
Try:
while($file = readdir($dir)) {
if (...) { }
}
The !== false portion is not required. if readdir reaches the end, it returns a false, and the loop will automatically terminate. Your version is being parsed as:
$file gets the value of (readdir($file) is not false)
e.g. you're assigning the boolean result of readdir($file) !== false, not the return value from readdir.
to expand on my comment below, regarding operator precedence:
PHP's parse tree of your loop setup looks like this, expanded:
$bool = (readdir($dir) !== false);
$file = $bool;
To fix this, either remove the !== false portion entirely, or enforce your own precedence with extra brackets:
($file = readdir($dir)) !== false

PHP readdir( ) returns " . " and " .. " entries

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
}

How to check if directory contents has changed with PHP?

I'm writing a photo gallery script in PHP and have a single directory where the user will store their pictures. I'm attempting to set up page caching and have the cache refresh only if the contents of the directory has changed. I thought I could do this by caching the last modified time of the directory using the filemtime() function and compare it to the current modified time of the directory. However, as I've come to realize, the directory modified time does not change as files are added or removed from that directory (at least on Windows, not sure about Linux machines yet).
So my questions is, what is the simplest way to check if the contents of a directory have been modified?
As already mentioned by others, a better way to solve this would be to trigger a function when particular events happen, that changes the folder.
However, if your server is a unix, you can use inotifywait to watch the directory, and then invoke a PHP script.
Here's a simple example:
#!/bin/sh
inotifywait --recursive --monitor --quiet --event modify,create,delete,move --format '%f' /path/to/directory/to/watch |
while read FILE ; do
php /path/to/trigger.php $FILE
done
See also: http://linux.die.net/man/1/inotifywait
What about touching the directory after a user has submitted his image?
Changelog says: Requires php 5.3 for windows to work, but I think it should work on all other environments
with inotifywait inside php
$watchedDir = 'watch';
$in = popen("inotifywait --monitor --quiet --format '%e %f' --event create,moved_to '$watchedDir'", 'r');
if ($in === false)
throw new Exception ('fail start notify');
while (($line = fgets($in)) !== false)
{
list($event, $file) = explode(' ', rtrim($line, PHP_EOL), 2);
echo "$event $file\n";
}
Uh. I'd simply store the md5 of a directory listing. If the contents change, the md5(directory-listing) will change. You might get the very occasional md5 clash, but I think that chance is tiny enough..
Alternatively, you could store a little file in that directory that contains the "last modified" date. But I'd go with md5.
PS. on second thought, seeing as how you're looking at performance (caching) requesting and hashing the directory listing might not be entirely optimal..
IMO edubem's answer is the way to go, however you can do something like this:
if (sha1(serialize(Map('/path/to/directory/', true))) != /* previous stored hash */)
{
// directory contents has changed
}
Or a more weak / faster version:
if (Size('/path/to/directory/', true) != /* previous stored size */)
{
// directory contents has changed
}
Here are the functions used:
function Map($path, $recursive = false)
{
$result = array();
if (is_dir($path) === true)
{
$path = Path($path);
$files = array_diff(scandir($path), array('.', '..'));
foreach ($files as $file)
{
if (is_dir($path . $file) === true)
{
$result[$file] = ($recursive === true) ? Map($path . $file, $recursive) : $this->Size($path . $file, true);
}
else if (is_file($path . $file) === true)
{
$result[$file] = Size($path . $file);
}
}
}
else if (is_file($path) === true)
{
$result[basename($path)] = Size($path);
}
return $result;
}
function Size($path, $recursive = true)
{
$result = 0;
if (is_dir($path) === true)
{
$path = Path($path);
$files = array_diff(scandir($path), array('.', '..'));
foreach ($files as $file)
{
if (is_dir($path . $file) === true)
{
$result += ($recursive === true) ? Size($path . $file, $recursive) : 0;
}
else if (is_file() === true)
{
$result += sprintf('%u', filesize($path . $file));
}
}
}
else if (is_file($path) === true)
{
$result += sprintf('%u', filesize($path));
}
return $result;
}
function Path($path)
{
if (file_exists($path) === true)
{
$path = rtrim(str_replace('\\', '/', realpath($path)), '/');
if (is_dir($path) === true)
{
$path .= '/';
}
return $path;
}
return false;
}
Here's what you may try. Store all pictures in a single directory (or in /username subdirectories inside it to speed things up and to lessen the stress on the FS) and set up Apache (or whaterver you're using) to serve them as static content with "expires-on" set to 100 years in the future. File names should contain some unique prefix or suffix (timestamp, SHA1 hash of file content, etc), so whenever uses changes the file its name gets changed and Apache will serve a new version, which will get cached along the way.
You're thinking the wrong way.
You should execute your directory indexer script as soon as someone's uploaded a new file and it's moved to the target location.
Try deleting the cached version when a user uploads a file to his directory.
When someone tries to view the gallery, look if there's a cached version first. If there's a cached version, load it, otherwise, generate the page, cache it, done.
I was looking for something similar and I just found this:
http://www.franzone.com/2008/06/05/php-script-to-monitor-ftp-directory-changes/
For me looks like a great solution since I'll have a lot of control (I'll be doing an AJAX call to see if anything changed).
Hope that this helps.
Here is a code sample, that would return 0 if the directory was changed.
I use it in backups.
The changed status is determined by presence of files and their filesizes.
You could easily change this, to compare file contents by replacing
$longString .= filesize($file);
with
$longString .= crc32(file_get_contents($file));
but it will affect execution speed.
#!/usr/bin/php
<?php
$dirName = $argv[1];
$basePath = '/var/www/vhosts/majestichorseporn.com/web/';
$dataFile = './backup_dir_if_changed.dat';
# startup checks
if (!is_writable($dataFile))
die($dataFile . ' is not writable!');
if (!is_dir($basePath . $dirName))
die($basePath . $dirName . ' is not a directory');
$dataFileContent = file_get_contents($dataFile);
$data = #unserialize($dataFileContent);
if ($data === false)
$data = array();
# find all files ang concatenate their sizes to calculate crc32
$files = glob($basePath . $dirName . '/*', GLOB_BRACE);
$longString = '';
foreach ($files as $file) {
$longString .= filesize($file);
}
$longStringHash = crc32($longString);
# do changed check
if (isset ($data[$dirName]) && $data[$dirName] == $longStringHash)
die('Directory did not change.');
# save hash do DB
$data[$dirName] = $longStringHash;
file_put_contents($dataFile, serialize($data));
die('0');

PHP readdir is not reading some files

I'm using the following code to loop through a directory to print out the names of the files. However, not all of the files are displayed. I have tried using clearstatcache with no effect.
$str = '';
$ignore = array('.', '..');
$dh = #opendir( $path );
if ($dh === FALSE)
{
// error
}
$file = readdir( $dh );
while( $file !== FALSE )
{
if (in_array($file, $ignore, TRUE)) { break; }
$str .= $file."\n";
$file = readdir( $dh );
}
Here's the contents of the directory right now:
root.auth test1.auth test2.auth test3.auth test5.auth
However, test5.auth does not appear. If I rename it to test4.auth it does not appear. If I rename it to test6.auth it does appear. This is reliable behaviour - I can rename it several times and it still won't show up unless I rename it to test6.auth.
What on earth could be happening?
I'm running Arch Linux (kernel 2.6.26-ARCH) with PHP Version 5.2.6 and Apache/2.2.9 with Suhosin-Patch. My filesystem is ext3 and I'm running fam 2.6.10.
Continue won't work either, because you will skip the line that reads the next file.
You could get rid of the first $file = readdir( $dh ); and then do
while (false !== ($file = readdir($dh))) {
if (in_array($file, $ignore, TRUE)) { continue; }
$str .= $file."\n";
}
Your break keywords messes up your code:
Your loop very likely first encounters the '.' directory and than breaks out of your while loop.
try replacing it with a continue and you should be fine.
if (in_array($file, $ignore, TRUE)) { break; }
Surely that should be continue not break?

Categories