July 29, 2006

Approximating Master Pages in PHP

Most sites on the Internet have common design elements that do not change from page to page. Usually, only content or minor navigational cues vary. To handle the unchanging aspects, developers have often relied on server-side includes - dividing regions like headers, side navigations and footers into separate files.

But now with the advent of ASP.NET 2.0 Master Pages offer a more complete templating alternative. With Master Pages an entire template common to the site is stored in one file with a .master extension. Developers add a ContentPlaceHolder control inside a Master Page to indicate where they would like their page specific content to appear. Then one or more Web Forms may be set up to automatically dress themselves with the shared visual components defined in the Master Page.

So is it possible to make PHP emulate this behavior? To some degree, yes, with output buffering and one server-side include file that represents the "Master Page." Lets say we are aiming to create our HTML by combining this template file and a content file (we'll call these master.php and index.php, respectfully). Our simplified example will aim to achieve the following end result:



The template that will act as our "Master Page" will need to contain all aspects of the design labeled "Template Specific." Since no such control like the ContentPlaceHolder exists, we will use PHP variables to indicate where we would like our page specific content to appear.

master.php
<html>
<head>
<title><?php echo $pagetitle; ?></title>
</head>
<body style="margin-top:20px;margin-left:20px;margin-right:20px;">
<table width="100%" border="0" cellpadding="10" cellspacing="0"border="0">
<tr bgcolor="#33FFFF">
<td colspan="5"><h2>Template Specific Header</h2></td>
</tr>
<tr bgcolor="#EEEEEE">
<td nowrap><a href=#">Navigation Link 1</a></td>
<td nowrap><a href="#">Navigation Link 2</a></td>
<td nowrap><a href="#">Navigation Link 3</a></td>
<td nowrap><a href="#">Navigation Link 4</a></td>
<td width="100%">&nbsp;</td>
</tr>
</table>
<br />
<table width="100%" cellpadding="10" cellspacing="0" border="0">
<tr>
<td width="30%" valign="top" bgcolor="#EEEEEE"><strong>Template Specific
Navigation</strong><br /><br />
<a href="#">Link 1</a><br />
<a href="#">Link 2</a><br />
<a href="#">Link 3</a><br />
</td>
<td width="70%" valign="top"><?php
echo $pagemaincontent;
?></td>
</tr>
</table>
<br />
<table width="100%" cellspacing="0" cellpadding="10" border="0">
<tr>
<td colspan="2" bgcolor="#33FFFF">Template Specific Footer</td>
</tr>
</table>
</body>
</html>

Now any pages that we would like to have adhere to the template only need to define the page specific variables and include our master.php file.

index.php
<?php
//Buffer larger content areas like the main page content
ob_start();
?>
<em>Page Specific Content Text</em><br />
Lorem ipsum dolor sit amet, consectetuer adipiscing
elit, sed nonummy nibh euismod tincidunt ut laoreet
dolore magna aliat volutpat. Ut wisi enim ad minim
veniam, quis nostrud exercita ullamcorper
suscipit lobortis nisl ut aliquip ex consequat.
<br /><br />
Duis autem vel eum iriure dolor in hendrerit in
vulputate velit molestie consequat, vel illum
dolore eu feugiat nulla facilisis ats eros et
accumsan et iusto odio dignissim qui blandit
prasent up zzril delenit augue duis dolore te
feugait nulla facilisi. Lorem euismod tincidunt
erat volutpat.
<?php
//Assign all Page Specific variables
$pagemaincontent = ob_get_contents();
ob_end_clean();
$pagetitle = "Page Specific Title Text";
//Apply the template
include("master.php");
?>

Even though it's not perfectly aligned with ASP.NET's Master Page feature set, the technique described above allows us to consolidate what might otherwise be numerous server-side include files.

48 comments:

Anonymous said...

this is a good post.

Anonymous said...

It's very nice.
i done my site with using this concept.

it's really helped me to follow this concept & moreover it's very clear (everyone can understood).

Anonymous said...

nice post man, very helpful!

Student said...

Phew... taking my ASP.Net class right now, and was almost swayed by the concept of master pages. I still love PHP more, and your post was very helpful.

Anonymous said...

That's some sucky HTML you got there. Not so "certifiedly" professional as one would wish.

Anonymous said...

Thanks for the post. It is very helpful. I am just wondering, if this would have a significant effect on page load times?

James Laver said...

This is a simple way of doing it but there are much better ways.

I personally these days prefer smarty which compiles itself to php code anyway so there's no slowdown except the initial compile (and, granted, two modification time checks on the template file and the php code generated from the template, but that's nothing) and you get on the plus side a nice seperation of content and code.

