How to model a templating class - php

I am writing a basic templating class for my own project. The basic usage is this:
$template = new Template('template_file.php');
$template->assignVariable('pageTitle', 'Home page');
$template->render();
Contents of 'template_file.php':
<?php print $pageTitle; ?>
This is what template class does step by step:
Stores variables in a private array when assignVariable method is called
When render method is called, extracts stored variables, includes template file in a ob_start() and ob_end_clean() block. Stores output in a variable with ob_get_contents() and then prints stored output.
I know this is a very simple templating class but works as expected. The question is should I delegate the including the template file to another class? I had this question when I was writing the unit tests for this class. I thought that file system interaction should be encapsulated. What do you think? If you think that it should not, how can I mock including a file in my tests?
Maybe I just pass the contents of the template file to the class like this:
$templateContent = file_get_contents('template_file.php');
$template = new Template($templateContent);
...
Edit: I decided to encapsulate the input process of template class for the sake of writing better unit tests and encapsulation. But as johannes pointed out, I needed to use eval() for that purpose which seemed not right. Johannes pointed me to the direction of stream wrappers for mocking the including in unit tests. But that inspired a new idea on me. Here is what I am going to do; I will continue to use include() in my template class but this time with stream wrappers. I will pass protocol handler to my template class while initializing it. This way I can create my own stream wrappers for fetching template data from database or using a local variable. Here are the examples:
$template = new Template('file://template_file.php');
stream_wrapper_register('database', 'My_Database_Stream');
$template = new Template('database://templates/3'); // templates table, row id 3
stream_wrapper_register('var', 'My_Var_Stream');
$myTemplate = '<?php print "Hello world!"; ?>';
$template = new Template('var://myTemplate');
I have already implement custom stream wrapper for local variables. Here it is:
class My_Var
{
protected $position;
protected $variable;
function stream_open($path, $mode, $options, &$openedPath) {
$url = parse_url($path);
global $$url['host'];
$this->variable = $$url['host'];
$this->position = 0;
return true;
}
public function stream_read($count) {
$ret = substr($this->variable, $this->position, $count);
$this->position = strlen($ret);
return $ret;
}
public function stream_eof() {
return $this->position >= strlen($this->variable);
}
}
stream_wrapper_register('var', 'My_Var');
$myvar = '<?php print "mert"; ?>';
include 'var://myvar';
exit;

I always liked the approach of this fellow:
http://www.massassi.com/php/articles/template_engines/
This approach takes advantage of the fact that PHP has started as a template engine. (The author also notes that it is silly to write a bloated template engine in PHP, when it is in fact itself a templating engine.) It might not really answer your question directly, but maybe it helps you.

By passing the contents using file_get_contents() and such you have to use eval() for execution which is bad in multiple ways. One of the most relevant here is that an opcode cache can't cache the file then. Doing an include('template_file.php'); let's APC or others cache the compiled script.

Irrespective of the evils of eval vs using a include, to answer your question, I'd have to agree and use a separate class to encapsulate the I/O aspect of your solution.
Whilst this might seem like overkill (as it will be in most instances), this is probably the only sane way to provide the isolation of control/dependency injection that you're after.

The question is should I delegate the including the template file to another class?
The question is, why not?

Related

Are static functions any better than globals? [duplicate]

