When displaying images on our website, we check if the file exists with a call to file_exists()
. We fall back to a dummy image if the file was missing.
Howe
Use absolute paths! Depending on your include_path
setting PHP checks all(!) these dirs if you check relative file paths! You might unset include_path
temporarily before checking the existence.
realpath()
does the same but I don't know if it is faster.
But file access I/O is always slow. A hard disk access IS slower than calculating something in the processor, normally.
Benchmarks with PHP 5.6:
0.0012969970 : stream_resolve_include_path + include
0.0013520717 : file_exists + include
0.0013728141 : @include
0.0000281333 : file_exists + include
0.0000319480 : stream_resolve_include_path + include
0.0001471042 : @include
0.0000281333 : file_exists + include
0.0000360012 : stream_resolve_include_path + include
0.0001239776 : @include
Code:
// microtime(true) is less accurate.
function microtime_as_num($microtime){
$time = array_sum(explode(' ', $microtime));
return $time;
}
function test_error_suppression_include ($file) {
$x = 0;
$x = @include($file);
return $x;
}
function test_file_exists_include($file) {
$x = 0;
$x = file_exists($file);
if ($x === true) {
include $file;
}
return $x;
}
function test_stream_resolve_include_path_include($file) {
$x = 0;
$x = stream_resolve_include_path($file);
if ($x !== false) {
include $file;
}
return $x;
}
function run_test($file, $test_name) {
echo $test_name . ":\n";
echo str_repeat('=',strlen($test_name) + 1) . "\n";
$results = array();
$dec = 10000000000; // digit precision as a multiplier
$i = 0;
$j = 0;
$time_start = 0;
$time_end = 0;
$x = -1;
$time = 0;
$time_start = microtime();
$x= test_error_suppression_include($file);
$time_end = microtime();
$time = microtime_as_num($time_end) - microtime_as_num($time_start);
$results[$time*$dec] = '@include';
$i = 0;
$j = 0;
$time_start = 0;
$time_end = 0;
$x = -1;
$time = 0;
$time_start = microtime();
$x= test_stream_resolve_include_path_include($file);
$time_end = microtime();
$time = microtime_as_num($time_end) - microtime_as_num($time_start);
$results[$time * $dec] = 'stream_resolve_include_path + include';
$i = 0;
$j = 0;
$time_start = 0;
$time_end = 0;
$x = -1;
$time = 0;
$time_start = microtime();
$x= test_file_exists_include($file);
$time_end = microtime();
$time = microtime_as_num($time_end) - microtime_as_num($time_start);
$results[$time * $dec ] = 'file_exists + include';
ksort($results, SORT_NUMERIC);
foreach($results as $seconds => $test) {
echo number_format($seconds/$dec,10) . ' : ' . $test . "\n";
}
echo "\n\n";
}
run_test($argv[1],$argv[2]);
Command line Execution:
php test.php '/path/to/existing_but_empty_file.php' 'Existing File'
php test.php '/path/to/non_existing_file.php' 'Invalid File'
php test.php '/path/invalid/non_existing_file.php' 'Invalid Folder'
file_exists()
should be a very inexpensive operation. Note too that file_exists
builds its own cache to help with performance.
See: http://php.net/manual/en/function.file-exists.php
I don't exactly know what you want to do, but you could just let the client handle it.
You could do a cronjob to periodically create a list of images and store them in DB/file/BDB/...
Every half an hour should be fine, but be sure to create an interface to reset cache in case of file addition/delete.
And then, it's also easy to run find . -mmin -30 -print0 on the shell and add new files.
I find 1/2ms per call very, very affordable. I don't think there are much faster alternatives around, as the file functions are very close to the lower layers that handle file operations.
You could however write a wrapper to file_exists() that caches results into a memcache or similar facility. That should reduce the time to next to nothing in everyday use.