I was coding a TemplateView Class which is replacing #somevariable# placeholders with it's controller set correspondent. ViewHelpers are called via placeholders that simulate a function like #headerFiles()# for example.
The thing missing is a foreach loop definition like for example the smarty
{foreach from=$var key=index item=value} definition.
Until now I was iterating over a database object inside the controller and having a heredok or string concatenation assigned to the view. So there is a lot of html inside the controllers which I think should not be the controllers job.
I already implemented smarty into the app for testing and it's working fine - But when I decide to really use it I would have to change so many things like all partial templates would have to be .tpl files and all template directories would have to be merged ? or in one place and so on.
So my question is:
Are there any good examples or codesnippets I could use to implement a custom foreach loop method ? Before I have to change everything to use smarty. I also want to avoid using plain php like <?php foreach(): endforeach; ?>
UPDATE:
what concerns would one have going this direction ?
In a template:
#foreach array#
if($key !== 'fart'){
echo $val;
}
#endforeach#
class Foreacher {
private $template;
protected $array = array('fart' => 'poop', 'foo', 'bar');
public function __construct($template){
$this->template = file_get_contents($template);
}
public function parse(){
if(preg_match_all('/#foreach ([\w]+)#(.*?)#endforeach#/is', $this->template, $matches, PREG_SET_ORDER)){
$var = $matches[0][1];
$freach = "<?php foreach(\$this->$var as \$key => \$val){";
$freach .= $matches[0][2];
$freach .= "} ?>";
$parsed = str_replace($matches[0][0], $freach, $this->template);
$this->render($parsed);
}
}
public function __get($var){
if(isset($this->$var)){
return $this->$var;
}
return null;
}
protected function render($string){
$tmp = tmpfile();
fwrite($tmp, $string);
fseek($tmp, 0);
ob_start();
$file = stream_get_meta_data($tmp);
include $file['uri'];
$data = ob_get_clean();
fclose($tmp);
echo $data;
}
}
Related
I have a simple template engine that works fine the simple template, but I don't know how to adapt it to make it work with loops :
class Template {
public $template;
function getFile($file) {
$this->template = file_get_contents($file);
}
function set($tag, $content) {
$this->template = str_replace("{".$tag."}", $content, $this->template);
}
function ouput() {
eval("?>".$this->template."<?");
}
}
That's the loop I want to parse and display:
{{#each Stuff}}
{{Thing}} are {{Desc}}
{{/each}}
I dont want use any SMARTY or Twig engine.
Any idea please?
Ok, keep in mind this is just for learning purposes. You can't ask on SO for the whole code, you need to try and post question about your tries.
This code parse a string for a foreach and then executes it:
<?php
$var = array(2, 4);
$str = 'for i in var';
$a = explode(' ', $str);
foreach (${$a[3]} as $i => $value)
{
echo $value;
}
Read this part from PHP docs to understand what i did.
i want to use different objects inside the template. different objects are created on different section of the site.
currently my code is
public function notify ($template, $info)
{
ob_start();
include $template;
$content = ob_get_clean();
//... more further code
}
as you see $info parameter. i dont want to use $info inside templates, but i was to use $photo, $admin or whatever is passed to it.
which i use like
// for feed
$user->notify('email_template_feed.php', $feed);
// for new photo - i would also like to use $user inside templates
$user->notify('email_template_photo.php', $photo);
how can i do that? cant use global, because is inside function and function is getting called dynamically on different locations/sections of the site, which can further extend.
You can't.
Solution 1
Instead you could use an array and extract its values:
public function notify ($__template, array $info)
{
ob_start();
extract($info);
include $__template;
$content = ob_get_clean();
//... more further code
}
Example 1
if you call it with:
$user->notify('email_template_feed.php', array('feed' => $feed));
and inside the template email_template_feed.php:
...
<?=$feed?>
...
it will print:
...
FEED
...
Solution 2
You could also pass the name of the variable as third parameter:
public function notify ($template, $info, $name)
{
ob_start();
$$name = $info;
unset($info);
include $template;
$content = ob_get_clean();
//... more further code
}
Example 2
You could then call it via:
$user->notify('email_template_feed.php', $feed, 'feed');
For a group project I am trying to create a template engine for PHP for the people less experienced with the language can use tags like {name} in their HTML and the PHP will replace that tag with a predefined variable from an array. As well as supporting loops.
This is well beyond the expectations of the project, but as I have experience with PHP I thought it would be a good challenge to keep me busy!
My main questions are, how do I do the loop part of the parser and is this the best way to implement such a system. Before you just recommend an existing template system, I would prefer to create it myself for experience and because everything in our project has to be our own.
At the moment the basic parsing is done with regex and preg_replace_callback, it checks if $data[name] exists and if it does replaces it.
I have tried to do the loop a variety of different ways but am not sure if I am on the correct track!
An example if the data the parsing engine was given is:
Array
(
[title] => The Title
[subtitle] => Subtitle
[footer] => Foot
[people] => Array
(
[0] => Array
(
[name] => Steve
[surname] => Johnson
)
[1] => Array
(
[name] => James
[surname] => Johnson
)
[2] => Array
(
[name] => josh
[surname] => Smith
)
)
[page] => Home
)
And the page it was parsing was something like:
<html>
<title>{title}</title>
<body>
<h1>{subtitle}</h1>
{LOOP:people}
<b>{name}</b> {surname}<br />
{ENDLOOP:people}
<br /><br />
<i>{footer}</i>
</body>
</html>
It would produce something similar to:
<html>
<title>The Title</title>
<body>
<h1>Subtitle</h1>
<b>Steve</b> Johnson<br />
<b>James</b> Johnson<br />
<b>Josh</b> Smith<br />
<br /><br />
<i>Foot</i>
</body>
</html>
Your time is incredibly appreciated with this!
Many thanks,
P.s. I completely disagree that because I am looking to create something similar to what already exists for experience, my well formatted and easy to understand question gets down voted.
P.p.s It seems there is a massive spread of opinions for this topic, please don't down vote people because they have a different opinion to you. Everyone is entitled to their own!
A simple approach is to convert the template into PHP and run it.
$template = preg_replace('~\{(\w+)\}~', '<?php $this->showVariable(\'$1\'); ?>', $template);
$template = preg_replace('~\{LOOP:(\w+)\}~', '<?php foreach ($this->data[\'$1\'] as $ELEMENT): $this->wrap($ELEMENT); ?>', $template);
$template = preg_replace('~\{ENDLOOP:(\w+)\}~', '<?php $this->unwrap(); endforeach; ?>', $template);
For example, this converts the template tags to embedded PHP tags.
You'll see that I made references to $this->showVariable(), $this->data, $this->wrap() and $this->unwrap(). That's what I'm going to implement.
The showVariable function shows the variable's content. wrap and unwrap is called on each iteration to provide closure.
Here is my implementation:
class TemplateEngine {
function showVariable($name) {
if (isset($this->data[$name])) {
echo $this->data[$name];
} else {
echo '{' . $name . '}';
}
}
function wrap($element) {
$this->stack[] = $this->data;
foreach ($element as $k => $v) {
$this->data[$k] = $v;
}
}
function unwrap() {
$this->data = array_pop($this->stack);
}
function run() {
ob_start ();
eval (func_get_arg(0));
return ob_get_clean();
}
function process($template, $data) {
$this->data = $data;
$this->stack = array();
$template = str_replace('<', '<?php echo \'<\'; ?>', $template);
$template = preg_replace('~\{(\w+)\}~', '<?php $this->showVariable(\'$1\'); ?>', $template);
$template = preg_replace('~\{LOOP:(\w+)\}~', '<?php foreach ($this->data[\'$1\'] as $ELEMENT): $this->wrap($ELEMENT); ?>', $template);
$template = preg_replace('~\{ENDLOOP:(\w+)\}~', '<?php $this->unwrap(); endforeach; ?>', $template);
$template = '?>' . $template;
return $this->run($template);
}
}
In wrap() and unwrap() function, I use a stack to keep track of current state of variables. Precisely, wrap($ELEMENT) saves the current data to the stack, and then add the variables inside $ELEMENT into current data, and unwrap() restores the data from the stack back.
For extra security, I added this extra bit to replace < with PHP echos:
$template = str_replace('<', '<?php echo \'<\'; ?>', $template);
Basically to prevent any kind of injecting PHP codes directly, either <?, <%, or <script language="php">.
Usage is something like this:
$engine = new TemplateEngine();
echo $engine->process($template, $data);
This isn't the best method, but it is one way it could be done.
Ok firstly let me explain something tell you that PHP IS A TEMPLATE PARSER.
Doing what your doing is like creating a template parser from a template parser, pointless and to be quite frank it iterates me that template parser's such as smarty have become so well at a pointless task.
What you should be doing is creating a template helper, not a parser as there redundant, in programming terms a template file is referred to as a view and one of the reasons they was given a particular name is that people would know there separate from Models, Domain Logic etc
What you should be doing is finding a way to encapsulate all your view data within your views themselves.
An example of this is using 2 classes
Template
TemplateScope
The functionality of the template class is for the Domain Logic to set data to the view and process it.
Here's a quick example:
class Template
{
private $_tpl_data = array();
public function __set($key,$data)
{
$this->_tpl_data[$key] = $data;
}
public function display($template,$display = true)
{
$Scope = new TemplateScope($template,$this->_tpl_data); //Inject into the view
if($display === true)
{
$Scope->Display();
exit;
}
return $Scope;
}
}
This is extreamly basic stuff that you could extend, oko so about the Scope, This is basically a class where your views compile within the interpreter, this will allow you to have access to methods within the TemplateScope class but not outside the scope class, i.e the name.
class TemplateScope
{
private $__data = array();
private $compiled;
public function __construct($template,$data)
{
$this->__data = $data;
if(file_exists($template))
{
ob_start();
require_once $template;
$this->compiled = ob_get_contents();
ob_end_clean();
}
}
public function __get($key)
{
return isset($this->__data[$key]) ? $this->__data[$key] : null;
}
public function _Display()
{
if($this->compiled !== null)
{
return $this->compiled;
}
}
public function bold($string)
{
return sprintf("<strong>%s</strong>",$string);
}
public function _include($file)
{
require_once $file; // :)
}
}
This is only basic and not working but the concept is there, Heres a usage example:
$Template = new Template();
$Template->number = 1;
$Template->strings = "Hello World";
$Template->arrays = array(1,2,3,4)
$Template->resource = mysql_query("SELECT 1");
$Template->objects = new stdClass();
$Template->objects->depth - new stdClass();
$Template->display("index.php");
and within template you would use traditional php like so:
<?php $this->_include("header.php") ?>
<ul>
<?php foreach($this->arrays as $a): ?>
<li><?php echo $this->bold($a) ?></li>
<?php endforeach; ?>
</ul>
This also allows you to have includes within templates that still have the $this keyword access to then include themselves, sort of recursion (but its not).
Then, don't pro-grammatically create a cache as there is nothing to be cached, you should use memcached which stores pre compiled source code within the memory skipping a large portion of compile / interpret time
If I'm not worried about caching or other advanced topics that would push me to an established template engine like smarty, I find that PHP itself is a great template engine. Just set variables in a script like normal and then include your template file
$name = 'Eric';
$locations = array('Germany', 'Panama', 'China');
include('templates/main.template.php');
main.tempate.php uses an alternative php tag syntax that is pretty easy for non php people to use, just tell them to ignore anything wrapped in a php tag :)
<h2>Your name is <?php echo $name; ?></h2>
<?php if(!empty($locations)): ?>
<ol>
<?php foreach($locations as $location): ?>
<li><?php echo $location; ?></li>
<?php endforeach; ?>
</ol>
<?php endif; ?>
<p> ... page continues ... </p>
I had a very basic answer to something KINDA like this back before I started using DOMXPath.
class is something like this (not sure if it works quite like what you want but food for thought as it works very simple
<?php
class template{
private $template;
function __CONSTRUCT($template)
{
//load a template
$this->template = file_get_contents($template);
}
function __DESTRUCT()
{
//echo it on object destruction
echo $this->template;
}
function set($element,$data)
{
//replace the element formatted however you like with whatever data
$this->template = str_replace("[".$element."]",$data,$this->template);
}
}
?>
with this class you would just create the object with whatever template you wanted and use the set function to place all your data.
simple loops after the object is created can probably accomplish your goal.
good luck
Smarty :) ...
php:
$smarty->assign("people",$peopleArray)
smarty template:
{foreach $people as $person}
<b>{$person.name}</b> {$person.surname}<br />
{/foreach}
Couple other things to do but that's what smarty will be like essentially.
Use Smarty.
I need to make a small and simple php template engine I searched a lot and many of them were too complex to understand and I don't want to use smarty and other similar engines, I have got some idea from Stack Overflow like this:
$template = file_get_contents('file.html');
$array = array('var1' => 'value',
'txt' => 'text');
foreach($array as $key => $value)
{
$template = str_replace('{'.$key.'}', $value, $template);
}
echo $template;
Now instead of echo the template I just want to add include "file.html" and it will display the file with correct variable values and I want to put the engine in a separate place and just include it in the template what I want to use it declare the array and at the end include the html file like phpbb. Sorry I am asking to much but can anyone just explain the basic concept behind this?
EDIT: Well let me be frank i am making a forum script and i have got tons of ideas for it but i want make its template system like phpbb so i need a separate template engine custom one if you can help then please you are invited to work with me. sorry for the ad.. :p
file.html:
<html>
<body>
<h3>Hi there, <?php echo $name ?></h3>
</body>
</html>
file.php:
<?php
$name = "Keshav";
include('file.html');
?>
Doesn't get simpler than this. Yes, it uses global variables, but if simple is the name of the game, this is it. Simply visit 'http://example.com/file.php' and off you go.
Now, if you want the user to see 'file.html' in the browser's address bar, you'd have to configure your webserver to treat .html files as PHP scripts, which is a little more complicated, but definitely doable. Once that's done, you can combine both files into a single one:
file.html:
<?php
$name = "Keshav";
?>
<html>
<body>
<h3>Hi there, <?php echo $name ?></h3>
</body>
</html>
What if, for a script easier to maintain, you move those to functions?
something like this:
<?php
function get_content($file, $data)
{
$template = file_get_contents($file);
foreach($data as $key => $value)
{
$template = str_replace('{'.$key.'}', $value, $template);
}
return $template;
}
And you can use it this way:
<?php
$file = '/path/to/your/file.php';
$data = = array('var1' => 'value',
'txt' => 'text');
echo get_content($file, $data);
Once you iron out all bugs, fix huge performance problem you're getting yourself into, you'll end up with template engine just like Smarty and otheres.
Such find'n'replace approach is much slower than compilation to PHP. It does not handle escaping very well (you'll run into XSS problems). It will be quite difficult to add conditions and loops, and you will need them sooner or later.
<?php
class view {
private $file;
private $vars = array();
public function __construct($file) {
$this->file = $file;
}
public function __set($key, $val) {
$this->vars[$key] = $val;
}
public function __get($key, $val) {
return (isset($this->vars[$key])) ? $this->vars[$key] : null;
}
public function render() {
//start output buffering (so we can return the content)
ob_start();
//bring all variables into "local" variables using "variable variable names"
foreach($this->vars as $k => $v) {
$$k = $v;
}
//include view
include($this->file);
$str = ob_get_contents();//get teh entire view.
ob_end_clean();//stop output buffering
return $str;
}
}
Here's how to use it:
<?php
$view = new view('userprofile.php');
$view->name = 'Afflicto';
$view->bio = "I'm a geek.";
echo $view->render();
I'm sure there's a very easy explanation for this. What is the difference between this:
function barber($type){
echo "You wanted a $type haircut, no problem\n";
}
call_user_func('barber', "mushroom");
call_user_func('barber', "shave");
... and this (and what are the benefits?):
function barber($type){
echo "You wanted a $type haircut, no problem\n";
}
barber('mushroom');
barber('shave');
Always use the actual function name when you know it.
call_user_func is for calling functions whose name you don't know ahead of time but it is much less efficient since the program has to lookup the function at runtime.
Although you can call variable function names this way:
function printIt($str) { print($str); }
$funcname = 'printIt';
$funcname('Hello world!');
there are cases where you don't know how many arguments you're passing. Consider the following:
function someFunc() {
$args = func_get_args();
// do something
}
call_user_func_array('someFunc',array('one','two','three'));
It's also handy for calling static and object methods, respectively:
call_user_func(array('someClass','someFunc'),$arg);
call_user_func(array($myObj,'someFunc'),$arg);
the call_user_func option is there so you can do things like:
$dynamicFunctionName = "barber";
call_user_func($dynamicFunctionName, 'mushroom');
where the dynamicFunctionName string could be more exciting and generated at run-time. You shouldn't use call_user_func unless you have to, because it is slower.
With PHP 7 you can use the nicer variable-function syntax everywhere. It works with static/instance functions, and it can take an array of parameters. More info at https://trowski.com/2015/06/20/php-callable-paradox
$ret = $callable(...$params);
I imagine it is useful for calling a function that you don't know the name of in advance...
Something like:
switch($value):
{
case 7:
$func = 'run';
break;
default:
$func = 'stop';
break;
}
call_user_func($func, 'stuff');
There are no benefits to call it like that, the word user mean it is for multiple user, it is useful to create modification without editing in core engine.
it used by wordpress to call user function in plugins
<?php
/* main.php */
require("core.php");
require("my_plugin.php");
the_content(); // "Hello I live in Tasikmalaya"
...
<?php
/* core.php */
$listFunc = array();
$content = "Hello I live in ###";
function add_filter($fName, $funct)
{
global $listFunc;
$listFunc[$fName] = $funct;
}
function apply_filter($funct, $content)
{
global $listFunc;
foreach ($listFunc as $key => $value)
{
if ($key == $funct and is_callable($listFunc[$key]))
{
$content = call_user_func($listFunc[$key], $content);
}
}
echo $content;
}
function the_content()
{
global $content;
$content = apply_filter('the_content', $content);
echo $content;
}
....
<?php
/* my_plugin.php */
function changeMyLocation($content){
return str_replace('###', 'Tasikmalaya', $content);
}
add_filter('the_content', 'changeMyLocation');
in your first example you're using function name which is a string. it might come from outside or be determined on the fly. that is, you don't know what function will need to be run at the moment of the code creation.
When using namespaces, call_user_func() is the only way to run a function you don't know the name of beforehand, for example:
$function = '\Utilities\SearchTools::getCurrency';
call_user_func($function,'USA');
If all your functions were in the same namespace, then it wouldn't be such an issue, as you could use something like this:
$function = 'getCurrency';
$function('USA');
Edit:
Following #Jannis saying that I'm wrong I did a little more testing, and wasn't having much luck:
<?php
namespace Foo {
class Bar {
public static function getBar() {
return 'Bar';
}
}
echo "<h1>Bar: ".\Foo\Bar::getBar()."</h1>";
// outputs 'Bar: Bar'
$function = '\Foo\Bar::getBar';
echo "<h1>Bar: ".$function()."</h1>";
// outputs 'Fatal error: Call to undefined function \Foo\Bar::getBar()'
$function = '\Foo\Bar\getBar';
echo "<h1>Bar: ".$function()."</h1>";
// outputs 'Fatal error: Call to undefined function \foo\Bar\getBar()'
}
You can see the output results here: https://3v4l.org/iBERh it seems the second method works for PHP 7 onwards, but not PHP 5.6.