<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0"><id>tag:blogger.com,1999:blog-28215292</id><updated>2013-05-23T20:00:03.510+02:00</updated><category term="Eclipse PHP IDE" /><category term="edegem" /><category term="zf" /><category term="mock objects" /><category term="Motel 6" /><category term="tek11" /><category term="books" /><category term="bug" /><category term="bugs" /><category term="development" /><category term="sebastian bergmann" /><category term="community" /><category term="Zend Core" /><category term="selenium" /><category term="hostweb" /><category term="phpbnl10" /><category term="opensocial" /><category term="mobileme" /><category term="chris cornut" /><category term="job" /><category term="configuration" /><category term="embedded device" /><category term="video" /><category term="zend_framework" /><category term="bookreview" /><category term="php 5" /><category term="tek09" /><category term="StarBucks" /><category term="rant" /><category term="planet-php" /><category term="phpnw12" /><category term="facebook" /><category term="seven things" /><category term="feweb" /><category term="Wendy's" /><category term="bru" /><category term="uml scheme" /><category term="java" /><category term="handhelds" /><category term="php architect" /><category term="authentication" /><category term="new website" /><category term="phpgg" /><category term="error handling" /><category term="webinar" /><category term="new logo" /><category term="data model" /><category term="wildfire" /><category term="marc delisle" /><category term="azure" /><category term="qaseries" /><category term="example" /><category term="zendcon09" /><category term="graphical designer" /><category term="Zend Yellow Pages" /><category term="international" /><category term="2007" /><category term="Zend" /><category term="dutch" /><category term="anysurfer" /><category term="Zend Studio" /><category term="testfest" /><category term="tek13" /><category term="stefan koopmanschap" /><category term="devzone" /><category term="mvc" /><category term="dojo" /><category term="uncon" /><category term="view" /><category term="objects of desire" /><category term="Eclipse" /><category term="delijn" /><category term="up" /><category term="crushed" /><category term="unit testing" /><category term="feedburner" /><category term="WAMP" /><category term="conferences" /><category term="open-source" /><category term="google" /><category term="php5" /><category term="podcast" /><category term="packt publishing" /><category term="web applications" /><category term="chris shiflett" /><category term="event" /><category term="zendcon07" /><category term="phpnw10" /><category term="zendcon12" /><category term="in2it" /><category term="confoo11" /><category term="torvalds" /><category term="new features" /><category term="babelfish" /><category term="Steve Jobs" /><category term="translations" /><category term="spl" /><category term="IT consulting" /><category term="darby felton" /><category term="php.ini" /><category term="zfce" /><category term="september" /><category term="brussels" /><category term="qedwiki" /><category term="uml" /><category term="contact management" /><category term="VUA" /><category term="web server" /><category term="london" /><category term="zendcon08" /><category term="60secondtech" /><category term="microsoft windows genuine advantage program foute vertaling illegal wrong translation" /><category term="web application development" /><category term="catch" /><category term="dirk merkel" /><category term="tricks" /><category term="orkut" /><category term="kevlin henney" /><category term="round" /><category term="microsoft windows vista games computergames" /><category term="developer's guide" /><category term="sqlite" /><category term="ZendFramework" /><category term="remote" /><category term="phpmyadmin" /><category term="tux" /><category term="Zend_Controller_Router_Rewrite" /><category term="meeting" /><category term="felix de vliegher" /><category term="mash-ups" /><category term="ewt08" /><category term="ausy" /><category term="jquery" /><category term="database testing" /><category term="early bird" /><category term="zendcon" /><category term="voipbuster gratis bellen sip linux macos" /><category term="12.1&quot;" /><category term="matthew weir o phinney" /><category term="ipod" /><category term="Mission" /><category term="iad" /><category term="twitter" /><category term="kernel" /><category term="Fisherman's Wharf" /><category term="Zend Certification" /><category term="symfony" /><category term="project management" /><category term="zend_form" /><category term="gmail" /><category term="zendx_jquery" /><category term="baby xander" /><category term="dpc09" /><category term="discussion" /><category term="linus" /><category term="carglass steenslag voorruit barst" /><category term="phpbelgium" /><category term="patrick allaert" /><category term="phpunit" /><category term="FOSDEM" /><category term="web developers" /><category term="microsoft windows novell linux patents patenten steve ballmer" /><category term="detachment" /><category term="vbulletin" /><category term="settings" /><category term="open source" /><category term="zenduncon" /><category term="pdo" /><category term="microsoft windows bsod installatie" /><category term="step-by-step" /><category term="presentation" /><category term="exceptions" /><category term="php center of expertise" /><category term="iphone" /><category term="hackathon" /><category term="pda" /><category term="fluffy" /><category term="zend technologies" /><category term="roosendaal" /><category term="web 2.0" /><category term="tips" /><category term="stefan priebsch" /><category term="elephant" /><category term="zendcon10" /><category term="windows azure" /><category term="zend framework certified engineer" /><category term=".net" /><category term="bubbling" /><category term="proximus" /><category term="tf09" /><category term="review" /><category term="sites" /><category term="phpazurecontest" /><category term="LPI" /><category term="bughuntday" /><category term="MySQL" /><category term="ZendCon 07" /><category term="smart business strategies" /><category term="phpdoc" /><category term="display_errors" /><category term="derrick rethans" /><category term="1991" /><category term="security" /><category term="belgacom smtp outage telenet bofh morse" /><category term="keynote" /><category term="feweb web2.0 secondlife skynetblogs flickr youtube" /><category term="acme" /><category term="pfc11" /><category term="cloud" /><category term="werkstations dell rollout new hardware" /><category term="10.2" /><category term="nexen" /><category term="flying" /><category term="trac" /><category term="class diagrams" /><category term="try" /><category term="joind.in" /><category term="jarno brandt" /><category term="Andries Seutens" /><category term="zend framework" /><category term="US NAVY" /><category term="remi woler" /><category term="elephpant" /><category term="credit crunch" /><category term="samurai" /><category term="Fleet Week" /><category term="ivo jansch" /><category term="itunes" /><category term="phptestfest" /><category term="bugzilla" /><category term="roaming" /><category term="phptek" /><category term="phpbenelux" /><category term="Alcatraz" /><category term="factuur communicatie kosten kortingen" /><category term="apple" /><category term="dia" /><category term="mvc-pattern" /><category term="amazon.com" /><category term="conference" /><category term="zend_config_xml" /><category term="phpwomen" /><category term="dpc2008" /><category term="zend framework standards" /><category term="dutch php conference 2008" /><category term="php-abstract" /><category term="2012" /><category term="zend studio for eclipse" /><category term="phplondon08" /><category term="crowd" /><category term="namespaces" /><category term="ibm" /><category term="agile" /><category term="packard bell" /><category term="helper" /><category term="chicago" /><category term="fosdem08" /><category term="windows" /><category term="bugtracking" /><category term="moeders" /><category term="17" /><category term="recruitment" /><category term="matthew weier o phinney" /><category term="Yahoo" /><category term="amsterdam" /><category term="apache" /><category term="linux" /><category term="Hyatt" /><category term="dutch php conference 2009" /><category term="php 5.3" /><category term="kerst feesten kerstfeest" /><category term="guide" /><category term="birthday" /><category term="opensuse" /><category term="phpbnl12" /><category term="php" /><category term="zend_captcha" /><category term="ajax" /><category term="zc10" /><category term="tutorial" /><category term="Zend Platform" /><category term="phpnw" /><category term="grmpyprogrammer" /><category term="calevans" /><category term="sfo" /><category term="keith casey" /><category term="ibuildings" /><category term="pfz" /><category term="how-to" /><category term="training courses" /><category term="php community" /><category term="book" /><category term="Zend Guard" /><category term="dpc10" /><category term="virus anti-virus anti-spam firewall ids pds" /><category term="french" /><category term="certification" /><category term="captcha" /><category term="phpbnl11" /><category term="tekx" /><category term="ligaya turmelle" /><category term="damien seguy" /><category term="qa" /><category term="expert php 5 tools" /><category term="de zandloper" /><category term="zend_layout" /><category term="imap" /><category term="wemmel" /><category term="San Francisco" /><category term="cal evans" /><category term="job changes" /><category term="microsoft" /><category term="tagging" /><category term="laptop crash enclosure 2.5 hard disk" /><category term="phpnw09" /><category term="fail" /><category term="mobistar" /><category term="failure" /><category term="commuting" /><title type="text">DragonBe's PHP blog</title><subtitle type="html">Blogging about PHP and related technologies</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://www.dragonbe.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://www.dragonbe.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default?start-index=26&amp;max-results=25" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>200</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/dragonbe" /><feedburner:info uri="dragonbe" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><link rel="license" type="text/html" href="http://creativecommons.org/licenses/by-nc-sa/3.0/" /><logo>http://creativecommons.org/images/public/somerights20.gif</logo><entry><id>tag:blogger.com,1999:blog-28215292.post-7300835551489017412</id><published>2013-05-23T20:00:00.000+02:00</published><updated>2013-05-23T20:00:03.513+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="example" /><category scheme="http://www.blogger.com/atom/ns#" term="tek13" /><category scheme="http://www.blogger.com/atom/ns#" term="selenium" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><category scheme="http://www.blogger.com/atom/ns#" term="phpunit" /><title type="text">UA Testing with Selenium and PHPUnit</title><content type="html">&lt;br /&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;Last week I spoke at&amp;nbsp;&lt;a href="http://tek.phparch.com/" style="color: #0069d6;"&gt;php[tek] 2013&lt;/a&gt;&amp;nbsp;where I explained to people how to get started with&amp;nbsp;&lt;a href="http://www.seleniumhq.org/" style="color: #0069d6;"&gt;Selenium IDE&lt;/a&gt;&amp;nbsp;to record user interaction with the web interface, convert them to PHPUnit testcases and automatically execute them on multiple browsers on multiple platforms.&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;The&amp;nbsp;&lt;a href="https://joind.in/8157" style="color: #0069d6;"&gt;feedback&lt;/a&gt;&amp;nbsp;I got was awesome, you're all a great crowd! But on&amp;nbsp;&lt;a href="https://twitter.com/dragonbe" style="color: #0069d6;"&gt;twitter&lt;/a&gt;&amp;nbsp;I also received a bunch of questions regarding how to set up multiple platforms and why I used Windows in my presentation to deploy to.&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;So today I deceided it was time to write a full article on this subject.&lt;/div&gt;&lt;h2 style="color: #404040; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 24px; line-height: 36px; margin: 0px; padding: 0px;"&gt;What is Selenium?&lt;/h2&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;Selenium is a tool that allows you to continuously test user interfaces of web applications. The most common usages for Selenium testing are the following:&lt;/div&gt;&lt;ul style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px;"&gt;&lt;li&gt;testing elements are (not) on the web interface&lt;/li&gt;&lt;li&gt;users can't break out a certain flow on the web interface&lt;/li&gt;&lt;li&gt;calculated values are correct after modification&lt;/li&gt;&lt;li&gt;errors appear on screen when mistakes are made by users&lt;/li&gt;&lt;li&gt;reported issues are valid&lt;/li&gt;&lt;/ul&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;In general we call these type of tests&amp;nbsp;&lt;strong&gt;User Acceptance Tests&lt;/strong&gt;&amp;nbsp;or&amp;nbsp;&lt;strong&gt;UAT&lt;/strong&gt;&amp;nbsp;and are all focused from the point of the end-user, the person using the web interface to accomplish a certain goal.&lt;/div&gt;&lt;h2 style="color: #404040; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 24px; line-height: 36px; margin: 0px; padding: 0px;"&gt;Why are they important?&lt;/h2&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;UAT have their own right to exist. Just like regular unit, performance and stress tests they have their own agenda and are adressing a particular part of your application that needs testing. All to prevent your customers/visitors from finding issues, bugs or just unfunctional pieces on your web application and loose their trust in your products or services.&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;Therefor it's always good to invest in the "visibile" part of your web application. Especially when using javascript, you want to ensure it always works as intended.&lt;/div&gt;&lt;h2 style="color: #404040; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 24px; line-height: 36px; margin: 0px; padding: 0px;"&gt;Disclaimer&lt;/h2&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;Selenium tests are in no way a replacement for regular unit tests. Their focus is on generated output of your web application within a browser. Unit tests are still necessary to ensure the logic of your application is not broken when making modifications or adding new functionality!&lt;/div&gt;&lt;h2 style="color: #404040; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 24px; line-height: 36px; margin: 0px; padding: 0px;"&gt;Setting things up&lt;/h2&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;You can write your own Selenium tests by hand, but the easiest way is to use the&amp;nbsp;&lt;a href="http://getfirefox.com/" style="color: #0069d6;"&gt;Firefox&lt;/a&gt;&amp;nbsp;plugin to recored Selenium tests.&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;Start&amp;nbsp;&lt;a href="http://getfirefox.com/" style="color: #0069d6;"&gt;Firefox&lt;/a&gt;&amp;nbsp;and download the plugin at the&amp;nbsp;&lt;a href="http://docs.seleniumhq.org/download" style="color: #0069d6;"&gt;Selenium IDE download page&lt;/a&gt;.&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;When you are asked to "restart", wait a little and scroll down on the downloads page to&amp;nbsp;&lt;strong&gt;PHP Formatter&lt;/strong&gt;&amp;nbsp;and install this formatter plugin as well.&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;Once all is installed, restart your&amp;nbsp;&lt;a href="http://getfirefox.com/" style="color: #0069d6;"&gt;Firefox&lt;/a&gt;&amp;nbsp;browser.&lt;/div&gt;&lt;iframe allowfullscreen="" frameborder="0" height="315" src="http://www.youtube.com/embed/qqVmGReJ5lk" style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px;" width="420"&gt;&lt;/iframe&gt;&lt;span style="background-color: white; color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px;"&gt;&lt;/span&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;Now you're all set to get started creating UA tests.&lt;/div&gt;&lt;h2 style="color: #404040; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 24px; line-height: 36px; margin: 0px; padding: 0px;"&gt;Overview of what we're doing here&lt;/h2&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;The Selenium IDE plugin allows you to record all events you make on the web application you want to test. These can be mouse clicks, entering text inside input fields, pressing enter, drag-and-drop and so much more. All activities are being captured using this recording tool.&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;For my example I used a simple project that's still a work-in-progress. There were already some issues reported in the bug tracker and I'm showing here how to get started with confirming the issue is truely an issue. Then we fix the issue and rerun our test to see it resolves.&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;Next we're going to export the Selenium test as PHPUnit test and run it as we'd normally run our unit tests. We can use our IDE, command line (as I will use) or a continuous integration (CI) system to run these tests over and over again.&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;Since we have a Selenium test we can modify our test case to verify our application works on multiple browsers.&lt;/div&gt;&lt;h3 style="color: #404040; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 18px; line-height: 36px; margin: 0px; padding: 0px;"&gt;Step 1: Verify an issue from bug tracker&lt;/h3&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;In many cases you're already working on a project and you have issues to resolve. The easiest way to get started with UAT would be to pick an issue and verify the issue is genuine.&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;&lt;img alt="Issue listing" src="http://blob.phpdev.nu/theialive/issue_listings.png" /&gt;&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;Go to your bug tracker and pick an issue. Look at the detailed description of the issue as this will be your&amp;nbsp;&lt;strong&gt;test goal&lt;/strong&gt;.&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;&lt;img alt="Detailed listing" src="http://blob.phpdev.nu/theialive/issue_details.png" /&gt;&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;Now that we have a full understanding of the issue, we can start recording a Selenium test. Our goal is to ensure we can reproduce the issue which is hard sometimes as reports (like this) can specify items that are missing.&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;Therefor we're going to add "assertions" in our Selenium test of items we expect to be present, but aren't at the moment. This may sound weird, but this way we think ahead on how we need to resolve the issue if the reported issue is genuine.&lt;/div&gt;&lt;iframe allowfullscreen="" frameborder="0" height="315" src="http://www.youtube.com/embed/Tof4zH7WLEo?list=PLTYgnWRibrEO_j-tc-lA690Uru5vzLICb" style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px;" width="560"&gt;&lt;/iframe&gt;&lt;span style="background-color: white; color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px;"&gt;&lt;/span&gt;&lt;h3 style="color: #404040; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 18px; line-height: 36px; margin: 0px; padding: 0px;"&gt;Step 2: Fix the reported issue and test again&lt;/h3&gt;&lt;iframe allowfullscreen="" frameborder="0" height="315" src="http://www.youtube.com/embed/IXXAh8IQyZ4?list=PLTYgnWRibrEO_j-tc-lA690Uru5vzLICb" style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px;" width="560"&gt;&lt;/iframe&gt;&lt;span style="background-color: white; color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px;"&gt;&lt;/span&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;Once we fixed the issue, we can then rerun our test and see it succeed.&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;We now save the Selenium test as .html file.&lt;/div&gt;&lt;h3 style="color: #404040; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 18px; line-height: 36px; margin: 0px; padding: 0px;"&gt;Step 3: Convert to PHPUnit test&lt;/h3&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;In the menu of your Selenium IDE window, you can select "File" -&amp;gt; "Export as" -&amp;gt; "PHP (PHPUnit)" and store this in the application's tests directory. I have chosen to create an additional directory called "seleneum" so I can differentiate unit tests for the project.&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;&lt;img alt="Export as PHPUnit test" src="http://blob.phpdev.nu/theialive/export_to_phpunit.png" /&gt;&lt;/div&gt;&lt;h3 style="color: #404040; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 18px; line-height: 36px; margin: 0px; padding: 0px;"&gt;Step 5: Set up Selenium Server&lt;/h3&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;In order to run your exported Selenium test in an automated, headless way you need to set up a computer that will run the Selenium Server service (as our unit tests will try to contact it for running the tests within a browser). Selenium Server is a Java daemon that will continuously listen to incomming calls from unit tests, so no matter if you run it manually from your IDE or command line, or you have a CI system to run your unit tests automatically, it should be able to contact the system.&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;I have chosen to use a Windows 7 64bit virtual machine as it supports the most essential browsers like Internet Explorer, Firefox, Google Chrome and others, but as the Selenium Server is a Java service, you can run it on Windows, Linux and Mac OS.&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;Go back to the&amp;nbsp;&lt;a href="http://seleniumhq.com/" style="color: #0069d6;"&gt;Selenium HQ&lt;/a&gt;&amp;nbsp;website and download the selenium-server-standalone-X.X.X.jar where X.X.X represents the current version of the tool.&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;&lt;img alt="Download Selenium Server" src="http://blob.phpdev.nu/theialive/Download_Selenium_Server.png" /&gt;&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;Once downloaded, you can set up your test machine for receiving Selenium Test calls. I created a simple startSeleniumStandalone.BAT file that I launch during startup.&lt;/div&gt;&lt;pre style="border: 1px solid rgb(217, 217, 217); color: #737373; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; font-size: 11px; line-height: 16px; margin-bottom: 18px; padding: 14px; white-space: pre-wrap; word-wrap: break-word;"&gt;&lt;code style="background-color: white; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; padding: 0px;"&gt;"C:\Program Files (x64)\Java\jre6\bin\java.exe" -jar "C:\Tools\Selenium\selenium-server-standalone-X.X.X.jar" --port 12666&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;This will launch Selenium Server on port 12666 and will execute for each browser defined a new window.&lt;/div&gt;&lt;h3 style="color: #404040; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 18px; line-height: 36px; margin: 0px; padding: 0px;"&gt;Step 6: Modifying your PHPUnit Selenium Test&lt;/h3&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;When you first export your Selenium test to a PHPUnit Testcase you need to modify a couple of things before you can execute it. This is the&amp;nbsp;&lt;strong&gt;raw&lt;/strong&gt;export from Selenium IDE:&lt;/div&gt;&lt;pre style="border: 1px solid rgb(217, 217, 217); color: #737373; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; font-size: 11px; line-height: 16px; margin-bottom: 18px; padding: 14px; white-space: pre-wrap; word-wrap: break-word;"&gt;&lt;code style="background-color: white; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; padding: 0px;"&gt;&amp;lt;?php&lt;br /&gt;class Example extends PHPUnit_Extensions_SeleniumTestCase&lt;br /&gt;{&lt;br /&gt;  protected function setUp()&lt;br /&gt;  {&lt;br /&gt;    $this-&amp;gt;setBrowser("*chrome");&lt;br /&gt;    $this-&amp;gt;setBrowserUrl("http://www.theialive.com/");&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public function testMyTestCase()&lt;br /&gt;  {&lt;br /&gt;    $this-&amp;gt;open("/");&lt;br /&gt;    $this-&amp;gt;click("link=login");&lt;br /&gt;    $this-&amp;gt;waitForPageToLoad("30000");&lt;br /&gt;    $this-&amp;gt;type("id=email", "dragonbe+tek13@gmail.com");&lt;br /&gt;    $this-&amp;gt;type("id=password", "test1234");&lt;br /&gt;    $this-&amp;gt;click("id=signin");&lt;br /&gt;    $this-&amp;gt;waitForPageToLoad("30000");&lt;br /&gt;    $this-&amp;gt;click("link=Test demo");&lt;br /&gt;    $this-&amp;gt;waitForPageToLoad("30000");&lt;br /&gt;    $this-&amp;gt;assertEquals("Done", $this-&amp;gt;getText("xpath=//th[5]"));&lt;br /&gt;    $this-&amp;gt;click("link=[EDIT]");&lt;br /&gt;    $this-&amp;gt;waitForPageToLoad("30000");&lt;br /&gt;    $this-&amp;gt;assertTrue($this-&amp;gt;isElementPresent("id=done"));&lt;br /&gt;    $this-&amp;gt;click("link=sign off");&lt;br /&gt;    $this-&amp;gt;waitForPageToLoad("30000");&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;?&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;First we need to rename our test class to something more meaningful, like&amp;nbsp;&lt;code style="background-color: #fee9cc; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; color: rgba(0, 0, 0, 0.746094); font-family: Monaco, 'Andale Mono', 'Courier New', monospace; font-size: 12px; padding: 1px 3px;"&gt;MarkTaskDoneTest&lt;/code&gt;.&lt;/div&gt;&lt;pre style="border: 1px solid rgb(217, 217, 217); color: #737373; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; font-size: 11px; line-height: 16px; margin-bottom: 18px; padding: 14px; white-space: pre-wrap; word-wrap: break-word;"&gt;&lt;code style="background-color: white; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; padding: 0px;"&gt;class MarkTaskDoneTest extends PHPUnit_Extensions_SeleniumTestCase&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;Secondly we need to modify our setup so it can connect with our Selenium Server and run Internet Explorer.&lt;/div&gt;&lt;pre style="border: 1px solid rgb(217, 217, 217); color: #737373; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; font-size: 11px; line-height: 16px; margin-bottom: 18px; padding: 14px; white-space: pre-wrap; word-wrap: break-word;"&gt;&lt;code style="background-color: white; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; padding: 0px;"&gt;protected function setUp()&lt;br /&gt;{&lt;br /&gt;    $this-&amp;gt;setBrowser("*iexplore");&lt;br /&gt;    $this-&amp;gt;setBrowseUrl("http://www.theialive.com/");&lt;br /&gt;    $this-&amp;gt;setHost('192.168.10.33');&lt;br /&gt;    $this-&amp;gt;setPort(12666);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;Lastly we modify our test method to something meaningful like&amp;nbsp;&lt;code style="background-color: #fee9cc; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; color: rgba(0, 0, 0, 0.746094); font-family: Monaco, 'Andale Mono', 'Courier New', monospace; font-size: 12px; padding: 1px 3px;"&gt;testMarkTaskAsDone&lt;/code&gt;:&lt;/div&gt;&lt;pre style="border: 1px solid rgb(217, 217, 217); color: #737373; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; font-size: 11px; line-height: 16px; margin-bottom: 18px; padding: 14px; white-space: pre-wrap; word-wrap: break-word;"&gt;&lt;code style="background-color: white; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; padding: 0px;"&gt;public function testMarkTaskAsDone()&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;We also add the following line to our test so on our test machine, all will be executed in full screen. Not really a required feature, but allways nice to see stuff running automatically.&lt;/div&gt;&lt;pre style="border: 1px solid rgb(217, 217, 217); color: #737373; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; font-size: 11px; line-height: 16px; margin-bottom: 18px; padding: 14px; white-space: pre-wrap; word-wrap: break-word;"&gt;&lt;code style="background-color: white; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; padding: 0px;"&gt;$this-&amp;gt;windowMaximize();&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;So our full test class would look like the following:&lt;/div&gt;&lt;pre style="border: 1px solid rgb(217, 217, 217); color: #737373; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; font-size: 11px; line-height: 16px; margin-bottom: 18px; padding: 14px; white-space: pre-wrap; word-wrap: break-word;"&gt;&lt;code style="background-color: white; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; padding: 0px;"&gt;&amp;lt;?php&lt;br /&gt;/**&lt;br /&gt; * Class MarkTaskDoneTest&lt;br /&gt; *&lt;br /&gt; * @group SeleniumTest&lt;br /&gt; */&lt;br /&gt;class MarkTaskDoneTest extends PHPUnit_Extensions_SeleniumTestCase&lt;br /&gt;{&lt;br /&gt;    protected function setUp()&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;setBrowser("*iexplore");&lt;br /&gt;        $this-&amp;gt;setBrowserUrl("http://www.theialive.com/");&lt;br /&gt;        $this-&amp;gt;setHost('192.168.10.33');&lt;br /&gt;        $this-&amp;gt;setPort(12666);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public function testMarkTaskAsDone()&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;windowMaximize();&lt;br /&gt;        $this-&amp;gt;open("/");&lt;br /&gt;        $this-&amp;gt;click("link=login");&lt;br /&gt;        $this-&amp;gt;waitForPageToLoad("30000");&lt;br /&gt;        $this-&amp;gt;type("id=email", "dragonbe+tek13@gmail.com");&lt;br /&gt;        $this-&amp;gt;type("id=password", "test1234");&lt;br /&gt;        $this-&amp;gt;click("id=signin");&lt;br /&gt;        $this-&amp;gt;waitForPageToLoad("30000");&lt;br /&gt;        $this-&amp;gt;click("link=Test demo");&lt;br /&gt;        $this-&amp;gt;waitForPageToLoad("30000");&lt;br /&gt;        $this-&amp;gt;assertEquals("Done", $this-&amp;gt;getText("xpath=//th[5]"));&lt;br /&gt;        $this-&amp;gt;click("link=[EDIT]");&lt;br /&gt;        $this-&amp;gt;waitForPageToLoad("30000");&lt;br /&gt;        $this-&amp;gt;assertTrue($this-&amp;gt;isElementPresent("id=done"));&lt;br /&gt;        $this-&amp;gt;click("link=sign off");&lt;br /&gt;        $this-&amp;gt;waitForPageToLoad("30000");&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;That's it! Now all is ready to run our tests using PHPUnit on the command line.&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;&lt;img alt="Running Selenium Test on CLI" src="http://blob.phpdev.nu/theialive/Running_single_Selenium_Test_on_CLI.png" /&gt;&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;And this is how our unit tests run on our test machine:&lt;/div&gt;&lt;iframe allowfullscreen="" frameborder="0" height="315" src="http://www.youtube.com/embed/pDaS9q42J2w?list=PLTYgnWRibrEO_j-tc-lA690Uru5vzLICb" style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px;" width="560"&gt;&lt;/iframe&gt;&lt;span style="background-color: white; color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px;"&gt;&lt;/span&gt;&lt;h3 style="color: #404040; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 18px; line-height: 36px; margin: 0px; padding: 0px;"&gt;Step 7: Testing with multiple browsers&lt;/h3&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;Running these tests is fun, but they still test a single browser. A better way would be to set up a base TestCase where you set all browser configurations and extend this base TestCase whenever you create a Selenium Test.&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;File: tests/SeleniumTestCase.php&lt;/div&gt;&lt;pre style="border: 1px solid rgb(217, 217, 217); color: #737373; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; font-size: 11px; line-height: 16px; margin-bottom: 18px; padding: 14px; white-space: pre-wrap; word-wrap: break-word;"&gt;&lt;code style="background-color: white; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; padding: 0px;"&gt;&amp;lt;?php&lt;br /&gt;&lt;br /&gt;require_once 'PHPUnit/Extensions/SeleniumTestCase.php';&lt;br /&gt;&lt;br /&gt;class SeleniumTestCase extends PHPUnit_Extensions_SeleniumTestCase&lt;br /&gt;{&lt;br /&gt;    const TEST_HUB = '192.168.10.33';&lt;br /&gt;    const TEST_PORT = 12666;&lt;br /&gt;&lt;br /&gt;    const USERNAME = 'dragonbe+tek13@gmail.com';&lt;br /&gt;    const PASSWORD = 'test1234';&lt;br /&gt;    const BASURL = 'http://www.theialive.com';&lt;br /&gt;&lt;br /&gt;    public static $browsers = array (&lt;br /&gt;        array (&lt;br /&gt;            'name' =&amp;gt; 'Internet Explorer 8 on Windows 7',&lt;br /&gt;            'browser' =&amp;gt; '*iexplore',&lt;br /&gt;            'host' =&amp;gt; self::TEST_HUB,&lt;br /&gt;            'port' =&amp;gt; self::TEST_PORT,&lt;br /&gt;        ),&lt;br /&gt;        array (&lt;br /&gt;            'name' =&amp;gt; 'Firefox on Windows 7',&lt;br /&gt;            'browser' =&amp;gt; '*firefox',&lt;br /&gt;            'host' =&amp;gt; self::TEST_HUB,&lt;br /&gt;            'port' =&amp;gt; self::TEST_PORT,&lt;br /&gt;        ),&lt;br /&gt;        array (&lt;br /&gt;            'name' =&amp;gt; 'Google Chrome on Windows 7',&lt;br /&gt;            'browser' =&amp;gt; '*googlechrome',&lt;br /&gt;            'host' =&amp;gt; self::TEST_HUB,&lt;br /&gt;            'port' =&amp;gt; self::TEST_PORT,&lt;br /&gt;        ),&lt;br /&gt;    );&lt;br /&gt;&lt;br /&gt;    protected function setUp()&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;setBrowserUrl(self::BASURL);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;Now we just need to extend from our base test case to continuously use all configured browsers.&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;File: tests/selenium/TaskTest.php&lt;/div&gt;&lt;pre style="border: 1px solid rgb(217, 217, 217); color: #737373; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; font-size: 11px; line-height: 16px; margin-bottom: 18px; padding: 14px; white-space: pre-wrap; word-wrap: break-word;"&gt;&lt;code style="background-color: white; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; font-family: Monaco, 'Andale Mono', 'Courier New', monospace; padding: 0px;"&gt;&amp;lt;?php&lt;br /&gt;/**&lt;br /&gt; * Class MarkTaskDoneTest&lt;br /&gt; *&lt;br /&gt; * @group SeleniumTest&lt;br /&gt; */&lt;br /&gt;require_once 'SeleniumTestCase.php';&lt;br /&gt;class MarkTaskDoneTest extends SeleniumTestCase&lt;br /&gt;{&lt;br /&gt;    public function testMarkTestAsDone()&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;windowMaximize();&lt;br /&gt;        $this-&amp;gt;open("/");&lt;br /&gt;        $this-&amp;gt;click("link=login");&lt;br /&gt;        $this-&amp;gt;waitForPageToLoad("30000");&lt;br /&gt;        $this-&amp;gt;type("id=email", TestCase::USERNAME);&lt;br /&gt;        $this-&amp;gt;type("id=password", TestCase::PASSWORD);&lt;br /&gt;        $this-&amp;gt;click("id=signin");&lt;br /&gt;        $this-&amp;gt;waitForPageToLoad("30000");&lt;br /&gt;        $this-&amp;gt;click("link=Test demo");&lt;br /&gt;        $this-&amp;gt;waitForPageToLoad("30000");&lt;br /&gt;        $this-&amp;gt;assertEquals("Done", $this-&amp;gt;getText("xpath=//th[5]"));&lt;br /&gt;        $this-&amp;gt;click("link=[EDIT]");&lt;br /&gt;        $this-&amp;gt;waitForPageToLoad("30000");&lt;br /&gt;        $this-&amp;gt;assertTrue($this-&amp;gt;isElementPresent("id=done"));&lt;br /&gt;        $this-&amp;gt;click("link=sign off");&lt;br /&gt;        $this-&amp;gt;waitForPageToLoad("30000");&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;Running it on CLI will execute 3 times this test as we're testing 3 browsers: Internet Explorer, Firefox and Google Chrome.&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;&lt;img alt="Running on CLI" src="http://blob.phpdev.nu/theialive/Running_multiple_Selenium_Test_on_CLI.png" /&gt;&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;On our test machine it would look something like this:&lt;/div&gt;&lt;iframe allowfullscreen="" frameborder="0" height="315" src="http://www.youtube.com/embed/6jGhYW3JFs0?list=PLTYgnWRibrEO_j-tc-lA690Uru5vzLICb" style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px;" width="560"&gt;&lt;/iframe&gt;&lt;span style="background-color: white; color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px;"&gt;&lt;/span&gt;&lt;h2 style="color: #404040; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 24px; line-height: 36px; margin: 0px; padding: 0px;"&gt;Closing Remarks&lt;/h2&gt;&lt;h3 style="color: #404040; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 18px; line-height: 36px; margin: 0px; padding: 0px;"&gt;Authentication&lt;/h3&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;When running Selenium Tests and your application requires authentication, the best thing you can do is to have your test do the following:&lt;/div&gt;&lt;ul style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px;"&gt;&lt;li&gt;log into your application (carefull, passwords are plain text!!!)&lt;/li&gt;&lt;li&gt;run your tests and assertsions&lt;/li&gt;&lt;li&gt;log out of your application&lt;/li&gt;&lt;/ul&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;This way your tests always behave the same.&lt;/div&gt;&lt;h3 style="color: #404040; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 18px; line-height: 36px; margin: 0px; padding: 0px;"&gt;Multi platform setup&lt;/h3&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;As good developer I not only test on different browsers, but also on multiple platforms. I use virtual machines for this that are always running and I use the following setup to achieve this:&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;&lt;img alt="Multi-node testing with Selenium" src="http://blob.phpdev.nu/theialive/Continuous_UA_Testing.png" /&gt;&lt;/div&gt;&lt;h3 style="color: #404040; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 18px; line-height: 36px; margin: 0px; padding: 0px;"&gt;Dom changes&lt;/h3&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;Be careful as Selenium tests heavily depend on the&amp;nbsp;&lt;a href="http://en.wikipedia.org/wiki/Document_Object_Model" style="color: #0069d6;"&gt;DOM&lt;/a&gt;&amp;nbsp;of a web application. Any changes there can have an effect on your Selenium Tests and might result in failing tests.&lt;/div&gt;&lt;h2 style="color: #404040; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 24px; line-height: 36px; margin: 0px; padding: 0px;"&gt;Recommended reading&lt;/h2&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;See my presentation as it was given at&amp;nbsp;&lt;a href="http://tek.phparch.com/" style="color: #0069d6;"&gt;php[tek] 2013&lt;/a&gt;&lt;/div&gt;&lt;iframe allowfullscreen="" frameborder="0" height="486" marginheight="0" marginwidth="0" mozallowfullscreen="" scrolling="no" src="http://www.slideshare.net/slideshow/embed_code/21344408" style="border-color: rgb(204, 204, 204); border-style: solid; border-width: 1px 1px 0px; color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 5px;" webkitallowfullscreen="" width="597"&gt;&lt;/iframe&gt;&lt;span style="background-color: white; color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px;"&gt;&lt;/span&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 5px;"&gt;&lt;strong&gt;&lt;a href="http://www.slideshare.net/DragonBe/ua-testing-with-selenium-and-php-unit" style="color: #0069d6;" target="_blank" title="UA testing with selenium and php unit"&gt;UA testing with selenium and php unit&lt;/a&gt;&amp;nbsp;&lt;/strong&gt;from&amp;nbsp;&lt;strong&gt;&lt;a href="http://www.slideshare.net/DragonBe" style="color: #0069d6;" target="_blank"&gt;Michelangelo van Dam&lt;/a&gt;&lt;/strong&gt;&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;These books I can highly recommend reading!&lt;/div&gt;&lt;iframe frameborder="0" marginheight="0" marginwidth="0" scrolling="no" src="http://rcm.amazon.com/e/cm?t=in2it-20&amp;amp;o=1&amp;amp;p=8&amp;amp;l=as1&amp;amp;asins=1849518300&amp;amp;ref=tf_til&amp;amp;fc1=000000&amp;amp;IS2=1&amp;amp;lt1=_blank&amp;amp;m=amazon&amp;amp;lc1=0000FF&amp;amp;bc1=000000&amp;amp;bg1=FFFFFF&amp;amp;f=ifr" style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; height: 240px; line-height: 18px; width: 120px;"&gt;&lt;/iframe&gt;&lt;span style="background-color: white; color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px;"&gt;&amp;nbsp;&lt;/span&gt;&lt;iframe frameborder="0" marginheight="0" marginwidth="0" scrolling="no" src="http://rcm.amazon.com/e/cm?t=in2it-20&amp;amp;o=1&amp;amp;p=8&amp;amp;l=as1&amp;amp;asins=1849515743&amp;amp;ref=tf_til&amp;amp;fc1=000000&amp;amp;IS2=1&amp;amp;lt1=_blank&amp;amp;m=amazon&amp;amp;lc1=0000FF&amp;amp;bc1=000000&amp;amp;bg1=FFFFFF&amp;amp;f=ifr" style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; height: 240px; line-height: 18px; width: 120px;"&gt;&lt;/iframe&gt;&lt;span style="background-color: white; color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px;"&gt;&amp;nbsp;&lt;/span&gt;&lt;iframe frameborder="0" marginheight="0" marginwidth="0" scrolling="no" src="http://rcm.amazon.com/e/cm?t=in2it-20&amp;amp;o=1&amp;amp;p=8&amp;amp;l=as1&amp;amp;asins=0321534468&amp;amp;ref=tf_til&amp;amp;fc1=000000&amp;amp;IS2=1&amp;amp;lt1=_blank&amp;amp;m=amazon&amp;amp;lc1=0000FF&amp;amp;bc1=000000&amp;amp;bg1=FFFFFF&amp;amp;f=ifr" style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; height: 240px; line-height: 18px; width: 120px;"&gt;&lt;/iframe&gt;&lt;span style="background-color: white; color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px;"&gt;&amp;nbsp;&lt;/span&gt;&lt;iframe frameborder="0" marginheight="0" marginwidth="0" scrolling="no" src="http://rcm.amazon.com/e/cm?t=in2it-20&amp;amp;o=1&amp;amp;p=8&amp;amp;l=as1&amp;amp;asins=0470872497&amp;amp;ref=tf_til&amp;amp;fc1=000000&amp;amp;IS2=1&amp;amp;lt1=_blank&amp;amp;m=amazon&amp;amp;lc1=0000FF&amp;amp;bc1=000000&amp;amp;bg1=FFFFFF&amp;amp;f=ifr" style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; height: 240px; line-height: 18px; width: 120px;"&gt;&lt;/iframe&gt;&lt;span style="background-color: white; color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px;"&gt;&lt;/span&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;&lt;a href="https://leanpub.com/grumpy-phpunit" style="color: #0069d6;"&gt;&lt;img alt="The Grumpy Programmer's PHPUnit Cookbook" src="https://s3.amazonaws.com/titlepages.leanpub.com/grumpy-phpunit/small?1365270960" style="border: none;" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="color: #737373; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', Arial, sans-serif; font-size: 13px; line-height: 18px; margin-bottom: 9px; padding: 0px;"&gt;&lt;a href="https://leanpub.com/grumpy-testing" style="color: #0069d6;"&gt;&lt;img alt="The Grumpy Programmer's Guide To Building Testable PHP Applications" src="https://s3.amazonaws.com/titlepages.leanpub.com/grumpy-testing/small?1360292805" style="border: none;" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/KRIWCBDP7Fg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/7300835551489017412/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2013/05/ua-testing-with-selenium-and-phpunit.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/7300835551489017412" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/7300835551489017412" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/KRIWCBDP7Fg/ua-testing-with-selenium-and-phpunit.html" title="UA Testing with Selenium and PHPUnit" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://img.youtube.com/vi/qqVmGReJ5lk/default.jpg" height="72" width="72" /><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2013/05/ua-testing-with-selenium-and-phpunit.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-7389037723913674917</id><published>2013-05-20T14:14:00.001+02:00</published><updated>2013-05-20T14:14:14.799+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="chicago" /><category scheme="http://www.blogger.com/atom/ns#" term="tek13" /><category scheme="http://www.blogger.com/atom/ns#" term="php community" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><title type="text">Survived php tek 2013</title><content type="html">&lt;a href="https://twitter.com/CaseySoftware/status/335192997983555584/photo/1" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="112" src="http://1.bp.blogspot.com/-DZ6tgyZSHRw/UZoBfcvrm0I/AAAAAAAADxk/Sv6pwwXRMQw/s200/BKbYQXhCEAArUQm.jpg-large.jpeg" width="200" /&gt;&lt;/a&gt;If you were last week in Chicago, you've might felt the city was buzzing &lt;a href="http://php.net/"&gt;PHP&lt;/a&gt; all over the place. &lt;a href="http://tek.phparch.com/"&gt;php[tek] 2013&lt;/a&gt; was taking place at the &lt;a href="http://www.sheratonchicagoohare.com/"&gt;Sheraton Gateway Suites Chicago O'Hare&lt;/a&gt; in Rosemont, just outside of Chicago city.&lt;br /&gt;&lt;br /&gt;This year it was also the first time &lt;a href="http://musketeers.me/"&gt;Musketeers.me&lt;/a&gt;, a php consulting team from the East Coast, was running the show, putting their own signature onto the event. And with great success I might add. A well deserved applause to &lt;a href="https://twitter.com/kevinbruce"&gt;Eli White&lt;/a&gt;, &lt;a href="https://twitter.com/kevinbruce"&gt;Kevin Bruce&lt;/a&gt;, &lt;a href="https://twitter.com/sandys1"&gt;Sandy Smith&lt;/a&gt;, &lt;a href="https://twitter.com/omerida"&gt;Oscar Merida&lt;/a&gt; and of course the &lt;a href="https://twitter.com/e3betht"&gt;Beth Tucker Long&lt;/a&gt; for their unlocked achievement running a great conference.&lt;br /&gt;&lt;br /&gt;For me was also the first time I was running the uncon, where attendees and conference speakers could propose talks they wanted to present at the uncon and have other attendees vote for them.&lt;br /&gt;&lt;br /&gt;One uncon talk really stood out: "&lt;a href="https://speakerdeck.com/funkatron/open-sourcing-mental-illness"&gt;Open Sourcing mental illness&lt;/a&gt;" by Ed Finkler (&lt;a href="https://twitter.com/funkatron"&gt;@funkatron&lt;/a&gt;) where he discussed the issues people face who suffer a mental illness. It was an emotional talk where Ed described his own experiences and how his mind made him think about things differently then the rest of us. For me it was a real eye-opener and made me understand that there are people that don't really take things for granted.&lt;br /&gt;&lt;br /&gt;This year's edition was filed with very good talks and it's almost impossible to give my feedback on all of them. A few talks that I attended really stood out that I would really want to promote here.&lt;br /&gt;&lt;br /&gt;&lt;a href="https://twitter.com/enygma"&gt;Chris Cornut&lt;/a&gt;, the driving force behind &lt;a href="http://phpdeveloper.org/"&gt;phpdeveloper.org&lt;/a&gt;, talked about "&lt;a href="https://speakerdeck.com/ccornutt/beyond-the-basics-security-with-php-1"&gt;Beyond the Basics: Security with PHP&lt;/a&gt;" where he did not just list the top 10 of &lt;a href="https://www.owasp.org/index.php/Main_Page"&gt;OWASP&lt;/a&gt;, but also gave good advices on how to protect yourself against most of the common attacks. A good closing hint: be better secured than the next guy.&lt;br /&gt;&lt;br /&gt;&lt;a href="https://twitter.com/jeremykendall"&gt;Jeremy Kendall&lt;/a&gt; was giving good advice on how to improve your code you wrote a couple of weeks or months ago in his talk "&lt;a href="http://www.slideshare.net/jeremykendall/php-102-out-with-the-bad-in-with-the-good-21331876"&gt;PHP 102: Out with the Bad, In with the Good&lt;/a&gt;". What I like about Jeremy is he can bring very complex subjects in an easy, understandable way that even a novice can understand complex software engineering stuff. And so he did with this talk.&lt;br /&gt;&lt;br /&gt;One talk I missed which I really wanted to see was the &lt;a href="http://www.slideshare.net/VampyreBytes/php-ooh-shiny"&gt;Distractions&lt;/a&gt; talk of &lt;a href="https://twitter.com/sprunka"&gt;Sean Prunka&lt;/a&gt;, or how to deal with distractions when you're a developer. When I look at the&lt;a href="https://joind.in/8152"&gt; reviews on joind.in&lt;/a&gt; I see he did an amazing good job giving this was his first conference talk ever. So hopefully he will do a webinar or an online recording of this talk *hint, hint*.&lt;br /&gt;&lt;br /&gt;This year &lt;a href="http://in2it.be/"&gt;my company&lt;/a&gt; was sponsoring the hackathon and can be called a good success, knowing we had to compete against Lego fun party. According to &lt;a href="https://twitter.com/lornajane"&gt;Lorna&lt;/a&gt; a bunch of pull requests were made for joind.in (the community feedback platform for conference speakers). And we captured the first pull request on twitter.&lt;br /&gt;&lt;br /&gt;&lt;blockquote class="twitter-tweet"&gt;Can I get a woot? First @&lt;a href="https://twitter.com/joindin"&gt;joindin&lt;/a&gt; pull request of the night has been merged at &lt;a href="https://twitter.com/search/%23tek13"&gt;#tek13&lt;/a&gt; hackathon!&lt;br /&gt;— Lorna Mitchell (@lornajane) &lt;a href="https://twitter.com/lornajane/status/335177872660656128"&gt;May 16, 2013&lt;/a&gt;&lt;/blockquote&gt;&lt;script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"&gt;&lt;/script&gt; &lt;br /&gt;At the introduction of the hackathon we also had a nice surprise for &lt;a href="https://twitter.com/caseysoftware"&gt;Mr. Keith Casey&lt;/a&gt; from his colleagues at &lt;a href="http://twillio.com/"&gt;Twillio&lt;/a&gt; where he worked 2 years now.&lt;br /&gt;&lt;br /&gt;&lt;iframe allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/AMiNRZrLjhM?list=UUIQxFQaVSwOFsv1Tf4cvspA" width="560"&gt;&lt;/iframe&gt; &lt;br /&gt;And of course the hallway tracks were not only fun, but also hugely valuable for the community. A spontanious group were giving lessons on doing TDD, others were hacking on gadgets, or just having discussions on best practices. Guess who talked about "how to get started with a user group" and got 3 people stating they would either start or reboot a user group in their area. So Riga in Latvia,&amp;nbsp;Charlottesville in Virginia and Paris in France: get ready as there's a php user group coming near you!&lt;br /&gt;&lt;br /&gt;In my experience the best php[tek] ever, and I'm really looking forward to the 2014 edition. If you don't believe me, have a look at the &lt;a href="http://www.flickr.com/photos/dragonbe/sets/72157633536845602"&gt;pictures taken&lt;/a&gt; at &lt;a href="http://tek.phparch.com/"&gt;php[tek] 2013&lt;/a&gt;. They will tell the story.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/dragonbe/8749902690/" title="Getting to 100% code coverage by DragonBe, on Flickr"&gt;&lt;img alt="Getting to 100% code coverage" height="500" src="http://farm3.staticflickr.com/2813/8749902690_339b0b970b_q.jpg" width="500" /&gt;&lt;/a&gt;&lt;a href="http://www.flickr.com/photos/dragonbe/8746896935/" title="Sitting at @ramseyben his talk &amp;quot;API first&amp;quot; #tek13 by DragonBe, on Flickr"&gt;&lt;img alt="Sitting at @ramseyben his talk &amp;quot;API first&amp;quot; #tek13" height="500" src="http://farm8.staticflickr.com/7306/8746896935_45f3491b08.jpg" width="500" /&gt;&lt;/a&gt;&lt;a href="http://www.flickr.com/photos/dragonbe/8746755887/" title="Teenage Mutant PHP Turtles taking over @rdohms ' talk #tek13 by DragonBe, on Flickr"&gt;&lt;img alt="Teenage Mutant PHP Turtles taking over @rdohms ' talk #tek13" height="500" src="http://farm8.staticflickr.com/7317/8746755887_c603f08eff.jpg" width="500" /&gt;&lt;/a&gt;&lt;a href="http://www.flickr.com/photos/dragonbe/8746638468/" title="Hackathon now #tek13 by DragonBe, on Flickr"&gt;&lt;img alt="Hackathon now #tek13" height="500" src="http://farm9.staticflickr.com/8416/8746638468_72f0890f09.jpg" width="500" /&gt;&lt;/a&gt;&lt;a href="http://www.flickr.com/photos/dragonbe/8745612978/" title="Listening to @jeremykendall about better coding #tek13 by DragonBe, on Flickr"&gt;&lt;img alt="Listening to @jeremykendall about better coding #tek13" height="500" src="http://farm8.staticflickr.com/7287/8745612978_62b0461545.jpg" width="500" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Maybe I'll see you next year at #tek14!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/3oE_G4Pmuho" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/7389037723913674917/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2013/05/survived-php-tek-2013.html#comment-form" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/7389037723913674917" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/7389037723913674917" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/3oE_G4Pmuho/survived-php-tek-2013.html" title="Survived php tek 2013" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-DZ6tgyZSHRw/UZoBfcvrm0I/AAAAAAAADxk/Sv6pwwXRMQw/s72-c/BKbYQXhCEAArUQm.jpg-large.jpeg" height="72" width="72" /><thr:total>3</thr:total><georss:featurename>O&amp;#39;Hare, Chicago, IL, USA</georss:featurename><georss:point>41.99719964056275 -87.8836727142334</georss:point><georss:box>41.99424914056275 -87.8887152142334 42.000150140562745 -87.8786302142334</georss:box><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2013/05/survived-php-tek-2013.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-5696676395234724594</id><published>2013-03-17T12:08:00.000+01:00</published><updated>2013-03-17T12:08:54.488+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="grmpyprogrammer" /><category scheme="http://www.blogger.com/atom/ns#" term="unit testing" /><category scheme="http://www.blogger.com/atom/ns#" term="database testing" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><category scheme="http://www.blogger.com/atom/ns#" term="pdo" /><category scheme="http://www.blogger.com/atom/ns#" term="mock objects" /><category scheme="http://www.blogger.com/atom/ns#" term="phpunit" /><title type="text">Look mama, no databases</title><content type="html">I recently got a question about testing services, where this person was under the impression that when calling services you did not need to know what was going on. You just pass in data and a result is returned. How this result was given was of no importance, all you needed to test was that you got a result and it matched up with what was expected.&lt;br /&gt;&lt;br /&gt;The only problem in this case was that he was talking about testing the business logic in the service and I replied he should perform tests on that business logic instead of just asserting the returned result. After some further discussions it was clear that he tested the service "as is", meaning the service makes a call to a database to gather its results and returns the calculation on those results.&lt;br /&gt;&lt;br /&gt;When I state "as is", I truly mean the way it's being used in production. So the database call collects real data on which business logic is applied. You can see this is not a healthy situation, especially when you also have services that apply business logic on data and store it back into the database.&lt;br /&gt;&lt;br /&gt;In "&lt;a href="https://leanpub.com/grumpy-phpunit?ref=dragonbe"&gt;The Grumpy Programmer's PHPUnit Cookbook&lt;/a&gt;", author &lt;a href="https://twitter.com/grmpyprogrammer"&gt;Chris Hartjes&lt;/a&gt; wrote this one sentence that says it all: "&lt;i&gt;Unit test suites are meant to be testing code, not the ability of a database server to return results&lt;/i&gt;". And he's right, you shouldn't use database connections when your testing business rules and functional logic.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;DISCLAIMER&lt;/b&gt;: I'm still using PHPUnit 3.4.15 for testing Zend Framework 1 projects, so all tests here should work with higher versions of PHPUnit. If not, let me know in the comments.&lt;br /&gt;&lt;br /&gt;Say you have a service that calculates margins for sold products, it's easy to connect to a sandboxed database and retrieve the product purchase and sales prices, retrieve the commission for the sales person on top of it all and return the calculated margin. Easy enough, right? But say you have thousands of tests like this that all connect to several tables in the database. Running tests will only go slower and slower until it reaches the point developers on the team don't run them because they will take too long to run.&lt;br /&gt;&lt;br /&gt;Example: Product.php&lt;br /&gt;&lt;br /&gt;&lt;pre code="php"&gt;&amp;lt;?php&lt;br /&gt;&lt;br /&gt;class Product&lt;br /&gt;{&lt;br /&gt;    protected $_db;&lt;br /&gt;&lt;br /&gt;    public function __construct()&lt;br /&gt;    {&lt;br /&gt;        $dsn = 'mysql:dbname=test;host=localhost;';&lt;br /&gt;        $user = 'testuser';&lt;br /&gt;        $pass = 'test123';&lt;br /&gt;        $this-&amp;gt;_db = new PDO($dsn, $user, $pass);&lt;br /&gt;    }&lt;br /&gt;    public function calculateMargins($productId)&lt;br /&gt;    {&lt;br /&gt;        $sql = 'SELECT * FROM product&lt;br /&gt;                    LEFT JOIN employee&lt;br /&gt;                        ON product.employeeId = employee.employeeId&lt;br /&gt;                    WHERE product.id = ?';&lt;br /&gt;        $stmt = $this-&amp;gt;_db-&amp;gt;prepare(&lt;br /&gt;            $sql, array (PDO::ATTR_CURSOR =&amp;gt; PDO::CURSOR_SCROLL));&lt;br /&gt;&lt;br /&gt;        $stmt-&amp;gt;execute(array ($productId));&lt;br /&gt;        $result = $stmt-&amp;gt;fetchAll();&lt;br /&gt;        $margin = ($result[0]['salesPrice'] - $result[0]['purchasePrice'])&lt;br /&gt;                * (1 - $result[0]['commission']);&lt;br /&gt;        return $margin;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;This can be just a normal class like you must have dozens off. In the case where a test is being made to validate this business logic, this class will connect to your database, retrieves the data and performs the calculation.&lt;br /&gt;&lt;br /&gt;Example test that will pass when the values are the following:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;purchasePrice: 299.95&lt;/li&gt;&lt;li&gt;salesPrice: 399.95&lt;/li&gt;&lt;li&gt;commission: 15%&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;pre code="php"&gt;&amp;lt;?php&lt;br /&gt;require_once 'Product.php';&lt;br /&gt;&lt;br /&gt;class ProductTest extends PHPUnit_Framework_TestCase&lt;br /&gt;{&lt;br /&gt;    /**&lt;br /&gt;     * @backupGlobals disabled&lt;br /&gt;     * @backupStaticAttributes disabled&lt;br /&gt;     */&lt;br /&gt;    public function testCalculateProductMargin()&lt;br /&gt;    {&lt;br /&gt;        $expectedMargin = 85.00;&lt;br /&gt;        $product = new Product();&lt;br /&gt;        $result = $product-&amp;gt;calculateMargins(1);&lt;br /&gt;        $this-&amp;gt;assertSame($expected, $result,&lt;br /&gt;            sprintf('Expected margin of %02f did not match actual margin %02f',&lt;br /&gt;                $expected, $result));&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The main issue I have with this test is that I'm too depending on the database to deliver me the data. And what happens if another developer needs to modify the data in the database for his tests? Exactly, my test will fail.&lt;br /&gt;&lt;br /&gt;How can we solve this so we can test the business logic without connecting to the database?&lt;br /&gt;&lt;br /&gt;The first thing we need to do is to ensure we can pass in a database replacement into our Product class, our mock object. So we rewrite the Product class a little so the constructor accepts a dbAdapter as parameter.&lt;br /&gt;&lt;br /&gt;&lt;pre code="php"&gt;&amp;lt;?php&lt;br /&gt;&lt;br /&gt;class Product&lt;br /&gt;{&lt;br /&gt;    protected $_db;&lt;br /&gt;&lt;br /&gt;    public function __construct($db = null)&lt;br /&gt;    {&lt;br /&gt;        if (null === $db) {&lt;br /&gt;            $dsn = 'mysql:dbname=test;host=localhost;';&lt;br /&gt;            $user = 'testuser';&lt;br /&gt;            $pass = 'test123';&lt;br /&gt;            $db = new PDO($dsn, $user, $pass);&lt;br /&gt;        }&lt;br /&gt;        $this-&amp;gt;_db = $db;&lt;br /&gt;    }&lt;br /&gt;    public function calculateMargins($productId)&lt;br /&gt;    {&lt;br /&gt;        $sql = 'SELECT * FROM product&lt;br /&gt;                    LEFT JOIN employee&lt;br /&gt;                        ON product.employeeId = employee.employeeId&lt;br /&gt;                    WHERE product.id = ?';&lt;br /&gt;        $stmt = $this-&amp;gt;_db-&amp;gt;prepare(&lt;br /&gt;            $sql, array (PDO::ATTR_CURSOR =&amp;gt; PDO::CURSOR_SCROLL));&lt;br /&gt;&lt;br /&gt;        $stmt-&amp;gt;execute(array ($productId));&lt;br /&gt;        $result = $stmt-&amp;gt;fetchAll();&lt;br /&gt;        $margin = ($result[0]['salesPrice'] - $result[0]['purchasePrice'])&lt;br /&gt;                * (1 - $result[0]['commission']);&lt;br /&gt;        return $margin;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The next thing we need to do is to modify our ProductTest class so we can set our values directly in our test, without relying on databases for providing us this data.&lt;br /&gt;&lt;br /&gt;&lt;pre code="php"&gt;&amp;lt;?php&lt;br /&gt;require_once 'Product.php';&lt;br /&gt;&lt;br /&gt;class ProductTest extends PHPUnit_Framework_TestCase&lt;br /&gt;{&lt;br /&gt;    /**&lt;br /&gt;     * @backupGlobals disabled&lt;br /&gt;     * @backupStaticAttributes disabled&lt;br /&gt;     */&lt;br /&gt;    public function testCalculateProductMargin()&lt;br /&gt;    {&lt;br /&gt;        $data = array (&lt;br /&gt;            array (&lt;br /&gt;                'purchasePrice' =&amp;gt; 299.95,&lt;br /&gt;                'salesPrice' =&amp;gt; 399.95,&lt;br /&gt;                'commission' =&amp;gt; 0.15,&lt;br /&gt;            ),&lt;br /&gt;        );&lt;br /&gt;&lt;br /&gt;        $stmt = $this-&amp;gt;getMock('PDOStatement', array ('execute','fetchAll'));&lt;br /&gt;        $stmt-&amp;gt;expects($this-&amp;gt;any())&lt;br /&gt;             -&amp;gt;method('execute')&lt;br /&gt;             -&amp;gt;will($this-&amp;gt;returnValue(true));&lt;br /&gt;        $stmt-&amp;gt;expects($this-&amp;gt;any())&lt;br /&gt;             -&amp;gt;method('fetchAll')&lt;br /&gt;             -&amp;gt;will($this-&amp;gt;returnValue($data));&lt;br /&gt;&lt;br /&gt;        $pdo = $this-&amp;gt;getMock('PDO', array('prepare'),&lt;br /&gt;            array('sqlite:dbname=:memory'),'PDOMock',true);&lt;br /&gt;        $pdo-&amp;gt;expects($this-&amp;gt;any())&lt;br /&gt;            -&amp;gt;method('prepare')&lt;br /&gt;            -&amp;gt;will($this-&amp;gt;returnValue($stmt));&lt;br /&gt;&lt;br /&gt;        $expectedMargin = 85.00;&lt;br /&gt;        $product = new Product($pdo);&lt;br /&gt;        $result = $product-&amp;gt;calculateMargins(1);&lt;br /&gt;        $this-&amp;gt;assertSame($expected, $result,&lt;br /&gt;            sprintf('Expected margin of %02f did not match actual margin %02f',&lt;br /&gt;                $expected, $result));&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Yes, we need to mock out our PDO object and our PDOStatement object just to mock the behaviour of our database call, but the benefit is now we can focus on the business logic instead of expecting everything will be provided. We now have just one set of values, but we can just add more and more values to it and move it to a data provider.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;?php&lt;br /&gt;require_once 'Product.php';&lt;br /&gt;&lt;br /&gt;class ProductTest extends PHPUnit_Framework_TestCase&lt;br /&gt;{&lt;br /&gt;    public function marginDataProvider()&lt;br /&gt;    {&lt;br /&gt;        return array (&lt;br /&gt;            array (299.95, 399.95, 0.15, 85.00),&lt;br /&gt;            array (1200.00, 1500.00, 0.10, 270.00),&lt;br /&gt;            array (1200.00, 1200.00, 0.10, 0.00),&lt;br /&gt;            array (1200.00, 1000.00, 0.10, -180.00),&lt;br /&gt;        );&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * @backupGlobals disabled&lt;br /&gt;     * @backupStaticAttributes disabled&lt;br /&gt;     * @dataProvider marginDataProvider&lt;br /&gt;     */&lt;br /&gt;    public function testCalculateProductMargin($pp, $sp, $comm, $expected)&lt;br /&gt;    {&lt;br /&gt;        $data = array (&lt;br /&gt;            array (&lt;br /&gt;                'purchasePrice' =&amp;gt; $pp,&lt;br /&gt;                'salesPrice' =&amp;gt; $sp,&lt;br /&gt;                'commission' =&amp;gt; $comm,&lt;br /&gt;            ),&lt;br /&gt;        );&lt;br /&gt;&lt;br /&gt;        $stmt = $this-&amp;gt;getMock('PDOStatement', array ('execute','fetchAll'));&lt;br /&gt;        $stmt-&amp;gt;expects($this-&amp;gt;any())&lt;br /&gt;             -&amp;gt;method('execute')&lt;br /&gt;             -&amp;gt;will($this-&amp;gt;returnValue(true));&lt;br /&gt;        $stmt-&amp;gt;expects($this-&amp;gt;any())&lt;br /&gt;             -&amp;gt;method('fetchAll')&lt;br /&gt;             -&amp;gt;will($this-&amp;gt;returnValue($data));&lt;br /&gt;&lt;br /&gt;        $pdo = $this-&amp;gt;getMock('PDO', array('prepare'),&lt;br /&gt;            array('sqlite:dbname=:memory'),'PDOMock_' . uniqid(),true);&lt;br /&gt;        $pdo-&amp;gt;expects($this-&amp;gt;any())&lt;br /&gt;            -&amp;gt;method('prepare')&lt;br /&gt;            -&amp;gt;will($this-&amp;gt;returnValue($stmt));&lt;br /&gt;&lt;br /&gt;        $product = new Product($pdo);&lt;br /&gt;        $result = $product-&amp;gt;calculateMargins(1);&lt;br /&gt;        $this-&amp;gt;assertSame($expected, $result,&lt;br /&gt;            sprintf('Expected margin of %02f did not match actual margin %02f',&lt;br /&gt;                $expected, $result));&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;So now we can test multiple values over and over again, and if for some reason we need to test a specific combination of values, we just add it to our data provider method. In this example we added values to see what happens when we have no margin and when we have sold products below purchase price.&lt;br /&gt;&lt;br /&gt;The end result is you have a test that will test the business requirements, isn't making an expensive database call (runs faster) and allows you to modify the test in case the scope of business logic changes (e.g. no commission is paid when sales price is equal or below the purchase price).&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/bdad6OMmUk0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/5696676395234724594/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2013/03/look-mama-no-databases.html#comment-form" title="8 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/5696676395234724594" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/5696676395234724594" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/bdad6OMmUk0/look-mama-no-databases.html" title="Look mama, no databases" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><thr:total>8</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2013/03/look-mama-no-databases.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-8032090713264671839</id><published>2012-11-28T14:50:00.001+01:00</published><updated>2012-11-28T14:50:06.586+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="php" /><category scheme="http://www.blogger.com/atom/ns#" term="zend framework" /><category scheme="http://www.blogger.com/atom/ns#" term="Zend_Controller_Router_Rewrite" /><title type="text">Route default is not defined</title><content type="html">Working this long with &lt;a href="http://framework.zend.com/"&gt;Zend Framework&lt;/a&gt; makes me do things that have become a real routine and I don't pay much attention to it.&amp;nbsp;So when I just add few custom routes to my&amp;nbsp;&lt;a href="http://framework.zend.com/"&gt;Zend Framework&lt;/a&gt;&amp;nbsp;application, I don't make a big fuss out of it. But apparently people do struggle with it. They get messages like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="php"&gt;Zend_Controller_Router_Exception: Route default is not defined&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Apparently when you create custom routes,&amp;nbsp;&lt;a href="http://framework.zend.com/"&gt;Zend Framework&lt;/a&gt;&amp;nbsp;"forgets" about the default route, causing this error to appear.&lt;br /&gt;&lt;br /&gt;To quickly resolve this issue, you just open up &lt;b&gt;application/Bootstrap.php&lt;/b&gt; and add the following to your routines:&lt;br /&gt;&lt;br /&gt;&lt;pre class="php" code="php"&gt;    protected function _initDefaultRoutes()&lt;br /&gt;    {   &lt;br /&gt;        $frontController = Zend_Controller_Front::getInstance();&lt;br /&gt;        $frontController-&amp;gt;getRouter()-&amp;gt;addDefaultRoutes();&lt;br /&gt;    }   &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you already have routine for loading routes, just ensure you add the default routes!&lt;br /&gt;&lt;br /&gt;&lt;pre class="php" code="php"&gt;    protected function _initRouteSetup()&lt;br /&gt;    {   &lt;br /&gt;        $frontController = Zend_Controller_Front::getInstance();&lt;br /&gt;        $router = $frontController-&amp;gt;getRouter();&lt;br /&gt;        $config = new Zend_Config_Ini('/path/to/config.ini', APPLICATION_ENV);&lt;br /&gt;        $router-&amp;gt;addConfig($config, 'routes');&lt;br /&gt;&lt;br /&gt;        // ensure you load the default routes as well!!!&lt;br /&gt;        $router-&amp;gt;addDefaultRoutes();&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;With this blog article I hope to save a bunch of people a lot of time looking for an example and get on with building awesome applications.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/xqdXCZiUI1A" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/8032090713264671839/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2012/11/route-default-is-not-defined.html#comment-form" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/8032090713264671839" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/8032090713264671839" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/xqdXCZiUI1A/route-default-is-not-defined.html" title="Route default is not defined" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><thr:total>3</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2012/11/route-default-is-not-defined.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-1630820417458901162</id><published>2012-11-13T00:40:00.001+01:00</published><updated>2012-11-13T00:42:12.613+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="zenduncon" /><category scheme="http://www.blogger.com/atom/ns#" term="qa" /><category scheme="http://www.blogger.com/atom/ns#" term="zendcon" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><category scheme="http://www.blogger.com/atom/ns#" term="zend framework" /><category scheme="http://www.blogger.com/atom/ns#" term="mock objects" /><title type="text">Learning lessons at ZendUncon</title><content type="html">In &lt;a href="http://www.dragonbe.com/2012/11/zendcon-2012.html"&gt;my previous post&lt;/a&gt; I already mentioned&amp;nbsp;&lt;span style="background-color: white; font-family: 'Trebuchet MS', Trebuchet, Verdana, sans-serif; font-size: 14px;"&gt;Sebastian Jerzy Wilczyński (&lt;/span&gt;&lt;a href="http://twitter.com/dj_sebastian_w" style="background-color: white; color: #e0ad12; font-family: 'Trebuchet MS', Trebuchet, Verdana, sans-serif; font-size: 14px;"&gt;@dj_sebastian_w&lt;/a&gt;&lt;span style="background-color: white; font-family: 'Trebuchet MS', Trebuchet, Verdana, sans-serif; font-size: 14px;"&gt;) and his uncon session "&lt;/span&gt;&lt;a href="https://github.com/swilczynski/unittesting" style="background-color: white; color: #e0ad12; font-family: 'Trebuchet MS', Trebuchet, Verdana, sans-serif; font-size: 14px;"&gt;Unit Testing for Databases using fixtures and phpunit&lt;/a&gt;". It made me wonder how much difference it would make if you use fixtures instead of testing database interactions using predefined sets of data.&lt;br /&gt;&lt;br /&gt;Since I work a lot with Zend Framework I was looking at how I could use fixtures and mock objects to follow Sebastian's guidelines. So I gave it a try. This is what I came up with.&lt;br /&gt;&lt;br /&gt;I use the domain model design pattern for accessing data storages with Zend Framework so this means I have a model (Order_Model_Order) which is the representation of my data object, a mapper (Order_Model_OrderMapper) to connect my model with an underlying data storage backend and a table gateway object (Order_Model_DbTable_Order) to link everything to the database. The object we're going to focus on is Order_Model_OrderMapper as this is the link that connects my model to the database.&lt;br /&gt;&lt;br /&gt;At first I start my test class with fixtures.&lt;br /&gt;&lt;br /&gt;&lt;pre code="php"&gt;&amp;lt;?php&lt;br /&gt;&lt;br /&gt;class Order_Model_OrderMapperTest extends PHPUnit_Framework_TestCase&lt;br /&gt;{&lt;br /&gt;    protected $_order = array (&lt;br /&gt;        0 =&amp;gt; array (&lt;br /&gt;            'id'             =&amp;gt; 1,&lt;br /&gt;            'status'         =&amp;gt; 0,&lt;br /&gt;            'price'          =&amp;gt; 2499.95,&lt;br /&gt;            'currency'       =&amp;gt; 'USD',&lt;br /&gt;            'exchange_rate'  =&amp;gt; 1.2714,&lt;br /&gt;            'vat_id'         =&amp;gt; 3,&lt;br /&gt;            'vat_percentage' =&amp;gt; 0.00,&lt;br /&gt;            'created_by'     =&amp;gt; 400,&lt;br /&gt;            'created_at'     =&amp;gt; '2012-02-29 10:11:12',&lt;br /&gt;            'updated_by'     =&amp;gt; 303,&lt;br /&gt;            'updated_at'     =&amp;gt; '2012-11-04 03:01:01',&lt;br /&gt;        ),&lt;br /&gt;        1 =&amp;gt; array (&lt;br /&gt;            'id'             =&amp;gt; 1,&lt;br /&gt;            'status'         =&amp;gt; 0,&lt;br /&gt;            'price'          =&amp;gt; 10000.00,&lt;br /&gt;            'currency'       =&amp;gt; 'EUR',&lt;br /&gt;            'exchange_rate'  =&amp;gt; 1.00,&lt;br /&gt;            'vat_id'         =&amp;gt; 1,&lt;br /&gt;            'vat_percentage' =&amp;gt; 21.00,&lt;br /&gt;            'created_by'     =&amp;gt; 400,&lt;br /&gt;            'created_at'     =&amp;gt; '2012-02-29 10:11:12',&lt;br /&gt;            'updated_by'     =&amp;gt; 303,&lt;br /&gt;            'updated_at'     =&amp;gt; '2012-11-04 03:01:01',&lt;br /&gt;        ),&lt;br /&gt;    );&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The next thing we need to come up with is how to mock out the data gateway as we don't want to connect to the database of course.  Because my mapper class Order_Model_OrderMapper has 2 methods to set and retrieve the data gateway it's easy to flip in and out the data gateway object.  Here's my Order_Model_OrderMapper class: &lt;br /&gt;&lt;br /&gt;&lt;pre code="php"&gt;&amp;lt;php&lt;br /&gt;class Order_Model_OrderMapper&lt;br /&gt;{&lt;br /&gt;    /**&lt;br /&gt;     * @var Zend_Db_Table_Abstract&lt;br /&gt;     */&lt;br /&gt;    protected $_dbTable;&lt;br /&gt;    /**&lt;br /&gt;     * Sets the data gateway for this Mapper class&lt;br /&gt;     * &lt;br /&gt;     * @param Zend_Db_Table $dbTable&lt;br /&gt;     * @throws In2it_Model_Exception&lt;br /&gt;     */&lt;br /&gt;    public function setDbTable($dbTable)&lt;br /&gt;    {&lt;br /&gt;        if (is_string($dbTable)) {&lt;br /&gt;            if (!class_exists($dbTable)) {&lt;br /&gt;                throw new In2it_Model_Exception(&lt;br /&gt;                    'Non-existing data gateway provided'&lt;br /&gt;                );&lt;br /&gt;            }&lt;br /&gt;            $dbTable = new $dbTable;&lt;br /&gt;        }&lt;br /&gt;        if (!$dbTable instanceof Zend_Db_Table_Abstract) {&lt;br /&gt;            throw new In2it_Model_Exception('Invalid data gateway provided');&lt;br /&gt;        }&lt;br /&gt;        $this-&amp;gt;_dbTable = $dbTable;&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * Retrieves the data gateway class from this Mapper class&lt;br /&gt;     * &lt;br /&gt;     * @return Zend_Db_Table&lt;br /&gt;     * @throws In2it_Model_Exception &lt;br /&gt;     */&lt;br /&gt;    public function getDbTable()&lt;br /&gt;    {&lt;br /&gt;        if (!isset ($this-&amp;gt;_dbTable)) {&lt;br /&gt;            throw new In2it_Model_Exception('Data gateway not set');&lt;br /&gt;        }&lt;br /&gt;        return $this-&amp;gt;_dbTable;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    ...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;All we need to do now is create a mock object that we can inject in our mapper test so we don't need to connect to the database in order to test our mapper logic.  &lt;br /&gt;&lt;br /&gt;&lt;pre code="php"&gt;public function testOrderMapperCanFindSingleItem()&lt;br /&gt;{&lt;br /&gt;    $order = new Order_Model_Order();&lt;br /&gt;    $orderMapper = new Order_Model_OrderMapper();&lt;br /&gt;        &lt;br /&gt;    $mockDb = $this-&amp;gt;getMock('Order_Model_DbTable_Order', array ('find'));&lt;br /&gt;    $mockDb-&amp;gt;expects($this-&amp;gt;atLeastOnce())&lt;br /&gt;           -&amp;gt;method('find')&lt;br /&gt;           -&amp;gt;will($this-&amp;gt;returnValue(new ArrayIterator($this-&amp;gt;_order)));&lt;br /&gt;    $orderMapper-&amp;gt;setDbTable($mockDb);&lt;br /&gt;&lt;br /&gt;    $orderMapper-&amp;gt;find($order, 1);&lt;br /&gt;    $this-&amp;gt;assertEquals($this-&amp;gt;_order[0], $order-&amp;gt;toArray());&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So we create our $mockDb object replacing our data gateway object Order_Model_DbTable_Order, and by overriding the find method we can now control how we want this method to operate. By returning an ArrayIterator object, we can use the iterator method current() which is used by the find method in our mapper class.  All that's left is to find our order and assert it's the same as our fixture.&lt;br /&gt;&lt;br /&gt;I hope this might give a few ideas on saving time running your tests. The less you have expensive connections, the quicker your tests will execute.&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/ATOZs2kYixs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/1630820417458901162/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2012/11/learning-lessons-at-zenduncon.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/1630820417458901162" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/1630820417458901162" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/ATOZs2kYixs/learning-lessons-at-zenduncon.html" title="Learning lessons at ZendUncon" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><thr:total>1</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2012/11/learning-lessons-at-zenduncon.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-5168185973736976857</id><published>2012-11-11T22:48:00.002+01:00</published><updated>2012-11-11T22:48:46.992+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="zenduncon" /><category scheme="http://www.blogger.com/atom/ns#" term="zendcon" /><category scheme="http://www.blogger.com/atom/ns#" term="zendcon12" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><title type="text">ZendCon 2012</title><content type="html">October is often the month where all PHP businesses look towards sunny California, because it's ZendCon time! This year was no exception and it was a true blast. And ZendCon means not just 4 days of PHP goodness but also 2 days and a half of Uncon power. And this year I had the honour and pleasure to be in charge of the ZendUncon sessions.&lt;br /&gt;&lt;br /&gt;Because my involvement in those uncon sessions, I missed most of the regular tracks. But on the bright side I was able to discover new speakers and was sitting in a few awesome sessions I would like to recommend here.&lt;br /&gt;&lt;br /&gt;Let me explain the idea behind uncon sessions first, so you at least have a good grasp of the importance of these sessions. An uncon session is different from regular conference sessions because they are not scheduled and can be given by anybody. It's a great way for conference attendees to give a talk or a round table discussion. For the first situation, people can learn how to speak in public and get their first steps in talking at conferences. It's also a solution for open-source project leads to teach and answer questions regarding their projects.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-kwJUfcGACr4/UKAVLfO5hWI/AAAAAAAADpQ/TRQCn-xvoVQ/s1600/IMG_2950+-+Version+2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="297" src="http://3.bp.blogspot.com/-kwJUfcGACr4/UKAVLfO5hWI/AAAAAAAADpQ/TRQCn-xvoVQ/s400/IMG_2950+-+Version+2.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Every day of the conference a clean registration board was set up, and people could select a specific time they wanted to run an uncon session. Of course this meant that availability is a "first-come, first-serve" kind of thing. Early people were able to select the best slots, because they could compare their uncon session with the conference schedule and pick a slot when none of the regular conference sessions was of interest for the speaker.&lt;br /&gt;&lt;br /&gt;Back to the ZendUncon sessions of ZendCon 2012. Three days of uncon goodness, making it an unforgettable event. Let me highlight a couple of remarkable talks I've seen during these three days, which I can highly recommend to have at any given PHP conference.&lt;br /&gt;&lt;br /&gt;Stefan Koopmanschap (&lt;a href="http://twitter.com/skoop"&gt;@skoop&lt;/a&gt;) with "&lt;a href="https://speakerdeck.com/skoop/conference-speaking-101-zendcon-2012-uncon"&gt;Conference Speaking 101&lt;/a&gt;" where he gives a whole bunch of tips and concepts anyone could use as advice for speaking at conferences. Even the regular speakers!&lt;br /&gt;&lt;br /&gt;First time speaker Sebastian Jerzy Wilczyński (&lt;a href="http://twitter.com/dj_sebastian_w"&gt;@dj_sebastian_w&lt;/a&gt;) with "&lt;a href="https://github.com/swilczynski/unittesting"&gt;Unit Testing for Databases using fixtures and phpunit&lt;/a&gt;" where he gives a very interesting point of view for testing database interactions using fixtures without reaching the database.&lt;br /&gt;&lt;br /&gt;Best talk was given by Lorna Mitchel (&lt;a href="http://twitter.com/lornajane"&gt;@lornajane&lt;/a&gt;) with "&lt;a href="https://speakerdeck.com/lornajane/git-githu"&gt;Git + Github: everything you need to know!&lt;/a&gt;" with a standing room and enormous positive feedback.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-ZIHBJs7UDjU/UKAc-YflTnI/AAAAAAAADpg/G5LR4CH6j8E/s1600/IMG_2958.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="298" src="http://4.bp.blogspot.com/-ZIHBJs7UDjU/UKAc-YflTnI/AAAAAAAADpg/G5LR4CH6j8E/s400/IMG_2958.jpg" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Elizabeth Marie Smith (&lt;a href="http://twitter.com/auroraeosrose"&gt;@auroraeosrose&lt;/a&gt;) was giving a true community driven talk "&lt;a href="http://www.slideshare.net/auroraeosrose/mentoring-developers-zendcon-2012"&gt;Mentoring Developers (and phpmentoring.org)&lt;/a&gt;" where she explained why and how experienced developers could take mentorship to assist and train developers seeking mentoring (apprentices). If you're in a situation where you're looking for mentorship, please visit phpmentoring.org for more information.&lt;br /&gt;&lt;br /&gt;If you're interested in the whole ZendUncon series, you can find all the talks and most of the slides on the &lt;a href="https://joind.in/event/view/1106"&gt;joind.in page for ZendUncon&lt;/a&gt;.&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/1uqb4uPkwEo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/5168185973736976857/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2012/11/zendcon-2012.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/5168185973736976857" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/5168185973736976857" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/1uqb4uPkwEo/zendcon-2012.html" title="ZendCon 2012" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-kwJUfcGACr4/UKAVLfO5hWI/AAAAAAAADpQ/TRQCn-xvoVQ/s72-c/IMG_2950+-+Version+2.jpg" height="72" width="72" /><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2012/11/zendcon-2012.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-7714366095311867207</id><published>2012-10-10T23:32:00.001+02:00</published><updated>2012-10-10T23:33:43.440+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="conference" /><category scheme="http://www.blogger.com/atom/ns#" term="phpnw12" /><category scheme="http://www.blogger.com/atom/ns#" term="phpnw" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><title type="text">Returning from PHPNW12</title><content type="html">This last weekend Manchester (UK) was buzzing PHP all over the place, because &lt;a href="http://conference.phpnw.org.uk/phpnw12"&gt;PHPNW12&lt;/a&gt;was held in the city center! I arrived on Thursday, a day before the tutorial day and had an opportunity to talk to a bunch of early attendees and got to meet new faces in the community.&lt;br /&gt;&lt;br /&gt;For the past 4 years I've been attending this community driven conference, seeing it grow into a professional organized event run by professionals that know their stuff on both PHP and organizing a good gig. And I was really honored to be the closing keynote for this year's conference.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Tutorial Day&lt;/h2&gt;&lt;a href="http://conference.phpnw.org.uk/phpnw12"&gt;PHPNW12&lt;/a&gt; is known to have incredible good tutorial sessions, given by involved people into any given project. This time it was no different. I attended the&amp;nbsp;&lt;a href="http://framework.zend.com/"&gt; Zend Framework&lt;/a&gt; 2 tutorial track given by no one less than Rob Allen (&lt;a href="http://twitter.com/akrabat"&gt;@akrabat&lt;/a&gt;) and Evan Cory (&lt;a href="http://twitter.com/EvanDotPro"&gt;@EvanDotPro&lt;/a&gt;). Since they were heavily involved in the development process before &lt;a href="http://framework.zend.com/"&gt; Zend Framework&lt;/a&gt; 2 was stable, they were best positioned to give this tutorial. And they did a great job! I even got a certificate to prove I participated this tutorial.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/dragonbe/8075054687"&gt;&lt;img alt="Tutorial day ZF2" border="0" height="179" src="http://farm8.staticflickr.com/7108/8075054687_a8fb133906_m.jpg" width="240" /&gt;&lt;/a&gt;&lt;a href="http://www.flickr.com/photos/dragonbe/8075049550"&gt;&lt;img alt="ZF2 Tutorial Certificate" border="0" height="179" src="http://farm8.staticflickr.com/7256/8075049550_c0ebb6445e_m.jpg" width="240" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;h2&gt;Hackathon evening&lt;/h2&gt;In the evening a hackathon session was organized and in2it vof was proud sponsor for the pizza's. We should also have sponsored the wifi as lack of internet was a serious issue during this event.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/dragonbe/8075051968"&gt;&lt;img alt="Hacking in progress" border="0" height="179" src="http://farm8.staticflickr.com/7134/8075051968_ce89577231_m.jpg" width="240" /&gt;&lt;/a&gt;&lt;a href="http://www.flickr.com/photos/dragonbe/8075056808"&gt;&lt;img alt="Coding goes better with beer and pizza" border="0" height="179" src="http://farm8.staticflickr.com/7249/8075056808_8cfc568f2c_m.jpg" width="240" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;h2&gt;Conference&lt;/h2&gt;The conference itself was a great event on it's own. Meeting new folks and seeing old friends is one of those magical moments you experience when going to such a community driven conference. But Jeremy Coates (&lt;a href="http://twitter.com/phpcodemonkey"&gt;@phpcodemonkey&lt;/a&gt;) and his crew did a great job putting great names down for this conference. Names like Thijs Feryn, Lorna Mitchell, Igor Wiedler, Ian Barber, Rowan Merewood, Mike van Riel, Ben Waine and Anthony Ferrara (just to name a few). And guess what, the wifi was stable during the whole conference!!!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/dragonbe/8075112794"&gt;&lt;img alt="Opening Keynote" border="0" height="179" src="http://farm8.staticflickr.com/7121/8075112794_c1903fa5f6_m.jpg" width="240" /&gt;&lt;/a&gt;&lt;a href="http://www.flickr.com/photos/dragonbe/8075120203"&gt;&lt;img alt="Jeroen talks no-sql" border="0" height="179" src="http://farm8.staticflickr.com/7260/8075120203_bed091d290_m.jpg" width="240" /&gt;&lt;/a&gt;&lt;br /&gt;I had the honor to close this conference with a community keynote called "&lt;a href="https://speakerdeck.com/u/dragonbe/p/community-works-for-business"&gt;Community  Works for business too!&lt;/a&gt;", a talk to inspire business owners and to provide great arguments for developers within businesses to become more involved in community work. Even though the audience felt it was too much focused to business owners, I still believe it was a magical closure for three great days of brain hurting talks and enormous amount of information. I'm already looking forward to next year.&lt;br /&gt;&lt;br /&gt;&lt;script async="" class="speakerdeck-embed" data-id="50715bc3d2895300020234d5" data-ratio="1.3333333333333333" src="//speakerdeck.com/assets/embed.js"&gt;&lt;/script&gt;&lt;br /&gt;I also want to express my sincere gratitude to Rob Alan (&lt;a href="http://twitter.com/akrabat"&gt;@Akrabat&lt;/a&gt;) for taking an awesome photo of me.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.flickr.com/photos/akrabat/8066296845"&gt;&lt;img alt="Michelangelo by Akrabat" border="0" height="240" src="http://farm9.staticflickr.com/8456/8066296845_942523dffd_m.jpg" width="159" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;An awesome community driven PHP conference, period. Make sure you attend #phpnw13!&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/q6mSgLYsqXY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/7714366095311867207/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2012/10/returning-from-phpnw12.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/7714366095311867207" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/7714366095311867207" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/q6mSgLYsqXY/returning-from-phpnw12.html" title="Returning from PHPNW12" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><thr:total>1</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2012/10/returning-from-phpnw12.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-8690112674578374608</id><published>2011-12-23T19:36:00.002+01:00</published><updated>2011-12-23T19:36:48.041+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="phpbenelux" /><category scheme="http://www.blogger.com/atom/ns#" term="phpbnl12" /><category scheme="http://www.blogger.com/atom/ns#" term="zf" /><category scheme="http://www.blogger.com/atom/ns#" term="windows azure" /><category scheme="http://www.blogger.com/atom/ns#" term="configuration" /><category scheme="http://www.blogger.com/atom/ns#" term="azure" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><category scheme="http://www.blogger.com/atom/ns#" term="zend framework" /><category scheme="http://www.blogger.com/atom/ns#" term="microsoft" /><category scheme="http://www.blogger.com/atom/ns#" term="hackathon" /><title type="text">Microsoft Hackathon at PHPBenelux Conference 2012</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-oa-ee4Z6KOc/TvTKAG41NLI/AAAAAAAACz8/Gos97OnSass/s1600/Microsoft+School+of+PHP.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="252" src="http://4.bp.blogspot.com/-oa-ee4Z6KOc/TvTKAG41NLI/AAAAAAAACz8/Gos97OnSass/s640/Microsoft+School+of+PHP.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;I started these series on &lt;a href="http://www.dragonbe.com/search/label/windows%20azure"&gt;Zend Framework Apps on Windows Azure&lt;/a&gt; and maybe it's nice to know I'll be at the &lt;a href="http://conference.phpbenelux.eu/2012/2011/12/microsoft-hackathon/?utm_source=dragonbe.com&amp;amp;utm_medium=blog&amp;amp;utm_content=microsoft%2Bhackathon&amp;amp;utm_campaign=phpbnl12_msft_hackathon"&gt;PHPBenelux Conference 2012 Microsoft Hackathon&lt;/a&gt; where I'll be joined by Windows Azure MVP &lt;a href="http://twitter.com/maartenballiauw"&gt;Maarten Balliauw&lt;/a&gt;, &lt;a href="http://twitter.com/katriendg"&gt;Katrien De Graeve&lt;/a&gt; and &lt;a href="http://twitter.com/craigkitterman"&gt;Craig Kitterman&lt;/a&gt; of Microsoft and offer you the opportunity to hack on the PHP tools Microsoft and partners have developped to integrate your apps on the various Microsoft platforms.&lt;br /&gt;&lt;br /&gt;Even if you have no knowledge of any of Microsoft's PHP solutions you're more then happy to join us and see how you can build applications so they can be distributed with the &lt;a href="http://www.microsoft.com/web/downloads/platform.aspx"&gt;Web Platform Installer&lt;/a&gt; for running on Windows IIS servers.&lt;br /&gt;&lt;br /&gt;Or maybe you just ended up with a new job in a company that only runs Windows Servers. We'll explain what you need to do so your applications run smoothly on these &lt;a href="http://www.iis.net/"&gt;Microsoft IIS&lt;/a&gt; infrastructures.&lt;br /&gt;&lt;br /&gt;And of course we'll have a look at &lt;a href="http://www.windowsazure.com/en-us/"&gt;Windows Azure&lt;/a&gt;, the cloud solution of Microsoft that allows you to develop PHP apps for the future. With grandmasters &lt;a href="http://twitter.com/maartenballiauw"&gt;Maarten Balliauw&lt;/a&gt; and &lt;a href="http://twitter.com/craigkitterman"&gt;Craig Kitterman&lt;/a&gt; it's going to be an experience comparable to Alice in Wonderland. Are you ready to see how deep the Microsoft PHP tunnel runs?&lt;br /&gt;&lt;br /&gt;&lt;a href="http://shop.phpbenelux.eu/"&gt;Get your tickets now&lt;/a&gt; for the &lt;a href="http://conference.phpbenelux.eu/?utm_source=dragonbe.com&amp;amp;utm_medium=blog&amp;amp;utm_content=microsoft%2Bhackathon&amp;amp;utm_campaign=phpbnl12_msft_hackathon"&gt;PHPBenelux Conference 2012&lt;/a&gt; and join us to hack and learn more of all the Microsoft PHP tools and blow away your teammates and competitors with awesome applications.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/MJ-F-i2YQYA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/8690112674578374608/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2011/12/microsoft-hackathon-at-phpbenelux.html#comment-form" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/8690112674578374608" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/8690112674578374608" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/MJ-F-i2YQYA/microsoft-hackathon-at-phpbenelux.html" title="Microsoft Hackathon at PHPBenelux Conference 2012" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-oa-ee4Z6KOc/TvTKAG41NLI/AAAAAAAACz8/Gos97OnSass/s72-c/Microsoft+School+of+PHP.png" height="72" width="72" /><thr:total>4</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2011/12/microsoft-hackathon-at-phpbenelux.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-1265492212204894604</id><published>2011-12-18T20:56:00.000+01:00</published><updated>2011-12-18T20:56:53.807+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="zf" /><category scheme="http://www.blogger.com/atom/ns#" term="windows azure" /><category scheme="http://www.blogger.com/atom/ns#" term="configuration" /><category scheme="http://www.blogger.com/atom/ns#" term="azure" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><category scheme="http://www.blogger.com/atom/ns#" term="zend framework" /><title type="text">Configuring Zend Framework apps for Windows Azure</title><content type="html">&lt;p&gt;Building web applications is nothing new anymore, as we've been doing it since the early days of the internet, but we've always done this on a single system. Even when &lt;a href="http://framework.zend.com"&gt;Zend Framework&lt;/a&gt; came round, we kept doing the same thing and build apps for a single environment.&lt;br&gt;But as I've discussed already in &lt;a href="http://www.dragonbe.com/2011/12/windows-azure-for-php-developers.html"&gt;my previous article&lt;/a&gt;, developing for the cloud requires another approach.&lt;/p&gt;&lt;p&gt;&lt;a href="https://picasaweb.google.com/lh/photo/U40EQIjUcDC6_kibOqgX1tHMK1EdodWiRI_bfz35pn8?feat=embedwebsite"&gt;&lt;img src="https://lh6.googleusercontent.com/-KplgwR3a25A/TupASDK7F7I/AAAAAAAACzg/qtQdeCbrl1A/s640/one%252520to%252520cloud.png" height="303" width="640"&gt;&lt;/a&gt;&lt;/p&gt;As you can see, your system now falls appart into all different components that are systems by themselves. And each system has its own purpose, completely independent from each other.&lt;br&gt;&lt;p&gt;With &lt;a href="http://framework.zend.com/"&gt;Zend Framework&lt;/a&gt;developing applications running on these separate compontents becomes really easy. It's like having your cloud toolbox right in your pocket.&lt;br&gt;&lt;/p&gt;&lt;h2&gt;Databases&lt;/h2&gt;&lt;p&gt;With &lt;a href="http://framework.zend.com/"&gt;Zend Framework&lt;/a&gt;, connecting to databases is really easy and swapping out a database brand is just a matter of modifying your configuration application/configs/application.ini.&lt;br&gt;&lt;/p&gt;&lt;pre&gt;resources.db.adapter = "pdo_mysql"&lt;br&gt;resources.db.params.host = "10.20.30.40"&lt;br&gt;resources.db.params.username = "user1"&lt;br&gt;resources.db.params.password = "secret"&lt;br&gt;resources.db.params.dbname = "db1"&lt;br&gt;resources.db.isDefaultTableAdapter = true&lt;br&gt;&lt;/pre&gt;&lt;p&gt;Even if you need to connect to multiple databases, you can just pile them up as configuration setting and be done with it.&lt;br&gt;&lt;/p&gt;&lt;pre&gt;resources.multidb.server1.adapter = "pdo_mysql"&lt;br&gt;resources.multidb.server1.host = "10.20.30.40"&lt;br&gt;resources.multidb.server1.username = "user1"&lt;br&gt;resources.multidb.server1.password = "secret1"&lt;br&gt;resources.multidb.server1.dbname = "db1"&lt;br&gt;resources.multidb.server1.default = true&lt;br /&gt;&lt;br&gt;resources.multidb.server2.adapter = "pdo_pgsql"&lt;br&gt;resources.multidb.server2.host = "10.20.30.41"&lt;br&gt;resources.multidb.server2.username = "user2"&lt;br&gt;resources.multidb.server2.password = "secret2"&lt;br&gt;resources.multidb.server2.dbname = "db2"&lt;br&gt;&lt;br&gt;resources.multidb.server3.adapter = "pdo_sqlite"&lt;br&gt;resources.multidb.server3.dbname = APPLICATION_PATH "/files/db/project.db"&lt;br&gt;&lt;/pre&gt;&lt;p&gt;But this is just the basics. When dealing with the cloud you often get a connection string for the host, so it's real easy to hook up to an relational database in the cloud. Here's an example to connecting to SQL Azure.&lt;br&gt;&lt;/p&gt;&lt;pre&gt;resources.db.adapter = "SQLSRV"&lt;br&gt;resources.db.params.host = "abcdefghijk.database.windows.net"&lt;br&gt;resources.db.params.port = 1234&lt;br&gt;resources.db.params.username = "user1"&lt;br&gt;resources.db.params.password = "secret"&lt;br&gt;resources.db.params.dbname = "db1"&lt;br&gt;resources.db.isDefaultTableAdapter = true&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Bottom line, no worries connecting to a cloud database. &lt;a href="http://framework.zend.com/"&gt;Zend Framework&lt;/a&gt; has your back!&lt;br&gt;&lt;/p&gt;&lt;h2&gt;Sessions&lt;/h2&gt;&lt;p&gt;As you want to assist your visitors as much as possible, you probably want to use sessions in your application. If you don't configure anything, PHP stores session on your filesystem by default and so does &lt;a href="http://framework.zend.com/"&gt;Zend Framework&lt;/a&gt;.&lt;br&gt;&lt;/p&gt;&lt;p&gt;For the cloud, you don't want to write to your local filesystem, so you set up session storage. I chose to use the SQL Azure server I already had setup using the following settings.&lt;br&gt;&lt;/p&gt;&lt;pre&gt;resources.session.use_only_cookies = true&lt;br&gt;resources.session.gc_maxlifetime = 864000&lt;br&gt;resources.session.remember_me_seconds = 864000&lt;br&gt;resources.session.saveHandler.class = "Zend_Session_SaveHandler_DbTable"&lt;br&gt;resources.session.saveHandler.options.name = "session"&lt;br&gt;resources.session.saveHandler.options.primary = "id"&lt;br&gt;resources.session.saveHandler.options.modifiedColumn = "modified"&lt;br&gt;resources.session.saveHandler.options.dataColumn = "data"&lt;br&gt;resources.session.saveHandler.options.lifetimeColumn = "lifetime"&lt;br&gt;&lt;/pre&gt;&lt;p&gt;No further changes need to be done as all fields are defined and this Zend_Session_SaveHandler_DbTable takes care of all the rest.&lt;br&gt;&lt;/p&gt;&lt;h2&gt;Caching&lt;/h2&gt;&lt;p&gt;Just like databases, providing caching for your application requires just a few simple configuration settings, this example sets up a memcache service.&lt;br&gt;&lt;/p&gt;&lt;pre&gt;resources.cachemanager.memcached.frontend.name = Core&lt;br /&gt;resources.cachemanager.memcached.frontend.options.automatic_serialization = On&lt;br /&gt;resources.cachemanager.memcached.backend.name = Libmemcached&lt;br /&gt;resources.cachemanager.memcached.backend.options.servers.one.host = localhost&lt;br /&gt;resources.cachemanager.memcached.backend.options.servers.one.port = 11211&lt;br /&gt;resources.cachemanager.memcached.backend.options.servers.one.persistent = On&lt;/pre&gt;&lt;p&gt;Caching on the cloud requires a little different approach as most cloud services offer their own flavor of caching, making it difficult to find a PHP driver that is capable to access this cloud caching layer. But don't let this stop you in moving to the cloud. Windows Azure provides a superb caching platform, and I'll show you later in these series how to modify your application as it requires a little tweek on the configuration of your Windows Azure installation.&lt;br&gt;&lt;/p&gt;&lt;h2&gt;Storage&lt;/h2&gt;&lt;p&gt;Uploading and distributing files can be considered as an importan part of any application, and with &lt;a href="http://framework.zend.com/"&gt;Zend Framework&lt;/a&gt; you can manage file uploads relatively simple using Zend_Form and Zend_File.&lt;br&gt;&lt;/p&gt;&lt;p&gt;An example would be to upload a small image.&lt;br&gt;&lt;/p&gt;&lt;pre&gt;public function uploadAction()&lt;br&gt;{&lt;br&gt; $adapter = new Zend_File_Transfer_Adapter_Http();&lt;br&gt; &lt;br&gt; $adapter-&amp;gt;setDestination(APPLICATION_PATH '/files/upload');&lt;br&gt;&lt;br&gt; if (!$adapter-&amp;gt;receive()) {&lt;br&gt; $messages = $adapter-&amp;gt;getMessages();&lt;br&gt; echo implode("\n", $messages);&lt;br&gt; }&lt;br&gt; $this-&amp;gt;view-&amp;gt;filename = $adapter-&amp;gt;getFileName('avatar', false);&lt;br&gt;}&lt;br&gt;&lt;/pre&gt;&lt;p&gt;Saving into a specific location is done with $adapter-&amp;gt;setDestination(), but this still requires the usage of a local location! And we know that in the cloud saving locally has no use! Luckily for you, &lt;a href="http://framework.zend.com/"&gt;Zend Framework&lt;/a&gt; has a bunch of components that will allow you to store files onto a Windows Azure Storage instance.&lt;br&gt;&lt;/p&gt;&lt;pre&gt; public function uploadAction()&lt;br&gt; {&lt;br&gt; $config = new Zend_Config_Ini(APPLICATION_PATH . '/configs/api.ini', APPLICATION_ENV);&lt;br&gt; &lt;br&gt; $azure = new My_Cloud_WindowsAzure_AzureStorage(&lt;br&gt; $config-&amp;gt;azure-&amp;gt;storage-&amp;gt;account, &lt;br&gt; $config-&amp;gt;azure-&amp;gt;storage-&amp;gt;primkey);&lt;br&gt; &lt;br&gt; $container = $config-&amp;gt;azure-&amp;gt;storage-&amp;gt;container;&lt;br&gt; &lt;br&gt; $adapter = new Zend_File_Transfer_Adapter_Http();&lt;br&gt; $adapter-&amp;gt;setDestination(APPLICATION_PATH '/files/upload');&lt;br&gt;&lt;br&gt; if (!$adapter-&amp;gt;receive()) {&lt;br&gt; $messages = $adapter-&amp;gt;getMessages();&lt;br&gt; echo implode("\n", $messages);&lt;br&gt; }&lt;br&gt; &lt;br&gt; $storageClient = new Zend_Service_WindowsAzure_Storage_Blob(&lt;br&gt; My_Cloud_WindowsAzure_AzureStorage::AZURE_STORAGE_HOST, &lt;br&gt; $azure-&amp;gt;getAccountName(), $azure-&amp;gt;getPrimaryKey());&lt;br&gt; &lt;br&gt; $result = null;&lt;br&gt; &lt;br&gt; if (!$storageClient-&amp;gt;containerExists($container)) {&lt;br&gt; $result = $storageClient-&amp;gt;createContainer($container);&lt;br&gt; $storageClient-&amp;gt;setContainerAcl($container, &lt;br&gt; Zend_Service_WindowsAzure_Storage_Blob::ACL_PUBLIC_CONTAINER);&lt;br&gt; }&lt;br&gt;&lt;br&gt; $fileName = $adapter-&amp;gt;getFileName('resume', true);&lt;br&gt; $result = $storageClient-&amp;gt;putBlob(&lt;br&gt; $container, basename($fileName), $fileName&lt;br&gt; );&lt;br&gt; &lt;br&gt; $this-&amp;gt;view-&amp;gt;filename = basename($fileName);&lt;br&gt; $this-&amp;gt;view-&amp;gt;location = sprintf('http://%s.%s/%s/%s',&lt;br&gt; $azure-&amp;gt;getAccountName(),&lt;br&gt; My_Cloud_WindowsAzure_AzureStorage::AZURE_STORAGE_HOST,&lt;br&gt; $config-&amp;gt;azure-&amp;gt;storage-&amp;gt;container,&lt;br&gt; $result-&amp;gt;Name);&lt;br&gt; }&lt;br&gt;&lt;/pre&gt;&lt;p&gt;As you can see, you only need to add a little more functionality to the first example. But once you have everything in place, nothing can stop you achieving your goals.&lt;br&gt;&lt;/p&gt;&lt;pre&gt;; These are local configuration settings&lt;br&gt;; Primarily used to access API's using credential tokens&lt;br&gt;[production]&lt;br&gt;...&lt;br&gt;azure.storage.account = "myblobstorageaccount1"&lt;br&gt;azure.storage.primkey = "fjldjljdlfjadladjsljdfljfdljd/akjddfjldfjjd22kajdajfei3234ajldjfjklajlajd=="&lt;br&gt;azure.storage.container = "myblobstoragecontainer"&lt;br&gt;...&lt;br&gt;&lt;br&gt;[staging: production]&lt;br&gt;[testing: production]&lt;br&gt;[development: production]&lt;br&gt;&lt;/pre&gt;&lt;p&gt;In this example I used an api.ini which is very convenient to store your most import settings like account names, api keys and passwords to various api services. It's just a file containing key-value pairs for easy configuration. In this case my INI settings look like the following listing.&lt;br&gt;&lt;/p&gt;&lt;h2&gt;Next&lt;/h2&gt;&lt;p&gt;Now that we've talked about the specific settings in your &lt;a href="http://framework.zend.com/"&gt;Zend Framework&lt;/a&gt; application it's about time we set up our environment, download all the tools, register for a Windows Azure account and get started with running our &lt;a href="http://framework.zend.com/"&gt;Zend Framework&lt;/a&gt; app in the cloud. So stay tuned for more.&lt;br&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/qxfNNoY-zCk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/1265492212204894604/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2011/12/configuring-zend-framework-apps-for.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/1265492212204894604" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/1265492212204894604" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/qxfNNoY-zCk/configuring-zend-framework-apps-for.html" title="Configuring Zend Framework apps for Windows Azure" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><thr:total>2</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2011/12/configuring-zend-framework-apps-for.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-6208232362701973063</id><published>2011-12-11T23:03:00.001+01:00</published><updated>2011-12-11T23:06:06.810+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="azure" /><category scheme="http://www.blogger.com/atom/ns#" term="windows" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><category scheme="http://www.blogger.com/atom/ns#" term="microsoft" /><title type="text">Windows Azure for PHP developers</title><content type="html">&lt;h2&gt;Introduction&lt;/h2&gt;&lt;p&gt;Since a couple of years the term "cloud" has moved from simple buzz-word to real business opportunities that make a difference within the market. The technology behind "cloud" is not new as we've grown accustomed to server clusters, distributed datacenters and separation of responsibilities, but the "cloud" offers everyone the opportunity to scale and not just the companies who have a huge cash reserve to purchase another rack with servers.&lt;/p&gt;&lt;p&gt;The strange thing is that cloud is being provided by companies you wouldn't expect to serve it. Best known are &lt;a href="http://www.amazon.com"&gt;Amazon.com&lt;/a&gt; and &lt;a href="http://www.rackspace.com"&gt; Rackspace.com&lt;/a&gt; that offers a complete set of services you can use to run applications in the cloud, they provide the infrastructure and you have to set up and maintain your platform and infrastructure. &lt;a href="http://www.microsoft.com/"&gt;Microsoft&lt;/a&gt; also jumped on this cloud hype offering a platform in the cloud. And this is exactly the reason it caught my attention and got me interested. I'm a developer and I don't want to fiddle with setting up and maintaining an operating system, basically since I don't have the time for it.&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.amazon.com/gp/product/0981034527/ref=as_li_qf_sp_asin_il?ie=UTF8&amp;amp;tag=in2it-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=0981034527"&gt;&lt;img src="http://ws.assoc-amazon.com/widgets/q?_encoding=UTF8&amp;amp;Format=_SL110_&amp;amp;ASIN=0981034527&amp;amp;MarketPlace=US&amp;amp;ID=AsinImage&amp;amp;WS=1&amp;amp;tag=in2it-20&amp;amp;ServiceVersion=20070822" align="left" border="0"&gt;&lt;/a&gt;&lt;img src="http://www.assoc-amazon.com/e/ir?t=in2it-20&amp;amp;l=as2&amp;amp;o=1&amp;amp;a=0981034527" alt="" style="border:none !important; margin:0px !important;" border="0" height="1" width="1"&gt;If you are eager to learn more about cloud and what different kind of cloud solutions are out there, I can recommend the book "&lt;a href="http://www.amazon.com/gp/product/0981034527/ref=as_li_qf_sp_asin_tl?ie=UTF8&amp;amp;tag=in2it-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=0981034527"&gt;PHP       Development In The Cloud&lt;/a&gt;" by &lt;a href="http://twitter.com/ijansch"&gt;Ivo Jansch&lt;/a&gt; and &lt;a href="http://twitter.com/vitoc"&gt;Vito Chin&lt;/a&gt;. In this book both authors look beyond the buzz of the cloud and discuss what each cloud service provider has to offer for PHP developers and how you can easily consume their services using &lt;a href="http://www.php.net"&gt;PHP&lt;/a&gt; code. A must read for everyone that has an interest in developing web applications using &lt;a href="http://www.php.net/"&gt;PHP&lt;/a&gt; and cloud technology, as I already posted &lt;a href="http://www.dragonbe.com/2011/05/cloud-computing-is-no-longer-buzz-word.html?utm_source=blog%2Barticle&amp;amp;utm_medium=link&amp;amp;utm_term=cloud%2Bazure&amp;amp;utm_content=article%2Breferral&amp;amp;utm_campaign=windows%2Bazure"&gt;a book review for this book&lt;/a&gt; here on &lt;a href="http://www.dragonbe.com/?utm_source=blog%2Barticle&amp;amp;utm_medium=link&amp;amp;utm_term=cloud%2Bazure&amp;amp;utm_content=article%2Breferral&amp;amp;utm_campaign=windows%2Bazure"&gt;my       blog&lt;/a&gt;.&lt;br&gt;&lt;/p&gt;&lt;h2&gt;Windows Azure&lt;/h2&gt;&lt;p&gt;I got in touch with &lt;a href="http://www.microsoft.com/"&gt;Microsoft&lt;/a&gt;'s      &lt;a href="http://www.windowsazure.com/en-us/"&gt;Windows Azure&lt;/a&gt;back in 2008 through our local &lt;a href="http://www.microsoft.com/"&gt;Microsoft&lt;/a&gt; Evengalist &lt;a href="http://twitter.com/katriendg"&gt;Katrien De Graeve&lt;/a&gt;, who told me &lt;a href="http://www.windowsazure.com/en-us/"&gt;Windows Azure&lt;/a&gt; is like running a Windows 2008 Server with IIS in the cloud. Along with &lt;a href="http://twitter.com/maartenballiauw"&gt;Maarten      Balliauw&lt;/a&gt; &lt;a href="http://www.microsoft.com/"&gt;Microsoft&lt;/a&gt;was working out more tools and features to support &lt;a href="http://www.php.net/"&gt;PHP&lt;/a&gt; (amongst other open source technologies) on their new cloud platform.&lt;br&gt;&lt;/p&gt;&lt;p&gt;I was completely sold when &lt;a href="http://twitter.com/joshholmes"&gt;Josh Holmes&lt;/a&gt; came to Brussels in 2009 and told us more about what &lt;a href="http://www.windowsazure.com/en-us/"&gt;Windows Azure&lt;/a&gt; has to offer and how perfectly it is to build applications consuming these cloud services, without having to deal with setting up and maintaining the platform the run on. And so, I started playing with it and discovering about what &lt;a href="http://www.windowsazure.com/en-us/"&gt;Windows Azure&lt;/a&gt; is all about, what kind of services they offer and how I as a &lt;a href="http://www.php.net/"&gt;PHP&lt;/a&gt; developer could make good use out of it. In 2010 &lt;a href="http://phpbenelux.eu"&gt;PHPBenelux&lt;/a&gt;and &lt;a href="http://www.microsoft.com"&gt;Microsoft&lt;/a&gt; joind efforts to promote &lt;a href="http://www.windowsazure.com/en-us/"&gt;Windows  Azure&lt;/a&gt; by organizing a &lt;a href="http://www.phpazurecontest.com/"&gt;PHPonAzure contest&lt;/a&gt; to develop an application that would run and make use of &lt;a href="http://www.windowsazure.com/en-us/"&gt;Windows Azure&lt;/a&gt;'s services.&lt;br&gt;&lt;/p&gt;&lt;h2&gt;Developing for cloud&lt;/h2&gt;&lt;p&gt;When developing &lt;a href="http://www.php.net/"&gt;PHP&lt;/a&gt;applications, you mostly develop for a single server setup or maybe one server running your &lt;a href="http://www.php.net/"&gt;PHP&lt;/a&gt;code and one server for the database. This is the easiest way to set up an environment as you don't need to worry about anything except to connect to the correct database.&lt;br&gt;When developing for the cloud, more things should be considered before you get started. Some things that you take for granted require some additional thoughts when designing the architecture of your application. Fortunately for you, there are only a few things to take into account before you can deploy your latest killer app in the cloud provided by &lt;a href="http://www.windowsazure.com/en-us/"&gt;Windows Azure&lt;/a&gt;.&lt;br&gt;&lt;/p&gt;&lt;h3&gt;Operating System&lt;/h3&gt;&lt;p&gt;On a bare metal server or even a virtual server, you know you need to set up your own operating system (or have your hosting service provider or sysadmin do this for you). This means you need to take responsibilty in keeping your OS up-to-date with security fixes and all kinds of software upgrades.&lt;br&gt;&lt;a href="http://www.windowsazure.com/en-us/"&gt;Windows Azure&lt;/a&gt; is a platform on it's own, comparable to a fully set up Windows 2008 Server complete with IIS 7. Microsoft takes care of updating and securing their instances you use without compromizing your application.&lt;br&gt;&lt;/p&gt;&lt;h3&gt;Data Storage&lt;br&gt;&lt;/h3&gt;&lt;p&gt;When creating a normal website session data, logs, caches and file uploads are often stored on the local filesystem. With a cloud solution, you need to consider that multiple instances of your web application might be up and running and don't share each others filesytem. To facilitate filesystem storage, cloud services offer so called "file buckets" or storage endpoints. &lt;a href="http://www.windowsazure.com/en-us/"&gt;Windows Azure&lt;/a&gt;offers 5 types of storage:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Blob storage: simple binary storage for video, images, audio and other types of files&lt;br&gt;&lt;/li&gt;&lt;li&gt;Table storage: a structured storage for large amounts of data, behaving like a NoSQL storage&lt;br&gt;&lt;/li&gt;&lt;li&gt;Queue: a messaging queue to transfer messages between applications and services&lt;br&gt;&lt;/li&gt;&lt;li&gt;Windows Azure Drive: a mounted shared disk partition formatted as NTFS VHD&lt;/li&gt;&lt;li&gt;SQL Azure: a true relational database based on Microsoft SQL Server technology&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;One storage location I need to mention a little separate is Windows Azure Cache, a memory storage. It's a caching layer comparable to &lt;a href="http://memcached.org/"&gt;Memcached&lt;/a&gt; or &lt;a href="http://www.iis.net/download/wincacheforphp"&gt;WinCache&lt;/a&gt;, residing completely in memory.&lt;br&gt;&lt;/p&gt;&lt;p&gt;File uploads are easy as you only need to worry where they end up and how you make sure files cannot overwrite existing files, unless specified to do so. Using one of the 5 storage solutions provided by &lt;a href="http://www.windowsazure.com/en-us/"&gt;Windows Azure&lt;/a&gt; is not difficult using &lt;a href="http://www.php.net/"&gt;PHP&lt;/a&gt;, it only requires correct settings for transferring the uploaded file to the chosen storage. &lt;a href="http://www.php.net/"&gt;PHP&lt;/a&gt;'s &lt;a href="http://www.php.net/manual/en/function.move-uploaded-file.php"&gt;move_uploaded_file()&lt;/a&gt;method can be used in most cases. In other cases, use the &lt;a href="http://phpazure.codeplex.com/"&gt;Windows Azure SDK for PHP&lt;/a&gt;as it offers a rich abstraction layer for the different storage options of &lt;a href="http://www.windowsazure.com/en-us/"&gt;Windows Azure&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Sessions in &lt;a href="http://www.php.net/"&gt;PHP&lt;/a&gt; can easily be stored in a database, a storage endpoint or on a memory layer by modifying the &lt;a href="http://www.php.net/manual/en/function.session-set-save-handler.php"&gt;session_set_save_handler()&lt;/a&gt;. &lt;a href="http://www.windowsazure.com/en-us/"&gt;Windows Azure&lt;/a&gt; can store session data on the blob storage, table storage, the Windows Azure Drive, in SQL Azure or on it's Cache layer. I prefer to use either the SQL Azure or the Cache, as they both perform really fast, even for session data.&lt;br&gt;&lt;/p&gt;&lt;p&gt;When using logs within your application, you also need to be aware that your log files should reside somwehere central. Best is to either log against Table storage or SQL Azure as it's a fast way to keep track of changes and easily accessible. But, storing logs on the blob storage is perfectly good as well, only know it might grow your storage space too much.&lt;br&gt;&lt;/p&gt;&lt;p&gt;Caching is something best handled by Windows Azure Cache, the memory storage I mentioned earlier. It's fast, very easy to maintain and super easy to scale in or out.&lt;br&gt;&lt;/p&gt;&lt;p&gt;Data itself can be stored in SQL Azure or on Table Storage. SQL Azure is a full featured relational database, comparable to Microsoft SQL Server. It offers all the features you can expect from a relational database, including stored procedures, transactions and separation of responsibilities. Table Storage is a simple key/value storage table, that behaves more like a NoSQL storage and thus capable of storing documents and constructs in an organized way.&lt;br&gt;&lt;/p&gt;&lt;h3&gt;Other considerations&lt;br&gt;&lt;/h3&gt;&lt;p&gt;Cloud applications should be designed to scale horizontally. This means when your application requires more resources to cope with the usage, more instances of the application will run together behaving as a single application.&lt;br&gt;&lt;/p&gt;&lt;p&gt;Cloud is also "pay as you go". If you use a little, you pay a little. If you use a lot, you pay also a lot but less then when you need to scale with traditional hardware. The following graphs by &lt;a href="http://twitter.com/joshholmes"&gt;Josh Holmes&lt;/a&gt;explains it best. &lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="https://picasaweb.google.com/lh/photo/TeTiUWtrO_4eRoqltjplX9HMK1EdodWiRI_bfz35pn8?feat=embedwebsite"&gt;&lt;img src="https://lh6.googleusercontent.com/-Tfojy7ybCK0/TuUjssiG28I/AAAAAAAACyk/4-FJI5v21g0/s400/in_house_costs_by_josh_holmes.png" height="300" width="400"&gt;&lt;/a&gt; &lt;a href="https://picasaweb.google.com/lh/photo/xgjXfxriv_uxeoF8OZM_PdHMK1EdodWiRI_bfz35pn8?feat=embedwebsite"&gt;&lt;img src="https://lh4.googleusercontent.com/-AVyWahjFY2c/TuUiE-17HrI/AAAAAAAACyY/SKZD4alGWsQ/s400/cloud_computing_costs_by_josh_holmes.png" height="298" width="400"&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Now it doesn't matter if you're a big company or just a start-up. You can launch your product slowly and grow as your usage and budget grows. A perfect match.&lt;br&gt;&lt;/p&gt;&lt;h2&gt;Next&lt;/h2&gt;&lt;p&gt;Now that I've introduced you to what &lt;a href="http://www.windowsazure.com/en-us/"&gt;Windows Azure&lt;/a&gt; has to offer &lt;a href="http://www.php.net/"&gt;PHP&lt;/a&gt; developers, or any other technology developer for that matter, I show you how I have a simple Zend Framwork application build on a traditional LAMP stack deployed to &lt;a href="http://www.windowsazure.com/en-us/"&gt;Windows Azure&lt;/a&gt;. So stay tuned for more.&lt;br&gt;&lt;/p&gt;&lt;h2&gt;Related links&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.windowsazure.com"&gt;Windows Azure Website&lt;br&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://phpazure.codeplex.com"&gt;Windows Azure SDK for PHP&lt;/a&gt;&lt;br&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.slideshare.net/joshholmes/microsoftzend-webcast-on-cloud-computing"&gt;Josh Holmes Microsoft/Zend Webcast on Cloud Computing&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.phpazurecontest.com/"&gt;PHPonAzureContest&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/0981034527/ref=as_li_qf_sp_asin_tl?ie=UTF8&amp;amp;tag=in2it-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=0981034527"&gt;PHP       Development In The Cloud&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/h3EEyPSwdOA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/6208232362701973063/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2011/12/windows-azure-for-php-developers.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/6208232362701973063" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/6208232362701973063" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/h3EEyPSwdOA/windows-azure-for-php-developers.html" title="Windows Azure for PHP developers" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2011/12/windows-azure-for-php-developers.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-4062473648337560478</id><published>2011-11-16T18:54:00.001+01:00</published><updated>2011-11-16T19:13:30.228+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="phpbenelux" /><category scheme="http://www.blogger.com/atom/ns#" term="conference" /><category scheme="http://www.blogger.com/atom/ns#" term="2012" /><category scheme="http://www.blogger.com/atom/ns#" term="early bird" /><title type="text">PHPBenelux Conference 2012 Announcement</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://conference.phpbenelux.eu/2012/2011/11/workshops-early-bird-ticket-sales-announced/" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="62" src="http://conference.phpbenelux.eu/2012/wp-content/themes/phpbnl/images/phpbnl2012.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;Hey, just letting you guys know there's an awesome conference happening in January: The &lt;a href="http://conference.phpbenelux.eu/2012/2011/11/workshops-early-bird-ticket-sales-announced/"&gt;PHPBenelux Conference 2012&lt;/a&gt;! They just opened up their ticket sales at really low prices, so even if you come from abroad it's still cheap.&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://phpbenelux.eu/"&gt;PHPBenelux&lt;/a&gt; team only announced the names of tutorial speakers, but with names like &lt;a href="http://www.jansch.nl/"&gt;Ivo Jansch&lt;/a&gt;, &lt;a href="http://www.rinne.info/"&gt;Torsten Rhinne&lt;/a&gt;, &lt;a href="http://fabien.potencier.org/"&gt;Fabien Potencier&lt;/a&gt; and &lt;a href="http://mwop.net/"&gt;Matthew Weier O'Phinney &lt;/a&gt;you know it's going to rock! And that's tutorials only.&lt;br /&gt;&lt;br /&gt;It's all happening on January 27 and 28, so make sure you block those days in your calendar, because the center of Europe is going to buzz PHP like it never had before!!! &lt;a href="http://conference.phpbenelux.eu/2012/2011/11/workshops-early-bird-ticket-sales-announced/"&gt;Get your tickets now&lt;/a&gt; while their still hot (and cheaper than cheap)!!!&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/TS6Qo_xfj04" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/4062473648337560478/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2011/11/phpbenelux-conference-2012-announcement.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/4062473648337560478" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/4062473648337560478" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/TS6Qo_xfj04/phpbenelux-conference-2012-announcement.html" title="PHPBenelux Conference 2012 Announcement" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2011/11/phpbenelux-conference-2012-announcement.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-6449759132514720607</id><published>2011-09-18T22:32:00.000+02:00</published><updated>2011-09-18T22:32:52.405+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="conference" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><category scheme="http://www.blogger.com/atom/ns#" term="pfz" /><category scheme="http://www.blogger.com/atom/ns#" term="pfc11" /><title type="text">Pfcongres 2011 wrap up</title><content type="html">This Saturday I started early to go to &lt;a href="http://pfcongres.nl/"&gt;Pfcongres&lt;/a&gt;, a small PHP community conference in Utrecht organized by the Dutch PHP user group &lt;a href="http://www.pfz.nl/"&gt;PHPFreakz&lt;/a&gt; and after about two hours driving I arrived about half an hour in time before the conference started, with me kick starting with my keynote "&lt;a href="http://www.slideshare.net/DragonBe/community-works-pfcongres-2011"&gt;Community Works&lt;/a&gt;", a 199 slide deck explaining what the PHP community is and how people can be part of that community.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.slideshare.net/DragonBe/community-works-pfcongres-2011"&gt;My keynote&lt;/a&gt; was well received by the audience, although some comments were made it needed a more elegant flow of community tips. It even started a discussion whether I should use visuals or not to emphasize my words. I will take these comments into consideration when I'm reviewing these slides.&lt;br /&gt;&lt;br /&gt;Although I spend a major part of the conference in the so-called "Hallway tracks" I was able to jump into a couple of great talks that have intrigued me to look into their details. A couple of tracks that really stood out were "&lt;a href="http://www.slideshare.net/norm2782/pf-congres20110917-datastructures"&gt;SPL Data Structures and their Complexity&lt;/a&gt;" by&amp;nbsp;&lt;a href="http://twitter.com/#!/norm2782"&gt;Jurrien Stutterheim&lt;/a&gt;, "&lt;a href="http://www.slideshare.net/jaytaph/15-protips-for-mysql-users-pfz"&gt;15 Protips for MySQL users&lt;/a&gt;" by &lt;a href="http://twitter.com/#!/JayTaph"&gt;Joshua Thijssen&lt;/a&gt; and of course the closing keynote "PHP — Status Check" by no one less than co-founder of Zend Mr. &lt;a href="http://twitter.com/#!/zeevs"&gt;Zeev Suraski&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The good&lt;/b&gt;&lt;br /&gt;The team of&amp;nbsp;&lt;a href="http://www.pfz.nl/"&gt;PHPFreakz&lt;/a&gt;&amp;nbsp;managed to do the impossible: creating a good balance for Dutch and non-Dutch speakers, offering a good lunch (especially for Dutch standards) with a wide variety of sandwiches and a constant flow of good coffee.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The bad&lt;/b&gt;&lt;br /&gt;I have no complaints regarding the organization, the audience or the venue. I just felt bad I had to wake up at 6am just to be there on time.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The ugly&lt;/b&gt;&lt;br /&gt;The venue's wifi was a real burden, and I know from experiences that most conference wifi settings are flakey as a rule, but this wifi experience was bad, even considering the flakey ones.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;br /&gt;This conference showed the world again that a small community group can amaze everyone by bringing quality speakers into their community offering them high level content at a really reasonable prize. I'd like to express my gratitude to the organization to be part of this event.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Bonus challenge&lt;/b&gt;&lt;br /&gt;I keep thinking about what&amp;nbsp;&lt;a href="http://twitter.com/#!/JayTaph"&gt;Joshua Thijssen&lt;/a&gt;&amp;nbsp;said during his talk about getting certified as a MySQL engineer. As it was on my to-do list for a couple of years, I think I needed the push Joshua had given me to follow up on my commitments and schedule an exam.&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/CIHR8xP46yE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/6449759132514720607/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2011/09/pfcongres-2011-wrap-up.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/6449759132514720607" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/6449759132514720607" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/CIHR8xP46yE/pfcongres-2011-wrap-up.html" title="Pfcongres 2011 wrap up" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2011/09/pfcongres-2011-wrap-up.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-4714988347243590658</id><published>2011-09-15T10:46:00.001+02:00</published><updated>2011-09-15T10:46:35.560+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="qa" /><category scheme="http://www.blogger.com/atom/ns#" term="phpdoc" /><category scheme="http://www.blogger.com/atom/ns#" term="in2it" /><category scheme="http://www.blogger.com/atom/ns#" term="qaseries" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><category scheme="http://www.blogger.com/atom/ns#" term="phpunit" /><title type="text">Quality Assurance on PHP projects - PHPUnit lessons learned</title><content type="html">This is what I like about the PHP community: people work out similar ideas and share what they've learned in the process. Sometimes people pick up ideas and work them out, sometimes people just continue the conversation.&lt;br /&gt;&lt;br /&gt;And I believe &lt;a href="http://lars-tesmer.com/blog"&gt;Lars Tesmer&lt;/a&gt; has done the latter, where he &lt;a href="http://lars-tesmer.com/blog/2011/09/08/what-my-co---workers-and-i-learned-when-trying-to-write-unit-tests-for-phpunit/"&gt;blogs on testing PHPUnit itself and the lessons learned in that process&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Thank you Lars for sharing your lessons.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/z1qkzI1_8MI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/4714988347243590658/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2011/09/quality-assurance-on-php-projects_15.html#comment-form" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/4714988347243590658" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/4714988347243590658" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/z1qkzI1_8MI/quality-assurance-on-php-projects_15.html" title="Quality Assurance on PHP projects - PHPUnit lessons learned" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><thr:total>4</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2011/09/quality-assurance-on-php-projects_15.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-849480286855330673</id><published>2011-09-03T23:27:00.000+02:00</published><updated>2011-09-03T23:27:29.560+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="qa" /><category scheme="http://www.blogger.com/atom/ns#" term="phpdoc" /><category scheme="http://www.blogger.com/atom/ns#" term="in2it" /><category scheme="http://www.blogger.com/atom/ns#" term="qaseries" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><category scheme="http://www.blogger.com/atom/ns#" term="phpunit" /><title type="text">Quality Assurance on PHP projects - PHPUnit part 4</title><content type="html">&amp;nbsp; &amp;nbsp; &amp;nbsp;In parts &lt;a href="http://www.dragonbe.com/2011/08/quality-assurance-on-php-projects_17.html"&gt;one&lt;/a&gt;, &lt;a href="http://www.dragonbe.com/2011/08/quality-assurance-on-php-projects_23.html"&gt;two&lt;/a&gt; and &lt;a href="http://www.dragonbe.com/2011/08/quality-assurance-on-php-projects_28.html"&gt;three&lt;/a&gt; we focussed on writing tests for a game     of tic-tac-toe, with in parts two and three we optimized our tests     so they focus on the functionality of the individual parts Grid and     Player, with a collection class Players to handle Player objects.&lt;br /&gt;    &lt;br /&gt;    In this fourth part we should focus on playing the game. Let's go     over the steps again:&lt;br /&gt;    &lt;br /&gt;&lt;ul&gt;&lt;li&gt;each player chooses a symbol&lt;/li&gt;&lt;li&gt;for each turn (max 9 turns)&lt;br /&gt;      &lt;/li&gt;&lt;ul&gt;&lt;li&gt;a player places his symbol on the grid&lt;/li&gt;&lt;li&gt;if 3 symbols appear in a single row (horizontal, vertical or           diagonal)&lt;/li&gt;&lt;ul&gt;&lt;li&gt;player is a winner&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/ul&gt;So this is a simple and a straightforward rule we can turn into     code. But let's look at the tests we've written in part 1 to see if     they're still valid.&lt;br /&gt;    &lt;pre&gt;    public function testGameCanBePlayed()&lt;br /&gt;    {&lt;br /&gt;        $playerX = $this-&amp;gt;_ttt-&amp;gt;getPlayers()-&amp;gt;seek(0)-&amp;gt;current();&lt;br /&gt;        $playerO = $this-&amp;gt;_ttt-&amp;gt;getPlayers()-&amp;gt;seek(1)-&amp;gt;current();&lt;br /&gt;        &lt;br /&gt;        $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(0, 0, $playerX));&lt;br /&gt;        $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(0, 1, $playerX));&lt;br /&gt;        $this-&amp;gt;assertTrue($this-&amp;gt;_ttt-&amp;gt;play(0, 2, $playerX));&lt;br /&gt;        &lt;br /&gt;        $this-&amp;gt;_ttt-&amp;gt;setGrid(new Grid());&lt;br /&gt;        &lt;br /&gt;        $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(0, 0, $playerX));&lt;br /&gt;        $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(1, 0, $playerX));&lt;br /&gt;        $this-&amp;gt;assertTrue($this-&amp;gt;_ttt-&amp;gt;play(2, 0, $playerX));&lt;br /&gt;        &lt;br /&gt;        $this-&amp;gt;_ttt-&amp;gt;setGrid(new Grid());&lt;br /&gt;        &lt;br /&gt;        $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(0, 0, $playerX));&lt;br /&gt;        $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(1, 1, $playerX));&lt;br /&gt;        $this-&amp;gt;assertTrue($this-&amp;gt;_ttt-&amp;gt;play(2, 2, $playerX));&lt;br /&gt;        &lt;br /&gt;        $this-&amp;gt;_ttt-&amp;gt;setGrid(new Grid());&lt;br /&gt;        &lt;br /&gt;        $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(0, 2, $playerX));&lt;br /&gt;        $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(1, 1, $playerX));&lt;br /&gt;        $this-&amp;gt;assertTrue($this-&amp;gt;_ttt-&amp;gt;play(2, 0, $playerX));&lt;br /&gt;    }&lt;/pre&gt;As you see, we only tested the functionality to see if we can have a     winner when 3 identical symbols appear in a single row horizontal,     vertical or diagonal. We can make a couple of comments on this test:&lt;br /&gt;    &lt;ul&gt;&lt;li&gt;this test is named wrong as it doesn't test the gameplay         functionality&lt;/li&gt;&lt;li&gt;this test should be branched out into different tests for each         row&lt;/li&gt;&lt;li&gt;this test doesn't test turn-by-turn game play&lt;/li&gt;&lt;/ul&gt;In other words, we need to clean this up and come up with better     tests! Let's focus on tearing this test into 3 separate tests,     testing just a single direction. Again, we can use a dataProvider     method for this.&lt;br /&gt;    &lt;pre&gt;    public function rowColProvider()&lt;br /&gt;    {&lt;br /&gt;        return array (&lt;br /&gt;            array (array (array (0,0), array (0,1), array (0,2))),&lt;br /&gt;            array (array (array (0,0), array (1,0), array (2,0))),&lt;br /&gt;            array (array (array (0,0), array (1,1), array (2,2))),&lt;br /&gt;            array (array (array (0,2), array (1,1), array (2,0))),&lt;br /&gt;        );&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * @dataProvider rowColProvider&lt;br /&gt;     */&lt;br /&gt;    public function testGameplayCanDetectWinner($rowCols)&lt;br /&gt;    {&lt;br /&gt;        $player = $this-&amp;gt;_ttt-&amp;gt;getPlayers()-&amp;gt;seek(0)-&amp;gt;current();&lt;br /&gt;        $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play($rowCols[0][0], $rowCols[0][1], $player));&lt;br /&gt;        $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play($rowCols[1][0], $rowCols[1][1], $player));&lt;br /&gt;        $this-&amp;gt;assertTrue($this-&amp;gt;_ttt-&amp;gt;play($rowCols[2][0], $rowCols[2][1], $player));&lt;br /&gt;    }&lt;/pre&gt;Now we know that with each turn, the return value will indicate if     we have a winner (TRUE) or not (FALSE). But we still need to see if     we can have the same result when playing with two players. As you  see with the last line, the third entry gives us a positive result.&lt;br /&gt;&lt;br /&gt;But we still don't have a gameplay going! We need to have two players  enter the arena and each player playing turn by turn. So how do we  approach this? Well, the easiest way for now is to create a test that  does just that. The benefit here is we can decide which player is going  to win the game.&lt;br /&gt;&lt;pre&gt;    public function testGameCanBePlayed()&lt;br /&gt;    {&lt;br /&gt;        $playerX = $this-&amp;gt;_ttt-&amp;gt;getPlayers()-&amp;gt;seek(0)-&amp;gt;current();&lt;br /&gt;        $playerO = $this-&amp;gt;_ttt-&amp;gt;getPlayers()-&amp;gt;seek(1)-&amp;gt;current();&lt;br /&gt;        $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(0, 0, $playerX));&lt;br /&gt;        $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(0, 1, $playerO));&lt;br /&gt;        $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(1, 1, $playerX));&lt;br /&gt;        $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(2, 2, $playerO));&lt;br /&gt;        $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(1, 0, $playerX));&lt;br /&gt;        $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(2, 0, $playerO));&lt;br /&gt;        $this-&amp;gt;assertTrue($this-&amp;gt;_ttt-&amp;gt;play(1, 2, $playerX));&lt;br /&gt;    }&lt;/pre&gt;Visual this result looks like the following grid:&lt;br /&gt;&lt;pre&gt; X | O |   &lt;br /&gt;&lt;span style="font-weight: bold;"&gt; X | X | X &lt;/span&gt;&lt;br /&gt; O |   | O &lt;/pre&gt;But most importantly our tests are still green, giving us that wonderful feeling of achievement.&lt;br /&gt;&lt;br /&gt;Let's finish up our tests so we can see no one can play any further  after we've got a winner. PHPUnit provides a nice anotation we can use  for this: depends. So now we can test that the game is stopped,  depending on our test "testGameCanBePlayed".&lt;br /&gt;For this to work, we need to return our game (in this case  $this-&amp;gt;_ttt). Just add the following line at the bottom of the test  class "testGameCanBePlayed".&lt;br /&gt;&lt;pre&gt;    public function testGameCanBePlayed()&lt;br /&gt;    {&lt;br /&gt;        $playerX = $this-&amp;gt;_ttt-&amp;gt;getPlayers()-&amp;gt;seek(0)-&amp;gt;current();&lt;br /&gt;        $playerO = $this-&amp;gt;_ttt-&amp;gt;getPlayers()-&amp;gt;seek(1)-&amp;gt;current();&lt;br /&gt;        $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(0, 0, $playerX));&lt;br /&gt;        $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(0, 1, $playerO));&lt;br /&gt;        $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(1, 1, $playerX));&lt;br /&gt;        $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(2, 2, $playerO));&lt;br /&gt;        $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(1, 0, $playerX));&lt;br /&gt;        $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(2, 0, $playerO));&lt;br /&gt;        $this-&amp;gt;assertTrue($this-&amp;gt;_ttt-&amp;gt;play(1, 2, $playerX));&lt;br /&gt;        return $this-&amp;gt;_ttt;&lt;br /&gt;    }&lt;/pre&gt;And the next step is simple:&lt;br /&gt;&lt;pre&gt;    /**&lt;br /&gt;     * @depends testGameCanBePlayed&lt;br /&gt;     * @expectedException RuntimeException&lt;br /&gt;     */&lt;br /&gt;    public function testGameStopsAfterWinning($game)&lt;br /&gt;    {&lt;br /&gt;        $playerO = $game-&amp;gt;getPlayers()-&amp;gt;seek(1)-&amp;gt;current();&lt;br /&gt;        $game-&amp;gt;play(2,1, $playerO);&lt;br /&gt;    }&lt;/pre&gt;In order for this test to succeed we need to add a couple of things to our game:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;a status property that will tell us if there's a winner&lt;/li&gt;&lt;li&gt;a setter method to flip the flag once a winner is detected&lt;/li&gt;&lt;li&gt;a checking method to verify a winner is not yet detected&lt;/li&gt;&lt;li&gt;modify our play method so it throws a RuntimeException when we try to play when a winner exists.&lt;/li&gt;&lt;/ul&gt;&lt;pre&gt;&amp;lt;?php&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;class Tictactoe&lt;br /&gt;{&lt;br /&gt;    ...&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * Status indicator to say there's a winner or not&lt;br /&gt;     * &lt;br /&gt;     * @var bool&lt;br /&gt;     */&lt;br /&gt;    protected $_winner = false;&lt;br /&gt;&lt;br /&gt;    ...&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * Sets a flag to indicate this game has a winner&lt;br /&gt;     * &lt;br /&gt;     * @param bool $flag&lt;br /&gt;     * @return Tictactoe&lt;br /&gt;     */&lt;br /&gt;    public function setWinner($flag = true)&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_winner = $flag;&lt;br /&gt;        return $this;&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * Checks if the game has a winner&lt;br /&gt;     * &lt;br /&gt;     * @return bool&lt;br /&gt;     */&lt;br /&gt;    public function hasWinner()&lt;br /&gt;    {&lt;br /&gt;        return $this-&amp;gt;_winner;&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * Plays the game and returns TRUE if a player has become a winner, FALSE&lt;br /&gt;     * if the player is not (yet) a winner.&lt;br /&gt;     * &lt;br /&gt;     * @param int $row&lt;br /&gt;     * @param int $column&lt;br /&gt;     * @param Player $player&lt;br /&gt;     * @return bool&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;     * @throws RuntimeException&lt;/span&gt;&lt;br /&gt;     */&lt;br /&gt;    public function play($row, $column, Player $player)&lt;br /&gt;    {&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;        if ($this-&amp;gt;hasWinner()) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;            throw new RuntimeException('Game already has a winner');&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;        }&lt;/span&gt;&lt;br /&gt;        $this-&amp;gt;getGrid()-&amp;gt;setSymbol($row, $column, $player-&amp;gt;getSymbol());&lt;br /&gt;        return $this-&amp;gt;isWinner($player);&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * Returns TRUE if a player has become a winner, FALSE if not.&lt;br /&gt;     * &lt;br /&gt;     * @param Player $player&lt;br /&gt;     * @return bool&lt;br /&gt;     */&lt;br /&gt;    public function isWinner(Player $player)&lt;br /&gt;    {&lt;br /&gt;        if ($this-&amp;gt;getGrid()-&amp;gt;inRow($player-&amp;gt;getSymbol())) {&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;            $this-&amp;gt;setWinner();&lt;/span&gt;&lt;br /&gt;            return true;&lt;br /&gt;        }&lt;br /&gt;        if ($this-&amp;gt;getGrid()-&amp;gt;inColumn($player-&amp;gt;getSymbol())) {&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;            $this-&amp;gt;setWinner();&lt;/span&gt;&lt;br /&gt;            return true;&lt;br /&gt;        }&lt;br /&gt;        if ($this-&amp;gt;getGrid()-&amp;gt;inDiagonal($player-&amp;gt;getSymbol())) {&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;            $this-&amp;gt;setWinner();&lt;/span&gt;&lt;br /&gt;            return true;&lt;br /&gt;        }&lt;br /&gt;        return false;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;That's it. We covered the main purpose of the game and we did it semi  test driven. We can start playing a cute little game of Tictactoe and  covered a couple important features of &lt;a href="http://phpunit.de/"&gt;PHPUnit&lt;/a&gt;. I also showed that it's  not a bad thing if you modify parts of your tests to achieve new or  other specifications.&lt;br /&gt;&lt;br /&gt;The &lt;a href="https://github.com/DragonBe/tictactoe"&gt;full game source code&lt;/a&gt; can be found on &lt;a href="https://github.com/DragonBe"&gt;my github account&lt;/a&gt; (&lt;a href="https://github.com/DragonBe/tictactoe"&gt;https://github.com/DragonBe/tictactoe&lt;/a&gt;),  and as you go over the log, you can follow along with these series as  well. When playing with the source code, you might think about testing  edge cases: playing 9 rounds and no winner, try to place a symbol off  the grid, place twice a symbol on the grid of a single player  (cheating), and so on. &lt;br /&gt;&lt;br /&gt;Another thing could be that you turn this little game into an online  game. What are the things you need to concider in that situation. Are  our tests still valid or do we need to modify our architecture and our  tests to support that kind of game playing?&lt;br /&gt;&lt;br /&gt;Since it's on &lt;a href="http://github.com/"&gt;GitHub&lt;/a&gt;, you can fork it and send me a pull request once you have an edge case figured out.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/NL4Yd2Y7VxU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/849480286855330673/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2011/09/quality-assurance-on-php-projects.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/849480286855330673" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/849480286855330673" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/NL4Yd2Y7VxU/quality-assurance-on-php-projects.html" title="Quality Assurance on PHP projects - PHPUnit part 4" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2011/09/quality-assurance-on-php-projects.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-6691411242144597783</id><published>2011-08-28T23:30:00.000+02:00</published><updated>2011-08-28T23:30:30.982+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="qa" /><category scheme="http://www.blogger.com/atom/ns#" term="phpdoc" /><category scheme="http://www.blogger.com/atom/ns#" term="in2it" /><category scheme="http://www.blogger.com/atom/ns#" term="qaseries" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><category scheme="http://www.blogger.com/atom/ns#" term="phpunit" /><title type="text">Quality Assurance on PHP projects - PHPUnit part 3</title><content type="html">&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;div style="text-align: left;"&gt;Time for the third part on unit testing with phpunit in my &lt;a href="http://www.dragonbe.com/search/label/qaseries"&gt;Quality Assurance on PHP projects&lt;/a&gt; series. In &lt;a href="http://www.dragonbe.com/2011/08/quality-assurance-on-php-projects_17.html"&gt;part one&lt;/a&gt; we started writing unit tests for a simple game of tic-tac-toe. In &lt;a href="http://www.dragonbe.com/2011/08/quality-assurance-on-php-projects_23.html"&gt;part two&lt;/a&gt; we started converting our unit tests into actual code and moved our  general unit test code for grids into a Grid focussed unit test. In this  part, we're looking at how we can optimize the tests for our players.&lt;br /&gt;&lt;br /&gt;When we look at our initial tests for players, we only focussed on two  things: each player has a symbol and players are managed within a  collection class.&lt;br /&gt;&lt;pre&gt;    public function testGamePlayersAreSetAtStart()&lt;br /&gt;    {&lt;br /&gt;        $players = $this-&amp;gt;_ttt-&amp;gt;getPlayers();&lt;br /&gt;        $this-&amp;gt;assertInstanceOf('Players', $players);&lt;br /&gt;        $this-&amp;gt;assertEquals(2, count($players));&lt;br /&gt;        $this-&amp;gt;assertEquals(Player::PLAYER_X, $players-&amp;gt;seek(0)-&amp;gt;current()-&amp;gt;getSymbol());&lt;br /&gt;        $this-&amp;gt;assertEquals(Player::PLAYER_O, $players-&amp;gt;seek(1)-&amp;gt;current()-&amp;gt;getSymbol());&lt;br /&gt;    }&lt;/pre&gt;As you can imagine, this is a very minimalistic test that cannot cope  with all player related issues. We did have two classes to handle all  player related issues and thus we need to move our test out of our  TictactoeTest class.&lt;br /&gt;&lt;br /&gt;A quick look at the functionality of a player should give us more fuel  to creat better unit tests. Bytaking a new look at the functionality of a  single player, we can optimize our tests. Since we already have some  working code, let's take a quick look at the Player class to figure out  what is being expected.&lt;br /&gt;&lt;pre&gt;&amp;lt;?php&lt;br /&gt;/**&lt;br /&gt; * TicTacToe&lt;br /&gt; * &lt;br /&gt; * A simple game that's played with two players, each taking a turn by marking&lt;br /&gt; * a field in a grid of 3 x 3 with either an X or an O (one symbol per player).&lt;br /&gt; * Winner is the one who has 3 identical symbols in a single horizontal,&lt;br /&gt; * vertical or diagonal row.&lt;br /&gt; * &lt;br /&gt; * @package Tictactoe&lt;br /&gt; * @license "Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)"&lt;br /&gt; * @link http://creativecommons.org/licenses/by-sa/3.0/&lt;br /&gt; */&lt;br /&gt;/**&lt;br /&gt; * Player&lt;br /&gt; * &lt;br /&gt; * This Player class is responsible to set up and maintain a single player&lt;br /&gt; * &lt;br /&gt; * @package Tictactoe&lt;br /&gt; * @category Tictactoe&lt;br /&gt; *&lt;br /&gt; */&lt;br /&gt;class Player&lt;br /&gt;{&lt;br /&gt;    /**&lt;br /&gt;     * Defines the symbol X for a player to choose&lt;br /&gt;     * &lt;br /&gt;     * @var string&lt;br /&gt;     */&lt;br /&gt;    const PLAYER_X = 'X';&lt;br /&gt;    /**&lt;br /&gt;     * Defines the symbol X for a player to choose&lt;br /&gt;     * &lt;br /&gt;     * @var string&lt;br /&gt;     */&lt;br /&gt;    const PLAYER_O = 'O';&lt;br /&gt;    /**&lt;br /&gt;     * The symbol a player chooses&lt;br /&gt;     * &lt;br /&gt;     * @var string&lt;br /&gt;     */&lt;br /&gt;    protected $_symbol;&lt;br /&gt;    /**&lt;br /&gt;     * Constructor for a single player, with an optional symbol (X or O) to&lt;br /&gt;     * define the player's playing symbol&lt;br /&gt;     * &lt;br /&gt;     * @param string $symbol&lt;br /&gt;     */&lt;br /&gt;    public function __construct($symbol = null)&lt;br /&gt;    {&lt;br /&gt;        if (null !== $symbol) {&lt;br /&gt;            $this-&amp;gt;setSymbol($symbol);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * Sets a symbol for this Player&lt;br /&gt;     * &lt;br /&gt;     * @param string $symbol&lt;br /&gt;     * @return Player&lt;br /&gt;     */&lt;br /&gt;    public function setSymbol($symbol)&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_symbol = (string) $symbol;&lt;br /&gt;        return $this;&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * Retrieves the chosen symbol from this Player&lt;br /&gt;     * &lt;br /&gt;     * @return string&lt;br /&gt;     */&lt;br /&gt;    public function getSymbol()&lt;br /&gt;    {&lt;br /&gt;        return $this-&amp;gt;_symbol;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;As you can see, we only care about the symbol of the player. Apparently  this is more than enough for now. But when we check our main  TictactoeTest we see no specific tests for our Player class. About time  to do something about it.&lt;br /&gt;&lt;pre&gt;&amp;lt;?php&lt;br /&gt;class PlayerTest extends PHPUnit_Framework_TestCase&lt;br /&gt;{&lt;br /&gt;    protected $_player;&lt;br /&gt;    protected function setUp()&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_player = new Player();&lt;br /&gt;        parent::setUp();&lt;br /&gt;    }&lt;br /&gt;    protected function tearDown()&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_player = null;&lt;br /&gt;        parent::tearDown();&lt;br /&gt;    }&lt;br /&gt;    public function testPlayerHasNoSymbolAtStartup()&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;assertNull($this-&amp;gt;_player-&amp;gt;getSymbol());&lt;br /&gt;    }&lt;br /&gt;    public function goodSymbolProvider()&lt;br /&gt;    {&lt;br /&gt;        return array (&lt;br /&gt;            array ('O'),&lt;br /&gt;            array ('X'),&lt;br /&gt;        );&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * @dataProvider goodSymbolProvider&lt;br /&gt;     */&lt;br /&gt;    public function testPlayerCanSetASymbol($symbol)&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_player-&amp;gt;setSymbol($symbol);&lt;br /&gt;        $this-&amp;gt;assertEquals($symbol, $this-&amp;gt;_player-&amp;gt;getSymbol());&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;In our code and in our tests we only set a symbol, but don't check if  the symbol is valid. The rules of this game do specify that only a X or a  O are allowed. So we need to add additional logic to our tests and  code. Let's work on our test first.&lt;br /&gt;&lt;pre&gt;    public function badSymbolProvider()&lt;br /&gt;    {&lt;br /&gt;        return array (&lt;br /&gt;            array ('a'),&lt;br /&gt;            array (123),&lt;br /&gt;            array ('Hello World!'),&lt;br /&gt;            array (0x123),&lt;br /&gt;        );&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * @dataProvider badSymbolProvider&lt;br /&gt;     * @expectedException InvalidArgumentException&lt;br /&gt;     */&lt;br /&gt;    public function testPlayerThrowsExceptionWithBadSymbol($symbol)&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_player-&amp;gt;setSymbol($symbol);&lt;br /&gt;    }&lt;/pre&gt;By using the '&lt;a href="http://www.phpunit.de/manual/current/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.exceptions"&gt;expectedException&lt;/a&gt;' annotation we can verify that a specific exception is thrown. For most exceptions you can often use one of the &lt;a href="http://www.php.net/manual/en/spl.exceptions.php"&gt;SPL exceptions&lt;/a&gt;. We choose '&lt;a href="http://www.php.net/manual/en/class.invalidargumentexception.php"&gt;InvalidArgumentException&lt;/a&gt;'  as the symbol is an invalid argument for this Player class. Of course  our tests will fail as we haven't forseen this feature or behaviour.&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;img alt="InvalidArgumentException" src="https://lh4.googleusercontent.com/-x7vhNaN82u4/TlqfESCwmNI/AAAAAAAACww/r3VWhRRRQNw/s800/Player%252520invalidargument%252520test.png" style="height: 520px; width: 699px;" title="InvalidArgumentException test fails" /&gt;&lt;br /&gt;&lt;/div&gt;We need to modify our code so it can be throw an  InvalidArgumentException when wrong arguments (read symbols) are being  provided. We want our tests to succeed, right?&lt;br /&gt;&lt;pre&gt;    /**&lt;br /&gt;     * Sets a symbol for this Player&lt;br /&gt;     * &lt;br /&gt;     * @param string $symbol&lt;br /&gt;     * @return Player&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;     * @throws InvalidArgumentException&lt;/span&gt;&lt;br /&gt;     */&lt;br /&gt;    public function setSymbol($symbol)&lt;br /&gt;    {&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;        $validSymbols = array (self::PLAYER_O, self::PLAYER_X);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;        if (!in_array($symbol, $validSymbols)) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;            throw new InvalidArgumentException(&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;                'Player can only choose between X or O');&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;        }&lt;/span&gt;&lt;br /&gt;        $this-&amp;gt;_symbol = (string) $symbol;&lt;br /&gt;        return $this;&lt;br /&gt;    }&lt;/pre&gt;As you can see, it's just a small but significant change that ensures  proper symbols are being used in our game. And our tests now succeed.&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;img alt="InvalidArgumentException" src="https://lh4.googleusercontent.com/-hFm7TqNVe0A/TlqijrGHwTI/AAAAAAAACxE/yMA1nl-ZCQI/s800/Player%252520invalidargument%252520test%252520succeed.png" style="height: 520px; width: 699px;" title="Now our InvalidArgumentException tests succeed" /&gt;&lt;br /&gt;  &lt;div style="text-align: left;"&gt;So we can increase the quality of the game by making sure only allowed symbols are used.&lt;br /&gt;    &lt;br /&gt;We also have a collection class called Players that contain both  players. We could have used an array, but as we don't know yet what kind  of functionality we want for all players. In our code we just created  this collection that implements the &lt;a href="http://www.php.net/manual/en/class.seekableiterator.php"&gt;SeekableIterator&lt;/a&gt; and &lt;a href="http://www.php.net/manual/en/class.countable.php"&gt;Countable&lt;/a&gt; interfaces. Our PlayersTest should be able to add a Player object, even  two Player objects but certainly not more than 2 players. In test code  this becomes a simple straightforward test.&lt;br /&gt;    &lt;pre&gt;&amp;lt;?php&lt;br /&gt;class PlayersTest extends PHPUnit_Framework_TestCase&lt;br /&gt;{&lt;br /&gt;    protected $_players;&lt;br /&gt;    &lt;br /&gt;    protected function setUp()&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_players = new Players();&lt;br /&gt;        parent::setUp();&lt;br /&gt;    }&lt;br /&gt;    protected function tearDown()&lt;br /&gt;    {&lt;br /&gt;        parent::tearDown();&lt;br /&gt;        $this-&amp;gt;_players = null;&lt;br /&gt;    }&lt;br /&gt;    public function testPlayersIsEmptyAtStartUp()&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;assertEmpty($this-&amp;gt;_players-&amp;gt;getPlayers());&lt;br /&gt;    }&lt;br /&gt;    public function testPlayerCanAddPlayer()&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_players-&amp;gt;addPlayer(new Player());&lt;br /&gt;        $this-&amp;gt;assertEquals(1, count($this-&amp;gt;_players));&lt;br /&gt;    }&lt;br /&gt;    public function testPlayerCanTwoPlayerObjects()&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_players-&amp;gt;addPlayer(new Player())&lt;br /&gt;                       -&amp;gt;addPlayer(new Player());&lt;br /&gt;        $this-&amp;gt;assertEquals(2, count($this-&amp;gt;_players));&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * @expectedException OverflowException&lt;br /&gt;     */&lt;br /&gt;    public function testPlayerCannotAddMoreThenTwoPlayerObjects()&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_players-&amp;gt;addPlayer(new Player())&lt;br /&gt;                       -&amp;gt;addPlayer(new Player())&lt;br /&gt;                       -&amp;gt;addPlayer(new Player());&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;Since we implement 2 interfaces we know the functionality of them, and  therefor we should not test for it as they will throw errors if not  implemented correctly. If you still doubt yourself on working with these  interfaces, create a separate set of tests that just verifies your  collection class is behaving as expected. Our collection class Players  will look like this.&lt;br /&gt;    &lt;pre&gt;&amp;lt;?php&lt;br /&gt;/**&lt;br /&gt; * TicTacToe&lt;br /&gt; * &lt;br /&gt; * A simple game that's played with two players, each taking a turn by marking&lt;br /&gt; * a field in a grid of 3 x 3 with either an X or an O (one symbol per player).&lt;br /&gt; * Winner is the one who has 3 identical symbols in a single horizontal,&lt;br /&gt; * vertical or diagonal row.&lt;br /&gt; * &lt;br /&gt; * @package Tictactoe&lt;br /&gt; * @license "Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)"&lt;br /&gt; * @link http://creativecommons.org/licenses/by-sa/3.0/&lt;br /&gt; */&lt;br /&gt;/**&lt;br /&gt; * Players&lt;br /&gt; * &lt;br /&gt; * This Players class is a collection container for both players, implementing&lt;br /&gt; * both Countable and SeekableIterator interfaces from SPL&lt;br /&gt; * &lt;br /&gt; * @package Tictactoe&lt;br /&gt; * @category Tictactoe&lt;br /&gt; * @see SeekableIterator&lt;br /&gt; * @see Countable&lt;br /&gt; * @link http://php.net/spl&lt;br /&gt; *&lt;br /&gt; */&lt;br /&gt;class Players implements SeekableIterator, Countable&lt;br /&gt;{&lt;br /&gt;    const MAX_PLAYERS = 2;&lt;br /&gt;    /**&lt;br /&gt;     * Contains Player objects&lt;br /&gt;     * &lt;br /&gt;     * @var array&lt;br /&gt;     */&lt;br /&gt;    protected $_players = array ();&lt;br /&gt;    /**&lt;br /&gt;     * Specifies the position in the stack&lt;br /&gt;     * &lt;br /&gt;     * @var int&lt;br /&gt;     */&lt;br /&gt;    protected $_position;&lt;br /&gt;    /**&lt;br /&gt;     * Keeps track of the count&lt;br /&gt;     * &lt;br /&gt;     * @var int&lt;br /&gt;     */&lt;br /&gt;    protected $_count;&lt;br /&gt;    /**&lt;br /&gt;     * Constructor for this Players collection, allows setting players with&lt;br /&gt;     * optional provided array of Player objects&lt;br /&gt;     * &lt;br /&gt;     * @param array $array&lt;br /&gt;     */&lt;br /&gt;    public function __construct(array $array = null)&lt;br /&gt;    {&lt;br /&gt;        if (null !== $array) {&lt;br /&gt;            foreach ($array as $player) {&lt;br /&gt;                $this-&amp;gt;addPlayer($player);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * Adds a Player object to the collection&lt;br /&gt;     * &lt;br /&gt;     * @param Player $player&lt;br /&gt;     * @return Players&lt;br /&gt;     * @throw Overflow&lt;br /&gt;     */&lt;br /&gt;    public function addPlayer(Player $player)&lt;br /&gt;    {&lt;br /&gt;        if (self::MAX_PLAYERS &amp;lt;= $this-&amp;gt;count()) {&lt;br /&gt;            throw new OverflowException('Cannot add more Player objects');&lt;br /&gt;        }&lt;br /&gt;        $this-&amp;gt;_players[] = $player;&lt;br /&gt;        return $this;&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * Retrieves a list of Player objects currently in the collection&lt;br /&gt;     * &lt;br /&gt;     * @return array&lt;br /&gt;     */&lt;br /&gt;    public function getPlayers()&lt;br /&gt;    {&lt;br /&gt;        return $this-&amp;gt;_players;&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * Sets the internal stack pointer back to 0&lt;br /&gt;     * &lt;br /&gt;     * @see SeekableIterator::rewind()&lt;br /&gt;     */&lt;br /&gt;    public function rewind()&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_position = 0;&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * Sets the internal stack pointer to the next position&lt;br /&gt;     * &lt;br /&gt;     * @see SeekableIterator::next()&lt;br /&gt;     */&lt;br /&gt;    public function next()&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_position++;&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * Checks if the current position in the stack is still valid&lt;br /&gt;     * &lt;br /&gt;     * @see SeekableIterator::valid()&lt;br /&gt;     * @return boolean&lt;br /&gt;     */&lt;br /&gt;    public function valid()&lt;br /&gt;    {&lt;br /&gt;        return isset ($this-&amp;gt;_player[$this-&amp;gt;_position]);&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * Retrieves the current object from the stack&lt;br /&gt;     * &lt;br /&gt;     * @see SeekableIterator::current()&lt;br /&gt;     * @return Player&lt;br /&gt;     */&lt;br /&gt;    public function current()&lt;br /&gt;    {&lt;br /&gt;        return $this-&amp;gt;_players[$this-&amp;gt;_position];&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * Retrieves the current position from the stack&lt;br /&gt;     * &lt;br /&gt;     * @see SeekableIterator::key()&lt;br /&gt;     * @return int&lt;br /&gt;     */&lt;br /&gt;    public function key()&lt;br /&gt;    {&lt;br /&gt;        return $this-&amp;gt;_position;&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * Moves the position to the given stack postion&lt;br /&gt;     * &lt;br /&gt;     * @see SeekableIterator::seek()&lt;br /&gt;     * @throws OutOfBoundsException&lt;br /&gt;     */&lt;br /&gt;    public function seek($position)&lt;br /&gt;    {&lt;br /&gt;        if (!isset ($this-&amp;gt;_players[$position])) {&lt;br /&gt;            throw new OutOfBoundsException('No more items in this stack');&lt;br /&gt;        }&lt;br /&gt;        $this-&amp;gt;_position = $position;&lt;br /&gt;        return $this;&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * Keeps record of the count in this collection&lt;br /&gt;     * &lt;br /&gt;     * @see Countable::count()&lt;br /&gt;     */&lt;br /&gt;    public function count()&lt;br /&gt;    {&lt;br /&gt;        return count($this-&amp;gt;_players);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;Ok, we now have our Grid and our Player objects tested. Next up is  testing the game itself. Are all our tests taking care of all rules  defined by the game? What are the edge cases? What is not acceptable?  All this and more in the next eppisode of '&lt;a href="http://www.dragonbe.com/search/label/qaseries"&gt;Quality Assurance on PHP projects&lt;/a&gt;'.&lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;  &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/TplnzApunkY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/6691411242144597783/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2011/08/quality-assurance-on-php-projects_28.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/6691411242144597783" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/6691411242144597783" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/TplnzApunkY/quality-assurance-on-php-projects_28.html" title="Quality Assurance on PHP projects - PHPUnit part 3" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://lh4.googleusercontent.com/-x7vhNaN82u4/TlqfESCwmNI/AAAAAAAACww/r3VWhRRRQNw/s72-c/Player%252520invalidargument%252520test.png" height="72" width="72" /><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2011/08/quality-assurance-on-php-projects_28.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-4297124943059406814</id><published>2011-08-23T00:08:00.002+02:00</published><updated>2011-08-23T00:08:46.756+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="qa" /><category scheme="http://www.blogger.com/atom/ns#" term="phpdoc" /><category scheme="http://www.blogger.com/atom/ns#" term="in2it" /><category scheme="http://www.blogger.com/atom/ns#" term="qaseries" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><category scheme="http://www.blogger.com/atom/ns#" term="phpunit" /><title type="text">Quality Assurance on PHP projects - PHPUnit part 2</title><content type="html">&amp;nbsp; &amp;nbsp; &amp;nbsp;I hope everyone enjoyed my &lt;a href="http://www.dragonbe.com/2011/08/quality-assurance-on-php-projects_17.html"&gt;first         article&lt;/a&gt; on unit testing with &lt;a href="http://phpunit.de/"&gt;phpunit&lt;/a&gt;    where I started writing a few tests that would guide us building our     little game of tictactoe. Today I'm going start with turning these     tests into working code and adjusting our tests to have a clear separation of responsibility. Since we already know what the code should     produce, we only have to work out the details.&lt;br /&gt;&lt;br /&gt;Our tests tell us we have four classes:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Tictactoe: the main class that is responsible for the game and         how it should be played&lt;/li&gt;&lt;li&gt;Grid: is the class that's responsible for setting up the         playing grid&lt;/li&gt;&lt;li&gt;Players: a collection class containing both player objects&lt;/li&gt;&lt;li&gt;Player: the class defining a single player&lt;/li&gt;&lt;/ul&gt;Our first test concerns all about the grid we're going to use.     Here's the test again:&lt;br /&gt;&lt;pre class="code"&gt;    public function testGameGridIsSetAtStart()&lt;br /&gt;    {&lt;br /&gt;        $grid = $this-&amp;gt;_ttt-&amp;gt;getGrid();&lt;br /&gt;        $this-&amp;gt;assertInstanceOf('Grid', $grid);&lt;br /&gt;        $this-&amp;gt;assertEquals(3, count($grid-&amp;gt;getRows()));&lt;br /&gt;        foreach ($grid-&amp;gt;getRows() as $row) {&lt;br /&gt;            $this-&amp;gt;assertInternalType('array', $row);&lt;br /&gt;            $this-&amp;gt;assertEquals(3, count($row));&lt;br /&gt;            $this-&amp;gt;assertNull($row[0]);&lt;br /&gt;            $this-&amp;gt;assertNull($row[1]);&lt;br /&gt;            $this-&amp;gt;assertNull($row[2]);&lt;br /&gt;        }&lt;br /&gt;    }   &lt;/pre&gt;What can we learn from this test?&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Grid is part of our main TicTacToe class&lt;/li&gt;&lt;li&gt;has a method that fetches rows of type array&lt;/li&gt;&lt;ul&gt;&lt;li&gt;and each row has columns as an array&lt;br /&gt;        &lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;We can also make the following assumptions:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Grid class will instantiate a grid with 3 rows and 3 columns&lt;/li&gt;&lt;li&gt;When calling "getRows()" we retrieve an array of 3 columns&lt;/li&gt;&lt;li&gt;Each field in the grid will have a null value as a default&lt;/li&gt;&lt;li&gt;A Player must have a way to set his "symbol" on a specific position on the grid&lt;/li&gt;&lt;li&gt;Must have a way to verify if a given symbol exists in either a horizontal, a vertical or a diagonal row&lt;br /&gt;  &lt;/li&gt;&lt;/ul&gt;With this information we can start writing our Grid class&lt;br /&gt;&lt;pre class="code"&gt;&amp;lt;?php&lt;br /&gt;/**&lt;br /&gt; * TicTacToe&lt;br /&gt; * &lt;br /&gt; * A simple game that's played with two players, each taking a turn by marking&lt;br /&gt; * a field in a grid of 3 x 3 with either an X or an O (one symbol per player).&lt;br /&gt; * Winner is the one who has 3 identical symbols in a single horizontal,&lt;br /&gt; * vertical or diagonal row.&lt;br /&gt; * &lt;br /&gt; * @package Tictactoe&lt;br /&gt; * @license "Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)"&lt;br /&gt; * @link http://creativecommons.org/licenses/by-sa/3.0/&lt;br /&gt; */&lt;br /&gt;/**&lt;br /&gt; * Grid&lt;br /&gt; * &lt;br /&gt; * This Grid class is responsible to set up and maintain the playing field&lt;br /&gt; * &lt;br /&gt; * @package Tictactoe&lt;br /&gt; * @category Tictactoe&lt;br /&gt; *&lt;br /&gt; */&lt;br /&gt;class Grid&lt;br /&gt;{&lt;br /&gt;    /**&lt;br /&gt;     * Constant to define the number of rows&lt;br /&gt;     * @var int&lt;br /&gt;     */&lt;br /&gt;    const ROWS = 3;&lt;br /&gt;    /**&lt;br /&gt;     * Constant to define the number of colomns&lt;br /&gt;     * @var int&lt;br /&gt;     */&lt;br /&gt;    const COLS = 3;&lt;br /&gt;    /**&lt;br /&gt;     * Container for all rows and columns&lt;br /&gt;     * &lt;br /&gt;     * @var array&lt;br /&gt;     */&lt;br /&gt;    protected $_rows = array ();&lt;br /&gt;    /**&lt;br /&gt;     * Constructor for this Grid class that will set up our grid with 3 rows&lt;br /&gt;     * and 3 columns while setting the value of each field to NULL&lt;br /&gt;     */&lt;br /&gt;    public function __construct()&lt;br /&gt;    {&lt;br /&gt;        for ($i = 0; $i &amp;lt; self::ROWS; $i++) {&lt;br /&gt;            $columns = array ();&lt;br /&gt;            for ($j = 0; $j &amp;lt; self::COLS; $j++) {&lt;br /&gt;                $columns[$j] = null;&lt;br /&gt;            }&lt;br /&gt;            $this-&amp;gt;addRow($columns);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * Adds a row to the grid, requiring an array of 3 fields representing&lt;br /&gt;     * the columns&lt;br /&gt;     * &lt;br /&gt;     * @param array $row&lt;br /&gt;     * @return Grid&lt;br /&gt;     */&lt;br /&gt;    public function addRow(array $row)&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_rows[] = $row;&lt;br /&gt;        return $this;&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * Retrieves all rows from this grid, including an array for the columns&lt;br /&gt;     * for each row&lt;br /&gt;     * &lt;br /&gt;     * @return array&lt;br /&gt;     */&lt;br /&gt;    public function getRows()&lt;br /&gt;    {&lt;br /&gt;        return $this-&amp;gt;_rows;&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * Sets the symbol for each field&lt;br /&gt;     * &lt;br /&gt;     * @param int $row The position of the field in the row&lt;br /&gt;     * @param int $column The position of the field in the column&lt;br /&gt;     * @param string $symbol&lt;br /&gt;     */&lt;br /&gt;    public function setSymbol($row, $column, $symbol)&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_rows[$row][$column] = $symbol;&lt;br /&gt;        return $this;&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * Retrieves the current symbol from a given cordinate on the grid&lt;br /&gt;     * &lt;br /&gt;     * @param int $row The postion of the field in the row&lt;br /&gt;     * @param int $column The position of the field in the column&lt;br /&gt;     * @return string&lt;br /&gt;     */&lt;br /&gt;    public function getSymbol($row, $column)&lt;br /&gt;    {&lt;br /&gt;        return $this-&amp;gt;_rows[$row][$column];&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * Validation method to verify if a given symbol is found 3 times in a&lt;br /&gt;     * single row. If we have 3 matches, it will return TRUE. In all other&lt;br /&gt;     * cases it will return FALSE.&lt;br /&gt;     * &lt;br /&gt;     * @param string $symbol&lt;br /&gt;     * @return boolean&lt;br /&gt;     */&lt;br /&gt;    public function inRow($symbol)&lt;br /&gt;    {&lt;br /&gt;        foreach ($this-&amp;gt;getRows() as $row) {&lt;br /&gt;            $match = 0;&lt;br /&gt;            foreach ($row as $column) {&lt;br /&gt;                if ($symbol === $column) {&lt;br /&gt;                    $match++;&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;            if (self::ROWS === $match) {&lt;br /&gt;                return true;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        return false;&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * Validation method to verify if a given symbol is found 3 times in a&lt;br /&gt;     * single column. If we have 3 matches, it will return TRUE. In all other&lt;br /&gt;     * cases it will return FALSE.&lt;br /&gt;     * &lt;br /&gt;     * @param string $symbol&lt;br /&gt;     * @return boolean&lt;br /&gt;     */&lt;br /&gt;    public function inColumn($symbol)&lt;br /&gt;    {&lt;br /&gt;        for ($i = 0; $i &amp;lt; self::COLS; $i++) {&lt;br /&gt;            $match = 0;&lt;br /&gt;            for ($j = 0; $j &amp;lt; self::ROWS; $j++) {&lt;br /&gt;                if ($symbol === $this-&amp;gt;_rows[$j][$i]) {&lt;br /&gt;                    $match++;&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;            if (self::COLS === $match) {&lt;br /&gt;                return true;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        return false;&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * Validation method to verify if a given symbol is found 3 times in a&lt;br /&gt;     * single diagonal row. If we have 3 matches, it will return TRUE. In all &lt;br /&gt;     * other cases it will return FALSE.&lt;br /&gt;     * &lt;br /&gt;     * @param string $symbol&lt;br /&gt;     * @return boolean&lt;br /&gt;     */&lt;br /&gt;    public function inDiagonal($symbol)&lt;br /&gt;    {&lt;br /&gt;        $match1 = $match2 = 0;&lt;br /&gt;        for ($i = 0; $i &amp;lt; self::ROWS; $i++) {&lt;br /&gt;            if ($symbol === $this-&amp;gt;_rows[$i][$i]) {&lt;br /&gt;                $match1++;&lt;br /&gt;            }&lt;br /&gt;            if ($symbol === $this-&amp;gt;_rows[$i][self::COLS - 1 - $i]) {&lt;br /&gt;                $match2++;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        if (self::ROWS === $match1 || self::ROWS === $match2) {&lt;br /&gt;            return true;&lt;br /&gt;        }&lt;br /&gt;        return false;&lt;br /&gt;    }&lt;br /&gt;}    &lt;/pre&gt;Now we have a grid class, but we cannot test it properly as we use a  Tictactoe class to validate everything. No problem, we can modify our  tests as well to better serve our needs. Actually, we already have  defined our test criteria. Let's list them again:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="text-decoration: line-through;"&gt;Grid is part of our main TicTacToe class&lt;/span&gt; (is not really relevant for our Grid functionality)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;has a method that fetches rows of type array&lt;/li&gt;&lt;ul&gt;&lt;li&gt;and each row has columns as an array&lt;br /&gt;       &lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Grid class will instantiate a grid with 3 rows and 3 columns&lt;/li&gt;&lt;li&gt;When calling "getRows()" we retrieve an array of 3 rows with each 3 columns&lt;/li&gt;&lt;li&gt;Each field in the grid will have a null value as a default&lt;/li&gt;&lt;li&gt;A Player must have a way to set his "symbol" on a specific position on the grid&lt;/li&gt;&lt;li&gt;Must have a way to verify if a given symbol exists in either a horizontal, a vertical or a diagonal row&lt;br /&gt;  &lt;/li&gt;&lt;/ul&gt;The first four criterea we already addressed in our general Tictactoe  test case. To separate responsibilities, we can move our test case from  our main TictactoeTest class into a GridTest class. &lt;br /&gt;&lt;pre&gt;&amp;lt;?php&lt;br /&gt;class GridTest extends PHPUnit_Framework_TestCase&lt;br /&gt;{&lt;br /&gt;    const TEST_SYMBOL = 'X';&lt;br /&gt;    &lt;br /&gt;    protected $_grid;&lt;br /&gt;    &lt;br /&gt;    protected function setUp()&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_grid = new Grid();&lt;br /&gt;        parent::setUp();&lt;br /&gt;    }&lt;br /&gt;    protected function tearDown()&lt;br /&gt;    {&lt;br /&gt;        parent::tearDown();&lt;br /&gt;        $this-&amp;gt;_grid = null;&lt;br /&gt;    }&lt;br /&gt;    public function testGameGridIsSetAtStart()&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;assertEquals(Grid::ROWS, count($this-&amp;gt;_grid-&amp;gt;getRows()));&lt;br /&gt;        foreach ($this-&amp;gt;_grid-&amp;gt;getRows() as $row) {&lt;br /&gt;            $this-&amp;gt;assertInternalType('array', $row);&lt;br /&gt;            $this-&amp;gt;assertEquals(Grid::COLS, count($row));&lt;br /&gt;            foreach ($row as $column) {&lt;br /&gt;                $this-&amp;gt;assertNull($column);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;So now we have exactly the same as before, except we created a separate  TestCase specifically designed for Grid related tests. This allows us to  follow up on issues people might report later on (things we haven't  thought of or just don't know yet), as we can write now a specific test  for our Grid.&lt;br /&gt;&lt;br /&gt;But we're not there yet, we still have 2 more things to verify:  positioning of a symbol and verifying we have a 3 identical symbols in  any of the three rows, columns or diagonal rows.&lt;br /&gt;&lt;br /&gt;Let's look at our test verifying the positioning of a symbol:&lt;br /&gt;&lt;pre&gt;    public function testGridCanPositionASymbol()&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_grid-&amp;gt;setSymbol(0, 0, self::TEST_SYMBOL);&lt;br /&gt;        $this-&amp;gt;assertNotNull($this-&amp;gt;_grid-&amp;gt;getSymbol(0,0));&lt;br /&gt;        $this-&amp;gt;assertEquals(self::TEST_SYMBOL, $this-&amp;gt;_grid-&amp;gt;getSymbol(0,0));&lt;br /&gt;    }&lt;/pre&gt;But as you see, this is not really working for us. We can only ferify  just one position on the grid and if we want to test more positions, our  test method would grow exponentionally. Say hello to the "dataProvider"&lt;br /&gt;&lt;pre&gt;    public function cordinateProvider()&lt;br /&gt;    {&lt;br /&gt;        return array (&lt;br /&gt;            array (0,0), array (0,1), array (0,2), array (1,0), array (1,1),&lt;br /&gt;            array (1,2), array (2,0), array (2,1), array (2,2),&lt;br /&gt;        );&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * @dataProvider cordinateProvider&lt;br /&gt;     */&lt;br /&gt;    public function testGridCanPositionASymbol($row, $column)&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_grid-&amp;gt;setSymbol($row, $column, self::TEST_SYMBOL);&lt;br /&gt;        $this-&amp;gt;assertNotNull($this-&amp;gt;_grid-&amp;gt;getSymbol($row, $column));&lt;br /&gt;        $this-&amp;gt;assertEquals(self::TEST_SYMBOL, $this-&amp;gt;_grid-&amp;gt;getSymbol($row, $column));&lt;br /&gt;    }&lt;/pre&gt;The @dataProvider tag in your codeblock comment tells PHPUnit to use the  method specified by this tag as a "provider" of data. Each row in this  data provider method (as it returns an array of arrays) will be a  provisioner for your test class, and for each row, your test will be  executed from a clean state, so you don't have any corruption due to bad  resetting of your objects.&lt;br /&gt;&lt;br /&gt;Hey, now we can also use this to verify if we have 3 identical symbols  in a horizontal, vertical or diagonal row! Let's work that out as well.&lt;br /&gt;&lt;pre&gt;    protected function _setDataOnGrid($data)&lt;br /&gt;    {&lt;br /&gt;        foreach ($data as $field) {&lt;br /&gt;            list ($row, $column) = $field;&lt;br /&gt;            $this-&amp;gt;_grid-&amp;gt;setSymbol($row, $column, self::TEST_SYMBOL);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    public function horizontalRowProvider()&lt;br /&gt;    {&lt;br /&gt;        return array (&lt;br /&gt;            array (array (array (0,0), array (0,1), array (0,2))),&lt;br /&gt;            array (array (array (1,0), array (1,1), array (1,2))),&lt;br /&gt;            array (array (array (2,0), array (2,1), array (2,2))),&lt;br /&gt;        );&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * @dataProvider horizontalRowProvider&lt;br /&gt;     */&lt;br /&gt;    public function testGridHasThreeSymbolsInARow($data)&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_setDataOnGrid($data);&lt;br /&gt;        $this-&amp;gt;assertTrue($this-&amp;gt;_grid-&amp;gt;inRow(self::TEST_SYMBOL));&lt;br /&gt;    }&lt;br /&gt;    public function VerticalRowProvider()&lt;br /&gt;    {&lt;br /&gt;        return array (&lt;br /&gt;            array (array (array (0,0), array (1,0), array (2,0))),&lt;br /&gt;            array (array (array (0,1), array (1,1), array (2,1))),&lt;br /&gt;            array (array (array (0,2), array (1,2), array (2,2))),&lt;br /&gt;        );&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * @dataProvider VerticalRowProvider&lt;br /&gt;     */&lt;br /&gt;    public function testGridHasThreeSymbolsInAColumn($data)&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_setDataOnGrid($data);&lt;br /&gt;        $this-&amp;gt;assertTrue($this-&amp;gt;_grid-&amp;gt;inColumn(self::TEST_SYMBOL));&lt;br /&gt;    }&lt;br /&gt;    public function DiagonalRowProvider()&lt;br /&gt;    {&lt;br /&gt;        return array (&lt;br /&gt;            array (array (array (0,0), array (1,1), array (2,2))),&lt;br /&gt;            array (array (array (0,2), array (1,1), array (2,0))),&lt;br /&gt;        );&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * @dataProvider DiagonalRowProvider&lt;br /&gt;     */&lt;br /&gt;    public function testGridHasThreeSymbolsInADiagonal($data)&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_setDataOnGrid($data);&lt;br /&gt;        $this-&amp;gt;assertTrue($this-&amp;gt;_grid-&amp;gt;inDiagonal(self::TEST_SYMBOL));&lt;br /&gt;    }&lt;/pre&gt;Oh, Wow! We now have a complete Grid test case that isolates all Grid related tasks and responsibilities.&lt;br /&gt;&lt;pre&gt;&amp;lt;?php&lt;br /&gt;class GridTest extends PHPUnit_Framework_TestCase&lt;br /&gt;{&lt;br /&gt;    const TEST_SYMBOL = 'X';&lt;br /&gt;    &lt;br /&gt;    protected $_grid;&lt;br /&gt;    &lt;br /&gt;    protected function setUp()&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_grid = new Grid();&lt;br /&gt;        parent::setUp();&lt;br /&gt;    }&lt;br /&gt;    protected function tearDown()&lt;br /&gt;    {&lt;br /&gt;        parent::tearDown();&lt;br /&gt;        $this-&amp;gt;_grid = null;&lt;br /&gt;    }&lt;br /&gt;    public function testGameGridIsSetAtStart()&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;assertEquals(Grid::ROWS, count($this-&amp;gt;_grid-&amp;gt;getRows()));&lt;br /&gt;        foreach ($this-&amp;gt;_grid-&amp;gt;getRows() as $row) {&lt;br /&gt;            $this-&amp;gt;assertInternalType('array', $row);&lt;br /&gt;            $this-&amp;gt;assertEquals(Grid::COLS, count($row));&lt;br /&gt;            foreach ($row as $column) {&lt;br /&gt;                $this-&amp;gt;assertNull($column);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    public function cordinateProvider()&lt;br /&gt;    {&lt;br /&gt;        return array (&lt;br /&gt;            array (0,0), array (0,1), array (0,2), array (1,0), array (1,1),&lt;br /&gt;            array (1,2), array (2,0), array (2,1), array (2,2),&lt;br /&gt;        );&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * @dataProvider cordinateProvider&lt;br /&gt;     */&lt;br /&gt;    public function testGridCanPositionASymbol($row, $column)&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_grid-&amp;gt;setSymbol($row, $column, self::TEST_SYMBOL);&lt;br /&gt;        $this-&amp;gt;assertNotNull($this-&amp;gt;_grid-&amp;gt;getSymbol($row, $column));&lt;br /&gt;        $this-&amp;gt;assertEquals(self::TEST_SYMBOL, $this-&amp;gt;_grid-&amp;gt;getSymbol($row, $column));&lt;br /&gt;    }&lt;br /&gt;    protected function _setDataOnGrid($data)&lt;br /&gt;    {&lt;br /&gt;        foreach ($data as $field) {&lt;br /&gt;            list ($row, $column) = $field;&lt;br /&gt;            $this-&amp;gt;_grid-&amp;gt;setSymbol($row, $column, self::TEST_SYMBOL);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    public function horizontalRowProvider()&lt;br /&gt;    {&lt;br /&gt;        return array (&lt;br /&gt;            array (array (array (0,0), array (0,1), array (0,2))),&lt;br /&gt;            array (array (array (1,0), array (1,1), array (1,2))),&lt;br /&gt;            array (array (array (2,0), array (2,1), array (2,2))),&lt;br /&gt;        );&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * @dataProvider horizontalRowProvider&lt;br /&gt;     */&lt;br /&gt;    public function testGridHasThreeSymbolsInARow($data)&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_setDataOnGrid($data);&lt;br /&gt;        $this-&amp;gt;assertTrue($this-&amp;gt;_grid-&amp;gt;inRow(self::TEST_SYMBOL));&lt;br /&gt;    }&lt;br /&gt;    public function VerticalRowProvider()&lt;br /&gt;    {&lt;br /&gt;        return array (&lt;br /&gt;            array (array (array (0,0), array (1,0), array (2,0))),&lt;br /&gt;            array (array (array (0,1), array (1,1), array (2,1))),&lt;br /&gt;            array (array (array (0,2), array (1,2), array (2,2))),&lt;br /&gt;        );&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * @dataProvider VerticalRowProvider&lt;br /&gt;     */&lt;br /&gt;    public function testGridHasThreeSymbolsInAColumn($data)&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_setDataOnGrid($data);&lt;br /&gt;        $this-&amp;gt;assertTrue($this-&amp;gt;_grid-&amp;gt;inColumn(self::TEST_SYMBOL));&lt;br /&gt;    }&lt;br /&gt;    public function DiagonalRowProvider()&lt;br /&gt;    {&lt;br /&gt;        return array (&lt;br /&gt;            array (array (array (0,0), array (1,1), array (2,2))),&lt;br /&gt;            array (array (array (0,2), array (1,1), array (2,0))),&lt;br /&gt;        );&lt;br /&gt;    }&lt;br /&gt;    /**&lt;br /&gt;     * @dataProvider DiagonalRowProvider&lt;br /&gt;     */&lt;br /&gt;    public function testGridHasThreeSymbolsInADiagonal($data)&lt;br /&gt;    {&lt;br /&gt;        $this-&amp;gt;_setDataOnGrid($data);&lt;br /&gt;        $this-&amp;gt;assertTrue($this-&amp;gt;_grid-&amp;gt;inDiagonal(self::TEST_SYMBOL));&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;Running this test along with our initial TictactoeTest should result in a warm, fuzzy, green feeling when you see this screen:&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-57nwaK_TOpo/TlLSah9P9EI/AAAAAAAACwo/2bvbjThl1ww/s1600/TictactoeTest+with+GridTest.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="297" src="http://4.bp.blogspot.com/-57nwaK_TOpo/TlLSah9P9EI/AAAAAAAACwo/2bvbjThl1ww/s400/TictactoeTest+with+GridTest.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;Next time, let's look at our Players as the need also some respect, right.&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/5ovrT2RU-T4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/4297124943059406814/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2011/08/quality-assurance-on-php-projects_23.html#comment-form" title="6 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/4297124943059406814" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/4297124943059406814" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/5ovrT2RU-T4/quality-assurance-on-php-projects_23.html" title="Quality Assurance on PHP projects - PHPUnit part 2" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-57nwaK_TOpo/TlLSah9P9EI/AAAAAAAACwo/2bvbjThl1ww/s72-c/TictactoeTest+with+GridTest.png" height="72" width="72" /><thr:total>6</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2011/08/quality-assurance-on-php-projects_23.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-3184454531336041882</id><published>2011-08-17T02:02:00.011+02:00</published><updated>2011-08-17T18:36:39.579+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="qa" /><category scheme="http://www.blogger.com/atom/ns#" term="phpdoc" /><category scheme="http://www.blogger.com/atom/ns#" term="in2it" /><category scheme="http://www.blogger.com/atom/ns#" term="qaseries" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><category scheme="http://www.blogger.com/atom/ns#" term="phpunit" /><title type="text">Quality Assurance on PHP projects - PHPUnit part 1</title><content type="html">&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;Of all tools available for improving quality assurance, there's one tool that is the core tool you have to master: PHPUnit. PHPUnit is a complete testing framework crafted by Sebastian Bergmann (&lt;a href="http://twitter.com/!#/s_bergmann"&gt;@s_bergmann&lt;/a&gt;), who ported existing xUnit frameworks to PHP. And with this testing framework you're able to test your functionality in an automated way before you push code into production.&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;b&gt;[editorial]&lt;/b&gt;&amp;nbsp;As I cannot summarize the whole usage of phpunit in one blogpost, this will be a sequence of several articles that will pick up a specific task you want to cover with phpunit.&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;Installation&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;Well, first of all you need to have PHPUnit installed on your system. The easiest way to accomplish this is to use the PEAR installer.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;user@server: $ pear channel-discover pear.phpunit.de&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;user@server: $ pear channel-discover components.ez.no&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;user@server: $ pear channel-discover&amp;nbsp;pear.symfony-project.com&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;Once you've got the right channels, you can install the framework:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;user@server: $ pear install phpunit/PHPUnit&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;Configuration&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;You can use three methods to configure the way PHPUnit executes tests on your project:&lt;/span&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;a configuration file&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;command line parameters&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;a combination of both&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;Using a configuration file called "phpunit.xml" is by far the easiest way to run your unit tests in a consistent and organized way. It's a simple XML configuration file and the following works for me most of the times.&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;b&gt;NOTE:&lt;/b&gt;&amp;nbsp;I use phpunit with a configuration file that matches my project settings, so you might need to modify these settings if you're using a framework or have a different source code layout.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;lt;phpunit bootstrap="./TestHelper.php" colors="true"&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;lt;testsuite name="Unit test suite"&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;directory&amp;gt;./&amp;lt;/directory&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;lt;/testsuite&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;lt;filter&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;whitelist&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;directory suffix=".php"&amp;gt;../src/&amp;lt;/directory&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/whitelist&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/filter&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;lt;/phpunit&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;This simple configuration file has 3 major parts:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;1. The phpunit configuration tag&lt;/span&gt;&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;This tag starts the complete configuration, but also includes a bootstrap file (for bootstrapping your application like Zend Framework's bootstrapper class) and enables colors to show green, yellow and red colored bars to indicate the status of your tests. Since I'm a fan of a warm, fuzzy feeling whenever I see green, I enable this by default.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;2. Testsuite section&lt;/span&gt;&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;The testsuite is a wrapper that bundles all your tests into a single suite, so you can call it immediately at execution. You can provide it with a name and a directory.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;This section is very useful if you have multiple sections in your projects that you want to separate, as you can define the test suites based on their paths on the filesystem.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;i&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;3. Filtering&lt;/span&gt;&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;Filtering allows you to quickly define what needs to be tested and what needs to be excluded. In my case I need just to investigate my Zend Framework application path and my custom library path. I also want to exclude all my templates as they don't contain any logic.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;You can add also a section for logging where you can define code coverage reports (requires XDebug), a testdox progress report and other kinds of reports provided by PHPUnit. But since I will transfer these tasks to another tool, I'm not going to discuss it here.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;I also use a bootstrap file for phpunit called TestHelper.php that allows me to "bootstrap" my test suite. This bootstrap file sets my defined constants, include paths, timezone and error reporting levels.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;This is a very minimalistic bootstrapper:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;lt;?php&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;// file: tests/TestHelper.php&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;// set our app paths and environments&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;define('BASE_PATH', realpath(dirname(__FILE__) . '/../'));&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;define('APPLICATION_PATH', BASE_PATH . '/src');&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;define('TEST_PATH', BASE_PATH . '/tests');&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;define('APPLICATION_ENV', 'testing');&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;// Include path&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;set_include_path(&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; '.'&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; . PATH_SEPARATOR . BASE_PATH . '/src'&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; . PATH_SEPARATOR . get_include_path()&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;// Set the default timezone !!!&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;date_default_timezone_set('Europe/Brussels');&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;// We wanna catch all errors en strict warnings&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;error_reporting(E_ALL|E_STRICT);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;So how do you start?&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;Testing is really simple but for most people the complexity of having code testing other code and just report back if it's working or not is a bit confusing for people that haven't written any tests before.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;For the most part, in my professional career I get a lot of excuses like "no time", "no budget" or "no interest". If it's truly such a big problem to cope with something as simple as writing a test, a career change might be in order. And I mean it. I believe that if you're serious about your job, you have to take a little responsibility in it. And writing tests is not that hard.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;Let's begin with a classic example: a tic-tac-toe game. This is a simple pen-and-paper game for two players (O and X) that uses a grid of 3 by 3. The purpose of the game is to have a row, a column or a diagonal row filled with only O's or X's. More information can be found at&amp;nbsp;&lt;a href="http://en.wikipedia.org/wiki/Tic-tac-toe"&gt;http://en.wikipedia.org/wiki/Tic-tac-toe&lt;/a&gt;.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;img src="file:///Users/dragonbe/Library/Application%20Support/Evernote/data/101370/content/p102/f4076c20ff12932de40a018a5428e534.png" /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;img border="0" height="53" src="http://1.bp.blogspot.com/-4Ex34JazSYI/Tkr7D_zfMOI/AAAAAAAACwk/C9OZJsMTeMY/s320/Tic-tac-toe-game-1.png" width="320" /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;source:&amp;nbsp;http://en.wikipedia.org/wiki/File:Tic-tac-toe-game-1.png&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;So how do you start with this kind of a game? Very simple: define the objects, the rules and the goal of this game in a list that makes sense to you. I like to define it as follows:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;objects:&lt;/span&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;symbol X for user 1&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;symbol Y for user 2&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;play grid of 3 rows by 3 columns&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;rules:&lt;/span&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;write your symbol once for each turn within the grid&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;goal:&lt;/span&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;prevent opponent to win&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;win by having 3 symbols on a single row, a single column or a single diagonal row&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;Since I have tendency to write my tests before I write the code, as it allows me to think about what I expect I should get from class methods. Another benefit is that you write smaller classes with methods that just do what needs to be done.&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;TictactoeTest&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;Let's define our general outline for our main class Tictactoe. We already&amp;nbsp;know&amp;nbsp;a few&amp;nbsp;things: two players, a grid of 3 rows by 3 columns and a winner when three identical symbols are in a single row horizontal, vertical or diagonal.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;In test code it would look something like this:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;lt;?php&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;// file: tests/TictactoeTest.php&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;require_once 'Tictactoe.php';&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;class TictactoeTest extends PHPUnit_Framework_TestCase&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;{&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public function testGameGridIsSetAtStart()&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // test to see a grid is created of 3 by 3 and all fields are null&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public function testGamePlayersAreSetAtStart()&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // test to see players are set up at start with symbols X and O&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public function testGameCanBePlayed()&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // play a simple game with two players turn by turn&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // returning true or false would notify us if there's a winner or not&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;We test 3 major things here: that a grid is set, that two players are created and that the game can be played. We don't worry for edge cases or exceptions now, as we can take care of that later.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;When we run this test, we get a bunch of errors, basically saying that we don't have a Tictactoe class file yet. At this point, this is very normal.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;user@server:/dev/ttt/tests $ phpunit&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;PHP Warning:&amp;nbsp; require_once(Tictactoe.php): failed to open stream: No such file or directory in /dev/ttt/tests/TictactoeTest.php on line 3&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Warning: require_once(Tictactoe.php): failed to open stream: No such file or directory in /dev/ttt/tests/TictactoeTest.php on line 3&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;PHP Fatal error:&amp;nbsp; require_once(): Failed opening required 'Tictactoe.php' (include_path='.:/dev/ttt/src:.:/usr/lib/php/pear') in /dev/ttt/tests/TictactoeTest.php on line 3&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;Fatal error: require_once(): Failed opening required 'Tictactoe.php' (include_path='.:/dev/ttt/src:.:/usr/lib/php/pear') in /dev/ttt/tests/TictactoeTest.php on line 3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;We've got are main goals set, now it's time to make some assertions to see we get the information we want.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;First of all, we're going to setUp and tearDown our test case, so we know each test starts with a clean slate.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;class TictactoeTest extends PHPUnit_Framework_TestCase&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;{&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected $_ttt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected function setUp()&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; parent::setUp();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $this-&amp;gt;_ttt = new Tictactoe();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected function tearDown()&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $this-&amp;gt;_ttt = null;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; parent::tearDown();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;Now we need to fill in the blanks regarding the test methods. Let's start with our first method:&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; public function testGameGridIsSetAtStart()&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // we should be able to retrieve the grid from our Tictactoe class&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $grid = $this-&amp;gt;_ttt-&amp;gt;getGrid();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // let's make sure we created a grid class to handle all grid related manipulations&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $this-&amp;gt;assertInstanceOf('Grid', $grid);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // we know the grid is 3 x 3, so let's count the rows&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $this-&amp;gt;assertEquals(3, count($grid-&amp;gt;getRows()));&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // and for each row, let's count the columns&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // and since we're at it, let's check that their fields are empty&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; foreach ($grid-&amp;gt;getRows() as $row) {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $this-&amp;gt;assertInternalType('array', $row);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $this-&amp;gt;assertEquals(3, count($row));&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; foreach ($row as $column) {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $this-&amp;gt;assertNull($column);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;So, what do we learn from this test? We need to have a Grid class to take care of all grid related things and we need to ensure each field is empty before we start playing. Take a note of this as we need it later!&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;The next step is to set up our players. We know we have two players and each player has his own symbol (X or O).&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; public function testGamePlayersAreSetAtStart()&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // we should be able to retrieve all players from our Tictactoe class&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $players = $this-&amp;gt;_ttt-&amp;gt;getPlayers();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // ensure that players have their own class to take care of all players&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $this-&amp;gt;assertInstanceOf('Players', $players);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // we know we only have two players&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $this-&amp;gt;assertEquals(2, count($players));&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // we want to ensure that each player has the correct symbol&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $this-&amp;gt;assertEquals(Player::PLAYER_X, $players-&amp;gt;seek(0)-&amp;gt;current()-&amp;gt;getSymbol());&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $this-&amp;gt;assertEquals(Player::PLAYER_O, $players-&amp;gt;seek(1)-&amp;gt;current()-&amp;gt;getSymbol());&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;This test teaches us we need another two classes for our players. One class is acting as a collection implementing the SeekableIterator and Countable interfaces from SPL (I just love these, so I know I will use them!). The second class defines the player and the symbol it gets.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;Our last test method is all about playing the game.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; public function testGameCanBePlayed()&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // let's pick just one player&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $player = $this-&amp;gt;_ttt-&amp;gt;getPlayers()-&amp;gt;seek(0)-&amp;gt;current();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // and see if we can win if we created a row&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(0, 0, $player));&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $this-&amp;gt;assertFalse($this&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;-&amp;gt;_ttt-&amp;gt;play&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;(0, 1, $player));&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $this-&amp;gt;assertTrue($this&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;-&amp;gt;_ttt-&amp;gt;play&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;(0, 2, $player));&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $this-&amp;gt;_ttt-&amp;gt;setGrid(new Grid());&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // and see if we can win if we created a column&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $this-&amp;gt;assertFalse($this&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;-&amp;gt;_ttt-&amp;gt;play&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;(0, 1, $player));&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $this-&amp;gt;assertFalse($this&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;-&amp;gt;_ttt-&amp;gt;play&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;(1, 1, $player));&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $this-&amp;gt;assertTrue($this&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;-&amp;gt;_ttt-&amp;gt;play&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;(2, 1, $player));&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $this-&amp;gt;_ttt-&amp;gt;setGrid(new Grid());&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // and see if we can win if we go diagonal left to right&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(0, 0, $player));&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(1, 1, $player));&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $this-&amp;gt;assertTrue($this-&amp;gt;_ttt-&amp;gt;play(2, 2, $player));&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;$this-&amp;gt;_ttt-&amp;gt;setGrid(new Grid());&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // and see if we can win if we go diagonal right to left&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(0, 0, $player));&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $this-&amp;gt;assertFalse($this-&amp;gt;_ttt-&amp;gt;play(1, 1, $player));&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; $this-&amp;gt;assertTrue($this-&amp;gt;_ttt-&amp;gt;play(2, 2, $player));&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;In my next article we're going to set up our classes that should turn this test into a success. In the mean time, I challenge you to figure out how the code might look like. And if you want, share that code with everyone by posting it on &lt;a href="http://pastebin.com/"&gt;pastebin.com&lt;/a&gt;, &lt;a href="http://pastie.org/"&gt;pastie.org&lt;/a&gt; or using &lt;a href="https://gist.github.com/"&gt;github's gist&lt;/a&gt;.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/h2uJQUQcv8Y" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/3184454531336041882/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2011/08/quality-assurance-on-php-projects_17.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/3184454531336041882" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/3184454531336041882" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/h2uJQUQcv8Y/quality-assurance-on-php-projects_17.html" title="Quality Assurance on PHP projects - PHPUnit part 1" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-4Ex34JazSYI/Tkr7D_zfMOI/AAAAAAAACwk/C9OZJsMTeMY/s72-c/Tic-tac-toe-game-1.png" height="72" width="72" /><thr:total>1</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2011/08/quality-assurance-on-php-projects_17.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-8258757649478547706</id><published>2011-08-07T22:28:00.001+02:00</published><updated>2011-08-07T22:28:43.789+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="qa" /><category scheme="http://www.blogger.com/atom/ns#" term="phpdoc" /><category scheme="http://www.blogger.com/atom/ns#" term="in2it" /><category scheme="http://www.blogger.com/atom/ns#" term="qaseries" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><title type="text">Quality Assurance on PHP projects - PHPDocumentor feedback</title><content type="html">&lt;!--?xml version="1.0" encoding="UTF-8" standalone="no"?--&gt; &lt;span class="Apple-style-span" style="font-family: inherit;"&gt;First of all, thank you all for the enormous feedback I got on my latest article on documentation of code. I got a lot of comments on the usage of&amp;nbsp;&lt;a href="http://www.phpdoc.org/"&gt;PHPDocumentor&lt;/a&gt;&amp;nbsp;for the following reasons:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;the project seems to be no longer maintained&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;it's not ready for php 5.3&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;it uses too much resources when using it on big projects&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;I have to agree that these reasons are valid enough to step away from&amp;nbsp;&lt;a href="http://www.phpdoc.org/"&gt;PHPDocumentor&lt;/a&gt;&amp;nbsp;as a tool for documentation purposes and look for a better alternative. So I've investigated one tool most people have commented on or tweet-ed/facebook-ed/g+-ed on:&amp;nbsp;&lt;a href="http://www.docblox-project.org/"&gt;DocBlox&lt;/a&gt;.&lt;/span&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;Before I start a flamewar, I'm all for investigating more tools as time permits me, but these are my first impressions on the tool that had the most buzz in my personal social zone. If your tool of preference is not listed here, write an article on it and put the link here in the comments (or ping back to this article in your own post).&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;a href="http://www.docblox-project.org/"&gt;DocBlox&lt;/a&gt;&amp;nbsp;seems to be the youngest project of all the various projects suggested to me, but gets a lot of support from major players in the field of &lt;a href="http://www.php.net/"&gt;PHP&lt;/a&gt; and is supported by my favorite build tool&amp;nbsp;&lt;a href="http://www.phing.info/"&gt;Phing&lt;/a&gt;&amp;nbsp;(a tool I'll describe in detail in another article).&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;Installation&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;You can simply use&amp;nbsp;&lt;a href="http://pear.php.net/"&gt;PEAR&lt;/a&gt;&amp;nbsp;to install&amp;nbsp;&lt;a href="http://www.docblox-project.org/"&gt;DocBlox&lt;/a&gt;.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;user@server $:&amp;nbsp;pear channel-discover&amp;nbsp;pear.docblox-project.org&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;user@server $:&amp;nbsp;pear channel-discover&amp;nbsp;pear.michelf.com&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;Select a package you need, in this case&amp;nbsp;docblox/DocBlox-0.12.1 as the project is still in beta (when writing this article) and version DocBlox-0.12.2 has some issues&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;user@server $: pear install -a docblox/DocBlox-0.12.1&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;Usage&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;a href="http://www.docblox-project.org/"&gt;DocBlox&lt;/a&gt;&amp;nbsp;is pretty simple in it's usage and works indeed better than&amp;nbsp;&lt;a href="http://www.phpdoc.org/"&gt;PHPDocumentor&lt;/a&gt;, although I had to contact the project lead Mike van Riel (&lt;a href="http://twitter.com/#!/mvriel"&gt;@mvriel&lt;/a&gt;) regarding a dependency error on&amp;nbsp;&lt;a href="http://framework.zend.com/"&gt;Zend Framework&lt;/a&gt;, but it should be fixed now or any time soon.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;My dear friend Matthew Weier O'Phinney (&lt;a href="http://twitter.com/#!/weierophinney"&gt;@weierophinney&lt;/a&gt;) had taken up my challenge to&amp;nbsp;&lt;a href="http://weierophinney.net/matthew/archives/265-Using-DocBlox.html"&gt;write an article&lt;/a&gt;&amp;nbsp;about the usage of&amp;nbsp;&lt;a href="http://www.docblox-project.org/"&gt;DocBlox&lt;/a&gt;&amp;nbsp;and I have to say, it's a very good introduction that allowed me to use it immediately.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;As a result, I was able to test-run both&amp;nbsp;&lt;a href="http://www.phpdoc.org/"&gt;PHPDocumentor&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href="http://www.docblox-project.org/"&gt;DocBlox&lt;/a&gt;&amp;nbsp;using&amp;nbsp;&lt;a href="http://www.phing.info/"&gt;Phing&lt;/a&gt;&amp;nbsp;resulting in a small win for&amp;nbsp;&lt;a href="http://www.docblox-project.org/"&gt;DocBlox&lt;/a&gt;. I haven't made a true benchmark test including memory consumption, cpu usage but I think that someone with more sysadmin background knowledge can easily set it up to give a more in-depth detail on which tool performs the best.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/--WIuSHOm8Mk/Tj7zto62KlI/AAAAAAAACwA/RWlyOg93XiU/s1600/Running+DocBlox.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;img border="0" height="475" src="http://2.bp.blogspot.com/--WIuSHOm8Mk/Tj7zto62KlI/AAAAAAAACwA/RWlyOg93XiU/s640/Running+DocBlox.png" width="640" /&gt;&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-hfEjzswrpD0/Tj7zuOswYkI/AAAAAAAACwE/FY8GLzZd-1o/s1600/Running+PHPDocumentor.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;span class="Apple-style-span" style="font-family: inherit;"&gt;&lt;img border="0" height="451" src="http://2.bp.blogspot.com/-hfEjzswrpD0/Tj7zuOswYkI/AAAAAAAACwE/FY8GLzZd-1o/s640/Running+PHPDocumentor.png" width="640" /&gt;&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/AyPTO6HcuBs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/8258757649478547706/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2011/08/quality-assurance-on-php-projects.html#comment-form" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/8258757649478547706" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/8258757649478547706" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/AyPTO6HcuBs/quality-assurance-on-php-projects.html" title="Quality Assurance on PHP projects - PHPDocumentor feedback" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/--WIuSHOm8Mk/Tj7zto62KlI/AAAAAAAACwA/RWlyOg93XiU/s72-c/Running+DocBlox.png" height="72" width="72" /><thr:total>4</thr:total><georss:featurename>Battelsesteenweg 134, 2800 Arrondissement of Mechelen, Belgium</georss:featurename><georss:point>51.0301343 4.4660718</georss:point><georss:box>51.0276378 4.4611363 51.0326308 4.4710073</georss:box><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2011/08/quality-assurance-on-php-projects.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-8035120336788065724</id><published>2011-07-26T23:28:00.002+02:00</published><updated>2011-07-26T23:28:44.680+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="qa" /><category scheme="http://www.blogger.com/atom/ns#" term="phpdoc" /><category scheme="http://www.blogger.com/atom/ns#" term="in2it" /><category scheme="http://www.blogger.com/atom/ns#" term="qaseries" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><title type="text">Quality Assurance on PHP projects - PHPDocumentor</title><content type="html">It goes without saying people should document their code so that after a few weeks, months, years they still know what they did initially and why. Besides providing a mental note in the code, it also helps your IDE to figure out what your class is all about, which parameters should be used with the methods and what their return types are.&lt;br /&gt;&lt;br /&gt;But theory is not always reality and unfortunately I've come across too many lines of code that were just lines of code, no comments or annotations provided. So, in best cases I could guess the types and parameters, but in many it was too obfuscated. I already talked about usage of a code sniffer like PHP_CodeSniffer in my &lt;a href="http://www.dragonbe.com/2011/07/quality-assurance-on-php-projects_17.html"&gt;previous post&lt;/a&gt; where you can validate the usage of comments in the code. But forcing developers (using a pre-commit checker) into writing documentation with their code is not really a good thing. Once people are forced in a corner, they tend to figure out ways to escape and your whole idea to deliver better code goes out the window.&lt;br /&gt;&lt;br /&gt;As a consultant I face many situations where code is not documented or not documented enough, and the top 5 excuses I always get are:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;writing documentation is not requested by the customer&lt;/li&gt;&lt;li&gt;no time, too many things need to be done between releases&lt;/li&gt;&lt;li&gt;too much work, and no one looks at the code anyway&lt;/li&gt;&lt;li&gt;why should I?&lt;/li&gt;&lt;li&gt;we charge extra for documentation, but the customer doesn't want to pay it&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Like I said, these are mere excuses. I'm not here to judge these people, but I have to emphasize these excuses lack a real fundament to be acceptable in a professional environment. Maybe I'm taking to much pride in my "craft" as PHP developer, but don't you want to offer your customers the best you're company can provide?&lt;br /&gt;&lt;br /&gt;Let's turn things around and imagine a new developer joins your team. Wouldn't it be great if you had documentation that could create complete documentation on the source code so it's easy to get into the flow of things? And when looking at the code in their favorite IDE whenever they access existing code, wouldn't it be great they could see instantly what parameters are used for a certain method or function with a brief explanation why it's useful? Maybe that's why I consider writing documentation part of writing code, invalidating all 5 excuses mentioned earlier.&lt;br /&gt;&lt;h2&gt;Documentation 101&lt;/h2&gt;Writing documentation alongside your code is really not that much of work when you think of it. For all classes, you probably have a template that you can use that details your library of code components.&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&amp;lt;?php&lt;br /&gt;/**&lt;br /&gt;&amp;nbsp;* My Awesome Library&lt;br /&gt;&amp;nbsp;*&lt;br /&gt;&amp;nbsp;* This library provides additional functionality and resources&lt;br /&gt;&amp;nbsp;* for our standard framework [fill in your favorite framework]&lt;br /&gt;&amp;nbsp;*&lt;br /&gt;&amp;nbsp;* @category My&lt;br /&gt;&amp;nbsp;* @license Copyright 2011 © My Company, Inc. - All rights reserved&lt;br /&gt;&amp;nbsp;*/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When you actually write your class components, just explain in short what they do and why they've been created.&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;/**&lt;br /&gt;&amp;nbsp;* My_Amazing_Component_Class&lt;br /&gt;&amp;nbsp;*&lt;br /&gt;&amp;nbsp;* This class provides amazing functionality that extends the framework&lt;br /&gt;&amp;nbsp;* that's being used by this company.&lt;br /&gt;&amp;nbsp;*&lt;br /&gt;&amp;nbsp;* @package My_Amazing&lt;br /&gt;&amp;nbsp;* @subpackage My_Amazing_Component&lt;br /&gt;&amp;nbsp;* @version $Id:$&lt;br /&gt;&amp;nbsp;*/&lt;br /&gt;class My_Amazing_Component_Class&lt;br /&gt;{&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Once you get to the point where you write the method, you only need to provide some additional information about what it does, what the parameters are, the return types and if they throw exceptions or not&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;/**&lt;br /&gt;&amp;nbsp;* We need to take two parameters to figure out which is the location&lt;br /&gt;&amp;nbsp;* that we want to display on the map.&lt;br /&gt;&amp;nbsp;*&lt;br /&gt;&amp;nbsp;* @param float $latitude The latitude of the spot&lt;br /&gt;&amp;nbsp;* @param float $longitude The longitude of the spot&lt;br /&gt;&amp;nbsp;* @return string The link that displays the spot on a map&lt;br /&gt;&amp;nbsp;* @throws OutOfBoundsException When values are invalid&lt;br /&gt;&amp;nbsp;*/&lt;br /&gt;public function findTheSpot($latitude, $longitude)&lt;br /&gt;{&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see, not really difficult to get the documentation in place and when you do it immediately you don't even waste too much time writing it.&lt;br /&gt;&lt;h2&gt;Automated API documentation&lt;/h2&gt;If you have the documentation in place, there are tools out there that allow to generate API documentation quickly and easily. There are less known but very promising tools like &lt;a href="http://www.stack.nl/~dimitri/doxygen/"&gt;Doxygen&lt;/a&gt;, &lt;a href="http://www.docblox-project.org/"&gt;DocBlox&lt;/a&gt;, &lt;a href="http://freshmeat.net/projects/phpdocgen/"&gt;phpdocgen&lt;/a&gt; and many more. But the most known is still &lt;a href="http://www.phpdoc.org/"&gt;PHPDocumentor&lt;/a&gt;. The tools that were mentioned here all have their pros and cons, but their usage is pretty simple and their outcome is exactly what you need to have good documentation of your source code.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.phpdoc.org/"&gt;PHPDocumentor&lt;/a&gt; is easy to use and requires just a couple of parameters to generate high quality API documentation.&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;user@server $: phpdoc \&lt;br /&gt;&amp;nbsp; -o HTML:frames:earthli \&lt;br /&gt;&amp;nbsp; -ric docs/README.txt \&lt;br /&gt;&amp;nbsp; -ti "PHPDocumentor Example" \&lt;br /&gt;&amp;nbsp; -dc MyCompany \&lt;br /&gt;&amp;nbsp; -dn My_Amazing \&lt;br /&gt;&amp;nbsp; -d application/models,application/forms,library/My \&lt;br /&gt;&amp;nbsp; -i *.phtml \&lt;br /&gt;&amp;nbsp; -t docs/api&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Options used here:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;-o HTML:frames:earthli&lt;/b&gt; defines the template to use, in this example we use the framed earthly HTML template&lt;/li&gt;&lt;li&gt;&lt;b&gt;-ric docs/README.txt&lt;/b&gt; defines the README, INSTALL and CHANGELOG files&lt;/li&gt;&lt;li&gt;&lt;b&gt;-ti "PHPDocumentor Example"&lt;/b&gt; provides the title&lt;/li&gt;&lt;li&gt;&lt;b&gt;-dc MyCompany&lt;/b&gt; sets the default category if no category is being provided&lt;/li&gt;&lt;li&gt;&lt;b&gt;-dn My_Amazing&lt;/b&gt; sets the default package if no package is being provided&lt;/li&gt;&lt;li&gt;&lt;b&gt;-d application/models,application/forms,library/My&lt;/b&gt; defines the directories that contain the php code you want to document&lt;/li&gt;&lt;li&gt;&lt;b&gt;-i *.phtml&lt;/b&gt; defines files that should be ignored when rendering&lt;/li&gt;&lt;li&gt;&lt;b&gt;-t docs/api&lt;/b&gt; defines the location where the API should be generated&lt;/li&gt;&lt;/ul&gt;Running it on command line will look more or less like this&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;iframe allowfullscreen="" frameborder="0" height="349" src="http://www.youtube.com/embed/nPhyATNhnr0" width="425"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;The result of it is that you get very nice documentation that can be handed out to all developers on your team, third party service providers building on top of your applications or new people on your team.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-dxQeU_Ri27c/Ti8rrVWxAeI/AAAAAAAACv0/JpzPIATbdCI/s1600/Api_Documentation.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="279" src="http://2.bp.blogspot.com/-dxQeU_Ri27c/Ti8rrVWxAeI/AAAAAAAACv0/JpzPIATbdCI/s400/Api_Documentation.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;As you can see, isn't it great to have documentation with your code that requires not much of additional work? Don't forget, it's not just great to create smashing API documents, but it also helps your IDE as it will be able to give your more detail what kind of parameters and methods your classes provide and need.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/4YgcP8Zg8a0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/8035120336788065724/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2011/07/quality-assurance-on-php-projects_26.html#comment-form" title="14 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/8035120336788065724" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/8035120336788065724" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/4YgcP8Zg8a0/quality-assurance-on-php-projects_26.html" title="Quality Assurance on PHP projects - PHPDocumentor" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://img.youtube.com/vi/nPhyATNhnr0/default.jpg" height="72" width="72" /><thr:total>14</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2011/07/quality-assurance-on-php-projects_26.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-3619693195929198981</id><published>2011-07-17T23:10:00.000+02:00</published><updated>2011-07-17T23:10:57.295+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="qa" /><category scheme="http://www.blogger.com/atom/ns#" term="in2it" /><category scheme="http://www.blogger.com/atom/ns#" term="qaseries" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><title type="text">Quality Assurance on PHP projects - PHP_CodeSniffer</title><content type="html">&lt;br /&gt;&lt;div style="font-family: Arial;"&gt;&lt;a href="http://pear.php.net/manual/en/package.php.php-codesniffer.php"&gt;PHP_CodeSniffer&lt;/a&gt;&amp;nbsp;is probably the most convenient tool out there to analyze your source code and to verify it complies to company policies. Although it's debatable why source code should follow strict guidelines, it's only a matter of time before you discover yourself that it pays off to have a code base that appears to be written by one developer.&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;The first question you have to ask is what standard are you going to implement. There are several standards already packaged with&amp;nbsp;&lt;a href="http://pear.php.net/manual/en/package.php.php-codesniffer.php"&gt;PHP_CodeSniffer&lt;/a&gt;, but are they useful within your company? Maybe you want to extend or override some standards with your own implementation. Do remember, the standards supplied with&amp;nbsp;&lt;a href="http://pear.php.net/manual/en/package.php.php-codesniffer.php"&gt;PHP_CodeSniffer&lt;/a&gt;&amp;nbsp;have been negotiated over and over by the developers for ages. So if you want to define your own standards, be warned that it can be a long and tedious track before you can agree on a specific standard.&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Installation&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;Installing&amp;nbsp;&lt;a href="http://pear.php.net/manual/en/package.php.php-codesniffer.php"&gt;PHP_CodeSniffer&lt;/a&gt;&amp;nbsp;is easy when using the&amp;nbsp;&lt;a href="http://pear.php.net/"&gt;PEAR&lt;/a&gt;&amp;nbsp;framework. Make sure you have installed and upgraded the pear libraries that come with your OS. After that all you need to do as&amp;nbsp;&lt;b&gt;root&lt;/b&gt;&amp;nbsp;or&amp;nbsp;&lt;b&gt;Administrator&lt;/b&gt;&amp;nbsp;is the following.&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;user@server: $ pear install PHP_CodeSniffer&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;Or you can go to the&amp;nbsp;&lt;a href="http://pear.php.net/package/PHP_CodeSniffer/download"&gt;download page&lt;/a&gt;&amp;nbsp;of&amp;nbsp;&lt;a href="http://pear.php.net/manual/en/package.php.php-codesniffer.php"&gt;PHP_CodeSniffer&lt;/a&gt;&amp;nbsp;and download the source package yourself and install it the way you want it. In most cases, the&amp;nbsp;&lt;a href="http://pear.php.net/"&gt;PEAR&lt;/a&gt;&amp;nbsp;installation is a more elegant, easy way to install the tool.&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Configuration &amp;amp; Execution&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;a href="http://pear.php.net/manual/en/package.php.php-codesniffer.php"&gt;PHP_CodeSniffer&lt;/a&gt;doesn't require much configuration, but you have to decide on which coding standard you want to check the code base.&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;Standards provided by&amp;nbsp;&lt;a href="http://pear.php.net/manual/en/package.php.php-codesniffer.php"&gt;PHP_CodeSniffer&lt;/a&gt;&amp;nbsp;are the following:&lt;/div&gt;&lt;ul style="font-family: Arial;"&gt;&lt;li&gt;Zend&lt;/li&gt;&lt;li&gt;PEAR&lt;/li&gt;&lt;li&gt;PHPCS&lt;/li&gt;&lt;li&gt;Squiz&lt;/li&gt;&lt;li&gt;MySource&lt;/li&gt;&lt;/ul&gt;&lt;span class="Apple-style-span" style="font-family: Arial;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;user@server: phpcs --standard=PEAR /path/to/php/sources&lt;/span&gt;&lt;/span&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;But as stated, you can also define your own standard and provide the base path of the repository on command line&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;user@server: phpcs --standard=/path/to/my/standards /path/to/php/sources&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;There are a lot of extra options provided with this tool, but let me focus here on the more important ones you might find useful in your day-to-day usage of&amp;nbsp;&lt;a href="http://pear.php.net/manual/en/package.php.php-codesniffer.php"&gt;PHP_CodeSniffer&lt;/a&gt;.&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Ignoring files and paths&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;If you have a couple of external libraries or test scripts in your PHP projects, you might want to exclude them because they're not really part of your concerns. Wouldn't it be easy to just exclude them from the analysis? The following command will exclude paths you have no interest in.&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;user@server: phpcs --standard=PEAR --ignore=*/tests/*,*/library/Zend/*&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;b&gt;&lt;i&gt;Output options&lt;/i&gt;&lt;/b&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;Sometimes you require a different report than the default report that covers all information. Maybe you require a simple summary, a blame report, source report or a report formatted in XML or CSV for usage in another tool. It's only one option away.&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;The summary report:&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;user@server: phpcs --standard=PEAR --report=summary&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;The blame report (requires project to be checked out from a Subversion server):&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;user@server: phpcs --standard=PEAR --report=svnblame&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;More on these report formats can be found on the&amp;nbsp;&lt;a href="http://pear.php.net/manual/en/package.php.php-codesniffer.reporting.php"&gt;documentation pages&lt;/a&gt;&amp;nbsp;of&amp;nbsp;&lt;a href="http://pear.php.net/manual/en/package.php.php-codesniffer.php"&gt;PHP_CodeSniffer&lt;/a&gt;.&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;Roundup&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;Running&amp;nbsp;&lt;a href="http://pear.php.net/manual/en/package.php.php-codesniffer.php"&gt;PHP_CodeSniffer&lt;/a&gt;&amp;nbsp;on the command line is a very convenient way to investigate if the source code is following the standards everyone has agreed upon.&amp;nbsp;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;object width="320" height="266" class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://i.ytimg.com/vi/PJTRih_F_DI/0.jpg"&gt;&lt;param name="movie" value="http://www.youtube.com/v/PJTRih_F_DI?f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" /&gt;&lt;param name="bgcolor" value="#FFFFFF" /&gt;&lt;embed width="320" height="266"  src="http://www.youtube.com/v/PJTRih_F_DI?f=user_uploads&amp;c=google-webdrive-0&amp;app=youtube_gdata" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;You can also set it as a pre-commit hook for your revision control system, but in my experience it has a negative effect on the productivity of the development team. But it never hurts to try it out and see for yourself if it's a positive step or causes frustrations.&amp;nbsp;A full description on how to set it up for Subversion is explained on the&amp;nbsp;&lt;a href="http://pear.php.net/manual/en/package.php.php-codesniffer.svn-pre-commit.php"&gt;SVN pre-commit page&lt;/a&gt;&amp;nbsp;of&amp;nbsp;&lt;a href="http://pear.php.net/manual/en/package.php.php-codesniffer.php"&gt;PHP_CodeSniffer&lt;/a&gt;.&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;When you want to ensure everyone on your team follows the standard policies of your department or company,&amp;nbsp;&lt;a href="http://pear.php.net/manual/en/package.php.php-codesniffer.php"&gt;PHP_CodeSniffer&lt;/a&gt;&amp;nbsp;is a great tool to identify where developers need to modify their code so it complies.&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&amp;nbsp;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/67BLlxa7zyk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/3619693195929198981/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2011/07/quality-assurance-on-php-projects_17.html#comment-form" title="10 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/3619693195929198981" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/3619693195929198981" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/67BLlxa7zyk/quality-assurance-on-php-projects_17.html" title="Quality Assurance on PHP projects - PHP_CodeSniffer" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><thr:total>10</thr:total><georss:featurename>Unknown location.</georss:featurename><georss:point>51.03014045425552 4.465899467468262</georss:point><georss:box>51.027643954255524 4.4609639674682615 51.03263695425552 4.470834967468262</georss:box><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2011/07/quality-assurance-on-php-projects_17.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-2257045993992571256</id><published>2011-07-14T23:13:00.002+02:00</published><updated>2011-07-17T23:18:06.189+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="qa" /><category scheme="http://www.blogger.com/atom/ns#" term="in2it" /><category scheme="http://www.blogger.com/atom/ns#" term="qaseries" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><title type="text">Quality Assurance on PHP projects - PHPLint</title><content type="html">&lt;br /&gt;&lt;div style="font-family: Arial;"&gt;&lt;b&gt; &lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;div style="font-weight: normal;"&gt;&lt;b&gt;PHP Lint&lt;/b&gt;&lt;/div&gt;&lt;div style="font-weight: normal;"&gt;PHP Lint is probably the easiest way to validate your code on syntax errors, but it's also the most overlooked feature of PHP on command line.&lt;/div&gt;&lt;div style="font-weight: normal;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal;"&gt;&lt;i&gt;Provides a convenient way to perform only a syntax check on the given PHP code. On success, the text No syntax errors detected in &lt;filename&gt; is written to standard output and the shell return code is 0. On failure, the text Errors parsing &lt;filename&gt; in addition to the internal parser error message is written to standard output and the shell return code is set to -1.&lt;/filename&gt;&lt;/filename&gt;&lt;/i&gt;&lt;br /&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;This option won't find fatal errors (like undefined functions). Use the -f to test for fatal errors too.&lt;/i&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="font-weight: normal;"&gt;Quote taken from&amp;nbsp;&lt;a href="http://www.php.net/manual/en/features.commandline.options.php"&gt;http://www.php.net/manual/en/features.commandline.options.php&lt;/a&gt;.&lt;/div&gt;&lt;div style="font-weight: normal;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal;"&gt;Example of detecting failures only&lt;/div&gt;&lt;div style="font-weight: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;user@machine: $ /usr/bin/php -l /path/to/myfile.php&lt;/span&gt;&lt;/div&gt;&lt;div style="font-weight: normal;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal;"&gt;Example of detecting failures and fatal errors&lt;/div&gt;&lt;div style="font-weight: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;user@machine: $ /usr/bin/php -lf /path/to/myfile.php&lt;/span&gt;&lt;/div&gt;&lt;div style="font-weight: normal;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal;"&gt;Since this tool also returns numeric return codes, you can use it immediately as a pre-commit hook of your favorite revision control system like Git or Subversion. And with PHP on command line, you can create your own hooks with PHP.&amp;nbsp;&lt;/div&gt;&lt;div style="font-weight: normal;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal;"&gt;Example of a Git pre-commit hook taken from&amp;nbsp;&lt;a href="http://twitter.com/#!/tswicegood"&gt;Travis Swicegood&lt;/a&gt;'s article "&lt;a href="http://phpadvent.org/2008/dont-commit-that-error-by-travis-swicegood"&gt;Don't submit that error&lt;/a&gt;" on the PHP Advent pages.&lt;/div&gt;&lt;div style="font-weight: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;#!/usr/bin/php&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;/span&gt;&lt;/div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;// author: Travis Swicegood&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;// link:&amp;nbsp;&lt;/span&gt;&lt;a href="http://phpadvent.org/2008/dont-commit-that-error-by-travis-swicegood"&gt;http://phpadvent.org/2008/dont-commit-that-error-by-travis-swicegood&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;$output = array();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;$return = 0;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;$php = '/usr/bin/php';&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;exec('git rev-parse --verify HEAD 2&amp;gt; /dev/null', $output, $return);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;$against = $return == 0 ? 'HEAD' : '4b825dc642cb6eb9a060e54bf8d69288fbee4904';&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;exec("git diff-index --cached --name-only {$against}", $output);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;$filename_pattern = '/\.php$/';&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;$exit_status = 0;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;foreach ($output as $file) {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (!preg_match($filename_pattern, $file)) {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // don't check files that aren't PHP&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; continue;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $lint_output = array();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; exec("{$php} -lf " . escapeshellarg($file), $lint_output, $return);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ($return == 0) {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; continue;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; echo implode("\n", $lint_output), "\n";&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $exit_status = 1;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;exit($exit_status);&lt;/span&gt;&lt;/div&gt;&lt;/span&gt;&lt;/div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;div style="font-weight: normal;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-weight: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;Example of a SVN pre-commit hook I use myself&lt;/span&gt;&lt;/div&gt;&lt;div style="font-weight: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;#!/usr/bin/php&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;/span&gt;&lt;/div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;* Script that processes your code before it's committed to a Subversion&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;* repository.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;*/&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;* @var string Contains the path to the repository&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;*/&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;$repo = $argv[1];&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;* @var string Contains the ID of the transaction&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;*/&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;$transaction = $argv[2];&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;// Define the command line tools here&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;define('PHP', '/usr/bin/php');&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;define('SVNLOOK', '/usr/bin/svnlook');&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;define('GREP', '/usr/bin/grep');&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;define('AWK', '/usr/bin/awk');&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;* Checks for commit messages and sees if they are present. More fine&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;* grained validation of commit messages can be provided here. If a&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;* correct message was given, the function returns false. In all other&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;* cases it will return an error message.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;*&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;* @param string $transaction The ID of the transaction&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;* @param string $repo The path to the repository&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;* @return bool|string&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;*/&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;function checkCommitMessage($transaction, $repo)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;{&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $message = false;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $log = SVNLOOK . " log -t {$transaction} {$repo}";&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $commitMessage = null;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; exec($log, $commitMessage);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ('' === (string) $commitMessage[0]) {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $message = 'Required commit message is missing' . PHP_EOL;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return $message;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;* Checks the syntax of PHP Code with PHP Lint. If no errors where&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;* detected, it will return false. In all other cases, it will&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;* return the error messages&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;*&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;* @param string $transaction The ID of the transaction&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;* @param string $repo The path to the repository&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;* @return bool|string&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;*/&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;function checkSyntax($transaction, $repo)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;{&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $messages = array ();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $result = null;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $return = false;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; $changed = SVNLOOK . " changed -t {$transaction} {$repo}|"&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; . GREP . " \"^[UA]\"|"&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; . GREP . " \"\\.php\$\"|"&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; . AWK . " '{print \$2}'";&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; exec ($changed, $result);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; foreach ($result as $file) {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $phpLint = array ();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $lint = SVNLOOK . " cat -t {$transaction} {$repo} {$file}|"&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; . PHP . " -l 2&amp;gt;&amp;amp;1";&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $error = exec ($lint);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ('No syntax errors detected in -' === $error) continue;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $messages[] = "{$file} contains PHP syntax errors";&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (!empty ($messages)) {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; $return = implode(PHP_EOL, $messages);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return $return;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;/**&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;* Checks return values of these pre-commit functions and returns&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;* the return code: 0 for no messages and 1 in any other case.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;*&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;* @param bool|string The message output of pre-commit functions&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;* @return int The exit code&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;*/&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;function getExitCodes($message)&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;{&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (false !== $message) {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; file_put_contents('php://stderr', $message);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return 1;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return 0;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;$exitCodes = array ();&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;$exitCodes[] = getExitCodes(checkCommitMessage($transaction, $repo));&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;$exitCodes[] = getExitCodes(checkSyntax($transaction, $repo));&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;if (in_array(1, $exitCodes)) {&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; exit(1);&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;exit(0);&lt;/span&gt;&lt;/div&gt;&lt;/span&gt;&lt;/div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;div style="font-weight: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-weight: normal;"&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;Once you have a pre-commit hook in place, you don't have to worry that you commit code contains errors and might cause a failure for other developers in your team. The revision control system will notify you about any failures.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New';"&gt;&lt;/span&gt;&lt;/span&gt;&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/s51r8mhrMa8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/2257045993992571256/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2011/07/quality-assurance-on-php-projects.html#comment-form" title="8 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/2257045993992571256" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/2257045993992571256" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/s51r8mhrMa8/quality-assurance-on-php-projects.html" title="Quality Assurance on PHP projects - PHPLint" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><thr:total>8</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2011/07/quality-assurance-on-php-projects.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-263592044342837157</id><published>2011-07-14T12:56:00.000+02:00</published><updated>2011-07-14T23:02:47.413+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="qa" /><category scheme="http://www.blogger.com/atom/ns#" term="in2it" /><category scheme="http://www.blogger.com/atom/ns#" term="qaseries" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><title type="text">Quality Assurance on PHP projects - Introduction</title><content type="html">&lt;span class="Apple-style-span" style="font-family: Arial;"&gt;Quality Assurance has become an increasing important part of web application development, especially with PHP applications. When I look back in history it was common people just deployed web apps and tested it with a browser on the production server. We all know this process couldn't cover all possible user interactions and many bugs were reported back by end users the days after the deployment of a new release.&lt;/span&gt;&lt;br /&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;span class="Apple-style-span" style="font-family: Arial;"&gt;Luckily there are a lot of tools available that allows you to increase quality of these web applications, and the best part is they are all based on PHP! Let's have a quick look at what tools are interesting to start improving quality assurance on your PHP projects.&lt;/span&gt;&lt;br /&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;These series will cover most of the tools that will allow you to ensure your PHP projects will have an increased quality and your developers become more confident in their work.&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;If you want to know more about increased quality and better code, I can recommend reading the PHP QA Book, written by&amp;nbsp;&lt;a href="http://twitter.com/#!/spriebsch"&gt;Stefan Priebsch&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href="http://twitter.com/#!/s_bergmann"&gt;Sebastian Bergmann&lt;/a&gt;. It's official title is "&lt;a href="http://www.amazon.com/gp/product/0470872497/ref=as_li_tf_tl?ie=UTF8&amp;amp;tag=in2it-20&amp;amp;linkCode=as2&amp;amp;camp=217145&amp;amp;creative=399373&amp;amp;creativeASIN=0470872497"&gt;Real-World Solutions for Developing High-Quality PHP Frameworks and Applications&lt;/a&gt;" published by&amp;nbsp;&lt;a href="http://www.wrox.com/WileyCDA/WroxTitle/Real-World-Solutions-for-Developing-High-Quality-PHP-Frameworks-and-Applications.productCd-0470872497.html"&gt;Wrox Press&lt;/a&gt;&amp;nbsp;(ISBN&amp;nbsp;978-0-470-87249-9).&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;iframe frameborder="0" marginheight="0" marginwidth="0" scrolling="no" src="http://rcm.amazon.com/e/cm?t=in2it-20&amp;amp;o=1&amp;amp;p=8&amp;amp;l=as1&amp;amp;asins=0470872497&amp;amp;ref=tf_til&amp;amp;fc1=000000&amp;amp;IS2=1&amp;amp;lt1=_blank&amp;amp;m=amazon&amp;amp;lc1=0000FF&amp;amp;bc1=000000&amp;amp;bg1=FFFFFF&amp;amp;f=ifr" style="height: 240px; width: 120px;"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial;"&gt;In these series I do want to dig a little deeper in tools you might have heard of or concepts you agree are important, but are too high-level to comprehend. I also would like to open up a discussion to see if these tools are giving you the expected results or not.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/UWJvZDkZcPU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/263592044342837157/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2011/07/quality-assurance-on-php-projects_14.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/263592044342837157" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/263592044342837157" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/UWJvZDkZcPU/quality-assurance-on-php-projects_14.html" title="Quality Assurance on PHP projects - Introduction" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2011/07/quality-assurance-on-php-projects_14.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-1234462302801257363</id><published>2011-05-25T17:12:00.001+02:00</published><updated>2011-05-25T17:16:11.040+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="cloud" /><category scheme="http://www.blogger.com/atom/ns#" term="bookreview" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><title type="text">book review: php development in the cloud</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.phparch.com/books/php-development-in-the-cloud-a-phparchitect-guide/" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://www.phparch.com/wp-content/themes/phpa/thumb.php?src=http://www.phparch.com/wp-content/uploads/2011/02/PHPCloud-cover-small.jpg&amp;amp;w=260" width="162" /&gt;&lt;/a&gt;&lt;/div&gt;Cloud computing is no longer a buzz word, a hype that's been shouted at many technical conferences. No, it has turned into a real business model many people make a living off. And in that perspective I started exploring the world of cloud computing about a year ago. I also teamed up with Microsoft to promote and explore their new Windows Azure platform and even co-organized a contest deploying PHP applications on the Microsoft cloud solution.&lt;br /&gt;&lt;br /&gt;A couple of weeks ago, my dear friend Cal Evans asked me to review the book "&lt;a href="http://www.phparch.com/books/php-development-in-the-cloud-a-phparchitect-guide/"&gt;PHP Development in the Cloud&lt;/a&gt;" written by "Ivo Jansch" and "Vito Chin". I accepted as I was very interesting in exploring more of the cloudy world I just found myself in the middle of. Another reason was that both authors were fellow co-workers at Ibuildings back in the day. I promised this would be an objective review seen through the eyes of a developer just started exploring the mystifying world of cloud solutions.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The book&lt;/b&gt;&lt;br /&gt;Let me say the book is a fun read. You can take it up to your bedroom as late-night read, read during lunch or even in the garden when the kids are playing about. It all evolves around an photo managing application that uses the power of the cloud to process uploaded images and manage them in a new distribute way. Both Ivo and Vito build up the story where they take one feature at the time and use one of the provided cloud platforms to facilitate or expand the application's functionality.&lt;br /&gt;&lt;br /&gt;They also explain in plain words the different cloud solutions that are being offered these days and throw terms at you like IaaS, PaaS and SaaS with a detailed description why you would choose this specific cloud architecture followed by code examples. And this little extra attention to detail is something I could really appreciate as I was exploring these architectures but couldn't tie it to useful use cases why I should choose architecture A for this, but B for that. I'm glad Ivo and Vito cleared this fog for me.&lt;br /&gt;&lt;br /&gt;At the end of the book you have a functional photo managing application that uses cloud solutions from different cloud suppliers, each with it's specific usefulness. I think that working out the example code and playing with it was what I needed to get the clue as I'm more focussed on hacking code.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The good&lt;/b&gt;&lt;br /&gt;Like I already explained in my introduction, this book is well written and the order of chapters make it very easy to expand functionality step-by-step. If you're already familiar with cloud solutions, this book gives you some hands-on examples you can try out on different cloud platform architectures so you can decide which vendor provides the best solution for your needs.&lt;br /&gt;&lt;br /&gt;I also enjoy seeing various references to other open-source tools that allow you to fine-tune and optimize your application, infrastructure or platform you want to run your application or feature on.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The bad&lt;/b&gt;&lt;br /&gt;Although the book was well written and examples were clear and understandable, I found it hard to get some of the examples deployed on cloud solutions I haven't played with before. Luckily the internet is a good resource finding solutions to issues that crosses your path in the book. But I do want to state a small warning that if you're absolutely new to cloud development, the exercises in the book might frustrate you as you have to do additional searches to get it all working.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The ugly&lt;/b&gt;&lt;br /&gt;Although this book is complete in describing all features of cloud architectures and solutions, I do miss a little disclaimer that indicates the examples are written by technology experts and require some knowledge on some of the implementations they use. Even for me some things I had to reread as I have no degree in Computer Science and some of the concepts were unknown to me. In my case, Google was my friendly teacher to bring me back up to speed.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;br /&gt;If you're doing PHP and you're deploying apps on cloud technology, you'll find this book very interesting as it gives you some specific usages for each of the cloud solution providers. If either PHP or cloud technology is new to you, I'd recommend to get up to speed first before you jump into the dark.&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/2oC-eH5VnKo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/1234462302801257363/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2011/05/cloud-computing-is-no-longer-buzz-word.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/1234462302801257363" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/1234462302801257363" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/2oC-eH5VnKo/cloud-computing-is-no-longer-buzz-word.html" title="book review: php development in the cloud" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><thr:total>2</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2011/05/cloud-computing-is-no-longer-buzz-word.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-1598475297379303225</id><published>2011-03-16T17:42:00.000+01:00</published><updated>2011-03-16T17:42:58.868+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="discussion" /><category scheme="http://www.blogger.com/atom/ns#" term="twitter" /><title type="text">Twitter and wrong business decisions</title><content type="html">You've must have been on a trip to Mars if you haven't heard about the ordeal of Twitter, &lt;a href="https://groups.google.com/d/topic/twitter-development-talk/yCzVnHqHIWo/discussion"&gt;saying&lt;/a&gt; they close down their services to give it's customers (???) a consistent user experience. In short, they don't want external developers to further develop applications on their API.&lt;br /&gt;&lt;br /&gt;This blog article is not about bashing some more on decisions made by Twitter's management and VC's, but to open up a discussion to see if there's no better value to be found for the company to make money without killing innovation. I'd like to know your points of view on this discussion, maybe not to save Twitter, but the next best thing that comes along feeling pressure of VC's wanting to see money for their hard invested money. &lt;br /&gt;&lt;br /&gt;If I was a VC or a major stockholder of Twitter company, I'd think twice on killing competitive client developers! [Editorial: I'm no VC nor stockholder, but I do run a business]&lt;br /&gt;&lt;br /&gt;First of all you don't have the world geniuses working at Twitter, so you might kill innovation that could bring your service on the next level. I love how &lt;a href="http://cotweet.com/"&gt;CoTweet&lt;/a&gt; allows teams to share one twitter resource and delegate tasks to each other. This is just one of the many innovations that sprung out of nowhere just because of the existence of Twitter.&lt;br /&gt;&lt;br /&gt;Adds, good to have them but the revenue you earn on adds is still peanuts compared to other solutions, although in the case of Twitter those are a lot of peanuts. Don't use a stupid bar that annoys people (because it's intrusive and unwanted). Inject an add in between every five or ten tweets, like commercials do on TV or in the newspaper. Twitter is the source, so it's easy to implement it. You can even have trending, profiled or geolocated ads send to individual users as Twitter has all that data. Bang! There's some real money to be found.&lt;br /&gt;&lt;br /&gt;The added value for Twitter can also be found in it's eco-system, as "to tweet" is generally recognized as a verb. Take a look at &lt;a href="http://github.com/"&gt;GitHub&lt;/a&gt;'s business model! They've a free platform to share code with the world, but charge for private use. I am sure there's a lot of potential right there, as more and more people are so familiar with sending out a tweet, it can co-exists within companies as their own private twitter flow. Even more, for enterprizes like newsmedia it can also be a business model to have a "sticky" tweet to give them extra attention when major events occur (like we see now in Japan). Even give international aid services a "place" to show where people can contribute, but I suggest to make that a free service.&lt;br /&gt;&lt;br /&gt;One thing I do want to say to the folks over at Twitter is this: you have a huge user base and you might keep it for a very long time. But don't forget, it can all be over when someone comes up with the next best thing. You've provided a service that's been used to provide event coverage as it happened, crumbled governments in North Africa, witnessed thousands of reunions of people being at the same spot and so much more. Developers are building new tools on top of that for giving people an added experience on top of what you've originally set up. Closing the doors now on those developers is not just acting as a jerk, but is also a motivation for developers to come up with the next best thing a whole lot quicker.&lt;br /&gt;&lt;br /&gt;Let's talk and see how you can become again the hero you used to be.&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/kkfCMJX4VJc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/1598475297379303225/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2011/03/twitter-and-wrong-business-decisions.html#comment-form" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/1598475297379303225" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/1598475297379303225" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/kkfCMJX4VJc/twitter-and-wrong-business-decisions.html" title="Twitter and wrong business decisions" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><thr:total>3</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2011/03/twitter-and-wrong-business-decisions.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-350185812483052843</id><published>2011-02-23T14:31:00.000+01:00</published><updated>2011-02-23T14:31:14.560+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="bookreview" /><category scheme="http://www.blogger.com/atom/ns#" term="jquery" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><title type="text">Book review: CMS Design Using PHP and JQuery</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://www.packtpub.com/cms-design-using-php-and-jquery/book" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/-bc78kK9RFzg/TWULvsjEoaI/AAAAAAAACr4/wk_skdop028/s1600/CMS+Design+Using+PHP+and+JQuery.png" /&gt;&lt;/a&gt;&lt;/div&gt;A couple of weeks ago I got a request from &lt;a href="http://www.packtpub.com/"&gt;Packt Publishing&lt;/a&gt; to review the book "&lt;a href="https://www.packtpub.com/cms-design-using-php-and-jquery/book"&gt;CMS Design Using PHP and JQuery&lt;/a&gt;". I accepted the offer because we're using more and more &lt;a href="http://www.jquery.com/"&gt;JQuery&lt;/a&gt; in our applications these days and I thought this book would give me a better insight in how to best use &lt;a href="http://www.jquery.com/"&gt;JQuery&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;After receiving &lt;a href="https://www.packtpub.com/cms-design-using-php-and-jquery/book"&gt;the book&lt;/a&gt; I started reading it. But right from the start the author displayed bad practices and mis-use of PHP. Reading the book from front to back, the author &lt;a href="http://verens.com/"&gt;Kae Verens&lt;/a&gt; (&lt;a href="http://twitter.com/#%21/kae_verens"&gt;@kae_verens&lt;/a&gt;) has confronted me with bad use of variables (like $a, $b, $c), bad use of PHP structures and a complete wrong approach of using JavaScript, where JavaScript should enrich an application instead of incorporating business logic.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The good:&lt;/b&gt;&lt;br /&gt;It was nice to see &lt;a href="http://www.jquery.com/"&gt;JQuery&lt;/a&gt; has a variety of plugins to get things done, most of the plugins discussed in the book were developed by the author himself.&lt;br /&gt;I do like the way &lt;a href="http://verens.com/"&gt;Kae Verens&lt;/a&gt; (&lt;a href="http://twitter.com/#%21/kae_verens"&gt;@kae_verens&lt;/a&gt;) breaks up a CMS basic functionality and targets it one by one to describe it's functionality and purpose. Without the code samples, this book is a good guide for anyone who wants to build a home-grown CMS.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The bad:&lt;/b&gt;&lt;br /&gt;It's not bad, it's worse!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The ugly:&lt;/b&gt;&lt;br /&gt;As already stated in the introduction, this book is a complete contradiction to the things we try to promote. Clear separation of business logic and presentation layers (where JavaScript is another presentation layer) has gone out of the window in this book.&lt;br /&gt;&lt;br /&gt;Since the audience for this book are beginner to intermediate PHP developers, I can only curse at the author to be more responsible. Never use meaningless variables like $a, $b, $c!!!&lt;br /&gt;&lt;br /&gt;Filtering and Validation of received data was something I missed in the code.&lt;br /&gt;&lt;br /&gt;Another thing, if you use &lt;a href="http://php.net/spl"&gt;SPL&lt;/a&gt; functions like DirectoryIterator to traverse a directory on your filesystem, use the &lt;a href="http://php.net/spl"&gt;SPL&lt;/a&gt; methods that come with it (e.g. $dir-&amp;gt;isDot() to verify if it's a "." or "..").&lt;br /&gt;&lt;br /&gt;At the end of the book the author says this application only works on a *NIX platform. I think this statement reflects how this application is build: poor design.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Conclusion:&lt;/b&gt;&lt;br /&gt;After reading this book, I should advice &lt;a href="http://www.packtpub.com/"&gt;Packt Publishing&lt;/a&gt; to put a sticker on it "Warning: explicit content - bad coding practices" as this book is dangerous in the hands of novice PHP developers.&lt;br /&gt;&lt;br /&gt;I'm very sorry for the authors and &lt;a href="http://www.packtpub.com/"&gt;Packt Publishing&lt;/a&gt; as the topic of this book is very promising and could be a good read for everyone, but at this point with this type of coding I strongly advice people &lt;b&gt;NOT&lt;/b&gt; to buy this book. Maybe a next edition…&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/yIye9FwlodU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/350185812483052843/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2011/02/book-review-cms-design-using-php-and.html#comment-form" title="9 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/350185812483052843" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/350185812483052843" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/yIye9FwlodU/book-review-cms-design-using-php-and.html" title="Book review: CMS Design Using PHP and JQuery" /><author><name>Michelangelo van Dam</name><uri>https://plus.google.com/115104112579818801836</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-VRBQCDEOxBU/AAAAAAAAAAI/AAAAAAAADp4/5gTxVjn51yw/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-bc78kK9RFzg/TWULvsjEoaI/AAAAAAAACr4/wk_skdop028/s72-c/CMS+Design+Using+PHP+and+JQuery.png" height="72" width="72" /><thr:total>9</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://www.dragonbe.com/2011/02/book-review-cms-design-using-php-and.html</feedburner:origLink></entry></feed>
