<?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>Stut.net &#187; Misc</title>
	<atom:link href="http://stut.net/category/misc/feed/" rel="self" type="application/rss+xml" />
	<link>http://stut.net</link>
	<description>Ramblings of a random software engineer</description>
	<lastBuildDate>Thu, 08 Jul 2010 10:42:58 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Twitter-related domain names for sale</title>
		<link>http://stut.net/2010/07/06/twitter-related-domain-names-for-sale/</link>
		<comments>http://stut.net/2010/07/06/twitter-related-domain-names-for-sale/#comments</comments>
		<pubDate>Tue, 06 Jul 2010 15:00:43 +0000</pubDate>
		<dc:creator>Stuart</dc:creator>
				<category><![CDATA[Misc]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://stut.net/?p=470</guid>
		<description><![CDATA[I&#8217;m putting some domain names up for sale, including twitapps.com/net/org, and several others that are ideal for Twitter-related services, or whatever. For more info please see the blog post over on 3ft9.com. If you liked this you might also like these:Handling email notifications from TwitterNew company site for 3ft9links for 2007-08-04What&#8217;s going on?Has that website [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_jade" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fstut.net%252F2010%252F07%252F06%252Ftwitter-related-domain-names-for-sale%252F%22%2C%20%22shorturl%22%3A%20%22http%3A%2F%2Fbit.ly%2Faz8yKy%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Twitter-related%20domain%20names%20for%20sale%22%20%7D);"></div>
<p><a href="http://3ft9.com/19-twitter-related-domains-for-sale">I&#8217;m putting some domain names up for sale</a>, including twitapps.com/net/org, and several others that are ideal for Twitter-related services, or whatever.</p>
<p>For more info please <a href="http://3ft9.com/19-twitter-related-domains-for-sale">see the blog post over on 3ft9.com</a>.</p>
<div id="crp_related"><h3>If you liked this you might also like these:</h3><ul><li><a href="http://stut.net/2009/03/08/handling-email-notifications-from-twitter/" rel="bookmark" class="crp_title">Handling email notifications from Twitter</a></li><li><a href="http://stut.net/2008/12/05/new-company-site-for-3ft9/" rel="bookmark" class="crp_title">New company site for 3ft9</a></li><li><a href="http://stut.net/2007/08/04/links-for-2007-08-04/" rel="bookmark" class="crp_title">links for 2007-08-04</a></li><li><a href="http://stut.net/2009/11/02/whats-going-on/" rel="bookmark" class="crp_title">What&#8217;s going on?</a></li><li><a href="http://stut.net/2008/05/23/has-that-website-gone-titsup/" rel="bookmark" class="crp_title">Has that website gone TitsUp?</a></li></ul></div>
]]></content:encoded>
			<wfw:commentRss>http://stut.net/2010/07/06/twitter-related-domain-names-for-sale/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>What&#8217;s going on?</title>
		<link>http://stut.net/2009/11/02/whats-going-on/</link>
		<comments>http://stut.net/2009/11/02/whats-going-on/#comments</comments>
		<pubDate>Mon, 02 Nov 2009 12:25:32 +0000</pubDate>
		<dc:creator>Stuart</dc:creator>
				<category><![CDATA[Misc]]></category>

		<guid isPermaLink="false">http://stut.net/blog/?p=388</guid>
		<description><![CDATA[I know it&#8217;s been a while since posted anything on this blog, but I&#8217;ve had a lot going on and it hasn&#8217;t really been at the forefront of my mind. So, what&#8217;s been going on? Firstly I&#8217;ve shut TwitApps.com down. I won&#8217;t go into the reasons because they&#8217;re well documented in the blog post announcing [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_jade" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fstut.net%252F2009%252F11%252F02%252Fwhats-going-on%252F%22%2C%20%22shorturl%22%3A%20%22http%3A%2F%2Fbit.ly%2Fc9b5x3%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22What%27s%20going%20on%3F%22%20%7D);"></div>
<p>I know it&#8217;s been a while since posted anything on this blog, but I&#8217;ve had a lot going on and it hasn&#8217;t really been at the forefront of my mind. So, what&#8217;s been going on?</p>
<p><span id="more-388"></span>Firstly <a href="http://twitapps.com/">I&#8217;ve shut TwitApps.com down</a>. I won&#8217;t go into the reasons because they&#8217;re well documented in <a href="http://3ft9.com/10-twitapps-shutting-down">the blog post announcing it</a>. I&#8217;ve also <a href="http://3ft9.com/17-twitapps-code-released">released the code</a> so anyone can make a clone if they feel so inspired. One big side effect of this is that I&#8217;m open to offers for that domain name and a bunch of others I don&#8217;t see myself using anymore. Please only contact me with serious offers.</p>
<p>Secondly I&#8217;ve been doing a lot of soul searching lately, and one of the results of that has been my <a href="http://tastingtheacorn.com/">new blog over at tastingtheacorn.com</a> where I&#8217;m writing for my benefit rather than as a service to others, which is how I view this site. Check it out and let me know what you think.</p>
<p>Finally I have been all over the place in terms of my career lately, which I&#8217;ve <a href="http://tastingtheacorn.com/my-new-plan">written about on my new blog</a> so I won&#8217;t repeat it here. Ultimately it means I&#8217;m on the lookout for interesting projects to get involved with. If you think I may be able to help you or your company please let me know.</p>
<p>Hopefully that explains where I&#8217;ve been. I plan to continue publishing tech and development posts on this site for the foreseeable future, but how often depends on how much my focus gets split in my new reality. I also plan to change the layout of this site &#8211; it&#8217;s starting to depress me because I never really finished it.</p>
<div id="crp_related"><h3>If you liked this you might also like these:</h3><ul><li><a href="http://stut.net/2008/12/05/new-company-site-for-3ft9/" rel="bookmark" class="crp_title">New company site for 3ft9</a></li><li><a href="http://stut.net/2010/07/06/twitter-related-domain-names-for-sale/" rel="bookmark" class="crp_title">Twitter-related domain names for sale</a></li><li><a href="http://stut.net/2009/01/31/twitter-unfollows-too-much-information/" rel="bookmark" class="crp_title">Twitter unfollows: too much information?</a></li><li><a href="http://stut.net/2007/11/19/that-other-bit-of-news/" rel="bookmark" class="crp_title">That other bit of news</a></li><li><a href="http://stut.net/2007/05/05/switched-to-wordpress/" rel="bookmark" class="crp_title">Switched to WordPress</a></li></ul></div>
]]></content:encoded>
			<wfw:commentRss>http://stut.net/2009/11/02/whats-going-on/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP Job Queue</title>
		<link>http://stut.net/2009/05/29/php-job-queue/</link>
		<comments>http://stut.net/2009/05/29/php-job-queue/#comments</comments>
		<pubDate>Fri, 29 May 2009 20:26:58 +0000</pubDate>
		<dc:creator>Stuart</dc:creator>
				<category><![CDATA[Misc]]></category>

		<guid isPermaLink="false">http://stut.net/blog/?p=380</guid>
		<description><![CDATA[One of the pillars of a scalable website is ensuring that only activity which is required to build a page should be performed during the processing of a page request. Activities that fall under this category commonly include sending emails, recording statistics and general housekeeping such as removing temporary files. Back when I started working [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_jade" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fstut.net%252F2009%252F05%252F29%252Fphp-job-queue%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22PHP%20Job%20Queue%22%20%7D);"></div>
<p>One of the pillars of a scalable website is ensuring that only activity which is required to build a page should be performed during the processing of a page request. Activities that fall under this category commonly include sending emails, recording statistics and general housekeeping such as removing temporary files.</p>
<p>Back when I started working on sites big enough for these activities to cause a problem I went down the obvious route of making a PHP CLI script for each job that needed doing and getting it to run using cron. This worked for a while but as the sites I was working on got bigger and more complex it quickly became clear that this was becoming difficult to manage, so I started to consider alternatives.</p>
<p><span id="more-380"></span>At the time all this happened I was mainly working with a site that ran on <a href="http://www.zend.com/en/products/platform/" target="_blank">Zend Platform</a>. One of the recent additions at the time was a module called <a href="http://www.zend.com/en/products/platform/product-comparison/job-queues" target="_blank">Job Queue</a> which appeared to do exactly what I needed. Unfortunately after spending a fair amount of time developing the infrastructure required to make it run the jobs I discovered that it really wasn&#8217;t very well tested; it was far from production quality <a href="#fn1">[1]</a> and nowhere near reliable enough so I went back to the drawing board.</p>
<h2>Time passes&#8230;</h2>
<p>After thinking about what I needed from such a system and what I had available I came up with an arrangement I&#8217;ve been using ever since with great success.</p>
<p>The core of the system is a DB table and a PHP script. The table contains the definition of jobs that need running and the script, erm, runs them.</p>
<p>I won&#8217;t go into the details of the table because the only important parts as far as this system go are the run_at, processor_pid and schedule_* fields.</p>
<p>The <em>run_at</em> field is a timestamp that simply indicates the time when that job should be executed.</p>
<p>The <em>processor_pid</em> field is an unsigned integer that defaults to 0 and will indicate the process that&#8217;s running a job if any.</p>
<p>The <em>schedule_*</em> fields specify how often the job should be executed. There are a number of ways of organising these depending on what your requirements are, but they fall into two general categories.</p>
<div style="padding-left: 1.5em;">
	<strong>Periodic only</strong><br />
	If you only need to say &#8220;run again in <em>n</em> seconds&#8221; then this is the one for you. Use a single field called <em>schedule</em> and put <em>n</em> in there.</p>
<p>	<strong>Complex</strong><br />
	If you need something more flexible then you&#8217;ll need to use a number of fields (or a single field you can parse) to specify how to calculate the next run_at time. For example a period (daily, weekly, monthly or anually) and a day/time field allows you to configure any of the following&#8230;</p>
<ul>
<li>Every day at 10am</li>
<li>Every Monday at 1am</li>
<li>The 1st of every month at midnight</li>
<li>Every year on February 14th at 8pm</li>
<li>etc&#8230;</li>
</ul>
</div>
<p>Before I move on to the script I apparently need to cover something that I missed when I first wrote this. <strong>You need to specify what to run in this table!</strong> I though this was pretty obvious but based on initial feedback I was wrong.</p>
<p>This can take any number of forms and will depend greatly on your specific application. Over the years I&#8217;ve used a number of different methods including a PHP script name, a CLI command and a method in a static class. Whatever you use you&#8217;ll want to make sure you do sufficient checks to ensure it&#8217;s secure.</p>
<p>Ok, on to the script which is designed to be run by cron according to a schedule that allows it to keep up with the size of the job queue you anticipate.</p>
<p>The following is a list of the basic steps the script performs. For this example I&#8217;m continuing with the assumption that the job queue is a table in a database.</p>
<ol>
<li>
		Get the pid and put it in $pid.
	</li>
<li>
		Execute this SQL query&#8230;</p>
<div style="padding-left: 1.5em; font-family: monospace;">
			update `job_queue`<br />
			set `processor_pid` = &#8220;$pid&#8221;<br />
			where `run_at` <= unix_timestamp()<br />
			&nbsp;&nbsp;and `processor_pid` = 0<br />
			order by run_at asc<br />
			limit 1
		</div>
</li>
<li>
		Check mysql_affected_rows() and exit if 0.
	</li>
<li>
		Execute this SQL query&#8230;</p>
<div style="padding-left: 1.5em; font-family: monospace;">
			select * from `job_queue`<br />
			where `processor_pid` = &#8220;$pid&#8221;
		</div>
<p>		Note that these SQL statements (step 2 and this one) atomically grab a job and lock it. If you&#8217;re using a different storage system for your queue you&#8217;ll need to lock it while you select a job to run and then mark it as in progress.
	</li>
<li>
		Run the job. As mentioned already this can mean any number of things and will depend on your particular application. One of the useful things you can do here is to set up a clean, safe environment for the job to run in, along with ways to capture errors and other outputs so you can do something useful with them.
	</li>
<li>
		As soon as the job has finished executing we mark it as completed and record the success or failure status.
	</li>
<li>
		If the job has a schedule (i.e. it&#8217;s a recurring job) we create a new job by effectively cloning the job we&#8217;ve just run. We now calculate the time it should be run according to the schedule definition and save that in the run_at field of the new job. Finally we set the <em>processor_pid</em> of the new job to 0 so it&#8217;s then available to step 2 of this process.
	</li>
<li>
		Depending on the job configuration and status we now either remove the completed job from the queue or archive it complete with errors and output for later inspection.
	</li>
<li>
		If this processor has been running for > 60 minutes it exits, otherwise it goes back to step 2 and looks for another job to run.
	</li>
</ol>
<p>To get this to do something useful we configure it to run via cron every <em>n</em> minutes where <em>n</em> depends upon the anticipated size of your job queue. For example running it every minute will automatically scale it up to 60 concurrent jobs at any one time. Running it every 5 minutes will reduce this to 12, and so on. There&#8217;s also nothing stopping you putting more than one line into the crontab so it runs two processes every minute which increases concurrent processors to 120.</p>
<p>Assuming your job queue is network accessible this system also scales across multiple machines with minimal changes. In fact the only change that&#8217;s required it to incorporate a machine identified into the <em>processor_pid</em> field. This could be as simple as <em>&lt;machine&gt;_&lt;pid&gt;</em>; the key thing is that it&#8217;s guaranteed to be unique to a given process across your entire infrastructure.</p>
<h2>Crashed jobs</h2>
<p>One problem you may need to deal with is how to handle crashed jobs. This will happen, you can&#8217;t get away from it and you&#8217;ll need a way to detect and deal with it when it does. Luckily the job queue makes detection fairly straightforward.</p>
<p>On a single machine you can implement a script (either run via cron separately or indeed run by the job queue) that will check that for each job that has a <em>processor_pid</em> > 0 there is a PHP process running with that PID.</p>
<p>If and when you&#8217;ve scaled across multiple machines this script essentially remains the same except that you need to run it on every machine that runs the job queue and filter the PID&#8217;s you check.</p>
<p>As far as what to do when you find a crashed job that&#8217;s really something you need to consider on a case-by-case basis. At the very basic level the script could simply reset the <em>processor_pid</em> field to 0 so it gets run again. At the other end of the spectrum in a very flexible system you could have a way to run a job with a flag to indicate that it had previously crashed; each job can then deal with crashes in their own custom way.</p>
<h2>Potential additions</h2>
<p>The components described above is just the core of a job queue system; you can add a lot of useful stuff above and around it to make it easier to manage and provide better feedback from your periodic tasks.</p>
<ul>
<li>
		<strong>Performance metrics</strong><br />
		Since you&#8217;re running all your jobs from a central script adding code around the actual execution to record execution time, load and possibly memory usage too is pretty simple. You then have the ability to compare the time a job took to previous executions of that job to detect potential errors.
	</li>
<li>
		<strong>Management interface</strong><br />
		Since you have all the information regarding the jobs in the queue, what&#8217;s running right now and the status of jobs that have previously been executed it&#8217;s a pretty small leap to build a UI that will let you view and manage the whole thing. This can be especially useful for presenting error messages and performance metrics.
	</li>
</ul>
<h2>Final thoughts</h2>
<p>I hope that&#8217;s useful to someone, I&#8217;ve certainly found it applicable to most web applications I now deal with. As I mentioned a few times you can implement the various parts in a number of ways, in particular how jobs are specified and how the scheduling works.</p>
<p>As an example of this flexibility I&#8217;ll make a passing mention to one implementation I&#8217;ve done that only needed to be able to execute scripts daily, hourly or every 15 minutes. To accomplish this I simply created a folder for the scripts, and three folders named <em>15min</em>, <em>hourly</em> and <em>daily</em> within that. The processor script uses a custom locking system and CLI arguments to run each set of scripts according the the directories they&#8217;re in.</p>
<p>This system has proven to work very well and will continue to do everything that site needs until we need to execute something at a specific time; a requirement that has not yet surfaced.</p>
<p>One of the many side projects I&#8217;m working on is a reusable version of this system. At the moment it&#8217;s a fairly messy combination of scripts that doesn&#8217;t work very well so far, but as soon as I have something worth sharing I&#8217;ll definitely do so on this blog. Stay tuned.</p>
<p>If you have an questions or suggestions for improvement please don&#8217;t hesitate to leave a comment or <a href="/who#contact">contact me privately</a>.</p>
<p><a name="fn1"></a> [1] <small>This was a few years back and I&#8217;ve heard that the Job Queue module has received some attention since then so is now a lot better, but I no longer use Zend Platform so I&#8217;m not in a position to comment. If budget is not an issue for you I&#8217;d recommend checking it out.</small></p>
<div id="crp_related"><h3>If you liked this you might also like these:</h3><ul><li><a href="http://stut.net/2008/06/08/php-models-2/" rel="bookmark" class="crp_title">PHP Models</a></li><li><a href="http://stut.net/2005/06/11/pay-per-mile/" rel="bookmark" class="crp_title">Pay per mile</a></li><li><a href="http://stut.net/2008/06/18/sending-email/" rel="bookmark" class="crp_title">Sending email</a></li><li><a href="http://stut.net/2005/02/05/rename-a-mysql-database/" rel="bookmark" class="crp_title">Rename a MySQL Database</a></li><li><a href="http://stut.net/2007/11/10/web-hosting-a-mugs-game/" rel="bookmark" class="crp_title">Web hosting &#8211; a mugs game</a></li></ul></div>
]]></content:encoded>
			<wfw:commentRss>http://stut.net/2009/05/29/php-job-queue/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Extreme Sheep LED Art</title>
		<link>http://stut.net/2009/03/23/extreme-sheep-led-art/</link>
		<comments>http://stut.net/2009/03/23/extreme-sheep-led-art/#comments</comments>
		<pubDate>Mon, 23 Mar 2009 11:21:57 +0000</pubDate>
		<dc:creator>Stuart</dc:creator>
				<category><![CDATA[Misc]]></category>

		<guid isPermaLink="false">http://stut.net/blog/2009/03/23/extreme-sheep-led-art/</guid>
		<description><![CDATA[Not sure if I believe this is real or not, but either way it&#8217;s a pretty cool idea. If you liked this you might also like these:BeardymanAlexei Arkhipovskiy &#8211; SharmankaBigDog &#8230; WOW!Amazing MachinesTweetMeme Live Test Video from April 2009]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_jade" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fstut.net%252F2009%252F03%252F23%252Fextreme-sheep-led-art%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Extreme%20Sheep%20LED%20Art%22%20%7D);"></div>
<p>Not sure if I believe this is real or not, but either way it&#8217;s a pretty cool idea.</p>
<p>  <object height="295" width="480"><param name="movie" value="http://www.youtube-nocookie.com/v/D2FX9rviEhw&amp;hl=en&amp;fs=1&amp;rel=0" /><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><embed src="http://www.youtube-nocookie.com/v/D2FX9rviEhw&amp;hl=en&amp;fs=1&amp;rel=0" type="application/x-shockwave-flash" height="295" width="480"></embed></object>  </p>
<div id="crp_related"><h3>If you liked this you might also like these:</h3><ul><li><a href="http://stut.net/2010/07/08/beardyman/" rel="bookmark" class="crp_title">Beardyman</a></li><li><a href="http://stut.net/2009/01/25/alexei-arkhipovskiy-sharmanka/" rel="bookmark" class="crp_title">Alexei Arkhipovskiy &#8211; Sharmanka</a></li><li><a href="http://stut.net/2008/03/28/bigdog-wow/" rel="bookmark" class="crp_title">BigDog &#8230; WOW!</a></li><li><a href="http://stut.net/2006/09/29/amazing-machines/" rel="bookmark" class="crp_title">Amazing Machines</a></li><li><a href="http://stut.net/2010/02/19/tweetmeme-live-test-video-from-april-2009/" rel="bookmark" class="crp_title">TweetMeme Live Test Video from April 2009</a></li></ul></div>
]]></content:encoded>
			<wfw:commentRss>http://stut.net/2009/03/23/extreme-sheep-led-art/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Twitter unfollows: too much information?</title>
		<link>http://stut.net/2009/01/31/twitter-unfollows-too-much-information/</link>
		<comments>http://stut.net/2009/01/31/twitter-unfollows-too-much-information/#comments</comments>
		<pubDate>Fri, 30 Jan 2009 23:54:19 +0000</pubDate>
		<dc:creator>Stuart</dc:creator>
				<category><![CDATA[Misc]]></category>

		<guid isPermaLink="false">http://stut.net/blog/?p=354</guid>
		<description><![CDATA[A couple of days ago I released a new TwitApps tool called Follows. I know I haven&#8217;t talked about TwitApps on this blog yet &#8211; that post is coming &#8211; but if you follow me on Twitter you should be aware of it. The Follows service monitors your followers on Twitter and sends you a [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_jade" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fstut.net%252F2009%252F01%252F31%252Ftwitter-unfollows-too-much-information%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Twitter%20unfollows%3A%20too%20much%20information%3F%22%20%7D);"></div>
<p><img src="http://stut.net/wp-content/uploads/2009/01/dont-follow-me-sign.gif" border="0" alt="dont_follow_me_sign.gif" width="310" height="310" align="right" />A couple of days ago I released a new <a href="http://twitapps.com/">TwitApps</a> tool called <a href="http://twitapps.com/follows/">Follows</a>. I know I haven&#8217;t talked about TwitApps on this blog yet &#8211; that post is coming &#8211; but if you <a href="http://twitter.com/stut">follow me on Twitter</a> you should be aware of it.</p>
<p>The Follows service monitors your followers on Twitter and sends you a daily, weekly or monthly email telling you who&#8217;s followed and who&#8217;s unfollowed you since your last email. Twitter itself has the facility to send you an email every time someone starts following you but does not offer any sort of notification when they stop.</p>
<p>I can see why Twitter have done it this way. When someone starts following you it&#8217;s likely you&#8217;ll want to check out their tweets and you might decide to follow them back. The same logic doesn&#8217;t really apply to when people stop following you, or does it?<br />
<span id="more-354"></span><br />
I built the Follows service primarily as a technical exercise, but also because I was curious to get some visibility on people who stop following me.</p>
<p>A while back a service called <a href="http://useqwitter.com/">Qwitter</a> appeared on the scene that offered an unfollow notification service so I signed up. Unfortunately it stopped working shortly after that for unknown reasons, but in the few days while it did work it was quite enlightening. By paying attention to both follow and unfollow notifications it was possible to spot some interesting trends.</p>
<p>By a strange coincidence it would appear that shortly after I launched my Follows service Qwitter started working again, but so far it doesn&#8217;t appear to be particularly reliable. It attempts to match an unfollow event to one of your tweets, effectively trying to find the cause of the action. This is daft primarily because there&#8217;s not always a single reason why people stop following you but also because they can&#8217;t possibly monitor your followers continuously &#8211; <a href="http://apiwiki.twitter.com/REST+API+Documentation#RateLimiting">the Twitter API has limits</a> that would prevent that &#8211; so it&#8217;s likely the tweet they attribute the action to was not your latest tweet at the time.</p>
<p>Since Follows went live I&#8217;ve been keeping track of what people tweet about it and Qwitter and I&#8217;ve noticed some interesting behaviour in response to notifications.</p>
<p>Quite a few users react to people unfollowing them by calling them out with a public tweet asking them why. I can understand the desire to get an explanation but doing it publicly seems a bit &#8230; well I dunno really, but it certainly isn&#8217;t the best way to go about asking the question.</p>
<p>The other common reaction is the realisation that their self-esteem can&#8217;t take being informed when people stop finding them interesting. Again I can understand this to a certain extent but it&#8217;s not something you can take personally and remain well-balanced. It&#8217;s the constant battle between the need for validation and the bliss of ignorance &#8211; part of the &#8220;human condition&#8221;.</p>
<p>Another interesting side-effect has been some comments from other Twitterers to the effect that such a service is pointless and/or purely egotistical, but I&#8217;m convinced there&#8217;s great value in getting visibility of the ongoing changes to your follower list.</p>
<p>I use the daily emails I get from Follows to do two things. First of all I check every new follower to see if it&#8217;s worth following them back, just like I did when I was getting the individual notifications from Twitter but now I only do it once a day which is far more efficient.</p>
<p>The second thing I do is have a look at every ex follower and if I&#8217;m following them I consider whether it&#8217;s worth continuing to follow them despite their decision to stop following me. I would never ask them about it because if someone has a problem with what I&#8217;m saying, whether it&#8217;s frequency or content, I would hope they&#8217;d tell me about it. It&#8217;s difficult to learn without feedback.</p>
<p>For me Twitter is not a popularity contest and it&#8217;s not an ego broadcasting outlet. It&#8217;s a conversation. If someone decides to stop following me I take that to mean they no longer want to participate in conversations with me. That&#8217;s fine, but it doesn&#8217;t necessarily mean I no longer want to participate in conversations with them, or that I&#8217;m no longer interested in what they have to say, but knowing they have made that decision is empowering.</p>
<p>What do you think? Do you see value in knowing if and when people stop following you or is it too much information?</p>
<div id="crp_related"><h3>If you liked this you might also like these:</h3><ul><li><a href="http://stut.net/2008/04/04/twitter-public-timeline/" rel="bookmark" class="crp_title">Twitter public timeline</a></li><li><a href="http://stut.net/2008/05/23/has-that-website-gone-titsup/" rel="bookmark" class="crp_title">Has that website gone TitsUp?</a></li><li><a href="http://stut.net/2009/03/08/handling-email-notifications-from-twitter/" rel="bookmark" class="crp_title">Handling email notifications from Twitter</a></li><li><a href="http://stut.net/2008/01/28/tweetmeme-launch/" rel="bookmark" class="crp_title">Tweetmeme Launch</a></li><li><a href="http://stut.net/2007/11/19/that-other-bit-of-news/" rel="bookmark" class="crp_title">That other bit of news</a></li></ul></div>
]]></content:encoded>
			<wfw:commentRss>http://stut.net/2009/01/31/twitter-unfollows-too-much-information/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Alexei Arkhipovskiy &#8211; Sharmanka</title>
		<link>http://stut.net/2009/01/25/alexei-arkhipovskiy-sharmanka/</link>
		<comments>http://stut.net/2009/01/25/alexei-arkhipovskiy-sharmanka/#comments</comments>
		<pubDate>Sun, 25 Jan 2009 09:09:25 +0000</pubDate>
		<dc:creator>Stuart</dc:creator>
				<category><![CDATA[Misc]]></category>

		<guid isPermaLink="false">http://stut.net/blog/2009/01/25/alexei-arkhipovskiy-sharmanka/</guid>
		<description><![CDATA[Absolute genius! If you liked this you might also like these:BeardymanTweetMeme Live Test Video from April 2009Extreme Sheep LED ArtBigDog &#8230; WOW!Amazing Machines]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_jade" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fstut.net%252F2009%252F01%252F25%252Falexei-arkhipovskiy-sharmanka%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Alexei%20Arkhipovskiy%20-%20Sharmanka%22%20%7D);"></div>
<p>Absolute genius!</p>
<p>  <object width="480" height="385"><param name="movie" value="http://www.youtube.com/v/F6pPP2kfHzU&amp;hl=en&amp;fs=1&amp;rel=0&amp;color1=0x3a3a3a&amp;color2=0x999999" /><param name="allowFullScreen" value="true" /><param name="allowscriptaccess" value="always" /><embed src="http://www.youtube.com/v/F6pPP2kfHzU&amp;hl=en&amp;fs=1&amp;rel=0&amp;color1=0x3a3a3a&amp;color2=0x999999" type="application/x-shockwave-flash" width="480" height="385"></embed></param></param></param></object>  </p>
<div id="crp_related"><h3>If you liked this you might also like these:</h3><ul><li><a href="http://stut.net/2010/07/08/beardyman/" rel="bookmark" class="crp_title">Beardyman</a></li><li><a href="http://stut.net/2010/02/19/tweetmeme-live-test-video-from-april-2009/" rel="bookmark" class="crp_title">TweetMeme Live Test Video from April 2009</a></li><li><a href="http://stut.net/2009/03/23/extreme-sheep-led-art/" rel="bookmark" class="crp_title">Extreme Sheep LED Art</a></li><li><a href="http://stut.net/2008/03/28/bigdog-wow/" rel="bookmark" class="crp_title">BigDog &#8230; WOW!</a></li><li><a href="http://stut.net/2006/09/29/amazing-machines/" rel="bookmark" class="crp_title">Amazing Machines</a></li></ul></div>
]]></content:encoded>
			<wfw:commentRss>http://stut.net/2009/01/25/alexei-arkhipovskiy-sharmanka/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Snippet: Simple templates with PHP</title>
		<link>http://stut.net/2008/10/28/snippet-simple-templates-with-php/</link>
		<comments>http://stut.net/2008/10/28/snippet-simple-templates-with-php/#comments</comments>
		<pubDate>Tue, 28 Oct 2008 20:36:09 +0000</pubDate>
		<dc:creator>Stuart</dc:creator>
				<category><![CDATA[Misc]]></category>

		<guid isPermaLink="false">http://stut.net/blog/?p=249</guid>
		<description><![CDATA[Several templating systems exist for PHP, but since PHP was originally created as a templating language I&#8217;ve never understaood why more developers don&#8217;t use pure PHP in their view layer. The following snippet is a small function that facilitates precisely that. The only setup required is to simply define TPL_DIR to point to the root [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_jade" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fstut.net%252F2008%252F10%252F28%252Fsnippet-simple-templates-with-php%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Snippet%3A%20Simple%20templates%20with%20PHP%22%20%7D);"></div>
<p>Several templating systems exist for PHP, but since PHP was originally created as a templating language I&#8217;ve never understaood why more developers don&#8217;t use pure PHP in their view layer. The following snippet is a small function that facilitates precisely that.</p>
<p>The only setup required is to simply define TPL_DIR to point to the root directory of your templates. You can then call the function, passing it the relative path to a template file in that directory, an array of variables to be used by the template and you can optionally have it return the results rather than sending them to the browser.</p>
<pre name="code" class="php">
function &#038; TPL($____filename, &#038;$____data = array(), $____return = false)
{
  $____retval = '';

  $____tplfilename = TPL_DIR.'/'.$____filename;

  if (file_exists($____tplfilename))
  {
    if ($____return) ob_start();

    extract($____data);

    require $____tplfilename;

    if ($____return) $____retval = ob_get_clean();
  }
  else
  {
    trigger_error('Template not found: '.$____filename, E_USER_ERROR);
  }

  return $____retval;
}
</pre>
<p>As an example let&#8217;s take a simple hello world (you saw that coming right?!). First the template&#8230;</p>
<pre name="code" class="php">
&lt;html&gt;
  &lt;head&gt;
    &lt;meta http-equiv="Content-type" content="text/html; charset=utf-8"&gt;
    &lt;title&gt;&lt;?php echo htmlentities($title, ENT_QUOTES, 'UTF-8'); ?&gt;&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;Hello &lt;?php echo htmlentities($name['first'], ENT_QUOTES, 'UTF-8'); ?&gt; &lt;?php echo htmlentities($name['last'], ENT_QUOTES, 'UTF-8'); ?&gt;&lt;/h1&gt;
  &lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Now let&#8217;s use it&#8230;</p>
<pre name="code" class="php">
define('TPL_DIR', '/var/www/public_html/templates');

$data = array();
$data['title'] = 'Hello World Example';
$data['name'] = array('first' =&gt; 'Joe', 'last' =&gt; 'Bloggs');

TPL('homepage/hello.tpl.php', $data);
</pre>
<p>That&#8217;s all there is to it. From this simple yet powerful base I&#8217;ve built fully-featured classes that handle all aspects of template management and processing, it&#8217;s an extremely stable foundation.</p>
<p>Comments, suggestions, requests and abuse are all welcomed.</p>
<div id="crp_related"><h3>If you liked this you might also like these:</h3><ul><li><a href="http://stut.net/2008/06/08/where-are-these-backslashes-coming-from/" rel="bookmark" class="crp_title">Where are these backslashes coming from?</a></li><li><a href="http://stut.net/2008/10/20/snippet-singletons-with-php/" rel="bookmark" class="crp_title">Snippet: Singletons with PHP</a></li><li><a href="http://stut.net/2008/07/20/mysql-sessions/" rel="bookmark" class="crp_title">MySQL Sessions</a></li><li><a href="http://stut.net/2008/10/15/snippet-page-expiry-in-php/" rel="bookmark" class="crp_title">Snippet: Page expiry in PHP</a></li><li><a href="http://stut.net/2008/06/08/php-models-2/" rel="bookmark" class="crp_title">PHP Models</a></li></ul></div>
]]></content:encoded>
			<wfw:commentRss>http://stut.net/2008/10/28/snippet-simple-templates-with-php/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Snippet: Password generator for PHP</title>
		<link>http://stut.net/2008/10/23/snippet-password-generator-for-php/</link>
		<comments>http://stut.net/2008/10/23/snippet-password-generator-for-php/#comments</comments>
		<pubDate>Wed, 22 Oct 2008 23:47:38 +0000</pubDate>
		<dc:creator>Stuart</dc:creator>
				<category><![CDATA[Misc]]></category>

		<guid isPermaLink="false">http://stut.net/blog/?p=245</guid>
		<description><![CDATA[Really short snippet today &#8211; a password generator. Not much to say about this one since it&#8217;s pretty simple. Pass it the length of password you want and optionally a string of valid characters it can use and it&#8217;ll give you a random string back. function GeneratePassword($len, $allowedchars = false) { if ($allowedchars === false) [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_jade" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fstut.net%252F2008%252F10%252F23%252Fsnippet-password-generator-for-php%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Snippet%3A%20Password%20generator%20for%20PHP%22%20%7D);"></div>
<p>Really short snippet today &#8211; a password generator. Not much to say about this one since it&#8217;s pretty simple. Pass it the length of password you want and optionally a string of valid characters it can use and it&#8217;ll give you a random string back.</p>
<pre name="code" class="php">
function GeneratePassword($len, $allowedchars = false)
{
  if ($allowedchars === false)
    $allowedchars = 'abcdefghijklmnopqrstuvwxyz01234567890';
  $retval = '';
  $maxidx = strlen($allowedchars)-1;
  for ($i = 0; $i < $len; $i++)
  {
    $retval .= $allowedchars[rand(0,$maxidx)];
  }
  return $retval;
}
</pre>
<p>I've got something a bit meatier lined up for the next snippet so I recommend subscribing to <a href="http://feeds.feedburner.com/Stut">the RSS feed</a> to make sure you don't miss it. As always comments, questions, suggestions and requests are welcomed.</p>
<div id="crp_related"><h3>If you liked this you might also like these:</h3><ul><li><a href="http://stut.net/2008/10/16/snippet-cookie-class-for-php/" rel="bookmark" class="crp_title">Snippet: Cookie class for PHP</a></li><li><a href="http://stut.net/2008/10/17/snippet-time-class-for-php/" rel="bookmark" class="crp_title">Snippet: Time class for PHP</a></li><li><a href="http://stut.net/2008/07/20/mysql-sessions/" rel="bookmark" class="crp_title">MySQL Sessions</a></li><li><a href="http://stut.net/2008/10/15/snippet-page-expiry-in-php/" rel="bookmark" class="crp_title">Snippet: Page expiry in PHP</a></li><li><a href="http://stut.net/2008/10/28/snippet-simple-templates-with-php/" rel="bookmark" class="crp_title">Snippet: Simple templates with PHP</a></li></ul></div>
]]></content:encoded>
			<wfw:commentRss>http://stut.net/2008/10/23/snippet-password-generator-for-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Snippet: Singletons with PHP</title>
		<link>http://stut.net/2008/10/20/snippet-singletons-with-php/</link>
		<comments>http://stut.net/2008/10/20/snippet-singletons-with-php/#comments</comments>
		<pubDate>Mon, 20 Oct 2008 19:09:00 +0000</pubDate>
		<dc:creator>Stuart</dc:creator>
				<category><![CDATA[Misc]]></category>

		<guid isPermaLink="false">http://stut.net/blog/?p=263</guid>
		<description><![CDATA[Today&#8217;s snippet describes the singleton pattern. The singleton pattern is a method of creating a class that statically maintains an instance of itself and ensures that no other instances can be created. This is useful for classes that implement app-wide services such as logging, DB access and other shared resources. The basic requirements for a [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_jade" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fstut.net%252F2008%252F10%252F20%252Fsnippet-singletons-with-php%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Snippet%3A%20Singletons%20with%20PHP%22%20%7D);"></div>
<p>Today&#8217;s snippet describes the singleton pattern. The singleton pattern is a method of creating a class that statically maintains an instance of itself and ensures that no other instances can be created. This is useful for classes that implement app-wide services such as logging, DB access and other shared resources.</p>
<p>The basic requirements for a singleton class are as follows&#8230;</p>
<ul>
<li><strong>The constructor is private</strong><br />This ensures that nothing outside the class can create instances.</li>
<li><strong>A static method exists that returns <em>the</em> instance</strong><br />When first called the method creates a statically stored instance of the class. Subsequent calls return the existing instance.</li>
<li><strong>Magic methods to prevent the instance from being cloned or serialized <snall><em>(PHP-specific)</em></small></strong><br />PHP provides functionality to clone and serialize objects. By adding simple implementations of the <em>__clone</em> and <em>__wakeup</em> magic methods we can prevent this from happening.
</ul>
<p>That&#8217;s basically it. Here&#8217;s a simple example&#8230;</p>
<pre name="code" class="php">
class Singleton
{
  static private $_instance = null;

  public static function &#038; Instance()
  {
    if (is_null(self::$_instance))
    {
      self::$_instance = new self();
    }
    return self::$_instance;
  }

  private function __construct()
  {
    // Do normal instance initialisation here
    // Nothing singleton-related should be present
  }

  public function __destruct()
  {
    // This is just here to remind you that the
    // destructor must be public even in the case
    // of a singleton.
  }

  public function __clone()
  {
    trigger_error('Cloning instances of this class is forbidden.', E_USER_ERROR);
  }

  public function __wakeup()
  {
    trigger_error('Unserializing instances of this class is forbidden.', E_USER_ERROR);
  }

  private $var = '';

  public function SetVar($val)
  {
    $this-&gt;var = $val;
  }

  public function GetVar()
  {
    return $this-&gt;var;
  }
}
</pre>
<p>This is an extremely simple example but it demonstrates all of the core concepts. You use this class as follows&#8230;</p>
<pre name="code" class="php">
$obj1 = Singleton::Instance();
$obj1-&gt;SetVar('some value');

$obj2 = Singleton::Instance();
echo $obj2-&gt;GetVar(); // This will echo 'some value'
</pre>
<p>If you attempt to clone or unserialize an instance your script will fail with an error.</p>
<p>I use this pattern all over the place and definitely recommend using it where it makes sense in your applications. An alternative would be to implement the entire class statically and to be honest I&#8217;m not sure whether there are advantages to either implementation. If someone knows please share in the comments.</p>
<p>That&#8217;s it for this snippet, stay tuned for more. As always comments, questions, suggestions and requests are welcomed.</p>
<div id="crp_related"><h3>If you liked this you might also like these:</h3><ul><li><a href="http://stut.net/2008/10/17/snippet-time-class-for-php/" rel="bookmark" class="crp_title">Snippet: Time class for PHP</a></li><li><a href="http://stut.net/2008/10/16/snippet-cookie-class-for-php/" rel="bookmark" class="crp_title">Snippet: Cookie class for PHP</a></li><li><a href="http://stut.net/2008/10/28/snippet-simple-templates-with-php/" rel="bookmark" class="crp_title">Snippet: Simple templates with PHP</a></li><li><a href="http://stut.net/2008/06/08/php-models-2/" rel="bookmark" class="crp_title">PHP Models</a></li><li><a href="http://stut.net/2008/07/20/mysql-sessions/" rel="bookmark" class="crp_title">MySQL Sessions</a></li></ul></div>
]]></content:encoded>
			<wfw:commentRss>http://stut.net/2008/10/20/snippet-singletons-with-php/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Snippet: Time class for PHP</title>
		<link>http://stut.net/2008/10/17/snippet-time-class-for-php/</link>
		<comments>http://stut.net/2008/10/17/snippet-time-class-for-php/#comments</comments>
		<pubDate>Fri, 17 Oct 2008 16:28:47 +0000</pubDate>
		<dc:creator>Stuart</dc:creator>
				<category><![CDATA[Misc]]></category>

		<guid isPermaLink="false">http://stut.net/blog/?p=251</guid>
		<description><![CDATA[Pretty boring snippet today, but I find it immensely useful. My time class provides representations of periods from OneMinute to OneYear. It only has one method which will calculate an absolute time by adding a given time period to the current time. You can optionally provide a format intended for use by the date function [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_jade" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fstut.net%252F2008%252F10%252F17%252Fsnippet-time-class-for-php%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Snippet%3A%20Time%20class%20for%20PHP%22%20%7D);"></div>
<p>Pretty boring snippet today, but I find it immensely useful. My time class provides representations of periods from OneMinute to OneYear. It only has one method which will calculate an absolute time by adding a given time period to the current time. You can optionally provide a format intended for use by the date function to return a formatted string rather than a timestamp.</p>
<p>Hope you find it useful too.</p>
<pre name="code" class="php">
	class Time
	{
		const OneMinute = 60;
		const FiveMinutes = 300;
		const TenMinutes = 600;
		const FifteenMinutes = 900;
		const HalfHour = 1800;
		const OneHour = 3600;
		const SixHours = 21600;
		const HalfDay = 43200;
		const OneDay = 86400;
		const SevenDays = 604800;
		const ThirtyDays = 2592000;
		const OneYear = 31536000;

		public static function GetAbsolute($time, $format = false)
		{
			if (is_numeric($time) and $time &lt; (time()-1))
			{
				$time = time() + $time;
			}
			else
			{
				$time = strtotime($time);
			}
			return (false === $format ? $time : date($format, $time));
		}
	}
</pre>
<p>Comments, questions, suggestions and requests are welcomed as always.</p>
<div id="crp_related"><h3>If you liked this you might also like these:</h3><ul><li><a href="http://stut.net/2008/10/16/snippet-cookie-class-for-php/" rel="bookmark" class="crp_title">Snippet: Cookie class for PHP</a></li><li><a href="http://stut.net/2008/10/15/snippet-page-expiry-in-php/" rel="bookmark" class="crp_title">Snippet: Page expiry in PHP</a></li><li><a href="http://stut.net/2008/10/23/snippet-password-generator-for-php/" rel="bookmark" class="crp_title">Snippet: Password generator for PHP</a></li><li><a href="http://stut.net/2008/10/20/snippet-singletons-with-php/" rel="bookmark" class="crp_title">Snippet: Singletons with PHP</a></li><li><a href="http://stut.net/2004/10/29/athletics-club-programming-challenge/" rel="bookmark" class="crp_title">Athletics Club Programming Challenge</a></li></ul></div>
]]></content:encoded>
			<wfw:commentRss>http://stut.net/2008/10/17/snippet-time-class-for-php/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
