is_readable
Checks whether a file or directory exists and can be opened for reading by the current process.
is_readable(string $filename): bool- Added in PHP 4.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$filename | string | Path to the file or directory to test. Stream wrapper URLs are supported if the wrapper implements a stat handler. |
Return value
booltrue if the path exists and is readable by the running process, false otherwise.
Errors
Does not throw. It returns false rather than warning when the path is missing, but an open_basedir violation emits an E_WARNING.
is_readable() asks the operating system whether the process running PHP — typically the web server user such as www-data, not your login account — could open the given path for reading. It answers true for directories as well as regular files, and false when the path does not exist, so it also works as an existence check.
When to reach for it
Reach for it when you want to skip or report a file gracefully instead of letting a warning leak into the output: walking a directory of config fragments where some may be locked down, or turning a missing template into a clear message like "templates/mail.html is not readable by the web server". It is also useful in health-check scripts that verify deployment permissions before the application starts serving traffic.
What trips people up
The check is advisory, not a lock. Between is_readable() returning true and your fopen() call, the file can be deleted or its permissions changed — a TOCTOU race. In code that is about to read the file anyway, calling fopen() (or file_get_contents()) with @ and testing the return value is both faster and correct. Results also come from PHP's stat cache; if something else in the same request changed permissions, call clearstatcache() first.
Example
When to use it
- Skip config fragments in a conf.d directory that the web server user cannot open, instead of emitting warnings.
- Produce a readable deployment error when an .env or credentials file was uploaded with the wrong owner.
- Validate a user-supplied template path (after realpath() containment) before including it.
Frequently asked questions
What is the difference between is_readable and file_exists?
file_exists() only reports that the path is there; is_readable() additionally checks that the current process has permission to open it. A file owned by root with mode 0600 makes file_exists() return true and is_readable() return false.
Why does is_readable return false for a file I can clearly read?
PHP runs as the web server user, not as you, so the relevant permissions are the server's. open_basedir restrictions, SELinux or AppArmor policies, and a missing execute bit on a parent directory will all produce false even when the file itself is world-readable.
Does is_readable work on directories?
Yes, and it returns true for any directory the process can list. If you want to reject directories, test is_file() as well.
More examples
Prefer opening over checking
The output is the file's first line. When you are going to read the file immediately, opening it and testing the handle avoids the gap between the permission check and the read, and costs one syscall instead of two.
<?php
file_put_contents('config.json', '{"debug":true}');
$fh = @fopen('config.json', 'r');
if ($fh === false) {
echo "cannot read config.json\n";
} else {
echo fgets($fh);
fclose($fh);
}
unlink('config.json');Report which paths a script can actually read
The current directory and the running script report as readable, the missing file does not. This is the shape of a deployment health check: list the paths the app needs and print a verdict for each.
<?php
$paths = ['.', __FILE__, 'no-such-file.txt', 'php://memory'];
foreach ($paths as $p) {
printf("%-20s %s\n", $p, is_readable($p) ? 'readable' : 'NOT readable');
}Require a readable regular file
The first call prints the log line; the second throws because '.' is a directory. Pairing is_file() with is_readable() is what most callers actually mean.
<?php
function requireReadableFile(string $path): string {
if (!is_file($path) || !is_readable($path)) {
throw new RuntimeException("Not a readable file: $path");
}
return file_get_contents($path);
}
file_put_contents('app.log', "started\n");
echo requireReadableFile('app.log');
try { requireReadableFile('.'); } catch (RuntimeException $e) { echo $e->getMessage(), "\n"; }
unlink('app.log');
Discussion