February 5, 2020
Recently I had to touch one of our older APIs that is running in PHP. It had been many years since I had touched a PHP API, and was several OS reinstalls and at least 1 laptop ago. So naturally I was apprehensive of the native extensions and requirements to get the PHP environment running.
..in comes Laradock..
Some of the other developers on the team, who have more experience in the PHP world that I have moved to a more modern way of developing PHP by using docker containers. I have naturally been using docker containers for pretty much all services for several years now, but I had never used it in this way for development. So I wanted to share my experience.
Laradock is a collection of docker containers that can be run to create a connected PHP environment that is isolated from your machine, so you do not need to worry about the native requirements of the extensions. These docker containers are all available via the open source GitHub repository laradock/laradock.
The first thing that we need to do is configure a few items in the Laradock project for our project. The project I was working on was a very simple rest API that has very few dependencies, for this project we use these Laradock containers:
To get our application to work inside the docker containers we need to enable a few extensions that will be installed into the php-fpm and workspace containers. To enable these extensions we need to configure a .env file that docker-compose will use when starting the docker services.
First we need to rename the env-example file to .env, this should be in the root of the Laradock project (next to the docker-compose.yml file). This file contains a very long list of variables that are used by the docker-compose.yml to build the docker images. The changes needed in this file are dependent on your application, for our application I needed to change:
There is documentation for the Laradock configuration available on laradock.io.
In some of the containers that we need we have to add some extra configurations to allow us to use a debugger. In the xdebug.ini we need to change the following configuration options to enable the xdebug configuration.
A quick word about the working directory. Laradock will by default mount the parent directory of the Laradock root, into the docker containers as '/var/www/'. So if you clone the Laradock project to '~/repo/laradock' then the whole of the '~/repo' directory will be mounted into the directory '/var/www/' inside the docker container. This information will be need when configuring NGINX and IntelliJ IDEA.
The NGINX container is the webserver that we are using to serve the application, as this is running locally we need to make a few modifications to ensure that the application is served correctly. The configuration file for NGINX is in the 'nginx -> sites' folder in the Laradock repository. First we create a new config file, I like to name this after the application that it will be running, in this case I have named it 'account.conf' (as the application is related to account management).
In this file we need to define the configuration of the application, as a template I recommend copying the 'default.conf' file that is in the directory. Then all we need to do is correct a few items.
With the above configuration it will be possible to access the account service on the hostname 'accountservice'. However unless you happen to have a DNS that resolves this to 'localhost', it will be difficult to access this hostname. Assuming you are not running your own DNS server, the easiest way to get 'accountservice' to resolve to 'localhost' is to add an entry to your '/etc/hosts' that looks like this.
$> sudo vi /etc/hosts
# laradock config
This approach will allow you to host multiple PHP APIs through the Laradock containers, by simply adding a NGINX config with the 'service_name' and 'root'. Then adding an appropriate config into you machines '/etc/hosts' file. Both the NGINX configs and the '/etc/hosts' file can be modified without the need to rebuild the docker containers, although a restart of the NGINX container might be necessary.
Once we have configured the environment we have to build the dockers, to do this we simply run the docker-compose command to start the NGINX container. This will automatically build the images if they are not available on your machine already.
docker-compose up -d nginx
..time for a tea...
This part of the process will take quite some time as it has to download and compile all the native extensions and build the docker images. Although this will take a while the first time, once it is built you will only need to rebuild it if you change the native extensions that are used inside the docker containers. If you do need to rebuild the docker containers then you can do this with the command:
docker-compose up --build -d nginx
We now have all the docker containers that we need to start the Laradock services, this will allow us to develop PHP without having to install and compile native extensions on to our development machine. This allows us to maintain a clean development environment, and allows us to easily change the version of the extensions and PHP we are using.
Now that the PHP environment is running, we need to tell IntelliJ IDEA how to use the Laradock PHP environment. To do this we need to install a few plugins:
Once we have the plugins installed we need to configure a few things.
First we need to add an interpreter that uses the docker image we created using Laradock. In settings we need to open 'Language & Frameworks -> PHP', and click the button to select the CLI Interpreter. Here we need to add a new docker remote interpreter that uses the image created by Laradock, this should be listed once you have configured the docker server, and should have the name 'laradock_php-fpm'.
The docs from JetBrains are very detailed in how to configure the docker server and the remote docker interpreter.
Once this is configured this will allow IntelliJ IDEA to compile/interpret PHP code using the docker image, this is how IntelliJ IDEA will be able to run unit tests, and any scripts that you want to execute directly. This will not however have any effect on the debugging, we will configure this soon.
Before we can start debugging we need to just configure the file mappings, in IntelliJ IDEA settings open 'Language & Frameworks -> PHP -> Servers'. Click the '+' icon to add a new server, and give it a name (I used Laradock). Then check the box 'Use path mappings', you should then see a folder tree appear with the 'Project files' and 'Include path' sections. Under 'Project files' you should see the full path to the root of the PHP project, we need to tell IntelliJ IDEA where this will be when we are using the debugger.
We need to do this because we are using the Laradock images with a docker mount of the project folders in the docker file system. All we need to do is set the absolute path to the working directory inside the docker container (see the section above about Working Directory), for me this was '/var/www/account-service'.
The last thing to do is to validate the config, this can be done with the built in tool in IntelliJ IDEA. This is in 'Language & Frameworks -> PHP -> Debug' section of IntelliJ IDEA settings. In here there is a 'Pre-Configuration' section, that lists the steps to get going with debugging in PHP. Under step 1 there is link to 'Validate', if you click this you will get a small dialog that wants to know what environment to configure. Here all we need to do is give it the URL to the web server, this was configured in the NGINX container, and for me was 'http://accountservice/'. We also need to check the 'Path to create validation script' is correct, this should be the location on your machine that is mounted to the NGINX config 'root' for the application, for me this was '~/repo/account-service/public'.
If all the configuration is working you should see something similar to the above. You might notice the warning, this is due to the docker network layers and is not a problem for us as we have enabled 'debug.remote' in the debug.ini. This will tell PHP to look for the debugger on the source of the incoming request, which in our case will be the docker host machine.
We should now have a fully configured environment that is able to debug PHP from IntelliJ IDEA. You will be able to install breakpoints in your PHP code, and then trigger the code by access the application in your browser by accessing the hostname that was configured in your NGINX and hosts file, e.g. http://accountservice.
As you can see from this post it can be very complicated to get a PHP environment configured especially when dealing with debugging. When comparing to node, python or Java it is very overwhelming and confusing. Not only are there many different configuration options that need to be set, if any of them are wrong then you normally get no errors or warning you simply get nothing. I hope that this post helps someone to get some clarity in how to configure a PHP environment that has the least affect on your development environments, and is isolated enough to allow different version of PHP to be used.
I will be following this post up will a post about how to use the Laradock images to debug and running unit tests for even easier PHP development.
At nerd.vision we have been looking at other language support, if PHP support would interest you please comment on our road map.
Experienced developer in various languages, currently a product owner of nerd.vision leading the back end architecture.