Caching a Template in php - php

I have a template which is of .php extension.
This template contains the html maarkup along with some php variables. This is how it looks like.
include_once VIEWDIR . 'documentation/common/header.php';
include_once VIEWDIR . 'documentation/content/'.$this->view.'.php';
include_once VIEWDIR . 'documentation/common/leftsidebar.php';
include_once VIEWDIR . 'documentation/common/rightsidebar.php';
This is the way i am caching.
ob_start();
include_once('template.php');
$templateCache = ob_get_clean();
Then i store this in a .cache file.
The problem is when i load the template from cache, it is not able to read the php variables.
I know i am doing something wrong but not able to catch it. Please help.

Caching in general
Caching, by definition, caches the values of the respective variables.
What you want to do is to delete the cache when one of the variables used in the template changes its value.
This way, the cache will get regenerated, with the new values, and you'll have the advantages of both worlds:
on one side, a speedier HTTP answer, when there's a cache hit
on the other side, dynamicity of the page, when some values change
Caching in your case
Disclaimer: This does not necessarily uses your problem as a showcase. You should improve your question if you need a more to-the-point answer.
If your template has some areas that change often, and some that don't, then you should cache separately only those portions that do not change often, and leave the overall template which contains the changing variables uncached as a whole.
Do not forget to treat individual caches as described above to have a proper behavior of the system.
So instead of caching the whole file, you rewrite it as something like this:
echo $cache->get(VIEWDIR . 'documentation/common/header.php');//<-- this is a cache
include_once VIEWDIR . 'documentation/content/'.$this->view.'.php');
echo $cache->get(VIEWDIR . 'documentation/common/leftsidebar.php');//<-- this is a cache
echo $cache->get(VIEWDIR . 'documentation/common/rightsidebar.php');//<-- this is a cache
and you treat the individual parts of the template as caches themselves.

Just do not cache them.
Your way of "caching" is not caching at all, but it is not the main problem.
The thing is that I doubt you have any reason to cache your templates or their output.
So, just leave everything as is, without any "caching".

Related

Application Entrypoint

I'm building an application and using index.php as and entry point to different modules. I noticed SugarCRM does this and it appears like a good idea.
The URL Looks like this
http://www.mypage.com/index.php?mod=log&pag=login
Where mod is the module and pag is the page
The index.php looks line this:
<?PHP
define('INCLUDE_CHECK',true);
// Class Loader
require ('app/inc/app_autoload.php');
// HTML Header with js and css links
require ('header.php');
// Content Page
$url_module = $_GET["mod"];
$url_page = $_GET["pag"];
$content = $url_module."/".$url_page.".php";
// For the above URL $content = log/login.php
if (!file_exists ($content)) {
require ($content);
}else{
// Handle Error
}
// Footer
require ('footer.php');
?>
Is this safe?
If it's safe, Is it in line with practices?
This can be potentially unsafe. Depends on all the other PHP files that PHP can open. If all of them are class files that don't execute anything, then you're safe. However, if any of them execute something automatically...maybe not.
Let's say that you have PHP files inside a folder:
/secured/file.php
And let's say that the folder has an .htaccess that prohibits anyone from navigating to the page directly. Or better, let's say it's above your root directory. However, the hacker sends "../secured" as the value of mod and "file" as the value of page. In such a case, PHP may allow the person to include that file, and if it self-executes, it may have unintended consequences.
This is why Zend Framework requires explicit configuration of all MVC paths. Other frameworks allow for a some dynamic inclusion, but they often do something like append "Controller.php" to the end of the string, which ensures that the file included must be a Controller...and thus intended to be included in such a way.
When it comes to security, the best thing you can do is make sure that YOU...with all the knowledge of the entire server...can't open up any file that you don't want to be opened by someone else. If you can't get the code to do it, knowing what files are there, then you have implemented some decent (though likely still not flawless) security.

Clearing cache after development for visitor

