<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><title>Chris Morrell</title> <link>http://cmorrell.com</link> <description>The personal home page of Chris Morrell</description> <lastBuildDate>Wed, 17 Mar 2010 16:04:25 +0000</lastBuildDate> <generator>http://wordpress.org/?v=2.9.2</generator> <language>en</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/cmorrell" /><feedburner:info uri="cmorrell" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item><title>Namespacing ACL resources &amp; Galahad_Acl</title><link>http://feedproxy.google.com/~r/cmorrell/~3/I6YSF-USdFk/namespacing-acl-resources-galahad-acl-737</link> <comments>http://cmorrell.com/web-development/zf/namespacing-acl-resources-galahad-acl-737#comments</comments> <pubDate>Wed, 17 Mar 2010 16:04:25 +0000</pubDate> <dc:creator>Chris Morrell</dc:creator> <category><![CDATA[Zend Framework]]></category> <category><![CDATA[galahad-fe]]></category><guid isPermaLink="false">http://cmorrell.com/?p=737</guid> <description><![CDATA[In most of my applications I like to handle authorization (querying the ACL) in one (or more) of three ways:Authorize access to a model&#8217;s method
Authorize access to a controller action
Authorize access to an arbitrary &#8220;permission&#8221;In general I find it&#8217;s best to keep authorization within the domain (querying the ACL within my models when they&#8217;re accessed) [...]]]></description> <content:encoded><![CDATA[<p>In most of my applications I like to handle authorization (querying the ACL) in one (or more) of three ways:</p><ul><li>Authorize access to a model&#8217;s method</li><li>Authorize access to a controller action</li><li>Authorize access to an arbitrary &#8220;permission&#8221;</li></ul><p>In general I find it&#8217;s best to keep authorization within the domain (querying the ACL within my models when they&#8217;re accessed) as this provides the most consistent behavior.  For example, if I eventually add a REST API to my application I don&#8217;t have to duplicate all my authorization logic in the new REST controllers.  When the application calls something like <code>Default_Model_Post::save()</code> it either saves or throws an ACL exception, no matter where it was called from.  This is great in that it saves me from having to duplicate code and keeps my system more secure.</p><p>On the other hand, there are times when it&#8217;s just a lot easier to handle authorization in the controller.  For example, if guests should never access my &#8220;Admin&#8221; module, it doesn&#8217;t make sense to ever let them access /admin/ URLs.  Also, if you&#8217;re using Zend_Navigation, having ACL resources that match controller actions lets you utilize its ACL integration.</p><p>If you&#8217;re ever going to mix these two techniques, you&#8217;ll eventually bump into the case where a model and a controller share the same name.  What if you need to set permissions on a &#8220;user&#8221; controller and different permissions on a &#8220;user&#8221; model?  This is where namespacing comes into play.  As suggested by the <a
href="http://framework.zend.com/manual/en/zend.view.helpers.html#zend.view.helpers.initial.navigation.setup" target="_blank">Zend Framework manual</a>, I always name my controller action resources in the format <code>mvc:module.controller.action</code>.  I name my model resources similarly, in the format <code>model:module.modelName.methodName</code>.  In both theses cases, &#8220;mvc&#8221; and &#8220;model&#8221; are the namespace, and everything following the colon is the actual resource name.  Now I can refer to my &#8220;admin&#8221; module as <code>mvc:admin</code> and the models within my admin module as <code>model:admin</code>.</p><p>This is where things get interesting.  If you set up your ACL chains correctly, you can set permissions on whole modules or models and have those rules cascade to their child controllers or methods.  For example, say you set up your ACL as follows:</p><pre class="brush: php;">
$acl = new Zend_Acl();
$acl-&gt;addResource('mvc:');
$acl-&gt;addResource('mvc:admin', 'mvc:');
$acl-&gt;addResource('mvc:admin.user', 'mvc:admin');
$acl-&gt;addResource('mvc:admin.user.create', 'mvc:admin.user');

$acl-&gt;addRole('guest');
$acl-&gt;addRole('admin', 'guest');

$acl-&gt;deny();
$acl-&gt;allow('admin', 'mvc:admin');
</pre><p>Now if a user with the role &#8220;admin&#8221; tries to access the resource &#8220;mvc:admin.user.create&#8221; (http://basename/admin/user/create) they will be allowed, but a user with the role &#8220;guest&#8221; will not.  Using this technique gives you as much granularity as you need in your ACL, but at the same time lets you set broad permissions where appropriate.</p><p>This is where <code>Galahad_Acl</code> comes into play.  Setting up all these resources can be tedious, as is checking permissions in each controller.  <code>Galahad_Acl</code> in conjunction with <code>Galahad_Model_Entity</code> and <code>Galahad_Controller_Plugin_Acl</code> automate everything but the actual permissions that are specific to your application.</p><p><span
id="more-737"></span></p><p>By default, whenever <code>Galahad_Acl</code> has a role added to it in the format &#8220;namespace:resource.subResourse&#8221; (etc) it automatically adds the resources up the chain.  For example, if I add &#8220;mvc:default.index.index&#8221; to a Galahad_Acl object, it would add the following resources to the ACL:</p><ul><li>mvc:</li><li>mvc:default (parent = &#8220;mvc:&#8221;)</li><li>mvc:default.index (parent = &#8220;mvc:default&#8221;)</li><li>mvc:default.index.index (parent = &#8220;mvc:default.index&#8221;)</li></ul><p><code>Galahad_Controller_Plugin_Acl</code> takes this a step further by automatically adding any controller action that&#8217;s dispatched to the ACL and then checking against the ACL.  This means that with the following ACL:</p><pre class="brush: php;">
$acl = new Galahad_Acl();
$acl-&gt;addRole('guest');
$acl-&gt;addRole('staff', 'guest');
$acl-&gt;addRole('admin', 'staff');
$acl-&gt;addResource('mvc:blog.entry.view');
$acl-&gt;deny();
$acl-&gt;allow('admin', 'mvc:');
$acl-&gt;allow('staff', 'mvc:blog');
$acl-&gt;allow('guest', 'mvc:blog.entry.view');
</pre><p>The following would be true:</p><ul><li>Role &#8220;admin&#8221; would be allowed access to any URL (&#8220;admin&#8221; is allowed access to &#8220;mvc:&#8221;)</li><li>Role &#8220;staff&#8221; would be allowed access to /blog/entry/edit even though permissions weren&#8217;t explicitly set for the resource &#8220;mvc:blog.entry.edit&#8221; (because &#8220;staff&#8221; is allowed to access &#8220;mvc:blog&#8221;)</li><li>Role &#8220;guest&#8221; would be allowed to view /blog/entry/view but no other portion of the blog (&#8220;guest&#8221; is allowed access to the specific resource &#8220;mvc:blog.entry.view&#8221;)</li></ul><p><code>Galahad_Model_Entity</code> works similarly.  By default each entity adds itself to the ACL in the format &#8220;model:module.modelName&#8221; so that the model <code>Default_Model_User</code> has the resource ID &#8220;model:default.user&#8221;.  Each model has a method called <code>_initAcl</code> which lets you manage permissions on a per-model basis.  This is better demonstrated in code:</p><pre class="brush: php;">
class Default_Model_Post extends Galahad_Model_Entity
{
    protected function _initAcl($acl)
    {
        // Deny permissions to anything on this model unless explicitly allowed
        // We don't have to add &quot;model:default.post&quot; because Galahad_Model_Entity
        // automatically does that for us
        $acl-&gt;deny(null, 'model:default.post');

        // Add additional resources (class methods)
        $acl-&gt;addResource('model:default.post.fetch');
        $acl-&gt;addResource('model:default.post.save');

        // Allow guests to fetch the content of posts
        $acl-&gt;allow('guest', 'model:default.post.fetch')

        // Allow admins to save changes to posts
        $acl-&gt;allow('admin', 'model:default.post.save')
    }

