Issues

ZF-6833: Zend_Loader::isReadable() returns false when valid file is in same directory but . is not in include_path

Description

First of all I guess I should apologise for posting two bug reports in one. They are very related though, so I hope it makes sense.

Method isReadable() in Loader.php will incorrectly return false when checking for a file in the same directory as the file calling isReadable() if "." is not part of the include_path (bug #1).

This could be fixed by adding "if (is_readable($filename)) return true;" before the call to fopen().

My suggested solution however, is to replace the entire method isReadable() with a new one using is_readable() instead of fopen(), which triggers a suppressed PHP warning if the file does not exist (bug #2).

My motivation for changing this function is that it triggers the error handler if the tested file is not found. The warning is suppressed so normally it won't show up anywhere, but I'm using a custom error handler and although I know how to ignore suppressed errors I can't do that. My custom error handler handles suppressed errors because they are often useful when dealing with real problems that are not application errors (for example to see the reason why a database connection failed, or why an xml parser could not parse). The suppressed error caused by Zend_Loader::isReadable() is not useful at all though (the warning reads "failed to open stream: No such file or directory", but we only need a true or false), and it should be unnecessary for a function that finds if a file is readable to cause a warning if the file is not readable. As it is, this is a huge annoyance because it causes Zend Framework to fill my error log with useless data and I can't work around it in my application.

You could argue that this is just my problem, and I guess it's not unlikely that I'm the only one with this problem right now, but I see no reason not to make the changes I suggest since both functionality and performance are the same as before, except you also get the added feature that isReadable() can be used with directories, just like the in-built PHP function is_readable().

As a sidenote, I think that this functionality really should be an integral part of PHP. If you agree, please consider voting for this 9 year old PHP feature request: http://bugs.php.net/bug.php?id=6932.

In the meantime we have to work around it. Here's my suggestion:


public static function isReadable($filename)
{
    if (($filename = (string)$filename) === '') return false;
    if (is_readable($filename)) return true;
    foreach (explode(PATH_SEPARATOR, get_include_path()) as $path)
    {
        if (is_readable($path . DIRECTORY_SEPARATOR . $filename)) return true;
    }
    return false;
}

I've spent quite a few hours testing and comparing the performance of both the existing and my suggested function, and my conclusion is that this new method performs just as well as the old one, even though it involves parsing include_path and looping through the directories. My assumption as to why fopen() does not perform much better is that it must return a file handle, but this is just a wild guess. Also, fopen() performs really bad compared to is_readable() when the file does not exist, probably since it has to trigger a warning as well.

Comments

Rewrote large parts of the original report in order to make its purpose more clear and friendly to whoever ends up reviewing it some day. Removed code to test performance because it took too much focus from the issue. If you're interested in that test code please let me know and I'll send it to you.

Resolved in version 1.10.1 see r20903.