<?xml version="1.0" encoding="utf-8" ?>

<rss version="2.0" 
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:admin="http://webns.net/mvcb/"
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
   xmlns:wfw="http://wellformedweb.org/CommentAPI/"
   xmlns:content="http://purl.org/rss/1.0/modules/content/"
   >
<channel>
    <title>&lt;?paul - PHP</title>
    <link>http://blog.preinheimer.com/</link>
    <description>Paul Reinheimer</description>
    <dc:language>en</dc:language>
    <generator>Serendipity 1.5.3 - http://www.s9y.org/</generator>
    <pubDate>Sun, 25 Jul 2010 07:56:53 GMT</pubDate>

    <image>
        <url>http://blog.preinheimer.com/templates/bulletproof/img/s9y_banner_small.png</url>
        <title>RSS: &lt;?paul - PHP - Paul Reinheimer</title>
        <link>http://blog.preinheimer.com/</link>
        <width>100</width>
        <height>21</height>
    </image>

<item>
    <title>Where’s it Up? </title>
    <link>http://blog.preinheimer.com/index.php?/archives/359-Wheres-it-Up.html</link>
            <category>PHP</category>
    
    <comments>http://blog.preinheimer.com/index.php?/archives/359-Wheres-it-Up.html#comments</comments>
    <wfw:comment>http://blog.preinheimer.com/wfwcomment.php?cid=359</wfw:comment>

    <slash:comments>2</slash:comments>
    <wfw:commentRss>http://blog.preinheimer.com/rss.php?version=2.0&amp;type=comments&amp;cid=359</wfw:commentRss>
    

    <author>nospam@example.com (Paul Reinheimer)</author>
    <content:encoded>
    &lt;p&gt;&lt;a href=&quot;http://wonderproxy.com/&quot;&gt;WonderProxy&lt;/a&gt; is proud to present &lt;a href=&quot;http://wheresitup.com/&quot;&gt;Where’s it Up?&lt;/a&gt;, a new tool to help system administrators determine whether or not their site is up from around the world. The tool accepts a URL, and allows you to select global locations. It then attempts to connect to the given server and issue a HEAD request from the global locations you selected, and reports the results.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Key Features&lt;/b&gt;
&lt;ul&gt;
&lt;li&gt;Servers around the world&lt;/li&gt;
&lt;li&gt;Local DNS resolution on each server 
&lt;li&gt;Reports IP and timing information
&lt;li&gt;Follows a reasonable number of redirects&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Key Technology&lt;/b&gt;&lt;ul&gt;
&lt;li&gt;Curl&lt;/li&gt;
&lt;li&gt;PHP with &lt;a href=&quot;http://pecl.php.net/package/pecl_http&quot;&gt;pecl_http&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Memcached&lt;/li&gt;
&lt;li&gt;Gearman&lt;/li&gt;
&lt;li&gt;Supervisord&lt;/li&gt;
&lt;/ul&gt;&lt;/p&gt;
&lt;p&gt;
&lt;h3&gt;How it Works&lt;/h3&gt;&lt;br /&gt;
Building a reasonably robust application was trivial, thanks to being able to leverage the great technology built by others. When a request is received, an intermediate script does some basic checking, then passes off a number of jobs to Gearman (one per location requested), the job ID is handed back to the user and results will be displayed via ajax. In the background Supervisord keeps five workers running all the time, they basically spin waiting for new work. When a job arrives they first check to see if there’s a recently cached result in memcached. Failing that an SSH tunnel is used to connect to the requested server, the request is issued with curl, and the results parsed. This result information is pushed into memcached. &lt;/p&gt;

&lt;p&gt;On the client side, requests are made back to the server including the job id, and already obtained results. New results are handed off as they become available.&lt;/p&gt;

&lt;p&gt;The system also uses memcached to throttle incoming requests from a given IP based on the example given by &lt;a href=&quot;http://simonwillison.net/2009/Jan/7/ratelimitcache/&quot;&gt;Simon Willison&lt;/a&gt;. In this case I’m actually using Swatch Beat time to manage the count; the edge case over midnight is quite easy.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Gearman&lt;/b&gt; is helpful in this case for three reasons: it provides great separation between the client requesting the work, and the workers actually completing it. It also trivially allows for multiple workers to be used in the resolution of requests. Finally, it provides a very effective work queue; when the site is under heavy load it will just take a bit longer to resolve requests. A small side benefit is the ability to pull down all the workers, update the worker script, then restart them, all without affecting the jobs in queue (except for the momentary delay). &lt;/p&gt;

&lt;p&gt;&lt;b&gt;Memcached&lt;/b&gt; is helpful as it’s a quick place to store the values that handles expiry for me automagically. In this particular case, as there’s a single server involved, something like APC would provider a thinner solution, however I have a lot of experience building apps with Memcached as a storage engine, and not much using APC.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Supervisord&lt;/b&gt; is helpful as it keeps the workers running, starts them up after they crash (hasn’t happened yet) and on machine boot. &lt;a href=&quot;http://phpadvent.org/2009/daemonize-your-php-by-sean-coates&quot;&gt;The dæmonize your PHP&lt;/a&gt; post by &lt;a href=&quot;http://seancoates.com/is&quot;&gt;Sean Coates&lt;/a&gt; was incredibly helpful in getting this going. &lt;/p&gt;

&lt;p&gt;&lt;b&gt;SSH&lt;/b&gt; is helpful as it allows us to display accurate timing information from that machine to the requested location. We could use our own proxies directly, but then we&#039;re dealing with the time to connect from Washington -&gt; Sydney -&gt; Your website. That extra bit of math in the middle is only moderately tricky, but more importantly: inconsistent. The tunnel lets us execute the commands directly on the remote server.&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;
&lt;h3&gt;Why we built it&lt;/h3&gt;&lt;br /&gt;
We&#039;ve used tools in the past that allow us to quickly check if a site is up or down. They&#039;re nifty, but these days many sites are using &lt;a href=&quot;http://en.wikipedia.org/wiki/Anycast&quot;&gt;anycast&lt;/a&gt; DNS to publish different IPs globally, with multiple servers and multiple data centers. Simple tools simply aren&#039;t able to adequately express whether or not a site is up, or down. By leveraging the network of servers we already had, building a more advanced and complete tool was easy.&lt;/p&gt; 
    </content:encoded>

    <pubDate>Wed, 21 Jul 2010 12:04:35 -0400</pubDate>
    <guid isPermaLink="false">http://blog.preinheimer.com/index.php?/archives/359-guid.html</guid>
    
</item>
<item>
    <title>XHGui Improvements</title>
    <link>http://blog.preinheimer.com/index.php?/archives/358-XHGui-Improvements.html</link>
            <category>PHP</category>
    
    <comments>http://blog.preinheimer.com/index.php?/archives/358-XHGui-Improvements.html#comments</comments>
    <wfw:comment>http://blog.preinheimer.com/wfwcomment.php?cid=358</wfw:comment>

    <slash:comments>3</slash:comments>
    <wfw:commentRss>http://blog.preinheimer.com/rss.php?version=2.0&amp;type=comments&amp;cid=358</wfw:commentRss>
    

    <author>nospam@example.com (Paul Reinheimer)</author>
    <content:encoded>
    I merged my Highcharts branch into master today, including a bunch of improvements to the GUI for XHProf including:
&lt;ul&gt;
&lt;li&gt;Using Highcharts for graphing URLs over time, which allows for multiple axis per graph&lt;/li&gt;
&lt;li&gt;Adding a pie chart to the individual run page to show where time was spent on page load&lt;/li&gt;
&lt;li&gt;Ability to merge various calls for display within the pie chart (e.g. mysql_* into mysql)&lt;/li&gt;
&lt;li&gt;Switched to the javascript tablesorter for the single run results&lt;/li&gt;
&lt;li&gt;Filter to allow you to view results only from a specific server or domain&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;http://highcharts.com/&quot;&gt;Highcharts&lt;/a&gt; is a commercial library in Javascript for graphing, it&#039;s really easy to use and offers more features than the Google libraries used previously. I&#039;ve purchased a license for the application so that anyone using this gui, regardless of the type of application they&#039;re profiling, can do so. The Highcharts integration was done by my colleague &lt;a href=&quot;http://ca.linkedin.com/pub/graham-slater/19/37/177&quot;&gt;Graham Slater&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I really didn&#039;t consider the branch &quot;done&quot;, the diff page still needs the pie charts, and to be switched over to the new table format. However, the license file received some serious updates to accurately represent the state of the code base and I considered making that file accurate to be a high priority.&lt;/p&gt;

