List files and download The following code list all files in a folder and allows the user to download one of them. It is working but I think it could be done better.

Is there a way to reduce the number of forms to only one and to improve the way I retrieve the name of the file to download?

My second concern is about the security of my getFile function.

function listDir($directoryName)
{
    $directory = opendir($directoryName);
    $files = array();
    while ($elem = readdir($directory))
    {
        if ($elem != '.' &amp;&amp; $elem != '..' &amp;&amp; $elem != '.htaccess')
        {
            $files[] = $elem;
        }
    }
    closedir();
    if (!empty($directory))
    {
        sort($files);
        echo "&lt;ul&gt;";
        foreach ($files as $file)
        {
            $filePath = $directoryName.'/'.$file;
            echo "&lt;form method='post' action=".htmlentities($_SERVER['PHP_SELF'])."&gt;";
            echo "&lt;li&gt;($fileDate)";
            echo "&lt;input type='hidden' name='$file' value='$filePath'&gt;";
            echo " &lt;button type='submit' name='getFile'&gt;Télécharger&lt;/button&gt; ";
            echo "$file&lt;/li&gt;";
            echo "&lt;/form&gt;";
        }
        echo "&lt;/ul&gt;";
    }
}

function getFile($file)
{
    if (file_exists($file))
    {
        header('Content-Description: File Transfer');
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename='.basename($file));
        header('Expires: 0');
        header('Cache-Control: must-revalidate');
        header('Pragma: public');
        header('Content-Length: ' . filesize($file));
        ob_clean();
        flush();
        readfile($file);
        exit;
    }
}

if (isset($_POST['getFile']))
{
    $keys = array_keys($_POST);
    $file = $_POST[$keys[0]];
    getFile($file);
}

listDir('data');
Let's post a dry review first, before jumping in to the conceptual stuff:


You are allowing your end-users download any file on the server. All I need to do is to change the path to /etc/passwd on any form and submit, and I get your server's password file! And if I don't have permissions to that, no biggy, I can still download all of your owned files on the server.
Invalid HTML markup - &lt;form&gt; isn't allowed directly inside of &lt;ul&gt;. Only &lt;li&gt; is.
Separate logic and presentation - You shouldn't mix the part where the user sees your form, and the part where he downloads the file.


A better approach?

A better approach would be a PHP file which is capable of serving files. That PHP file needs to make sure that the file being requested is found in a very specific list of allowed directories (i.e. I allow from images/ and from static/ but not from anywhere else.

Then, when you download a file, all you need to do is to link (With an &lt;a&gt;) to

http://example.com/path/to/file_server.php?filename=somefile.jpg


And have the PHP file_server handle the serving of the requested file.

Getting Fancy

You can even use .htaccess and mod_rewrite to rewrite URLs like this:

http://example.com/static/somefile.jpg


to

http://example.com/path/to/file_server.php?filename=somefile.jpg


Making the fact that PHP is the one sending the file completely transparent to the user!