PHP54Compatibility 0.2 released

Last night I have merged in a few pull requests adding additional sniffs to the existing collection and packaged it all. List of all supported checks is in the README file. Check the repository on github to see it. Big thanks go to Laura Beth, Nathaniel McHugh and Ben Selby for all the contributions. I have created a pear package and published it on my github pear channel so you can easily install it from there. No more alpha release either, so even easier to install.

$> pear channel-discover proofek.github.com/pear
$> pear install proofek/PHP54Compatibility

There are 2 dependencies at the moment: PHP 5.4 and PHP_CodeSniffer 1.3+. A few people asked my to downgrade the minimum PHP version to 5.3. The reason why I want PHP 5.4 is I want to use 5.4 tokaniser to make sure  code is parsed the way it will be parsed on the target PHP 5.4 platform. If you want to run pre-checks, before you actually upgrade to 5.4, use -f flag to perform forced installation:

$> pear install -f proofek/PHP54Compatibility

But I would still encourage you to upgrade first to get the best results.

PHP 5.4 Compatibility Coding Standard for PHP_CodeSniffer

So with PHP 5.3 upgrade underway (and PHP 5.4 out of the door now!) I thought it’s time to prepare for PHP 5.4 and make sure we’re compatible. So by looking at Wim Godden’s PHP53Compatibility code sniffs I have created a base for PHP 5.4 sniffs that we want to use to make sure we’re compatible. It’s a base, so it’s not finished/perfect in any sense so I will be relying heavily on PHP Community to help me out with it. I will obviously be working on it hard, but any pull requests are greatly appreciated.

So where is it then?! It’s on github! I have also set up my personal github PEAR channel and will release a first alpha package so you can install it easily.

Right, back to work!

PHPUnit assertion gotcha

A recent discovery. If you found yourself upgrading from PHPUnit 3.5.x to PHPUnit 3.6.x be aware of a subtle change. assertEquals() and I guess all related assertions will behave a bit differently after the upgrade. When you assert 2 values and one of them is an object with defined __toString() function PHPUnit 3.5.x will fail the test, while PHPUnit 3.6 will happily pass it (by I am guessing converting the object to string). See an example on gisthub for more details:

$ phpunit testCaseTest.php
PHPUnit 3.5.15 by Sebastian Bergmann.
F.
Time: 1 second, Memory: 4.50Mb
There was 1 failure:
1) TestCase::testAssertStringWithObject
Failed asserting that
myClass Object
(
)
matches expected .
/private/tmp/Testing/testCaseTest.php:16
FAILURES!
Tests: 2, Assertions: 2, Failures: 1.
$ phpunit testCaseTest.php
PHPUnit 3.6.10 by Sebastian Bergmann.
..
Time: 1 second, Memory: 5.50Mb
OK (2 tests, 2 assertions)

PHP Benelux 2012 – 4 days of awesomeness

PHP BeneluxI was extremely pleased that I was invited to talk at the annual PHP Benelux conference in Antwerp. It was my first time in Antwerp, but I have heard about this conference so much before. And I must tell, all of it was true.

The arrival

But starting from the beginning, which was the landing of PHPNWAir plane (as named by Michelangelo van Dam) with a representation of 7 people from British PHP NorthWest Community (including 3 speakers – Rowan Merewood, Jeremy Coates and me) in Brussels Charleoi. And yes, I do admit it was a poor choice of mine to decide to fly Ryanair from Manchester to this airport. Connection is bad, it’s about an hour drive from Antwerp, and if not the help from PHP Benelux crew (namely @DragonBe and @Martin1982) we would be in trouble. So I am sorry once again guys, I won’t do it again! :D

Socials

From then on I will only guess what heppened next, as I have problems recalling what happened on what day. Speakers dinner was an amazing opportunity to meet the conference crew and speak to the other speakers. A rare occasion indeed. Some of them I already knew from previous conferences like PHPNW, PHPUK and 4developers, but some of them I had the pleasure of meeting for the first time, face to face. There is nothing better, then speaking to other experienced developers at a conference, truly amazing and unique experience. How often you have an opportunity to meet people like @derickr, @weierophinney, @rdohms, @davidcoallier, @dzuelke, @joshholmes, @h, @DragonBe, @skoop and @ianbarber in one place at the same time? And that’s only a short list, cause I could go and go and go naming them one after another!

