Travis CI, PHP 5.2 and PHPUnit

Testing a PHP 5.2 compatible package on Travis with PHPUnit.

Why bother?

In a canned answer: due to WordPress minimum requirements.
While PHP 5.2 has been deprecated for some time there is still software that will probably not run on it (a little portion of WordPress installations still runs on PHP 5.2).
Second best answer for the thrill of it.
Third answer: there are some edge cases that will only really pop up when running the tests on PHP 5.2 that go way beyond linting the code or using it in a virtual machine; that’s the reason why the WordPress automated test suite itself runs on PHP 5.2 too.

Real world example

I’ve decided to put my own attempt at a dependency injection container that’s PHP 5.2 compatible to the test and tap into Travis CI and its support of PHP 5.2 builds to try and test it on milestone PHP versions from 5.2 up to 7 and hhvm.
What I thought would be a question of minutes became a question of hours as I’ve found out the many edge cases and pitfalls of such and endeavor and extended my knowledge of the CI environment.

PHPUnit version

The latest version of PhpUnit to run on PHP 5.2 is the 3.6.12.
Due to the age of the version it is not available using Composer writing something like this:

composer require --dev phpunit/phpunit:3.6.12

The oldest version of PHPUnit available on Packagist is the 3.7 that is, but, compatible with PHP 5.3 and above.
The solution is to rely on the power of the open source community and specifically the garex/phpunit package.
Following the instructions I’ve copied and pasted the require-dev and repositories line into the composer.json file ending up with this:

{
"name": "lucatume/di52",
"description": "A PHP 5.2 compatible dependency injection container.",
"authors": [
{
"name": "Luca Tumedei",
"email": "luca@theaveragedev.com"
}
],
"psr-0": {
"src/"
]
}
},
"minimum-stability": "dev",
"require": {
"php": ">=5.2",
"xrstf/composer-php52": "1.*"
},
"require-dev": {
"phpunit/phpunit-php52": "dev-3.6.12-php52",
"phpunit/phpunit-mock-objects-php52": "dev-1.1.0-php52"
},
"repositories": [
{
"type": "git",
"url": "https://github.com/garex/phpunit"
},
{
"type": "git",
"url": "https://github.com/garex/phpunit-mock-objects"
}
],
"scripts": {
"post-install-cmd": [
"xrstf\\Composer52\\Generator::onPostInstallCmd"
],
"post-update-cmd": [
"xrstf\\Composer52\\Generator::onPostInstallCmd"
],
"xrstf\\Composer52\\Generator::onPostInstallCmd"
]
}
}

A composer update makes sure that the dependencies are downloaded.
What’s to note is that the package will not install a phpunit bin but a phpunit-php52 bin; calling it from the project root folder is easy enough:

./vendor/bin/phpunit-php52

Still not on PHP 5.2

On my local machine I use a setup similar to the Laravel Valet relying on homebrew to manage the services; being as close as possible to the machine it saves tons on battery life that would else be consumed by virtual machines and convoluted setups; it’s not how I develop any project but it is how I developed DI52.
I’ve been a MAMP user in my not so far first days and to this day I run to it every now and then when I need to test a project in a PHP 5.2 environment; it comes with the php executables of many php versions and I’ve set an alias in my terminal to be able to execute a command using a specific one, e.g.

alias php52="/Applications/MAMP/bin/php/php5.2.17/bin/php"

will allow me to run a PHP script using the 5.2 bin quickly.
Once the dependencies are in I ran:

php52 ./vendor/bin/phpunit-php52

and cried at the unending list of errors.

The right PHPUnit manual

One of the errors that popped up the most, beside the syntax based ones, are those where I’m using fucntions and possibilities that are not part of PHP 5.2; one above all: closures.
While I do want to be able to use closures in di52 I also need to skip any closure based test if the tests are running on PHP 5.2. It took me a while to figure out that some of the features I was relying upon to exclude tests where simply not part of PHPUnit version 3.6.12 code; the decisive action was reading the right manual: the version 3.6 one.
I had a number of gotchas about how things work and ended up creating separate test cases for any test using closures.

Updating Composer on Travis and PHP 5.2

The problem with the setup is that when the package is tested on Travis and PHP 5.2 Composer will not be available.
This is really a minor nuisance that’s easily resolved by using the same tool Travis uses to manage PHP versions: phpenv(!g).
In di52 Travis configuration file I’m conditionally switching the global PHP version to 5.3 to update Composer and conditionally switching it back to run the tests:

language: php

php:
- '5.2'
- '5.3'
- '5.4'
- '5.5'
- '5.6'
- '7.0'
- hhvm
- nightly

before_install:
- if [[ ${TRAVIS_PHP_VERSION:0:3} == "5.2" ]]; then phpenv global 5.3; fi - composer update - if [[${TRAVIS_PHP_VERSION:0:3} == "5.2" ]]; then phpenv global "\$TRAVIS_PHP_VERSION"; fi

script:
- ./vendor/bin/phpunit-php52 --version
- ./vendor/bin/phpunit-php52

Excluding non PHP 5.2 compatible tests

The DI52 package is PHP 5.2 compat but aims at being a solution I can use everywhere, for that reason I’m testing it on the array of PHP versions supported by Travis.
Since I’ve moved the tests that can run on PHP 5.3 and above to separate classes the last piece of the puzzle is to explicitly include those only if the required PHP version check is satisfied:

<phpunit bootstrap='tests/bootstrap.php'
colors='true'>
<testsuites>
<testsuite name='All'>
<file>tests/ContainerTest.php</file>
<directory>tests/bindings</directory>
<directory>tests/functional</directory>
<directory suffix="Test.php" phpVersion="5.3.0" phpVersionOperator=">=">tests/closure</directory>
</testsuite>
</testsuites>
</phpunit>

In a satisfying display of gamification here are the results: