Skip to content

Instantly share code, notes, and snippets.

@daggerhart
Last active May 17, 2023 19:16
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save daggerhart/730e8a9d79f37a461d985b158d0108ab to your computer and use it in GitHub Desktop.
Save daggerhart/730e8a9d79f37a461d985b158d0108ab to your computer and use it in GitHub Desktop.
A simple PHP 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 = 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();
}
}
<?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
) );
<div class="user-profile">
<h3 class="title"><?php echo $user_name; ?></h3>
<div class="content"><?php echo $user_bio; ?></div>
</div>
@daggerhart
Copy link
Author

@Cyclonecode, good catch on the doulbe $this->folder = $this->folder = ..., that was a typo I had obviously missed for years.

The $folder constructor parameter is optional. You shouldn't have to provide a folder path during construction of the object.

@Cyclonecode
Copy link

Cyclonecode commented Sep 16, 2019

Ah, but if you don't supply a folder path the following will never work right:
https://gist.github.com/daggerhart/730e8a9d79f37a461d985b158d0108ab#file-simple-template-class-php-L72

@daggerhart
Copy link
Author

@Cyclonecode, that's true. I made it optional in the constructor, but when not provided a user would need to make use of the set_folder() method themselves. What would you recommend as an improvement to the class? I'm open to any ideas that make it more reliable and easy to use.

@Cyclonecode
Copy link

Cyclonecode commented Sep 17, 2019

First of all I think I would set visibility for the different methods,. For instance I guess the render_template(), find_template() and so on really should not be used by a user directly? If this is the case I really think they should be declared as private or protected. Of course the folder property also should be set to private or protected in this case. After that I think changing the render() method to something like this might be a good idea:

	public function render( $suggestions, $variables = array() ){
                // Make sure we have a folder path, perhaps we should also check so the folder is readable?
                if ( !$this->folder || !is_dir($this->folder)) {
			throw new Exception('You need to set the base folder for all templates using set_folder().');
                }
		$template = $this->find_template( $suggestions );
		$output = '';
		if ( $template ){
			$output = $this->render_template( $template, $variables );
		}
		return $output;
	}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment