Maugrim The Reaper's Blog
Entries from April 2008
License
Syndicate This Blog
Planet PHP
Certified Schizophrenic - zillablog - PHP
Ogres Select Consumption Over Networking (OSCON) - Terry Chay
What MySQL 5 features do I use? - Lukas Smith
Zend Framework 1.6 Release Candidate 1 now available! - planetphp
PHPSecInfo talk at OSCON 2008 - Ed Finkler
Usability FAIL - Brian Moon
Usability FAIL - Brian Moon
Dealing with non-paying client - Michael Kimsal
OSCON 2008: Day One - Ben Ramsey
Quality Assurance in PHP Projects - Sebastian Bergmann
Wednesday, July 23. 2008
Ogres Select Consumption Over Networking (OSCON) - Terry Chay
Tuesday, July 22. 2008
What MySQL 5 features do I use? - Lukas Smith
Tuesday, July 22. 2008
Zend Framework 1.6 Release Candidate 1 now available! - planetphp
Tuesday, July 22. 2008
PHPSecInfo talk at OSCON 2008 - Ed Finkler
Tuesday, July 22. 2008
Usability FAIL - Brian Moon
Tuesday, July 22. 2008
Usability FAIL - Brian Moon
Tuesday, July 22. 2008
Dealing with non-paying client - Michael Kimsal
Tuesday, July 22. 2008
OSCON 2008: Day One - Ben Ramsey
Monday, July 21. 2008
Quality Assurance in PHP Projects - Sebastian Bergmann
Monday, July 21. 2008
Statistics
Last entry: 18.07.2008 17:27
368 entries written
1136 comments have been made
Calendar
Quicksearch
Comments
Pádraic Brady about A Domain Specific Language for Behaviour Driven Development (BDD) in PHP
Tue, 22.07.2008 13:55
I'll never deny an influence f rom rspec
- only the accusa
tion that I'm motivated to re-
implement Ruby because i [...]
Tue, 22.07.2008 13:55
I'll never deny an influence f rom rspec
Federico about A Domain Specific Language for Behaviour Driven Development (BDD) in PHP
Tue, 22.07.2008 10:11
Looking at your example, the f irst thing that comes to my mi nd is RSpec: [geshi lang=ru by] # bowling_spec.rb [...]
Tue, 22.07.2008 10:11
Looking at your example, the f irst thing that comes to my mi nd is RSpec: [geshi lang=ru by] # bowling_spec.rb [...]
Pádraic Brady about A Domain Specific Language for Behaviour Driven Development (BDD) in PHP
Mon, 21.07.2008 11:15
The only change you made thoug h was to use a lamdba - it sti ll suffers from the same probl ems of being verbose, no [...]
Mon, 21.07.2008 11:15
The only change you made thoug h was to use a lamdba - it sti ll suffers from the same probl ems of being verbose, no [...]
Lars Strojny about A Domain Specific Language for Behaviour Driven Development (BDD) in PHP
Sun, 20.07.2008 10:56
I'm not convinced if a DSL is the way to go. What about a re ally base class for BDD storie s. [geshi lang=php] [...]
Sun, 20.07.2008 10:56
I'm not convinced if a DSL is the way to go. What about a re ally base class for BDD storie s. [geshi lang=php] [...]
Pádraic Brady about A Domain Specific Language for Behaviour Driven Development (BDD) in PHP
Sat, 19.07.2008 00:21
Final clarification - the exte rnal DSL is not based on any e xisting language. Man, I will never mention Ruby in a [...]
Sat, 19.07.2008 00:21
Final clarification - the exte rnal DSL is not based on any e xisting language. Man, I will never mention Ruby in a [...]
Dougal Matthews about A Domain Specific Language for Behaviour Driven Development (BDD) in PHP
Fri, 18.07.2008 23:50
If your going to change langua ge. Go for Python, I find it f ar superior to Ruby. By the way, I don't think decr [...]
Fri, 18.07.2008 23:50
If your going to change langua ge. Go for Python, I find it f ar superior to Ruby. By the way, I don't think decr [...]
Pádraic Brady about A Domain Specific Language for Behaviour Driven Development (BDD) in PHP
Fri, 18.07.2008 20:44
And I misread you
. Sorry.
The DSL is for BDD which pla
ces an emphasis on intuitive p
rediction - "equals" is [...]
Fri, 18.07.2008 20:44
And I misread you
Pádraic Brady about A Domain Specific Language for Behaviour Driven Development (BDD) in PHP
Fri, 18.07.2008 20:17
PHP and Ruby are both general programming languages. The Zen d Framework and Ruby On Rails are both frameworks. I c [...]
Fri, 18.07.2008 20:17
PHP and Ruby are both general programming languages. The Zen d Framework and Ruby On Rails are both frameworks. I c [...]
Pádraic Brady about A Domain Specific Language for Behaviour Driven Development (BDD) in PHP
Fri, 18.07.2008 20:04
Hi Vincent - == is the PHP equ ality comparison, just as === is for making an identical che ck. Plain old = is just [...]
Fri, 18.07.2008 20:04
Hi Vincent - == is the PHP equ ality comparison, just as === is for making an identical che ck. Plain old = is just [...]
Les about A Domain Specific Language for Behaviour Driven Development (BDD) in PHP
Fri, 18.07.2008 19:10
Never been one to argue with y ou but you've just gone and co mpared PHP to Ruby which is a very load no no... Th [...]
Fri, 18.07.2008 19:10
Never been one to argue with y ou but you've just gone and co mpared PHP to Ruby which is a very load no no... Th [...]
Vincent about A Domain Specific Language for Behaviour Driven Development (BDD) in PHP
Fri, 18.07.2008 18:58
Hmm, why are you using "==" in stead of "equal"?
Fri, 18.07.2008 18:58
Hmm, why are you using "==" in stead of "equal"?
Justin about Google roll out OAuth Authorisation to all Google Data APIs
Wed, 16.07.2008 01:00
Crazy release time hasn't let me check it out again - but th anks for addressing those conc erns. It looks like tha [...]
Wed, 16.07.2008 01:00
Crazy release time hasn't let me check it out again - but th anks for addressing those conc erns. It looks like tha [...]
Pádraic Brady about Google roll out OAuth Authorisation to all Google Data APIs
Tue, 01.07.2008 12:15
Hi Justin, Zend_Crypt_Hmac is available from the ZF Incub ator repository as it is not y et within Core. It does [...]
Tue, 01.07.2008 12:15
Hi Justin, Zend_Crypt_Hmac is available from the ZF Incub ator repository as it is not y et within Core. It does [...]
Justin about Google roll out OAuth Authorisation to all Google Data APIs
Tue, 01.07.2008 01:41
Hey, I'm trying to test out ba sed on the code you linked - b ut it looks like the Zend_Cryp t_Hmac package is not av [...]
Tue, 01.07.2008 01:41
Hey, I'm trying to test out ba sed on the code you linked - b ut it looks like the Zend_Cryp t_Hmac package is not av [...]
Pádraic Brady about MIA from Planet PHP
Wed, 25.06.2008 17:21
Yes, I noticed. I haven't re-s ubmitted a request yet though. So they are still aggregating the posts I was refused [...]
Wed, 25.06.2008 17:21
Yes, I noticed. I haven't re-s ubmitted a request yet though. So they are still aggregating the posts I was refused [...]
Categories
Top Referrers
Show tagged entries
application security
astrum futura
bdd
behavior-driven development
behaviour-driven development
design patterns
devnetwork
eve online
games
htmlpurifier
irish php user group
irishisms
maugrim
microformat
mock objects
mvc
oauth
openid
openid and yadis
pc gaming
pear
phing
php
php game development
php games
php general
php security
phpmock
phpspec
phpunit
qgl
quantum game library
quantum star se
rantings
simpletest
solar empire
tdd
tutorial
unit testing
xp programming
xrd
xrds
xri
yadis
yaml
zend framework
zf proposalAn Example Zend Framework Blog Application - Part 4: Setting the Design Stage with Blueprint CSS Framework and Zend_Layout
One of the things I know with a certainty that sometimes depresses me is that my design skills are on the level of a 6 year old. In fact the 6 year old with crayons is likely superior in all respects
. To disguise this failing, I have fallen back on the big guns in the CSS world who have decided it might be a good idea to make my life easier by inventing CSS Frameworks I can use. My current favourite is the Blueprint CSS Framework which I've found let's me create simple designs that can get close enough to being good, that most professionals might even be fooled - until they read the source code and realise just how bad I am!
Previously: Part 3: A Simple Hello World Tutorial
But, to be honest, it saves me a lot of time. I really don't like editing CSS when I can entice a monkey with vast bags of peanuts to do a better job later on. For the moment I just need a nice default design which looks okay. In this article we're going to take a stab at setting up a default blog style, using some filler content, and finally capturing the design with a Zend_View template to be consumed by Zend_Layout as a common HTML Layout for the entire future blog.
Inside
Blueprint contains a default set of reasonable styling. This include the most fantastic of them all - when included correctly into any HTML document it sets all browsers to the same set of defaults. This completely negates the usual pain of Firefox, IE6, IE7 and Safari having sufficiently different styles internally that designs need a ton of small browser specific adjustments by hand. By hand is evil - Blueprint does it without interference.
To complete this step, create an empty CSS file called
Famous last words...
We already have a "Hello, World" page from Part 3 of this article series - so let's stick with what we have and throw in a bunch of HTML for a potential main blog index page. We'll use lot's of words like "lorem ipsum" in keeping with a long history ("lorem ipsum" is the start of a long block of text used as filler text in printers since the 1500's - the full traditionally used text itself is over 2000 years old!)
A quick word on using Blueprint CSS. Blueprint assumes you can't align divs (in this case not far from the truth
) so they use a handy grid based system. Each page is divided into a number of fixed width grids. If you know the proportions, you can give columns a grid-width and then never look at anything in a CSS file using the term "float" again. Here I assume a page has 24 fixed length columns. Main text from blog entries will be displayed within columns 1-16. I'll have a column for non-entry content spanning 7 columns to the right. Headers and footers will take up the full page width of 24 columns.
Starting simple, open up the
Go ahead and reopen
In the HTML we did some very simple things. We have the standard 3 block solution - Header, Content, Footer. Both are contained in div's with assigned "span" width. Content is further divided into two columns with "span" widths - the total of which add to 1 less than the total of 24 spans. The reason for the missing span is simply to allow room for further styling which in varying browsers can have knock on effects - typically using the full span wodth across multiple column blocks ends up with a browser breaching the fixed 24 width and leaving a column dropped below the others.
Let's add a few modifications to allow for a more realistic appearance of blog entries with a title and other assembled summary data.
What we now have is pretty basic - but even in black and white it looks clean and quite blog-like. If you disable CSS for your browser, well, it still looks quite fine which is always a positive thing.
A simple tactic to avoid these symptoms is to segregate common elements in one single location, and make sure individual templates remain blissfully unaware of it's existence (then those 100 templates have nothing to edit if the central common template is replace for 50 of them!).
In the Zend Framework this tactic was initially proposed over a year ago when attention was slowly turning towards the weak Zend_View side of the MVC equation. I proposed Zend_View Enhanced which include, among a lot of things, a Zend_View based solution to the common layout problem. Ralph Schindler proposed around the same time a Zend_Layout component for similar purposes. Eventually I lost on that score, and Zend_Layout was adopted. Such is life - at least the rest of Zend_View Enhanced with some modification was also adopted, making for a powerful templating solution taking full advantage of the PHP language (and not a limited subset obscured behind a secondary tag language).
Examining our design above we can spot the common bits: header, footer and the righthand column. Zend_Layout uses a separate template - the Zend Framework manual is extremely obscure about how to actually get the Layout part working (hopefully fixed soon) but it's very simple.
Create a new file in
You'll notice I replaced the fake entries with a reference to a
The process is simple. You visit a Controller and Action. That Action's template is rendered, and saved to the Layout View Helper. The Layout is rendered last, and the pre-rendered content inserted as the Layout template defines using a call to
With our Layout template prepared, let's remove all those common items from our Action specific template at
Now we're getting somewhere interesting. The revised template, only contains the content unique to that Controller/Action combination and it's output will be inserted into the common Layout we created - after we setup Zend_Layout!
Since we're using paths a lot, I also added a $root static property rather than repeating
The new addition sets up Zend_Layout for use - it's automatically integrated into our application thereafter so there's no specific on/off switch outside the Boostrap we need worry about. We pass in a few starting options to customise Layouts. We set the path where Layouts can be found, and provide a default file name (minus the .phtml file extension) to use.
One final step, because it's bugging me as I write this. Let's replace the Hello, World title in our IndexController (
Go ahead and fire up a browser pointing to
Conclusion
This series, as you can tell, is all about taking small concrete steps. In this installment we've added a basic design, added the Blusprint CSS Framework to our toolbox, and set the pace for future templating by eradicating repetitive template code; moving it to a discrete layout template. I see it this as the conclusion of our setup tasks. In Part 3, we had a simple Hello World example. Now we have a skeletal blog design and a Layout moving. We're looking good!
Next up: It's time to do away with the filler content and investigate a means of writing and saving real entries to the database. Once we have the most basic blog imaginable running, we can turn our attention to a new round of requirements like syntax highlighting (it will be a programming blog afterall) and perhaps a few design touchups to add a dash of colour.
Note: The source code for this entry is available to browse, or checkout with subversion, from http://svn.astrumfutura.org/zfblog/tags/Part4. The full source code for the entire application (as it exists thus far) from http://svn.astrumfutura.org/zfblog.
Continue to: Part 5: Creating Models with Zend_Db and adding an Administration Module
Previously: Part 3: A Simple Hello World Tutorial
But, to be honest, it saves me a lot of time. I really don't like editing CSS when I can entice a monkey with vast bags of peanuts to do a better job later on. For the moment I just need a nice default design which looks okay. In this article we're going to take a stab at setting up a default blog style, using some filler content, and finally capturing the design with a Zend_View template to be consumed by Zend_Layout as a common HTML Layout for the entire future blog.
Step 1: Adding Blueprint to the Project
You can download the Blueprint CSS framework from http://code.google.com/p/blueprintcss/. It is released under a dual licensing system using a Modified MIT License, or the GNU General Public License. Decompress the archive of the latest 0.7 release (as at time of writing) and return to our current project'spublic directory.Inside
public create a new directory called css. Into this new directory, copy the decompressed blueprint directory and LICENSE file from the Blueprint distribution. We retain the license so future redistributions of the project (if any) include a copy for reference.Blueprint contains a default set of reasonable styling. This include the most fantastic of them all - when included correctly into any HTML document it sets all browsers to the same set of defaults. This completely negates the usual pain of Firefox, IE6, IE7 and Safari having sufficiently different styles internally that designs need a ton of small browser specific adjustments by hand. By hand is evil - Blueprint does it without interference.
To complete this step, create an empty CSS file called
style.css inside the /public/css directory. We'll include this in our HTML as a point for customising the Blueprint CSS and adding additional styling as needed.Step 2: Creating a Test HTML Document applying Blueprint CSS
The first step to any good design is using Photoshop or gimp to draw one up. I'm really bad at that however - so I stick with lots of filler text and write plain HTML from the start. Besides, it's a blog page - how complex can it be?We already have a "Hello, World" page from Part 3 of this article series - so let's stick with what we have and throw in a bunch of HTML for a potential main blog index page. We'll use lot's of words like "lorem ipsum" in keeping with a long history ("lorem ipsum" is the start of a long block of text used as filler text in printers since the 1500's - the full traditionally used text itself is over 2000 years old!)
A quick word on using Blueprint CSS. Blueprint assumes you can't align divs (in this case not far from the truth
Starting simple, open up the
/application/views/scripts/index/index/phtml template used by our default Index controller and edit to contain the following:<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="language" content="en" />
<title><?php echo $this->escape($this->title) ?></title>
<link rel="stylesheet" href="/css/blueprint/screen.css" type="text/css" media="screen, projection">
<link rel="stylesheet" href="/css/style.css" type="text/css" media="screen, projection">
<link rel="stylesheet" href="/css/blueprint/print.css" type="text/css" media="print">
<!--[if IE]><link rel="stylesheet" href="/css/blueprint/ie.css" type="text/css" media="screen, projection"><![endif]-->
</head>
<body>
<div class="container">
<div class="block">
<div id="zfHeader" class="column span-24">
<h1>Lorem Ipsum</h1>
</div>
</div>
<div class="block">
<div id="zfContent" class="column span-16">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
<div id="zfExtraRight" class="column span-7">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
</div>
<div class="block">
<div id="zfFooter" class="span-24">
<p>Copyright © 2008 Pádraic Brady</p>
</div>
</div>
</div>
</body>
</html>
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="language" content="en" />
<title><?php echo $this->escape($this->title) ?></title>
<link rel="stylesheet" href="/css/blueprint/screen.css" type="text/css" media="screen, projection">
<link rel="stylesheet" href="/css/style.css" type="text/css" media="screen, projection">
<link rel="stylesheet" href="/css/blueprint/print.css" type="text/css" media="print">
<!--[if IE]><link rel="stylesheet" href="/css/blueprint/ie.css" type="text/css" media="screen, projection"><![endif]-->
</head>
<body>
<div class="container">
<div class="block">
<div id="zfHeader" class="column span-24">
<h1>Lorem Ipsum</h1>
</div>
</div>
<div class="block">
<div id="zfContent" class="column span-16">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
<div id="zfExtraRight" class="column span-7">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
</div>
<div class="block">
<div id="zfFooter" class="span-24">
<p>Copyright © 2008 Pádraic Brady</p>
</div>
</div>
</div>
</body>
</html>
Go ahead and reopen
http://zfblog/ in a browser. You'll quickly notice that with little effort, it looks oddly pretty and the main content and right column are aligned exactly even.In the HTML we did some very simple things. We have the standard 3 block solution - Header, Content, Footer. Both are contained in div's with assigned "span" width. Content is further divided into two columns with "span" widths - the total of which add to 1 less than the total of 24 spans. The reason for the missing span is simply to allow room for further styling which in varying browsers can have knock on effects - typically using the full span wodth across multiple column blocks ends up with a browser breaching the fixed 24 width and leaving a column dropped below the others.
Let's add a few modifications to allow for a more realistic appearance of blog entries with a title and other assembled summary data.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="language" content="en" />
<title><?php echo $this->escape($this->title) ?></title>
<link rel="stylesheet" href="/css/blueprint/screen.css" type="text/css" media="screen, projection">
<link rel="stylesheet" href="/css/style.css" type="text/css" media="screen, projection">
<link rel="stylesheet" href="/css/blueprint/print.css" type="text/css" media="print">
<!--[if IE]><link rel="stylesheet" href="/css/blueprint/ie.css" type="text/css" media="screen, projection"><![endif]-->
</head>
<body>
<div class="container">
<div class="block">
<div id="zfHeader" class="column span-24">
<h1>Lorem Ipsum</h1>
</div>
</div>
<div class="block">
<div id="zfContent" class="column span-16">
<h2>Excepteur Sint</h2>
<p><?php echo gmdate('D, j M Y H:i:s T', time()) ?><br />
Posted by Pádraic Brady</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<hr />
<p>Tags: Lorem, Ipsum, Excepteur</p>
<h2>Excepteur Sint</h2>
<p><?php echo gmdate('D, j M Y H:i:s T', time()) ?><br />
Posted by Pádraic Brady</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<hr />
<p>Tags: Lorem, Ipsum, Excepteur</p>
</div>
<div id="zfExtraRight" class="column span-7">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
</div>
<div class="block">
<div id="zfFooter" class="span-24">
<p>Copyright © 2008 Pádraic Brady</p>
</div>
</div>
</div>
</body>
</html>
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="language" content="en" />
<title><?php echo $this->escape($this->title) ?></title>
<link rel="stylesheet" href="/css/blueprint/screen.css" type="text/css" media="screen, projection">
<link rel="stylesheet" href="/css/style.css" type="text/css" media="screen, projection">
<link rel="stylesheet" href="/css/blueprint/print.css" type="text/css" media="print">
<!--[if IE]><link rel="stylesheet" href="/css/blueprint/ie.css" type="text/css" media="screen, projection"><![endif]-->
</head>
<body>
<div class="container">
<div class="block">
<div id="zfHeader" class="column span-24">
<h1>Lorem Ipsum</h1>
</div>
</div>
<div class="block">
<div id="zfContent" class="column span-16">
<h2>Excepteur Sint</h2>
<p><?php echo gmdate('D, j M Y H:i:s T', time()) ?><br />
Posted by Pádraic Brady</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<hr />
<p>Tags: Lorem, Ipsum, Excepteur</p>
<h2>Excepteur Sint</h2>
<p><?php echo gmdate('D, j M Y H:i:s T', time()) ?><br />
Posted by Pádraic Brady</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<hr />
<p>Tags: Lorem, Ipsum, Excepteur</p>
</div>
<div id="zfExtraRight" class="column span-7">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
</div>
<div class="block">
<div id="zfFooter" class="span-24">
<p>Copyright © 2008 Pádraic Brady</p>
</div>
</div>
</div>
</body>
</html>
What we now have is pretty basic - but even in black and white it looks clean and quite blog-like. If you disable CSS for your browser, well, it still looks quite fine which is always a positive thing.
Step 3: Splitting persistent markup into a Zend_Layout template
Traditionally, PHP templating has rarely addressed a common problem we have using templates. Somewhere in the design above (you can spot this easily) are elements common to almost every future page of our blog application. A stock solution is often to push these elements into separate templates, and include them as needed. This poses the problem of widespread references to these templates. What happens if we decide they only apply to half our site now, and need to replace them for 50% of all existing page specific templates? I call it PAIN. Others call it MIND-NUMBING.A simple tactic to avoid these symptoms is to segregate common elements in one single location, and make sure individual templates remain blissfully unaware of it's existence (then those 100 templates have nothing to edit if the central common template is replace for 50 of them!).
In the Zend Framework this tactic was initially proposed over a year ago when attention was slowly turning towards the weak Zend_View side of the MVC equation. I proposed Zend_View Enhanced which include, among a lot of things, a Zend_View based solution to the common layout problem. Ralph Schindler proposed around the same time a Zend_Layout component for similar purposes. Eventually I lost on that score, and Zend_Layout was adopted. Such is life - at least the rest of Zend_View Enhanced with some modification was also adopted, making for a powerful templating solution taking full advantage of the PHP language (and not a limited subset obscured behind a secondary tag language).
Examining our design above we can spot the common bits: header, footer and the righthand column. Zend_Layout uses a separate template - the Zend Framework manual is extremely obscure about how to actually get the Layout part working (hopefully fixed soon) but it's very simple.
Create a new file in
/application/views/layouts called common.phtml. What we'll do is copy in the above index.phtml template but delete the sections that are likely to change with different page views - namely the entries.<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="language" content="en" />
<title><?php echo $this->escape($this->title) ?></title>
<link rel="stylesheet" href="/css/blueprint/screen.css" type="text/css" media="screen, projection">
<link rel="stylesheet" href="/css/style.css" type="text/css" media="screen, projection">
<link rel="stylesheet" href="/css/blueprint/print.css" type="text/css" media="print">
<!--[if IE]><link rel="stylesheet" href="/css/blueprint/ie.css" type="text/css" media="screen, projection"><![endif]-->
</head>
<body>
<div class="container">
<div class="block">
<div id="zfHeader" class="column span-24">
<h1>Lorem Ipsum</h1>
</div>
</div>
<div class="block">
<div id="zfContent" class="column span-16">
<?php echo $this->layout()->content ?>
</div>
<div id="zfExtraRight" class="column span-7">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
</div>
<div class="block">
<div id="zfFooter" class="span-24">
<p>Copyright © 2008 Pádraic Brady</p>
</div>
</div>
</div>
</body>
</html>
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="language" content="en" />
<title><?php echo $this->escape($this->title) ?></title>
<link rel="stylesheet" href="/css/blueprint/screen.css" type="text/css" media="screen, projection">
<link rel="stylesheet" href="/css/style.css" type="text/css" media="screen, projection">
<link rel="stylesheet" href="/css/blueprint/print.css" type="text/css" media="print">
<!--[if IE]><link rel="stylesheet" href="/css/blueprint/ie.css" type="text/css" media="screen, projection"><![endif]-->
</head>
<body>
<div class="container">
<div class="block">
<div id="zfHeader" class="column span-24">
<h1>Lorem Ipsum</h1>
</div>
</div>
<div class="block">
<div id="zfContent" class="column span-16">
<?php echo $this->layout()->content ?>
</div>
<div id="zfExtraRight" class="column span-7">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</div>
</div>
<div class="block">
<div id="zfFooter" class="span-24">
<p>Copyright © 2008 Pádraic Brady</p>
</div>
</div>
</div>
</body>
</html>
You'll notice I replaced the fake entries with a reference to a
layout() function. Function calls on the template object instance generally are either called from the template object (an instance of Zend_View) or are references to a View Helper (a small class encapsulating reusable presentation logic - we'll hear a lot more about these later). In this case, it refers to the Zend_View_Helper_Layout class. This class instance has a public variable $content which contains all content rendered from all preceeding templates (bear in mind Layouts are rendered last - after Action specific templates).The process is simple. You visit a Controller and Action. That Action's template is rendered, and saved to the Layout View Helper. The Layout is rendered last, and the pre-rendered content inserted as the Layout template defines using a call to
$this->layout()->content.With our Layout template prepared, let's remove all those common items from our Action specific template at
/application/views/scripts/index/index.phtml:<h2>Excepteur Sint</h2>
<p><?php echo gmdate('D, j M Y H:i:s T', time()) ?><br />
Posted by Pádraic Brady</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<hr />
<p>Tags: Lorem, Ipsum, Excepteur</p>
<h2>Excepteur Sint</h2>
<p><?php echo gmdate('D, j M Y H:i:s T', time()) ?><br />
Posted by Pádraic Brady</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<hr />
<p>Tags: Lorem, Ipsum, Excepteur</p>
<p><?php echo gmdate('D, j M Y H:i:s T', time()) ?><br />
Posted by Pádraic Brady</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<hr />
<p>Tags: Lorem, Ipsum, Excepteur</p>
<h2>Excepteur Sint</h2>
<p><?php echo gmdate('D, j M Y H:i:s T', time()) ?><br />
Posted by Pádraic Brady</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<hr />
<p>Tags: Lorem, Ipsum, Excepteur</p>
Now we're getting somewhere interesting. The revised template, only contains the content unique to that Controller/Action combination and it's output will be inserted into the common Layout we created - after we setup Zend_Layout!
Step 4: Setting up Zend_Layout for operation
Getting Zend_Layout working is again quite simple. We just need to make a few additions to our Bootstrap file. Specifically to thesetupView() static method.<?php
require_once 'Zend/Loader.php';
class Bootstrap
{
public static $frontController = null;
public static $root = '';
public static function run()
{
self::setupEnvironment();
self::prepare();
$response = self::$frontController->dispatch();
self::sendResponse($response);
}
public static function setupEnvironment()
{
error_reporting(E_ALL|E_STRICT);
ini_set('display_errors', true);
date_default_timezone_set('Europe/London');
self::$root = dirname(dirname(<u>_FILE_</u>));
// FILE constant should have two underscores
// preceeding and following "FILE"
}
public static function prepare()
{
self::setupFrontController();
self::setupView();
}
public static function setupFrontController()
{
Zend_Loader::registerAutoload();
self::$frontController = Zend_Controller_Front::getInstance();
self::$frontController->throwExceptions(true);
self::$frontController->returnResponse(true);
self::$frontController->setControllerDirectory(
self::$root . '/application/controllers'
);
}
public static function setupView()
{
$view = new Zend_View;
$view->setEncoding('UTF-8');
$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer($view);
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
Zend_Layout::startMvc(
array(
'layoutPath' => self::$root . '/application/views/layouts',
'layout' => 'common'
)
);
}
public static function sendResponse(Zend_Controller_Response_Http $response)
{
$response->setHeader('Content-Type', 'text/html; charset=UTF-8', true);
$response->sendResponse();
}
}
require_once 'Zend/Loader.php';
class Bootstrap
{
public static $frontController = null;
public static $root = '';
public static function run()
{
self::setupEnvironment();
self::prepare();
$response = self::$frontController->dispatch();
self::sendResponse($response);
}
public static function setupEnvironment()
{
error_reporting(E_ALL|E_STRICT);
ini_set('display_errors', true);
date_default_timezone_set('Europe/London');
self::$root = dirname(dirname(<u>_FILE_</u>));
// FILE constant should have two underscores
// preceeding and following "FILE"
}
public static function prepare()
{
self::setupFrontController();
self::setupView();
}
public static function setupFrontController()
{
Zend_Loader::registerAutoload();
self::$frontController = Zend_Controller_Front::getInstance();
self::$frontController->throwExceptions(true);
self::$frontController->returnResponse(true);
self::$frontController->setControllerDirectory(
self::$root . '/application/controllers'
);
}
public static function setupView()
{
$view = new Zend_View;
$view->setEncoding('UTF-8');
$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer($view);
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
Zend_Layout::startMvc(
array(
'layoutPath' => self::$root . '/application/views/layouts',
'layout' => 'common'
)
);
}
public static function sendResponse(Zend_Controller_Response_Http $response)
{
$response->setHeader('Content-Type', 'text/html; charset=UTF-8', true);
$response->sendResponse();
}
}
Since we're using paths a lot, I also added a $root static property rather than repeating
dirname() everywhere!The new addition sets up Zend_Layout for use - it's automatically integrated into our application thereafter so there's no specific on/off switch outside the Boostrap we need worry about. We pass in a few starting options to customise Layouts. We set the path where Layouts can be found, and provide a default file name (minus the .phtml file extension) to use.
One final step, because it's bugging me as I write this. Let's replace the Hello, World title in our IndexController (
/application/controllers/IndexController.php) with something more meaningful.class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$this->view->title = 'Maugrim\'s Marvelous Blog';
}
}
{
public function indexAction()
{
$this->view->title = 'Maugrim\'s Marvelous Blog';
}
}
Go ahead and fire up a browser pointing to
http://zfblog/.Conclusion
This series, as you can tell, is all about taking small concrete steps. In this installment we've added a basic design, added the Blusprint CSS Framework to our toolbox, and set the pace for future templating by eradicating repetitive template code; moving it to a discrete layout template. I see it this as the conclusion of our setup tasks. In Part 3, we had a simple Hello World example. Now we have a skeletal blog design and a Layout moving. We're looking good!
Next up: It's time to do away with the filler content and investigate a means of writing and saving real entries to the database. Once we have the most basic blog imaginable running, we can turn our attention to a new round of requirements like syntax highlighting (it will be a programming blog afterall) and perhaps a few design touchups to add a dash of colour.
Note: The source code for this entry is available to browse, or checkout with subversion, from http://svn.astrumfutura.org/zfblog/tags/Part4. The full source code for the entire application (as it exists thus far) from http://svn.astrumfutura.org/zfblog.
Continue to: Part 5: Creating Models with Zend_Db and adding an Administration Module
Posted by Pádraic Brady on Tuesday, April 29. 2008 at 11:51 in PHP General, PHP Security, Zend Framework
19 Comments Trackback (1)
19 Comments Trackback (1)
An Example Zend Framework Blog Application - Part 3: A Simple Hello World Tutorial
It's almost obligatory when introducing a new programming topic, that the author present the simplest possible example. Usually this means getting a programming language or framework to print "Hello World" to the screen. I'm going to be no different. So much for originality...
Previously: Part 2: The MVC Application Architecture
You'll need to download the Zend Framework 1.5 (latest minor release) from http://framework.zend.com and put it somewhere on your include path. I try to minimise the number of include paths I end up having so I sometimes pick very odd places (like the PEAR directory which is usually already in the include_path) or another central library location like PEAR where I might have a dozen libraries all congregated.
A common solution found on blogs and articles is to put the framework into a "library" directory within your application directory tree. Ordinarily I don't recommend this unless your application needs a specific version of the Zend Framework - centralising a Zend Framework location for multiple applications to access can make maintenance a bit easier.
I've settled on using a virtual host mainly because it's easy to setup, and it helps avoid some of those annoying base path issues you get in your HTML when using mod_rewrite pretty urls - especially in development where, otherwise, the locahost document root becomes a mass of sub-directories upon sub-directories that end up causing base href issues so easily. Usually I open up the virtual-hosts.conf file from Apache's conf directory and add something like:
This should result in all HTTP queries for http://zfblog/ reaching the selected document root (which is the public directory for the project containing index.php, located in our subversion's working copy "trunk" directory). We'll create this directory structure a little later. We usually also need to reference localhost to ensure it's recognised as a host correctly - otherwise any localhost addresses you already have will suddenly divert to zfblog's document root myseriously
.
For those of you on Windows XP or Vista, you also need to add the new virtual domain to your hosts file (usually at C:\WINNT\system32\drivers\etc\hosts). This is tricky in Vista - you will probably need to alter the file's permissions using an Administrator account to grant the local user additional Modify privileges. Make sure to reverse the privileges after! Edit it to look this:
Restart your browser and with a little luck the browser will at least give us a directory listing (once you add the document root "public" directory as shown below).
The least exciting part is the creation of a tree of directories to contain our source code. Wil Sinclair over at Zend is working on a Zend_Build component and this will one day hopefully eliminate too much of the repetition in creating these directories and default files. Until then let the monotony ensue (or keep a skeleton setup handy for copy and pasting
).
Here's a snapshot of what the Hello World example looks like in the directory view from Eclipse:

