<?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: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>2012-05-21T07:50:21.529+02:00</updated><category term="Eclipse PHP IDE" /><category term="edegem" /><category term="zf" /><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="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="facebook" /><category term="seven things" /><category term="feweb" /><category term="Wendy's" /><category term="bru" /><category term="uml scheme" /><category term="handhelds" /><category term="java" /><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="zendcon09" /><category term="graphical designer" /><category term="Zend Yellow Pages" /><category term="2007" /><category term="international" /><category term="Zend" /><category term="dutch" /><category term="anysurfer" /><category term="Zend Studio" /><category term="testfest" /><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="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="meeting" /><category term="felix de vliegher" /><category term="mash-ups" /><category term="ewt08" /><category term="ausy" /><category term="jquery" /><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="Mission" /><category term="ipod" /><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="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="Andries Seutens" /><category term="jarno brandt" /><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="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="calevans" /><category term="sfo" /><category term="keith casey" /><category term="ibuildings" /><category term="pfz" /><category term="how-to" /><category term="training courses" /><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://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/s512-c/photo.jpg" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>193</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-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;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-8690112674578374608?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/H5DZ6gJBB3yJhgzHMVabik0P8Eg/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/H5DZ6gJBB3yJhgzHMVabik0P8Eg/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/H5DZ6gJBB3yJhgzHMVabik0P8Eg/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/H5DZ6gJBB3yJhgzHMVabik0P8Eg/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&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="2 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://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/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>2</thr:total><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;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-1265492212204894604?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/SgurwUvljMHlG-l0BPPUegTTR0k/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/SgurwUvljMHlG-l0BPPUegTTR0k/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/SgurwUvljMHlG-l0BPPUegTTR0k/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/SgurwUvljMHlG-l0BPPUegTTR0k/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&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="1 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://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://lh6.googleusercontent.com/-KplgwR3a25A/TupASDK7F7I/AAAAAAAACzg/qtQdeCbrl1A/s72-c/one%252520to%252520cloud.png" height="72" width="72" /><thr:total>1</thr:total><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;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-6208232362701973063?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/ihZiL_VljzB6vSvd1yzrJsY8lQY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ihZiL_VljzB6vSvd1yzrJsY8lQY/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/ihZiL_VljzB6vSvd1yzrJsY8lQY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ihZiL_VljzB6vSvd1yzrJsY8lQY/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&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://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://lh6.googleusercontent.com/-Tfojy7ybCK0/TuUjssiG28I/AAAAAAAACyk/4-FJI5v21g0/s72-c/in_house_costs_by_josh_holmes.png" height="72" width="72" /><thr:total>0</thr:total><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;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-4062473648337560478?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/M__MdfZNc0H43s9fCsqgNSrinSk/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/M__MdfZNc0H43s9fCsqgNSrinSk/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/M__MdfZNc0H43s9fCsqgNSrinSk/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/M__MdfZNc0H43s9fCsqgNSrinSk/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&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://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><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;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-6449759132514720607?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/ggdYbGX2QYIpEW8sAb25DKbK54Y/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ggdYbGX2QYIpEW8sAb25DKbK54Y/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/ggdYbGX2QYIpEW8sAb25DKbK54Y/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ggdYbGX2QYIpEW8sAb25DKbK54Y/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&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://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><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;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-4714988347243590658?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/08QmajVUK36FGWYe1LQja0S3HkE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/08QmajVUK36FGWYe1LQja0S3HkE/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/08QmajVUK36FGWYe1LQja0S3HkE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/08QmajVUK36FGWYe1LQja0S3HkE/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&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="2 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://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/s512-c/photo.jpg" /></author><thr:total>2</thr:total><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;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-849480286855330673?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/pxQia_ffjMo8qHVebaj6Evtb2tw/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/pxQia_ffjMo8qHVebaj6Evtb2tw/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/pxQia_ffjMo8qHVebaj6Evtb2tw/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/pxQia_ffjMo8qHVebaj6Evtb2tw/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&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://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><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;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-6691411242144597783?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/HCPCAfbEdakUZkmdrE3mE3WHsmI/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/HCPCAfbEdakUZkmdrE3mE3WHsmI/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/HCPCAfbEdakUZkmdrE3mE3WHsmI/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/HCPCAfbEdakUZkmdrE3mE3WHsmI/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&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://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/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><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;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-4297124943059406814?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/eA9JcOIj7kjPBVMa2T-6kb3r-no/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/eA9JcOIj7kjPBVMa2T-6kb3r-no/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/eA9JcOIj7kjPBVMa2T-6kb3r-no/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/eA9JcOIj7kjPBVMa2T-6kb3r-no/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&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://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/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><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;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-3184454531336041882?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/cJCkVaycG6Oohd5bQrv9wMlXFlo/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/cJCkVaycG6Oohd5bQrv9wMlXFlo/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/cJCkVaycG6Oohd5bQrv9wMlXFlo/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/cJCkVaycG6Oohd5bQrv9wMlXFlo/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&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://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/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><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;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-8258757649478547706?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Gx46iGL_79V0CP6Ozc-8z6c4HGY/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Gx46iGL_79V0CP6Ozc-8z6c4HGY/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Gx46iGL_79V0CP6Ozc-8z6c4HGY/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Gx46iGL_79V0CP6Ozc-8z6c4HGY/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&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://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/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><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;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-8035120336788065724?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/dZIZ8SBUl07cy1cV1ruKUIDfluE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/dZIZ8SBUl07cy1cV1ruKUIDfluE/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/dZIZ8SBUl07cy1cV1ruKUIDfluE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/dZIZ8SBUl07cy1cV1ruKUIDfluE/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&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="13 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://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/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>13</thr:total><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;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-3619693195929198981?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Py53Chfsdbg5ILKWRfKvndTBJ1g/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Py53Chfsdbg5ILKWRfKvndTBJ1g/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Py53Chfsdbg5ILKWRfKvndTBJ1g/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Py53Chfsdbg5ILKWRfKvndTBJ1g/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&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://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/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><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;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-2257045993992571256?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/C_WrytuBvQwRWC0jIRCItUdOdNU/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/C_WrytuBvQwRWC0jIRCItUdOdNU/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/C_WrytuBvQwRWC0jIRCItUdOdNU/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/C_WrytuBvQwRWC0jIRCItUdOdNU/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&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="7 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://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/s512-c/photo.jpg" /></author><thr:total>7</thr:total><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;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-263592044342837157?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/OkGggorVw8ZUdrPn15l5m_YEEzE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/OkGggorVw8ZUdrPn15l5m_YEEzE/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/OkGggorVw8ZUdrPn15l5m_YEEzE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/OkGggorVw8ZUdrPn15l5m_YEEzE/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&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://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><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;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-1234462302801257363?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/q7eH5-j40wJQ2c5rDsKAEQlKIg0/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/q7eH5-j40wJQ2c5rDsKAEQlKIg0/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/q7eH5-j40wJQ2c5rDsKAEQlKIg0/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/q7eH5-j40wJQ2c5rDsKAEQlKIg0/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&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://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/s512-c/photo.jpg" /></author><thr:total>2</thr:total><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;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-1598475297379303225?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/5HMrLkKHU3Y_pQttYreyAhcfUlA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/5HMrLkKHU3Y_pQttYreyAhcfUlA/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/5HMrLkKHU3Y_pQttYreyAhcfUlA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/5HMrLkKHU3Y_pQttYreyAhcfUlA/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&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://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/s512-c/photo.jpg" /></author><thr:total>3</thr:total><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;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-350185812483052843?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/unC6srbsL4kwKJ9bf6GUYP36W6Q/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/unC6srbsL4kwKJ9bf6GUYP36W6Q/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/unC6srbsL4kwKJ9bf6GUYP36W6Q/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/unC6srbsL4kwKJ9bf6GUYP36W6Q/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&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="8 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://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/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>8</thr:total><feedburner:origLink>http://www.dragonbe.com/2011/02/book-review-cms-design-using-php-and.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-6855769807097195282</id><published>2011-02-19T20:14:00.000+01:00</published><updated>2011-02-19T20:14:11.761+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="azure" /><category scheme="http://www.blogger.com/atom/ns#" term="phpazurecontest" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><category scheme="http://www.blogger.com/atom/ns#" term="zend framework" /><title type="text">PHP on Azure and you in Vegas</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.phpazurecontest.com/wp-content/uploads/2011/01/azurecontestthumb-213x300.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="200" src="http://www.phpazurecontest.com/wp-content/uploads/2011/01/azurecontestthumb-213x300.png" width="141" /&gt;&lt;/a&gt;&lt;/div&gt;If you're developing apps in &lt;a href="http://php.net/"&gt;PHP&lt;/a&gt; and you want a challenge that will get you some places, be sure to check out the &lt;a href="http://phpazurecontest.com/"&gt;PHP on Azure&lt;/a&gt; contest. Build an app with PHP, deploy it to Windows Azure and participate in a contest with a killer prize: an all-in trip to Las Vegas!&lt;br /&gt;&lt;br /&gt;The rules are simple:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;your app has to be written in PHP&lt;/li&gt;&lt;li&gt;preferably own development app&lt;/li&gt;&lt;li&gt;use as much as Windows Azure services&lt;/li&gt;&lt;li&gt;blog about overcoming this challenge (good/bad/ugly) &lt;/li&gt;&lt;/ul&gt;A jury will review your app and give you points on implementation, challenge and usage of Windows Azure technologies. But also on how you documented the process of deploying your app to Windows Azure.&lt;br /&gt;&lt;br /&gt;Register before February 28, 2011. The contest itself runs from February 1, 2011 until May 15, 2011. If you register quickly, you can get attend a free Windows Azure training course given by Maarten "Mr. Azure" Balliauw on February 22, 2011.&lt;br /&gt;&lt;br /&gt;Show the world your skills and participate. Full details of the contest can be found on &lt;a href="http://phpazurecontest.com/"&gt;http://phpazurecontest.com&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;If you're unsure how to start, I'm working on an example application to be deployed anywhere, including Windows Azure.&lt;br /&gt;&lt;ul&gt;&lt;li&gt; Get the sources: &lt;a href="http://github.com/DragonBe/wingz"&gt;http://github.com/DragonBe/wingz&lt;/a&gt;&lt;/li&gt;&lt;li&gt;See it run on Linux: &lt;a href="http://wingz.cc/"&gt;http://wingz.cc&lt;/a&gt;&lt;/li&gt;&lt;li&gt;See it run on Windows 2008 Server with IIS 7.5: &lt;a href="http://wingz.be/"&gt;http://wingz.be&lt;/a&gt;&lt;/li&gt;&lt;li&gt;See it run on Windows Azure: Updating…&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-6855769807097195282?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/UPes09Wj_3Vp5AynJF8AH32VoXI/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/UPes09Wj_3Vp5AynJF8AH32VoXI/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/UPes09Wj_3Vp5AynJF8AH32VoXI/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/UPes09Wj_3Vp5AynJF8AH32VoXI/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/F83g7h6h--I" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/6855769807097195282/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2011/02/php-on-azure-and-you-in-vegas.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/6855769807097195282" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/6855769807097195282" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/F83g7h6h--I/php-on-azure-and-you-in-vegas.html" title="PHP on Azure and you in Vegas" /><author><name>Michelangelo van Dam</name><uri>https://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.dragonbe.com/2011/02/php-on-azure-and-you-in-vegas.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-776366791355020872</id><published>2011-02-04T23:41:00.000+01:00</published><updated>2011-02-04T23:41:43.120+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="agile" /><category scheme="http://www.blogger.com/atom/ns#" term="bookreview" /><category scheme="http://www.blogger.com/atom/ns#" term="samurai" /><title type="text">Book review: The Agile Samurai</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://shared1.pragprog.com/images/covers/original/jtrap.jpg" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0" height="135" width="112" src="http://shared1.pragprog.com/images/covers/original/jtrap.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;About two months ago I purchased "&lt;a href="http://www.pragprog.com/titles/jtrap/the-agile-samurai"&gt;The Agile Samurai&lt;/a&gt;" by &lt;a href="http://rasmusson.wordpress.com/"&gt;Jonathan Rasmusson&lt;/a&gt; from &lt;a href="http://www.pragprog.com"&gt;Pragmatic Bookshelf&lt;/a&gt; after seeing his presentation "&lt;a href="http://agilewarrior.files.wordpress.com/2009/05/agile-in-a-nutshell.pdf"&gt;Agile in a nutshell&lt;/a&gt;". I was already impressed by how he presented the agile scrum management cycle, as he gave already some good hints and tips on how to implement scrum management processes in ongoing projects.&lt;br /&gt;&lt;br /&gt;But it was not until I read the book that I discovered Jonathan is a true expert in this field. With "&lt;a href="http://www.pragprog.com/titles/jtrap/the-agile-samurai"&gt;The Agile Samurai&lt;/a&gt;" Jonathan explains in simple words the idea behind scrum, what steps to take in starting scrum management giving pointers on how to deal with developers, managers and customers and how to get everyone involved.&lt;br /&gt;&lt;br /&gt;He also gives a lot of hints with his "Master Sensei and the aspiring warrior" where he summarizes each chapter with a dialogue between a student and his master. Throughout the book he also reflects on his own experiences describing a circumstance and how he personally turned a bad situation into a success story.&lt;br /&gt;&lt;br /&gt;The book is filled with pretty graphics that not only describes a given situation with one image, but also challenges the reader in thinking further ahead. Since agile processes aren't new, Jonathan refers in his book a lot to other books that go deeper on a given topic Jonathan only touches briefly.&lt;br /&gt;&lt;br /&gt;Besides going into detail on managing projects, he also describes topics like "technical depth" (a &lt;a href="http://www.slideshare.net/mobile/enaramore/technical-debt-phpbenelux"&gt;topic very well explained&lt;/a&gt; by &lt;a href="http://naramore.net/blog/"&gt;Elizabeth Naramore&lt;/a&gt; at several PHP conferences), test driven development and continuous integration.&lt;br /&gt;&lt;br /&gt;All in all, a must read for anyone who's already familiar with scrum project management processes and want to learn tricks how to get management and customers along for the agile ride. But it's an excellent guide for people who want to start managing projects in an agile way. No matter if you're a developer, a lead developer, a manager or a customer… this book is the best reference for optimizing the way a project can be turned into a success story or dropped before too much time, money and resources are spilled. Become &lt;a href="http://www.pragprog.com/titles/jtrap/the-agile-samurai"&gt;The Agile Samurai&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-776366791355020872?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/dYbc1ahaBTPhBV_N6J_BF_fdR_0/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/dYbc1ahaBTPhBV_N6J_BF_fdR_0/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/dYbc1ahaBTPhBV_N6J_BF_fdR_0/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/dYbc1ahaBTPhBV_N6J_BF_fdR_0/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/INxVRrh0EZc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/776366791355020872/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2011/02/book-review-agile-samurai.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/776366791355020872" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/776366791355020872" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/INxVRrh0EZc/book-review-agile-samurai.html" title="Book review: The Agile Samurai" /><author><name>Michelangelo van Dam</name><uri>https://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.dragonbe.com/2011/02/book-review-agile-samurai.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-8985732859244428750</id><published>2011-01-31T12:29:00.000+01:00</published><updated>2011-01-31T12:29:07.195+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="phpbenelux" /><category scheme="http://www.blogger.com/atom/ns#" term="phpbnl11" /><title type="text">Post PHPBenelux Conference 2011 review</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_HhRzjL1_W1c/S2mx6u7RQoI/AAAAAAAACog/JziIvZyXyWc/s1600/phpbenelux_logo.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_HhRzjL1_W1c/S2mx6u7RQoI/AAAAAAAACog/JziIvZyXyWc/s1600/phpbenelux_logo.png" /&gt;&lt;/a&gt;&lt;/div&gt;Last weekend Antwerp was buzzing PHP ! &lt;a href="http://www.phpbenelux.eu/?utm_source=post%2Bconference%2Bblog%2Bpost&amp;amp;utm_medium=blog&amp;amp;utm_term=phpbnl11&amp;amp;utm_content=phpbenelux_sponsorship&amp;amp;utm_campaign=conference%2Breview"&gt;PHPBenelux&lt;/a&gt; organized a two-day PHP conference at &lt;a href="http://www.terelst.be/?utm_source=post%2Bconference%2Bblog%2Bpost&amp;amp;utm_medium=blog&amp;amp;utm_term=phpbnl11&amp;amp;utm_content=phpbenelux_sponsorship&amp;amp;utm_campaign=conference%2Breview"&gt;Ter Elst&lt;/a&gt; in Antwerp, Belgium.&lt;br /&gt;&lt;br /&gt;The conference was a blast! Over 250 attendees coming from within the Benelux and out, 29 national and international speakers, 18 sponsors and a superb staff of both the &lt;a href="http://www.phpbenelux.eu/?utm_source=post%2Bconference%2Bblog%2Bpost&amp;amp;utm_medium=blog&amp;amp;utm_term=phpbnl11&amp;amp;utm_content=phpbenelux_sponsorship&amp;amp;utm_campaign=conference%2Breview"&gt;PHPBenelux&lt;/a&gt; organization as well from the venue &lt;a href="http://www.terelst.be/?utm_source=post%2Bconference%2Bblog%2Bpost&amp;amp;utm_medium=blog&amp;amp;utm_term=phpbnl11&amp;amp;utm_content=phpbenelux_sponsorship&amp;amp;utm_campaign=conference%2Breview"&gt;Ter Elst&lt;/a&gt; itself.&lt;br /&gt;&lt;br /&gt;Our success wasn't possible with the support of our sponsors (&lt;a href="http://www.microsoft.com/?utm_source=post%2Bconference%2Bblog%2Bpost&amp;amp;utm_medium=blog&amp;amp;utm_term=phpbnl11&amp;amp;utm_content=phpbenelux_sponsorship&amp;amp;utm_campaign=conference%2Breview"&gt;Microsoft&lt;/a&gt;, &lt;a href="http://www.ibuildings.com/?utm_source=post%2Bconference%2Bblog%2Bpost&amp;amp;utm_medium=blog&amp;amp;utm_term=phpbnl11&amp;amp;utm_content=phpbenelux_sponsorship&amp;amp;utm_campaign=conference%2Breview"&gt;Ibuildings&lt;/a&gt;, &lt;a href="http://www.combell.com/?utm_source=post%2Bconference%2Bblog%2Bpost&amp;amp;utm_medium=blog&amp;amp;utm_term=phpbnl11&amp;amp;utm_content=phpbenelux_sponsorship&amp;amp;utm_campaign=conference%2Breview"&gt;Combell&lt;/a&gt;, &lt;a href="http://www.phpro.be/?utm_source=post%2Bconference%2Bblog%2Bpost&amp;amp;utm_medium=blog&amp;amp;utm_term=phpbnl11&amp;amp;utm_content=phpbenelux_sponsorship&amp;amp;utm_campaign=conference%2Breview"&gt;PHPro&lt;/a&gt;, &lt;a href="http://www.zend.com/?utm_source=post%2Bconference%2Bblog%2Bpost&amp;amp;utm_medium=blog&amp;amp;utm_term=phpbnl11&amp;amp;utm_content=phpbenelux_sponsorship&amp;amp;utm_campaign=conference%2Breview"&gt;Zend&lt;/a&gt;, &lt;a href="http://www.facebook.com/?utm_source=post%2Bconference%2Bblog%2Bpost&amp;amp;utm_medium=blog&amp;amp;utm_term=phpbnl11&amp;amp;utm_content=phpbenelux_sponsorship&amp;amp;utm_campaign=conference%2Breview"&gt;Facebook&lt;/a&gt;, &lt;a href="http://www.amazium.com/?utm_source=post%2Bconference%2Bblog%2Bpost&amp;amp;utm_medium=blog&amp;amp;utm_term=phpbnl11&amp;amp;utm_content=phpbenelux_sponsorship&amp;amp;utm_campaign=conference%2Breview"&gt;Amazium&lt;/a&gt;, &lt;a href="http://www.github.com/?utm_source=post%2Bconference%2Bblog%2Bpost&amp;amp;utm_medium=blog&amp;amp;utm_term=phpbnl11&amp;amp;utm_content=phpbenelux_sponsorship&amp;amp;utm_campaign=conference%2Breview"&gt;github&lt;/a&gt;, &lt;a href="http://www.telenet.be/?utm_source=post%2Bconference%2Bblog%2Bpost&amp;amp;utm_medium=blog&amp;amp;utm_term=phpbnl11&amp;amp;utm_content=phpbenelux_sponsorship&amp;amp;utm_campaign=conference%2Breview"&gt;Telenet&lt;/a&gt;, &lt;a href="http://www.oracle.com/?utm_source=post%2Bconference%2Bblog%2Bpost&amp;amp;utm_medium=blog&amp;amp;utm_term=phpbnl11&amp;amp;utm_content=phpbenelux_sponsorship&amp;amp;utm_campaign=conference%2Breview"&gt;Oracle&lt;/a&gt;, &lt;a href="http://www.qafoo.com/?utm_source=post%2Bconference%2Bblog%2Bpost&amp;amp;utm_medium=blog&amp;amp;utm_term=phpbnl11&amp;amp;utm_content=phpbenelux_sponsorship&amp;amp;utm_campaign=conference%2Breview"&gt;qafoo&lt;/a&gt;, &lt;a href="http://www.cloudcontrol.com/?utm_source=post%2Bconference%2Bblog%2Bpost&amp;amp;utm_medium=blog&amp;amp;utm_term=phpbnl11&amp;amp;utm_content=phpbenelux_sponsorship&amp;amp;utm_campaign=conference%2Breview"&gt;CloudControl&lt;/a&gt;, &lt;a href="http://www.daycamp4developers.com/?utm_source=post%2Bconference%2Bblog%2Bpost&amp;amp;utm_medium=blog&amp;amp;utm_term=phpbnl11&amp;amp;utm_content=phpbenelux_sponsorship&amp;amp;utm_campaign=conference%2Breview"&gt;DayCamp4Developers&lt;/a&gt;, &lt;a href="http://www.enrise.com/?utm_source=post%2Bconference%2Bblog%2Bpost&amp;amp;utm_medium=blog&amp;amp;utm_term=phpbnl11&amp;amp;utm_content=phpbenelux_sponsorship&amp;amp;utm_campaign=conference%2Breview"&gt;enrise&lt;/a&gt;, &lt;a href="http://www.in2it.be/?utm_source=post%2Bconference%2Bblog%2Bpost&amp;amp;utm_medium=blog&amp;amp;utm_term=phpbnl11&amp;amp;utm_content=phpbenelux_sponsorship&amp;amp;utm_campaign=conference%2Breview"&gt;in2it&lt;/a&gt;, &lt;a href="http://www.servergrove.com/?utm_source=post%2Bconference%2Bblog%2Bpost&amp;amp;utm_medium=blog&amp;amp;utm_term=phpbnl11&amp;amp;utm_content=phpbenelux_sponsorship&amp;amp;utm_campaign=conference%2Breview"&gt;ServerGrove&lt;/a&gt;, &lt;a href="http://www.webclusive.com/?utm_source=post%2Bconference%2Bblog%2Bpost&amp;amp;utm_medium=blog&amp;amp;utm_term=phpbnl11&amp;amp;utm_content=phpbenelux_sponsorship&amp;amp;utm_campaign=conference%2Breview"&gt;WEBclusive&lt;/a&gt; and &lt;a href="http://www.yappa.be/?utm_source=post%2Bconference%2Bblog%2Bpost&amp;amp;utm_medium=blog&amp;amp;utm_term=phpbnl11&amp;amp;utm_content=phpbenelux_sponsorship&amp;amp;utm_campaign=conference%2Breview"&gt;yappa&lt;/a&gt;) who chipped in a lot so we were able to keep our entrance tickets as low as possible. If you enjoyed the conference, be sure to visit their website and send them a thank you note. They deserve it.&lt;br /&gt;&lt;br /&gt;I would also would express my sincere gratitude to the people behind &lt;a href="http://www.phpbenelux.eu/?utm_source=post%2Bconference%2Bblog%2Bpost&amp;amp;utm_medium=blog&amp;amp;utm_term=phpbnl11&amp;amp;utm_content=phpbenelux_sponsorship&amp;amp;utm_campaign=conference%2Breview"&gt;PHPBenelux&lt;/a&gt;, the team who has given their full 100% in getting this conference going. In my opinion, they're the heros: Stefan Koopmanschap (&lt;a href="http://twitter.com/skoop"&gt;@skoop&lt;/a&gt;), Thijs Feryn (&lt;a href="http://twitter.com/thijsferyn"&gt;@ThijsFeryn&lt;/a&gt;), Martin De Keijzer (&lt;a href="http://twitter.com/martin1982"&gt;@Martin1982&lt;/a&gt;), Paul Borgermans (&lt;a href="http://twitter.com/paulborgermans"&gt;@paulborgermans&lt;/a&gt;), Gerrit "Remi" te Sligte (&lt;a href="http://twitter.com/remi_woler"&gt;@Remi_Woler&lt;/a&gt;) and last but not least Kana Yeh (&lt;a href="http://twitter.com/missyeh"&gt;@MissYeh&lt;/a&gt;). And I would like to thank the volunteers to step up and rise to the occasion.&lt;br /&gt;&lt;br /&gt;Thank you speakers for giving such good talks! The audience loved it and we loved you having with us. Don't forget to submit for next year!&lt;br /&gt;&lt;br /&gt;And I would like to thank the audience! You guys were great and the feedback we got from you all is worth all the effort. Be sure to look us up next year for the &lt;a href="http://conference.phpbenelux.eu/"&gt;PHPBenelux Conference 2012&lt;/a&gt;!.&lt;br /&gt;&lt;br /&gt;A couple of the responses:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://twitter.com/ElizabethN/statuses/31819994392170496" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="190" src="http://3.bp.blogspot.com/_HhRzjL1_W1c/TUaaWBZhgUI/AAAAAAAACrk/FKyI-WmoYMs/s320/elizabethn_status_31819994392170496.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://twitter.com/pmjones/statuses/31812555349426177" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="190" src="http://2.bp.blogspot.com/_HhRzjL1_W1c/TUabDQQ7hBI/AAAAAAAACro/Dt_NqWA-CZE/s320/pmjones_statuses_31812555349426177.png" width="320" /&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://twitter.com/RowdyRabouw/statuses/31767231922704384" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="187" src="http://4.bp.blogspot.com/_HhRzjL1_W1c/TUabgvMaJ7I/AAAAAAAACrs/_bULYbIoyFE/s320/rowdyrabouw_statuses_31767231922704384.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&amp;nbsp;More tweets like this can be found on &lt;a href="http://twitter.com/"&gt;twitter.com&lt;/a&gt;, searching for &lt;a href="http://search.twitter.com/search?q=%23phpbnl11"&gt;#phpbnl11&lt;/a&gt;. Also check out some statistical information of the event at &lt;a href="http://www.slurp140.com/phpbnl11"&gt;TwitterSlurp 140&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-8985732859244428750?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/cvBdi67Io2O0ZVtr5iw7URsOrto/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/cvBdi67Io2O0ZVtr5iw7URsOrto/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/cvBdi67Io2O0ZVtr5iw7URsOrto/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/cvBdi67Io2O0ZVtr5iw7URsOrto/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/iXLiPD3tIIc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/8985732859244428750/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2011/01/post-phpbenelux-conference-2011-review.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/8985732859244428750" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/8985732859244428750" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/iXLiPD3tIIc/post-phpbenelux-conference-2011-review.html" title="Post PHPBenelux Conference 2011 review" /><author><name>Michelangelo van Dam</name><uri>https://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_HhRzjL1_W1c/S2mx6u7RQoI/AAAAAAAACog/JziIvZyXyWc/s72-c/phpbenelux_logo.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.dragonbe.com/2011/01/post-phpbenelux-conference-2011-review.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-7343138323928558535</id><published>2011-01-21T12:07:00.000+01:00</published><updated>2011-01-21T12:07:26.808+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="tek11" /><category scheme="http://www.blogger.com/atom/ns#" term="confoo11" /><category scheme="http://www.blogger.com/atom/ns#" term="unit testing" /><category scheme="http://www.blogger.com/atom/ns#" term="webinar" /><category scheme="http://www.blogger.com/atom/ns#" term="Zend" /><category scheme="http://www.blogger.com/atom/ns#" term="zend framework" /><title type="text">Unit testing after Zend Framework 1.8 webinar</title><content type="html">&lt;a href="http://www.zend.com/en/resources/webinars/?src=dragonbe.com" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://static.zend.com/topics/resources-page-webianrs-image.jpg" /&gt;&lt;/a&gt;Just a quick not for those of you who missed my &lt;a href="http://www.zend.com/en/resources/webinars/framework?src=dragonbe.com"&gt;Zend webinar&lt;/a&gt; (&lt;a href="http://twitter.com/WebinarsAtZend"&gt;@WebinarsAtZend&lt;/a&gt;) on "&lt;a href="http://www.slideshare.net/DragonBe/unit-testing-after-zf-18"&gt;Unit Testing after Zend Framwork 1.8&lt;/a&gt;" last Wednesday. You can view the whole webinar on the &lt;a href="http://www.zend.com/en/resources/webinars/framework?src=dragonbe.com"&gt;Zend website&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I want to thank &lt;a href="http://www.zend.com/"&gt;Zend Technologies, Inc.&lt;/a&gt; for giving me the opportunity to present and I wish to express my sincere gratitude to all attendees (about 150) for enduring me the whole hour. Your feedback on &lt;a href="http://twitter.com/DragonBe"&gt;twitter&lt;/a&gt; and &lt;a href="http://facebook.com/DragonBe"&gt;facebook&lt;/a&gt; was awesome.&lt;br /&gt;&lt;br /&gt;If you have a &lt;a href="http://php.net/"&gt;php&lt;/a&gt; topic and you want to do a webinar, go to &lt;a href="http://www.zend.com/en/community/call-for-webinars"&gt;Zend's Call for Webinar page&lt;/a&gt;. You help the community and it's a very good reference in your resume! So, &lt;a href="http://www.zend.com/en/community/call-for-webinars"&gt;check it out&lt;/a&gt;!&lt;br /&gt;&lt;br /&gt;If you want to attend one of my life appearances, see me at:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://confoo.ca/"&gt;ConFoo&lt;/a&gt;: "Write PHP, deploy anywhere" and "Improving QA on php projects"&lt;/li&gt;&lt;li&gt;&lt;a href="http://tek.phparch.com/"&gt;TEK11&lt;/a&gt;: "Unit testing Zend Framework", "Write PHP, deploy anywhere" and "Improving QA on php projects"&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-7343138323928558535?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Ios6xsjGnZ0Txbc7MYMnVsJWJgo/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Ios6xsjGnZ0Txbc7MYMnVsJWJgo/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Ios6xsjGnZ0Txbc7MYMnVsJWJgo/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Ios6xsjGnZ0Txbc7MYMnVsJWJgo/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/CCKui6T4SmE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/7343138323928558535/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2011/01/unit-testing-after-zend-framework-18.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/7343138323928558535" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/7343138323928558535" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/CCKui6T4SmE/unit-testing-after-zend-framework-18.html" title="Unit testing after Zend Framework 1.8 webinar" /><author><name>Michelangelo van Dam</name><uri>https://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/s512-c/photo.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.dragonbe.com/2011/01/unit-testing-after-zend-framework-18.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-8226722712752168448</id><published>2010-12-22T01:24:00.000+01:00</published><updated>2010-12-22T01:24:05.315+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="recruitment" /><category scheme="http://www.blogger.com/atom/ns#" term="job" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><title type="text">Job hunting 101</title><content type="html">In this day and age, everyone uses the internet for finding new jobs or to find a new employee, but more and more I sense that it's all turning into one big mess.&lt;br /&gt;&lt;br /&gt;This blog post is devoted to all those people contacting me because they have the best job position. Most hits I get through my online resume at &lt;a href="http://michelangelovandam.com/"&gt;michelangelovandam.com&lt;/a&gt; or my &lt;a href="http://www.linkedin.com/in/michelangelovandam"&gt;linkedin profile&lt;/a&gt;. I'm not complaining about the fact that I got contacted this way, but I just mention some points of concern that might be useful for recruiters to at least get me interested in what they have to offer.&lt;br /&gt;&lt;br /&gt;I mean "&lt;b&gt;SR. Level PHP Web Developers needed for DIRECT HIRE position&lt;/b&gt;" is not the subject that might appeal me. I've got a great job and even if I wanted to leave it would take me at least a couple of months to be actually gone, so direct hire is not really something that attracts me.&lt;br /&gt;&lt;br /&gt;Another subject entered my exploding mailbox read "&lt;b&gt;Senior Java architect/analist-programmeur&lt;/b&gt;". I mean Java !?! The keyword matching tool you're using should know the difference between Java and PHP. Are you taking any time to review my profile after it popped-up in the keyword matching resultlist ? Apparently you didn't, so why should I bother ?&lt;br /&gt;&lt;br /&gt;Whenever there's a big company opening up a position for a senior PHP developer, don't think you're the first recruiter contacting me. On average I get 20 to 50 phone calls and over 100 mails on the day the position is send out. So, be upfront and don't try to disguise the name of your client because I already know who it is (I got those same details you have). And if you send me the job position, don't just copy/paste it from the mail you received, but give it your own business look and feel. Why should I be interested in something I've received already 100-fold ?&lt;br /&gt;&lt;br /&gt;I know it's a tough market out there, lot's of competition and so many technologies and skills to keep track off. I know, but it is &lt;b&gt;your business&lt;/b&gt;! You decided to dive right into the job chaos, so don't cry if you have to do a bit of work. Do you think we just turn on the computer and it writes code itself ? You do your job, I do mine.&lt;br /&gt;&lt;br /&gt;I'm not all against recruiters, because I do get a lot of PHP developers asking me about a good position and it's always nice to help them out finding a match to his desires. Having a selected group of recruiters in my contact list is a great value for the developers and the recruiters.&lt;br /&gt;&lt;br /&gt;And through this process I learned a couple of things:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;php developers like to work&lt;/li&gt;&lt;li&gt;they don't like to be stuck in traffic on their way to work (lost time vs. work)&lt;/li&gt;&lt;li&gt;they like pet projects / knowledge sharing&lt;/li&gt;&lt;li&gt;conferences, user group meetings, socializing&lt;/li&gt;&lt;/ul&gt;So this basically translates into the following:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;give me a project and I'll get it done&lt;/li&gt;&lt;li&gt;let me do this from my lazy chair at home&lt;/li&gt;&lt;li&gt;I wanna work on this open-source project that's been used in the company&lt;/li&gt;&lt;li&gt;once a month I like to have a competence meeting on PHP or company technologies&lt;/li&gt;&lt;li&gt;there's a conference almost every month somewhere, I'd like a ticket to one of them&lt;/li&gt;&lt;/ul&gt;Strange folks these PHP developers, aren't they. They don't ask much, a little more than those internet hippies from the early days "will work for bandwidth", but still not that much.&lt;br /&gt;&lt;br /&gt;If you're in the recruiting business, see if you can contact your client and see if you can arrange a deal where you convert all the "usual" stuff into something special. Also, if you get a lot of PHP jobs on your desk, get to know the end-user, being the PHP developer. See if there's a user group in your country/area and check them out. Touch base with them and get to know the community. Learn from them as they will gladly tell you what a "dream job" should be in order for them to switch jobs. Why not buy drinks at the end of the user group meeting ? *hint*&lt;br /&gt;&lt;br /&gt;You can find user groups easily on google "php user group &amp;lt;location&amp;gt;" or in my example "&lt;a href="http://www.google.com/search?q=php+user+group+belgium"&gt;php user group belgium&lt;/a&gt;".&lt;br /&gt;&lt;br /&gt;And if you're out looking for a new PHP job, go check out the community itself. Most of us have a list of companies with PHP job vacancies or we have a shortlist of trustworthy, understanding recruiters that have touched base with the PHP community and won't let you down once you sign up.&lt;br /&gt;&lt;br /&gt;Well, glad I got this out now. Between starting this post and finsihing it (about an hour) I got again 24 requests from different recruiting agencies for the same job. Just making my point.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-8226722712752168448?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/FDjwX8mNvEsIJ7MBkvJKAiJUHNs/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/FDjwX8mNvEsIJ7MBkvJKAiJUHNs/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/FDjwX8mNvEsIJ7MBkvJKAiJUHNs/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/FDjwX8mNvEsIJ7MBkvJKAiJUHNs/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/el0gINABP70" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/8226722712752168448/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2010/12/job-hunting-101.html#comment-form" title="10 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/8226722712752168448" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/8226722712752168448" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/el0gINABP70/job-hunting-101.html" title="Job hunting 101" /><author><name>Michelangelo van Dam</name><uri>https://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/s512-c/photo.jpg" /></author><thr:total>10</thr:total><feedburner:origLink>http://www.dragonbe.com/2010/12/job-hunting-101.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-6514727335062836291</id><published>2010-11-08T19:06:00.000+01:00</published><updated>2010-11-08T19:06:10.143+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="zendcon10" /><category scheme="http://www.blogger.com/atom/ns#" term="zendcon" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><title type="text">ZendCon 2010 Wrap up</title><content type="html">Last week Santa Clara was buzzing &lt;a href="http://php.net/"&gt;PHP&lt;/a&gt; at &lt;a href="http://zendcon.com/"&gt;ZendCon 2010&lt;/a&gt;, the &lt;a href="http://php.net/"&gt;PHP&lt;/a&gt; conference of the year where vendors and developers unite to talk about, learn and see new innovations and best practices in the world of &lt;a href="http://php.net/"&gt;PHP&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I was invited by &lt;a href="http://www.zend.com/"&gt;Zend&lt;/a&gt; to present 3 talks: "&lt;a href="http://www.slideshare.net/DragonBe/unit-testing-after-zf-18"&gt;Unit Testing after Zend Framework 1.8&lt;/a&gt;" (&lt;a href="http://joind.in/2243"&gt;http://joind.in/2243&lt;/a&gt;), "&lt;a href="http://www.slideshare.net/DragonBe/why-zend-framework-powers-the-enterprise"&gt;Why Zend Framework powers the enterprise&lt;/a&gt;" (&lt;a href="http://joind.in/2264"&gt;http://joind.in/2264&lt;/a&gt;) and "&lt;a href="http://www.slideshare.net/DragonBe/improving-qa-on-php-projects"&gt;Improving QA on PHP development projects&lt;/a&gt;" (&lt;a href="http://joind.in/2279"&gt;http://joind.in/2279&lt;/a&gt;).&lt;br /&gt;From the reactions I received on &lt;a href="http://joind.in/"&gt;joind.in&lt;/a&gt;, I'm glad I faced the audience and shared my knowledge. My goal was reached as I merely got the audience interested in the subjects I presented.&lt;br /&gt;&lt;br /&gt;At the end of the conference I was also invited by &lt;a href="http://blog.calevans.com/"&gt;Cal Evans&lt;/a&gt; to sit in the closing keynote panel to discuss "The ROI of community involvement" (&lt;a href="http://joind.in/2288"&gt;http://joind.in/2288&lt;/a&gt;) alongside community icons like &lt;a href="http://benramsey.com/"&gt;Ben Ramsey&lt;/a&gt;, &lt;a href="http://caseysoftware.com/blog"&gt;Keith Casey&lt;/a&gt;, &lt;a href="http://weierophinney.net/matthew/"&gt;Matthew Weier O'Phinney&lt;/a&gt; and &lt;a href="http://www.joshholmes.com/blog/"&gt;Josh Holmes&lt;/a&gt;.&lt;br /&gt;As it's hard for me to stop talking about community involvement, I do think this panel discussion was a good initiative to start a dialogue with business owners to think about contributing and participating in open-source projects and how they can benefit from having their developers use company time to work on these open-source projects. &lt;br /&gt;&lt;br /&gt;I also took charge of this year's &lt;a href="http://zendcon.com/"&gt;ZendUncon&lt;/a&gt; and it turned out to be a huge success. We had uncon speakers that discussed a variety of topics, but I did see a lot of database and unit testing topics. We even had &lt;a href="http://joind.in/"&gt;joind.in&lt;/a&gt; founder &lt;a href="http://blog.phpdeveloper.org/"&gt;Chris Cornutt&lt;/a&gt; participating through &lt;a href="http://skype.com/"&gt;Skype&lt;/a&gt; the round table discussion "&lt;a href="http://joind.in/talk/view/2341"&gt;What is joind.in and why does it matter&lt;/a&gt;".&lt;br /&gt;&lt;a href="http://www.zend.com/"&gt;Zend&lt;/a&gt; offered the uncon speaker with the most votes on &lt;a href="http://joind.in/"&gt;joind.in&lt;/a&gt; a prize, being a lifetime Zend Studio license. This prize was given to &lt;a href="http://engineering.shopopensky.com/post/zendcon-2010-in-santa-clara-california"&gt;Bulat Shakirzyanov&lt;/a&gt; (&lt;a href="http://twitter.com/avalanche123"&gt;@avalanche123&lt;/a&gt;) with his talk on Unit Testing who had the most votes at the end of &lt;a href="http://zendcon.com/"&gt;ZendCon 2010&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;In general, it was a great conference. A bit cloudy for most people (as reported on &lt;a href="http://topnews.us/content/228574-php-developers-opine-cloud-computing-over-hyped-and-vendor-driven"&gt;Topnews&lt;/a&gt;, &lt;a href="http://computerworld.co.nz/news.nsf/development/cloud-computing-underwhelms-php-developers"&gt;ComputerWorld&lt;/a&gt; and &lt;a href="http://itmanagement.earthweb.com/osrc/article.php/3911121/Zend-Updates-PHP-IDE-Framework-for-the-Cloud.htm"&gt;DataMation&lt;/a&gt;), but the atmosphere was awesome. I met a lot of new friends and got rejoined with as I call my &lt;a href="http://php.net/"&gt;PHP&lt;/a&gt; family. &lt;a href="http://www.zend.com/"&gt;Zend&lt;/a&gt; has done a great job releasing new versions of their products (Zend Studio, Zend Server, Zend Framework) and their new website. We got a good discussion on &lt;a href="http://framework.zend.com/"&gt;Zend Framework&lt;/a&gt; 2.0 and where the framework is heading towards. And I learned a lot speaking to people in the hallway.&lt;br /&gt;&lt;br /&gt;For me, it was exhausting but it was all worth the effort. Who knows, we might see some of the unknown uncon speakers appearing at an international conference somewhere in the world. I had a blast!!!&lt;br /&gt;&lt;br /&gt;I would like to say thank you to &lt;a href="http://www.zend.com/"&gt;Zend&lt;/a&gt;, the sponsors, the venue staff and to all &lt;a href="http://zendcon.com/"&gt;ZendCon&lt;/a&gt; attendees who have participated in the ZendUncon sessions and have rated the talks they attended on &lt;a href="http://joind.in/"&gt;joind.in&lt;/a&gt; (&lt;a href="http://joind.in/event/ZendCon2010"&gt;http://joind.in/event/ZendCon2010&lt;/a&gt; and &lt;a href="http://joind.in/event/ZendUncon2010"&gt;http://joind.in/event/ZendUncon2010&lt;/a&gt;). If you were there but didn't rate any talks yet, do so now. Speakers and conference organizers want your feedback to do a better job the next time. So, help them to help you better!&lt;br /&gt;&lt;br /&gt;See you all at a &lt;a href="http://php.net/"&gt;PHP&lt;/a&gt; conference near you!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-6514727335062836291?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/mmF5vavjWO5z62IdVZJ9nWty-rM/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/mmF5vavjWO5z62IdVZJ9nWty-rM/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/mmF5vavjWO5z62IdVZJ9nWty-rM/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/mmF5vavjWO5z62IdVZJ9nWty-rM/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/GwqLfRBSeZ8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/6514727335062836291/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2010/11/zendcon-2010-wrap-up.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/6514727335062836291" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/6514727335062836291" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/GwqLfRBSeZ8/zendcon-2010-wrap-up.html" title="ZendCon 2010 Wrap up" /><author><name>Michelangelo van Dam</name><uri>https://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/s512-c/photo.jpg" /></author><thr:total>1</thr:total><georss:featurename>Santa Clara, CA, USA</georss:featurename><georss:point>37.402479 -121.976948</georss:point><georss:box>37.393956 -121.99153899999999 37.411001999999996 -121.962357</georss:box><feedburner:origLink>http://www.dragonbe.com/2010/11/zendcon-2010-wrap-up.html</feedburner:origLink></entry><entry><id>tag:blogger.com,1999:blog-28215292.post-4704049511581846858</id><published>2010-10-21T20:07:00.000+02:00</published><updated>2010-10-21T20:07:28.039+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="zc10" /><category scheme="http://www.blogger.com/atom/ns#" term="zendcon" /><category scheme="http://www.blogger.com/atom/ns#" term="php" /><title type="text">Speaking at ZendCon 2010</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://zendcon.com/?utm_source=DragonBe&amp;amp;utm_medium=Blogger&amp;amp;utm_term=zend%2Bphp%2Bconference&amp;amp;utm_campaign=zend_phpconf10" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://farm2.static.flickr.com/1311/5101394365_cdc466e09a.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;This year I was invited to speak at &lt;a href="http://zendcon.com/?utm_source=DragonBe&amp;amp;utm_medium=Blogger&amp;amp;utm_term=zend%2Bphp%2Bconference&amp;amp;utm_campaign=zend_phpconf10"&gt;ZendCon 2010&lt;/a&gt;, the PHP conference of the year in Santa Clara, CA (USA). I'm giving three talks there and I host the uncon sessions (see &lt;a href="http://www.dragonbe.com/2010/09/zendcon-unconference-announcement.html"&gt;my previous announcement about that&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;I'm not only sharing the stage with great icons from the community, but I'm also celebrating the fact I recently became a &lt;a href="http://www.dragonbe.com/2010/10/preparing-for-php-53-certification-exam.html"&gt;ZCE for PHP 5.3&lt;/a&gt;.&lt;br /&gt;At the end of &lt;a href="http://zendcon.com/?utm_source=DragonBe&amp;amp;utm_medium=Blogger&amp;amp;utm_term=zend%2Bphp%2Bconference&amp;amp;utm_campaign=zend_phpconf10"&gt;ZendCon&lt;/a&gt;, I'll be a part of a community panel to answer questions regarding community and user groups. At &lt;a href="http://conference.phpnw.org.uk/phpnw10/?utm_source=DragonBe&amp;amp;utm_medium=Blogger&amp;amp;utm_term=zend%2Bphp%2Bconference&amp;amp;utm_campaign=zend_phpconf10"&gt;PHPNW&lt;/a&gt; I closed the conference with a short but powerful community talk that inspired people to start a local user group in the area they live. I hope to inspire more people at &lt;a href="http://zendcon.com/?utm_source=DragonBe&amp;amp;utm_medium=Blogger&amp;amp;utm_term=zend%2Bphp%2Bconference&amp;amp;utm_campaign=zend_phpconf10"&gt;ZendCon&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;&lt;b&gt;Overview of talks&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span"&gt;&lt;span class="Apple-style-span"&gt;&lt;b&gt;Improving QA on PHP projects&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Everyone talks about raising the bar on quality of code, but it's always hard to start implementing it when you have no clue where to start. With this talk I'm showing that there are many levels developers can improve themselves by using the right tools. Many tools are&amp;nbsp;&lt;a href="http://www.php.net/"&gt;PHP&lt;/a&gt;&amp;nbsp;based, so you can implement more features if you need them because it's all&amp;nbsp;&lt;a href="http://www.php.net/"&gt;PHP&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span"&gt;&lt;span class="Apple-style-span"&gt;&lt;b&gt;Unit testing after Zend Framework 1.8&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;With the release of &lt;a href="http://framework.zend.com/?utm_source=DragonBe&amp;amp;utm_medium=Blogger&amp;amp;utm_term=zend%2Bphp%2Bconference&amp;amp;utm_campaign=zend_phpconf10"&gt;Zend Framework&lt;/a&gt; 1.8 lots of things changed internally, and so changed the way to test your applications. My presentation will go in depth in the new unit testing features and will give examples to test your application on several levels like database tables, web services, library components and controllers.&lt;br /&gt;Some parts require specific attention in order to ensure a good quality of Tests (like Zend_Application, Zend_Db, Zend_Ldap, Zend_Service, Zend_Controller) while other parts can be included as parts of earlier mentioned tests.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span"&gt;&lt;span class="Apple-style-span"&gt;&lt;b&gt;Why Zend Framework powers the enterprise&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;With this talk I explain a bit of history of how I've got involved with&amp;nbsp;&lt;a href="http://framework.zend.com/?utm_source=DragonBe&amp;amp;utm_medium=Blogger&amp;amp;utm_term=zend%2Bphp%2Bconference&amp;amp;utm_campaign=zend_phpconf10"&gt;Zend Framework&lt;/a&gt;&amp;nbsp;and how it became the most popular framework within enterprises and other businesses. I showcase also a couple of projects that you don't immediately link to&amp;nbsp;&lt;a href="http://framework.zend.com/?utm_source=DragonBe&amp;amp;utm_medium=Blogger&amp;amp;utm_term=zend%2Bphp%2Bconference&amp;amp;utm_campaign=zend_phpconf10"&gt;Zend Framework&lt;/a&gt;, just to show that it is possible to go beyond the sky and have space as your final fronteer.&lt;br /&gt;&lt;br /&gt;If you're going, great ! I'll see you there. I'll be the guy on stage… 4 times.&lt;br /&gt;&lt;br /&gt;If you're not going, think again. &lt;a href="http://zendcon.com/?utm_source=DragonBe&amp;amp;utm_medium=Blogger&amp;amp;utm_term=zend%2Bphp%2Bconference&amp;amp;utm_campaign=zend_phpconf10"&gt;ZendCon&lt;/a&gt; is not only a place to learn all there is to learn about&amp;nbsp;&lt;a href="http://www.php.net/"&gt;PHP&lt;/a&gt;, it's also the place to approach people behind all those cool open-source tools you're using each day. They won't bite you if you ask them questions. Meet the community around&amp;nbsp;&lt;a href="http://www.php.net/"&gt;PHP&lt;/a&gt;&amp;nbsp;and experience first hand what it feels like to have the best support network ever. You even have stage-time to present &lt;a href="http://www.php.net/"&gt;PHP&lt;/a&gt; stuff yourself !!! How cool is that.&lt;br /&gt;&lt;br /&gt;Ok, so it is expensive. But see what you get in return: new friends, new knowledge, great atmosphere and above all… a grasp of what the &lt;a href="http://phpc.org/?utm_source=DragonBe&amp;amp;utm_medium=Blogger&amp;amp;utm_term=zend%2Bphp%2Bconference&amp;amp;utm_campaign=zend_phpconf10"&gt;PHP community&lt;/a&gt; is all about. It's not too late to &lt;a href="http://zendcon.com/?utm_source=DragonBe&amp;amp;utm_medium=Blogger&amp;amp;utm_term=zend%2Bphp%2Bconference&amp;amp;utm_campaign=zend_phpconf10"&gt;get your ticket&lt;/a&gt; and join the fun !&lt;br /&gt;&lt;br /&gt;See you there…&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/28215292-4704049511581846858?l=www.dragonbe.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/YZCnm1ssQzCrxSQOG61YjnWwB30/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/YZCnm1ssQzCrxSQOG61YjnWwB30/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/YZCnm1ssQzCrxSQOG61YjnWwB30/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/YZCnm1ssQzCrxSQOG61YjnWwB30/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/dragonbe/~4/CRuSof8iJ1M" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.dragonbe.com/feeds/4704049511581846858/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.dragonbe.com/2010/10/speaking-at-zendcon-2010.html#comment-form" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/4704049511581846858" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/28215292/posts/default/4704049511581846858" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/dragonbe/~3/CRuSof8iJ1M/speaking-at-zendcon-2010.html" title="Speaking at ZendCon 2010" /><author><name>Michelangelo van Dam</name><uri>https://profiles.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/AAAAAAAACwQ/oW2Y2K2M1U4/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://farm2.static.flickr.com/1311/5101394365_cdc466e09a_t.jpg" height="72" width="72" /><thr:total>3</thr:total><feedburner:origLink>http://www.dragonbe.com/2010/10/speaking-at-zendcon-2010.html</feedburner:origLink></entry></feed>

