Standardy a sady
Co přesně předepisuje PSR-12, PER Coding Style 3.1, Nette, Symfony nebo Laravel? Nejlépe vám odpoví samotný konfigurační soubor daného presetu – má totiž naprosto stejnou strukturu jako vaše vlastní konfigurace. Na této stránce najdete každý z nich v plném znění, včetně komentářů objasňujících důvody jednotlivých voleb. Standard definuje celkový vizuální vzhled kódu a projekt si z nich vybere právě jeden; sada reprezentuje konkrétní záměr a můžete ji libovolně připojit k jakémukoliv standardu.
Každý preset vychází z předchůdců uvedených v jeho klíči use a upravuje pouze to, co sám výslovně
přepíše: standard nette tedy staví na perCs a ten vychází z psr12. Výslednou
efektivní konfiguraci včetně původu jednotlivých hodnot vám kdykoliv vypíše příkaz
dresscode config --preset nette. Způsob skládání presetů s vaší projektovou konfigurací detailně rozebírá
stránka Konfigurace; význam každého klíče popisuje
katalog Pravidla.
Standardy
Pětice vestavěných standardů komplexně určuje vše, co v kódu vidíte: odsazení, umístění závorek, mezery,
prázdné řádky i pravidla zalamování. Standardy psr12 a perCs jsou napsány striktně podle
oficiálních specifikací PHP-FIG, oddíl po oddílu, a nepřidávají nic vlastního. Standardy nette,
symfony a laravel staví na základech PER Coding Style a doplňují zvyklosti a požadavky svých
ekosystémů.
Řádek přesahující limit file.lineLength standard automaticky zalomí všude tam, kde to syntaxe dovoluje. Kde
zalomení provést nelze (například u dlouhých textových řetězců nebo komentářů), tam psr12 a
perCs hlásí pouze varování, protože PSR-12 považuje limit 120 znaků za doporučený (soft limit). Standard
nette jej naproti tomu hlásí jako striktní chybu, zatímco symfony a laravel délku
řádků nehlídají vůbec.
psr12
Standard PSR-12, oddíl po oddílu přesně podle specifikace PHP-FIG.
# PSR-12 Extended Coding Style
#
# PSR-12, section by section; the specification defines the style and this preset adds nothing of its own.
severity:
file.longLines: warning # what nothing could spread only warns, the limit being soft
file:
bom: forbidden
openingTag: required # `<?php`, not `<?` or `<?PHP`
strictTypes:
position: nextLine # §3, `<?php` on a line of its own
closingTagAtEnd: forbidden # a `?>` before HTML stays
lineEnding: majority # as most lines of the file, LF on a tie
finalLineEndings: 1
trailingWhitespace: forbidden # the text of a string stays
lineLength: 120 # what is wider is spread
longLines: forbidden
statementsPerLine: 1 # `foo(); bar();` takes two lines
indentation:
unit: '4 spaces'
binaryOperator: 0 # `&& $b` right below `$a`, a level deeper after `$x =` or `return`
ternary: 1 # `?` and `:` opening a line
switchCase: 1 # `case` inside `switch`
chain: flat # every link one level below the start of the chain
commentLines: aligned
braces:
position:
class: nextLine
function: nextLine # one with parameters on one line
multilineSignature: sameLine # `) {` on a line of their own
closure: sameLine
anonymousClass: sameLine
controlStructure: sameLine
continuingKeyword: sameLine # `} else {`
emptyBody: # not `{}`
class: expanded
function: expanded
closure: expanded
propertyHook: expanded
bracelessBody: forbidden # `if ($a) foo();` gets braces
singlelineClosure: forbidden # `function () { return 1; }` is spread
blankLines:
afterOpeningTag: 1
afterDeclare: 1
beforeNamespace: 1
afterNamespace: 1
afterImports: 1
beforeDeclaration: 1
betweenImports: 0
betweenImportKinds: 1
beforeOpenBrace:
class: 0 # §4.1: no blank line between the head and `{` below it
bodyStart:
class: 0
anonymousClass: 0
controlStructure: 0 # §5: the body on the next line after `{`
bodyEnd:
class: 0
anonymousClass: 0
function: 0 # §4.4, §7: `}` on the next line following the body
closure: 0
controlStructure: 0 # §5
branch: 0
betweenTraitUses: 0
afterTraitUses: 1
multiline:
items:
argument: perLine # an argument per line once spread
parameter: perLine # a parameter per line once spread or too wide
for: perLine # a section of `for` per line
implements: perLine # an interface per line below `implements`
matchCondition: perLine # a condition of a `match` arm per line, `=>` after the last
# the parts of a condition and the operands as their author spread them, a line each where too wide
condition: [perLine, fill]
logical: [perLine, fill]
concatenation: [perLine, fill]
arithmetic: [perLine, fill]
bitwise: [perLine, fill]
edges:
argument: apart # `foo(` above the arguments, `)` below them
parameter: apart
implements: apart
condition: apart # `if (` above `$a`, `) {` below the last part
operatorPosition: # `+ $b` opens the line, not ends the one before
operators: lineStart
concatenation: keep
ternary: keep
spacing:
declare: 'declare(strict_types=1)'
call: 'foo($a, $b)' # `function foo($a)` too
padding: # `foo($a)`, `if ($a)`, `($a + $b)`
argument: forbidden
parameter: forbidden
closureUse: forbidden
controlStructure: forbidden
grouping: forbidden
keyword: required # `return $x`, `if (`, `} else {`, `new`, `class Foo extends Bar`
openBrace: required # `if ($a) {`, `function () {`
alternativeSyntax: 'if ($a):'
body: required # `{ return 1; }`, `{ get; set; }`
import: 'use Acme\{Foo, Bar};'
comma: '$a, $b'
alignment:
comma: forbidden # no extra spaces after a comma
cast: '(int) $x'
reference: '&$x'
spread: '...$x'
unaryOperator: '-$x' # `++$i` too
logicalNot: ['!$x', '! $x'] # `! $x` may stay
binaryOperator: '$a + $b' # `=`, `=>`, `instanceof` too
concatenation: '$a . $b'
ternary: '$a ? $b : $c' # `$a ?: $b` too
anonymousClass: 'new class ($a)'
anonymousFunction: 'function ($x)'
typeDeclaration: '?int $x' # `): int` too
catchType: 'catch (A | B $e)'
switchCase: 'case 1:'
# names are reported, never renamed
naming:
class: PascalCase
method: camelCase # `__construct()` passes
constant: UPPER_CASE
casing:
keyword: lowercase # `self`, `parent` too
trueFalseNull: lowercase
type: lowercase # `int`, `string`, `void`
imports:
leadingBackslash: forbidden # `use Foo\Bar;`, not `use \Foo\Bar;`
order:
kinds: [class, function, constant] # the names of a kind in the order written
classes:
visibility: required # `var` is `public`; interface methods and set visibility too
modifiers:
order: canonical # `final public static`
members:
order: [traitUse]
grouping: # `public $a, $b;` is split
property: separated
traitUse: separated
emptyParentheses:
instantiation: required # `new Foo()`
controlFlow:
elseif: 'elseif' # not `else if`
switch:
fallThroughComment: no break # the comment of a fall-through
emptyStatement: forbidden # `foo();;`
expressions:
cast: canonical # `(int)`, not `(integer)` or `( int )`
upgrading:
php:
caseSemicolon: forbidden # `case 1:`, not `case 1;`
perCs
Standard PER Coding Style 3.1, přímý nástupce PSR-12: přináší
koncové čárky u víceřádkových seznamů, zkrácené anonymní funkce (arrow functions), zkrácená prázdná těla
{}, atributy, výčty (enumy), heredoc a property hooky.
# PER Coding Style 3.1
#
# PSR-12 plus what PER Coding Style added (trailing commas, short closures, empty bodies as `{}`, attributes,
# enumerations, heredoc, property hooks); the specification defines the style and this preset adds nothing of its own.
use: psr12
indentation:
heredoc: 1 # body and closing marker one level deeper than the start
braces:
emptyBody: # `function foo(): void {}`, `new class {}`
class: collapsed
function: collapsed
closure: collapsed
propertyHook: collapsed
anonymousClass: collapsed
blankLines:
afterPhpdoc: 0 # §12.2: no blank line between the docblock,
afterAttributes: 0 # the attributes and the structure
multiline:
items:
array: perLine # an item per line once spread
list: perLine
chain: perLine # a link per line once spread
ternary: perLine # `?` and `:` each on a line once spread
edges:
array: apart # `[` above the items, `]` below them
list: apart
chain: apart # `$form` above `->addText()`
operatorPosition:
concatenation: lineStart # `. $b` opens the line
ternary: lineStart # `? $b` and `: $c` open the lines
# a comma after the last item where the closing bracket has a line of its own, none on one line
trailingComma:
array: required
argument: required
parameter: required
matchArm: required
closureUse: required
import: optional
list: optional
attributePosition: above # a group per line above the declaration, and above a parameter in a list spread over lines
spacing:
attribute: '#[Foo($a)]'
arrowFunction: 'fn($x) => $x'
semicolon:
before: '$a;'
catchType: 'catch (A|B $e)' # as a union type
namedArgument: 'foo(name: $x)'
naming:
enumCase: PascalCase # reported, never renamed
classes:
visibility:
publicWithSet: keep # `public` may be omitted with a set visibility
members:
grouping:
constant: separated # `const A = 1, B = 2;` is split
emptyParentheses:
anonymousClass: forbidden # `new class {}`, not `new class() {}`
attribute: forbidden # `#[Foo]`, not `#[Foo()]`
instantiationWrapping: forbidden # `new Foo()->bar()`, not `(new Foo())->bar()`, from PHP 8.4
protectedInEnum: forbidden # `private const X`
functions:
arrowFunction:
emptyBody: required # `fn() => null`, not `function () {}`
types:
nullable: '?int' # not `int|null`
nullPosition: last # `int|string|null`
literals:
heredocWithoutInterpolation: forbidden # a nowdoc `<<<'EOT'` instead
arraySyntax:
array: short # `[1, 2]`, not `array(1, 2)`
phpdoc:
aboveAttributes: required
nette
Nette Coding Standard představuje PER Coding Style
obohacený o tabulátory, typické rozvržení knihoven Nette, zápis declare(strict_types=1) přímo na řádku s
<?php a důkladnou hygienu kódu. Kromě perCs integruje sady cleanup,
types a correctness, přičemž několik jejich pravidel vrací na keep: větev
else následující po return, ladicí volání, třídy s __toString() bez rozhraní
Stringable, oba způsoby zápisu polí v phpDoc a nativní typové deklarace parametrů a vlastností. Pokud projekt
tyto sady uvede v use vedle standardu nette, vypnutá rozhodnutí se znovu neaktivují (každá sada se
uplatní pouze jednou pod standardem); v případě zájmu si je v konfiguraci zapnete sami.
# Nette Coding Standard
#
# PER Coding Style with tabs and the layout of the Nette libraries, the hygiene of the code, and the choices the Nette
# libraries make: strict types declared on the line of the opening tag, how names are cased, in what order the members
# of a class stand, which functions and constructs are out, and what a doc comment of theirs holds. The layout departs
# from PER Coding Style in two blank lines between methods, none inside the braces of a body, one closing a branch or
# a case another one follows when its last statement stands apart, a tab that may align commas, the brace of
# a multi-line signature below a return type, promoted properties on lines of their own, an expression on the line of
# its return, and a chain, an array or a condition that may keep the shape it has.
use:
- perCs
- cleanup
- types
- correctness
file:
strictTypes:
declaration: required # risky, `'1'` passed to `int` then throws
position: sameLine
lineLength: 140 # a tab counts as 4 columns
lineLengthExcept: [imports, strings] # a line too wide only for its strings is not reported
indentation:
unit: tab
chain: nested # a link may stand one level deeper than the one before
braces:
position:
typedMultilineSignature: nextLine # `{` below `): Foo`, otherwise `) {`
emptyBody: # not `{}` as in PER Coding Style
class: expanded
function: expanded
closure: expanded
propertyHook: expanded
anonymousClass: keep
singlelineClosure: keep
alternativeSyntax: forbidden # no `if: ... endif;`
# a range `1-2` leaves the choice to the author
blankLines:
betweenImportKinds: 0
beforeDeclaration: 2 # above the class of a file
aroundDeclarations: 1-2 # around one among statements
betweenStatementsInFunction: 0-2
aroundMethods: 2
aroundInterfaceMethods: 1
betweenMemberKinds: 0-1 # members without a doc comment
betweenMembers: 0-1
beforeDocumentedMember: 1
bodyStart:
blocks: 0
bodyEnd:
blocks: 0
branch: lastStatementSetApart # before `} else {` where the last statement stands apart
switchCase: lastStatementSetApart # likewise before `case`
multiline:
items:
array: fill # items may share a line
list: fill
import: fill # the names filling the lines
argument: fillPlain # `foo($a, $b, self::X)` share lines, `foo(fn() => 1, bar())` one on a line
matchArm: perLine
matchCondition: fill # conditions may share a line
edges:
array: [apart, attached] # `]` begins a line where the first item does
list: [apart, attached]
import: apart
chain: attached # `$form->addText()` above `->setRequired()`
condition: [apart, closingApart] # `if (` above `$a`, or `if ($a` above `&& $b`
maxWidth:
array: 130
fillMinItems:
array: 6 # fewer items stand one on a line
list: 6
matchCondition: 6
spreadItem: sole # `foo(bar(` above the arguments of `bar()`, `))` below them
spreadAlways: [promotedProperty] # however short
expressionStart: # `return [`, not `return` above `[`
return: sameLine
throw: sameLine
yield: sameLine
echo: sameLine
semicolon: sameLine
trailingComma: required # a group use and `list()` too
spacing:
alignment:
comma: tabs # tabs aligning columns may stay
ternary: forbidden # no extra spaces around `?` and `:`
semicolon:
after: 'for ($i = 0; $i < 5; $i++)'
logicalNot: '!$x' # never `! $x`
doubleColon: 'Foo::bar()'
objectOperator: '$a->b()'
padding:
array: forbidden # `[1, 2]`, `[$a, $b] = $x`
list: forbidden
offset: forbidden # `$a[0]`
comment:
marker: '// text' # also `#` and `/* */`
afterCode: '$a; // text' # at least one space
# names are reported, never renamed
naming:
function: camelCase
constant: PascalCase
property: camelCase
variable: camelCase
abstractClassAffix: forbidden # `Foo`, not `AbstractFoo`
interfaceAffix: forbidden # `Foo`, not `FooInterface`
traitAffix: forbidden
errorAffix: forbidden
casing:
magicConstant: uppercase
class: declared # `DateTime`, not `datetime`
function: declared # `strlen()`, not `StrLen()`
method: declared # `$o->getTotal()`, not `$o->gettotal()`, with `typeAnalysis`
qualification:
class:
otherNamespace: imported
currentNamespace: bare
phpdoc: imported # `@param Order $order`, not `@param \Acme\Shop\Order $order`
currentClass: self # `self::bar()` inside `Foo`, not `Foo::bar()`
imports:
order:
names: alphabetical # classes, functions, constants, each alphabetically
grouping:
class: [separated, grouped] # `use A; use B;`, a group use stays
function: [combined, grouped] # `use function a, b;`
constant: [combined, grouped] # `use const A, B;`
classes:
visibility:
interfaceMethod: forbidden # public anyway
publicWithSet: required # `public private(set)`
members:
# the methods then in the order written
order: [traitUse, enumCase, publicConstant, protectedConstant, privateConstant, publicProperty, protectedProperty, privateProperty]
grouping:
constant: keep # `const A = 1, B = 2;` may stay
emptyParentheses:
instantiation: forbidden # `new Foo`, but `new Foo()->bar()`
functions:
innerFunction: forbidden
arrowFunction:
oneExpression: required # `fn() =>` for a closure only returning
emptyBody: keep # `function () {}` stays a callback doing nothing
arrowFunctionNested: yes # also one holding another closure
types:
declaration: # a type only where it says something
parameter: keep
property: keep
return: keep
anonymousFunction: keep
controlFlow:
switch:
fallThroughComment: break omitted # the comment of a fall-through
elseAfterReturn: keep # may stay
expressions:
comparison:
loose: forbidden # risky
notEquals: '!='
yoda: forbidden # `$a === 1`
assignment:
addingOne: forbidden # `$i++`, not `$i += 1`
increment: postfix # `$i++;`
ternary:
short: required # `$a ?: $b`
coalesce: required # `$a ?? $b`
wordLogicalOperator: forbidden
explicitPrecedence: required # `($a && $b) || $c`
doubleNegation: forbidden # `(bool) $a`, not `!!$a`
literals:
quotes: single
arraySyntax: short # `[1, 2]`, `[$a] =`
implicitBackslash: forbidden # `"\\d"`, not `"\d"`
digitSeparator:
integer: required # `1_000_000`
fraction: required
digitSeparatorMinDigits:
integer: 7
fraction: 20
comments:
singleline: '//' # not `#`
phpdoc:
annotation:
casing: canonical # `@inheritDoc`, not `@inheritdoc`
inlineVar: forbidden # becomes `assert()`, risky
stars: aligned # below the first `*` of `/**`
blankLines: trimmed
property:
plainComment: forbidden # not `//` or `/* */`
lineSpan: collapsed # `/** @var int */`
promoted: '@var' # not `@param` of the constructor
forbiddenAnnotations: [@access, @author, @copyright, @created, @license, @package, @since, @subpackage, @todo, @version]
# `Constructor.`, `Name getter.`
forbiddenLines: ['~^(?:(?!private|protected|static)\S+ )?(?:con|de)structor\.\z~i', '~^\S+ [gs]etter\.\z~i']
types:
array: keep # both notations stay
nullPosition: last
hygiene:
is_null: forbidden # `$x === null`
settype: forbidden # `$x = (int) $x`
conversionFunction: forbidden # `(int) $x`, not `intval($x)`
dirnameOfFile: forbidden # `__DIR__`
nestedDirname: forbidden # `dirname($p, 2)`
classNameNotation:
currentClass: required # `self::class`, `static::class`
object: required # `$object::class`
mistakes:
globalStatement: forbidden
debugOutput: keep # may stay
upgrading:
php:
deprecatedApi: forbidden # `mb_convert_encoding()`, not `utf8_encode()`, `E_STRICT` reported
deprecatedArgument: forbidden # `trigger_error($m, E_USER_ERROR)` reported
dollarBraceInterpolation: forbidden # `{$name}`, not `${name}`
implicitNullable: forbidden # `?int $x = null`, not `int $x = null`
backticks: forbidden # `shell_exec()`
syntax:
octalPrefix: required # `0o17`, not `017`
classes:
Stringable: keep # implied by `__toString()`
# comments that silence a decision on their line
suppressionComments:
'~\bintentionally\s+[!=]=~': {decisions: [expressions.comparison.loose, mistakes.strictArgument], example: '// intentionally =='}
'~^//\s*@\s+\S~': {decisions: mistakes.errorSuppression, example: '// @ <why>'}
symfony
Symfony Coding Standards tak, jak je definuje oficiální sada @Symfony v PHP CS Fixeru (zatím bez většiny
jejích specifických phpDoc pravidel). Přejímá PER Coding Style a doplňuje pravidla pro importy, mezery a prázdné řádky
typické pro Symfony, přičemž zalamování konstrukcí na více řádků ponechává plně na uvážení autora.
# Symfony Coding Standards
#
# The Symfony Coding Standards as the @Symfony rule set of PHP CS Fixer defines them, without most of its rules about
# phpDoc, of which it takes only an empty doc comment, the trimming and the canonical types: PER Coding Style, which the
# set builds on, with the casing, the imports, the blank lines and the whitespace Symfony adds, and without the rules
# that would break a construct over lines, which Symfony leaves to the author.
use: perCs
file:
lineLength: none
longLines: keep
indentation:
binaryOperator: keep
chain: keep
heredoc: keep # heredoc_indentation belongs to another set
braces:
emptyBody: # not `{}` as in PER Coding Style
class: expanded
function: expanded
closure: expanded
propertyHook: expanded
anonymousClass: keep
singlelineClosure: keep
bareStatementGroup: forbidden # no `{ ... }` that nothing opens
alternativeSyntax: forbidden # no `if: ... endif;`
# a range `0-1` leaves the choice to the author, `1+` is one or more
blankLines:
afterDeclare: keep # @Symfony leaves the line below a declare
aroundDeclarations: 0-1 # around one among statements
aroundMethods: 1
aroundInterfaceMethods: 1
betweenTraitUses: keep
afterTraitUses: keep
beforeStatement: {return: 1+} # a `return` set apart from the code above it
belowComment:
inlineVar: 0 # `/** @var Order $order */` right above its statement
multiline:
# no fixer of the set breaks a call, a signature, a chain, a condition, a ternary, an operator or an array
items: keep
edges: keep
operatorPosition: # operator_linebreak moves only a boolean operator
operators: keep
logical: lineStart
condition: lineStart
trailingComma:
argument: optional # may stay or not where spread, never on one line
closureUse: keep
attributePosition:
parameter: keep
spacing:
arrowFunction: 'fn ($x) => $x'
alignment:
comma: optional # aligning spaces and tabs may stay
binaryOperator: forbidden # no extra spaces around an operator
semicolon:
after: 'for ($i = 0; $i < 5; $i++)'
logicalNot: '!$x' # never `! $x`
concatenation: '$a.$b'
objectOperator: '$a->b()'
padding:
array: forbidden # `[1, 2]`, `[$a, $b] = $x`
list: forbidden
offset: forbidden # `$a[0]`
anonymousClass: 'new class($a)'
comment:
marker: '// text' # also `#` and `/* */`
afterCode: '$a; // text' # at least one space
casing:
magicConstant: uppercase
class: declared # `DateTime`, not `datetime`
function: declared # `strlen()`, not `StrLen()`
method: declared # `__toString()`, other methods with `typeAnalysis`
hexDigit: keep # integer_literal_case left to the project
# global_namespace_import imports nothing: a class is written with the backslash, a function and a constant too where
# they are qualified, and a bare one stays bare
qualification:
class:
global: backslashed # `\DateTime`
function:
global: [backslashed, bare] # `\ucfirst()` or `ucfirst()`, never imported
optimized: [backslashed, bare]
constant:
global: [backslashed, bare]
optimized: [backslashed, bare]
imports:
order:
names: alphabetical # classes, functions, constants, each alphabetically
grouping: separated # `use A; use B;`, not `use A, B;` or `use Acme\{A, B};`
unused: forbidden
aliasEqualToName: forbidden # `use Foo;`, not `use Foo as Foo;`
classes:
visibility:
publicWithSet: required # `public private(set)`, as modifier_keywords writes it
emptyParentheses:
attribute: keep # `#[Foo()]` stays, as in @Symfony
untypedPropertyNullDefault: forbidden # `public $a;`, not `public $a = null;`
instantiationWrapping: keep # @Symfony leaves the parentheses
protectedInFinalClass: forbidden # `private` in a final class with no parent and no trait
functions:
trailingBareReturn: forbidden # no `return;` closing a function
controlFlow:
elseAfterReturn: forbidden # the `else` after a `return` goes, its body following the `if`
expressions:
comparison:
notEquals: '!=' # not `<>`
yoda: required # `null === $a`
assignment:
addingOne: forbidden # `++$i`, not `$i += 1`
increment: prefix # `++$i;`
doubleNegation: forbidden # `(bool) $a`, not `!!$a`
parenthesesAfterConstruct: forbidden # `return $x;`, not `return ($x);`
literals:
quotes: single
heredocWithoutInterpolation: keep # heredoc_to_nowdoc belongs to another set
concatenatedLiterals: keep # no_useless_concat_operator left to the project
comments:
singleline: '//' # not `#`
empty: forbidden
phpdoc:
aboveAttributes: keep # @Symfony leaves the order alone, unlike PER Coding Style
blankLines: trimmed
empty: forbidden
types:
builtin: canonical # `int`, not `integer`
duplicate: forbidden # each type of a union once
nullPosition: last
mistakes:
continueInSwitch: forbidden # a `continue` only leaving a `switch`, reported
upgrading:
php:
dollarBraceInterpolation: forbidden # `{$name}`, not `${name}`
implicitNullable: forbidden # `?int $x = null`, not `int $x = null`
backticks: forbidden # `shell_exec()`
laravel
Styl frameworku Laravel definovaný výchozím presetem laravel nástroje Laravel Pint: PER Coding Style se
spojováním řetězců bez mezer, negací ! $x, vynechanými závorkami u new Foo a plně
importovanými názvy tříd v phpDoc. Rozdíly oproti chování Pintu popisuje stránka Přechod z Laravel Pintu.
# Laravel
#
# The style of Laravel as the preset laravel of Laravel Pint defines it: PER Coding Style with the concatenation, the
# negation and the instantiation Laravel writes, the arrow function spaced, the imports qualified and sorted, a blank
# line before `return` and `continue`, the phpDoc trimmed, and without the rules that break a construct over lines,
# which Pint leaves to the author.
use: perCs
file:
lineLength: none
longLines: keep
strictTypes:
position: keep
indentation:
heredoc: keep
binaryOperator: keep
ternary: keep
braces:
alternativeSyntax: forbidden
bareStatementGroup: forbidden
position:
anonymousClass: nextLine
emptyBody:
propertyHook: keep
blankLines:
bodyStart:
controlStructure: keep
bodyEnd:
function: keep
closure: keep
controlStructure: keep
branch: keep
class: keep
anonymousClass: keep
aroundMethods: 1
aroundInterfaceMethods: 1
beforeOpenBrace:
class: keep
afterTraitUses: keep
betweenMemberKinds: 1
betweenMembers: # enum cases together
constant: 1
property: 1
beforeDocumentedMember: 1
afterDeclare: keep
afterAttributes: keep
beforeStatement:
continue: '1+'
return: '1+'
# method_argument_space ignores what spreads over lines, and Pint has nothing that would spread a construct
multiline:
items:
array: keep
list: keep
implements: keep
condition: keep
for: keep
logical: keep
concatenation: keep
arithmetic: keep
bitwise: keep
chain: keep
matchCondition: keep
ternary: keep
argument: keep
parameter: keep
edges:
array: keep
list: keep
implements: keep
condition: keep
chain: keep
argument: keep
parameter: keep
trailingComma:
argument: optional # arrays only
parameter: optional
matchArm: keep
closureUse: keep
operatorPosition:
condition: keep
comparison: keep
logical: keep
concatenation: keep
arithmetic: keep
bitwise: keep
coalesce: keep
pipe: keep
ternary: keep
attributePosition:
declaration: keep
parameter: keep
semicolon: sameLine
spacing:
anonymousClass: 'new class($a)'
comment:
marker: // text
concatenation: $a.$b
alignment:
binaryOperator: forbidden
comma: optional
objectOperator: '$a->b()'
doubleColon: 'Foo::bar()'
logicalNot: ! $x
reference: keep
spread: keep
namedArgument: keep
attribute: keep
alternativeSyntax: keep
body: keep
import: keep
arrowFunction: 'fn ($x) => $x'
padding:
array: forbidden
list: forbidden
offset: forbidden
semicolon:
after: 'for ($i = 0; $i < 5; $i++)'
naming:
class: keep
method: keep
constant: keep
enumCase: keep
casing:
magicConstant: uppercase
hexDigit: uppercase
function: declared # `strlen()`, not `StrLen()`
qualification:
staticInFinalClass: required # `self::` in a final class
class:
otherNamespace: imported
currentNamespace: bare
phpdoc: imported # `@param Request $request`, not `@param \Illuminate\Http\Request $request`
imports:
grouping:
class: separated
function: separated
constant: separated
order:
kinds: [constant, class, function]
names: alphabetical
unused: forbidden
aliasEqualToName: forbidden
classes:
members:
order: keep
grouping:
traitUse: keep
protectedInEnum: keep
visibility:
constant: keep
emptyParentheses:
instantiation: forbidden # `new Foo`
attribute: keep
instantiationWrapping: keep
functions:
trailingBareReturn: forbidden
arrowFunction:
emptyBody: keep
unreachableParameterDefault: forbidden
types:
nullPosition: keep
controlFlow:
switch:
fallThroughComment: keep
expressions:
parenthesesAfterConstruct: forbidden # `return $x;`, not `return ($x);`
assignment:
increment: postfix
doubleNegation: forbidden
comparison:
notEquals: '!='
yoda: forbidden
literals:
arraySyntax:
list: short
quotes: single
comments:
singleline: //
phpdoc:
empty: forbidden
forbiddenAnnotations: [@access, @package, @subpackage]
aboveAttributes: keep
stars: aligned
blankLines: trimmed
types:
builtin: canonical
annotation:
nativeTypeOnly: forbidden
hygiene:
aliasFunctions: [core, imap, pg]
upgrading:
php:
implicitNullable: forbidden
Sady
Sada o vizuálním vzhledu kódu nerozhoduje, a proto se se zvoleným standardem nikdy nedostane do křížku. Připojuje se
v konfiguraci vedle něj (use: [nette, modernizations]) a v čase se plynule rozvíjí: jakmile DressCode v nové
verzi získá další pravidla daného záměru, automaticky přibudou do příslušné sady a váš projekt je získá bez
nutnosti měnit konfigurační soubor.
modernizations
Modernější jazykové konstrukce PHP nahrazující starší zápisy téhož významu (každá podmíněná tím, zda ji cílové PHP projektu již bezpečně podporuje) a modernizace API nabízené soubory nainstalovaných migračních balíčků.
# Modernizations
#
# The construct a newer PHP has, written where an older one says the same, each where the target version of the
# project has it, and the newer form the upgrading files of the installed packages offer. It decides nothing about the
# layout, so it goes with any standard.
classes:
instantiationWrapping: forbidden # `new Foo()->bar()`, not `(new Foo())->bar()`
functions:
arrowFunction:
oneExpression: required # `fn() =>` for a closure only returning
arrowFunctionNested: yes # also one holding another closure
expressions:
ternary:
short: required # `$a ?: $b`
coalesce: required # `$a ?? $b`
literals:
arraySyntax: short # `[1, 2]`, `[$a] =`
hygiene:
classNameNotation:
currentClass: required # `self::class`, `static::class`
object: required # `$object::class`, PHP 8.0
upgrading:
packages: yes # as the upgrading files of the packages say
php:
sleepAndWakeup: forbidden # `__serialize()` and `__unserialize()` instead
syntax:
spreadForArrayMerge: required # `[...$a, ...$b]`, not `array_merge($a, $b)`
dynamicClassConstant: required # `X::{$name}`, not `constant(X::class . '::' . $name)`
match: required # for a `switch` returning or assigning
throwExpression: required # `$a ?? throw new E`
cloneWith: required # `clone($o, [...])`
nullsafe: required # `$a?->b`, not `$a !== null ? $a->b : null`
pipe: required # `$x |> a(...) |> b(...)`, not `b(a($x))`
firstClassCallables: required # `foo(...)`, not `Closure::fromCallable('foo')`
octalPrefix: required # `0o17`, not `017`
newInInitializer: required # `Clock $c = new SystemClock`, not a `null` swapped in the body
promotedProperties: required
readonlyClasses: required # `readonly class`, not `readonly` on every property
functions:
array_is_list: required
arraySearchFunctions: required # `array_find()`, `array_any()` for a `foreach`
arrayFirstLast: required # `array_first()`, not `reset()`
clamp: required # `clamp($x, $lo, $hi)`, not `max($lo, min($x, $hi))`
get_debug_type: required
json_validate: required # not `json_decode()` only testing
substringFunctions: required # `str_contains()`, not `strpos() !== false`
roundingMode: required # `RoundingMode::HalfAwayFromZero`, not `PHP_ROUND_HALF_UP`
phpdoc:
deprecated: required # `#[\Deprecated]`, not `@deprecated`
deprecations
Prvky, které novější verze PHP označily za zastaralé nebo odstranily: automaticky opravené tam, kde bezpečná náhrada existuje, a ohlášené tam, kde musí rozhodnout vývojář. K tomu navíc zavržená volání evidovaná v souborech nainstalovaných migračních balíčků.
# Deprecations
#
# What a newer PHP deprecated or removed, fixed where a replacement exists and reported where none does, and what the
# upgrading files of the installed packages list as retired. A deprecation is reported whatever the target, the code
# meeting it wherever it runs on the version that made it.
classes:
overridingMethod:
signature: required # a child matching a parent changed by an upgrade
parameterName: required
expressions:
cast: canonical # `(int)`, not `(integer)`, PHP 8.5
# what a library or PHP breaks in a major version, reported
mistakes:
finalParent: forbidden # a class extending a final one
unimplementedAbstractMethod: forbidden
__serialize: required # beside `Serializable`, PHP 8.1
upgrading:
packages: yes # as the upgrading files of the packages say
declarations:
deprecatedClass: forbidden # by what `@deprecated` names
deprecatedMember: forbidden
deprecatedFunction: forbidden
php:
nullArrayKey: forbidden # `['' => 1]`, not `[null => 1]`, PHP 8.5
nullArgument: forbidden # `trim($text ?? '')`, not `trim($text)` of a null, PHP 8.1
floatArrayKey: forbidden # `$a[(int) $ratio]`, not `$a[$ratio]` of a float, PHP 8.1
dynamicProperty: forbidden # a property the class declares, reported, PHP 8.2
caseSemicolon: forbidden # `case 1:`, not `case 1;`, PHP 8.5
implicitNullable: forbidden # `?int $x = null`, not `int $x = null`, PHP 8.4
deprecatedApi: forbidden # `mb_convert_encoding()`, not `utf8_encode()`, `E_STRICT` reported
deprecatedArgument: forbidden # `trigger_error($m, E_USER_ERROR)` reported
constructorReturnValue: forbidden # PHP 8.6
sleepAndWakeup: forbidden # `__serialize()`, PHP 8.5
returnInFinally: forbidden # PHP 8.6
csvEscapeArgument: required # the `escape` of `fgetcsv()` written out, PHP 8.4
dollarBraceInterpolation: forbidden # `{$name}`, not `${name}`, PHP 8.2
reservedNames: forbidden # `let`, `is`, `_`, PHP 8.6
backticks: forbidden # `shell_exec()`, PHP 8.5
syntax:
firstClassCallables: required # `foo(...)`, not `Closure::fromCallable('foo')`
cleanup
Zbytečný kód, který v programu reálně nic nedělá: duplicita toho, co jazyk říká sám, mrtvý kód, který se nikdy nevykoná, zbytečné aliasy funkcí i komentáře bez informační hodnoty.
# Cleanup
#
# Code that is there for nothing: what repeats what the language already says, what can never run, an alias of a
# function, a comment or a doc comment that says nothing. It decides nothing about the layout, so it goes with any
# standard.
braces:
bareStatementGroup: forbidden # no `{ ... }` that nothing opens
qualification:
leadingBackslash: forbidden # `strlen()` in a file without a namespace, not `\strlen()`
imports:
leadingBackslash: forbidden # `use Foo\Bar;`, not `use \Foo\Bar;`
unused: forbidden
aliasEqualToName: forbidden # `use Foo;`, not `use Foo as Foo;`
fromCurrentNamespace: forbidden # `use Acme\Shop\Order;` in `namespace Acme\Shop`
classes:
modifiers:
implied: forbidden # `final` in a final class
overridingMethod:
parentCallOnly: forbidden # one only forwarding to `parent::`, risky
ReturnTypeWillChange: forbidden # on a method declaring its return type
untypedPropertyNullDefault: forbidden # `public $a;`, not `public $a = null;`
functions:
trailingBareReturn: forbidden # no `return;` closing a function
unreachableParameterDefault: forbidden # a default before a required parameter
controlFlow:
emptyStatement: forbidden # `foo();;`
ifReturningTrueFalse: forbidden # `return $a > 1;` for an `if` returning `true` or `false`
elseAfterReturn: forbidden # the `else` after a `return` goes, its body following the `if`
expressions:
parenthesesAfterConstruct: forbidden # `return $x;`, not `return ($x);`
assignment:
targetInValue: forbidden # `$a += $b`, not `$a = $a + $b`
ternary:
ofTrueFalse: forbidden # `$a > 1`, not `$a > 1 ? true : false`
literals:
classNameInString: forbidden # `Order::class`, not `'Acme\Shop\Order'`
concatenatedLiterals: forbidden # `'ab'`, not `'a' . 'b'`
emptyStringConcatenation: forbidden # no `. ''`
comments:
empty: forbidden
phpdoc:
empty: forbidden
inheritdocOnly: forbidden # one holding only `{@inheritDoc}`
annotation:
nativeTypeOnly: forbidden # `@param int $a` on `int $a`
constantVar: forbidden # a bare `@var int` on a class constant
hygiene:
aliasFunctions: [core, imap, pg] # `count()`, not `sizeof()`
strlenEmptyTest: forbidden # `$s === ''`, not `strlen($s) === 0`
countEmptyTest: forbidden # `$a === []`, not `count($a) === 0`
call_user_func: forbidden # `$f($x)`, not `call_user_func($f, $x)`, risky
__invokeCall: forbidden # `$f($x)`, not `$f->__invoke($x)`
defaultArgument: forbidden # `json_encode($a)`, not `json_encode($a, 0)`
unreadCatchVariable: forbidden # `catch (E)` where `$e` is not read
repeatedCatchBody: forbidden # `catch (A|B $e)` for two catches of one body
separateIssets: forbidden # `isset($a, $b)`
separateUnsets: forbidden # `unset($a, $b);`
uselessVersionCheck: forbidden # no `if (PHP_VERSION_ID >= 80000)` the target always passes
correctness
Konstrukce, které s vysokou pravděpodobností představují chybu bez ohledu na zvolený formátovací styl.
# Correctness
#
# What is most likely a mistake whatever the style: a call comparing loosely where it should not, `$this` where no
# object is, a `catch` nothing reaches, a doc comment contradicting itself or written so that no tool reads it,
# invisible characters in the code.
phpdoc:
annotation:
extraParam: forbidden # a `@param` of no parameter
duplicateParam: forbidden # a second `@param` of one parameter
duplicateReturn: forbidden
empty: forbidden # `@param`, `@return`, `@var`, `@see` without content
duplicateVar: forbidden
plainComment: forbidden # `/* @var */` and `/**@var`, which no tool reads
mistakes:
__debugInfoReturningNull: forbidden # `[]`, not `null`
thisOutsideObject: forbidden # in a static method or a function, reported
staticSetState: required
continueInSwitch: forbidden # a `continue` only leaving a `switch`, reported
unreachableCatch: forbidden # after `catch (Throwable)`
exceptionWhereThrowableBelongs: forbidden # `catch (Exception)` where `Throwable` is meant, reported
errorSuppression: forbidden # the `@` operator, reported
invisibleCharacter: forbidden # zero-width, no-break and bidi characters, escaped
strictArgument: required # `in_array($a, $b, true)`, risky
trailingWhitespaceInString: forbidden # in a multi-line string or inline HTML
consecutivePhpdocs: forbidden # the first of two is lost, reported
repeatedAssignment: forbidden # `$a = $a = 1`
looseNumberComparison: forbidden # `0 == 'a'`, true before PHP 8.0, reported
countOfUncountable: forbidden # `count()` of no array and no `Countable`, reported
debugOutput: forbidden # `// var_dump($a);`, risky
types
Typové deklarace zapsané všude tam, kde jim samotné PHP rozumí.
# Types
#
# Types written where PHP reads them: a native type on every parameter, property and return that has one, one notation
# of each type (`?int`, `int` rather than `integer`, `array<T>` rather than `T[]`), `never` on a function that only
# throws, `Stringable` on a class with `__toString()`.
types:
declaration:
parameter: required # from `@param`, risky
property: required # from `@var`, risky
return: required # from `@return`, `void` where nothing returns, risky
anonymousFunction: required # `void` where a closure returns nothing, risky
nullable: '?int' # `?int`, not `int|null`
nullPosition: last # `int|string|null`
phpdoc:
annotation:
nativeTypeOnly: forbidden # the annotation the native type now says
types:
builtin: canonical # `int`, not `integer`
duplicate: forbidden # each type of a union once
array: 'array<T>' # `array<int>`, not `int[]`
mistakes:
neverReturnType:
function: required # on a function that always throws
method: required
upgrading:
classes:
Stringable: required # on a class with `__toString()`
compilerOptimizations
Globální funkce a konstanty zapsané do tvaru, který kompilátor PHP optimalizuje přímo při překladu (podrobnosti přináší stránka Rychlejší volání, když je jméno jisté).
# Compiler optimizations
#
# Global names written in the form PHP optimizes when it compiles the code: a function the compiler turns into an
# opcode and a constant it computes with are imported, so that the compiler knows them, and every other global name is
# written bare.
qualification:
function:
global: bare # `array_map()`, reached by the fallback at run time
optimized: imported # `use function strlen;`, its arguments positional
constant:
global: bare # `E_ALL`
optimized: [imported, backslashed] # `PHP_VERSION_ID` imported or `\PHP_VERSION_ID`