&lt;h3&gt;Screenshots&lt;/h3&gt;
&lt;p&gt;&lt;a class=&quot;serendipity_image_link&quot;  href=&#039;http://blog.preinheimer.com/uploads/highchart2.png&#039;&gt;&lt;!-- s9ymdb:20 --&gt;&lt;img class=&quot;serendipity_image_left&quot; width=&quot;110&quot; height=&quot;62&quot;  src=&quot;http://blog.preinheimer.com/uploads/highchart2.serendipityThumb.png&quot;  alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;a class=&quot;serendipity_image_link&quot;  href=&#039;http://blog.preinheimer.com/uploads/2010-06-14_1115.png&#039;&gt;&lt;!-- s9ymdb:19 --&gt;&lt;img class=&quot;serendipity_image_left&quot; width=&quot;110&quot; height=&quot;62&quot;  src=&quot;http://blog.preinheimer.com/uploads/2010-06-14_1115.serendipityThumb.png&quot;  alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;

&lt;p&gt;As always, the code is available at &lt;a href=&quot;http://github.com/preinheimer/xhprof&quot;&gt;GitHub&lt;/a&gt;&lt;/p&gt; 
    </content:encoded>

    <pubDate>Mon, 14 Jun 2010 11:15:54 -0400</pubDate>
    <guid isPermaLink="false">http://blog.preinheimer.com/index.php?/archives/358-guid.html</guid>
    
</item>
<item>
    <title>The accountability problem - Concluded</title>
    <link>http://blog.preinheimer.com/index.php?/archives/357-The-accountability-problem-Concluded.html</link>
            <category>PHP</category>
    
    <comments>http://blog.preinheimer.com/index.php?/archives/357-The-accountability-problem-Concluded.html#comments</comments>
    <wfw:comment>http://blog.preinheimer.com/wfwcomment.php?cid=357</wfw:comment>

    <slash:comments>6</slash:comments>
    <wfw:commentRss>http://blog.preinheimer.com/rss.php?version=2.0&amp;type=comments&amp;cid=357</wfw:commentRss>
    

    <author>nospam@example.com (Paul Reinheimer)</author>
    <content:encoded>
    &lt;p&gt;Last week I blogged about &lt;a href=&quot;http://blog.preinheimer.com/index.php?/archives/356-The-accountability-problem.html&quot;&gt;the accountability problem&lt;/a&gt;: how to handle tracking who made what change, and providing the ability to revert it.&lt;br /&gt;

Thanks to some great comments, and offline discussions, I&amp;rsquo;m seeing a few options, and have changed my mind on my desired approach. There&#039;s also some great technical reading on the subject: &lt;a href=&quot;http://en.wikipedia.org/wiki/Slowly_changing_dimension&quot;&gt;Slowly changing dimension&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;Option 1: Store history in the table&lt;/h3&gt;
&lt;p&gt;This was the approach I considered initially. Storing each revision of a record in the table, with an active bit set to indicate which version is active. More details in the &lt;a href=&quot;http://blog.preinheimer.com/index.php?/archives/356-The-accountability-problem.html&quot;&gt;original post&lt;/a&gt;. This approach is simple: it doesn&amp;rsquo;t require more tables, queries are still pretty simple, and with a combination of timestamps it&amp;rsquo;s easy to track down associated records that were changed. &lt;/p&gt;

&lt;p&gt;The downsides are pretty clear, and somewhat damaging. The number of rows in the most commonly hit tables will grow much faster than the table overall, which is sub-optimal. It can also complicate issues like reporting and the like. In the case of highly normalized designs this design will likely break down, as you&amp;rsquo;ll need to match revisions across multiple tables on every query.&lt;/p&gt; 

&lt;span style=&quot;font-family: verdana&quot;&gt;classes&lt;/span&gt;
&lt;table border=&quot;1&quot;&gt;
&lt;tr&gt;&lt;td&gt;id&lt;/td&gt;&lt;td&gt;version&lt;/td&gt;&lt;td&gt;active&lt;/td&gt;&lt;td&gt;session&lt;/td&gt;&lt;td&gt;title&lt;/td&gt;&lt;td&gt;start_date&lt;/td&gt;&lt;td&gt;end_date&lt;/td&gt;&lt;td&gt;timestamp&lt;/td&gt;&lt;td&gt;login_id&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;Spring&lt;/td&gt;&lt;td&gt;Dancing 101&lt;/td&gt;&lt;td&gt;May 1, 2009&lt;/td&gt;&lt;td&gt;May 28, 2009&lt;/td&gt;&lt;td&gt;1274467708&lt;/td&gt;&lt;td&gt;	23&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Spring&lt;/td&gt;&lt;td&gt;Dancing 101&lt;/td&gt;&lt;td&gt;May 1, 2010&lt;/td&gt;&lt;td&gt;May 28, 2010&lt;/td&gt;&lt;td&gt;1274467709&lt;/td&gt;&lt;td&gt;	23&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Summer&lt;/td&gt;&lt;td&gt;Dancing 102&lt;/td&gt;&lt;td&gt;June 1, 2010&lt;/td&gt;&lt;td&gt;July 15, 2010	&lt;/td&gt;&lt;td&gt;1274467710&lt;/td&gt;&lt;td&gt;27&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Fall&lt;/td&gt;&lt;td&gt;Dancing 101&lt;/td&gt;&lt;td&gt;September 1, 2010&lt;/td&gt;&lt;td&gt;September 28, 2010&lt;/td&gt;&lt;td&gt;1274467808&lt;/td&gt;&lt;td&gt;35&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;
&lt;p&gt;I would have used this approach in a smaller project. The system I&amp;rsquo;m replacing has been running for ten years, I have to plan for this lasting that long with only minimal maintenance. I&amp;rsquo;d rather not watch those row counts become 3-5x what they should be. &lt;/p&gt;

&lt;h3&gt;Option 2: Store histories in an external table&lt;/h3&gt;
&lt;p&gt;Rather than storing the histories in the regular table (say classes) creating a duplicate table (classes_history) and storing the revisions there. This helps trim the size of your most heavily used tables, and keeps things relatively simple. &lt;/p&gt;

&lt;p&gt;While trimming the row count, it does double the table count, and still fails to handle complicated objects, or normalized table structures where a single datum requires multiple tables. &lt;/p&gt;
&lt;span style=&quot;font-family: verdana&quot;&gt;classes&lt;/span&gt;
&lt;table border=&quot;1&quot;&gt;
&lt;tr&gt;&lt;td&gt;id&lt;/td&gt;&lt;td&gt;session&lt;/td&gt;&lt;td&gt;title&lt;/td&gt;&lt;td&gt;start_date&lt;/td&gt;&lt;td&gt;end_date&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Spring&lt;/td&gt;&lt;td&gt;Dancing 101&lt;/td&gt;&lt;td&gt;May 1, 2010&lt;/td&gt;&lt;td&gt;May 28, 2010&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;Summer&lt;/td&gt;&lt;td&gt;Dancing 102&lt;/td&gt;&lt;td&gt;June 1, 2010&lt;/td&gt;&lt;td&gt;July 15, 2010&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;Fall&lt;/td&gt;&lt;td&gt;Dancing 101&lt;/td&gt;&lt;td&gt;September 1, 2010&lt;/td&gt;&lt;td&gt;September 28, 2010&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;span style=&quot;font-family: verdana&quot;&gt;classes_history&lt;/span&gt;
&lt;table border=&quot;1&quot;&gt;
&lt;tr&gt;&lt;td&gt;id&lt;/td&gt;&lt;td&gt;version&lt;/td&gt;&lt;td&gt;active&lt;/td&gt;&lt;td&gt;session&lt;/td&gt;&lt;td&gt;title&lt;/td&gt;&lt;td&gt;start_date&lt;/td&gt;&lt;td&gt;end_date&lt;/td&gt;&lt;td&gt;timestamp&lt;/td&gt;&lt;td&gt;login_id&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;Spring&lt;/td&gt;&lt;td&gt;Dancing 101&lt;/td&gt;&lt;td&gt;May 1, 2009&lt;/td&gt;&lt;td&gt;May 28, 2009&lt;/td&gt;&lt;td&gt;1274467708&lt;/td&gt;&lt;td&gt;	23&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;
&lt;p&gt;I&amp;rsquo;ll be using this method for my upcoming project. &lt;/p&gt;



&lt;h3&gt;Option 3: Store serialized information &lt;/h3&gt;
&lt;p&gt;Rather than storing rows when information is modified, the entire encapsulating object is serialized and stored. This can be done either with one table per object type, or in one large history table.&lt;/p&gt;

&lt;p&gt;The advantage here is that each revision stands alone and complete. Composite records are stored complete, and in a single location. This makes obtaining an accurate history relatively easy. On the downside, since information is serialized it&amp;rsquo;s hard to locate specific pieces of information via sql. This approach seems perfect for complex objects where a single composite record spans many tables, but a little overkill for this specific case.&lt;/p&gt;

