PHP including nested templates refuses to render master template on some pages - php

I have a PHP site that is using this piece of code to render content pages inside the master template.
function render($template, $vars = array(), $supertemplates = array()) {
if (is_array($vars)) extract($vars); // Extract the vars to local namespace
else $content = $vars;
ob_start(); // Start output buffering
include($template.'.tpl.php'); // Include the file
$content = ob_get_contents(); // Get the content of the buffer
ob_end_clean(); // End buffering and discard
foreach ($supertemplates as $supertemplate)
$content = render($supertemplate, array('content' => $content));
return $content; // Return the content
}
The application itself is a huge file index.php (in the root of the site) with a router that takes an argument and then executes the appropriate function.
This is the list function
function list() {
//Get some data from the database ($data)
print render('templates/_list', array('data' => $data), array('templates/master'));
exit;
}
The list template then renders another template inside itself.
But currently the only html sent to the browser, is the list template, the mastertemplate is not even sent to the browser. I have seen this a few times before, but it would usually go away after a browser refresh.
Do you have any idea why this is happening?
This only happens on some templates, not all, but i have not been able to find out why that is, as i don't belive there are any major differences in what the pages that work, and the one that dont' does.

Related

Return html response from PHP page in to variable

I am trying to generate an email with some HTML that is created via another PHP file.
Email generating file is run by a cron running every hour.
Another file exists that generates the HTML required for the email.
The HTML generating file does not have a function that I can call, for instance:
$emailBody = generateHTML($id);
The HTML generating file was designed to be included on a page that you wished to display the HTML, for instance:
include "htmlGenerator.php";
My question is: How can I get what the htmlgenerator.php file returns in to a variable, ready to be pushed in to an email.
Apologies if this is not very clear, I will be happy to answer any questions.
Thanks in advance!
If I understood what you said, you can use output buffering,
so that you can get the output of htmlGenerator.php
For example:
ob_start();
// as an example
echo "Hello World";
// or in our case
include "htmlGenerator.php";
$result = ob_get_contents();
ob_end_clean();
You can also create a simple function like this:
function include_output($filename)
{
ob_start();
include $filename;
$contents = ob_get_contents();
ob_end_clean();
return $contents;
}
$mycontent = include_output("htmlGenerator.php");
Read the php documentation for further details.
Check out the built-in functions ob_start and ob_get_clean.
You can then do something like:
ob_start();
include( "file.php");
$var = ob_get_clean();

(PHP) How to pass the previous INCLUDE / REQUIRE calls into child files?

I'm creating my own Templating Script by using the ob_get_contents() as a core method. By using it, it can render out the other files, calling from the one single file.
Just like, lets assume we have 4 files:
index.php
header.html
footer.html
functions.php
index.php will call and render the contents of other files (2 html files here). By using the following codes:
//index.php
function render($file) {
if (file_exists($file)) {
ob_start();
include($file);
$content = ob_get_contents();
ob_end_clean();
return $content;
}
}
echo render('header.html');
echo render('footer.html');
But (for example) when header.html contains a call include('functions.php'), the included file (functions.php) can't be used again in footer.html. I mean, i have to make a include again in footer.html. So here, the line include('functions.php') have to be containing in both of files.
How to include() a file without calling it again from child files?
When you use ob_start() (Output buffering), you end up only with the output of the file, meaning file executed the output is returned by ob_get_content(). As only output is returned that other file is unaware of the includes.
So the answer is: you can't do it with output buffering. Or include your files before ob_start with include_once.
That could work like something like this:
//index.php
function render($file) {
if(!isset($GLOBALS['included'])) {
$GLOBALS['included'] = array();
}
if (!in_array($file, $GLOBALS['included']) && file_exists($file)) {
ob_start();
include($file);
$content = ob_get_contents();
ob_end_clean();
$GLOBALS['included'][] = $file;
return $content;
}
}
echo render('header.html');
echo render('footer.html');
Alternatively you could use include_once (include_once $file;)and PHP will do it for you.
Though I suggest you just make sure the file loading structure is in such shape that these events never happen.

fetch templates from database/string

I store my templates as files, and would like to have the opportunity to store them also in a MySql db.
My template System
//function of Template class, where $file is a path to a file
function fetch() {
ob_start();
if (is_array($this->vars)) extract($this->vars);
include($file);
$contents = ob_get_contents();
ob_end_clean();
return $contents;
}
function set($name, $value) {
$this->vars[$name] = is_object($value) ? $value->fetch() : $value;
}
usage:
$tpl = & new Template('path/to/template');
$tpl->set('titel', $titel);
Template example:
<h1><?=titel?></h1>
<p>Lorem ipsum...</p>
My approach
Selecting the the template from the database as a String
what i got is like $tpl = "<h1><?=$titel? >...";
Now I would like to pass it to the template system, so I extended my constructor and the fetch function:
function fetch() {
if (is_array($this->vars)) extract($this->vars);
ob_start();
if(is_file($file)){
include($file);
}else{
//first idea: eval ($file);
//second idea: print $file;
}
$contents = ob_get_contents();
ob_end_clean();
return $contents;
}
'eval' gives me an Parsing exception, because it interprets the whole String as php, not just the php part.
'print' is really strange: It doesn't print the staff between , but I can see it in the source code of the page. php function are beeing ignored.
So what should I try instead?
Maybe not the best solution, but its simple and it should work:
fetch your template from the db
write a file with the template
include this file
(optional: delete the file)
If you add a Timestamp column to your template table, you can use the filesystem as a cache. Just compare the timestamps of the file and the database to decide if its sufficient to reuse the file.
If you prepend '?>' to your eval, it should work.
<?php
$string = 'hello <?php echo $variable; ?>';
$variable = "world";
eval('?>' . $string);
But you should know that eval() is a rather slow thing. Its resulting op-code cannot be cached in APC (or similar). You should find a way to cache your templates on disk. For one you wouldn't have to pull them from the database every time they're needed. And you could make use of regular op-code caching (done transparently by APC).
Every time I see some half-baked home-grown "template engine", I ask myself why the author did not rely on one of the many existing template engines out there? Most of them have already solved most of the problems you could possible have. Smarty (and Twig, phpTAL, …) make it a real charme to pull template sources from wherever you like (while trying to maintain optimal performance). Do you have any special reasons for not using one of these?
I would do pretty much the same thing as tweber except I would prefer depending on the local file timestamps rather than the DB.
Something like this: Each file has a TTL ( expiration time ) of lets say 60 seconds. The real reason is to avoid hitting the DB too hard/often needlessly, you'll quickly realize just how much faster filesystem access is compared to network and mysql especially if the mysql instance is running on a remote server.
# implement a function that gets the contents of the file ( key here is the filename )
# from DB and saves them to disk.
function fectchFreshCopy( $filename ) {
# mysql_connect(); ...
}
if (is_array($this->vars)) extract($this->vars);
ob_start();
# first check if the file exists already
if( file_exits($file) ) {
# now check the timestamp of the files creation to know if it has expired:
$mod_timestamp = filemtime( $file );
if ( ( time() - $mod_timestamp ) >= 60 ) {
# then the file has expired, lets fetch a fresh copy from DB
# and save it to disk..
fetchFreshCopy();
}
}else{
# the file doesnt exist at all, fetch and save it!
fetchFreshCopy();
}
include( $file );
$contents = ob_get_contents();
ob_end_clean();
return $contents;
}
Cheers, hope thats useful

Php ob_get_contents() returns empty string if the included file during buffering is not small

Let's say i have a file consisting of 5 lines of text and every line has 50 chars. The output buffer contents are returned correctly but if I have a file containing 100 lines of text the output buffer returns empty string (string with value null).
I do it like so:
ob_start();
include "file.php"
$string = ob_get_contents();
ob_end_clean();
OR
$string = $this->load->view('view', $data, true);
Im doing this inside codeigniter if that makes any difference.
I tried to load the file with Code igniter's load->view function with third parameter set to true the result is the same. Tried also giving ob_start() a big number -> ob_start(9999999); same result;
I'd propose to use ob_get_flush to ensure, that nothing is still kept in some internal buffer..
Quite unlikely, but what does this instead of your code print?
require_once( "file.php" );
Just to ensure, that the stuff in file.php isn't surrounded by <?php /** **/ php?>.
And what does
echo ob_get_level();
output just before your code? Shouldn't be relevant, if other outbut-buffering is already enabled, but...
Why are you using ob functions to get the contents of a file? Why not file_get_contents?
you have just to add some lines in index.php (the root of CodeIgniter)
ob_start();
/*
*---------------------------------------------------------------
* APPLICATION ENVIRONMENT
*------------
+
require_once BASEPATH.'core/CodeIgniter.php';
$data = ob_get_contents();
ob_clean();
echo $data; /// or anything else
that's all !

Set included PHP and HTML content to a variable

I am working on a small MVC "framework", which will simply provide a more organized workspace for me, as I've found I love CakePHP but only for it's organization. Now, I'm trying to replicate the layouts (which contain <?php echo $title_for_layout; ?> and <?php echo $content_for_layout; ?>,) and I'm curious how I would be able to load a mixed PHP and HTML file... save what would normally be outputted to the browser to a variable... set the variables mentioned inside the variable, and then output the layout variable and have PHP use the variables inside it to setup the layout exactly how I want it.
Any ideas?
Capture the buffer output to a string with the output buffer control functions.
$string = getIncludeContents('template.php');
function getIncludeContents($filename) {
if (is_file($filename)) {
ob_start();
include $filename;
$contents = ob_get_contents();
ob_end_clean();
return $contents;
}
return false;
}
Example taken from.

Categories