The conference was executed perfectly! I couldn’t be looked after better even by my own mother! @ThijsFeryn@DragonBe@Martin1982 and the rest of the crew – it’s a massive thank you from me! Sponsors did great too – @ibuildings and @engineyard responsible for the afterconference socials helped a lot with maintaining the spirit and helping everybody enjoy the conference to its very limits! We had so much fun bowling on Friday night (and yes I won!) and watching @juokaz and @dzuelke competing – whether it was about which phone is better or who’s better at Kinect Tennis! Can’t deny the fact that beer tasting session and Belgian fries and spécialités also helped during that day.

The winter BBQ, well… it was bloody cold, but again the beers, liquors, hot food and @Orchestra_io scarf helped to survive the evening. We even managed to go for a pizza to Antwerp late night when we got hungry! Some people even managed to come back in a car’s boot!

Learning

But conference it’s not about fun, but first of all about learning. And hell yeah, there was a lot to learn! I have seen a number of talks (and shamefully missed a few ones for reasons I am not gonna mention here ;) ).

I’ll start with @dzuelke‘s  “Large-Scale Data Processing with Hadoop and PHP”. David is always an inspiration for me – an excellent speaker with really good talk. This one definitely gave me a few ideas how I could use Hadoop in my daily job. Hopefully I will find some time to come up with some prototypes.

Helgi Þorbjörnsson also gave a solid talk on “PHAR, the PHP .exe format”. While I already knew a few things about phar, he definitely managed to mention some interesting things about phar I didn’t know before. I really liked the way he engaged the audience. Really good idea!

Rowan Merewood and his “Estimation or, “How to Dig you own Grave”” was another great performance. Nothing new for me there (well, we worked together for a number of years), but I always admire Rowan for his slides and the way he talks. A true masterpiece.

Matthew Weier O’Phinney’s “VIM + *nix Toolchain == PHP IDE” definitely inspired me again to come back and learn a few new things about vim. Inspirational speaker with a lot of charisma!

“PHP traits, treat or threat?” by Nick Belhomme was the talk I really wanted to see since I really didn’t have any time to look at PHP 5.4. While the talk was a bit chaotic at times for me, I still learned a lot about traits, and as of now, I know one thing – I don’t think I like them and I don’t think I will use them – it’s more of a threat for me. They seem to be too messy.

You could tell Marcel was nervous during his “The lust for knowledge and experience” talk. But still it was a good talk with very good professional slides. It was a chance for me to rethink a few things about the relations between “the master”, “apprentice” and “a junior”.

Finally, the best talk of the conference from David Coallier “Taking it to the next level”. I absolutely loved the way he did his talk and how he engaged the audience. He only made me realise how rarly we tell other how much we appriciate their work and how we all take the work of others for granted. Thanks you very much for that David!

My software metrics talk

At last I’d like to thank everybody for attending my “Magic Behind the Numbers – Software Metrics In Practice” talk. I am really glad, that more and more people are looking at improving the quality of their code and want to learn more about it. I was impressed with the fact how many people in Benelux are using tools like PHP Depend and PHP Mess Detector! Keep it up guys! Thank you very much for all the feedback. I’ll definitely use it in the future to improve my presentation and make it more interesting. You can find the slides from my talk on slideshare.

Summary

I will definitely do my best to come back to Antwerp next year, but until then I hope to see you next month in London at PHPUK 2012!

Software metrics at PHPNW 2011 Conference

PHPNW11I am really excited to be able to speak again in Manchester this October. This time I will be speaking about the ways and tools you can use to assess your code quality and its design. While software metrics help a lot and tell a lot about code, you have to remember they only should be treated as a guidance, not the goal in itself.  So be careful and don’t start coding only to make the metrics look pretty. Here is an excellent video from Alberto Savoia for these who’s gone a bit too far with software metrics.

See you in Manchester!

Spring cleaning? Hell no!

http://flic.kr/p/xSB58

It’s summer time already, but every time I hear about cleaning revolution it reminds me of spring cleaning. And I can tell you I hate spring cleaning! I don’t get the whole idea of one time movements just because you didn’t care enough to keep your things tidy all year long!

