If you are unfamiliar with some of the basic concepts around templating, take a moment to review my previous post: How to Create a Simple PHP Templating Function, where I explain how to look for files, buffer output, and extract variables into scope.

For those unfamiliar with classes and OOP, know that this post doesn’t attempt to explain the concept too much, but instead provides a practical example of how a class can be used.

And before we get started, here is a simple OOP vocabulary list for reference.

  • Encapsulation – the concept of grouping like-functionality together.
  • Class – group of functions and variables that work together as a single concept. When you write a class, you have “Encapsulated” those functions and variables.
  • Method – a function that is part of a class.
  • Property – a variable that is part of a class.
  • Instantiate – the creation of a new object from a class.
  • Instance / Object – a run-time value of some class.

Now let’s take a look at this new template class:

<?php
/**
 * Class Template - a very simple PHP class for rendering PHP templates
 */
class Template {
	/**
	 * Location of expected template
	 *
	 * @var string
	 */
	public $folder;
	/**
	 * Template constructor.
	 *
	 * @param $folder
	 */
	function __construct( $folder = null ){
		if ( $folder ) {
			$this->set_folder( $folder );
		}
	}
	/**
	 * Simple method for updating the base folder where templates are located.
	 *
	 * @param $folder
	 */
	function set_folder( $folder ){
		// normalize the internal folder value by removing any final slashes
		$this->folder = $this->folder = rtrim( $folder, '/' );
	}
	/**
	 * Find and attempt to render a template with variables
	 *
	 * @param $suggestions
	 * @param $variables
	 *
	 * @return string
	 */
	function render( $suggestions, $variables = array() ){
		$template = $this->find_template( $suggestions );
		$output = '';
		if ( $template ){
			$output = $this->render_template( $template, $variables );
		}
		return $output;
	}
	/**
	 * Look for the first template suggestion
	 *
	 * @param $suggestions
	 *
	 * @return bool|string
	 */
	function find_template( $suggestions ){
		if ( !is_array( $suggestions ) ) {
			$suggestions = array( $suggestions );
		}
		$suggestions = array_reverse( $suggestions );
		$found = false;
		foreach( $suggestions as $suggestion ){
			$file = "{$this->folder}/{$suggestion}.php";
			if ( file_exists( $file ) ){
				$found = $file;
				break;
			}
		}
		return $found;
	}
	/**
	 * Execute the template by extracting the variables into scope, and including
	 * the template file.
	 *
	 * @internal param $template
	 * @internal param $variables
	 *
	 * @return string
	 */
	function render_template( /*$template, $variables*/ ){
		ob_start();
		foreach ( func_get_args()[1] as $key => $value) {
			${$key} = $value;
		}
		include func_get_args()[0];
		return ob_get_clean();
	}
}
simple-template-class.php

The main take away here is that we have broken our previous single function into multiple smaller functions, and encapsulated them together within a class.

Here is a contrived usage of this class:

<?php
$tpl = new Template( 'path/to/templates/folder' );

print $tpl->render( 'my_template_name', array(
  'some_var' => 'that has this value',
  'another_var' => 'with another value',
) );

Let’s walk through what is going on above:

  1. First, we instantiate the class as a new object named $tpl. During the creation of the object, we pass in the folder location where we expect our class to find template files.
  2. Next, we run the render() method, providing our template name and the variables we would like to inject into the scope of the template.
  3. The render() method then executes another method within the class named find_template(), which loops through the suggestions and looks for the first file it can find.
  4. If a template file was found, the render() method then calls the render_template() method.
  5. The render_template() method first starts an output buffer so that our output is captured as opposed to being sent to the screen.Then it dynamically defines the variables into the scope and includes the found template file.When done, it returns the buffered output to the caller (in this case the “caller” is the render() method).
  6. Finally, the render() method returns the output to the caller (in this case the “caller” is the above example usage of this class).

How this class improves upon the previous function:

  1. No hard-coded template path. Now each instance of this class can define its own template path, and an instance’s folder can but updated using the set_folder() method.
  2. No potential for extract variable collisions, as no variables are defined within the scope of the render_template() method.By using the func_get_args() function to access the values for parameters passed into the render_template() method, we avoid naming the variables within the scope. This mean that when we later extract() the variables, it is not possible for an extracted variable to conflict with another variable within the method.
  3. Template suggestions are run through array_reverse() before searched, to provided a better developer experience when building the $suggestions array.This means that when you are creating a list of template suggestions, you can easily add more-specific suggestions to the end of the array, as opposed to needing to unshift() them to the beginning.Example:

    Example Template

    <div class="user-profile">
    	<h3 class="title"><?php echo $user_name; ?></h3>
    	<div class="content"><?php echo $user_bio; ?></div>
    </div>
    user-profile-example-template.php

    Using Template Suggestions

    <?php
    $suggestions = array( 'user-profile-example-template' );
    // provide more-specific template suggestions for logged in users
    if ( user_is_logged_in() ) {
      
      // provide a generic template suggestion for all users of a given type
      if ( user_is_of_special_type() ) {
        $suggestions[] = 'user-profile-' . $user->type;
      }
      // provide the most-specific template suggestion possible for this user.
      $suggestions[] = 'user-profile-' . $user->id;
    }
    $tpl = new Template( 'path/to/templates/folder' );
    $tpl->render( $suggestions, array(
      'user_name' => $user->name,
      'user_bio' => $user->bio
    ) );
    user-profile-example-template-suggestions-usage.php

    While this example is contrived, hopefully it is clear to see how easily you can add more-specific suggestions to the existing list of suggestions.

There we have it. Using the same concepts presented in the previous blog post we have broken that function into smaller pieces and encapsulated those pieces within a class. In doing so, we have provided significantly better (more flexible, less error-prone) functionality and developer experience.

Let me know what you think!

References

4 thoughts on “Simple PHP Template Class

Leave a Reply

Your email address will not be published. Required fields are marked *