1

I'm just very slowly starting to sink into object-oriented programming, so please be gentle on me.

I have a custom class for Smarty that was partially borrowed. This is how the only example reflects the basic idea of using it across my current project:

class Template {

    function Template() {
        global $Smarty;
        if (!isset($Smarty)) {
            $Smarty = new Smarty;
        }
    }

    public static function display($filename) {
        global $Smarty;
        if (!isset($Smarty)) {
            Template::create();
        }
        $Smarty->display($filename);
    }

Then in the PHP, I use the following to display templates based on the above example:

Template::display('head.tpl');
Template::display('category.tpl');
Template::display('footer.tpl');

I made the following example of code (see below) work across universally, so I wouldn't repeat the above lines (see 3 previous lines) all the time in each PHP file.

I would just like to set, e.g.:

Template::defauls();

that would load:

Template::display('head.tpl');
Template::display('template_name_that_would_correspond_with_php_file_name.tpl');
Template::display('footer.tpl');

As you can see Template::display('category.tpl'); will always be changing based on the PHP file, which name is corresponded with the template name, meaning, if for example, PHP file is named stackoverflow.php then the template for it would be stackoverflow.tpl.


I've tried my solution that have worked fine but I don't like it the way it looks (the way it's structured).

What I did was:

  1. Assigned in config a var and called it $current_page_name (that derives the current PHP page name, like this: basename($_SERVER['PHP_SELF'], ".php"); ), which returned, for e.g.: category.
  2. In PHP file I used Template::defaults($current_page_name);
  3. In my custom Smarty class I added the following:


public static function defaults($template) {

   global $Smarty;

   global $msg;
   global $note;
   global $attention;
   global $err;

   if (!isset($Smarty)) {
      Templates::create();
   }
      Templates::assign('msg', $msg);
      Templates::assign('note', $note);
      Templates::assign('attention', $attention);
      Templates::assign('err', $err);

      Templates::display('head.tpl');
      Templates::display($template . '.tpl');
      Templates::display('footer.tpl');
}

Is there a way to make it more concise and well structured? I know about Code Review but I would like you, guys, to take a good look at it.

Ilia Ross
  • 13,086
  • 11
  • 53
  • 88
  • One small point on your code, you should pull out `if (!isset($Smarty)) { ...etc`to a seperate function so you can simply call `CheckSmartyExists()` in each function – Toby Allen Sep 16 '12 at 08:41
  • Toby, thanks! Well not only this. Other people recommended to get rid of using `Globals` and I have no idea of how to make it right. So far I always get errors. This OO makes me frustrated but I get more understanding everyday but it's so slowly for me. Procedural so much easier.. I heard that you could pass the variable by reference but still have no clue so far. – Ilia Ross Sep 16 '12 at 08:44
  • To get rid of globals, pass them all into your object as an array and then access the passed in object, my suggestion, is just good programming practice, whether oop or procedural. – Toby Allen Sep 16 '12 at 08:46
  • Alright, I will keep trying.. Maybe someone could provide a good working example and explanation. Thanks, pal!:) – Ilia Ross Sep 16 '12 at 08:51
  • Check my first try? What's the problem? http://codepad.org/u98EBoSF – Ilia Ross Sep 16 '12 at 09:26

4 Answers4

2

This looks like you haven't loaded Smarty, that's why the error happens. You need to start by including Smarty before the class starts. If you follow my other config suggestion you should start by including that one as well.

In you Template class, just add the following function:

function defaults() {
    // Don't know if you need the assignes, havn't used Smarty, but if so, insert them here...

    Template::display( Config::get('header_template') ); //header_template set in the Config file
    Template::display( basename($_SERVER['PHP_SELF'], ".php") . '.tpl' );
    Template::display( Config::get('footer_template') ); //footer_template set in the Config file
}

Now you should be able to use it in any file:

$template = new Template();
$template->defaults();

EDIT:

A singleton is in every sense the same as a global, that will keep your same problem. But your problem is that if you try to use one of the Template's static functions you are in the "static" mode, which means the constructor have not been run. And Smarty has not been assigned. If you want to go this road, you can do one of two thinks:

  1. Make the Template a real singleton, meaning set the constructor to private add a function getInstance, that returns a instance of the class, and then use that object to call the functions in it (which should not be static), or

  2. Make all those static functions check if smarty is set, and if it's not, create a new instance of smarty, otherwise use the one that already is instantiated to run its function.

EDIT 2:

Here's the proper way to make a singleton:

class Singleton {
    private static $instance = null;
    // private static $smarty = null;

    private function __construct() {
        //self::$smarty = new Smarty();
    }

    public static function getInstance() {
        if( self::$instance === null ) {
            self::$instance = self();
        }
        return self::$instance;
    }