The whole thing gets triggered now and again, because another developer gets annoyed with the amount of rubbish in the error log. Through years of not caring “suddenly” it’s hard to spot major issues through the flood of notices, warnings and stricts. So Tech Debt Tuesdays, unit test Thursdays and  clean up Fridays take place. Yada yada yada. Everybody get annoyed trying to fix it and after that all gets to normal. Why?! Have Starbucks Mondays and Orange Wednesdays instead – it’s fun! Do your day to day job and keep your code tidy. Every day. Every hour. Every minute. You spot something, you fix it. Simple. And it doesn’t matter whether it’s your code or not. Whether you are going to change it or not. The fact you have seen it is good enough to make you fix it.

I can only repeat after Uncle Bob Martin:

Cleaning code is just part of the normal everyday job of a programmer. It should not be something special on the schedule.

Teams should gradually fix code quality _outside_ the schedule. The improvements should be long term an on-going. Never schedule them!

Code quality issues should _never_ be in the backlog, or on the schedule. They should never get to that point.

Go team!

Running php linter before pushing changes to a git repository

A problem

Have you ever reviewed code that contained parse errors and wasn’t even proper PHP? I have and that’s why I have finally decided to stop that. Well, I have not decided to stop reviewing code, I have decided to prevent people committing PHP code which contain parse errors. With git hooks it’s fairly easy.

One way with pre-commit hook

One way to do it would be with pre-commit hook. Travis Swicegood explained it very well in his blog post. One problem with that is that every developer would have to deploy that hook locally in every repository. So that was really not an option for us.

Or another with pre-receive hook

What I wanted to do was to prevent people pushing broken code to git repositories stored on our git server (sort of in-house github). So I would deploy a hook on the server and the check would be done in there. pre-receive hook seems to be an ideal place for that. You can find our current version of the hook below:

Installation

To get it installed simply copy pre-receive hook into hooks subdirectory of a repository on a git server and make the file executable.

Internals

So how does it work? It’s actually pretty simple. If the hook exists with a non zero error code the push will fail, otherwise it will succeed. So all we have to do is to get a list of files changed in a given changeset and run php linter against them.

The hook accepts 3 parameters:

  • old revision id (ie. 325fbce03ba542c37c8529f36bf66998594e8384)
  • new revision id (ie. 58127dc11f4fd0c433cc6485a10925be5b56e1bb)
  • full reference name (ie. refs/heads/master)

Running git diff with –name-only option will give us list of changed files. But we cannot simply run php linter at this point as you have to remember that on git server there is just bare repository, without working directory tree. So we have to fetch the files straight from git context. We can fetch an object contents from index using git cat-file command, but it requires a unique object identifier to return file contents, so first we need to get this first. git ls-tree command is just what we need. Running it with new revision id and file name, we will get all the information we need!

#> git ls-tree 58127dc11f4fd0c433cc6485a10925be5b56e1bb Test/Application/WebTest.test.php
100644 blob 30a5e1914fe075db11c31780454fcc9d1d83aa9d	Test/Application/WebTest.test.php

Now, when we run git cat-file with the object type we got back (blob in case of files) and object id, we will get contents of that file, that we can finally pipe into php linter process. If the process returns any non-zero error code we display an appropriate message and stop script execution with a non-zero code:

smarek@28:~/git/modules/Sandbox (master)$ git push origin master
Counting objects: 14, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (8/8), done.
Writing objects: 100% (9/9), 857 bytes, done.
Total 9 (delta 2), reused 0 (delta 0)

Running php linter...
Error parsing file 'Test/Application/WebTest.test.php'

error: hooks/pre-receive exited with error code 255
To git:Sandbox.git
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'git:Sandbox.git'

otherwise we just stop the script with a zero code!

smarek@28:~/git/modules/Sandbox (master)$ git push origin master
Counting objects: 8, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 658 bytes, done.
Total 6 (delta 0), reused 0 (delta 0)

Running php linter...
No errors detected

To git:Sandbox.git
90f43b9..36582e1  master -> master

No more parse errors! FTW!

Running multiple MySQL instances on MacOsX

Recently I had to look at a tool developed in-house to archive data from a main db cluster to an archive cluster. As I had to implement a brand new feature I wanted to test it first on my machine and cover it fully with unit tests, but to do so I obviously needed 2 MySQL instances running on the same machine – one working as a source db and another that I would be archiving the data to.
I’ve already had MySQL installed from a dmg archive (version 5.1.52) running on localhost on port 3306 and as the installed sets it up system-wise I couldn’t just install another one running on a different port. So I looked at one of the MySQL features allowing to run its multiple instances on one machine. And I must say it works really nice and the setup process is really simple.

