<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Kerry&#039;s Blog</title>
	<atom:link href="http://www.xmech.net/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.xmech.net</link>
	<description>Kerry Peterson</description>
	<lastBuildDate>Thu, 26 Aug 2010 19:07:37 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Running of the Leopards 5k Results</title>
		<link>http://www.xmech.net/life/running-of-the-leopards-5k-results/</link>
		<comments>http://www.xmech.net/life/running-of-the-leopards-5k-results/#comments</comments>
		<pubDate>Mon, 29 Mar 2010 00:08:48 +0000</pubDate>
		<dc:creator>kerry</dc:creator>
				<category><![CDATA[Life]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://www.xmech.net/?p=190</guid>
		<description><![CDATA[Yesterday Jamie and I ran a 5k put on by a local high school. This was Jamie&#8217;s first 5k and my 3rd so we were pretty stoked to see how well we would do. The run was great and we did pretty good however I was disappointed that the race statistics didn&#8217;t have a break [...]]]></description>
			<content:encoded><![CDATA[<p>Yesterday Jamie and I ran a 5k put on by a local high school. This was Jamie&#8217;s first 5k and my 3rd so we were pretty stoked to see how well we would do. The run was great and we did pretty good however I was disappointed that the race statistics didn&#8217;t have a break down by age group, instead there was one massive list containing everyone in the order they finished: <a href="http://www.utah.usatf.org/assoc/ut/lepresults10.htm">http://www.utah.usatf.org/assoc/ut/lepresults10.htm</a></p>
<p>I enjoy looking at my age group to see how I rank when compared to others near my age so I decided to write a quick script to generate age groups for the race: <a href="../Leopards5kAgeGroups.html">http://www.xmech.net/Leopards5kAgeGroups.html</a></p>
<p>I did much better than my last 5k (4th out of 5th in my age group) and I hope to be in the top 10 next time.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xmech.net/life/running-of-the-leopards-5k-results/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>8:36PM Photo Project</title>
		<link>http://www.xmech.net/life/836pm-photo-project/</link>
		<comments>http://www.xmech.net/life/836pm-photo-project/#comments</comments>
		<pubDate>Mon, 08 Mar 2010 20:25:18 +0000</pubDate>
		<dc:creator>kerry</dc:creator>
				<category><![CDATA[Life]]></category>

		<guid isPermaLink="false">http://www.xmech.net/?p=187</guid>
		<description><![CDATA[While browsing twitter I found a reference to something called the &#8220;8:36 project&#8221;. You can read about it here however the idea is to take a picture of what you&#8217;re doing (no matter how mundane) at exactly 8:36 PM every day. Being able to look back at your collection of photos will allow you to [...]]]></description>
			<content:encoded><![CDATA[<p>While browsing twitter I found a reference to something called the &#8220;8:36 project&#8221;. You can read about it <a href="http://enjoymentland.com/2009/02/07/836pm/" target="_blank">here</a> however the idea is to take a picture of what you&#8217;re doing (no matter how mundane) at exactly 8:36 PM every day.</p>
<p>Being able to look back at your collection of photos will allow you to build an idea of how your life unfolded through a given month, year, and beyond.</p>
<p>To start things off I snapped a picture Sunday night:</p>
<p><img class="aligncenter" title="Gnocchi" src="http://lh6.ggpht.com/_0McL0FrJEnE/S5SGyaEQpfI/AAAAAAAADwM/o01qGXtBRA4/s400/photo%20%282%29.jpg" alt="" width="400" height="300" /></p>
<p>Jamie and I decided to try and make some Gnocchi for dinner (it&#8217;s always nice to spend some time together cooking) and it turned out great.</p>
<p>I&#8217;ll be <a href="http://picasaweb.google.com/skergX/836pm?feat=directlink">posting my photos to my picasa album</a> and hope to continue this indefinitely.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xmech.net/life/836pm-photo-project/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Botting Facebooks Zoo World</title>
		<link>http://www.xmech.net/programming/botting-facebooks-zoo-world/</link>
		<comments>http://www.xmech.net/programming/botting-facebooks-zoo-world/#comments</comments>
		<pubDate>Fri, 22 Jan 2010 05:55:02 +0000</pubDate>
		<dc:creator>kerry</dc:creator>
				<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://www.xmech.net/?p=179</guid>
		<description><![CDATA[Over the past few weeks a certain someone I know has been playing a real-time game on Facebook called &#8220;Zoo World&#8221;. Users earn money by adding animals, buildings, etc to their zoo and then use this money to improve their zoo (think Roller Coaster Tycoon). Money is earned in real time which means that every [...]]]></description>
			<content:encoded><![CDATA[<p>Over the past few weeks a certain someone I know has been playing a real-time game on Facebook called &#8220;Zoo World&#8221;. Users earn money by adding animals, buildings, etc to their zoo and then use this money to improve their zoo (think Roller Coaster Tycoon). Money is earned in real time which means that every tick (5 minutes) you receive a sum of money depending on how well your zoo is rated.</p>
<p>Zoo World also has a few mechanisms in place that prevent people from earning money while not at their computer, specifically a button appears when a period of inactivity has passed and the zoo day timer stops ticking until you press this button (there&#8217;s a handful of other buttons that can appear but this is the main culprit).</p>
<p>Recently I discovered java&#8217;s robot library (java.awt.robot) and this seemed like the perfect opportunity to use it and <a href="http://www.xmech.net/portfolio/desktop/zoo/">ZooBot</a> is the result. ZooBot is a little application that will scan the screen every few minutes looking for these buttons and presses them if they&#8217;re found.</p>
<p>Now you can earn cash while sleeping! I wonder what other facebook games I could use method on&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xmech.net/programming/botting-facebooks-zoo-world/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Automate site testing with a robot</title>
		<link>http://www.xmech.net/programming/automate-site-testing-with-a-robot/</link>
		<comments>http://www.xmech.net/programming/automate-site-testing-with-a-robot/#comments</comments>
		<pubDate>Sun, 10 Jan 2010 05:35:54 +0000</pubDate>
		<dc:creator>kerry</dc:creator>
				<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://www.xmech.net/?p=90</guid>
		<description><![CDATA[Intro We&#8217;ve all been there, you&#8217;re working on a website and you&#8217;re constantly testing your changes to make sure you haven&#8217;t broken anything unexpectedly. This can really become time consuming when you make a code change that could affect multiple features. This process is also susceptible to human error (sometimes you might forget to test [...]]]></description>
			<content:encoded><![CDATA[<h2>Intro</h2>
<p>We&#8217;ve all been there, you&#8217;re working on a website and you&#8217;re constantly testing your changes to make sure you haven&#8217;t broken anything unexpectedly. This can really become time consuming when you make a code change that could affect multiple features. This process is also susceptible to human error (sometimes you might forget to test a feature or run slightly different tests each time). If only there was a way to take the human out of the equation and make this process faster&#8230; wait a minute, we can write a robot to run our tests for us! Let&#8217;s take a look at how we would accomplish this using PHP.</p>
<p>The idea of using a robot to run tests for us is nothing new, take a look at some common <a href="http://www.phpunit.de/" target="_blank">unit testing</a> <a href="http://www.simpletest.org/" target="_blank">methods</a> and you&#8217;ll get an idea. However if we write our robot in PHP we can easily run it from a development environment without needing to download, install, or modify any existing code.</p>
<h2>Setup The Example Project</h2>
<p>Before we can begin we&#8217;ll need to setup a simple application so our robot has something to test. The example we&#8217;ll be using is a very rudimentary todo list manager. We want to make sure users can perform the following actions: sign-in, add/remove tasks, and then sign-out. <a href="http://www.xmech.net/tutorials/todo.zip">Download this zip file</a> which contains a few php files and unzip it into your document root directory on your local test machine.</p>
<p>The example application is relatively straight forward so you can browse through it if you&#8217;d like; however we&#8217;ll go over most of it as we go through the tutorial. There are a few steps we need to complete before we can start writing our robot. First open the mysql.php file and provide a valid username/password.</p>
<pre class="brush: php;">
// MySQL database connection settings
$username = &quot;change&quot;;
$password = &quot;me&quot;;
</pre>
<p>Now execute the setup file by visiting it (<a href="http://localhost/todo/setup.php" target="_blank">http://localhost/todo/setup.php</a>) in your browser. If everything went as expected you should see the word &#8220;Success&#8221; and we can continue on (this will create a database, required tables, and some test data for us to work with).</p>
<p>Now you can play around with the todo app (log in with test/password1, create and delete a task, logout).</p>
<p>(Please note, the application is extremely basic since we&#8217;re only using it as an example, therefor I&#8217;ve ignored some common practices like hashing passwords, cleaning user input, styles, etc)</p>
<h2>Building Our Robot</h2>
<p>With all of the setup out of the way we can finally get started on the fun part! We&#8217;ll be using <a href="http://php.net/manual/en/book.curl.php" target="_blank">cURL</a> to talk with our server and regular expressions to decide if our test passed or failed. <a href="http://www.xmech.net/tutorials/todoRobotDisplay.php" target="_blank">You can view the complete script here</a>, don&#8217;t worry, we&#8217;ll walk through it step by step.</p>
<p><strong>Base Robot Class</strong></p>
<p>We&#8217;ll start by looking at our base robot class, later we&#8217;ll build our own todo robot class to work specifically with our example and we&#8217;ll inherit from our base robot class. This way you can easily extend the base robot to build robots for other projects.</p>
<p>Lets start by defining a few variables that will be used to report the status of our tests as well as the variable that our cURL instance will be assigned to:</p>
<pre class="brush: php;">
class Robot {

    private $passTemplate = ': &lt;span style=&quot;color: green;&quot;&gt;Pass&lt;/span&gt;';
    private $failTemplate = ': &lt;span style=&quot;color: red;&quot;&gt;Fail&lt;/span&gt;';
    private $ch;
</pre>
<p>The class constructor takes care of configuring the bulk of our cURL options however there is one item that needs some attention:</p>
<pre class="brush: php;">
    function __construct() {
        // Sometimes servers are configured with &quot;&amp;amp;&quot; as their url separator.
        // This can break the url strings we construct so we'll change it to use the real &quot;&amp;&quot; character.
        ini_set('arg_separator.output', '&amp;');
        $this-&gt;configureCurl();
    }
</pre>
<p>You can find more details about <a href="http://www.sitepoint.com/blogs/2005/03/21/php-and-standards-arg_separatoroutput/" target="_blank">arg_separator here</a>.</p>
<pre class="brush: php;">
    function getResultTemplate($status) {
        if ($status == &quot;passed&quot;) {
            return $this-&gt;passTemplate;
        } else {
            return $this-&gt;failTemplate;
        }
    }
</pre>
<p>The above function will retrieve the formatted pass/fail string we defined earlier so we can output it to display the status of a test.</p>
<p>It&#8217;s fairly likely that our robot will need to interact with forms to complete some of our tests so we need a way to encode our test strings before they can be sent in a URL as a form post. Lucky for us PHP happens to have a function that will take care of this for us so all we need to do is pass an array containing our test strings. The array key should be the name of the form and the value should be the contents of the form field. You&#8217;ll see an example of this later on.</p>
<pre class="brush: php;">
    function buildUrlQuery($data) {
        return http_build_query($data);
    }
</pre>
<p>Next we have the cURL configuration function which was called by the robot class constructor. The comments should give you a good idea of what each option does however there are a few that need some extra explanation:</p>
<ul>
<li>CURLOPT_USERAGENT: Some websites check the user agent so you might need to mascurade as a web browser.</li>
<li>CURLOPT_COOKIEFILE: cURL doesn&#8217;t keep track of cookies by default so if your application uses sessions we need to define a file that cURL can write and load cookies from.</li>
<li>CURLOPT_FOLLOWLOCATION: This tells cURL to automatically follow any header redirects.</li>
<li>CURLOPT_RETURNTRANSFER: Normally cURL will respond with a true or false after the request. Setting this option will return the any HTML that the server sent to us as a response to our request.</li>
</ul>
<pre class="brush: php;">
    function configureCurl() {
        // Open connection
        $this-&gt;ch = curl_init();

        // Fake user agent
        //curl_setopt($this-&gt;ch, CURLOPT_USERAGENT, &quot;Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1&quot;);

        // Handle cookies (this allows sessions to work)
        curl_setopt($this-&gt;ch, CURLOPT_COOKIEFILE, 'cookies.txt');

        // Automatically follow 302 HTTP redirects
        curl_setopt($this-&gt;ch, CURLOPT_FOLLOWLOCATION, 1);

        // Tells cURL to return any HTML instead of a boolean depending on request status
        curl_setopt($this-&gt;ch, CURLOPT_RETURNTRANSFER, 1);

        // Use a Post instead of usual Get
        curl_setopt($this-&gt;ch, CURLOPT_POST, true);
    }
</pre>
<p>We wrap up the robot base class with the function that actually runs the tests. This includes setting the cURL url to our test url and we&#8217;ll also set the post data (CURLOPT_POSTFIELDS) that we encoded earlier.</p>
<pre class="brush: php;">
    function runTest($url, $urlFields) {
        // Set the url
        curl_setopt($this-&gt;ch, CURLOPT_URL, $url);

        // Assign form post fields from our data set
        curl_setopt($this-&gt;ch, CURLOPT_POSTFIELDS, $urlFields);

        // Execute request
        $result = curl_exec($this-&gt;ch);

        // Info about the post request
        //$info = curl_getinfo($this-&gt;ch);

        return $result;
    }
}
</pre>
<p>Notice that we return the $result containing HTML thanks to the CURLOPT_RETURNTRANSFER setting which we configured earlier. Later we&#8217;ll use the $result data in our todo robot class to check and see if the test passed or failed.</p>
<p>Also notice the $info variable containing a large amount of meta information about our request (times, sizes, counts, etc). It&#8217;s commented out because it&#8217;s generally not helpful but could come in handy if a request isn&#8217;t functioning properly.</p>
<p>That&#8217;s all for the base class, now we can move on to our todo robot class where we create the actual tests that will run against our todo application.</p>
<p><strong>Todo Robot Class</strong></p>
<p>First we&#8217;ll go ahead and define our class and inherit from the base class:</p>
<pre class="brush: php;">
class TodoRobot extends Robot {
</pre>
<p>You&#8217;ll notice that each test has it&#8217;s own function so we can call them independently. Lets start with the login test which takes two arguments, a username and password:</p>
<pre class="brush: php;">
    function testLogin($username, $password) {
</pre>
<p>Next construct a string (using our buildUrlQuery function from earlier) that contains the data normally sent to the server by the login form:</p>
<pre class="brush: php;">
        $urlArgs = $this-&gt;buildUrlQuery(array(&quot;username&quot; =&gt; $username, &quot;password&quot; =&gt; $password));
</pre>
<p>Now we can build our first test. We need to send the request to the location that the login form is pointing to. Open the index.php page and you&#8217;ll notice that the form is pointing to login.php</p>
<pre class="brush: xml;">
&lt;form method=&quot;post&quot; action=&quot;login.php&quot;&gt;
</pre>
<p>So we&#8217;ll configure our test to run against login.php with our encoded arguments:</p>
<pre class="brush: php;">
        $result = $this-&gt;runTest(&quot;http://localhost/todo/login.php&quot;, $urlArgs);
</pre>
<p>Next we need to find out if our test passed or failed. We&#8217;ll use regular expressions to look for a unique status identifier in the $result HTML. Before we can continue we need to see what will happen if we login. Fire up the todo app and login using valid credentials. Upon a successful login you&#8217;ll notice the form is replaced with the following:</p>
<pre class="brush: xml;">
&lt;a href=&quot;add.php&quot;&gt;Add new item&lt;/a&gt; | &lt;a href=&quot;logout.php&quot;&gt;Logout&lt;/a&gt;
</pre>
<p>Using this we can look for the words &#8220;Add new item&#8221; to verify we logged in successfully. Now return the successful test template string so we can print out the status. This approach also covers any failed login attempts (if you were to pass an incorrect username/password combination).</p>
<pre class="brush: php;">
        if (preg_match('/Add\snew\sitem/', $result)) {
            return $this-&gt;getResultTemplate(&quot;passed&quot;);
        } else {
            return $this-&gt;getResultTemplate(&quot;failed&quot;);
        }
    }
</pre>
<p>From here on out the rest of our todo robot consists of different test functions: adding a task, removing a task, and finally logging out. All tests follow the same format as our login test. You can see how easy it is to quickly generate various tests, all you need to do is follow the same steps as we preformed in the login test:</p>
<ul>
<li>Perform the action you&#8217;re trying to test by hand</li>
<li>Find unique text to match on and decide if your test passed</li>
<li>Return a passed/failed status message</li>
</ul>
<pre class="brush: php;">
    function testAddingTask() {
        $urlArgs = $this-&gt;buildUrlQuery(array(&quot;item&quot; =&gt; &quot;Task added by robot!&quot;));
        $result = $this-&gt;runTest(&quot;http://localhost/todo/addItem.php&quot;, $urlArgs);

        if (preg_match('/Item\sadded/', $result)) {
            return $this-&gt;getResultTemplate(&quot;passed&quot;);
        } else {
            return $this-&gt;getResultTemplate(&quot;failed&quot;);
        }
    }

    function testRemovingTask($itemNumber) {
        $urlArgs = $this-&gt;buildUrlQuery(array(&quot;item&quot; =&gt; $itemNumber));
        $result = $this-&gt;runTest(&quot;http://localhost/todo/remove.php&quot;, $urlArgs);

        if (preg_match('/Item\sremoved/', $result)) {
            return $this-&gt;getResultTemplate(&quot;passed&quot;);
        } else {
            return $this-&gt;getResultTemplate(&quot;failed&quot;);
        }
    }

    function testLogout() {
        $result = $this-&gt;runTest(&quot;http://localhost/todo/logout.php&quot;, &quot;&quot;);

        if (preg_match('/Logged\sout/', $result)) {
            return $this-&gt;getResultTemplate(&quot;passed&quot;);
        } else {
            return $this-&gt;getResultTemplate(&quot;failed&quot;);
        }
    }
}
</pre>
<p>Finally we can run our first test!</p>
<h2>Running the robot</h2>
<p>Instantiate the robot, print some text to the screen so we know which test is running, and run it!</p>
<pre class="brush: php;">
$todoRobot = new TodoRobot();
echo &quot;&lt;h3&gt;Login&lt;/h3&gt;&quot;;
echo &quot;Correct credentials&quot;  . $todoRobot-&gt;testLogin(&quot;test&quot;, &quot;password1&quot;);
echo &quot;&lt;br /&gt;&lt;hr&gt;&quot;;
</pre>
<p>The rest of the tests:</p>
<pre class="brush: php;">
echo &quot;&lt;h3&gt;Add Task&lt;/h3&gt;&quot;;
echo &quot;Adding task&quot; . $todoRobot-&gt;testAddingTask();
echo &quot;&lt;br /&gt;&lt;hr&gt;&quot;;
echo &quot;&lt;h3&gt;Remove Task&lt;/h3&gt;&quot;;
echo &quot;Remove task&quot; . $todoRobot-&gt;testRemovingTask(&quot;1&quot;);
echo &quot;&lt;br /&gt;&lt;hr&gt;&quot;;
echo &quot;&lt;h3&gt;Logout&lt;/h3&gt;&quot;;
echo &quot;Logout&quot; . $todoRobot-&gt;testLogout();
</pre>
<p>Go ahead and run the robot (<a href="http://localhost/todo/todoRobot.php" target="_blank">http://localhost/todo/todoRobot.php</a>) and you should see all of the tests have run and passed!</p>
<p>Now lets take a look at what a failed test would look like. Replace the login test block with the following code:</p>
<pre class="brush: php;">
echo &quot;&lt;h3&gt;Login&lt;/h3&gt;&quot;;
echo &quot;Correct credentials&quot;  . $todoRobot-&gt;testLogin(&quot;test&quot;, &quot;password1&quot;);
echo &quot;&lt;br /&gt;Wrong username/password&quot; . $todoRobot-&gt;testLogin(&quot;kerry&quot;, &quot;password1&quot;);
echo &quot;&lt;br /&gt;Only username&quot; . $todoRobot-&gt;testLogin(&quot;kerry&quot;, &quot;&quot;);
echo &quot;&lt;br /&gt;&lt;hr&gt;&quot;;
</pre>
<p>Run the robot again and you&#8217;ll see that our recently added tests have failed. This is to be expected because we didn&#8217;t pass the correct credentials. You can see how this would be useful because if the tests had passed we&#8217;d have a serious issue on our hands.</p>
<h2>Conclusion</h2>
<p>Hopefully you can see how beneficial having a robot to automate your testing can be. After you make a large code change you can run your robot and make sure the basic functionality of your website hasn&#8217;t been changed in any unexpected ways.</p>
<h2>Helpful Links</h2>
<ul>
<li><a href="http://www.xmech.net/tutorials/todoRobotDisplay.php" target="_blank">Complete robot file</a></li>
<li><a href="http://www.xmech.net/tutorials/todo.zip">Todo example application files</a></li>
<li><a href="http://php.net/manual/en/book.curl.php" target="_blank">cURL library</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.xmech.net/programming/automate-site-testing-with-a-robot/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Manipulating XML with PHP</title>
		<link>http://www.xmech.net/programming/manipulating-xml-with-php/</link>
		<comments>http://www.xmech.net/programming/manipulating-xml-with-php/#comments</comments>
		<pubDate>Mon, 07 Dec 2009 23:03:12 +0000</pubDate>
		<dc:creator>kerry</dc:creator>
				<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://www.xmech.net/?p=48</guid>
		<description><![CDATA[This article will walk you through the process of parsing an XML document using PHP&#8217;s SimpleXML extension. We&#8217;ll make some changes to the XML and then save the modifications. Intro If you&#8217;re reading this I&#8217;m going to assume you&#8217;re familiar with XML so we&#8217;ll skip the XML introduction and get right into it. We&#8217;re using [...]]]></description>
			<content:encoded><![CDATA[<p>This article will walk you through the process of parsing an XML document using PHP&#8217;s SimpleXML extension. We&#8217;ll make some changes to the XML and then save the modifications.</p>
<h2>Intro</h2>
<p>If you&#8217;re reading this I&#8217;m going to assume you&#8217;re familiar with XML so we&#8217;ll skip the XML introduction and get right into it. We&#8217;re using SimpleXML because true to it&#8217;s name, it makes working with an XML document simple. However it has a large draw back that might cause you to use a different parsing method, the problem lies with the entire XML document needing to be loaded into memory before parsing can begin. This isn&#8217;t a problem with a 100k document however if you&#8217;re parsing something in 100+MB range&#8230; you should probably use different method such as parsing from a stream.</p>
<p>Let&#8217;s use the following scenario: Twitter is popular with the hip kids these days so lets generate a list of people I follow whom have at least 2,000 people following them. We&#8217;ll need to add some change data (so any apps that consume our modified data will know how many followers we originally had) and then save our changes to a new XML document.</p>
<p>Lucky for us twitter just so happens to provide the data we need in XML format:</p>
<p><a href="http://twitter.com/statuses/friends.xml?screen_name=dremex" target="_blank">http://twitter.com/statuses/friends.xml?screen_name=dremex</a></p>
<p>Now we have a list of the last 100 people I&#8217;ve followed and we can begin to parse through the document.</p>
<h2>Code Walkthrough</h2>
<p>Here&#8217;s the complete code for this article, go ahead and take a look and then we&#8217;ll break it down and walk through it:</p>
<p><a href="http://www.xmech.net/tutorials/twitterFollowersDisplay.php" target="_blank">http://www.xmech.net/tutorials/twitterFollowersDisplay.php</a></p>
<p>First we&#8217;ll start with the bottom of the file and then walk through each class function call:</p>
<pre class="brush: php;">
$friendList = new TwitterFollowers(&quot;http://twitter.com/statuses/friends.xml?screen_name=dremex&quot;, 2000);
</pre>
<p>We&#8217;re going to create a TwitterFollowers object and pass it the url of our friends feed followed by the number of followers we want to check to make sure our friends have.</p>
<div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 274px; width: 1px; height: 1px;"><span style="white-space: pre;"> </span>function TwitterFollowers($file, $maxFollowersCount) {</div>
<div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 274px; width: 1px; height: 1px;"><span style="white-space: pre;"> </span>$this-&gt;loadXml($file);</div>
<div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 274px; width: 1px; height: 1px;"><span style="white-space: pre;"> </span>$this-&gt;maxFollowersCount = $maxFollowersCount;</div>
<div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 274px; width: 1px; height: 1px;"><span style="white-space: pre;"> </span>$this-&gt;originalFollowers = $this-&gt;followerCount();</div>
<div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 274px; width: 1px; height: 1px;"><span style="white-space: pre;"> </span>}</div>
<pre class="brush: php;">
function TwitterFollowers($file, $maxFollowersCount) {
    $this-&gt;loadXml($file);
    $this-&gt;maxFollowersCount = $maxFollowersCount;
    $this-&gt;originalFollowers = $this-&gt;followerCount();
}
</pre>
<p>The class constructor takes care of loading our friends feed, assigns a local variable to keep track of how many followers to check for, and sets up a statistic to keep track of our original follower count. Let&#8217;s go through each step in order:</p>
<pre class="brush: php;">
function loadXml($file) {
    $this-&gt;xml = simplexml_load_file($file);
}
</pre>
<p>This is the first step in parsing any XML with SimpleXML. There are two methods to load XML data, simplexml_load_file and simplexml_load_string. Pretty self explanitory, file loads from a file and string loads from a string. For our project we&#8217;re going to use the file loading (or in our case a URL) and load the XML data into our local $xml variable.</p>
<p>As an FYI the syntax for string version of our document would be:</p>
<pre class="brush: php;">$this-&gt;xml = simplexml_load_string($fileAsString);</pre>
<p>The next function we&#8217;re going to look at grabs the number of our current friends:</p>
<pre class="brush: php;">
function followerCount() {
    return count($this-&gt;xml-&gt;user);
}
</pre>
<p>This is where SimpleXML starts to shine with its simplicity. SimpleXML assigns elements to array key value pairs so we can easily iterate over them, more on this later.</p>
<p>Next we want to go ahead and check our friends for their followers count and remove any who don&#8217;t have at least 2,000:</p>
<pre class="brush: php;">$friendList-&gt;updateFollowerList();</pre>
<p>This calls our objects &#8220;updateFollowerList&#8221; function:</p>
<pre class="brush: php;">
function updateFollowerList() {
    $this-&gt;checkFollowers();
    $this-&gt;removeFollowers();
}
</pre>
<p>Let&#8217;s take a look at both of these starting with checking follower count:</p>
<pre class="brush: php;">
function checkFollowers() {
    foreach ($this-&gt;xml-&gt;user as $user) {
        if ($user-&gt;followers_count &lt; $this-&gt;maxFollowersCount) {
            array_push($this-&gt;toRemove, $user);
        }
    }
}
</pre>
<p>As I mentioned before, since SimpleXML created an array for us, we can easily iterate over each user record and check to see if they have fewer followers than our $maxFollowersCount variable we setup at the beginning.</p>
<p>It&#8217;s interesting to include the following code so you can get a feel for how the SimpleXML array is structured. Just above the foreach in the &#8220;checkFollowers&#8221; function, go ahead and add:</p>
<pre class="brush: php;">
echo &quot;&lt;pre&gt;&quot;;
print_r($this-&gt;xml);
echo &quot;&lt;/pre&gt;&quot;;
</pre>
<p>Now the SimpleXML array will be displayed in a readable manner. It should look something like this:</p>
<p><a href="http://www.xmech.net/tutorials/simplexmlFormatExample.html" target="_blank">http://www.xmech.net/tutorials/simplexmlFormatExample.html</a></p>
<p>Go ahead and remove the debug info and let&#8217;s continue on. If the user doesn&#8217;t have enough followers we&#8217;ll go ahead an add it to and array of &#8220;users to remove&#8221; so we can remove the users later. We don&#8217;t want to remove them now because we would loose our position in the loop and never parse through all of the records.</p>
<p>Next we call the &#8220;removeFollowers&#8221; function that handles removing friends from the data set:</p>
<pre class="brush: php;">
function removeFollowers() {
    foreach ($this-&gt;toRemove as $user) {
        $oNode = dom_import_simplexml($user);
        $oNode-&gt;parentNode-&gt;removeChild($oNode);
    }
}
</pre>
<p>Now we&#8217;re going to walk through our list of friends to remove and using DOM we&#8217;ll remove them from our SimpleXML array. Unfortunately SimpleXML has no way to remove elements. First we&#8217;ll call dom_import_simplexml and pass it our user (a SimpleXML element) and then remove it. Now our $followers array will no longer contain this user.</p>
<p>Next we&#8217;ll need to add statistics for any changes made to this document:</p>
<pre class="brush: php;">$friendList-&gt;addChanges();</pre>
<p>SimpleXML makes adding elements simple:</p>
<pre class="brush: php;">
function addChanges() {
    $this-&gt;xml-&gt;addChild('changes');
    $this-&gt;xml-&gt;changes-&gt;addAttribute('before', $this-&gt;originalFollowers);
    $this-&gt;xml-&gt;changes-&gt;addAttribute('after', $this-&gt;followerCount());
}
</pre>
<p>First we&#8217;ll name our new element &#8220;changes&#8221; and give it a few attributes such as our friend count before and after our changes. The above code will add the following child element to our SimpleXML data at the end of our document:</p>
<pre class="brush: xml;">&lt;changes before=&quot;50&quot; after=&quot;25&quot;/&gt;</pre>
<p>Now we&#8217;ll wrap up by saving our modified document:</p>
<pre class="brush: php;">$friendList-&gt;saveFollowerList(&quot;updatedFollowers.xml&quot;);</pre>
<p>Once again SimpleXML makes our life easy:</p>
<pre class="brush: php;">
function saveFollowerList($file) {
    $this-&gt;xml-&gt;asXML($file);
}
</pre>
<p>We simply need to call the &#8220;asXML&#8221; and feed it a file name and it will save our XML to a file. If no file name is passed it will return the data as a string (handy for debugging).</p>
<p>That&#8217;s it! However there are a few things that could be done to improve usability.</p>
<h2>Error Handling</h2>
<p>Lets and add some error checking to make sure our XML document isn&#8217;t malformed before we begin parsing. After the opening php tag add the following:</p>
<pre class="brush: php;">libxml_use_internal_errors(true);</pre>
<p>This command will let us handle any errors instead of libxml.</p>
<p>Next replace the &#8220;loadXML&#8221; function with:</p>
<pre class="brush: php;">
function loadXml($file) {
    $this-&gt;xml = simplexml_load_file($file);
    if (!$this-&gt;xml) {
        echo &quot;&lt;b&gt;Failed loading XML&lt;/b&gt;:&lt;br /&gt;&quot;;
        foreach(libxml_get_errors() as $error) {
            echo $error-&gt;message;
        }
        exit(1);
    }
}
</pre>
<p>First we&#8217;ll check to see if we manage to load any XML data. Since we toggled the internal libxml error to true our $xml variable will be empty if there were errors when SimpleXML tried to load the file. Now we can inform the user that there were errors and then iterate over them using libxml_get_errors().</p>
<p>libxml_get_errors() will provide an error message that contains the following properties: message, line, and column of the error. For our purposes we&#8217;re just going to use message since it contains the entire error. After printing all of the errors out we&#8217;ll exit so we don&#8217;t continue and try to parse through an empty document.</p>
<p>Now modify the followers.xml document so the first three lines are:</p>
<pre class="brush: xml;">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;users type=&quot;array&quot;&gt;
&lt;user&gt;&lt;user2&gt;
</pre>
<p>Run the script and you&#8217;ll get the following error:</p>
<p><strong>&#8220;Failed loading XML</strong>:<br />
Opening and ending tag mismatch: user2 line 3 and user expected &#8216;&gt;&#8217; Premature end of data in tag users line 2&#8243;</p>
<h2>Conclusion</h2>
<p>Using SimpleXML makes a somewhat difficult task (working with XML) simple enough that some massive changes to a document can be done in only a few short lines.</p>
<h2>Helpful links</h2>
<ul>
<li><a href="http://apiwiki.twitter.com/Twitter-REST-API-Method%3A-statuses%C2%A0friends" target="_blank">http://apiwiki.twitter.com/Twitter-REST-API-Method%3A-statuses%C2%A0friends</a></li>
<li><a href="http://php.net/manual/en/book.simplexml.php" target="_blank">http://php.net/manual/en/book.simplexml.php</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.xmech.net/programming/manipulating-xml-with-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Now it&#8217;s looking like a theatre room</title>
		<link>http://www.xmech.net/house/now-its-looking-like-a-theatre-room/</link>
		<comments>http://www.xmech.net/house/now-its-looking-like-a-theatre-room/#comments</comments>
		<pubDate>Mon, 16 Nov 2009 19:37:57 +0000</pubDate>
		<dc:creator>kerry</dc:creator>
				<category><![CDATA[House]]></category>
		<category><![CDATA[Theatre Room]]></category>

		<guid isPermaLink="false">http://www.xmech.net/?p=21</guid>
		<description><![CDATA[After taking a few months off we&#8217;ve finished more of the theatre room. This weekend we wrapped up painting the ceiling slats, window frame, and installed a set of blinds. We&#8217;ll be installing the flooring later this week (which means I have to finish putting the cement sealer on the floor&#8230; I&#8217;m not looking forward [...]]]></description>
			<content:encoded><![CDATA[<p>After taking a few months off we&#8217;ve finished more of the theatre room. This weekend we wrapped up painting the ceiling slats, window frame, and installed a set of blinds.</p>
<p><img class="alignnone" title="Window and ceiling" src="http://lh6.ggpht.com/_0McL0FrJEnE/SwGl4XTv0OI/AAAAAAAADaM/cBnXnCe_uoA/s640/window.jpg" alt="" width="640" height="480" /></p>
<p><img class="alignnone" title="Ceiling slats" src="http://lh4.ggpht.com/_0McL0FrJEnE/SwGl4RXtAlI/AAAAAAAADaI/YpJOXeVdOQQ/s640/roof.jpg" alt="" width="640" height="480" /></p>
<p><img class="alignnone" title="Window close up" src="http://lh4.ggpht.com/_0McL0FrJEnE/SwGl4bvlF0I/AAAAAAAADaE/cjkObzboBh8/s512/windowClose.jpg" alt="" width="384" height="512" /></p>
<p>We&#8217;ll be installing the flooring later this week (which means I have to finish putting the cement sealer on the floor&#8230; I&#8217;m not looking forward to that)</p>
<p><img class="alignnone" title="Wood waiting to be installed" src="http://lh6.ggpht.com/_0McL0FrJEnE/SwGl4LO0ZCI/AAAAAAAADaA/EYzBF8pcu3c/s512/floor.jpg" alt="" width="384" height="512" /></p>
<p>For anyone concerned with the acoustic problems a wood floor may cause, there will be a sound absorbing layer underneath the wood and we plan to add an area rug for the middle of the room that should soak up sound at the proper bounce angles.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xmech.net/house/now-its-looking-like-a-theatre-room/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Paint Complete</title>
		<link>http://www.xmech.net/house/paint-complete/</link>
		<comments>http://www.xmech.net/house/paint-complete/#comments</comments>
		<pubDate>Sun, 16 Aug 2009 19:13:56 +0000</pubDate>
		<dc:creator>kerry</dc:creator>
				<category><![CDATA[House]]></category>
		<category><![CDATA[Theatre Room]]></category>

		<guid isPermaLink="false">http://www.xmech.net/?p=19</guid>
		<description><![CDATA[Wall paint complete! We also built a cabinet (from scratch) to house all of the electronics. We still need to fill in some gaps, sand, router the edges, and paint but it&#8217;s turning out very nice. We also cleaned up the speaker wiring and added our face plates]]></description>
			<content:encoded><![CDATA[<p>Wall paint complete!</p>
<p><img class="aligncenter" title="First Coat" src="http://lh3.ggpht.com/_0McL0FrJEnE/SwGl8PkV7dI/AAAAAAAADac/EIcJYs6l71s/s640/coat1.jpg" alt="" width="640" height="480" /></p>
<p><img class="alignnone" title="Second Coat" src="http://lh5.ggpht.com/_0McL0FrJEnE/SwGl72rABCI/AAAAAAAADaY/V7lyJ92Cn_I/s640/coat2.jpg" alt="" width="640" height="480" /></p>
<p>We also built a cabinet (from scratch) to house all of the electronics. We still need to fill in some gaps, sand, router the edges, and paint but it&#8217;s turning out very nice.</p>
<p><img class="alignnone" title="Cabinet" src="http://lh5.ggpht.com/_0McL0FrJEnE/SwGl8GYf87I/AAAAAAAADag/SAJunn84VcM/s640/cabinet.jpg" alt="" width="640" height="480" /></p>
<p>We also cleaned up the speaker wiring and added our face plates</p>
<p><img class="alignnone" title="Wiring mess" src="http://lh3.ggpht.com/_0McL0FrJEnE/SwGl4fw0-AI/AAAAAAAADaQ/1yVdfc74bAQ/s512/wiring.jpg" alt="" width="384" height="512" /></p>
<p><img class="alignnone" title="Finished faceplate" src="http://lh3.ggpht.com/_0McL0FrJEnE/SwGl71BWxJI/AAAAAAAADaU/Y4HjKkdJ8ZM/s512/faceplate.jpg" alt="" width="384" height="512" /></p>
]]></content:encoded>
			<wfw:commentRss>http://www.xmech.net/house/paint-complete/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Drywall (Part 2)</title>
		<link>http://www.xmech.net/house/drywall-part-2/</link>
		<comments>http://www.xmech.net/house/drywall-part-2/#comments</comments>
		<pubDate>Fri, 17 Jul 2009 19:35:45 +0000</pubDate>
		<dc:creator>kerry</dc:creator>
				<category><![CDATA[House]]></category>
		<category><![CDATA[Theatre Room]]></category>

		<guid isPermaLink="false">http://www.xmech.net/house/drywall-part-2/</guid>
		<description><![CDATA[We&#8217;ve finally finished mudding and sanding! We also managed to order and paint all of the face plates for outlets and speaker boxes. Now it&#8217;s time to put some paint on the walls.]]></description>
			<content:encoded><![CDATA[<p>We&#8217;ve finally finished mudding and sanding!</p>
<p><img src="http://lh6.ggpht.com/_0McL0FrJEnE/SmDR5LVVrUI/AAAAAAAADIs/FRCaiHgLgI0/s400/mudFrontLeft.jpg" /></p>
<p><img src="http://lh3.ggpht.com/_0McL0FrJEnE/SmDR5QSdQAI/AAAAAAAADI0/-X_0sFT0qAc/s400/mudFrontRight.jpg" /></p>
<p><img src="http://lh3.ggpht.com/_0McL0FrJEnE/SmDR5qWwBFI/AAAAAAAADI4/Hwlr4SFQPW0/s400/mudRearRight.jpg" /></p>
<p><img src="http://lh3.ggpht.com/_0McL0FrJEnE/SmDR5_g79RI/AAAAAAAADI8/LAI1tQtNnmM/s400/mudRearLeft.jpg" /></p>
<p>We also managed to order and paint all of the face plates for outlets and speaker boxes.</p>
<p><img src="http://lh6.ggpht.com/_0McL0FrJEnE/SmDR4Ceh2ZI/AAAAAAAADIg/N3ax7SFtwPA/s400/facePlatesWhite.jpg" /></p>
<p><img src="http://lh4.ggpht.com/_0McL0FrJEnE/SmDR4t6BokI/AAAAAAAADIk/pstjbPub0PE/s400/facePlatesPrimed.jpg" /></p>
<p><img src="http://lh4.ggpht.com/_0McL0FrJEnE/SmDR40u8UGI/AAAAAAAADIo/m-eCn0N6_KA/s400/facePlatesBlack.jpg" /></p>
<p>Now it&#8217;s time to put some paint on the walls.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xmech.net/house/drywall-part-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Drywall (Part 1)</title>
		<link>http://www.xmech.net/house/drywall-part-1/</link>
		<comments>http://www.xmech.net/house/drywall-part-1/#comments</comments>
		<pubDate>Sun, 05 Jul 2009 21:14:59 +0000</pubDate>
		<dc:creator>kerry</dc:creator>
				<category><![CDATA[House]]></category>
		<category><![CDATA[Theatre Room]]></category>

		<guid isPermaLink="false">http://www.xmech.net/house/drywall-part-1/</guid>
		<description><![CDATA[Another productive week spent working on the theatre room. This week we finished pulling the speaker and video cabling and managed to get all of the drywall up. Now it looks more like a room!]]></description>
			<content:encoded><![CDATA[<p>Another productive week spent working on the theatre room. This week we finished pulling the speaker and video cabling and managed to get all of the drywall up. Now it looks more like a room!</p>
<p><img src="http://lh6.ggpht.com/_0McL0FrJEnE/SlIyez6QNVI/AAAAAAAADCQ/tkiJoE_KOW4/s400/cables.jpg" /></p>
<p><img src="http://lh5.ggpht.com/_0McL0FrJEnE/SlIyfT39CwI/AAAAAAAADCY/47HTyyExemU/s400/drywall.jpg" /></p>
<p><img src="http://lh5.ggpht.com/_0McL0FrJEnE/SlIyfObOSBI/AAAAAAAADCU/anvMX7SgxrM/s400/frontIns.jpg" /><br />
<img src="http://lh5.ggpht.com/_0McL0FrJEnE/SlIyeY25fAI/AAAAAAAADCI/CA0Q_ixFXNo/s400/rearCornerDuringDrywall.jpg" /></p>
<p><img src="http://lh3.ggpht.com/_0McL0FrJEnE/SlIyfbU7YVI/AAAAAAAADCc/aNFkX4ZEV28/s400/frontCornerDuringDrywall.jpg" /></p>
<p><img src="http://lh5.ggpht.com/_0McL0FrJEnE/SlIyfnowy4I/AAAAAAAADCg/-JhSMzlV1Q0/s400/frontDrywall.jpg" width="400" height="300" /></p>
<p><img src="http://lh5.ggpht.com/_0McL0FrJEnE/SlIydkWAtaI/AAAAAAAADB8/l2ONXjd7LVg/s400/rearCornerDrywall.jpg" /></p>
]]></content:encoded>
			<wfw:commentRss>http://www.xmech.net/house/drywall-part-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Electrical Complete</title>
		<link>http://www.xmech.net/house/electrical-complete/</link>
		<comments>http://www.xmech.net/house/electrical-complete/#comments</comments>
		<pubDate>Mon, 29 Jun 2009 17:21:32 +0000</pubDate>
		<dc:creator>kerry</dc:creator>
				<category><![CDATA[House]]></category>
		<category><![CDATA[Theatre Room]]></category>

		<guid isPermaLink="false">http://www.xmech.net/house/electrical-complete/</guid>
		<description><![CDATA[We had a productive weekend in the theatre room. All of the electrical (lights and outlets), PVC pipe for wires, and the last of the framing (around the window) is complete. All of our wires should be arriving later today so we can glue the PVC and string the wires through later tonight. We&#8217;re hoping [...]]]></description>
			<content:encoded><![CDATA[<p>We had a productive weekend in the theatre room. All of the electrical (lights and outlets), PVC pipe for wires, and the last of the framing (around the window) is complete.</p>
<p>All of our wires should be arriving later today so we can glue the PVC and string the wires through later tonight.</p>
<p>We&#8217;re hoping to start dry walling mid week if the budget can hold up to it.</p>
<p><img src="http://lh4.ggpht.com/_0McL0FrJEnE/SkhEru5ddnI/AAAAAAAADAo/OMIueNLiS_Q/s400/rearWire.jpg" /></p>
<p><img src="http://lh3.ggpht.com/_0McL0FrJEnE/SkhEseLV5RI/AAAAAAAADAw/KVfm-eguL0E/s400/rearCornerWire.jpg" /></p>
]]></content:encoded>
			<wfw:commentRss>http://www.xmech.net/house/electrical-complete/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