    public function doSomething() {
        //self::$smarty->doSomething();
    }
}

It's used like this:

$singleton = Singletong::getInstance();
$singleton->doSomething();

I commented out the things you probably want do to to make this a singleton wrapper around a singleton Smarty object. Hope this helps.

EDIT 3:

Here's a working copy of your code:

class Template {
    private static $smarty_instance;
    private static $template_instance;

    private function Template() {
        self::$smarty_instance = new Smarty();
        $this->create();
    }

    public static function getInstance() {
        if( ! isset( self::$template_instance ) ) {
            self::$template_instance = new self();
        }
        return self::$template_instance;
    }

    private function create() {
        self::$smarty_instance->compile_check = true;
        self::$smarty_instance->debugging = false;
        self::$smarty_instance->compile_dir   = "/home/docs/public_html/domain.org/tmp/tpls";
        self::$smarty_instance->template_dir  = "/home/docs/public_html/domain.org";
        return true;
    }

    public function setType($type) {
        self::$smarty_instance->type = $type;
    }

    public function assign($var, $value) {
        self::$smarty_instance->assign($var, $value);
    }

    public function display($filename) {
        self::$smarty_instance->display($filename);
    }

    public function fetch($filename) {
        return self::$smarty_instance->fetch($filename);
    }

    public function defaults($filename) {
        global $user_message;
        global $user_notification;
        global $user_attention;
        global $user_error;

        self::$smarty_instance->assign('user_message', $user_message);
        self::$smarty_instance->assign('user_notification', $user_notification);
        self::$smarty_instance->assign('user_attention', $user_attention);
        self::$smarty_instance->assign('user_error', $user_error);

        self::$smarty_instance->assign('current_page', $filename);

        self::$smarty_instance->display('head.tpl');
        self::$smarty_instance->display($filename . '.tpl');
        self::$smarty_instance->display('footer.tpl');
    }
}

When using this function, you should use it like this:

$template = Template::getInstance();
$template->defaults($filename);

Try it now.

Community
  • 1
  • 1
Krycke
  • 3,106
  • 1
  • 17
  • 21
  • Krycke, my friend, thanks for your advice and following this post!:) I posted Online Example to show what I have tried in changing my original, working code. I'm surely including Smarty! I gave you one vote for 'config suggestion'. I'm adding the link to you and provide fully and perfectly working code but like I said, I think it could be structured better. Please look at present, working code and if you have better ideas on what I have tried please share. Here is the code: http://codepad.viper-7.com/ZJHRs9 . Don't look at the errors in the output on Codepad Viper. Code works fine. – Ilia Ross Sep 16 '12 at 16:00
  • I'm basically trying to create proper PHP Singleton. – Ilia Ross Sep 16 '12 at 16:20
  • Another try http://codepad.org/4jMlLqJz and I get error: `Fatal error: Call to a member function assign() on a non-object` – Ilia Ross Sep 16 '12 at 16:34
  • I liked your suggestion. Could you though provide working example of how I could use my singleton custom class without using `global $Smarty`? I understand that it's technically the same, just out of curiosity as I'm learning on OOP. Don't bother about other global vars. I got this point from the previous post.. Well you know! ;) – Ilia Ross Sep 16 '12 at 16:59
  • I'm completely confused. Was it hard for you to get into OOP? Could you just take my current and working example http://codepad.viper-7.com/ZJHRs9 and re-write it just the same BUT make sure that your example will avoid using `global $Smarty`. But on the rest keep it the same? How to achieve this? What I have in this example it's not Singleton? How would you call it? Many thanks! – Ilia Ross Sep 16 '12 at 18:28
  • Seems like I'm moving on slowly but surely - Have working example.. Not aware fully of how it works. Will post soon.. – Ilia Ross Sep 16 '12 at 18:58
  • 1
    OO is hard to get used to if you haven't worked with it before. You can't really read to understand. You have to do. Just keep to it, and it will all make sense in the end. Working copy above. – Krycke Sep 16 '12 at 19:05
  • Interesting but it doesn't work. It returns fatal error: `Call to a member function assign() on a non-object in`. Why we don't use constructor here? I have finally achieved this and it's working just great, take a look: http://codepad.viper-7.com/4QucIC Could you please not delete your answer but fix it so I could compare? – Ilia Ross Sep 16 '12 at 19:14
  • It seems that the object (Smarty) is not created? – Ilia Ross Sep 16 '12 at 19:20
  • Thanks for providing working example. Even though I had a small problem with loading `$filename` in the way of `$template->defaults($filename);`. On the rest it's fine. – Ilia Ross Sep 17 '12 at 07:15
  • Krycke, you have helped me a lot, so thank you for everything, pal!;) I'm accepting your answer! – Ilia Ross Sep 17 '12 at 07:51
