<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
    <channel>
        <title>Ramblings of a web guy (Tag: php)</title>
        <description>Brian Moon, of dealnews.com, shares what he knows (and learns) about PHP, MySQL and other stuff</description>
        <link>http://brian.moonspot.net/feed.php?type=rss&amp;amp;tag=php</link>
        <lastBuildDate>Tue, 07 Sep 2010 21:16:39 -0500</lastBuildDate>
        <generator>Wordcraft 0.10</generator>
        <item>
            <guid>http://brian.moonspot.net/php-5-3-mysqlnd</guid>
            <title>PHP 5.3 and mysqlnd - Unexpected results</title>
            <link>http://brian.moonspot.net/php-5-3-mysqlnd</link>
            <description><![CDATA[I have started seriously using PHP 5.3 recently due to it finally making it into <a>Portage</a>. (Gentoo really isn't full of bleeding edge packages people.) I have used mysqlnd a little here and there in the past, but until it was really coming to my servers I did not put too much time into it.<br><br><strong>What is mysqlnd?</strong><br><br>mysqlnd is short for MySQL Native Driver. In short, it is a driver for MySQL for PHP that uses internal functions of the PHP engine rather than using the externally linked libmysqlclient that has been used in the past. There are two reasons for this. The first reason is licensing. MySQL is a GPL project. The GPL and the PHP License don't play well together. The second is better memory management and hopefully more performance. Being a performance junky, this is what peaked my interests. Enabling mysqlnd means it is used by the older MySQL extension, the newer MySQLi extension and the MySQL PDO driver.<br><br><strong>New Key Feature - fetch_all</strong><br><br>One new feature of mysqlnd was the fetch_all method on MySQLi Result objects. At both <a href="http://dealnews.com/">dealnews.com</a> and in <a href="http://www.phorum.org/">Phorum</a> I have written a function to simply run a query and fetch all the results into an array and return it. It is a common operation when writing API or ORM layers. mysqlnd introduces a native fetch_all method that does this all in the extension. No PHP code needed. PDO already offers a fetchAll method, but PDO comes with a little more overhead than the native extensions and I have been using mysql functions for 14 years. I am very happy using them.<br><br><strong>Store Result vs. Use Result</strong><br><br>I have spoken in the past (see my slides and interview: <a>MySQL Tips and Tricks</a>) about using mysql_unbuffered_query or using mysqli_query with the MYSQLI_USE_RESULT flag. Without going into a whole post about that topic, it basically allows you to stream the results from MySQL back into your PHP code rather than having them buffered in memory. In the case of libmysqlclient, they could be buffered twice. So, my natural thought was that using MYSQLI_USE_RESULT with fetch_all would yield the most awesome performance ever. The data would not be buffered and it would get put into a PHP array in C instead of native code. The code I had hoped to use would look like:<pre>$res = $db-&gt;query($sql, MYSQLI_USE_RESULT);<br>$rows = $res-&gt;fetch_all(MYSQLI_ASSOC);<br></pre>
But, I quickly found out that this does not work. For some reason, this is not supported. fetch_all only works with the default which is MYSQLI_STORE_RESULT. I filed a bug which was marked bogus. Which I put back to new because I really don't see a reason this should not work other than a complete oversight by the mysqlnd developers. So, I started doing some tests in hopes I could show the developers how much faster using MYSQLI_USE_RESULT could be. What happened next was not expected. I ended up benchmarking several different options for fetching all the rows of a result into an array.<br><br><strong>Test Data</strong><br><br>I tested using PHP 5.3.3 and MySQL 5.1.44 using InnoDB tables. For test data I made a table that has one varchar(255) column. I filled that table with 30k rows of random lengths between 10 and 255 characters. I then selected all rows and fetched them using 4 different methods.<span class="methodname"></span><span style="font-weight: bold;"></span><br><ol><li><a href="http://us.php.net/manual/en/mysqli-result.fetch-all.php">mysqli_result::fetch_all*</a></li><li><a href="http://us.php.net/manual/en/pdostatement.fetchall.php">PDOStatement::fetchAll</a></li><li><a href="http://us.php.net/manual/en/mysqli.query.php">mysqli_query with MYSQLI_STORE_RESULT followed by a loop</a></li><li><a href="http://us.php.net/manual/en/mysqli.query.php">mysqli_query with MYSQLI_USE_RESULT followed by a loop</a></li></ol></li>In addition, I ran this test with mysqlnd enabled and disabled. For mysqli_result::fetch_all, only mysqlnd was tested as it is only available with mysqlnd. I ran each test 6 times and threw out the worst and best result for each test. FWIW, the best and worst did not show any major deviation for any of the tests. For measuring memory usage, I read the VmRSS value from Linux's /proc data. memory_get_usage() does not show the hidden memory used by libmysqlclient and does not seem to show all the memory used by mysqlnd either.<br><br><img  alt="" style="width: 574px;" src="http://content.screencast.com/users/brianlmoon/folders/Jing/media/18ecdea6-1a2c-4b9b-b267-b7676effdcb1/00000117.png"><br><br>So, that is what I found. The memory usage graphs are all what I thought they would be. PDO has more overhead by its nature. Storing the result always uses more memory than using it. mysqli_result::fetch_all uses less memory than the loop, but more than directly using the results. <br><br>There are some very surprising things in the timing graphs however. First, the tried and true method of using the result followed by a loop is clearly still the right choice in libmysqlclient. However, it is a horrible choice for mysqlnd. I don't really see why this is so. It is nearly twice as slow. There is something really, really wrong with MYSQLI_USE_RESULT in mysqlnd. There is no reason it should ever be slower than storing the result and then reading it again. This is also evidenced in the poor performance of PDO (since even PDO uses mysqlnd when enabled). PDO uses an unbuffered query for its fetchAll method and it too got slower. It is noticably slower than libmysqlclient. The good news I guess is that if you are using mysqlnd, the fetch_all method is the best option for getting all the data back. <br><br><strong>Next Steps</strong><br><br>My next steps from here will be to find some real workloads that I can test this on. Phorum has several places where I can apply real world pages loads to these different methods and see how they perform. Perhaps the test data is too small. Perhaps the number of columns would have a different effect. I am not sure.<br><br>If you are reading this and have worked on or looked at the mysqlnd code and can explain any of it, please feel free to comment.]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Tue, 03 Aug 2010 08:00:00 -0500</pubDate>
            <category>mysql</category>
            <category>php</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/php-memcached-issues</guid>
            <title>PHP and Memcached: The state of things</title>
            <link>http://brian.moonspot.net/php-memcached-issues</link>
            <description><![CDATA[Memcached is the de facto standard for caching in dynamic web sites. PHP is the one of the most widely used languages on the web. So, naturally there is lots of interest in using the two together. There are two choices for using memcached with PHP: <a href="http://pecl.php.net/package/memcache">PECL/memcache</a> and <a href="http://pecl.php.net/package/memcached">PECL/memcached</a>.
 Great names huh? But as of this writing there are issues with the two most popular Memcached libraries for PHP. This is a summary of those issues that I hope will help people being hurt by them and may bring about some change.<br><br><strong>PECL/memcache</strong><br><br>This is the older of the two and was the first C based extension for using memcached with PHP. Before this all the options were native PHP code. While they worked, they were slower of course. C &gt; PHP. That is just fact. However, there has not been much active development on this code in some time. Yes, they have fixed bugs, but support for new features in the memcached server have not been added to this extension. Newer version of the server suppot a more efficient binary protocol and many new features. In addition, there are some parts of the extension that simply don't work anymore. <br><br>The most glaring one is the <a href="http://us3.php.net/manual/en/memcache.delete.php">delete()</a> function. It takes a second parameter that is documented as: "the item will expire after
 <em><tt class="parameter">timeout</tt></em> seconds". In fact that was never a feature of memcached. It was completely misunderstood by the original extension authors. When that parameter was supported, it locked the key for <em><tt class="parameter">timeout</tt></em> seconds and would not allow a new add operation on that key. Second, this feature was completely removed in memcached 1.4.0. So, if you send a timeout to the delete function, you simply get a failure. This is creating a huge support issue in the memcached community (not the PHP/PECL community) with people not being able to delete keys because of bad documentation and unsupported behavior. I sent a documentation patch for the this function to the PHP Docs list. I then modified it based on feedback. But since I have heard nothing about it getting merged. I have a PHP svn account, but even if I do have karma on the docs repository, I don't want to hijack them without the support of the people that work on the docs all the time. If you are reading this and can change the docs, please make the documentation for that function say "DON'T USE THIS PARAMETER, IT HAS BEEN DEPRECATED IN THE MEMCACHED SERVER!" or something.<br><br>Not too long ago the extension was <a href="http://marc.info/?l=pecl-dev&m=126926486801449&w=2">officially abandoned</a> by its original maintainers. Some people stepped up and claimed they wanted to see it continue. But, since that time, there have been no releases. There are bugs being closed though so maybe there is good things coming.<br><br>A very problematic issue with this extension is with the 3.0 beta release. It needs to just die. It has huge bugs that, IMO, were introduced by the previous maintainers in an effort to bring it up to speed, but never saw them through to make sure the new code worked. In their defense, it is marked as beta on PECL. But, thanks to Google, people don't see the word beta anymore. There are lots of people using this version and when they get bad results they blame the whole memcached world. Really, the new maintainers would do the world a favor if they just removed the 3.x releases from the PECL site.<br><br><strong>PECL/memcached</strong><br><br>This extension was started by Andrei Zmievski while working at Digg.com as an open source developer. It uses <a href="http://libmemcached.org/">libmemcached</a>, a C++ memcached library that does all the memcached work. This made it quite easy to support the new features in the memcached daemon as it as it was being developed at the same time as the new server. However, it has not had a stable release on PECL in nearly a year except for release to make it compatible with new versions of libmemcached. No bug fixes and no new features. There are currently 28 open bugs on PECL for this extension. Not all of which are bugs. Some are feature requests. The ironic thing is that the <a href="http://github.com/andreiz/php-memcached">GitHub repository</a> for this extension has seen a lot of development. But, none of these bug fixes have made it into the official PECL channel. And some of these bugs are major and others are just huge WTF for a developer.<br><br>The most major bug is one that I found. If you use persistent connections with this extension, it basically leaks those connections, not reusing an existing connection but also not closing the ones already made. This uses up the memory in your processes until they crash and it creates an exponential number of connections to your memcached server until it has no more connections available. Andrei does report in the bug that it is fixed in hist GitHub. But, for now, you can't use persistent connections.<br><br>The big WTF for a developer is that some functions don't take a numeric key and use it properly.<pre>&lt;?php<br><br>$mc = new Memcached();<br><br>$mc-&gt;addServer("localhost", 11211);<br><br>$mc-&gt;set(123, "yes!");<br><br>var_dump($mc-&gt;getMulti(array(123)));<br><br>?&gt;</pre>The above code should generate:<pre>array(1) {<br>&nbsp; [123]=&gt;<br>&nbsp; string(4) "yes!"<br>}</pre>But, in reality, it generates:<pre>bool(false)</pre>The set succeeds, but the getMulti fails. Again, this is being fixed in GitHub, but is not available for release on PECL.<br><br><strong>Compatibility</strong><br><br>One big issue with these two extensions is that they are not drop in replacements for each other. If you want to move from one to the other, you have to take into consideration some time to convert your code. For instance, the older extension's set() function takes flags as the third parameter. The newer extension does not take a flags parameter at all. The older uses get() with an array to do a multi-get and the newer extension has a separate method for that called getMulti(). There are others as well.<br><br><strong>Summary</strong><br><br>So, what should you do as a PHP developer? If you are deploying memcached today, I would use the 2.2.x branch of PECL/memcache. It is the most stable. Just avoid the delete bug. It is not as fast and does not have the features. But, it is very reliable for set, get, add.... the basics of memcached. For the long term, it is a bit unclear. PECL/memcached looks to fix a lot of things in 2.0. But, I have not used it yet. In addition the long term growth of the project is a bit in question. Will there be a 2.1, 2.2, etc? I hope so. The other unknown is if the those people fixing the PECL/memcache bugs will keep it up and release a good stable product that supports new features of the server. Again, I hope so. The best scenario would be to have a choice between two fully compatible and feature rich extensions. Keep your fingers crossed.]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Wed, 23 Jun 2010 11:48:31 -0500</pubDate>
            <category>memcached</category>
            <category>php</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/php-generated-code</guid>
            <title>PHP generated code tricks</title>
            <link>http://brian.moonspot.net/php-generated-code</link>
            <description><![CDATA[<span style="font-family: Verdana,Arial,Helvetica,sans-serif;"></span>Something that is great about PHP is that you can write code that generates more PHP code to be used later. Now, I am not saying this a best practice. I am sure it violates some rule in some book somewhere. But, sometimes you need to be a rule breaker.<br>
<br>
A simple example is taking a database of configuration information and dumping it to an array. We do this for each publication we operate. We have a publication table. It contains the name, base URL and other stuff that is specific to that publication. But, why query the database for something that only changes once in a blue moon? We could cache it, but that would still require an on demand database hit. The easy solution is to just dump the data to a PHP array and put it on disk.<br>

<pre>&lt;?php<br><br>$sql = "select * from publications";<br><br>$res = $mysqli-&gt;query($sql);<br><br>while($row = $res-&gt;fetch_assoc()){<br><br>&nbsp;&nbsp;&nbsp; $pubs[$row["publication_id"]] = $row;<br><br>}<br><br>$pubs_ser = str_replace("'", "\\'", serialize($pubs));<br><br>$php_code = "&lt;?php global \$PUBLICATIONS; \$PUBLICATIONS = unserialize('$pubs_ser'); ?&gt;";<br><br>file_put_contents("/some/path/publications.php", $php_code);<br><br>?&gt;<br></pre>

Now you can include the publications.php file and have a global variable named $PUBLICATIONS that holds the publication settings. But, how do we load a single publication without knowing numeric ids? Well, you could make some constants.

<pre>&lt;?php<br><br>$sql = "select * from publications";<br><br>$res = $mysqli-&gt;query($sql);<br><br>while($row = $res-&gt;fetch_assoc()){<br><br>&nbsp;&nbsp;&nbsp; $pubs[$row["publication_id"]] = $row;<br><br>&nbsp;&nbsp;&nbsp; $constants[$row["publication_id"]] = strtoupper($row["name"]);<br><br>}<br><br>$pubs_ser = str_replace("'", "\\'", serialize($pubs));<br><br>$php_code = "&lt;?php\n";<br><br>$php_code.= "global \$PUBLICATIONS;\n";<br><br>$php_code.= "\$PUBLICATIONS = unserialize('$pubs_ser');\n";<br><br>foreach($constants as $id=&gt;$const){<br><br>&nbsp;&nbsp;&nbsp; $php_code.= "define('$const', $id);\n";<br><br>}<br><br>$php_code.= "?&gt;";<br><br>file_put_contents("/some/path/publications.php", $php_code);<br><br>?&gt;<br><br></pre>

So, now, we have constants. We can do stuff like:
<pre>&lt;?php<br><br>//load a publication<br><br>require_once "publications.php";<br><br>echo $PUBLICATIONS[DEALNEWS]["name"];<br><br>?&gt;</pre>But, how about autoloading? It would be nice if I could just autoload the constants.
<pre>&lt;?php<br><br>$sql = "select * from publications";<br><br>$res = $mysqli-&gt;query($sql);<br><br>while($row = $res-&gt;fetch_assoc()){<br><br>&nbsp;&nbsp;&nbsp; $pubs[$row["publication_id"]] = $row;<br><br>&nbsp;&nbsp;&nbsp; $constants[$row["publication_id"]] = strtoupper($row["name"]);<br><br>}<br><br>$pubs_ser = str_replace("'", "\\'", serialize($pubs));<br><br>$php_code = "&lt;?php\n";<br><br>$php_code.= "class PUB_DATA {\n";<br><br>foreach($constants as $id=&gt;$const){<br><br>&nbsp;&nbsp;&nbsp; $php_code.= " const $const = $id;\n";<br><br>}<br><br>$php_code.= "&nbsp;&nbsp;&nbsp; protected \$pubs_ser = '$pubs_ser';\n";<br><br>$php_code.= "}";<br><br>$php_code.= "?&gt;";<br><br>file_put_contents("/some/path/pub_data.php", $php_code);<br><br>?&gt;</pre>
Then we create a class in our autoloading directory that extends that object.<br><pre>&lt;?php<br><br>require_once "pub_data.php";<br><br>class Publication extends PUB_DATA {<br><br>&nbsp;&nbsp;&nbsp; private $pub;<br><br>&nbsp;&nbsp;&nbsp; public function __construct($pub_id) {<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $pubs = unserialize($this-&gt;pubs_ser);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $this-&gt;pub = $pubs[$pub_id];<br><br>&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; public function __get($var) {<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(isset($this-&gt;pub[$var])){<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return $this-&gt;pub[$var];<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Exception<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br><br>&nbsp;&nbsp;&nbsp; }<br><br>}<br><br>?&gt;</pre>
Great, now we can do things like:
<pre>$pub = new Publication(Publication::DEALNEWS);<br><br>echo $pub-&gt;name;<br></pre>
The only problem that remains is dealing with getting the generated code to all your servers. We use rsync. It works quite well. You may have a different solution for your team. Back when we ran our own in house ad server we did all the ad work this way. None of the ad calls ever hit the database to get ads. We stored stats on disk in logs and processed them on a schedule. It was a very solid solution.<br>
<br>
One more benefit of using generated files on disk is that they can be cached by APC or XCache. This means you don't have to actually hit disk for them all the time.]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Fri, 18 Jun 2010 13:56:07 -0500</pubDate>
            <category>php</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/mysql-conference-review</guid>
            <title>MySQL Conference Review</title>
            <link>http://brian.moonspot.net/mysql-conference-review</link>
            <description><![CDATA[I am back home from a good week at the 2010 O'Reilly MySQL Conference &amp; Expo. I had a great time and got to see some old friends I had not seen in a while.<br><br>Oracle gave the opening keynote and it went pretty much like I thought it would. Oracle said they will keep MySQL alive. They talked about the new 5.5 release. It was pretty much the same keynote Sun gave last year. Time will tell what Oracle does with MySQL.<br><br>The expo hall was sparse. Really sparse. There were a fraction of the booths compared to the past. I don't know why the vendors did not come. Maybe because they don't want to compete with Oracle/Sun? In the past you would see HP or Intel have a booth at the conference. But, with Oracle/Sun owning MySQL, why even try. Or maybe they are not allowed? I don't know. It was just sad.<br><br>I did stop by the <a href="http://www.maatkit.org/">Maatkit</a> booth and was embarrassed to tell <a href="http://www.xaprb.com/">Baron</a> (its creator) I was not already using it. I had heard people talk about it in the past, but never stopped to see what it does. It would have only saved me hours and hours of work over the last few years. Needless to say it is now being installed on our servers. If you use MySQL, just go install Maatkit now and start using it. Don't be like me. Don't wait for years, writing the same code over and over to do simple maintenance tasks.<br><br><a href="http://gearman.org/">Gearman</a> had a good deal of coverage at the conference. There were three talks and a <a href="http://en.wikipedia.org/wiki/Birds_of_a_Feather_%28computing%29">BoF</a>. All were well attended. Some people seemed to have an AHA! moment where they saw how Gearman could help their architecture. I also got to sit down with the PECL/gearman maintainers and discuss the recent bug I found that is keeping me from using it.<br><br>I spoke about <a href="http://memcached.org/">Memcached</a> as did others. Again, there was a BoF. It was well attended and people had good questions about it. There seemed to be some FUD going around that memcached is somehow inefficient or not keeping up with technology. However, I have yet to see numbers or anything that proves any of this. They are just wild claims by people that have something to sell. Everyone wants to be the caching company since there is no "Memcached, Inc.". There is no company in charge. That is a good thing, IMO.<br><br>That brings me to my favorite topic for the conference, Drizzle. I <a href="http://brian.moonspot.net/2008/07/24/156/">wrote about Drizzle</a> here on this blog when it was first announced. At the time MySQL looked like it was moving forward at a good pace. So, I had said that it would only replace MySQL in one part of our stack. However, after what, in my opinion, has been a lack of real change in MySQL, I think I may have changed my mind. Brian Aker echoed this sentiment in <a href="http://www.slideshare.net/brianaker/drizzle-keynote-at-the-mysql-users-conference">his keynote address about Drizzle</a>. He talked about how MySQL AB and later Sun had stopped focusing on the things that made MySQL popular and started trying to be a cheap version of Oracle. That is my interpretation of what he said, not his words. <br><br>Why is Drizzle different? Like Memcached and Gearman, there is no "Drizzle, Inc.". It is an Open Source project that is supported by the community. It is being supported by companies like <a href="http://www.rackspacecloud.com/blog/2010/03/13/rackspace-and-drizzle-its-time-to-rethink-everything/">Rackspace who hired five developers</a> to work on it. The code is kept on <a href="https://launchpad.net/drizzle">Launchpad</a> and is completely open. Anyone can create a branch and work on the code. If your patches are good, they will be merged into the main branch. But, you can keep your own branch going if you want to. Unlike the other forks, Drizzle has started over in both the code and the community. I personally see it as the only way forward. It is not ready today, but my money is on Drizzle five or ten years from now.]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Sat, 17 Apr 2010 10:54:52 -0500</pubDate>
            <category>drizzle</category>
            <category>gearman</category>
            <category>memcached</category>
            <category>mysql</category>
            <category>php</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/devops-dealnews</guid>
            <title>DevOps at dealnews.com</title>
            <link>http://brian.moonspot.net/devops-dealnews</link>
            <description><![CDATA[I was telling someone how we roll changes to production at dealnews and they seemed really amazed by it. I have never really thought it was that impressive. It just made sense. It has kind of happened organically here over the years. Anyhow, I thought I would share.<br><br><strong>Version Control</strong><br><br>So, to start with, everything is in SVN. PHP code, Apache configs, DNS and even the scripts we use to deploy code. That is huge. We even have a misc directory in SVN where we put any useful scripts we 
use on our laptops for managing our code base. Everyone can share that 
way. Everyone can see what changed when. We can roll things back, branch if we need to, etc. I don't know how anyone lives with out. We did way back when. It was bad. People were stepping on each other. It was a mess. We quickly decided it did not work. <br><br>For our PHP code, we have trunk and a production branch. There are also a couple of developers (me) that like to have their own branch because they break things for weeks at a time. But, everything goes into trunk from my branch before going into production. We have a PHP script that can merge from a developer branch into trunk with conflict resolution assistance built in. It is also capable of merging changes from trunk back into a branch. Once it is in trunk we use our staging environment to put it into production.<br><br><strong>Staging/Testing<br></strong><br>Everything has a staging point. For our PHP code, it is a set of test staging servers in our home office that have a checkout of the production branch. To roll code, the developer working on the project logs in via ssh to a staging server as a restricted user and uses a tool we created that is similar to the Python based <a href="http://www.orcaware.com/svn/wiki/Svnmerge.py">svnmerge.py</a>. Ours is written in PHP and tailored for our directory structure and roll out procedures. It also runs php -l on all .php and .html files as a last check for any errors. Once the merge is clean, the developer(s) use the staging servers just as they would our public web site. The database on the staging server is updated nightly from production. It is as close to a production view of our site as you can get without being on production. Assuming the application performs as expected, the developer uses the merge tool to commit the changes to the production branch. They then use the production staging servers to deploy.<br><br><strong>Rolling to Production </strong><br><br>For deploying code and hands on configuration changes into our production systems, we have a staging server in our primary data center. The developer (that is key IMO) logs in to the production staging servers, as a restricted user, and uses our Makefile to update the checkout and rsync the changes to the servers. Each different configuration environment has an accompanying nodes file that lists the servers that are to receive code from the checkout. This ensures that code is rolled to servers in the correct order. If an application server gets new markup before the supporting CSS or images are loaded onto the CDN source servers, you can get an ugly page. The Makefile is also capable of copying files to a single node. We will often do this for big changes. We can remove a node from service, check code out to it, and via VPN access that server directly to review how the changes worked. <br><br>For some services (cron, syslog, ssh, snmp and ntp) we use Puppet to manage configuration and to ensure the packages are installed. Puppet and Gentoo get along great. If someone mistakenly uninstalls cron, Puppet will put it back for us. (I don't know how that could happen, but ya never know). We hope to deploy more and more Puppet as we get comfortable with it. <br><br><strong>Keeping Everyone in the Loop</strong><br><br>Having everyone know what is going on is important. To do that, we start with Trac for ticketing. Secondly, we use OpenFire XMPP server throughout the company. The devops team has a channel that everyone is in all day. When someone rolls code to production, the scripts mentioned above that sync code out to the servers sends a message via an XMPP bot that we wrote using Ruby (Ruby has the best multi-user chat libraries for XMPP). It interfaces with Trac via HTTP and tells everyone what changesets were just rolled and who committed them. So, in 5 minutes if something breaks, we can go back and look at what just rolled.<br><br>In addition to bots telling us things, there is a cultural requirement. Often before a big roll out, we will discuss it in chat. That is the part than can not be scripted or programmed. You have to get your developers and operations talking to each other about things.<br><br><strong>Final Thoughts</strong><br><br>There are some subtle concepts in this post that may not be clear. One is that the code that is written on a development server is the exact same code that is <a href="http://brian.moonspot.net/develop-for-production">used on a production server</a>. It is not massaged in any way. Things like database server names, passwords, etc. are all kept in <a href="http://brian.moonspot.net/using-ini-files-for-php-application-settings">configuration files on each node</a>. They are tailored for the data center that server lives in. Another I want to point out again is that the person that wrote the code is responsible all the way through to production. While at first this may make some developers nervous, it eventually gives them a sense of ownership. Of course, we don't hire someone off the street and give them that access.&nbsp; But it is expected that all developers will have that responsibility eventually.]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Mon, 05 Apr 2010 08:00:00 -0500</pubDate>
            <category>devops</category>
            <category>php</category>
            <category>svn</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/logging-with-mysql</guid>
            <title>Logging with MySQL</title>
            <link>http://brian.moonspot.net/logging-with-mysql</link>
            <description><![CDATA[I was reading a post by <span class="post-author vcard"><span class="fn">Dathan Vance Pattishall titled "</span></span><a href="http://mysqldba.blogspot.com/2010/03/cassandra-is-my-nosql-solution-but.html">Cassandra is my NoSQL solution but..</a>". In the post, Dathan explains that he uses Cassandra to store clicks because it can write a lot faster than MySQL. However, he runs into problems with the read speed when he needs to get a range of data back from Cassandra. This is the number one problem I have with NoSQL solutions.<br><br>SQL is really good at retrieving a set of data based on a key or range of keys. Whereas NoSQL products are really good at writing things and retrieving one item from storage. When looking at redoing our architecture a few years ago to be more scalable, I had to consider these two issues. For what it is worth, the NoSQL market was not nearly as mature as it is now. So, my choices were much more limited. In the end, we decided to stick with MySQL. It turns out that a primary or unique key lookup on a MySQL/InnoDB table is really fast. It is sort of like having a key/value storage system. And, I can still do range based queries against it.<br><br>But, back to Dathan's problem: clicks. We store clicks at dealnews. Lots of clicks. We also store views. We store more views than we do clicks. So, lots of views and lots of clicks. (Sorry for the vague numbers, company secrets and all. We are a top <a href="http://siteanalytics.compete.com/dealnews.com/?metric=rank&months=12">1,000 Compete.com</a> site during peak shopping season.) And we do it all in MySQL. And we do it all with one server. I should disclose we are deploying a 
second server, but it is more for high availability than processing 
power. Like Dathan, we only use about the last 24 hours of data at any given time. There are three keys for us doing logging like this in MySQL.<br><br><strong>Use MyISAM<br><br></strong>MyISAM supports concurrent inserts. Concurrent inserts means that inserts can add rows to the end of a table while selects are being performed on other parts of the data set. This is exactly the use case for our logging. There are caveats with range queries as pointed out by the <a href="http://www.mysqlperformanceblog.com/2006/06/13/myisam-concurrent-insert/">MySQL Performance Blog</a>.<strong> <br><br>Rotating tables<br><br></strong>MySQL (and InnoDB in particular) really sucks at deleting rows. Like, really sucks. Deleting causes locks. Bleh. So, we never delete rows from our logging tables. Instead, nightly we rotate the tables. RENAME TABLE is an (near) atomic process in MySQL. So, we just create a new table.<br><pre>create table clicks_new like clicks;<br>rename table clicks to clicks_2010032500001, clicks_new to clicks;</pre>
<br>Tada! We now have an empty table for today's clicks. We now drop any table with a date stamp that is longer than x days old. Drops are fast, we like drops.<br><br>For querying these tables, we use UNION. It works really well. We just issue a SHOW TABLES LIKE 'clicks%' and union the query across all the tables. Works like a charm.<br><br><strong>Gearman</strong><br><br>So, I get a lot of flack at work for my outright lust for <a href="http://gearman.org/">Gearman</a>. It is my new <a href="http://en.wikipedia.org/wiki/Duct_tape#In_popular_culture">duct tape</a>. When you have a scalability problem, there is a good chance you can solve it with Gearman. So, how does this help with logging to MySQL? Well, sometimes, MySQL can become backed up with inserts. It happens to the best of us. So, instead of letting that pile up in our web requests, we let it pile up in Gearman. Instead of having our web scripts write to MySQL directly, we have them fire Gearman background jobs with the logging data in them. The Gearman workers can then write to the MySQL server when it is available. Under normal operating procedure, that is in near real time. But, if the MySQL server does get backed up, the jobs just queue up in Gearman and are processed when the MySQL server is available.<br><br><strong>BONUS! Insert Delayed</strong><br><br>This is our old trick before we used Gearman. MySQL (MyISAM) has a neat feature where you can have <a href="http://dev.mysql.com/doc/refman/5.1/en/insert-delayed.html">inserts delayed</a> until the table is available. The query is sent to the MySQL server and it answers with success immediately to the client. This means your web script can continue on and not get blocked waiting for the insert. But, MySQL will only queue up so many before it starts erroring out. So, it is not as fool proof as a job processing system like Gearman.<br><br><strong>Summary</strong><br><br>To log with MySQL:<br><ul><li>Use MyISAM with concurrent inserts</li><li>Rotate tables daily and use UNION to query</li><li>Use delayed inserts with MySQL or a job processing agent like Gearman</li></ul>Happy logging!<br><br>PS: You may be asking, "Brian, what about <a href="http://dev.mysql.com/doc/refman/5.1/en/partitioning.html">Partitioned Tables</a>?" I asked myself that before deploying this solution. More importantly, in IRC I asked <a href="http://krow.livejournal.com/">Brian Aker</a> about MySQL partitioned tables. I am paraphrasing, but he said that if I ever think I might alter that table, I would not trust it with the partitions in MySQL. So, that kind of turned me off of them.]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Wed, 24 Mar 2010 00:20:12 -0500</pubDate>
            <category>gearman</category>
            <category>mysql</category>
            <category>php</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/php-progress-bar</guid>
            <title>PHP command line progress bar</title>
            <link>http://brian.moonspot.net/php-progress-bar</link>
            <description><![CDATA[Was just looking through some code and came across this function I wrote some time ago. If you do a lot of your processing scripts in PHP like we do, you probably need to know what is going on sometimes. So, I made a progress bar for use on the cli. I thought I would share it.&nbsp; <a href="http://www.screencast.com/users/brianlmoon/folders/Jing/media/822d9970-a6a3-4071-bdc6-1303cce9800a">Here is a video</a> of it in action. And the <a href="http://brian.moonspot.net/status_bar.php.txt">code can be found here</a>.]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Wed, 10 Mar 2010 11:33:34 -0600</pubDate>
            <category>php</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/php-ob-start-headers</guid>
            <title>ob_start and HTTP headers</title>
            <link>http://brian.moonspot.net/php-ob-start-headers</link>
            <description><![CDATA[I was helping someone in IRC deal with some "headers already sent" issues and told them to use <a href="http://php.net/manual/en/function.ob-start.php">ob_start</a>. Very diligently, the person went looking for why that was the right answer. He did not find a good explination. I looked around and I did not either. So, here is why this happens and why ob_start can fix it.<br><br><strong>How HTTP works</strong><br><br>HTTP is the communication protocol that happens between your web server and the user's browser.&nbsp; Without too much detail, this is broken into two pieces of data: headers and the body.&nbsp; The body is the HTML you send. But, before the body is sent, the HTTP headers are sent. Here is an example of an HTTP request response including headers:<br><code>HTTP/1.1 200 OK<br>Date: Fri, 29 Jan 2010 15:30:34 GMT<br>Server: Apache<br>X-Powered-By: PHP/5.2.12-pl0-gentoo<br>Set-Cookie: WCSESSID=xxxxxxxxxxxxxxxxxxxxxxxxxxxx; expires=Sun, 28-Feb-2010 15:30:34 GMT; path=/<br>Content-Encoding: gzip<br>Vary: Accept-Encoding<br>Keep-Alive: timeout=15, max=99<br>Connection: Keep-Alive<br>Transfer-Encoding: chunked<br>Content-Type: text/html; charset=UTF-8<br><br>
&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"&gt;<br>&lt;html&gt;<br> &lt;head&gt;<br> &lt;title&gt;Ramblings of a web guy&lt;/title&gt;<br>.<br>.<br>
</code>
So, all those lines before the HTML starts have to come first. HTTP headers are where things like cookies and redirection occur. When a PHP script starts to send HTML out to the browser, the headers are stopped and the body begins. When your code tries to set a cookie after this has started, you get the "headers already sent" error message.<br><br><strong>How ob_start works</strong><br><br>So, how does ob_start help? The ob in ob_start stands for output buffering. ob_start will buffer the output (HTML) until the page is completely done. Once the page is completely done, the headers are sent and then the output is sent. This means any calls to setcookie or the header function will not cause an error and will be sent to the browser properly. You do need to call ob_start before any output occurs. If you start output, it is too late.<br><br><strong>The down side</strong><br><br>The down side of doing this is that the output is buffered and sent all at once. That means that the time between the user request and the time the first byte gets back to the user is longer than it has to be. However, in modern PHP application design, this is often already the case. An MVC framework for example would do all the data gathering before any presentation is done. So, your application may not have any issue with this.<br><br>Another down side is that you (or someone) could get lazy and start throwing setcookie calls in any old place. This should be avoided. It is simply not good programming design. In a perfect world, we would not need output buffering to solve this problem for us.]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Fri, 29 Jan 2010 10:00:00 -0600</pubDate>
            <category>http</category>
            <category>php</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/using-ini-files-for-php-application-settings</guid>
            <title>Using ini files for PHP application settings</title>
            <link>http://brian.moonspot.net/using-ini-files-for-php-application-settings</link>
            <description><![CDATA[At dealnews we have three tiers of servers. First is our development servers, then staging and finally production. The complexity of the environment increases at each level. On a development server, everything runs on the localhost: mysql, memcached, etc. At the staging level, there is a dedicated MySQL server. In production, it gets quite wild with redundant services and two data centers.<br><br>One of the challenges of this is where and how to store the connection information for all these services. We have done several things in the past. The most common thing is to store this information in a PHP file. It may be per server or there could be one big file like:<br><br>

<code>

&lt;?php<br>

<br>

if(DEV){<br>

&nbsp;&nbsp;&nbsp;&nbsp;$server = "localhost";<br>

} else {<br>

&nbsp;&nbsp;&nbsp;&nbsp;$server = "10.1.1.25";<br>

}<br>

<br>

?&gt;

</code><br><br>This gets messy quickly. Option two is to deploy a single file that has the settings in a PHP array. And that is a good option. But, we have taken that one step further using some PHP ini trickeration. We use ini files that are loaded at PHP's startup and therefore the information is kept in PHP's memory at all times.<br><br>When compiling PHP, you can specify the --with-config-file-scan-dir to tell PHP to look in that directory for additional ini files. Any it finds will be parsed when PHP starts up. Some distros (Gentoo I know) use this for enabling/disabling PHP extensions via configuration. For our uses we put our custom configuration files in this directory. FWIW, you could just put the above settings into php.ini, but that is quite messy, IMO.<br><br>To get to this information, you can't use ini_get() as you might think.&nbsp; No, you have to use get_cfg_var() instead. get_cfg_var returns you the setting, in php.ini or any other .ini file when PHP was started. ini_get will only return values that are registered by an extension or the PHP core. Likewise, you can't use ini_set on these variables. Also, get_cfg_var will always reflect the initial value from the ini file and not anything changed with ini_set.<br><br>So, lets look at an example.<br><br>

<code>

;&nbsp;db.ini<br>

[myconfig]<br>

myconfig.db.mydb.db&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;mydb<br>

myconfig.db.mydb.user&nbsp;&nbsp;&nbsp;=&nbsp;user<br>

myconfig.db.mydb.pass&nbsp;&nbsp;&nbsp;=&nbsp;pass<br>

myconfig.db.mydb.server&nbsp;=&nbsp;host</code><br>



<br>This is our ini file. the group in the braces is just for looks. It has no impact on our usage. Because this is parsed along with the rest of our php.ini, it needs a unique namespace within the ini scope. That is what myconfig is for. We could have used a DSN style here, but it would have required more parsing in our PHP code.<br><br>



<code>



&lt;?php<br>

<br>

/**<br>

&nbsp;*&nbsp;Creates&nbsp;a&nbsp;MySQLi&nbsp;instance&nbsp;using&nbsp;the&nbsp;settings&nbsp;from&nbsp;ini&nbsp;files<br>

&nbsp;*<br>

&nbsp;*&nbsp;@author&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Brian&nbsp;Moon&nbsp;&lt;brianm@dealnews.com&gt;<br>

&nbsp;*&nbsp;@copyright&nbsp;&nbsp;1997-Present&nbsp;dealnews.com,&nbsp;Inc.<br>

&nbsp;*<br>

&nbsp;*/<br>

<br>

class&nbsp;MyDB&nbsp;{<br>

<br>

&nbsp;&nbsp;&nbsp;&nbsp;/**<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Namespace&nbsp;for&nbsp;my&nbsp;settings&nbsp;in&nbsp;the&nbsp;ini&nbsp;file<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/<br>

&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;INI_NAMESPACE&nbsp;=&nbsp;"dealnews";<br>

<br>

&nbsp;&nbsp;&nbsp;&nbsp;/**<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Creates&nbsp;a&nbsp;MySQLi&nbsp;instance&nbsp;using&nbsp;the&nbsp;settings&nbsp;from&nbsp;ini&nbsp;files<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;&nbsp;&nbsp;string&nbsp;&nbsp;$group&nbsp;&nbsp;The&nbsp;group&nbsp;of&nbsp;settings&nbsp;to&nbsp;load.<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@return&nbsp;&nbsp;object<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/<br>

&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;function&nbsp;init($group)&nbsp;{<br>

<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;$dbs&nbsp;=&nbsp;array();<br>

<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(!is_string($group))&nbsp;{<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;new&nbsp;Exception("Invalid&nbsp;group&nbsp;requested");<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>

<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(empty($dbs["group"])){<br>

<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$prefix&nbsp;=&nbsp;MyDB::INI_NAMESPACE.".db.$group";<br>

<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$db&nbsp;&nbsp;&nbsp;=&nbsp;get_cfg_var("$prefix.db");<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$host&nbsp;=&nbsp;get_cfg_var("$prefix.server");<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$user&nbsp;=&nbsp;get_cfg_var("$prefix.user");<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$pass&nbsp;=&nbsp;get_cfg_var("$prefix.pass");<br>

<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$port&nbsp;=&nbsp;get_cfg_var("$prefix.port");<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(empty($port)){<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$port&nbsp;=&nbsp;null;<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>

<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$sock&nbsp;=&nbsp;get_cfg_var("$prefix.socket");<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(empty($sock)){<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$sock&nbsp;=&nbsp;null;<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>

<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$dbs[$group]&nbsp;=&nbsp;new&nbsp;MySQLi($host,&nbsp;$user,&nbsp;$pass,&nbsp;$db,&nbsp;$port,&nbsp;$sock);<br>

<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(!$dbs[$group]&nbsp;||&nbsp;$dbs[$group]-&gt;connect_errno){<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;new&nbsp;Exception("Invalid&nbsp;MySQL&nbsp;parameters&nbsp;for&nbsp;$group");<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>

<br>

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;$dbs[$group];<br>

<br>

&nbsp;&nbsp;&nbsp;&nbsp;}<br>

<br>

}<br>

<br>

?&gt;



</code>

<br><br>We can now call DB::init("myconfig") and get a mysqli object that is connected to the database we want. No file IO was needed to load these settings except when the PHP process started initially.&nbsp; They are truly constant and will not change while this process is running.<br><br>Once this was working, we created separate ini files for our different datacenters. That is now simply configuration information just like routing or networking configuration. No more worrying in code about where we are.<br><br>We extended this to all our services like memcached, gearman or whatever. We keep all our configuration in one file rather than having lots of them. It just makes administration easier. For us it is not an issue as each location has a unique setting, but every server in that location will have the same configuration.<br><br>Here is a more real example of how we set up our files.<br><br>



<code>

[myconfig.db]<br>

myconfig.db.db1.db&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;db1<br>

myconfig.db.db1.server&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;db1hostname<br>

myconfig.db.db1.user&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;db1username<br>

myconfig.db.db1.pass&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;db1password<br>

<br>

myconfig.db.db2.db&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;db2<br>

myconfig.db.db2.server&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;db2hostname<br>

myconfig.db.db2.user&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;db2username<br>

myconfig.db.db2.pass&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;db2password<br>

<br>

[myconfig.memcache]<br>

myconfig.memcache.app.servers&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;10.1.20.1,10.1.20.2,10.1.20.3<br>

myconfig.memcache.proxy.servers&nbsp;&nbsp;=&nbsp;10.1.20.4,10.1.20.5,10.1.20.6<br>

<br>

[myconfig.gearman]<br>

myconfig.gearman.workload1.servers&nbsp;=&nbsp;10.1.20.20<br>

myconfig.gearman.workload2.servers&nbsp;=&nbsp;10.1.20.21

</code>]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Tue, 19 Jan 2010 17:24:35 -0600</pubDate>
            <category>gearman</category>
            <category>memcached</category>
            <category>mysql</category>
            <category>php</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/php-autoloading</guid>
            <title>Autoloading for fun and profit</title>
            <link>http://brian.moonspot.net/php-autoloading</link>
            <description><![CDATA[So, as I stated in a <a href="http://brian.moonspot.net/php-code-organization">previous
post</a>, the code base here at dealnews is going through some
changes.&nbsp; I have been working heavily on those changes since
then.&nbsp; Testing and benchmarking to see what works best. One of
those changes is the heavy use of <a href="http://php.net/manual/en/language.oop5.autoload.php">autoloading</a>.&nbsp;<br>

<br>
During the holidays, I always like to read the <a href="http://phpadvent.org/2009">PHP Advent</a>.&nbsp; One of the posts
this year was by <a href="http://marcelesser.wordpress.com/">Marcel
Esser</a> titled <em><a href="http://phpadvent.org/2009/you-dont-need-all-that-by-marcel-esser">You
Don’t Need All That</a></em>. It was a great post and echos many
things I have said about PHP and web development. In his post,
Marcel benchmarks the difference between using an autoloader and
using straight require statements. I was not surprised by the
result. The autoloading overhead and class overhead is well known
to me. It is one thing that kept me from using any heavy OOP (we
banned classes on our user facing pages for a long time) in PHP 4
and PHP 5.0. It has gotten a lot better however. Class overhead is
very, very small now. Especially when using it as a namespacing for
static functions. However, there is one word of caution I would
like to add to his statements. When you use require/include
statements instead of autoloading, you end up with a file like
this:<br>

<code>
&lt;?php<br>
<br>
require "DB.php";<br>
require "Article.php";<br>
<br>
<br>
function myfunc1() {<br>
&nbsp;&nbsp;&nbsp;&nbsp;DB::somemethod();<br>
}<br>
<br>
function myfunc2(){<br>
&nbsp;&nbsp;&nbsp;&nbsp;Article::somemethod();<br>
}<br>
<br>
?&gt;</code>That file needs to require two files, but each one is only
needed by one function in the file.&nbsp; This is the dilemma we
have found ourselves in.&nbsp; We have a file that is filled with
functions for building, taking apart, repairing, fetching, or
anything else you can think to do with a URL. So, at the top of
that file are 13 require statements. This all happened organically
over time. But, now we are in a situation where we load lots of
files that may not even be needed to begin with. By moving to an
autloading system, we will only include the code we need. This
saves cycles and file IO.<br>
<br>
Again, Marcel's post was dead on. Our front end is written with
multiple entry points.&nbsp; We use auto_prepend files to do any
common work that all requests need (sessions, loading the
application settings, etc).&nbsp; The front page of <a href="http://dealnews.com/">dealnews.com</a> is about 600 lines of PHP
that does the job as quickly as possible. But, we are moving it to
use autoloaded code because its require list has grown larger than
I would like and some of those requires are not always "required"
to generate the page.<br>
<br>
One point Marcel did make was about how modern MVC systems are part
of the problem. We don't have a highly structured traditional MVC
layout of our code. We use a very wide, flat directory structure
rather than deep nesting of classes and objects. We also don't do a
lot of overloading. Maybe 1% of our classes extend another and
never more than one deep. All that makes for much quicker loading
objects vs. some of the packaged frameworks like Zend
Framework.<br>
<br>
So, as Marcel warned, be aware of both your use of autoloading and
require statements. Both can be bad when used the wrong way.]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Fri, 08 Jan 2010 09:44:47 -0600</pubDate>
            <category>php</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/php-code-organization</guid>
            <title>Code Organization Dilemma</title>
            <link>http://brian.moonspot.net/php-code-organization</link>
            <description><![CDATA[So, we have been building up our code library at <a href=
"http://dealnews.com/">dealnews</a> for 9 years. It was started at
the end of PHP3 and the beginning of PHP4. So, we did not have
autoloading, static functions, and all that jazz. Classes had lots
of overhead in early PHP4 so we started down a pure procedural road
in 2000. And for a long time, it was very maintainable. We had 2 or
3 developers for most of this time. We now have 5 or 6 depending on
whether we have contractors. There are starting to be too many
files and too many functions. We find ourselves adding new files
when some new function is created instead of adding it to an
existing file because we don't want to have huge files with 100
functions in them. File names and function names are getting longer
and more ambiguous. For example, we have a file called
url_functions.php. It contains functions to generate URLs for
different types of pages on the site, functions to fetch URLs from
the web and functions to parse URLs from an article. Those probably
don't all belong in one file. But, they got nickle and dimed in
there over time. So, now, we are inclined to not add anything to
that file and make new files for new semi-URL related functions.
Ugh.<br>
<br>
It is time to start thinkinb about a reorganization. There are
1,900+ functions in 400+ files in our code library. This is just
our library. This does not include the code that actually builds a
page and generates output. It does not include our cron jobs or
system administration scripts. Yeah, that is a lot. So, where do we
go from here? Some things are easy to do. For example, we have a
file called string.php. Most all the functions in that file can
easily be moved a String class with static functions that can be
accessed via an autoloader.<br>
<br>
Then we have the various ways we deal with the articles on the web
site. I have <a href=
"http://brian.moonspot.net/2007/06/23/caching-and-patience/">written</a>
<a href=
"http://brian.moonspot.net/2007/08/29/out-with-cluster-hello-replication/">
about</a> our front end vs. back end system before. What this means
for our code base is that we have two ways to deal with an article.
One is in our highly relational backend system. The other is in our
optimized front end database servers. So, one Article object won't
really do. We already have an Article object that serves as an ORM
interface for the backend. To access the front end data, we
currently have a library of functions (fetch_article for a single,
fetch_articles for a set, etc.) but it does not fit with an
autoloading environment. It also is not related to the object (the
article) and is associated with where the data is stored. New
developers don't grok the server infrastructure, so the code
organization may not make sense to them. We have about 10 different
objects that need both a back end and front end interface.<br>
<br>
On the other hand, I really don't want to end up with a class named
FrontEndArticle and BackEndArticle. Much less do we want to have
stuff like BackEnd_Article where the file is actually in
BackEnd/Article.php somewhere. The verbosity becomes overwhelming
and hard to read, IMO.<br>
<br>
So, what are others doing with huge code bases? I see lots of
projects with 100 or so functions/methods in 20-30 files.&nbsp;
Frameworks have it easy because they don't have a CEO that wants
something on this one page to be different than it is on every
other page where that data is used. We have to deal with those
types of hacks in an elegant way that can be maintained.]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Wed, 18 Nov 2009 08:00:00 -0600</pubDate>
            <category>php</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/forums-are-crap-help-us</guid>
            <title>Forums are crap. Can we get some help?</title>
            <link>http://brian.moonspot.net/forums-are-crap-help-us</link>
            <description><![CDATA[Amy Hoy has written a blog post about why <a href=
"http://sweatyd.posterous.com/forums-are-crap">forums are crap</a>.
And she is right. Forum software does not always do a good job of
helping people communicate. I have worked with Amy. She did a great
analysis of <a href="http://dealnews.com/">dealnews.com</a> that
led to our new design. So, she is not to be ignored.<br>
<br>
However, as a software developer (<a href=
"http://www.phorum.org/">Phorum</a>), I see a lot of problems and
no answers.&nbsp; And it is not all on the software.&nbsp; Web site
owners use forums to solve problems that they really, really suck
at.&nbsp; Ideally, every web site would be very unique for their
audience.&nbsp; They would use a custom solution that fits a number
of patterns that best solves their problem.&nbsp; However, most web
site owners don't want to take the time to do such things.&nbsp;
They want a one stop, drop in solution. See the monolith that is
vBulletin, scary.<br>
<br>
And what if a forum is the best solution? Well, software
developers, in general, are not good designers. They don't think
like normal people. And they don't see their applications as a
whole, but as pieces that do jobs. The forum software market has
been run by software developers for over 10 years. Most of them all
are still copies of what <a href=
"http://en.wikipedia.org/wiki/UBB.classic">UBB</a> was 13 years
ago. And software (like Phorum) that has tried to be different is
shunned by the online communities of the world because they don't
work/look/feel like every other forum software on the planet.<br>
<br>
So, as software developers, what are we to do? We want to make
great software. We want to help our users help their users. But,
what we have been doing for 10+ years has only been adequate. As
the leader of an open source forum software project, I am open to
any and all ideas.]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Mon, 12 Oct 2009 10:01:44 -0500</pubDate>
            <category>mysql</category>
            <category>phorum</category>
            <category>php</category>
            <category>usability</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/what-is-memcached</guid>
            <title>Memcached: What is it and what does it do?</title>
            <link>http://brian.moonspot.net/what-is-memcached</link>
            <description><![CDATA[I spoke at <a href="http://cw.mtacon.com/">CodeWorks</a> in
Atlanta, GA this week.&nbsp; I totally dropped the ball promoting
it on my blog.&nbsp; It was a neat venue.&nbsp; Rather than a large
conference they are doing a traveling show.&nbsp; Seven cities in
14 days.&nbsp; Many of the presenters are working in every
city.&nbsp; Crazy.&nbsp; I was just in Atlanta.&nbsp; It is close
to home and easy for me to get to.<br>
<br>
I spoke about <a href=
"http://code.google.com/p/memcached/">memcached</a>.&nbsp; I tried
to dig a bit deeper into how memcached works.&nbsp; On the mailing
list we get a lot of new people that make assumptions about
memcached.&nbsp; Most talks I have seen focus on why caching is
good, how to use memcached, the performance gain.&nbsp; I kind of
assumed everyone knew that stuff already.&nbsp; I guess you could
say I gave a talk that was the real FAQs of the project.<br>
<br>
Here are the slides.&nbsp; <a href=
"http://twitter.com/Derickr">Derick Rethans</a> took video of the
talk.&nbsp; When he gets that online I will add it to this
post.<br>
<br>

<div style="width: 425px; text-align: left;" id="__ss_2097515">
    <a style=
    "margin: 12px 0pt 3px; font-family: Helvetica,Arial,Sans-serif; font-style: normal; font-variant: normal; font-weight: normal; font-size: 14px; line-height: normal; font-size-adjust: none; font-stretch: normal; display: block; text-decoration: underline;"
    href=
    "http://www.slideshare.net/brianlmoon/memcached-what-is-it-and-what-does-it-do-2097515"
    title="Memcached: What is it and what does it do?">Memcached:
    What is it and what does it do?</a><object style="margin: 0px;"
    width="425" height="355">
        <param name="movie" value=
        "http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=memcached-090930111655-phpapp02&amp;stripped_title=memcached-what-is-it-and-what-does-it-do-2097515">
        <param name="allowFullScreen" value="true">
        <param name="allowScriptAccess" value="always">
        <embed src=
        "http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=memcached-090930111655-phpapp02&amp;stripped_title=memcached-what-is-it-and-what-does-it-do-2097515"
        type="application/x-shockwave-flash" allowscriptaccess=
        "always" allowfullscreen="true" width="425" height="355">
    </object>
    <div style=
    "font-size: 11px; font-family: tahoma,arial; height: 26px; padding-top: 2px;">
    View more <a style="text-decoration: underline;" href=
    "http://www.slideshare.net/">documents</a> from <a style=
    "text-decoration: underline;" href=
    "http://www.slideshare.net/brianlmoon">Brian Moon</a>.
    </div>
</div>]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Wed, 30 Sep 2009 11:49:39 -0500</pubDate>
            <category>memcached</category>
            <category>php</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/wordcraft-0-10-available</guid>
            <title>Wordcraft 0.10 available</title>
            <link>http://brian.moonspot.net/wordcraft-0-10-available</link>
            <description><![CDATA[The latest package of <a href=
"http://brian.moonspot.net/what-is-wordcraft-">Wordcraft</a>, the
PHP/MySQL based blog software that runs this site, is available for
<a href=
"http://code.google.com/p/wordcraft/downloads/list?can=3">download
from Google Code</a>.&nbsp; Just some minor bug fixes and cosmetic
stuff.&nbsp; Its getting a little use in the wild.&nbsp; That is
always fun to see.]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Mon, 10 Aug 2009 00:12:20 -0500</pubDate>
            <category>mysql</category>
            <category>php</category>
            <category>wordcraft</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/php-fork</guid>
            <title>Forking PHP!</title>
            <link>http://brian.moonspot.net/php-fork</link>
            <description><![CDATA[We use PHP everywhere in our stack. For us, it makes sense because
we have hired a great staff of PHP developers. So, we leverage that
talent by using PHP everywhere we can.
<p>
 One place where people seem to stumble with PHP is with long
 running PHP processes or parallel processing. The <a href="http://us.php.net/pcntl">pcntl extension</a> gives you the
 ability to fork PHP processes and run lots of children like
 many other unix daemons might. We use this for various things.
 Most notably, we use it run <a href="http://gearman.org/">Gearman</a> worker processes. While at
 the OReilly Open Sourc Convention in 2009, we were asked about
 how we pulled this off. So, we are releasing the two scripts
 that handle the forking and some instructions on how we use
 them.
</p>

<p>
 This is not a detailed post about long running PHP
 scripts.&nbsp; Maybe I can get to the dos and don'ts of that
 another time.&nbsp; But, these are the scripts we use to manage
 long running processes.&nbsp; They work great for us on
 Linux.&nbsp; They will not run on Windows at all.&nbsp; We also
 never had any trouble running them on Mac OS X.
</p>

<p>
 The first script, prefork.php, is for forking a given function
 from a given file and running <em>n</em> children that will
 execute that function. There can be a startup function that is
 run before any forking begins and a shutdown function to run
 when all the children have died.
</p>

<p>
 The second script, prefork_class.php, uses a class with defined
 methods instead of relying on the command line for function
 names. This script has the added benefit of having functions
 that can be run just before each fork and after each fork. This
 allows the parent process to farm work out to each child by
 changing the variables that will be present when the child
 starts up. This is the script we use for managing our Gearman
 workers. We have a class that controls how many workers are
 started and what functions they provide. I may release a
 generic class that does that soon. Right now it is tied to our
 code library structure pretty tightly.
</p>

<p>
 We have also included two examples. They are simple, but do
 work to show you how the scripts work.
</p>

<p>
 You can download the code from the <a href="http://dealnews.com/developers/">dealnews.com developers'
 page</a>. </p><p><strong>UPDATE: </strong>I have released a <a href="http://github.com/brianlmoon/GearmanManager">Gearman Worker Manager</a> on Github.</p>]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Thu, 23 Jul 2009 13:17:42 -0500</pubDate>
            <category>gearman</category>
            <category>php</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/pecl-memcache-mac-osx</guid>
            <title>Building PECL/memcache on Mac OS X</title>
            <link>http://brian.moonspot.net/pecl-memcache-mac-osx</link>
            <description><![CDATA[My coworker Rob ran into an issue building the PECL/memcache
extension on his Mac.&nbsp; He did <a href=
"http://codelemur.wordpress.com/2009/05/30/pecl-memcache-and-php-on-mac-os-x-leopard/">
find the solution</a> however.&nbsp; You can read and leave
comments on his blog.]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Sun, 31 May 2009 22:23:29 -0500</pubDate>
            <category>apache</category>
            <category>apple</category>
            <category>memcached</category>
            <category>php</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/using-the-glammp-stack</guid>
            <title>The rise of the GLAMMP stack</title>
            <link>http://brian.moonspot.net/using-the-glammp-stack</link>
            <description><![CDATA[First there was <a href=
"http://en.wikipedia.org/wiki/LAMP_%28software_bundle%29">LAMP</a>.&nbsp;
But are you using GLAMMP?&nbsp; You have probably not heard of it
because we just coined the term while chatting at work.&nbsp; You
know LAMP (Linux, Apache, MySQL and PHP or Perl and sometimes
Python). So, what are the extra letters for?<br>
<br>
<strong>The G is for Gearman</strong> - <a href=
"http://www.gearman.org/">Gearman</a> is a system to farm out work
to other machines, dispatching function calls to machines that are
better suited to do work, to do work in parallel, to load balance
lots of function calls, or to call functions between languages.<br>
<br>
<strong>The extra M is for Memcached</strong> - <a href=
"http://www.danga.com/memcached/">memcached</a> is a
high-performance, distributed memory object caching system, generic
in nature, but intended for use in speeding up dynamic web
applications by alleviating database load.<br>
<br>
More and more these days, you can't run a web site on <em>just</em>
LAMP.&nbsp; You need these extra tools (or ones like them) to do
all the cool things you want to do.&nbsp; What other tools do we
need to work into the acronym?&nbsp; <a href=
"http://www.postgresql.org/">PostgreSQL</a> replaces MySQL in lots
of stacks to form LAPP.&nbsp; I guess <a href=
"http://drizzle.org/">Drizzle</a> may replace MySQL in some stacks
soon.&nbsp; For us, it will likely be <a href=
"http://brian.moonspot.net/2008/07/24/156/">added to the
stack</a>.&nbsp; Will that make it GLAMMPD?&nbsp; We need more
vowels!&nbsp; If you are starting the next must use tool for
running web sites on open source software, please use a vowel for
the first letter.<br>
<br>]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Fri, 22 May 2009 11:34:39 -0500</pubDate>
            <category>apache</category>
            <category>gearman</category>
            <category>linux</category>
            <category>memcache</category>
            <category>mysql</category>
            <category>php</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/finding-register-globals</guid>
            <title>Is there a program for finding uses of register_globals?</title>
            <link>http://brian.moonspot.net/finding-register-globals</link>
            <description><![CDATA[register_globals is going way in PHP6.&nbsp; That is fine with
me.&nbsp; Super globals are cool and I have taken to using <a href=
"http://php.net/filter_input_array">filter_input_array</a> these
days anyhow.&nbsp; However, our code base is now 10+ years old at
dealnews.&nbsp; Most of the forward facing code was completely
rewritten in the last couple of years due to architecture
changes.&nbsp; Many new projects had register_globals turned off
via php_admin_flag in Apache.&nbsp; So, that area is not that big
of a problem.&nbsp; However, our internal admin areas have not all
be rewritten because, well frankly, they still work.&nbsp; Yeah,
stuff written for PHP4 in 2000 is still working.&nbsp; KISS helps a
lot with that.&nbsp; But, this code, somewhere in there, may still
be relying on register_globals.&nbsp; Now, we could go line by line
and try and fix it.&nbsp; But, it seems like a program could be
written to do this job.&nbsp; I mean, I use jEdit and it can
highlight unset vars using the PHPParserPlugin just fine.&nbsp; I
bet Zend IDE can do the same.&nbsp; Has anyone written such a tool
for the command line?&nbsp; There will be false positives I
know.&nbsp; Things like passing a variable by reference to a
function would look like a use before set.&nbsp; But, I can deal
with those if I don't have to go line by line through tons of old
code.&nbsp; What would the rules look like for such an
animal?&nbsp; This would be a great project to get off the ground
before PHP6 hits.&nbsp; Ideally you could provide a list of
variables for it to ignore.&nbsp; We have some globals we set up in
prepends and includes.<br>]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Fri, 15 May 2009 12:38:58 -0500</pubDate>
            <category>php</category>
            <category>security</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/speaking-velocity-2009</guid>
            <title>Scaling for the Expected and Unexpected - Speaking at Velocity</title>
            <link>http://brian.moonspot.net/speaking-velocity-2009</link>
            <description><![CDATA[Last year I was surprised to be <a href=
"http://brian.moonspot.net/2008/06/18/did-you-know-i-am-going-to-be-at-velocity/">
going to Velocity</a>.&nbsp; Read the post, it was an
adventure.&nbsp; But, I <a href=
"http://brian.moonspot.net/2008/07/01/velocity-conference-roundup/">
really like the conference</a>.&nbsp; It is the perfect conference
for me.&nbsp; While a good majority of my work is done coding
PHP/MySQL apps, I tend to focus on architecture, frameworks,
performance and that kind of stuff.&nbsp; So, a web performance and
operations conference is just perfect.<br>
<br>
Last year, I was on a panel with <a href=
"http://en.oreilly.com/velocity2008/public/schedule/detail/4762">some
great guys</a>.&nbsp; I was able to share just a bit about my
experience dealing with the instant success of a web site.&nbsp;
This year, my proposal was accepted to talk more about dealing with
success of a web site.&nbsp; The talk will be focused on my
experience at <a href="http://dealnews.com/">dealnews.com</a> and
from working with power users for <a href=
"http://www.phorum.org/">Phorum</a>.&nbsp; Here is the summary:<br>

<blockquote>
    <div>
        <p>
            Lots of people talk about scaling and performance. But,
            are they preparing for all the things that could
            happen? There are multiple problems and there is not
            one solution to solve them all.
        </p>

        <p>
            Everything is running fine and <span class=
            "caps">BAM</span>! – your site is linked from the front
            page of Yahoo! What do you do? How can you handle that
            sudden rush of traffic. Requests per second are running
            5x normal levels. Servers have <span class=
            "caps">CPU</span> spikes. Daemons are hitting the
            maximums. You are running out of bandwidth. How could
            you have been prepared for this? What are the tools and
            techniques for this type of sudden rush?
        </p>

        <p>
            Or, lets say you have just come out of a meeting where
            everyone discovered that your site is growing in
            traffic 70% – 80% year over year. That means that 1
            million page views this month will be nearly 3 million
            this time in 2 years. How can you plan for that? You
            don’t want to redesign the whole architecture every 2
            years. What methods could be used to deal with this
            constant long term growth?
        </p>

        <p>
            While there is no magic bullet for either of these
            scenarios, there are techniques used by many sites out
            there to help you get through these situations. This
            session will cover some of these techniques and talk
            about their pros and cons.
        </p>
    </div>
</blockquote>

<div>
    <p>
        I must admit, this if the first time since 2000 that I am a
        little intimidated to speak at a conference.&nbsp; The
        people that present and attend Velocity are so
        awesome.&nbsp; I just hope I don't disappoint.
    </p>
</div>]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Thu, 07 May 2009 11:57:27 -0500</pubDate>
            <category>mysql</category>
            <category>php</category>
            <category>velocity</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/net-gearman-php-5.2.9</guid>
            <title>Net::Gearman and PHP 5.2.9</title>
            <link>http://brian.moonspot.net/net-gearman-php-5.2.9</link>
            <description><![CDATA[I just discovered an incompatibility between <a href=
"http://pear.php.net/package/Net_Gearman/">Net Gearman</a> and PHP
5.2.9+.&nbsp; json_decode was <a href=
"http://bugs.php.net/bug.php?id=45989">changed in 5.2.9</a> to
return NULL on invalid JSON strings.&nbsp; Previously, the bare
string had been returned if it was not valid JSON.&nbsp; This was
nice in a way as you could pass a scalar string to json_decode and
not worry about it.&nbsp; But, in reality, it would make debugging
a nightmare for JSON.<br>
<br>
I have updated <a href=
"http://github.com/brianlmoon/net_gearman/tree/master">my github
fork</a> and requested a pull into the <a>main branch</a>.&nbsp;
Once that is done a new PEAR release can be done.]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Tue, 21 Apr 2009 12:48:53 -0500</pubDate>
            <category>gearman</category>
            <category>php</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/dont-use-the-die-function</guid>
            <title>The death of die()</title>
            <link>http://brian.moonspot.net/dont-use-the-die-function</link>
            <description><![CDATA[I am calling it.&nbsp; The death of the PHP <a href=
"http://www.php.net/die">die</a> function.&nbsp; Now, I have no
actual authority to do so.&nbsp; My PHP CVS karma does not extend
that far.&nbsp; And I doubt it will actually get removed despite it
being nothing more than an alias for <a href=
"http://www.php.net/exit">exit</a> now.<br>
<br>
No, what I would like to call a death to is the usage of die such
as:<br>
<br>
<code>$conn = mysql_connect($server, $user, $pass) or die("Could
not connect to MySQL, but needed to tell the whole
world");</code><br>
I don't know who thought that particular usage was good, but they
need to .... no, that is harsh.&nbsp; I just really wish they had
never done that.<br>
<br>
So, what should you use?&nbsp; Well, there are a couple of options
depending on what context you are working in and whether or not the
failure is actually catastrophic.<br>
<br>
<strong>Exceptions</strong><br>
<br>
If you are using OOP in your PHP code, <a href=
"http://www.php.net/exceptions">Exceptions</a> are the logic choice
for dealing with errors.&nbsp; I have mixed feelings about
them.&nbsp; But, it has more to do with the catching of exceptions
than the throwing of them.&nbsp; If you are going to live in a
world of exceptions, please catch them and provide useful error
messages.&nbsp; The PHP world is not too bad about that, but I have
read too many Java error logs full of huge, verbose exception dumps
in my life already.&nbsp; Please don't follow that technique in
PHP.<br>
<br>
<strong>trigger_error</strong><br>
<br>
The function <a href=
"http://www.php.net/trigger_error">trigger_error</a> is quite
handy.&nbsp; It allows you, a common PHP coder, to create errors
just like the core system.&nbsp; So, the error messages are
familiar to anyone that is used to seeing PHP errors.&nbsp; So, if
your system is configured to log errors and not display them,
errors from trigger_error will be treated the same as built in
errors.<br>
<br>
Also, errors thrown with trigger_error are caught by a custom
<a href="http://www.php.net/set_error_handler">error handler</a>
just like built in errors.&nbsp; They can be logged, printed,
whatever you want from that error handler, just like normal PHP
errors.&nbsp; There are even several levels of errors you can raise
like notices, warnings, errors, and even deprecated.&nbsp; Again,
just like the built in PHP errors.<br>
<br>
<strong>FATAL Errors</strong><br>
<br>
trigger_error is also the most suitable way, IMO, to end a script
immediately.<br>
<br>
<code>$conn = mysql_connect($server, $user, $pass);<br>
if(!$conn) {<br>
&nbsp;&nbsp;&nbsp; trigger_error("Could not connect to MySQL
database.", E_USER_ERROR);<br>
}</code><br>
Now that will not be told to the whole world if you have
display_errors set to Off as you should in any production
environment.]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Thu, 16 Apr 2009 23:13:42 -0500</pubDate>
            <category>php</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/wordcraft-0-9-1-available</guid>
            <title>Wordcraft 0.9.1 available</title>
            <link>http://brian.moonspot.net/wordcraft-0-9-1-available</link>
            <description><![CDATA[There are several key changes in Wordcraft 0.9.1. The two big
things are:<br>

<ul>
    <li>
        <span>Tokens on post forms in the admin to help ward off
        <a href=
        "http://shiflett.org/articles/cross-site-request-forgeries">
        CSRF attacks</a>.&nbsp;&nbsp;</span>
    </li>

    <li>
        <span>Database schema updates automated.</span>
    </li>
</ul>
<span>The first comes as a result of us doing the same work on
Phorum recently.&nbsp; I realized I needed the same protection in
Wordcraft.&nbsp; The second was done out of neccesity as I changed
the datetime fields in the database schema into int fields.&nbsp;
Not sure why I ever made them datetime fields.&nbsp; Unix
timestamps are much easier to work with.&nbsp; It saves many
strtotime() calls and will make eventual time zone settings much
easier to implement.<br>
<br>
In addition to those two big ones, there were some notable small
ones:<br></span>
<ul>
    <li>HTML 4.01 validation fixes
    </li>

    <li>Ensuring UTF-8 on all encoding function calls
    </li>

    <li>Protection against hitting the back button when writing a
    post (most annoying on Macs as the back button and the
    beginning of line keystroke is the same).
    </li>
</ul>
And there were other a few <a href=
"http://code.google.com/p/wordcraft/source/list">other bug
fixes</a>.<br>
<br>
I will or course need many more testers and users before I can ever
declare this software as stable.&nbsp; If you need a simple blog,
give it a try.<br>
<br>
<strong>About Wordcraft</strong><br>
Wordcraft aims to be a simple, lightweight blogging
application.&nbsp; Wordcraft is written exclusively for PHP 5+ and
MySQL 5.0+ using only the PHP mysqli extension, UTF-8, and HTML
4.01 to achieve that simpleness.<br>]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Wed, 08 Apr 2009 08:00:00 -0500</pubDate>
            <category>mysql</category>
            <category>php</category>
            <category>wordcraft</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/mod-substitute-mod-proxy-problem</guid>
            <title>mod_substitute is cool.  But, be careful with mod_proxy</title>
            <link>http://brian.moonspot.net/mod-substitute-mod-proxy-problem</link>
            <description><![CDATA[For our development servers, we have always used output buffering
to replace the URLs (dealnews.com) with the URL for that
development environment.&nbsp; Where we run into problems is with
CSS and JavaScript.&nbsp; If those files contains URLs for images
(CSS) or AJAX (JS) the URLS would not get replaced.&nbsp; Our
solution has been to parse those files as PHP (on the dev boxes
only) and have some output buffering replace the URLs in those
files.&nbsp; That has caused various problems over the years and
even some confusion for new developers.&nbsp; So, I got to looking
for a different solution.&nbsp; Enter <span><a href=
"http://httpd.apache.org/docs/2.2/mod/mod_substitute.html">mod_substitute</a>
for Apache 2.2.</span><br>

<blockquote>
    <em><a href=
    "http://httpd.apache.org/docs/2.2/mod/mod_substitute.html">mod_substitute</a>
    provides a mechanism to perform both regular expression and
    fixed string substitutions on response bodies.</em> - Apache
    Documentation<br>
</blockquote>
Cool!&nbsp; I put in the URL mappings and VIOLA!&nbsp; All was
right in the world.<br>
<br>
Fast forward a day.&nbsp; Another developer is testing some new
code and finds that his XML is getting munged.&nbsp; At first we
blamed libxml because we had just been through an ordeal with a bad
combination of a libxml compile option and PHP a while back.&nbsp;
Maybe we missed that box when we fixed it.&nbsp; We recompiled
everything on the dev box but there was no change.&nbsp; So I
started to think what was recently different with the dev
boxes.&nbsp; So, I turn off mod_substitute.&nbsp; Dang, that fixed
it.&nbsp; I looked at my substitution strings and everything looked
fine.&nbsp; After cursing and being depressed that such a cool tool
was not working, I took a break to let it settle in my mind.<br>
<br>
I came back to the computer and decided to try a virgin Apache 2.2
build.&nbsp; I downloaded the source from the web site instead of
building from Gentoo's Portage.&nbsp; Sure enough, a simple test
worked fine.&nbsp; No munging.&nbsp; So, I loaded up the dev box
Apache configuration into the newly compiled Apache.&nbsp; Sure
enough, munged XML.&nbsp; ARGH!!<br>
<br>
Up until this point, I had configured the substitutions globally
and not in a particular virtual host.&nbsp; So, I moved it all into
one virtual host configuration.&nbsp; Still broken.<br>
<br>
A little more background on our config.&nbsp; We use mod_proxy to
emulate some features that we get in production with our F5 BIG-IP
load balancers.&nbsp; So, all requests to a dev box hit a mod_proxy
virtual host and are then directed to the appropriate virtual host
via a proxied request.&nbsp;<br>
<br>
So, I got the idea to hit the virtual host directly on its port and
skip mod_proxy.&nbsp; Dang, what do you know.&nbsp; It worked
fine.&nbsp; So, something about the output of the backend request
and mod_proxy was not playing nice.&nbsp; So, hmm.&nbsp; I got the
idea to move the mod_substitute directives into the mod_proxy
virtual hosts configuration.&nbsp; Tested and working fine.&nbsp;
So, basically, this ensures that the substitution filtering is done
only after the proxy and all other requests have been
processed.&nbsp; I am no Apache developer, so I have not dug any
deeper.&nbsp; I have a working solution and maybe this blog post
will reach someone that can explain it.&nbsp; As for
mod_substitute, here is the way my config looks.<br>
<br>
In the VirtualHost that is our global proxy, I have this:<br>
<br>
<code>FilterDeclare DN_REPLACE_URLS<br>
FilterProvider DN_REPLACE_URLS SUBSTITUTE resp=Content-Type
$text/<br>
FilterProvider DN_REPLACE_URLS SUBSTITUTE resp=Content-Type
$/xml<br>
FilterProvider DN_REPLACE_URLS SUBSTITUTE resp=Content-Type
$/json<br>
FilterProvider DN_REPLACE_URLS SUBSTITUTE resp=Content-Type
$/javascript<br>
FilterChain DN_REPLACE_URLS</code><br>
<br>
Elsewhere, in a file that is local to each dev host, I keep the
actual mappings for that particular host:<br>
<br>
<code>Substitute
"s|http://dealnews.com|http://somedevbox.dealnews.com|in"<br>
Substitute
"s|http://dealmac.com|http://somedevbox.dealmac.com|in"<br>
# etc....</code><br>
<br>
I am trying to think of other really cool uses for this.&nbsp; Any
ideas?]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Tue, 07 Apr 2009 20:03:22 -0500</pubDate>
            <category>apache</category>
            <category>html</category>
            <category>php</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/best-practices-escape-html</guid>
            <title>Best practices for escaping HTML</title>
            <link>http://brian.moonspot.net/best-practices-escape-html</link>
            <description><![CDATA[I am working on <a href=
"http://code.google.com/p/wordcraft/">Wordcraft</a>, trying to get
the last annoying HTML validation errors worked out.&nbsp; Thinks
like ampersands in URLs.&nbsp; In doing so, I am asking myself
where the escaping should take place. In the case of Wordcraft,
there are several parts to it.<br>

<ol>
    <li>The code that pulls data from the database.&nbsp; Obviously
    not the right place.
    </li>

    <li>The code that formats data like dates and such.&nbsp; It
    also organizes data from several data sources into one nice
    tidy array.&nbsp; Hmm, maybe
    </li>

    <li>The parts of the code that set up the output data for the
    templates.
    </li>

    <li>The templates themselves.
    </li>
</ol>
Now, I am sure 1 is not the place.&nbsp; And I really would not
want 4 to be the place.&nbsp; That would make for some ugly
templating.&nbsp; Plus, the templates, IMO, should assume the data
is ready to be output.&nbsp; So, that leaves the code that does the
formatting and the code that does the data setup.<br>
<br>
Of those two, I guess the place to do this job is in the data
setup.&nbsp; Wordcraft has a $WCDATA array that is available in the
scope of the templates.&nbsp; I suppose anything that goes into
that array should be escaped as appropriate.<br>
<br>
I largely wrote this blog post as a <a href=
"http://headrush.typepad.com/creating_passionate_users/2005/01/rubberducking_a.html">
teddy bear exercise</a>.&nbsp; But, I am curious.&nbsp; Where and
when do you escape your data for use in HTML documents?]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Fri, 20 Mar 2009 22:55:00 -0500</pubDate>
            <category>html</category>
            <category>php</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/php-history-newline-closing-tag</guid>
            <title>The history of PHP eating newlines after the closing tag</title>
            <link>http://brian.moonspot.net/php-history-newline-closing-tag</link>
            <description><![CDATA[Have you ever noticed that PHP eats the newlines after a closing
PHP tag?&nbsp; Not sure what I mean?&nbsp; There is lots on
<a href="http://www.google.com/search?q=php+newline+close+tag">Google
about it</a>.&nbsp; Here is an example.<br>
<br>
<code>Hello there!<br>
&lt;?php<br>
<br>
// this is just a dump PHP block<br>
<br>
?&gt;<br>
How are you?</code><br>
becomes:<br>
<br>
<code>Hello there!<br>
How are you?</code><br>
I was talking about this with a coworker tonight.&nbsp; He is
trying to generate some XML and, like me and <a href=
"http://shiflett.org/blog/2005/oct/php-stripping-newlines">Chis
Shiflett</a>, is anal about his output.&nbsp; You see, what happens
in modern use of PHP as a template language is something like
this:<br>
<br>
<code>&lt;?php<br>
<br>
$subelement = range(1, 10);<br>
<br>
?&gt;<br>
&lt;somexml&gt;<br>
&nbsp;&nbsp;&nbsp; &lt;element&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;?php
foreach($subelement as $e) { ?&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;subelement&gt;&lt;?php echo $e; ?&gt;&lt;/subelement&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;?php } ?&gt;<br>
&nbsp;&nbsp;&nbsp; &lt;/element&gt;<br>
&lt;/somexml&gt;</code><br>
That code will output this mess:<br>
<br>
<code>&lt;somexml&gt;<br>
&nbsp;&nbsp;&nbsp; &lt;element&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;subelement&gt;1&lt;/subelement&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;subelement&gt;2&lt;/subelement&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;subelement&gt;3&lt;/subelement&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;subelement&gt;4&lt;/subelement&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;subelement&gt;5&lt;/subelement&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;subelement&gt;6&lt;/subelement&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;subelement&gt;7&lt;/subelement&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;subelement&gt;8&lt;/subelement&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;subelement&gt;9&lt;/subelement&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;subelement&gt;10&lt;/subelement&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&lt;/element&gt;<br>
&lt;/somexml&gt;</code><br>
So, why does PHP do this?&nbsp; Well, you have to <a href=
"http://marc.info/?t=90279165800002&amp;r=1&amp;w=2">go back 11
years</a>.&nbsp; PHP 3 was emerging.&nbsp; I was just starting to
use it for <a href="http://www.phorum.org/">Phorum</a> at the
time.&nbsp; There were two reasons.<br>
<br>
The first was that you would want the newline after the first
closing tag to be removed as it would remove the existence of the
PHP block completely.&nbsp; At the time, people were shunned for
writing PHP as a tag looking language.&nbsp; <a href=
"http://en.wikipedia.org/wiki/ColdFusion">ColdFusion</a> was new
then too and the PHP community liked to point and laugh at it.<br>
<br>
The second case (and this is probably a more legitimate one) was
that many editors (some still do this for some insane reason) force
every friggin file to end in a newline.&nbsp; We did not have
output buffering in those days.&nbsp; It was the stone age
man.&nbsp; So, to get around the "Headers already sent" errors,
Zeev decided to make the PHP ending tag be "?&gt; with an optional
newline".&nbsp; It was a heated debate on the PHP Internals (then
php-dev) list.&nbsp; So much that I remembered it and <a href=
"http://marc.info/?t=90279165800002&amp;r=1&amp;w=2">dug it up on
MARC</a>.<br>
<br>
Heck, now I want to add to it.&nbsp; I would like it please if PHP
could remove any leading, non-newline whitespace before an open
tag.&nbsp; That would solve this problem.&nbsp; Yeah, more
magic!&nbsp; Nothing like it.<br>
<br>
To me, the worst alternative to all this is the lack of a closing
tag in a file.&nbsp; <a href="http://xkcd.com/100/">My OCD</a> just
can't deal with that.&nbsp; Please, baby seals cry when you don't
use a closing tag.]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Tue, 03 Mar 2009 21:51:18 -0600</pubDate>
            <category>php</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/wordcraft-0-8-available</guid>
            <title>Wordcraft 0.8 available</title>
            <link>http://brian.moonspot.net/wordcraft-0-8-available</link>
            <description><![CDATA[I am pleased to announce the release of <span><a href=
"http://wordcraft.googlecode.com/files/wordcraft-0.8.tar.gz">Wordcraft
0.8</a>.&nbsp; I have managed to release about once a month since
November.&nbsp; I also have actually gotten some feedback and
tickets posted.&nbsp; Thanks to those that have tried it out.<br>
<br>
I have decided to go back to YUI's Editor.&nbsp; I tried TinyMCE in
the last release.&nbsp; But, using it full time I found it messed
with my HTML too much for my liking.&nbsp; When I would switch to
raw HTML mode and add something like a &lt;code&gt; tag, it would
be lost when saving the data back into the WYSIWYG editor.<br>
<br>
I also converted the admin HTML to HTML 4.01 Transitional.&nbsp; I
never use XHTML anymore these days.&nbsp; So, I was writing invalid
XHTML inadvertantly.<br>
<br>
I worked on the session handling some more in this release.&nbsp;
Users should stay logged in to the admin better now.<br>
<br>
I put comment blocks in all the files and documented every
function.&nbsp; This should help anyone wanting to dig in and help
out.<br>
<br>
I fixed several bugs reported by users (or maybe just testers, not
sure).&nbsp; Thanks for that and keep the feedback
coming.<br></span>]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Mon, 23 Feb 2009 08:00:00 -0600</pubDate>
            <category>Blogging</category>
            <category>MySQL</category>
            <category>PHP</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/mysql-alter-multiple-things</guid>
            <title>Altering several things in a MySQL table at one time</title>
            <link>http://brian.moonspot.net/mysql-alter-multiple-things</link>
            <description><![CDATA[To some kick ass DBA or someone that works on MySQL internals, this
is probably a no brainer.&nbsp; But, for just joe schmoe power user
types, we wonder about these things.&nbsp; We could not find the
answer on the internet, so I decided to blog about it.&nbsp; We
expected the result we found, but I think it's good
information.<br>
<br>
Have you ever had to make changes to several indexes or columns in
a table?&nbsp; With <a href="http://www.phorum.org/">Phorum</a>, we
keep a series of scripts that run queries to upgrade the database
as we make changes.&nbsp; Recently I had to delete about 12 keys
and add about 7 back in their place.&nbsp; I initially thought to
make two sql statements.&nbsp; One to delete indexes and one to
create new ones.&nbsp; But, Thomas, one of the other developers,
wondered if that was really any better.&nbsp; So, I decided to run
some tests.<br>
<br>
First I altered a table with 70k rows, dropping one key.&nbsp; That
took 16 seconds.&nbsp; I then added a key to that same table.&nbsp;
Again, about 16 seconds.&nbsp; So, I then dropped a key and added a
key in one query.&nbsp; Again 16 seconds.&nbsp; So, I decided to go
for the ultimate challenge.&nbsp; I dropped 12 keys and added back
12 keys all in one query.&nbsp; TADA!&nbsp; 16 or so seconds.&nbsp;
The table was an InnoDB table, so I repeated after converting the
table to MyISAM.&nbsp; Again, all the alters took about the same
time, regardless of the number of changes.<br>
<strong><br>
Update</strong><br>
<strong><br></strong>I was told in the comments that the data was
not big enough for a real test.&nbsp; I hold to the fact that
because, as Eric Bergen pointed out, the most time is taken in
copying the data, the data size is not that important here.&nbsp;
However, I tried it out on a table with 750k rows just to
see.&nbsp; Dropping 4 keys on a table with 12 keys took 1min
46sec.&nbsp; Adding 4 keys to the same table, (now with 8 keys)
took 1min 52sec.&nbsp; Adding 4 keys and dropping 4 keys in one
alter table took 1min 49sec.&nbsp; So, looks like I was
right.&nbsp; For fun, I decided to drop all the keys except the
primary.&nbsp; That only took 28sec.&nbsp; That is what I
guessed.&nbsp; The slow part is copying the table to a new
table.&nbsp; That includes filling in indexes.&nbsp; It is much
faster to write a table with indexes than it is a table with 12
indexes.&nbsp; However, that was not my use case and not the reason
for my testing.&nbsp; But, it is good information to attach to this
blog post.]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Sun, 22 Feb 2009 18:00:00 -0600</pubDate>
            <category>mysql</category>
            <category>php</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/null-isset-php</guid>
            <title>Null vs. isset()</title>
            <link>http://brian.moonspot.net/null-isset-php</link>
            <description><![CDATA[<p>
    I am working with a newcomer to PHP and he asked me about
    setting a variable to null and how to check that.&nbsp; He had
    found some example or information that showed that setting a
    varaible equal to null would unset the variable.&nbsp; So, he
    was unclear if he could then reliably check if the variable was
    equal to null.&nbsp; Having avoided null like the plague in my
    years of PHP, I was not sure.&nbsp; So, I mocked up a quick
    script to see what the states of a variable are in relation to
    null.
</p>

<pre>
&lt;?php<br>
<br>
error_reporting(E_ALL);<br>
<br>
$null = null;<br>
<br>
if($null == null){<br>
  echo "Yes, it is equivilent to null\n";<br>
}<br>
<br>
if($null === null){<br>
  echo "Yes, it is null\n";<br>
}<br>
<br>
if(isset($null)){<br>
  echo "Yes, it is set\n";<br>
}<br>
<br>
if(empty($null)){<br>
  echo "Yes, it is empty\n";<br>
}<br>
<br>
?&gt;<br>
</pre>
<p>
    The output of this script is:
</p>

<pre>
Yes, it is equivilent to null<br>
Yes, it is null<br>
Yes, it is empty<br>
</pre>
<p>
    It should also be noted that without declaring the variable,
    you will see PHP notices about an undefined variable.&nbsp; So,
    there is a slight difference between and unset variable and a
    variable that has been set to null.
</p>

<pre>
<br>
</pre>]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Wed, 28 Jan 2009 14:46:30 -0600</pubDate>
            <category>php</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/wordcraft-0-7-available</guid>
            <title>Wordcraft 0.7 available</title>
            <link>http://brian.moonspot.net/wordcraft-0-7-available</link>
            <description><![CDATA[<p>
    I am happy to announce <a href=
    "http://wordcraft.googlecode.com/files/wordcraft-0.7.tar.gz">Wordcraft
    0.7</a>.&nbsp; There are two big changes in this release.&nbsp;
    On the front end, I added a simple search.&nbsp; It just uses a
    LIKE clause.&nbsp; But, I figure a lot of blogs never reach
    1,000 posts.&nbsp; Even at 10,000 posts, a LIKE would not be
    too bad.&nbsp; On the backend, I have switched the post editor
    to <a href="http://tinymce.moxiecode.com/">TinyMCE</a>. YUI's
    editor is decent, but it needs polish.&nbsp; Perhaps my time
    with Wordpress just made me more familiar with it.&nbsp;
    TinyMCE does save XHTML.&nbsp; I put some code in the PHP side
    to use Tidy if it is available to convert it to HTML 4.01,
    which I prefer.
</p>

<p>
    Also in this release:
</p>

<ul>
    <li>Fixed an XSS issue in tag.php.
    </li>

    <li>Fixing a parsing issue with anchor tags when doing
    pingbacks.
    </li>

    <li>Fixed an error when the remote site can not be contacted.
    </li>

    <li>Fix for pingbacks with nice URLs enabled. Was blocking
    pingbacks.
    </li>
</ul>]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Mon, 12 Jan 2009 08:00:00 -0600</pubDate>
            <category>mysql</category>
            <category>php</category>
            <category>wordcraft</category>
        </item>
        <item>
            <guid>http://brian.moonspot.net/seven-things-about-brian-moon</guid>
            <title>Seven Things about me - tagged by Brian DeShong</title>
            <link>http://brian.moonspot.net/seven-things-about-brian-moon</link>
            <description><![CDATA[So, my Haystacks teammate <a href=
"http://www.deshong.net/?p=145">Brian DeShong</a> tagged me in his
list of seven.&nbsp; We won<br>
that trivia contest by the way.&nbsp; It was a real team
effort.<br>
<br>
So, here goes my seven things:<br>

<ol>
    <li>I have six kids.&nbsp; Okay, let that sink in.&nbsp; Yes,
    six.&nbsp; Logan(12), Macy(11), Molly(9), Parker(7), Collin(3),
    and Hudson(6 months).&nbsp; I know what causes it.&nbsp; Yes,
    it is hard at times.&nbsp; But, there are those moments when
    you are sitting in the yard or in the den and all is right in
    the world.&nbsp; The best program I will ever write will not
    compare to what have done with my children.&nbsp; They are
    truly my greatest project.&nbsp; My wonderful wife blogs about
    them at <a href="http://moonmania.blogspot.com/">Moonmania</a>.
    </li>

    <li>I started my career as a Visual Basic programmer.&nbsp; PHP
    and VB are very much alike.&nbsp; Neither is OOP, yet people
    keep trying to make them so.&nbsp; You can write truly powerful
    applications if you know what you are doing.&nbsp; Much of the
    outside community thinks poorly of the language.&nbsp; In both
    cases I was way too busy making cool things and getting stuff
    done to care or pay attention.
    </li>

    <li>I play <a href="http://www.pdga.com/">disc golf</a>.&nbsp;
    It is a great sport.&nbsp; It reminds me of the Open Source
    community in that it is a very community driven sport.&nbsp;
    The courses are installed and maintained by the players,
    usually on public land such as parks.&nbsp; The tournaments are
    run by local players.&nbsp; The sport is largely managed by
    players.&nbsp; I maintain the web site of one of the larger
    regional (maybe the largest) <a href="http://www.sndg.org/">
        tournament series</a> in the world.&nbsp; I have played in
        most cities that I have visited including those I go to for
        conferences.&nbsp; We have a group of 9 or so people at
        dealnews that play on a semi-regular basis when the weather
        is right.
    </li>

    <li>I never graduated college.&nbsp; The VB work and then the
    PHP work got in the way.&nbsp; It is a regret in some
    ways.&nbsp; But, I can't tell you what I would have changed
    about how my life has gone.&nbsp; Perhaps I will go back and
    finish my degree at some point.&nbsp; Sadly, in this world,
    there are doors that are closed to you no matter how brilliant
    you are if you don't have a simple piece of paper.&nbsp; I know
    people with that piece of paper that ended up working fast
    food.&nbsp; But, they could get an interview where I could
    not.&nbsp; They would not get the job, but I can't even get in
    the door.
    </li>

    <li>I have contributed two PHP internals functions.&nbsp;
    mysql_fetch_assoc and the now deprecated <span class="refname">
        set_file_buffer</span>.&nbsp; In the case of
        mysql_fetch_assoc, I was fixing what was IMO a bug.&nbsp;
        mysql_fetch_array originally returned only an associative
        array.&nbsp; Someone decided it should return the data in
        both associative and numeric forms.&nbsp; mysql_fetch_row
        already existed to return numeric keys.&nbsp; So, I simply
        copied it and added mysql_fetch_assoc.&nbsp; That was the
        real wild wild west days of PHP CVS.&nbsp; You either had
        full access or you didn't have any access.&nbsp; I did not
        keep my development up on PHP so I lost karma on the core
        later.&nbsp; My C is not super polished.&nbsp; Still I will
        admit I liked being part of the club.
    </li>

    <li>I watch pro wrestling.&nbsp; There I said it.&nbsp; Guilty
    pleasure.&nbsp; The Tivo makes it easy.&nbsp; 2 hour show only
    takes about 40 minutes.&nbsp; Heh, I skip most of the
    wrestling.&nbsp; Not everyone is entertaining.&nbsp; My great
    grandmother would sit and watch it every Saturday night.&nbsp;
    I was hooked from that point.&nbsp; I won't get going on
    it.&nbsp; I would lose you guys.&nbsp; FWIW, I am a big fan of
    mixed martial arts too.&nbsp; That stuff IS real and very
    exciting.
    </li>

    <li>I am a compulsive code rewriter and/or write it myself kind
    of guy.&nbsp; It is something I struggle with every day.&nbsp;
    It is why I started <a href="http://www.phorum.org/">Phorum</a>
    and <a href=
    "http://brian.moonspot.net/what-is-wordcraft-">Wordcraft</a>.&nbsp;
    Luckily, the guys I work with at both dealnews and on Phorum
    are good programmers.&nbsp; My biggest problem is opening a
    file I last worked on 5 years ago.&nbsp; The way we write web
    applicaitons has evolved so much in the 11 years I have been
    doing this.&nbsp; I am using the same language, but have such
    different ideas.&nbsp; I can only imagine what the next 11
    years will bring.
    </li>
</ol>
<br>
I will be tagging these seven.&nbsp; Forgive me if I get a title
wrong.<br>

<ol>
    <li>
        <a href="http://krow.livejournal.com/">Brian
        Aker</a>.&nbsp; The creators of sites like Facebook and
        Digg may have had some great ideas.&nbsp; But, without guys
        like Brian Aker, they would be nowhere.&nbsp; He makes the
        things that make dealnews, Facebook and Digg
        possible.&nbsp; And he continues to contribute with Drizzle
        and the new C based gearmand.
    </li>

    <li>
        <a href="http://www.kitchensoap.com/">John
        Allspaw</a>.&nbsp; John manages the operations team for
        Flickr.&nbsp; He is a smart guy.&nbsp; He and I see eye to
        eye on a lot of web performance topics.
    </li>

    <li>
        <a href="http://www.joestump.net/">Joe Stump</a>. Joe is
        the lead architect for Digg.&nbsp; I have not met Joe in
        person.&nbsp; But, I have heard him interviewed and we are
        on the same page about a lot of things.&nbsp; I have
        started using some of his <a href=
        "http://code.google.com/p/digg">contributed PHP</a> code
        lately as well.
    </li>

    <li>
        <a href="http://consoleninja.net/">Alan Kasindorf aka
        Dormando</a>.&nbsp; Alan works for operations at Six
        Apart.&nbsp; They bought Danga.&nbsp; They formerly owned
        Live Journal.&nbsp; He has kind of become the care taker of
        memcached, gearmand and all those other cool things that
        Danga created.&nbsp; The community is helping him more each
        day, but he still does a lot of work for those
        projects.&nbsp; Including, but not limited to coding.
    </li>

    <li>
        <a href="http://jpipes.com/index.php">Jay Pipes</a>.&nbsp;
        I think I saw him on someone else's list.&nbsp; But, I am
        putting him down anyway.&nbsp; Jay has been good to me over
        the years.&nbsp; It is cool that someone can chair the
        MySQL Conference, go around talking to user groups and
        commit code to fix MySQL bugs.&nbsp; Jay is great because
        he and I can disagree on just about everything when it
        comes to programming then toast our beers.&nbsp; You need
        people like that in the world.
    </li>

    <li>
        <a href="http://www.marketing-ontheweb.com/blog/">John
        Allen</a>.&nbsp; I work with John at dealnews.&nbsp; He has
        learned the search engine optimization and marketing world
        the way I learned programming.&nbsp; John posses skills
        that I don't.&nbsp; We butt heads on a regular basis about
        things.&nbsp; But, the end result is always better than
        what existed before we started.&nbsp; I have learned many
        things from him.
    </li>

    <li>
        <a href="http://glazed.org/">Daniel Beckham</a>.&nbsp;
        Daniel is my partner in crime at dealnews.&nbsp; We have
        been a two headed monster (yeah, a monster.&nbsp; Just ask
        the CEO.) for 8 or so years now.&nbsp; Unfortunately,
        Daniel does not blog or twitter or much of anything like
        that.&nbsp; So, we likely won't see his seven things.&nbsp;
        Many of the things I have blogged about when it comes to
        building dealnews' architecture were thought of and done by
        both of us.&nbsp; I am just a show off extrovert that needs
        a lot of attention.&nbsp; So, I do all the blogging and
        talking at conferences. &nbsp;
    </li>
</ol>
And, I am told I need to post this for my taggies to follow:<br>

<ul>
    <li>Link your original tagger(s), and list these rules on your
    blog.
    </li>

    <li>Share seven facts about yourself in the post - some random,
    some weird.
    </li>

    <li>Tag seven people at the end of your post by leaving their
    names and the links to their blogs.
    </li>

    <li>Let them know they’ve been tagged by leaving a comment on
    their blogs and/or Twitter.
    </li>
</ul>]]></description>
            <dc:creator>brianlmoon</dc:creator>
            <pubDate>Mon, 05 Jan 2009 00:08:27 -0600</pubDate>
            <category>mysql</category>
            <category>php</category>
        </item>
    </channel>
</rss>
