basename
Returns the trailing name component of a path, optionally with a given suffix removed.
basename(string $path, string $suffix = ""): 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. |
$suffixoptional | string= "" | If the name component ends in this string, it is cut off. Matching is exact and case-sensitive. |
Return value
stringThe base name of the given path — everything after the final directory separator, with any trailing separators ignored and the suffix removed if it matched.
Errors
Nothing. It never touches the filesystem and never emits a warning.
basename() is pure string arithmetic. It splits on directory separators and hands back the last piece — it does not stat the path, so it works fine on a path that does not exist, on a URL-shaped string, or on something a user typed.
When to reach for it
Use it to turn a full path into a display name for a log line or a download header, and to strip any directory the user tried to sneak into an uploaded filename. Because basename("../../etc/passwd") is just passwd, it neutralises the directory part of traversal input — though on its own it is not a complete upload defence, since it happily returns .htaccess or shell.php.
What trips people up
The suffix is removed only on an exact match, so basename("IMAGE.PNG", ".png") returns IMAGE.PNG unchanged. If you want the extension gone whatever it is, use pathinfo($path, PATHINFO_FILENAME).
Separator handling is platform-dependent. On Linux and macOS a backslash is an ordinary filename character, so a Windows-style path arriving from a client comes back whole. Normalise with str_replace("\\", "/", $path) first when the path did not come from your own filesystem.
Trailing separators are ignored: basename("/var/log/") is log, not an empty string.
Example
When to use it
- Show just the file name in a log line or an admin table without the storage directory
- Strip any directory the client put in an uploaded file's name before saving it
- Build a Content-Disposition download header from a full disk path
- Turn /srv/reports/2026-q1.csv into the report label 2026-q1 by passing '.csv' as the suffix
Frequently asked questions
What is the difference between basename and pathinfo?
basename() returns only the final path component, while pathinfo() returns an array with the directory, base name, extension and extension-less filename at once. Use pathinfo() when you need the extension removed regardless of what it is, since basename()'s suffix argument only strips an exact, case-sensitive match.
Does basename check whether the file exists?
No. It is a string operation that never touches the filesystem, so it works on paths that do not exist and never emits a warning. Use file_exists() or is_file() if you need to know whether the path is real.
Why does basename return the whole path for a Windows path on Linux?
Backslash is only a directory separator on Windows; on Linux and macOS it is an ordinary character in a filename. Run str_replace("\\", "/", $path) over the string first when the path was produced on a different platform.
More examples
Normalising a Windows path first
On a non-Windows host the first call finds no forward slash, so it returns the entire string C:\Users\ana\photo.jpg. After the backslashes are swapped for forward slashes, the same call correctly returns photo.jpg. This is the usual cause of upload filenames that arrive with the client's whole disk path baked in.
<?php
$win = "C:\\Users\\ana\\photo.jpg";
echo basename($win), "\n";
echo basename(str_replace("\\", "/", $win)), "\n";
$unix = "/home/ana/photo.jpg";
echo basename($unix), "\n";Building labels from a list of paths
The loop prints app, db and cron — the suffix .log is stripped from each. The array_map call uses the first-class callable syntax to build the full base names, showing [app.log, db.log, cron.log]. Note that array_map cannot pass the suffix, so map with an arrow function if you need it.
<?php
$paths = ["/srv/logs/app.log", "/srv/logs/db.log", "/srv/logs/cron.log"];
foreach ($paths as $p) {
echo basename($p, ".log"), "\n";
}
print_r(array_map(basename(...), $paths));Comparing with pathinfo
The second echo shows the case-sensitivity trap: .pdf does not match .PDF, so the name comes back untouched. pathinfo() returns dirname, basename, extension and filename in one array, and PATHINFO_FILENAME drops whatever the extension actually is — invoice.2026 — which is what you usually want when the case is unpredictable.
<?php
$path = "/srv/uploads/invoice.2026.PDF";
echo basename($path), "\n";
echo basename($path, ".pdf"), "\n"; // case-sensitive: no change
print_r(pathinfo($path));
echo pathinfo($path, PATHINFO_FILENAME), "\n";
Discussion