ftp_mlsd()
Learn the PHP ftp_mlsd() function: list an FTP directory in a structured, machine-readable format. Syntax, return values, examples and error handling.
What is ftp_mlsd()?
ftp_mlsd() is a built-in PHP function (available since PHP 7.2) that lists the contents of a directory on an FTP server using the FTP MLSD command. Unlike older listing functions, it returns a structured, machine-readable result: instead of a raw text blob you have to parse yourself, you get an array of entries where each file or directory carries named attributes such as its type, size and modification time.
This matters because directory listings from FTP servers are notoriously inconsistent — ftp_rawlist() returns lines whose format depends on the server's operating system. MLSD was added to the FTP protocol (RFC 3659) precisely to standardize this, and ftp_mlsd() exposes it directly.
Use ftp_mlsd() when you need to:
- Enumerate files and know each one's size, type, or timestamp.
- Reliably tell directories apart from files (the
typeattribute does this for you). - Avoid the brittle string-parsing that
ftp_rawlist()forces on you.
If you only need a flat list of file names, ftp_nlist() is simpler. If you must support pre-7.2 servers or servers without MLSD support, fall back to ftp_rawlist().
Syntax of ftp_mlsd()
ftp_mlsd(FTP\Connection $ftp, string $directory): array|falseThe function takes two required parameters:
| Parameter | Description |
|---|---|
$ftp | The FTP connection identifier returned by ftp_connect() (or ftp_ssl_connect()). |
$directory | The path of the directory to list, e.g. /public_html or . for the current directory. |
Both parameters are required. The MLSD command always operates on an explicit path — pass . if you want the directory you are currently in.
It returns an array of entries on success, or false on failure (for example, if the path does not exist or the server does not support MLSD).
What ftp_mlsd() returns
Each element of the returned array is an associative array describing one entry. The exact keys depend on what the server reports, but the common ones are:
| Key | Meaning |
|---|---|
name | The file or directory name. |
type | The entry type: file, dir, cdir (current dir, .), or pdir (parent dir, ..). |
size | Size in bytes (usually present only for files). |
modify | Last-modified timestamp, formatted as YYYYMMDDHHMMSS. |
perms | Permissions string as reported by the server. |
unique | A server-assigned identifier that is unique per file. |
The shape of a single entry looks like this:
[
'name' => 'report.pdf',
'type' => 'file',
'size' => '20480',
'modify' => '20240115093000', // 2024-01-15 09:30:00
'perms' => 'adfr',
]Usage of ftp_mlsd()
Before listing anything, you connect with ftp_connect() and authenticate with ftp_login(). The full flow looks like this:
<?php
// 1. Open a connection
$ftp = ftp_connect('ftp.example.com');
// 2. Authenticate
ftp_login($ftp, 'username', 'password');
// 3. Switch to passive mode (recommended behind firewalls/NAT)
ftp_pasv($ftp, true);
// 4. Get a structured listing of the directory
$entries = ftp_mlsd($ftp, '/public_html');
// 5. Always close the connection when done
ftp_close($ftp);Iterating over the listing
The real value of ftp_mlsd() is the structured data. Here we print every file with a human-readable size and skip the . and .. pseudo-entries:
<?php
$entries = ftp_mlsd($ftp, '.');
foreach ($entries as $entry) {
// Skip the current-dir and parent-dir markers
if (in_array($entry['type'], ['cdir', 'pdir'], true)) {
continue;
}
if ($entry['type'] === 'dir') {
echo "[DIR] {$entry['name']}\n";
} else {
$kb = round($entry['size'] / 1024, 1);
echo "[FILE] {$entry['name']} ({$kb} KB)\n";
}
}The modify field is a 14-digit timestamp. You can turn it into a real date with DateTime:
<?php
$modified = DateTime::createFromFormat('YmdHis', $entry['modify']);
echo $modified->format('Y-m-d H:i:s');Error handling in ftp_mlsd()
ftp_mlsd() returns false on failure, so always check the result before looping over it — calling foreach on false would emit a warning and process nothing.
<?php
$entries = ftp_mlsd($ftp, '/path/that/may/not/exist');
if ($entries === false) {
echo "Failed to retrieve the directory listing.\n";
} else {
foreach ($entries as $entry) {
echo $entry['name'] . "\n";
}
}Use the strict comparison === false. An empty directory returns an empty array [], which is falsy — a loose if (!$entries) check would wrongly treat an empty (but valid) directory as an error.
Common reasons ftp_mlsd() fails:
- The server does not support the
MLSDcommand (older or minimal FTP servers). Fall back toftp_rawlist(). - The directory path is wrong or you lack permission to read it.
- You are not yet logged in, or the connection has timed out.
ftp_mlsd() vs. other listing functions
| Function | Returns | Best for |
|---|---|---|
ftp_mlsd() | Structured array (name, type, size, modify…) | Reliable metadata across servers (PHP 7.2+) |
ftp_nlist() | Flat array of names | A quick list of file names only |
ftp_rawlist() | Array of raw ls-style lines | Legacy servers without MLSD |
Conclusion
ftp_mlsd() is the modern, dependable way to list an FTP directory in PHP: it hands you a structured array with each entry's type, size, and modification time, sparing you the fragile text-parsing that older functions require. Pair it with ftp_connect(), ftp_login(), and ftp_close() for a complete, robust FTP listing workflow — and remember to check for === false before iterating.