Monday, March 14, 2011

PHP case insensitive file_exists

We know that PHP's file_exists is case sensitive on *nix system. However, i've been through a situation that i need this function case insensitive. For example, there exists a file: /var/www/project/public/images/company/logo.png. In Url it will be http://project.com/images/company/logo.png. Now, if the html code is < img src = "http://project.com/images/Company/logo.PNG" > or even < img src = "images/Company/Logo.png" >, it works fine on windows. But on linux, it won't work. My task is, i need to allow users to export some special html pages in a zip archive so that they can browse them offline. If the html pages contain image links, the images will be exported too if they exists. Well, as you can imagine, it is pretty much like the "Save as..." function when you right click your mouse on a web page.

I can't find a perfect solution. Some solutions only allow you to have file name case insensitive, and you still have to ensure the directory name of the file must be correct in case sensitive way. Some solutions use customized glob() functions that i can't easily understand. So i finally decided to implement my own solution based on my situation.

Here is my thought: some specific path is defined in your config file and you can never get it wrong. For example, you may have config settings like this:
//paths
$conf['root_dir'] = dirname(__FILE__) . '/..';
$conf['public'] = $conf['root_dir'] . '/public';
$conf['images'] = $conf['public'] . '/images';
So, the only uncertain part is the sub folders and files under the 'images' folder. Here is how i implement my case insensitive file_exists():
//$filename is the full path file name, like /var/www/project/public/images/Company/Logo.PNG
//$baseDir is the base directory that you can't get it wrong, here it might be /var/www/project/public/images/
//return: the function return false if file NOT exists,
//it will return the correct case sensitive path if the file exists in case insensitive way.

function caseInsensitiveFileExists($filename, $baseDir)
{
//well if file_exists() can find the $filename, we can return
if(file_exists($filename) {
return $filename;
}
//we only do this on *nix system, on windows, we don't have to worry about case sensitive issue.
if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($baseDir));
foreach ($iterator as $value) {
if ($value->isFile()) {
$filePath = $value->getPathname();
//if they are equals, we know the $filename exists.
if (strotolower(filePath) == strtolower($filename)) {
return filePath;
}
}
}
}
return false;
}


How is this function's performance if we have huge number of files and folders under $baseDir? I haven't really tested this issue yet. At the moment this function serves my purpose very well. However, i'm looking forward to any other decent implementation.