dirname
Returns the parent directory of a path, optionally going up several levels.
dirname(string $path, int $levels = 1): string- Added in PHP 4.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$path | string | A path. On Windows both / and \ act as directory separators; everywhere else only / does. |
$levelsoptional | int= 1 | The number of parent directories to go up. Must be an integer greater than 0. |
Return value
stringThe path of the parent directory. If the path has no separators, a dot is returned. If the path is the root, the root is returned.
Errors
Throws a ValueError when levels is less than 1.
dirname() chops the last component off a path string. Like basename(), it is pure string work — it never checks that the directory exists and never emits a warning.
When to reach for it
The classic use is locating your project root from a file inside it: dirname(__DIR__, 2) reads far better than three stacked calls or a chain of /.. segments that realpath() then has to untangle. It is also how you find the parent of a target path before mkdir()-ing it, and how you build sibling paths next to a config file.
Prefer __DIR__ over dirname(__FILE__); the magic constant is resolved at compile time and does the same thing.
What trips people up
A bare filename has no directory, so dirname("file.txt") is "." — not an empty string. Concatenating blindly gives "./file.txt", which usually still works but looks wrong in logs and breaks string comparisons.
At the root the function is a fixed point: dirname("/") is "/", and asking for more levels than the path has also lands on "/". A loop that climbs by calling dirname() until it "runs out" never terminates — compare against the previous value instead.
Example
When to use it
- Find the project root from a bootstrap file with dirname(__DIR__, 2)
- Create a target file's parent directory with mkdir(dirname($path), 0775, true)
- Build a sibling path such as app.local.ini next to an already-known config file
- Group a list of file paths by the folder they live in
Frequently asked questions
What is the difference between dirname(__FILE__) and __DIR__?
They produce the same string, but __DIR__ is resolved when the file is compiled and costs nothing at runtime. Use __DIR__, and use dirname(__DIR__, 2) when you need to climb from there.
Does dirname check that the directory exists?
No. It is a string operation only, so it happily returns a parent path for a file that was never created. Use is_dir() if you need to know whether the result is real.
How do I get the directory two levels up?
Pass 2 as the second argument: dirname($path, 2). It is equivalent to dirname(dirname($path)) but clearer and slightly faster.
More examples
Climbing several levels at once
Levels 1 to 4 walk up to /srv/app/src/Http, /srv/app/src, /srv/app and /srv. Asking for 10 levels does not error — it clamps at the root and returns /. That clamping is why you cannot detect "I have gone too far" from the return value alone.
<?php
$file = "/srv/app/src/Http/Controller.php";
for ($i = 1; $i <= 4; $i++) {
echo $i, ": ", dirname($file, $i), "\n";
}
echo "way too far: ", dirname($file, 10), "\n";Building sibling and cousin paths
The first path stays in the same folder, the second steps up two levels to /etc/myapp and then down into logs. The final var_dump is the trap: a relative filename with no directory yields a single dot, so concatenation produces ./app.local.ini rather than a bare name.
<?php
$config = "/etc/myapp/conf.d/app.ini";
$sibling = dirname($config) . "/app.local.ini";
$logs = dirname($config, 2) . "/logs";
echo $sibling, "\n";
echo $logs, "\n";
var_dump(dirname("app.ini")); // string(1) "."Creating the parent directory before writing
dirname() gives cache/http, and mkdir()'s third argument creates both levels in one call. This is the standard guard before file_put_contents(), which — like copy() — fails if the parent directory is missing rather than creating it.
<?php
$target = "cache/http/page-1.html";
$dir = dirname($target);
if (!is_dir($dir)) {
mkdir($dir, 0775, true);
}
file_put_contents($target, "<h1>hello</h1>");
echo $dir, " -> ", file_get_contents($target), "\n";
unlink($target);
rmdir("cache/http");
rmdir("cache");
Discussion