Writing PHP applications in a team requires clear guidelines and a lot of alignment. In my book, Clean Code in PHP, I dedicated a full chapter, Working in a team, on how to write PHP software together with other developers.
Coding Guidelines are easy to introduce yet still compelling, so it makes them a perfect tool to introduce to early. Together with your team, you agree on standards to write your software.
This is not only important for onboarding new colleagues, which surely will be happy to see how the new team they will be part of writes the code. It is also useful during Code Reviews—once agreed on a rule, you don’t need to argue about in anymore.
The following article is supposed to give you some inspiration for your team’s guidelines. Please don’t consider them as the single truth or set in stone: every software team is different, and what works for one team, might not work for the other.
Naming Conventions
Services, Repositories, and Models
Written in UpperCamelCase. Use the type as suffix.
Examples:
UserService
ProductRepository
OrderModel
Events
Written in UpperCamelCase. Use the correct tense to indicate if the event is fired before or after the actual event.
Examples:
DeletingUser
is the event before the deletionDeleteUser
is the actual eventUserDeleted
is the event after the deletion
Properties, Variables, and Methods
Written in lowerCamelCase.
Examples:
$someProperty
$longerVariableName
$myMethod
Tests
Written in lowerCamelCase. Use the word test
as a prefix.
Examples:
testClassCanDoSomething()
Traits
Written in UpperCamelCase. Use the adjective to describe what the Trait is used for.
Examples:
Loggable
Injectable
Interfaces
Written in UpperCamelCase. Use the word Interface
as suffix.
Examples:
WriterInterface
LoggerInterface
General PHP conventions
Comments and DocBlocks
Since PHP 8, most information in DocBlocks can be reused by native PHP features, particularly type hints. Redundant or even useless DocBlocks simply waste time of the developers, while wrong DocBlocks can cause confusion and bugs.
// Avoid redundant DocBlocks /** * @param int $property * @return void */ public function setProperty(int $property): void { // ... } // Avoid useless Docblocks /** * @param $property */ public function setProperty(int $property): void { // ... } // Example for wrong DocBlocks /** * @param string $property */ public function setProperty(int $property): void { // ... }
Ternary Operators
Every part should be written in a single line to increase readability. Exceptions can be made for very short statements.
// Example for short statement $isFoo ? ‘foo’ : ‘bar’; // Usual notation $isLongerVariable ? ‘longerFoo’ : ‘longerBar’;
Do not use nested ternary operators, as they are hard to read and debug.
// Example for nested operators $number > 0 ? 'Positive' : ($number < 0 ? 'Negative' : 'Zero');
Constructor
Use Constructor Property Promotion for shorter classes, if working with PHP 8+.
// Before PHP 8+ class ExampleDTO { public string $name; public function __construct( string $name ) { $this->name = $name; } } // Since PHP 8+ class ExampleDTO { public function __construct( public string $name, ) {} }
Arrays
Always use the short array notation and keep the comma after the last entry.
// Old notation $myArray = array( 'first entry', 'second entry' ); // Short array notation $myArray = [ 'first entry', 'second entry', ];
Control structures
Always use brackets, even for one-liners.
// Bad if ($statement === true) do_something(); // Good if ($statement === true) { do_something(); }
Avoid else statements and return early, as this is easier to read and reduces the complexity of your code.
// Bad if ($statement) { // Statement was successful return; } else { // Statement was not successful return; } // Good if (!$statement) { // Statement was not successful return; } // Statement was successful return;
Exception Handling
Empty catch blocks should be avoided, as they silently swallow error messages and thus can make it difficult to find bugs. Instead, log the error message or at least write a comment which explains why the exception can be ignored.
// Bad try { $this->someUnstableCode(); } catch (Exception $exception) {} // Good try { someUnstableCode(); } catch (Exception $exception) { $this->logError($exception->getMessage()); }
Architectural patterns
Coding Guidelines are not limited on how to format code or name elements. They can also help you to control how the code is written in an architectural sense.
Fat models, skinny controllers
If the Model View Controller (MVC) pattern is used, the business logic should be located inside models or similar classes, like services or repositories. Controllers should only contain as little code as possible which is required to receive or transfer data between the views and the models.
Framework-agnostic code
In the context of the fat models, skinny controllers approach, you will probably come across the term framework-agnostic business logic. It means that the code that contains your business rules should use features of the underlying framework as few as possible. This makes framework updates or even migrations to other frameworks much easier.
Single responsibility principle
Classes and methods should only have one responsibility, as stated by the Single Responsibility Principle.
Framework guidelines
- How to access the database?
- How to configure routes?
- How to register new services?
- How is Authentication handled within your project?
- How should errors or other debug information be logged?
- How to create and organize view files?
- How to handle translations?
Since the answers to these questions highly depend on the used framework, we cannot give you recommendations here. You will need to set them up together with your team. In the next section, we will give you some ideas on how to do that.