&lt;span style=&quot;font-family: verdana&quot;&gt;classes&lt;/span&gt;
&lt;table border=&quot;1&quot;&gt;
&lt;tr&gt;&lt;td&gt;id&lt;/td&gt;&lt;td&gt;version&lt;/td&gt;&lt;td&gt;active&lt;/td&gt;&lt;td&gt;session&lt;/td&gt;&lt;td&gt;title&lt;/td&gt;&lt;td&gt;start_date&lt;/td&gt;&lt;td&gt;end_date&lt;/td&gt;&lt;td&gt;timestamp&lt;/td&gt;&lt;td&gt;login_id&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Spring&lt;/td&gt;&lt;td&gt;Dancing 101&lt;/td&gt;&lt;td&gt;May 1, 2010&lt;/td&gt;&lt;td&gt;May 28, 2010&lt;/td&gt;&lt;td&gt;1274467709&lt;/td&gt;&lt;td&gt;	23&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Summer&lt;/td&gt;&lt;td&gt;Dancing 102&lt;/td&gt;&lt;td&gt;June 1, 2010&lt;/td&gt;&lt;td&gt;July 15, 2010	&lt;/td&gt;&lt;td&gt;1274467710&lt;/td&gt;&lt;td&gt;27&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Fall&lt;/td&gt;&lt;td&gt;Dancing 101&lt;/td&gt;&lt;td&gt;September 1, 2010&lt;/td&gt;&lt;td&gt;September 28, 2010&lt;/td&gt;&lt;td&gt;1274467808&lt;/td&gt;&lt;td&gt;35&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;span style=&quot;font-family: verdana&quot;&gt;history&lt;/span&gt;
&lt;table border=&quot;1&quot;&gt;
&lt;tr&gt;&lt;td&gt;id&lt;/td&gt;&lt;td&gt;version&lt;/td&gt;&lt;td&gt;table&lt;/td&gt;&lt;td&gt;data&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;classes&lt;/td&gt;&lt;td&gt;a:8:{s:2:&quot;id&quot;;i:1;s:7:&quot;version&quot;;i:1;s:7:&quot;session&quot;;s:6:&quot;spring&quot;;s:5:&quot;title&quot;;s:11:&quot;Dancing 101&quot;;s:10:&quot;start_date&quot;;s:11:&quot;May 1, 2009&quot;;s:8:&quot;end_date&quot;;s:12:&quot;May 28, 2009&quot;;s:9:&quot;timestamp&quot;;s:10:&quot;1274467708&quot;;s:8:&quot;login_id&quot;;i:23;}&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;br /&gt;
&lt;p&gt;I&#039;ll consider this for larger projects with complex objects.&lt;/p&gt;

&lt;br /&gt;
&lt;h3&gt;Other points to ponder:&lt;/h3&gt;
&lt;ul&gt;
 &lt;li&gt;How long will you store histories?&lt;/li&gt;
 &lt;ul&gt;
  &lt;li&gt;Forever (&amp;infin;)&lt;/li&gt;
  &lt;li&gt;Some fixed period of time (30 days)&lt;/li&gt;
  &lt;li&gt;Application specific (e.g. storing change to a class for three months after it ends)&lt;/li&gt;
 &lt;/ul&gt;
 &lt;li&gt;Storage engine for history&lt;/li&gt;
 &lt;ul&gt;
  &lt;li&gt;MyISAM&lt;/li&gt;
  &lt;li&gt;Archive&lt;/li&gt;
 &lt;/ul&gt;
  &lt;li&gt;Backup strategies &lt;/li&gt;
 &lt;ul&gt;
 &lt;li&gt;History tables are all appends&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;
&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;
Thanks to everyone who commented on the last post. 
    </content:encoded>

    <pubDate>Tue, 25 May 2010 13:43:32 -0400</pubDate>
    <guid isPermaLink="false">http://blog.preinheimer.com/index.php?/archives/357-guid.html</guid>
    
</item>
<item>
    <title>The accountability problem</title>
    <link>http://blog.preinheimer.com/index.php?/archives/356-The-accountability-problem.html</link>
            <category>PHP</category>
    
    <comments>http://blog.preinheimer.com/index.php?/archives/356-The-accountability-problem.html#comments</comments>
    <wfw:comment>http://blog.preinheimer.com/wfwcomment.php?cid=356</wfw:comment>

    <slash:comments>16</slash:comments>
    <wfw:commentRss>http://blog.preinheimer.com/rss.php?version=2.0&amp;type=comments&amp;cid=356</wfw:commentRss>
    

    <author>nospam@example.com (Paul Reinheimer)</author>
    <content:encoded>
    &lt;p&gt;I&#039;m working on a volunteer side project for a local dance studio, the website will handle the standard sort of things: creating classes, registering students, matching leads to follows, etc. &lt;/p&gt;

&lt;p&gt;We would like to have some level of accountability when various records (user, class, etc) are modified. That way if problems arise later, we can see who did what, and when. If needs be, even roll the change back.&lt;/p&gt;

&lt;p&gt;This must be a common problem, how have you solved it?&lt;/p&gt;

&lt;p&gt;My (proposed) solution is something like this:

Take an ordinary table like class:&lt;table border=&quot;1&quot;&gt;
&lt;tr&gt;&lt;td&gt;id&lt;/td&gt;&lt;td&gt;session&lt;/td&gt;&lt;td&gt;title&lt;/td&gt;&lt;td&gt;start_date&lt;/td&gt;&lt;td&gt;end_date&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Spring&lt;/td&gt;&lt;td&gt;Dancing 101&lt;/td&gt;&lt;td&gt;May 1, 2010&lt;/td&gt;&lt;td&gt;May 28, 2010&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;Summer&lt;/td&gt;&lt;td&gt;Dancing 102&lt;/td&gt;&lt;td&gt;June 1, 2010&lt;/td&gt;&lt;td&gt;July 15, 2010&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;Fall&lt;/td&gt;&lt;td&gt;Dancing 101&lt;/td&gt;&lt;td&gt;September 1, 2010&lt;/td&gt;&lt;td&gt;September 28, 2010&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;&lt;/p&gt;
&lt;p&gt;And add a few columns to for change management &lt;table border=&quot;1&quot;&gt;
&lt;tr&gt;&lt;td&gt;id&lt;/td&gt;&lt;td&gt;version&lt;/td&gt;&lt;td&gt;active&lt;/td&gt;&lt;td&gt;session&lt;/td&gt;&lt;td&gt;title&lt;/td&gt;&lt;td&gt;start_date&lt;/td&gt;&lt;td&gt;end_date&lt;/td&gt;&lt;td&gt;timestamp&lt;/td&gt;&lt;td&gt;login_id&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;Spring&lt;/td&gt;&lt;td&gt;Dancing 101&lt;/td&gt;&lt;td&gt;May 1, 2009&lt;/td&gt;&lt;td&gt;May 28, 2009&lt;/td&gt;&lt;td&gt;1274467708&lt;/td&gt;&lt;td&gt;	23&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Spring&lt;/td&gt;&lt;td&gt;Dancing 101&lt;/td&gt;&lt;td&gt;May 1, 2010&lt;/td&gt;&lt;td&gt;May 28, 2010&lt;/td&gt;&lt;td&gt;1274467709&lt;/td&gt;&lt;td&gt;	23&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Summer&lt;/td&gt;&lt;td&gt;Dancing 102&lt;/td&gt;&lt;td&gt;June 1, 2010&lt;/td&gt;&lt;td&gt;July 15, 2010	&lt;/td&gt;&lt;td&gt;1274467710&lt;/td&gt;&lt;td&gt;27&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Fall&lt;/td&gt;&lt;td&gt;Dancing 101&lt;/td&gt;&lt;td&gt;September 1, 2010&lt;/td&gt;&lt;td&gt;September 28, 2010&lt;/td&gt;&lt;td&gt;1274467808&lt;/td&gt;&lt;td&gt;35&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;&lt;/p&gt;

&lt;p&gt;The list of classes is obtained by selecting where the active bit is set. Administrators will be able to look at the list of changes and see who made them, and when. I’m tracking login id rather than simply user id so information like IP, and timestamp can be associated with the login, rather than simply who. In the case of reverting a change, the desired version will be duplicated into a new row, and have the active bit set. In reality active will always be the highest revision number (and latest date) but the duplication makes queries easier. &lt;/p&gt;

&lt;p&gt;I think there’s a lot of power in this approach, but it is a bit complicated. Thoughts?&lt;/p&gt; 
    </content:encoded>

    <pubDate>Fri, 21 May 2010 13:39:43 -0400</pubDate>
    <guid isPermaLink="false">http://blog.preinheimer.com/index.php?/archives/356-guid.html</guid>
    
</item>
<item>
    <title>A GUI for XHProf</title>
    <link>http://blog.preinheimer.com/index.php?/archives/355-A-GUI-for-XHProf.html</link>
            <category>PHP</category>
    
    <comments>http://blog.preinheimer.com/index.php?/archives/355-A-GUI-for-XHProf.html#comments</comments>
    <wfw:comment>http://blog.preinheimer.com/wfwcomment.php?cid=355</wfw:comment>

    <slash:comments>1</slash:comments>
    <wfw:commentRss>http://blog.preinheimer.com/rss.php?version=2.0&amp;type=comments&amp;cid=355</wfw:commentRss>
    

    <author>nospam@example.com (Paul Reinheimer)</author>
    <content:encoded>
    &lt;p&gt;Facebook was kind enough to open source the &lt;a href=&quot;http://github.com/facebook/xhprof&quot;&gt;XHProf&lt;/a&gt; extension last year, but it flew under my radar until I saw a presentation including it earlier this year when they showed off &lt;a href=&quot;http://github.com/facebook/hiphop-php&quot;&gt;HipHop&lt;/a&gt;. XHProf provides profiling information about your application, while being lightweight enough to run on a production server (against a percentage of requests). Once we got it installed we ran into a few limitations with the existing GUI. With the help of Graham Slater (one of our front end designers) I hacked on the Facebook code to come up with our own flavor. Let me stress the word “hack.&quot; I was given some really tough deadlines for this project so functionality was literally hacked into the existing codebase.&lt;/p&gt;

