<?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>Laughing Meme &#187; Code</title>
	<atom:link href="http://laughingmeme.org/tag/code/feed/" rel="self" type="application/rss+xml" />
	<link>http://laughingmeme.org</link>
	<description>Just another WordPress weblog</description>
	<lastBuildDate>Tue, 09 Mar 2010 01:35:11 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>photosthatmatter &amp; FlickrApp</title>
		<link>http://laughingmeme.org/2009/07/22/photosthatmatter-flickrapp/</link>
		<comments>http://laughingmeme.org/2009/07/22/photosthatmatter-flickrapp/#comments</comments>
		<pubDate>Wed, 22 Jul 2009 20:27:37 +0000</pubDate>
		<dc:creator>Kellan</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[appengine]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[flickr]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://laughingmeme.org/?p=4254</guid>
		<description><![CDATA[

Aaron&#8217;s FlickrApp is a brain dead easy to use subclass of webapp.RequestHandler that turns Flickr into an single-sign on service for Google AppEngine.

As a bonus, you get a valid Flickr auth_token for every signed in user.   This makes writing Flickr API apps about the simplest thing ever.

Case in point, I wrote the largely [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://photosthatmatter.appspot.com"><img src="http://farm3.static.flickr.com/2667/3747384500_dacdd8c86e.jpg" width="500" height="251" alt="Picture 53" border="0" /></a></p>

<p>Aaron&#8217;s <a href="http://github.com/straup/gae-flickrapp/tree/master">FlickrApp</a> is a brain dead easy to use subclass of <a href="http://code.google.com/appengine/docs/python/gettingstarted/handlingforms.html">webapp.RequestHandler</a> that turns <a href="http://flickr.com">Flickr</a> into an single-sign on service for <a href="http://code.google.com/appengine/">Google AppEngine</a>.</p>

<p>As a bonus, you get a valid Flickr auth_token for every signed in user.   This makes writing <a href="http://flickr.com/services/api">Flickr API apps</a> about the simplest thing ever.</p>

<p>Case in point, I wrote the largely mis-named <a href="http://photosthatmatter.appspot.com">photosthatmatter</a> app last night in slightly less then 20 minutes, while waiting for dinner to simmer.   Shows the most interesting photo from each for your contact in a given time period.  Great for catching up on things you missed  as they flowed by the first time.</p>

<p>YMMV, but it works for me.</p>
]]></content:encoded>
			<wfw:commentRss>http://laughingmeme.org/2009/07/22/photosthatmatter-flickrapp/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>rev=canonical: url shortening that doesn&#8217;t hurt the internet</title>
		<link>http://laughingmeme.org/2009/04/06/revcanonical-url-shortening-that-doesnt-hurt-the-internet/</link>
		<comments>http://laughingmeme.org/2009/04/06/revcanonical-url-shortening-that-doesnt-hurt-the-internet/#comments</comments>
		<pubDate>Mon, 06 Apr 2009 16:54:39 +0000</pubDate>
		<dc:creator>Kellan</dc:creator>
				<category><![CDATA[Aside]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[cool uris don't change]]></category>
		<category><![CDATA[gae]]></category>
		<category><![CDATA[history]]></category>
		<category><![CDATA[infrastructure]]></category>
		<category><![CDATA[revcanonical]]></category>
		<category><![CDATA[urls]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://laughingmeme.org/?p=4219</guid>
		<description><![CDATA[A URL shortener that implements rev=&#8221;canonical&#8221;.
]]></description>
			<content:encoded><![CDATA[<p>A URL shortener that implements rev=&#8221;canonical&#8221;.</p>
]]></content:encoded>
			<wfw:commentRss>http://laughingmeme.org/2009/04/06/revcanonical-url-shortening-that-doesnt-hurt-the-internet/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>New Amazon AWS Signature Version 2 is  &#8220;OAuth-compatible&#8221;</title>
		<link>http://laughingmeme.org/2008/12/30/new-amazon-aws-signature-version-2-is-oauth-compatible/</link>
		<comments>http://laughingmeme.org/2008/12/30/new-amazon-aws-signature-version-2-is-oauth-compatible/#comments</comments>
		<pubDate>Tue, 30 Dec 2008 20:10:22 +0000</pubDate>
		<dc:creator>Kellan</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[amazon]]></category>
		<category><![CDATA[aws]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[crypo]]></category>
		<category><![CDATA[dork]]></category>
		<category><![CDATA[oauth]]></category>
		<category><![CDATA[openweb]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[simpledb]]></category>

		<guid isPermaLink="false">http://laughingmeme.org/?p=4138</guid>
		<description><![CDATA[

Spent a couple hours last night writing the core of a stripped down, PHP4 compatible API library for Amazon SimpleDB (in the style of my (http://laughingmeme.org/2008/12/11/my-flickr-api-library-for-php/) library.  Just not a fan of abstraction for its own sake).  In the process I discovered that Amazon had revved the version on their &#8220;Signature Method&#8221;.  [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.flickr.com/photos/briannegus/1397852047/" title="Enigma rotors by Brian Negus, on Flickr"><img src="http://farm2.static.flickr.com/1200/1397852047_3128ce06df.jpg" width="500" height="375" alt="Enigma rotors" /></a></p>

<p>Spent a couple hours last night writing the core of a stripped down, PHP4 compatible API library for <a href="http://aws.amazon.com/simpledb/">Amazon SimpleDB</a> (in the style of my <a href="http://laughingmeme.org/2008/12/11/my-flickr-api-library-for-php/">flickr simple</a> library.  Just not a fan of abstraction for its own sake).  In the process I discovered that Amazon had <a href="http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1928">revved the version on their &#8220;Signature Method&#8221;</a>.  Which is good news as SignatureVersion 1 contains a classic crypto-blunder in its design, namely it encourages collisions.  (<a href="http://www.daemonology.net/blog/2008-12-18-AWS-signature-version-1-is-insecure.html">more details</a>, also <a href="http://www.phreedom.org/research/rogue-ca/">why you care about collisions</a>)  To date the solution was use SSL, and wait patiently, very patiently.  So yay for Amazon fixing this!  And in fairness, first couple of drafts of the OAuth spec contained a similar issue, though it got ironed out quickly.  Yay for many eyes and the open web.</p>

<h3>&#8220;OAuth-compatible&#8221; signing</h3>

<p>Great things are more secure, good news and all, but that isn&#8217;t what caught my eye.  This block of text did:
<blockquote><em>
Here is what&#8217;s different about forming the string to sign for signature version 2:</p>

<ul>
<li>You include additional components of the request in the string to sign</li>
<li>You include the query string control parameters (the equals signs and ampersands) in the string to sign</li>
<li>You sort the query string parameters using byte ordering</li>
<li>You URL encode the query string parameters and their values before signing the request
</em></blockquote></li>
</ul>

<p>You really have to be an <a href="http://oauth.net/core/1.0/#anchor1">OAuth-dork</a> to find anything special with that paragraph, but if you were, you&#8217;d notice that those 4 bullets are an incredibly succinct description of generating an OAuth signature. (in fact a more succinct description then appears anywhere in the <a href="http://oauth.net/core/1.0/">OAuth documentation</a></p>

<p>Which meant that my SimpleDB library can reuse most of the logic from my OAuth library to do the trickiest part of the API call, namely the signing.  (Additionally it means that security reviews of both protocols support each other)</p>

<p>So my AWS signing method is a approximately a dozen characters different then my OAuth method and as straightforward as:</p>

<pre><code>    .....

    $signature = aws_request_signature(AWS_SECRET_KEY, $http_method, AWS_SIMPLEDB_SERVICEURL, $parameters);
    $parameters['Signature'] = $signature;

    $encoded_params = array();

    foreach ($parameters as $k =&gt; $v){
        $encoded_params[] = oauth_urlencodeRFC3986($k).'='.oauth_urlencodeRFC3986($v);
    }

    $request_url = AWS_SIMPLEDB_SERVICEURL . '?' . implode('&amp;', $encoded_params);

    .....

    function aws_request_signature($key, $http_method, $service_url, $parameters) {
        $base_string = aws_base_string($http_method, $service_url, $parameters);
        return base64_encode(hash_hmac('sha1', $base_string, $key, true));
    }

    function aws_base_string($http_method, $service_url, $parameters) {
        $parsed = parse_url($service_url);

        $host = strtolower($parsed['host']);
        $path = $parsed['path'] ? $parsed['path'] : '/';
        $data = array(
            strtoupper($http_method),
            $host,
            $path,
            oauth_normalized_request_params($parameters)
        );

        $base_string = join("\n", $data);
        return $base_string;
    }
</code></pre>

<p>(this uses my personal OAuth library, but your library should have similar methods)</p>

<p>Sure made my jobs of implementing a library easier.  If you&#8217;re going to invent a new crypto protocol, please consider doing like Amazon, and re-using the basic building blocks. (which also happen to be best practices)</p>
]]></content:encoded>
			<wfw:commentRss>http://laughingmeme.org/2008/12/30/new-amazon-aws-signature-version-2-is-oauth-compatible/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>My Flickr API library for PHP</title>
		<link>http://laughingmeme.org/2008/12/11/my-flickr-api-library-for-php/</link>
		<comments>http://laughingmeme.org/2008/12/11/my-flickr-api-library-for-php/#comments</comments>
		<pubDate>Thu, 11 Dec 2008 23:05:59 +0000</pubDate>
		<dc:creator>Kellan</dc:creator>
				<category><![CDATA[Aside]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[appengine]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[flickr]]></category>
		<category><![CDATA[library]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://laughingmeme.org/?p=4116</guid>
		<description><![CDATA[I&#8217;m a big believer in Norvig&#8217;s &#8220;Code is liability&#8221; maxim.  Which is how I justify my ugly, but functional Flickr API implementation, in 40 lines of PHP (not the most expressive of languages), which I wrote in about 15 minutes one evening, and I now use for all of my Flickr side projects.  [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m a big believer in Norvig&#8217;s &#8220;Code is liability&#8221; maxim.  Which is how I justify my ugly, but functional Flickr API implementation, in 40 lines of PHP (not the most expressive of languages), which I wrote in about 15 minutes one evening, and I now use for all of my Flickr side projects.  And all apropos of digging through other folks Flickr API impls, trying to get them working on GAE.   Thankfully <a href="http://blech.vox.com/library/post/flickrapi-google-app-engine.html">blech is already there</a>. </p>
]]></content:encoded>
			<wfw:commentRss>http://laughingmeme.org/2008/12/11/my-flickr-api-library-for-php/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Wordpress: Resetting your password the &#8220;hard&#8221; way.</title>
		<link>http://laughingmeme.org/2008/11/19/wordpress-resetting-your-password-the-hard-way/</link>
		<comments>http://laughingmeme.org/2008/11/19/wordpress-resetting-your-password-the-hard-way/#comments</comments>
		<pubDate>Wed, 19 Nov 2008 23:09:53 +0000</pubDate>
		<dc:creator>Kellan</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[hashing]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://laughingmeme.org/?p=4084</guid>
		<description><![CDATA[

If you&#8217;ve forgetten your password to your Wordpress install it has a nifty email-a-one-time-key-to-retrieve-password flow built in.  Which for some reason never works on my box. (probably has to do with how I have Postfix setup doing 2ndary MXing)  In the olden days the solution to this problem was to connect your database [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.flickr.com/photos/sarabbit/1937032483/" title="more yarnings by Sarabbit, on Flickr"><img src="http://farm3.static.flickr.com/2288/1937032483_41c5f3965b.jpg" width="500" height="375" alt="more yarnings" /></a></p>

<p>If you&#8217;ve forgetten your password to your <a href="http://wordpress.org">Wordpress</a> install it has a nifty email-a-one-time-key-to-retrieve-password flow built in.  Which for some reason never works on my box. (probably has to do with how I have Postfix setup doing 2ndary MXing)  In the olden days the solution to this problem was to connect your database and <code>UPDATE</code> the <code>user_pass</code> field with an <code>MD5</code> of your desired new password.  </p>

<p>But now we&#8217;re living in the future, so things are more complicated.  I still connect to my database, and manually <code>UPDATE users set user_pass=$hashed where ID=1</code>, but now I need this <a href="http://laughingmeme.org/code/wp_hasher.php.txt">handy script to generate the hashed password</a> for me.  Hopefully the 3 other people in the world this is useful for will find this blog post.</p>

<p>(And now I might start blogging again)</p>

<p>Photo from <a href="http://www.flickr.com/photos/sarabbit">sarabbit</a></p>
]]></content:encoded>
			<wfw:commentRss>http://laughingmeme.org/2008/11/19/wordpress-resetting-your-password-the-hard-way/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>bceval: arbitrary precision math for PHP without extensions</title>
		<link>http://laughingmeme.org/2008/03/25/bceval-arbitrary-precision-math-for-php-without-extensions/</link>
		<comments>http://laughingmeme.org/2008/03/25/bceval-arbitrary-precision-math-for-php-without-extensions/#comments</comments>
		<pubDate>Tue, 25 Mar 2008 20:50:38 +0000</pubDate>
		<dc:creator>Kellan</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[big numbers]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[math]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[shell]]></category>
		<category><![CDATA[unix]]></category>

		<guid isPermaLink="false">http://laughingmeme.org/2008/03/25/bceval-arbitrary-precision-math-for-php-without-extensions/</guid>
		<description><![CDATA[I needed arbitrary precision math in PHP, and wasn&#8217;t willing to rebuild PHP to add the bcmath extensions.  All hail backticks.

function bceval($expr) {
  return trim(`echo "$expr" &#124; bc`);
}


Used like so

$end = bceval("$start + $batchsize - 1");


Wet cat territory.
]]></description>
			<content:encoded><![CDATA[<p>I needed arbitrary precision math in PHP, and wasn&#8217;t willing to rebuild PHP to add the <a href="http://www.php.net/bc">bcmath</a> extensions.  All hail backticks.</p>

<pre><code>function bceval($expr) {
  return trim(`echo "$expr" | bc`);
}
</code></pre>

<p>Used like so</p>

<pre><code>$end = bceval("$start + $batchsize - 1");
</code></pre>

<p><a href="http://simonwillison.net/2003/Jun/24/philosophy/">Wet cat</a> territory.</p>
]]></content:encoded>
			<wfw:commentRss>http://laughingmeme.org/2008/03/25/bceval-arbitrary-precision-math-for-php-without-extensions/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Reading a file backwards in PHP</title>
		<link>http://laughingmeme.org/2008/02/28/reading-a-file-backwards-in-php/</link>
		<comments>http://laughingmeme.org/2008/02/28/reading-a-file-backwards-in-php/#comments</comments>
		<pubDate>Thu, 28 Feb 2008 16:58:51 +0000</pubDate>
		<dc:creator>Kellan</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[unix]]></category>

		<guid isPermaLink="false">http://laughingmeme.org/2008/02/28/reading-a-file-backwards-in-php/</guid>
		<description><![CDATA[This morning I needed to read from a file line by line from the bottom.  In PHP.  Perl, of course, has a module to do this.  A quick view source decided that I didn&#8217;t want to get into file seeks before breakfast.  Very happy with my solution:

$file = popen("tac $filename",'r');

while ($line [...]]]></description>
			<content:encoded><![CDATA[<p>This morning I needed to read from a file line by line from the bottom.  In PHP.  Perl, of course, <a href="http://search.cpan.org/dist/File-ReadBackwards/ReadBackwards.pm">has a module to do this</a>.  A quick view source decided that I didn&#8217;t want to get into file seeks before breakfast.  Very happy with my solution:</p>

<pre><code>$file = popen("tac $filename",'r');

while ($line = fgets($file)) {
  echo $line;
}
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://laughingmeme.org/2008/02/28/reading-a-file-backwards-in-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Sharing from within Google Reader</title>
		<link>http://laughingmeme.org/2008/01/04/sharing-from-within-google-reader/</link>
		<comments>http://laughingmeme.org/2008/01/04/sharing-from-within-google-reader/#comments</comments>
		<pubDate>Fri, 04 Jan 2008 11:06:00 +0000</pubDate>
		<dc:creator>Kellan</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[del.icio.us]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[google reader]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[sharing]]></category>
		<category><![CDATA[social]]></category>

		<guid isPermaLink="false">http://laughingmeme.org/2008/01/04/sharing-from-within-google-reader/</guid>
		<description><![CDATA[Collapsing the GTalk buddy list, and Reader sharing list was a serious blunder, and one that could use a bit more ink spilled about it.  But one click sharing is one of my favorite Reader features.

GData Won&#8217;t Save You

Except there is a bit of a problem.  I don&#8217;t really want to share with [...]]]></description>
			<content:encoded><![CDATA[<p>Collapsing the GTalk buddy list, and Reader sharing list was a serious blunder, and one that could use a bit more ink spilled about it.  But one click sharing is one of my favorite Reader features.</p>

<h3>GData Won&#8217;t Save You</h3>

<p>Except there is a bit of a problem.  I don&#8217;t really want to share with other Google Reader users, I&#8217;m not even sure I&#8217;m destined to be a long time Reader user.  I want to share links the way I&#8217;m already doing it, through del.icio.us.</p>

<p>No problem, Reader has an Atom feed of shared items.  A really good feed, with the source info maintained, well formed, nicely done.  Simplest thing in the world to parse the feed, and write the entries back to del.icio.us.  And I can tag any post in Reader, which is perfect, easy Ajaxy sharing into del.icio.us with a few minutes work.</p>

<p>Except for reasons I can&#8217;t fathom Reader isn&#8217;t including my tags in the Shared Items feed.  Which all of a sudden makes my data feel a bit more locked up and trapped then I&#8217;d really like.</p>

<h3>For Our Sins</h3>

<p>Casting around a bit for a solution, I noticed the &#8220;Email&#8221; button, which allows me to send a link via email, along with a short note, and so <a href="http://laughingmeme.org/code/email_to_del.pl.txt">&#8220;Email to del.icio.us&#8221;</a> was born.</p>

<p>Super quick and dirty Perl script that:</p>

<ol>
<li>Parse the Google Reader HTML email for the relevant URL (no semantic markup, alas)</li>
<li>Pull the del.icio.us link description from the subject</li>
<li>Look for a line beginning &#8220;tags: &#8221; followed by a space separated list of tags.</li>
<li>Look for a line beginning &#8220;note: &#8221; for the extended description.</li>
</ol>

<p>Add the following rule to /etc/aliases file, and away you go.</p>

<p><code>to_del:          | /home/you/email_to_del.pl</code></p>

<p>Takes 10-15 seconds vs 1 second to share, but much more flexible.</p>

<p>And Perl is still unbeatable when it comes to these kind of scripts.</p>
]]></content:encoded>
			<wfw:commentRss>http://laughingmeme.org/2008/01/04/sharing-from-within-google-reader/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Patch for Net::Netflix 0.3</title>
		<link>http://laughingmeme.org/2007/11/26/patch-for-netnetflix-03/</link>
		<comments>http://laughingmeme.org/2007/11/26/patch-for-netnetflix-03/#comments</comments>
		<pubDate>Tue, 27 Nov 2007 05:53:15 +0000</pubDate>
		<dc:creator>Kellan</dc:creator>
				<category><![CDATA[Aside]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[netflix]]></category>
		<category><![CDATA[perl]]></category>

		<guid isPermaLink="false">http://laughingmeme.org/2007/11/26/patch-for-netnetflix-03/</guid>
		<description><![CDATA[Fixes backing up ratings, and adds a getQueue() method.
]]></description>
			<content:encoded><![CDATA[<p>Fixes backing up ratings, and adds a getQueue() method.</p>
]]></content:encoded>
			<wfw:commentRss>http://laughingmeme.org/2007/11/26/patch-for-netnetflix-03/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How to calculate a Base64 encoded HMAC-SHA1 in PHP for OAuth</title>
		<link>http://laughingmeme.org/2007/11/08/how-to-calculate-a-base64-encoded-hmac-sha1-in-php-for-oauth/</link>
		<comments>http://laughingmeme.org/2007/11/08/how-to-calculate-a-base64-encoded-hmac-sha1-in-php-for-oauth/#comments</comments>
		<pubDate>Thu, 08 Nov 2007 19:51:53 +0000</pubDate>
		<dc:creator>Kellan</dc:creator>
				<category><![CDATA[Aside]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[hashing]]></category>
		<category><![CDATA[hmac-sha1]]></category>
		<category><![CDATA[oauth]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[security]]></category>

		<guid isPermaLink="false">http://laughingmeme.org/2007/11/08/how-to-calculate-a-base64-encoded-hmac-sha1-in-php-for-oauth/</guid>
		<description><![CDATA[HMAC-SHA1 is the suggested default signing algorithm for OAuth 1.0 Core.  This is a code snippet showing how to calculate a valid OAuth HMAC-SHA1 signature using PHP4 without any PEAR dependencies.
]]></description>
			<content:encoded><![CDATA[<p>HMAC-SHA1 is the suggested default signing algorithm for OAuth 1.0 Core.  This is a code snippet showing how to calculate a valid OAuth HMAC-SHA1 signature using PHP4 without any PEAR dependencies.</p>
]]></content:encoded>
			<wfw:commentRss>http://laughingmeme.org/2007/11/08/how-to-calculate-a-base64-encoded-hmac-sha1-in-php-for-oauth/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>OAuth in PHP (for Twitter)</title>
		<link>http://laughingmeme.org/2007/10/16/oauth-in-php-for-twitter/</link>
		<comments>http://laughingmeme.org/2007/10/16/oauth-in-php-for-twitter/#comments</comments>
		<pubDate>Tue, 16 Oct 2007 07:15:10 +0000</pubDate>
		<dc:creator>Kellan</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[oauth]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://laughingmeme.org/2007/10/16/oauth-in-php-for-twitter/</guid>
		<description><![CDATA[Mike released HTTP_Request_OAuth today, so I spent a little while this evening coding up Service_Twitter as helper class for making OAuth authorized requests against the Twitter API.

Both are early enough in the dev cycle to be called proof of concepts.

Mostly I wrote it because I had always envisioned there being wrapper libraries around the low [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://mike.teczno.com/">Mike</a> released <a href="http://teczno.com/HTTP_Request_Oauth.phps"><code>HTTP_Request_OAuth</code></a> today, so I spent a little while this evening coding up <a href="http://laughingmeme.org/code/Service_Twitter.php.txt"><code>Service_Twitter</code></a> as helper class for making OAuth authorized requests against the Twitter API.</p>

<p>Both are early enough in the dev cycle to be called proof of concepts.</p>

<p>Mostly I wrote it because I had always envisioned there being wrapper libraries around the low level OAuth implementations that wrapped the calls, and constants, and as Mike graciously went out and wrote a low level library I felt compelled to write a wrapper.</p>

<p>Also <a href="http://laughingmeme.org/code/twittclient.php.txt">twittclient</a>, an interactive client for getting an authed access token, essential to bootstrapping development.</p>

<p>And nota bene, HRO currently only supports the MD5 signing algorithm, which is undefined in the core spec, and subject to change.  (Just in case you didn&#8217;t believe me about the early state of things.)</p>

<h3>update 2008/4/18</h3>

<p>This code no longer works because Twitter has taken down their (slightly non-compliant) OAuth endpoint.  When they add OAuth support back in, I&#8217;ll link to it.</p>
]]></content:encoded>
			<wfw:commentRss>http://laughingmeme.org/2007/10/16/oauth-in-php-for-twitter/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Coding a Twitter killbot</title>
		<link>http://laughingmeme.org/2007/03/09/coding-a-twitter-killbot/</link>
		<comments>http://laughingmeme.org/2007/03/09/coding-a-twitter-killbot/#comments</comments>
		<pubDate>Sat, 10 Mar 2007 02:44:14 +0000</pubDate>
		<dc:creator>Kellan</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[jabber]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[twitter]]></category>
		<category><![CDATA[xmpp]]></category>

		<guid isPermaLink="false">http://laughingmeme.org/2007/03/09/coding-a-twitter-killbot/</guid>
		<description><![CDATA[

j = Jabber::Simple.new(jid, pass)

j.received_messages.each do &#124;mesg&#124;
    if mesg.body.match(/austin&#124;sxsw/i)
        sender = mesg.elements['//screen_name'].text
        j.deliver('twitter@twitter.com', "leave #{sender}")
    end
end


Though truth be told, it isn&#8217;t running.
]]></description>
			<content:encoded><![CDATA[<p><a href="http://twitter.com/kellan/statuses/6098611"><img src="http://laughingmeme.org/img/killbot.png" border="0"></a></p>

<pre><code>j = Jabber::Simple.new(jid, pass)

j.received_messages.each do |mesg|
    if mesg.body.match(/austin|sxsw/i)
        sender = mesg.elements['//screen_name'].text
        j.deliver('twitter@twitter.com', "leave #{sender}")
    end
end
</code></pre>

<p>Though truth be told, it isn&#8217;t running.</p>
]]></content:encoded>
			<wfw:commentRss>http://laughingmeme.org/2007/03/09/coding-a-twitter-killbot/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Looking at PHP5&#8217;s DateTime and DateTimeZone</title>
		<link>http://laughingmeme.org/2007/02/27/looking-at-php5s-datetime-and-datetimezone/</link>
		<comments>http://laughingmeme.org/2007/02/27/looking-at-php5s-datetime-and-datetimezone/#comments</comments>
		<pubDate>Wed, 28 Feb 2007 00:47:09 +0000</pubDate>
		<dc:creator>Kellan</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[calendaring]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[datetime]]></category>
		<category><![CDATA[Longer]]></category>
		<category><![CDATA[olson]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[php5]]></category>
		<category><![CDATA[timezone]]></category>

		<guid isPermaLink="false">http://laughingmeme.org/2007/02/27/looking-at-php5s-datetime-and-datetimezone/</guid>
		<description><![CDATA[Looking over the PHP5.2 changelog I noticed that somewhere along the way PHP5 seems to have picked up a provocatively named pair of classes, DateTime and DateTimeZone.  

There is something fundamentally brash, brazen even, to releasing a class named DateTime.  As a calendar geek I imagine upon seeing &#8220;new DateTime()&#8221; I feel something [...]]]></description>
			<content:encoded><![CDATA[<p>Looking over the <a href="http://www.php.net/ChangeLog-5.php">PHP5.2 changelog</a> I noticed that somewhere along the way PHP5 seems to have picked up a provocatively named pair of classes, DateTime and DateTimeZone.  </p>

<p>There is something fundamentally brash, brazen even, to releasing a class named DateTime.  As a calendar geek I imagine upon seeing &#8220;new DateTime()&#8221; I feel something akin to what an old thespian feels when they see a company putting on a production of the Scottish play &#8212; it&#8217;s a decidedly mixed emotion.  But I&#8217;m going to bump my way through learning how to use this new DateTime lib, bringing <strong>all my preconceptions</strong> about how it should work.  The odds of this being interesting to you is probably nil unless you&#8217;re in one or two very small cliques, feel free to read on, or browse away.</p>

<p>I&#8217;m primarily working in PHP4 right now, so my first step was to grab a copy of <a href="http://www.mamp.info/en/home.php">MAMP 1.5b</a> getting me a nice PHP5.2 sandbox to play with.</p>

<p>The new objects are <a href="http://www.php.net/manual/en/ref.datetime.php">documented here</a>, apparently there are functional equivalents for each of the object methods, and they use the <a href="http://pecl.php.net/package/timezonedb/">PECL timezomedb</a>.  </p>

<p>Hey!  timezonedb!  First fence cleared!  A timezone database compiled into a native format based on Olson is the <em>one true solution</em>, and I can update it independently, the most recent release being based on 2007b.  Sweet.  </p>

<p><a href="http://php.net/manual/en/function.date-create.php">Constructor</a> takes an initialization string that it passes to <a href="http://php.net/manual/en/function.strtotime.php">strtotime()</a>, and an optional DateTimeZone obj.  Defaults to &#8220;now&#8221;</p>

<pre><code>$date = new DateTime();
echo $date . "\n";
&gt; Object of class DateTime could not be converted to string 
</code></pre>

<p>Oops, no <code>__toString()</code> method defined.  You&#8217;ll need to use the <code>format()</code> instance method.  If you end up using the DateTime objects, you&#8217;ll be seeing a lot of <code>format()</code>, more on that in a bit.  </p>

<p><code>format()</code> uses the <a href="http://php.net/manual/en/function.date.php">date() formatting strings</a> (not the <code>strftime</code> format strings).  Also takes a number of useful constants, most usefully your pal and mine RFC3339 (aka W3CDTF aka Dublin Core/Atom date format).</p>

<pre><code>echo $date-&gt;format(DATE_RFC3339) . "\n";
&gt; 2007-02-22T15:23:47-05:00
</code></pre>

<p>Note: thats a constant, if you pass in the string &#8216;DATE_RFC3339&#8242;, and you&#8217;ll get odd looking results.</p>

<p>Here we can see the default constructor sets both the time and a timezone &#8212; correctly, for the moment, identifying my timezone as <code>America/New_York</code>.   That&#8217;s somewhat contentious behaviour, some people will tell you that dates with unspecified timezones should either be in UTC or be &#8220;floating&#8221;, divorced from any timezone.  Why?  At least in part because across platforms and boxes timezone guessing is going to be non-deterministic &#8212; the script that worked when you ran it locally on your Mac laptop in New York, might fail on your ISP&#8217;s servers.  You get a hint of this reading over the timezone guessing rules on <a href="http://php.net/date_default_timezone_get"><code>date_default_timezone_get</code></a>.  There is also the fact that I&#8217;m currently moving at about 400mph and will be in a different timezone real soon now.  However you can set the default to something reasonable in a script, or in the php.ini.  (consider this my recommendation)</p>

<pre><code>date_default_timezone_set('UTC');
$date = new DateTime();
echo $date-&gt;format(DATE_RFC3339);
&gt; 2007-02-22T20:44:49+00:00
</code></pre>

<p>Yay, that worked.  Okay, but lets display that datetime in the local timezone.  (after all the <em>point</em> of this entire exercise will be the ability to work painlessly in multiple timezones).</p>

<pre><code>$date-&gt;setTimezone('America/New_York');
&gt; DateTime::setTimezone() expects parameter 1 to be DateTimeZone
</code></pre>

<p>Siiiigh.  Not smart enough to cast strings into TimeZone objects (holds true for the constructor as well, so no <code>new DateTime('now', 'UTC'))</code>.  Now its time to learn how to use DateTimeZone.</p>

<h3>Working with DateTimeZone, All Hail Olson</h3>

<p>I mentioned briefly earlier that PHP is now shipping with an extension timezonedb, which is a compiled version of the <a href="http://en.wikipedia.org/wiki/Zoneinfo">Olson database</a>.  The Olson database is a massive, largely volunteer effort to catalog the various timezones both in use, and those that have been in the past.  Time is a political issue, <a href="http://laughingmeme.org/2005/04/04/daylight-saving-sucks-war-time/">particularly day light savings</a>, and as such the rules governing it are arbitrary, whimsical, and subject to frequent change.  (p.s. gotten a panicked memo yet about new daylight savings compliance for March 11th?  No?  Where did you say you worked?)</p>

<p>Note: Olson also uses a longer form of the zone names then we usually see in the U.S., this is to combat ambiguity.  See <a href="http://www.php.net/manual/en/timezones.php">Appendix H</a> for a list of timezone names, including some <a href="http://www.php.net/manual/en/timezones.others.php">handy shortcuts</a>.</p>

<pre><code>$tz = new DateTimeZone('America/New_York');
$date-&gt;setTimezone($tz);
echo $date-&gt;format(DATE_RFC3339) . "\n";
&gt; 2007-02-22T16:02:55-05:00
</code></pre>

<p>This is starting to get long winded, but, hey, PHP5 supports object dereferencing on returns.  Maybe this will work.</p>

<pre><code>echo $date-&gt;setTimezone($tz)-&gt;format(DATE_RFC3339) . "\n";
&gt;  Call to a member function format() on a non-object
</code></pre>

<p>Nope. Oh well.</p>

<h3>Date vs Datetime?</h3>

<p>Say I&#8217;ve got a nice platonic date, say November 11th.  There is no time element associated with this, so timezones are kind of irrelevant.  I mean Nov. 11th <em>starts</em> at different times through out the world, but Nov. 11th is universal. (as long as you&#8217;re using the same version of Gregorian as most of the rest of us)  Ideally this date would float above timezone issues, but that isn&#8217;t how PHP does it, 2007-11-11 is treated internally as midnight on the 11th, which is certainly simpler, but disappointing.  You can prove this like so:</p>

<pre><code>$date = new DateTime('2007-11-11');
$date-&gt;setTimeZone($tz);
echo $date-&gt;format(DATE_RFC3339) . "\n";
&gt; 2007-11-10T19:00:00-05:00
</code></pre>

<p>The other useful DateTimeZone method is getOffest() </p>

<pre><code>echo $tz-&gt;getOffset($date); 
&gt; -18000
</code></pre>

<h3>Daylight Saving, March 11th, and Why Programmers Are a Grouchy Lot</h3>

<p>Note: <code>getOffset</code>, which returns a timezone&#8217;s offset in seconds from UTC, takes a DateTime obj because offsets can be date sensitive due to daylight savings.  Really without daylight saving this stuff would all be pretty straightforward.    Let&#8217;s test to make sure the offsets are correct at the boundary.</p>

<pre><code>echo $tz_nyc-&gt;getOffset(new DateTime('2007-03-11 1:00')) . "\n";
echo $tz_nyc-&gt;getOffset(new DateTime('2007-03-11 2:00')) . "\n";
&gt; -18000
&gt; -14400

(-18000/(60*60) == -5 hours) 
(-14400/(60*60) == -4 hours) 
</code></pre>

<p><strong>Yay!</strong> They got the memo about <a href="http://en.wikipedia.org/wiki/Energy_Policy_Act_of_2005">U.S. Energy Policy Act of 2005</a>.</p>

<h3>The Basics:  Accessors and Mutators</h3>

<p>So what are some other basic desires?</p>

<p>Get epoch seconds!  Except for their kind of limited range epoch seconds are great, and have helped a generation of programmers put off worrying about timezones as long as possible.  They&#8217;re also the backbone of PHP&#8217;s traditional date/time methods.</p>

<p>Alas, there isn&#8217;t an accessor method for getting epoch seconds, you&#8217;ll have to use <code>format()</code>. </p>

<p>In fact DateTime doesn&#8217;t expose <strong>any</strong> of the accessors you&#8217;d expect, so you&#8217;ll be using <code>format</code> a <strong>lot</strong> if you want to access pieces of your date. (for you know, display purposes, or manipulation, or building queries, or pretty much doing anything you&#8217;d want to do with a date)</p>

<p>examples of the <code>format()</code> as all purpose accessor pattern:</p>

<pre><code>epoch:  $date-&gt;format('U'); // 1173596400
year:   $date-&gt;format('Y'); // 2007
month:  $date-&gt;format('n'); // 3
day:    $date-&gt;format('j'); // 11
dow:    $date-&gt;format('l'); // Sunday
</code></pre>

<p>&#8230; etc &#8230;</p>

<p>So now you have accessors for the full range of <a href="http://php.net/manual/en/function.date.php">date() formatting strings</a>.  You just have to jump through a hope.</p>

<p>Pretty much the only accessor is <code>getTimezone()</code></p>

<pre><code>echo $date-&gt;getTimeZone();   // hope springs eternal!
&gt; Object of class DateTimeZone could not be converted to string
echo $date-&gt;getTimeZone()-&gt;getName() . "\n";
&gt; America/New_York
</code></pre>

<h3>Mutators</h3>

<p>Speaking of accessors, DateTime is a little sparse on mutators as well: <code>setTime()</code>, <code>setDate()</code>, and the mysteriously named <code>setISODate()</code>.</p>

<pre><code>$date-&gt;setDate('2007', '1', '1')-&gt;format(DATE_RFC3339);  // who am I kidding?
&gt; Call to a member function format() on a non-object 
$date-&gt; setDate('2007', '1', '1');
echo $date-&gt;format(DATE_RFC3339) . "\n";
&gt; 2007-01-01T02:00:00-05:00
</code></pre>

<p>Now what if I want to set just the day?</p>

<p>Maybe</p>

<pre><code>$date-&gt; setDate(null, null, '11');
echo $date-&gt;format(DATE_RFC3339) . "\n";
&gt; -001-12-11T02:00:00-05:00
</code></pre>

<p>Nope.  </p>

<p>Instead you&#8217;ll need to pull out the year and month (using our <code>format()</code> accessors) and pass those back in just to set the day.</p>

<pre><code>$date-&gt; setDate('2007', '1', '1');   // jan 1.  
$date-&gt;setDate($date-&gt;format('Y'), $date-&gt;format('n'), 11);
echo $date-&gt;format(DATE_RFC3339) . "\n"; 
</code></pre>

<p>Clunky.  </p>

<p><code>setTime()</code> works the same, but for time.  </p>

<p>e.g. Setting just the minutes, 33 minutes past, but keep hours, and seconds constant:</p>

<pre><code>$date-&gt;setTime($date-&gt;format('G'), 33, $date-&gt;format('s'));
echo $date-&gt;format(DATE_RFC3339) . "\n"; 
&gt; 2007-01-11T02:33:00-05:00
</code></pre>

<p>So what is an ISODate?  I&#8217;m unclear, and so is PHP&#8217;s documentation.  The docs show the call signature taking a $year, $week, and optional $day, while the description talks about $year, $month, $day.  Looking at the code looks like $week is the proper call, $month is cut and paste error from <code>setDate()</code>.  So I guess this is a method for setting day by the &#8220;week of the year&#8221; a concept more popular in Europe then in the US.  Not sure what ISO has to do with it.   So what is our current week of the year?</p>

<pre><code>echo $date-&gt;format('N') . "\n";  // 'N' is new in 5.1.0
&gt; 4 
</code></pre>

<p>Jan 11th was in the 4th week of the 2007?  Go figure.  </p>

<pre><code>$date-&gt;setISODate(2007, 4, 5);  // fifth day of the 4th week?
echo $date-&gt;format(DATE_RFC3339) . "\n";
&gt; 2007-01-26T02:33:00-05:00
</code></pre>

<p>Um.  You know what?  You&#8217;re on your own with <code>setISODate</code>, sorry.</p>

<h3>Date Math: Adding and Subtracting Deltas aka $date->modify($str)</h3>

<p>PHP5 for better or worse has very limited operator overloading, so no $dt1 + $dt2 * $dt3 / $dt4.  Instead the primary method for doing math is <code>modify()</code></p>

<p>Thankfully PHP&#8217;s <code>strtotime()</code> method is a gem, and one of the things it handles is relative dates.  <code>strtotime()</code> + relative dates is the secret to doing math with PHP5&#8217;s DateTime.</p>

<p>Lets get a basic date to start with:</p>

<pre><code>$date = new DateTime('today');
echo $date-&gt;format(DATE_RFC3339) . "\n";
&gt; 2007-02-22T00:00:00+00:00
</code></pre>

<p><strong>Note:</strong> <code>modify()</code> is destructive. It changes the original datetime object (as the name suggests).  You&#8217;ll need to jump through some hopes to keep a copy of your original date.  More later.</p>

<p>Add/subtract N days:</p>

<pre><code>foreach (range(1,10) as $n) {
   $date-&gt;modify("+1 days");
   echo $date-&gt;format("Y-m-d") . "\n";
}

&gt; 2007-02-23
&gt; 2007-02-24
&gt; 2007-02-25
&gt; 2007-02-26
&gt; 2007-02-27
&gt; 2007-02-28
&gt; 2007-03-01
&gt; 2007-03-02
&gt; 2007-03-03
&gt; 2007-03-04

$date-&gt;modify("-10 days");
echo $date-&gt;format("Y-m-d") . "\n";

&gt; 2007-02-22

$date-&gt;modify("-1 month");
echo $date-&gt;format("Y-m-d") . "\n";
&gt; 2007-01-22
// or alternately
$date-&gt;modify("1 month ago");
echo $date-&gt;format("Y-m-d") . "\n";
&gt; 2006-12-22
</code></pre>

<h3>Cloning DateTime objects to work around <code>modify</code></h3>

<p>Of course you <strong>usually</strong> want to keep the original when doing date math, so <code>modify()</code>&#8217;s lack of idempotentce is annoying.  Lets say I&#8217;m building a SQL query to select events happening in the next 7 days.</p>

<p>In an <strong>ideal</strong> world the code would like this:</p>

<pre><code>$start = new DateTime('today');
$end = $start + 7;
echo "select between " . $start-&gt;format('Y-m-d') . " and " . $end-&gt;format('Y-m-d') . "\n";
</code></pre>

<p>The above of course is just a pipe dream.  But wouldn&#8217;t it be nice?</p>

<p>I&#8217;d settle for:</p>

<pre><code>$end = $start-&gt;calc("+7 days");
</code></pre>

<p>Or even:</p>

<pre><code>$end = $start-&gt;clone-&gt;modify('+7 days');
</code></pre>

<p>None of the above examples remotely work.  </p>

<p>Instead use:</p>

<pre><code>$start = new DateTime('today');
$end = clone $start;
$end-&gt;modify('7 days 3 minutes 42 seconds ago');
</code></pre>

<p>Now format our SQL query for our example:</p>

<pre><code>echo "select between " . $start-&gt;format(DATE_RFC3339) . " and " . $end-&gt;format(DATE_RFC3339) . "\n";
&gt; select between 2007-02-22T00:00:00+00:00 and 2007-02-14T23:56:18+00:00
</code></pre>

<p>Awkward, but it gets the job done.</p>

<p>At least the relative date format is super flexible and expressive.  As far as I know the closest thing to documentation is from the <a href="http://www.gnu.org/software/tar/manual/html_node/tar_109.html">GNU tar manual on date input formats</a>. (just like CVS)  Btw. if you ever want nightmares, take a look at the <code>scan</code> method in PHP&#8217;s parse_date.c and be thankful that isn&#8217;t your job to maintain <img src='http://laughingmeme.org/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>

<h3>Date Math: Comparison and Differences</h3>

<p>Beyond adding deltas (&#8220;+7 days&#8221;), the other common date math is comparing two datetimes, to find out which is more recent, and getting the difference between them.  DateTime supports no methods for comparing two datetimes.  The simplest solution for doing comparison is to compare epoch seconds.</p>

<p><strong>Note:</strong>  This method only works for dates that can be represented by epoch seconds. PHP uses a signed int for epoch seconds, so the range is limited by the size of the max int on your platform.  Generally you get approximately 138 years, 1901 to 2038.  There are other schemes besides epoch seconds for mapping dates to an easily comparable number; <a href="http://en.wikipedia.org/wiki/Julian_day">MJDs</a>, and <a href="http://cr.yp.to/proto/utctai.html">Tai time</a> being two.  See also <em>Rheingold &amp; Dershowitz 1997</em></p>

<pre><code>$d1 = new DateTime("today");
$d2 = new DateTime("tomorrow");
if ($d1-&gt;format('U') &lt; $d2-&gt;format('U')) {
   echo "true\n";
} 
&gt; true
</code></pre>

<p>If you&#8217;re going to be comparing a large number of dates you might consider a memoization technique like the <a href="http://en.wikipedia.org/wiki/Schwartzian_transform">Schwartzian transform</a>.</p>

<p>We can get the difference in seconds using the same hack of casting to epochs.</p>

<pre><code>echo $d2-&gt;format('U') - $d1-&gt;format('U') . "\n";
&gt; 86400
</code></pre>

<p>Ideally we&#8217;d then divide the difference seconds to get the difference in hours, days, weeks, or months.  However the following naive solution <strong>won&#8217;t</strong> work.  </p>

<pre><code>$diff / (60*60*24);  // calculate difference in days, **BADLY**
</code></pre>

<p>Why not?  Because days don&#8217;t always have 24 hours.  Sometimes they have 23 hours, sometimes they have 25.  Daylight saving strikes again.  (If you want to be even more pedantic, minutes are also not 60 seconds long, sometimes they&#8217;re 61 seconds long if we have a leap second)</p>

<p>Basically you need to break yourself of thinking of datetime units as being fungible.  You can&#8217;t simply calculate minutes from seconds, or days from hours.  Just like you can&#8217;t divide days by 30 to get an accurate number of months.  There are solutions, but they&#8217;re a bit beyond this blog post.</p>

<h3>new DateTime from Epoch Seconds</h3>

<p>So, <strong>non-fungible</strong>, remember that.</p>

<p>But sometimes you&#8217;ve cast DateTimes down to epochs to do math.  And then you&#8217;ll want to cast back to a DateTime.  </p>

<p>Alas DateTime doesn&#8217;t have a constructor that takes an epoch, and passing a epoch to the default constructor will throw an exception, rather you want:</p>

<pre><code>$from_epoch = new DateTime(date('c', '-568080000'));
echo $from_epoch-&gt;format('Y-m-d') . "\n";
&gt; 1952-01-01   // expected result
</code></pre>

<h3>Conclusions</h3>

<p>DateTime/DateTimeZone get timezones right, and for solving that hard problem they deserve all possibles accolades.  </p>

<p>The rest of the API however is kind of simplistic, awkward to work with, and verbose.</p>

<p>Single most useful change: have DateTime methods actually return the object making it possible to use a slightly more abbreviated calls.</p>

<p>I had thought about writing up a few more recipes, like nth dow of the month, and such.  But we were coming in for descent, and it was time to be done.  Might happen in the future.  </p>

<p>Also if anyone has any power to <strong>enhance</strong> the DateTime object, I hope some of the above can act as a road map for a more expressive and powerful core library.  Or ping me, happy to chat.</p>
]]></content:encoded>
			<wfw:commentRss>http://laughingmeme.org/2007/02/27/looking-at-php5s-datetime-and-datetimezone/feed/</wfw:commentRss>
		<slash:comments>20</slash:comments>
		</item>
		<item>
		<title>Weather over Twitter</title>
		<link>http://laughingmeme.org/2007/01/12/weather-over-twitter/</link>
		<comments>http://laughingmeme.org/2007/01/12/weather-over-twitter/#comments</comments>
		<pubDate>Sat, 13 Jan 2007 07:25:42 +0000</pubDate>
		<dc:creator>Kellan</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[jabber]]></category>
		<category><![CDATA[local]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[syndication]]></category>
		<category><![CDATA[twitter]]></category>
		<category><![CDATA[weather]]></category>
		<category><![CDATA[webservices]]></category>

		<guid isPermaLink="false">http://laughingmeme.org/2007/01/12/weather-over-twitter/</guid>
		<description><![CDATA[

And while we&#8217;re talking about recent hacks, Blaine and I whipped up a Jabber bot using his Jabber::Simple and the Yahoo weather feeds, to provide twice daily weather updates via Twitter.

Jabber is an intriguing platform to build on top of, and the more I play with it the more potential I find.   I [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.flickr.com/photos/bonsaikiptb/200041749/" title="Photo Sharing"><img src="http://farm1.static.flickr.com/70/200041749_ff642a45be.jpg" width="500" height="334" alt="Too Close!!!" /></a></p>

<p>And while we&#8217;re talking about recent hacks, <a href="http://romeda.org/blog/2006/12/weather-by-twitter.html">Blaine</a> and I whipped up a Jabber bot using his <a href="http://romeda.org/blog/2006/11/announcing-jabbersimple.html">Jabber::Simple</a> and the <a href="http://developer.yahoo.com/weather/">Yahoo weather feeds</a>, to provide twice daily weather updates via <a href="http://twitter.com">Twitter</a>.</p>

<p><a href="http://jabber.org">Jabber</a> is an intriguing platform to build on top of, and the more I play with it the more potential I find.   I keep checking in on it every few years (since MetaEvents days), but recently its gotten much more interesting.  In part thats Google&#8217;s adoption of the standard (and the subsequent enhancement in tools, libraries, and clients), and partially standards bake slowly, but at the core of it I think we&#8217;re reaching a point in the evolution of the Web where Internet-scale deployed messaging standards have a lot to offer of us.  A protocol for when HTTP fails you.</p>

<blockquote>
  <p>If you follow these bots, you&#8217;ll receive those updates wherever you normally get your Twitters; IM, Phone, RSS, or just on the web. So far, we have bots for the following cities: <a href="http://twitter.com/wxboston">Boston</a>, <a href="http://twitter.com/wxbrighton">Brighton</a>, <a href="http://twitter.com/wxchicago">Chicago</a>, <a href="http://twitter.com/wxhelsinki">Helsinki</a>, <a href="http://twitter.com/wxlondon">London</a>, <a href="http://twitter.com/wxla">Los Angeles</a>, <a href="http://twitter.com/wxnyc">New York</a>, <a href="http://twitter.com/wxparis">Paris</a>, <a href="http://twitter.com/wxpdx">Portland</a>, <a href="http://twitter.com/wxsf">San Francisco</a>, <a href="http://twitter.com/wxseattle">Seattle</a>, <a href="http://twitter.com/wxsingapore">Singapore</a>, and <a href="http://twitter.com/wxyvr">Vancouver</a>. If you&#8217;d like to see another city, just ask and we&#8217;ll provide.</p>
</blockquote>

<p>Slightly out of date source available at <a href="http://code.google.com/p/twitter-weather/">twitter-weather &#8211; Google Code</a></p>

<p>And taking requests for new cities.  Probably do a big batch of new ones sometime next week.  (not really an automated process)</p>

<p>Photo by <a href="http://www.flickr.com/photos/bonsaikiptb">bonsaikiptb</a></p>
]]></content:encoded>
			<wfw:commentRss>http://laughingmeme.org/2007/01/12/weather-over-twitter/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>Flexible Category Lists for Wordpress</title>
		<link>http://laughingmeme.org/2007/01/12/flexible-category-lists-for-wordpress/</link>
		<comments>http://laughingmeme.org/2007/01/12/flexible-category-lists-for-wordpress/#comments</comments>
		<pubDate>Sat, 13 Jan 2007 06:52:31 +0000</pubDate>
		<dc:creator>Kellan</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[meta]]></category>
		<category><![CDATA[tagging]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://laughingmeme.org/2007/01/12/flexible-category-lists-for-wordpress/</guid>
		<description><![CDATA[One of the side effect of overloading (perverting?) the Wordpress category system to do tagging is you end up with over 1000 categories.  The posting interface gets unhappy, and the wp_list_cats template tag becomes pretty much useless.

(This post is another Wordpress meta post, one of several I&#8217;ve got queued up in my head, and [...]]]></description>
			<content:encoded><![CDATA[<p>One of the side effect of overloading (perverting?) the <a href="http://wordpress.org">Wordpress</a> category system to do tagging is you end up with over 1000 categories.  The posting interface gets unhappy, and the <a href="http://codex.wordpress.org/Template_Tags/wp_list_cats"><code>wp_list_cats</code></a> template tag becomes pretty much useless.</p>

<p>(This post is another Wordpress meta post, one of several I&#8217;ve got queued up in my head, and probably interesting to 3 people in the known universe, but here you go.)</p>

<p><a href="/code/better_cat_lists.php.txt"><code>better_cat_lists</code></a> is Wordpress plugin that adds a couple of more flexible methods.</p>

<h3><code>wp_list_popular_categories</code></h3>

<p>works like <code>wp_list_cats</code>, but only categories with $cat_threshold posts  in them.</p>

<pre><code>wp_list_popular_categories('sort_column=name&amp;cat_threshold=20');
</code></pre>

<h3><code>wp_list_recent_categories</code></h3>

<p>works like <code>wp_list_cats</code>, but only from posts with the last $n days.  You can also limit the total number with $cat_limit</p>

<pre><code>wp_list_recent_categories('days_ago=180&amp;cat_limit=80');
</code></pre>

<p>Both methods use the same hack, they overload the <code>list_cats_exclusions</code> callback to do positive match instead of the intended negative match &#8211; appending <code>and cat_ID in ($cat_ids)</code> to the exclusion string.  PHP at its finest, quick and dirty, like monkey patching but without language level support for it. </p>
]]></content:encoded>
			<wfw:commentRss>http://laughingmeme.org/2007/01/12/flexible-category-lists-for-wordpress/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Session &#8220;Flash&#8221; in PHP</title>
		<link>http://laughingmeme.org/2005/12/07/session-flash-in-php/</link>
		<comments>http://laughingmeme.org/2005/12/07/session-flash-in-php/#comments</comments>
		<pubDate>Thu, 08 Dec 2005 07:03:00 +0000</pubDate>
		<dc:creator>Kellan</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[patterns]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[webdev]]></category>

		<guid isPermaLink="false">http://lm.quxx.info/?p=3147</guid>
		<description><![CDATA[Doing some PHP hacking tonight, I was missing Rails&#8217; &#8220;flash&#8221;.  So I coded up a quick and dirty implementation of the read-once session status notification pattern.  The code
]]></description>
			<content:encoded><![CDATA[<p>Doing some PHP hacking tonight, I was missing Rails&#8217; &#8220;flash&#8221;.  So I coded up a quick and dirty implementation of the read-once session <a href="http://blog.ianbicking.org/web-application-patterns-status-notification.html">status notification pattern</a>.  <a href="http://laughingmeme.org/code/flash.inc.php.txt">The code</a></p>
]]></content:encoded>
			<wfw:commentRss>http://laughingmeme.org/2005/12/07/session-flash-in-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Flushing Rails Database (MySQL) Sessions</title>
		<link>http://laughingmeme.org/2005/12/07/flushing-rails-database-mysql-sessions/</link>
		<comments>http://laughingmeme.org/2005/12/07/flushing-rails-database-mysql-sessions/#comments</comments>
		<pubDate>Wed, 07 Dec 2005 21:40:00 +0000</pubDate>
		<dc:creator>Kellan</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[cron]]></category>
		<category><![CDATA[hacking]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[typo]]></category>

		<guid isPermaLink="false">http://lm.quxx.info/?p=3146</guid>
		<description><![CDATA[I use Rails&#8217; database session backend for LM. (for login, as well as &#8220;flash&#8221;)  Without any sort of built in garbage collection the sessions table gets very large, very quickly.  Beyond aesthetic issues, this can also cause MySQL&#8217;s key buffer to fill up. (which on Debian is by default set quite low)

So I [...]]]></description>
			<content:encoded><![CDATA[<p>I use Rails&#8217; <a href="http://wiki.rubyonrails.com/rails/pages/HowtoChangeSessionStore">database session backend</a> for <a href="http://laughingmeme.org">LM</a>. (for login, as well as <a href="http://api.rubyonrails.com/classes/ActionController/Flash.html">&#8220;flash&#8221;</a>)  Without any sort of built in garbage collection the sessions table gets <strong>very</strong> large, very quickly.  Beyond aesthetic issues, this can also cause MySQL&#8217;s key buffer to fill up. (which on Debian is by default set quite low)</p>

<p>So I wrote up a quick flush method, and saved it in a file <code>models/session.rb</code>.</p>

<pre><code>class CGI::Session::ActiveRecordStore::Session
  def self.flush_old_empty_sessions
     self.delete_all "DATE_SUB(NOW(),INTERVAL 6 HOUR) &gt; 
     updated_at and BIT_LENGTH(data) &lt;= 688"
  end
end
</code></pre>

<p>This says nuke all sessions which are over 6 hours old, and which are empty.  (688 is the length of the serialized session with an empty flash)</p>

<p>MySQL specific, and susceptible to changes in either session structure or its serialization.  But it was quick and easy and worked for me.</p>

<p>Then you simply need a cron job like: <code>ruby script/runner  'CGI::Session::ActiveRecordStore::Session.flush_old_empty_sessions'</code></p>
]]></content:encoded>
			<wfw:commentRss>http://laughingmeme.org/2005/12/07/flushing-rails-database-mysql-sessions/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>PEAR: No release with state equal to: &#8217;stable&#8217; found for &#8230;</title>
		<link>http://laughingmeme.org/2004/08/23/pear-no-release-with-state-equal-to-stable-found-for-2/</link>
		<comments>http://laughingmeme.org/2004/08/23/pear-no-release-with-state-equal-to-stable-found-for-2/#comments</comments>
		<pubDate>Tue, 24 Aug 2004 05:38:33 +0000</pubDate>
		<dc:creator>Kellan</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[pear]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://laughingmeme.org/2004/08/23/pear-no-release-with-state-equal-to-stable-found-for-2/</guid>
		<description><![CDATA[Trying to use:

pear install Some_Package 


but getting 

No release with state equal to: 'stable' found for Some_Package?


Some people will tell you to try: 

pear install -f Some_Package 


(i.e. force install)  A better solution is: 

pear remote-info Some_Package


You&#8217;ll see near the top the latest version, e.g. Lastest: 0.8RC3.

Then you can do: 

pear install Some_Package-0.8RC3


Sometimes you&#8217;ll [...]]]></description>
			<content:encoded><![CDATA[<p>Trying to use:</p>

<pre><code>pear install Some_Package 
</code></pre>

<p>but getting </p>

<pre><code>No release with state equal to: 'stable' found for Some_Package?
</code></pre>

<p>Some people will tell you to try: </p>

<pre><code>pear install -f Some_Package 
</code></pre>

<p>(i.e. force install)  A better solution is: </p>

<pre><code>pear remote-info Some_Package
</code></pre>

<p>You&#8217;ll see near the top the latest version, e.g. Lastest: 0.8RC3.</p>

<p>Then you can do: </p>

<pre><code>pear install Some_Package-0.8RC3
</code></pre>

<p>Sometimes you&#8217;ll already have a package installed (e.g. <code>"Package 'Foo_Package' is already installed"</code>), but need to upgrade to a unstable version.</p>

<pre><code>pear remote-info Foo_Package

Package details:
================
Latest      1.0.2
Installed   1.0.1
...

pear install -f Foo_Package-1.0.2
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://laughingmeme.org/2004/08/23/pear-no-release-with-state-equal-to-stable-found-for-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MySQL, and the CASE for Class Table Inheritance</title>
		<link>http://laughingmeme.org/2004/08/14/mysql-and-the-case-for-class-table-inheritance/</link>
		<comments>http://laughingmeme.org/2004/08/14/mysql-and-the-case-for-class-table-inheritance/#comments</comments>
		<pubDate>Sun, 15 Aug 2004 01:08:00 +0000</pubDate>
		<dc:creator>Kellan</dc:creator>
				<category><![CDATA[case]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[eaa]]></category>
		<category><![CDATA[enterprise]]></category>
		<category><![CDATA[Longer]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[oop]]></category>
		<category><![CDATA[patterns]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[sql]]></category>

		<guid isPermaLink="false">http://lm.quxx.info/?p=883</guid>
		<description><![CDATA[
At work we&#8217;re using Class Table Inheritance to model the core data structures of our as yet nameless open source CRM. (actually it has a code name, but I don&#8217;t like it, so we&#8217;ll pretend it&#8217;s nameless)  



This week as I learned both the name of this pattern, and the SQL to implement it [...]]]></description>
			<content:encoded><![CDATA[<p>
At <a href="http://groundspring.org">work</a> we&#8217;re using <a href="http://martinfowler.com/eaaCatalog/classTableInheritance.html">Class Table Inheritance</a> to model the core data structures of our as yet nameless open source <acronym title="Community Relationship Management">CRM</acronym>. (actually it has a code name, but I don&#8217;t like it, so we&#8217;ll pretend it&#8217;s nameless)  
</p>

<p><p>
This week as I learned both the name of this pattern, and the SQL to implement it efficiently in MySQL I thought I&#8217;d share some notes on what we&#8217;ve come up with.
</p>
<span id="more-883"></span>
<p>
<h3>Class Table Inheritance</h3>
<acronym title="Class Table Inheritance">CTI</acronym> is a pattern where your schema  hews closely to the your class hierarchy &#8212; you have a table for each class, and object attributes are stored as columns in the table for that class.  This is as opposed to <a href="http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html">Single Table Inheritance</a>, which stores all attributes for every member of an inheritance tree in a single table, or <a href="http://www.martinfowler.com/eaaCatalog/concreteTableInheritance.html">Concrete Table Inheritance</a> which duplicates the inherited fields to each table.
</p>
<p>
We&#8217;re using CTI for a number of reasons, some excellent, and some because that is how our brains work.  One key benefit is we get the ability to operate on heterogenous collections of object. (i.e. display a sorted list of contact objects irrespective of whether they are individuals, organizations, etc.)  The fact that something as fundamental as polymorphism can be difficult to accomplish using other O/R techniques hilights the general difficulty and <a href="http://www.google.com/search?q=impedance+mismatch" title="6 of the first 10 results are about O/R">impedance mismatch</a> of object to relational mapping.
</p>
<p>
There are, however a few challenges involved. 
</p>
<p>
<h3>The Model</h3>
But quickly a trivial mock up of the data model to give us something to play with.  In this model we have 3 classes, a base Contact class, and 2 types of contacts Individual and Organization which inherit from Contact.  In the database we model this as a Contact table, with a primary id, and any fields which the Contact the parent class provides.  Individual and Organization each get their own table, with class specific attributes, plus the primary id from Contact which is both the primary id, and a foreign key into Contact.  For every contact stored in the database there is a record in the Contact table, and a record in either Individual or Organization.
</p>
<p>
<img src="http://laughingmeme.org/img/simple_contact_model.gif" />
</p>
<p>
<h3>Challenge 1:</h3>
How do you retrieve your data now that it is broken across multiple tables?  The obvious (and obviously wrong) solution would be to first select against the Contact table, examine the resulting data, and select against the appropriate children tables. (remember this example is a drastically simplified data model)
</p>
<p>
<h3>Solution 1:</h3>
My current solution is to do a 3 way left outer join, and then instantiate the objects out of fields which aren&#8217;t null.  It can look a bit ugly with large numbers of columns, but is actually pretty simple, and initial benchmarks against a test data set of 1.3 million records suggests it scales nicely.</p>

<pre>
SELECT 
  c.id as id, c.contactType,
  i.firstName, i.lastName,
  o.name as orgName
FROM
  Contact as c
LEFT JOIN Individual as i ON i.id = c.id 
LEFT JOIN Organization as o ON o.id = c.id
</pre>

<p>(And it&#8217;s pretty easy to coax the necessary SQL out of our modified version of <a href="http://pear.php.net/package/DB_DataObject">DB_DataObject</a>. I&#8217;ll demonstrate that in a future entry)
</p>
<p>
<h3>Challenge 2:</h3>
One of the good reasons (in fact the only concrete one I&#8217;ve given you) to go with CTI and its potential added complexity is the desire to sort a mixed list of contact types.   But what do you do when you want to sort on an amorphous concept like &#8220;Name&#8221;?  Individual might sort by &#8220;Lastname, Firstname&#8221;, while we just use &#8220;orgName&#8221; for an organization.  Where is our polymorphism now?
</p>
<p>
One solution would be select all known records, and do sorting and slicing at the application level.  (This is so wrong it makes my head hurt)  Another solution would be store a field like &#8220;sortName&#8221; on Contact, which you could calculate and save on inserts and updates.  This is on the right track.
</p>
<p>
<h3>Solution 2:</h3>
The solution (which <a href="http://blogs.onenw.org/carl">Carl</a> clued me into while we waited to get into the zoo), is to use the <a href="http://dev.mysql.com/doc/mysql/en/Control_flow_functions.html">SQL CASE statement</a> to calculate &#8220;sortName&#8221; on the fly.  Below is our SQL from below with the new logic in green.</p>

<pre>
SELECT 
  c.id as id, c.contactType,
  i.firstName, i.lastName,
  o.name as orgName<span style="color:green;">,
CASE 
 WHEN c.contactType = 'Individual' THEN CONCAT(i.lastName, i.firstName)
 WHEN c.contactType = 'Organization' THEN o.name
END as sortName</span>
FROM
  Contact as c
LEFT JOIN Individual as i ON i.id = c.id 
LEFT JOIN Organization as o ON o.id = c.id
<span style="color:green;">ORDER by sortName</span>
</pre>

<p></p>
<p>
This simply adds a switch statement to your field list conditionally setting the value of sortName.
</p>
<p>
There is nothing quite so satisfying as finding the right tool for the right job.  I&#8217;m currently totally in love with the CASE statement, and think everyone should know about it. (hence this bit of evangelism)
</p>
<p>
Tip: don&#8217;t forget that &#8216;,&#8217; after orgName, I keep forgetting it and wondering why my SQL isn&#8217;t working.
</p>
<p>
So concludes today&#8217;s edition of &#8220;Enterprise Development with MySQL&#8221; (&#8220;Kellan Learns SQL&#8221; didn&#8217;t sound as impressive).  In the near future I hope to get a chance to write up some of the PHP libs we&#8217;ve been building/modifying to support object rich web development techniques, completing the picture. (<acronym title="Objects Linux Apache MySQL PHP">OLamp</acronym> anyone?)
</p>
<p>
<h3>Mixing Metaphors</h3>
Single, and Concrete <acronym title="Table Inheritance">TI</acronym> both have their places, and the good news is you can mix these patterns without much difficulty.  Still I&#8217;m happy with both the flexibility and &#8220;OO-ness&#8221; of Concrete Table Inheritance, and once again impressed by the speed, and power of MySQL. (N.B. we&#8217;re targeting 4.1.x, but these above examples all work with 4.0.16 and 4.0.20)
</p></p>
]]></content:encoded>
			<wfw:commentRss>http://laughingmeme.org/2004/08/14/mysql-and-the-case-for-class-table-inheritance/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Semi-Intelligent String Cropping with PHP</title>
		<link>http://laughingmeme.org/2004/05/25/semi-intelligent-string-cropping-with-php/</link>
		<comments>http://laughingmeme.org/2004/05/25/semi-intelligent-string-cropping-with-php/#comments</comments>
		<pubDate>Wed, 26 May 2004 05:53:28 +0000</pubDate>
		<dc:creator>Kellan</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://laughingmeme.org/2004/05/25/semi-intelligent-string-cropping-with-php/</guid>
		<description><![CDATA[Problem: You want to display the first N characters of a string.

Solution 1: Use substr. Unfortunately substr will more often then not leave a word fragment dangling at the end of your new string, which looks broken.

Solution 2: Use substr, but add an ellipsis (&#8230;) at the end of the string. Now the user has [...]]]></description>
			<content:encoded><![CDATA[<p>Problem: You want to display the first N characters of a string.</p>

<p>Solution 1: Use substr. Unfortunately substr will more often then not leave a word fragment dangling at the end of your new string, which looks broken.</p>

<p>Solution 2: Use substr, but add an ellipsis (&#8230;) at the end of the string. Now the user has a clue as to why there is a dangling word fragment, but the ellipsis is misleading if the your original string was actually less then N characters long initially.</p>

<p>Solution 3: Check if the string is longer then N characters, and if it is use Solution 2, otherwise leave it alone. This is an improvement, but in practice those dangling fragments, even with their ellipsises are kind of unsightly.</p>

<p>Solution 4: Try to find a natural breaking point within the desired crop length, and break on that. If not found revert to Solution 2. Below is some quick code to do this in PHP. It uses strpos instead of regex for speed. It doesn&#8217;t handle &#8220;quoted text&#8221; intelligently, and it doesn&#8217;t look for paragrah breaks (I&#8217;m using it for shorter crop lengths, 100-150 characters), but it is a starting point. Also when cropping strings, it is often important to strip the HTML from them first, or you&#8217;re essentially guarenteed to end up with broken HTML.</p>

<pre><code>function crop($str, $len) {
    if ( strlen($str) &lt;= $len ) {
        return $str;
    }

    // find the longest possible match
    $pos = 0;
    foreach ( array('. ', '? ', '! ') as $punct ) {
        $npos = strpos($str, $punct);
        if ( $npos &gt; $pos &amp;&amp; $npos &lt; $len ) {
            $pos = $npos;
        }
    }

    if ( !$pos ) {
        // substr $len-3, because the ellipsis adds 3 chars
        return substr($str, 0, $len-3) . '...'; 
    }
    else {
        // $pos+1 to grab punctuation mark
        return substr($str, 0, $pos+1);
    }
}
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://laughingmeme.org/2004/05/25/semi-intelligent-string-cropping-with-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