smarty

Anonymous said...

I agree I really like the concept. At work I use ASP.net master pages, and I really wished there was some sort of method for php. I saw another blog about how to use a template, but each section was an include file. I'm pretty sure there is some sort of security flaw using include('$page.php'), but this examples method doesn't seem to have that problem and it's very easy to implement.

ivanfx said...

Great stuff! Thanks!

Mark said...

This was a very helpful article. Using this method are you able to put php code into the man content page or will this not be parsed?

Tron said...

Hi Mark,

Yes, with this method you can add PHP code to the main content page and it'll be parsed properly.

Anonymous said...

Thanks! That helps much.

Anonymous said...

Just wanted to say thanks.

I'm a professional asp.net developer myself, but I have just started learning php a couple of days ago and have been scratching my head a little with the whole masterpage / template thing!

Within a minute of reading this post, everything is just how i wanted it...Thanks!

Jean said...

I've tryed this method. It seems that using { or } in the nested code somehow all goes wrong. It's hard to nest code without block separators :(

Rookie said...

Thanks for the posting. This is very useful.

Miguel said...

A very simple and effective solution, specially when you've been coding ASP.NET websites and then you face PHP again and don't want to end up using Dreamweaver's propietary template system (which works great but I prefer to write PHP with a better code editor).

Markus Ram said...

BRILLIANT!!!!
I work in a Microsoft shop and am sometimes befuddled by the burden of .NET. This is clear and simple, and worked on the first attempt after popping up almost in the #1 position on Google.
Thank you thank you thank you.

Joe said...

I'm trying to get this to work and am having some really weird issues. Mainly, about half of the "head" elements (the "link" and "script" tags) are being put in the body instead of staying in the head. Anyone else have this problem? I've made a detailed post here, for anyone that cares to look. Thanks!

Anonymous said...

A simple yet powerful approach.
This is exactly what I was looking for.
A master page system with one include.

Anonymous said...

Very good post, makes updates to my pages fast and simple!

Anonymous said...

Thank you - my site become nicer!

Anonymous said...

Thank you! Now my site become nicer ...

Anonymous said...

Thank you. It was very helpfull post

Anonymous said...

Legendary! Thanks.

Anonymous said...

PERFECT!! make my code so much cleaner! Thanks!

Sumit said...

Hi, Your Idea is great but I am not able use AJAX under this Master page concept.
I am pasting my code here.
//Javascript file
//ajax.js

var xmlhttp;
function GetXmlHttpObject()
{
if (window.XMLHttpRequest)
{
// code for IE7+, Firefox, Chrome, Opera, Safari
return new XMLHttpRequest();
}
if (window.ActiveXObject)
{
// code for IE6, IE5
return new ActiveXObject("Microsoft.XMLHTTP");
}
return null;
}

function Paging(str)
{
xmlhttp=GetXmlHttpObject();
if (xmlhttp==null)
{
alert ("Browser does not support HTTP Request");
return;
}
var url="mysqlpaging.php";
url=url+"?page="+str;
//url=url+"&sid="+Math.random();
xmlhttp.onreadystatechange=PageChanged;
xmlhttp.open("GET",url,true);
//alert(xmlhttp.status);
xmlhttp.send(null);
}
function PageChanged()
{
if (xmlhttp.readyState==4)
{
//alert(xmlhttp.readyState);
document.getElementById("Contents").innerHTML=xmlhttp.responseText;
}
}



//MySqlPaging.php
display();
$pagging->printPagesNums();
$pagemaincontent = ob_get_contents();
ob_end_clean();
//print $pagging->returnQuery();
$pagetitle = "Paging Example";
//Apply the template
include("master.php");
?>

But what the problem occurs is that when "include'master.php'" is included it includes my Paging result in Master page's "Table Data Tag" second and considers it as new page and paste it is new page under "Table Data Tag".
So please help me.

Please help because I want to implement more AJAX functionalities in my Website.

shahmat said...

Excelente. Gracias

varun said...

made my day
thanks dude
Abaxis Software

avejidah said...

I originally designed my newest site like this, but it has some downfalls. First: If you have an include file that needs to be included by all pages, it makes sense to include it in the master page (i.e. something like Doctrine, nusoap, xajax, etc). Because the master page is included by the content pages at the end of the file, the classes provided by the global includes will not be accessible in the content pages, requiring the content pages to include the files. This become cumbersome and in the end requires nearly twice as many includes (the way I had it set up at least - one ajax include, one normal php include - repeated global includes). Second: When I changed my site's layout to instead include a header and a footer in each content page (which cut the number of pages on my site nearly in half) I noticed an enormous load decrease on my larger data-driven pages. If you have a large data-driven page, the server has to buffer the data on each load. On large, heavy traffic pages this causes a huge performance hit. My suggestion is to include a header file and a footer file. In the header file you can have a variable to control css includes and js includes (which can still be output in the head section), as well as additional onload functions, etc. If you think about it, including a header and a footer file in each content page actually decreases the amount of code in each content page by a few lines. I would research this "master page" solution before diving in and designing a whole site this way, not that a page can't be done this way.

Anonymous said...

Why not just use asp.net? It's sooooooo much better than php.

Anonymous said...

Thanks ....

Anonymous said...

Excellent work! This is very helpful, and the possibilities are endless.

Thanks so much for sharing! I develop in php for personal use and ASP.NET for work. It's nice to be able to share features like this between the two.

Anonymous said...

Brilliant - I was looking for something like this - my background is c#.net and was missing the master pages. This is just the ticket!!

Ziko said...

Excellent.

Adam Ralph said...

Thanks - work well!

Anonymous said...

This is awesome, totally used it in a site!!

Anonymous said...

Well, this is not THE ticket, and not an "approximation" (it works in reverse) but it's a good jumping-off point.

"Why not use asp.net...?" Well, I do. But not everyone I help has (or wants) a windows-based server and this helps - I don't have to repeat simple-HTML in 20 content pages.

X3STeNLiTE said...

Awesome finally I found the best way to make the master page. I'll make simpler function for my website.

X3STeNLiTE said...

Awesome!! Finally I found the way to make master page for PHP. thank to you. I'll make simpler function for my website.

Abaxsoft web Solutions said...

years ago i said thank you for this post and now m here again tried this code again and its not working
any help ???

Anonymous said...

Hі there! Thіs is my fіrѕt cοmmеnt
here ѕo I just wanted to gіve a quіck shout out anԁ tell you I truly enjоy reading through your posts.
Can yоu recοmmend any otheг blogs/webѕites/forumѕ that gо over the ѕame subjectѕ?
Thankѕ foг your timе!
my web page: Recommended Reading

Best PHP developer said...

I made like so many versions of this code
broke it even further
integrated it with ajax, jquery
n wow
now all my application works through
this concept only
Cool Right ?
Cool Cool Cool ..!!

momna zaneb said...

hey your tutorial is very useful but i have an issue my content is not set when i apply master page on it. content is displayed on right upper most corner plz help :(

Anonymous said...

Like!

Satish Badliya said...

hi many many thanks for this post.it is very helpful for me..

Scott Bowman said...

This is a great article and easy to understand. However, being new to PHP and trying to work through your example I'm getting an error: syntax error, unexpected '  ob_start' on line 3 of your index.php. Is there some unmentioned step that needs to be done to use the output buffer routines? Please advise.

Anonymous said...

Hello,
this is good post.
but am having problem with css. When folder hierarchy increased css is not working.
Only working with root files.

Anonymous said...

The Root directory symbol is /. so use ../css/stylesheet.css like this syntax. it will ork