0

You can get current file name in your defaults() function. Use this piece of code:

$currentFile = $_SERVER['REQUEST_URI'];
$parts = explode('/', $currentFile);
$fileName = array_pop($parts);
$viewName = str_replace('.php', '.tpl', $fileName);

$viewName is the name that you need.

Nikola K.
  • 7,093
  • 13
  • 31
  • 39
Mohsen
  • 1
  • Thank you, Mohsen but like I said, I would like to see more concise and well structured implementations. What you have offered I did in one line `basename($_SERVER['PHP_SELF'], ".php");` ;) your example will just make it bigger. Not what I was looking for.. – Ilia Ross Sep 15 '12 at 09:57
0

This is a quick wrapper I made for Smarty, hope it gives you some ideas

class Template extends Smarty
{
     public $template = null;
     public $cache    = null;
     public $compile  = null;

     public function var($name, $value, $cache)
     {
         $this->assign($name, $value, $cache);
     }

     public function render($file, $extends = false)
     {
         $this->prep();

         $pre  = null;
         $post = null;

         if ($extends)
         {
             $pre = 'extends:';
             $post = '|header.tpl|footer.tpl';
         }

         if ($this->prep())
         {
             return $this->display($pre . $file . $post);
         }
      }

      public function prep()
      {
          if (!is_null($this->template))
          {
              $this->setTemplateDir($this->template);

              return true;
          }

          if (!is_null($this->cache))
          {
              $this->setCacheDir($this->cache);
          }

          if (!is_null($this->compile))
          {
              $this->setCompileDir($this->compile);

              return true;
          }

              return false;
      }
}

Then you can use it like this

$view = new Template();

$view->template = 'path/to/template/';
$view->compile  = 'path/to/compile/'
$view->cache    = 'path/to/cache';

$view->assign('hello', 'world');

// or

$view->var('hello', 'world');

$view->render('index.tpl');

//or

$view->render('index.tpl', true); // for extends functionality

I did this kinda fast, but just to show you the basic ways you can use smarty. In a more complete version you could probably want to check to see if compile dir is writable, or if file templates exist etc.

Eli
  • 4,329
  • 6
  • 53
  • 78
  • That's it! Thanks, Eli!! ;) Even though you have syntax errors in your code I followed your idea. I will post mine, the one that I stopped on. Please comment later. – Ilia Ross Sep 17 '12 at 07:16
0

After trying for few days to solve this simple problem, I have finally came up with working and fully satisfying solution. Remember, I'm just a newby in object-oriented programming and that's the main reason why it took so long.

My main idea was not to use global $Smarty in my initial code that worked already fine. I like to use my Smarty as just simple as entering, e.g.: Template::assign('array', $array). To display defaults, I came up with the trivial solution (read my initial post), where now it can be just used Template::defaults(p()); to display or assign anything that is repeated on each page of your project.

For doing that, I personally stopped on the following fully working solution:

function p() {
    return basename($_SERVER['PHP_SELF'], ".php");
}

require('/smarty/Smarty.class.php');

class Template
{
    private static $smarty;
    static function Smarty()
    {
        if (!isset(self::$smarty)) {
            self::$smarty                 = new Smarty();
            self::Smarty()->compile_check = true;
            self::Smarty()->debugging     = false;
            self::Smarty()->plugins_dir   = array(
                '/home/docs/public_html/domain.com/smarty/plugins',
                '/home/docs/public_html/domain.com/extensions/smarty');
            self::Smarty()->compile_dir   = "/home/docs/public_html/domain.com/cache";
            self::Smarty()->template_dir  = "/home/docs/public_html/domain.org";
        }
        return self::$smarty;
    }
    public static function setType($type)
    {
        self::Smarty()->type = $type;
    }
    public static function assign($var, $value)
    {
        self::Smarty()->assign($var, $value);
    }
    public static function display($filename)
    {
        self::Smarty()->display($filename);
    }
    public static function fetch($filename)
    {
        self::Smarty()->fetch($filename);
    }
    public static function defaults($filename)
    {
        Template::assign('current_page_name', $filename);
        Template::display('head.tpl');
        Template::display($filename . '.tpl');
        Template::display('footer.tpl');
    }
}

Please use it if you like it in your projects but leave comments under this post if you think I could improve it or you have any suggestions.

Initial idea of doing all of that was learning and exercising in writing a PHP code in object-oriented style.

Ilia Ross
  • 13,086
  • 11
  • 53
  • 88
  • 1
    looks good, there are many ones to go about a problem. finding out those ways is the fun part as it molds your oop styles down the road. Great job! – Eli Sep 17 '12 at 11:19