&lt;h3&gt;Key Features:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Nice interface&lt;/li&gt;
&lt;li&gt;MySQL backend for data storage&lt;/li&gt;
&lt;li&gt;Stores key values such as Peak Memory Usage, Wall Time, and CPU Ticks in the database directly for easy lookup.&lt;/li&gt;
&lt;li&gt;Stores Get, Post, and Cookie data for easy comparison (so you can determine why a run hit that execution path)&lt;/li&gt;
&lt;li&gt;Display includes easy lookup for worst runs, most recent, etc.&lt;/li&gt;
&lt;li&gt;Support for profiling a percentage of requests, or on demand&lt;/li&gt;
&lt;li&gt;Support for not including a link to profile requests on certain files (like xml documents, images, etc).&lt;/li&gt;
&lt;li&gt;Uses Google Data Visualization API to graph runs over time&lt;/li&gt;
&lt;li&gt;Concept of “Similar” urls, to handle ?story=23 and ?story=24 having different URLs, but similar to identical code execution paths&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Screenshots:&lt;/h3&gt;
&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://blog.preinheimer.com/uploads/2010-04-26_1443.png&#039;&gt;&lt;!-- s9ymdb:11 --&gt;&lt;img class=&quot;serendipity_image_left&quot; width=&quot;110&quot; height=&quot;65&quot; style=&quot;float: left; border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://blog.preinheimer.com/uploads/2010-04-26_1443.serendipityThumb.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://blog.preinheimer.com/uploads/2010-04-26_1444.png&#039;&gt;&lt;!-- s9ymdb:12 --&gt;&lt;img class=&quot;serendipity_image_left&quot; width=&quot;110&quot; height=&quot;65&quot; style=&quot;float: left; border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://blog.preinheimer.com/uploads/2010-04-26_1444.serendipityThumb.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://blog.preinheimer.com/uploads/2010-04-26_1445.png&#039;&gt;&lt;!-- s9ymdb:13 --&gt;&lt;img class=&quot;serendipity_image_left&quot; width=&quot;110&quot; height=&quot;65&quot; style=&quot;float: left; border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://blog.preinheimer.com/uploads/2010-04-26_1445.serendipityThumb.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;a class=&#039;serendipity_image_link&#039; href=&#039;http://blog.preinheimer.com/uploads/2010-04-26_1447.png&#039;&gt;&lt;!-- s9ymdb:14 --&gt;&lt;img class=&quot;serendipity_image_left&quot; width=&quot;110&quot; height=&quot;65&quot; style=&quot;float: left; border: 0px; padding-left: 5px; padding-right: 5px;&quot; src=&quot;http://blog.preinheimer.com/uploads/2010-04-26_1447.serendipityThumb.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;
&lt;br style=&quot;clear: both;&quot; /&gt;

&lt;h3&gt;Download:&lt;/h3&gt;
&lt;p&gt;Please download the source code from &lt;a href=&quot;http://github.com/preinheimer/xhprof&quot;&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;Warnings:&lt;/h3&gt;
&lt;p&gt;Seriously, we hacked this up! Please let me know if you encounter any bugs, there&#039;s probably a bunch. &lt;/p&gt; 
    </content:encoded>

    <pubDate>Mon, 26 Apr 2010 14:54:58 -0400</pubDate>
    <guid isPermaLink="false">http://blog.preinheimer.com/index.php?/archives/355-guid.html</guid>
    
</item>
<item>
    <title>Memory usage in PHP</title>
    <link>http://blog.preinheimer.com/index.php?/archives/354-Memory-usage-in-PHP.html</link>
            <category>PHP</category>
    
    <comments>http://blog.preinheimer.com/index.php?/archives/354-Memory-usage-in-PHP.html#comments</comments>
    <wfw:comment>http://blog.preinheimer.com/wfwcomment.php?cid=354</wfw:comment>

    <slash:comments>22</slash:comments>
    <wfw:commentRss>http://blog.preinheimer.com/rss.php?version=2.0&amp;type=comments&amp;cid=354</wfw:commentRss>
    

    <author>nospam@example.com (Paul Reinheimer)</author>
    <content:encoded>
    &lt;p&gt;A colleague called me over today for some help with a memory usage issue in his PHP script. The script was performing the basic (but critical) task of importing data, pulling it in from MySQL in large chunks, then exporting it elsewhere. He was receiving the wonderful &quot;&lt;span style=&quot;font-family: Verdana, Helvetica; background-color: #EFEFDF;&quot;&gt;Fatal error: Allowed memory size of XXXX bytes exhausted (tried to allocate YY bytes&lt;/span&gt;)&quot; error message.&lt;/p&gt;

&lt;p&gt;The code was following a basic flow:&lt;br /&gt;
10 Get stuff from the database (large array of arrays)&lt;br /&gt;
20 Iterate over it, doing stuff&lt;/br&gt;
30 Goto 10&lt;/p&gt;

&lt;p&gt;I fixed the memory usage exceeded problem with an &lt;span style=&quot;font-family: Verdana, Helvetica; background-color: #EFEFDF;&quot;&gt;unset()&lt;/span&gt;, at the end of a loop.&lt;/p&gt;

&lt;p&gt;Take a look at this sample program:
&lt;span style=&quot;font-family: Verdana, Helvetica; background-color: #EFEFDF; display: block; white-space: pre;&quot;&gt;loopOverStuff();

function loopOverStuff()
{
    $var = null;
    for($i = 0; $i &lt; 10; $i++)
    {
         $var = getData();
         //Do stuff
    }
}


function getData()
{
    $a = str_repeat(&quot;This string is exactly 40 characters long&quot;, 20000);
    return $a;
}
&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;The important thing to realize is that PHP will end up needing around twice as much memory as &lt;span style=&quot;font-family: Verdana, Helvetica; background-color: #EFEFDF;&quot;&gt;getData()&lt;/span&gt; takes. The problem is the line &lt;span style=&quot;font-family: Verdana, Helvetica; background-color: #EFEFDF;&quot;&gt;$var = getData()&lt;/span&gt;. The first time it is called &lt;span style=&quot;font-family: Verdana, Helvetica; background-color: #EFEFDF;&quot;&gt;$var&lt;/span&gt; is incredibly small, it&#039;s clobbered and the return value of &lt;span style=&quot;font-family: Verdana, Helvetica; background-color: #EFEFDF;&quot;&gt;getData()&lt;/span&gt;is assigned to it. The second time through the loop &lt;span style=&quot;font-family: Verdana, Helvetica; background-color: #EFEFDF;&quot;&gt;$var&lt;/span&gt; still holds the value from the previous iteration, so while &lt;span style=&quot;font-family: Verdana, Helvetica; background-color: #EFEFDF;&quot;&gt;getData()&lt;/span&gt; is executing you&#039;re maintaining the original data (in &lt;span style=&quot;font-family: Verdana, Helvetica; background-color: #EFEFDF;&quot;&gt;$var&lt;/span&gt;), and a whole new set (being built in &lt;span style=&quot;font-family: Verdana, Helvetica; background-color: #EFEFDF;&quot;&gt;getData()&lt;/span&gt;).&lt;/p&gt;

&lt;p&gt;Fixing this is incredibly easy:
&lt;span style=&quot;font-family: Verdana, Helvetica; background-color: #EFEFDF; display: block; white-space: pre;&quot;&gt;function loopOverStuff()
{
    $var = null;
    for($i = 0; $i &lt; 10; $i++)
     {
         $var = stealMemories();
        //Do Stuff
        unset($var);
     }
}
&lt;/span&gt;&lt;/p&gt;
This way we avoid the duplication in memory of those values on that line. To see this happen in more detail take a look at this sample script with ouptut: &lt;a href=&quot;http://example.preinheimer.com/memory-usage-example.php&quot;&gt;memory-usage-example.php&lt;/a&gt;.&lt;/p&gt;

&lt;/p&gt;This isn&#039;t critical, except when it is. Once &lt;span style=&quot;font-family: Verdana, Helvetica; background-color: #EFEFDF;&quot;&gt;loopOverStuff()&lt;/span&gt; completes, and the function ends. The memory is released back to the rest of PHP automatically. You&#039;ll only run into problems where &lt;span style=&quot;font-family: Verdana, Helvetica; background-color: #EFEFDF;&quot;&gt;Other Stuff + (2 * memory needed in loop) &gt; Memory Limit&lt;/span&gt;. There are better architectures available to avoid the issue entirely (like not storing everything in the array, just to iterate over it later) but they&#039;re an issue for a different post. &lt;/p&gt;

