<?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>@ fake&#039;s</title>
	<atom:link href="http://plind.dk/feed/" rel="self" type="application/rss+xml" />
	<link>http://plind.dk</link>
	<description>customized rants from a PHP dev&#039;s viewpoint</description>
	<lastBuildDate>Thu, 26 Jan 2012 21:00:00 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Debugging javascript</title>
		<link>http://plind.dk/2012/01/26/debugging-javascript/</link>
		<comments>http://plind.dk/2012/01/26/debugging-javascript/#comments</comments>
		<pubDate>Thu, 26 Jan 2012 21:00:00 +0000</pubDate>
		<dc:creator>Peter</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[learning]]></category>
		<category><![CDATA[chrome]]></category>
		<category><![CDATA[debugging]]></category>
		<category><![CDATA[ie9]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://plind.dk/?p=487</guid>
		<description><![CDATA[Over the last week, I&#8217;ve had the joy of debugging some nasty javascript bugs. Generally, I really like javascript, especially since discovering Douglas Crockfords take on it (available in boiled-down form at Amazon). It turned from the typical &#8220;Javascript is a ridiculous toy language that doesn&#8217;t work&#8221; to &#8220;My goodness, there is real beauty here!&#8221;. [...]]]></description>
			<content:encoded><![CDATA[<p>Over the last week, I&#8217;ve had the joy of debugging some nasty javascript bugs. Generally, I really like javascript, especially since discovering <a href="http://www.crockford.com/">Douglas Crockfords take on it</a> (available in boiled-down form at <a href="http://www.amazon.com/exec/obidos/ASIN/0596517742/wrrrldwideweb">Amazon</a>). It turned from the typical &#8220;Javascript is a ridiculous toy language that doesn&#8217;t work&#8221; to &#8220;My goodness, there is real beauty here!&#8221;. There are, however, some downsides to it &#8211; a number in the language itself and then a fair amount connected to it. In fact, by far the worst parts of javascript seem to me to be the implementations of it. Yes, it&#8217;s time for the good old &#8220;I hate you, Microsoft!!&#8221;</p>
<h2>Bug 1: Ajax post request</h2>
<p>The first bug I had to fix was not really a javascript bug. The only buggy part about it, in terms of javascript, was that I was allowed to write buggy code with no alarms going off. The problematic code was sending a post request done through pure javascript &#8211; no jQuery magic or anything like it, just pure javascript. Because the request needed to be a fullblown multipart/form-data request, the headers were from constructed from scratch &#8211; this included creating the boundary string for the request. Unfortunately, the code I was using was creating a flawed boundary (in particular, it included a semicolon at the end of the boundary) but all browsers happily sent the request along. And everything worked smoothly, till we upgraded the PHP binary on our server: in version 5.3.9, PHP enforces the HTTP standard more strictly and so it simply drops the incoming POST data if the boundary string isn&#8217;t formatted properly. Which meant that all of a sudden, the ajax requests dropped like bricks.</p>
<p>How do you debug that? You can see that there&#8217;s no POST data coming through, but you also know the script sending the data worked until recently &#8211; and you know the script hasn&#8217;t changed. What you don&#8217;t know is that the server was upgraded &#8211; but you learn this after about 1 hour. However, you do know that Chrome sends the data through just fine &#8211; but IE and Firefox don&#8217;t. Where do you start looking for the problem? Nowhere do you get any errors (PHP doesn&#8217;t notify you that the data from the POST request was dropped &#8211; and the browsers don&#8217;t tell you that the request is faulty), so you have to guess at what the error might be.</p>
<p>What led me to the answer was that Google Chrome detected the problem and fixed it &#8211; although silently. Chrome rewrites the boundary identifier before sending the request through &#8211; if it detects a faulty boundary identifier. So when I tried googles boundary string, suddenly things worked. A bit of experimentation later and I had the solution &#8211; which also led me to the <a href="http://php.net/ChangeLog-5.php#5.3.9">changelog for PHP 5.3.9</a>, where <a href="https://bugs.php.net/bug.php?id=55504">this thing is mentioned</a>.</p>
<h2>Bug 2: cloning in IE9</h2>
<p>The second bug that caused me massive headaches was cloning DOM elements in IE9. I do the cloning in order to send back html to the server, for converting to PDF. Turns out that it&#8217;s much, much, MUCH easier to deal with the html through the DOM (and especially through jQuery or other such libraries) than it is to deal with it through PHP. So what is done is that the main node is cloned, various bits/pieces removed and then the content is sent through a request as a text string. How could this go wrong, you ask? Well, turns out that IE9 decided to throw hissy fits and just randomly apply classes to elements. Well, not quite randomly: in the process of cloning, it came across one class-name it liked so much, that it decided to apply it to about 75% of all elements on the page (but not all the elements &#8211; that would have been too consistent).</p>
<p>What fascinates me about this is that all other browsers I have tried work just fine. Yes, that also means IE7 and IE8 (we don&#8217;t cater to IE6 on the project) &#8211; no trouble there. Microsoft actually worked hard to make sure that there were new bugs in IE9. Well, I suppose the world wouldn&#8217;t have been the same if one of their browsers actually worked as it should.</p>
<p>Of course, this is exaggerating things. Most likely, the fault is mine for cloning elements with IDs. Still, I think Microsoft got the golden rule of interoperability wrong (the one that goes something like: &#8220;<em>be lax</em> on your <em>input</em> but <em>strict</em> on your <em>output&#8221;</em>): instead of being lax about input, they decided to be strict about it &#8230; while they in general are rather lax about their output (another example of this sending a response to an ajax request &#8211; you had better not think to set the charset to utf8, as that will just result in errors you have no idea how decipher).</p>
<p>What makes me think that? Well, the fact that my hacky workaround works: grab the string representation of the node to clone, add an extra bit to all IDs, then create a new element from that and start working. And hey presto, problem solved. Do I feel dirty now? Yes. Is my loathing of IE and MS bigger? Yes. Was I fooled again by them, thinking that IE had in fact improved? Yes. Shame on me? Yes.</p>
<h2>The twist</h2>
<p>I have recently developed a small error-handling script in JS (after reading about a new service that lets you install that in an easy fashion on your site and thinking &#8220;I can do that&#8221;) and have put that up on the site. I had hoped to glean information from this but it turned out utterly useless for both bugs &#8211; because none of them were bugs in javascript or with my programming. They were bugs of the implementation of javascript in browsers &#8211; not of the language itself.</p>
<p>In fact, the error handler only served to confuse things, because it has picked up on a number of bugs, including errors in browser plugins and weird script handler issues in the Bing crawler. I don&#8217;t regret putting it into place but certainly needs a lot of analysing before it gives off any goodies.</p>
<p>In the end, only lucky guessing and some deductions provided answers. Of course, various tools could probably have helped, but I would have needed to know that these would be useful in tracking down the error &#8211; so I would still have had to guess at the problem. I have learned a bit more about browsers, and &#8211; most importantly &#8211; about errors. The worst possible error is the one disguised as success: either your typical silent error (such as dropping the POST vars without mentioning it) or your more atypical everything-is-fine error (where things fail but appearances are kept up).</p>
]]></content:encoded>
			<wfw:commentRss>http://plind.dk/2012/01/26/debugging-javascript/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP RESTful frameworks</title>
		<link>http://plind.dk/2011/11/14/php-restful-frameworks/</link>
		<comments>http://plind.dk/2011/11/14/php-restful-frameworks/#comments</comments>
		<pubDate>Mon, 14 Nov 2011 20:50:31 +0000</pubDate>
		<dc:creator>Peter</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Infosys]]></category>
		<category><![CDATA[learning]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[epiphany]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[frapi]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[recess]]></category>
		<category><![CDATA[restful]]></category>
		<category><![CDATA[slim]]></category>
		<category><![CDATA[tonic]]></category>

		<guid isPermaLink="false">http://plind.dk/?p=477</guid>
		<description><![CDATA[I have to build a RESTful API for Fastaval. The purpose is to tie together three different tools, two of which are built by ourselves (but different developers) and one of which is WordPress. Instead of trying to roll everything into one codebase the solution I&#8217;m opting for is adding a mediator service in the [...]]]></description>
			<content:encoded><![CDATA[<p>I have to build a RESTful API for <a href="http://www.fastaval.dk">Fastaval</a>. The purpose is to tie together three different tools, two of which are built by ourselves (but different developers) and one of which is WordPress. Instead of trying to roll everything into one codebase the solution I&#8217;m opting for is adding a mediator service in the middle. And for that I&#8217;ll use a PHP RESTful framework. So the question was, which one to use.</p>
<p>I had a quick look at the scene and came up with the following candidates:</p>
<ul>
<li><a href="http://peej.github.com/tonic/">Tonic</a></li>
<li><a href="http://www.slimframework.com/">Slim</a></li>
<li><a href="http://www.recessframework.org/">Recess</a></li>
<li><a href="https://github.com/jmathai/epiphany">Epiphany</a></li>
<li><a href="http://getfrapi.com/">Frapi</a></li>
</ul>
<p>There are others as well, but these looked like the best candidates for quickly setting up a small API.</p>
<h2>Measuring</h2>
<p>My needs for the framework are:</p>
<ul>
<li>small size</li>
<li>get out of the way</li>
<li>don&#8217;t force me to do more stuff than I need</li>
<li>few requirements</li>
<li>ability to get into it in 5-10 minutes</li>
</ul>
<h3>Tonic</h3>
<p>This framework seems nicely done. However, after looking at the source and the setup for 5 minutes, I found more overhead than I want. Specifically, the process of dispatching from request to controller involved much more code than I think should be needed. On top of that, it seems to include Smarty, which is 100% overkill for my needs (and which I&#8217;m generally opposed to). And as a final hint that I wouldn&#8217;t like the framework, it included a module for doing basic http authentication. Not exactly something I want in my RESTful API.</p>
<h3>Slim</h3>
<p>Here there&#8217;s very little setup needed before you&#8217;re running a RESTful service. The framework really does all it can to get out of the way, so after looking for 2 minutes at the included example index file you&#8217;ll know how to get the basics going. The size of the framework is about 230K, which is not bad. Overall a very good contestant.</p>
<h3>Recess</h3>
<p>After installing this and setting it up, I browse to the test page only to find it needs a database to work. Instant dismissal: if my API doesn&#8217;t need a database I&#8217;m sure as hell not going to be bothered with creating one for the framework.</p>
<h3>Epiphany</h3>
<p>This is the second final contestant for the framework to use. Again, fairly simple setup and you&#8217;ll know very fast after installing it how it works, so you won&#8217;t waste a lot of time reading docs before you can begin to use it. I like it a bit less than Slim, though, as it seems to need more manual setup (more static calls than I would expect, before things are running). However, very small size (80K), nice set of libraries, a nice surprise in terms of flexibility, so this one is definitely not out of the race.</p>
<h3>Frapi</h3>
<p>This seems like a nice a fairly advanced framework, however, right out of the box it involves too much complexity to be of any use to me. First off, I need to setup two virtualhosts for it, one for the frontend and one for the backend. I don&#8217;t need that, as I don&#8217;t need a backend. Secondly, I couldn&#8217;t even get to the point of figuring out if Frapi needs a backend, as it wouldn&#8217;t run without APC which I don&#8217;t have running on my small netbook. So it got disqualified from the race right there. If it hadn&#8217;t, it would have been on account of size, as it packs in 31MB just in libraries.</p>
<h2>Conclusions</h2>
<p>I&#8217;ll probably go with Slim though I&#8217;ll try to do a minimal test-case of Slim and Epiphany (just with an auth module) to compare developing with both. I&#8217;m sure the other frameworks have positive sides as well, but they strike me as overkill for a RESTful API. I get why you would want to make things slightly more complex if the goal is developing web applications (as Tonic is advertised for, for instance) but including smarty? That&#8217;s simply unnecessary bloat to me.</p>
<p>One thing I skipped over here is obviously the existing bigger frameworks like CodeIgniter, CakePHP, Zend, etc. All these are way overkill for an API, if you ask me. They don&#8217;t get out of the way, instead they dictate most of your choices (not necessarily a bad thing &#8211; enforcing best practices is good in my book &#8211; just not needed here). Zend, for instance, has a good set of modules that could come in quite handy for a RESTful api (likely why Frapi includes a whole lot of Zend code), but Zend seems to be built upon the idea that Java development practices are good and can/should be used in PHP. Which is quite simply wrong, if you ask me.</p>
<p>So yeah, the proper tool for the job, which in this case amounts to Slim or Epiphany. Hereby recommended.</p>
]]></content:encoded>
			<wfw:commentRss>http://plind.dk/2011/11/14/php-restful-frameworks/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mail madness</title>
		<link>http://plind.dk/2011/10/12/mail-madness/</link>
		<comments>http://plind.dk/2011/10/12/mail-madness/#comments</comments>
		<pubDate>Wed, 12 Oct 2011 18:20:13 +0000</pubDate>
		<dc:creator>Peter</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[email]]></category>
		<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[user experience]]></category>
		<category><![CDATA[webmail]]></category>

		<guid isPermaLink="false">http://plind.dk/?p=472</guid>
		<description><![CDATA[Another post not on the topic I set out a little while ago, this time about email &#8211; specifically webmail. A family member asked me to backup their webmail account and it so happens to be a Hotmail account. Naively I figure that by now, Microsoft must have created a fairly nice webmail experience that [...]]]></description>
			<content:encoded><![CDATA[<p>Another post not on the topic I set out a little while ago, this time about email &#8211; specifically webmail. A family member asked me to backup their webmail account and it so happens to be a Hotmail account. Naively I figure that by now, Microsoft must have created a fairly nice webmail experience that will allow users to export their mail. A bit of research later and I get the first disappointment: no, no such option exists. Microsoft would love for you to use Hotmail, but reserves the right to extend the middle finger to anyone that might be interested in getting their own personal email out of Hotmail.</p>
<p>Fine, option #2: connect to Hotmail with an email client, say Thunderbird. Setup is easy, indeed it goes very fast, but what&#8217;s this? POP3? But I want IMAP, as I don&#8217;t want to bother with checking that messages are not deleted online and I also want to download everything in the various folders. Some more research and it&#8217;s revealed that while M$ has no problem bleeding money out of their crap search engine, they can&#8217;t be bothered to provide a proper interface to their webmail. How can these morons still be in business? How is it possible that they lure so many people into using their crap?</p>
<p>Grudgingly, we move on to option #3: Microsofts own Windows Live Mail. As the company is known for it&#8217;s loser-mentality (keep everything proprietary and make sure your own developers know &#8220;the secret APIs&#8221;) one might expect that this program (package, in fact: another loser-mentality streak &#8211; you need to download an entire package to get to their mail program, even though you have no use for the other crap) would work. However, that&#8217;s not the case: it only downloads whatever&#8217;s in the inbox. Microsoft does not believe in backup, apparently.</p>
<p>Conclusion: the only thing I actually managed to do to backup the emails was to print them to PDF. Yes, that&#8217;s how horribly bad Microsofts email service is. It really, truly boggles my mind why anyone would ever trust anything to do with email from this company (so please dump your POS exchange servers &#8211; there are better alternatives anyway and they&#8217;re free).</p>
<p>Well, not entirely true: I also managed to get so angry that I wrote to Microsoft that they could &#8220;fuck off and die&#8221;. That&#8217;s how bad my user experience was.</p>
]]></content:encoded>
			<wfw:commentRss>http://plind.dk/2011/10/12/mail-madness/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Late night coding</title>
		<link>http://plind.dk/2011/09/14/late-night-coding/</link>
		<comments>http://plind.dk/2011/09/14/late-night-coding/#comments</comments>
		<pubDate>Wed, 14 Sep 2011 21:25:06 +0000</pubDate>
		<dc:creator>Peter</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://plind.dk/?p=469</guid>
		<description><![CDATA[While avoiding the new learning series I dreamt up I&#8217;ve been doing some reading and playing with postfix and ldap. The first topic came about because of some stuff at work (had to set up postfix with authentication, turned out to be much more complex than one would expect) and I wanted to make sure [...]]]></description>
			<content:encoded><![CDATA[<p>While avoiding the <a title="Me trying to learn scala" href="http://plind.dk/2011/07/24/new-learning-series/">new learning series</a> I dreamt up I&#8217;ve been doing some reading and playing with postfix and ldap. The first topic came about because of some stuff at work (had to set up postfix with authentication, turned out to be much more complex than one would expect) and I wanted to make sure I knew how to do it in the future. The second part came about because my server setup is too messy &#8211; I want to deal with the hosting in a proper fashion where adding, updating, etc. is easy. Hence ldap came to mind: I want to run an ldap server in one of the containers on the server, so all the other containers can authenticate against it as needed and grab other info from there as well.</p>
<p>So to make sure I don&#8217;t forget these things after I&#8217;ve done them I&#8217;ve got a library of files containing notes on how to do things. On top of that, I&#8217;ve also got <a href="http://trac.edgewall.org/">Trac</a> running on my server. I installed it mainly to keep track of hosting info (such as server details, bugs and other things I needed to take care of, projects from clients, etc) but I discovered it would also be useful to migrate my library of notes onto Trac. Or rather, I haven&#8217;t migrated it, I&#8217;ve just copied it over to trac while keeping (and working on) the local copy. Hence the need to sync everything. Locally, I keep the notes in git, making for easy syncing across the network &#8211; but I don&#8217;t have anything like that setup for trac. Hence, a bit of late night coding and hey presto! git hooks. I still need to work a bit on the pre-commit hook but the post-commit hook is done. Just a very simple script to update trac after I&#8217;ve committed changes to my notes locally. It looks like this:</p>
<pre>// script residing in the main repo folder

#!/usr/bin/php
&lt;?php
define('TRAC_AUTH', 'username:password');
define('COOKIEFILE', tempnam('/tmp', 'cookies_'));

$self = array('web_hook.php', 'web_update.php');

if (empty($_SERVER['argv'][1])) {
    echo "Call with name of file" . PHP_EOL;
    exit(1);
}

if (in_array($_SERVER['argv'][1], $self)) {
    exit(0);
}

$url_base   = 'http://path/to/server/wiki/';
$url_suffix = '?action=edit';

$url_array = array(
    'server.txt'      =&gt; 'Server',
    'development.txt' =&gt; 'Development',
    'email.txt'       =&gt; 'Email',
    'git.txt'         =&gt; 'Git',
);

if (!isset($url_array[$_SERVER['argv'][1]])) {
    echo "No such file set: " . $_SERVER['argv'][1] . PHP_EOL;
    exit(1);
}

$url_part = $url_array[$_SERVER['argv'][1]];

$data = fetch_data($url_base . $url_part . $url_suffix);
if (!$data) {
    echo "Could not fetch data" . PHP_EOL;
    exit(1);
}

if (!put_data($data, $_SERVER['argv'][1], $url_base . $url_part)) {
    echo "Could not update trac" . PHP_EOL;
    exit(1);
}

/**
 * posts data to the proper
 * page, updating trac info
 *
 * @param string $data
 * @param string $filename
 * @param string $url
 *
 * @return string
 */
function put_data($data, $filename, $url) {
    $dom = new DOMDocument();
    $dom-&gt;loadHTML($data);

    $xpath = new DOMXPath($dom);

    $form_token  = $xpath-&gt;query('//form[@id="edit"]//input[@name="__FORM_TOKEN"]')-&gt;item(0)-&gt;getAttribute('value');
    $version     = $xpath-&gt;query('//form[@id="edit"]//input[@name="version"]')-&gt;item(0)-&gt;getAttribute('value');
    $action      = $xpath-&gt;query('//form[@id="edit"]//input[@name="action"]')-&gt;item(0)-&gt;getAttribute('value');
    $from_editor = $xpath-&gt;query('//form[@id="edit"]//input[@name="from_editor"]')-&gt;item(0)-&gt;getAttribute('value');

    $comment = "Saved from git hook";
    $text    = file_get_contents($filename);
    if (empty($text)) {
        echo "No text to save";
        exit(1);
    }

    $post_data = array(
        '__FORM_TOKEN'  =&gt; $form_token,
        'version'       =&gt; $version,
        'action'        =&gt; $action,
        'from_editor'   =&gt; $from_editor,
        'text'          =&gt; $text,
        'comment'       =&gt; $comment,
    );

    $curl_handle = curl_init();
    curl_setopt($curl_handle, CURLOPT_URL, $url);
    curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl_handle, CURLOPT_USERPWD, TRAC_AUTH);
    curl_setopt($curl_handle, CURLOPT_POST, true);
    curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $post_data);
    curl_setopt($curl_handle, CURLOPT_COOKIEFILE, COOKIEFILE);

    return curl_exec($curl_handle);
}

/**
 * fetches the data from trac
 *
 * @param string $url
 *
 * @return string
 */
function fetch_data($url) {
    $curl_handle = curl_init();
    curl_setopt($curl_handle, CURLOPT_URL, $url);
    curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl_handle, CURLOPT_USERPWD, TRAC_AUTH);
    curl_setopt($curl_handle, CURLOPT_COOKIEJAR, COOKIEFILE);

    return curl_exec($curl_handle);
}

// this is the post-commit file in .git/hooks/
for LINE in `git log --oneline --name-only -1 HEAD | awk '{if ($2 == "") print $1}'`
do
    if ! ./web_update.php $LINE
    then
        echo "Failed to update trac with changes"
    fi
done</pre>
<p>It should be fairly easy to figure out what&#8217;s going on. The post-commit hook checks all the lines from the log for the last commit and filters the output, passing it to the update script as needed. The update script fetches the edit page from trac for the given file, scrapes out the needed variables to post back to trac, then sends a post request to trac, thus updating the trac wiki.</p>
<p>Now, I&#8217;m sure there are better ways (most likely some sort of trac plugin or api) to update trac remotely &#8230; but the point is, this is just a bit of late night coding and done deal: I&#8217;m now updating my trac wiki whenever I commit my notes locally.</p>
]]></content:encoded>
			<wfw:commentRss>http://plind.dk/2011/09/14/late-night-coding/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Interlude: nginx + php</title>
		<link>http://plind.dk/2011/08/03/interlude-nginx-php/</link>
		<comments>http://plind.dk/2011/08/03/interlude-nginx-php/#comments</comments>
		<pubDate>Wed, 03 Aug 2011 18:34:10 +0000</pubDate>
		<dc:creator>Peter</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[learning]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[benchmarking]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[php fpm]]></category>
		<category><![CDATA[server]]></category>

		<guid isPermaLink="false">http://plind.dk/?p=466</guid>
		<description><![CDATA[In a fit of &#8220;no I don&#8217;t want to do more programming right now&#8221; I got back to another interesting topic: alternatives to the Apache server. While I love it for it&#8217;s stability and ease of setup (I rarely have problems with that aspect of web-development), it&#8217;s still good to know of the alternatives and [...]]]></description>
			<content:encoded><![CDATA[<p>In a fit of &#8220;no I don&#8217;t want to do more programming right now&#8221; I got back to another interesting topic: alternatives to the <a href="http://www.apache.org/">Apache</a> server. While I love it for it&#8217;s stability and ease of setup (I rarely have problems with that aspect of web-development), it&#8217;s still good to know of the alternatives and to see what else is out there. I&#8217;ll likely never play with <a href="http://www.iis.net/">IIS</a> (unless MS decides it should be free and work on a free OS &#8230; madness, I know) but luckily there are other very interesting alternatives, one of them being <a href="http://nginx.org/">nginx</a>.</p>
<p>The interesting thing about nginx is that it&#8217;s so very lightweight. In fact, on my server, I run nginx with php as a proxy for web and mail to a number of VPS setups. The individual VPS setups that run webapps typically run Apache, and not one of them is remotely close to the same memory footprint as nginx + php. This is not much of an observation though, seeing as the Apache installs are not trimmed to be low-resource &#8211; however, I haven&#8217;t trimmed nginx either, it just is small-footprint resource-wise. Thus, for the average layperson sysadmin (a contradiction in terms, though not in practice) an nginx + PHP install can make rather good sense, especially if you&#8217;re on a low-powered VPS setup.</p>
<p>So I decided to look a bit more into nginx and PHP. As mentioned I already have it running on my server but that was a fairly fast setup without too much thought &#8211; just consulting the various tutorials I could find while trying to sidestep any obvious issues. Hence, more study was needed.</p>
<h2>Installing nginx</h2>
<p>On Debian/Ubuntu you can just apt-get nginx</p>
<pre>sudo apt-get install nginx</pre>
<p>That will, however, install a slightly older version of nginx. To get the newest stable, go to <a href="http://nginx.org/en/download.html">http://nginx.org/en/download.html</a> and grab the latest.</p>
<pre>sudo -s
cd /opt
wget http://nginx.org/download/nginx-1.0.5.tar.gz &amp;&amp; tar -zxvvf nginx-1.0.5.tar.gz
rm nginx-1.0.5.tar.gz
cd nginx-1.0.5
./configure --http-log-path=/var/log/nginx/ --error-log-path=/var/log/nginx/ --with-http_ssl_module --with-mail --with-mail_ssl_module --with-imap --with-imap_ssl_module --with-pcre
make
make install</pre>
<p>You&#8217;ll need a number of packages to be able to run this, but the configure command should complain and tell you which if you don&#8217;t know. Also note that the mail and imap configuration settings above are in place because I use nginx to proxy email access to the individual VPS machines I have setup &#8211; if you don&#8217;t need that, you don&#8217;t need those settings. After you&#8217;re through running the commands (apt-get or manual install), you should have nginx setup and ready to run (running actually, if you used apt-get).</p>
<p>After this, you need to get PHP installed, and here things get more tricky. There are a number of ways of going about things:</p>
<ul>
<li>compile PHP yourself from source (<a href="http://php.net/downloads.php">http://php.net/downloads.php</a>) or install php5-cgi using apt-get. Then use your a homegrown script to manage PHP</li>
<li>as above, but use spawn-fcgi to manage PHP (depending upon your OS version, you might need to install lighttpd as spawn-fcgi only became</li>
<li>use PHP-FPM (manually compiled from <a href="http://php-fpm.org/">http://php-fpm.org/</a> or installed via apt-get)</li>
</ul>
<p>The configuration from this point on differs a bit, because you&#8217;ll either be setting up your own script, configuring spawn-fcgi or configuring PHP-FPM. However, the most relevant part for this post refers to nginx and PHP communicating &#8211; for info on the bits about setting things up, you can check out <a href="http://library.linode.com/web-servers/nginx/php-fastcgi">Linode Library</a> for scripts and info. The <a href="http://wiki.nginx.org/Configuration">nginx wiki</a> also has good info. What it boils down to, in the end, is whether nginx will be communicating with PHP over TCP or through sockets. If you google you&#8217;ll find differing advices but the majority seems to point in the direction of sockets. The reasoning behind this is that sockets are faster than TCP &#8211; there&#8217;s much less overhead. You might also find some warnings that some people have had problems with sockets though &#8211; so it&#8217;s not an easy choice, and essentially you need to test things out yourself.</p>
<p>Which I then did &#8211; or rather, I settled for sockets, then decided to do some benchmark testing, and then figured out there was a need to test out both before you actually choose. The configuration I went with looks like:</p>
<pre># relevant nginx part
location / {
    root           html;
    fastcgi_pass   unix:/tmp/php5-fpm.sock;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  /var/www/nginx-php/$fastcgi_script_name;
    include        fastcgi_params;
}

# relevant php-fpm part, from /etc/php5/fpm/pool.d/
listen = /tmp/php5-fpm.sock</pre>
<p>Together, the two will make the server serve requests to php-fpm through sockets. Now, because I was playing with nginx and PHP-fastcgi I thought it would be interesting to see how it would handle a bunch of requests, so I fired up ab (Apache Benchmark) from apache and started flooding nginx. First I went with something like:</p>
<pre>ab -kc 100 -n 1000 nginx-php:8000/</pre>
<p>which worked fine (if you&#8217;re not familiar with the options for ab, the line above will fire 100 concurrent keepalive requests at the server at nginx-php, port 8000, until a 1000 requests have been sent. I had put a .php script with nothing but a single echo at the end of that url, just to check the throughput. Then, just for kicks, I upped the concurrency to 200 requests a second &#8211; and suddenly I start seeing errors. Checking the error log of nginx, I see the following:</p>
<pre>connect() to unix:/tmp/php5-fpm.sock failed (11: Resource temporarily unavailable) while connecting to upstream</pre>
<p>I then added a -v 3 to the ab command line, to get some more detailed info about what nginx would output if that happened. Turns out you&#8217;ll get something like this:</p>
<pre>WARNING: Response code not 2xx (502)
LOG: header received:
HTTP/1.1 502 Bad Gateway</pre>
<p>Essentially, what happens is that nginx tries to open a connection to PHP-fastcgi, fails for one reason or another, then responds to the user with a 502 Bad Gateway.</p>
<p>Thinking that maybe it was a limitation based on the php setup I tried changing the fastcgi settings (how many PHP processes to keep around, max processes, etc) but no change &#8211; I kept coming up against the 502. The limit on my local setup seems to be around 150-160 concurrent requests &#8211; more than that and errors crop up. However, I then switched to a TCP based setup and the errors are gone. Testing a bit, I up the concurrency to 400 connections, still no errors. At triple the concurrency I start to see problems, but not before.</p>
<p>However, instead of calling this a win for TCP there&#8217;s the other aspect: speed. Running a test with 100 concurrent connections for a total of 50,000 requests, sockets gave a 15% speed increase compared to TCP. Looking at CPU consumption, on the other hand, showed a higher spike with sockets than TCP (reasonably explained by bigger throughput). Memory consumption didn&#8217;t spike much for either setup, though sockets used a tad more.</p>
<h2>Conclusion</h2>
<p>You need to figure out what your likely scenario is when setting up your server. That should more or less self-explanatory &#8211; however, it&#8217;s something you find out time and again as you play around with hardware and software.</p>
<p>While I have seen some possible solutions on how to ramp up the concurrency for nginx + PHP using sockets, the main thing to do is ask yourself: will I actually ever hit much more than 100 concurrent requests that I need to forward to PHP? If not, then you&#8217;re safe using sockets. If you expect to hit much more, you should probably look into how to optimize nginx with PHP &#8211; or consider other options that might scale easier.</p>
]]></content:encoded>
			<wfw:commentRss>http://plind.dk/2011/08/03/interlude-nginx-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

