PHP template with Flow3 - php

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!

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") ;
}
}

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.

How to model a templating class

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?

How to achieve "require_global"?

Current situation:
I have the current version of my MVC Framework which uses classes as controllers.
I have some "vintage" modules from my old MVC Framework which uses simple, flat includes as controllers.
Much simplified that means:
New Version:
<?PHP
class blaController extends baseController {
private $intVar;
function dosomethingFunction() {
$this->intVar = 123;
$this->view('myView');
}
}
?>
Old Version:
<?PHP
$globalVar = 123;
// view "controllername" is automatically shown
?>
I'm now trying to write a wrapper to be able to use my old controllers in my new MVC without having to rewrite everything. To do so, I have a "wrapper" controller:
class wrapController extends baseController {
function dosomethingFunction() {
require 'old_dosomething.function.php';
$this->view('old_dosomething_view');
}
}
(Once again: This is VERY, VERY simplified - just to get the idea over. Not actual code.)
The problem with that approach is, that the previously global variable $globalVar now only exists inside of the method "dosomethingFunction" and cannot be accessed by the view.
This wouldn't be the case if I could force the require to behave as "in global scope" so that $globalVar would once again be available in global scope.
So: Is there some way to achieve "require_global" or something similar?
(One solution for my problem would be to modify my old controllers to start with a bunch of "global" commands, but I'd prefer a solution where I don't have to change so much of that old code.)
(Note: Please don't tell me that GLOBALS are bad. It totally misses the point of this question. Just accept that it is a requirement to keep some old code working in a newer, cleaner environment.)
You can add local variables defined within dosomethingFunction() to global scope:
class wrapController extends baseController {
function dosomethingFunction() {
require 'old_dosomething.function.php';
//begin added code
$vararr = get_defined_vars();
foreach($vararr as $varName => $varValue)
$GLOBALS[$varName] = $varValue;
//end added code
$this->view('old_dosomething_view');
}
}
Note, that for this to work as expected, you should call require before using any other thing in the function. get_defined_vars() returns only variables from the current scope, so no array_diff hacks are needed.
This is the easiest solution I can think of.
Use the get_defined_vars() function twice and get a diff of each call to determine what variables were introduced by the required file.
Example:
$__defined_vars = get_defined_vars();
require('old_dosomething.function.php');
$__newly_defined_vars = array_diff_assoc($__defined_vars, get_defined_vars());
$GLOBALS = array_merge($GLOBALS, $__newly_defined_vars);
$this->view('old_dosomething_view');
Hmmm, this is an issue I've never before seen. I suppose you could do this
class wrapController extends baseController {
function dosomethingFunction() {
require 'old_dosomething.function.php';
// Force "old" globals into global scope
$GLOBALS['globalVar'] = $globalVar;
$this->view('old_dosomething_view');
}
}
But that's a pretty tedious, manual process as well, depending on how many globals we're talking about. I'll think about this, but I don't know of any "auto-magic" solution off the top of my head.
For anybody interested: My (so far) final version:
class wrapController extends baseController {
function dosomethingFunction() {
// ... do some initialisation stuff ...
$__defined_vars = array_keys(get_defined_vars());
require 'old_dosomething.function.php';
$__newly_defined_vars = array_diff(
array_keys(get_defined_vars()),
$__defined_vars,
array('__defined_vars')
);
foreach ($__newly_defined_vars as $var) {
$GLOBALS[$var] = &$$var;
}
$this->view('old_dosomething_view');
}
}
Ugly, but it works. Thanks for all your great help!
Have you tried Zend_Registry from Zend Framework?
The registry is a container for storing objects and values in the application space. By storing the value in the registry, the same object is always available throughout your application. This mechanism is an alternative to using global storage.
http://framework.zend.com/manual/en/zend.registry.html

Categories