Expressions
The expressions section decides on operators and expressions: strict comparison with
===, != instead of <>, && instead of and,
(int) instead of (integer), Yoda conditions, parentheses where operator precedence is easy to get wrong,
and ternary operators that can be written shorter with ?? or ?:, or not at all.
Most decisions of this section make the code shorter and keep its meaning. The exception is
comparison.loose: forbidden: it turns == into ===, and code that relied on the loose
comparison then behaves differently, so the fix is a risky fix. With types from
PHPStan, DressCode makes the fix by itself where the code compares values for which loose and strict comparison give the same
result; elsewhere it waits for your permission in the fixRisky key.
expressions:
comparison:
loose: forbidden
notEquals: '!='
wordLogicalOperator: forbidden
assignment:
targetInValue: forbidden
coalesce: required
ternary:
short: required
ofTrueFalse: forbidden
doubleNegation: forbidden
if ($status == 'paid' and $total <> 0) { // The `==` comparison must be written `===`. // The `and` operator must be written `&&`. // The `<>` comparison must be written `!==`.
$count = $count + 1; // The assignment must be written `+=` instead of repeating its target.
$name = isset($input) ? $input : 'anonymous'; // The ternary testing for null must be written with `??`.
$title = $this->title ? $this->title : $default; // The ternary repeating its condition must be written `?:`.
$valid = $total > 0 ? true : false; // Useless ternary operator, because the condition itself is the result.
$flag = !!$value; // The double negation `!!` must be written as a `(bool)` cast.
}
if ($status === 'paid' && $total !== 0) {
$count += 1;
$name = $input ?? 'anonymous';
$title = $this->title ?: $default;
$valid = $total > 0;
$flag = (bool) $value;
}
Here the <> is changed twice: notEquals turns it into !=, and loose
then turns that into !==.
expressions.cast
The type name of a cast.
canonical:(int),(bool),(float),(string)in lowercase, never(integer),(Boolean),(double),(binary)keep: nothing is enforced, the code stays as it is
Default keep · standards: perCs canonical, psr12 canonical,
nette canonical, symfony canonical, laravel canonical · covers
lowercase_cast, short_scalar_cast, Generic.PHP.LowerCaseType,
PSR12.Keywords.ShortFormTypeKeywords,
SlevomatCodingStandard.PHP.TypeCast, Squiz.WhiteSpace.CastSpacing
expressions.assignment.targetInValue
$a = $a + $b is $a += $b.
Not about the $a += 1 it may write, which expressions.assignment.addingOne writes as an
increment.
forbidden: never therekeep: nothing is enforced, the code stays as it is
Default keep · standards: perCs keep, psr12 keep, nette
forbidden, symfony keep, laravel keep · covers
assign_null_coalescing_to_coalesce_equal, long_to_shorthand_operator,
SlevomatCodingStandard.ControlStructures.RequireNullCoalesceEqualOperator, SlevomatCodingStandard.Operators.RequireCombinedAssignmentOperator
expressions.explicitPrecedence
An operand where logical or bitwise operators are easy to misread is parenthesized.
required: always therekeep: nothing is enforced, the code stays as it is
Default keep · standards: perCs keep, psr12 keep, nette
required, symfony keep, laravel keep ·
covers Generic.CodeAnalysis.RequireExplicitBooleanOperatorPrecedence
expressions.assignment.increment
An increment or a decrement whose value is not read, a statement or a step of for; inside an expression it stays
as it is.
prefix:++$i,--$ipostfix:$i++,$i--keep: nothing is enforced, the code stays as it is
Default keep · standards: perCs keep, psr12 keep, nette
postfix, symfony prefix, laravel postfix · covers increment_style
expressions.assignment.addingOne
$i += 1, which is $i++, or ++$i by expressions.assignment.increment.
forbidden: never therekeep: nothing is enforced, the code stays as it is
Default keep · standards: perCs keep, psr12 keep, nette
forbidden, symfony forbidden, laravel keep ·
covers standardize_increment
expressions.wordLogicalOperator
and, or bind weaker than = and are written &&, ||.
forbidden: never therekeep: nothing is enforced, the code stays as it is
Default keep · standards: perCs keep, psr12 keep, nette
forbidden, symfony keep, laravel keep · covers
logical_operators, Squiz.Operators.ValidLogicalOperators
expressions.doubleNegation
!!$a is (bool) $a.
forbidden: never therekeep: nothing is enforced, the code stays as it is
Default keep · standards: perCs keep, psr12 keep, nette
forbidden, symfony forbidden, laravel forbidden ·
covers no_short_bool_cast
expressions.comparison.loose
A loose comparison, ==, != or <>, is written === or
!==.
forbidden: never therekeep: nothing is enforced, the code stays as it is
Default keep · standards: perCs keep, psr12 keep, nette
forbidden, symfony keep, laravel keep · covers
strict_comparison, SlevomatCodingStandard.Operators.DisallowEqualOperators
expressions.comparison.notEquals
The operator of inequality.
Not about whether the comparison is loose, which expressions.comparison.loose decides.
'!=':!=, never<>keep: nothing is enforced, the code stays as it is
Default keep · standards: perCs keep, psr12 keep, nette
'!=', symfony '!=', laravel '!=' · covers standardize_not_equals
expressions.coalesce
$a ?? $b written for isset($a) ? $a : $b and $a !== null ? $a : $b.
required: always therekeep: nothing is enforced, the code stays as it is
Default keep · standards: perCs keep, psr12 keep, nette
required, symfony keep, laravel keep · covers
ternary_to_null_coalescing, SlevomatCodingStandard.ControlStructures.RequireNullCoalesceOperator
expressions.ternary.short
$a ?: $b, written for $a ? $a : $b or the other way round where the condition can be read twice.
required: always thereforbidden: never therekeep: nothing is enforced, the code stays as it is
Default keep · standards: perCs keep, psr12 keep, nette
required, symfony keep, laravel keep · covers ternary_to_elvis_operator,
SlevomatCodingStandard.ControlStructures.DisallowShortTernaryOperator, SlevomatCodingStandard.ControlStructures.RequireShortTernaryOperator
Shortening changes how many times the condition is evaluated, and that is not always harmless:
array_shift($queue) ? array_shift($queue) : null removes two items from the queue, while the short form removes only
one. So only expressions that can be read twice without a side effect are shortened: variables, array items, properties, constants
and scalar values. This is where the syntax tree shows its value: PHP CS Fixer marks its counterpart
ternary_to_elvis_operator as risky, because in a flat array of tokens it cannot tell a call from a variable; here the
tree decides.
expressions:
ternary:
short: required
$name = $input ? $input : 'anonymous'; // The ternary repeating its condition must be written `?:`.
$size = $options['size'] ? $options['size'] : 10; // The ternary repeating its condition must be written `?:`.
$next = array_shift($queue) ? array_shift($queue) : null;
$name = $input ?: 'anonymous';
$size = $options['size'] ?: 10;
$next = array_shift($queue) ? array_shift($queue) : null;
expressions.parenthesesAfterConstruct
return ($x); is return $x;.
forbidden: never therekeep: nothing is enforced, the code stays as it is
Default keep · standards: perCs keep, psr12 keep, nette
forbidden, symfony forbidden, laravel forbidden · covers include,
no_unneeded_control_parentheses,
SlevomatCodingStandard.ControlStructures.LanguageConstructWithParentheses, Squiz.Strings.EchoedStrings
expressions.ternary.ofTrueFalse
$a > 1 ? true : false is the condition itself, where it is a boolean.
forbidden: never therekeep: nothing is enforced, the code stays as it is
Default keep · standards: perCs keep, psr12 keep, nette
forbidden, symfony keep, laravel keep ·
covers SlevomatCodingStandard.ControlStructures.UselessTernaryOperator
expressions.comparison.yoda
A Yoda comparison, the constant on its left side, a comparison of two variables or calls staying as it is.
forbidden: never there, the variable on the left and the constant on the right,$a === 1required: always there, the constant on the left,1 === $a, so that an accidental assignment does not compilekeep: nothing is enforced, the code stays as it is
Default keep · standards: perCs keep, psr12 keep, nette
forbidden, symfony required, laravel forbidden · covers yoda_style,
SlevomatCodingStandard.ControlStructures.DisallowYodaComparison, SlevomatCodingStandard.ControlStructures.RequireYodaComparison