PHP makes it very easy to include everything from interpreting form submissions to calls to the database to rendering HTML in one file. As a project grows, this can make code difficult to test and update.
In this post, I’ll discuss an approach to separating application logic from template files using output buffering, and how that can help to make your code easier to work with.
The Problem
When not using a framework, it’s not uncommon for a single PHP file to include both a lot of logic and a lot of HTML to render the result of the logic. For example, a file could define functions, make queries to the database, and render the page’s body.
But to create a unit-testable application, it’s beneficial to isolate different pieces of the app—e.g. a repository layer for calls to the database, model classes representing data, and view layers that are primarily HTML.
Separating Logic from Templates
My goal here is to separate logic that provides the data to be rendered from a template file that renders the data with HTML. This separation allows for reusability of the template in the event that it could be fed from multiple data sources, and allows for simpler testing of the logic without the HTML being printed. Let’s take a look at a simple example that renders a table of users.
Template
Here’s the code for a template file. Notice that the only logic is a foreach
and simple short tags. Otherwise the file is just HTML.
<div class="user-list">
<table>
<tr>
<th>index</th>
<th>user name</th>
</tr>
<? foreach ($this->getUserNames() as $index => $userName) { ?>
<tr>
<td><?= $index ?></td>
<td><?= $userName ?></td>
</tr>
<? } ?>
</table>
</div>
Backing Class
And here’s the file for the backing class. Notice that it is only a class, with no HTML. It’s easy to write unit tests against the class using a library like phpunit. An interesting peice here is the render
method, which makes use of ob_start and ob_get_clean(). That’s the magic that holds things together. We’ll take a look at that next.
<?
class UserList {
public function getUserNames() {
// hard-coded for simplicity
// in practice, this could query the database, call into a repository class, etc
return [
'Alice',
'Bob'
];
}
public function __toString() {
return $this->render();
}
public function render() {
ob_start();
include('path/to/user-list.template.php'); // or wherever your template is located
return ob_get_clean();
}
}
>?
Output Buffering
Output buffering allows for any output (e.g. echo
statements and rendered HTML) to be stored to a separate buffer instead of being directly output. There are a number of functions related to output buffering, but the two we’ll use are ob_start
and ob_get_clean
.
ob_srtart
begins output buffering. We next include the template file. Its output is rendered to the buffer.ob_get_clean
does two things: it gets the contents of the buffer as a string, and it cleans out the buffer. When the string is returned, it can be output elsewhere in the application.
Putting it Together
Next, let’s take a look at including our UserList in another file. Imagine we have a page that renders a bunch of other components, and we wish to include the users list. We could do something like this:
<?
require_once 'presenters/user-list.php'; // or whatever file your UserList class is in
?>
<html>
<body>
<!-- other html before the user list -->
<?= new UserList($userRepository) ?>
<!-- other html after the user list -->
</body>
</html>
One cool feature here is that __toString()
calls into the render
method, so there’s no need to directly call render
.
Great post Tyler, Yes many times it difficult to distinguish the codes. it often making confuse to me and as I m a noob in this field. It really helpful thanks a lot.