Set up global symlinks

First of all make sure that the following binaries are in your system PATH.

  • mysql
  • mysqldump
  • my_print_defaults

I have simply simlinked them in /usr/bin directory:

lrwxr-xr-x   1 root    wheel          26 26 Nov 08:17 mysql -> /usr/local/mysql/bin/mysql
lrwxr-xr-x   1 root    wheel          30  9 Mar 11:08 mysqldump -> /usr/local/mysql/bin/mysqldump
lrwxr-xr-x   1 root    wheel          38 18 May 13:12 my_print_defaults -> /usr/local/mysql/bin/my_print_defaults

Setting up separate data directory

The second MySQL instance will store data in a separate data directory so you have to set it up first. Simply use mysql_install_db script to set it up. I have decided to store them  in /usr/local/mysql/data2

sudo /usr/local/mysql/scripts/mysql_install_db --user=_mysql --basedir=/usr/local/mysql/ --datadir=/usr/local/mysql/data2

MySQL on MacOSx is run by default as _mysql, that’s why I have used –user option when I run the script to set up permissions in a similar way.

Set up multiple instances in my.cnf file

The last thing that I had to do was to configure my instances in my.cnf file which I have placed in /etc folder. Here’s my configuration:

[mysqld_multi]
mysqld     = /usr/local/mysql/bin/mysqld_safe
mysqladmin = /usr/local/mysql/bin/mysqladmin
user       = root

[mysqld1]
socket     = /tmp/mysql.sock
port       = 3306
pid-file   = /usr/local/mysql/data/mysqld1.pid
datadir    = /usr/local/mysql/data
language   = /usr/local/mysql/share/english
user       = _mysql

[mysqld2]
socket     = /tmp/mysql.sock2
port       = 3307
pid-file   = /usr/local/mysql/data2/mysqld2.pid
datadir    = /usr/local/mysql/data2
language   = /usr/local/mysql/share/english
user       = _mysql

Every instance is configured in its own section, where you can configure the port you want the instance to be running on and where is the instance data directory.

Managing and accessing multiple instances
To start or stop an instance use mysqld_multi tool and specify the instance number (it matches the number specified in my.cnf file).

sudo /usr/local/mysql/bin/mysqld_multi start 1
sudo /usr/local/mysql/bin/mysqld_multi stop 2

To access mutliple instances you can either connect via IP address:

mysql --host=127.0.0.1 --port=3307 -uroot

or socket

mysql --socket=/tmp/mysql.sock2

For some weird reason to access second instance via IP running locally I had to use address 127.0.0.1 and not localhost name, otherwise the client always connected to the main instance running on port 3306.

Simples!

Resources:

February aftermatch

Finally, during the first days of my holiday, I have found a few days to finish this post.

Well, Feb this year has been extremely busy. First hot house at work, a brand new idea for me that worked brilliantly, then PHP Unconference Europe in Manchester, and finally PHPUK 2011 in London. And although that was already March, it’s worth adding PHPNW monthly user group meeting in Manchester as well, where I had a talk about PHPUnit, the next one in “Back to basics” series.

But lets start from the beggining.

The hot house

Brand new idea for me. We got off site for 3 days from the office to prototype and implement a first version of our end to end continuous integration process. And by end-to-end I mean end-to-end - form project initialisation to the final release integration and automated deployment. It’s been 3 very exhausting days, not the usual 9-5 working day you usually have at work, but it was well worth having it. The idea of it is, you get the gather the whole team – developers, project managers, BAs, QA engineers and everybody else that are crucial for the project, in one room, away from any sort of distraction and plan (and most importantly execute) the whole thing. The 3 days have a specific structure, with the 1st day being sort of introduction and planning, 2nd day being entirely focused on execution and 3rd day, the shorter one, being a wrap-up and summary of the whole event with the main goal of raising post event actions that are meant to be executed in the office afterwards.

During this 3 days we achieved something that we couldn’t in the past several months - we have put a concept in place and implemented it proving the the whole thing can work. Now we know that we can automate the process of developing, building and testing single project workstreams and when they are ready put them altogether during a merge into a release, build, deploy and test the whole release. That includes both PHP and Java artifacts, DB changes, internal CMS artifacts and regression selenium packs and functional tests developed and executed during the project. Oh! How I love automation.

