Functions

The functions section decides on functions and closures (anonymous functions): whether a closure that only returns one expression is written as an arrow function, whether a closure that does not use $this gets static, whether true and false are passed as named arguments, and what to do with a return at the end of a function, with a parameter default that is never used, and with a function declared inside another function.

How a signature and its parentheses are written belongs to other sections: the spacing section covers the space after function and fn, the braces section covers the brace of a closure, and the types section covers the types of parameters.

functions:
	arrowFunction:
		oneExpression: required
	trailingBareReturn: forbidden
	unreachableParameterDefault: forbidden
$names = array_map(function ($user) {  // The closure returning a single expression must be written as an arrow function.
	return $user->name;
}, $users);

function writeLog(string $message, $level = 'info', $context)  // Useless default value of `$level`, because a required parameter follows.
{
	echo $message;
	return;  // Useless return, because the function ends right after it.
}
$names = array_map(fn($user) => $user->name, $users);

function writeLog(string $message, $level, $context)
{
	echo $message;
}

A closure is rewritten to an arrow function only where the two mean the same: the closure does not capture a variable by reference, it does not touch a variable it does not name, and no comment is lost.

functions.trailingBareReturn

return; as the last statement.

  • forbidden: never there
  • keep: nothing is enforced, the code stays as it is

Default keep · standards: perCs keep, psr12 keep, nette forbidden, symfony forbidden, laravel forbidden · covers no_useless_return

functions.arrowFunction.oneExpression

An arrow function written for a closure whose body is a single return, where that is equivalent: no variable captured by reference, none reached by a name it does not spell out, no comment lost.

Not about the static of the arrow function, which functions.staticClosure decides.

  • required: always there
  • keep: nothing is enforced, the code stays as it is

Default keep · standards: perCs keep, psr12 keep, nette required, symfony keep, laravel keep · covers use_arrow_functions, SlevomatCodingStandard.Functions.RequireArrowFunction

functions.arrowFunction.emptyBody

fn() => null written for a closure with an empty body, one returning void, never or by reference aside.

  • required: always there
  • keep: nothing is enforced, the code stays as it is

Default keep · standards: perCs required, psr12 keep, nette keep, symfony required, laravel keep

functions.arrowFunctionNested

Whether a closure holding another closure or arrow function becomes one too.

  • yes or no

Default no · standards: perCs no, psr12 no, nette yes, symfony no, laravel no · covers SlevomatCodingStandard.Functions.RequireArrowFunction

functions.namedBooleanArgument

A true or false passed by position to a parameter declared bool, written with the name of the parameter, json_decode($json, associative: true); not where a positional argument follows it, nor in a call of a setter, setVisible(true), or of a function PHP calls without a frame or optimizes.

  • required: always there
  • keep: nothing is enforced, the code stays as it is

Default keep · standards: perCs keep, psr12 keep, nette keep, symfony keep, laravel keep

A named argument says what the true or false turns on or off, so the reader does not have to look up the signature. DressCode names only arguments at the end of a call, with no positional argument after them, because PHP does not accept a positional argument after a named one. A setter like setVisible(true) already says this in its name, so it stays as it is. Functions that PHP calls without a stack frame, or that the compiler optimizes, such as in_array(), also stay as they are: a named argument would cost them the optimization. You can list other functions and methods in functions.namedBooleanArgumentExcept.

DressCode knows the parameter names of PHP functions by itself. For methods, new and the functions of your project, it needs the types of the code; without them, it leaves such calls alone.

functions:
	namedBooleanArgument: required
	namedBooleanArgumentExcept: [print_r]
$data = json_decode($json, true);  // The boolean argument of `json_decode()` must be named `associative`.
$html = htmlspecialchars($text, ENT_QUOTES, 'UTF-8', false);  // The boolean argument of `htmlspecialchars()` must be named `double_encode`.
echo print_r($data, true);
$data = json_decode($json, associative: true);
$html = htmlspecialchars($text, ENT_QUOTES, 'UTF-8', double_encode: false);
echo print_r($data, true);

The fix is a risky fix for a call to a method that a child class can override, such as $this->render(true) in a class that is not final, or a call through an interface: the child class can give the parameter a different name, and the call would then fail. Calls to a private or final method, calls to a method of a final class, calls through self:: and parent::, and new are not risky. Risky fixes are made once you list the decision in the fixRisky key.

functions.namedBooleanArgumentExcept

The functions and methods whose arguments stay positional, a method written with the class declaring it or the one it is called on, Acme\Shop\Cart::add.

  • a list of names

Default [] · standards: perCs [], psr12 [], nette [], symfony [], laravel []

functions.innerFunction

A named function declared inside a function, a method, a closure or a property hook, which is only reported.

  • forbidden: never there
  • keep: nothing is enforced, the code stays as it is

Default keep · standards: perCs keep, psr12 keep, nette forbidden, symfony keep, laravel keep · covers Squiz.PHP.InnerFunctions

functions.staticClosure

The static keyword of a closure or an arrow function that does not use $this.

  • required: always there, static function
  • keep: nothing is enforced, the code stays as it is

Default keep · standards: perCs keep, psr12 keep, nette keep, symfony keep, laravel keep · covers static_lambda, SlevomatCodingStandard.Functions.StaticClosure

The fix is a risky fix: a closure can be bound to an object somewhere else, through bindTo() or Closure::bind(), and that cannot be seen from a single file. The fix is made once you list the decision in the fixRisky key.

functions.unreachableParameterDefault

A default a required parameter after it makes unreachable.

  • forbidden: never there
  • keep: nothing is enforced, the code stays as it is

Default keep · standards: perCs keep, psr12 keep, nette forbidden, symfony keep, laravel forbidden · covers no_unreachable_default_argument_value, PEAR.Functions.ValidDefaultValue, SlevomatCodingStandard.Functions.UselessParameterDefaultValue