<?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, 07 May 2010 09:11:14 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<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 [...]]]></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;">&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;">&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;">&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;">&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 [...]]]></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;">
$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;">
$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;">
$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 [...]]]></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 [...]]]></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 [...]]]></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;">
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>
		<item>
		<title>Tag cloud view helper</title>
		<link>http://blog.amnuts.com/2008/06/11/tag-cloud-view-helper/</link>
		<comments>http://blog.amnuts.com/2008/06/11/tag-cloud-view-helper/#comments</comments>
		<pubDate>Wed, 11 Jun 2008 13:43:16 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[Code snippets]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[tag cloud]]></category>
		<category><![CDATA[view helper]]></category>

		<guid isPermaLink="false">http://blog.amnuts.com/?p=66</guid>
		<description><![CDATA[Here&#8217;s a little view helper to display a tag cloud.  All you have to do is supply an array of tags, with the tag name being the index and how many times it&#8217;s used as the value, and the url you&#8217;d like the tags to go to.


&#60; ?php

/**
 * Display a tag cloud
 *
 [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://blog.amnuts.com/wp-content/uploads/2008/06/tagcloud.gif"><img class="alignright size-full wp-image-67" title="Tag cloud example" src="http://blog.amnuts.com/wp-content/uploads/2008/06/tagcloud.gif" alt="" width="222" height="180" /></a>Here&#8217;s a little view helper to display a tag cloud.  All you have to do is supply an array of tags, with the tag name being the index and how many times it&#8217;s used as the value, and the url you&#8217;d like the tags to go to.</p>
<p><span id="more-66"></span></p>
<div style="clear:both;"></div>
<pre class="brush: php;">&lt; ?php

/**
 * Display a tag cloud
 *
 * @category   Amnuts
 * @package    Amnuts_View
 * @subpackage Amnuts_View_Helper
 * @copyright  Copyright (c) 2008 Andrew Collington (http://www.amnuts.com/)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Amnuts_View_Helper_TagCloud
{
    public $view;

    /**
     * Output the tag cloud.
     *
     * The $tags parameter is expected to be an array with the tag being the
     * index and the number of times it's used as the value.  For example:
     *
     *     array(
     *         'foo' =&gt; 3,
     *         'bar' =&gt; 1,
     *         'dog' =&gt; 5,
     *         'cat' =&gt; 1
     *     )
     *
     * The url will have the tag text appended to it, so that if you supply
     * '/filter/by/tag/' as the url, then given the above array you will have:
     *
     *     &lt;a href=&quot;/filter/by/tag/foo&quot;&gt;foo&lt;/a&gt;
     *     &lt;a href=&quot;/filter/by/tag/bar&quot;&gt;bar&lt;/a&gt;
     *
     * and so on.
     *
     * @param array $tags The tag array
     * @param string $url The link for each tag (with the tag name appended)
     * @param int|string $minFont The minimum font value
     * @param int|string $maxFont The maximum font value
     * @param string $unit The unit of size type (%, em, px, etc.)
     * @return string
     */
    public function tagCloud(array $tags, $url, $minFont = 100, $maxFont = 150, $unit = '%')
    {
        $xhtml = '';
        $cloud = array();

        if (!empty($tags)) {
            $min  = min(array_values($tags));
            $max  = max(array_values($tags));
            $diff = $max - $min;
            if (!$diff) {
                ++$diff;
            }

            foreach ($tags as $tag =&gt; $count) {
                $size = $minFont + ($count - $min) * ($maxFont - $minFont) / $diff;
                $cloud[] = '&lt;a href=&quot;' . $url . urlencode($tag)
                         . '&quot; style=&quot;font-size:' . $size . $unit . ';&quot;&gt;'
                         . $this-&gt;view-&gt;escape($tag) . '&lt;/a&gt;';
            }
            $xhtml .= '&lt;div class=&quot;tagCloudContainer&quot;&gt;&lt;h4&gt;Tag cloud&lt;/h4&gt;' . join(', ', $cloud) . &quot;&lt;/div&gt;\n&quot;;
        }
        return $xhtml;
    }

    /**
     * Set the view object
     *
     * @param Zend_View_Interface $view
     */
    public function setView(Zend_View_Interface $view)
    {
        $this-&gt;view = $view;
    }
}

?&gt;</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.amnuts.com/2008/06/11/tag-cloud-view-helper/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>StringToTitle filter</title>
		<link>http://blog.amnuts.com/2008/06/05/stringtotitle-filter/</link>
		<comments>http://blog.amnuts.com/2008/06/05/stringtotitle-filter/#comments</comments>
		<pubDate>Thu, 05 Jun 2008 12:38:14 +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=65</guid>
		<description><![CDATA[I like the filtering capabilities of the Zend Framework, but for some reason there doesn&#8217;t seem to be a string to title case filter (though there is a string to upper and string to lower).  So here it is:

&#60; ?php

/**
 * @see Zend_Filter_Interface
 */
require_once 'Zend/Filter/Interface.php';

/**
 * Convert a string to titlecase.
 *
 * Seemingly [...]]]></description>
			<content:encoded><![CDATA[<p>I like the filtering capabilities of the Zend Framework, but for some reason there doesn&#8217;t seem to be a string to title case filter (though there is a string to upper and string to lower).  So here it is:</p>
<p><span id="more-65"></span></p>
<pre class="brush: php;">&lt; ?php

/**
 * @see Zend_Filter_Interface
 */
require_once 'Zend/Filter/Interface.php';

/**
 * Convert a string to titlecase.
 *
 * Seemingly missing from the core Zend distribution.
 */
class StringToTitle implements Zend_Filter_Interface
{
    /**
     * Encoding for the input string
     *
     * @var string
     */
    protected $_encoding = null;

    /**
     * Set the input encoding for the given string
     *
     * @param  string $encoding
     * @throws Zend_Filter_Exception
     */
    public function setEncoding($encoding = null)
    {
        if (!function_exists('mb_convert_case')) {
            require_once 'Zend/Filter/Exception.php';
            throw new Zend_Filter_Exception('mbstring is required for this feature');
        }
        $this-&gt;_encoding = $encoding;
    }

    /**
     * Defined by Zend_Filter_Interface
     *
     * Returns the string $value, converting characters to titlecase as necessary
     *
     * @param  string $value
     * @return string
     */
    public function filter($value)
    {
        if ($this-&gt;_encoding) {
            return mb_convert_case((string) $value, MB_CASE_TITLE, $this-&gt;_encoding);
        }
        return ucwords((string) $value);
    }
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.amnuts.com/2008/06/05/stringtotitle-filter/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Auto generating basic models for a Zend Framework app</title>
		<link>http://blog.amnuts.com/2008/04/11/auto-generating-basic-models-for-a-zend-framework-app/</link>
		<comments>http://blog.amnuts.com/2008/04/11/auto-generating-basic-models-for-a-zend-framework-app/#comments</comments>
		<pubDate>Fri, 11 Apr 2008 13:18:39 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[Code snippets]]></category>
		<category><![CDATA[Experiments]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[models]]></category>

		<guid isPermaLink="false">http://blog.amnuts.com/2008/04/11/auto-generating-basic-models-for-a-zend-framework-app/</guid>
		<description><![CDATA[Do you have a database with foreign keys and just wish you could have something automatically create your ZF models from it? Well, today that was me. So as a little proof of concept, this is the code I came up with to do it for me&#8230;
But before we get to that, a few caveats:

It&#8217;s [...]]]></description>
			<content:encoded><![CDATA[<p>Do you have a database with foreign keys and just wish you could have something automatically create your ZF models from it? Well, today that was me. So as a little proof of concept, this is the code I came up with to do it for me&#8230;</p>
<p>But before we get to that, a few caveats:</p>
<ul>
<li>It&#8217;s just a proof of concept</li>
<li>The output needs updating for proper reference names, etc.</li>
<li>Outputs everything to screen in one go and doesn&#8217;t save the files.</li>
</ul>
<p>However, it might be handy to someone, so I post it up for your comments.<br />
<span id="more-63"></span></p>
<pre class="brush: php;">&lt; ?php

require_once 'Zend/Loader.php';
Zend_Loader::registerAutoload();

$config = array(
	'host'	 =&gt; 'localhost',
	'username' =&gt; 'myusername',
	'password' =&gt; 'mypassword',
	'dbname'   =&gt; 'mydbname'
);
try {
	$db = Zend_Db::factory('pdo_mysql', $config);
} catch (Zend_Db_Exception $e) {
	echo $e-&gt;getMessage();
	die;
}

$model = &lt; &lt;&lt;EOT
&lt;?php

class %s extends Zend_Db_Table_Abstract
{
	protected \$_name = '%s';
%s
}
EOT;

$refmap_outer = &lt;&lt;&lt;EOT
	protected \$_referenceMap	= array(
%s
	);
EOT;

$refmap_inner = &lt;&lt;&lt;EOT
		'%s' =&gt; array(
			'columns'		   =&gt; array('%s'),
			'refTableClass'	 =&gt; '%s',
			'refColumns'		=&gt; array('%s')
		)
EOT;

$toTable = new Zend_Filter_Inflector(
	':tbl',
	array(':tbl' =&gt; array('Word_CamelCaseToUnderscore', 'StringToLower'))
);

$toClass = new Zend_Filter_Inflector(
	':tbl',
	array(':tbl' =&gt; array('StringToLower', 'Word_UnderscoreToCamelCase'))
);

foreach ($db-&gt;listTables() as $table) {
	$sql = &quot;
		select
			tc.constraint_name,
			kcu.table_name,
			kcu.column_name,
			kcu.referenced_table_name,
			kcu.referenced_column_name
		from
			information_schema.table_constraints tc,
			information_schema.key_column_usage kcu
		where
			tc.table_name = &quot; . $db-&gt;quote($table) . &quot;
			and tc.constraint_type = 'FOREIGN KEY'
			and kcu.constraint_name = tc.constraint_name
		&quot;;
	$keys = $db-&gt;fetchAll($sql);

	$refs = array();
	if (!empty($keys)) {
		$r = 0;
		foreach ($keys as $key) {
			$refs[] = sprintf($refmap_inner,
					'ref' . ++$r,
					$key['column_name'],
					$toClass-&gt;filter(array('tbl' =&gt; $key['referenced_table_name'])),
					$key['referenced_column_name']
				);
		}
	}
	printf($model,
		$toClass-&gt;filter(array('tbl' =&gt; $table)),
		$toTable-&gt;filter(array('tbl' =&gt; $table)),
		(!empty($refs) ? sprintf($refmap_outer, join(&quot;,\n&quot;, $refs)) : '')
	);
}
</pre>
<p>So assuming I had a database structure like:</p>
<pre class="brush: sql;">--
-- Table structure for table `users`
--
CREATE TABLE `users` (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `added` datetime NOT NULL,
  `username` varchar(32) NOT NULL,
  `password` varchar(64) NOT NULL,
  `name_first` varchar(32) NOT NULL,
  `name_last` varchar(32) NOT NULL,
  `name_nick` varchar(32) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Table structure for table `user_tags`
--
CREATE TABLE `user_tags` (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `user_id` int(10) UNSIGNED NOT NULL,
  `tag_user_id` int(10) UNSIGNED NOT NULL,
  `tag` varchar(24) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `tag` (`tag`),
  KEY `fk_user_tags_user_id` (`user_id`),
  KEY `fk_user_tags_tag_user_id` (`tag_user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Constraints for table `user_tags`
--
ALTER TABLE `user_tags`
  ADD CONSTRAINT `fk_user_tags_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  ADD CONSTRAINT `fk_user_tags_tag_user_id` FOREIGN KEY (`tag_user_id`) REFERENCES `users` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION;</pre>
<p>it would output something like:</p>
<pre class="brush: php;">&lt; ?php

class UserTags extends Zend_Db_Table_Abstract
{
	protected $_name = 'user_tags';
	protected $_referenceMap	= array(
		'ref1' =&gt; array(
			'columns'		   =&gt; array('user_id'),
			'refTableClass'	 =&gt; 'Users',
			'refColumns'		=&gt; array('id')
		),
		'ref2' =&gt; array(
			'columns'		   =&gt; array('tag_user_id'),
			'refTableClass'	 =&gt; 'Users',
			'refColumns'		=&gt; array('id')
		)
	);
}

&lt;?php

class Users extends Zend_Db_Table_Abstract
{
	protected $_name = 'users';
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.amnuts.com/2008/04/11/auto-generating-basic-models-for-a-zend-framework-app/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Zend Framework 1.5 preview release</title>
		<link>http://blog.amnuts.com/2008/01/29/zend-framework-15-preview-release/</link>
		<comments>http://blog.amnuts.com/2008/01/29/zend-framework-15-preview-release/#comments</comments>
		<pubDate>Tue, 29 Jan 2008 09:55:46 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://blog.amnuts.com/2008/01/29/zend-framework-15-preview-release/</guid>
		<description><![CDATA[For those that don&#8217;t know by now, version 1.5 of the Zend Framework is now out in preview release.  Congratulations to everyone who has had apart in getting out this release &#8211; from programmers to documentation writers to project managers!
There are a lot of very interesting updates and new features.  Some notable ones [...]]]></description>
			<content:encoded><![CDATA[<p>For those that don&#8217;t know by now, <a href="http://devzone.zend.com/article/3020-Zend-Framework-1.5.0-Preview-Release-now-available">version 1.5 of the Zend Framework is now out in preview release</a>.  Congratulations to everyone who has had apart in getting out this release &#8211; from programmers to documentation writers to project managers!</p>
<p>There are a lot of very interesting updates and new features.  Some notable ones are the inclusion of Zend_Form, Zend_Layout, OpenID and LDAP adapters for authentication, Technorati web service, as well has handy tweaks Zend_Db_Table such as being able to directly access the select object.</p>
<p>As it&#8217;s a preview release the code isn&#8217;t intended for production systems just yet, though I hope the time frame for getting it to stable release is short enough so that I can use it soon, but long enough to work out any major kinks. <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/2008/01/29/zend-framework-15-preview-release/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Force a file download</title>
		<link>http://blog.amnuts.com/2007/10/24/force-a-file-download/</link>
		<comments>http://blog.amnuts.com/2007/10/24/force-a-file-download/#comments</comments>
		<pubDate>Wed, 24 Oct 2007 12:03:01 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[Code snippets]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[snippet]]></category>

		<guid isPermaLink="false">http://blog.amnuts.com/2007/10/24/force-a-file-download/</guid>
		<description><![CDATA[Here&#8217;s a small function that will allow you to force a file download.
/**
 * Force a file download via HTTP.
 *
 * File is required to be on the same server and accessible via a path.
 * If the file cannot be found or some other error occurs then a
 * '204 No content' header [...]]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s a small function that will allow you to force a file download.</p>
<pre class="brush: php;">/**
 * Force a file download via HTTP.
 *
 * File is required to be on the same server and accessible via a path.
 * If the file cannot be found or some other error occurs then a
 * '204 No content' header is sent.
 *
 * @param string $path Path and file name
 * @param string $name Name of file when saved on user's computer,
 *                     null for basename from path
 * @param string $type Content type header info (e.g., 'application/vnd.ms-excel')
 * @return void
 * @access public
 */
/* public static */ function download($path, $name = null, $type = 'binary/octet-stream')
{
    if (headers_sent()) {
        echo 'File download failure: HTTP headers have already been sent and cannot be changed.';
        exit;
    }

    $path = realpath($path);
    if ($path === false || !is_file($path) || !is_readable($path)) {
        header('HTTP/1.0 204 No Content');
        exit;
    }

    $name = (empty($name)) ? basename($path) : $name;
    $size = filesize($path);

    header('Expires: Mon, 20 May 1974 23:58:00 GMT');
    header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
    header('Cache-Control: no-store, no-cache, must-revalidate');
    header('Cache-Control: post-check=0, pre-check=0', false);
    header('Cache-Control: private');
    header('Pragma: no-cache');
    header(&quot;Content-Transfer-Encoding: binary&quot;);
    header(&quot;Content-type: {$type}&quot;);
    header(&quot;Content-length: {$size}&quot;);
    header(&quot;Content-disposition: attachment; filename=\&quot;{$name}\&quot;&quot;);
    readfile($path);
    exit;
}</pre>
<p>Very easy to use, too!  Here are some examples of how you might call the function:</p>
<pre class="brush: php;">download('./myfile.txt');

download(__FILE__, 'a file for you.php');

download('/home/you/files/spreadsheet.xml', 'ssheet_' . date('Ymd'), 'application/vnd.ms-excel');</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.amnuts.com/2007/10/24/force-a-file-download/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
