<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>simon r jones</title>
	
	<link>http://www.simonrjones.net</link>
	<description />
	<lastBuildDate>Tue, 24 Jan 2012 00:06:42 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/simonrjones" /><feedburner:info uri="simonrjones" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>The Reichenbach Fall</title>
		<link>http://feedproxy.google.com/~r/simonrjones/~3/yjsR1dgc4yo/</link>
		<comments>http://www.simonrjones.net/2012/01/the-reichenbach-fall/#comments</comments>
		<pubDate>Tue, 24 Jan 2012 00:04:04 +0000</pubDate>
		<dc:creator>Simon R Jones</dc:creator>
				<category><![CDATA[Misc]]></category>

		<guid isPermaLink="false">http://www.simonrjones.net/?p=312</guid>
		<description><![CDATA[I love Sherlock, but just how did he pull off his own fake death at the end of series two? Molly was obviously involved, my wife thought he just threw Moriarty off the roof but on a second watch it doesn&#8217;t look that simple.
From the moment Sherlock leaves the reporter Kitty Riley&#8217;s house he says [...]]]></description>
			<content:encoded><![CDATA[<p>I love Sherlock, but just how did he pull off his own fake death at the end of series two? Molly was obviously involved, my wife thought he just threw Moriarty off the roof but on a second watch it doesn&#8217;t look that simple.<span id="more-312"></span></p>
<p>From the moment Sherlock leaves the reporter Kitty Riley&#8217;s house he says &#8220;There&#8217;s only one thing he needs to do to complete his game and that&#8217;s &#8211; &#8220;. He stops mid sentence, suddenly aware he has to commit suicide to stop Moriarty. From that point on my theory is he is planning his own fake death.</p>
<p>After that he visits Molly, and clearly engages her assistance in his plan. He then summons Watson to help him work out how to get Moriarty&#8217;s computer code. However, within a few minutes he seems to work out the whole binary code thing but keeps it from Watson. In fact he looks rather shifty towards Watson as if he&#8217;s hiding something. An external shot follows, and the night wears on. </p>
<p>In the next scene Watson looks exhausted after waking from sleeping at the desk and receives a call informing him Mrs Hudson has been shot. When he takes the call Sherlock again looks very shifty. It&#8217;s my theory Sherlock arranges that call to get Watson temporarily out of the way. Watson rather cuttingly says &#8220;friends protect people&#8221; in reply to Sherlock&#8217;s refusal to join him. Of course, Sherlock is doing this precisely in order to protect him. </p>
<p>Sherlock then meets Moriarty on the rooftop, in what is a superb verbal clash of two crazed geniuses. There are a few shots of the ground, where we see a clear rectangle drawn with what looks to be chalk on the pavement. This is Sherlock&#8217;s target.</p>
<p>After Sherlock seems to acquiesce to jumping he asks Moriarty for &#8220;a little privacy&#8221;. He briefly looks down and then laughs, I presume by this point the truck has parked next to the pavement marks, ready to put his plan into action.</p>
<p>After Moriarty&#8217;s shock suicide by gunshot Sherlock appears to panic. I don&#8217;t think Sherlock could have anticipated that. But it could also be to make it seem to those watching that he&#8217;s loosing it. </p>
<p>Watson arrives, the taxi setting down on the opposite side of the road. There are a few shots of a low building near the entrance to Barts which is in between Watson and the pavement where Sherlock falls. When Watson gets out of the taxi he directs him to a specific position, so he can&#8217;t see where he falls. That&#8217;s also why he later cries out to Watson &#8220;stay exactly where you are, don&#8217;t move&#8221;.</p>
<p>He talks on the phone for a while, presumably to ensure there are no pedestrians near when he falls. And of course, to try to convince Watson he really is the fake Moriarty wants everyone to believe.</p>
<p>When he does fall the street is totally empty.  It is very early in the morning, and two buses picked up all the waiting passengers. And I think it is Sherlock that falls &#8211; from the scene that much seems obvious. Presumably he lands on something that breaks his fall (provided by the driver of the van) and most likely swaps his body with a relatively fresh cadaver in his clothes. </p>
<p>At this point Watson&#8217;s view is obstructed by both the building in front of him and the van. And as soon as he runs to Sherlock he&#8217;s violently knocked to the ground by a cyclist &#8211; who promptly flees the scene.</p>
<p>The van next to the chalk rectangle suspiciously leaves very shortly after Sherlock falls. My guess is with Sherlock inside and driven by Molly. The body on the pavement is lying sideways and just outside of the chalk rectangle. Possibly because the body was just thrown out of the van? </p>
<p>So I think Sherlock knew he had to jump before he got there and he&#8217;d laid out an elaborate plan to fake his own death. This must have required the help of Molly (who helpfully works in a mortuary) and at least one other (the cyclist). It all seems very clever, and Sherlock had to keep the secret for months afterwards to ensure the assassins really were called off. </p>
<p>I wonder if the above is right, I guess I&#8217;ll just have to wait until series 3, whenever that will be&#8230;</p>
<p>Best line.. Sherlock: You&#8217;re insane. Moriarty: You&#8217;re just getting that now!</p>
<img src="http://feeds.feedburner.com/~r/simonrjones/~4/yjsR1dgc4yo" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.simonrjones.net/2012/01/the-reichenbach-fall/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.simonrjones.net/2012/01/the-reichenbach-fall/</feedburner:origLink></item>
		<item>
		<title>2012</title>
		<link>http://feedproxy.google.com/~r/simonrjones/~3/WWPXmOZd9pA/</link>
		<comments>http://www.simonrjones.net/2012/01/2012/#comments</comments>
		<pubDate>Sun, 01 Jan 2012 23:59:33 +0000</pubDate>
		<dc:creator>Simon R Jones</dc:creator>
				<category><![CDATA[Misc]]></category>

		<guid isPermaLink="false">http://www.simonrjones.net/?p=305</guid>
		<description><![CDATA[All has been a bit quiet here of late, 2011 was a very busy year for me both with work and family life. With half an hour left of the first day of 2012 I thought I&#8217;d record a few resolutions for the oncoming year.

Photography &#8211; I have a great Canon 40D camera which apart [...]]]></description>
			<content:encoded><![CDATA[<p>All has been a bit quiet here of late, 2011 was a very busy year for me both with work and family life. With half an hour left of the first day of 2012 I thought I&#8217;d record a few resolutions for the oncoming year.</p>
<ul>
<li>Photography &#8211; I have a great Canon 40D camera which apart from a fabulous trip to New York City has been unloved in the past year. I want to photograph more in 2012, starting with my attempt at the <a href="http://www.flickr.com/photos/simonrjones/sets/72157628670939621/">365 project over on Flickr</a></li>
<li>Writing &#8211; I also love writing, when I give myself the time to do so. I intend to write more in 2012 and will try to get my writing published elsewhere than just my own blog.
<li>Family &#8211; With Daniel almost 2 and Billy 4, our two boys are becoming very interesting little people. It goes without saying the next year will be full of lots of family time, but it doesn&#8217;t stop me making sure I remember it should be my focus.</li>
<li>Cycling &#8211; I rarely cycle to work these days, even though the 4 mile journey is less stressful via bike than car (and takes about the same time). Really need to do this more to offset the beer and pies <img src='http://www.simonrjones.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </li>
</ul>
<p>Let&#8217;s see how I do in a few months time!..</p>
<img src="http://feeds.feedburner.com/~r/simonrjones/~4/WWPXmOZd9pA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.simonrjones.net/2012/01/2012/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.simonrjones.net/2012/01/2012/</feedburner:origLink></item>
		<item>
		<title>Taming MAMP on the command line (and upgrading PEAR)</title>
		<link>http://feedproxy.google.com/~r/simonrjones/~3/S4w1d_KAevs/</link>
		<comments>http://www.simonrjones.net/2011/06/taming-mamp-on-the-command-line-and-upgrading-pear/#comments</comments>
		<pubDate>Fri, 24 Jun 2011 16:44:19 +0000</pubDate>
		<dc:creator>Simon R Jones</dc:creator>
				<category><![CDATA[Misc]]></category>

		<guid isPermaLink="false">http://www.simonrjones.net/2011/06/taming-mamp-on-the-command-line-and-upgrading-pear/</guid>
		<description><![CDATA[I&#8217;ve posted a new article to our company website on how to get MAMP, the command line and PEAR working together in harmony. 
Take a look at http://www.studio24.net/blog/taming-mamp-on-the-command-line-pear
]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve posted a new article to our company website on how to get MAMP, the command line and PEAR working together in harmony. </p>
<p>Take a look at <a href="http://www.studio24.net/blog/taming-mamp-on-the-command-line-pear">http://www.studio24.net/blog/taming-mamp-on-the-command-line-pear</a></p>
<img src="http://feeds.feedburner.com/~r/simonrjones/~4/S4w1d_KAevs" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.simonrjones.net/2011/06/taming-mamp-on-the-command-line-and-upgrading-pear/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.simonrjones.net/2011/06/taming-mamp-on-the-command-line-and-upgrading-pear/</feedburner:origLink></item>
		<item>
		<title>“EMBARRASSING: Father caught daughter on webcam” Facebook exploit</title>
		<link>http://feedproxy.google.com/~r/simonrjones/~3/A73k0gqHX8w/</link>
		<comments>http://www.simonrjones.net/2011/05/facebook-father-daughter-webcam-exploit/#comments</comments>
		<pubDate>Mon, 16 May 2011 23:28:15 +0000</pubDate>
		<dc:creator>Simon R Jones</dc:creator>
				<category><![CDATA[Misc]]></category>

		<guid isPermaLink="false">http://www.simonrjones.net/?p=292</guid>
		<description><![CDATA[I spotted a post today on Facebook which looked rather suspicious. The link was titled &#8220;EMBARRASSING: Father caught daughter on WEBCAM!!!&#8221; and was obviously designed to lure people in to clicking on the link. It went to the URL qok7.info which claimed to have a YouTube security verification notice (a CAPTCHA) you had to fill [...]]]></description>
			<content:encoded><![CDATA[<p>I spotted a post today on Facebook which looked rather suspicious. The link was titled &#8220;EMBARRASSING: Father caught daughter on WEBCAM!!!&#8221; and was obviously designed to lure people in to clicking on the link. It went to the URL <code>qok7.info</code> which claimed to have a YouTube security verification notice (a CAPTCHA) you had to fill in before viewing the video. </p>
<p>In fact, it&#8217;s a clickjacking exploit that contains a hidden form which submits a public comment on your Facebook account with a link back to this site. I first came across clickjacking exploits on <a href="http://shiflett.org/blog/2009/feb/twitter-dont-click-exploit">Chris Shiflett&#8217;s blog</a>, it&#8217;s a cunning method of hiding a real form within an iframe behind something like an image that usually has something clickable on it. In this case it has a fake CAPTCHA form whose fake form elements are lined up to submit the real Facebook status update form hidden in the iframe.</p>
<p>This exploit may be related to the daughter on webcam issue reported by <a href="http://nakedsecurity.sophos.com/2011/05/14/dad-catches-daughters-on-webcam-beware-viral-facebook-video-link/">Sophos</a> or this might just be an example of very successful keywords used by scammers.</p>
<p>I see it&#8217;s been reported on Facebook&#8217;s <a href="http://www.facebook.com/topic.php?uid=31987371885&#038;topic=17880">security pages</a>, I don&#8217;t know if it&#8217;s something Facebook can technically fix but I would hope they can ban links from this website to avoid users inadvertently spreading this exploit.</p>
<p>So if you&#8217;re a Facebook user don&#8217;t go clicking on links about daughters on webcams. Or any suspicious links for that matter. Always check URLs and if it looks dodgy, get out of there!</p>
<img src="http://feeds.feedburner.com/~r/simonrjones/~4/A73k0gqHX8w" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.simonrjones.net/2011/05/facebook-father-daughter-webcam-exploit/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://www.simonrjones.net/2011/05/facebook-father-daughter-webcam-exploit/</feedburner:origLink></item>
		<item>
		<title>Checking your Zend Framework route order</title>
		<link>http://feedproxy.google.com/~r/simonrjones/~3/Z9B8S5qLEGM/</link>
		<comments>http://www.simonrjones.net/2011/02/checking-your-zend-framework-route-order/#comments</comments>
		<pubDate>Tue, 08 Feb 2011 08:39:39 +0000</pubDate>
		<dc:creator>Simon R Jones</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Quick tips]]></category>
		<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://www.simonrjones.net/?p=271</guid>
		<description><![CDATA[The order that you create your routes in Zend Framework is important, with the last route defined in your code being matched first. This allows you to set up custom routes and if these aren&#8217;t matched Zend Framework helpfully falls back to the default route which is set up first. If you have a lot [...]]]></description>
			<content:encoded><![CDATA[<p>The order that you create your routes in Zend Framework is important, with the last route defined in your code being matched first. This allows you to set up custom routes and if these aren&#8217;t matched Zend Framework helpfully falls back to the default route which is set up first. If you have a lot of routes though, set up in different places, it can get difficult to verify the order of your routes.</p>
<p>Just use this snippet of code in your controller to return a list of route names set up in your ZF application in the order they are matched via the routing system (i.e. the route at number 1 is matched first, then route 2, etc).</p>
<pre class="brush: php">// Output list of routes, in the order they are matched
echo '&lt;ol&gt;';
$routes = array_reverse($this-&gt;getFrontController()-&gt;getRouter()-&gt;getRoutes());
foreach ($routes as $name =&gt; $route) {
    echo "	&lt;li&gt;$name&lt;/li&gt;\n";
}
echo '&lt;/ol&gt;';</pre>
<p>Find out more about the <a href="http://framework.zend.com/manual/en/zend.controller.router.html">ZF Router</a> at the ZF manual.</p>
<img src="http://feeds.feedburner.com/~r/simonrjones/~4/Z9B8S5qLEGM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.simonrjones.net/2011/02/checking-your-zend-framework-route-order/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.simonrjones.net/2011/02/checking-your-zend-framework-route-order/</feedburner:origLink></item>
		<item>
		<title>A contents index for Zend Framework manual pages</title>
		<link>http://feedproxy.google.com/~r/simonrjones/~3/KmKkp9Yxch4/</link>
		<comments>http://www.simonrjones.net/2010/08/contents-index-for-zf-manual-pages/#comments</comments>
		<pubDate>Thu, 05 Aug 2010 22:24:36 +0000</pubDate>
		<dc:creator>Simon R Jones</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://www.simonrjones.net/?p=239</guid>
		<description><![CDATA[The good old Zend Framework manual pages do suffer from being somewhat lengthy. I&#8217;ve thought they could do with an index to make navigation easier on those oh-so-long pages. So I wrote a quick JavaScript bookmarklet to do just that.


Just drag this ZF Manual Index bookmark onto your browser bookmarks bar. Clicking the bookmark will [...]]]></description>
			<content:encoded><![CDATA[<p>The good old <a href="http://framework.zend.com/manual/en/">Zend Framework manual pages</a> do suffer from being somewhat lengthy. I&#8217;ve thought they could do with an index to make navigation easier on those oh-so-long pages. So I wrote a quick JavaScript bookmarklet to do just that.</p>
<p><span id="more-239"></span><br />
<img src="http://www.simonrjones.net/wp-content/uploads/2010/08/Picture-2.png" alt="The content index inserted into a ZF manual page" title="The content index inserted into a ZF manual page" width="653" height="370" class="aligncenter size-full wp-image-243" /></p>
<p>Just drag this <a id="rs-login" href="javascript:ZF();function ZF(){var m=document.getElementById('manual-container');var h=C('h2');h.innerHTML='Index of Contents';var l=C('ul');var d=m.getElementsByTagName('div');var c=d.length;for(var y=0;y<c;y++){if(d[y].className=='section'){var i=C('li');var a=C('a');S(a,'href','#'+d[y].id);a.innerHTML=d[y].getElementsByTagName('h1')[0].innerHTML;A(i,a);A(l,i)}}var p=m.getElementsByTagName('hr')[0];m.insertBefore(C('hr'),p);m.insertBefore(h,p);m.insertBefore(l,p)}function C(n){return document.createElement(n)}function S(e,n,v){e.setAttribute(n,v)}function A(e,c){e.appendChild(c)}" onclick="alert('Drag this button onto your browser bookmarks bar.'); return false;">ZF Manual Index</a> bookmark onto your browser bookmarks bar. Clicking the bookmark will add a contents index to the top of any Zend Framework manual page. Helpful links ahoy!</p>
<p>If you want to <a href="/code/zf-manual-index.js">view the source code take a peek over here</a>.</p>
<img src="http://feeds.feedburner.com/~r/simonrjones/~4/KmKkp9Yxch4" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.simonrjones.net/2010/08/contents-index-for-zf-manual-pages/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://www.simonrjones.net/2010/08/contents-index-for-zf-manual-pages/</feedburner:origLink></item>
		<item>
		<title>Understanding the stack index for Zend Framework Controller plugins</title>
		<link>http://feedproxy.google.com/~r/simonrjones/~3/s6yT3YH2Hsk/</link>
		<comments>http://www.simonrjones.net/2010/07/undertstanding-zf-plugins-stack-index/#comments</comments>
		<pubDate>Thu, 15 Jul 2010 10:34:29 +0000</pubDate>
		<dc:creator>Simon R Jones</dc:creator>
				<category><![CDATA[Quick tips]]></category>
		<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://www.simonrjones.net/?p=227</guid>
		<description><![CDATA[Zend Framework Controller plugins are a powerful way to inject logic into your controller system at various points, such as before and after an action dispatch. Plugins are run in the order they are added, though it is possible to change the order by defining a custom stack index. ZF internal plugins such as Zend_Controller_Plugin_ErrorHandler, [...]]]></description>
			<content:encoded><![CDATA[<p>Zend Framework Controller plugins are a powerful way to inject logic into your controller system at various points, such as before and after an action dispatch. Plugins are run in the order they are added, though it is possible to change the order by defining a custom stack index. ZF internal plugins such as Zend_Controller_Plugin_ErrorHandler, which displays a nice Error 404 page, has a stack index of 100 to ensure it runs near the end of any plugin cycle. However, it&#8217;s not so obvious from the <a href="http://zendframework.com/manual/en/zend.controller.plugins.html">ZF manual</a> how to set a custom stack index.<br />
<span id="more-227"></span><br />
For example, you may have a common admin system layout that does various things to your admin page layout before the page is displayed. This could be registered in your controller as so:</p>
<pre class="brush: php">
$front = $this->getFrontController();
$adminLayout = new My_Controller_Plugin_AdminLayout($this->_helper->layout());
$front->registerPlugin($adminLayout);
</pre>
<p>If you want to alter the plugin so it runs after any other local plugins you could specify a stack index of 10 as a second argument:</p>
<pre class="brush: php">$front->registerPlugin($adminLayout, 10);</pre>
<p>However, unless you know what the existing plugin stack indexes are it&#8217;s difficult to know what stack index you should be using. You can use this small code snippet to output the plugin name and its associated stack index, again via your controller.</p>
<pre class="brush: php">foreach ($front->getPlugins() as $stackIndex => $plugin) {
    Zend_Debug::dump(get_class($plugin), $stackIndex);
}</pre>
<p>The standard ZF plugin list should be something like:</p>
<pre>99 string(36) "Zend_Layout_Controller_Plugin_Layout"
100 string(35) "Zend_Controller_Plugin_ErrorHandler"
999 string(33) "Zend_Wildfire_Channel_HttpHeaders"</pre>
<p>Hope this helps shed light on a rather useful feature of ZF! You can find out more at this excellent Devzone article on <a href="http://devzone.zend.com/article/3372">Front Controller Plugins in Zend Framework</a>.</p>
<img src="http://feeds.feedburner.com/~r/simonrjones/~4/s6yT3YH2Hsk" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.simonrjones.net/2010/07/undertstanding-zf-plugins-stack-index/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		<feedburner:origLink>http://www.simonrjones.net/2010/07/undertstanding-zf-plugins-stack-index/</feedburner:origLink></item>
		<item>
		<title>Sub-modules in Zend Framework</title>
		<link>http://feedproxy.google.com/~r/simonrjones/~3/M9DQU_A6Gao/</link>
		<comments>http://www.simonrjones.net/2010/06/sub-modules-in-zend-framework/#comments</comments>
		<pubDate>Sun, 20 Jun 2010 14:14:03 +0000</pubDate>
		<dc:creator>Simon R Jones</dc:creator>
				<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://www.simonrjones.net/?p=213</guid>
		<description><![CDATA[Following on from my post on Admin sub-modules I&#8217;ve refactored the code into a more generic sub-modules system. And fixed some bugs!
Its main features are:

Organise modules into sub-folders where you need to support a complex collection of controllers, views, models, etc
Supports URLs in the format: /sub-module/module/controller/action
Supports ID route: /sub-module/module/controller/action/id
Registers controller folder to support above URLs
Autoloads [...]]]></description>
			<content:encoded><![CDATA[<p>Following on from my post on <a href="/2010/06/admin-sub-modules-in-zf/">Admin sub-modules</a> I&#8217;ve refactored the code into a more generic sub-modules system. And fixed some bugs!</p>
<p>Its main features are:</p>
<ul>
<li>Organise modules into sub-folders where you need to support a complex collection of controllers, views, models, etc</li>
<li>Supports URLs in the format: /sub-module/module/controller/action</li>
<li>Supports ID route: /sub-module/module/controller/action/id</li>
<li>Registers controller folder to support above URLs</li>
<li>Autoloads module resources (using Zend_Application_Module_Autoloader) in the format: submodulenameModulename_Resource (i.e. AdminUser_Form_Registration)</li>
</ul>
<p>This supports URL routes such as:</p>
<p><em>www.domain.com/admin/user/ -><br />
application/admin-modules/user/controllers/IndexController.php</p>
<p>www.domain.com/cms/news -><br />
application/cms-modules/news/controllers/IndexController.php</em></p>
<p>It&#8217;s a bootstrap resource that can be enabled in your application.ini file as so:</p>
<pre>; 'admin' key is the name of sub-module group = path to sub-modules directory
resources.subModules.admin.directory = APPLICATION_PATH "/admin-modules"</pre>
<ul>
<li><a href="/code/sub-modules/Submodules.zip">Download S24_Application_Resource_Submodules</a></li>
<li><a href="/code/sub-modules/Submodules.phps">View S24_Application_Resource_Submodules source code</a></li>
</ul>
<img src="http://feeds.feedburner.com/~r/simonrjones/~4/M9DQU_A6Gao" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.simonrjones.net/2010/06/sub-modules-in-zend-framework/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.simonrjones.net/2010/06/sub-modules-in-zend-framework/</feedburner:origLink></item>
		<item>
		<title>Zend Framework Application Patterns at DPC10</title>
		<link>http://feedproxy.google.com/~r/simonrjones/~3/iuzIAU9dqsw/</link>
		<comments>http://www.simonrjones.net/2010/06/zend-framework-application-patterns-dpc10/#comments</comments>
		<pubDate>Fri, 11 Jun 2010 11:26:20 +0000</pubDate>
		<dc:creator>Simon R Jones</dc:creator>
				<category><![CDATA[Community]]></category>
		<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://www.simonrjones.net/?p=201</guid>
		<description><![CDATA[I&#8217;m currently in the fine city of Amsterdam enjoying what is incredibly my first PHP conference in ten years of developing with the language! Yesterday was tutorial day, with the full conference starting today, and I sat in Zend Framework Application Patterns by the informative and engaging Matthew Weier O&#8217;Phinney and Rob Allen. 
The session [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m currently in the fine city of Amsterdam enjoying what is incredibly my first PHP conference in ten years of developing with the language! Yesterday was tutorial day, with the full conference starting today, and I sat in Zend Framework Application Patterns by the informative and engaging <a href="http://weierophinney.net/">Matthew Weier O&#8217;Phinney</a> and <a href="http://akrabat.com/">Rob Allen</a>. </p>
<p>The session was excellent, well worth attending, and dipped into many areas of ZF. Some of which I knew already, but there was certainly enough good tips on how to organise applications efficiently in ZF which I&#8217;ll be telling my team all about when I get back to the UK. </p>
<p>My notes from the tutorial day appear below, be warned they are rather long! You can also review the <a href="http://www.slideshare.net/weierophinney/zend-framework-workshop-4480868">Zend Framework Workshop slides</a> over at Slideshare.<br />
<span id="more-201"></span></p>
<h2>Service layers</h2>
<p>Think of the application as an onion layer. From the inside out:</p>
<ul>
<li>Data Access objects (inner layer)</li>
<li>Data Mappers, Repositories, Transaction scripts</li>
<li>Domain Models and Entities (plain old PHP objects)</li>
<li>Service layer (outer layer: how will I interact with all this, the public API)</li>
</ul>
<p>It&#8217;s better to have a rich domain model, validation in domain model.</p>
<ul>
<li>Start with a plain old PHP object</li>
<li>Define schema based on objects you use</li>
<li>Create objects <em>first</em></li>
<li>Create schema once models have been created and tested</li>
</ul>
<p>Use mappers or transaction scripts to translate objects to data and back again.</p>
<p>Use Service Object to manipulate entities. Ie.</p>
<ul>
<li>fetchEntry()</li>
<li>fetchComments()</li>
<li>addComment(), etc</li>
</ul>
<p>What&#8217;s in a Service Layer?</p>
<ul>
<li>Application specific logic</li>
<li>Forms, validation, caching</li>
</ul>
<p>All the bits and pieces of the app I don&#8217;t want to deal with when writing the front-end.</p>
<p>Q: Should we use services for everything? <br />
A: Use for all parts of an app, easy to test</p>
<p>Rob added: </p>
<ul>
<li>he uses the form as the service layer</li>
<li>uses a mapper class based on Zend<em>Db</em>Table</li>
<li>compose the form in the service layer</li>
<li>tend to put search indexing on mapper layer (postSave method)</li>
</ul>
<h2>ACL</h2>
<p>ACL is roles, resources and rights (resource privileges). By default ACL operates as a whitelist (i.e. open up privileges to those you trust).  Blacklisting is less secure, not recommended.</p>
<p>One approach is to extend Zend_Acl and set up roles and resources in constructor. Roles are simply text strings. Resources are much the same but have an array of privileges that can also be passed (i.e. read, write).</p>
<p>Suggested logic:</p>
<ul>
<li>Check ACL</li>
<li>Check the role acting on a resource has a right to perform the action</li>
<li>isAllowed() &#8211; checks role, resource, privilege</li>
</ul>
<p>Matthew added:</p>
<ul>
<li>I like to throw a specific exception for failed ACLs so I can detect it</li>
<li>I can then check for another exception case in the ErrorController to display a Not Authorised page (nice approach)</li>
</ul>
<h2>Paginators</h2>
<ul>
<li>Return paginators from your domain models</li>
<li>Consumers can specify offset and limit</li>
<li>Zend_Paginator implements IteratorAggregate and toJson()</li>
<li>Paginators have efficient, lazy loading</li>
</ul>
<p>I.e. fetchAll() in domain model returns paginator</p>
<h2>Zend_Application</h2>
<ul>
<li>For common configuration &amp; per-environment configuration</li>
<li>Resource injection so you can use bootstrap as resource repository</li>
<li>Resources: in bootstrap class or as a re-usable plugin resource</li>
<li>Reusing for a service endpoint (i.e. set environment = jsonrpc)</li>
<li>Dependency tracking</li>
<li>Accessing resources via controller</li>
<li>Module bootstrapping. Not particularly good at present. Runs all module bootstraps if exists. Order of running module/main app boostraps ambigious. Don&#8217;t mix resource names/application.ini key names in main app + module. Kathryn Reeve wrote a nice <a href="http://binarykitten.me.uk/dev/zend-framework/177-active-module-based-config-with-zend-framework.html">module bootstrapping solution</a>. </li>
</ul>
<h2>Routing clinic</h2>
<p>As an aside throughout the day Matthew and Rob have been using the syntax <code>use Zend_Long_Class_Name as ShortName</code> &#8211; nice way to alias class names in PHP 5.3</p>
<ul>
<li>Basics: Name the route, specify the path, specify default values and optional validation</li>
<li>RegexRoute can support: <code>'blog/(?&lt;id&gt;[^.]*\+)\.(?&lt;format&gt;xml|json)'</code></li>
<li>That regex route stores first param as &#8216;id&#8217;, 2nd as &#8216;format&#8217; (that&#8217;s the handy <code>?&lt;name&gt;</code> format)</li>
<li>Regex routes are very fast, even more so in PHP 5.3</li>
<li>Touched on hostname routing which you then need to chain with a normal route</li>
<li>RESTful routes Zend via <a href="http://www.framework.zend.com/manual/en/zend.controller.router.html#zend.rest.route">Zend_Rest_Route</a></li>
<li>Can specify as default route, or add it for all controllers in a module</li>
<li>Or set REST routes for specific module / controller / actions</li>
<li>Controller then needs to extend Zend<em>Rest</em>Controller (listAction, postAction, etc)</li>
<li>ID is accessed via getParam(&#8217;id&#8217;)</li>
</ul>
<h2>Layout View Helpers</h2>
<ul>
<li>You should always run output via the $this->escape() view helper! In ZF2 it will be default when you echo content</li>
<li>Layout helpers: Set metadata, aggregate content, rendered in layout</li>
<li>If you have CSS/JS related to a particular layout put it in your view script. Really important for maintenance.</li>
<li>doctype() affects rendering of other view helpers, i.e. forms. Ideally needs to be set in bootstrap: <code>resources.view.doctype = XHTML1_STRICT</code></li>
<li>Head view helpers &#8211; can append and prepend content.
<ul>
<li>headMeta(), headTitle(), headScript(), headStyle(), headLink()</li>
<li>inlineStyle() &#8211; usually at top of body</li>
<li>inlineScript() &#8211; usually at bottom of body</li>
</ul>
</li>
<li>Use a layout helper if your functionality is shared across multiple view scripts to set all this common stuff</li>
<li>Append and prepend helps give an order to things like how CSS files are loaded</li>
<li><code>echo $this->baseUrl('path/to/file')</code> will return BASE_URL . &#8216;path/to/file&#8217; which is handy</li>
</ul>
<h2>Zend_Navigation</h2>
<ul>
<li>Helps manage trees of pointers to web pages</li>
<li>Supports HTML header links (i.e. next, previous, alt formats)</li>
<li>Can generate XML Sitemaps (used by Google)</li>
<li>Translate link text</li>
<li>Conditionally display links based on ACL</li>
<li>Two main concepts are pages and containers</li>
<li>Page types: MVC (router based URLs), URI (static URLs)</li>
<li><code>$page = Zend_Navigation_Page::factory(array('uri' =&gt; '/uri'))</code></li>
<li>Containers implements RecursiveInterator (iterate entire tree) &amp; Countable</li>
<li>Pages are also a container, you can have trees of pages</li>
<li>Add pages with $container->addPage()</li>
<li>Can add multiple pages via addPages()</li>
<li>You can remove pages based on index or order number </li>
<li>Visibility and active page properties affect whether a page is rendered to the page</li>
<li>Can find pages via methods like findOneBy(), findAllBy(), findOneByLabel()</li>
<li>You can define your own metadata for pages. For example if you have a &#8216;tag&#8217; property, find method for nav is findAllBy(&#8217;tag&#8217;, &#8216;value&#8217;) or findAllByTag(&#8217;value&#8217;)</li>
<li>
<p>Rendering on the page:</p>
<pre>foreach ($container as $page) {
    echo $page->label;
}</pre>
</li>
</ul>
<p><em>Cache your definitions</em></p>
<ul>
<li>On first request build programmatically and cache via toArray()</li>
<li>On subsequent requests load from cached array</li>
<li>Only use when you need it (i.e. not when doing JSON requests, service calls, etc)</li>
</ul>
<h3>Navigation view helpers</h3>
<ul>
<li><code>$view-&gt;navigation()-&gt;setAcl($acl);</code></li>
<li><code>$view-&gt;navigation()-&gt;setRole($role);</code></li>
<li>Can set default ACL and default roles in your bootstrap. I&#8217;d suggest passing ACL from your controller into your view.</li>
<li>Check for ACLs with hasAcl() &amp; hasRole()</li>
<li>echo <code>$this-&gt;navigation()-&gt;htmlify($page);</code></li>
<li>Renders link, uses label as title attribute</li>
<li>
<p>Conditionally echo link</p>
<pre>if ($this->navigation()->accept($page)) {
    $this->navigation()->htmlify($page);
}</pre>
</li>
<li>
<p>Note that other Nav view helpers are ACL-aware. I.e. <code>echo $this-&gt;navigation()-&gt;sitemap()</code></p>
</li>
<li>Sitemap specific metadata: lastmod, changefreq, priority</li>
<li>Set priority of news list pages lower than news pages themselves, so Google deeplinks to article pages rather than news list pages</li>
<li>Render breadcrumbs via <code>$this-&gt;navigation()-&gt;breadcrumb()</code></li>
<li>Useful configuration is setMaxDepth($depth) to avoid very long breadcrumbs</li>
<li><code>echo $view-&gt;navigation()-&gt;menu()-&gt;renderSubMenu()</code> &#8211; current active tree only</li>
<li>Gotcha with breadcrumbs. If current page is hidden it won&#8217;t render breadcrumb</li>
</ul>
<h2>Caching</h2>
<ul>
<li>Principle: Request from browser, is it cached? If so serve from cache, if not generate and save in cache</li>
<li>Adapter based system. Front-end = what to cache, Back-end = where to cache</li>
<li>Front-end options: set lifetime = null to cache forever (you then control when you empty the cache)</li>
<li>Manually initialise cache in bootstrap. I.e. _initCache()</li>
<li>
<p><strong>Don&#8217;t do it that way!</strong> There is a better way! Use Zend_Cache_Manager (manage multiple caches)</p>
<ul>
<li>Lazy loads on demand</li>
<li>Contains preconfigured caches</li>
<li>Application resource</li>
<li>Can use action helper to access it</li>
</ul>
</li>
</ul>
<pre>resources.cachemanager.default.frontend.options.lifetime = "..."
backend.options.cache<em>dir = APPLICATION</em>PATH ".."</pre>
<p>&#8216;default&#8217; is name of cache. Make sure you use different keys for multiple caches.</p>
<p>Controller:</p>
<pre><code>protected function _getCache()
{
   $bootstrap = $this->getInvokeArg('boostrap');
   return $bootstrap->getResource('cachemanager')-&gt;getCache('default');
}

// Helper
$cache = $this-&gt;_helper-&gt;getHelper('Cache')-&gt;getManager()-&gt;getCache('default');
</code></pre>
<ul>
<li>Zend_Cache_Page = full page caching &#8211; skip entire MVC stack</li>
<li>Implement in index.php before config loading</li>
<li>frontend options: &#8216;regexps&#8217;states which pages to enable page caching for. Not usually what you want for all pages on a site (i.e. dynamic content, forms for error validation, shopping baskets, etc)</li>
<li>You can state cache everything apart from exceptions with regex rules</li>
</ul>
<p><em>Some numbers to think about</em></p>
<ul>
<li>No caching: 12 trans/sec, cache DB: 29 trans/sec, cache page: 359 trans/sec</li>
<li>
<p>With APC enabled: no caching: 19 trans/secs, cache db: 289 trans/sec, cache page: 3251 trans/sec</p>
</li>
<li>
<p><a href="http://framework.zend.com/manual/en/zend.cache.backends.html#zend.cache.backends.static">Static page cache</a> exists to store cache of HTML files to local document root. Amazing performance.</p>
</li>
</ul>
<h2>Context Switching</h2>
<ul>
<li>Context switching is the act of providing different output based on criteria from the request</li>
<li>I.e. XMLHttpRequest, REST, Mobile device</li>
<li><a href="http://wurfl.sourceforge.net/">WRFL</a> &#8211; list of all mobile platforms and browser detection strings &amp; capabilities</li>
<li>Works via a &#8216;format&#8217; parameter request. This can be set via:<br />
&#8211; Query param<br />
&#8211; Special route<br />
&#8211; Chained routes (&#8217;xml&#8217;, chain with a &#8216;.&#8217; &#8211; i.e. controller/action.xml)<br />
&#8211; HTTP headers (Accept header)</li>
<li>ContextSwitch action helper determines whether action has context matching the format</li>
<li>Additional view suffix is added (i.e. viewscript.json.phtml, viewscript.xml.phtml)</li>
<li>Keeps things explicit so you know where your code/views are </li>
</ul>
<p>Add context switching to controller:</p>
<pre><code>$contextSwitch = $this-&gt;_helper-&gt;getHelper('contextSwitch');
$contextSwitch-&gt;addActionContext('action', 'format')
              -&gt;initContext();
</code></pre>
<h2>Sitemaps and robots.txt</h2>
<p>A neat example of context switching from Rob.</p>
<ul>
<li>Two static routes sitemap.xml &amp; robots.txt </li>
<li>Format values of &#8216;xml&#8217; and &#8216;txt&#8217;</li>
<li>Have to manually add context for txt (sets the correct Content-type header)</li>
<li>View = sitemap.xml.phtml &amp; robots.txt.phtml</li>
<li>Can support HTML version of sitemap in sitemap.phtml</li>
<li>So context switching changes Content-type and view script and uses same controller action</li>
<li>Can do other stuff by getting param &#8216;format&#8217;</li>
</ul>
<h2>Header detection</h2>
<ul>
<li>setHeader(&#8217;Vary&#8217;, &#8216;Accept&#8217;) &#8211; tells browser not to cache Accept header. Important!</li>
<li>Set format based on accept header</li>
<li>getHeader(&#8217;Accept&#8217;): application/json or application/xml</li>
<li>To be honest I got lost around here. Will check out slides &amp; Matthew&#8217;s blog later <img src='http://www.simonrjones.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </li>
</ul>
<h2>Form Decorators</h2>
<ul>
<li>Renders elements &amp; forms</li>
<li>Decorators stack from inside to outside</li>
<li>The order of your decorators is the most important thing</li>
</ul>
<p>Standard element decorators:</p>
<ol>
<li>Zend_Form_Decorator_ViewHelper &#8211; renders input field</li>
<li>Zend_Form_Decorator_Errors &#8211; UL list of errors</li>
<li>Zend_Form_Decorator_Description &#8211; P description</li>
<li>Zend_Form_Decorator_HtmlTag &#8211; DL/DL tags</li>
<li>
<p>Zend_Form_Decorator_Label &#8211; field label</p>
</li>
</ol>
<ul>
<li>The above happens in Zend_Form_Element::loadDefaultDecorators()</li>
<li>Decorator itself states whether it wraps other elements or not </li>
<li>Change this with: element->placement->prepend() &#8211; puts in front</li>
</ul>
<p>Standard form decorators:</p>
<ol>
<li>Zend_Form_Decorator_FormElements &#8211; form elements</li>
<li>Zend_Form_Decorator_HtmlTag  &#8211; DL tag</li>
<li>Zend_Form_Decorator_Form &#8211; FORM tag</li>
</ol>
<p>Common use cases to customise decorators:</p>
<ul>
<li>Change form element HTML (i.e. ul instead of dl)
<ul>
<li>Clear decorators and re-add them as you want them</li>
</ul>
</li>
<li>Change HTML outputted, i.e. parse form description using markdown
<ul>
<li>Extend Description decorator and alter output of description HTML</li>
</ul>
</li>
</ul>
<p>If you use your own decorator classes:</p>
<p><code>$form->addPrefixPath()</code> &#8211; for Form class<br />
<code>$form->addElementPrefixPath()</code> &#8211; for Form element &amp; Validate classes</p>
<ul>
<li>Write your own decorators. Implement a render() method to return HTML. Extends Zend_Form_Decorator_Abstract</li>
<li>render($content) accepts previously rendered content. You need to return the content you get in so you can wrap / append / prepend content &#8211; (that bit never made sense to me before!)</li>
<li>Good idea to check the user defined separator &amp; placement and alter output accordingly (otherwise you may break expected functionality)</li>
<li>Sidenote: Rob uses simple <code>App_</code> namespace for client website specific library files. Nice and straightforward</li>
<li>Rob displays errors on form, not element. He removes errors on each element and puts a list at the top. This is achieved via a custom decorator called <code>Form_Errors</code></li>
</ul>
<h2>Testing</h2>
<ul>
<li>What do I test?
<ul>
<li>Domain models  </li>
<li>Service layer objects</li>
<li>Integration testing (does your MVC generate the content you&#8217;re expecting?)</li>
</ul>
</li>
</ul>
<p><em>Testing MVC pages</em></p>
<ul>
<li>Extend Zend_Test_PHPUnit_ControllerTestCase</li>
<li>setup() creates instance of Zend_Application</li>
<li>assertions to match content. Uses CSS selectors aka jQuery (nice)</li>
<li>also supports XPath expressions to match content</li>
<li>redirect assertions</li>
<li>response assertions (response codes, matching header content)</li>
<li>request assertions (matching module, controller, action, route)</li>
</ul>
<p>I.e. Assert a H1 heading exists: <code>$this-&gt;assertQuery('#content h1')</code></p>
<h2>Q&amp;A</h2>
<p>I quizzed Rob in the break on CMS style routing where all page URLs are stored in a database. He expanded on this in the Q&amp;A session.</p>
<ul>
<li>MS_Controller_Router<em>Route</em>PostSlug (a slug = a URL fragment in good old WordPress terminology) </li>
<li>implements Zend_Controller_Router_Route_Interface</li>
<li>$router->addRoute(&#8217;postSlug&#8217;, new MS<em>Controller</em>Router<em>Route</em>PostSlug)</li>
<li>Must implement 3 methods:
<ul>
<li>getInstance() &#8211; not used in Rob&#8217;s example, <code>return new self();</code> neatly disables it</li>
<li>match() &#8211; must return array of module, controller, action. (This is totally undocumented in code or manual! Nice to see an example)</li>
<li>asemble() &#8211; creates a URL via url view helper. Usage may pass menu ID to create URL</li>
</ul>
</li>
</ul>
<p><em>match($path)</em></p>
<ul>
<li>method argument $path = URL path</li>
<li>if return false, router falls through to next route</li>
<li>Get slugs/URL fragments from DB in bootstrap &amp; cache it for speed</li>
<li>$slugs = array(URL => array(&#8217;module&#8217;, &#8216;controller&#8217;, &#8216;action&#8217;, &#8216;navigaton object&#8217;, &#8216;menu_id&#8217;) )</li>
<li>custom keys accessed via $request->getParam(&#8217;menu_id&#8217;)</li>
</ul>
<p><em>assemble($data = array(), etc)</em></p>
<ul>
<li>For normal routes you pass the module, controller, action, etc in via the $data array</li>
<li>In this example you can put the following into the $data array: full url, page, menuId</li>
<li>Returns URL string: output URL string . $this->_assembleRemainingParameters($data)</li>
<li>That last bit is important if any custom params have been passed to assemble by the user</li>
</ul>
<p>We all finished off with a stirring debate on how awful WSYIWYG editors are when someone asked how Rob convinces clients to use markdown.</p>
<ul>
<li>Rob just doesn&#8217;t give them a choice, doesn&#8217;t get many problems most clients OK (he is tech director so he can also decide within his company)</li>
<li>Rob uses <a href="http://daringfireball.net/projects/markdown/">Markdown</a> for clients (exactly what I used to markup all these notes!)</li>
<li>If you allow HTML input from the user then don&#8217;t try to sanitize with Zend<em>Filter</em>Striptags. Use <a href="http://htmlpurifier.org/">HTML Purifier</a> instead which can be used to create whitelists. Pádraic Brady has <a href="http://blog.astrumfutura.com/index.php?/plugin/tag/htmlpurifier">blogged a lot about HTML Purifier</a></li>
</ul>
<img src="http://feeds.feedburner.com/~r/simonrjones/~4/iuzIAU9dqsw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.simonrjones.net/2010/06/zend-framework-application-patterns-dpc10/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://www.simonrjones.net/2010/06/zend-framework-application-patterns-dpc10/</feedburner:origLink></item>
		<item>
		<title>Admin sub-modules in Zend Framework</title>
		<link>http://feedproxy.google.com/~r/simonrjones/~3/xIywv8oVOWk/</link>
		<comments>http://www.simonrjones.net/2010/06/admin-sub-modules-in-zf/#comments</comments>
		<pubDate>Tue, 08 Jun 2010 20:19:50 +0000</pubDate>
		<dc:creator>Simon R Jones</dc:creator>
				<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://www.simonrjones.net/?p=92</guid>
		<description><![CDATA[Modules in Zend Framework essentially allow us to organise a collection of controllers into sub-folders, giving URL to filesystem mapping such as:
domain.com/user/register -> app/modules/user/RegisterController.php
While useful when we need to expand our URLs (and organisation of code) beyond one set of controllers, there are a few things they don&#8217;t currently solve which I think would make [...]]]></description>
			<content:encoded><![CDATA[<p>Modules in Zend Framework essentially allow us to organise a collection of controllers into sub-folders, giving URL to filesystem mapping such as:</p>
<p><em>domain.com/user/register -> app/modules/user/RegisterController.php</em></p>
<p>While useful when we need to expand our URLs (and organisation of code) beyond one set of controllers, there are a few things they don&#8217;t currently solve which I think would make them first-class citizens within ZF.<br />
<span id="more-92"></span><br />
I quite frequently find myself wanting some form of sub-modules, usually in admin systems where one &#8220;admin&#8221; module just doesn&#8217;t cut it for larger sites. Without modules we&#8217;re stuck with an <code>AdminController.php</code> which obviously gets very messy, very quickly. With modules, we can have an &#8220;admin&#8221; module with as many controllers as we wish. This is fine, until a site needs to manage lots of discrete elements.</p>
<p>For example, imagine a site with a CMS, user management system and a custom system to search for wines. While you could have a CmsController, UserController and WinesController in your &#8220;admin&#8221; module, it&#8217;s likely Cms and User would benefit from splitting out into multiple controllers of their own.</p>
<p>There&#8217;s also the issue of modularity. If I design a user management app which I want to re-use, it&#8217;s easier if I can install this into a folder (or SVN external it). If reusable components are mixed in the same folder they become harder to maintain and keep up to date. </p>
<p>In one attempt to solve this I created an Admin Modules bootstrap resource, which hijacks the domain.com/admin route and enables you to add admin modules to this URL. </p>
<p>To enable an admin module simply create a folder in your application folder called admin-modules. Within here simply place each module. Mapping works as follows:</p>
<p><em>domain.com/admin/  <br />
-> application/admin-modules/default/controllers/IndexController.php</em></p>
<p><em>domain.com/admin/user/  <br />
-> application/admin-modules/user/controllers/IndexController.php</em></p>
<p>Within each admin module the normal ZF rules for URL routing applies. For convenience my bootstrap resource also sets up an ID route: </p>
<p><em>domain.com/admin/user/groups/view/4  <br />
-> application/admin-modules/user/controllers/GroupsController.php / viewAction</em><br /> passing a param called &#8220;id&#8221; with a value of 4.</p>
<p>The filesystem structure works as so:</p>
<pre>
application/
            admin-modules/
                          default/
                                  controllers/
                                  etc..
                          user/
                               controllers/
                               etc..
</pre>
<p>So <code>admin-modules</code> basically works like a copy of the main modules folder. Just for admin-only modules.</p>
<p>To enable this simply add the following line to your Zend_Application application.ini config file:</p>
<pre>; Register a custom Bootstrap resource path (needs to be in your include path)
pluginPaths.S24_Application_Resource = "S24/Application/Resource"

; Enable admin modules
resources.adminModules.enable = []</pre>
<p><strong>Note: </strong> Please now see the <a href="http://www.simonrjones.net/2010/06/sub-modules-in-zend-framework/">sub-modules system</a> for an improved way to implement this. </p>
<img src="http://feeds.feedburner.com/~r/simonrjones/~4/xIywv8oVOWk" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.simonrjones.net/2010/06/admin-sub-modules-in-zf/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		<feedburner:origLink>http://www.simonrjones.net/2010/06/admin-sub-modules-in-zf/</feedburner:origLink></item>
	</channel>
</rss>
