fileperms
Returns a file's permission bits and type as a numeric mode integer.
fileperms(string $filename): int|false- Added in PHP 4.0.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$filename | string | Path to the file. |
Return value
int|falseThe file's mode as an integer — permission bits plus file-type bits — or false on failure.
Errors
Emits an E_WARNING and returns false if the stat call fails, for example when the file does not exist.
fileperms returns the raw st_mode value from the operating system's stat() call. That single integer carries two different things: the low 12 bits are the permission bits you would pass to chmod, and the high bits encode the file type.
When to reach for it
- Auditing that config files are not world-readable before an app boots.
- Reporting the current mode in an installer or diagnostics page.
- Comparing a deployed file's mode against an expected value.
What trips people up
The number does not look like 0644 because it is decimal and includes type bits — a regular 0644 file returns 33188. To get the familiar four-digit octal, use substr(sprintf('%o', fileperms($f)), -4). Dropping the substr leaves the type prefix in place, so you see 100644.
To test one permission, mask it: fileperms($f) & 0200 is non-zero when the owner can write. To test the type, mask with 0170000 and compare — 0100000 is a regular file, 0040000 a directory.
Windows does not have a Unix permission model; the values it returns are an approximation and should not drive security decisions. Results are also stat-cached, so call clearstatcache() after a chmod.
If you only want to know whether your process can read or write the path, is_readable() and is_writable() answer that directly and account for the effective user.
Example
When to use it
- Warn during startup if a secrets file is readable by group or other.
- Show the current octal mode of upload directories in a health-check endpoint.
- Verify a deploy script left executable bits on the right scripts.
Frequently asked questions
Why does fileperms return 33188 instead of 644?
33188 is the full `st_mode` in decimal: the permission bits plus the regular-file type marker. Convert it with `substr(sprintf('%o', fileperms($f)), -4)` to get `0644`.
How do I check if a file is writable with fileperms?
Mask the owner-write bit with `fileperms($f) & 0200`, but for a real answer use `is_writable($f)` — it accounts for which user the PHP process is running as, which the raw bits do not.
Does fileperms work on Windows?
It returns a value, but Windows uses ACLs rather than Unix permission bits, so the result is a rough approximation and should not be used for security checks.
More examples
Why the number looks wrong
The full octal starts with `100`, the regular-file type marker, followed by the permission digits. Trimming to the last four characters is what turns the stat value into the `0644`-style mode people expect.
<?php
file_put_contents('modes.txt', 'x');
clearstatcache();
$p = fileperms('modes.txt');
echo "decimal: $p\n";
echo "full octal: ", sprintf('%o', $p), "\n";
echo "just the mode: ", substr(sprintf('%o', $p), -4), "\n";
echo "just the type: ", sprintf('%o', $p & 0170000), "\n";
unlink('modes.txt');Flagging a world-readable secrets file
The bitmasks 0040 and 0004 isolate the group-read and other-read bits. On a typical umask the file is created 0644, so both checks report that others can read it and the warning fires.
<?php
file_put_contents('secrets.env', "API_KEY=abc123\n");
clearstatcache();
$perms = fileperms('secrets.env');
$group = ($perms & 0040) !== 0;
$other = ($perms & 0004) !== 0;
echo "mode: ", substr(sprintf('%o', $perms), -4), "\n";
echo "group readable: ", var_export($group, true), "\n";
echo "other readable: ", var_export($other, true), "\n";
echo ($group || $other) ? "WARNING: tighten this file\n" : "looks fine\n";
unlink('secrets.env');Decoding the file type from the mode
Masking with 0170000 strips the permission bits and leaves only the type field, printing `directory`, `regular file`, `directory`. For readable type names, `filetype()` does the same job without the bit twiddling.
<?php
mkdir('perm-dir');
file_put_contents('perm-file.txt', 'x');
clearstatcache();
foreach (['perm-dir', 'perm-file.txt', '.'] as $path) {
$type = fileperms($path) & 0170000;
$name = match ($type) {
0040000 => 'directory',
0100000 => 'regular file',
default => 'other',
};
echo str_pad($path, 16), $name, "\n";
}
unlink('perm-file.txt');
rmdir('perm-dir');
Discussion