<?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; Code snippets</title>
	<atom:link href="http://blog.amnuts.com/category/snippets/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>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>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>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>
		<item>
		<title>Simple image view helper for Zend Framework</title>
		<link>http://blog.amnuts.com/2007/10/03/simple-image-view-helper-for-zend-framework/</link>
		<comments>http://blog.amnuts.com/2007/10/03/simple-image-view-helper-for-zend-framework/#comments</comments>
		<pubDate>Wed, 03 Oct 2007 11:35:51 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[Code snippets]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[view helper]]></category>

		<guid isPermaLink="false">http://blog.amnuts.com/2007/10/03/simple-image-view-helper-for-zend-framework/</guid>
		<description><![CDATA[Here&#8217;s a simply view helper for the Zend Framework that can be used to display image tags.  It checks to see if the image file exists and if not then it&#8217;ll use the data url scheme to output a very simple image that, ironically, says &#8216;NO IMG&#8217; on it.    Please note, [...]]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s a simply view helper for the Zend Framework that can be used to display image tags.  It checks to see if the image file exists and if not then it&#8217;ll use the <a href="http://tools.ietf.org/html/rfc2397">data url scheme</a> to output a very simple image that, ironically, says &#8216;NO IMG&#8217; on it. <img src='http://blog.amnuts.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />   Please note, though, that I&#8217;ve only seen Firefox support this scheme, as wonderful as it is!</p>
<p><span id="more-56"></span></p>
<pre class="brush: php;">&lt; ?php

class Zend_View_Helper_Img
{
    public $view;
    protected static $_baseurl = null;
    protected static $_cache = null;

    /**
     * Constructor
     */
    public function __construct()
    {
        if (null === self::$_baseurl) {
            $url = Zend_Controller_Front::getInstance()-&gt;getRequest()-&gt;getBaseUrl();
            $root = '/' . trim($url, '/');
            if ('/' == $root) {
                $root = '';
            }
            self::$_baseurl = $root . '/';
        }
    }

    /**
     * Output the &lt;img /&gt; tag
     *
     * @param string $path
     * @param array $params
     * @return string
     */
    public function img($path, $params = array())
    {
        $plist = array();
        $paramstr = null;
        $imagepath = self::$_baseurl . ltrim($path, '/');
        if (!isset(self::$_cache[$path])) {
            self::$_cache[$path] = file_exists(realpath($_SERVER['DOCUMENT_ROOT'] . '/' . $imagepath));
        }
        if (!isset($params['alt'])) {
            $params['alt'] = '';
        }
        foreach ($params as $param =&gt; $value) {
            $plist[] = $param . '=&quot;' . $this-&gt;view-&gt;escape($value) . '&quot;';
        }
        $paramstr = ' ' . join(' ', $plist);
        return '&lt;img src=&quot;' .
                ((self::$_cache[$path])
                    ? self::$_baseurl . ltrim($path, '/')
                    : 'data:image/gif;base64,R0lGODlhFAAUAIAAAAAAAP///yH5BAAAAAAALAAAAAAUABQAAAI5jI+pywv4DJiMyovTi1srHnTQd1BRSaKh6rHT2cTyHJqnVcPcDWZgJ0oBV7sb5jc6KldHUytHi0oLADs=') .
                '&quot;' . $paramstr . ' /&gt;';
    }

    /**
     * Set the view object
     *
     * @param Zend_View_Interface $view
     * @return void
     */
    public function setView(Zend_View_Interface $view)
    {
        $this-&gt;view = $view;
    }
}</pre>
<p>Usage is really easy:</p>
<pre class="brush: php;">&lt; ?php echo $this-&gt;img('/images/logo.png', array('id' =&gt; 'logo', 'title' =&gt; 'Cool Company')); ?&gt;</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.amnuts.com/2007/10/03/simple-image-view-helper-for-zend-framework/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Robust email address validator &#8211; with address suggestions!</title>
		<link>http://blog.amnuts.com/2007/08/30/robust-email-address-validator-with-address-suggestions/</link>
		<comments>http://blog.amnuts.com/2007/08/30/robust-email-address-validator-with-address-suggestions/#comments</comments>
		<pubDate>Thu, 30 Aug 2007 19:20:28 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[Code snippets]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://blog.amnuts.com/2007/08/30/robust-email-address-validator-with-address-suggestions/</guid>
		<description><![CDATA[I&#8217;m sure you&#8217;ve seen the simple email address format validation function; they&#8217;re usually a simple regular expressing that just check the address portion (the user@example.org bit).  That&#8217;s really only a bit of the validation that should be done.  The RFC822 specs detail that the format of email addresses can be much larger, for [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m sure you&#8217;ve seen the simple email address format validation function; they&#8217;re usually a simple regular expressing that just check the address portion (the user@example.org bit).  That&#8217;s really only a bit of the validation that should be done.  The RFC822 specs detail that the format of email addresses can be much larger, for example, it could be something like <strong>&#8220;Andrew Collington &amp; Co.&#8221; &lt;a.collington@example.org&gt;</strong>, and, of course, the simple regex on that would fail.  But even a check on the address format isn&#8217;t often enough&#8230;  The user could enter a correctly formatted email address but simply have mis-spelled the address&#8230;  they may accidentally type in user@yahooo.com, or user@hitmail.co.uk rather than hotmail.co.uk, and things like that.  In which case you may want to check the MX and/or A record to see if its a valid domain.  And whilst you&#8217;re doing that, why not check to see if it&#8217;s a commonly used email host that maybe they&#8217;ve typed in wrong?</p>
<p>So here is a class that will allow you to do all that in one easy method call:</p>
<p><span id="more-50"></span></p>
<pre class="brush: php;">&lt;?php

define('EMAILCHECK_INVALID_ADDRESS', 0);
define('EMAILCHECK_INVALID_DNS_MX',  1);
define('EMAILCHECK_INVALID_DNS_A',   2);
define('EMAILCHECK_GUESS_THRESHOLD', 80);

class MailCheck
{
    /**
     * Validate an email address format against RFC822 specs.
     *
     * This method will check an email address against the RFC822 specs, as
     * well as validating the MX record if required.  Optionally you can have
     * the email address route portion return (rather than the boolean true
     * value).  This can be handy if you have an email address such as:
     *
     *    &quot;Andrew Collington &amp; Co.&quot; &lt;a.collington@example.org&gt;
     *
     * and you just want to use the actual address portion.  By default the
     * method will return true/false else route/false.
     *
     * Adapted from PHP code (Changes (c) 2005 Padraic Brady) which is a
     * translation of Perl code (Copyright 1997 O'Reilly &amp; Associates, Inc.).
     *
     * Based on optimised email regex in Perl Copyright 1997 O'Reilly &amp;
     * Associates, Inc. The &quot;Mastering Regular Expressions&quot; Email Regex
     * (from book on page 295 et seq).
     *
     * @param string $email The email address to validate
     * @param boolean $checkmx Check the MX Record if $email is valid
     * @return bool|string
     * @access public
     * @see &lt;http://www.faqs.org/rfcs/rfc822.html&gt;
     * @see &lt;http://examples.oreilly.com/regex/email-opt.pl&gt;
     */
    public static function format($email, $checkmx = false, $returnroute = false)
    {
        // Some things for avoiding backslashitis later on.
        $esc        = '\\\\';               $Period      = '\.';
        $space      = '\040';               $tab         = '\t';
        $OpenBR     = '\[';                 $CloseBR     = '\]';
        $OpenParen  = '\(';                 $CloseParen  = '\)';
        $NonASCII   = '\x80-\xff';          $ctrl        = '\000-\037';
        $CRlist     = '\n\015';  // note: this should really be only \015.

        // Items 19, 20, 21
        $qtext = &quot;[^{$esc}{$NonASCII}{$CRlist}\&quot;]&quot;;
        $dtext = &quot;[^{$esc}{$NonASCII}{$CRlist}{$OpenBR}{$CloseBR}]&quot;;
        $quoted_pair = &quot; {$esc} [^{$NonASCII}] &quot;;

        // Items 22 and 23, comment.
        // Impossible to do properly with a regex, I make do by allowing at most one level of nesting.
        $ctext = &quot; [^{$esc}{$NonASCII}{$CRlist}()] &quot;;

        // $Cnested matches one non-nested comment.
        // It is unrolled, with normal of $ctext, special of $quoted_pair.
        $Cnested = &quot;{$OpenParen}{$ctext}*(?: {$quoted_pair} {$ctext}* )*{$CloseParen}&quot;;

        // $comment allows one level of nested parentheses
        // It is unrolled, with normal of $ctext, special of ($quoted_pair|$Cnested)
        $comment = &quot;{$OpenParen}{$ctext}*(?:(?: {$quoted_pair} | {$Cnested} ){$ctext}*)*{$CloseParen}&quot;;

        // $X is optional whitespace/comments.
        // Nab whitespace.  If comment found, allow more spaces.
        $X = &quot;[{$space}{$tab}]*(?: {$comment} [{$space}{$tab}]* )*&quot;;

        // Item 10: atom
        $atom_char   = &quot;[^($space)&lt;&gt;\@,;:\&quot;.$esc$OpenBR$CloseBR$ctrl$NonASCII]&quot;;
        // some number of atom characters not followed by something that could be part of an atom
        $atom = &quot;{$atom_char}+(?!{$atom_char})&quot;;

        // Item 11: doublequoted string, unrolled.
        $quoted_str = &quot;\&quot;{$qtext} *(?: {$quoted_pair} {$qtext} * )*\&quot;&quot;;

        // Item 7: word is an atom or quoted string
        $word = &quot;(?:{$atom}|{$quoted_str})&quot;;

        // Item 12: domain-ref is just an atom
        $domain_ref = $atom;

        // Item 13: domain-literal is like a quoted string, but [...] instead of  &quot;...&quot;
        $domain_lit  = &quot;{$OpenBR}(?: {$dtext} | {$quoted_pair} )*{$CloseBR}&quot;;

        // Item 9: sub-domain is a domain-ref or domain-literal
        $sub_domain  = &quot;(?:{$domain_ref}|{$domain_lit}){$X}&quot;;

        // Item 6: domain is a list of subdomains separated by dots.
        $domain = &quot;{$sub_domain}(?:{$Period} {$X} {$sub_domain})*&quot;;

        // Item 8: a route. A bunch of &quot;@ $domain&quot; separated by commas, followed by a colon.
        $route = &quot;\@ {$X} {$domain}(?: , {$X} \@ {$X} {$domain} )*:{$X}&quot;;

        // Item 6: local-part is a bunch of $word separated by periods
        $local_part = &quot;{$word} {$X}(?:{$Period} {$X} {$word} {$X})*&quot;;

        // Item 2: addr-spec is local@domain
        $addr_spec  = &quot;{$local_part} \@ {$X} {$domain}&quot;;

        // Item 4: route-addr is &lt;route? addr-spec&gt;
        // parenthases around the route_addr to capture this in the final regexpr
        $route_addr = &quot;&lt; ({$X}(?: {$route} )?{$addr_spec})&gt;&quot;;

        // Item 3: phrase... like ctrl, but without tab
        $phrase_ctrl = '\000-\010\012-\037';

        // Like atom-char, but without listing space, and uses phrase_ctrl.
        // Since the class is negated, this matches the same as atom-char plus space and tab
        $phrase_char = &quot;[^()&lt;&gt;\@,;:\&quot;.{$esc}{$OpenBR}{$CloseBR}{$NonASCII}{$phrase_ctrl}]&quot;;

        // We've worked it so that $word, $comment, and $quoted_str to not consume trailing $X
        // because we take care of it manually.
        $phrase = &quot;{$word}{$phrase_char} *(?:(?: {$comment} | {$quoted_str} ){$phrase_char} *)*&quot;;

        // Item #1: mailbox is an addr_spec or a phrase/route_addr
        $mailbox = &quot;{$X}(?:{$addr_spec}|{$phrase}  ({$route_addr}))&quot;;

        // perform actual regex check to our recieved email address
        $matches = array();
        $isValid = preg_match('/^'.$mailbox.'$/xS', $email, $matches);
        $route = $matches[count($matches) - 1];

        // check the MX Record is needs be
        if ($isValid &amp;&amp; $checkmx) {
            list(, $host) = explode('@', $route);
            return (@checkdnsrr($host, 'MX')) ? (($returnroute) ? $route : true) : false;
        }

        // finally return status
        return ($isValid &amp;&amp; $returnroute) ? $route : $isValid;
    }

    /**
     * Check email addresses for validity
     *
     * You can pass either a string of an array of strings.  The string
     * contains the email address you want to check.  The method will check
     * that the format is valid and then check the A and MX record.  If
     * either the A or MX record fail then it will attempt to make some
     * suggestions of popular email hosts.
     *
     * @param string|array $email Email address of array of email addresses
     * @return array
     * @access public
     */
    public static function address($email)
    {
        // default metaphone conversions
        $mphones = array(
            'ALKK'     =&gt; 'aol.co.uk',
            'ALKM'     =&gt; 'aol.com',
            'EKSMPLKM' =&gt; 'example.com',
            'HTMLKK'   =&gt; 'hotmail.co.uk',
            'HTMLKM'   =&gt; 'hotmail.com',
            'KKLMLKM'  =&gt; 'googlemail.com',
            'KMLKM'    =&gt; 'gmail.com',
            'MSNKK'    =&gt; 'msn.co.uk',
            'MSNKM'    =&gt; 'msn.com',
            'YHKK'     =&gt; 'yahoo.co.uk',
            'YHKM'     =&gt; 'yahoo.com'
        );
        $emails = $suggestions = array();

        // validate email address format
        if (is_array($email)) {
            $email = array_unique($email);
            foreach ($email as $e) {
                $route = self::format($e, false, true);
                if (!$route) {
                    $emails[$e]['error'][EMAILCHECK_INVALID_ADDRESS] = true;
                } else {
                    list($emails[$e]['user'], $emails[$e]['domain']) = explode('@', $route);
                }
            }
        } else {
            $route = self::format($email, false, true);
            if (!$route) {
                $emails[$email]['error'] = EMAILCHECK_INVALID_ADDRESS;
            } else {
                list($emails[$email]['user'], $emails[$email]['domain']) = explode('@', $route);
            }
        }

        // check domains
        foreach ($emails as $orig =&gt; $e) {
            if ($e['domain']) {
                if (empty($e['error'])) {
                    $valid = checkdnsrr($e['domain'], 'MX');
                    if (!$valid) {
                        $emails[$orig]['error'][EMAILCHECK_INVALID_DNS_MX] = true;
                    }
                }
                if (empty($e['error'])) {
                    $valid = checkdnsrr($e['domain'], 'A');
                    if (!$valid) {
                        $emails[$orig]['error'][EMAILCHECK_INVALID_DNS_A] = true;
                    }
                }
                // failing anything, get suggestions
                if (!empty($emails[$orig]['error']) &amp;&amp; !isset($e['error'][EMAILCHECK_INVALID_ADDRESS])) {
                    $emeta = metaphone($e['domain']);
                    $suggestions = array();
                    foreach ($mphones as $code =&gt; $addr) {
                        $percent = 0;
                        $lev = levenshtein($emeta, $code);
                        $sim = similar_text($emeta, $code, $percent);
                        $score = round($percent + max(-$lev + $sim, 0));
                        if ($score &gt;= EMAILCHECK_GUESS_THRESHOLD) {
                            $suggestions[$addr] = $score;
                        }
                    }
                    if (!empty($suggestions)) {
                        arsort($suggestions);
                        $emails[$orig]['suggestion'] = array_keys($suggestions);
                    }
                }
            }
        }

        // send back results
        return $emails;
    }
}

?&gt;</pre>
<p>If I call it like this:</p>
<pre class="brush: php;">$validate = MailCheck::address('example@yahooo.co');
print_r($validate);</pre>
<p>I will get the following returned:</p>
<pre>Array
(
    [example@yahooo.co] => Array
        (
            [domain] => yahooo.co
            [user] => example
            [error] => Array
                (
                    [1] => 1
                    [2] => 1
                )
            [suggestion] => Array
                (
                    [0] => yahoo.com
                    [1] => yahoo.co.uk
                )
        )
)</pre>
<p>Which is basically saying that while the address <em>example@yahooo.co</em> is a valid format, the domain <em>yahooo.co</em> doesn&#8217;t have a valid MX or A record, and it thinks that perhaps the user meant to use <em>yahoo.com</em> or <em>yahoo.co.uk</em>.</p>
<p>The <strong>address</strong> method can take an array of email addresses as well as a single string.</p>
<p>A further example of use is:</p>
<pre class="brush: php;">&lt;?php

require('MailCheck.php');
$valid = MailCheck::address(array('user1@yahoo.com', 'user2@somethingmadeup.com', 'user3 is wrong'));
foreach ($valid as $address =&gt; $status) {
    echo $address, ' is ', (empty($status['error'])) ? 'valid' : 'not valid', &quot;&lt;br /&gt;\n&quot;;
}

?&gt;</pre>
<p>Which echos:</p>
<pre>user1@yahoo.com is valid
user2@somethingmadeup.com is not valid
user3 is wrong is not valid</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.amnuts.com/2007/08/30/robust-email-address-validator-with-address-suggestions/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Getting a list of project tags from Subversion</title>
		<link>http://blog.amnuts.com/2007/08/14/getting-a-list-of-project-tags-from-subversion/</link>
		<comments>http://blog.amnuts.com/2007/08/14/getting-a-list-of-project-tags-from-subversion/#comments</comments>
		<pubDate>Tue, 14 Aug 2007 23:29:00 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[Code snippets]]></category>
		<category><![CDATA[Subversion]]></category>

		<guid isPermaLink="false">http://blog.amnuts.com/2007/08/14/getting-a-list-of-project-tags-from-subversion/</guid>
		<description><![CDATA[So there you are nicely tagging your project in Subversion, but for some reason you need to get a list of all the tags being used&#8230;  That situation came up for me today.  I thought it was going to be some really complex way of getting the tags, involving the use of hook [...]]]></description>
			<content:encoded><![CDATA[<p>So there you are nicely tagging your project in Subversion, but for some reason you need to get a list of all the tags being used&#8230;  That situation came up for me today.  I thought it was going to be some really complex way of getting the tags, involving the use of hook scripts and the like.  But it turns out that with some command line goodness it&#8217;s actually much more simple.  Here&#8217;s how to do it:</p>
<pre class="brush: plain;">svnlook tree --full-paths /home/path/to/svn/project | \
egrep -a '/?tags/.+' | \
sed -re 's!.*/?tags/([^/]*).*!\1!' | \
sort -u</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.amnuts.com/2007/08/14/getting-a-list-of-project-tags-from-subversion/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Create a random thumbnail of a video file</title>
		<link>http://blog.amnuts.com/2007/06/22/create-a-random-thumbnail-of-a-video-file/</link>
		<comments>http://blog.amnuts.com/2007/06/22/create-a-random-thumbnail-of-a-video-file/#comments</comments>
		<pubDate>Fri, 22 Jun 2007 12:07:19 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[Code snippets]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://blog.amnuts.com/2007/06/22/create-a-random-thumbnail-of-a-video-file/</guid>
		<description><![CDATA[Create a random thumbnail of a video file
Looking at sites like YouTube, you may think it&#8217;s quite hard to create a lot of different thumbnails from video files, and have them from random times within that file.  But, no, it&#8217;s not!  As this article shows, by using the very fabulous FFmpeg library, it&#8217;s [...]]]></description>
			<content:encoded><![CDATA[<p>Create a random thumbnail of a video file</p>
<p>Looking at sites like YouTube, you may think it&#8217;s quite hard to create a lot of different thumbnails from video files, and have them from random times within that file.  But, no, it&#8217;s not!  As this article shows, by using the very fabulous <a href="http://ffmpeg.mplayerhq.hu/">FFmpeg</a> library, it&#8217;s actually a very short amount of code that&#8217;s required to create all those lovely random thumbnails.</p>
<p><span id="more-46"></span></p>
<p>First a little information about FFmpeg.  If you haven&#8217;t come across this yet then you&#8217;re missing out on an excellent bit of software to put in your tool kit.  It can convert and stream all manner of video and audio files, is open source (under LGPL, but with some optional GPL bits), and is always being worked on and developed.</p>
<p><a href="http://ffmpeg.mplayerhq.hu/">FFmpeg&#8217;s homepage is at http://ffmpeg.mplayerhq.hu/</a>, where you can grab the latest revision from SVN.  If you want it for Windows, then one location I found with quite recent builds is <a href="http://ffdshow.faireal.net/mirror/ffmpeg/">http://ffdshow.faireal.net/mirror/ffmpeg/</a>, but if you know any better ones then let me know.</p>
<p>So on with the code&#8230;</p>
<pre class="brush: php;">&lt;?php

// where ffmpeg is located, such as /usr/sbin/ffmpeg
$ffmpeg = 'ffmpeg';

// the input video file
$video  = dirname(__FILE__) . '/demo.mpg';

// where you'll save the image
$image  = dirname(__FILE__) . '/demo.jpg';

// default time to get the image
$second = 1;

// get the duration and a random place within that
$cmd = &quot;$ffmpeg -i $video 2&gt;&amp;1&quot;;
if (preg_match('/Duration: ((\d+):(\d+):(\d+))/s', `$cmd`, $time)) {
    $total = ($time[2] * 3600) + ($time[3] * 60) + $time[4];
    $second = rand(1, ($total - 1));
}

// get the screenshot
$cmd = &quot;$ffmpeg -i $video -deinterlace -an -ss $second -t 00:00:01 -r 1 -y -vcodec mjpeg -f mjpeg $image 2&gt;&amp;1&quot;;
$return = `$cmd`;

// done! <img src='http://blog.amnuts.com/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' />
echo 'done!';

?&gt;</pre>
<p>Nice and short, and that&#8217;s even with me being generous over white space!</p>
<p>The two key portions in this code are the ones to get the random time location of the video, which we do by parsing the output from a call to ffmpeg, and then running another command to generate the image.</p>
<p>When running the first command it&#8217;s important to remember the <strong>2&gt;&amp;1</strong> to redirect the stderr to stdout, as it&#8217;s actually an error message we want to capture.  To be on the safe side, I simple add it to all my command line calls to FFmpeg (which in this example is only two!)</p>
<p>Now that you have your thumbnail image you may be thinking that you need to use GD, ImageMagick, or some other graphics library to resize the image, right?  Well, you could, if you wanted to, but you could also do it in the very same command line to create the thumbnail!  Basically, you&#8217;d use the <strong>-s</strong> flag to resize the thumbnail.  For example:</p>
<pre class="brush: php;">$cmd = &quot;$ffmpeg -i $video -deinterlace -an -ss $second -t 00:00:01 -r 1 -y -s 320x240 -vcodec mjpeg -f mjpeg $image 2&gt;&amp;1&quot;;</pre>
<p>You may also notice the <strong>-deinterlace</strong> flag in there.  You may not need this &#8211; it depends on whether you video file needs deinterlacing or not.  All of the flags are detailed in the <a href="http://ffmpeg.mplayerhq.hu/ffmpeg-doc.html">FFmpeg documentation</a>.</p>
<p>Oh, and if you&#8217;re taking user input for the video file or output image file, then please remember to escape your variables accordingly!  Use something like the <a href="http://www.php.net/escapeshellcmd">escapeshellcmd()</a> PHP function.</p>
<p>Have fun grabbing your video frames!!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.amnuts.com/2007/06/22/create-a-random-thumbnail-of-a-video-file/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Create a registration key</title>
		<link>http://blog.amnuts.com/2007/06/05/create-a-registration-key/</link>
		<comments>http://blog.amnuts.com/2007/06/05/create-a-registration-key/#comments</comments>
		<pubDate>Tue, 05 Jun 2007 09:22:55 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[Code snippets]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://blog.amnuts.com/2007/06/05/create-a-registration-key/</guid>
		<description><![CDATA[This simple function allows you to generate a random registration key in the format, &#8216;1224-54B1-7D35-5EF7&#8242;.
function registration_key()
{
    return strtoupper(substr(chunk_split(sprintf('%03d%s',
                      rand(0,999), uniqid('')), 4, '-'), 0, -1));
}
]]></description>
			<content:encoded><![CDATA[<p>This simple function allows you to generate a random registration key in the format, &#8216;1224-54B1-7D35-5EF7&#8242;.</p>
<pre class="brush: php;">function registration_key()
{
    return strtoupper(substr(chunk_split(sprintf('%03d%s',
                      rand(0,999), uniqid('')), 4, '-'), 0, -1));
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.amnuts.com/2007/06/05/create-a-registration-key/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Validate a UK postcode</title>
		<link>http://blog.amnuts.com/2007/06/05/validate-a-uk-postcode/</link>
		<comments>http://blog.amnuts.com/2007/06/05/validate-a-uk-postcode/#comments</comments>
		<pubDate>Tue, 05 Jun 2007 09:19:36 +0000</pubDate>
		<dc:creator>Andy</dc:creator>
				<category><![CDATA[Code snippets]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://blog.amnuts.com/2007/06/05/validate-a-uk-postcode/</guid>
		<description><![CDATA[You&#8217;ve probably seen this before, if not a thousand times, but here&#8217;s a simple function to check the format of a UK postcode.
function valid_uk_postcode($postcode)
{
    return (preg_match('/^([A-PR-UWYZ][A-HK-Y0-9][AEHMNPRTVXY0-9]?[ABEHMNPRVWXY0-9]? {0,2}[0-9][ABD-HJLN-UW-Z]{2}&#124;GIR 0AA)$/i', $postcode)) ?
        true : false;
}
]]></description>
			<content:encoded><![CDATA[<p>You&#8217;ve probably seen this before, if not a thousand times, but here&#8217;s a simple function to check the format of a UK postcode.</p>
<pre class="brush: php;">function valid_uk_postcode($postcode)
{
    return (preg_match('/^([A-PR-UWYZ][A-HK-Y0-9][AEHMNPRTVXY0-9]?[ABEHMNPRVWXY0-9]? {0,2}[0-9][ABD-HJLN-UW-Z]{2}|GIR 0AA)$/i', $postcode)) ?
        true : false;
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.amnuts.com/2007/06/05/validate-a-uk-postcode/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
