<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	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/"
	>

<channel>
	<title>Made of Everything You&#039;re Not &#187; Brain Dump</title>
	<atom:link href="http://blog.ericlamb.net/category/brain-dump/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.ericlamb.net</link>
	<description>Thoughts on programming, people and life</description>
	<lastBuildDate>Thu, 27 Oct 2011 01:29:00 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=</generator>
		<item>
		<title>It Was Supposed To Be Just An Experiment&#8230;</title>
		<link>http://blog.ericlamb.net/2011/10/it-was-supposed-to-be-just-an-experiment/</link>
		<comments>http://blog.ericlamb.net/2011/10/it-was-supposed-to-be-just-an-experiment/#comments</comments>
		<pubDate>Thu, 27 Oct 2011 01:21:42 +0000</pubDate>
		<dc:creator>Eric Lamb</dc:creator>
				<category><![CDATA[Brain Dump]]></category>
		<category><![CDATA[Rant]]></category>
		<category><![CDATA[CartThrob]]></category>
		<category><![CDATA[ExpressionEngine]]></category>
		<category><![CDATA[mithra62]]></category>

		<guid isPermaLink="false">http://blog.ericlamb.net/?p=3768</guid>
		<description><![CDATA[Last June I decided to do something unprecedented for me; I dipped my toe into the ExpressionEngine add-on market to try and sell some custom add-ons I&#8217;d written. Being the open source loving hippie that I am this felt pretty out of character for me at the time and I got a bit of grief [...]]]></description>
			<content:encoded><![CDATA[<p>Last June I decided to do something unprecedented for me; I dipped my toe into the <a href="http://expressionengine.com/" onclick="return TrackClick('http%3A%2F%2Fexpressionengine.com%2F','ExpressionEngine')" title="ExpressionEngine" target="_blank">ExpressionEngine</a> add-on market to try and sell some custom add-ons I&#8217;d written. Being the open source loving hippie that I am this felt pretty out of character for me at the time and I got a bit of grief for it from friends and my own id. But I&#8217;ve been talking about moving out of client services for far too long and figured a little experiment by writing some add-ons and selling them through <a href="http://devot-ee.com/" onclick="return TrackClick('http%3A%2F%2Fdevot-ee.com%2F','Devot%3Aee')" title="Devot:ee" target="_blank">Devot:ee</a> (the de facto marketplace for ExpressionEnigne add-ons) would be a good introduction into full time commercial development. Looking at things 4 months later I realize that deciding do that was one of most important things I could have done for my career.</p>
<div id="attachment_3777" class="wp-caption aligncenter" style="width: 367px"><a href="http://mithra62.com" onclick="return TrackClick('http%3A%2F%2Fmithra62.com','mithra62')"><img src="http://blog.ericlamb.net/wp-content/uploads/2011/10/mithra62_logo2.png" alt="mithra62" title="mithra62" width="357" height="185" class="size-full wp-image-3777" /></a><p class="wp-caption-text">mithra62</p></div>
<p>Those of you who read this blog regularly (which, I admit hasn&#8217;t been that regular of late) probably noticed that I&#8217;ve been writing more and more about <a href="http://blog.ericlamb.net/tag/expression-engine/" title="ExpressionEngine">ExpressionEngine</a> over the last year and a half. Obviously, I&#8217;ve been doing a lot of ExpressionEngine development. Mostly for client work but, like most things a good programmer does, I eventually built up a go to library of custom stuff for when new projects started. Just your normal stuff really; a couple extensions and modules to make my client&#8217;s lives easier and give that little &#8220;extra&#8221; to make a project sparkle. </p>
<p>Then, my <a href="http://blog.ericlamb.net/2011/05/cartthrob-2-0-beta-fun/" title="CartThrob 2.0 Beta Fun">CartThrob</a> project ended. This is a project that will forever have a place in my heart. Not the site specifically mind you, which is pretty awesome BTW, but the clients tight budget combined with needing more from the site than they had initially outlined created a dilemma that presented an opportunity. I could either hand off the site as is, which was to the client&#8217;s spec but not to their expectations, or I could continue working without getting paid to make the client happy. Then, a third option came to mind. </p>
<p>I proposed to the client that I build an add-on to handle their needs on the condition that I retain ownership of the add-on. The plan was simple; I&#8217;d write this add-on and then sell it on Devot:ee in the hopes that eventually I&#8217;d recoup my loss in time investment. Hence, the birth of my first commercial add-on now called <a href="http://mithra62.com/projects/view/ct-admin/" onclick="return TrackClick('http%3A%2F%2Fmithra62.com%2Fprojects%2Fview%2Fct-admin%2F','CT+Admin')" title="CT Admin" target="_blank">CT Admin</a>. That&#8230; I then sat on for a couple months while I worked up the courage to put it up for sale. </p>
<p>The more I thought about releasing CT Admin the more nervous I became over the prospect of putting out an add-on that&#8217;s only useful for users of another third party add-on. Starting out with a customer base that&#8217;s a small slice of another company&#8217;s customer base isn&#8217;t exactly a good way to measure an industry. So, I decided to release a second add-on around the same time as CT Admin. </p>
<p>This second add-on, now named <a href="http://mithra62.com/projects/view/backup-pro/" onclick="return TrackClick('http%3A%2F%2Fmithra62.com%2Fprojects%2Fview%2Fbackup-pro%2F','Backup+Pro')" title="Backup Pro" target="_blank">Backup Pro</a>, was at the time a specialized backup add-on I&#8217;d written for my hosted clients which due to certain configuration conflicts couldn&#8217;t use any of the existing ExpressionEngine backup add-ons on the market. Releasing Backup Pro was also a gamble though of a different sort; as mentioned there were existing add-ons within that niche and and one of those competitors has fiercely loyalty customers (Hi Tom!). But still, two add-ons are better than one. Right?</p>
<p>So on June 10th I had a stiff drink and posted my add-ons to Devot:ee. And a whole 24 hours later I sold my first add-on. Immediately, I realized I was in. I was hooked. The joy I felt when I got the email notification from Devot:ee about the sale was absolute and total. Someone had paid <em>me</em> money for proprietary code I had written that I was going to be able to sell again. Fuck. That was the sweetest $24 I ever made. </p>
<p>But $24 also came with a weird sense of responsibility I never really felt writing OSS code. With OSS I&#8217;d always kept things at my pace and, unless there was anything critical that affected me personally, individual issues other users experienced weren&#8217;t that big of a priority. But, with the ExpressionEngine add-ons, that someone had paid real cash money for, the paradigm shifted from random and faceless users to clients. In essence my customers became my clients and that&#8217;s a dynamic I&#8217;ve always been comfortable with in terms of knowing how to respond. Put simple; complete and total support until they&#8217;re happy. </p>
<p>And that&#8217;s really the key to everything right there. What initially started out as a small experiment I personally likened to an OSS project morphed into me sharing some responsibility with other developer&#8217;s client work. Shit got real all of a sudden. People were paying money on my code with the belief that the code would help them solve their client&#8217;s problems. For those users who had issues (and there were more than a few) my responsibility could not be denied. </p>
<p>Initially, I was naive enough to think I could just post my add-ons to Devot:ee and go about my life. I had no concept of how much care would have to go into support and customer satisfaction or the amount of hours I&#8217;d spend in the Devot:ee forums and answering questions on Twitter. To give an idea of just how many updates there have been; at the time of this writing Backup Pro is at version 1.8.1. That&#8217;s a lot of bug fixes and feature requests in just under 4 months. Almost to the point of ridiculousness. </p>
<p>But it&#8217;s addictive. Extremely so. The more feedback I got, and the more features and bugs that were fixed or added, the better the products became. The process creates a really nice positive feedback loop wherein we&#8217;re all winners. The customer gets a better product, I get to create a better product which, in turn, makes my products more appealing to others. It&#8217;s really something to be a part of. </p>
<p>So after 4 months of progressively improving my products, networking within the community, building a dedicated site for marketing and having an absurd advertising appetite and a library of 7 add-ons now, I look around and realize that I&#8217;m a part of something I had never anticipated; the ExpressionEngine community. And it feels like home.</p>
<div><a class="addthis_button" href="http://blog.ericlamb.net//addthis.com/bookmark.php?v=250" addthis:url='http://blog.ericlamb.net/2011/10/it-was-supposed-to-be-just-an-experiment/' addthis:title='It Was Supposed To Be Just An Experiment&#8230; '><img src="//cache.addthis.com/cachefly/static/btn/v2/lg-share-en.gif" width="125" height="16" alt="Bookmark and Share" style="border:0"/></a></div>]]></content:encoded>
			<wfw:commentRss>http://blog.ericlamb.net/2011/10/it-was-supposed-to-be-just-an-experiment/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Importing Legacy Users Into ExpressionEngine</title>
		<link>http://blog.ericlamb.net/2011/07/importing-legacy-users-into-expressionengine/</link>
		<comments>http://blog.ericlamb.net/2011/07/importing-legacy-users-into-expressionengine/#comments</comments>
		<pubDate>Thu, 21 Jul 2011 12:17:11 +0000</pubDate>
		<dc:creator>Eric Lamb</dc:creator>
				<category><![CDATA[Brain Dump]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[ExpressionEngine]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://blog.ericlamb.net/?p=3753</guid>
		<description><![CDATA[Fuck. We spent all this time convincing one of the oldest clients with the agency into upgrading their old and kludgy, PHP 3 era, nightmare website into a snazzy ExpressionEngine 2.2 gem of modern technology. But, like the assholes we are, we didn&#8217;t quite think through the fact that they have a complicated data structure [...]]]></description>
			<content:encoded><![CDATA[<p>Fuck. We spent all this time convincing one of the oldest clients with the agency into upgrading their old and kludgy, PHP 3 era, nightmare website into a snazzy ExpressionEngine 2.2 gem of modern technology. But, like the assholes we are, we didn&#8217;t quite think through the fact that they have a complicated data structure (having been built when PHP <em>really</em> sucked) that we&#8217;d have to import into ExpressionEngine. So, you know, fuck&#8230;</p>
<p>But then, after the pants come on and a moment to reflect is had and the obviousness of the overreaction becomes clear. This is ExpressionEngine after all; there&#8217;s bound to be some utility available to handle this with the least amount of pain possible. And there is: the <a title="ExpressionEngine Member Import Utility" href="http://expressionengine.com/user_guide/cp/tools/utilities/member_import/index.html" onclick="return TrackClick('http%3A%2F%2Fexpressionengine.com%2Fuser_guide%2Fcp%2Ftools%2Futilities%2Fmember_import%2Findex.html','ExpressionEngine+Member+Import+Utility')" target="_blank">Member Import Utility</a> and, even better, it&#8217;s included into the core of ExpressionEngine so it&#8217;s already there.</p>
<p>And in traditional ExpressionEngine fashion its built in much the same and logical way most would design an import system. There are two different modes to work with: one to <a title="Import from XML" href="http://expressionengine.com/user_guide/cp/tools/utilities/member_import/import_from_xml.html" onclick="return TrackClick('http%3A%2F%2Fexpressionengine.com%2Fuser_guide%2Fcp%2Ftools%2Futilities%2Fmember_import%2Fimport_from_xml.html','Import+from+XML')" target="_blank">import an ExpressionEngine Member XML file</a> and another to create said file from a <a title="Convert Delimited Data to XML" href="http://expressionengine.com/user_guide/cp/tools/utilities/member_import/convert_to_xml.html" onclick="return TrackClick('http%3A%2F%2Fexpressionengine.com%2Fuser_guide%2Fcp%2Ftools%2Futilities%2Fmember_import%2Fconvert_to_xml.html','Convert+Delimited+Data+to+XML')" target="_blank">CSV formatted file</a>.</p>
<p>The CSV format conversion wasn&#8217;t really all that for the purposes of this project (mostly due to conditional rewriting of specific column entries) but it does have it&#8217;s uses. For instance, with smaller, self contained, exports it should be a snap to easily hand over the export straight from phpMyAdmin and convert it into an <a title="Member XML Format" href="http://expressionengine.com/user_guide/cp/tools/utilities/member_import/member_xml_format.html" onclick="return TrackClick('http%3A%2F%2Fexpressionengine.com%2Fuser_guide%2Fcp%2Ftools%2Futilities%2Fmember_import%2Fmember_xml_format.html','Member+XML+Format')" target="_blank">ExpressionEngine Member XML</a> file. One big drawback though is that the CSV import doesn&#8217;t appear to create new custom member fields on import so manual mapping is required per import.</p>
<p>The flip side of the CSV format convertor is the straight ExpressionEngine Member XML import utility. The big drag with this, and only if you don&#8217;t use the CSV convertor, is that a script has to be written to handle the conversion. Not too big a deal but, considering most installations will vary, this is going to require manual intervention every time a new system is imported. But, unlike the CSV convertor, the Member XML importer does actually create missing custom fields so updating the system shouldn&#8217;t be too big  a drag.</p>
<p>For my purposes I wrote a dirty little script to create the XML file for me so it was simply a matter of importing. I&#8217;m lazy, in the long term, so keeping as much of this as simple as possible is always a priority for me. But, since it&#8217;s a throw away thing cleanliness and best practices is less important than getting me more time to do real work. A word of caution though; the XML parser within ExpressionEngine isn&#8217;t very helpful when it comes to error messages and telling you what went wrong. You will curse.</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
</pre></td><td class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #000088;">$sql</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;SELECT * FROM users&quot;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$result</span> <span style="color: #339933;">=</span> <span style="color: #990000;">mysql_query</span><span style="color: #000;">&#40;</span><span style="color: #000088;">$sql</span><span style="color: #000;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #22f;">if</span><span style="color: #000;">&#40;</span><span style="color: #339933;">!</span><span style="color: #000088;">$result</span><span style="color: #000;">&#41;</span>
<span style="color: #000;">&#123;</span>
	<span style="color: #22f;">echo</span> <span style="color: #990000;">mysql_error</span><span style="color: #000;">&#40;</span><span style="color: #000;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #990000;">exit</span><span style="color: #339933;">;</span>
<span style="color: #000;">&#125;</span>
<span style="color: #000088;">$ee</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;&lt;members&gt;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
<span style="color: #22f;">while</span><span style="color: #000;">&#40;</span><span style="color: #000088;">$row</span> <span style="color: #339933;">=</span> <span style="color: #990000;">mysql_fetch_assoc</span><span style="color: #000;">&#40;</span><span style="color: #000088;">$result</span><span style="color: #000;">&#41;</span><span style="color: #000;">&#41;</span>
<span style="color: #000;">&#123;</span>
	<span style="color: #000088;">$ee</span> <span style="color: #339933;">.=</span> <span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\t</span>&lt;member&gt;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
	<span style="color: #000088;">$ee</span> <span style="color: #339933;">.=</span> <span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\t</span><span style="color: #000099; font-weight: bold;">\t</span>&lt;username&gt;&lt;![CDATA[&quot;</span><span style="color: #339933;">.</span><span style="color: #000088;">$row</span><span style="color: #000;">&#91;</span><span style="color: #0000ff;">'first_name'</span><span style="color: #000;">&#93;</span><span style="color: #339933;">.</span><span style="color: #0000ff;">&quot; &quot;</span><span style="color: #339933;">.</span><span style="color: #000088;">$row</span><span style="color: #000;">&#91;</span><span style="color: #0000ff;">'last_name'</span><span style="color: #000;">&#93;</span><span style="color: #339933;">.</span><span style="color: #0000ff;">&quot;]]&gt;&lt;/username&gt;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
	<span style="color: #000088;">$ee</span> <span style="color: #339933;">.=</span> <span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\t</span><span style="color: #000099; font-weight: bold;">\t</span>&lt;screen_name&gt;&lt;![CDATA[&quot;</span><span style="color: #339933;">.</span><span style="color: #000088;">$row</span><span style="color: #000;">&#91;</span><span style="color: #0000ff;">'first_name'</span><span style="color: #000;">&#93;</span><span style="color: #339933;">.</span><span style="color: #0000ff;">&quot; &quot;</span><span style="color: #339933;">.</span><span style="color: #000088;">$row</span><span style="color: #000;">&#91;</span><span style="color: #0000ff;">'last_name'</span><span style="color: #000;">&#93;</span><span style="color: #339933;">.</span><span style="color: #0000ff;">&quot;]]&gt;&lt;/screen_name&gt;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
	<span style="color: #000088;">$ee</span> <span style="color: #339933;">.=</span> <span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\t</span><span style="color: #000099; font-weight: bold;">\t</span>&lt;legacy_subscriber_id&gt;&lt;![CDATA[&quot;</span><span style="color: #339933;">.</span><span style="color: #000088;">$row</span><span style="color: #000;">&#91;</span><span style="color: #0000ff;">'subscriber_id'</span><span style="color: #000;">&#93;</span><span style="color: #339933;">.</span><span style="color: #0000ff;">&quot;]]&gt;&lt;/legacy_subscriber_id&gt;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
	<span style="color: #22f;">foreach</span><span style="color: #000;">&#40;</span><span style="color: #000088;">$row</span> <span style="color: #22f;">AS</span> <span style="color: #000088;">$key</span> <span style="color: #339933;">=&gt;</span> <span style="color: #000088;">$value</span><span style="color: #000;">&#41;</span>
	<span style="color: #000;">&#123;</span>
		<span style="color: #22f;">if</span><span style="color: #000;">&#40;</span><span style="color: #000088;">$value</span> <span style="color: #339933;">==</span> <span style="color: #0000ff;">''</span><span style="color: #000;">&#41;</span>
		<span style="color: #000;">&#123;</span>
			<span style="color: #22f;">continue</span><span style="color: #339933;">;</span>
		<span style="color: #000;">&#125;</span>
		<span style="color: #000088;">$ee</span> <span style="color: #339933;">.=</span> <span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\t</span><span style="color: #000099; font-weight: bold;">\t</span>&lt;&quot;</span><span style="color: #339933;">.</span><span style="color: #000088;">$key</span><span style="color: #339933;">.</span><span style="color: #0000ff;">&quot;&gt;&lt;![CDATA[&quot;</span><span style="color: #339933;">.</span><span style="color: #000088;">$value</span><span style="color: #339933;">.</span><span style="color: #0000ff;">&quot;]]&gt;&lt;/&quot;</span><span style="color: #339933;">.</span><span style="color: #000088;">$key</span><span style="color: #339933;">.</span><span style="color: #0000ff;">&quot;&gt;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
	<span style="color: #000;">&#125;</span>
	<span style="color: #000088;">$ee</span> <span style="color: #339933;">.=</span> <span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\t</span>&lt;/member&gt;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
<span style="color: #000;">&#125;</span>
&nbsp;
<span style="color: #000088;">$ee</span> <span style="color: #339933;">.=</span> <span style="color: #0000ff;">'&lt;/members&gt;'</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #22f;">echo</span> <span style="color: #000088;">$ee</span><span style="color: #339933;">;</span>
<span style="color: #990000;">exit</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></td></tr></table></div>

<p>The above code is fine if your existing database is cool but, and this is weird, ExpressionEngine goes full retard when it encounters anything it doesn&#8217;t like in the XML and won&#8217;t let you proceed. It&#8217;s really quite stunning. The XML importer knows that duplicate email addresses are bad in the database but instead of, say, allowing you to skip or force things the importer will just die and scream about a duplicate leaving you the only option of then manually pruning your data. Why they couldn&#8217;t be bothered with a checkbox or something allowing you to skip bad entries is beyond me but it makes the experience really, <em>really</em>, painful if your data sucks. </p>
<p>Still though, if your data is good to begin with the importer is a nice touch.</p>
<div><a class="addthis_button" href="http://blog.ericlamb.net//addthis.com/bookmark.php?v=250" addthis:url='http://blog.ericlamb.net/2011/07/importing-legacy-users-into-expressionengine/' addthis:title='Importing Legacy Users Into ExpressionEngine '><img src="//cache.addthis.com/cachefly/static/btn/v2/lg-share-en.gif" width="125" height="16" alt="Bookmark and Share" style="border:0"/></a></div>]]></content:encoded>
			<wfw:commentRss>http://blog.ericlamb.net/2011/07/importing-legacy-users-into-expressionengine/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Customizing ExpressionEngine 2.x Message Pages</title>
		<link>http://blog.ericlamb.net/2011/06/customizing-expressionengine-2-x-message-pages/</link>
		<comments>http://blog.ericlamb.net/2011/06/customizing-expressionengine-2-x-message-pages/#comments</comments>
		<pubDate>Tue, 07 Jun 2011 07:00:47 +0000</pubDate>
		<dc:creator>Eric Lamb</dc:creator>
				<category><![CDATA[Brain Dump]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[ExpressionEngine]]></category>

		<guid isPermaLink="false">http://blog.ericlamb.net/?p=3718</guid>
		<description><![CDATA[An unfortunate issue with ExpressionEngine is how internal messaging is handled. It&#8217;s really unfortunate that such a nice platform like ExpressionEngine shows such a wart when it comes to managing system pages and templates. Anyone who&#8217;s ever had to modify the message pages can tell you; it&#8217;s a dirty and counter intuitive deed requiring a [...]]]></description>
			<content:encoded><![CDATA[<p>An unfortunate issue with <a href="http://expressionengine.com/" onclick="return TrackClick('http%3A%2F%2Fexpressionengine.com%2F','ExpressionEngine')" title="ExpressionEngine" target="_blank">ExpressionEngine</a> is how internal messaging is handled. It&#8217;s really unfortunate that such a nice platform like ExpressionEngine shows such a wart when it comes to managing system pages and templates. Anyone who&#8217;s ever had to modify the message pages can tell you; it&#8217;s a dirty and counter intuitive deed requiring a lack of best practices that will leave you feeling pretty gross. </p>
<div id="attachment_3736" class="wp-caption aligncenter" style="width: 639px"><img src="http://blog.ericlamb.net/wp-content/uploads/2011/06/ee_default_message2.jpg" alt="Customizing ExpressionEngine 2.x Message Pages" title="Customizing ExpressionEngine 2.x Message Pages" width="629" height="130" class="size-full wp-image-3736" /><p class="wp-caption-text">Customizing ExpressionEngine 2.x Message Pages</p></div>
<p>The current method of modifying the message pages works as you would expect; this being an ExpressionEngine site you just go to the Design section and you&#8217;ll find them under &#8220;Message Pages&#8221; . There are 3 different types of message pages you can edit (Email Notifications, User Messages and Offline Template) and while it would seem logical on how what to do to get going you&#8217;d probably be wrong (if you hadn&#8217;t ever tried before). There are a couple problems here&#8230;</p>
<p>First, you can&#8217;t use EE template tags within those templates. It&#8217;s the damnedest thing too because some of those templates (User Messages and Email Notifications) do allow <em>specific</em> tags; they just don&#8217;t parse any others. So, if you&#8217;re a good little programmer and you build your site using global embeds for building your templates (head &#038; footer for example) you&#8217;re hosed. Your only option is to hard code your HTML which, if you&#8217;ve built even a simple site, you&#8217;ll instantly be filled with remorse and guilt over creating such a maintenance nightmare. </p>
<p>Second, and after the above it&#8217;s a minor issue though if you&#8217;re good enough for the above to be an issue this&#8217;ll probably piss you off too; you can&#8217;t save those templates as files. This means version control is going to be a bit of a problem. After nearly 10 years of having version control beat into me this hurt pretty bad. To be honest, because of tight deadline and even tighter budgets I could have lived with the guilt and shame of hard coding just to finish the project but taking away my version control was like adding insult to injury. </p>
<p>It&#8217;s quite the head scratcher when you get right down to it; a smart and clever company redesigns their entire product from the ground up and completely drops the ball when it comes to the messaging system. It&#8217;s a shame&#8230;</p>
<p>There is another option though it does require money; a really nice extension called <a title="Custom System Messages" href="http://devot-ee.com/add-ons/custom-system-messages/" onclick="return TrackClick('http%3A%2F%2Fdevot-ee.com%2Fadd-ons%2Fcustom-system-messages%2F','Custom+System+Messages')" target="_blank">Custom System Messages</a>. The extension was written by <a href="http://devot-ee.com/developers/brian-litzinger/" onclick="return TrackClick('http%3A%2F%2Fdevot-ee.com%2Fdevelopers%2Fbrian-litzinger%2F','Brian+Litzinger')" title="Brian Litzinger" target="_blank">Brian Litzinger</a> and is available for $12 through the ExpressionEngine marketplace <a href="http://devot-ee.com/" onclick="return TrackClick('http%3A%2F%2Fdevot-ee.com%2F','Devot%3Aee')" title="Devot:ee" target="_blank">Devot:ee</a>.</p>
<div id="attachment_3738" class="wp-caption aligncenter" style="width: 310px"><a href="http://blog.ericlamb.net/wp-content/uploads/2011/06/custom_system_message.jpg" onclick="return TrackClick('http%3A%2F%2Fblog.ericlamb.net%2Fwp-content%2Fuploads%2F2011%2F06%2Fcustom_system_message.jpg','Custom+System+Messages')"><img src="http://blog.ericlamb.net/wp-content/uploads/2011/06/custom_system_message-300x139.jpg" alt="Custom System Messages" title="Custom System Messages" width="300" height="139" class="size-medium wp-image-3738" /></a><p class="wp-caption-text">Custom System Messages</p></div>
<p>Custom System Messages works exactly as you would expect ExpressionEngine to work; through a nice interface you choose custom templates for specific messages. You can set a different template for both message scenarios (General Error and Form Submission Error) as well as change the behavior of the {link} template tag so it doesn&#8217;t use the lame ass JavaScript &#8220;back link&#8221;. </p>
<p>The best part though is that Custom System Messages allows you to set special templates based off of all the actions your site has (even those from other modules and extensions). So, for example, if you have a CartThrob site you can set a special template for when adding an item to your cart fails and separate template for when updating an item within a cart fails. It&#8217;s really quite slick and for only $12 it&#8217;s very reasonable. Priced just enough to dissuade any developer from rolling their own solution. </p>
<p>I do have to say how disappointing it is to see ExpressionEngine lacking this built in. To me, it&#8217;s reminiscent of buying a car and being charged extra for the gas cap. Sure, I can just go buy a gas cap for a couple bucks but it really should be a part of the car to begin with. </p>
<div><a class="addthis_button" href="http://blog.ericlamb.net//addthis.com/bookmark.php?v=250" addthis:url='http://blog.ericlamb.net/2011/06/customizing-expressionengine-2-x-message-pages/' addthis:title='Customizing ExpressionEngine 2.x Message Pages '><img src="//cache.addthis.com/cachefly/static/btn/v2/lg-share-en.gif" width="125" height="16" alt="Bookmark and Share" style="border:0"/></a></div>]]></content:encoded>
			<wfw:commentRss>http://blog.ericlamb.net/2011/06/customizing-expressionengine-2-x-message-pages/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Connect to Multiple Databases with Zend Framework</title>
		<link>http://blog.ericlamb.net/2011/05/connect-to-multiple-databases-with-zend-framework/</link>
		<comments>http://blog.ericlamb.net/2011/05/connect-to-multiple-databases-with-zend-framework/#comments</comments>
		<pubDate>Tue, 31 May 2011 07:00:54 +0000</pubDate>
		<dc:creator>Eric Lamb</dc:creator>
				<category><![CDATA[Brain Dump]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[zend framework]]></category>

		<guid isPermaLink="false">http://blog.ericlamb.net/?p=3614</guid>
		<description><![CDATA[I recently had a project where there would be 2 databases being used together; one locally and the other hosted externally by the client. Using traditional PHP this wouldn&#8217;t be a problem (just pass a link to the database I want to use on a per query basis and I&#8217;m done), but, since this project [...]]]></description>
			<content:encoded><![CDATA[<p>I recently had a project where there would be 2 databases being used together; one locally and the other hosted externally by the client. Using traditional PHP this wouldn&#8217;t be a problem (just pass a link to the database I want to use on a per query basis and I&#8217;m done), but, since this project was to use the <a title="Zend Framework" href="http://framework.zend.com/" onclick="return TrackClick('http%3A%2F%2Fframework.zend.com%2F','Zend+Framework')" target="_blank">Zend Framework</a> and I&#8217;d never attempted this sort of thing before with it, I was in quite the pickle.</p>
<p>Doing some quick Googling turned up an article that seemed to fit the bill: <a title="Zend Framework: Connecting to 2 databases" href="http://blog.ekini.net/2009/03/04/zend-framework-connecting-to-2-databases/" onclick="return TrackClick('http%3A%2F%2Fblog.ekini.net%2F2009%2F03%2F04%2Fzend-framework-connecting-to-2-databases%2F','Zend+Framework%3A+Connecting+to+2+databases')" target="_blank">Zend Framework: Connecting to 2 databases</a> written by <a title="Wenbert Del Rosario" href="http://blog.ekini.net/" onclick="return TrackClick('http%3A%2F%2Fblog.ekini.net%2F','Wenbert+Del+Rosario')" target="_blank">Wenbert Del Rosario</a>. Unfortunately though, while it was a good read and very informative, my circumstances were a little different. The database I had to connect to was hosted on an external network that I wouldn&#8217;t have local access to. I had to go over the wide Internet to connect. Using Wenbert&#8217;s tutorial would entail a constant connection on every request whether it needed it or not and that just wouldn&#8217;t do.</p>
<p>Anyone who&#8217;s ever had to connect to an external database can tell you it&#8217;s far from an ideal scenario. The latency involved with this strategy is noticeable and caching is pretty much required for any sane strategy. Still, Wenbert&#8217;s article was definitely helpful for developing my strategy.</p>
<p>As in Wenbert&#8217;s article I started with putting the connection information into my config.ini file</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">//config.ini</span>
resources<span style="color: #339933;">.</span>db<span style="color: #339933;">.</span>adapter <span style="color: #339933;">=</span> PDO_MYSQL
resources<span style="color: #339933;">.</span>db<span style="color: #339933;">.</span>params<span style="color: #339933;">.</span>host <span style="color: #339933;">=</span> localhost
resources<span style="color: #339933;">.</span>db<span style="color: #339933;">.</span>params<span style="color: #339933;">.</span>username <span style="color: #339933;">=</span> <span style="color: #339933;">****</span>
resources<span style="color: #339933;">.</span>db<span style="color: #339933;">.</span>params<span style="color: #339933;">.</span>password <span style="color: #339933;">=</span> <span style="color: #339933;">****</span>
resources<span style="color: #339933;">.</span>db<span style="color: #339933;">.</span>params<span style="color: #339933;">.</span>dbname <span style="color: #339933;">=</span> local_db 
&nbsp;
externaldb<span style="color: #339933;">.</span>adapter <span style="color: #339933;">=</span> PDO_MYSQL
externaldb<span style="color: #339933;">.</span>params<span style="color: #339933;">.</span>host <span style="color: #339933;">=</span> example<span style="color: #339933;">.</span>com
externaldb<span style="color: #339933;">.</span>params<span style="color: #339933;">.</span>username <span style="color: #339933;">=</span> <span style="color: #339933;">****</span>
externaldb<span style="color: #339933;">.</span>params<span style="color: #339933;">.</span>password <span style="color: #339933;">=</span> <span style="color: #339933;">****</span>
externaldb<span style="color: #339933;">.</span>params<span style="color: #339933;">.</span>dbname <span style="color: #339933;">=</span> external_db</pre></div></div>

<p>Then, I created a standard Zend_Db_Table class that handled the connection:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #666666; font-style: italic;">//Model/DbTable/External.php</span>
<span style="color: #666666; font-style: italic;">//name made up to protect the client but do yourself a favor and name your shit logically</span>
<span style="color: #000000; font-weight: bold;">class</span> Model_DbTable_External <span style="color: #000000; font-weight: bold;">extends</span> Model_DbTable_Abstract
<span style="color: #000;">&#123;</span>
   <span style="color: #009933; font-style: italic;">/**
     * Doesn't matter for use but ZF demands a $_name variable
     * @var string
     */</span>
	<span style="color: #000000; font-weight: bold;">protected</span> <span style="color: #000088;">$_name</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;content&quot;</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> init<span style="color: #000;">&#40;</span><span style="color: #000;">&#41;</span>
	<span style="color: #000;">&#123;</span>
		<span style="color: #000088;">$settings</span> <span style="color: #339933;">=</span> Zend_Registry<span style="color: #339933;">::</span><span style="color: #004000;">get</span><span style="color: #000;">&#40;</span><span style="color: #0000ff;">'settings'</span><span style="color: #000;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">db</span> <span style="color: #339933;">=</span> Zend_Db<span style="color: #339933;">::</span><span style="color: #004000;">factory</span><span style="color: #000;">&#40;</span><span style="color: #000088;">$settings</span><span style="color: #000;">&#91;</span><span style="color: #0000ff;">'externaldb'</span><span style="color: #000;">&#93;</span><span style="color: #000;">&#91;</span><span style="color: #0000ff;">'adapter'</span><span style="color: #000;">&#93;</span><span style="color: #339933;">,</span> <span style="color: #000088;">$settings</span><span style="color: #000;">&#91;</span><span style="color: #0000ff;">'externaldb'</span><span style="color: #000;">&#93;</span><span style="color: #000;">&#91;</span><span style="color: #0000ff;">'params'</span><span style="color: #000;">&#93;</span><span style="color: #000;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #000;">&#125;</span>
<span style="color: #000;">&#125;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>With the above out of the way I can now connect to the external database. I just have to pipe all my SQL through the above class. To that end, and to provide a nice wrapper for the caching, I create a Model that has methods for all the SQL my app will need:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #000000; font-weight: bold;">class</span> Model_External <span style="color: #000000; font-weight: bold;">extends</span> Model_Abstract
<span style="color: #000;">&#123;</span>
	<span style="color: #009933; font-style: italic;">/**
	 * The key to use for the cache items
	 * @var string
	 */</span>
	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000088;">$cache_key</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'external_db'</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> __construct<span style="color: #000;">&#40;</span><span style="color: #000;">&#41;</span>
	<span style="color: #000;">&#123;</span>
		parent<span style="color: #339933;">::</span>__construct<span style="color: #000;">&#40;</span><span style="color: #000;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #000088;">$db</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Model_DbTable_External<span style="color: #339933;">;</span>
		<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">db</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$db</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">db</span><span style="color: #339933;">;</span>
	<span style="color: #000;">&#125;</span>
&nbsp;
	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> testSystem<span style="color: #000;">&#40;</span><span style="color: #000088;">$id</span><span style="color: #000;">&#41;</span>
	<span style="color: #000;">&#123;</span>
		<span style="color: #000088;">$key</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$cache_key</span><span style="color: #339933;">.</span><span style="color: #009900; font-weight: bold;">__FUNCTION__</span><span style="color: #339933;">.</span><span style="color: #000088;">$id</span><span style="color: #339933;">;</span>
		<span style="color: #22f;">if</span><span style="color: #000;">&#40;</span><span style="color: #339933;">!</span><span style="color: #000088;">$data</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">cache</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">load</span><span style="color: #000;">&#40;</span><span style="color: #000088;">$key</span><span style="color: #000;">&#41;</span><span style="color: #000;">&#41;</span> 
		<span style="color: #000;">&#123;</span>	
			<span style="color: #666666; font-style: italic;">//just a random query with joins and whatnots to show  it's possible :)</span>
			<span style="color: #000088;">$sql</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">db</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">select</span><span style="color: #000;">&#40;</span><span style="color: #000;">&#41;</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">from</span><span style="color: #000;">&#40;</span><span style="color: #990000;">array</span><span style="color: #000;">&#40;</span><span style="color: #0000ff;">'e'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'example_table1'</span><span style="color: #000;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #990000;">array</span><span style="color: #000;">&#40;</span><span style="color: #0000ff;">'e.id'</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'e.title'</span><span style="color: #000;">&#41;</span><span style="color: #000;">&#41;</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">where</span><span style="color: #000;">&#40;</span><span style="color: #0000ff;">'e.id = ?'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$id</span><span style="color: #000;">&#41;</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">limit</span><span style="color: #000;">&#40;</span><span style="color: #0000ff;">'6'</span><span style="color: #000;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #000088;">$data</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">db</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">fetchAll</span><span style="color: #000;">&#40;</span><span style="color: #000088;">$sql</span><span style="color: #000;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">cache</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">save</span><span style="color: #000;">&#40;</span><span style="color: #000088;">$data</span><span style="color: #339933;">,</span> <span style="color: #000088;">$key</span><span style="color: #339933;">,</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">cache_key</span><span style="color: #000;">&#41;</span><span style="color: #000;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #000;">&#125;</span>
		<span style="color: #22f;">return</span> <span style="color: #000088;">$data</span><span style="color: #339933;">;</span>
	<span style="color: #000;">&#125;</span>
<span style="color: #000;">&#125;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>So, using the above class I&#8217;ve abstracted out the SQL and provided caching through the below class Model_Abstract:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #666666; font-style: italic;">//models/Abstract.php</span>
abstract <span style="color: #000000; font-weight: bold;">class</span> Model_Abstract
<span style="color: #000;">&#123;</span>
	<span style="color: #009933; font-style: italic;">/**
	 * The database object
	 * @var object
	 */</span>
	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000088;">$db</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #009933; font-style: italic;">/**
	 * The Cache Object
	 * @var object
	 */</span>
	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000088;">$cache</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #009933; font-style: italic;">/**
	 * The stored cache name
	 * @var string
	 */</span>
	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000088;">$cache_key</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">null</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> __construct<span style="color: #000;">&#40;</span><span style="color: #000;">&#41;</span>
	<span style="color: #000;">&#123;</span>
		<span style="color: #000088;">$c</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Model_Cache<span style="color: #339933;">;</span>
		<span style="color: #666666; font-style: italic;">// getting a Zend_Cache_Core object</span>
		<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">cache</span> <span style="color: #339933;">=</span> Zend_Cache<span style="color: #339933;">::</span><span style="color: #004000;">factory</span><span style="color: #000;">&#40;</span>
	                    <span style="color: #0000ff;">'Core'</span><span style="color: #339933;">,</span>
	                    <span style="color: #0000ff;">'File'</span><span style="color: #339933;">,</span>
	                    <span style="color: #990000;">array</span><span style="color: #000;">&#40;</span><span style="color: #0000ff;">'lifetime'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #cc66cc;">720</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'automatic_serialization'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #009900; font-weight: bold;">true</span><span style="color: #000;">&#41;</span><span style="color: #339933;">,</span>
	                    <span style="color: #990000;">array</span><span style="color: #000;">&#40;</span><span style="color: #0000ff;">'cache_dir'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'/path/to/cache/'</span><span style="color: #000;">&#41;</span>
		<span style="color: #000;">&#41;</span><span style="color: #339933;">;</span>		
	<span style="color: #000;">&#125;</span>
<span style="color: #000;">&#125;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>Putting it all together I now have a mechanism to connect to an external database, perform queries against said database and can cache the results pretty easily and with minimal code. One of the advantages of this approach is that connections to the database only happen when needed instead of on every page request as in Wenbert&#8217;s article. So, for example, so long as the cached items exist then no database connection is created and no latency (outside of hitting the filesystem of course) is created.</p>
<p>Still though, there are ways to improve on this approach. For example, using APC instead of the file system for the caching would help as would increasing the cache lifetime from 2 hours to something more but, overall, the above should be a good starting point. </p>
<div><a class="addthis_button" href="http://blog.ericlamb.net//addthis.com/bookmark.php?v=250" addthis:url='http://blog.ericlamb.net/2011/05/connect-to-multiple-databases-with-zend-framework/' addthis:title='Connect to Multiple Databases with Zend Framework '><img src="//cache.addthis.com/cachefly/static/btn/v2/lg-share-en.gif" width="125" height="16" alt="Bookmark and Share" style="border:0"/></a></div>]]></content:encoded>
			<wfw:commentRss>http://blog.ericlamb.net/2011/05/connect-to-multiple-databases-with-zend-framework/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>CartThrob 2.0 Beta Fun</title>
		<link>http://blog.ericlamb.net/2011/05/cartthrob-2-0-beta-fun/</link>
		<comments>http://blog.ericlamb.net/2011/05/cartthrob-2-0-beta-fun/#comments</comments>
		<pubDate>Thu, 26 May 2011 22:00:58 +0000</pubDate>
		<dc:creator>Eric Lamb</dc:creator>
				<category><![CDATA[Brain Dump]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[CartThrob]]></category>
		<category><![CDATA[e-commerce]]></category>
		<category><![CDATA[ExpressionEngine]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://blog.ericlamb.net/?p=3668</guid>
		<description><![CDATA[About a year ago I reviewed various shopping cart platforms for what was to be an &#8220;upcoming&#8221; project. After numerous delays and client hand holding (with the requisite back and forth) one year later that project finally began. And, as has lately become a trend in my professional life, the client&#8217;s spec combined with a [...]]]></description>
			<content:encoded><![CDATA[<p>About a year ago I reviewed various <a title="Should We Use Prestashop" href="http://blog.ericlamb.net/2010/05/should-we-use-prestashop/" onclick="return TrackClick('http%3A%2F%2Fblog.ericlamb.net%2F2010%2F05%2Fshould-we-use-prestashop%2F','Should+We+Use+Prestashop')">shopping</a> <a title="Should We Use OpenCart" href="http://blog.ericlamb.net/2010/05/should-we-use-opencart/" onclick="return TrackClick('http%3A%2F%2Fblog.ericlamb.net%2F2010%2F05%2Fshould-we-use-opencart%2F','Should+We+Use+OpenCart%3F')" onclick="return TrackClick('http%3A%2F%2Fblog.ericlamb.net%2F2010%2F05%2Fshould-we-use-opencart%2F','Should+We+Use+OpenCart')">cart</a> <a title="A Closer Look At Avactis" href="http://blog.ericlamb.net/2010/05/a-closer-look-at-avactis/" onclick="return TrackClick('http%3A%2F%2Fblog.ericlamb.net%2F2010%2F05%2Fa-closer-look-at-avactis%2F','A+Closer+Look+At+Avactis')" onclick="return TrackClick('http%3A%2F%2Fblog.ericlamb.net%2F2010%2F05%2Fa-closer-look-at-avactis%2F','A+Closer+Look+At+Avactis')">platforms</a> for what was to be an &#8220;upcoming&#8221; project. After numerous delays and client hand holding (with the requisite back and forth) one <em>year </em>later that project finally began. And, as has lately become a trend in my professional life, the client&#8217;s spec combined with a drastically shortened and accelerated deadline forced us to go with something completely unknown and with only cursory inspection; the 3rd party e-commerce <a title="ExpressionEngine" href="http://expressionengine.com/" onclick="return TrackClick('http%3A%2F%2Fexpressionengine.com%2F','ExpressionEngine')" target="_blank">ExpressionEngine</a> module <a title="CartThrob" href="http://cartthrob.com/" onclick="return TrackClick('http%3A%2F%2Fcartthrob.com%2F','CartThrob+Logo')" onclick="return TrackClick('http%3A%2F%2Fcartthrob.com%2F','CartThrob')" target="_blank">CartThrob 2.0</a> (beta at the time of this review BTW).</p>
<div id="attachment_3689" class="wp-caption aligncenter" style="width: 342px"><a title="CartThrob Logo" href="http://cartthrob.com/" onclick="return TrackClick('http%3A%2F%2Fcartthrob.com%2F','CartThrob+Logo')" onclick="return TrackClick('http%3A%2F%2Fcartthrob.com%2F','CartThrob')" target="_blank"><img class="size-full wp-image-3689" title="CartThrob" src="http://blog.ericlamb.net/wp-content/uploads/2011/05/cartthrob_logo.png" alt="CartThrob" width="332" height="117" /></a><p class="wp-caption-text">CartThrob</p></div>
<p>Of course this presented the problem of actually using an unknown  package to build a site with. Thankfully though, while CartThrob has a  steep learning curve and is most certainly missing key functionality,  it&#8217;s an overall nice package that allowed us to do what we needed. Sure, there&#8217;s pain involved (lots of configuration, testing and set up) but with a little elbow grease it all worked out.</p>
<p>As mentioned, CartThrob is an ExpressionEngine module for managing an online store. It&#8217;s available for both ExpressionEngine branches (1.x and 2.x) and was voted <a href="http://devot-ee.com/academee-awards/2010/" onclick="return TrackClick('http%3A%2F%2Fdevot-ee.com%2Facademee-awards%2F2010%2F','Module+of+the+Year+by+Devot%3Aee+in+2010')" target="_blank">Module of the Year by Devot:ee in 2010</a> (for the 1.x version only). This being an ExpressionEngine project CartThrob would, in theory, be a nice fit.</p>
<p>One of the really nice things about using CartThrob is that it uses the ExpressionEngine admin panel for managing your store. This meant there wouldn&#8217;t be a second administration site that the client would have to learn. (Personally, I always found dual admins to be janky at best and sloppy/lazy at worst so this was a definite bonus in favor CartThrob.)</p>
<p>But this is also double edged because, well, pretty much everything about your store is saved into the entries system within ExpressionEngine. This, of course, causes your Entries panel to become an eye sore whenever you have to do anything there. Instead of having a dedicated section to manage products you use the Entries section. Instead of a dedicated section to manage your orders you use the Entries section. Instead of a dedicated section to manage&#8230; well, you get the idea; everything is managed through the entries. This makes sense for the product management but when you have to dig through entries to find orders and coupons and discounts (etc) things get a little jarring for some people (even internally with my team) so it&#8217;s a concern.</p>
<p>Still, <em>single freaking admin</em> so it was kind of a lesser evil compromise.</p>
<p>The unusual thing though is that, for how thoroughly the management of items was punted (though, to be fair, it looks to be a feature versus bug debate more than &#8220;punting&#8221;), CartThrob has an extremely deep configuration section where you can customize CartThrob to function just right. CartThrob has all the basic customization functionality one would expect from any modern shopping cart platform (tax, shipping, payments with lots of merchant options, etc) plus a bunch of settings just to get CartThrob working (CartThrob is NOT an out of the box solution). It&#8217;s all presented in an easy to use interface that&#8217;s designed to mesh with the ExpressionEngine administration panel design which makes for an easier experience.</p>
<p>As far as merchant accounts go (for accepting payments through your site) there&#8217;s the usual suspects of <a title="Authorize.net" href="http://www.authorize.net/" onclick="return TrackClick('http%3A%2F%2Fwww.authorize.net%2F','Authorize.net')" target="_blank">Authorize.net</a>, <a title="Paypal" href="http://www.paypal.com/" onclick="return TrackClick('http%3A%2F%2Fwww.paypal.com%2F','Paypal')" target="_blank">Paypal</a> and <a title="SagePay" href="http://www.sagepay.com/" onclick="return TrackClick('http%3A%2F%2Fwww.sagepay.com%2F','SagePay')" target="_blank">SagePay</a> built right in but also some offline solutions for pay by check and credit accounts. For this project we used Authorize.net which went smooth for the most part but I can&#8217;t speak for the other options though if they&#8217;re anything like the Authorize.net one they&#8217;re probably cool. CartThrob also includes some extensions and hooks so writing your own payment gateway is possible if you need.</p>
<p>CartThrob can also hook into the ExpressionEngine member module so your customers can be registered internally. Like pretty much everything else with CartThrob there&#8217;s a bit of configuration involved to get it up and working though. Unfortunately though, there&#8217;s not really any out of the box functionality for your members to take advantage of. For example, there isn&#8217;t any way for your customers to view their past orders when logged in and when using the control panel and viewing a member&#8217;s profile there&#8217;s no relationship with CartThrob. You&#8217;re gonna have to write all that stuff yourself but, and I don&#8217;t know why, but CartThrob doesn&#8217;t store any relationships between ExpressionEngine members and it&#8217;s customers so expect a challenge.</p>
<p>In terms of the user side, the actual storefront, everything is done using ExpressionEngine templates and <a href="http://cartthrob.com/docs/tags/" onclick="return TrackClick('http%3A%2F%2Fcartthrob.com%2Fdocs%2Ftags%2F','CartThrob+Template+Tags')" title="CartThrob Template Tags" target="_blank">template tags</a>. There are lots of module methods available and even though it&#8217;s far from a complete package of the expected functionality. In fact, on our team, we got into a habit of thinking of CartThrob as a Lego set for <em>creating</em> what we want instead of <em>having</em> what we want already.</p>
<p>For example, to view the details about a users cart you&#8217;d use the template code below:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000;">&#123;</span><span style="color: #990000;">exp</span><span style="color: #339933;">:</span>cartthrob<span style="color: #339933;">:</span>cart_info<span style="color: #000;">&#125;</span>
	<span style="color: #000;">&#123;</span>total_items<span style="color: #000;">&#125;</span> <span style="color: #339933;">&lt;</span>br <span style="color: #339933;">/&gt;</span>
	<span style="color: #000;">&#123;</span>total_unique_items<span style="color: #000;">&#125;</span> <span style="color: #339933;">&lt;</span>br <span style="color: #339933;">/&gt;</span>
	<span style="color: #000;">&#123;</span>cart_entry_ids<span style="color: #000;">&#125;</span> <span style="color: #339933;">&lt;</span>br <span style="color: #339933;">/&gt;</span>
	<span style="color: #000;">&#123;</span>cart_shipping<span style="color: #000;">&#125;</span> <span style="color: #339933;">&lt;</span>br <span style="color: #339933;">/&gt;</span>
	<span style="color: #000;">&#123;</span>cart_tax_rate<span style="color: #000;">&#125;</span> <span style="color: #339933;">&lt;</span>br <span style="color: #339933;">/&gt;</span>
	<span style="color: #000;">&#123;</span>cart_tax<span style="color: #000;">&#125;</span> <span style="color: #339933;">&lt;</span>br <span style="color: #339933;">/&gt;</span>
	<span style="color: #000;">&#123;</span>cart_subtotal<span style="color: #000;">&#125;</span> <span style="color: #339933;">&lt;</span>br <span style="color: #339933;">/&gt;</span>
	<span style="color: #000;">&#123;</span>cart_total<span style="color: #000;">&#125;</span> <span style="color: #339933;">&lt;</span>br <span style="color: #339933;">/&gt;</span>
<span style="color: #000;">&#123;</span><span style="color: #339933;">/</span><span style="color: #990000;">exp</span><span style="color: #339933;">:</span>cartthrob<span style="color: #339933;">:</span>cart_info<span style="color: #000;">&#125;</span></pre></div></div>

<p>On the surface this appears to be nice because it keeps things within the core ExpressionEngine development process. And it would be except, well, CartThrob isn&#8217;t abstract enough. Too many times I would try to do something that seemed obvious, like <a href="http://cartthrob.com/forums/viewthread/2391/" onclick="return TrackClick('http%3A%2F%2Fcartthrob.com%2Fforums%2Fviewthread%2F2391%2F','How+to+build+a+logical+checkout+process')" title="How to build a logical checkout process" target="_blank">checking for shippable items within a users cart</a>, and would come up short; that functionality doesn&#8217;t exist. </p>
<p>Still though, don&#8217;t get me wrong. Yes, some of the above sucks. Absolutely. But CartThrob is still under development. Plus, since it&#8217;s a commercial script ($99) there&#8217;s financial incentive to improve it. And, truth be told, for as painful as certain points were it&#8217;s still way more fun to build a site with CartThrob than most of the other options I&#8217;ve used or reviewed like <a href="http://blog.ericlamb.net/2010/05/should-we-use-opencart/" onclick="return TrackClick('http%3A%2F%2Fblog.ericlamb.net%2F2010%2F05%2Fshould-we-use-opencart%2F','Should+We+Use+OpenCart%3F')" onclick="return TrackClick('http%3A%2F%2Fblog.ericlamb.net%2F2010%2F05%2Fshould-we-use-opencart%2F','Should+We+Use+OpenCart')" title="Should We Use OpenCart?">OpenCart</a> or <a href="http://blog.ericlamb.net/2010/05/a-closer-look-at-avactis/" onclick="return TrackClick('http%3A%2F%2Fblog.ericlamb.net%2F2010%2F05%2Fa-closer-look-at-avactis%2F','A+Closer+Look+At+Avactis')" onclick="return TrackClick('http%3A%2F%2Fblog.ericlamb.net%2F2010%2F05%2Fa-closer-look-at-avactis%2F','A+Closer+Look+At+Avactis')" title="A Closer Look At Avactis">Avactis</a>. </p>
<p>Plus, again, <em>single freaking admin</em>!</p>
<div><a class="addthis_button" href="http://blog.ericlamb.net//addthis.com/bookmark.php?v=250" addthis:url='http://blog.ericlamb.net/2011/05/cartthrob-2-0-beta-fun/' addthis:title='CartThrob 2.0 Beta Fun '><img src="//cache.addthis.com/cachefly/static/btn/v2/lg-share-en.gif" width="125" height="16" alt="Bookmark and Share" style="border:0"/></a></div>]]></content:encoded>
			<wfw:commentRss>http://blog.ericlamb.net/2011/05/cartthrob-2-0-beta-fun/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ExpressionEngine and the Mystery of M00o93H7pQ09L8X1t49cHY01Z5j4TT91fGfr</title>
		<link>http://blog.ericlamb.net/2011/05/expressionengine-and-the-mystery-of-m00o93h7pq09l8x1t49chy01z5j4tt91fgfr/</link>
		<comments>http://blog.ericlamb.net/2011/05/expressionengine-and-the-mystery-of-m00o93h7pq09l8x1t49chy01z5j4tt91fgfr/#comments</comments>
		<pubDate>Tue, 03 May 2011 11:53:21 +0000</pubDate>
		<dc:creator>Eric Lamb</dc:creator>
				<category><![CDATA[Brain Dump]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[ExpressionEngine]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://blog.ericlamb.net/?p=3651</guid>
		<description><![CDATA[Recently, I came across a weird issue while developing an ExpressionEngine 2.0 module. The issue was that, for some reason, my module would output a random string instead of the intended content. For example, instead of displaying the HTML, that the module created, the module would display the string M00o93H7pQ09L8X1t49cHY01Z5j4TT91fGfr instead. Tracking down this issue [...]]]></description>
			<content:encoded><![CDATA[<p>Recently, I came across a weird issue while developing an <a title="ExpressionEngine" href="http://expressionengine.com/" onclick="return TrackClick('http%3A%2F%2Fexpressionengine.com%2F','ExpressionEngine')" target="_blank">ExpressionEngine</a> 2.0 module. The issue was that, for some reason, my module would output a random string instead of the intended content. For example, instead of displaying the HTML, that the module created, the module would display the string M00o93H7pQ09L8X1t49cHY01Z5j4TT91fGfr instead. Tracking down this issue was symptomatic of the proverbial needle in a haystack but I learned a lot about how ExpressionEngine&#8217;s internals work and was once again reminded to be pragmatic about my development strategies.</p>
<div id="attachment_3655" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.flickr.com/photos/rafaelm/145958282/" onclick="return TrackClick('http%3A%2F%2Fwww.flickr.com%2Fphotos%2Frafaelm%2F145958282%2F','random%2C+the+lion')"><img class="size-medium wp-image-3655" title="random, the lion" src="http://blog.ericlamb.net/wp-content/uploads/2011/05/random_the_lion-300x225.jpg" alt="random, the lion" width="300" height="225" /></a><p class="wp-caption-text">random, the lion</p></div>
<p>My first and automatic reaction when coming up against some random and meaningless output string is to just Google it and see what happens. Unfortunately though, this particular search, at the time at least, returned around 35,000 sites that were outputting the string and only 1 that had a <a title="Out from plugin getting over-written by EE?" href="http://expressionengine.com/archived_forums/viewthread/123475/" onclick="return TrackClick('http%3A%2F%2Fexpressionengine.com%2Farchived_forums%2Fviewthread%2F123475%2F','Out+from+plugin+getting+over-written+by+EE%3F')" target="_blank">reference to the issue with a possible explanation</a>. All of a a sudden this became an actual thing.</p>
<p>Putting aside the fact that there are around 35,000 sites with the string within Google for the moment (and that&#8217;s really a problem to be discussed) it took a bunch of digging but I finally found a few things:</p>
<p>First, this is an issue that has been within ExpressionEngine for <em>years.</em> Doing a <a title="ExpressionEngine Forum Search" href="http://expressionengine.com/archived_forums/search/" onclick="return TrackClick('http%3A%2F%2Fexpressionengine.com%2Farchived_forums%2Fsearch%2F','ExpressionEngine+Forum+Search')" target="_blank">search</a> on the ExpressionEngine site lists the first entry of this string at <a title="EE1.6 upgrade - Hash values apppearing throughout site" href="http://expressionengine.com/archived_forums/viewthread/54903/" onclick="return TrackClick('http%3A%2F%2Fexpressionengine.com%2Farchived_forums%2Fviewthread%2F54903%2F','EE1.6+upgrade+-+Hash+values+apppearing+throughout+site')" target="_blank">2007</a> (sorry for the lack of a link; ExpressionEngine search is cached so old search URLs don&#8217;t work sometimes). My initial reaction was that this was kind of sloppy; EllisLab updated ExpressionEngine to 2.0 recently, from the ground up to use <a title="CodeIgniter" href="http://codeigniter.com/" onclick="return TrackClick('http%3A%2F%2Fcodeigniter.com%2F','CodeIgniter')" target="_blank">CodeIgniter</a> I believe, yet this issue got ported over?</p>
<p>After doing further research though, I came to believe the above is more to do with the design of ExpressionEngine than anything else like copying code. Turns out, there&#8217;s some logic to that random and oblique string.</p>
<p>Basically, that string (M00o93H7pQ09L8X1t49cHY01Z5j4TT91fGfr) is a template holder for the content you want to output. It&#8217;s a little complicated in how it works but thanks to <a title="Explanation of EE Template Parsing" href="http://expressionengine.com/archived_forums/viewthread/85160/#j1" onclick="return TrackClick('http%3A%2F%2Fexpressionengine.com%2Farchived_forums%2Fviewthread%2F85160%2F%23j1','Explanation+of+EE+Template+Parsing')" target="_blank">Derek Jones over at the ExpressionEngine forums</a> there&#8217;s a succinct, though still a little complicated but less complicated than I could explain it, explanation of what&#8217;s going on.</p>
<blockquote><p>
&#8230;</p>
<p>The template parser is an extremely complex gizmo, so let me try to explain it stripped of what techno mumbo jumbo I can. This will still be long, so bear with me.</p>
<p>The template parser intelligently saves on resources by only processing a given tag once, no matter how many times it occurs on the template.  This means that if your tag is exactly the same, including its tagdata, EE only execute’s that modules code once, and replaces all copies of that tag with the output.</p>
<p>So first, when the template parser goes through the template finding tags, it replaces them all with temporary markers (the gibberish), and adds each tag to a list of tags to be parsed, in the order they are encountered on the template, top down.  Each marker starts with the letter “M” followed by the number (starting at 0) that tag is sitting in on The List.</p>
<p>Second, the parser goes down its list of tags.  It parses tag #0, and then replaces all of #0’s marker with that tag’s output.</p>
<p>——————-</p>
<p>Ok that’s the setup.  Here’s why it affected you, and why this side effect is not a problem (and also extremely, extremely rare). Remember, top down, tags will be assigned 0, 1, 2, etc.  So I’m abstracting it out to show the nesting and the positioning only.  Just focus on the numbers.</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">{exp:tag0}foo{/exp:tag0}
{exp:tag1}
    bar
    {exp:tag0}foo{/exp:tag0}
{/exp:tag1}</pre></div></div>

<p>Step 1, the tags are grabbed and replaced with markers.  The first tag encountered is replaced first.</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">M0
{exp:tag1}
    bar
    M0
{/exp:tag1}</pre></div></div>

<p>And then the next tag:</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">M0
M1</pre></div></div>

<p>All tags have been collected and put on The List, so step 2, the parser goes down The List, and parses tag #0:</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">foo
M1</pre></div></div>

<p>See the problem?  The second M0 is sitting protected inside the marker for tag #1, so it doesn’t get replaced.  Then tag #1 gets parsed.</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">foo
    bar
    M0</pre></div></div>

<p>And the marker is left in the output.  EE doesn’t go back and parse tag #0 again, it’s already done that once, and will not do it again.</p>
<p>So the circumstances required for this problem to exist each are uncommon, and combined, very very rare:</p>
<p>1) Multiple exact copies of a given tag<br />
2) One or more copies nested inside another tag (tag nesting itself is also rare, and usually not recommended)<br />
3) The tag of which there are copies must sit at a position in the template above any occurrences of it being nested in another tag.
</p></blockquote>
<p>From the above it was easy to conclude that the template parsing stuff was breaking down in my module. Why, I had no idea, but at least I had a known starting point. While it was easy to conclude that my issue lay with the template parsing though, it was 100% off point for my particular issue which had to do with, of all things, the case of the module name. But, I digress. </p>
<p>The point is that for years now, and through multiple versions and rewrites, ExpressionEngine has consistently carried the same &#8220;issue&#8221;. Understanding the intent of the design for the template parsing helps put some perspective on things: it&#8217;s definitely a side effect of the particular approach. It bugged, and surprised, me that ExpressionEngine would have such oblique and useless output on such a widespread basis (35,000 site&#8217;s with that string within Google is a bit much in my opinion) but the more I thought about it the more I came to understand the complexity of what was involved in being helpful when the issue cropped up. Aside from abandoning the approach what could really be done?</p>
<p>It&#8217;s a nice reminder that development is often a balancing act between getting what you want and dealing with a counter issue. The ExpressionEngine team developed a strategy to enable them to effectively and efficiently meet their requirements but there&#8217;s sometimes an issue; a random string gets output instead of the template data. Plus, since the cause could be for anything from bad module installation data (as I had) to an issue within a template there&#8217;s no real way to provide helpful error messages when the template parsing does fail. But, on the other hand, the template parser does exactly what it was designed to do (and it does it well). </p>
<p>Which would you choose?</p>
<div><a class="addthis_button" href="http://blog.ericlamb.net//addthis.com/bookmark.php?v=250" addthis:url='http://blog.ericlamb.net/2011/05/expressionengine-and-the-mystery-of-m00o93h7pq09l8x1t49chy01z5j4tt91fgfr/' addthis:title='ExpressionEngine and the Mystery of M00o93H7pQ09L8X1t49cHY01Z5j4TT91fGfr '><img src="//cache.addthis.com/cachefly/static/btn/v2/lg-share-en.gif" width="125" height="16" alt="Bookmark and Share" style="border:0"/></a></div>]]></content:encoded>
			<wfw:commentRss>http://blog.ericlamb.net/2011/05/expressionengine-and-the-mystery-of-m00o93h7pq09l8x1t49chy01z5j4tt91fgfr/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>3 Lines of Code&#8230;</title>
		<link>http://blog.ericlamb.net/2011/04/3-lines-of-code/</link>
		<comments>http://blog.ericlamb.net/2011/04/3-lines-of-code/#comments</comments>
		<pubDate>Sat, 16 Apr 2011 04:32:00 +0000</pubDate>
		<dc:creator>Eric Lamb</dc:creator>
				<category><![CDATA[Brain Dump]]></category>
		<category><![CDATA[wp-click-track]]></category>

		<guid isPermaLink="false">http://blog.ericlamb.net/?p=3643</guid>
		<description><![CDATA[I just pushed an update to the WordPress plugin repository for the latest update to Wp-Click-Track. It&#8217;s been around 9 months since I&#8217;ve had a chance to even look at my own stats with Wp-Click-Track much less fix a couple bugs that have cropped up since but I finally found the time. And then, after [...]]]></description>
			<content:encoded><![CDATA[<p>I just pushed an update to the WordPress plugin repository for the latest update to Wp-Click-Track. It&#8217;s been around 9 months since I&#8217;ve had a chance to even look at my own stats with <a title="Wp-Click-Track" href="http://blog.ericlamb.net/projects/wp-click-track/">Wp-Click-Track</a> much less fix a couple bugs that have cropped up since but I finally found the time. And then, after spending 10 whole minutes looking into the cause of a couple bugs when I found that 3 lines of code fixed all of them.</p>
<p>3 little lines of code to fix the timezone bug, disappearing stats bug and random timestamp issues.</p>
<p>Also, please keep in mind that Wp-Click-Track is a labor of love; I work full time as a freelance programmer and try to keep a healthy social life so sometimes my priorities are a little wacked. Point being, I try to stay on top of these things but sometimes life gets in the way. But, I will get to it I can promise that.</p>
<div><a class="addthis_button" href="http://blog.ericlamb.net//addthis.com/bookmark.php?v=250" addthis:url='http://blog.ericlamb.net/2011/04/3-lines-of-code/' addthis:title='3 Lines of Code&#8230; '><img src="//cache.addthis.com/cachefly/static/btn/v2/lg-share-en.gif" width="125" height="16" alt="Bookmark and Share" style="border:0"/></a></div>]]></content:encoded>
			<wfw:commentRss>http://blog.ericlamb.net/2011/04/3-lines-of-code/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Custom Routes With Zend Framework</title>
		<link>http://blog.ericlamb.net/2011/04/custom-routes-with-zend-framework/</link>
		<comments>http://blog.ericlamb.net/2011/04/custom-routes-with-zend-framework/#comments</comments>
		<pubDate>Fri, 08 Apr 2011 18:43:58 +0000</pubDate>
		<dc:creator>Eric Lamb</dc:creator>
				<category><![CDATA[Brain Dump]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[routes]]></category>
		<category><![CDATA[zend framework]]></category>

		<guid isPermaLink="false">http://blog.ericlamb.net/?p=3609</guid>
		<description><![CDATA[When I first started using the Zend Framework I didn&#8217;t give much thought into the way it handles routing URLs to the controllers/action combo. Honestly, while I had become used to so called search engine friendly, or pretty, URLs, I grew up on query strings where the idea of URLs was more about function than [...]]]></description>
			<content:encoded><![CDATA[<p>When I first started using the <a href="http://framework.zend.com/" onclick="return TrackClick('http%3A%2F%2Fframework.zend.com%2F','Zend+Framework')" title="Zend Framework" target="_blank">Zend Framework</a> I didn&#8217;t give much thought into the way it handles routing URLs to the controllers/action combo. Honestly, while I had become used to so called search engine friendly, or pretty, URLs, I grew up on query strings where the idea of URLs was more about function than form. So when I moved over to Zend Framework as a framework I didn&#8217;t give a damn how the URLs were parsed and routed to which controller/action. I just accepted the natural order of things. </p>
<div id="attachment_3627" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.flickr.com/photos/orudge/5602610471/lightbox/#/photos/orudge/5602610471/" onclick="return TrackClick('http%3A%2F%2Fwww.flickr.com%2Fphotos%2Forudge%2F5602610471%2Flightbox%2F%23%2Fphotos%2Forudge%2F5602610471%2F','Road+Signs')" target="_blank"><img class="size-medium wp-image-3627" title="Road Signs" src="http://blog.ericlamb.net/wp-content/uploads/2011/04/routes-300x199.jpg" alt="Road Signs by orudge" width="300" height="199" /></a><p class="wp-caption-text">Road Signs by orudge</p></div>
<p>Recently though, I took on a project to build a sort of quick and dirty CMS with a couple unique requirements:</p>
<ol>
<li>It had to use the Zend Framework</li>
<li>URLs would be broken down into a /category/page/subpage scheme.</li>
</ol>
<p>This was the first time I&#8217;ve ever had a client demand a project use the Zend Framework. Pretty cool. But, this presented a problem because, as mentioned above, by default the Zend Framework has URL schemes based on the controller and action you&#8217;re looking to access like /controller/action/. This just wouldn&#8217;t work.</p>
<p>Since the clients URLs would mostly be based on user generated strings, what we were calling URL slugs, the default routing scheme just wouldn&#8217;t do. I say mostly because there were other areas of the project that would use the normal scheme for a couple modules. Either way, thankfully, using <a href="http://framework.zend.com/manual/1.11/en/zend.controller.router.html#zend.controller.router.routes.standard" onclick="return TrackClick('http%3A%2F%2Fframework.zend.com%2Fmanual%2F1.11%2Fen%2Fzend.controller.router.html%23zend.controller.router.routes.standard','Zend_Controller_Router_Route')" title="Zend_Controller_Router_Route" target="_blank">Zend_Controller_Router_Route</a> made this pretty much a snap (a little head bashing but not too much).</p>
<p>First though, a little background. Because of the above requirement from the client on URL schemes, I was thinking the best thing to do was to route all user generated URLs to to a single controller and handle the rest from there. I looked but there didn&#8217;t seem to be any easy way to route all URLs <em>except</em> the specific modules I wanted to exclude which meant I had to pull out the categories and iterate over them to create each route individually. (If anyone knows of a way to reverse the design and create a route for everything but certain strings let me know.)</p>
<p>To do this, in my bootstrap class I just created a new _init function like the below:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
</pre></td><td class="code"><pre class="php" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">//bootstrap</span>
<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> _initRoutes<span style="color: #000;">&#40;</span><span style="color: #000;">&#41;</span>
<span style="color: #000;">&#123;</span>
	<span style="color: #000088;">$url_data</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">cat</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">getCategoryUrls</span><span style="color: #000;">&#40;</span><span style="color: #000;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #000088;">$frontController</span> <span style="color: #339933;">=</span> Zend_Controller_Front<span style="color: #339933;">::</span><span style="color: #004000;">getInstance</span><span style="color: #000;">&#40;</span><span style="color: #000;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #000088;">$router</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$frontController</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">getRouter</span><span style="color: #000;">&#40;</span><span style="color: #000;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #22f;">foreach</span><span style="color: #000;">&#40;</span><span style="color: #000088;">$url_data</span> <span style="color: #22f;">AS</span> <span style="color: #000088;">$url</span><span style="color: #000;">&#41;</span>
	<span style="color: #000;">&#123;</span>
&nbsp;
		<span style="color: #000088;">$route</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Zend_Controller_Router_Route <span style="color: #000;">&#40;</span>
			<span style="color: #000088;">$url</span><span style="color: #000;">&#91;</span><span style="color: #0000ff;">'url_slug'</span><span style="color: #000;">&#93;</span><span style="color: #339933;">.</span><span style="color: #0000ff;">'/*'</span><span style="color: #339933;">,</span>
			<span style="color: #990000;">array</span><span style="color: #000;">&#40;</span><span style="color: #0000ff;">'controller'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'pages'</span><span style="color: #339933;">,</span> 
			<span style="color: #0000ff;">'action'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #0000ff;">'index'</span><span style="color: #000;">&#41;</span>
		<span style="color: #000;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
		<span style="color: #000088;">$router</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">addRoute</span><span style="color: #000;">&#40;</span><span style="color: #000088;">$url</span><span style="color: #000;">&#91;</span><span style="color: #0000ff;">'url_slug'</span><span style="color: #000;">&#93;</span><span style="color: #339933;">,</span> <span style="color: #000088;">$route</span><span style="color: #000;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #000;">&#125;</span> 
<span style="color: #000;">&#125;</span></pre></td></tr></table></div>

<p>In a nutshell, all the above is doing is looping over an array of category URL slugs and adding a custom route for each category that sends all requests to the Pages controller and the Index action. From there I parse the URL and proceed accordingly for the rest of the module. </p>
<p>For this example I&#8217;m doing a couple thing; using the Zend_Controller_Router_Route class and using wild cards (*). The Zend_Controller_Router_Route class takes two parameters, the first being the route and the second being an array of options (I&#8217;m only passing the controller and action but it&#8217;s possible to send others). For the route, and because it&#8217;s using the wildcard (*), I&#8217;m basically saying take any requests for the &#8220;url_slug&#8221; and anything after it and use the controller &#8220;Pages&#8221; and the &#8220;Index&#8221; action to handle everything. </p>
<p>After passing the Zend_Controller_Router_Route object to the $router, passing the unique name for the route as the first parameter, I was good to go. </p>
<p>That said, there&#8217;s a lot more that can be done with the custom routing stuff within the Zend Framework above what&#8217;s outlined above. There&#8217;s a pretty good article by <a href="http://www.wdvl.com/Authoring/PHP/Zend_Routes/Jason_Gilmore04282010.html" onclick="return TrackClick('http%3A%2F%2Fwww.wdvl.com%2FAuthoring%2FPHP%2FZend_Routes%2FJason_Gilmore04282010.html','Creating+Custom+Routes+with+the+Zend+Framework')" title="Creating Custom Routes with the Zend Framework" target="_blank">Jason Gilmore</a> that goes into some detail into how to create default URL parameters to make the URLs look even prettier. Definitely worth checking out. </p>
<div><a class="addthis_button" href="http://blog.ericlamb.net//addthis.com/bookmark.php?v=250" addthis:url='http://blog.ericlamb.net/2011/04/custom-routes-with-zend-framework/' addthis:title='Custom Routes With Zend Framework '><img src="//cache.addthis.com/cachefly/static/btn/v2/lg-share-en.gif" width="125" height="16" alt="Bookmark and Share" style="border:0"/></a></div>]]></content:encoded>
			<wfw:commentRss>http://blog.ericlamb.net/2011/04/custom-routes-with-zend-framework/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The Commodity HTPC Dream is Finally Realized</title>
		<link>http://blog.ericlamb.net/2011/03/the-commodity-htpc-dream-is-finally-realized/</link>
		<comments>http://blog.ericlamb.net/2011/03/the-commodity-htpc-dream-is-finally-realized/#comments</comments>
		<pubDate>Wed, 09 Mar 2011 01:18:30 +0000</pubDate>
		<dc:creator>Eric Lamb</dc:creator>
				<category><![CDATA[Brain Dump]]></category>
		<category><![CDATA[htpc]]></category>
		<category><![CDATA[windows media center]]></category>

		<guid isPermaLink="false">http://blog.ericlamb.net/?p=3494</guid>
		<description><![CDATA[Ever since I first heard about home theater PCs (HTPC), way back in 2002, I&#8217;ve wanted one. Badly. To me, an HTPC is one of those killer products that offered what I would consider to be a perfect experience; the ability to have all my media centralized around a single interface and made for a [...]]]></description>
			<content:encoded><![CDATA[<p>Ever since I first heard about home theater PCs (<a title="HTPC" href="http://en.wikipedia.org/wiki/Htpc" onclick="return TrackClick('http%3A%2F%2Fen.wikipedia.org%2Fwiki%2FHtpc','HTPC')" target="_blank">HTPC</a>), way back in 2002, I&#8217;ve wanted one. Badly. To me, an HTPC is one of those killer products that offered what I would consider to be a perfect experience; the ability to have all my media centralized around a single interface and made for a more traditional entertainment experience. The problem though was that, until recently, setting up a system was a little outside my comfort zone when it came to the costs. It was fucking expensive to set up back then. But, that was then and the times they do a change. It&#8217;s not only ridiculously easy to  build an HTPC but also quite reasonable in terms of the costs.</p>
<div id="attachment_3496" class="wp-caption aligncenter" style="width: 310px"><a href="http://blog.ericlamb.net/wp-content/uploads/2011/02/Mediacenter5536.png" onclick="return TrackClick('http%3A%2F%2Fblog.ericlamb.net%2Fwp-content%2Fuploads%2F2011%2F02%2FMediacenter5536.png','The+HTPC+Dream+is+Finally+Realized')"><img class="size-medium wp-image-3496" title="The HTPC Dream is Finally Realized" src="http://blog.ericlamb.net/wp-content/uploads/2011/02/Mediacenter5536-300x168.png" alt="The HTPC Dream is Finally Realized" width="300" height="168" /></a><p class="wp-caption-text">The HTPC Dream is Finally Realized</p></div>
<p>Back in 2002 I was a lowly intern level scrub with the pay to match and computers capable of running Windows Media Center were a little expensive and required a shit ton of upgrades hardware wise. You couldn&#8217;t just buy an inexpensive model off the show room floor and expect to have any cash left over. I don&#8217;t recall the specific prices involved for all the components but I do remember knowing with a complete and total certainty that getting an HTPC at that time just wasn&#8217;t going to happen. So, broken hearted, I moved on and focused on my career and drinking for the next 9 years.</p>
<p>Then, last August, I started looking into getting one put together again. I had some other, business, needs that required a dedicated desktop computer, to be placed in a closet, and I just figured, what the hell, why not kill two birds with one stone; have my utility computer and look into getting a Windows Media all at the same time. I mean, why not? The main purpose was for a backup machine which would only be ran on a slim time window and the rest of the time it&#8217;ll just be sitting there. Kind of a waste to just leave it sitting there doing nothing most of the day, right? Right?</p>
<p>Now, since I had done all the research into what it would take to build an HTPC back in the day I started looking up all the parts to build one. For my preferred system I was thinking about:</p>
<ol>
<li><a title="ATX Media Center Case" href="http://www.newegg.com/Product/Product.aspx?Item=N82E16811144231" onclick="return TrackClick('http%3A%2F%2Fwww.newegg.com%2FProduct%2FProduct.aspx%3FItem%3DN82E16811144231','ATX+Media+Center+Case')" target="_blank">ATX Media Center case</a> $75</li>
<li>An <a title="ATX Media Center Case" href="http://www.newegg.com/Product/Product.aspx?Item=N82E16813131352" onclick="return TrackClick('http%3A%2F%2Fwww.newegg.com%2FProduct%2FProduct.aspx%3FItem%3DN82E16813131352','ATX+Media+Center+Case')" target="_blank">ATX Mother Board</a> that can have a decent amount of RAM $299</li>
<li>A decent amount of <a title="12 GB RAM" href="http://www.crucial.com/store/listparts.aspx?model=Rampage%20II%20Extreme" onclick="return TrackClick('http%3A%2F%2Fwww.crucial.com%2Fstore%2Flistparts.aspx%3Fmodel%3DRampage%2520II%2520Extreme','12+GB+RAM')" target="_blank">RAM </a>$149</li>
<li>A <a title="Intel Core i7-940" href="http://www.newegg.com/Product/Product.aspx?Item=N82E16819115201&amp;cm_re=LGA1366_Core_i7-_-19-115-201-_-Product" onclick="return TrackClick('http%3A%2F%2Fwww.newegg.com%2FProduct%2FProduct.aspx%3FItem%3DN82E16819115201%26amp%3Bcm_re%3DLGA1366_Core_i7-_-19-115-201-_-Product','Intel+Core+i7-940')" target="_blank">processor</a> to match the motherboard $269</li>
<li><a title="HDMI Video Card" href="http://www.newegg.com/Product/Product.aspx?Item=N82E16814130535&amp;cm_re=hdmi_videocard-_-14-130-535-_-Product" onclick="return TrackClick('http%3A%2F%2Fwww.newegg.com%2FProduct%2FProduct.aspx%3FItem%3DN82E16814130535%26amp%3Bcm_re%3Dhdmi_videocard-_-14-130-535-_-Product','HDMI+Video+Card')" target="_blank">HDMI Video Card</a> $99</li>
<li>1 <a title="Blu Ray Disk Drive" href="http://www.newegg.com/Product/Product.aspx?Item=N82E16827136181" onclick="return TrackClick('http%3A%2F%2Fwww.newegg.com%2FProduct%2FProduct.aspx%3FItem%3DN82E16827136181','Blu+Ray+Disk+Drive')" target="_blank">Blu Ray Disk Drive</a> $90</li>
<li><a title="500 Gig HDD" href="http://www.newegg.com/Product/Product.aspx?Item=N82E16822145299" onclick="return TrackClick('http%3A%2F%2Fwww.newegg.com%2FProduct%2FProduct.aspx%3FItem%3DN82E16822145299','500+Gig+HDD')" target="_blank">500 Gig HDD</a> (with a few extra external terabyte drives for the media) $60</li>
</ol>
<p>I figured I&#8217;d get the parts and put it together myself; these things are like legos with how everything snaps together only where it belongs. Add a little <a title="MythTV" href="http://www.mythtv.org/" onclick="return TrackClick('http%3A%2F%2Fwww.mythtv.org%2F','MythTV')" target="_blank">MythTV</a> (go <a title="FOSS" href="http://en.wikipedia.org/wiki/Free_and_open_source_software" onclick="return TrackClick('http%3A%2F%2Fen.wikipedia.org%2Fwiki%2FFree_and_open_source_software','FOSS')">FOSS</a>) and I&#8217;d be good to go for a bit of time and around $1,100 (after shipping). But, the practical side of me couldn&#8217;t accept that like I assumed I would. $1,100 is still a lot of money regardless of how you look at it and, I couldn&#8217;t kid myself, it was going to take a bunch of effort to learn MythTV as a platform, not to mention how long it would take to put the thing together (I try to never underestimate my ability to procrastinate). So I decided to look up good old Dell and see what my options were. My mind blew.</p>
<p>Turns out that the lowest <a title="Inspiron 560 Desktop" href="http://www.dell.com/us/business/p/inspiron-560/pd?oc=bi1s560&amp;model_id=inspiron-560" onclick="return TrackClick('http%3A%2F%2Fwww.dell.com%2Fus%2Fbusiness%2Fp%2Finspiron-560%2Fpd%3Foc%3Dbi1s560%26amp%3Bmodel_id%3Dinspiron-560','Inspiron+560+Desktop')" target="_blank">Dell desktop</a> model available at the time had HDMI outputs native and is only $299. I don&#8217;t know when it started to be &#8220;standard&#8221; for commodity PCs to come with HDMI outputs but that was a good call. Plus, since the Dells come with Windows 7 Premium, and that it comes with Windows Media Center as a part of the OS, there&#8217;s no need to learn MythTV (something I just wasn&#8217;t motivated to do).</p>
<p>Still, the specs for the Dell wasn&#8217;t really all that great; only 2 Gigs of RAM and a Celeron processor (why do they still make these?) just wasn&#8217;t going to cut it. After upping the specs on the CPU and ordering 6 Gigs of RAM from Crucial the total cost ran around $450 and came with a year warranty which was something unexpected but nice.</p>
<p>It&#8217;s pretty cool that getting a home HTPC set up is now as easy as ordering any other computer.</p>
<div><a class="addthis_button" href="http://blog.ericlamb.net//addthis.com/bookmark.php?v=250" addthis:url='http://blog.ericlamb.net/2011/03/the-commodity-htpc-dream-is-finally-realized/' addthis:title='The Commodity HTPC Dream is Finally Realized '><img src="//cache.addthis.com/cachefly/static/btn/v2/lg-share-en.gif" width="125" height="16" alt="Bookmark and Share" style="border:0"/></a></div>]]></content:encoded>
			<wfw:commentRss>http://blog.ericlamb.net/2011/03/the-commodity-htpc-dream-is-finally-realized/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Practical PHP Reflection</title>
		<link>http://blog.ericlamb.net/2011/03/practical-php-reflection/</link>
		<comments>http://blog.ericlamb.net/2011/03/practical-php-reflection/#comments</comments>
		<pubDate>Tue, 01 Mar 2011 08:00:12 +0000</pubDate>
		<dc:creator>Eric Lamb</dc:creator>
				<category><![CDATA[Brain Dump]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[reflection api]]></category>

		<guid isPermaLink="false">http://blog.ericlamb.net/?p=3401</guid>
		<description><![CDATA[The Reflection API in PHP is one of those language features that can kind of creep up on you; it&#8217;s just not needed for a vast majority of projects. In fact, I&#8217;d wager there are a bunch of professional developers who have never had to make use of the Reflection API. Hell, I spent 8 [...]]]></description>
			<content:encoded><![CDATA[<p>The <a title="Reflection API in PHP" href="http://us.php.net/manual/en/book.reflection.php" onclick="return TrackClick('http%3A%2F%2Fus.php.net%2Fmanual%2Fen%2Fbook.reflection.php','Reflection+API+in+PHP')" target="_blank">Reflection API in PHP</a> is one of those language features that can kind of creep up on you; it&#8217;s just not needed for a vast majority of projects. In fact, I&#8217;d wager there are a bunch of professional developers who have never had to make use of the Reflection API. Hell, I spent 8 years writing code professionally before I ever had to use it (though, because of some unrelated .NET research I was peripherally aware of it). It&#8217;s just not something that really comes up all that often in the day to day coding tasks. </p>
<p>Then, one day, out of nowhere, it comes up. The perfect problem where the Reflection API appears to be the perfect solution. For me this came up a couple months ago while I was working on a European zip code radius project that had to be built using one of those obfuscated and ill documented 3rd party commercial programs (nothing worse than when the platform is forced on you). So, I had to use this program that was intentionally encoded to prevent me from doing what I wanted to do. I couldn&#8217;t even look at the code; it was completely obfuscated.</p>
<p>Before getting into things it should be noted that the Reflection API is capable above and beyond what I&#8217;m outlining; usually for the kind of high level stuff like core <a title="PHP 5's Reflection API" href="http://weierophinney.net/matthew/archives/125-PHP-5s-Reflection-API.html" onclick="return TrackClick('http%3A%2F%2Fweierophinney.net%2Fmatthew%2Farchives%2F125-PHP-5s-Reflection-API.html','PHP+5')" target="_blank">Zend Framework components</a> and anything you&#8217;d want <em>way</em> abstracted. </p>
<p>So, yeah, weird problem.</p>
<p>This is where reflection comes in for me. It was made for problems like the above; according to the manual:</p>
<blockquote><p>
PHP 5 comes with a complete reflection API that adds the ability to reverse-engineer classes, interfaces, functions, methods and extensions. Additionally, the reflection API offers ways to retrieve doc comments for functions, classes and methods.
</p></blockquote>
<p>So, the theory was that I could use the Reflection API to look into the obfuscated code and get some insight into what was going on and what I had to work with. I figured that, at the least, if I could see what methods, properties and comments the code had; maybe I&#8217;d get some clue about what the code was about. Using, for example, the internal PHP class &#8220;SimpleXMLElement&#8221; it&#8217;s real simple to get a nice map of the class by calling the class <a title="ReflectionClass" href="http://us.php.net/manual/en/class.reflectionclass.php" onclick="return TrackClick('http%3A%2F%2Fus.php.net%2Fmanual%2Fen%2Fclass.reflectionclass.php','ReflectionClass')" target="_blank">ReflectionClass</a> and passing the name of the class you want to get details on as a parameter like the below:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
</pre></td><td class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #000088;">$reflector</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> ReflectionClass<span style="color: #000;">&#40;</span><span style="color: #0000ff;">&quot;SimpleXMLElement&quot;</span><span style="color: #000;">&#41;</span><span style="color: #339933;">;</span> 
<span style="color: #22f;">echo</span> <span style="color: #000088;">$reflector</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></td></tr></table></div>

<p>The above outputs an eye gouging but verbose class map:</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">Class</span> <span style="color: #000;">&#91;</span> <span style="color: #339933;">&lt;</span>internal<span style="color: #339933;">:</span>SimpleXML<span style="color: #339933;">&gt;</span> <span style="color: #339933;">&lt;</span>iterateable<span style="color: #339933;">&gt;</span> <span style="color: #000000; font-weight: bold;">class</span> SimpleXMLElement implements Traversable <span style="color: #000;">&#93;</span> <span style="color: #000;">&#123;</span>
&nbsp;
  <span style="color: #339933;">-</span> Constants <span style="color: #000;">&#91;</span><span style="color: #cc66cc;">0</span><span style="color: #000;">&#93;</span> <span style="color: #000;">&#123;</span>
  <span style="color: #000;">&#125;</span>
&nbsp;
  <span style="color: #339933;">-</span> Static properties <span style="color: #000;">&#91;</span><span style="color: #cc66cc;">0</span><span style="color: #000;">&#93;</span> <span style="color: #000;">&#123;</span>
  <span style="color: #000;">&#125;</span>
&nbsp;
  <span style="color: #339933;">-</span> Static methods <span style="color: #000;">&#91;</span><span style="color: #cc66cc;">0</span><span style="color: #000;">&#93;</span> <span style="color: #000;">&#123;</span>
  <span style="color: #000;">&#125;</span>
&nbsp;
  <span style="color: #339933;">-</span> Properties <span style="color: #000;">&#91;</span><span style="color: #cc66cc;">0</span><span style="color: #000;">&#93;</span> <span style="color: #000;">&#123;</span>
  <span style="color: #000;">&#125;</span>
&nbsp;
  <span style="color: #339933;">-</span> Methods <span style="color: #000;">&#91;</span><span style="color: #cc66cc;">14</span><span style="color: #000;">&#93;</span> <span style="color: #000;">&#123;</span>
    Method <span style="color: #000;">&#91;</span> <span style="color: #339933;">&lt;</span>internal<span style="color: #339933;">:</span>SimpleXML<span style="color: #339933;">,</span> ctor<span style="color: #339933;">&gt;</span> final <span style="color: #000000; font-weight: bold;">public</span> method __construct <span style="color: #000;">&#93;</span> <span style="color: #000;">&#123;</span>
    <span style="color: #000;">&#125;</span>
&nbsp;
    Method <span style="color: #000;">&#91;</span> <span style="color: #339933;">&lt;</span>internal<span style="color: #339933;">:</span>SimpleXML<span style="color: #339933;">&gt;</span> <span style="color: #000000; font-weight: bold;">public</span> method asXML <span style="color: #000;">&#93;</span> <span style="color: #000;">&#123;</span>
    <span style="color: #000;">&#125;</span>
&nbsp;
    Method <span style="color: #000;">&#91;</span> <span style="color: #339933;">&lt;</span>internal<span style="color: #339933;">:</span>SimpleXML<span style="color: #339933;">&gt;</span> <span style="color: #000000; font-weight: bold;">public</span> method saveXML <span style="color: #000;">&#93;</span> <span style="color: #000;">&#123;</span>
    <span style="color: #000;">&#125;</span>
&nbsp;
    Method <span style="color: #000;">&#91;</span> <span style="color: #339933;">&lt;</span>internal<span style="color: #339933;">:</span>SimpleXML<span style="color: #339933;">&gt;</span> <span style="color: #000000; font-weight: bold;">public</span> method xpath <span style="color: #000;">&#93;</span> <span style="color: #000;">&#123;</span>
    <span style="color: #000;">&#125;</span>
&nbsp;
    Method <span style="color: #000;">&#91;</span> <span style="color: #339933;">&lt;</span>internal<span style="color: #339933;">:</span>SimpleXML<span style="color: #339933;">&gt;</span> <span style="color: #000000; font-weight: bold;">public</span> method registerXPathNamespace <span style="color: #000;">&#93;</span> <span style="color: #000;">&#123;</span>
    <span style="color: #000;">&#125;</span>
&nbsp;
    Method <span style="color: #000;">&#91;</span> <span style="color: #339933;">&lt;</span>internal<span style="color: #339933;">:</span>SimpleXML<span style="color: #339933;">&gt;</span> <span style="color: #000000; font-weight: bold;">public</span> method attributes <span style="color: #000;">&#93;</span> <span style="color: #000;">&#123;</span>
    <span style="color: #000;">&#125;</span>
&nbsp;
    Method <span style="color: #000;">&#91;</span> <span style="color: #339933;">&lt;</span>internal<span style="color: #339933;">:</span>SimpleXML<span style="color: #339933;">&gt;</span> <span style="color: #000000; font-weight: bold;">public</span> method children <span style="color: #000;">&#93;</span> <span style="color: #000;">&#123;</span>
    <span style="color: #000;">&#125;</span>
&nbsp;
    Method <span style="color: #000;">&#91;</span> <span style="color: #339933;">&lt;</span>internal<span style="color: #339933;">:</span>SimpleXML<span style="color: #339933;">&gt;</span> <span style="color: #000000; font-weight: bold;">public</span> method getNamespaces <span style="color: #000;">&#93;</span> <span style="color: #000;">&#123;</span>
    <span style="color: #000;">&#125;</span>
&nbsp;
    Method <span style="color: #000;">&#91;</span> <span style="color: #339933;">&lt;</span>internal<span style="color: #339933;">:</span>SimpleXML<span style="color: #339933;">&gt;</span> <span style="color: #000000; font-weight: bold;">public</span> method getDocNamespaces <span style="color: #000;">&#93;</span> <span style="color: #000;">&#123;</span>
    <span style="color: #000;">&#125;</span>
&nbsp;
    Method <span style="color: #000;">&#91;</span> <span style="color: #339933;">&lt;</span>internal<span style="color: #339933;">:</span>SimpleXML<span style="color: #339933;">&gt;</span> <span style="color: #000000; font-weight: bold;">public</span> method getName <span style="color: #000;">&#93;</span> <span style="color: #000;">&#123;</span>
    <span style="color: #000;">&#125;</span>
&nbsp;
    Method <span style="color: #000;">&#91;</span> <span style="color: #339933;">&lt;</span>internal<span style="color: #339933;">:</span>SimpleXML<span style="color: #339933;">&gt;</span> <span style="color: #000000; font-weight: bold;">public</span> method addChild <span style="color: #000;">&#93;</span> <span style="color: #000;">&#123;</span>
    <span style="color: #000;">&#125;</span>
&nbsp;
    Method <span style="color: #000;">&#91;</span> <span style="color: #339933;">&lt;</span>internal<span style="color: #339933;">:</span>SimpleXML<span style="color: #339933;">&gt;</span> <span style="color: #000000; font-weight: bold;">public</span> method addAttribute <span style="color: #000;">&#93;</span> <span style="color: #000;">&#123;</span>
    <span style="color: #000;">&#125;</span>
&nbsp;
    Method <span style="color: #000;">&#91;</span> <span style="color: #339933;">&lt;</span>internal<span style="color: #339933;">:</span>SimpleXML<span style="color: #339933;">&gt;</span> <span style="color: #000000; font-weight: bold;">public</span> method __toString <span style="color: #000;">&#93;</span> <span style="color: #000;">&#123;</span>
    <span style="color: #000;">&#125;</span>
&nbsp;
    Method <span style="color: #000;">&#91;</span> <span style="color: #339933;">&lt;</span>internal<span style="color: #339933;">:</span>SimpleXML<span style="color: #339933;">&gt;</span> <span style="color: #000000; font-weight: bold;">public</span> method <span style="color: #990000;">count</span> <span style="color: #000;">&#93;</span> <span style="color: #000;">&#123;</span>
    <span style="color: #000;">&#125;</span>
  <span style="color: #000;">&#125;</span>
<span style="color: #000;">&#125;</span></pre></div></div>

<p>As you can see, the above lists every property, method and constant about a class along with any parent classes and interfaces. If there were any variables the methods accepted it would also list those as well but SimpleXMLElement doesn&#8217;t have any. </p>
<p>It&#8217;s also possible to get the individual elements on a one by one basis while using the ReflectionClass object; for example the below methods returns the class name, any comments (if they&#8217;re in docblock format), the constants, parent class, constructor and default properties for the class DateTime.</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #000088;">$reflector</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> ReflectionClass<span style="color: #000;">&#40;</span><span style="color: #0000ff;">&quot;DateTime&quot;</span><span style="color: #000;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #22f;">echo</span> <span style="color: #0000ff;">&quot;Class name: &quot;</span><span style="color: #339933;">.</span><span style="color: #000088;">$reflector</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">getName</span><span style="color: #000;">&#40;</span><span style="color: #000;">&#41;</span><span style="color: #339933;">.</span><span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
<span style="color: #22f;">echo</span> <span style="color: #0000ff;">&quot;Doc Comment:&quot;</span><span style="color: #339933;">.</span><span style="color: #990000;">var_dump</span><span style="color: #000;">&#40;</span><span style="color: #000088;">$reflector</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">getDocComment</span><span style="color: #000;">&#40;</span><span style="color: #000;">&#41;</span><span style="color: #000;">&#41;</span><span style="color: #339933;">.</span><span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
<span style="color: #22f;">echo</span> <span style="color: #0000ff;">&quot;Contants: &quot;</span><span style="color: #339933;">.</span><span style="color: #990000;">var_dump</span><span style="color: #000;">&#40;</span><span style="color: #000088;">$reflector</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">getConstants</span><span style="color: #000;">&#40;</span><span style="color: #000;">&#41;</span><span style="color: #000;">&#41;</span><span style="color: #339933;">.</span><span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
<span style="color: #22f;">echo</span> <span style="color: #0000ff;">&quot;Parent Class: &quot;</span><span style="color: #339933;">.</span><span style="color: #990000;">var_dump</span><span style="color: #000;">&#40;</span><span style="color: #000088;">$reflector</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">getParentClass</span><span style="color: #000;">&#40;</span><span style="color: #000;">&#41;</span><span style="color: #000;">&#41;</span><span style="color: #339933;">.</span><span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
<span style="color: #22f;">echo</span> <span style="color: #0000ff;">&quot;Constructor: &quot;</span><span style="color: #339933;">.</span><span style="color: #990000;">var_dump</span><span style="color: #000;">&#40;</span><span style="color: #000088;">$reflector</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">getConstructor</span><span style="color: #000;">&#40;</span><span style="color: #000;">&#41;</span><span style="color: #000;">&#41;</span><span style="color: #339933;">.</span><span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
<span style="color: #22f;">echo</span> <span style="color: #0000ff;">&quot;Default Properties: &quot;</span><span style="color: #339933;">.</span><span style="color: #990000;">var_dump</span><span style="color: #000;">&#40;</span><span style="color: #000088;">$reflector</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">getDefaultProperties</span><span style="color: #000;">&#40;</span><span style="color: #000;">&#41;</span><span style="color: #000;">&#41;</span><span style="color: #339933;">.</span><span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
<span style="color: #000000; font-weight: bold;">?&gt;</span></pre></div></div>

<p>Outputs</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">Class</span> name<span style="color: #339933;">:</span> DateTime
Doc Comment<span style="color: #339933;">:</span> bool<span style="color: #000;">&#40;</span><span style="color: #009900; font-weight: bold;">false</span><span style="color: #000;">&#41;</span>
Contants<span style="color: #339933;">:</span> <span style="color: #990000;">array</span><span style="color: #000;">&#40;</span><span style="color: #cc66cc;">11</span><span style="color: #000;">&#41;</span> <span style="color: #000;">&#123;</span>
  <span style="color: #000;">&#91;</span><span style="color: #0000ff;">&quot;ATOM&quot;</span><span style="color: #000;">&#93;</span><span style="color: #339933;">=&gt;</span>
  string<span style="color: #000;">&#40;</span><span style="color: #cc66cc;">13</span><span style="color: #000;">&#41;</span> <span style="color: #0000ff;">&quot;Y-m-d<span style="color: #000099; font-weight: bold;">\T</span>H:i:sP&quot;</span>
  <span style="color: #000;">&#91;</span><span style="color: #0000ff;">&quot;COOKIE&quot;</span><span style="color: #000;">&#93;</span><span style="color: #339933;">=&gt;</span>
  string<span style="color: #000;">&#40;</span><span style="color: #cc66cc;">16</span><span style="color: #000;">&#41;</span> <span style="color: #0000ff;">&quot;l, d-M-y H:i:s T&quot;</span>
  <span style="color: #000;">&#91;</span><span style="color: #0000ff;">&quot;ISO8601&quot;</span><span style="color: #000;">&#93;</span><span style="color: #339933;">=&gt;</span>
  string<span style="color: #000;">&#40;</span><span style="color: #cc66cc;">13</span><span style="color: #000;">&#41;</span> <span style="color: #0000ff;">&quot;Y-m-d<span style="color: #000099; font-weight: bold;">\T</span>H:i:sO&quot;</span>
  <span style="color: #000;">&#91;</span><span style="color: #0000ff;">&quot;RFC822&quot;</span><span style="color: #000;">&#93;</span><span style="color: #339933;">=&gt;</span>
  string<span style="color: #000;">&#40;</span><span style="color: #cc66cc;">16</span><span style="color: #000;">&#41;</span> <span style="color: #0000ff;">&quot;D, d M y H:i:s O&quot;</span>
  <span style="color: #000;">&#91;</span><span style="color: #0000ff;">&quot;RFC850&quot;</span><span style="color: #000;">&#93;</span><span style="color: #339933;">=&gt;</span>
  string<span style="color: #000;">&#40;</span><span style="color: #cc66cc;">16</span><span style="color: #000;">&#41;</span> <span style="color: #0000ff;">&quot;l, d-M-y H:i:s T&quot;</span>
  <span style="color: #000;">&#91;</span><span style="color: #0000ff;">&quot;RFC1036&quot;</span><span style="color: #000;">&#93;</span><span style="color: #339933;">=&gt;</span>
  string<span style="color: #000;">&#40;</span><span style="color: #cc66cc;">16</span><span style="color: #000;">&#41;</span> <span style="color: #0000ff;">&quot;D, d M y H:i:s O&quot;</span>
  <span style="color: #000;">&#91;</span><span style="color: #0000ff;">&quot;RFC1123&quot;</span><span style="color: #000;">&#93;</span><span style="color: #339933;">=&gt;</span>
  string<span style="color: #000;">&#40;</span><span style="color: #cc66cc;">16</span><span style="color: #000;">&#41;</span> <span style="color: #0000ff;">&quot;D, d M Y H:i:s O&quot;</span>
  <span style="color: #000;">&#91;</span><span style="color: #0000ff;">&quot;RFC2822&quot;</span><span style="color: #000;">&#93;</span><span style="color: #339933;">=&gt;</span>
  string<span style="color: #000;">&#40;</span><span style="color: #cc66cc;">16</span><span style="color: #000;">&#41;</span> <span style="color: #0000ff;">&quot;D, d M Y H:i:s O&quot;</span>
  <span style="color: #000;">&#91;</span><span style="color: #0000ff;">&quot;RFC3339&quot;</span><span style="color: #000;">&#93;</span><span style="color: #339933;">=&gt;</span>
  string<span style="color: #000;">&#40;</span><span style="color: #cc66cc;">13</span><span style="color: #000;">&#41;</span> <span style="color: #0000ff;">&quot;Y-m-d<span style="color: #000099; font-weight: bold;">\T</span>H:i:sP&quot;</span>
  <span style="color: #000;">&#91;</span><span style="color: #0000ff;">&quot;RSS&quot;</span><span style="color: #000;">&#93;</span><span style="color: #339933;">=&gt;</span>
  string<span style="color: #000;">&#40;</span><span style="color: #cc66cc;">16</span><span style="color: #000;">&#41;</span> <span style="color: #0000ff;">&quot;D, d M Y H:i:s O&quot;</span>
  <span style="color: #000;">&#91;</span><span style="color: #0000ff;">&quot;W3C&quot;</span><span style="color: #000;">&#93;</span><span style="color: #339933;">=&gt;</span>
  string<span style="color: #000;">&#40;</span><span style="color: #cc66cc;">13</span><span style="color: #000;">&#41;</span> <span style="color: #0000ff;">&quot;Y-m-d<span style="color: #000099; font-weight: bold;">\T</span>H:i:sP&quot;</span>
<span style="color: #000;">&#125;</span>
&nbsp;
Parent <span style="color: #000000; font-weight: bold;">Class</span><span style="color: #339933;">:</span> bool<span style="color: #000;">&#40;</span><span style="color: #009900; font-weight: bold;">false</span><span style="color: #000;">&#41;</span>
Constructor<span style="color: #339933;">:</span> object<span style="color: #000;">&#40;</span>ReflectionMethod<span style="color: #000;">&#41;</span><span style="color: #666666; font-style: italic;">#2 (2) {
</span>  <span style="color: #000;">&#91;</span><span style="color: #0000ff;">&quot;name&quot;</span><span style="color: #000;">&#93;</span><span style="color: #339933;">=&gt;</span>
  string<span style="color: #000;">&#40;</span><span style="color: #cc66cc;">11</span><span style="color: #000;">&#41;</span> <span style="color: #0000ff;">&quot;__construct&quot;</span>
  <span style="color: #000;">&#91;</span><span style="color: #0000ff;">&quot;class&quot;</span><span style="color: #000;">&#93;</span><span style="color: #339933;">=&gt;</span>
  string<span style="color: #000;">&#40;</span><span style="color: #cc66cc;">8</span><span style="color: #000;">&#41;</span> <span style="color: #0000ff;">&quot;DateTime&quot;</span>
<span style="color: #000;">&#125;</span>
<span style="color: #22f;">Default</span> Properties<span style="color: #339933;">:</span> <span style="color: #990000;">array</span><span style="color: #000;">&#40;</span><span style="color: #cc66cc;">0</span><span style="color: #000;">&#41;</span> <span style="color: #000;">&#123;</span>
<span style="color: #000;">&#125;</span></pre></div></div>

<p>Check out the documentation; there are a bunch of other details you can get from other methods. </p>
<p>This is really just the tip of the iceberg though. There are other classes that allow even deeper insight into <a title="ReflectionFunction" href="http://us.php.net/manual/en/class.reflectionfunction.php" onclick="return TrackClick('http%3A%2F%2Fus.php.net%2Fmanual%2Fen%2Fclass.reflectionfunction.php','ReflectionFunction')" target="_blank">functions</a>, <a title="ReflectionMethod" href="http://us.php.net/manual/en/class.reflectionmethod.php" onclick="return TrackClick('http%3A%2F%2Fus.php.net%2Fmanual%2Fen%2Fclass.reflectionmethod.php','ReflectionMethod')" target="_blank">methods</a>,  <a title="ReflectionExtension" href="http://us.php.net/manual/en/class.reflectionextension.php" onclick="return TrackClick('http%3A%2F%2Fus.php.net%2Fmanual%2Fen%2Fclass.reflectionextension.php','ReflectionExtension')" target="_blank">extensions</a> and much more. Definitely worth checking out. </p>
<div><a class="addthis_button" href="http://blog.ericlamb.net//addthis.com/bookmark.php?v=250" addthis:url='http://blog.ericlamb.net/2011/03/practical-php-reflection/' addthis:title='Practical PHP Reflection '><img src="//cache.addthis.com/cachefly/static/btn/v2/lg-share-en.gif" width="125" height="16" alt="Bookmark and Share" style="border:0"/></a></div>]]></content:encoded>
			<wfw:commentRss>http://blog.ericlamb.net/2011/03/practical-php-reflection/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>

