Here is a first draft at a PHP template function:

/**
 * Simple Templating function
 *
 * @param $file   - Path to the PHP file that acts as a template.
 * @param $args   - Associative array of variables to pass to the template file.
 * @return string - Output of the template file. Likely HTML.
 */
function template( $file, $args ){
  // ensure the file exists
  if ( !file_exists( $file ) ) {
    return '';
  }

  // Make values in the associative array easier to access by extracting them
  if ( is_array( $args ) ){
    extract( $args );
  }

  // buffer the output (including the file is "output")
  ob_start();
    include $file;
  return ob_get_clean();
}

And here is how you would use this function:

$file = __DIR__ . '/templates/row-template.php';

$rows = array(
  array( 'id' => 1, 'name' => 'first row', 'etc' => 'and more...' ),
  array( 'id' => 2, 'name' => 'second row', 'etc' => 'nothing special' ),
);

$output = '';

foreach ( $rows as $row ){
  $output.= template( $file, $row );
}

print $output;

And your row-template.php file might look a little something like this:

<div id="row-<?php echo $id; ?>" class="row">
  <h2><?php print $name; ?></h2>
  <p><?php print $etc; ?></p>
</div>

That’s pretty much it!

Breaking down the function

Let’s talk about what this function is doing and why.

  1. First we make sure the template file exists, because otherwise what’s the point?
     if ( !file_exists( $file ) ) {
        return '';
      }
    
  2. Next, if the $args parameter is an array, extract it.
      if ( is_array( $args ) ){
        extract( $args );
      }
    

    extract() is an interesting PHP function that creates individual variables from an associative array. Or as the documentation would describe it, “Import variables into the current symbol table from an array”.

    This example may be useful to understanding extract():

    $my_array = array(
      'first_var' => 'has this value',
      'second_var' => 'has a different value',
    );
    
    extract( $my_array );
    
    print $first_var;
    // outputs: has this value
    
    print $second_var;
    // outputs: has a different value
    

    Side note: If you’re just now learning about extract(), know that it can be a problematic function. Since with extract() you’re creating variables arbitrarily, you can accidentally overwrite existing variables in the scope. This case mitigates the potential problems by having very-few total variables within scope to begin with, but consider this: if the $args array contained a key named ‘file’, you’d break the whole thing. So there is definitely room for improvement here.

  3. Next we start an output buffer. This means that if following code produces output (such as echoing a string, including an html file, or a php error occurs), then instead of sending that output to the screen, that output is stored in memory.
    ob_start();
    
  4. Including the template file executes the PHP and sends output to our buffer.
      include $file;
    
  5. Finally, we get the contents of our buffer and return that content to the caller.
    return ob_get_clean();
    

Ways this function could be improved

One way we can improve this function is to require less coding to use it. Currently the function requires you to provide the full path to the template file (or find some other work around).

print template( '/var/www/html/templates/row-template.php', $args );

That will get tiresome quickly, so let’s set two requirements: first we’ll restrict where templates can be located in the system, and second we’ll require the file extension be .php.

function template( $name, $args ){
  $file = '/var/www/html/templates/'.$name.'.php';

  // ensure the file exists
  if ( !file_exists( $file ) ) {
    return '';
  }
  // ...

Now we can call the function with much less code.

print template( 'row-template', $args );

Nice.

Template Suggestions

Another way this function could be improved is if we allowed for an array of template names to be passed in, thus providing a simple “template suggestions” system that will look for the first available file.

When adding this “template suggestions” feature we don’t want to make it harder to use the function in any way, so we’ll allow the $names parameter to be either a single template name or array of template names.

function template( $names, $args ){
  // allow for single file names
  if ( !is_array( $names ) ) { 
    $names = array( $names ); 
  }

  $found = false;
  foreach ( $names as $name ) {
    $file = '/var/www/html/templates/'.$name.'.php';

    if ( file_exists( $file ) ) {
      $found = $file;
      break;
    }
  }

  if ( ! $found ) {
    return '';
  }
// ...

That’s pretty good stuff. Now we can offer a list of template suggestions to the function, and it will only execute the first one found.

For example:

// most specific to most general
$suggestions = array(
  'row-' . $data->id,
  'row-' . $data->type,
  'row-default',
);

print template( $suggestions, $args );

The above execution will search for each template name in order, starting with the most specific possible template, and ending with a default template.

The glaring remaining issue with this function is the hard-coded template location ($file = '/var/www/html/templates/'.$name.'.php';). You’ll want to change this to use a more flexible path. For now, I’ll use the __DIR__ constant, which is the directory that this function is in.

Now in the spirit of keeping this function simple, we’ll change this file path to use the directory the function resides in and leave it there. Here is the final resulting function:

Next up: Write this template function as a Simple PHP Template Class

About the Author

Jonathan Daggerhart

Long time Drupal and WordPress developer. I like to write modules and plugins, and I dabble in frontend and design.

Leave a Reply

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