Adventures in Continuous Integration (PHP, Xinc, Phing etc)

I’ve had cron’ed unit tests running for ages which happily spam me when stuff breaks – and likewise adding e.g. phpdoc generation and so on into the mix wouldn’t be too hard.

Anyway, for want of something better to do with my time I thought I’d look into CI in a bit more depth for one customer’s project. As some background, we’ve maintained their software for about the last 12-18 months, the project is largely procedural – although we’re introducing Propel, Zend Framework, Smarty etc into the mix slowly over time. We’ve also added a number of unit tests to try and keep some of the pain points in the project under control.

So, there’s the background.

With regards to CI within a PHP environment there seem to be three options:

  1. phpUnderControl
  2. Xinc
  3. Hudson

To the best of my knowledge 1 & 3 require Tomcat, and therefore are Java based. I thought I’d try and make my life easy and stick with Xinc which is written in PHP (and perhaps therefore something I can hack/patch/modify if needs be).

In retrospect I’m questioning whether I made the right choice – Xinc seems to be unmaintained and unloved at the moment.

Xinc Installation

It should be the case of doing something easy like :

pear channel-discover pear.xinc.eu
pear install xinc/xinc

Unfortunately, the Xinc project seems a little unloved as of late, and it’s necessary to use an unofficial mirror :
pear channel-discover pear.ctrl-zetta.com
pear install ctrl-zetta/Xinc

(This required rummaging through Xinc’s issue log… *sigh*).

Follow the instructions and it’s not really difficult to install. There’s no requirement for a database or anything.

Once installed, edit /etc/xinc/config.xml and comment out the <project>…</project> block and instead only edit /etc/xinc/conf.d/whatever.xml – in my case I just copied the skeleton one and added in stuff… giving something like the following project.xml

In a nutshell, this says:

  1. Run from /var/www/xinc/whatever.test.palepurple.co.uk
  2. Every 900 seconds rebuild using what’s defined in the <builders> tag
  3. Always build (hence <buildalways/>) – in reality, you’d probably want the <svn directory=${dir}” update=”true”/> enabled so rebuilds only occur if someone’s changed svn.
  4. Once the build is complete, publish the php docs (found in ${dir}/apidocs)
  5. Once the build is complete report the results of the unit tests using ${dir}/report/logfile.xml – obviously this path needs to match up with what’s in your phing build.xml file.
  6. If a build fails, email root
  7. If a build succeeds after a failure, email root
  8. When a build succeeds, run the publish.xml file through phing (target: build) – this is used to create a .tar.gz with appropriate numbering which appears within Xinc’s web ui for download.

Obviously in my case, this didn’t get me very far initially as the project wasn’t using phing.. so that was task #2.

Phing

I had a few issues once I started to phing-ise things – firstly, I’ve always historically used SimpleTest as my unit test framework of choice – unfortunately it’s phing and Xinc integration isn’t all that good – and phpUnit is clearly superior in this respect. So I quickly converted out tests from SimpleTest to phpUnit – thankfully this wasn’t too hard as all my tests extent a local class (LocalTest) (hence the exclude line in the build.xml file below) to which I just added a few aliasing methods in so all the PhpUnit/SimpleTest method name differences (e.g. assertEqual($x,$y) and assertEquals($x, $y)) were handled along with crude mimicking of some of SimpleTest’s web_tester functionality.

Anyway, once that was done, it was pretty easy to pinch various bits of config from everywhere and get something like the following build.xml file – when Xinc runs it defines a few properties – so I’ve added in a couple of lines to the build.xml to ensure that these properties are set to something (incase I’m running phing from the command line and not via Xinc).

In my case it was necessary to explicitly exclude e.g. the Zend Framework or ezComponents from various tasks (e.g. phpdoc api generation and code sniffing). In this project’s case the code for each is explicitly within the hierarchy – as opposed to being a system wide include.

Running a particular ‘target’ is just be a case of doing ‘phing -f build.xml tests’ (for instance). phing will default to using ‘build.xml’, so the ‘-f build.xml’ is redundant.

Firing up Xinc (/etc/init.d/xinc start) and tail’ing /var/log/xinc.log give me a good idea of what was going on, and eventually with a bit of prodding I got it all working.

I then thought I ought to integrate test code coverage reports – as they’d be a useful addition and something I can point the customer to – at this point I discovered I needed to hack phing a little to get it to work with phpUnit’s xml output format to create the code coverage report. A patch of what’s needed should be here but phing.info has been down for the last few days… so manually :

In /usr/share/php/phing:

Edit: tasks/ext/phpunit/formatter/XMLPHPUnitResultFormatter.php and change

