Loading...
Loading...
Marco Franssen

In this blog post, I want to show you how to use Gulp.js to automate PHP code quality checks, from linting and tests to static analysis. Before we dive into the examples, let's take a quick look at Gulp.js itself. If you already know it, feel free to skip ahead to the plugins.
Easy to use By preferring code over configuration, gulp keeps things simple and makes complex tasks manageable. Efficient Using the power of node streams, gulp gives you fast builds that don't write intermediary files to disk.
Those statements come from the Gulp.js homepage. Like Grunt.js, Gulp.js is a task runner built on Node.js, which also had the io.js fork at the time of writing. Grunt.js takes a configuration-based approach to tasks, while Gulp.js uses a code-based approach. Both task runners have a wide range of plugins.
So what is the real power of Gulp.js?
In my opinion, it's the use of Node.js streams, which makes Gulp.js a fast, memory-efficient task runner. The difference between Gulp.js and Grunt.js is especially noticeable on larger projects with lots of files.
Many developers limit these task runners to JavaScript projects. Last time, I showed you how Grunt.js can automate some .NET/C# tasks. Today, I want to show you how Gulp.js can help with PHP projects too. So here's my call to action: stop limiting yourself and try it on whatever project you're working on, no matter the language!
In a previous blog post, I showed you how to use Grunt.js to automate development tasks. Today, we'll look at Gulp.js, so let's get started. Install the npm package and create a gulpfile.js in the root of your project.
npm install -g gulp
npm install --save-dev gulp"use strict";
var gulp = require("gulp");Like Grunt.js, Gulp.js has plenty of plugins. Let's zoom in on a few we can use with PHP.
Some of these tasks might look a little silly at first, but keep reading. They'll make more sense when we get to Gulp's watch feature.
npm install --save-dev phplintAdd the following code to your gulpfile.js. A glob pattern selects the files to check with phplint, and exclusion patterns let you skip folders.
var phplint = require("phplint").lint;
gulp.task("phplint", function (cb) {
phplint(
["./**/*.php", "!node_modules/**/*", "!vendor/**/*"],
{ limit: 10 },
function (err, stdout, stderr) {
if (err) {
cb(err);
} else {
cb();
}
}
);
});This task runs phplint on every file matching the glob patterns.
npm install --save-dev gulp-phpunit
composer require --dev "phpunit/phpunit"Add the following task to your gulpfile.js.
var phpunit = require("gulp-phpunit");
gulp.task("phpunit", function () {
return gulp
.src("phpunit.xml")
.pipe(phpunit("vendor/bin/phpunit"))
.on("error", console.error("TESTS FAILED:\nYou killed someones baby!"))
.pipe(function () {
console.log("TESTS PASSED:\nAwesome you rock!");
});
});This task runs PHPUnit with the settings in your phpunit.xml file.
npm install --save-dev gulp-phpcs gulp-shell
composer require --dev "squizlabs/php_codesniffer"Add the following tasks to your gulpfile.js.
var phpcs = require("gulp-phpcs");
gulp.task("phpcs", function () {
return gulp
.src(["./**/*.php", "!node_modules/", "!vendor/**/*"])
.pipe(
phpcs({
bin: "vendor/bin/phpcs",
standard: "PSR2",
warningSeverity: 0,
})
)
.pipe(phpcs.reporter("log"));
});
gulp.task(
"phpcbf",
shell.task([
"vendor/bin/phpcbf --standard=PSR2 --ignore=vendor/,some/other/folder folder/to/include another/folder/to/include somefiletoinclude.php server.php",
])
);As you can see, phpcs also ships with phpcbf, which can automatically fix many coding standard violations. Unfortunately, there's no Gulp.js plugin for this tool in this setup, but we can use gulp-shell to run it. Both tasks use the PSR2 standard.
npm install --save-dev gulp-shell
composer require --dev "phpdocumentor/phpdocumentor"Add the following task to your gulpfile.js.
var shell = require("gulp-shell");
gulp.task(
"phpdoc",
shell.task([
"vendor/bin/phpdoc -d . -t docs/phpdoc -i vendor/,node_modules/,server.php",
])
);It's a similar story for phpdoc. We use gulp-shell to run it from the root of our project, excluding vendor, node_modules, and server.php from the generated documentation.
npm install --save-dev gulp-shell
composer require --dev "phpmd/phpmd"Add the following task to your gulpfile.js.
var shell = require("gulp-shell");
gulp.task(
"phpmd",
shell.task([
"vendor/bin/phpmd . codesize,unusedcode,naming,design,cleancode,controversial --reportfile docs/phpmd.html --exclude vendor/,node_modules/ --suffixes php",
])
);There's no Gulp.js plugin for PHP Mess Detector in this setup either, but no worries: we can use gulp-shell again. Here, phpmd checks code size, unused code, naming, design, clean code, and the controversial ruleset.
npm install --save-dev gulp-shell
composer require --dev "pdepend/pdepend"Add the following task to your gulpfile.js.
var shell = require("gulp-shell"),
pdependDocs = "docs/pdepend";
gulp.task(
"pdepend",
shell.task([
"mkdir -p " + pdependDocs,
"vendor/bin/pdepend --summary-xml=" +
pdependDocs +
"/summary.xml --jdepend-chart=" +
pdependDocs +
"/chart.svg --overview-pyramid=" +
pdependDocs +
"/pyramid.svg --ignore=vendor,node_modules --suffix=php .",
])
);For pdepend, we also use the shell plugin. The array contains two commands: the first creates a folder for the output, and the second runs pdepend, writing summary.xml, chart.svg, and pyramid.svg to the docs/pdepend folder.
Unlike Grunt.js, Gulp.js has built-in file watching, so you don't need another plugin to watch your files and directories for changes.
With Gulp.js watch, we can monitor our project files and automatically run tasks when those files change. Let's use it to trigger some of our checks whenever a file matches the glob patterns.
gulp.task('watch', function () {
gulp.watch(['composer.json', 'phpunit.xml', './**/*.php', '!vendor/**/*', !node_modules/**/*'], function (event) {
console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
});
gulp.watch('composer.json', ['dump-autoload']);
gulp.watch(['phpunit.xml', './**/*.php', '!vendor/**/*', !node_modules/**/*'], ['phplint', 'phpcs', 'phpunit']);
});
gulp.task('default', ['phplint', 'phpcs', 'phpunit', 'watch']);The task above prints a short message when a file changes and runs the configured tasks. We've also defined a default task that runs our quality checks once and then starts watching. Each time you change a PHP file, phplint, phpcs, and phpunit run again, giving you immediate feedback.
These examples are just the beginning of what you can do with Gulp.js. You could also use Git hooks to run some of these tasks before a push. I'd love to hear how you're using it in your projects.
Marco Franssen
Compare Jasmine 2.4 and Mocha 2.3, including test structure, Chai assertions, Sinon spies and asynchronous testing with callbacks and promises.
Marco Franssen
Configure SSH keys, Pageant, agent forwarding, and Git Bash on Windows to work with Linux VMs and Git without repeatedly entering key passphrases.
Marco Franssen
Customize a Debian VirtualBox VM with Packer user variables, file uploads, and shell provisioners in this 2014 tutorial covering MySQL and Apache setup.
Marco Franssen
Build a reproducible Debian Wheezy VM with Packer and VirtualBox in this 2014 tutorial, using a JSON template and preseed file for unattended installation.