mehulkar
mehulkar

Reputation: 4964

Populate an unordered list from a list of PHP files

The Situation:

I have a Projects page with a content area and a sidebar. I want the sidebar to contain a dynamic list of projects. The content area has an div with the id "post".

I have a subfolder containing .php files corresponding to projects that contain html content about each project.

The Challenge:

I would like the sidebar to generate an unordered list based on the filenames of the php files (or if possible, an h1 element inside each php file).

When clicked, I would like each of the items in this unordered list to populate the div id "post" in the content area with the contents of the php file to which it corresponds.

Is this possible?

I know it would be easier with a CMS like Wordpress, but I want to know how to do it without an SQL database if possible. Keep in mind, I know almost nothing about PHP. I have stuck to html/css so far.

Upvotes: 1

Views: 696

Answers (2)

user212218
user212218

Reputation:

Listing Files

# index.php

<?php
if( $files = glob('/path/to/directory/*.php') )
{
  ?>
  <ul id="sidebar">
    <?php
      foreach( $files as $path_raw )
      {
        $file_raw = basename($path_raw);

        $file_safe     = htmlentities($file_raw);
        $file_urlsafe  = urlencode($file_raw);
        ?>
    <li><a class="file-link" href="/post.php?file=<?php echo $file_urlsafe; ?>"><?php echo $file_safe; ?></a></li>
  </ul>
  <?php
}

To locate the files, we will want to use glob(). In this case, pass in a path to a directory (/path/to/directory) and a filename pattern (*.php) to find all '.php' files in the specified directory.

glob() returns an array, so we will need to iterate over the result using foreach.

Since we are providing an absolute path to the directory where the files are located, glob() will return an array of absolute paths, so we will need to use basename() to strip off the directory info and just get the filename.

Although unusual, it is possible for filenames to have unsafe characters in them, so we need to escape the values using urlencode() for URL strings (the href for the anchor tag) and htmlentities() otherwise (the text of the anchor tag).

Reading Files

The link in the unordered list references a file named post.php under the web server's document root.

# post.php

<?php
$basedir   = '/path/to/directory';

if( empty($_GET['file']) )
{
  // Handle error condition:  no filename provided.
}

$file_raw  = $_GET['file'];
$file_safe = basename($file_raw);

if( ! is_file($file_safe) )
{
  // Handle error condition:  file does not exist or is not a file.
}
elseif( ! is_readable($file_safe) )
{
  // Handle error condition:  file exists, but is not readable (probably permissions issue).
}

passthru($file_safe);

post.php Expects a $_GET value named file to be provided (taken care of by clicking on one of the links in the sidebar). It is important to note a couple of things:

  • Web browsers are capable of requesting any URL, so we cannot assume that the user arrived at post.php by clicking on a sidebar link, so we must check a few things:
    • The file value might be missing (we can check for this by using empty()).
    • The file value might have a different value than we were expecting (in this case, we will use basename() to ensure that we are dealing with a filename and not an injected path).
  • Even if the file value is valid, it might reference a path that is not actually a file, or it points to a file that the webserver cannot access (we check for these cases by using is_file() and is_readable(), respectively).

Finally, once we are sure that the file value points to a valid file, we send its contents to the web browser using passthru().

The Javascript

The only thing left to do is to use some Javascript so that clicking on one of the sidebar links displays the contents in the #post div. I will use jQuery here for brevity:

# index.php

(function($){
  $(function(){
    var post = $('#post');
    $('#sidebar a.file-link').click(function( e ){
      post.load($(this).attr('href'));
      e.preventDefault();
    });
  });
})(jQuery);

This code leverages the .load() method, which executes an ajax call and replaces the selected element with the content from that request.

We use .click() to set the ajax call to only trigger when the user clicks on one of the links in the sidebar, and we determine which URL to go to by calling .attr() on the clicked link to extract its href attribute.

Note that we use the event's preventDefault method rather than returning false to avoid potential for unwanted side effects.

Upvotes: 0

mowwwalker
mowwwalker

Reputation: 17344

The Solution:

function getfiles($dir){
    if(!is_dir($dir))
        return false;
    $dirhandle = opendir($dir);
    $files = array();
    while($entry = readdir($dirhandle)){
        if($entry!='.' && $entry!='..' && !is_dir($dir.'/'.$entry))
            $files[]=$entry;
    }
    return $files;
}

Returns an array of the files. There are three special entries in a directory that aren't files. . refers to the directory it's in. .. refers to the parent directory. Finally, there are other directories. So far as I know, everything else is a file.

And then:

function createlist($dir){
    if(!$files=getfiles($dir))
        return false;
    ?>
    <script type="text/javascript" >
        function getcontent(xthis) {  
            var httpRequest;
            makeRequest(xthis.href);   
            function makeRequest(url) {  
                if (window.XMLHttpRequest) { // Mozilla, Safari, ...  
                    httpRequest = new XMLHttpRequest();  
                } else if (window.ActiveXObject) { // IE  
                    try {  
                        httpRequest = new ActiveXObject("Msxml2.XMLHTTP");  
                    }   
                    catch (e) {  
                        try {  
                            httpRequest = new ActiveXObject("Microsoft.XMLHTTP");  
                        }   
                        catch (e) {}  
                    }  
                }  

                if (!httpRequest) {   
                    return false;  
                }  
                httpRequest.onreadystatechange = putContents;  
                httpRequest.open('GET', url);  
                httpRequest.send();  
            }  

            function putContents() {  
                if (httpRequest.readyState === 4) {  
                    if (httpRequest.status === 200) {  
                        document.getElementById("post").innerHTML=httpRequest.responseText;  
                    } else {  
                        return false; 
                    }  
                }  
            }  
        }
    </script>
    <?PHP
    echo "<ul>\n";
    foreach($files as $file){
        echo "\t<li><a onclick=\"getcontent(this);return false;\" href=".$dir."/$file>$file</a></li>\n";
    }
    echo "</ul>";
    return true;
}

Ajax functions courtesy of https://developer.mozilla.org/en/AJAX/Getting_Started.

Upvotes: 1

Related Questions