$this->logger = new PHPUnit_Util_Log_JUnit(null, true) to

$this->logger = new PHPUnit_Util_Log_XML(null, true);

And change the require_once() call at the top of the file to become require_once ‘PHPUnit/Util/Log/XML.php’).

No doubt the above won’t be required once a new release of phing is made – I’m running v2.4.1.

And, if your code has an implicit dependency on various variables being global – and they’re not implicitly declared as global within an include file – it will fail and look like phpunit is trampling on globals; it’s not. Just edit the include file and be explicit with respect to global definition. You will probably need to tell phpUnit to not serialise globals between test calls as some variables ( e.g. a PDO connection) can’t be serialised…. this can be done by setting a property within your test class(es) called backupGlobals to false.

And, if everything works well, you’ll see something like the attached screenshot [[Screenshot 1]]

Summary

Xinc appears unmaintained; patching of it is probably required, but it does appear to work.

I’m glad I’ve finally started to use phing – I can see it being of considerable use in future projects when we have to deploy via FTP or something.

Google News Sitemap + WordPress

Annoyingly the current version of the google-news-sitemap plugin for WordPress (v1.4) doesn’t work with some silly XML namespace error reported by google.

See http://wordpress.org/support/topic/364929 and effectively the ‘patch’ on the Google Support forum thing which works fine (there are two bits of the plugin which need updating – whcih correlate to the two parts mentioned in the posting etc)

Bit annoyed that the fix is so easy – yet the plugin hasn’t been updated yet. Grr.

Silly SoapClient

Sam made some changes to a SOAP service one customer has – and suddenly our automated tests kept failing. “WTF?” we thought.

We persistently got the same error (e.g. Fatal error: SOAP-ERROR: Encoding: object hasn’t ‘SortResults’ property in ….) yet the generated WSDL file (when viewed through a web browser etc) no longer has SortResults in it.

We checked :

  • Proxy settings,
  • Apache configuration (and restarted Apache)
  • svn reverted files
  • DNS and more.

Then it turned out the SoapClient PHP object caches the WSDL file by default in e.g. /tmp/wsdl-blahblahblah. (Where blahblahblah looks very much like an md5). And it doesn’t make much of an effort to check it’s validity. Obviously the documentation does state this, but it does seem like the wrong default behaviour to me.

Solution: Add something to the automated tests to delete /tmp/wsdl-*. OR pass into the SoapClient constructor an array of options like :

