PHP developers face problems quite often when switching from project to project while using a similar but not identical environment. This is why it’s crucial to be able to run an isolated environment for each project with the following stack:

PHP 5.6 - 7.2

MySql || Postgres

Laravel 5.0 - 5.7

Nodejs

Apache || Nginx

After investigating possible solutions, we found a tool that would:

Allow users to deploy an environment quickly and easily.

Be suitable for use in production environments.

This solution turned out to be Docker + docker-compose. Click here and here to learn how to install it. There are installation options for all popular systems.

I have to say that when launched, Docker does not create a full-fledged virtual environment with an operating system, but it works at the Linux kernel level. Therefore, running Windows inside Docker will not work. The main concepts of Docker are containers and images. I will try to draw parallels with OOP.

In Docker, images are used to describe data types in much the same way that classes are used in OOP for the same purpose. It contains a system template. This can be, for example, pure Ubuntu or with pre-installed services (for example, PHP or MySQL). There is already a public repository with images, so before you create your template, you should look for a ready-made solution.

The container is a specific implementation of the image. You can represent it as an object. Using the same image, many different containers can be launched. Containers are lightweight, and many containers can be running on one machine. For example, I can simultaneously launch 20 projects with about 70 containers with no discomfort or problems when working.

Docker-compose is a little different. This is a utility that allows you to conveniently manage containers within a single project. I don't see any reason why not to use it from the very beginning of your PHP development process.

Docker use case description in PHP development

Let’s try to run the project on Laravel in Docker step by step. First, we’ll create an empty folder in which we will place the file docker-compose.yml with the content:

version: '3'



services:

nginx:

image: nginx:latest

container_name: test_nginx

ports:

- 8070:80

volumes:

- ./src:/usr/share/nginx/html

Next, inside our folder, we will create another folder, which we will call src. Inside it will be the project code. For example, we can create it inside the index.php file with the content of “hello world” and go back to the root of the project.

To get started, it is enough to write “docker-compose up -d” in the terminal.

Note: When you first start downloading, it may take some time.

After launch, we should see the following:

Now I’ll explain what our config means in the docker-compose file.

image: nginx: latest - Here we indicate which image will use the current image.

container_name: test_nginx - This is how our container will be named. This is an optional field. If you do not specify, then the name of the current folder + the name of the service will be taken (in our case it is nginx).

ports:

- 8070: 80 - port forwarding. We cannot specify port 80 for the server of all our projects. Each service will use its own unique port.

volumes:

- ./src:/usr/share/nginx/html - The container is an environment isolated from our operating system. However, we need the ability to transfer project files there. Thus, Docker should take the files in our operating system along the path ./src and forward them inside the container along the path / usr / share / nginx / html.

So we have now launched a server in a virtual environment. Now we want to tie more PHP projects here. Our config is slightly increased:

version: '3'



services:

nginx:

image: nginx:latest

container_name: test_nginx

ports:

- 8070:80

volumes:

- ./src:/usr/share/nginx/html

- ./nginx/nginx.conf:/etc/nginx/nginx.conf

depends_on:

- php

php:

image: php:7.2-fpm

container_name: test_php

volumes:

- ./src:/usr/share/nginx/html

The PHP section was added. In it, we add a new container, where fpm will be placed to execute PHP files. Why do we not combine server and interpreter in one container? The ideology is to make the containers as lightweight and simple as possible. Ideally, one process is one container. Docker works in such a way that if no process is running in the container, the container reloads. When we have only one nginx in the container and when problems arise and the server is stopped, Docker will take care to restart it. If we have many processes in one container, this does not happen. Therefore, the fewer processes in the container, the better.

Another change: New volumes were added.

- ./nginx/nginx.conf:/etc/nginx/nginx.conf

To do this, we will create an nginx folder in the project root, in which we will create the nginx.conf file with the following content:

http {

server {

root / usr / share / nginx / html;

listen 80;

server_name localhost;



location ~ \ .php $ {

try_files $ uri = 404;

fastcgi_split_path_info ^ (. + \. php) (/.+) $;

fastcgi_pass php: 9000;

fastcgi_index index.php;

include fastcgi_params;

fastcgi_param SCRIPT_FILENAME $ document_root $ fastcgi_script_name;

fastcgi_param PATH_INFO $ fastcgi_path_info;

}

location / {

index index.html index.htm;

}

}

}

The last thing left to change is the src / index.html file and replace it with src / index.php with the content:

<? = "hello world"?>

Restart Docker with the docker-compose restart command and check:

The only thing left to do is add the database. The resulting docker-compose file will look like this:

version: '3'



services:

nginx:

image: nginx: latest

container_name: test_nginx

ports:

- 8070: 80

volumes:

- ./src:/usr/share/nginx/html

- ./nginx/nginx.conf:/etc/nginx/nginx.conf

depends_on:

- php

php:

image: php: 7.2-fpm

container_name: test_php

volumes:

- ./src:/usr/share/nginx/html

depends_on:

- db

links:

- db:database_host

db:

image: "mysql: 5.7"

ports:

- "3390: 3306"

restart: unless-stopped

container_name: test_db

volumes:

- db_data: / var / lib / mysql

environment:

MYSQL_ROOT_PASSWORD: root

MYSQL_USER: userMYSQL_PASSWORD: password

MYSQL_DATABASE: database

volumes:

db_data:

We have added the last container with MySQL. There is an interesting and very important point. When reloading containers, all information inside is erased. But what about the data in the database? For this, we can use named volumes, which are declared here:

volumes:

db_data:

In this config, we are telling Docker to store the current database in a named volume that will not be erased when the container is reloaded.

volumes:

- db_data: / var / lib / mysql

It remains only to add Laravel to the src folder and set the path to its public folder in the nginx.conf file in the nginx config. Thus, our favorite framework will work in an isolated environment with the correct version of PHP, MySQL, and others.

Summary: using Docker for PHP developers

Using Docker has the following advantages

PHP developers have a single, consistent environment. You can forget about the strange bugs caused by different versions of PHP.

Acceleration of setup time and project deployment: New developers no longer have to spend valuable time installing new environments for new projects each time.

Disadvantages of using Docker

Developers who are not familiar with this technology will have to spend time studying it. It will be especially difficult for junior-level developers.

During the web development process, errors may occur that can be very difficult to fix. Sometimes a considerable amount of time is required to be spent on it.

In general, the use of Docker is 100% justified, and I believe that in 2019 there will be no life without Docker in web development in general and for each PHP developer in particular.

P.S. This article is not a complete description of the capabilities of Docker in PHP projects. Many steps are simplified or not mentioned at all. It is simply a starting point for further study.