PER-CS is the next evolution of the PSR set of Coding Standards from the PHP-FIG (Framework Interoperability Group). It extends the Coding Standards laid out in PSR-12 to the newest functionality added to PHP such as the match keyword, enums, attributes, and more.
This document describes the changes and additions on a section by section basis between PER-CS v2.0 and PER-CS v1.0 (which is a direct equivalent of PSR12 with very minor changes).
It is derived in part from a GitHub-generated diff and focuses on the changes on a section-by-section basis as its focus is to be more readable.
This document intends to provide a summary of these changes that can then be used to drive action lists for toolset producers to support PER-CS v2.0.
This document is non-normative. The published 2.0 PER-CS specification is the canonical source for the PER-CS formatting expectations.
Numerous constructs now allow a sequence of values to have an optional trailing comma:
- If the final item is on the same line then there MUST NOT be a trailing comma
- If the final item is not on the same line then there MUST be a trailing comma
<?php
$sequence = [1, 2, 3, 5, 8, 13];
function beep(
string $a,
string $b,
string $c,
) {
// ...
}
If a function or method contains no statements or comments (such as an empty no-op implementation or when using constructor property promotion), then the body SHOULD be abbreviated as {} and placed on the same line as the previous symbol, separated by a space. So, for a method of a subclass that does nothing:
<?php
class SubClass extends BaseClass
{
protected function init() {}
}
Modifier keywords are keywords that alter how PHP handles classes, properties and methods.
These keywords MUST BE ordered as follows:
[abstract|final] [public|protected|private] [static] [readonly] [type] name
<?php
namespace Vendor\Package;
abstract class ClassName
{
protected static readonly string $foo;
final protected int $beep;
abstract protected function zim();
final public static function bar()
{
// method body
}
}
Furthermore, all keywords must be on a single line and MUST be separated by a single space.
If using named arguments, there MUST NOT be a space between the argument name and the colon, and there MUST be a single space between the colon and the argument value.
<?php
somefunction($a, b: $b, c: 'c');
Method chaining MAY be put on separate lines, where each subsequent line is indented once. When doing so, the first method MUST be on the next line.
<?php
$someInstance
->create()
->prepare()
->run();
Function callable references - there must not be whitespace surrounding the '...' operator ()
<?php
$callable = $item->doSomething(...);
The match keyword is now covered.
<?php
$result = match ($a) {
'foo' => 'Foo',
'bar' => 'Bar',
default => 'Baz',
};
<?php
$returnValue = match ($expr) {
0 => 'First case',
1, 2, 3 => multipleCases(),
default => 'Default case',
};
A new subsection about Short Closures, as per the link above. Example as follows:
<?php
$func = fn(int $x, int $y): int => $x + $y;
$func = fn(int $x, int $y): int
=> $x + $y;
$func = fn(
int $x,
int $y,
): int
=> $x + $y;
$result = $collection->reduce(fn(int $x, int $y): int => $x + $y, 0);
Enums are now covered, as per the link above. Please see below for examples.
<?php
enum Suit: string
{
case Hearts = 'H';
case Diamonds = 'D';
case Clubs = 'C';
case Spades = 'S';
}
<?php
enum Size
{
case Small;
case Medium;
case Large;
public const Huge = self::Large;
}
This is a new section about Heredoc and Nowdoc notation as per the link above. Example follows:
<?php
function allowed()
{
$allowedHeredoc = <<<COMPLIANT
This
is
a
compliant
heredoc
COMPLIANT;
$allowedNowdoc = <<<'COMPLIANT'
This
is
a
compliant
nowdoc
COMPLIANT;
var_dump(
'foo',
<<<'COMPLIANT'
This
is
a
compliant
parameter
COMPLIANT,
'bar',
);
}
This is a new section about arrays, as per the link above. Example as follows:
<?php
$arr1 = ['single', 'line', 'declaration'];
$arr2 = [
'multi',
'line',
'declaration',
['values' => 1, 5, 7],
[
'nested',
'array',
],
];
This is a new section, as per the above link. The following is an example of valid usage.
<?php
#[Foo]
#[Bar('baz')]
class Demo
{
#[Beep]
private Foo $foo;
public function __construct(
#[Load(context: 'foo', bar: true)]
private readonly FooService $fooService,
#[LoadProxy(context: 'bar')]
private readonly BarService $barService,
) {}
/**
* Sets the foo.
*/
#[Poink('narf'), Narf('poink')]
public function setFoo(#[Beep] Foo $new): void
{
// ...
}
#[Complex(
prop: 'val',
other: 5,
)]
#[Other, Stuff]
#[Here]
public function complicated(
string $a,
#[Decl]
string $b,
#[Complex(
prop: 'val',
other: 5,
)]
string $c,
int $d,
): string {
// ...
}
}