&lt;p&gt;For a very simple base case demonstration of the issue take a look at the &lt;a href=&quot;http://example.preinheimer.com/memory-usage-simple-example.php&quot;&gt;simple example&lt;/a&gt;.&lt;/p&gt; 
    </content:encoded>

    <pubDate>Fri, 19 Mar 2010 15:38:54 -0400</pubDate>
    <guid isPermaLink="false">http://blog.preinheimer.com/index.php?/archives/354-guid.html</guid>
    
</item>
<item>
    <title>Search without the Database</title>
    <link>http://blog.preinheimer.com/index.php?/archives/353-Search-without-the-Database.html</link>
            <category>PHP</category>
    
    <comments>http://blog.preinheimer.com/index.php?/archives/353-Search-without-the-Database.html#comments</comments>
    <wfw:comment>http://blog.preinheimer.com/wfwcomment.php?cid=353</wfw:comment>

    <slash:comments>0</slash:comments>
    <wfw:commentRss>http://blog.preinheimer.com/rss.php?version=2.0&amp;type=comments&amp;cid=353</wfw:commentRss>
    

    <author>nospam@example.com (Paul Reinheimer)</author>
    <content:encoded>
    I gave my second talk at #ConFoo today, this one was on Searching without the Database. This talk was based on a situation at work where I replaced a MySQL search solution with a Sphinx + Memcached solution for higher performance. If you&#039;re interested, here&#039;s the slides: &lt;a href=&quot;http://blog.preinheimer.com/talks/Search without the DB - ConFoo 2010.pdf&quot;&gt;Search without the DB - ConFoo 2010.pdf&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
If you attended my talk, you can rate it on the &lt;a href=&quot;http://joind.in/talk/view/1383&quot;&gt;Joind.in Page&lt;/a&gt; 
    </content:encoded>

    <pubDate>Fri, 12 Mar 2010 17:27:35 -0500</pubDate>
    <guid isPermaLink="false">http://blog.preinheimer.com/index.php?/archives/353-guid.html</guid>
    
</item>
<item>
    <title>ConFoo - PHP in the Enterprise</title>
    <link>http://blog.preinheimer.com/index.php?/archives/352-ConFoo-PHP-in-the-Enterprise.html</link>
            <category>PHP</category>
    
    <comments>http://blog.preinheimer.com/index.php?/archives/352-ConFoo-PHP-in-the-Enterprise.html#comments</comments>
    <wfw:comment>http://blog.preinheimer.com/wfwcomment.php?cid=352</wfw:comment>

    <slash:comments>1</slash:comments>
    <wfw:commentRss>http://blog.preinheimer.com/rss.php?version=2.0&amp;type=comments&amp;cid=352</wfw:commentRss>
    

    <author>nospam@example.com (Paul Reinheimer)</author>
    <content:encoded>
    I just finished my PHP in the Enterprise talk at &lt;a href=&quot;http://confoo.ca/&quot;&gt;ConFoo&lt;/a&gt;, Slides are available here: &lt;a href=&quot;http://blog.preinheimer.com/talks/PHP in the Enterprise - Confoo March 2010.pdf&quot;&gt;PHP in the Enterprise - ConFoo March 2010&lt;/a&gt; 
    </content:encoded>

    <pubDate>Wed, 10 Mar 2010 16:24:43 -0500</pubDate>
    <guid isPermaLink="false">http://blog.preinheimer.com/index.php?/archives/352-guid.html</guid>
    
</item>
<item>
    <title>A Customer found a Bug</title>
    <link>http://blog.preinheimer.com/index.php?/archives/350-A-Customer-found-a-Bug.html</link>
            <category>PHP</category>
    
    <comments>http://blog.preinheimer.com/index.php?/archives/350-A-Customer-found-a-Bug.html#comments</comments>
    <wfw:comment>http://blog.preinheimer.com/wfwcomment.php?cid=350</wfw:comment>

    <slash:comments>7</slash:comments>
    <wfw:commentRss>http://blog.preinheimer.com/rss.php?version=2.0&amp;type=comments&amp;cid=350</wfw:commentRss>
    

    <author>nospam@example.com (Paul Reinheimer)</author>
    <content:encoded>
    So this morning I woke up to an email (well, several, but only one is relevant) from a &lt;a href=&quot;http://wonderproxy.com/?from=blog&quot; title=&quot;WonderProxy is a GeoIP proxy service&quot;&gt;WonderProxy&lt;/a&gt; customer. They had discovered a bug in the website. A rather serious bug mind you, as it stopped them from adding servers to their account, and thus prevented them from actually using the service.&lt;br /&gt;
&lt;br /&gt;
Here’s how I approached the problem:&lt;br /&gt;
&lt;ol&gt;&lt;br /&gt;
&lt;li&gt;Reproduce the issue&lt;br /&gt;
I logged into the website as the customer and attempted to reproduce the issue, no problems. I tried again on the development version of the website and reproduced it there as well.&lt;/li&gt;&lt;br /&gt;
&lt;li&gt;Estimate bug fix&lt;br /&gt;
I took a look at the code and estimated that I could resolve the bug in under ten minutes. The code base for the WonderProxy website is rather small, and bugs there can’t affect customer usage (unless they break the DB) so I wasn’t too concerned about accidentally taking something down. The short time frame for release also let me fix the bug before contacting the customer.&lt;/li&gt;&lt;br /&gt;
&lt;li&gt;Fix the bug&lt;br /&gt;
Found the bug, fixed it. Tested it out in development, checked other accounts to look for unrelated breaks. Finally I rolled it out from development to production and checked again.&lt;/li&gt;&lt;br /&gt;
&lt;li&gt;Contact Customer&lt;br /&gt;
Replying to the customer I indicated that problem was indeed on our end (the customer wasn’t using the site wrong or anything), and that it had been fixed. Additionally I had updated the billing period to reflect the fact that the account hadn’t been usable up until the fix was applied. As a token of appreciation I upgraded the account slightly.&lt;/li&gt;&lt;br /&gt;
&lt;/ol&gt;&lt;br /&gt;
&lt;br /&gt;
Overall, I found the process rather pain free. When I look at it from a customer’s prospective, I also feel like it was a reasonable problem resolution. &lt;br /&gt;
&lt;br /&gt;
I’d like to delve deeper into how this problem made it onto my production website, but that’s a separate post. For now I’m really just looking at the customer service side, there was a total of 67 minutes between the initial contact (on the part of the customer) and my reply with the fix in place.&lt;br /&gt;
&lt;br /&gt;
Thoughts? How did I do? 
    </content:encoded>

    <pubDate>Mon, 11 Jan 2010 21:20:41 -0500</pubDate>
    <guid isPermaLink="false">http://blog.preinheimer.com/index.php?/archives/350-guid.html</guid>
    
</item>
<item>
    <title>.htaccess to httpd.conf</title>
    <link>http://blog.preinheimer.com/index.php?/archives/340-.htaccess-to-httpd.conf.html</link>
            <category>PHP</category>
    
    <comments>http://blog.preinheimer.com/index.php?/archives/340-.htaccess-to-httpd.conf.html#comments</comments>
    <wfw:comment>http://blog.preinheimer.com/wfwcomment.php?cid=340</wfw:comment>

    <slash:comments>10</slash:comments>
    <wfw:commentRss>http://blog.preinheimer.com/rss.php?version=2.0&amp;type=comments&amp;cid=340</wfw:commentRss>
    

    <author>nospam@example.com (Paul Reinheimer)</author>
    <content:encoded>
    In the past week I&#039;ve dealt with some servers that were seeing some rather heavy load, with more traffic on the way. I noticed that the servers had &lt;a href=&quot;http://httpd.apache.org/docs/2.2/mod/core.html#allowoverride&quot;&gt;AllowOverride&lt;/a&gt; All in their httpd.conf files, which immediately struck me as a problem. While .htaccess files can be quite handy during development (AllowOverride instructs Apache to process those .htaccess files), they&#039;re also quite horrible in production due to the performance hits involved in using them. Since the files allow configuration to be changed on the fly, they need to be read on every request. This requires a lot of unnecessary stat calls with each and every request. &lt;br /&gt;