I'm creating a basic framework in PHP. I need to pass data for the current page into different functions, allow them to modify and save it, and then pass it back to the page to be displayed. I was originally planning on storing the data in a global variable like $GLOBALS['data'], but I'm starting to think that using a global is a bad idea. So I'm thinking that instead I will put a static variable in the system class, and access it using system::$data. So, my question is, which would be better and why?
This:
$GLOBALS['data'] = array();
$GLOBALS['data']['page_title'] = 'Home';
echo $GLOBALS['data']['page_title'];
Or this:
class system
{
public static $data = array()
}
function data($new_var)
{
system::$data = array_merge(system::$data, $new_var);
}
data(array('page_title' => 'Home'));
echo system::$data['page_title'];
There really is no difference between a global variable and a public static variable. The class variable is namespaced a tiny bit better, but that hardly makes any difference. Both are accessible anywhere at any time and both are global state.
As it happens, I just wrote an exhaustive article on the subject:
How Not To Kill Your Testability Using Statics
So, my question is, which would be better and why?
You already sense that there is some problem putting this all into globals. Although you have developed some thoughts to encapsulate things into a class.
I think that is a good starting point. Let's add some more spice to the cooking to get this more fluent at the beginning:
$data = new ArrayObject(array());
$data['page_title'] = 'Home';
You have created an object now that you can pass along containing your data. Just pass $data to the area's where it's needed. No global or global static variable needed.
You can even make that type more concrete later on by extending from ArrayObject with your own type.
For the record.
Pro of static:
Clarity of the code. For example:
function fn() {
System::data()
}
versus
function fn() {
global $system;
$system->data()
}
Cons of static:
If you are using psr-4 then you must add (and include) a new class (and a new file). It impacts the performance even if you use opcache (opcache aleviates it but it's not magic).
You must define a block of code.

Using clousers to handle templates in PHP

There is like a million Template Engine for PHP (Blade, Twig, Smarty, Mustache, ...), and i just hate the idea of creating a new syntax and compiler to write PHP inside HTML! I think it's just not smart (but this isn't what i am here to discuss :) ), what is wrong with writing PHP+HTML the usual way - not for logic - you know, all the variables and loops and defines you wanna use without this {{% %}} or that {:: ::} ! At least for performance sake!
Now, i am using Laravel these days , and it's awesome; it offers (besides Blade and any other 3rd party engine) a plain PHP templates system that uses ob_start/include/ob_get_clean and eval. I was very happy to know that i can avoid learning a new syntax to write PHP inside HTML.
Here is what i am suggesting; what about instead of using ob_* functions with include, we use Closures ? Here is a simple class i put together just to make a point:
class Templates{
static public $templates = array();
static public function create($name, $code){
self::$templates[$name] = $code;
}
static public function run($name, $data){
if(!isset(self::$templates[$name]) || !is_callable(self::$templates[$name])) return false;
return call_user_func(self::$templates[$name], $data);
}
}
And here is how to use it:
Templates::create('test', function($data){
return 'Hi '.$data['name'].' ! ';
});
for($i =0; $i < 10; $i++){
print Templates::run('test', array('name' => 'Jhon'));
}
I think this way is much better, since i wont need to do any output buffering or use eval. And to "separate concerns" here, we can put the Templates::create code in a separate file to keep things clean, in fact this way things can become more simple and elegant; we can make another method to load the template file:
static public function load($name){
self::create($name, include($name.'.php'));
}
And the content of the template file will be as simple as this:
return function($data){
return 'Hi '.$data['name'].' ! ';
};
What do you think of this ? Is there any problems with the approach or the performance of such use of Closures ?
I do not think there are any problems besides that if you put all closure functions into array, that would possibly mean that functions are kinda doing basically the same stuff.
What I mean by this:
In your example you have your functions accepting only 1 parameter. So, not to create a mess all functions you create would accept the same set of parameters and return the same type of data.
However when declared apart, functions may be supposed to do something different and unique.
Why such a solution is suitable: when using some engines, there may be a lot of different functions declared already. To resolve the conflict, they can be "hidden" inside arrays.
Also, some people even say that anonymous functions can be generally better in case of performance. But we have to test that first: to call a function you:
Call a static function run
Check a function for existence
Check a function for callability
And then use call_user_func which returns the return of your function. So, 3x return.
Update
My recomendations for you code:
Make all possible checks only when creating a function. That will greatly buff performance.
static public function create($name, $code){
if (!isset(self::$templates[$name])){
if (is_callable($code)){
self::$templates[$name] = $code ;
} else {
//func is not callable, Throw an exception.
}
} else {
//function already exists. Throw an exception.
}
}
That way you just can have 2x increase in performance:
static public function run($name, $data){
if (isset(self::$templates[$name])){
self::$templates[$name]($data); //Just make a straight call
} else {
//throw new Exception(0, "The func is not defined") ;
}
}

Dynamically creating classes