PHP Unconference Europe (19-20 Feb 2011)

Official site: http://www.phpuceu.org/phpuceu-2011/
Unconference wiki: http://eu.php-unconference.de/

I was looking forward to it very much. I have never been to an unconference before, and I didn’t quite get the whole idea of an unconference. Well, I knew roughly the idea behind it, just never seen it in action.

So, in general I am a bit disappointed. As a completely community driven event it was an enormous success. A load of people turned up, we had a few interesting proposals upfront, then a few more was added during the event. From the organisation point of view it was brilliant. Everything went smoothly, beer was good, food was good, people were great. The bit that needs improving are the talks itself. And I don’t blame anybody for it. In fact if I could blame somebody I would start from myself. The talks that were proposed upfront and were chosen by the community went quite well. The disussion panels - not so much. They weren’t bad, don’t get me wrong. They just simply could be better. But I already know, that next time I will come better prepared. Always learn on mistakes – that’s my motto.

PHPUK 2011 (25 Feb)

Official site: http://www.phpconference.co.uk/conference/php-uk-conference-2011

Just 5 days after, I attended another big UK event, this time in London. Schedule this year was pretty impressive. I was mainly interested in the two afternoon talks in Auditorium – Sebastian Bergmann’s “Agility and Quality” and Thorsten Rinne’s ”Continuous Improvement in PHP Projects”. But before I get to them, let’s rewind and start from the beginning.

The conference had started with Marco Tabini’s “Experience” key note. Marco refereshed the idea od user experience with a few funny anecdotes and targeted it specifically at developers. Nothing really new, but we all need to be reminded from time to time, that we should always be focused and don’t forget about end users and their needs.

After that I had to choose between 3 talks:

  • Martin Beeby’s “HTML5 and CSS3 Today”
  • Ivo Jansch’s “PHP in a Mobile Ecosystem”
  • Ian Barber’s “ZeroMQ Is The Answer”

I didn’t hesitate a second and went straight away to Sidetrack 2 to see what ZeroMQ is all about. And I don’t regret my decision. I think it was the best talk of PHPUK 2011. Ian kept me focused all the time and definitely convinced me to try out ZeroMQ. Great talk, with plenty of usage examples and a demo. Well done!

After a short break it was time for the talk I was waiting for. But again the choice for me wasn’t difficult either. We had:

  • “Xdebug” by Derick Rethans in Sidetrack 1
  • “Agility and Quality” by Sebastian Bergmann in Auditorium
  • “Running on Amazon EC2″ by Jonathan Weiss in Sidetrack 2

I had seen Derick’s talk before and I am not really interested in Amazon EC2, not just yet at least. So I went straight away to see Sebastian’s talk. Sebastian is a well known international speaker, practically an expert in QA for PHP topics. And maybe that’s why I was really disappointed. Sebastian seemed to be nervous at the beggining, although it went better as he went along. Still I was expecting something new. Instead of that, the talk brought up some of the well known “agility practices”, how they fit in during project lifecycle and how some of the tools can help to achieve that. Nice WOW theme made it look interesting, but not interesting enough to keep me focused.

I have stayed at the Auditorium to listen to “Continuous Improvement in PHP Projects” by Thorsten Rinne. The other 2 talks that I have missed were:

  • “Large-scale Data Processing with MapReduce and PHP” by David Zülke in Sidetrack 1
  • “The InnoDB Storage Engine for MySQL” by Morgan Tocker in Sidetrack 2

And I must say, that was the worst talk I have heard during the conference. It seemed Thorsten either didn’t prepare very well to do this talk or he had real problems expressing himself. Not that I couldn’t understand him, but he was repeating some phrases all over again. Adding to that, some of the things were pretty much the same things Sebastian has talked about just before. The significant amount of time was spent introducing and explaining how to install some CI tools. And then, during Q&A session, he said some things that I strongly disagree with, like advising to implement CI process (and doing unit tests) without telling that to management. In my opinion that is the worst thing that you can do. I really regret I didn’t go to Sidetrack 2 and didn’t see David Zülke’s talk. I am sure it was much much better.

