4
\$\begingroup\$

I'm in the process of cross-training myself from CFML to PHP as part of a learning exercise. I'm learning from the docs and online tutorials and googling.

As an initial exercise, I've knocked together the following PHP facsimile of some CFML code I have. I have a blog article explaining the requirement for the code. This is basically a work-around for the fact that the inbuilt PHP web server does not do directory browsing.

I'm just posting the PHP here in case anyone can make observations about how I might improve it. I'm more after PHP-specific info than coding structure "advice", but any observations one might make will be taken on board.

<?php
    // directory to create listing for, defaulting to the current one
    if (array_key_exists("dir", $_GET)){
        $dirCurrent = $_GET["dir"];
    }else{
        $dirCurrent = getcwd();
    }

    // sort out some baseline dirs / URLs
    $dirCurrent = realpath($dirCurrent);
    $dirBase    = realpath($_SERVER["DOCUMENT_ROOT"]);
    $urlBase    = "http://" . $_SERVER["HTTP_HOST"];

    if (strpos($dirCurrent, $dirBase) !== false && file_exists($dirCurrent)){
        // Used for building links to files
        $relativePath = str_replace($dirBase, "", $dirCurrent);
        $urlPath = str_replace("\\", "/", $relativePath);

        // provide a link to go up a level
        $parentDir = preg_replace("#[\\\/][^\\\/]+$#", "", $dirCurrent);
        $parentLinkUrl = $_SERVER["SCRIPT_NAME"] . "?dir=" . $parentDir;
        echo "<a href=\"$parentLinkUrl\">[Parent Directory]</a><br>";

        echo "<ul>";
        opendir($dirCurrent);
        while ($entry = readdir()) {
            if (in_array($entry, [".", ".."])){    // skip these two
                continue;
            }
            $linkText = $entry;
            $wholePathToFile = $dirCurrent . DIRECTORY_SEPARATOR . $entry;
            if (is_dir($wholePathToFile)){
                // we just want to link back to this file, passing the dir
                $linkUrl = $_SERVER["SCRIPT_NAME"] . "?dir=" . $wholePathToFile;
                $linkText .= "/"; // just to make it more clear it's a dir
            }else{
                // we want a link to the actual file
                $linkUrl = $urlPath . "/" . $entry;
            }
            echo "<li><a href=\"$linkUrl\">$linkText</a></li>";
        }
        closedir();
        echo "</ul>";
    }else{
        header("HTTP/1.0 404 Not Found");
    }    
?>
\$\endgroup\$
  • \$\begingroup\$ It boggles my mind that you thought that edit was necessary, Jamal. Do you not have anything better to do with your time? My wording was deliberate, and you have removed the tone & voice I was trying to convey. You should never edit someone else's writing to your own pointless cosmetic ends, especially if your tool is a linguistically blunt axe. At best you should offer suggestions. You are part of what makes Stack Exchange such an oppressive, negative place. Please find something better to do with your time. \$\endgroup\$ – Adam Cameron Oct 23 '14 at 19:37
3
\$\begingroup\$

Took this from your blog post:

What I need to do is this:

  • take a directory on the URL, which by default will be the current directory;
  • list the files / directories;
  • for each entry, convert the filesystem path to a URL, using the current HTTP host and port;
  • also provide a "parent directory" link too, for browsing upstream.

Knowing these requirements, your code is way too complicated. That's quite normal for a beginner with PHP, so it's no problem. PHP offers built-in solutions that from time to time make experienced PHP programmers think: "Why didn't I find that before?" -- So what you missed, is glob(), and maybe some other neat functions.

// Directory to create listing for, defaulting to the current one
$dirCurrent = '';
if (!empty($_GET['dir'])) {
    // Remove leading and trailing slashes
    $dirCurrent = trim($_GET['dir'], '/.');
}
if (!empty($dirCurrent)) {
    $dirCurrent .= '/';
}
$dirBase = realpath($_SERVER["DOCUMENT_ROOT"]);

// Since we make $dirCurrent relative, a basedir check is not needed
if (is_dir($dirBase . '/' . $dirCurrent)) {
    // Provide a link to go up a level
    if (!empty($dirCurrent)) {
        $parentDir = dirname($dirCurrent);
        $parentLinkUrl = $_SERVER["SCRIPT_NAME"] . "?dir=" . $parentDir;
        echo "<a href=\"$parentLinkUrl\">[Parent Directory]</a><br>";
    }
    echo "<ul>";
    foreach (glob("$dirBase/$dirCurrent*") as $entry) {
        // Hidden files (those starting with a dot) are not returned by glob
        $linkText = basename($entry);
        if (is_dir($entry)) {
            // We just want to link back to this file, passing the dir
            $linkUrl = $_SERVER["SCRIPT_NAME"] . "?dir=" . urlencode($dirCurrent . basename($entry));
            $linkText .= "/"; // Just to make it more clear it's a dir
        } else {
            // we want a link to the actual file
            $linkUrl = $dirCurrent . basename($entry);
        }
        echo "<li><a href=\"$linkUrl\">$linkText</a></li>";
    }
    echo "</ul>";
} else {
    header("HTTP/1.0 404 Not Found");
}
\$\endgroup\$
  • \$\begingroup\$ Cheers for the feedback! There's certainly a few new functions in there you've used which I'll need to look-up. Yuo're the second person who has said "it's more compex than it needs to be", but then provides a more simple solution which actually doesn't cover all the bases my code does, and indeed doesn't actually work even to the mimimum level of functionality (although like the other situation, it might be because your version doesn't seem to be Windows-aware). It's easy to be less complex if delivering less functionality ;-) \$\endgroup\$ – Adam Cameron May 7 '13 at 7:17
  • \$\begingroup\$ Problems with your version: * displays whole file system path, not just the file/dir name * doesn't default to current dir * dir links don't "browse into" the sub dir * clicking on a file doesn't run it (ends up with a URL like: localhost:8000/D:/websites/php.local//index.php * can navigate out above the web root by manipulating the URL Possibly all these only occur due to a reliance in your code on the file separator being a fwd slash not [whichever slash the current file system uses] ? Didn't review the code that closely. \$\endgroup\$ – Adam Cameron May 7 '13 at 7:24
  • \$\begingroup\$ Sorry for that - I forgot some calls to basename() in order to remove the path information from the file. Now it should work as intended. \$\endgroup\$ – nibra May 7 '13 at 11:53
  • \$\begingroup\$ Nice one, thanks mate. I'll pore over that a bit later. \$\endgroup\$ – Adam Cameron May 7 '13 at 14:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.