I have been using active record for quite a while, and I wanted a little change of scenery, some developer friends suggested looking into ORM, all of the ORM projects I have looked at require a separate class extending the ORM class.
My question is: is there any way to dynamically create a class using PHP from within a function without eval?
This is what I have:
<?php
class Constructor
{
function new_class($class)
{
$myself = get_called_class();
eval("class {$class} extends {$myself} { }");
}
function say_hi()
{
$class = get_called_class();
echo "Hi, {$class}!";
}
}
$constructor = new Constructor;
$constructor->new_class("Greeter");
$greeter = new Greeter;
$greeter->say_hi(); // Hi, Greeter!
But, my client informs me that eval is blocked on his environment due to him being on shared hosting.
You probably don't want to do that. But as a workaround, you could use the same approach as via eval(), but once you have constructed the string which you would feed to eval you just write it out as a file and include it again.
Something like this:
function my_eval($str)
{
$filename = uniqid().'.tmp';
file_put_contents ($filename, $str);
include $filename;
unlink ($filename);
}
I've written this from memory and not tested it, but I think it should do the trick. Only caveat I'd see right now is that you'd still essentially be doing the same as eval(), and this variant wouldn't allow you to create variables in the same scope as the calling context (although you could use $GLOBALS[] to get around that for global scope variables).

Global vs static variables in PHP

I'm creating a basic framework in PHP. I need to pass data for the current page into different functions, allow them to modify and save it, and then pass it back to the page to be displayed. I was originally planning on storing the data in a global variable like $GLOBALS['data'], but I'm starting to think that using a global is a bad idea. So I'm thinking that instead I will put a static variable in the system class, and access it using system::$data. So, my question is, which would be better and why?
This:
$GLOBALS['data'] = array();
$GLOBALS['data']['page_title'] = 'Home';
echo $GLOBALS['data']['page_title'];
Or this:
class system
{
public static $data = array()
}
function data($new_var)
{
system::$data = array_merge(system::$data, $new_var);
}
data(array('page_title' => 'Home'));
echo system::$data['page_title'];
There really is no difference between a global variable and a public static variable. The class variable is namespaced a tiny bit better, but that hardly makes any difference. Both are accessible anywhere at any time and both are global state.
As it happens, I just wrote an exhaustive article on the subject:
How Not To Kill Your Testability Using Statics
So, my question is, which would be better and why?
You already sense that there is some problem putting this all into globals. Although you have developed some thoughts to encapsulate things into a class.
I think that is a good starting point. Let's add some more spice to the cooking to get this more fluent at the beginning:
$data = new ArrayObject(array());
$data['page_title'] = 'Home';
You have created an object now that you can pass along containing your data. Just pass $data to the area's where it's needed. No global or global static variable needed.
You can even make that type more concrete later on by extending from ArrayObject with your own type.
For the record.
Pro of static:
Clarity of the code. For example:
function fn() {
System::data()
}
versus
function fn() {
global $system;
$system->data()
}
Cons of static:
If you are using psr-4 then you must add (and include) a new class (and a new file). It impacts the performance even if you use opcache (opcache aleviates it but it's not magic).
You must define a block of code.

PHP template with Flow3

Templating with Flow3 is done using Fluid templating language.
Is there an integrated/easy solution to use PHP templates instead? (without having to write a template renderer myself)
(to avoid misunderstandings: by PHP template I mean HTML code mixed with PHP)
The whole point of fluid is not to do this. You can write own viewhelpers for that.
A fast workaround would be to write a php viewhelper which uses eval(). But that would be really bad from a security point of view.
Your controller has a protected property, that defaults to Fluid, changing it to something else is really easy:
protected $viewFormatToObjectNameMap = array('json' => '\TYPO3\FLOW3\MVC\View\JsonView');
Now take a look at the EmptyController in the same directory: \TYPO3\FLOW3\MVC\View\
You can use this as start. What you basically have to do is to satisfy an assign method, the way how you can transfer variables to your theme.
Next, come up with a few standardizations (e.g. the Template-Files have to be in the folder \Resources\Private\Templates\ControllerName\ActionName.phtml, note the file extension) and say which variable will be available there (e.g. $view).
Now a very basic approach would be:
protected $view = array();
public function assign($key, $value) {
$this->view[$key] = $value;
}
public function render() {
$this->controllerContext->getResponse()->setHeader('Content-Type', 'text/html');
$view = $this->view;
ob_start();
include_once($this->getTemplatePathAndFilename());
$output = ob_get_contents();
ob_end_clean();
return $output;
}
}
For the $this->getTemplatePathAndFilename() function, you can use the code from the TYPO3\Fluid\View\TemplateView->getTemplatePathAndFilename() method and adjust it to your needs.
In your template files, you can now use the $view variable.
I'm at my parents for easter and they lack a runtime environment, so this is not tested and you'll likely run into problems, but this should get you started!

Categories