<?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>Perplexed Labs &#187; PHP</title>
	<atom:link href="http://blog.perplexedlabs.com/category/development/php-development/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.perplexedlabs.com</link>
	<description>web development war stories from the frontlines to the backend</description>
	<lastBuildDate>Sat, 24 Jul 2010 16:27:40 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>PHP Forking to Concurrency with pcntl_fork()</title>
		<link>http://blog.perplexedlabs.com/2010/03/02/php-forking-to-concurrency/</link>
		<comments>http://blog.perplexedlabs.com/2010/03/02/php-forking-to-concurrency/#comments</comments>
		<pubDate>Tue, 02 Mar 2010 13:00:10 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[concurrency]]></category>
		<category><![CDATA[fork]]></category>
		<category><![CDATA[parallel]]></category>
		<category><![CDATA[pcntl]]></category>
		<category><![CDATA[pcntl_fork]]></category>
		<category><![CDATA[pcntl_wait]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[process]]></category>
		<category><![CDATA[thread]]></category>
		<category><![CDATA[unix]]></category>

		<guid isPermaLink="false">http://blog.perplexedlabs.com/?p=376</guid>
		<description><![CDATA[I find it interesting and challenging to bend PHP in ways it probably shouldn't be bent. Almost always I walk away pleasantly surprised at it's ability to solve a variety of problems. Consider this example. Let's say you want to take advantage of more than one core for a given process. Perhaps it performs many [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/05/04/php-libmemcached-via-memcached-and-igbinary/' rel='bookmark' title='Permanent Link: PHP libmemcached via memcached and igbinary'>PHP libmemcached via memcached and igbinary</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/05/04/php-jquery-ajax-javascript-long-polling/' rel='bookmark' title='Permanent Link: PHP jQuery AJAX Javascript Long Polling'>PHP jQuery AJAX Javascript Long Polling</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/04/09/php-daisy-chain-class-method-calls/' rel='bookmark' title='Permanent Link: PHP Daisy Chain Class Method Calls'>PHP Daisy Chain Class Method Calls</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>I find it interesting and challenging to bend PHP in ways it probably shouldn't be bent.  Almost always I walk away pleasantly surprised at it's ability to solve a variety of problems.</p>
<p>Consider this example.  Let's say you want to take advantage of more than one core for a given process.  Perhaps it performs many intensive computations and on a single core would take an hour to run.  Since a PHP process is single threaded you won't optimally take advantage of the available multi-core resources you may have.</p>
<p>Fortunately, via the Process Control (<a href="http://php.net/manual/en/book.pcntl.php">PCNTL</a>) extension, PHP provides a way to fork new child processes.  Forking is the concept of duplicating a thread of execution from the parent to a new child.  <a href="http://www.php.net/manual/en/function.pcntl-fork.php">pcntl_fork()</a> is the function that does this.</p>
<p>The framework for using this extension is as follows:</p>
<pre class="brush: php;">
$maxChildren = 4;
$numChildren = 0;
foreach($unitsOfWork as $unit) {
	$pids[$numChildren] = pcntl_fork();
	if(!$pids[$numChildren]) {
		// do work
		doWork($unit);
		posix_kill(getmypid(), 9);
	} else {
		$numChildren++;
		if($numChildren == $maxChildren) {
			pcntl_wait($status);
			$numChildren--;
		}
	}
}
</pre>
<p>When a new child is forked via pcntl_fork() the pid is returned.  The if statement following the fork allows the child and parent to split their flow of execution based on who they are (i.e. the child does the work and kills itself - the parent tests for hitting the max number of children and waits, otherwise it creates another child).  The pcntl_wait() function is called when we hit $maxChildren, it blocks until a child exits.</p>
<p>Remember, if you want use database connections in your children, they each need to initialize their own connection.  Resources such as database connections are not thread safe.</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/05/04/php-libmemcached-via-memcached-and-igbinary/' rel='bookmark' title='Permanent Link: PHP libmemcached via memcached and igbinary'>PHP libmemcached via memcached and igbinary</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/05/04/php-jquery-ajax-javascript-long-polling/' rel='bookmark' title='Permanent Link: PHP jQuery AJAX Javascript Long Polling'>PHP jQuery AJAX Javascript Long Polling</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/04/09/php-daisy-chain-class-method-calls/' rel='bookmark' title='Permanent Link: PHP Daisy Chain Class Method Calls'>PHP Daisy Chain Class Method Calls</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2010/03/02/php-forking-to-concurrency/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger</title>
		<link>http://blog.perplexedlabs.com/2010/02/08/deployment-using-capistrano-and-webistrano-via-rails-and-phusion-passenger/</link>
		<comments>http://blog.perplexedlabs.com/2010/02/08/deployment-using-capistrano-and-webistrano-via-rails-and-phusion-passenger/#comments</comments>
		<pubDate>Mon, 08 Feb 2010 13:00:32 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[deployment]]></category>
		<category><![CDATA[mod_wsgi]]></category>
		<category><![CDATA[mongrel]]></category>
		<category><![CDATA[passenger]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[phusion passenger]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[subversion]]></category>

		<guid isPermaLink="false">http://blog.perplexedlabs.com/?p=408</guid>
		<description><![CDATA[I finally got around to setting up a more sophisticated deployment system for some of my apps. These apps include some built on a custom PHP framework and others that are Python / Django apps. I figured I'd share my experience... Why is a high-level deployment infrastructure important? Deployment is something that should be simple, [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/01/13/installing-ruby-enterprise-edition-with-phusion-passenger/' rel='bookmark' title='Permanent Link: Installing Ruby Enterprise Edition with Phusion Passenger'>Installing Ruby Enterprise Edition with Phusion Passenger</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/11/30/8-books-to-get-a-developer-for-the-holidays/' rel='bookmark' title='Permanent Link: 8 Books To Get A Developer For The Holidays'>8 Books To Get A Developer For The Holidays</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/06/01/be-language-agnostic-solve-the-problem/' rel='bookmark' title='Permanent Link: Be Language Agnostic &#8211; Solve the Problem!'>Be Language Agnostic &#8211; Solve the Problem!</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>I finally got around to setting up a more sophisticated deployment system for some of my apps.  These apps include some built on a custom PHP framework and others that are Python / Django apps.  I figured I'd share my experience...</p>
<p>Why is a high-level deployment infrastructure important?  Deployment is something that should be simple, accessible, and repeatable.  It should be as close to a "single click" as possible.  Previously, for me, it was a bash script that exported some SVN branches.  While this worked fine, as projects progress, you want some accountability, history, and the ability to roll back mission critical applications when something goes wrong with a deploy.</p>
<p><a href="http://www.capify.org/">Capistrano</a> is an open source, command line, deployment tool that provides all of these features.  It's written in Ruby.  You leverage a variety of built in "recipes" (Capistrano's term for a deployment script) that execute certain procedures to deploy an app.  Out-of-the-box it's ideally built to deploy a Rails app.  However, after some minor tweaks it can deploy most anything and do it well.  It can restart servers, update symlinks, change permissions - pretty much anything.  It assumes you access your POSIX compliant server via SSH via the same password (or have ssh keys setup).</p>
<p><a href="http://labs.peritor.com/webistrano">Webistrano</a> is an open source web front-end for Capistrano.  It's a convenience layer that abstracts the command line away and provides an interface to perform the same tasks.  This interface shows history as well as providing a convenient GUI for creating new deployment projects, stages, and recipes.  Highly recommended.</p>
<p>Let's get down to business.  This post makes a few assumptions about things you've already installed and used previously.</p>
<ul>
<li><a href="http://www.ruby-lang.org/en/">Ruby 1.8.5</a>+
<li><a href="http://rubyforge.org/projects/rubygems/">RubyGems</a>
<li><a href="http://www.mysql.com/">MySQL 5.0</a>+
<li><a href="http://www.apache.org/">Apache 2</a>+
<li><a href="http://subversion.tigris.org/">Subversion</a> and a repository containing the "production" branch of your app.
</ul>
<h3>Installing Capistrano</h3>
<p>Well, this is an easy one (you probably want to do this as root):</p>
<blockquote><p>
gem install capistrano
</p></blockquote>
<h3>Installing Webistrano</h3>
<p>Also fairly easy, with a little splash of configuration.</p>
<blockquote><p>
# wget http://labs.peritor.com/webistrano/attachment/wiki/Download/webistrano-1.4.zip<br />
# unzip webistrano-1.4.zip<br />
# mv webistrano-1.4 /path/to/where/you/want/webistrano
</p></blockquote>
<p>Setup the database tables and create a new webistrano user (obviously be conscious of your security preferences for access to your database in the host and password portions):</p>
<blockquote><p>
# mysql<br />
mysql> CREATE DATABASE `webistrano`;<br />
mysql> CREATE USER 'webistrano'@'localhost' IDENTIFIED BY 'password';<br />
mysql> GRANT ALL PRIVILEGES ON `webistrano`.* TO 'webistrano'@'localhost' WITH GRANT OPTION;
</p></blockquote>
<p>Now, in the directory where you placed webistrano you're going to want to copy <i>config/database.yml.sample</i> to <i>config/database.yml</i>.  Edit this file, in the production area, to match your database settings.  By default the file expects a socket to connect, you can chase this by specifying <i>host:</i> and <i>port:</i>.  (Keep in mind Webistrano is simply a Rails app).</p>
<p>You should now be able to have Rails migrate the new database you created.  In the webistrano directory:</p>
<blockquote><p>
# RAILS_ENV=production rake db:migrate
</p></blockquote>
<p>Finally, copy <i>config/webistrano_config.rb.sample</i> to <i>config/webistrano_config.rb</i> and edit according to your preferred mail settings.</p>
<p>We can now test to see if webistrano is working properly by serving it via mongrel:</p>
<blockquote><p>
# ruby script/server -d -e production -p 3000
</p></blockquote>
<p>This starts a single mongrel daemon, using the production environment, listening on port 3000.  You should now be able to hit http://127.0.0.1:3000/ and get the Webistrano login prompt.  If this is working, kill that mongrel instance.</p>
<p>For longer term serving I decided to go with Phusion Passenger (essentially mod_rails for Apache).  It's a nearly zero configuration solution for serving a rails app and will feel at home to anyone with experience serving PHP apps via Apache and mod_php.</p>
<h3>Installing Phusion Passenger</h3>
<p>Again, as root:</p>
<blockquote><p>
# gem install passenger<br />
# passenger-install-apache2-module
</p></blockquote>
<p>The second command will invoke an installer which compiled Passenger and provides instructions on integrating it into your Apache config.  Essentially, edit your httpd.conf as follows (<strong>these were specific to my install, make sure to use the ones provide by the installer for you</strong>):</p>
<blockquote><p>
LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-2.2.9/ext/apache2/mod_passenger.so<br />
PassengerRoot /usr/lib/ruby/gems/1.8/gems/passenger-2.2.9<br />
PassengerRuby /usr/bin/ruby
</p></blockquote>
<p>Now you can simply add VirtualHost entries to your httpd.conf for any of your Rails apps.  Let's add one for Webistrano:</p>
<blockquote><p>
&lt;VirtualHost *:80&gt;<br />
ServerName webistrano.mydomain.com<br />
DocumentRoot /path/to/webistrano/public<br />
&lt;/VirtualHost&gt;
</p></blockquote>
<p>Yes, Passenger makes it that simple.  Add configuration directives as needed for your environment.</p>
<p>Now Webistrano should be serving from the VirtualHost you specified, seamlessly, via Passenger.</p>
<h3>Deploying A Non-Rails App</h3>
<p>Now the fun stuff.</p>
<p>Capistrano breaks things down into projects, stages, and recipes.  Each app you want managed by capistrano should be it's own project.  Each project should have a stage for at least production and optionally staging and development.</p>
<p>Hosts are added globally and form the targets of a deploy for any given project.  Hosts can include web, app, and database servers.</p>
<p>Deployments in Capistrano are done to a child directory under "releases" named via the date and time of the deployment.  By default 5 releases are kept and available to rollback to.  Upon successful deployment a symlink (default is called "current" and can be modified via the <i>current_path</i> configuration variable) is updated to that release directory.  It is this symlink that should be targeted by your webserver (your DocumentRoot in Apache).</p>
<p>Capistrano also creates a "shared" directory that is symlinked to in each release useful for storing logs and other data that should be maintained through each deployment.</p>
<p>For non-rails apps you'll use the "Pure File" project type when creating your new project.  Upon project creation you can add configuration variables specific to your project.  I recommend using <i>:export</i> instead of <i>:checkout</i> for <i>deploy_via</i> for production subversion deployments as this doesn't expose .svn directories.  Use an SSH user that has enough permissions to create directories where your deploy will occur or, specify <i>use_sudo</i> to true and create a new configuration variable <i>admin_runner</i> and set it to the same user as <i>runner</i>.</p>
<p>Add a stage to your new project for "production".  In the "Manage Hosts" page add a new host for each of your application servers.  Then add each host as a target of your "production" stage of your project.</p>
<p>At this point you should be able to execute the "Setup" task for your "production" stage.  This is a one time task that simply creates the directories.</p>
<p>Assuming this went successfully, try doing a "Deploy" and see if that finishes without error.  You might have to play around with permissions and other minor issues - post a comment if you have any specific questions.</p>
<p>For my PHP framework there are a couple specific tasks I wanted to run in addition to the default Capistrano tasks.  You do this by creating custom recipes in the "Manage Recipes" page in Webistrano.  Recipes are simply procedures written in ruby.  Here's what my recipe looks like:</p>
<pre class="brush: ruby;">
namespace :deploy do
	task :setup, :except =&gt; { :no_release =&gt; true } do
		dirs = [deploy_to, releases_path, shared_path]
		dirs += shared_children.map { |d| File.join(shared_path, d) }
		run &quot;#{try_sudo} mkdir -p #{dirs.join(' ')} &amp;&amp; #{try_sudo} chmod g+w #{dirs.join(' ')}&quot;
		run &quot;chmod 777 #{shared_path}/log&quot;
	end

	task :finalize_update, :except =&gt; { :no_release =&gt; true } do
		run &quot;mkdir -p #{latest_release}/app/tmp&quot;
		run &quot;chmod -R 777 #{latest_release}/app/tmp&quot;
		run &quot;rm -rf #{latest_release}/app/logs&quot;
		run &quot;ln -s #{shared_path}/log #{latest_release}/app/logs&quot;
		run &quot;cp #{latest_release}/public_html/.htaccess-production #{latest_release}/public_html/.htaccess&quot;
		run &quot;cp #{latest_release}/app/config/config-production.php #{latest_release}/app/config/config.php&quot;
		run &quot;cp #{latest_release}/app/config/db-default.php #{latest_release}/app/config/db.php&quot;
		run &quot;cp #{latest_release}/app/config/memcache-default.php #{latest_release}/app/config/memcache.php&quot;
	end
end
</pre>
<p>If you're not familiar with Ruby - what this code is essentially doing is overwriting two tasks in the :deploy namespace with my custom code.</p>
<p>The first, :setup, simply duplicates the base :setup functionality discussed above (creating the releases and shared directories) and chmods the shared log directory to be writable.</p>
<p>The second, :finalize_update, performs a variety of configuration tasks for a PHP app built with my framework.  Also, you'll notice that I'm removing my app's logs directory and symlinking to the shared log directory.  This way all releases will log to the same directory, consistently.  </p>
<p>In my case all of these procedures are command line instructions.  Alternatively, you can do a variety of things leveraging the full breadth of the Ruby language and any gem you'd like to introduce.   Things such as accessing your CDN API to clear image, JS, or CSS caching, etc.</p>
<h3>Deploying Django Apps</h3>
<p>First off it's worth noting that I serve my Django apps via mod_wsgi.  To make the deployment process easier here's what my app.wsgi script looks like:</p>
<pre class="brush: python;">
import os
import sys

appdir = os.path.normpath(os.path.join(os.path.realpath(os.path.dirname(__file__)), '..'))
sys.path.insert(0, appdir)
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
os.environ['PYTHON_EGG_CACHE'] = os.path.join(appdir, '.python-eggs')
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()
</pre>
<p>This code allows us to avoid having to hardcode paths in the wsgi script (and thus avoid having to change them when we deploy).  It assumes the following directory structure:</p>
<blockquote><p>
.python-eggs (egg cache)<br />
apps (apps path is added to python system path in settings.py)<br />
public (where your .wsgi script resides)<br />
site_media<br />
templates<br />
settings.py<br />
settings-production.py (used for deploy)<br />
urls.py<br />
...
</p></blockquote>
<p>If you follow this convention, the following Capistrano recipe works great:</p>
<pre class="brush: ruby;">
namespace :deploy do
	task :setup, :except =&gt; { :no_release =&gt; true } do
		dirs = [deploy_to, releases_path, shared_path]
		dirs += shared_children.map { |d| File.join(shared_path, d) }
		run &quot;#{try_sudo} mkdir -p #{dirs.join(' ')} &amp;&amp; #{try_sudo} chmod g+w #{dirs.join(' ')}&quot;
		run &quot;chmod 777 #{shared_path}/log&quot;
	end

	task :finalize_update, :except =&gt; { :no_release =&gt; true } do
		run &quot;rm -rf #{latest_release}/logs&quot;
		run &quot;ln -s #{shared_path}/log #{latest_release}/logs&quot;
		run &quot;cp #{latest_release}/settings-production.py #{latest_release}/settings.py&quot;
		run &quot;mkdir -p #{latest_release}/.python-eggs&quot;
		run &quot;chmod 777 #{latest_release}/.python-eggs&quot;
	end
end
</pre>
<h3>Fin</h3>
<p>This should give you a nice intro to leveraging Capistrano via Webistrano.  Feel free to comment with questions, suggestions, or anything else!</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/01/13/installing-ruby-enterprise-edition-with-phusion-passenger/' rel='bookmark' title='Permanent Link: Installing Ruby Enterprise Edition with Phusion Passenger'>Installing Ruby Enterprise Edition with Phusion Passenger</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/11/30/8-books-to-get-a-developer-for-the-holidays/' rel='bookmark' title='Permanent Link: 8 Books To Get A Developer For The Holidays'>8 Books To Get A Developer For The Holidays</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/06/01/be-language-agnostic-solve-the-problem/' rel='bookmark' title='Permanent Link: Be Language Agnostic &#8211; Solve the Problem!'>Be Language Agnostic &#8211; Solve the Problem!</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2010/02/08/deployment-using-capistrano-and-webistrano-via-rails-and-phusion-passenger/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>8 Books To Get A Developer For The Holidays</title>
		<link>http://blog.perplexedlabs.com/2009/11/30/8-books-to-get-a-developer-for-the-holidays/</link>
		<comments>http://blog.perplexedlabs.com/2009/11/30/8-books-to-get-a-developer-for-the-holidays/#comments</comments>
		<pubDate>Mon, 30 Nov 2009 13:30:06 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Book Reviews]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Clojure]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[book review]]></category>
		<category><![CDATA[clojure]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[programmer]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://blog.perplexedlabs.com/?p=385</guid>
		<description><![CDATA[Send this to your significant other/parent/relative/friend so, instead of that sweater, you get one of these nuggets of awesome this Christmas. The Pragmatic Programmer: From Journeyman to Master Write better, cleaner, more maintainable code. Learn how to manage your projects and focus on shipping your product. With insight that covers the gamut of software development [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/06/01/be-language-agnostic-solve-the-problem/' rel='bookmark' title='Permanent Link: Be Language Agnostic &#8211; Solve the Problem!'>Be Language Agnostic &#8211; Solve the Problem!</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/02/08/deployment-using-capistrano-and-webistrano-via-rails-and-phusion-passenger/' rel='bookmark' title='Permanent Link: Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger'>Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/07/28/django-1-0-template-development-sample-chapter-serving-multiple-templates/' rel='bookmark' title='Permanent Link: Django 1.0 Template Development: Sample Chapter &#8220;Serving Multiple Templates&#8221;'>Django 1.0 Template Development: Sample Chapter &#8220;Serving Multiple Templates&#8221;</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>Send this to your significant other/parent/relative/friend so, instead of that sweater, you get one of these nuggets of awesome this Christmas.</p>
<h3><a href="http://www.amazon.com/gp/product/020161622X?ie=UTF8&#038;tag=perplabs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=020161622X">The Pragmatic Programmer: From Journeyman to Master</a><img src="http://www.assoc-amazon.com/e/ir?t=perplabs-20&#038;l=as2&#038;o=1&#038;a=020161622X" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></h3>
<p>Write better, cleaner, more maintainable code.  Learn how to manage your projects and focus on shipping your product.  With insight that covers the gamut of software development from low level to management<br />
this one is a must have for anyone involved in this industry.</p>
<h3><a href="http://www.amazon.com/gp/product/1934356344?ie=UTF8&#038;tag=perplabs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=1934356344">The Passionate Programmer: Creating a Remarkable Career in Software Development</a><img src="http://www.assoc-amazon.com/e/ir?t=perplabs-20&#038;l=as2&#038;o=1&#038;a=1934356344" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></h3>
<p>Highly recommended!  <a href="http://blog.perplexedlabs.com/2009/07/29/book-review-the-passionate-programmer/">Read my full review</a>.</p>
<h3><a href="http://www.amazon.com/gp/product/0735619670?ie=UTF8&#038;tag=perplabs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=0735619670">Code Complete: A Practical Handbook of Software Construction</a><img src="http://www.assoc-amazon.com/e/ir?t=perplabs-20&#038;l=as2&#038;o=1&#038;a=0735619670" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></h3>
<p>Another classic "software construction" book.  Sharpen your saw with timeless information that can be applied to any project in any language.  Less bugs, more productivity, more programmer happiness.</p>
<h3><a href="http://www.amazon.com/gp/product/1430219483?ie=UTF8&#038;tag=perplabs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=1430219483">Coders at Work</a><img src="http://www.assoc-amazon.com/e/ir?t=perplabs-20&#038;l=as2&#038;o=1&#038;a=1430219483" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></h3>
<p>This one is different.  Written as a set of interview transcripts with 15 legendary industry giants, this book is a fantastic insight into how some of the great minds think.  It's inspiring to hear it from the source, must have!</p>
<h3><a href="http://www.amazon.com/gp/product/1934356336?ie=UTF8&#038;tag=perplabs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=1934356336">Programming Clojure</a><img src="http://www.assoc-amazon.com/e/ir?t=perplabs-20&#038;l=as2&#038;o=1&#038;a=1934356336" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></h3>
<p>A developer should learn at least one new language a year.  This year that language should be Clojure.  Clojure is a dynamic, general purpose, language targeting the Java virtual machine and designed for multi-threaded use.  It's growing popularity, ability to leverage the Java standard library, and its multi-threaded nature make this a must have.</p>
<h3><a href="http://www.amazon.com/gp/product/0201835959?ie=UTF8&#038;tag=perplabs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=0201835959">The Mythical Man-Month: Essays on Software Engineering</a><img src="http://www.assoc-amazon.com/e/ir?t=perplabs-20&#038;l=as2&#038;o=1&#038;a=0201835959" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></h3>
<p>Another classic.  Primarily discusses project management from the perspective of Fred Brooks and his experiences at IBM.  Brooks' Law states that "adding manpower to a late software project makes it later".</p>
<h3><a href="http://www.amazon.com/gp/product/0321344758?ie=UTF8&#038;tag=perplabs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=0321344758">Don't Make Me Think: A Common Sense Approach to Web Usability</a><img src="http://www.assoc-amazon.com/e/ir?t=perplabs-20&#038;l=as2&#038;o=1&#038;a=0321344758" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></h3>
<p>Web developers should always keep in mind the user of the product their creating.  Usability becomes increasingly important as applications move to the web.  The design and usability of your app can make or break its success.  This classic is a must read.</p>
<h3><a href="http://www.amazon.com/gp/product/0201633612?ie=UTF8&#038;tag=perplabs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=0201633612">Design Patterns: Elements of Reusable Object-Oriented Software</a><img src="http://www.assoc-amazon.com/e/ir?t=perplabs-20&#038;l=as2&#038;o=1&#038;a=0201633612" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></h3>
<p>This classic known most commonly as the "gang of four" book is the definitive reference on design patterns.  Covering all of the most common cases and time and time again serving as an invaluable source of information.</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/06/01/be-language-agnostic-solve-the-problem/' rel='bookmark' title='Permanent Link: Be Language Agnostic &#8211; Solve the Problem!'>Be Language Agnostic &#8211; Solve the Problem!</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/02/08/deployment-using-capistrano-and-webistrano-via-rails-and-phusion-passenger/' rel='bookmark' title='Permanent Link: Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger'>Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/07/28/django-1-0-template-development-sample-chapter-serving-multiple-templates/' rel='bookmark' title='Permanent Link: Django 1.0 Template Development: Sample Chapter &#8220;Serving Multiple Templates&#8221;'>Django 1.0 Template Development: Sample Chapter &#8220;Serving Multiple Templates&#8221;</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/11/30/8-books-to-get-a-developer-for-the-holidays/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP Custom MySQL Session Handler</title>
		<link>http://blog.perplexedlabs.com/2009/10/05/php-custom-session-handler/</link>
		<comments>http://blog.perplexedlabs.com/2009/10/05/php-custom-session-handler/#comments</comments>
		<pubDate>Mon, 05 Oct 2009 14:00:30 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[memcache]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[session]]></category>
		<category><![CDATA[session handler]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/?p=302</guid>
		<description><![CDATA[The Problem I'm sure many have used PHP's default session handling capabilities. By default, PHP uses the filesystem to store session data naming files with their session id # and putting them in /tmp. This is done for the sake of simplicity. On a single-server, low load website, this particular setup works fine. It's when [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/02/06/mutex-with-php-and-mysql/' rel='bookmark' title='Permanent Link: Mutex with PHP and mySQL'>Mutex with PHP and mySQL</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/12/17/php-parallel-web-scraper/' rel='bookmark' title='Permanent Link: PHP Parallel Web Scraper'>PHP Parallel Web Scraper</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/02/04/php-simple-profiling-class/' rel='bookmark' title='Permanent Link: PHP Simple Profiling Class'>PHP Simple Profiling Class</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<h3>The Problem</h3>
<p>I'm sure many have used PHP's default session handling capabilities.  By default, PHP uses the filesystem to store session data naming files with their session id # and putting them in /tmp.</p>
<p>This is done for the sake of simplicity.  On a single-server, low load website, this particular setup works fine.  It's when you start having multiple simultaneous requests from a single client (identified by a session) that the problems begin to show their ugly heads.  Utilizing AJAX multiple simultaneous requests might be the norm, even for a low load website.</p>
<p>Essentially, in order to prevent <a href="http://en.wikipedia.org/wiki/Race_condition">race conditions</a> PHP internally uses a lock to maintain exclusive access to the file containing the session data for the client connection.  This means that as soon as a single request acquires exclusive access to that session file, no other request can access the file until the original request completes.  What happens when that second request asks to start the session?  It waits.</p>
<h3>The Solution</h3>
<p>Fortunately there's a solution to all this.  Implementing your own custom session handler and moving your session storage backend to another technology (such as a MySQL database or memcache) affords you the ability to handle simultaneous requests in a thread-safe manner.  Remember, it's a good thing that PHP prevents race conditions by locking the session file.  What we're looking to do is increase the granularity of the lock to the level of individual session data key =&gt; value pairs.</p>
<p>For this post we're going to stick to storing sessions in the database with our own custom session save handler.  Perhaps in another post I'll talk about doing the same in memcache.  It's the theory we're concerned about, not necessarily the exact storage mechanism implementation.</p>
<p>Implementing your own custom session handler is simply a matter of calling session_set_save_handler() with the appropriate callback methods for handling the following scenarios:</p>
<ul>
<li><strong>open</strong><br />
Open function, this works like a constructor in classes and is executed when the session is being opened. The open function expects two parameters, where the first is the save path and the second is the session name.</li>
<li><strong>close</strong><br />
Close function, this works like a destructor in classes and is executed when the session operation is done.</li>
<li><strong>read</strong><br />
Read function must return string value always to make save handler work as expected. Return empty string if there is no data to read. Return values from other handlers are converted to boolean expression. TRUE for success, FALSE for failure.</li>
<li><strong>write</strong><br />
The "write" handler is not executed until after the output stream is closed.</li>
<li><strong>destroy</strong><br />
The destroy handler, this is executed when a session is destroyed with session_destroy() and takes the session id as its only parameter.</li>
<li><strong>gc</strong><br />
The garbage collector, this is executed when the session garbage collector is executed and takes the max session lifetime as its only parameter.</li>
</ul>
<p>There's a well known trick for situations like this that allow you to pass a class method (static or instance) for callbacks.  Let's take a look at a simple example.  This correctly implements the required methods but obviously doesn't do much:</p>
<pre class="brush: php;">
class MySession
{
	public function __construct()
	{
		session_set_save_handler(
						array('MySession', 'sess_open'),
						array('MySession', 'sess_close'),
						array('MySession', 'sess_read'),
						array('MySession', 'sess_write'),
						array('MySession', 'sess_destroy'),
						array('MySession', 'sess_gc')
						);

		ini_set('session.auto_start',					0);
		ini_set('session.gc_probability',				1);
		ini_set('session.gc_divisor',					100);
		ini_set('session.gc_maxlifetime',				604800);
		ini_set('session.referer_check',				'');
		ini_set('session.entropy_file',					'/dev/urandom');
		ini_set('session.entropy_length',				16);
		ini_set('session.use_cookies',					1);
		ini_set('session.use_only_cookies',				1);
		ini_set('session.use_trans_sid',				0);
		ini_set('session.hash_function',				1);
		ini_seT('session.hash_bits_per_character',		5);

		session_cache_limiter('nocache');
		session_set_cookie_params(0, '/', '.mydomainname.com');
		session_name('mySessionName');
		session_start();
	}

	public static function sess_open($save_path, $session_name)
	{
		return true;
	}

	public static function sess_close()
	{
		return true;
	}

	public static function sess_read($id)
	{
		return '';
	}

	public static function sess_write($id, $sess_data)
	{
		return true;
	}

	public static function sess_destroy($id)
	{
		return true;
	}

	public static function sess_gc($maxlifetime)
	{
		return true;
	}
}
</pre>
<h3>SPL and Storing Session Data In MySQL</h3>
<p>Adding support for MySQL to this class is fairly trivial.  Let's start off by creating a table to store our session data:</p>
<pre class="brush: sql;">
CREATE TABLE `sessions` (
  `sesskey` char(32) NOT NULL,
  `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  `varkey` varchar(128) NOT NULL,
  `varval` longtext NOT NULL,
  PRIMARY KEY  (`sesskey`,`varkey`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
</pre>
<p><a href="http://us3.php.net/spl">PHP's SPL</a> provides the ability to create objects that behave as though they were arrays.  You can make objects that iterate, respond to accessing them via array notation ($object['key']), and lots of other interesting things.</p>
<p>We're going to enhance the MySession class with a couple SPL interfaces that will allow the object, when instantiated, to behave like an array.   We can then override the $_SESSION superglobal with an instance of our new MySession class.  It will provide an identical interface to access session data while internally storing session data to the database.  Also, internally it will use methods for row-level locking via MySQL's advisory locks (GET_LOCK() and RELEASE_LOCK()).</p>
<p>The next step is to implement the required methods for the interfaces we're using.  These methods allow the object to behave as though it's an array.</p>
<pre class="brush: php;">
class MySession implements Countable, ArrayAccess, Iterator
{
	private $index;
	private $curElement;
	private $locks = array();
	private $sessionName = 'sessionId';
	private $serialize = 'serialize';
	private $unserialize = 'unserialize';
	private $session_id = null;

	public function __construct()
	{
		session_set_save_handler(
						array('MySession', 'sess_open'),
						array('MySession', 'sess_close'),
						array('MySession', 'sess_read'),
						array('MySession', 'sess_write'),
						array('MySession', 'sess_destroy'),
						array('MySession', 'sess_gc')
						);

		ini_set('session.auto_start',					0);
		ini_set('session.gc_probability',				1);
		ini_set('session.gc_divisor',					100);
		ini_set('session.gc_maxlifetime',				604800);
		ini_set('session.referer_check',				'');
		ini_set('session.entropy_file',					'/dev/urandom');
		ini_set('session.entropy_length',				16);
		ini_set('session.use_cookies',					1);
		ini_set('session.use_only_cookies',				1);
		ini_set('session.use_trans_sid',				0);
		ini_set('session.hash_function',				1);
		ini_seT('session.hash_bits_per_character',		5);

		session_cache_limiter('nocache');
		session_set_cookie_params(0, '/', '.mydomainname.com');
		session_name('mySessionName');
		session_start();
	}

	public function destroy()
	{
		$sessionName = session_name();
		$cookieInfo = session_get_cookie_params();
		$cookieExpires = time() - 3600;
		if((empty($cookieInfo['domain'])) &amp;&amp; (empty($cookieInfo['secure']))) {
			setcookie($sessionName, '', $cookieExpires, $cookieInfo['path']);
		} elseif(empty($cookieInfo['secure'])) {
			setcookie($sessionName, '', $cookieExpires, $cookieInfo['path'], $cookieInfo['domain']);
		} else {
			setcookie($sessionName, '', $cookieExpires, $cookieInfo['path'], $cookieInfo['domain'], $cookieInfo['secure']);
		}
		unset($_COOKIE[$sessionName]);

		$dbo = DBO::getInstance();
		$q = &quot;DELETE FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;'&quot;;
		$dbo-&gt;query($q);

		session_destroy();
	}

	private function lockName($k)
	{
		return 'sesslock'.$this-&gt;session_id.$k;
	}

	public function locked($k)
	{
		$k = $this-&gt;lockName($k);

		return isset($this-&gt;locks[$k]);
	}

	public function acquire($k, $timeout = 0)
	{
		$k = $this-&gt;lockName($k);

		if(!isset($this-&gt;locks[$k])) {
			$dbo = DBO::getInstance();
			$q = &quot;SELECT GET_LOCK('&quot;.$k.&quot;', &quot;.$timeout.&quot;)&quot;;
			$rs = $dbo-&gt;query($q);
			$this-&gt;locks[$k] = $dbo-&gt;result($rs, 0);
			$dbo-&gt;fr($rs);

			return $this-&gt;locks[$k];
		}

		return false;
	}

	public function release($k)
	{
		$k = $this-&gt;lockName($k);

		unset($this-&gt;locks[$k]);

		$dbo = DBO::getInstance();
		$q = &quot;SELECT RELEASE_LOCK('&quot;.$k.&quot;')&quot;;
		$rs = $dbo-&gt;query($q);
		$ret = $dbo-&gt;fetch($rs);
		$dbo-&gt;fr($rs);

		return true;
	}

	public function count()
	{
		$dbo = DBO::getInstance();
		$q = &quot;SELECT COUNT(*) FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;'&quot;;
		$rs = $dbo-&gt;query($q);
		$ret = $dbo-&gt;result($rs, 0);
		$dbo-&gt;fr($rs);

		return $ret;
	}

	public function rewind()
	{
		$this-&gt;index = 0;
		$this-&gt;getCurElement();
	}

	private function getCurElement()
	{
		$dbo = DBO::getInstance();
		$q = &quot;SELECT `varkey`, `varval` FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;' LIMIT &quot;.$this-&gt;index.&quot;,1&quot;;
		$rs = $dbo-&gt;query($q);
		$row = $dbo-&gt;fetch($rs);
		$dbo-&gt;fr($rs);
		if(is_array($row) &amp;&amp; (count($row) == 2)) {
			$this-&gt;curElement = $row;
		} else {
			$this-&gt;curElement = array(null, null);
		}
	}

	public function key()
	{
		return $this-&gt;curElement[0];
	}

	public function current()
	{
		return call_user_func($this-&gt;unserialize, $this-&gt;curElement[1]);
	}

	public function next()
	{
		$this-&gt;index++;
		$this-&gt;getCurElement();
	}

	public function valid()
	{
		return ($this-&gt;curElement[0] !== null);
	}

	public function offsetSet($k, $v)
	{
		$dbo = DBO::getInstance();
		$q = &quot;REPLACE INTO `sessions` (`sesskey`, `varkey`, `varval`) VALUES ('&quot;.$this-&gt;session_id.&quot;', '&quot;.$k.&quot;', '&quot;.$dbo-&gt;sanitize(call_user_func($this-&gt;serialize, $v)).&quot;')&quot;;
		$dbo-&gt;query($q);
	}

	public function offsetGet($k)
	{
		$dbo = DBO::getInstance();
		$q = &quot;SELECT `varval` FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;' AND `varkey` = '&quot;.$k.&quot;'&quot;;
		$rs = $dbo-&gt;query($q);
		if($ret = $dbo-&gt;result($rs, 0)) {
			$ret = call_user_func($this-&gt;unserialize, $ret);
		}
		$dbo-&gt;fr($rs);

		return $ret;
	}

	public function offsetUnset($k)
	{
		$dbo = DBO::getInstance();
		$q = &quot;DELETE FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;' AND `varkey` = '&quot;.$k.&quot;'&quot;;
		$dbo-&gt;query($q);
	}

	public function offsetExists($k)
	{
		$dbo = DBO::getInstance();
		$q = &quot;SELECT `varval` FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;' AND `varkey` = '&quot;.$k.&quot;'&quot;;
		$rs = $dbo-&gt;query($q);
		$ret = $dbo-&gt;result($rs, 0);
		$dbo-&gt;fr($rs);

		return (bool)$ret;
	}

	public static function sess_open($save_path, $session_name)
	{
		return true;
	}

	public static function sess_close()
	{
		return true;
	}

	public static function sess_read($id)
	{
		return '';
	}

	public static function sess_write($id, $sess_data)
	{
		return true;
	}

	public static function sess_destroy($id)
	{
		return true;
	}

	public static function sess_gc($maxlifetime)
	{
		$dbo = DBO::getInstance();
		$q = &quot;DELETE FROM `sessions` WHERE `timestamp` &lt; '&quot;.date('Y-m-d H:i:s', time() - $maxlifetime).&quot;'&quot;;
		$dbo-&gt;query($q);

		return $dbo-&gt;query($q);
	}
}
</pre>
<p>The $dbo object is just an example of an interface to the database through the use of a singleton.  Replace the $dbo object with your preferred mysql database interface and you'll be set to go!</p>
<p>Starting your session is now as simple as:</p>
<pre class="brush: php;">
$_SESSION = new MySession;
</pre>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/02/06/mutex-with-php-and-mysql/' rel='bookmark' title='Permanent Link: Mutex with PHP and mySQL'>Mutex with PHP and mySQL</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/12/17/php-parallel-web-scraper/' rel='bookmark' title='Permanent Link: PHP Parallel Web Scraper'>PHP Parallel Web Scraper</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/02/04/php-simple-profiling-class/' rel='bookmark' title='Permanent Link: PHP Simple Profiling Class'>PHP Simple Profiling Class</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/10/05/php-custom-session-handler/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>XMLRPC Pingbacks Using PHP</title>
		<link>http://blog.perplexedlabs.com/2009/07/15/xmlrpc-pingbacks-using-php/</link>
		<comments>http://blog.perplexedlabs.com/2009/07/15/xmlrpc-pingbacks-using-php/#comments</comments>
		<pubDate>Wed, 15 Jul 2009 17:39:46 +0000</pubDate>
		<dc:creator>Eric</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[pingback]]></category>
		<category><![CDATA[xmlrpc]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/?p=308</guid>
		<description><![CDATA[Motivation: For a side project I am working on, I wanted to be able to send a trackback to WordPress blogs (or any blog with an XMLRPC endpoint), using PHP.   Simple enough. Methodology: I had a few different options.  I could have manually made a POST request to the endpoint, hand-coding the XML (this brute [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/04/09/php-daisy-chain-class-method-calls/' rel='bookmark' title='Permanent Link: PHP Daisy Chain Class Method Calls'>PHP Daisy Chain Class Method Calls</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/02/05/dynamic-javascript-script-insertion-for-embedding/' rel='bookmark' title='Permanent Link: PHP Dynamic JavaScript SCRIPT Insertion for Embedding'>PHP Dynamic JavaScript SCRIPT Insertion for Embedding</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/04/22/php-named-parameters/' rel='bookmark' title='Permanent Link: PHP Named Parameters'>PHP Named Parameters</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p><strong>Motivation: </strong>For a side project I am working on, I wanted to be able to send a trackback to WordPress blogs (or any blog with an XMLRPC endpoint), using PHP.   Simple enough.</p>
<p><strong>Methodology: </strong>I had a few different options.  I could have manually made a POST request to the endpoint, hand-coding the XML (this brute force method).  Or I could stand on the shoulders of giants and leverage the power of open-source libraries.  I chose the latter, in keeping with my new guiding principle of pursuing beauty, truth and goodness.</p>
<p>To brush up on how an XMLRPC pingback works, from a 30,000 foot level, I checked out the <a href="http://hixie.ch/specs/pingback/pingback-1.0">Pingback 1.0 Specification</a>.  As far as the libraries, I settled on <a href="http://phpxmlrpc.sourceforge.net">phpxmlrpc</a>.  First, the documentation existed and second, it was straightforward.  According to the <a href="http://phpxmlrpc.sourceforge.net/doc-1.1/index.html">documentation</a>, the process goes like this:</p>
<ol>
<li>Create an xmlrpc client</li>
<li>Set the debug level you think you need (optional)</li>
<li>Create a new message</li>
<li>Send the message</li>
<li>Parse the response</li>
</ol>
<p><strong>Implementation:</strong> Again, this isn't rocket science, but here's what I did.  Suggestions and critiques are welcome.</p>
<pre class="brush: php;">
$xmlrpc_client = new xmlrpc_client(&quot;xmlrpc.php&quot;, $host_url, 80);
$xmlrpc_client-&gt;setDebug(1); //this will print all the responses as they come back
$xmlrpc_message = new xmlrpcmsg(&quot;pingback.ping&quot;, array(new xmlrpcval($site_linking_from), new xmlrpcval($site_linking_to)));
$xmlrpc_response = $xmlrpc_client-&gt;send($xmlrpc_message);
if($xmlrpc_response-&gt;faultCode() == 0){
echo $xmlrpc_response-&gt;faultString();
}else{
echo &quot;Pingback successful&quot;;
}
</pre>
<p>A couple of notes.  On line 3, "pingback.ping" is the XMLRPC API method that handles pingbacks.  Also, the order of the sites you send to the call would be your site, and the site you're pinging.  I switched this order around the first time and got some interesting error messages.</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/04/09/php-daisy-chain-class-method-calls/' rel='bookmark' title='Permanent Link: PHP Daisy Chain Class Method Calls'>PHP Daisy Chain Class Method Calls</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/02/05/dynamic-javascript-script-insertion-for-embedding/' rel='bookmark' title='Permanent Link: PHP Dynamic JavaScript SCRIPT Insertion for Embedding'>PHP Dynamic JavaScript SCRIPT Insertion for Embedding</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/04/22/php-named-parameters/' rel='bookmark' title='Permanent Link: PHP Named Parameters'>PHP Named Parameters</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/07/15/xmlrpc-pingbacks-using-php/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>PHP 5.3.0 Released and Firefox 3.5</title>
		<link>http://blog.perplexedlabs.com/2009/06/30/php-5-3-0-released-and-firefox-3-5/</link>
		<comments>http://blog.perplexedlabs.com/2009/06/30/php-5-3-0-released-and-firefox-3-5/#comments</comments>
		<pubDate>Tue, 30 Jun 2009 17:00:32 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Random]]></category>
		<category><![CDATA[firefox]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/?p=304</guid>
		<description><![CDATA[Pretty major day in the world of the web, eh? The PHP development team announced the release of version 5.3.0. This is a major milestone. Here's what I'm most excited about: Lambda Functions and Closures ternary short cut "?:" Optional garbage collection for cyclic references I'm excited about support for namespaces, but not the particular [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2010/07/24/tornado-1-0-released/' rel='bookmark' title='Permanent Link: Tornado 1.0 Released'>Tornado 1.0 Released</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/11/11/remove-firefox-link-outline/' rel='bookmark' title='Permanent Link: Remove Firefox Link Outline'>Remove Firefox Link Outline</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/07/29/django-1-1-released/' rel='bookmark' title='Permanent Link: Django 1.1 Released'>Django 1.1 Released</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>Pretty major day in the world of the web, eh?</p>
<p>The PHP development team announced the <a href="http://php.net/releases/5_3_0.php">release of version 5.3.0</a>.  This is a <strong>major</strong> milestone.  Here's what I'm most excited about:</p>
<ul>
<li>Lambda Functions and Closures</li>
<li>ternary short cut "?:"</li>
<li>Optional garbage collection for cyclic references</li>
</ul>
<p>I'm excited about support for namespaces, but not the particular syntactical implementation chosen.</p>
<p>Also, the Mozilla team announced the official release of <a href="http://download.mozilla.org/?product=firefox-3.5&#038;os=win&#038;lang=en-US">Firefox 3.5</a>.  Firefox is my browser of choice thanks to the great web development plugins available for it such as:</p>
<ul>
<li><a href="http://getfirebug.com/">Firebug</a></li>
<li><a href="http://code.google.com/p/httpfox/">HttpFox</a></li>
<li><a href="http://chrispederick.com/work/web-developer/">Web Developer Toolbar</a>
</ul>
<p>Version 3.5 looks to make an already excellent browser even faster.</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2010/07/24/tornado-1-0-released/' rel='bookmark' title='Permanent Link: Tornado 1.0 Released'>Tornado 1.0 Released</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/11/11/remove-firefox-link-outline/' rel='bookmark' title='Permanent Link: Remove Firefox Link Outline'>Remove Firefox Link Outline</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/07/29/django-1-1-released/' rel='bookmark' title='Permanent Link: Django 1.1 Released'>Django 1.1 Released</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/06/30/php-5-3-0-released-and-firefox-3-5/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Be Language Agnostic &#8211; Solve the Problem!</title>
		<link>http://blog.perplexedlabs.com/2009/06/01/be-language-agnostic-solve-the-problem/</link>
		<comments>http://blog.perplexedlabs.com/2009/06/01/be-language-agnostic-solve-the-problem/#comments</comments>
		<pubDate>Mon, 01 Jun 2009 14:15:28 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/?p=280</guid>
		<description><![CDATA[I like Python. I like Ruby. I like C, C++, and Objective-C. I like Java. I also (actually) like PHP. I like programming - get it? Use whatever gets the job done and done well. Use whatever achieves the performance and scalability you require for a given task. Use what makes sense given a specific [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/11/30/8-books-to-get-a-developer-for-the-holidays/' rel='bookmark' title='Permanent Link: 8 Books To Get A Developer For The Holidays'>8 Books To Get A Developer For The Holidays</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/02/08/deployment-using-capistrano-and-webistrano-via-rails-and-phusion-passenger/' rel='bookmark' title='Permanent Link: Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger'>Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/03/20/django-and-python-first-impressions-part-ii/' rel='bookmark' title='Permanent Link: Django and Python First Impressions &#8211; Part II'>Django and Python First Impressions &#8211; Part II</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>I like Python.  I like Ruby.  I like C, C++, and Objective-C.  I like Java.  I also (actually) like PHP.  I like programming - get it?  Use whatever gets the job done and done well.  Use whatever achieves the performance and scalability you require for a given task.  Use what makes sense given a specific problem's domain.  <del datetime="2009-06-09T13:43:01+00:00">Use whatever aligns itself with the way your mind works</del>.  Don't be a one trick pony (that isn't a Django reference).  Learn multiple languages, their strengths, and their weaknesses.  Understand when a language's strengths will allow you to solve a problem faster, easier, better.  Don't force a square peg through a round hole.</p>
<p>Watch <strong><a href="http://www.itworld.com/video?bcpid=1578108607&#038;bclid=1588003312&#038;bctid=23220283001">this</a></strong> video.  It's excellent food for thought and drives home the importance, as a programmer, of learning new, different, languages to expand your ability to solve problems in a variety of ways.  The video mentions the <a href="http://en.wikipedia.org/wiki/Sapir%E2%80%93Whorf_hypothesis">Sapir–Whorf hypothesis</a> which suggests that a particular language influences how a person understands and interacts with the world.  This makes a lot of sense.</p>
<p><strong>A programmer who thinks only in terms of a single language will attempt to solve every problem with that language.  You need more than one tool on your belt because not every problem is a nail.</strong></p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/11/30/8-books-to-get-a-developer-for-the-holidays/' rel='bookmark' title='Permanent Link: 8 Books To Get A Developer For The Holidays'>8 Books To Get A Developer For The Holidays</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/02/08/deployment-using-capistrano-and-webistrano-via-rails-and-phusion-passenger/' rel='bookmark' title='Permanent Link: Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger'>Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/03/20/django-and-python-first-impressions-part-ii/' rel='bookmark' title='Permanent Link: Django and Python First Impressions &#8211; Part II'>Django and Python First Impressions &#8211; Part II</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/06/01/be-language-agnostic-solve-the-problem/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>PHP Drinks Java</title>
		<link>http://blog.perplexedlabs.com/2009/05/18/php-drinks-java/</link>
		<comments>http://blog.perplexedlabs.com/2009/05/18/php-drinks-java/#comments</comments>
		<pubDate>Mon, 18 May 2009 16:46:39 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/?p=267</guid>
		<description><![CDATA[This post got me thinking about exactly why it is that PHP developers dislike Java? While researching I stumbled upon this post which attempts to explain why you shouldn't treat PHP as if it were Java. The example singleton code looks nearly identical in both PHP and Java. The author suggests that an "experienced" PHP [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/02/01/get-it-done/' rel='bookmark' title='Permanent Link: Get It Done'>Get It Done</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/06/30/php-5-3-0-released-and-firefox-3-5/' rel='bookmark' title='Permanent Link: PHP 5.3.0 Released and Firefox 3.5'>PHP 5.3.0 Released and Firefox 3.5</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/05/04/php-jquery-ajax-javascript-long-polling/' rel='bookmark' title='Permanent Link: PHP jQuery AJAX Javascript Long Polling'>PHP jQuery AJAX Javascript Long Polling</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p><strong><a href="http://www.travisswicegood.com/index.php/2009/05/13/magic-is-to-python-as-java-is-to-php">This</a></strong> post got me thinking about exactly why it is that PHP developers dislike Java?</p>
<p>While researching I stumbled upon <strong><a href="http://phpadvent.org/2008/php-is-not-java-by-luke-welling">this</a></strong> post which attempts to explain why you shouldn't treat PHP as if it were Java.  The example singleton code looks nearly identical in both PHP and Java.  The author suggests that an "experienced" PHP developer wouldn't attempt to think in terms of Java and would instead choose to implement this concept in PHP with the use of a global variable.  This is ridiculous.  It's crap like this that gives PHP a bad rap.</p>
<p>No, <strong>PHP isn't Java</strong> and shouldn't be treated as such.  Nor should you abandon all logic and reason and disregard features of the language that make it <em>appear</em> Java-like.</p>
<p>PHP's object model borrows heavily from Java.  This is fact.  Its property and method visibility, single inheritance, interfaces, final classes, and abstractions are all very Java-like.  Not to mention exceptions and garbage collection.  I'll be damned if that doesn't cover a large portion of the features that developers use to write software in both PHP and Java.  Are all of these concepts exclusive to Java?  No, they're not.  It just seems that the PHP team felt that Java made some good decisions and decided to emulate them.</p>
<p>I'll bet that lots of PHP code is unknowingly written Java-like.  Is this because a PHP developer is "thinking in Java"?  Perhaps it's because the foundational toolset which a PHP developer was given was designed with Java in mind.  I'm not sure it makes sense to decide to use PHP to solve a problem and then fight with/ignore/don't take advantage of the features of the language.  I think that would be a good reason to choose another language, no?</p>
<p>How could a language (obviously) borrow so heavily from another language and simultaneously shun the mention of that language?</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/02/01/get-it-done/' rel='bookmark' title='Permanent Link: Get It Done'>Get It Done</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/06/30/php-5-3-0-released-and-firefox-3-5/' rel='bookmark' title='Permanent Link: PHP 5.3.0 Released and Firefox 3.5'>PHP 5.3.0 Released and Firefox 3.5</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/05/04/php-jquery-ajax-javascript-long-polling/' rel='bookmark' title='Permanent Link: PHP jQuery AJAX Javascript Long Polling'>PHP jQuery AJAX Javascript Long Polling</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/05/18/php-drinks-java/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>PHP libmemcached via memcached and igbinary</title>
		<link>http://blog.perplexedlabs.com/2009/05/04/php-libmemcached-via-memcached-and-igbinary/</link>
		<comments>http://blog.perplexedlabs.com/2009/05/04/php-libmemcached-via-memcached-and-igbinary/#comments</comments>
		<pubDate>Mon, 04 May 2009 15:00:41 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[igbinary]]></category>
		<category><![CDATA[libmemcached]]></category>
		<category><![CDATA[memcache]]></category>
		<category><![CDATA[pecl]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/?p=215</guid>
		<description><![CDATA[Found some great PHP resources that I'd like to share. I haven't seen much talk of these so I'm hoping I can help spread the word. First off libmemcached. Most PHP folks are familiar with the memcache (note the lack of a 'd' in the name) PECL extension. This extension exposes a simple API for [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2010/03/02/php-forking-to-concurrency/' rel='bookmark' title='Permanent Link: PHP Forking to Concurrency with pcntl_fork()'>PHP Forking to Concurrency with pcntl_fork()</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/10/05/php-custom-session-handler/' rel='bookmark' title='Permanent Link: PHP Custom MySQL Session Handler'>PHP Custom MySQL Session Handler</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/05/04/php-jquery-ajax-javascript-long-polling/' rel='bookmark' title='Permanent Link: PHP jQuery AJAX Javascript Long Polling'>PHP jQuery AJAX Javascript Long Polling</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>Found some great PHP resources that I'd like to share.  I haven't seen much talk of these so I'm hoping I can help spread the word.</p>
<p>First off <strong><a href="http://tangent.org/552/libmemcached.html">libmemcached</a></strong>.  </p>
<p>Most PHP folks are familiar with the <strong><a href="http://pecl.php.net/package/memcache">memcache</a></strong> (note the lack of a 'd' in the name) PECL extension.  This extension exposes a simple API for PHP apps to interact with memcache instances.  It works - it's simple, stable, and has been available since 2004.  Nothing special.</p>
<p>On the other hand, <strong><a href="http://tangent.org/552/libmemcached.html">libmemcached</a></strong> "is a small, thread-safe client library for the memcached protocol. The code has all been written with an eye to allow for both web and embedded usage."  - "It has been designed to be light on memory usage, thread safe, and provide full access to server side methods."  And, fortunately, there's a new PECL extension that wraps libmemcached in a client library for PHP called <strong><a href="http://www.pecl.php.net/package/memcached">memcached</a></strong> (note the 'd').  It was released in late January and is still considered "beta" however in my testing it has been stable.  This extension provides a rich interface to your memcache instances including the new check and set (cas), replace, and append operations.  As libmemcached becomes more widely adopted and its development continues, it makes sense to unify support behind a common library.</p>
<p>Lastly, <strong><a href="http://opensource.dynamoid.com/">igbinary</a></strong>.  This is a PHP extension which provides <em>binary</em> serialization for PHP objects and data.  It's a drop in replacement for PHP's built in serializer.  Why is this important?  When storing data in memcache it is first serialized (this is done automatically by the client library, such as memcached).  Conversely when retrieving data from memcache the data is unserialized.  The default PHP serializer uses a textual representation of data and objects.  This is a waste of memory.   Also, as objects increase in size and complexity the time it takes to (un)serialize increases significantly.  Igbinary stores data in a compact binary format which reduces the memory footprint and performs operations faster.  Most importantly memcached has built in support to take advantage of igbinary as its default serializer, yet another reason to use it as your memcache client library.</p>
<p>Check these resources out and let me know how they work for you!</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2010/03/02/php-forking-to-concurrency/' rel='bookmark' title='Permanent Link: PHP Forking to Concurrency with pcntl_fork()'>PHP Forking to Concurrency with pcntl_fork()</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/10/05/php-custom-session-handler/' rel='bookmark' title='Permanent Link: PHP Custom MySQL Session Handler'>PHP Custom MySQL Session Handler</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/05/04/php-jquery-ajax-javascript-long-polling/' rel='bookmark' title='Permanent Link: PHP jQuery AJAX Javascript Long Polling'>PHP jQuery AJAX Javascript Long Polling</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/05/04/php-libmemcached-via-memcached-and-igbinary/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>PHP jQuery AJAX Javascript Long Polling</title>
		<link>http://blog.perplexedlabs.com/2009/05/04/php-jquery-ajax-javascript-long-polling/</link>
		<comments>http://blog.perplexedlabs.com/2009/05/04/php-jquery-ajax-javascript-long-polling/#comments</comments>
		<pubDate>Mon, 04 May 2009 14:00:14 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[lighttpd]]></category>
		<category><![CDATA[long polling]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/?p=217</guid>
		<description><![CDATA[Background "Long polling" is the name used to describe a technique which: An AJAX request is made (utilizing a javascript framework such as jQuery) The server waits for the data requested to be available, loops, and sleeps (your server-side PHP script) This loop repeats after data is returned to the client and processed (usually in [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/11/07/real-time-ajax-javascript-progress-bar/' rel='bookmark' title='Permanent Link: Real-time &#8220;AJAX&#8221; JavaScript Progress Bar'>Real-time &#8220;AJAX&#8221; JavaScript Progress Bar</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/02/05/dynamic-javascript-script-insertion-for-embedding/' rel='bookmark' title='Permanent Link: PHP Dynamic JavaScript SCRIPT Insertion for Embedding'>PHP Dynamic JavaScript SCRIPT Insertion for Embedding</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/02/12/php-fast-large-megabyte-data-transfer-between-sessions/' rel='bookmark' title='Permanent Link: PHP fast, large (megabyte), data transfer between sessions'>PHP fast, large (megabyte), data transfer between sessions</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<h3>Background</h3>
<p>"Long polling" is the name used to describe a technique which:</p>
<ul>
<li>An AJAX request is made (utilizing a javascript framework such as jQuery)
<li>The server waits for the data requested to be available, loops, and sleeps (your server-side PHP script)
<li>This loop repeats after data is returned to the client and processed (usually in your AJAX request's onComplete callback function)
</ul>
<p>This essentially simulates a continuous real-time stream from the client to the server.  It can be more efficient than a regular polling technique because of the reduction in HTTP requests.  You're not asking over and over and over again for new data - you ask once and wait for an answer.  In most cases this reduces the latency in which data becomes available to your application.</p>
<p>There are a variety of use cases in which this technique can be handy.  At the top of the list are real-time web-based chat applications.  Each client executes a long polling loop for chat and user events (sign on/sign off/new message).  <a href="http://www.meebo.com">Meebo</a> is perhaps the greatest example of this.</p>
<p>It's important to note some of the server side technical limitations of long polling.  Because connections remain open for considerably longer time than a typical HTTP request/response cycle you want your web server to be able to handle a large number of simultaneous connections.  Apache isn't the best candidate for this type of situation.  <a href="http://nginx.net/">nginx</a> and <a href="http://www.lighttpd.net/">lighttpd</a> are two lightweight web servers built from the ground up to handle a high volume of simultaneous connections.  Both support the FastCGI interface and as such can be configured to support PHP.  Again, Meebo uses lighttpd.</p>
<p>For similar reasons - it's also a good idea to choose a different sub-domain to handle long polling traffic.  Because of client side browser limitations you don't want long polling connections interfering with regular HTTP traffic delivering page and media resources for your application.</p>
<h3>Implementation</h3>
<p><a href="http://www.jquery.com">jQuery</a> makes implementation a breeze.</p>
<pre class="brush: jscript;">
var lpOnComplete = function(response) {
	alert(response);
	// do more processing
	lpStart();
};

var lpStart = function() {
	$.post('/path/to/script', {}, lpOnComplete, 'json');
};

$(document).ready(lpStart);
</pre>
<p>Straightforward.  When the document is ready the loop begins.  Each iteration the returned data is processed and the loop is restarted.</p>
<p>On the server side - just like we discussed earlier:</p>
<pre class="brush: php;">
$time = time();
while((time() - $time) &lt; 30) {
	// query memcache, database, etc. for new data
	$data = $datasource-&gt;getLatest();

	// if we have new data return it
	if(!empty($data)) {
		echo json_encode($data);
		break;
	}

	usleep(25000);
}
</pre>
<p>Actually, a couple points of interest here.  We don't actually loop <em>infinitely</em> server side.  You may have noticed the logic for the while loop - if we've executed for more than 30 seconds we discontinue the loop and return nothing.  This nearly eliminates the possibility of substantial memory leaks.  Also, if we didn't put a cap on execution time we would need to print a "space" character and flush output buffers every iteration of the loop to keep PHP abreast to the status of this process/connection.  <strong>Without output being sent PHP cannot determine if the connection was lost via connection_status() or connection_aborted()</strong>.  As a result this could lead to a situation where there are an increasing number of "ghost" processes eating up server resources.  Not good!</p>
<p>That pretty much sums it up!  Not that difficult, right?</p>
<p>As always, questions/comments are welcome, hope this helps!</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/11/07/real-time-ajax-javascript-progress-bar/' rel='bookmark' title='Permanent Link: Real-time &#8220;AJAX&#8221; JavaScript Progress Bar'>Real-time &#8220;AJAX&#8221; JavaScript Progress Bar</a></li>
<li><a href='http://blog.perplexedlabs.com/2009/02/05/dynamic-javascript-script-insertion-for-embedding/' rel='bookmark' title='Permanent Link: PHP Dynamic JavaScript SCRIPT Insertion for Embedding'>PHP Dynamic JavaScript SCRIPT Insertion for Embedding</a></li>
<li><a href='http://blog.perplexedlabs.com/2008/02/12/php-fast-large-megabyte-data-transfer-between-sessions/' rel='bookmark' title='Permanent Link: PHP fast, large (megabyte), data transfer between sessions'>PHP fast, large (megabyte), data transfer between sessions</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/05/04/php-jquery-ajax-javascript-long-polling/feed/</wfw:commentRss>
		<slash:comments>28</slash:comments>
		</item>
	</channel>
</rss>
