PHP Apps with Multiple Document Roots

Many PHP developers using Nanobox have been waiting for a way to specify unique document roots per web component. While previously possible, it wasn't easy or intuitive. It consisted of generating custom webserver config files and specifying those config files in start commands. As of this week, the PHP engine now supports custom document roots per web component using the PHP engine's provided helper scripts.

PHP Engine Helper Scripts

In many ways, Nanobox's PHP engine is different from all other engines. One of these differences is that it generates all your webserver configuration files based off settings in your boxfile.yml. It also provides helper scripts that make it easy to start the necessary processes to run your app using the appropriate configuration files.

php-server   # Starts everything your app needs in dev
             # This should only be used for local development

start-php    # Starts PHP-FPM or the built-in PHP webserver
start-apache # Starts Apache
start-nginx  # Starts Nginx

Nanobox engines assume "One Codebase = One build." Even when running multiple web components, they all still use the same build. Since the PHP engine generates the necessary webserver config files as part of the build, the document_root specified in your boxfile.yml is used for all web components. With the recent update to the PHP engine, the helper scripts now provide a way to override the document_root specified in your boxfile.yml.

Each of the PHP engine's helper scripts now accept a single argument - a document root filepath.

php-server path/to/dir
start-php path/to/dir
start-apache path/to/dir
start-nginx path/to/dir

When passing a document root to the helper scripts, the filepath should be relative to the root of your project.

Real-World Example

Let's say I have a PHP application that consists of a simple front site, an API, and a blog. Each is essentially a standalone app. My front site consists of simple PHP templates populated by backend data, my blog is WordPress, and my API is, well, an API. My file structure looks like this:

my-awesome-app
  ├── api
  ├── front
  ├── wordpress
  └── boxfile.yml

I'm using Apache and PHP-FPM (the PHP Engine defaults), so Apache is the process that actually handles my document root. In each of my web components' Apache start commands, I just pass the directory that should be used as the document root into the start-apache helper.

run.config:
  engine: php
  ...

web.front:
  start:
    php: start-php
    apache: start-apache front
  routes:
    - /

web.blog:
  start:
    php: start-php
    apache: start-apache wordpress
  routes:
    - blog:/

web.api:
  start:
    php: start-php
    apache: start-apache api
  routes:
    - api:/

Quick Notes

Helpers & Webservers
Which helpers to use and which to pass the document root into depend on which webserver you're using. More information is provided below.

Routes
When using multiple web components, you must specify routes for each so your app's router knows where to route requests. With this configuration:

mydomain.com would route to web.front
blog.mydomain.com would route to web.blog
api.mydomain.com would route to web.api

Using Document Roots in Local Development

When running in a local dev environment, you can only use one document root at a time since the webserver is configured to listen on a specific port. If using the php-server helper to run multiple processes, they would all be listening on the same port and nothing would work due to port conflicts.

php-server api

Note: Since only one code container is started when developing locally, start commands and routes are not used. Whatever document root you use to start your app will be used for any/all DNS aliases assigned to your app.

Setting the Document Root with Different Webservers

The PHP engine provides options for both webservers and PHP interpreters:

  • Apache + PHP-FPM (the engine's default)
  • Apache + mod_php
  • Nginx + PHP-FPM
  • PHP's Built-In Webserver

Depending on which you're using, you will need a specific boxfile.yml configuration and your custom document root will need to be passed into a specific helper.

Apache + PHP-FPM

run.config:
  engine: php

web.front:
  start:
    php: start-php
    apache: start-apache path/to/dir
  routes:
    - /
...

Apache + mod_php

run.config:
  engine: php
  engine.config:
    apache_php_interpreter: mod_php

web.front:
  start:
    apache: start-apache path/to/dir
  routes:
    - /
...

Nginx + PHP-FPM

run.config:
  engine: php
  engine.config:
    webserver: nginx

web.front:
  start:
    php: start-php
    nginx: start-nginx path/to/dir
  routes:
    - /
...

PHP's Built-in Web Server

run.config:
  engine: php
  engine.config:
    webserver: builtin

web.front:
  start:
    php: start-php path/to/dir
  routes:
    - /
...

Posted in PHP