&lt;br /&gt;
I approached the development team requesting permission to turn the option off, in order to squeeze some extra performance out of each server. Rather than the happy smiles and agreement I expected, I was instead met with nervousness and uncertainty. Since the front end team uses them constantly, there was concerns that the change over from .htaccess to httpd.conf would consume a lot of time, and would be very difficult to maintain moving forward.&lt;br /&gt;
&lt;br /&gt;
In order to allay their fears I created a .htaccess to httpd.conf script with the help of my friend &lt;a href=&quot;http://drbacchus.com/&quot;&gt;Rich Bowen&lt;/a&gt;. It&#039;s designed to be executed from the command line from a sufficiently permissioned user from within the document root. It recursively scans all sub directories looking for .htaccess files, reads them in, and spits it all back out. There&#039;s a bit of intelligence involved in order to present the contents of .htaccess files with the least depth first (this way overriding works correctly), and warnings are generated if the RewiteBase instruction is encountered as it may require extra tweaking. Other than that I generally just pipe the output to htaccess.conf, then include that file from my basic configuration file. By re-running this script (and, of course, gracefully restating the server after checking to make sure all the conf files parse) as part of my normal sync process I get all the flexibility of .htaccess files, without the performance hit. &lt;br /&gt;
&lt;br /&gt;
The script requires PHP 5, probably 5.1+ would be safest.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
No warranties, test extensively. In terms of field testing (against my rather limited set of .htaccess files) it&#039;s been running on a site +10M uniques/day, using roughly 40 .htaccess files. I&#039;d love to hear from you if you end up using it. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Source:&lt;/b&gt;&lt;a href=&quot;http://blog.preinheimer.com/uploads/htaccess.phps&quot;&gt;.htaccess to httpd.conf&lt;/a&gt; converter, and again with thanks to &lt;a href=&quot;http://drbacchus.com/&quot;&gt;Rich Bowen&lt;/a&gt;.  
    </content:encoded>

    <pubDate>Mon, 12 Oct 2009 11:16:38 -0400</pubDate>
    <guid isPermaLink="false">http://blog.preinheimer.com/index.php?/archives/340-guid.html</guid>
    
</item>
<item>
    <title>Mobile Content Security</title>
    <link>http://blog.preinheimer.com/index.php?/archives/337-Mobile-Content-Security.html</link>
            <category>PHP</category>
    
    <comments>http://blog.preinheimer.com/index.php?/archives/337-Mobile-Content-Security.html#comments</comments>
    <wfw:comment>http://blog.preinheimer.com/wfwcomment.php?cid=337</wfw:comment>

    <slash:comments>9</slash:comments>
    <wfw:commentRss>http://blog.preinheimer.com/rss.php?version=2.0&amp;type=comments&amp;cid=337</wfw:commentRss>
    

    <author>nospam@example.com (Paul Reinheimer)</author>
    <content:encoded>
    &lt;p&gt;Securing content without proxying through PHP is a pretty common, and easily solved problem. You get Lighttpd (or any other webserver) to check some sort of a hash when it serves content. In the hash you include the users IP address, a timestamp, and probably the path to the file. This solution works remarkably well for the average desktop user. Sure there&#039;s outliers behind non-sticky load sharing proxies, but most people confidently ignore that portion of their user base.&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;Mobile users present a more serious problem. Their IP changes more frequently, under a large number of circumstances.  As users move from tower to tower, or as their carrier changes moods their IP changes, generally within the same subnet. That last statement there provides almost a tease as to a possible solution: only hash a portion of the IP address to allow for greater flexibility. &lt;/p&gt;&lt;br /&gt;
&lt;p&gt;Problems however, go a bit deeper.&lt;/p&gt;&lt;br /&gt;
&lt;p&gt;As devices get smarter, we&#039;re now able to do nifty things like surf the web at home (on our mobile device) using our WiFi connection. As we walk outside and down the street the device seamlessly fails over to the 3G, Edge, or whatever connection and our browsing session continues. The browser being blissfully unaware of the change can do nothing to help us. During a test over the weekend, I saw tens of thousands of instances of switching within the same subnet, and a few thousand instances of changes beyond. This is a &lt;b&gt;real problem&lt;/b&gt; for a mobile site.&lt;/p&gt; &lt;br /&gt;
&lt;p&gt;In order to solve the problem I&#039;ve had to balance a few needs: our need to provide even a modicum of protection for our content, the needs of our users to have links work when they view a page. To balance these I&#039;ve added some checks to our software that determine if a user&#039;s IP changes, when those changes are detected the secure links generated omit the IP address but use a far more aggressive timeout. Hopefully balancing our need to protect content, with our desire to serve it to expectant customers.&lt;/p&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;p&gt;Have you solved this problem differently?&lt;/p&gt;&lt;br /&gt;
&lt;br /&gt;
 
    </content:encoded>

    <pubDate>Wed, 09 Sep 2009 11:25:00 -0400</pubDate>
    <guid isPermaLink="false">http://blog.preinheimer.com/index.php?/archives/337-guid.html</guid>
    
</item>
<item>
    <title>Top 5 Reasons Windows shops should consider WIMP over LAMP</title>
    <link>http://blog.preinheimer.com/index.php?/archives/335-Top-5-Reasons-Windows-shops-should-consider-WIMP-over-LAMP.html</link>
            <category>PHP</category>
    
    <comments>http://blog.preinheimer.com/index.php?/archives/335-Top-5-Reasons-Windows-shops-should-consider-WIMP-over-LAMP.html#comments</comments>
    <wfw:comment>http://blog.preinheimer.com/wfwcomment.php?cid=335</wfw:comment>

    <slash:comments>8</slash:comments>
    <wfw:commentRss>http://blog.preinheimer.com/rss.php?version=2.0&amp;type=comments&amp;cid=335</wfw:commentRss>
    

    <author>nospam@example.com (Paul Reinheimer)</author>
    <content:encoded>
    &lt;i&gt;Disclaimer, I&#039;m no Windows Fanboy: I use a Mac at home, and at work deploy code to over a hundred LAMP servers on three continents (using a Windows desktop).&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
I&#039;m always looking for better ways to get my job done, save my employer money, and expand my skill set. So whenever I get a chance I&#039;m looking at the competition of the tools we&#039;re currently using to see if there&#039;s tricks to learn, or reasons to switch. I&#039;ve been keeping an eye on IIS on various Windows servers for a while, more recently I&#039;ve been looking at SyBase and SQL Anywhere, as well as CouchDB for data storage (but more on those two later).&lt;br /&gt;
&lt;br /&gt;
So: &lt;b&gt;Windows, WIMP, and why I think you should be giving it a serious look if you&#039;re currently a Windows shop. &lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;1. Use the expertise you already have in-house&lt;/h3&gt;If you&#039;re currently running Windows you already know how to: install patches, restart machines, manage security and ACLs, connect and administer, get help, etc. When I say &lt;b&gt;you&lt;/b&gt; I mean you as an organization, not an individual.&lt;br /&gt;
&lt;br /&gt;
When I was younger, and slightly more full of stupid than I am now. I made probably the stupidest decision of my professional life (thus far). I was in a Windows shop running Windows servers for everything, with quite a few applications running through Lotus Notes. I was tasked with building a new Intranet. Rather than re-training myself (one person, and an intern at that) to use the monstrosity that was Lotus Notes R5, I somehow managed to convince my boss that we should put a Linux machine in the server room. The project for the most part worked: I built a decent (by my then current standards) Intranet system for the company. &lt;br /&gt;
&lt;br /&gt;
I guarantee the system didn&#039;t survive six months after my departure.&lt;br /&gt;
&lt;br /&gt;
Why? Because maintaining it required everyone else (many more than one) in IT to learn what I was good at. They had to re-train themselves to maintain a single system in a network of many. It simply wasn&#039;t worth the effort.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;2. Avoid duplication of Servers&lt;/h3&gt;When you introduce a new stack, you&#039;re inevitably introducing a new server. That&#039;s an extra server to maintain, backup, power, upgrade, manage, etc. If you&#039;re doing a small roll out there&#039;s inevitably a machine in the room already that&#039;s not seeing a lot of load, just throw it on there. If it&#039;s an entirely different stack your current solutions for a lot of the problems aren&#039;t going to work (your backup scripts and applications for Windows won&#039;t auto-magically migrate over to Linux).&lt;br /&gt;
&lt;br /&gt;
If you read my first story, you can see how many additional problems I caused there. Backing up the machine was going to take extra effort. It was another slot in the rack that couldn&#039;t be used for anything else (despite ridiculously low load). &lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;3. Avoid duplication of Staff&lt;/h3&gt;When you&#039;re running multiple stacks for important or critical applications, you need staff to support them. If you&#039;ve got Windows servers and Linux servers you&#039;ve got staff supporting them both. Multiple sys-admins, multiple people on-call, multiple sets of vacations to manage.&lt;br /&gt;
&lt;br /&gt;
At my current employer, this is our current situation, as we have linux servers for the web, and Windows for everything else. This means we have two sets of system administrators, one for the Windows stuff, the other for Linux. We&#039;ve got multiple pagers so one person from each team can be on call 24x7. I&#039;m not suggesting this is bad per se, but it is an additional cost.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;4. Integrate tightly with current technologies&lt;/h3&gt;When you&#039;re consistently running systems of a similar stack, things just play nicely with each other. Reporting applications, backup, information sharing, etc. It all just works. You don&#039;t need a different username password combination for your development server as you do for your machine logon, and another one still to restart a service. If your security policies permit (and it seems wise) you can centralize this. &lt;br /&gt;
&lt;br /&gt;
At work we have passwords, many of them. We mount dev systems over semi-reliable samba shares, with unique logins and directory structures not entirely tied to our general login accounts. This causes both groups of sysadmins to come together for every new hire to create the appropriate directories and entries in various configuration files.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;5. There&#039;s features out there not available on LAMP&lt;/h3&gt;Many of us have become overly complacent in the thought of Microsoft as a follower in the web arena, products like Internet Explorer aren&#039;t doing much to shake this. IIS 7 is more than a follower, in a lot of realms I feel it has advantages over the technologies I&#039;m used to using. In particular I might call out their XML based configuration system, and per-directory configuration without the performance hit. In terms of configuration XML has a lot of advantages over .ini, in terms of parsing, grouping of elements, parent child relationships, etc. They&#039;re also easy to manipulate programatically with predictable results which allows Microsoft and others to create great user interfaces for their maintenance and upkeep (i&#039;ve never found a graphical apache configuration tool I didn&#039;t hate). For &lt;a href=&quot;http://learn.iis.net/page.aspx/122/getting-started-with-iis-7-configuration/&quot;&gt;per-directory configuration&lt;/a&gt; think .htaccess files, only with more power (like virtual hosts), and without the performance hit. Most production sites turn off .htaccess support (AllowOverride None) because otherwise you&#039;ve got a slew of needless stat calls on every request, with IIS 7 you leave them on, the server simply subscribes to the files so changes are pushed into the application by the operating system, rather than being pulled on each request. You&#039;ve essentially got drag and drop-able applications (since the entire app, configuration, data (SQLite), and all can reside on disk, in one folder) with no performance problems. Streaming media is also very easy on IIS servers, throw in the &lt;a href=&quot;http://www.iis.net/media&quot;&gt;media services plugin&lt;/a&gt; and you get a wealth of congfiguration options, a process that&#039;s still difficult to set up efficently on Apache. &lt;br /&gt;
&lt;br /&gt;
At work we end up leaving .htaccess support on a lot, mostly for some of our more frequently (daily) changing sites. By changing daily not only do I mean it, I also mean changes beyond simple database updates, drastic changes to code and presentation happen regularly. Rather than lean on our hosting provider for frequent restarts we&#039;ve ended up leaving .htaccess support enabled. To manage the performance hit these pages are relegated to their own machines, in separate clusters from the rest of our production sites. Managing system configuration with all the various virtual hosts has become so complex that only a few staff members are trusted with the task. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In conclusion, and to be perfectly clear. I&#039;m not suggesting that WIMP &lt;i&gt;is&lt;/i&gt; the right solution for you. I&#039;m merely encouraging that you pull your head out of the sand and consider your options. If you&#039;re already running a substantial Windows server room, there may be more reasons to stay, than to switch. &lt;br /&gt;
 
    </content:encoded>

    <pubDate>Tue, 14 Jul 2009 13:07:00 -0400</pubDate>
    <guid isPermaLink="false">http://blog.preinheimer.com/index.php?/archives/335-guid.html</guid>
    
