How to Create a Simple PHP Templating Function

Here is a first draft at a PHP template function:

<?php
/**
 * 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:

<?php
$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.

<?php
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:

<?php
// 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:

<?php
/**
 * Simple PHP Templating function
 *
 * @param $names  - string|array Template names
 * @param $args   - Associative array of variables to pass to the template file.
 * @return string - Output of the template file. Likely HTML.
 */
function template( $names, $args ){
  // allow for single file names
  if ( !is_array( $names ) ) { 
    $names = array( $names ); 
  }
  // try to find the templates
  $template_found = false;
  foreach ( $names as $name ) {
    $file = __DIR__ . '/templates/' . $name . '.php';
    if ( file_exists( $file ) ) {
      $template_found = $file;
      // stop after the first template is found
      break;
    }
  }
  // fail if no template file is found
  if ( ! $template_found ) {
    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 $template_found;
  return ob_get_clean();
}

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

7 Thoughts

Discussion

Patrick
February 6, 2019

Very nice tutorial. Thanks for that.

John Doe
March 6, 2019

very nice explanation. thank you for your effort.

RE666
October 16, 2019

What is the reason to echo $id but print $name and $etc ?

Thanks

Jonathan Daggerhart
October 16, 2019

No reason at all, echo and print do exactly the same thing. I didn’t even notice that I had used them both. Good catch and question!

Fareast
November 11, 2019

Hi, I would like to ask in the template it wont run loop function, can I know what I did wrong?

Josue Arambide
July 17, 2020

Thanks, i was looking for this solution

gargamel
April 19, 2021

Awesome tutorial! Thanks :)

Soh Kazmi
October 28, 2022

Thank you sir, it helped me alot.

Leave a Reply

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