    public function save()
    {
        if (!$this-&gt;getAcl()-&gt;isAllowed($this-&gt;getRole(), 'model:default.post.save')) {
            throw new Galahad_Acl_Exception('Current user is not allowed to save posts');
        }

        $dataMapper = $this-&gt;getDataMapper();
        return $dataMapper-&gt;save($this);
    }
}
</pre><p>So, as you can see, the model denies access to itself unless explicitly allowed, and then allows access to certain methods for certain roles.</p><p>All three of these classes are in their early stages of development, but I&#8217;d love some feedback on the ideas/suggestions on how to make them better.</p><p>Check out the code on GitHub:</p><ul><li><a
href="http://github.com/inxilpro/Galahad-FE/blob/master/library/Galahad/Acl.php" target="_blank">Galahad_Acl</a></li><li><a
href="http://github.com/inxilpro/Galahad-FE/blob/master/library/Galahad/Controller/Plugin/Acl.php" target="_blank">Galahad_Controller_Plugin_Acl</a></li><li><a
href="http://github.com/inxilpro/Galahad-FE/blob/master/library/Galahad/Model/Entity.php" target="_blank">Galahad_Model_Entity</a></li></ul><p>Also, for more information about the <code>Galahad_Model</code> system, check out my previous post on <a
href="http://cmorrell.com/web-development/more-php-modelling-383">modeling in the Zend Framework</a>.</p><div
class="su-linkbox" id="post-737-linkbox"><div
class="su-linkbox-label">Link to this post:</div><div
class="su-linkbox-field"><input
type="text" value="&lt;a href=&quot;http://cmorrell.com/web-development/zf/namespacing-acl-resources-galahad-acl-737&quot;&gt;Namespacing ACL resources &amp; Galahad_Acl&lt;/a&gt;" onclick="javascript:this.select()" readonly="readonly" style="width: 100%;" /></div></div>]]></content:encoded> <wfw:commentRss>http://cmorrell.com/web-development/zf/namespacing-acl-resources-galahad-acl-737/feed</wfw:commentRss> <slash:comments>0</slash:comments> <feedburner:origLink>http://cmorrell.com/web-development/zf/namespacing-acl-resources-galahad-acl-737</feedburner:origLink></item> <item><title>Zend Framework URI validator &amp; filter</title><link>http://feedproxy.google.com/~r/cmorrell/~3/RUJWUEpSvmc/validate-filter-url-728</link> <comments>http://cmorrell.com/web-development/zf/validate-filter-url-728#comments</comments> <pubDate>Fri, 12 Mar 2010 19:06:53 +0000</pubDate> <dc:creator>Chris Morrell</dc:creator> <category><![CDATA[Zend Framework]]></category><guid isPermaLink="false">http://cmorrell.com/?p=728</guid> <description><![CDATA[For the last couple of months I&#8217;ve been incorporating portions of applications I&#8217;m working on into my Galahad Framework Extension project.  Right now it&#8217;s not at a point where I&#8217;d feel comfortable promoting it (you can check out the project on GitHub if you want), but there are portions that are pretty solid that might [...]]]></description> <content:encoded><![CDATA[<p>For the last couple of months I&#8217;ve been incorporating portions of applications I&#8217;m working on into my Galahad Framework Extension project.  Right now it&#8217;s not at a point where I&#8217;d feel comfortable promoting it (you can <a
title="Galahad Framework Extension" href="http://bit.ly/d3ULiy" target="_blank">check out the project on GitHub</a> if you want), but there are portions that are pretty solid that might be useful to others right now.  Two such portions are <code>Galahad_Validate_Uri</code> and <code>Galahad_Filter_PrependHttp</code> which are both very useful for processing forms with URL fields.</p><p><span
id="more-728"></span></p><p>Recently I was building a form that contained a URL field, and after browsing the <a
href="http://framework.zend.com/manual/en/zend.validate.html" target="_blank">Zend_Validate docs</a>, I was surprised not to find a <code>Zend_Validate_Uri</code> component.  Luckily, <code>Zend_Uri</code> already validates URIs, so it was just a matter of writing a wrapper that followed the Zend_Validate APIs.  Basic usage:</p><pre class="brush: php;">
$validator = new Galahad_Validate_Uri();
$validator-&gt;isValid('http://www.google.com/');
</pre><p>Obviously this would be much more useful when combined with <code>Zend_Form</code> or some more extensive validation chains, but you get the point.</p><p>Grab <a
href="http://bit.ly/cFB0li" target="_blank">Galahad_Validate_Uri</a> from GitHub.</p><p>The second issue I often deal with is people forgetting (or not knowing) to include http:// in the URLs that they submit.  Rather than solve this at the controller-level, I decided this was a common enough problem to build a Zend_Filter component for.  Basic usage:</p><pre class="brush: php;">
$filter = new Galahad_Filter_PrependHttp(array(
    'allowedSchemes' =&gt; array('http://', 'https://', 'mailto:'),
    'checkUri' =&gt; true,
));
echo $filter-&gt;filter('google.com'); // Prints 'http://google.com'
</pre><p>This filter takes any string and prepends &#8220;http://&#8221; to it if it doesn&#8217;t already contain an allowed scheme (more on that below).  By default it allows &#8220;http://&#8221;, &#8220;https://&#8221; and &#8220;mailto:&#8221; schemes, but you could set this to anything (say you want to allow &#8220;itms://&#8221; [iTunes] links) by setting the &#8216;allowedSchemes&#8217; option.  It also (optionally) checks if the resulting URI is valid, and only applies the filter if it is (effectively only prepending &#8220;http://&#8221; to otherwise valid URIs).  Remember, though, that &#8220;http://something&#8221; is technically a valid URI, so that will be accepted.  In the future I hope to add additional options to limit to URLs that follow certain standards.</p><p>Grab <a
href="http://bit.ly/csqLWK" target="_blank">Galahad_Filter_PrependHttp</a> from GitHub.</p><p><strong>Update</strong>: I&#8217;ve submitted Zend_Filter_PrependHttp to the Zend Framework wiki for comments.  <a
href="http://framework.zend.com/wiki/display/ZFPROP/Zend_Filter_PrependHttp+-+Chris+Morrell" target="_blank">Check it out</a> and let me know if you think there&#8217;s anything I should change (I&#8217;ve posted a few ideas already).</p><p>The validator and filter work well together with <code>Zend_Form</code> to create URL form elements:</p><pre class="brush: php;">
$this-&gt;addElement('text', 'my_url', array(
    'label' =&gt; 'URL:',
    'filters' =&gt; array('StringTrim', 'StringToLower', new Galahad_Filter_PrependHttp()),
    'validators' =&gt; array(new Galahad_Validate_Uri()),
));
</pre><p>The form element generated by that code will produce fairly normalized, valid URIs.</p><div
class="su-linkbox" id="post-728-linkbox"><div
class="su-linkbox-label">Link to this post:</div><div
class="su-linkbox-field"><input
type="text" value="&lt;a href=&quot;http://cmorrell.com/web-development/zf/validate-filter-url-728&quot;&gt;Zend Framework URI validator &amp; filter&lt;/a&gt;" onclick="javascript:this.select()" readonly="readonly" style="width: 100%;" /></div></div>]]></content:encoded> <wfw:commentRss>http://cmorrell.com/web-development/zf/validate-filter-url-728/feed</wfw:commentRss> <slash:comments>2</slash:comments> <feedburner:origLink>http://cmorrell.com/web-development/zf/validate-filter-url-728</feedburner:origLink></item> <item><title>My response to livestream</title><link>http://feedproxy.google.com/~r/cmorrell/~3/xoqiXg7_Cfs/anti-anti-piracy-721</link> <comments>http://cmorrell.com/video/anti-anti-piracy-721#comments</comments> <pubDate>Wed, 03 Mar 2010 21:34:18 +0000</pubDate> <dc:creator>Chris Morrell</dc:creator> <category><![CDATA[Video]]></category> <category><![CDATA[piracy]]></category><guid isPermaLink="false">http://cmorrell.com/?p=721</guid> <description><![CDATA[Today livestream announced a new &#8220;zero tolerance on piracy&#8221; program.  The following is my response to their promotional email marketing this &#8220;feature.&#8221;To whom it may concern:
My company was considering using  livestream as a live streaming provider for our educational content.   This new initiative has convinced me to look elsewhere, as it has been [...]]]></description> <content:encoded><![CDATA[<p>Today livestream announced a new &#8220;zero tolerance on piracy&#8221; program.  The following is my response to their promotional email marketing this &#8220;feature.&#8221;<br
/> <span
id="more-721"></span></p><blockquote><p>To whom it may concern:</p><p>My company was considering using  livestream as a live streaming provider for our educational content.   This new initiative has convinced me to look elsewhere, as it has been  my experience that anti-piracy measures only hurt consumers and small  businesses.  It may be true that these measures protect very large (and  outmoded) copyright holders, but at the same time they hamper innovation  and limit consumer access to content.  We would rather partner with a  company that devotes 100% of its resources to providing the best service  possible.</p><p>Regards,</p><p>Chris Morrell</p></blockquote><p>Sure, piracy is a real issue.  As a content producer I certainly understand the desire to stop people from <em>stealing</em> stuff I worked really hard to make.  But I think anti-piracy is a much bigger issue.  DRM and its cohorts have made it impossible for me to consume content <em>I own</em> a few too many times for me to support a company that has declared that it&#8217;s making anti-piracy a top concern.</p><div
class="su-linkbox" id="post-721-linkbox"><div
class="su-linkbox-label">Link to this post:</div><div
class="su-linkbox-field"><input
type="text" value="&lt;a href=&quot;http://cmorrell.com/video/anti-anti-piracy-721&quot;&gt;My response to livestream&lt;/a&gt;" onclick="javascript:this.select()" readonly="readonly" style="width: 100%;" /></div></div>]]></content:encoded> <wfw:commentRss>http://cmorrell.com/video/anti-anti-piracy-721/feed</wfw:commentRss> <slash:comments>3</slash:comments> <feedburner:origLink>http://cmorrell.com/video/anti-anti-piracy-721</feedburner:origLink></item> <item><title>Soup phase 3 (eating!)  http:/…</title><link>http://feedproxy.google.com/~r/cmorrell/~3/E_0zsRW1vao/soup-phase-3-eating-http-711</link> <comments>http://cmorrell.com/twitter/soup-phase-3-eating-http-711#comments</comments> <pubDate>Fri, 26 Feb 2010 01:22:29 +0000</pubDate> <dc:creator>Chris Morrell</dc:creator> <category><![CDATA[twitter]]></category> <category><![CDATA[tweet]]></category><guid isPermaLink="false">http://cmorrell.com/twitter/soup-phase-3-eating-http-711</guid> <description><![CDATA[Soup phase 3 (eating!)  http://flic.kr/p/7FL3hR
Link to this post:]]></description> <content:encoded><![CDATA[<p>Soup phase 3 (eating!) <a
href="http://flic.kr/p/7FL3hR" rel="nofollow">http://flic.kr/p/7FL3hR</a></p><div
class="su-linkbox" id="post-711-linkbox"><div
class="su-linkbox-label">Link to this post:</div><div
class="su-linkbox-field"><input
type="text" value="&lt;a href=&quot;http://cmorrell.com/twitter/soup-phase-3-eating-http-711&quot;&gt;Soup phase 3 (eating!)  http:/&#8230;&lt;/a&gt;" onclick="javascript:this.select()" readonly="readonly" style="width: 100%;" /></div></div>]]></content:encoded> <wfw:commentRss>http://cmorrell.com/twitter/soup-phase-3-eating-http-711/feed</wfw:commentRss> <slash:comments>1</slash:comments> <feedburner:origLink>http://cmorrell.com/twitter/soup-phase-3-eating-http-711</feedburner:origLink></item> <item><title>Sweet.  Just saved about 60k a…</title><link>http://feedproxy.google.com/~r/cmorrell/~3/AzLehjVcxf0/sweet-just-saved-about-60k-a-710</link> <comments>http://cmorrell.com/twitter/sweet-just-saved-about-60k-a-710#comments</comments> <pubDate>Thu, 25 Feb 2010 20:29:10 +0000</pubDate> <dc:creator>Chris Morrell</dc:creator> <category><![CDATA[twitter]]></category> <category><![CDATA[tweet]]></category><guid isPermaLink="false">http://cmorrell.com/twitter/sweet-just-saved-about-60k-a-710</guid> <description><![CDATA[Sweet.  Just saved about 60k and 8 requests by installing PHP Speedy.  That plus WP-Super-Cache is a killer combo.  http://bit.ly/bi719l
Link to this post:]]></description> <content:encoded><![CDATA[<p>Sweet.  Just saved about 60k and 8 requests by installing PHP Speedy.  That plus WP-Super-Cache is a killer combo. <a
href="http://bit.ly/bi719l" rel="nofollow">http://bit.ly/bi719l</a></p><div
class="su-linkbox" id="post-710-linkbox"><div
class="su-linkbox-label">Link to this post:</div><div
class="su-linkbox-field"><input
type="text" value="&lt;a href=&quot;http://cmorrell.com/twitter/sweet-just-saved-about-60k-a-710&quot;&gt;Sweet.  Just saved about 60k a&#8230;&lt;/a&gt;" onclick="javascript:this.select()" readonly="readonly" style="width: 100%;" /></div></div>]]></content:encoded> <wfw:commentRss>http://cmorrell.com/twitter/sweet-just-saved-about-60k-a-710/feed</wfw:commentRss> <slash:comments>1</slash:comments> <feedburner:origLink>http://cmorrell.com/twitter/sweet-just-saved-about-60k-a-710</feedburner:origLink></item> <item><title>Stock: phase 2 http://flic.kr/…</title><link>http://feedproxy.google.com/~r/cmorrell/~3/jlhrtqSNOZQ/stock-phase-2-httpflic-kr-709</link> <comments>http://cmorrell.com/twitter/stock-phase-2-httpflic-kr-709#comments</comments> <pubDate>Thu, 25 Feb 2010 18:27:57 +0000</pubDate> <dc:creator>Chris Morrell</dc:creator> <category><![CDATA[twitter]]></category> <category><![CDATA[tweet]]></category><guid isPermaLink="false">http://cmorrell.com/twitter/stock-phase-2-httpflic-kr-709</guid> <description><![CDATA[Stock: phase 2 http://flic.kr/p/7FFSni
Link to this post:]]></description> <content:encoded><![CDATA[<p>Stock: phase 2 <a
href="http://flic.kr/p/7FFSni" rel="nofollow">http://flic.kr/p/7FFSni</a></p><div
class="su-linkbox" id="post-709-linkbox"><div
class="su-linkbox-label">Link to this post:</div><div
class="su-linkbox-field"><input
type="text" value="&lt;a href=&quot;http://cmorrell.com/twitter/stock-phase-2-httpflic-kr-709&quot;&gt;Stock: phase 2 http://flic.kr/&#8230;&lt;/a&gt;" onclick="javascript:this.select()" readonly="readonly" style="width: 100%;" /></div></div>]]></content:encoded> <wfw:commentRss>http://cmorrell.com/twitter/stock-phase-2-httpflic-kr-709/feed</wfw:commentRss> <slash:comments>1</slash:comments> <feedburner:origLink>http://cmorrell.com/twitter/stock-phase-2-httpflic-kr-709</feedburner:origLink></item> <item><title>Chicken stock making preparati…</title><link>http://feedproxy.google.com/~r/cmorrell/~3/JAbLXm5zwBU/chicken-stock-making-preparati-708</link> <comments>http://cmorrell.com/twitter/chicken-stock-making-preparati-708#comments</comments> <pubDate>Thu, 25 Feb 2010 17:56:10 +0000</pubDate> <dc:creator>Chris Morrell</dc:creator> <category><![CDATA[twitter]]></category> <category><![CDATA[tweet]]></category><guid isPermaLink="false">http://cmorrell.com/twitter/chicken-stock-making-preparati-708</guid> <description><![CDATA[Chicken stock making preparations.  http://flic.kr/p/7FFwNc
Link to this post:]]></description> <content:encoded><![CDATA[<p>Chicken stock making preparations. <a
href="http://flic.kr/p/7FFwNc" rel="nofollow">http://flic.kr/p/7FFwNc</a></p><div
class="su-linkbox" id="post-708-linkbox"><div
class="su-linkbox-label">Link to this post:</div><div
class="su-linkbox-field"><input
type="text" value="&lt;a href=&quot;http://cmorrell.com/twitter/chicken-stock-making-preparati-708&quot;&gt;Chicken stock making preparati&#8230;&lt;/a&gt;" onclick="javascript:this.select()" readonly="readonly" style="width: 100%;" /></div></div>]]></content:encoded> <wfw:commentRss>http://cmorrell.com/twitter/chicken-stock-making-preparati-708/feed</wfw:commentRss> <slash:comments>1</slash:comments> <feedburner:origLink>http://cmorrell.com/twitter/chicken-stock-making-preparati-708</feedburner:origLink></item> <item><title>Mobile App Development slides …</title><link>http://feedproxy.google.com/~r/cmorrell/~3/3agOEFK7DeE/mobile-app-development-slides-704</link> <comments>http://cmorrell.com/twitter/mobile-app-development-slides-704#comments</comments> <pubDate>Thu, 25 Feb 2010 05:40:22 +0000</pubDate> <dc:creator>Chris Morrell</dc:creator> <category><![CDATA[twitter]]></category> <category><![CDATA[tweet]]></category><guid isPermaLink="false">http://cmorrell.com/twitter/mobile-app-development-slides-704</guid> <description><![CDATA[Mobile App Development slides from @panma event will be online as soon as my DNS changes propagate.  Will post a link tomorrow some time.
Link to this post:]]></description> <content:encoded><![CDATA[<p>Mobile App Development slides from @<a
href="http://twitter.com/panma" class="aktt_username">panma</a> event will be online as soon as my DNS changes propagate.  Will post a link tomorrow some time.</p><div
class="su-linkbox" id="post-704-linkbox"><div
class="su-linkbox-label">Link to this post:</div><div
class="su-linkbox-field"><input
type="text" value="&lt;a href=&quot;http://cmorrell.com/twitter/mobile-app-development-slides-704&quot;&gt;Mobile App Development slides &#8230;&lt;/a&gt;" onclick="javascript:this.select()" readonly="readonly" style="width: 100%;" /></div></div>]]></content:encoded> <wfw:commentRss>http://cmorrell.com/twitter/mobile-app-development-slides-704/feed</wfw:commentRss> <slash:comments>1</slash:comments> <feedburner:origLink>http://cmorrell.com/twitter/mobile-app-development-slides-704</feedburner:origLink></item> <item><title>Mobile App Development for Web Developers</title><link>http://feedproxy.google.com/~r/cmorrell/~3/F3rOnwpiKtU/mobile-app-development-for-web-developers-696</link> <comments>http://cmorrell.com/misc/mobile-app-development-for-web-developers-696#comments</comments> <pubDate>Thu, 25 Feb 2010 05:00:34 +0000</pubDate> <dc:creator>Chris Morrell</dc:creator> <category><![CDATA[Misc]]></category><guid isPermaLink="false">http://www1.cmorrell.com/?p=696</guid> <description><![CDATA[On February 23rd I gave a talk at PANMA&#8217;s Mobile App Development Demystified event.  My talk was titled Mobile App Development from a Web Developer&#8217;s Perspective.  Here are my slides:I want to say thanks to everyone who came out to the event!  Also thanks to Joe Kaufman and Rob Hall for their [...]]]></description> <content:encoded><![CDATA[<p>On February 23rd I gave a talk at <a
href="http://www.panma.org/">PANMA</a>&#8217;s <em>Mobile App Development Demystified</em> event.  My talk was titled <em>Mobile App Development from a Web Developer&#8217;s Perspective</em>.  Here are my slides:</p><div
style="width:425px" id="__ss_3265945"> <object
width="405" height="339"><param
name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=mobileappdevelopment-100224102557-phpapp01&#038;rel=0&#038;stripped_title=mobile-app-development-3265945" /><param
name="allowFullScreen" value="true"/><param
name="allowScriptAccess" value="always"/><embed
src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=mobileappdevelopment-100224102557-phpapp01&#038;rel=0&#038;stripped_title=mobile-app-development-3265945" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="405" height="339"></embed></object></div><p><span
id="more-696"></span></p><p>I want to say thanks to everyone who came out to the event!  Also thanks to Joe Kaufman and Rob Hall for their fantastic presentations.  We ran a little long and some folks had to leave before seeing Joe&#8217;s demo, so I just wanted to mention it here.  For those of you who haven&#8217;t see it, <a
href=" http://gamesalad.com/">GameSalad</a> is pretty magical, and the demo definitely got more than one round of applause.  Check it out (take a minute or two to watch the demo video—you&#8217;ll be blown away).  You should also check out Rob&#8217;s <a
href="http://itunes.apple.com/us/app/phillygeekcentral/id357413307?mt=8">PhillyGeekCentral app</a> (written entirely in ActionScript 3 and compiled using Flash CS5).</p><p>Beyond that I just wanted to open up the comments for any feedback anyone might have on my talk, and to answer any questions that came up after we finished with Q&#038;A.  Feedback is particularly useful to me (and don&#8217;t be afraid to criticize—I can handle it) because I haven&#8217;t done much presenting and am still trying to get a sense for what works and what doesn&#8217;t.</p><p>There are two things that came up in private after the presentation that I think are worth sharing with everyone:</p><ol><li>A couple people asked me about styling applications written for PhoneGap or Titanium.  In the case of PhoneGap you don&#8217;t really get any built-in styles because it&#8217;s really just a web page running in a chrome-less browser.  That means if you want to mimic the iPhone&#8217;s UI or create something similar, you have to do it all with CSS and JavaScript.  Luckily there are a number of frameworks out there that do that for you, and when I get my slides online I&#8217;ll have a bunch of resources to start with.  For right now I would recommend checking out XUI and jQTouch.  These tools help you get a nice mobile UI up and running in no time.  (It&#8217;s worth mentioning that in the case of Titanium this is less of an issue because you can use native components.)</li><li>I also noticed that the question of local storage came up a few times, and I just wanted to clarify there.  Mobile Web Apps, PhoneGap Apps and Titanium Apps all have access to local storage.  Even in a web app, you can save settings to the phone that will still be there when the user closes Mobile Safari and reopens it.  You don&#8217;t have to talk to a web server at all if you don&#8217;t want to.</li></ol><p>Finally, I wanted to talk a little about choosing whether you should build a web app or a native app.  This is something I&#8217;ve been thinking about a lot and just didn&#8217;t have time to talk about last night.  Right now everyone is trying to get on Apple&#8217;s App Store.  It seems like most major web sites either have native apps on the store already, or are planning to do so soon.  But in the case of services that aren&#8217;t used daily (or at least weekly) I don&#8217;t think this makes any sense.  There&#8217;s a reason that Kayak.com (thanks to Andy Mroczkowski for this perfect example) is a web site and not a program that you download for your computer; For services that you only use time-to-time, it doesn&#8217;t make any sense to use a dedicated application.  So why would you want a dedicated application on your phone?  Well, you probably don&#8217;t.  But because everyone needs to be on the App Store today, that&#8217;s where you&#8217;ll find Kayak&#8217;s best mobile interactions.</p><p>That&#8217;s not to say that there aren&#8217;t a ton of instances where a native app wouldn&#8217;t be worth it.  I just think that far too many people are trying to develop native apps when they really aught to be working on great mobile web sites (or web apps).  Just like anything else, think about your users and their needs and choose the best option for them, even if it&#8217;s not 100% buzzword friendly.</p><p>Well, this post came out a lot longer than anticipated.  If you can&#8217;t tell, mobile development is a topic that I&#8217;m happy to talk about, so if you have question feel free to ask.</p><p>Here are some of the resources I mentioned for mobile app development:</p><ul><li><a
href="http://bit.ly/4Fkdnp">Safari Mobile Web Programming</a></li><li><a
href="http://www.w3.org/TR/2010/CR-mwabp-20100211/#bp-viewport">Viewport Meta Element</a></li><li><a
href="http://www.w3.org/TR/css3-mediaqueries/">CSS3 Media Queries</a></li><li><a
href="http://phonegap.com/">PhoneGap</a></li><li><a
href="http://www.appcelerator.com/">Titanium Mobile</a></li><li><a
href="http://developer.apple.com/iphone/">Apple iPhone Dev Center</a></li><li><a
href="http://dev.w3.org/geo/api/spec-source.html">W3C Geolocation API</a></li><li><a
href="http://bit.ly/bvlVJ8">Offline Storage &#038; Caching</a></li><li><a
href="http://webkit.org/blog/138/css-animation/">CSS3 Transitions</a></li></ul><div
class="su-linkbox" id="post-696-linkbox"><div
class="su-linkbox-label">Link to this post:</div><div
class="su-linkbox-field"><input
type="text" value="&lt;a href=&quot;http://cmorrell.com/misc/mobile-app-development-for-web-developers-696&quot;&gt;Mobile App Development for Web Developers&lt;/a&gt;" onclick="javascript:this.select()" readonly="readonly" style="width: 100%;" /></div></div>]]></content:encoded> <wfw:commentRss>http://cmorrell.com/misc/mobile-app-development-for-web-developers-696/feed</wfw:commentRss> <slash:comments>1</slash:comments> <feedburner:origLink>http://cmorrell.com/misc/mobile-app-development-for-web-developers-696</feedburner:origLink></item> <item><title>More PHP Modeling (w/ video demo)</title><link>http://feedproxy.google.com/~r/cmorrell/~3/dY0kSAbfMCU/more-php-modelling-383</link> <comments>http://cmorrell.com/web-development/more-php-modelling-383#comments</comments> <pubDate>Fri, 04 Dec 2009 16:49:26 +0000</pubDate> <dc:creator>Chris Morrell</dc:creator> <category><![CDATA[Web Development]]></category> <category><![CDATA[Zend Framework]]></category><guid isPermaLink="false">http://cmorrell.com/?p=383</guid> <description><![CDATA[[Updated with follow-up video]
About a month ago I posted some ideas about PHP modeling in the Zend Framework and requested feedback.  After a month of on-and-off discussions through this website and #zftalk I decided to sit down and implement things a little more.I now have some working base classes that can be found on [...]]]></description> <content:encoded><![CDATA[<p><strong>[Updated with follow-up video]</strong></p><p>About a month ago I posted some ideas about <a
href="http://cmorrell.com/web-development/php-modeling-in-zend-framework-360">PHP modeling in the Zend Framework</a> and requested feedback.  After a month of on-and-off discussions through this website and <a
href="http://zftalk.com/">#zftalk</a> I decided to sit down and implement things a little more.<br
/> <span
id="more-383"></span><br
/> I now have some working base classes that <a
href="http://github.com/inxilpro/Galahad-FE">can be found on GitHub</a>.  Right now I&#8217;m still thinking things out, so there&#8217;s no guarantee that&#8217;s the structure I&#8217;m going to finish with, but it&#8217;s what I&#8217;m playing with right now.  So far I&#8217;ve dropped the DAO interface and the Galahad_Service parent class all together (since both are going to be pretty unique to your application).  What&#8217;s left is mostly the Entity class and the DataMapper class (as well as a very generic Collection class).</p><p>I&#8217;ve also started to write some tooling for my modeling system, based on <a
href="http://framework.zend.com/manual/en/zend.tool.framework.html">Zend_Tool</a>.  Right now it&#8217;s generating the model itself, a DAO based on <a
href="http://framework.zend.com/manual/en/zend.db.table.html">Zend_Db_Table</a> and a <a
href="http://framework.zend.com/manual/en/zend.form.html">Zend_Form</a> (see <a
href="http://weierophinney.net/matthew/archives/200-Using-Zend_Form-in-Your-Models.html">Matthew Weier O&#8217;Phinney&#8217;s post about using forms in your models</a> for my reasoning there).  It doesn&#8217;t generate the DataMapper yet, but that&#8217;s just a matter of writing the code…</p><p>Again, I&#8217;d love some feedback on the direction this is going.  Check out the video below and then let me know.  Comment below, email me at <a
href="http://mailhide.recaptcha.net/d?k=01t3MHtCNlY1OI8TgogO8VwQ==&amp;c=3NSszgQSWON_Ovzh0YWmlyKF776ZaMWSTct2mtNMEaM=" onclick="window.open('http://mailhide.recaptcha.net/d?k=01t3MHtCNlY1OI8TgogO8VwQ==&amp;c=3NSszgQSWON_Ovzh0YWmlyKF776ZaMWSTct2mtNMEaM=', '', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=500,height=300'); return false;" title="Reveal this e-mail address">*****@cmorrell.com</a> or get in touch on Twitter: <a
href="http://twitter.com/inxilpro">@inxilpro</a>.</p><p>[<a
href="http://www.screencast.com/t/MTFlZDJiNW" target="_blank">View full size</a>, or watch below]</p><p><object
width="405" height="229"><param
name="movie" value="http://content.screencast.com/users/inxilpro/folders/Jing/media/33a696b5-cc1f-4f15-af43-d6fb0e2b5fac/jingh264player.swf"></param><param
name="quality" value="high"></param><param
name="bgcolor" value="#FFFFFF"></param><param
name="flashVars" value="thumb=http://content.screencast.com/users/inxilpro/folders/Jing/media/33a696b5-cc1f-4f15-af43-d6fb0e2b5fac/FirstFrame.jpg&#038;containerwidth=405&#038;containerheight=229&#038;content=http://content.screencast.com/users/inxilpro/folders/Jing/media/33a696b5-cc1f-4f15-af43-d6fb0e2b5fac/00000032.mp4"></param><param
name="allowFullScreen" value="true"></param><param
name="scale" value="showall"></param><param
name="allowScriptAccess" value="always"></param><param
name="base" value="http://content.screencast.com/users/inxilpro/folders/Jing/media/33a696b5-cc1f-4f15-af43-d6fb0e2b5fac/"></param> <embed
src="http://content.screencast.com/users/inxilpro/folders/Jing/media/33a696b5-cc1f-4f15-af43-d6fb0e2b5fac/jingh264player.swf" quality="high" bgcolor="#FFFFFF" width="405" height="229" type="application/x-shockwave-flash" allowScriptAccess="always" flashVars="thumb=http://content.screencast.com/users/inxilpro/folders/Jing/media/33a696b5-cc1f-4f15-af43-d6fb0e2b5fac/FirstFrame.jpg&#038;containerwidth=405&#038;containerheight=229&#038;content=http://content.screencast.com/users/inxilpro/folders/Jing/media/33a696b5-cc1f-4f15-af43-d6fb0e2b5fac/00000032.mp4" allowFullScreen="true" base="http://content.screencast.com/users/inxilpro/folders/Jing/media/33a696b5-cc1f-4f15-af43-d6fb0e2b5fac/" scale="showall"></embed></object></p><p>Follow-up Video (demo of a lot more code):</p><p><object
width="320" height="265"><param
name="movie" value="http://www.youtube.com/v/oABFXO9WV6w&#038;hl=en_US&#038;fs=1&#038;rel=0&#038;color1=0x5d1719&#038;color2=0xcd311b"></param><param
name="allowFullScreen" value="true"></param><param
name="allowscriptaccess" value="always"></param><embed
src="http://www.youtube.com/v/oABFXO9WV6w&#038;hl=en_US&#038;fs=1&#038;rel=0&#038;color1=0x5d1719&#038;color2=0xcd311b" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="320" height="265"></embed></object></p><div
class="su-linkbox" id="post-383-linkbox"><div
class="su-linkbox-label">Link to this post:</div><div
class="su-linkbox-field"><input
type="text" value="&lt;a href=&quot;http://cmorrell.com/web-development/more-php-modelling-383&quot;&gt;More PHP Modeling (w/ video demo)&lt;/a&gt;" onclick="javascript:this.select()" readonly="readonly" style="width: 100%;" /></div></div>]]></content:encoded> <wfw:commentRss>http://cmorrell.com/web-development/more-php-modelling-383/feed</wfw:commentRss> <slash:comments>3</slash:comments> <enclosure url="http://content.screencast.com/users/inxilpro/folders/Jing/media/97a6143f-377f-41ea-8e39-efa1b95a167e/00000031.mp4" length="7993182" type="video/mp4" /> <feedburner:origLink>http://cmorrell.com/web-development/more-php-modelling-383</feedburner:origLink></item> <item><title>Crazy idea…</title><link>http://feedproxy.google.com/~r/cmorrell/~3/1fHSr6PhWuU/crazy-idea%e2%80%a6-378</link> <comments>http://cmorrell.com/web-development/crazy-idea%e2%80%a6-378#comments</comments> <pubDate>Mon, 23 Nov 2009 21:35:38 +0000</pubDate> <dc:creator>Chris Morrell</dc:creator> <category><![CDATA[Web Development]]></category><guid isPermaLink="false">http://cmorrell.com/?p=378</guid> <description><![CDATA[I&#8217;ve been toying with the idea of using my cache as a data store for a project where the data doesn&#8217;t need to be updated very often.  Basically, I&#8217;d write out plain XHTML documents and then parse the data using XPath when needed.  But that&#8217;s a different story.  Once I decided to give my idea [...]]]></description> <content:encoded><![CDATA[<p>I&#8217;ve been toying with the idea of using my cache as a data store for a project where the data doesn&#8217;t need to be updated very often.  Basically, I&#8217;d write out plain XHTML documents and then parse the data using XPath when needed.  But that&#8217;s a different story.  Once I decided to give my idea a try, I started thinking about how to store authentication information.  The application doesn&#8217;t store any private information, so authentication is only needed to prove that you are authorized to edit the information.  So why not store the authentication information publicly as well (as an HTML comment at the top of the file)?  Here&#8217;s what I was thinking, in pseudo code:</p><pre>
identity = base64(encrypt_rijndael256([
	sha512_hmac(username, appUsernameSecret),
	sha512_hmac(password, appPasswordSecret)
], appSecret))
</pre><p>This would produce an base64 representation of an encrypted array of hashes.  Basically, the system would produce two hashes using HMAC and two separate secret keys (one for the username hash and one for the password hash).  It would store that data in a way that it could later retrieve it (in my case a serialized array) and then encrypt the whole thing with a third key (the base64 is just so it could easily be represented by an ASCII string).  That way there are multiple points of failure.  An attacker would have to know all three keys just to get at the hashes, but then that&#8217;s all they&#8217;d have.  They&#8217;d still need to brute force both the username and password separately.  It seems to me that this would be pretty darn secure.  Clearly not good enough for a bank, but certainly fine for a web app that would have very few negative consequences if it were broken into.</p><p>I would love feedback from someone who know&#8217;s what they&#8217;re talking about <img
src='http://d28lgmufr7w2pm.cloudfront.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />   Below is some working PHP code to illustrate my point:</p><p><span
id="more-378"></span></p><pre class="brush: php;">
class PublicAuth
{
    private $_secret;
    private $_usernameSecret;
    private $_passwordSecret;

    private $_td = null;
    private $_iv = null;

    public function __construct($secret, $usernameSecret, $passwordSecret)
    {
        $this-&gt;_secret = $secret;
        $this-&gt;_usernameSecret = $usernameSecret;
        $this-&gt;_passwordSecret = $passwordSecret;
    }

    public function generateIdentifier($username, $password, $algorithm = 'sha512')
    {
        return $this-&gt;_encrypt(serialize(array(
            hash_hmac($algorithm, $username, $this-&gt;_usernameSecret, true),
            hash_hmac($algorithm, $password, $this-&gt;_passwordSecret, true),
        )));
    }

    public function verifyIdentity($identifier, $username, $password, $algorithm = 'sha512')
    {
        $identifier = unserialize($this-&gt;_decrypt($identifier));
        return (hash_hmac($algorithm, $username, $this-&gt;_usernameSecret, true) == $identifier[0]
            &amp;amp;&amp;amp; hash_hmac($algorithm, $password, $this-&gt;_passwordSecret, true) == $identifier[1]);
    }

    private function _encrypt($string)
    {
        $this-&gt;_initMcrypt();
        return base64_encode(mcrypt_generic($this-&gt;_td, $string));
    }

    private function _decrypt($string)
    {
        $this-&gt;_initMcrypt();
        return mdecrypt_generic($this-&gt;_td, base64_decode($string));
    }

    private function _initMcrypt($algorithm = 'rijndael-256')
    {
        if (null == $this-&gt;_td || null == $this-&gt;_iv) {
            $this-&gt;_td = mcrypt_module_open($algorithm, '', 'ecb', '');
            $this-&gt;_iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($this-&gt;_td), MCRYPT_RAND);
        }

        mcrypt_generic_init($this-&gt;_td, $this-&gt;_secret, $this-&gt;_iv);
    }
}
</pre><p>And here&#8217;s some sample usage:</p><pre class="brush: php;">
$pa = new PublicAuth('a', 'b', 'c');
echo $pa-&gt;generateIdentifier('user', 'pass');
</pre><p>Which would print out:</p><pre>NLgAYjlGbmJA2Wdcgwntm4ixhHHCiZBA6TvgrVMgEOBEjZQJ0tHgAlw7931p2S6KRtfCkLjrsA2DBilcgBX/pPPXFgyAx3g0/CKMcjdU8DKn3/9M2aIZHOrdi/G68C0oxVe6pDlWvVwvofpJnu9RxMbFN49x1uVgBuHTjKagpD6y83fm+hX4G+CoPRcHM5PUq/nJ1iwtZipRtno8TllO6A==</pre><p>Then to verify the identity:</p><pre class="brush: php;">
$pa = new PublicAuth('a', 'b', 'c'); // Needs to be the same as when generated
var_export($pa-&gt;verifyIdentity($id, 'user', 'pass')); // $id contains the string above; returns TRUE
</pre><p>Thoughts?</p><div
class="su-linkbox" id="post-378-linkbox"><div
class="su-linkbox-label">Link to this post:</div><div
class="su-linkbox-field"><input
type="text" value="&lt;a href=&quot;http://cmorrell.com/web-development/crazy-idea%e2%80%a6-378&quot;&gt;Crazy idea…&lt;/a&gt;" onclick="javascript:this.select()" readonly="readonly" style="width: 100%;" /></div></div>]]></content:encoded> <wfw:commentRss>http://cmorrell.com/web-development/crazy-idea%e2%80%a6-378/feed</wfw:commentRss> <slash:comments>0</slash:comments> <feedburner:origLink>http://cmorrell.com/web-development/crazy-idea%e2%80%a6-378</feedburner:origLink></item> <item><title>Calculating the difference in days between two Zend_Date objects</title><link>http://feedproxy.google.com/~r/cmorrell/~3/sZanP-qd_i0/calculating-the-difference-in-days-between-two-zend_date-objects-373</link> <comments>http://cmorrell.com/web-development/calculating-the-difference-in-days-between-two-zend_date-objects-373#comments</comments> <pubDate>Wed, 18 Nov 2009 16:46:12 +0000</pubDate> <dc:creator>Chris Morrell</dc:creator> <category><![CDATA[Web Development]]></category> <category><![CDATA[Zend Framework]]></category> <category><![CDATA[zend]]></category> <category><![CDATA[zend_date]]></category><guid isPermaLink="false">http://cmorrell.com/?p=373</guid> <description><![CDATA[This just came up on #zftalk, and it appears that the information out there is either incomplete or incorrect, so I thought I&#8217;d just put out a simple solution.  Here&#8217;s a simple way to calculate the difference between two Zend_Date objects (in days):$jan1 = new Zend_Date('1.12.2009', Zend_Date::DATES);
echo &#34;\nJanuary first: &#34;, $jan1-&#62;toString();$christmas = new Zend_Date('25.12.2009', Zend_Date::DATES);
echo [...]]]></description> <content:encoded><![CDATA[<p>This just came up on <a
href="http://zftalk.com/">#zftalk</a>, and it appears that the information out there is either incomplete or incorrect, so I thought I&#8217;d just put out a simple solution.  Here&#8217;s a simple way to calculate the difference between two Zend_Date objects (in days):</p><pre class="brush: php;">
$jan1 = new Zend_Date('1.12.2009', Zend_Date::DATES);
echo &quot;\nJanuary first: &quot;, $jan1-&gt;toString();

$christmas = new Zend_Date('25.12.2009', Zend_Date::DATES);
echo &quot;\nChristmas is on: &quot;, $christmas-&gt;toString();

$diff = $christmas-&gt;sub($jan1);
echo &quot;\nNumber of days: &quot;, $diff / 60 / 60 / 24;
</pre><div
class="su-linkbox" id="post-373-linkbox"><div
class="su-linkbox-label">Link to this post:</div><div
class="su-linkbox-field"><input
type="text" value="&lt;a href=&quot;http://cmorrell.com/web-development/calculating-the-difference-in-days-between-two-zend_date-objects-373&quot;&gt;Calculating the difference in days between two Zend_Date objects&lt;/a&gt;" onclick="javascript:this.select()" readonly="readonly" style="width: 100%;" /></div></div>]]></content:encoded> <wfw:commentRss>http://cmorrell.com/web-development/calculating-the-difference-in-days-between-two-zend_date-objects-373/feed</wfw:commentRss> <slash:comments>1</slash:comments> <feedburner:origLink>http://cmorrell.com/web-development/calculating-the-difference-in-days-between-two-zend_date-objects-373</feedburner:origLink></item> <item><title>PHP Modeling (in Zend Framework)</title><link>http://feedproxy.google.com/~r/cmorrell/~3/co7kKOAyvHU/php-modeling-in-zend-framework-360</link> <comments>http://cmorrell.com/web-development/php-modeling-in-zend-framework-360#comments</comments> <pubDate>Thu, 05 Nov 2009 04:52:15 +0000</pubDate> <dc:creator>Chris Morrell</dc:creator> <category><![CDATA[Web Development]]></category> <category><![CDATA[Zend Framework]]></category> <category><![CDATA[design patterns]]></category> <category><![CDATA[modeling]]></category> <category><![CDATA[models]]></category> <category><![CDATA[PHP]]></category><guid isPermaLink="false">http://cmorrell.com/?p=360</guid> <description><![CDATA[I&#8217;ve been thinking a lot about Modeling in a MVC application, particularly in the Zend Framework.  Obviously each application is different, and any Model is going to be fairly unique to your application.  That&#8217;s why ZF doesn&#8217;t provide a base Model class.  That said, there are some design patterns that a lot [...]]]></description> <content:encoded><![CDATA[<p>I&#8217;ve been thinking a lot about Modeling in a MVC application, particularly in the <a
href="http://cmorrell.com/category/web-development/zf">Zend Framework</a>.  Obviously each application is different, and any Model is going to be fairly unique to your application.  That&#8217;s why ZF doesn&#8217;t provide a base <code>Model</code> class.  That said, there are some design patterns that a lot of people are using nowadays, and applications could use some base functionality to facilitate those patterns.</p><p>Zend Framework&#8217;s project lead, <a
href="http://weierophinney.net/matthew/">Matthew Weier O&#8217;Phinney</a>, has a lot of <a
href="http://www.slideshare.net/weierophinney/architecting-your-models">great thoughts about Modeling</a> that I&#8217;ve been trying to stick to.  In implementing those ideas, I&#8217;ve started thinking out some base classes to build my Models on top of.  Obviously these classes won&#8217;t work for everyone.  But they should work for a lot of &#8220;typical&#8221; web applications.</p><p><span
id="more-360"></span></p><p>That said, here are some notes that I&#8217;ve been putting together.  I&#8217;m posting them now (very early) in hopes that I get some feedback.</p><h3>The 6 Classes</h3><h4>Galahad_Model</h4><p>This is the base class that all my others extend.  It is there mostly to provide some helper functionality that all my other classes use (mostly for guessing class names based on a naming convention—more on that later).</p><h4>Galahad_Model_Entity</h4><p>The Entity is the base for all <em>things</em>.  For example, a <code>User</code> would extend the Entity class.  The <code>Galahad_Model_Entity</code> class has some basic methods for getting other objects.  I haven&#8217;t thought this entirely through yet, but an example would be the Entity&#8217;s &#8220;Parent&#8221; Service (see below) or maybe a <code>Form</code> associated with that model or something similar.</p><h4>Galahad_Model_Collection</h4><p>The Collection class is just a wrapper for an array of Entities.  I like this because it allows for type hinting/etc.  This is pretty much a generic wrapper for an array that implements Iterator and Countable.  I can&#8217;t think of much more that it needs.</p><h4>Galahad_Model_Service</h4><p>This probably doesn&#8217;t belong as part of the &#8220;Model&#8221;—I need to think that out.  The Service Layer is where your application logic happens that&#8217;s not strictly part of a particular Model (for example, an interaction between two Models).  An example might be authentication.  The service layer will often map pretty closely to a public API (although obviously there will be things that your application can do that shouldn&#8217;t be exposed to the public).</p><h4>Galahad_Model_DataMapper</h4><p>The <code>DataMapper</code> maps your <code>Entity</code> to the appropriate <code>DAO</code> (see below) and makes sure the <code>DAO</code> gets the data it&#8217;s expecting.  The way I think about this is that the Data Mapper expects an <code>Entity</code> as its input, but passes an array to the DAO.</p><h4>Galahad_Model_Dao</h4><p>The <em>Data Access Object</em> (DAO) is what takes the actual data in your entity and persists it.  The most common DAO is going to be a database, but a web service could be a DAO as could a filesystem or any other method of persisting data.  The DAO is going to have similar methods as the DataMapper, but it expects <em>just the data</em>—nothing else (that&#8217;s why you need the DataMapper to fetch and process the data in your Entity).  An easy way to show this is in code:</p><pre class="brush: php;">
class Default_Model_DataMapper_User
  extends Galahad_Model_DataMapper
{
    public function save(User $user)
    {
        $dao = $this-&gt;_getDao();
        $dao-&gt;save(array(
            'name' =&gt; $user-&gt;getName(),
            'email' =&gt; $user-&gt;getEmail(),
            'date_modified' =&gt; time(),
        ));
    }
}
class Default_Model_Dao_DbTable_User
  extends Zend_Db_Table_Abstract
  implements Galahad_Model_Dao_Interface
{
    protected $_name = 'user';

    public function save(Array $data)
    {
        $this-&gt;insert($data);
    }
}
</pre><h3>Notes</h3><p>I&#8217;m going to reiterate one more time that this is a very early concept and that I&#8217;m looking for feedback.  It could be that I&#8217;m thinking about things completely backwards and it all needs to be thrown away.  At this point I certainly don&#8217;t recommend people running too far with these ideas until they&#8217;ve been discussed/thought out a little more.</p><p><strong>About the Galahad_Model base class</strong>—since all the different pieces of your model will likely follow the same naming conventions, the <code>Galahad_Model</code> class provides some helper functionality to guess the name of classes.  For example, inside of <code>Default_Model_DataMapper_User</code> you&#8217;ll probably need to get an instance of <code>Default_Model_Dao_DbTable_User</code>.  So <code>Galahad_Model_DataMapper</code> provides a nice <code>getDao()</code> method to do this for you.  If you&#8217;ve set the DAO, it uses that, but if you didn&#8217;t, it assumes you want a DbTable, guesses the name for you based on the DataMapper&#8217;s class name (so that if you choose <code>My_Model_DataMapper_Person</code> it&#8217;ll know to return a <code>My_Model_Dao_DbTable_Person</code>) and instantiates it for you.</p><h3>Thoughts?</h3><p>What do you think?  Does this make any sense?  Or am I trying to make a simple thing more complicated than it needs to be?  I think a lot of this would make more sense in code, so maybe I&#8217;ll try to get what I have started cleaned up a little and attach it to this post.  In the meantime, I&#8217;d love feedback (in the comments below, or to <a
href="http://www.twitter.com/inxilpro">@inxilpro</a>.</p><div
class="su-linkbox" id="post-360-linkbox"><div
class="su-linkbox-label">Link to this post:</div><div
class="su-linkbox-field"><input
type="text" value="&lt;a href=&quot;http://cmorrell.com/web-development/php-modeling-in-zend-framework-360&quot;&gt;PHP Modeling (in Zend Framework)&lt;/a&gt;" onclick="javascript:this.select()" readonly="readonly" style="width: 100%;" /></div></div>]]></content:encoded> <wfw:commentRss>http://cmorrell.com/web-development/php-modeling-in-zend-framework-360/feed</wfw:commentRss> <slash:comments>7</slash:comments> <feedburner:origLink>http://cmorrell.com/web-development/php-modeling-in-zend-framework-360</feedburner:origLink></item> <item><title>Flickr/Tweetie Bridge (with flic.kr short URLs!)</title><link>http://feedproxy.google.com/~r/cmorrell/~3/u4Fbqg2E3RM/flickrtweetie-bridge-with-flickr-short-urls-355</link> <comments>http://cmorrell.com/open-source/flickrtweetie-bridge-with-flickr-short-urls-355#comments</comments> <pubDate>Sun, 18 Oct 2009 14:50:20 +0000</pubDate> <dc:creator>Chris Morrell</dc:creator> <category><![CDATA[Open Source]]></category><guid isPermaLink="false">http://cmorrell.com/?p=355</guid> <description><![CDATA[A couple of days ago I saw Bart Mroz testing out a new service that lets you post your images directly to Flickr via Tweetie 2.  It&#8217;s a great idea, but it seems like there&#8217;s an unnecessary 3rd party in there.  Flickr already has the http://flic.kr/ short URL, so it seems like you should be [...]]]></description> <content:encoded><![CDATA[<p>A couple of days ago I saw <a
href="http://twitter.com/bartmroz/status/4918748640" target="_blank">Bart Mroz testing out a new service</a> that lets you post your images directly to Flickr via <a
href="http://www.atebits.com/tweetie-iphone/" target="_blank">Tweetie 2</a>.  It&#8217;s a great idea, but it seems like there&#8217;s an unnecessary 3rd party in there.  Flickr already has the http://flic.kr/ short URL, so it seems like you should be able to post your images to Flickr and receive the official short URL for that image.</p><p>Well, that&#8217;s exactly what my Flickr/Tweetie Bridge does.  Just set it up, plug the URL into Tweetie, and you can start uploading/shortening with Flickr.  It hasn&#8217;t been very heavily tested, but it&#8217;s working fine for me.  <a
href="http://cmorrell.com/open-source/galahad-flickrtweetie-bridge">Check out the 0.1 release</a>.  It&#8217;s PHP5-only, and released under GPL.</p><p>Let me know if you come across any bugs, or have feature requests.</p><div
class="su-linkbox" id="post-355-linkbox"><div
class="su-linkbox-label">Link to this post:</div><div
class="su-linkbox-field"><input
type="text" value="&lt;a href=&quot;http://cmorrell.com/open-source/flickrtweetie-bridge-with-flickr-short-urls-355&quot;&gt;Flickr/Tweetie Bridge (with flic.kr short URLs!)&lt;/a&gt;" onclick="javascript:this.select()" readonly="readonly" style="width: 100%;" /></div></div>]]></content:encoded> <wfw:commentRss>http://cmorrell.com/open-source/flickrtweetie-bridge-with-flickr-short-urls-355/feed</wfw:commentRss> <slash:comments>13</slash:comments> <feedburner:origLink>http://cmorrell.com/open-source/flickrtweetie-bridge-with-flickr-short-urls-355</feedburner:origLink></item> <item><title>Better short URLs</title><link>http://feedproxy.google.com/~r/cmorrell/~3/DCWlmEUCpF4/better-short-urls-349</link> <comments>http://cmorrell.com/web-development/better-short-urls-349#comments</comments> <pubDate>Tue, 08 Sep 2009 17:21:31 +0000</pubDate> <dc:creator>Chris Morrell</dc:creator> <category><![CDATA[Web Development]]></category><guid isPermaLink="false">http://cmorrell.com/?p=349</guid> <description><![CDATA[Recently a bunch of people have been proposing ways to produce short URLs without relying on 3rd parties (tr.im nearly shutting down definitely hit home the need for this discussion).  One option was the rev=&#8221;canonical&#8221; attribute.  Others have been various rel values.  I like what PHP.net has done—just combine them all and see which one [...]]]></description> <content:encoded><![CDATA[<p>Recently a bunch of people have been proposing ways to produce short URLs without relying on 3rd parties (<a
href="http://tr.im/" target="_blank">tr.im</a> nearly shutting down definitely hit home the need for this discussion).  One option was the <a
href="http://shiflett.org/blog/2009/apr/save-the-internet-with-rev-canonical" target="_blank">rev=&#8221;canonical&#8221;</a> attribute.  Others have been various <code>rel</code> values.  I like what <a
href="http://www.php.net" target="_blank">PHP.net</a> has done—just combine them all and see which one wins out:</p><p><code>&lt;link rev="canonical" rel="self alternate shorter shorturl shortlink" href="..." /&gt;</code></p><p>I haven&#8217;t yet implemented my own short URLs, but when I do I think that the way I&#8217;ll go.</p><div
class="su-linkbox" id="post-349-linkbox"><div
class="su-linkbox-label">Link to this post:</div><div
class="su-linkbox-field"><input
type="text" value="&lt;a href=&quot;http://cmorrell.com/web-development/better-short-urls-349&quot;&gt;Better short URLs&lt;/a&gt;" onclick="javascript:this.select()" readonly="readonly" style="width: 100%;" /></div></div>]]></content:encoded> <wfw:commentRss>http://cmorrell.com/web-development/better-short-urls-349/feed</wfw:commentRss> <slash:comments>2</slash:comments> <feedburner:origLink>http://cmorrell.com/web-development/better-short-urls-349</feedburner:origLink></item> </channel> </rss><!-- This site's performance optimized by W3 Total Cache. Dramatically improve the speed and reliability of your blog!

Learn more about our WordPress Plugins: http://www.w3-edge.com/wordpress-plugins/

Minified using disk
Page Caching using disk
Database Caching 20/28 queries in 0.030 seconds using disk
Content Delivery Network via Amazon Web Services: CloudFront: d28lgmufr7w2pm.cloudfront.net

Served from: cmorrell.com @ 2010-03-17 12:04:38 -->