Copy this and create the same directory structure in a new directory called something like "zfblog". Putting it all in "trunk" is optional - I've using subversion here which is why that directory exists. If you haven't used Subversion before I highly recommend it - it's simple to learn and use.
Let's examine each of these directories briefly.
The "application" directory is where we place all the components of an application implementing the Model-View-Controller. Inside "application", "controllers", "models" and "views" represent respective locations of controller, model and view source code and templates. Inside "views" there is a subdivision between "filters", "helpers", "layouts" and "scripts". For now, remember we put all templates rendered by the View inside "scripts". In the screenshot, we see and index directory which will contain an "index.phtml" file, which is the view template for indexAction of an IndexController class. The others we'll meet later.
The "modules" directory is also for controllers, models, and views, but in this case they are catagorised into multiple divisions of an application. If you think about it, an application can be broken into several parts, for example, the main application and an administration part. The administration section could be partitioned into the Admin Module so it's not intermixed with the main application.
A Bootstrap.php file exists in the "application" directory which represents a class called "Bootstrap". It's purpose is to initialise the Zend Framework, adjust environment settings (for example, timezone and error_reporting level), and otherwise make application specific tweaks and additions before a HTTP request is processed. Most tutorials take an alternative view and put Bootstrap code into index.php. I strongly suggest you avoid this and use an actual class to organise the Bootstrap code - it makes it a lot easier to read if nothing else!
The "config" directory simply holds the configuration data for the application, for example, database connection details.
The "library" directory can hold a copy of the Zend Framework. Generally I avoid this since it's fairly easy to just add the Zend Framework to the PHP include_path, but it's still very useful if you want to change Zend Framework versions in use more flexibly. It's also simple to manage if you use it to add an svn::external reference to the Zend Framework subversion repository.
The "public" directory holds all public files accessible by a request to the web server. This includes an index.php file, which handles all application requests by calling on the Bootstrap class, as well as any CSS, HTML, Javascript files, etc.
A note on deployment practice: generally when deploying you'd want to move the "public" directory to your web server to be internet accessible, but keep the non-public directories somewhere below the web root. Since the public index.php ends up only making a simple reference to the Bootstrap file served by including it, this means most of the application files will not be accessible by internet users. It also means that the index.php file will end up deciding where the Bootstrap file is located - so it's the one public file that always needs to be edited by hand for deployment to change that location as needed.
The "tests" directory usually holds any application specific unit, integration and acceptance tests. As I noted previously I'm deliberately taking a test-light approach to stay on topic (I have more than one testing/development article on Zend Devzone if interested in applying TDD or BDD). Main tests I expect to have are for application specific classes: any Model logic for example.
At the end of this step you should have the directory structure in place containing some empty files as follows (create them now, and we'll fill them in later):
Note: Due to some Serendipity bug in processing Geshi output, underline tags replace some underline characters such as for the PHP FILE constant which should have two underscrores proceeding and following "FILE". Sorry about that - S9Y has it's moments.
The Bootstrap class has two main methods here: run() and prepare(). run() executes a full application request, whereas prepare() only sets up (but doesn't execute) the Front Controller. For now let's focus on run() and all the steps our Bootstrap takes when it's called. The prepare() method is something I use when applying TDD or BDD to developing controllers, since I then delegate controller execution to the testing framework.
The first thing is to setup the local environment. So here we've set a timezone (as required by PHP5), enabled PHP's display_errors option, and set a fairly stiff error reporting level. If putting these basics into a class seems counter intuitive, you can simply extract them to the file head. Because I usually test extensively, I usually don't want these running during my tests and interfering with test results.
Secondly we prepare the Front Controller. Zend_Controller_Front is an implementation of the Front Controller Design Pattern, which acts as a single point of entry into a Model-View-Controller architected application. I kid you not - every request which is not for an existing public file will go through it. In our case, since the Bootstrap is included into, and called by public/index.php, "index.php" is our single point of entry.
Here, the prepare() stage involves setting up the Front Controller:
- get an instance of Zend_Controller_Front
- set it to throw all Exceptions (might want to disable this in production)
- set it to return a Response object when dispatched
- tell it where to find the default controllers
The last step of prepare() makes some changes to the View. The reason I've done this is to show how to make an extremely common change - adding support for UTF-8 encoded output (such as my name!). Don't worry too much about the details now - the ViewRenderer Action Helper and the Helper Broker lets one add or modify Action Helpers and is well covered by Matthew Weier O'Phinney over on Zend Devzone - http://devzone.zend.com/article/3350-Action-Helpers-in-Zend-Framework.
Now, what about
Simplicity itself! The index is our Front Controller's single point of entry into a Zend Framework application, and all it needs to do is determine a valid include_path for the application and then delegate all other tasks to the Bootstrap class's run() method.
The final step is how to make all requests to "http://zfblog/" pass through index.php. This is solved for Apache web servers using it's Rewrite Module, which simply takes an incoming request URL, and rewrites it so it points to index.php. If you've never used Apache Rewrite before, you probably need to enable this module in http.conf before this works. Rewrite rules can be defined in Apache's http.conf file, or simply via a .htaccess file in "public" containing the rules:
The .htaccess file here first turns on Rewriting, and then specifies rules. The rules here map all requests to index.php, unless they relate to a file which exists on the server (i.e. it won't rewrite URLs to CSS, Javascript or image files).
If a URL to a Zend Framework application does not contain a controller or action reference, then the Zend Framework uses the default "index" value. Since we only intend requesting to the root URL of "http://zfblog/", we'll need an Index Controller containing an Index Action. Let's create one!
Add a file called "IndexController.php" in
All controllers must extend Zend_Controller_Action, since it contains all later referenced internal methods and properties common to all Controllers. If you need to tweak the basic Controller to add new methods or properties you may do so using a subclass of Zend_Controller_Action and then making all application Controllers extend this subclass instead. Be very careful however in subclassing this way - it's useful for adding new properties to the class, but additional methods are often better added as discrete Action Helpers.
Our new Action method ignores rendering; it's done automatically as discussed earlier. All we need to do is set View data we need. You can add almost anything to a View, since it just becomes a property in a View template. So feel free to add Objects and Arrays. If using Smarty or PHPTAL, this might be different.
Above we've told the View that templates might refer to a variable "title" and given it a value of "Hello, World!". Once the Action method is done, the ViewRenderer will kick in and try to render a template located at "/path/to/application/views/scripts/index/index.phtml".
And what use is a Controller, without a View to display?
The Zend_View component is responsible for rendering all View template, often with a little assistance (e.g. ViewRenderer or Zend_Layout) from the Controller. A Zend_View template is HTML (or some other output type) interspersed with PHP and it doesn't use a separate tag language such as what the Smarty template engine, for example, uses. Here's something to put into your index.phtml file for our first Zend Framework application:
As you can see, it's almost pure HTML with a single PHP inclusion for the title text (which we set from our indexAction() method earlier!). To ensure security against Cross Site Scripting (XSS) we should escape all output from within the application, and Zend_View uses the PHP htmlspecialchars() function from it's Zend_View::escape() method. Another noteworthy mention is that, since we setup our View to use UTF-8 encoding, this encoding is also passed into htmlspecialchars().
If using escape() everywhere sounds bad - well, it is, and it isn't. It's a pain since it's something that would obviously benefit from automation - so not applying escaping is the exception by default. On the other hand it must be done at all times so if you ever see a variable being echoed that's not surrounded by the escape() method you should proceed with care.
). As we proceed, there will more than likely be a few flaws to the simple approach, our Bootstrap will need to expand yet remain maintainable, and we haven't even looked at Zend_Db yet!
It's a start. If you're lucky, it may be many starts since you can reuse a similar basic skeleton to skip all those setup tasks for other Zend Framework projects.
Note: The source code for this entry is available to browse, or checkout with subversion, from http://svn.astrumfutura.org/zfblog/tags/Part3. The full source code for the entire application (as it exists thus far) from http://svn.astrumfutura.org/zfblog.
Continue to: Part 4: Setting the Design Stage with Blueprint CSS Framework and Zend_Layout
Previously: Part 2: The MVC Application Architecture
Step 1: Creating A New Application
Before we jump into programming with the Zend Framework there are a few setup tasks. We need to install the Zend Framework, get a virtual domain running, and have a basic collection of directories and emtpy files created to hold our source code.You'll need to download the Zend Framework 1.5 (latest minor release) from http://framework.zend.com and put it somewhere on your include path. I try to minimise the number of include paths I end up having so I sometimes pick very odd places (like the PEAR directory which is usually already in the include_path) or another central library location like PEAR where I might have a dozen libraries all congregated.
A common solution found on blogs and articles is to put the framework into a "library" directory within your application directory tree. Ordinarily I don't recommend this unless your application needs a specific version of the Zend Framework - centralising a Zend Framework location for multiple applications to access can make maintenance a bit easier.
I've settled on using a virtual host mainly because it's easy to setup, and it helps avoid some of those annoying base path issues you get in your HTML when using mod_rewrite pretty urls - especially in development where, otherwise, the locahost document root becomes a mass of sub-directories upon sub-directories that end up causing base href issues so easily. Usually I open up the virtual-hosts.conf file from Apache's conf directory and add something like:
NameVirtualHost ∗:80
<VirtualHost ∗:80>
DocumentRoot "/path/to/htdocs"
ServerName localhost
</VirtualHost>
<VirtualHost ∗:80>
ServerName zfblog
DocumentRoot "/path/to/project/trunk/public"
</VirtualHost>This should result in all HTTP queries for http://zfblog/ reaching the selected document root (which is the public directory for the project containing index.php, located in our subversion's working copy "trunk" directory). We'll create this directory structure a little later. We usually also need to reference localhost to ensure it's recognised as a host correctly - otherwise any localhost addresses you already have will suddenly divert to zfblog's document root myseriously
For those of you on Windows XP or Vista, you also need to add the new virtual domain to your hosts file (usually at C:\WINNT\system32\drivers\etc\hosts). This is tricky in Vista - you will probably need to alter the file's permissions using an Administrator account to grant the local user additional Modify privileges. Make sure to reverse the privileges after! Edit it to look this:
# Copyright (c) 1993-1999 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
127.0.0.1 localhost
127.0.0.1 zfblogRestart your browser and with a little luck the browser will at least give us a directory listing (once you add the document root "public" directory as shown below).
The least exciting part is the creation of a tree of directories to contain our source code. Wil Sinclair over at Zend is working on a Zend_Build component and this will one day hopefully eliminate too much of the repetition in creating these directories and default files. Until then let the monotony ensue (or keep a skeleton setup handy for copy and pasting
Here's a snapshot of what the Hello World example looks like in the directory view from Eclipse:

Copy this and create the same directory structure in a new directory called something like "zfblog". Putting it all in "trunk" is optional - I've using subversion here which is why that directory exists. If you haven't used Subversion before I highly recommend it - it's simple to learn and use.
Let's examine each of these directories briefly.
The "application" directory is where we place all the components of an application implementing the Model-View-Controller. Inside "application", "controllers", "models" and "views" represent respective locations of controller, model and view source code and templates. Inside "views" there is a subdivision between "filters", "helpers", "layouts" and "scripts". For now, remember we put all templates rendered by the View inside "scripts". In the screenshot, we see and index directory which will contain an "index.phtml" file, which is the view template for indexAction of an IndexController class. The others we'll meet later.
The "modules" directory is also for controllers, models, and views, but in this case they are catagorised into multiple divisions of an application. If you think about it, an application can be broken into several parts, for example, the main application and an administration part. The administration section could be partitioned into the Admin Module so it's not intermixed with the main application.
A Bootstrap.php file exists in the "application" directory which represents a class called "Bootstrap". It's purpose is to initialise the Zend Framework, adjust environment settings (for example, timezone and error_reporting level), and otherwise make application specific tweaks and additions before a HTTP request is processed. Most tutorials take an alternative view and put Bootstrap code into index.php. I strongly suggest you avoid this and use an actual class to organise the Bootstrap code - it makes it a lot easier to read if nothing else!
The "config" directory simply holds the configuration data for the application, for example, database connection details.
The "library" directory can hold a copy of the Zend Framework. Generally I avoid this since it's fairly easy to just add the Zend Framework to the PHP include_path, but it's still very useful if you want to change Zend Framework versions in use more flexibly. It's also simple to manage if you use it to add an svn::external reference to the Zend Framework subversion repository.
The "public" directory holds all public files accessible by a request to the web server. This includes an index.php file, which handles all application requests by calling on the Bootstrap class, as well as any CSS, HTML, Javascript files, etc.
A note on deployment practice: generally when deploying you'd want to move the "public" directory to your web server to be internet accessible, but keep the non-public directories somewhere below the web root. Since the public index.php ends up only making a simple reference to the Bootstrap file served by including it, this means most of the application files will not be accessible by internet users. It also means that the index.php file will end up deciding where the Bootstrap file is located - so it's the one public file that always needs to be edited by hand for deployment to change that location as needed.
The "tests" directory usually holds any application specific unit, integration and acceptance tests. As I noted previously I'm deliberately taking a test-light approach to stay on topic (I have more than one testing/development article on Zend Devzone if interested in applying TDD or BDD). Main tests I expect to have are for application specific classes: any Model logic for example.
At the end of this step you should have the directory structure in place containing some empty files as follows (create them now, and we'll fill them in later):
/application/Bootstrap.php
/application/controller/IndexController.php
/application/views/scripts/index/index.phtml
/public/index.php
/public/.htaccessStep 2: Implementing Our Bootstrap File
Bootstrapping is when we setup the initial environment, configuration and Zend Framework initialisation for our application. It's not a difficult file to write, but it can grow ever larger and complex as your application gains features. To manage this I strongly suggest implementing it as a class with bitesize methods. Breaking it up does wonders for your sanity. For our Hello World skeleton application,/application/Boostrap.php contains:<?php
require_once 'Zend/Loader.php';
class Bootstrap
{
public static $frontController = null;
public static function run()
{
self::setupEnvironment();
self::prepare();
$response = self::$frontController->dispatch();
self::sendResponse($response);
}
public static function setupEnvironment()
{
error_reporting(E_ALL|E_STRICT);
ini_set('display_errors', true);
date_default_timezone_set('Europe/London');
}
public static function prepare()
{
self::setupFrontController();
self::setupView();
}
public static function setupFrontController()
{
Zend_Loader::registerAutoload();
self::$frontController = Zend_Controller_Front::getInstance();
self::$frontController->throwExceptions(true);
self::$frontController->returnResponse(true);
self::$frontController->setControllerDirectory(
dirname(<u>_FILE_</u>) . '/controllers'
);
}
public static function setupView()
{
$view = new Zend_View;
$view->setEncoding('UTF-8');
$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer($view);
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
}
public static function sendResponse(Zend_Controller_Response_Http $response)
{
$response->setHeader('Content-Type', 'text/html; charset=UTF-8', true);
$response->sendResponse();
}
}
require_once 'Zend/Loader.php';
class Bootstrap
{
public static $frontController = null;
public static function run()
{
self::setupEnvironment();
self::prepare();
$response = self::$frontController->dispatch();
self::sendResponse($response);
}
public static function setupEnvironment()
{
error_reporting(E_ALL|E_STRICT);
ini_set('display_errors', true);
date_default_timezone_set('Europe/London');
}
public static function prepare()
{
self::setupFrontController();
self::setupView();
}
public static function setupFrontController()
{
Zend_Loader::registerAutoload();
self::$frontController = Zend_Controller_Front::getInstance();
self::$frontController->throwExceptions(true);
self::$frontController->returnResponse(true);
self::$frontController->setControllerDirectory(
dirname(<u>_FILE_</u>) . '/controllers'
);
}
public static function setupView()
{
$view = new Zend_View;
$view->setEncoding('UTF-8');
$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer($view);
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
}
public static function sendResponse(Zend_Controller_Response_Http $response)
{
$response->setHeader('Content-Type', 'text/html; charset=UTF-8', true);
$response->sendResponse();
}
}
Note: Due to some Serendipity bug in processing Geshi output, underline tags replace some underline characters such as for the PHP FILE constant which should have two underscrores proceeding and following "FILE". Sorry about that - S9Y has it's moments.
The Bootstrap class has two main methods here: run() and prepare(). run() executes a full application request, whereas prepare() only sets up (but doesn't execute) the Front Controller. For now let's focus on run() and all the steps our Bootstrap takes when it's called. The prepare() method is something I use when applying TDD or BDD to developing controllers, since I then delegate controller execution to the testing framework.
The first thing is to setup the local environment. So here we've set a timezone (as required by PHP5), enabled PHP's display_errors option, and set a fairly stiff error reporting level. If putting these basics into a class seems counter intuitive, you can simply extract them to the file head. Because I usually test extensively, I usually don't want these running during my tests and interfering with test results.
Secondly we prepare the Front Controller. Zend_Controller_Front is an implementation of the Front Controller Design Pattern, which acts as a single point of entry into a Model-View-Controller architected application. I kid you not - every request which is not for an existing public file will go through it. In our case, since the Bootstrap is included into, and called by public/index.php, "index.php" is our single point of entry.
Here, the prepare() stage involves setting up the Front Controller:
- get an instance of Zend_Controller_Front
- set it to throw all Exceptions (might want to disable this in production)
- set it to return a Response object when dispatched
- tell it where to find the default controllers
The last step of prepare() makes some changes to the View. The reason I've done this is to show how to make an extremely common change - adding support for UTF-8 encoded output (such as my name!). Don't worry too much about the details now - the ViewRenderer Action Helper and the Helper Broker lets one add or modify Action Helpers and is well covered by Matthew Weier O'Phinney over on Zend Devzone - http://devzone.zend.com/article/3350-Action-Helpers-in-Zend-Framework.
Now, what about
/public/index.php?<?php
// Update after deployment for location of non-public files
$root = dirname(dirname(<u>_FILE_</u>));
// We're assuming the Zend Framework is already on the include_path
set_include_path(
$root . '/application' . PATH_SEPARATOR
. $root . '/library' . PATH_SEPARATOR
. get_include_path()
);
require_once 'Bootstrap.php';
Bootstrap::run();
// Update after deployment for location of non-public files
$root = dirname(dirname(<u>_FILE_</u>));
// We're assuming the Zend Framework is already on the include_path
set_include_path(
$root . '/application' . PATH_SEPARATOR
. $root . '/library' . PATH_SEPARATOR
. get_include_path()
);
require_once 'Bootstrap.php';
Bootstrap::run();
Simplicity itself! The index is our Front Controller's single point of entry into a Zend Framework application, and all it needs to do is determine a valid include_path for the application and then delegate all other tasks to the Bootstrap class's run() method.
The final step is how to make all requests to "http://zfblog/" pass through index.php. This is solved for Apache web servers using it's Rewrite Module, which simply takes an incoming request URL, and rewrites it so it points to index.php. If you've never used Apache Rewrite before, you probably need to enable this module in http.conf before this works. Rewrite rules can be defined in Apache's http.conf file, or simply via a .htaccess file in "public" containing the rules:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .∗ index.phpThe .htaccess file here first turns on Rewriting, and then specifies rules. The rules here map all requests to index.php, unless they relate to a file which exists on the server (i.e. it won't rewrite URLs to CSS, Javascript or image files).
Step 4: Implementing The Index Controller
Before we continue, a quick word on controller/view wiring. Some time back it was decided to make automated rendering the default mode of operation for the Zend Framework. This means Controller will rarely need manual intervention to render a View - instead an Action Helper called the ViewRenderer (which should sound familiar since it used it to add UTF-8 encoding in our Bootstrap!) is called upon. This helper locates a View matching the name of the current Controller and Action and automatically renders it. There are ways to disable this automated rendering as documented in the manual - which is often useful when you want to bypass Zend_View (e.g. maybe you're outputting finalised JSON or XML and no further templating or processing is needed).If a URL to a Zend Framework application does not contain a controller or action reference, then the Zend Framework uses the default "index" value. Since we only intend requesting to the root URL of "http://zfblog/", we'll need an Index Controller containing an Index Action. Let's create one!
Add a file called "IndexController.php" in
application/controllers containing:<?php
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$this->view->title = 'Hello, World!';
}
}
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$this->view->title = 'Hello, World!';
}
}
All controllers must extend Zend_Controller_Action, since it contains all later referenced internal methods and properties common to all Controllers. If you need to tweak the basic Controller to add new methods or properties you may do so using a subclass of Zend_Controller_Action and then making all application Controllers extend this subclass instead. Be very careful however in subclassing this way - it's useful for adding new properties to the class, but additional methods are often better added as discrete Action Helpers.
Our new Action method ignores rendering; it's done automatically as discussed earlier. All we need to do is set View data we need. You can add almost anything to a View, since it just becomes a property in a View template. So feel free to add Objects and Arrays. If using Smarty or PHPTAL, this might be different.
Above we've told the View that templates might refer to a variable "title" and given it a value of "Hello, World!". Once the Action method is done, the ViewRenderer will kick in and try to render a template located at "/path/to/application/views/scripts/index/index.phtml".
And what use is a Controller, without a View to display?
Step 5: Implementing The View For IndexController::indexAction()
By default, the Zend Framework or more specifically the ViewRenderer Action Helper, will attempt to render a View template for all Controller Actions using a simple convention. For example, the template for IndexController::indexAction() should be located at "/index/index.phtml" within the "scripts" subdirectory of "views".The Zend_View component is responsible for rendering all View template, often with a little assistance (e.g. ViewRenderer or Zend_Layout) from the Controller. A Zend_View template is HTML (or some other output type) interspersed with PHP and it doesn't use a separate tag language such as what the Smarty template engine, for example, uses. Here's something to put into your index.phtml file for our first Zend Framework application:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="language" content="en" />
<title><?php echo $this->escape($this->title) ?></title>
</head>
<body>
<p>Hello, World!</p>
</body>
</html>
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="language" content="en" />
<title><?php echo $this->escape($this->title) ?></title>
</head>
<body>
<p>Hello, World!</p>
</body>
</html>
As you can see, it's almost pure HTML with a single PHP inclusion for the title text (which we set from our indexAction() method earlier!). To ensure security against Cross Site Scripting (XSS) we should escape all output from within the application, and Zend_View uses the PHP htmlspecialchars() function from it's Zend_View::escape() method. Another noteworthy mention is that, since we setup our View to use UTF-8 encoding, this encoding is also passed into htmlspecialchars().
If using escape() everywhere sounds bad - well, it is, and it isn't. It's a pain since it's something that would obviously benefit from automation - so not applying escaping is the exception by default. On the other hand it must be done at all times so if you ever see a variable being echoed that's not surrounded by the escape() method you should proceed with care.
Step 6: Did it work?
Go ahead and open up your browser. The base url of http://zfblog/ should now do it's thing and return a HTML document containing "Hello, World!".Conclusion
Getting a simple example up and running has already provided a few insights. But don't let your guard down just yet. Like any battle plan, it will not survive first contact with the enemy (Maugrim's Marvellous Blog is so evilIt's a start. If you're lucky, it may be many starts since you can reuse a similar basic skeleton to skip all those setup tasks for other Zend Framework projects.
Note: The source code for this entry is available to browse, or checkout with subversion, from http://svn.astrumfutura.org/zfblog/tags/Part3. The full source code for the entire application (as it exists thus far) from http://svn.astrumfutura.org/zfblog.
Continue to: Part 4: Setting the Design Stage with Blueprint CSS Framework and Zend_Layout
Posted by Pádraic Brady on Monday, April 28. 2008 at 17:42 in PHP General, PHP Security, Zend Framework
20 Comments Trackback (1)
20 Comments Trackback (1)

