Last Updated: February 25, 2016
· falsetto

Use PHP with your Yeoman dev server

Note: I haven't used this stack for a couple of years. Judging by the comments, this protip has gone fairly stale. Take what you can, but just know that YMMV.

Ever needed to use a PHP script in your Yeoman project? It's quite straightforward to do so while keeping all of the goodness that Yeoman brings together for you (particularly live reloading).

The integration is accomplished via a middleware by Felix Gnass named gateway. Gateway enables you to specify CGI handlers for requests matching certain extensions (.php in our case). Mr. Gnass wrote a blog post on this protip's topic, but didn't include step-by-step instructions for the middleware integration, which took me a while to figure out.

Example code

Generate an app with Yeoman

$ mkdir php-from-yeoman
$ cd php-from-yeoman
$ yo webapp

Ensure that php-cgi is in your $PATH

$ which php-cgi

If you get php-cgi not found, here's how to install it using Homebrew on OS X (for other OS's, Google is your friend):

brew tap homebrew/dupes
brew tap josegonzalez/homebrew-php
brew install php54

Setup gateway module

$ npm install gateway

Modify Gruntfile.js by adding the following near the top:

var gateway = require('gateway');

Modify the array returned by the function at connect.livereload.options.middleware to look like this:

middleware: function (connect) {
  return [
    gateway(__dirname + '/app', {
      '.php': 'php-cgi'
    mountFolder(connect, '.tmp'),
    mountFolder(connect, 'app')

Rename app/index.html and test

$ mv app/index.html app/index.php

Add <?php echo "<h1>Hello from PHP!</h1>"; within the body tag of app/index.php.

Then fire up your development server:

$ grunt server

Assuming that your browser launched and loaded http://localhost:9000/, you should see Hello from PHP! on the page.

Make some changes to app/index.php, save the file, and your browser should automatically reload.

Note: due a to naïve conditional in the livereload library, live reloading will only work for PHP files when the URL ends with a '/' (so basically, index.php files).

16 Responses
Add your response


Great tip, worked like a charm, I was using yeoman with angularjs and needed to bring in some php for server side support and your tip was very helpful and well written.


over 1 year ago ·

@davidchase03, glad to hear it!

over 1 year ago ·

Please provide a date for this article. It is incredibly disorienting to read blogs about cutting edge stuff without knowing if it's up to date or not.

over 1 year ago ·

Also, your solution worked perfectly, thank you for this :) Much obliged

over 1 year ago ·
Profile image 3 gray small

Great tip! Do you have any other tips about using a MAMP server to point to a Yeoman generated app? I mean, I don't want to use localhost:9000 anymore. I want my MAMP domain alias to point to /app and still be able to test with livereload. Is that possible?

over 1 year ago ·

@wjonthomas: Glad it helped!

Unfortunately, I've only investigated the process of getting a node.js server to interpret PHP files using the php-cgi cli. Getting a MAMP server (Apache+PHP) to load and execute node.js code based off of a Gruntfile would be a wholly different thing that I haven't tried.

I'd be very interested to know if you figure out how to do it.

over 1 year ago ·
Profile image 3 gray small

@falsetto I think a lot of people would be interested to see that. I fooled around for a bit. Seems like you can easer use the grunt connect server, or remove all of that convenience and use your own server. You lose a lot of the convenience of just running grunt server and being on your way though. I will definitely report back if I come across any ways to improve the workflow with Grunt/MAMP.

over 1 year ago ·

I created a patch to edit your Gruntfile.js to config the server to run on / directory instead of app/ (useful for legacy project). See:

over 1 year ago ·

Thanks!! Time saver!

over 1 year ago ·
48d74e69c570096b6d40ea72b9fcf921 normal

I'm interested in streamlining things like that as well. I think, though, that using pow might be a good option. This could give you the hostname flexibility, and also the convenience of firing things up with a single grunt watch command. I'll report back if I find success, but wanted to put the idea out there for someone to find if I wander off course :)

over 1 year ago ·

Sounds like what I was looking for. I am extremely interested for the workflow with Grunt/MAMP.

Thank you!

over 1 year ago ·

There's no definition of lrSnippet anywhere so I got the following warning:

Running "connect:livereload" (connect) task
Warning: lrSnippet is not defined Use --force to continue.

I added the following at the top of Gruntfile.js:

var lrSnippet = require('grunt-contrib-livereload/lib/utils').livereloadSnippet;

and installed grunt-contrib-livereload and saved to package.json with the following from the terminal:

npm install --save-dev grunt-contrib-livereload

Then I discovered I was missing other dependencies and added the following to Gruntfile:

var path = require('path');

var mountFolder = function folderMount(connect, point) {
return connect.static(path.resolve(point));

At this point I see an Internal Server Error 500. After some fiddling I found that changing the following:

gateway(__dirname + '/app', {
'.php': 'php-cgi'


gateway('<%= %>', {
'.php': 'php-cgi'

finally got everything in order and grunt serve ran successfully without a 500 error, although my browser only says: Cannot Get / Here's a gist of my gruntfile: any idea what's missing here? Also this is the feedback from grunt:

Running "connect:livereload" (connect) task
Verifying property connect.livereload exists in config...OK
File: [no files]
Options: protocol="http", port=9000, hostname="localhost", base=".", directory=null, keepalive=false, debug=false, livereload=35729, open, useAvailablePort=false, middleware=undefined
Started connect web server on http://localhost:9000

Not sure why it says middleware undefined either. :\ Really wish I could easily spin up a PHP webapp that's not just php served from one "api" folder and instead the entire app. Any assistance here is greatly appreciated.

over 1 year ago ·

Doesn't work with latest yeoman webapp

lrSnippet is not defined

mountFolder is not defined

// Chris D. 14-Jan-2015

over 1 year ago ·
9988b8bc96b4ab386fad51df509f75ff normal

Yesterday worked like a charm, today not. For some reason the app doesn't find bower components anymore. If I change my php file back to html and then Grunftile back to original, it all works.

This leaves me wondering: this can't be the easiest way to develop with php on 2015?


I also get "connect: res.headerSent: use standard res.headersSent" when I run grunt serve


Oh, I'm so incredibly noob. I 'm pretty new with this stuff so most of the time I just copy/paste the rows to various places not really understanding the process. I didn't realise that I'm not supposed to replace "connect.livereload.options.middleware" with

return [
gateway(__dirname + '/app', {
'.php': 'php-cgi'
mountFolder(connect, '.tmp'),
mountFolder(connect, 'app')

but leave the lines

connect().use('/bowercomponents', connect.static('./bowercomponents')),


Now it works again. However, there is a weird behavior: the browser downloads index.php on every livereload.

Any ideas?

over 1 year ago ·

I don't find "connect.livereload.options.middleware"
Where is this ?

over 1 year ago ·

Hey @abumalick, here's the line from the finished file:

I haven't used this dev environment for a couple years now, so YMMV.

Good luck!


over 1 year ago ·