The magic of grunt-contrib-connect, and how to run PHP with it
Note: This article is rather old. If you want to know more about `connect`, proceed, if you just want to have a PHP sever with livereload for your Grunt or Gulp setup, go there
One of the most loved Grunt.js extensions in our team is the ability to spawn a server for your project with the virtual push of a button, and to be able to see all the changes directly in the browser with a little Livereload magic. The seemingly endless Apache configuration days seemed to be over, and every front-end dev in our group was happy with the new workflow established. However, from time to time there was the urge to run some little PHP scripts. Not the big apps, mostly an inclusion of CMS managed labels. This need brought me to delve deeper into the mysteries of the connect task.
Behind every great task lies a great program: connect
Same goes for connect. The task you install with grunt-contrib-connect just provides an interface to SenchaLab's Connect node module. Connect itself is a middleware framework for node's
http package. Which means it comes with a lot of standard tasks which you need when creating a server.
Creating a server is already a rather simple task with node, but connect has some really neat built-in middleware for browsing a directory, serving files with the correct mime-type, handle sessions, etc. It also comes with a lot of third party middleware, one of the most popular ones being mentioned connect-livereload, or the proxy middleware we bespoke earlier on.
So how does this middleware tool work? Rather easy. Connect creates a stack of different middleware tasks and runs through this stack on every request taken. The middleware itself checks if it has something to do with this request, or if the request just has to be passed on to the next middleware in the stack.
res object should be familiar with all of you who did create a server with the
http module in node once. For all the others, in a nutshell:
req represents the request,
res the response, i.e. the stuff you want to appear in the browser.
next is a function which just calls the next middleware in the stack. Stephen Sugden wrote a good tutorial on creating middleware with really useful examples. So check that out if you want to know more on that topic.
Straight-forward, actually. It creates an array where to serve all static files in the defined base-directories (which can be an array of directories, but does not have to). It also uses connect.directory to make the main app directory browsable. This is the most basic server you can get.
At the time of this writing, it isn't possible to access the middleware array directly from your Gruntfile.js. But you can override the middleware function from the task, to create your very own stack of middleware for connect. Alas, this will kill the basic serving of directories and static files. So I suggest to reimplement the middleware function from above and insert your middleware snippets an the appropriate place. We'll get on to that below. The livereload option still will work whatsoever.
A PHP middleware
Before we continue, a quick disclaimer: Yeah, we are going to recreate the possibility of serving PHP files, but we won't be able to use all the server variables of PHP like
$_POST. Well, yet. I'm working on that issue, but for basic tasks this should work nonetheless.
So, to make PHP files parseable, we need to do two things:
- Create a middleware that executes PHP
- Include this middleware into our Grunt connect task
Even if it's kind of rough, the first part is actually really easy: Every time we get a request to a
.php file, we call the php command line interface to parse this file, and write the result into our response:
This code snippet makes use of the
exec module of node. The
directory parameter points to the served folder in your filesystem. This code above lacks some initialisation methods, but you can install the whole middleware function from the NPM registry via
npm install connect-php
Include the new middleware in your Gruntfile.js
And that's it. You are now able to parse basic PHP files!
Actually, the whole PHP thing is just a way of showing you how to extend connect with middleware that serves your own needs. For us, the possibility of
include-ing in PHP is enough to develop our website templates without having broken output while coding. It still lacks major features, but it's a good start. I'm thinking of reworking the script to tunnel all requests to a spawned PHP server to fill this gap, so stay tuned!
Trying to extend our developing environment with this certain feature taught me a lot about the mechanics behind Grunt.js and the Node.js cosmos behind that. I think it's the beauty of Grunt.js to demand nothing more than being a simple task runner for node tasks, and thus being exceptionally extensible. We didn't have this freedom and power with all the other build tools and developing environments we had before.