My Modern PHP Development Setup

With this combination of formatters, linters and build tools I am able to create a fully testable, completely portable solution with adheres to PSR coding standards.


  1. Use a Makefile and make good use of it
  2. Run everything in Docker
  3. Use CS Fixer To Adhere To Coding Standards
  4. Use PHP Stan
  5. Syncronise your team IDE’s with .editorconfig

Use a Makefile and make good use of it

Most developers use Makefiles, but very few use them effectively. Below is an example of my typical Makefile


	docker-compose up -d

	docker-compose rm -vsf
	docker-compose down -v --remove-orphans
	docker-compose build
	docker-compose up -d

	docker-compose down

	docker-compose run {container} composer require

	docker-compose run {container} composer require --dev

	docker-compose run {container} php index.php

	docker-compose run {container} bash

	docker-compose run {container} ./vendor/bin/phpunit ./tests/

	docker-compose run {container} ./vendor/bin/phpunit ./tests/ --group $(FILE)

	docker-compose run {container} ./vendor/bin/phpstan analyse $(FILE) --level 7

	docker-compose run {container} ./vendor/bin/php-cs-fixer fix $(FILE)

	docker-compose logs -f {container}

The commands explained;

up, down, and build speak for themselves. These easily allow me to interact with Docker saving me a few keystrokes. I also make sure that I have these same commands for every project. So I always know which shortcuts I have available to me.

require and require-dev are used to interact with a composer installation which lives inside my app container. I have found it useful to run composer solely from within the apps container, why? I have had problems in the past installing packages for differing PHP versions, eg my host machine in 7.1, but the project I am working on is 5.6. Running composer in the container isolates my app and makes sure that only the correct packages get installed.

run speaks for itself, and jumpin is just me being lazy saving me typing docker exec -it <container-id> bash

test of course runs my entire test suite but next up is where it gets really interesting.

Hooking you IDE into your make commands

The following commands have all been created to integrate into my IDE. My IDE of choice is PHPStorm.

test-file is ran every time I update a file. It requires one parameter which is FILE. Within PHPStorm I set up file watchers which can be seen below.

Screenshot oh PHPStorm

stan More on this below but I sometimes run this upon file save to statically examine my code in real time :).

cs-fixer PHP-cs-fixer is a great tool and a must have to ensure that you code matches the PSR Coding Standards

Run everything in Docker

And I mean everything, I even dare challenge you to not have PHP installed on your host machine. When I say run everything in docker what i mean by that is of course your app, composer, your unit tests, and finally your coding standards fixer. By running everything in Docker you are able to make your project 100% transportable so that anyone on any machine can simply checkout out your repo and get started straight away.

You have have seen from my makefile above every command is running my docker image. This will allow anybody, regardless of platform to pull down my repo, run tbe tests, and run the app. All without installing PHP on their local machine. Not bad ey?

Use CS-fixer to adhere to coding standards

As mentioned above CS Fixer is a good way to ensure that you team all code to a standard set of rules, see (PSR Coding Standards)[]. The documentation recommends that you install it locally, however following my philosophy of doing everything in the container I recommend installing it with composer via

composer require friendsofphp/php-cs-fixer

Use PHP Stan

For any project using php >= 7.1 PHPStan is a must have. Essentially it statically analyses your code to catch bugs that you had no idea were there. You will have to use parameter type hints and return types but by adding PHP Stan into your workflow you will soon notice the amount of subtle errors that already exist in your code and wil catch other future mistakes. A must have!

Use .editorconfig to standardise coding standards across your team

My final offering is that of .editorconfig. This file is checked into your source control and configures the IDE of your team members. This ensures that you are all using the correct formatting when working on the codebase. Saving you unnecessary code conflicts and office arguments on the type of tabs to use, and the size that they should be.

end_of_line = lf
insert_final_newline = true

indent_style = space
indent_size = 4

indent_style = tab
indent_size = 4

indent_style = space
indent_size = 2

indent_style = space
indent_size = 4