It has been a very long time since I have used PHP in any capacity for web development. And I am not planning doing so again any time soon (sorry). But I have had the opportunity to apply my practices on a very small, though still useful scale in PHP. From what I could gather it is indeed very possible to properly and continuously integrate PHP projects, provided you use the right tools for the job: a build tool and a dependency manager (optional but always recommended).
A simple project
Disclaimer: I do not condone illegal behaviour… But for research purposes I have created a custom search module for Synology Download Manager, and as it happens this requires PHP. Hence the synology-dlm-rarbg project (https://github.com/mcartoixa/synology-dlm-rarbg). No Laravel or Symfony involved here obviously but I want to believe that the concepts will remain the same on a more consequent project.
The main elements of the project are:
- The build file (
- The script file (
build.bat) that helps executing the build file locally.
- The CI configuration file (
The build file
Given my lack of recent experience with the technology I was a bit worried at first about the state of software development in PHP. But it turned out surprisingly pretty decent: you can find a fine dependency manager in Composer and a very decent build tool in Phing. It is based on the old timer Apache Ant and so probably suffers from the same drawbacks (that I will cover in a subsequent post). But like the original: as long as you respect the tool (which involves understanding it…) you can go pretty far with it.
- clean: cleans the build (the
- build: runs PHPLint on the source code and copies the source code and the dependencies in the
- If I had to do it today I would probably isolate the first action in an analyze target. The second action would do well in the package target. My own consistency evolves over time…
- test: executes the tests using PHPUnit.
- package: creates a package for the search module (which is an archive of the source code and its dependencies).
- rebuild: shortcut for the combination of clean and build.
- release: shortcut for the combination of clean, build, and package.
Before showing some code it appears that I may have over engineered this build a bit: I created a second build file
dlm\build.xml that is called by the first. Obviously I cannot clearly remember why right now but I think it had to do with the possibility of packaging multiple search modules in the same project. Anyway, how do you call another build file then? Simple:
<target name="test" depends="prepare.build"> <phing phingfile="build.xml" dir="./dlm" target="test" inheritAll="true" haltonfailure="true" /> </target>
<target name="test" depends="test.prepare"> <phpunit printsummary="true" bootstrap="vendor/autoload.php" haltonerror="true" haltonfailure="true"> <formatter type="plain" usefile="false" /> <batchtest> <fileset dir="./tests"> <include name="**/*Test*.php"/> </fileset> </batchtest> </phpunit> </target> <target name="test.prepare"> <composer command="update" composer="bin/composer.phar"> <arg value="-q" /> <arg value="-n" /> </composer> </target>
The key in this project is to be able to package the search module in the format that is expected by the Download Manager, which is explained in a PDF document downloadable somewhere on the Synology website. I would never remember how, but now it has been described in Phing it seems pretty straightforward:
<target name="package" depends="package.prepare,build"> <tar destfile="tmp/out/bin/mcartoixa_rarbg.dlm" compression="gzip"> <fileset dir="tmp/bin/dlm"> <include name="**/**" /> <exclude name="composer.*" /> </fileset> </tar> </target>
The script file
CALL .\bin\composer.bat install -q -n CALL .\vendor\bin\phing.bat -f %PROJECT% %TARGET%
As you can see Composer is itself stored in the repository as a PHAR archive. I could have chosen to download it instead. The key here is that the repository is ready for development: nothing to configure.
The CI configuration file
For whatever reason I have again chosen Travis CI as my Continuous Integration platform, which configuration is simply:
install: - php bin/composer.phar install -n script: - vendor/bin/phing -f build.xml prepare.version release
A more complete project
Nothing much to be found here (as usual?), but I think proper foundations have been laid out above. As with every technology it is essential to delve into the build tool, understand it and use it to its full potential. For instance, if Phing is anything like Apache Ant (and it looks like it is), understanding the core structures (like FileLists of FileSets) is very important.
Also, I would tend to install every dependency locally instead of globally (as part of the build, including frameworks like Laravel for instance). It makes it easier to handle the
%PATH% consistently for everyone (including your Continuous Integration platform) and it makes it easier to use different versions on different projects (or even branches).