PHP DevCenter
oreilly.comSafari Books Online.Conferences.

advertisement


Building a PHP Front Controller
Pages: 1, 2

What About Apache 2.x?

Users of Apache 2 will notice that simple.php does not work as a content handler. For Apache 2.0.x, AddHander works only for existing files. According to reports (specifically, Bugzilla bug ID 8431), Apache 1.x misused the term "handler" so this changed in 2.x.

There is hope. Per that bug report, Apache 2.1 may add a configuration directive to relax the restriction, such that AddHandler can refer to nonexistent files just as it did in Apache 1.x.

Putting It Together: An Overview of the Sample Site

This article's sample web site uses a Front Controller to separate content from layout. The controller maps request URIs to content files, then merges the content with a layout template at request time.

php_include/classes.php contains the helper classes that simplify the process:

  • AppConfig wraps an array of common application settings such as page background color.
  • SitePage represents a page of content. It wraps a title (for the browser title bar) and the path to a content file.
  • PageMap is the backbone of the controller's work. It associates SitePage objects with URIs and holds SitePages for HTTP errors (such as 404 and 500).
  • AliasMap holds aliases for the controller-managed URIs.
  • Content pages use AliasMap, via Util::aliasLink(), to refer to one another without hard-coding paths in <a> tags. (This is not required, but certainly eases maintenance.) Util exposes a second function, literalLink(), to create anchor tags to offsite resources.

SitePage, PageMap, and AliasMap could be replaced by an XML document; but they are straight code here for the sake of simplicity.

php_include/mappings.php configures the page mappings and populates AppConfig with some values. End-users can adjust site settings here without touching the support classes or controller code.

Controller.php, the controller, weighs in around 30 lines of code because the helper classes do the heavy lifting:

<?php

require_once( 'php_include/classes.php'  );
require_once( 'php_include/mappings.php' );

$daysToCache  = 1.5;
$cacheMaxAge  = ${daysToCache}*24*(60*60);

$layoutFile   = 'php_include/layout_main.php';

$requestedURI = ereg_replace(
    $config->getValue( 'URISuffix' ) .  '$' ,
    "" ,
    $_SERVER["REQUEST_URI"]
);

$page         = $pageMappings->getPage( $requestedURI );

if( is_null( $page ) )
{
    header( 'HTTP/1.0 404 Not Found' );
    $page = $pageMappings->getNotFoundPage();
}

if( ! headers_sent() )
{
    header(
        'Cache-Control: max-age=' . ${cacheMaxAge} . ',
        must-revalidate'
    );
    header(
        'Last-Modified: ' . gmdate('D, d M Y H:i:s' ,
        $page->getLastModified() ) . ' GMT'
    );
}

include( $layoutFile );

?>

The actual content files in the content directory are HTML fragments with no layout. The include() function merges them with the layout template. Adding a new page to the site requires placing the file under content, updating the PageMap, and (optionally) updating AliasMap.

The content and php_include directories should never allow direct access, so .htaccess files restrict normal web requests.

.htaccess in the base of the document root contains various Apache settings. Since virtual resources cannot be index documents, RedirectMatch forwards requests for the root URI to /main/index.site. (Change the host and port statements to make the sample code to work on your computer.) AddHandler directives associate Controller.php with requests ending in .site. ErrorDocument directives map HTTP status codes to controller-managed error pages.

The stub directories main and errors permit us to call controller-managed resources with directory paths, for logical grouping. (Recall Apache's handling of nonexistent directories, described above.)

Request Flow

When the site receives a request for a controller-managed file:

  1. The controller loads its helper classes and the mappings file which creates $config, the app-wide configuration object.
  2. The controller strips the custom file extension (as declared in mappings.php) from the request URI and pulls the matching SitePage object from the PageMap.
  3. If the content file exists and is readable, the $page variable will contain the SitePage object. Otherwise, $page refers to the designated 404-error page.
  4. The controller sets headers — in this case, a Last-Modified date — and passes control to the layout template.
  5. The layout template (php_include/layout_main.php) uses $config to set the header bar's background color and pulls the name of the content file from $page.
  6. The include() call inserts the content page in the middle of the template to complete the view. The server will interpret content files as PHP documents, so they may contain any valid PHP logic.

At this point, the server can return a complete HTML document to the client browser.

Is It Appropriate?

Few techniques are suitable for everyone. If your web site updates involve adding new pages in-flight, then the overhead of the map maintenance may work against you. However, if the dynamic nature of your site depends on something outside of the raw pages (such as a database) or if your changes involve carefully-tested migrations, then the Front Controller technique may benefit you long-term.

Conclusion

The Front Controller demonstrated in this article separates the request from the response, the layout from the content, and the content pages from each other. Such decoupling offers significant flexibility over both static HTML pages and duplicated, include()-based template pages.

The sample code is just a starting point. Certainly, the controller could do more in terms of logging or authentication checks. The SitePage class could also provide more information, such as per-page <META> tags. Finally, it would be trivial to implement automatic generation of a site map through a helper class that logically groups internal aliases.

For more detail on the Front Controller design pattern, refer to the texts listed in the Resources section.

Resources

  • The sample code download includes simple.php, the full sample site, and a minimal Apache httpd.conf to demonstrate web host requirements.
  • Patterns of Enterprise Application Architecture (Fowler)
  • Core J2EE Patterns (Alur, Crupi, Malks)
  • Writing Apache Modules with Perl and C (Stein, MacEachern)
  • Several concepts in this article (including PageMap and Util::aliasLink()) were inspired by Java's RequestDispatcher and the Tiles extension of the Jakarta Struts framework.

Q Ethan McCallum grew from curious child to curious adult, turning his passion for technology into a career.


Return to the PHP DevCenter.


Valuable Online Certification Training

Online Certification for Your Career
Earn a Certificate for Professional Development from the University of Illinois Office of Continuing Education upon completion of each online certificate program.

PHP/SQL Programming Certificate — The PHP/SQL Programming Certificate series is comprised of four courses covering beginning to advanced PHP programming, beginning to advanced database programming using the SQL language, database theory, and integrated Web 2.0 programming using PHP and SQL on the Unix/Linux mySQL platform.

Enroll today!


Sponsored by: