<?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>amnuts &#187; PHP</title>
	<atom:link href="http://blog.amnuts.com/category/php/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.amnuts.com</link>
	<description>php projects, javascript, and... stuff.</description>
	<lastBuildDate>Fri, 27 Jan 2012 21:15:48 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Merge two urls</title>
		<link>http://blog.amnuts.com/2011/09/26/merge-two-urls/</link>
		<comments>http://blog.amnuts.com/2011/09/26/merge-two-urls/#comments</comments>
		<pubDate>Mon, 26 Sep 2011 14:38:51 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[Code snippets]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://blog.amnuts.com/?p=245</guid>
		<description><![CDATA[]]></description>
			<content:encoded><![CDATA[<pre class="brush: php; title: ; notranslate">
/**
 * Combine two urls.
 *
 * The urls can be either a string or url parts that consist of:
 *
 *     scheme, host, port, user, pass, path, query, fragment
 *
 * If passed in as parts in an array, the query parameter can be either
 * a string or an array of name/value key pairs.  The query parameters
 * will be added on to the ones from the original url.  If you want to
 * remove query parameters, or any other parts of the url, you need to
 * pass the value in as null.
 *
 * Examples:
 *
 *     urlMerge(
 * 	       '/tests/section/people?id=9405',
 *         array('query' =&gt; array('found' =&gt; true, 'id' =&gt; null))
 *     );
 *
 *     urlMerge(
 * 	       'http://www.example.com/',
 *         array('scheme' =&gt; 'https', 'query' =&gt; 'foo=bar&amp;test=1'
 *     );
 *
 *     urlMerge(
 * 	       array('path' =&gt; '/tests/item', 'query' =&gt; 'id=9405'),
 *         'http://www.example.com'
 *     );
 *
 * @param  string|array $original
 * @param  string|array $new
 * @return string
 */
function urlMerge($original, $new)
{
    if (is_string($original)) {
        $original = parse_url($original);
    }
    if (is_string($new)) {
        $new = parse_url($new);
    }
    $qs = null;
    if (!empty($original['query']) &amp;&amp; is_string($original['query'])) {
        parse_str($original['query'], $original['query']);
    }
    if (!empty($new['query']) &amp;&amp; is_string($new['query'])) {
        parse_str($new['query'], $new['query']);
    }
    if (isset($original['query']) || isset($new['query'])) {
        if (!isset($original['query'])) {
            $qs = $new['query'];
        } elseif (!isset($new['query'])) {
            $qs = $original['query'];
        } else {
            $qs = array_merge($original['query'], $new['query']);
        }
    }
    $result = array_merge($original, $new);
    $result['query'] = $qs;
    foreach ($result as $k =&gt; $v) {
        if ($v === null) {
            unset($result[$k]);
        }
    }
    if (!empty($result['query'])) {
        $result['query'] = http_build_query($result['query']);
    }
    return (isset($result['scheme']) ? &quot;{$result['scheme']}://&quot; : '')
		. (isset($result['user']) ? $result['user']
		    . (isset($result['pass']) ? &quot;:{$result['pass']}&quot; : '').'@' : '')
		. (isset($result['host']) ? $result['host'] : '')
		. (isset($result['port']) ? &quot;:{$result['port']}&quot; : '')
		. (isset($result['path']) ? $result['path'] : '')
		. (!empty($result['query']) ? &quot;?{$result['query']}&quot; : '')
		. (isset($result['fragment']) ? &quot;#{$result['fragment']}&quot; : '');
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.amnuts.com/2011/09/26/merge-two-urls/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Sorting an array of objects by one or more object property</title>
		<link>http://blog.amnuts.com/2011/04/08/sorting-an-array-of-objects-by-one-or-more-object-property/</link>
		<comments>http://blog.amnuts.com/2011/04/08/sorting-an-array-of-objects-by-one-or-more-object-property/#comments</comments>
		<pubDate>Fri, 08 Apr 2011 16:18:26 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[Code snippets]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://blog.amnuts.com/?p=220</guid>
		<description><![CDATA[Quite often I find myself having an array of objects and needing to sort that array by one or more of the properties in the objects&#8230; Imagine, for example, getting a large result set from Zend_Db, or something similar, and ordering in the query just takes too long. Or perhaps you&#8217;re getting results from a [...]]]></description>
			<content:encoded><![CDATA[<p>Quite often I find myself having an array of objects and needing to sort that array by one or more of the properties in the objects&#8230;  Imagine, for example, getting a large result set from Zend_Db, or something similar, and ordering in the query just takes too long.  Or perhaps you&#8217;re getting results from a web service and that service doesn&#8217;t return the results in the order you&#8217;d like to use.  Have you ever found yourself in that situation, too?  On looking at the <a href="http://uk.php.net/usort">usort documentation</a> one day I came across a comment by someone called <em>Will Shaver</em> that did almost what I wanted.  With a little adaptation for my own use (being able to change the sort order, for example), it has become one of my favourite functions to use for sorting.</p>
<pre class="brush: php; title: ; notranslate">
    /**
     * Sort an array of objects.
     *
     * You can pass in one or more properties on which to sort.  If a
     * string is supplied as the sole property, or if you specify a
     * property without a sort order then the sorting will be ascending.
     *
     * If the key of an array is an array, then it will sorted down to that
     * level of node.
     *
     * Example usages:
     *
     * osort($items, 'size');
     * osort($items, array('size', array('time' =&gt; SORT_DESC, 'user' =&gt; SORT_ASC));
     * osort($items, array('size', array('user', 'forname'))
     *
     * @param array $array
     * @param string|array $properties
     */
    public static function osort(&amp;$array, $properties)
    {
        if (is_string($properties)) {
            $properties = array($properties =&gt; SORT_ASC);
        }
        uasort($array, function($a, $b) use ($properties) {
            foreach($properties as $k =&gt; $v) {
                if (is_int($k)) {
                    $k = $v;
                    $v = SORT_ASC;
                }
                $collapse = function($node, $props) {
                    if (is_array($props)) {
                        foreach ($props as $prop) {
                            $node = (!isset($node-&gt;$prop)) ? null : $node-&gt;$prop;
                        }
                        return $node;
                    } else {
                        return (!isset($node-&gt;$props)) ? null : $node-&gt;$props;
                    }
                };
                $aProp = $collapse($a, $k);
                $bProp = $collapse($b, $k);
                if ($aProp != $bProp) {
                    return ($v == SORT_ASC)
                        ? strnatcasecmp($aProp, $bProp)
                        : strnatcasecmp($bProp, $aProp);
                }
            }
            return 0;
        });
    }
</pre>
<p>Now a few cools things about the function:</p>
<ol>
<li>It uses <a href="http://php.net/manual/en/functions.anonymous.php">anonymous/lambda functions (or closures, whatever your prefer to call them)</a>, and that&#8217;s just plain fun</li>
<li>You can sort on more than one property and because the sorting is recursive, it&#8217;ll sort the second property within the confines of the first, the third within the confines of the second, and so on.  Think sorting in SQL</li>
<li>You can sort in ascending or descending order for any of the properties</li>
<li>It retains key associations so you could use this on an associative array of objects</li>
<li>If the parameter you want to sort on is an array itself then you can use any value (by specifying it&#8217;s key) in that array as the sorting value</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://blog.amnuts.com/2011/04/08/sorting-an-array-of-objects-by-one-or-more-object-property/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Extend Zend_View_Stream to easily escape view variables</title>
		<link>http://blog.amnuts.com/2010/10/31/easily-escape-view-variables/</link>
		<comments>http://blog.amnuts.com/2010/10/31/easily-escape-view-variables/#comments</comments>
		<pubDate>Sun, 31 Oct 2010 22:14:55 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[zend_view_stream]]></category>

		<guid isPermaLink="false">http://blog.amnuts.com/?p=207</guid>
		<description><![CDATA[Zend_View_Stream is used pretty much when ever you use Zend_View, and I&#8217;ve blogged about how handy it is before.  But as it&#8217;s a class like any other, you can extend it to give added functionality.  One such use is to add automatic escaping to your view variables when you want.  So instead of doing: You [...]]]></description>
			<content:encoded><![CDATA[<p>Zend_View_Stream is used pretty much when ever you use Zend_View, and <a href="http://blog.amnuts.com/2009/03/24/zend-framework-hidden-gems/">I&#8217;ve blogged about how handy it is before</a>.  But as it&#8217;s a class like any other, you can extend it to give added functionality.  One such use is to add automatic escaping to your view variables when you want.  So instead of doing:</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php echo $this-&gt;escape($this-&gt;var); ?&gt;
&lt;?= $this-&gt;escape($this-&gt;var); ?&gt;
</pre>
<p>You could simply do:</p>
<pre class="brush: php; title: ; notranslate">&lt;?=~ $this-&gt;var; ?&gt;</pre>
<p>That&#8217;s a lot simpler, isn&#8217;t it?<br />
<span id="more-207"></span><br />
To do this, we need to do two things; 1) extend the stream class and 2) make sure we register it before Zend Framework registers Zend_View_Stream.</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php

class My_Stream extends Zend_View_Stream
{
    /**
     * Opens the script file and converts markup.
     */
    public function stream_open($path, $mode, $options, &amp;$opened_path)
    {
        // get the view script source
        $path        = str_replace('zend.view://', '', $path);
        $this-&gt;_data = file_get_contents($path);

        /**
         * If reading the file failed, update our local stat store
         * to reflect the real stat of the file, then return on failure
         */
        if ($this-&gt;_data === false) {
            $this-&gt;_stat = stat($path);
            return false;
        }

        /**
         * Convert &lt;?= ?&gt; to long-form &lt;?php echo ?&gt; and &lt;? ?&gt; to &lt;?php ?&gt;
         *
         */
        $this-&gt;_data = preg_replace(
            '/\&lt;\?\=~ (.*?);? \?&gt;/',
            '&lt;?php echo $this-&gt;escape($1); ?&gt;',
            $this-&gt;_data
        );
        $this-&gt;_data = preg_replace(
            '/\&lt;\?\=/',
            '&lt;?php echo ',
            $this-&gt;_data
        );
        $this-&gt;_data = preg_replace(
            '/&lt;\?(?!xml|php)/s',
            '&lt;?php ',
            $this-&gt;_data
        );

        /**
         * file_get_contents() won't update PHP's stat cache, so we grab a stat
         * of the file to prevent additional reads should the script be
         * requested again, which will make include() happy.
         */
        $this-&gt;_stat = stat($path);

        return true;
    }
}
</pre>
<p>The observant of you will notice that the above is almost exactly the same as Zend_View_Stream::stream_open(), but with this bit of code added to it:</p>
<pre class="brush: php; title: ; notranslate">
        $this-&gt;_data = preg_replace(
            '/\&lt;\?\=~ (.*?);? \?&gt;/',
            '&lt;?php echo $this-&gt;escape($1); ?&gt;',
            $this-&gt;_data
        );
</pre>
<p>So if you don&#8217;t like using ~ to do the escape this is where you&#8217;d change it.</p>
<p>Now all you have to do is register your stream before Zend Framework does.  You could do this in your bootstrap file, your application class, or whereever it makes sense for your applicaiton.  Basically, you&#8217;d just be dropping in a line like this:</p>
<pre class="brush: php; title: ; notranslate">stream_register_wrapper('zend.view', 'My_Stream');</pre>
<p>Hopefully that gives you some ideas on extending the stream wrapper even more!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.amnuts.com/2010/10/31/easily-escape-view-variables/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHPNW</title>
		<link>http://blog.amnuts.com/2010/10/09/phpnw/</link>
		<comments>http://blog.amnuts.com/2010/10/09/phpnw/#comments</comments>
		<pubDate>Sat, 09 Oct 2010 18:24:34 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[phpnw]]></category>
		<category><![CDATA[phpnw10]]></category>

		<guid isPermaLink="false">http://blog.amnuts.com/2010/10/09/phpnw/</guid>
		<description><![CDATA[Usually I&#8217;m a total wallflower at conferences, gravitating to only the people I know. This time round I&#8217;m trying to change that and speak to people, ask speakers questions, and all that. Right now, though, I&#8217;m enjoying dinner.]]></description>
			<content:encoded><![CDATA[<p><img style="display: block; margin-right: auto; margin-left: auto;" src="http://blog.amnuts.com/wp-content/uploads/2010/10/wpid-wp-1286648268416.jpg" alt="image" /></p>
<p>Usually I&#8217;m a total wallflower at conferences, gravitating to only the people I know. This time round I&#8217;m trying to change that and speak to people, ask speakers questions, and all that.</p>
<p>Right now, though, I&#8217;m enjoying dinner. <img src='http://blog.amnuts.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://blog.amnuts.com/2010/10/09/phpnw/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHPNW Conference 2010</title>
		<link>http://blog.amnuts.com/2010/10/08/phpnw-conference-2010/</link>
		<comments>http://blog.amnuts.com/2010/10/08/phpnw-conference-2010/#comments</comments>
		<pubDate>Fri, 08 Oct 2010 09:38:22 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[conference]]></category>
		<category><![CDATA[phpnw]]></category>
		<category><![CDATA[phpnw10]]></category>

		<guid isPermaLink="false">http://blog.amnuts.com/?p=197</guid>
		<description><![CDATA[Going to be travelling to the PHPNW Conference today (it&#8217;s tomorrow, but I don&#8217;t fancy catching the stupidly early train to get there on time), but going over the schedule is a pain&#8230; There are just too many good talks! How can I possibly go see them all?! The 11:15 time slot is easy, that&#8217;ll [...]]]></description>
			<content:encoded><![CDATA[<p>Going to be travelling to the PHPNW Conference today (it&#8217;s tomorrow, but I don&#8217;t fancy catching the stupidly early train to get there on time), but going over the schedule is a pain&#8230; There are just too many good talks!  How can I possibly go see them all?!  The 11:15 time slot is easy, that&#8217;ll be <a href="http://conference.phpnw.org.uk/phpnw10/zend-framework-2-0-is-coming">Rob Allen&#8217;s talk on ZF 2</a> &#8211; we use it so much at work now that it&#8217;d be crazy to not find out what&#8217;s coming and how this may affect what we&#8217;re currently doing.  Same with the  <a href="http://conference.phpnw.org.uk/phpnw10/unit-testing-after-zend-framework-1-8">Michelangelo van Dam talk on unit testing with ZF</a>.  But the 12:15, 15:00 and 16:15 talks?  I have no idea what to choose!  <a href="http://conference.phpnw.org.uk/phpnw10/optimizing-zend-framework">Juozas Kaziukena&#8217;s Optimizing Zend Framework</a> might be worth while, but then again, is it all about ZF1 and how much will be relevant for ZF2?  The <a href="http://conference.phpnw.org.uk/phpnw10/practical-hiphop">HipHop talk by Scott McVicar</a> would be interesting. I can&#8217;t see it being deployed at my work, should still be a good talk&#8230; I&#8217;m liking the sound of the <a href="http://conference.phpnw.org.uk/phpnw10/database-version-control-without-pain">Database version control without pain by Harrie Verveer</a> as well!  And that still leaves me with two other time slots to decide on&#8230; Sheesh!</p>
<p>The agony of choice, eh? <img src='http://blog.amnuts.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://blog.amnuts.com/2010/10/08/phpnw-conference-2010/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>QrCode view helper</title>
		<link>http://blog.amnuts.com/2010/02/10/qrcode-view-helper/</link>
		<comments>http://blog.amnuts.com/2010/02/10/qrcode-view-helper/#comments</comments>
		<pubDate>Wed, 10 Feb 2010 16:36:16 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[qrcode]]></category>
		<category><![CDATA[zend_view]]></category>
		<category><![CDATA[zend_view_helper]]></category>

		<guid isPermaLink="false">http://blog.amnuts.com/?p=166</guid>
		<description><![CDATA[You see QrCodes popping up every now and again on sites, in publications and the like. I think they can be a very handy way for people with cameras on their phones to get a url or other content on to their phone very easily. (I&#8217;m thinking more about those people without iPhones or full [...]]]></description>
			<content:encoded><![CDATA[<p>You see QrCodes popping up every now and again on sites, in publications and the like.  I think they can be a very handy way for people with cameras on their phones to get a url or other content on to their phone very easily.  (I&#8217;m thinking more about those people without iPhones or full keyboards, of course!)</p>
<p>If you&#8217;ve never seen a QrCode before, it looks something like this:</p>
<p><img src="http://chart.apis.google.com/chart?cht=qr&amp;chl=http%3A%2F%2Fblog.amnuts.com%2F&amp;chld=M|0&amp;chs=100x100" alt="QR Code image" /></p>
<p>Now how cool would it be to be able to generate that automatically for each page on your site and allow people to be able bookmark that site on their phone?  Well, <strong><em>I</em></strong> think it&#8217;d be pretty cool!  So I came up with a very simple ZF view helper to do it for me.</p>
<p><span id="more-166"></span>This is the view helper code:</p>
<pre class="brush: php; title: ; notranslate">&lt;?php

/**
 * Output a QR code block.
 *
 * Currently, only via Google Chart API is supported, but it has
 * room to add other sources of qrcode generation.
 *
 * @category   Amnuts
 * @package    Amnuts_View
 * @subpackage Amnuts_View_Helper
 */
class Amnuts_View_Helper_QrCode extends Zend_View_Helper_Abstract
{
    protected $template = '
        &lt;div class=&quot;qrcode&quot;&gt;
            &lt;p&gt;Bookmark this page on your mobile&lt;/p&gt;
            &lt;img src=&quot;%s&quot; alt=&quot;QR Code image&quot; /&gt;
            &lt;p class=&quot;hint&quot;&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/QR_Code&quot;&gt;What is this?&lt;/a&gt;&lt;/p&gt;
        &lt;/div&gt;
        ';

    /**
     * Constructor.
     *
     * @return Amnuts_View_Helper_QrCode
     */
    public function qrCode($template = null)
    {
        if (null !== $template) {
            $this-&gt;template = $template;
        }
        return $this;
    }

    /**
     * Generate the QR code image via Google's Chart API.
     *
     * @param  array  $params
     * @return string
     */
    public function google($params = array())
    {
        $default = array(
            'text'       =&gt; $_SERVER['SCRIPT_URI'],
            'size'       =&gt; '100x100',
            'correction' =&gt; 'M',
            'margin'     =&gt; 0
        );
        $params = array_merge($default, $params);

        $params['text']   = urlencode($params['text']);
        $params['margin'] = (int)$params['margin'];
        if (!in_array($params['correction'], array('L', 'M', 'Q', 'H'))) {
            $params['correction'] = 'M';
        }
        if (!preg_match('/^\d+x\d+$/', $params['size'])) {
            $params['size'] = '100x100';
        }

        $url = &quot;http://chart.apis.google.com/chart?cht=qr&amp;chl={$params['text']}&quot;
             . &quot;&amp;chld={$params['correction']}|{$params['margin']}&quot;
             . &quot;&amp;chs={$params['size']}&quot;;
        return sprintf($this-&gt;template, $url);
    }
}
</pre>
<p>As it&#8217;s a view helper, it&#8217;s very simple to use within your view.  Just do something like:</p>
<pre class="brush: php; title: ; notranslate">&lt;?php echo $this-&gt;qrCode()-&gt;google(); ?&gt;</pre>
<p>If you want to change the default template for one instance, you can do that such as:</p>
<pre class="brush: php; title: ; notranslate">&lt;?php echo $this-&gt;qrCode('&lt;li&gt;&lt;img src=&quot;%s&quot; /&gt;&lt;/li&gt;')-&gt;google(); ?&gt;</pre>
<p>And, of course, you can change the parameters that the QrCode uses, too.  By default, if you don&#8217;t supply the &#8216;text&#8217; parameter then it will use the script uri.  But you could change that with something like:</p>
<pre class="brush: php; title: ; notranslate">&lt;?php echo $this-&gt;qrCode()-&gt;google(array('text' =&gt; 'Visit my site at: http://blog.amnuts.com/')); ?&gt;</pre>
<p>There are other options as view helper code shows.  But this should get you started on easily using QrCodes on your own site.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.amnuts.com/2010/02/10/qrcode-view-helper/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Adding new items to RSS feed &#8211; it shouldn&#8217;t be this hard!</title>
		<link>http://blog.amnuts.com/2010/01/28/adding-new-items-to-rss-feed-it-shouldnt-be-this-hard/</link>
		<comments>http://blog.amnuts.com/2010/01/28/adding-new-items-to-rss-feed-it-shouldnt-be-this-hard/#comments</comments>
		<pubDate>Thu, 28 Jan 2010 22:57:25 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[rss]]></category>
		<category><![CDATA[zend_feed]]></category>
		<category><![CDATA[zend_feed_writer]]></category>

		<guid isPermaLink="false">http://blog.amnuts.com/?p=155</guid>
		<description><![CDATA[I have just started to use the Zend_Feed related components in earnest and am really liking the Zend_Feed_Writer (new to ZF 1.10.0). So what I wanted to do was created an RSS feed file is one didn&#8217;t exist and then keep updating that file as-and-when new items came in. Seems a really easy and simple [...]]]></description>
			<content:encoded><![CDATA[<p>I have just started to use the Zend_Feed related components in earnest and am really liking the Zend_Feed_Writer (new to ZF 1.10.0).  So what I wanted to do was created an RSS feed file is one didn&#8217;t exist and then keep updating that file as-and-when new items came in.  Seems a really easy and simple thing to do, right?  That, unfortunately, has not been my experience.</p>
<p>I have to say that to documentation seems quite lacking on the ZF site (for all the the Feed components, really, not just the Writer).  Because of that, what follows may be idiotic and there really is an easy way.  If so, I hope that you will post up a comment and let me know because I&#8217;d love to learn!</p>
<p>On with what I did&#8230;</p>
<p><span id="more-155"></span><br />
My set up is actually to take an email sent to a particular address and add the contents to an RSS feed as-and-when the email arrives.  So when the first email comes in the RSS feed file needs to be created.  Using the Zend_Feed_Writer_Feed component, this is a really easy job.  Once saved the XML looks well structured and everything is as expected.  But then what happens when the next email comes in?  Obviously I wouldn&#8217;t want to create a new feed file because it&#8217;d remove any previous entries.  Also, I want to tweak the pubDate so that it reflects when this new email came in.  I also wanted to use the Zend_Feed_Writer_Entry component so that the structure of the new item matches the previous ones.</p>
<p>The first thing to do was to load the RSS file and tweak the pubDate.</p>
<pre class="brush: php; title: ; notranslate">
$now = new DateTime();
$now-&gt;setTimestamp(time());
$feed = new Zend_Feed_Rss(null, file_get_contents($feedFile));
$feed-&gt;pubDate = $now-&gt;format(DATE_RSS);
</pre>
<p>Then I created the entry:</p>
<pre class="brush: php; title: ; notranslate">
$entry = new Zend_Feed_Writer_Entry();
$entry-&gt;setId($entryId);
$entry-&gt;setTitle($emailSubject);
$entry-&gt;addAuthor(array(
    'name'  =&gt; $emailFromName,
    'email' =&gt; $emailFormAddress
));
$entry-&gt;setDateCreated($emailDate-&gt;getTimestamp());
$entry-&gt;setContent($emailBody);
</pre>
<p>At this point it would be <strong><em>really</em></strong> nice to have some kind of Zend_Feed_Rss::addEntry(Zend_Feed_Writer_Entry|string of entry xml), or ::appendEntry()/::prependEntry(), but I was not able to see anything of the sort.  So what I did was to create a DOMDocument with the feed file contents, render the entry and append it to the channel node.</p>
<pre class="brush: php; title: ; notranslate">
$rss = new DOMDocument();
$rss-&gt;loadXML($feed-&gt;saveXML());
$rss-&gt;formatOutput = true;
$rss-&gt;substituteEntities = false;

$renderer = new Zend_Feed_Writer_Renderer_Entry_Rss($entry);
$renderer-&gt;setRootElement($rss-&gt;documentElement);
$renderer-&gt;render();
$element = $renderer-&gt;getElement();

$channel  = $rss-&gt;getElementsByTagName('channel')-&gt;item(0);
$imported = $rss-&gt;importNode($element, true);
$channel-&gt;appendChild($imported);

file_put_contents($feedFile, $rss-&gt;saveXML());
</pre>
<p>Now, that&#8217;s not a lot of code, but does it seem even remotely logical to do that when there&#8217;s already a fairly inclusive API for the feeds &#8211; perhaps just not inclusive enough?</p>
<p>Like I mentioned at the start, though; if you know a better way then please let me know &#8211; I&#8217;m always happy to learn!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.amnuts.com/2010/01/28/adding-new-items-to-rss-feed-it-shouldnt-be-this-hard/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>PHP Team Development (book)</title>
		<link>http://blog.amnuts.com/2009/09/30/php-team-development-book/</link>
		<comments>http://blog.amnuts.com/2009/09/30/php-team-development-book/#comments</comments>
		<pubDate>Wed, 30 Sep 2009 09:13:57 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[book]]></category>
		<category><![CDATA[packt]]></category>

		<guid isPermaLink="false">http://blog.amnuts.com/?p=136</guid>
		<description><![CDATA[The other day I had a new book sent to me called PHP Team Development, written by Samisa Abeysinghe and published by Packt Publishing. Unfortunately, it arrived at work when I was on holiday so I haven&#8217;t been able to have a look at it yet. :-/ However, I&#8217;m back today and have the book [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.packtpub.com/php-team-development?utm_source=blog.amnuts.com&amp;utm_medium=link&amp;utm_content=blog&amp;utm_campaign=mdb_000684"><img style="float:right;border:0;margin:0 0 10px 10px;" src="https://www.packtpub.com/images/100x123/1847195067.png" alt="Book cover" /></a>The other day I had a new book sent to me called <a href="http://www.packtpub.com/php-team-development?utm_source=blog.amnuts.com&amp;utm_medium=link&amp;utm_content=blog&amp;utm_campaign=mdb_000684">PHP Team Development</a>, written by Samisa Abeysinghe and published by Packt Publishing.  Unfortunately, it arrived at work when I was on holiday so I haven&#8217;t been able to have a look at it yet. :-/  However, I&#8217;m back today and have the book in my hands  (well, not literally, of course, else typing would be much more difficult), so am looking forward to diving in to it.</p>
<p>Hopefully have a bit of a review posted up here some time soon!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.amnuts.com/2009/09/30/php-team-development-book/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Shorten urls automatically with a Zend Framework filter</title>
		<link>http://blog.amnuts.com/2009/02/15/shorten-urls-automatically-with-a-zend-framework-filter/</link>
		<comments>http://blog.amnuts.com/2009/02/15/shorten-urls-automatically-with-a-zend-framework-filter/#comments</comments>
		<pubDate>Sun, 15 Feb 2009 01:24:10 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[filter]]></category>

		<guid isPermaLink="false">http://blog.amnuts.com/?p=123</guid>
		<description><![CDATA[I think we can all agree that URL shortening services are great and are very handy to tidy up those long and obnoxious links. However, a lot of the time people simply forget to use them, or often don&#8217;t know about them in the first place. I&#8217;ve noticed this in a blog system I wrote [...]]]></description>
			<content:encoded><![CDATA[<p>I think we can all agree that URL shortening services are great and are very handy to tidy up those long and obnoxious links.  However, a lot of the time people simply forget to use them, or often don&#8217;t know about them in the first place.  I&#8217;ve noticed this in a blog system I wrote using Zend Framework.  On one hand I love that people post messages, but on the other it annoys me that they may supply a link that is so long it breaks the formatting of the page, or looks just plain ugly.</p>
<p>So what are my options?  I could train everyone who posts blogs on the system to use a url shortening service or I could manually tweak all the links myself.  As solutions they are not very practical at all; I don&#8217;t have the time to change any/all links myself, and I certainly don&#8217;t have enough patience to train everyone!  So an automatic way of doing things is needed, and the filtering in Zend Framework comes to the rescue!</p>
<p><span id="more-123"></span><br />
Zend Framework has a lot of filters that you can use right out of the box.  They do all sorts such as string tags, newlines, camel case strings, and so on.  The framework also provides an interface so you can easily create your own filters.  For example, <a href="/2008/06/05/stringtotitle-filter/">converting a string to title case</a>.  With that in mind you could see how easy it&#8217;d be to create a filter to scan for urls and shorten them.  And that&#8217;s exactly what this filter does!  It will scan the supplied text for any http or https urls, and then, if they are past a certain size, it will attempt to connect to a url shortening service and then replace the long urls with the short.</p>
<pre lang="php">
/**
 * Shorten urls found in text.
 *
 * This filter will scan the supplied text for any http or https urls, and
 * then, if they are past a certain size, it will attempt to connect to a
 * url shortening service and then replace the long urls with the short.
 *
 * Two services endpoints are in the class - tinyurl and is.gd - but there's
 * nothing to stop you supplying a different end point, so long as the format
 * of the end point has one string directive (%s) and the service simply
 * returns a short url.
 *
 * @copyright Copyright (c) 2009, Andrew Collington
<php @amnuts.com>
 * @license New BSD License
 */
class Amnuts_Filter_ShortenUrls implements Zend_Filter_Interface
{
    const REGEX = "~(?:https?://(?:(?:(?:(?:(?:[a-zA-Z\d](?:(?:[a-zA-Z\d]|-)*[a-zA-Z\d])?)\.)*(?:[a-zA-Z](?:(?:[a-zA-Z\d]|-)*[a-zA-Z\d])?))|(?:(?:\d+)(?:\.(?:\d+)){3}))(?::(?:\d+))?)(?:/(?:(?:(?:(?:[a-zA-Z\d$\-_.+!*'(),]|(?:%[a-fA-F\d]{2}))|[;:@&#038;=])*)(?:/(?:(?:(?:[a-zA-Z\d$\-_.+!*'(),]|(?:%[a-fA-F\d]{2}))|[;:@&#038;=])*))*)(?:\?(?:(?:(?:[a-zA-Z\d$\-_.+!*'(),]|(?:%[a-fA-F\d]{2}))|[;:@&#038;=])*))?)?)~";
    const SERVICE_ISGD    = 'http://is.gd/api.php?longurl=%s';
    const SERVICE_TINYURL = 'http://tinyurl.com/api-create.php?url=%s';

    /**
     * Options array containing:
     *
     *    - maxsize
     *      Maximum size the url can be before attempting to shorten it
     *    - service
     *      The url to the service API that returns a short url
     *    - timeout
     *      The maximum time allowed for a connection
     *
     * @var array
     */
    public $options = array(
        'maxsize' => 35,
        'service' => self::SERVICE_ISGD,
        'timeout' => 5
    );

    /**
     * Set options on construct
     *
     * @param array $options
     */
    public function __construct(array $options = array())
    {
        if (!empty($options)) {
            $this->options = array_merge($this->options, $options);
        }
    }

    /**
     * Filter the text.
     *
     * The filtering may take some time, depending on how many links are in
     * the text to be shortened, how responsive the service is, etc.
     *
     * @param string $text
     * @return string
     * @see Zend_Http_Client, Zend_Http_Response
     */
    public function filter($text)
    {
        $matches = $replacements = array();
        $matched = preg_match_all(self::REGEX, $text, $matches, PREG_PATTERN_ORDER);
        if ($matched) {
            $http = new Zend_Http_Client();
            foreach ($matches[0] as $url) {
                if (strlen($url) > $this->options['maxsize']) {
                    $http->setConfig(array(
                        'timeout' => $this->options['timeout']
                    ));
                    $http->setUri(
                        sprintf($this->options['service'], urlencode($url))
                    );
                    $response = $http->request(Zend_Http_Client::GET);
                    if ($response->isSuccessful()) {
                        $replacements[$url] = $response->getBody();
                    }
                    $http->resetParameters();
                }
            }
        }
        if (empty($replacements)) {
            return $text;
        }
        return str_replace(
            array_keys($replacements),
            array_values($replacements),
            $text
        );
    }
}
</php></pre>
<p>It&#8217;s very easy to use, too:</p>
<pre lang="php">
$shorten = new Amnuts_Filter_ShortenUrls();
$updated = $shorten->filter($text);
</pre>
<p>Here&#8217;s an example of using tinyURL as the service and only converting urls in the text that are longer than 50 characters:</p>
<pre lang="php">
$shorten = new Amnuts_Filter_ShortenUrls(array(
    'maxsize' => 50,
    'service' => Amnuts_Filter_ShortenUrls::SERVICE_TINYURL
));
$updated = $shorten->filter($text);
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.amnuts.com/2009/02/15/shorten-urls-automatically-with-a-zend-framework-filter/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Quick and easy email encoding view helper</title>
		<link>http://blog.amnuts.com/2008/09/23/quick-and-easy-email-encoding-view-helper/</link>
		<comments>http://blog.amnuts.com/2008/09/23/quick-and-easy-email-encoding-view-helper/#comments</comments>
		<pubDate>Tue, 23 Sep 2008 11:17:27 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[Code snippets]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[simple]]></category>
		<category><![CDATA[view helper]]></category>

		<guid isPermaLink="false">http://blog.amnuts.com/?p=73</guid>
		<description><![CDATA[Here&#8217;s a quick and easy view helper for Zend Framework that will encode an email address. It will encode just an email address or return a whole mailto link. The encoding is basically the same as in the Smarty template engine. Obviously there&#8217;s a lot of room for improvement; javascript encoding, representation as an image, [...]]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s a quick and easy view helper for Zend Framework that will encode an email address.  It will encode just an email address or return a whole mailto link.  The encoding is basically the same as in the Smarty template engine.</p>
<p>Obviously there&#8217;s a lot of room for improvement; javascript encoding, representation as an image, and so on&#8230; but then it wouldn&#8217;t be quick an easy &#8211; it&#8217;d be slightly longer and just a little more complex. <img src='http://blog.amnuts.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<p><span id="more-73"></span></p>
<pre class="brush: php; title: ; notranslate">
class Amnuts_View_Helper_EncodeEmail extends Zend_View_Helper_Abstract
{
    /**
     * Return the class on direct calls.
     *
     * @return Amnuts_View_Helper_EncodeEmail
     */
    public function encodeEmail()
    {
        return $this;
    }

    /**
     * Return an encoded email address to help against spam.
     *
     * @param string $address The email address to encode
     * @return string
     */
    public function address($address)
    {
        $xhtml = '';
        $address = (string)$address;
        $len = strlen($address);
        for ($x = 0; $x &lt; $len; $x++) {
            if (preg_match('!\w!', $address[$x])) {
                $xhtml .= '%' . bin2hex($address[$x]);
            } else {
                $xhtml .= $address[$x];
            }
        }
        return $xhtml;
    }

    /**
     * Return an encoded mailto link to help against spam.
     *
     * If no link text is supplied then the email address will be used
     * (as is generally preferred).
     *
     * @param string $address The email address to encode
     * @param null|string $text Text to use for link
     * @param array $attrs Any extra attributes for link
     * @return string
     */
    public function link($address, $text = null, array $attrs = array())
    {
        $encodedtext    = '';
        $encodedaddress = $this-&gt;address($address);
        if (null === $text) {
            $text = (string)$address;
        } else {
            $text = (string)$text;
        }
        $len = strlen($text);
        for ($x = 0; $x &lt; $len; $x++) {
            $encodedtext .= '&amp;#x' . bin2hex($text[$x]).';';
        }
        $xhtml = '&lt;a href=&quot;&amp;#109;&amp;#97;&amp;#105;&amp;#108;&amp;#116;&amp;#111;&amp;#58;' . $encodedaddress . '&quot;';
        if (!empty($attrs)) {
            foreach ($attrs as $k =&gt; $v) {
                $xhtml .= ' ' . $this-&gt;view-&gt;escape($k) . '=&quot;' . $this-&gt;view-&gt;escape($v) . '&quot;';
            }
        }
        $xhtml .= '&gt;' . $encodedtext . '';
        return $xhtml;
    }
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.amnuts.com/2008/09/23/quick-and-easy-email-encoding-view-helper/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