Next talk I have chosen was “Optimising a Zend Framework Application” by Rob Allen. It was a difficult choice, as I also wanted to see Stuart Herbert with his “Beyond Frameworks” talk in Sidetrack 1. I wasn’t that interested in Lorenzo Alberton’s “NoSQL Databases: What, When and Why”. All I can say, Rob delivered a good talk again. Learnt a few things about Zend Framework, which will definitely help me in my day to day job. I only heard that the other 2 talks were equally good and interesting!

At last I have attended “Advanced OO Patterns” by Tobias Schlitt in Auditorium. The other 2 talks were:

  • “Varnish in Action” by Thijs Feryn in Sidetrack 1
  • “99 Problems, But The Search Ain’t One” in Sidetrack 2

It was a good talk with a bit of humour. Tobias went through some of the design patterns explaining their pros and cons. Nothing really revolutionary, but it is always good to refresh this stuff from time to time. Shame that, at the same time Stuart Herbert and Jeremy Coates where discussing phpfundamentals matters at the lobby. I have left the talk to join them to learn more about that.

In general I must say it was a successful event, very well organised. Good food, free beers at the end and the after conference socials at “Slug And Lettuce” only made it better. I will definitely try to attend the next edition in 2012!

PHPNW montly user group meeting

I try to attend this local community event whenever I can, so I couldn’t miss it this time either. Especially that I was going to to do an introductory talk about PHPUnit. When I have arrived there was only a few people, so we have decided to wait a few more minutes to wait for the late comers. And it appeared to be a good decision as in the next 20 mins or so the room was nearly packed full! I went through some basic features of unit testing based on some live examples. A small accident near the end made me finish the talk without the slides (my MBP battery went out of power), so I had to improvise a bit, but I think, overall, it all went pretty well as we had quite a few questions afterwards and a really nice discussion at the end. You can find the slides from the talk on slideshare.

Autotesting with watchr, growl and PHPUnit

Inspired by a conversation at the office couple of days ago with a slightly bald software developer from the UK and @Stubbs I decided to play a bit with the concept of autotesting. I came across it about a year a go during BTDevCon and I really liked it, but back then I have seen it in action, when one of the developers was using it for a demo in Ruby. I haven’t heard about anything similar for PHP so I didn’t dig in further. Until now.

Let’s get back to the concept of autotesting again for a minute.  The other term for it is continuous testing and in short it is about getting an instant feedback on your code while you’re developing it. In practise, the test suite is being triggered every single time you save a file and the results of the run are displayed on the screen. You don’t have to run the test suite manually, it is done for you.

All you need is watchr, growl and growlnotify (which is available as part of Growl Extras). Watchr is a flexible tool that allows you to watch the filesystem and run an arbitrary command when a change is detected, growl is a notification system for Mac OS X and growlnotify is a command-line tool to post Growl notifications.

Watchr

To install watchr you can either install it using gem:

#> sudo gem install watchr
Successfully installed watchr-0.7
1 gem installed
Installing ri documentation for watchr-0.7...
Installing RDoc documentation for watchr-0.7...

or clone the latest version from github, generate the gem and install it from your local disk:

#> git clone git://github.com/mynyml/watchr.git
#> cd watchr
#> gem build watchr.gemspec
   Successfully built RubyGem
   Name: watchr
   Version: 0.7
   File: watchr-0.7.gem
#> sudo gem install watchr-0.7.gem
Successfully installed watchr-0.7
1 gem installed
Installing ri documentation for watchr-0.7...
Installing RDoc documentation for watchr-0.7...

Growl and growlnotify

Growl (with included extras) is available from its home page as a standard dmg image. Just follow onscreen installation instructions.

Setup watchr with PHPUnit

To set up watchr with PHPUnit you only need one configuration file. In there, you define which part of the filesystem you want to watch for changes and what do you want to do when the change occurs. In my case all I want is to execute my test suites. So here is the example file, called watchr.rb and stored in the top level directory of my module:

In the first line, I have set up watchr to watch any file with php extension for incoming changes and when it detects the change to execute code_changed() function. code_changed() does nothing else but executes phpunit command in my Test subdirectory and passes the results to growl() function.  And the growl() function basically parses the results, checks whether tests run successfully or not and executes growlnotify with an appropriate message and image.

Let the fun begin!

All you have to do now is run watchr, start changing your code and watch for growl notifications!

#> watchr watchr.rb
Positive notification

Positive notification

Failure notification

Failure notification

You can get both the configuration and images I have used from my github. Happy continuous testing!

Sources: