Skip to content

Instantly share code, notes, and snippets.

@thewilkybarkid
Last active March 29, 2016 15:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thewilkybarkid/4c08be6e48ce8c4a9496 to your computer and use it in GitHub Desktop.
Save thewilkybarkid/4c08be6e48ce8c4a9496 to your computer and use it in GitHub Desktop.
Proposed eLife PHP coding standard

eLife PHP coding standards

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, eg Default)
  • Do not use Interface, Trait and Exception suffixes.
  • Do not use —able suffixes (eg use HasTimestamp not Timestampable).
  • 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, never DateTime.

Further details

Note the use of 'class(es)' may refer to classes, interfaces, traits, and other similar structures.

Namespaces

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).

Naming

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).

Further reading

private and final

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.

Further reading

Testing

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.

Further reading

PHPDoc

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.

Dates

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment