<?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>timwhitlock.info &#187; php</title>
	<atom:link href="http://timwhitlock.info/blog/tag/php/feed/" rel="self" type="application/rss+xml" />
	<link>http://timwhitlock.info</link>
	<description>Tim Whitlock&#039;s personal site and blog</description>
	<lastBuildDate>Thu, 15 Dec 2011 13:51:45 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>PDF conversion with WebKit</title>
		<link>http://timwhitlock.info/blog/2011/12/14/pdf-conversion-with-webkit/</link>
		<comments>http://timwhitlock.info/blog/2011/12/14/pdf-conversion-with-webkit/#comments</comments>
		<pubDate>Wed, 14 Dec 2011 12:02:13 +0000</pubDate>
		<dc:creator>tim</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[pdf]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[webkit]]></category>
		<category><![CDATA[wkhtmltopdf]]></category>

		<guid isPermaLink="false">http://timwhitlock.info/?p=1345</guid>
		<description><![CDATA[It had been quite some years since I last looked at the options for PDF generation in PHP, so when I needed to add PDF support to Brandfeed I did a bit of research. I ended up on this Stackoverflow thread which overall seems to recommend TCPDF with some fairly strong supporters for other libraries, [...]]]></description>
			<content:encoded><![CDATA[<p><img class="size-full wp-image-1350 alignright" title="Brandfeed PDF icon screengrab" src="http://cf.timwhitlock.info/wp-content/uploads/2011/12/bf-pdf.jpg" alt="screen grab" width="392" height="199" /> It had been quite some years since I last looked at the options for PDF generation in PHP, so when I needed to add PDF support to <a href="http://brandfeed.net/" target="_blank">Brandfeed</a> I did a bit of research. I ended up on <a href="http://stackoverflow.com/questions/560583/which-is-the-best-pdf-library-for-php" target="_blank">this Stackoverflow thread</a> which overall seems to recommend <a href="http://www.tcpdf.org/" target="_blank">TCPDF</a> with some fairly strong supporters for other libraries, including <a href="http://www.mpdf1.com/mpdf/" target="_blank">mPDF</a>.</p>
<p>I wasn&#8217;t looking forward to trying them all out to decide which library to use, but as it turns out I didn&#8217;t have to. When I discovered <a href="http://code.google.com/p/wkhtmltopdf/" target="_blank">wkhtmltopdf</a>, my decision was made. (I know that sounds like a cheesy marketing testimonial, bare with me)</p>
<p><span id="more-1345"></span><strong>I only needed to convert HTML documents to PDF</strong>. i.e. I didn&#8217;t need to programmatically draw vectors, or anything like that. That&#8217;s a pretty important prerequisite. When you consider how good HTML and CSS are for laying out a document, it makes you wonder why you&#8217;d do it any other way. The catch is that your PDF generator needs to render your HTML as well as a browser does.</p>
<p>wkhtmltopdf is built on WebKit which means it does exactly that &#8211; it renders the PDF as well as a browser, because it basically is a browser. It even runs the JavaScript. The fact that other libraries have their own [possibly bespoke] rendering engines with huge limitations seems pretty crazy once you&#8217;ve seen wkhtmltopdf in action.</p>
<p>Anyway, I thought I&#8217;d share a few things that I encountered while getting this integrated to my PHP application.</p>
<h3>PHP Bindings</h3>
<p>Despite my initial excitement upon discovering  <a href="https://github.com/mreiferson/php-wkhtmltox" target="_blank">php-wkhtmltox</a> (PHP extension for wkhtmltopdf and wkhtmltoimage), I ended up not using this extension. My main reasons were as follows:</p>
<ul>
<li>Rather opaque configuration options</li>
<li>Clumsy when using with stdin and stdout</li>
</ul>
<p>I decided to execute the wkhtmltopdf binary via the shell and use <a href="http://php.net/manual/en/function.proc-open.php" target="_blank">PHPs procopen</a> and related functions to pipe in my HTML and grab my PDF without faffing around with temporary files. I&#8217;m unsure of the comparative overheads in using the shell versus using a PHP extension, but it works very well regardless.</p>
<h3>Compiling from source</h3>
<p><em>Don&#8217;t.</em></p>
<p>I&#8217;m not an expert sysadmin but I&#8217;ve compiled plenty of software on the Linux and Mac command ines and this did not go well. It requires first compiling the open source version of Quicktime, which is almost 1GB of source code and was building for over an hour before I hit Ctrl-C on my Mac. It failed first time on my Linux machines&#8230; basically I gave up.</p>
<p>Static binaries are available for both Linux and Mac, and they worked fine for me. I recommend saving yourself the pain and just using them, even if they&#8217;re not available for the latest version of wkhtmltopdf. (The Mac binary is a couple of versions old).</p>
<h3>Fonts</h3>
<p>On my Linux servers, after installing a whole bunch of X11 stuff I probably didn&#8217;t need and definitely didn&#8217;t understand, I discovered via <a href="http://code.google.com/p/wkhtmltopdf/wiki/static" target="_blank">the comments here</a> that I needed to install the <em>urw-fonts</em> package. If you don&#8217;t have the fonts installed correctly you get black squares instead of glyphs.</p>
<h3>JavaScript errors</h3>
<p>wkhtmltopdf is WebKit, which means it executes JavaScript too. I was using the same HTML template to render PDFs as our <a href="http://brandfeed.net/brand/post/print/1/5156" target="_blank">&#8216;print&#8217; page</a>. This page calls <code>window.print()</code> to invoke the printer dialogue. As it turns out (and it took me some head-scratching) wkhtmlpdf dies when you call this function, and it does so without any decent explanation of the error.</p>
<p>Rather than muck around with my PHP template, I just passed <code>--disable-javascript</code> to wkhtmltopdf, and everything was fine.</p>
]]></content:encoded>
			<wfw:commentRss>http://timwhitlock.info/blog/2011/12/14/pdf-conversion-with-webkit/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Varnish with PHP (and WordPress)</title>
		<link>http://timwhitlock.info/blog/2010/08/21/varnish-with-php-and-wordpress/</link>
		<comments>http://timwhitlock.info/blog/2010/08/21/varnish-with-php-and-wordpress/#comments</comments>
		<pubDate>Sat, 21 Aug 2010 15:34:16 +0000</pubDate>
		<dc:creator>tim</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[caching]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[varnish]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://web.2point1.com/?p=544</guid>
		<description><![CDATA[The final stage in speeding up my blog was to add some serious caching to the front of it. This may even have been overkill, because it was already pretty swift under nginx/php-fpm, but cutting out the database connections would speed it up even more. I had a quick go with the W3 Total Cache [...]]]></description>
			<content:encoded><![CDATA[<p>The final stage in <a href="http://web.2point1.com/2010/08/17/now-powered-by-wordpress-3-nginx-and-php-fpm/">speeding up my blog</a> was to add some serious caching to the front of it. This may even have been overkill, because it was already pretty swift under <a href="http://web.2point1.com/2010/08/17/php-fpm-5-3-3-under-nginx/" target="_blank">nginx/php-fpm</a>, but cutting out the database connections would speed it up even more.</p>
<p>I had a quick go with the <a href="http://wordpress.org/extend/plugins/w3-total-cache/" target="_blank">W3 Total Cache</a> WordPress plug-in, but it seems rather biased to running Apache (which I&#8217;m not) and I experienced some strange errors that I failed to immediately fix. Rather than wrestle with it, or try other WordPress caches, I decided to get to grips with <a href="http://varnish-cache.org/" target="_blank">Varnish</a>. This is something I&#8217;d been meaning to do for ages, and of course it isn&#8217;t limited to WordPress &#8211; <a href="http://varnish-cache.org/" target="_blank">Varnish</a> is a fabulous caching solution for whatever site you&#8217;re building.</p>
<p><span id="more-544"></span>The challenge with deploying Varnish to any site is to ensure you cache as much as you can while ensuring dynamic content is fresh when required. With WordPress as an example, the cases are fairly obvious. While logged in to the admin screens you don&#8217;t want to cache anything, but the outside world reading your posts should get cached pages whenever possible. In their simplest forms, these cases <em>almost </em>work out of the box with Varnish, but there are a few gotchas. There is also scope to cache more aggressively if you choose to, and most importantly you&#8217;ll want to ensure that when content is altered, the changes are visible to everyone immediately.</p>
<p><strong>Cookie gotcha no.1 </strong></p>
<p>By default, if you send a cookie to Varnish it assumes quite sensibly that you are in some way expecting dynamic content from the back end. e.g. you may be an admin user, logged in to the site. Of course Varnish can&#8217;t actually know what the cookie is for, so when your browser sends a completely redundant Google Analytics <a href="http://code.google.com/apis/analytics/docs/concepts/gaConceptsCookies.html" target="_blank">UTM cookie</a>, you&#8217;ll miss the cache. On this site, that means Varnish would only serve you a maximum of one cached page per visit.</p>
<p><strong>Anonymous session cookies</strong></p>
<p>If a visitor comments on a WordPress site, they are pseudo-logged-in by way of a simple cookie exchange. This means the site can do niceties like pre-populate comment forms for commenting again. These niceties are exactly that, so if you overlook this functionality and block those cookies you can serve more cached pages. In my case, I am happy to make this trade.</p>
<p>The following snippet from my configuration shows how I&#8217;ve maximised anonymous user caching while ensuring admin users always get dynamic content</p>
<pre>sub vcl_recv {
  # ...
  # admin users always miss the cache
  if( req.url ~ "^/wp-(login|admin)" || req.http.Cookie ~ "wordpress_logged_in_" ){
    return (pass);
  }
  # ignore any other cookies
  unset req.http.Cookie;
  return (lookup);
}</pre>
<p><strong>Purging when content changes</strong></p>
<p>HTTP defines a wealth of cache control headers that allow applications and clients to specify what is to be cached and for how long, etc.. but there is no standard way to tell a cache that certain content is to be purged. This is the main challenge of deploying Varnish to a site like WordPress.  A post may be updated, or a user may comment. You&#8217;ll want to purge any affected pages from the cache immediately, regardless of their original expiry. Putting that business logic into your VCL configuration would be hugely impractical &#8211; it&#8217;s really your application&#8217;s jurisdiction.</p>
<p>I looked at various purging techniques, and by far the best approach in my opinion is for your application to talk directly to the <a href="http://varnish-cache.org/wiki/CLI" target="_blank">Varnish administration CLI </a>which listens on the network on a different port to the cache itself. As long as your backend can access your front end by a secure, internal address, you can execute commands on all your Varnish front ends any time you want from your application.</p>
<p>To achieve this, I first had a look at the <a href="http://wordpress.org/extend/plugins/wordpress-varnish/" target="_blank">wp-varnish plug-in</a> which uses the <a href="http://varnish-cache.org/wiki/VCLExamplePurging" target="_blank">HTTP PURGE</a> method. I don&#8217;t like this approach at all; it&#8217;s very limited, too closely coupled to the VCL config, and also this plug-in doesn&#8217;t purge all pages relating to changes, such as tag and archive indexes.</p>
<p><strong>So, I rolled my own</strong></p>
<p>I started by writing a <a href="http://github.com/timwhitlock/php-varnish/blob/master/VarnishAdminSocket.php" target="_blank">VarnishAdminSocket</a> class, which talks to Varnish from PHP. This was pretty easy to do, because the <em>varnishadm</em> program uses a very basic text-based protocol. This isn&#8217;t designed for WordPress, this is designed for use in any PHP application.</p>
<p>My <a href="http://github.com/timwhitlock/php-varnish" target="_blank"><strong>php-varnish</strong> toolkit is on Github</a>. It includes a <a href="http://github.com/timwhitlock/php-varnish/blob/master/wordpress-plugin.php" target="_blank">WordPress plug-in </a>which I am using on this site and you can see <a href="http://github.com/timwhitlock/php-varnish/blob/master/wordpress-plugin/wordpress.vcl" target="_blank">the VCL configuration</a> I am using too.</p>
]]></content:encoded>
			<wfw:commentRss>http://timwhitlock.info/blog/2010/08/21/varnish-with-php-and-wordpress/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
		<item>
		<title>WordPress 3 &#8230; yep, it&#8217;s still WordPress</title>
		<link>http://timwhitlock.info/blog/2010/08/21/wordpress-3-is-still-wordpress/</link>
		<comments>http://timwhitlock.info/blog/2010/08/21/wordpress-3-is-still-wordpress/#comments</comments>
		<pubDate>Sat, 21 Aug 2010 13:38:42 +0000</pubDate>
		<dc:creator>tim</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://web.2point1.com/?p=516</guid>
		<description><![CDATA[As part of my &#8220;speeding up my blog&#8221; series, I planned to write a nice, informative post about upgrading to WordPress 3, deploying your theme to a CDN, and getting it all running under nginx. Unfortunately WordPress irritated me so much in the process, that this has turned into more of rant. Sorry in advance. [...]]]></description>
			<content:encoded><![CDATA[<p>As part of my &#8220;<a href="http://web.2point1.com/2010/08/17/now-powered-by-wordpress-3-nginx-and-php-fpm/">speeding up my blog</a>&#8221; series, I planned to write a nice, informative post about upgrading to WordPress 3, deploying your theme to a CDN, and getting it all <a href="http://web.2point1.com/2010/08/17/php-fpm-5-3-3-under-nginx/">running under nginx</a>. Unfortunately WordPress irritated me so much in the process, that this has turned into more of rant. Sorry in advance.</p>
<p>I have a like/hate relationship with WordPress. That is to say that it does a lot, it has a great admin area and there&#8217;s a large community producing themes and plug-ins. However, I am a PHP developer of many years, and every time I come into direct contact with the core WordPress code-base I end up being sick in my mouth.</p>
<p><em><span id="more-516"></span>What&#8217;s to hate?</em> Well it kind of reminds me of that old BMW advert (or was it Audi?) the one with the swan &#8211; On the surface it glides effortlessly, but under the water it&#8217;s an ugly son of a bitch, churning away to get the job done.  I had the misfortune of working on a nasty WPMU project last year and found myself looking under the <a href="http://simple.wikipedia.org/wiki/British_English#Vocabulary_in_British_English" target="_blank">bonnet</a> more than was good for me. Not only is WordPress a horribly written piece of PHP, (and possibly responsible for many a bad word said about PHP itself) it has a legacy of incremental improvements alongside a legacy of community-contributed code that strives to remain as reverse compatible as possible.</p>
<p>If you don&#8217;t want to read my rantings, skip to <a href="#bitattheend">the bit at the end</a>, otherwise &#8230; WordPress, let me count the ways a hate thee.</p>
<p><strong>1. Base URL hell</strong></p>
<p>I wanted to get as much of my static assets onto a CDN as I could &#8211; a topic I&#8217;ll cover in full later. This means serving static files (such as images and css) from a different base URL. It <em>should</em> be simple to upload your theme and plug-in directories to another server and ensure that WordPress points to the correct assets in your HTML. It is not simple. Just look at the <a href="http://codex.wordpress.org/Determining_Plugin_and_Content_Directories" target="_blank">hideousness of determining base URLs in WordPress</a>. This simple task should not require a complex plug-in to cover all possible legacy issues. Furthermore, WordPress allows <em>all</em> plug-ins to mess around with base URLs, which means one plug-in might completely undo the work of another.</p>
<p>Oh, by the way &#8211; WordPress defines the constant <code>WP_CONTENT_URL</code> before it loads any plug-ins, so the only way to override this is to define it in <code>wp-config.php</code>.</p>
<p><strong>2. Canonical domain ridiculousness</strong></p>
<p>If you access a WordPress site from a domain that is not hard-wired into the site&#8217;s database, it will redirect you to the &#8216;correct&#8217; URL. This is the default behaviour. So, if you want to host the same exact site from a different domain, or different port, (e.g. for debugging a distributed system) you will have to write a WordPress plug-in to override this behaviour. Why does this annoy me?</p>
<ol>
<li>It is not PHP&#8217;s job to decide what domain a site lives on &#8211; that is the job of the web server. A domain redirect like this should be done at server config level.</li>
<li>No system should hard-wire its domain into database-saved preferences. This is nuts, and WordPress does it in multiple places.</li>
</ol>
<p><strong>3. Magic quotes are the Devil\\\&#8217;s work</strong></p>
<p>Magic quotes are so utterly evil that they have been <a href="http://php.net/manual/en/security.magicquotes.php" target="_blank">removed from PHP 5.3</a>. They are testament to the amateur reputation of PHP. However, even in earlier versions of PHP, magic quotes could be easily disabled, and anyone who knows what they&#8217;re doing with PHP will have been doing so  for years. Furthermore any system written properly would insist on them being disabled. Not so with WordPress. In fact, being able to disable magic quotes poses a problem for WordPress. How can it guarantee to work if you can forcibly disable magic quotes?</p>
<p>Answer: By forcing them <em>even more forcibly</em>. WordPress 3 forces all script input through its own <code>wp_magic_quotes</code> function. Funnily enough <a href="http://wordpress.org/search/wp_magic_quotes" target="_blank">this isn&#8217;t documented</a>, but <a href="http://gist.github.com/542222" target="_blank">it looks like this</a>.</p>
<p><strong>4. mysqli support &#8211; or not.</strong></p>
<p>The core WordPress code-base uses the old mysql database driver. When I say <em>old</em>, consider that the mysqli (improved) extension was introduced for versions of MySQL &gt; 4.1.3  in PHP 5, which came out of beta in <strong>2004</strong>. WordPress does have a drop-in replacement system though, which allows you to place your own <code>db.php</code> file in <code>wp-content</code>. Here&#8217;s the third-party-developed <a href="http://barahmand.com/2010/06/wordpress-3-and-mysqli/" target="_blank">mysqli adapter for WordPress</a> that I am using on this site.</p>
<p>So that&#8217;s okay then, right? Well, judging by the required location for the <code>db.php</code> file, this adapter system looks rather like an after-thought. Using the alternative database adapter means calling <code>require_wp_db()</code> rather than just including the original <code>wp-includes/wp-db.php</code> file. Great &#8211; except is every plug-in out there doing this? Even WordPress itself isn&#8217;t fully patched to support this. At the time of writing, the &#8220;<em>famous </em>5-minute installer&#8221; uses the mysql adapter only &#8211; I had to <a href="http://gist.github.com/542243" target="_blank">patch wp-admin/install.php</a> to be able to install WordPress with only mysqli on my system.</p>
<p><strong>5. Depreciated code handling</strong></p>
<p>Watch out for depreciated function names. For example: WordPress changed <code>wp_specialchars</code> to <code>esc_html</code>. This will result in notices being raised in older themes, which you&#8217;ll only see if you enable <code>WP_DEBUG</code>. It&#8217;s a good idea to find-and-replace these if you&#8217;re upgrading an old theme to WP3. I don&#8217;t <em>really </em>hate WordPress for this, but it seems a bit of a pointless change, and isn&#8217;t it a bit late in the day for being pedantic about naming conventions? A whim like this just contributes to the amount of legacy in the system and the amount of code devoted to handling depreciation. There&#8217;s a 2,500 line file devoted to depreciating functions that are to be removed &#8220;later&#8221;. Making a clean break with WordPress 3 would have prevented many people from upgrading, so I understand why they do this; I just don&#8217;t like the effect it has on the code-base.</p>
<p>I could go on, but I&#8217;m hungry. If you want more, see what <a href="http://wonko.com/post/wordpresss_idea_of_sql_injection_security_addslashes" target="_blank">this guy has to say on the topic</a>.</p>
<p><a name="bitattheend"></a></p>
<p><strong>The bit at the end</strong></p>
<p>All in all, I&#8217;m disappointed that WordPress 3 wasn&#8217;t taken as an opportunity to improve upon its own legacy. It is fairly clear that popularity with amateurs, &#8216;ease of use&#8217; and backward compatibility is more important to the team than creating good, robust software that developers will appreciate. <a href="http://drupal.org/" target="_blank">Drupal</a> sets a much better example. I&#8217;m not saying I don&#8217;t have any gripes with Drupal, but they are much happier to take reduced backward compatibility on the chin when they release new versions. This means more incompatible modules, but such is the price of progress.</p>
]]></content:encoded>
			<wfw:commentRss>http://timwhitlock.info/blog/2010/08/21/wordpress-3-is-still-wordpress/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>PHP-FPM 5.3 under Nginx</title>
		<link>http://timwhitlock.info/blog/2010/08/17/php-fpm-5-3-3-under-nginx/</link>
		<comments>http://timwhitlock.info/blog/2010/08/17/php-fpm-5-3-3-under-nginx/#comments</comments>
		<pubDate>Tue, 17 Aug 2010 16:21:59 +0000</pubDate>
		<dc:creator>tim</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[php-fpm]]></category>

		<guid isPermaLink="false">http://web.2point1.com/?p=487</guid>
		<description><![CDATA[«« See previous post for getting nginx up and running. This post is about getting PHP running as a FastCGI. PHP 5.3 with FastCGI Process Manager Running PHP as a FastCGI (or even a regular CGI) means that it&#8217;s decoupled from the web server, and if you&#8217;ve historically run mod_php, then this is something you&#8217;ll [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://web.2point1.com/2010/08/17/now-powered-by-wordpress-3-nginx-and-php-fpm">«« See previous post </a>for getting nginx up and running. This post is about getting PHP running as a FastCGI.</p>
<h3><span id="more-487"></span>PHP 5.3 with FastCGI Process Manager</h3>
<p>Running PHP as a FastCGI (or even a regular CGI) means that it&#8217;s  decoupled from the web server, and if you&#8217;ve historically run mod_php,  then this is something you&#8217;ll have to get used to. For example,  restarting your web server won&#8217;t refresh your PHP settings. The CGI listens on a port much like the web server itself does, and that&#8217;s how  the web server passes requests though to PHP. There&#8217;s plenty of  information on why this is good news, <a href="http://2bits.com/articles/apache-fcgid-acceptable-performance-and-better-resource-utilization.html" target="_blank">like here</a>.</p>
<p>The nginx wiki has plenty of information and examples of <a href="http://wiki.nginx.org/PHPFcgiExample" target="_blank">running PHP as a FastCGI</a>, but I had some bad experiences using these examples. Quite  possibly this was due to my tuning it badly, but regardless, <strong>discovering <a href="http://php-fpm.org/" target="_blank">PHP-FPM</a> was a major result</strong>. Furthermore FPM is now bundled with <a href="http://www.php.net/archive/2010.php#id2010-07-22-2" target="_blank">PHP 5.3.3</a> &#8211; I thoroughly recommend, if you can upgrade to PHP 5.3.x that you do  so. If you can&#8217;t, and you still want to use PHP-FPM, it&#8217;s a lot more  effort to install and you may have to roll back your version of PHP  5.2.x &#8211; I won&#8217;t be covering that here, check the <a href="http://php-fpm.org/download/" target="_blank">various patches here</a> instead.</p>
<p>There doesn&#8217;t seem to be a huge amount of information about configuring PHP-FPM in PHP 5.3.3, so I&#8217;ll share my configs here. To start with building php-fpm from the downloaded PHP sources is as simple enabling the option. The rest of the build process is as normal, but you&#8217;ll have a php-fpm binary installed at <code>/usr/local/sbin/php-fpm</code></p>
<pre>$ ./configure --enable-fpm</pre>
<p>The  following is a single nginx server block, which defines a virtual host capable of executing PHP via FastCGI.</p>
<pre>server {
  listen       80;
  server_name  *.timwhitlock.info timwhitlock.info;
  root         /home/vhosts/timwhitlock.info/httpdocs;
  access_log   /home/vhosts/timwhitlock.info/log/access.log main;
  error_log    /home/vhosts/timwhitlock.info/log/error.log  warn;
  # Process PHP files with fastcgi
  location ~ \.php$ {
    if ( !-f $request_filename ) {
      return 404;
    }
   <strong> include /etc/nginx/fastcgi_params;</strong>
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
  }
  location / {
    index  index.php;
  }
}</pre>
<p>Notice the inclusion of <code>/etc/nginx/fastcgi_params</code>. This file may be bundled with your nginx installation already. If not, it looks like this:</p>
<pre>fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;

fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
#fastcgi_param  REDIRECT_STATUS    200;</pre>
<p>I also added the following lines, on the <a href="http://interfacelab.com/nginx-php-fpm-apc-awesome/" target="_blank">recommendation I found here</a> (in fact this is probably what went wrong with my earlier experiments with FPM under 5.2.x). I recommend adding these lines.</p>
<pre>fastcgi_connect_timeout 60;
fastcgi_send_timeout 180;
fastcgi_read_timeout 180;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
fastcgi_intercept_errors on;</pre>
<p>That&#8217;s got nginx ready to pass PHP execution onto port 9000, so now we  have to get PHP-FPM up and running on that port. PHP-FPM has it&#8217;s own  configuration, which as of this new release has shifted from an XML format to the  familiar <em>ini</em> style. This is a great move, firstly because it looks  nicer, and secondly because it also allows us to add additional PHP ini settings.</p>
<p>My main <code>php.ini</code> and <code>php-fpm.conf</code> files are under <code>/usr/local/etc</code> which is the default location. The <code>php-fpm.conf</code> file contains global settings and settings for individual resource pools. Each pool listens on its own port, and can have its own PHP settings.</p>
<p>If you need to support multiple hosts as I do, then you&#8217;ll probably want each nginx server to pass PHP requests on to a different pool, specified by its port. To achieve this, I removed all resource pools from the main config and added a wild-card inclusion much like I did with the nginx config. My main <code>php-fpm.conf</code> file looks like this:</p>
<pre>[global]

pid = /usr/local/var/run/php-fpm.pid
error_log = /usr/local/var/log/php-fpm.log
log_level = notice
emergency_restart_threshold = 0
emergency_restart_interval = 0
process_control_timeout = 0
daemonize = yes

;  pools defined in virtual hosts
include=/home/vhosts/*/conf/php-fpm.include</pre>
<p>Then each host has its own config in <code>php-fpm.include</code> as follows.</p>
<pre>[mypool]

listen = 127.0.0.1:9000
listen.backlog = -1
listen.allowed_clients = 127.0.0.1

; Unix user/group of processes
user = nginx
group = nginx

; Choose how the process manager will control the number of child processes.
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 10
pm.max_requests = 100

; Pass environment variables
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp

; host-specific php ini settings here
php_admin_value[open_basedir] = /home/vhosts/timwhitlock.info/httpdocs:/tmp</pre>
<p>Any directives missing here were just left as default. Note that I set the user to nginx, although I don&#8217;t know whether this is a good idea, or not. This is possibly just a throw-back to my <code>mod_php</code> days where PHP would always run as the <code>apache</code> user.</p>
<p><strong>Process manager tuning</strong></p>
<p>There&#8217;s not much documentation on the <code>pm.*</code> directives, namely <code>max_children, start_servers, min_spare_servers, max_spare_servers, and max_requests</code>. However, they are analogous to other settings you may be familiar with. The <a href="http://httpd.apache.org/docs/2.0/mod/prefork.html" target="_blank">well documented Apache directives</a>,  <code>MaxClients, StartServers, MinSpareServers, MaxSpareServers and MaxRequestsPerChild</code> are worth a look to make sense of these options.</p>
<p>Once a process has been used it still occupies memory, even when idle, so you don&#8217;t want more of these processes running than your server can handle. I kept my spares low, and my max children under a number I was confident the server would have enough memory to cope with. This is not a particular area of expertise for me, so I suggest doing your own research.</p>
<p>To start up PHP, run the program telling it where your configs are:</p>
<pre># php-fpm -c /usr/local/etc  -y /usr/local/etc/php-fpm.conf</pre>
<p>If you need to reload any configurations, you can do a graceful reload will the kill command, as follows taking the process id from disk:</p>
<pre># cat /usr/local/var/run/php-fpm.pid | xargs kill -s USR2</pre>
<p><strong>Update</strong></p>
<p>Since writing this I&#8217;ve improved my set up a bit</p>
<ol>
<li> Using <a href="http://svn.php.net/repository/php/php-src/branches/PHP_5_3/sapi/fpm/init.d.php-fpm.in" target="_blank">this init.d script</a> to start, stop and reload PHP configs</li>
<li>Upgrading to PHP 5.3.6: the processes now show the pool names, which is handy</li>
<li>I&#8217;m also using a Unix socket instead of a listening port</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://timwhitlock.info/blog/2010/08/17/php-fpm-5-3-3-under-nginx/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Now powered by WordPress 3, Nginx and PHP-FPM</title>
		<link>http://timwhitlock.info/blog/2010/08/17/now-powered-by-wordpress-3-nginx-and-php-fpm/</link>
		<comments>http://timwhitlock.info/blog/2010/08/17/now-powered-by-wordpress-3-nginx-and-php-fpm/#comments</comments>
		<pubDate>Tue, 17 Aug 2010 16:21:52 +0000</pubDate>
		<dc:creator>tim</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[fastcgi]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[php-fpm]]></category>
		<category><![CDATA[scaling]]></category>

		<guid isPermaLink="false">http://web.2point1.com/?p=479</guid>
		<description><![CDATA[Speeding up my blog Performance issues aren&#8217;t just for high traffic sites. I&#8217;m lucky if I get 50 visitors a day to this site, but by using scaling techniques popular with the big boys, I figured I could increase page load speeds, (good for visitors and good for SEO). If I could achieve this and [...]]]></description>
			<content:encoded><![CDATA[<h3>Speeding up my blog</h3>
<p>Performance issues aren&#8217;t just for high traffic sites. I&#8217;m lucky if I get 50 visitors a day to this site, but by using  scaling techniques popular with the big boys, I figured I could increase page  load speeds, (good for visitors and <a href="http://googlewebmastercentral.blogspot.com/2010/04/using-site-speed-in-web-search-ranking.html" target="_blank">good for SEO</a>). If I could achieve this <em>and </em>use less resources, perhaps I could even save some money on my hosting bills. I currently run a 512MB VPS on Slicehost, and I&#8217;d rather not increase this right now.</p>
<p>With a few days off work, I decided to take the plunge and swap out some of the server tech powering this blog. From the bottom up, so to speak, this was as follows -</p>
<ol>
<li>Replace Apache with Nginx (<a href="#more-479">below</a>)</li>
<li>Upgrade to PHP 5.3.3 and run as a FastCGI (<a href="http://web.2point1.com/2010/08/17/php-fpm-5-3-3-under-nginx/">next post</a>)</li>
<li>Upgrade to WordPress 3</li>
<li>Deploy a CDN</li>
<li>Add a Varnish cache for extra speed</li>
</ol>
<p>I&#8217;ll go through my experience across a number of posts, starting with Nginx. I shan&#8217;t replicate any existing documentation; I&#8217;m just going to go through what I did and point you at the resources you&#8217;ll need.<br />
<span id="more-479"></span></p>
<h3>Nginx (Engine X)</h3>
<p>Apache has long been my comfort zone, as I imagine it is for most PHP developers, but I&#8217;ve been listening to people over the past couple of years saying the old work horse is bloated, and needs shooting. It has certainly been falling out of favour with high-traffic sites, while Nginx seems to be <a href="http://news.netcraft.com/archives/2010/04/15/april_2010_web_server_survey.html" target="_blank">gaining popularity</a>. Before this upgrade I&#8217;d already tried Nginx out on a few other sites, and it&#8217;s great. <a href="http://wiki.nginx.org/Main" target="_blank">Read about Nginx here</a>. Here&#8217;s an <a href="http://www.joeandmotorboat.com/2008/02/28/apache-vs-nginx-web-server-performance-deathmatch/" target="_blank">Nginx-Apache comparison</a> if you want some stats &#8211; I have none to offer you.</p>
<p><strong>Installing nginx</strong></p>
<p>I found I could install a fairly recent version on Fedora using yum, so <code># yum install nginx</code> was all I needed to do. I found it was totally unavailable in other repos, including CentOS, so you may have to <a href="http://wiki.nginx.org/NginxInstall#Source_Releases" target="_blank">build from source</a>. I won&#8217;t replicate any such tutorials here, except to say that you will most likely have to update <a href="http://monkey.org/~provos/libevent/" target="_blank">libevent</a>. You&#8217;ll need a recent version of that for the rest of this stack anyway, so may as well build that upfront.</p>
<p><strong>Virtual hosts</strong></p>
<p>As I have a couple of other small sites on this sever, I needed to set up virtual hosting. I did this with an include directive in the main <code>/etc/nginx.conf</code>, as follows:</p>
<pre>http {
  # ....
  include /home/vhosts/*/conf/nginx.include;
}
</pre>
<p>So each of my host directories has a <a href="http://wiki.nginx.org/NginxHttpCoreModule#server" target="_blank">server {&#8230;} block</a> defined in <code>nginx.include</code>. I actually removed the default server block from the main config, although that is entirely up to you. <a href="http://wiki.nginx.org/NginxHttpCoreModule#Directives" target="_blank">All nginx core directives listed here</a>.</p>
<p>Starting nginx is as simple as running <code># nginx</code>, and a graceful reload of altered configs can be done with <code># nginx -s reload</code>. If you install via yum, you may also have a service script at <code>/etc/init.d/nginx</code>. To ensure nginx is up on reboot, I just chucked a start command into <code>/etc/rc.local</code> although, I&#8217;m sure there&#8217;s a better way to do that.</p>
<h3>PHP</h3>
<p>So, nginx is running, what to do about PHP? Waving goodbye to Apache also means waving goodbye to <code>mod_php</code>.   If you&#8217;ve previously run PHP as a FastCGI, then you&#8217;re ahead of the   game on this one. If not, then you&#8217;re about to leave another comfort   zone. I&#8217;ll cover <a href="http://web.2point1.com/2010/08/17/php-fpm-5-3-3-under-nginx/">running PHP-FPM next »»</a></p>
]]></content:encoded>
			<wfw:commentRss>http://timwhitlock.info/blog/2010/08/17/now-powered-by-wordpress-3-nginx-and-php-fpm/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>JASPA on ice</title>
		<link>http://timwhitlock.info/blog/2010/08/15/jaspa-on-ice/</link>
		<comments>http://timwhitlock.info/blog/2010/08/15/jaspa-on-ice/#comments</comments>
		<pubDate>Sun, 15 Aug 2010 12:16:44 +0000</pubDate>
		<dc:creator>tim</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[JASPA]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[php-fpm]]></category>

		<guid isPermaLink="false">http://web.2point1.com/?p=464</guid>
		<description><![CDATA[My much neglected personal project JASPA has been taken offline. All traffic to jaspa.org.uk is redirecting here for now. JASPA is/was a cross-compiler written in PHP which converts an ActionScript3-like language to native JavaScript prototypes. Essentially this is a language abstraction; an approach to JavaScript which is has its critics. I felt however that with [...]]]></description>
			<content:encoded><![CDATA[<p>My much neglected personal project <a href="/tag/jaspa">JASPA</a> has been taken offline. All traffic to <em>jaspa.org.uk</em> is redirecting here for now.</p>
<p><span id="more-464"></span>JASPA is/was a cross-compiler written in PHP which converts an ActionScript3-like language to native JavaScript prototypes. Essentially this is a language abstraction; an approach to JavaScript which is has <a href="http://ejohn.org/blog/javascript-language-abstractions/" target="_blank">its critics</a>. I felt however that with ActionScript being a derivative of ECMAScript that some such criticism was not quite applicable. It worked pretty well, and I used it some of my own work, but it has received little public interest, and I was unable to maintain it well enough for those who were interested. Thanks to all the people who did email me about the project &#8211; I may pick up the project again sometime, but I can&#8217;t make any promises.</p>
<p>My ultimate reason for physically bringing the site down was that was that it was powered by Mediawiki, which doesn&#8217;t <em>appear</em> to be compatible with PHP 5.3.x, and I wanted to upgrade so I could use the new version of PHP-FPM (post about that to follow). I could have debugged the wiki to see if 5.3.x was really the problem, but I decided instead to cut my losses. R.I.P.</p>
]]></content:encoded>
			<wfw:commentRss>http://timwhitlock.info/blog/2010/08/15/jaspa-on-ice/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>jParser and jTokenizer released</title>
		<link>http://timwhitlock.info/blog/2009/11/14/jparser-and-jtokenizer-released/</link>
		<comments>http://timwhitlock.info/blog/2009/11/14/jparser-and-jtokenizer-released/#comments</comments>
		<pubDate>Sat, 14 Nov 2009 17:24:52 +0000</pubDate>
		<dc:creator>tim</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jParser]]></category>
		<category><![CDATA[parsing]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[rainy day]]></category>

		<guid isPermaLink="false">http://web.2point1.com/?p=243</guid>
		<description><![CDATA[After nearly two years I&#8217;ve finally gotten around to releasing my PHP JavaScript parser, although documentation is still thin on the ground. Download jParser 1.0.0 (recommended) Download jParser devel package (Full source and build scripts) See the library examples running at apps.timwhitlock.info/jparser The library has been split in two: jTokenizer &#8211; A JavaScript tokenizer designed [...]]]></description>
			<content:encoded><![CDATA[<p>After nearly two years I&#8217;ve <em>finally</em> gotten around to releasing my PHP JavaScript parser, although documentation is still thin on the ground.</p>
<ul>
<li><strong><a href="http://web.2point1.com/wp-content/uploads/2009/11/jparser-1-0-0.tgz">Download jParser 1.0.0</a> </strong><strong>(recommended)<br />
</strong></li>
<li><a href="http://web.2point1.com/wp-content/uploads/2009/11/jparser-devel-1-0-0.tgz">Download jParser devel package</a> (Full source and build scripts)</li>
<li>See the library examples running at <a href="http://apps.timwhitlock.info/jparser/index.html" target="_blank">apps.timwhitlock.info/jparser</a></li>
</ul>
<p>The library has been split in two:</p>
<ol>
<li><strong>jTokenizer</strong> &#8211; A JavaScript tokenizer designed to mimic the <a href="http://www.php.net/manual/en/book.tokenizer.php" target="_blank">PHP tokenizer</a>.</li>
<li><strong>jParser </strong>- The fully blown JavaScript syntactical parser which generates a parse tree.</li>
</ol>
<p><span id="more-243"></span>The reason for the split is that for most purposes where you think you need a parser, you in fact just need a tokenizer. The tokenizer library is about 15KB, whereas the parser is over 700KB (minified), so you can see why you might not want to include it unnecessarily.</p>
<p>The library files <code>jparser.php</code> and <code>jtokenizer.php</code> are self-contained, minified files for production use. If you wish to inspect or modify the code you will need to download the devel package. This package provides a build script which collates the libraries into their distributable files.</p>
<h3>jTokenizer</h3>
<p>Possible uses for the tokenizer include code highlighting and simple manipulation of JavaScript source code.</p>
<p>The main function you will want to use is <code>j_token_get_all</code> which behaves the same as the PHP <a href="http://www.php.net/manual/en/function.token-get-all.php" target="_blank">token_get_all</a> function with the addition of a column number as well as a line number. Additionally there is the <code>j_token_name</code> as per the PHP <a href="http://www.php.net/manual/en/function.token-name.php" target="_blank">token_name</a> function.</p>
<h3>jParser</h3>
<p>This is a full, syntactical parser. On its own it simply generates a parse tree which can be traversed and manipulated. There is no proper documentation on this yet, but take a look at the node classes in the devel package if you are serious about doing something useful with this parser.</p>
<h3>Some other notes in no particular order</h3>
<p>The full parser uses a lot of juice. I recommend giving PHP loads of memory, and be careful what you throw at it if you&#8217;re going to run it on a production server.</p>
<p>A parser is not an interpreter or a JavaScript engine. If you want to develop such a thing in PHP you might be insane, but it could be done with this parser as a base.</p>
<p>The JParser parse tree is purposefully not a full tree, it collapses redundant nodes to save memory. If you want to see a full tree then take a look at the <code>JParserRaw</code> class. (devel package required)</p>
<p>Splitting the parsing process into two parts (tokenize/parse) is probably not the most efficient and probably uses more memory than it would another way. However, I figured it would be neat to mimic the PHP tokenizer functionality so that parsers could be built that take a stream of PHP tokens.</p>
]]></content:encoded>
			<wfw:commentRss>http://timwhitlock.info/blog/2009/11/14/jparser-and-jtokenizer-released/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>OAuth Fail</title>
		<link>http://timwhitlock.info/blog/2009/07/28/oauth-fail/</link>
		<comments>http://timwhitlock.info/blog/2009/07/28/oauth-fail/#comments</comments>
		<pubDate>Tue, 28 Jul 2009 22:34:27 +0000</pubDate>
		<dc:creator>tim</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Fail]]></category>
		<category><![CDATA[OAuth]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://web.2point1.com/2009/07/28/oauth-fail/</guid>
		<description><![CDATA[The day a thousand apps stool still I noticed some weeks ago that Twitter&#8217;s OAuth implementation didn&#8217;t appear to be verifying signatures. I knew this because I purposefully set an invalid access token which was accepted unconditionally. I thought this was odd, but as a newbie to OAuth I was just happy that my app [...]]]></description>
			<content:encoded><![CDATA[<h3>The day a thousand apps stool still</h3>
<p><strong>I noticed <a href="http://twitter.com/timwhitlock/status/2697185141" target="_blank">some weeks ago</a> that Twitter&#8217;s <a href="http://oauth.net/" target="_blank">OAuth</a> implementation didn&#8217;t appear to be verifying signatures</strong>. I knew this because I purposefully set an invalid access token which was accepted unconditionally. I thought this was odd, but as a newbie to OAuth I was just happy that my app was working, so I filed the problem at the back of my mind under &#8220;deal with it if it becomes a problem&#8221;. Today (the week I release by beloved <a href="http://twitblock.org/" target="_blank">TwitBlock</a> app) it very suddenly became a problem.</p>
<p><span id="more-126"></span>It seems that eventually they decided to fix the security hole, except they did it without telling anyone. In their defence this needed to be done quickly, and they were only going to be implementing what we all believed was already in place, namely the well-documented <a href="http://oauth.net/core/1.0a" target="_blank">OAuth standard</a>. The problem was that many people, including myself have publicly released apps that could never have been fully tested.</p>
<p>The damage was already done, i.e. broken apps in the public domain; but the clean up operation didn&#8217;t go well either. At the time of writing, no warning has been posted on <a href="http://status.twitter.com/" target="_blank">status.twitter.com</a>. The first we all knew about the change was broken apps, and disappointed users. A few threads popped up on <a href="http://groups.google.com/group/twitter-development-talk/browse_thread/thread/8d82abd005002ce0/a969b150d73f0c11">this Google Group</a> where devs banged their heads together to work out what was wrong with their code libraries. In my case I eventually tracked down the bug: The main cause of my broken library was that I was signing the full request URL complete with query string. This is incorrect, as <a href="http://oauth.net/core/1.0a#rfc.section.9.1.2" target="_blank">section.9.1.2</a> demonstrates. A bug that, if I&#8217;d been able to test properly in development, would not have made it into production.</p>
<h4>Twitter&#8217;s response</h4>
<p>Although I&#8217;ve not seen an official response, I did see a post on the afore-mentioned Google Group by one of the Twitter API developers. He admitted that they did not approach the situation as they should, but this post may have been deleted because I can no longer find it. I shall post it if I find it again.</p>
<h4>URL encoding bug</h4>
<p>Another bug (which was not actually the main problem) is that PHP&#8217;s rawurlencode function doesn&#8217;t quite fit the bill. <a href="http://oauth.net/core/1.0a#encoding_parameters" target="_blank">OAuth parameter encoding</a> implements RFC3986 whereas <a href="http://php.net/manual/en/function.rawurlencode.php" target="_blank">PHP&#8217;s rawurlencode function</a> implements RFC1738&#8230; the difference is that PHP will encode the &#8220;~&#8221; character. A simple fix is to retro decode the offending character, as follows:</p>
<pre class="code">function oauth_urlencode( $str ){
return str_replace('%7E', '~', rawurlencode($str) );
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://timwhitlock.info/blog/2009/07/28/oauth-fail/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>DIY Qwitter app</title>
		<link>http://timwhitlock.info/blog/2009/06/06/diy-qwitter-app/</link>
		<comments>http://timwhitlock.info/blog/2009/06/06/diy-qwitter-app/#comments</comments>
		<pubDate>Sat, 06 Jun 2009 20:36:33 +0000</pubDate>
		<dc:creator>tim</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://web.2point1.com/2009/06/06/diy-qwitter-app/</guid>
		<description><![CDATA[If you know about the Qwitter service, then you may also know what people say about it &#8211; that it plain doesn&#8217;t work. So for my first Twitter app, I decided to make one that does. I have been made aware since then that there is also Twitdiff, although I haven&#8217;t tried it at time [...]]]></description>
			<content:encoded><![CDATA[<p>If you know about the <a href="http://useqwitter.com/" target="_blank">Qwitter</a> service, then you may also know what people say about it &#8211; that it plain doesn&#8217;t work. So for my first Twitter app, I decided to make one that does.</p>
<p>I have been made aware since then that there is also <a href="http://twitdiff.appspot.com/" target="_blank">Twitdiff</a>, although I haven&#8217;t tried it at time of writing.</p>
<p>If you don&#8217;t know about Qwitter, it&#8217;s a service that monitors your Twitter followers and emails you if someone <em>unfollows</em> you. My app currently tweets the notification instead, so everyone will know you&#8217;ve been qwit.</p>
<p>I&#8217;m not offering my app as a public service [yet] I knocked it up in 2 hours and if you know what you&#8217;re doing with a LAMP set-up you can download it and run it yourself.</p>
<p><strong>» <a href="http://web.2point1.com/wp-content/uploads/2009/06/qwitter-0-1-2.tgz">Download qwitter 0.1.2</a></strong><br />
Requires PHP &gt;= 5.2.x  + json extension, MySQL &gt;= 5.0.45</p>
<h3><span id="more-121"></span>INSTALL</h3>
<p>Ensure you have a PHP binary at /usr/bin/php and check the version is suitable</p>
<pre class="code"><strong>$ which php</strong>
<font color="#999999">/usr/bin/php</font>
<strong>$ /usr/bin/php --version</strong>
<font color="#999999">PHP 5.2.x (cli) (built: ..........)
Copyright (c) 1997-2007 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2007 Zend Technologies</font></pre>
<p>Unzip the download, and ensure it&#8217;s executable:</p>
<pre class="code"><strong>$ tar -xvzf qwitter-0-1.tar.gz</strong><strong>
$ cd qwitter-0-1
$ chmod u+x qwitter</strong></pre>
<p>Configure it for your Twitter account with these two constants:</p>
<pre class="code">define( 'TWITTER_USER', '' );
define( 'TWITTER_PASSWORD', '' );</pre>
<p>Configure it for your MySQL server. These constants are the default:</p>
<pre class="code">define('PLUG_DB_NAME', 'qwitter');
define('PLUG_DB_USER', 'qwitter');
define('PLUG_DB_PASS', '');
define('PLUG_DB_HOST', 'localhost');
define('PLUG_DB_PORT', '3306');</pre>
<p>Create the database table.</p>
<pre class="code">CREATE TABLE `qwitter_followers` (
`id` varchar(11) collate utf8_unicode_ci NOT NULL,
`modified` timestamp NULL default NULL,
`created` timestamp NOT NULL default '0000-00-00 00:00:00',
`unfollowed` timestamp NULL default NULL,
PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;</pre>
<p>Note that you can configure the table name prefix, so you can run multiple qwitters on the same data source. See <code>QWITTER_TABLE_PREFIX</code> constant</p>
<h3>USAGE</h3>
<p>You should test it works by simply running it, but the only way the app can do its job for real is to be run frequently &#8211; the more frequent the better. To set up a cron job and run the app every 5 minutes, create a crontab entry like this:</p>
<p><code>*/5   *   *   *   *   /path/to/qwitter &gt;/dev/null 2&gt;&amp;1</code></p>
<p>If you don&#8217;t know how to set up a cronjob, you&#8217;d better Google it.</p>
<p>Note that if someone follows and unfollows you within this time window, you won&#8217;t be informed. The app needs to see the follower at least once before it will notice that you&#8217;ve been since unfollowed.</p>
<h3>CHANGELOG</h3>
<p><strong>1.0.1</strong><br />
Added <code>QWITTER_TABLE_PREFIX</code> config<br />
Fixed loophole where blocked users were considered qwitters<br />
<strong>1.0.2</strong><br />
Added <code>QWITTER_NOTIFY</code> to support emailing of notification</p>
<h3>TODO</h3>
<ol>
<li><strike>Check missing followers against your block list. Currently blocking a user raises a notification.</strike> todone</li>
<li>Implement OAuth, so this can be offered as a service</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://timwhitlock.info/blog/2009/06/06/diy-qwitter-app/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Bit.ly API command line tool</title>
		<link>http://timwhitlock.info/blog/2009/06/06/bitly-api-command-line-interface/</link>
		<comments>http://timwhitlock.info/blog/2009/06/06/bitly-api-command-line-interface/#comments</comments>
		<pubDate>Sat, 06 Jun 2009 13:54:18 +0000</pubDate>
		<dc:creator>tim</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[bitly]]></category>
		<category><![CDATA[cli]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[rest]]></category>
		<category><![CDATA[url shortnener]]></category>

		<guid isPermaLink="false">http://web.2point1.com/2009/06/06/bitly-api-command-line-interface/</guid>
		<description><![CDATA[I&#8217;ve knocked up a really simple command line tool for interacting with the bit.ly API. It&#8217;s simple because: The output is currently pretty raw The bit.ly API doesn&#8217;t actually do very much » Download version 0.1.1 Requires PHP &#62;= 5.2.x + json extension INSTALL Ensure you have a PHP binary at /usr/bin/php and check the [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve knocked up a really simple command line tool for interacting with the <a href="http://bit.ly">bit.ly</a> API.</p>
<p>It&#8217;s simple because:</p>
<ol>
<li>The output is currently pretty raw</li>
<li>The bit.ly API doesn&#8217;t actually do very much</li>
</ol>
<p><strong>» <a href="http://web.2point1.com/wp-content/uploads/2009/06/bitly-0-1-1.tgz" title="Bitly 0.1.1">Download version 0.1.1</a></strong></p>
<p>Requires PHP &gt;= 5.2.x + json extension</p>
<h3><span id="more-118"></span>INSTALL</h3>
<p>Ensure you have a PHP binary at /usr/bin/php and check the version is suitable</p>
<pre class="code"><strong>$ which php</strong>
<font color="#999999">/usr/bin/php</font>
<strong>$ /usr/bin/php --version</strong>
<font color="#999999">PHP 5.2.x (cli) (built: ..........)
Copyright (c) 1997-2007 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2007 Zend Technologies</font></pre>
<p>Unzip the download, and ensure it&#8217;s executable:</p>
<pre class="code"><strong>$ tar -xvzf bitly-0-1-1.tar.gz</strong><strong>
</strong><font color="#999999">bitly-0-1-1/
bitly-0-1-1/bitly
bitly-0-1-1/README</font>
<strong>$ cd bitly-0-1-1
$ chmod u+x bitly</strong></pre>
<p>Configure it for your bit.ly account, by redefining two constants at the top of the file. They look like this:</p>
<pre class="code">define( 'BITLY_USER', '' );
define( 'BITLY_PASSWORD', '' );</pre>
<p>Optionally move the executable somewhere useful:</p>
<pre class="code"><strong>$ mkdir -p ~/bin
$ mv -v bitly ~/bin</strong>
<font color="#999999">bitly -&gt;/home/xxxx/bin/bitly</font></pre>
<h3>USAGE</h3>
<p>Run the help command to see available commands</p>
<pre class="code"><strong>$ bitly -h</strong></pre>
<p>Example, to shorten a URL:</p>
<pre class="code"><strong>$ bitly shorten http://web.2point1.com</strong></pre>
<h3>CHANGELOG</h3>
<p><strong>Version 0.1</strong><br />
First release</p>
<p><strong>Version 0.1.1</strong><br />
Added &#8211;version switch<br />
Added history=1 into shorten method (ensures it actually goes into your bit.ly history)<br />
Added optional keyword for custom URLs to shorten method</p>
<h3>See also</h3>
<p><a href="http://code.google.com/p/bitly-api/wiki/ApiDocumentation">bit.ly API documentation</a></p>
]]></content:encoded>
			<wfw:commentRss>http://timwhitlock.info/blog/2009/06/06/bitly-api-command-line-interface/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>

