Firstly: follow PSR-1, PSR-2 and PSR-4 strictly.
4 spaces instead of tabs. \n
for newlines.
An explicit mention for PSR-2 Section 4.2
Property names SHOULD NOT be prefixed with a single underscore to indicate protected or private visibility.
Instead, Property names MUST NOT be prefixed with a single underscore to indicate protected or private visibility. They are already protected/private.
There is no reason to do this; we're not using a language with public-only access (e.g. Python), and if you want to access a private/protected member from outside you'd use reflection regardless and would be breaking every coding standard ever, as well as encapsulation.
On the same line as <?php
<?php namespace Foo\Bar;
class Baz {
//
}
Always use PHPDoc blocks. The minimum for a method/function is an @return
tag, while the minimum for a class member is an @var <type>
tag.
Exception: @return
is not required for __construct
.
Always declare @var
, not @type
, and always on multiple lines. Try to include a description.
Aka prefer this:
/**
* Internal items for this collection
*
* @var array
*/
protected $items;
And never do this:
/** @var array */
protected $items;
Follow this. It stands for:
- Single Responsibility Principle
- Open/Closed Principle
- Liskov Substitution Principle
- Interface Segregation Principle
- Dependency Inversion Principle (not explicitly injection)
In short (in order):
- Classes must only do one thing.
- Classes should be open for extension (think
protected
access), but closed for modification; this means follow semver and don't go changing a class at will. - Classes should implement an interface (contract); anything implementing that interface should be swappable at will in an IoC container or service locator.
- Many interfaces for specific tasks always always beat once big interface for many tasks.
- Let classes define their dependencies to an interface, preferrably with an IoC container and reflection. See below.
<?php namespace Foo\Bar;
use Foo\Something\FooInterface;
class Baz implements BazInterface {
/**
* Foo Instance or something
*
* @var \Foo\Something\FooInterface
*/
protected $foo;
/**
* Make a new Baz instance using a Foo-like object
*
* @param \Foo\Something\FooInterface $foo
*/
public function __construct(FooInterface $foo)
{
$this->foo = $foo;
}
}
Write classes like this. Let an IoC container or reflection automatically associate the interface needed with a concrete class and inject it.
This also means you can inject a mock into the class when testing.
Code should be tested. The easiest tests to set up with frameworks are functional tests, but a library that (for example) uses a lot of math should be unit tested and functional tested strictly.
Default to PHPUnit for testing, but PHPSpec, Behat, etc. can all be used. Just make sure they are in the require-dev
section of your composer.json
so that people can install and run them locally instead of globally.
Write tests before you start writing a class or it's not TDD!