If you're writing something for an application that already has a coding standard, use that. (So, if you're writing a Drupal module, follow Drupal's coding standard.)
If not, then following the Symfony coding standard (which follows PSR-0, PSR-1, PSR-2 and PSR-4), except for the following:
- Do not prefix classes with
Abstract
(or with any other meaningless word, egDefault
) - Do not use
Interface
,Trait
andException
suffixes. - Do not use
—able
suffixes (eg useHasTimestamp
notTimestampable
). - Do not abbreviate property names etc.
- Add
use
statements for items not in a namespace (eg don't typehint\Countable
). - Method names in test cases must be sentences in snake_case (eg
it_does_something
). - Only use PHPDoc when it adds something that can't be expressed in code.
- Use singles quotes in preference to double quotes (note they behave differently).
- Always use
DateTimeImmutable
, neverDateTime
.
Note the use of 'class(es)' may refer to classes, interfaces, traits, and other similar structures.
Use elife/<package>
as the Composer namespace (eg elife/podcasts
).
Use eLife\<package>
as the PHP namespace (eg eLife\Podcasts
). Classes can be grouped in further logical sub-namespaces (eg eLife\Podcasts\Client\GuzzleClient
).
Use clear and descriptive names for everything. It's common to see Foo implements FooInterface
, but what is Foo
? Why is it different to any other implementation to FooInterface
? Instead, use names based on what they do: CachingFoo implements Foo
, LoggingFoo implements Foo
, NullFoo implements Foo
.
Use named constructors where possible. Having both Time::fromString($time)
and Time::now()
is more useful/explicit than just new Time($time)
.
- Sensible Interfaces, Mathias Verraes
- Named Constructors in PHP, Mathias Verraes
- Extremely Defensive PHP, Marco Pivetta
Make classes/methods/properties final
and private
by default, only making them open when there is a genuine reason.
Avoid inheritance wherever possible. A notable exception is... an exception class.
- Final Classes: Open for Extension, Closed for Inheritance, Mathias Verraes
- When to declare classes final, Marco Pivetta
Don't mock classes, only mock interfaces. (And use alternative implementations rather than mocking libraries if possible, eg use InMemoryFoo
rather than mocking the Foo
interface everywhere. This is safer, as InMemoryFoo
should itself be covered by unit tests.)
Don't mock what you don't own. Abstract libraries behind local interfaces, eg implement a Things
collection interface with DoctrineORMThingRepository
. (In case DoctrineORMThingRepository
will need integration tests, using a real ORM—connect it to in-memory SQLite database.)
Use the right tool for the right job. It's perfectly fine to use Behat, phpspec and PHPUnit in the same project.
- The Little Mocker, Uncle Bob
- My top ten favourite PhpSpec limitations, Marcello Duarte
Only add PHPDoc when it adds something. Well-designed code doesn't need much.
For example, the following PHPDoc is completely superfluous:
/**
* Constructor.
*
* Creates a new object.
*
* @param string $name Name.
*/
function __construct(string $name)
{
// ...
}
A common use of PHPDoc is to state what exceptions are thrown:
/**
* @throws ServiceUnavailable
*/
function send(Request $request) : Response
{
// ...
}
Note that in this example, the $request
parameter and return type haven't been included as there's no need to.
Don't use things like @package
and @author
. They're meaningless.
Don't use DateTime
, always use DateTimeImmutable
. External code may only understand the former, however, in which case convert your DateTimeImmutable
into a DateTime
at the integration point.