$client = new SoapClient('http://somewhere/blah.php?wsdl", array(cache_wsdl => WSDL_CACHE_NONE)

Time wasted: Too much.

Still looking for a PHP contractor….

At work I’m still looking for a short term PHP contractor. Perhaps I’m being unrealistic in my expectations/requirements (rate/location/duration/skills etc), but nevertheless…. As I’ve not found anyone via normal channels (twitter/phpwm user group etc) I thought I’d turn to a random recruitment agency (who I’d spoken to a week or so ago).

Yesterday I interviewed one guy – who’d been a programmer for a number of years (10+) – using Visual Foxpro (whatever that is) – presumably it’s a dead language, as he wants to move across into PHP. He has very basic PHP experience (yet claims 2 years on his CV), figured out how to do FizzBuzz and Recursion without too much help – but didn’t know anything about object orientation, separation of concerns (specifically MVC), security (obvious SQL injection) or unit testing and failed to make any comment on what is almost the worst code I could find to present to him. This isn’t necessarily a problem – I would normally be happy to train someone – however, not when I’m paying him £25/hour and I’d be lucky if he was productive within a week. (Hint: students are better than this when they’ve only been in University for two years).

Today, I therefore continued hunting, with mixed success. I had three more CVs – all asking for more money, and one looked quite good – but had a requirement he worked remotely after the first few days (well he does live in Telford). Another, who is local, I’m interviewing tomorrow. Wanting to do some homework on him, I had a look at a couple of websites mentioned in his doctored CV  – the first is clearly .Net from the error message it throws when you pass a > into it’s search box – so either they replaced his PHP site quickly or his CV is misleading. The second has a PHP error on it – and is only (effectively) a themed wordpress site which looks like it’s slowly rotting. From these I found out his address (hint: whois $flamingodomain) and an invalid email address/domain (which archive.org seems to not do much with). Typing in his name into Google / LinkedIn, Facebook etc produces no obvious matches. So I know hardly anything about him, and for all intent he may as well not exist. Great sales job there.

From talking to the recruiters it seems it’s difficult to find decent PHP programmers – and anyone who may be decent will almost certainly not be programming PHP as their primary language (i.e. they’ll be doing web development in Java/.Net, and know PHP quite well). This seems a shame, but really only confirms what I already knew from interacting with others in the community. I’ve known for ages that I’ve effectively taken a large pay cut by running my own company, and doing PHP. It sucks that this continues to be the case. Clearly I’m a martyr or something.

So, if you happen to be a contractor looking for work, please make an effort. I’m not overly impressed so far, and may just end up stalling customers for another week/month instead.

(Oddly I wrote this post, posted it, and it vanished. What are you up to wordpress? Why do you want me to retype things in twice?)

Random PHP project progress

Initially when we founded Pale Purple all our new PHP development used a combination of Propel, Smarty and some inhouse glue. Over time we seem to have drifted towards the Zend Framework, but I’ve never been particularly happy with Zend_Db or Zend_View. Why the Zend Framework? Well, it has loads of useful components (Cache, Form, Routing, Mail etc) and it’s near enough an industry standard from what we see – and obviously I’d rather build on the shoulders of others than spend time developing an in-house framework no one else will ever use.

For one customer, we’re currently working on the next iteration of their code base – which incorporates a number of significant changes. When we inherited the code base from the previous developers we spent a long time patching various SQL Injection holes (casting to ints), moving over to use PDO’s prepared statements and trying to keep on top of the customer’s new functionality requests and support issues. There’s still a lot of horrible code to refactor, plenty of security holes (although none public facing) and we know we’re moving in the right direction – hopefully patching and duct tape will soon be a thing of the past as it will develop some form of architecture and look like someone has thought about design and long term maintenance.

I’ve started to properly do Test First Development – at least from a support perspective – as too often we’d find we would patch a bug, only for it to reappear again in a few weeks/months time. This has been especially useful with the SOAP interface the application exposes. The tests run every 5 minutes, and we all get emailed when stuff breaks – it took all of 30 minutes to setup and put in place – then it was just a case of actually writing the unit tests themselves (the tests take minutes to write; finding/fixing any bugs they pin point takes somewhat longer :-/ ). I’ve also abused Simpletest’s web testing ‘stuff’ to also act as an availability checker of the live site (i.e. hit a few remote URLs, and check that we don’t get error messages back and do see expected strings).

The original code base had no ‘model’ like layer (or MVC ‘compliance’) – files containing HTML, CSS, SQL, Javascript and PHP were the norm – we’ve added Propel to the project as the ‘model’ layer – which took a few hours; and then when reverse engineering the database we found a few oddities (tables without primary keys and so on) – anyway, moving the functionality from a handful of legacy objects across into the Propel ones seems to be well underway, and I for one will be glad to see the end of :

$x = new Foo(5);

Accompanied with code that does the equivalent of :

class Foo {
    public function __construct($id = false) {
        if($id != false) {
            // select * from foo where id = 5
            // populate $this; don't bother checking for the edge case where $id isn't valid
       }
       else {
           // insert into foo values ('');
          // populate $this->id; leaving all other fields as empty strings...
     }
     public function setBaz($nv) { // repeat for all table fields
         $this->baz = $nv;
         global $db;
         $db->query('update foo set baz = "' . $nv . '" where id = ' . $this->id);
     }
}

Finally, we have a meaningful directory structure – where some things aren’t exposed in the document root. Hopefully soon a front controller and some decent routing. At the moment a huge amount of code is just sat in the ‘public’ directory due to it’s nature. We hope to fix this in time, and move to using Zend Controller – once we get Smarty integrated with it.

Propel has added some nice new features since we last used it (effectively v1.2); it was a toss up between it and Doctrine (as obviously the ZF is moving in that direction) – but we already had knowledge/experience with Propel and it seemed the easier option.

I’m hoping that with time we’ll be able to get up to at least 60% test coverage of the code base – at that point we should be able to refactor the code far easier and with less fear. At the moment I doubt the unit tests cover more than 5-10% – although maybe it’s time I pointed xdebug or whatever at it to generate some meaningful stats.

My final task is to get some decent performance measurements out of the code base – so we can track any performance regressions. I’m fairly confident that moving to Propel will result in an speedup as duplicate object hydrations will be eliminated thanks to it’s instance pool, however having hard figures and nice graphs to point at would be ideal. So far I’ve knocked up my own script around ‘ab’ which stores some figures to a .csv file and uses ezComponents to generate a graph file. This seems to be a crap solution, but I can’t think or find anything better. Any suggestions dear Internet? Perhaps I should integrate changeset/revision id’s in my benchmarking too. Suggestions here would be exceedingly appreciated.

There, I should have ticked all necessary boxes wrt development practices now. Now to work on finding a contract PHP developer….