I'm developing sites and some visitor's browsers appear with old cache.
Is there a way we can clear visitor's browser cache using codes from the server side or even javascript so they don't have to clear themselves?
I cannot find the direct answer to this.
There must be a way big companies do like Facebook, Ebay etc
We have been using htaccess to determine the caching rules of the clients. We explicitly give the cache a 24h lifetime and we put no-cache rules the day before we do the update. It has helped but it is tedious and not so reliable.
Just posting it to give you ideas if no one answers, but I would really love to get the answer too. :)
First Method:
You can actually save the output of the page before you end the script, then load the cache at the start of the script.
example code:
<?php
$cachefile = 'cache/'.basename($_SERVER['PHP_SELF']).'.cache'; // e.g. cache/index.php.cache
$cachetime = 3600; // time to cache in seconds
if(file_exists($cachefile) && time()-$cachetime <= filemtime($cachefile)){
$c = #file_get_contents($cf);
echo $c;
exit;
}else{
unlink($cachefile);
}
ob_start();
// all the coding goes here
$c = ob_get_contents();
file_put_contents($cachefile);
?>
You can actually save the output of the page before you end the script, then load the cache at the start of the script.
example code:
If you have a lot of pages needing this caching you can do this:
in cachestart.php:
in cacheend.php:
<?php
$c = ob_get_contents();
file_put_contents($cachefile);
?>
Then just simply add
include('cachestart.php');
at the start of your scripts. and add
include('cacheend.php');
at the end of your scripts. Remember to have a folder named cache and allow PHP to access it.
Also do remember that if you're doing a full page cache, your page should not have SESSION specific display (e.g. display members' bar or what) because they will be cached as well. Look at a framework for specific-caching (variable or part of the page).
Second Method:
Use Squid or update the HTTP headers correctly to do browser caching.
PEAR has a caching package (actually two):
http://pear.php.net/package/Cache
Fourth Method:
Use http://memcached.org/. There's an explanation of how to do it on that site.
I usually use a combination of techniques:
HTML resulting from PHP code is not cached using the standard configuration, because it sends out the appropriate headers automatically.
Images and other binary assets get renamed if they change.
For JavaScript and CSS I add a automatically created unique code (e.a MD5 hash of the contents or the file size) to the filename (e.g. /public/styles.f782bed8.css) and remove it again with mod_rewrite. This way every change in the file results in a new file name. This can be done at runtime in PHP while outputting the HTML header, to have it fully automated. In this case however an MD5 might have a performance impact.

php website pre load or cache

i have a php based website which.
I use a switch case to include different pages and navigate.
i have employed a method so that my index page includes a navigation bar and a footer
my problem is that each time i navigate from one page to another everything is loaded again and makes the website heavy.
<?php include('models/header.php'); ?>
<div id="content">
<center>
<div id="switch" align="center">
<?php
switch($view)
{
case 'Index':
include('pages/index.php');
break;
case 'Services':
include('pages/Services.php');
break;
case 'About':
include('pages/about.php');
break;
case 'Contact':
include('pages/contact.php');
break;
case 'Download':
include('pages/download.php');
break;
default:
include('pages/error.php');
}
?>
</div>
</div>
</center>
<br>
<?php include('models/footer.php'); ?>
</div>
is there a way i can set it u so that these elements get preloaded once and stay in the cache so that they dont need to be loaded everytime i navigate to a new page...?
Given your code, you actually don't need to cache anything, doing so could lead to more overhead that it is actually needed.
Cached or not cached, you will still need to access a file, which your gain will be the opcode generation. But PHP still needs to access filesystem, except if you use a memcached solution with RAMFS, you won't note a real change.
However, you really need to cache your code, for obvious reasons, you should take a look at APC, which is an opcode cache for PHP.
Basically, it'll cache the calls you make to your included file and cache the PHP interpreter result.
Finally, I actually advise you to give a read to Best Practices for Speeding Up Your Web Site which will help you enhance user experience in a probably more notable way.
The elements in the page (such as images) will be fully reloading if the browser chooses so. If your elements are PHP files, they will generally be reloaded completely as PHP pages often change.
You could set headers in PHP that will tell the browser to cache the page for a certain amount of time, though. See http://php.net/manual/en/function.header.php for more info.
Theres a lot of different ways to do this. I would recommend using Smarty.
switch(strtolower($view)) {
case "download":
$smarty->assign("download_var", $downloadvar);
$smarty->display("Download.tpl");
break;
.....
}
UPDATE
I guess this a bit vague still. Smarty actually has a compiled templates directory that it keeps handy. You can configure smarty to cache in a lot of different ways but the basic idea is that you have a flat file thats precompiled and stored based on session ID.

Include CodeIgniter/Php web application within a genreated php page

The context:
I have a web application (e-commerce in few steps) written in php, I am writing a new version with CodeIgniter.
I have to include it within php pages generated by a CMS (sitezen).
/* generated html */
<?php include('my_app/index.php); ?>
/* generated html */
I cannot do anything about the CMS part, like working with an other one...
My problem:
With I cannot start the session before the header has been sent, I also get warnings when using the session but I can disable them.
My Workaround:
I didn't find any help relevant to my problem. The only workaround I could think of for the old php version is to send an ajax request to a php file starting the session.
This is working but there might be a better/cleaner solution, and I don't know how to do it with the CodeIgniter version.
I'd like to avoid using Iframes too!
If anyone knows a way to do it, or has any hint, it will be highly appreciated!
CodeIgniter is a good framework for doing everything in it (as most frameworks), but doesn't like being 'included' from outside.
Why do you need to include him into a different CMS? You may do the CMS in CodeIgniter (that's the base purporse of CodeIgniter), or the e-commerce in sitezen.
If it is because of the surrounding styles, the best it occurs to me is to have it coded also in CodeIgniter. That's not great because you have to mantain styles twice, but it is one of the cleanest ways of achieving what you want.
Warnings are there because of a reason: disabling them does not prevent the result from happening.
What happens to you is that you try to start a session that has already been started. In order to avoid that, you must give the second session a different name from the first. (In a call previous to session_start(), you'll want to call session_name().
Bad news are that once a session has been started, previous data from the session is no longer accesible, so if the CMS stores stuff in the session on __destruct(), the $_SESSION array where it stores the new data in will not be the same $_SESSION() used at the beginning of the CMS bootstrap.
And if you don't start a second session, you'll mix the CodeIgniter and sitezen variables inside the same array (beware of name collisions).
Code like this will NOT work (so, nesting sessions / restoring sessions is, as far as I know, not possible):
<?php
function show() {
echo "We are on [{$_SESSION['name']}] <br />\n";
}
session_name('SUPERSESSION'); session_start();
$_SESSION['name'] = "Super";
session_name('SESSION_ONE'); session_start();
$_SESSION['name'] = "ONE";
show(); # We are on [ONE]
session_destroy();
session_name('SESSION_TWO'); session_start();
$_SESSION['name'] = "TWO";
show(); # We are on [TWO]
session_destroy();
session_name('SESSION_ONE'); session_start();
show(); # We are on [empty] <- resume sessions does not work
session_destroy();
show(); # We are on [empty] <- nested sessions dont work
session_destroy();
To avoid headers already sent warning, start your code with ob_start() in your index.php, and ob_end_flush() at the end
Can't really be done without hacking the CMS considerably.
A CMS provides you with tools to do a specific job, so you are restricted to the CMS capabilities. Similiarly CI is a framework to help develop apps.
do you really have to include it within the CMS pages?
Why not create a link like:
site.com/my_store_app/codeigniter-stuff
then just link to it from within the CMS. You can reuse the existing template, so visually it will look like it's "within" the CMS, but you will be able to eliminate all these other problems.
You're essentially taking two completely different systems and attempting to stick them together.
I'm not sure if it would work for you but you could decide to include them trough curl. Another option is to include the pages directly. Do note that I'm not sure if this would work but if it does you won't be able to send PHP variables to it except trough the link.
include('http://www.example.com/codeigniter/controller/method/id');
Try this. If it works you can do something like this to control it:
include('http://www.example.com/codeigniter/'. $controller .'/'. $method .'/'. $id);
Note: sessions won't work on this method. If you really want sessions to work your best bet would be to separate the applications.
www.example.com <-- your cms
www.example.com/store <-- your webstore in CI

Best way to avoid code injection in PHP

My website was recently attacked by, what seemed to me as, an innocent code:
<?php
if ( isset( $ _GET['page'] ) ) {
include( $ _GET['page'] . ".php" );
} else {
include("home.php");
}
?>
There where no SQL calls, so I wasn't afraid for SQL Injection. But, apparently, SQL isn't the only kind of injection.
This website has an explanation and a few examples of avoiding code injection: http://www.theserverpages.com/articles/webmasters/php/security/Code_Injection_Vulnerabilities_Explained.html
How would you protect this code from code injection?
Use a whitelist and make sure the page is in the whitelist:
$whitelist = array('home', 'page');
if (in_array($_GET['page'], $whitelist)) {
include($_GET['page'].'.php');
} else {
include('home.php');
}
Another way to sanitize the input is to make sure that only allowed characters (no "/", ".", ":", ...) are in it. However don't use a blacklist for bad characters, but a whitelist for allowed characters:
$page = preg_replace('[^a-zA-Z0-9]', '', $page);
... followed by a file_exists.
That way you can make sure that only scripts you want to be executed are executed (for example this would rule out a "blabla.inc.php", because "." is not allowed).
Note: This is kind of a "hack", because then the user could execute "h.o.m.e" and it would give the "home" page, because all it does is removing all prohibited characters. It's not intended to stop "smartasses" who want to cute stuff with your page, but it will stop people doing really bad things.
BTW: Another thing you could do in you .htaccess file is to prevent obvious attack attempts:
RewriteEngine on
RewriteCond %{QUERY_STRING} http[:%] [NC]
RewriteRule .* /–http– [F,NC]
RewriteRule http: /–http– [F,NC]
That way all page accesses with "http:" url (and query string) result in an "Forbidden" error message, not even reaching the php script. That results in less server load.
However keep in mind that no "http" is allowed in the query string. You website might MIGHT require it in some cases (maybe when filling out a form).
BTW: If you can read german: I also have a blog post on that topic.
The #1 rule when accepting user input is always sanitize it. Here, you're not sanitizing your page GET variable before you're passing it into include. You should perform a basic check to see if the file exists on your server before you include it.
Pek, there are many things to worry about an addition to sql injection, or even different types of code injection. Now might be a good time to look a little further into web application security in general.
From a previous question on moving from desktop to web development, I wrote:
The OWASP Guide to Building Secure Web Applications and Web Services should be compulsory reading for any web developer that wishes to take security seriously (which should be all web developers). There are many principles to follow that help with the mindset required when thinking about security.
If reading a big fat document is not for you, then have a look at the video of the seminar Mike Andrews gave at Google a couple years back about How To Break Web Software.
I'm assuming you deal with files in the same directory:
<?php
if (isset($_GET['page']) && !empty($_GET['page'])) {
$page = urldecode($_GET['page']);
$page = basename($page);
$file = dirname(__FILE__) . "/{$page}.php";
if (!file_exists($file)) {
$file = dirname(__FILE__) . '/home.php';
}
} else {
$file = dirname(__FILE__) . '/home.php';
}
include $file;
?>
This is not too pretty, but should fix your issue.
pek, for a short term fix apply one of the solutions suggested by other users. For a mid to long term plan you should consider migrating to one of existing web frameworks. They handle all low-level stuff like routing and files inclusion in reliable, secure way, so you can focus on core functionalities.
Do not reinvent the wheel. Use a framework. Any of them is better than none. The initial time investment in learning it pays back almost instantly.
Some good answers so far, also worth pointing out a couple of PHP specifics:
The file open functions use wrappers to support different protocols. This includes the ability to open files over a local windows network, HTTP and FTP, amongst others. Thus in a default configuration, the code in the original question can easily be used to open any arbitrary file on the internet and beyond; including, of course, all files on the server's local disks (that the webbserver user may read). /etc/passwd is always a fun one.
Safe mode and open_basedir can be used to restrict files outside of a specific directory from being accessed.
Also useful is the config setting allow_url_fopen, which can disable URL access to files, when using the file open functions. ini-set can be used to set and unset this value at runtime.
These are all nice fall-back safety guards, but please use a whitelist for file inclusion.
I know this is a very old post and I expect you don't need an answer anymore, but I still miss a very important aspect imho and I like it to share for other people reading this post. In your code to include a file based on the value of a variable, you make a direct link between the value of a field and the requested result (page becomes page.php). I think it is better to avoid that.
There is a difference between the request for some page and the delivery of that page. If you make this distinction you can make use of nice urls, which are very user and SEO friendly. Instead of a field value like 'page' you could make an URL like 'Spinoza-Ethica'. That is a key in a whitelist or a primary key in a table from a database and will return a hardcoded filename or value. That method has several advantages besides a normal whitelist:
the back end response is effectively independent from the front end request. If you want to set up your back end system differently, you do not have to change anything on the front end.
Always make sure you end with hardcoded filenames or an equivalent from the database (preferrabley a return value from a stored procedure), because it is asking for trouble when you make use of the information from the request to build the response.
Because your URLs are independent of the delivery from the back end you will never have to rewrite your URLs in the htAccess file for this kind of change.
The URLs represented to the user are user friendly, informing the user about the content of the document.
Nice URLs are very good for SEO, because search engines are in search of relevant content and when your URL is in line with the content will it get a better rate. At least a better rate then when your content is definitely not in line with your content.
If you do not link directly to a php file, you can translate the nice URL into any other type of request before processing it. That gives the programmer much more flexibility.
You will have to sanitize the request, because you get the information from a standard untrustfull source (the rest of the Web). Using only nice URLs as possible input makes the sanitization process of the URL much simpler, because you can check if the returned URL conforms your own format. Make sure the format of the nice URL does not contain characters that are used extensively in exploits (like ',",<,>,-,&,; etc..).
#pek - That won't work, as your array keys are 0 and 1, not 'home' and 'page'.
This code should do the trick, I believe:
<?php
$whitelist = array(
'home',
'page',
);
if(in_array($_GET['page'], $whitelist)) {
include($_GET['page'] . '.php');
} else {
include('home.php');
}
?>
As you've a whitelist, there shouldn't be a need for file_exists() either.
Think of the URL is in this format:
www.yourwebsite.com/index.php?page=http://malicodes.com/shellcode.txt
If the shellcode.txt runs SQL or PHP injection, then your website will be at risk, right? Do think of this, using a whitelist would be of help.
There is a way to filter all variables to avoid the hacking. You can use PHP IDS or OSE Security Suite to help avoid the hacking. After installing the security suite, you need to activate the suite, here is the guide:
http://www.opensource-excellence.com/shop/ose-security-suite/item/414.html
I would suggest you turn on layer 2 protection, then all POST and GET variables will be filtered especially the one I mentioned, and if there are attacks found, it will report to you immediately/
Safety is always the priority

Categories