</item>
<item>
    <title>Storing Sessions in Memcache (how everything behaves)</title>
    <link>http://blog.preinheimer.com/index.php?/archives/334-Storing-Sessions-in-Memcache-how-everything-behaves.html</link>
            <category>PHP</category>
    
    <comments>http://blog.preinheimer.com/index.php?/archives/334-Storing-Sessions-in-Memcache-how-everything-behaves.html#comments</comments>
    <wfw:comment>http://blog.preinheimer.com/wfwcomment.php?cid=334</wfw:comment>

    <slash:comments>9</slash:comments>
    <wfw:commentRss>http://blog.preinheimer.com/rss.php?version=2.0&amp;type=comments&amp;cid=334</wfw:commentRss>
    

    <author>nospam@example.com (Paul Reinheimer)</author>
    <content:encoded>
    &lt;style type=&quot;text/css&quot;&gt;
ol#boh li {
  margin-bottom: 10px;
}
&lt;/style&gt;
&lt;p&gt;I’ve been using memcached to store session data for the past while, but we ran into a few problems at work that led me to dive in a bit deeper and see how PHP, Sessions, and memcached play along. &lt;/p&gt;

&lt;b&gt;Short Version&lt;/b&gt;
&lt;p&gt;When you start up a session a new entry is created in memcache, the expiry is the same as session.gc_maxlifetime. Session data is retrieved each time a session is started (either with &lt;span style=&quot;font-family: Verdana, Helvetica; background-color: #EFEFDF;&quot;&gt;session_start()&lt;/span&gt;, or if you have &lt;span style=&quot;font-family: Verdana, Helvetica; background-color: #EFEFDF;&quot;&gt;session.auto_start&lt;/span&gt; enabled, etc). &lt;/p&gt;

&lt;b&gt;Long Version:&lt;/b&gt;
&lt;p&gt;When session_start() is called for a new user several things happen very quickly:&lt;/p&gt;
&lt;ol style=&quot;margin: 0px; padding: 0px; list-style-position: outside;&quot; id=&quot;boh&quot;&gt;
&lt;li&gt;A session ID is generated for the end user, this is seen in the HTTP response headers. 
&lt;span style=&quot;font-family: Verdana, Helvetica; background-color: #EFEFDF;&quot;&gt;&lt;xmp&gt;HTTP/1.x 200 OK 
Date: Fri, 10 Jul 2009 16:07:21 GMT 
Server: Apache/2.2.11 
X-Powered-By: PHP/5.2.6-3ubuntu4.1 
Set-Cookie: PHPSESSID=c446fc5309c3b8d3aa6e90343dddab29; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT 
Vary: Accept-Encoding 
Content-Encoding: gzip 
Content-Length: 23 
Keep-Alive: timeout=15, max=100 
Connection: Keep-Alive 
Content-Type: text/html&lt;/xmp&gt;&lt;/span&gt;
&lt;li&gt;PHP Contacts memcached and attempts to retrieve any information in that particular session ID
&lt;span style=&quot;font-family: Verdana, Helvetica; background-color: #EFEFDF;&quot;&gt;get c446fc5309c3b8d3aa6e90343dddab29&lt;/span&gt;
&lt;li&gt;Memcached returns no data for that entry, as it doesn’t exist yet.
&lt;li&gt;Your page executes, setting data in the session
&lt;li&gt;PHP pushes data into memcached:
&lt;span style=&quot;font-family: Verdana, Helvetica; background-color: #EFEFDF;&quot;&gt;set c446fc5309c3b8d3aa6e90343dddab29 0 1440 32  name |s:6”asdasd;bar|s:3:”foo”;&lt;/span&gt;
&lt;/ol&gt;
&lt;p&gt;On every subsequent page where a session is used, a very similar sequence of events is executed. When the session is started PHP contacts memcached and asks for the session data. When the page is complete it pushes the data now in the session to memcached. 
That last step is kind of critical, because it’s what’s controlling the expiry of the data within memcached. Take a quick look at the data being set once more.&lt;/p&gt; 
&lt;span style=&quot;font-family: Verdana, Helvetica; background-color: #EFEFDF;&quot;&gt;set c446fc5309c3b8d3aa6e90343dddab29 0 1440 32  name |s:6”asdasd;bar|s:3:”foo”;&lt;/span&gt;
&lt;p&gt;Data is being set, the key is your session id, 0 is a set of flags that the client can use to mark the data (often used to mark data that’s been compressed). The 1440 represents the expiry time for the data, 1440 seconds from now (24 minutes). That value was taken directly from session.gc_maxlifetime . It’s important to keep in mind that what memcached guarantees is that the data will not be available after that expiry runs out, but it promises nothing about how long it will be available.  If your memcached instance crashes, or runs out of memory you’re going to start losing data a lot sooner. Finally we have the data in a somewhat truncated version of PHP’s serialize. &lt;/p&gt;

&lt;p&gt;The fact that the data is sent to memcached after every request, even if you haven’t changed what’s stored in the session, is rather important. It’s what makes your session last for session.gc_maxlifetime after the user last did something, rather than simply that amount plus the last time you requested a document.&lt;/p&gt; 
    </content:encoded>

    <pubDate>Fri, 10 Jul 2009 12:35:44 -0400</pubDate>
    <guid isPermaLink="false">http://blog.preinheimer.com/index.php?/archives/334-guid.html</guid>
    
</item>
<item>
    <title>You can’t maintain code you can’t easily understand</title>
    <link>http://blog.preinheimer.com/index.php?/archives/332-You-cant-maintain-code-you-cant-easily-understand.html</link>
            <category>PHP</category>
    
    <comments>http://blog.preinheimer.com/index.php?/archives/332-You-cant-maintain-code-you-cant-easily-understand.html#comments</comments>
    <wfw:comment>http://blog.preinheimer.com/wfwcomment.php?cid=332</wfw:comment>

    <slash:comments>18</slash:comments>
    <wfw:commentRss>http://blog.preinheimer.com/rss.php?version=2.0&amp;type=comments&amp;cid=332</wfw:commentRss>
    

    <author>nospam@example.com (Paul Reinheimer)</author>
    <content:encoded>
    I just helped a co-worker with a problem in a sorting mechanism inside Javascript. Here’s the snippet that caught my eye:&lt;br /&gt;
&lt;span style=&quot;font-family: Verdana, Helvetica; background-color: #EFEFDF;&quot;&gt;var ret = (a.l &amp;gt; b.l) ? -1 : ((a.n.toLowerCase() &amp;lt; b.n.toLowerCase()) ? -1 : 1);&lt;/span&gt;&lt;br /&gt;
I looked at the code, and stated: I don’t like nested ternaries, it’s too easy to mess up or miss something. My co-worker explained that the alternative was too long and drawn out, this is how it’s always done. I pondered the code for a moment, and asked what happened when &lt;span style=&quot;font-family: Verdana, Helvetica; background-color: #EFEFDF;&quot;&gt;a.l &amp;lt; b.l&lt;/span&gt;, as the case seems to be missing. That was the bug. &lt;br /&gt;
The people I’m working with are all smart, great developers.  We all miss things, tragically we’re not perfect. When you miss things, you need to be able to find them, writing overly complex code (I would consider nested ternaries an example of overly complex code) makes it really easy to find them. &lt;br /&gt;
I’m slowly working on improving our internal coding standards document with additional rules, at the time being I’m considering:&lt;br /&gt;
&lt;ol&gt;&lt;br /&gt;
&lt;li&gt;Don’t use GOTO&lt;br /&gt;
&lt;li&gt;Don’t use nested ternaries&lt;br /&gt;
&lt;li&gt;Document the heck out of variable variables ($$variable), or variable function calls ($this-&amp;gt;$method)&lt;br /&gt;
&lt;li&gt;Write commit messages&lt;br /&gt;
&lt;/ol&gt;&lt;br /&gt;
&lt;br /&gt;
Any other rules you follow?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note, while I am strongly discouraging the use of GOTO, I do not think adding it to PHP was a mistake. There are good use cases for the language construct, I just feel it’s an easy thing to fall back on when the real solution is to build your code in a more intelligent manner. &lt;br /&gt;
 
    </content:encoded>

    <pubDate>Thu, 25 Jun 2009 16:24:02 -0400</pubDate>
    <guid isPermaLink="false">http://blog.preinheimer.com/index.php?/archives/332-guid.html</guid>
    
</item>
<item>
    <title>Memcached Constants - Lame Code</title>
    <link>http://blog.preinheimer.com/index.php?/archives/330-Memcached-Constants-Lame-Code.html</link>
            <category>PHP</category>
    
    <comments>http://blog.preinheimer.com/index.php?/archives/330-Memcached-Constants-Lame-Code.html#comments</comments>
    <wfw:comment>http://blog.preinheimer.com/wfwcomment.php?cid=330</wfw:comment>

    <slash:comments>3</slash:comments>
    <wfw:commentRss>http://blog.preinheimer.com/rss.php?version=2.0&amp;type=comments&amp;cid=330</wfw:commentRss>
    

    <author>nospam@example.com (Paul Reinheimer)</author>
    <content:encoded>
    Please find below an incredibly lame piece of code to print out the values of all the &lt;a href=&quot;http://ca3.php.net/manual/en/book.memcached.php&quot;&gt;memcached&lt;/a&gt; constants defined on their doc page. I was running into a few errors, and &lt;a href=&quot;http://ca3.php.net/manual/en/memcached.getresultcode.php&quot;&gt;getResultCode()&lt;/a&gt; was obligingly returning the value of the &lt;a href=&quot;http://ca3.php.net/manual/en/memcached.constants.php&quot;&gt;constant&lt;/a&gt;, rather than the constant itself. I had to look up what that value meant. For some reason I couldn&#039;t reflect the extension to get the constants and values thereof, so with some lame scraping you get the following. Code under the jump, answers (as of our version of various libraries) here:&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote&gt;&lt;br /&gt;
Memcached::OPT_COMPRESSION - -1001&lt;br /&gt;
Memcached::OPT_SERIALIZER - -1003&lt;br /&gt;
Memcached::SERIALIZER_PHP - 1&lt;br /&gt;
Memcached::SERIALIZER_IGBINARY - 2&lt;br /&gt;
Memcached::SERIALIZER_JSON - 3&lt;br /&gt;
Memcached::OPT_PREFIX_KEY - -1002&lt;br /&gt;
Memcached::OPT_HASH - 2&lt;br /&gt;
Memcached::HASH_DEFAULT - 0&lt;br /&gt;
Memcached::HASH_MD5 - 1&lt;br /&gt;
Memcached::HASH_CRC - 2&lt;br /&gt;
Memcached::HASH_FNV1_64 - 3&lt;br /&gt;
Memcached::HASH_FNV1A_64 - 4&lt;br /&gt;
Memcached::HASH_FNV1_32 - 5&lt;br /&gt;
Memcached::HASH_FNV1A_32 - 6&lt;br /&gt;
Memcached::HASH_HSIEH - 7&lt;br /&gt;
Memcached::HASH_MURMUR - 8&lt;br /&gt;
Memcached::OPT_DISTRIBUTION - 9&lt;br /&gt;
Memcached::DISTRIBUTION_MODULA - 0&lt;br /&gt;
Memcached::DISTRIBUTION_CONSISTENT - 1&lt;br /&gt;
Memcached::OPT_LIBKETAMA_COMPATIBLE - 16&lt;br /&gt;
Memcached::OPT_BUFFER_WRITES - 10&lt;br /&gt;
Memcached::OPT_BINARY_PROTOCOL - 18&lt;br /&gt;
Memcached::OPT_NO_BLOCK - 0&lt;br /&gt;
Memcached::OPT_TCP_NODELAY - 1&lt;br /&gt;
Memcached::OPT_SOCKET_SEND_SIZE - 4&lt;br /&gt;
Memcached::OPT_SOCKET_RECV_SIZE - 5&lt;br /&gt;
Memcached::OPT_CONNECT_TIMEOUT - 14&lt;br /&gt;
Memcached::OPT_RETRY_TIMEOUT - 15&lt;br /&gt;
Memcached::OPT_SEND_TIMEOUT - 19&lt;br /&gt;
Memcached::OPT_RECV_TIMEOUT - 15&lt;br /&gt;
Memcached::OPT_POLL_TIMEOUT - 8&lt;br /&gt;
Memcached::OPT_CACHE_LOOKUPS - 6&lt;br /&gt;
Memcached::OPT_SERVER_FAILURE_LIMIT - 21&lt;br /&gt;
Memcached::HAVE_IGBINARY - #&amp;UNDEFINED;#&lt;br /&gt;
Memcached::HAVE_JSON - #&amp;UNDEFINED;#&lt;br /&gt;
Memcached::GET_PRESERVE_ORDER - 1&lt;br /&gt;
Memcached::RES_SUCCESS - 0&lt;br /&gt;
Memcached::RES_FAILURE - 1&lt;br /&gt;
Memcached::RES_HOST_LOOKUP_FAILURE - 2&lt;br /&gt;
Memcached::RES_UNKNOWN_READ_FAILURE - 7&lt;br /&gt;
Memcached::RES_PROTOCOL_ERROR - 8&lt;br /&gt;
Memcached::RES_CLIENT_ERROR - 9&lt;br /&gt;
Memcached::RES_SERVER_ERROR - 10&lt;br /&gt;
Memcached::RES_WRITE_FAILURE - 5&lt;br /&gt;
Memcached::RES_DATA_EXISTS - 12&lt;br /&gt;
Memcached::RES_NOTSTORED - 14&lt;br /&gt;
Memcached::RES_NOTFOUND - 16&lt;br /&gt;
Memcached::RES_PARTIAL_READ - 18&lt;br /&gt;
Memcached::RES_SOME_ERRORS - 19&lt;br /&gt;
Memcached::RES_NO_SERVERS - 20&lt;br /&gt;
Memcached::RES_END - 21&lt;br /&gt;
Memcached::RES_ERRNO - 26&lt;br /&gt;
Memcached::RES_BUFFERED - 32&lt;br /&gt;
Memcached::RES_TIMEOUT - 31&lt;br /&gt;
Memcached::RES_BAD_KEY_PROVIDED - 33&lt;br /&gt;
Memcached::RES_CONNECTION_SOCKET_CREATE_FAILURE - 11&lt;br /&gt;
Memcached::RES_PAYLOAD_FAILURE - -1001&lt;br /&gt;
&lt;/blockquote&gt;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;&lt;a href=&quot;http://blog.preinheimer.com/index.php?/archives/330-Memcached-Constants-Lame-Code.html#extended&quot;&gt;Continue reading &quot;Memcached Constants - Lame Code&quot;&lt;/a&gt;
    </content:encoded>

    <pubDate>Thu, 18 Jun 2009 14:40:31 -0400</pubDate>
    <guid isPermaLink="false">http://blog.preinheimer.com/index.php?/archives/330-guid.html</guid>
    
</item>

</channel>
</rss>