PHP DevCenter
oreilly.comSafari Books Online.Conferences.


Building a PHP Front Controller

by Q Ethan McCallum

I recently had the opportunity to implement some small (noncommercial) web sites in PHP. One of many decisions I faced concerned the templating strategy: duplicated, include()-based template pages weren't quite future-proof, while a formal, external template library would have been overkill.

Luckily, the web host in question permitted certain key Apache directives in .htaccess that let me customize request handling. Add to that PHP's OO support and my templating decision reached a comfortable middle ground in the form of a custom Front Controller.

The Front Controller design pattern describes a way to centralize processing in a web-based application. Routing all requests through a single entry point provides a place to apply common application logic, and, at the same time, reduce dependencies between other components. All of this adds up to a site that is easier to maintain and extend.

In this article, I will share a stripped-down version of the controller I implemented to mediate the relationship between content and layout.

What I explain here is not limited to PHP; the Front Controller design pattern is available in many server-based dynamic web design toolkits, such as mod_perl and Java servlets/JSP. Unlike those technologies, this article's implementation of the Front Controller requires only a modest Apache setup. Your PHP web host most likely supports the required configuration.

Related Reading

PHP Cookbook
By David Sklar, Adam Trachtenberg

The sample code was tested under Apache 1.3.27 and PHP 4.3.4.

First, Some Theory

A web server is typically a glorified file service. A client asks for a file and the server retrieves it. The Uniform Resource Identifier (URI) specifies a file under the document root, so the request and response are closely related.

Web servers may also invoke executables, such as a CGI or PHP scripts, instead of fetching raw files. Executables insert logic between the request and response, changing portions of the latter (to add a customized greeting or pull different records from a database, for example) with each call.

It's possible to route all requests through a single executable, generating an entirely different page for each one. In this case the executable is the site's Front Controller and the generated response is the view. The URI and request parameters comprise a command issued to the controller, which performs some logic to decide which view to return to the client.

The controller's central location in the request-response cycle makes it a suitable place to apply common logic for checking credentials or setting response headers.

A controller-managed web site operates independently of the underlying web server to a certain extent; file access, authentication, and content generation take place within the code. This is why the controller can access files from directories that otherwise have .htaccess restrictions.

Purists will note that the description here is abbreviated, with some participants, such as the dispatcher, folded into the main controller. The code is a starting point which you may extend as needed.

Direct and Indirect

Clients must call the controller either directly or indirectly. The direct method adds request parameters to the controller URI:


All intra-site links point to /controller.php, but the varied request parameters change which page to show. This works and has wide support among web servers, but the string of parameters can grow unwieldy.

The indirect way is cleaner and looks more natural, but the specific setup depends on the web server used. Apache's AddHandler configuration directive associates an executable or module with a path (such as /special) or filename extension (.site):


Notice that there is no explicit mention of the controller. Associating of the controller with the file extension .site takes place behind the scenes in the httpd.conf or .htaccess file.

The (hidden) executable here is an Apache content handler. When a user requests a matching URI, Apache short-circuits its normal request/response flow and passes control to the registered handler. It is then up to the handler to generate the content to return to the requesting browser.

This article will demonstrate the indirect method and use filename extensions.

A Simple Example

A concrete example will clarify the previous sections' points. Consider the following code, simple.php:

Controller called for URI
    <?= $_SERVER[ 'REQUEST_URI' ] ?>

<?php foreach( $_SERVER as $key => $val ){ ?>
    <b><?= ${key} ?>:</b>
    <tt><?= ${val} ?></tt>
<?php } ?>

Assume simple.php exists in the base directory of the document root of a web server listening on localhost:8000.

In .htaccess, the lines:

Action     controller-test /simple.php
AddHandler controller-test .tst

define /simple.php as the action controller-test and associate that action with the file extension .tst. When Apache receives a request for a filename ending in .tst, it will execute /simple.php instead of trying to serve a file from disk. (The executable named by Action is relative to the document root.)

Try it out: make web requests for simple.php then various .tst files. For example:


The first URL prints out all request parameters and server variables. So does the second URL, although the "request URI" line changes. Here, foo.tst was requested, and because of the AddHandler call in .htaccess, Apache let simple.php take over. Though it doesn't demonstrate much in the way of aesthetics, the output from simple.php shows the wealth of variables available to a PHP script that is called as a controller. (Sharp eyes may notice that it's the same set of variables available during normal PHP script execution.)

Fittingly, the third URL doesn't work. A requested resource may be purely virtual, but directories leading up to that resource must exist. Requests for /not_here/this_will_fail.tst fail because the directory not_here does not exist in the document root.

Nor can a controller-managed resource act as an index page. An explicit request for will work, but a request of http://localhost:8000/ will not attempt to load /, regardless of DirectoryIndex directives.

These two limitations are based on how Apache maps requests, described in detail in Writing Apache Modules with Perl and C.

Pages: 1, 2

Next Pagearrow

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: