3
\$\begingroup\$

Here is my template code

<?php

namespace classes;

/**
* Description of Template
*
* @author Aamir
*/
class Template {

    private
        $templateVars,
        $templateFile
    ;
    protected static
        $templateFolder,
        $masterTemplate,
        $masterInstance,
        $placeholder,
        $path_dir,
        $path_web

    ;

    /**
    * Constructor for the template
    * @param string $file file path
    * @param array $args associative array
    */
    public function __construct($file, $args = array()) {
        $this->templateFile = self::$path_dir . self::$templateFolder . '/' . $file . '.phtml';
        $this->templateVars = $args;
    }

    /**
    * Magic method to get the array element
    * @param string $name template variable name to fetch
    * @return mixed
    */
    public function __get($name) {
        if (array_key_exists($name, $this->templateVars)) {
        return $this->templateVars[$name];
        } else {
        return '';
        }
    }

    /**
    * Magic method to set template var
    * @param string $name
    * @param mixed $value
    */
    public function __set($name, $value) {
        $this->templateVars[$name] = $value;
    }

    /**
    * output the template to a string
    * @return string
    */
    public function __toString() {
        ob_start();
        include $this->templateFile;
        return ob_get_clean();
    }

    /**
    * Function to initialize the template
    * @param string $templatePath Template Path
    */
    public static function setTemplate($templatePath) {
        self::$templateFolder = $templatePath;
    }

    /**
    * Get current template path
    * @return string
    */
    public static function getTemplate() {
        return self::$templateFolder;
    }

    /**
    * Get current template path from root
    * It will print the path. To be used in image and file paths 
    */
    public static function getPath() {
        echo self::$path_web . self::$templateFolder . '/';
    }

    /**
    * Set Master Template for project
    *
    * @param string $masterTemplate master template file name from template folder
    */
    public static function setMaster($masterTemplate) {

        self::$masterTemplate = $masterTemplate;

        self::$placeholder = new Placeholder();
    }

    /**
    * Set the Paths for root directory, and URL based path
    * @param string $PATH_WEB URL Path e.g. http://localhost/project1/abc/
    * @param string $PATH_DIR Physical path e.g. /home/userA/www/project1/abc/
    */
    public static function setPaths($PATH_WEB, $PATH_DIR) {
        self::$path_web = $PATH_WEB;

        self::$path_dir = $PATH_DIR;
    }

    /**
    * Get the instance of Placeholder object
    * @return object
    */
    public static function getMasterPlaceHolder() {
        return self::$placeholder;
    }

    /**
    * Output the template to browser
    */
    public function render() {

        $master = new Template(self::$masterTemplate, array(
            'placeholder' => self::$placeholder,
            'content' => $this
            ));

        echo $master;
    }

}

Placeholder.php used in master template

<?php
namespace classes;

/**
 * Description of Placeholder
 *
 * @author aamir
 */
class Placeholder {


    private $templateVars;

    public function __get($name) {
        if (array_key_exists($name, $this->templateVars)) {
            return $this->templateVars[$name];
        } else {
            return '';
        }
    }

    public function __set($name, $value) {
        $this->templateVars[$name] = $value;
    }

}

USAGE

code from config.php

/**
 * Default Template folder and languages
 * setTemplate (Template folder);
 */
classes\template::setTemplate('template/template1');
classes\template::setMaster('master');

define('TEMPLATE_PATH', classes\template::getTemplate() . '/');

Pages.php

<?php
require 'config.php';

$template = new \classes\template('page');

$template->page_content = $pageData;

$template->render(); // will output including master template
// echo $template; // will output only the page template excluding master layout for AJAX out put

Pages.php

To change the contents of Master layout e.g. Change of Page title etc

$placeholder = \classes\template::getMasterPlaceHolder();

$placeholder->pageTitle = $cur_page['page_title'];
$placeholder->cur_page = getParam('p');

$template = new \classes\template('page');
$template->page_content = $pageData;
$template->render(); // will output including master template
// echo $template; // will output only the page template excluding master layout for AJAX out put

page.phtml file inside the template/template1 folder

<div class="left" style="width: 845px;"><?php

echo $this->page_content; // this will render page contents

?>
</div>

master.phtml (we need to define placeholders in the template)

<!DOCTYPE html>
<html>
    <head>
        <title><?php echo $this->placeholder->pageTitle ?></title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <link rel="stylesheet" href="<?php echo TEMPLATE_PATH ?>css/style.css" />
        <link rel="stylesheet" href="<?php echo TEMPLATE_PATH ?>css/fluid_grid.css" />
        <link type="text/css" href="<?php echo TEMPLATE_PATH ?>css/jquery.jscrollpane.css" rel="stylesheet" media="all" />
        <script type="text/javascript" src="<?php echo TEMPLATE_PATH ?>js/jquery-1.7.1.min.js"></script>
        <script type="text/javascript" src="<?php echo TEMPLATE_PATH ?>js/jquery.tooltip.pack.js"></script>
        <script type="text/javascript" src="<?php echo TEMPLATE_PATH ?>js/jquery.jscrollpane.min.js"></script>
        <script type="text/javascript" src="<?php echo TEMPLATE_PATH ?>js/jquery.mousewheel.js"></script>
        <script type="text/javascript" src="<?php echo TEMPLATE_PATH ?>js/jquery-ui-1.8.18.custom.min.js"></script>
    </head>
    <body>

        <?php echo $this->placeholder->beforeContent?>
        <div class="wrapper">
            <?php echo $this->content ?>

        </div>
    </body>
</html>

So basically, we set the template folder, and set template master file, and then where we have to display content we used $template = new template('templatefile') (it will add .phtml auto) and if we want full template including master we use $template->render() or if we just want to output without master for example for an AJAX request we simply echo it echo $template.

Please let me know how can I improve it.

\$\endgroup\$
3
  • \$\begingroup\$ I'm still looking over it, but the first major improvement I would suggest is to indent everything appropriately. You don't seem to indent the first level for some reason. \$\endgroup\$
    – mseancole
    Commented May 7, 2012 at 13:11
  • \$\begingroup\$ Formating of the code is disturbed in stackexchange, else it is formated properly in my netbeans IDE \$\endgroup\$ Commented May 8, 2012 at 5:32
  • \$\begingroup\$ Ah, well that's fine then. Just wanted to make sure you were indenting properly :) Taking a look at the changes now. BTW: I've updated the Class Properties section. Paul brought up a good point about that in another question. \$\endgroup\$
    – mseancole
    Commented May 8, 2012 at 13:08

1 Answer 1

3
\$\begingroup\$

A lot of these suggestions are just that, suggestions. I have little to add for improvement except for aesthetics or readability. So here it goes.

Class Properties

Cool thing about class properties is that they can be combined when initially declaring them. A lot of people don't like doing it because when they see examples of it its always shown as one long string. However, it doesn't all have to be on the same line. Unless you are planning on commenting your variables, PHP is flexible and will allow you to pretty it up with as much, or as little, white space as you like. This is how I like initially declaring my properties, but as I said, this is just a suggestion/preference, it is up to you.

private
    $templateVars,
    $templateFile
;
protected static
    $templateFolder,
    $masterTemplate,
    $masterInstance,
    $placeholder
;
  • DocBlock commented variables have to be declared separately. You don't have to comment your variables, I don't, but figured I'd mention this for completion.

Constants

This is the only real problem I see. Constants PATH_WEB and PATH_DIR came from no where and I can't find them defined anywhere else. Your models should not be dependent upon another source to work, else you risk fatal errors should that source ever become unavailable or changes. Models should always work error free by themselves. This rule isn't as important for views or controllers since they are already so dependent on other files, but it still sort of applies for them to. I wouldn't set a constant in a config file, TEMPLATE_PATH, to be used in a controller or view, instead I would set it in the controller so that there was one less dependency. In fact, I would probably do away with the config file all together and just throw that all into the controller. The rule of thumb here: set it where you need it.

Method Organization and Improvements

This is just a preference, but one I think most would agree with. I like to move all my magic methods __get(), __set(), __construct(), etc... to the top of my classes. This makes them easier to find because they will always be in the same place. The way you have them now, they are just kind of in the middle. Not that it applies here because you don't seem to be using private/protected methods, but I also separate my methods by type (private/protected or public) for similar reasons. The final thing I do is visually separate these sections using comment blocks, which makes it easier to scroll long classes to find a specific section.

Why did you make your construct method chainable? Never tried it myself because I never thought I'd be able to do it cleanly so I don't even know if it is possible to chain it. Never used namespaces before either, but it looks like it can be done cleanly enough with them, I'll have to take a look at that. You don't appear to be chaining it anyways though, so I would just remove the return from your construct method.

Why did you create your own tostring method when you are not doing anything with the overloaded magic tostring method? Just combine the two and stop manually calling the method. Just echo the class instance, thats the whole reason for it. Also, to keep with the accepted standard, methods should only be prefixed by a single underscore "_" if they are private.

Final Thoughts

Now, my brain started shutting down when I looked at your usage sample, so this review is not 100%, but it should be a good 90% at the very least. That has nothing to do with your coding at all, I'm just tired and my brain doesn't want to do the mental gymnastics required to link everything in my head right now. I'll try and come back to this later if I see anything with your usage sample.

\$\endgroup\$
3
  • \$\begingroup\$ Thank you for a very detailed reply. It really helped me. I have removed the _toString() method, i have removed the method chaining from constructor, added new static function of setPaths() where PATH_WEB and PATH_DIR can be set, if not set then it will be empty strings. Moved all the magic methods to top of the class. \$\endgroup\$ Commented May 8, 2012 at 6:01
  • \$\begingroup\$ I have updated the template class as per your suggestions. \$\endgroup\$ Commented May 8, 2012 at 10:08
  • \$\begingroup\$ @AamirMahmood: I don't really see anything else. It all looks good to me :) \$\endgroup\$
    – mseancole
    Commented May 8, 2012 at 13:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.