- Write functions with parameter and return types and explain what
strict_typesdoes - Use default, named and variadic arguments
- Apply
array_map,array_filter,array_reduceandusortwith arrow functions and closures
Imagine you calculate a product's price with VAT in ten different places on a shop website. When the tax rate changes, you have to fix ten places, and you are sure to miss one. A function solves this: you write the code once, give it a name and call it wherever you need.
Typed functions
A function is declared with the function keyword. In modern PHP it is standard to write the parameter types (float $price) and the return type (: float). A function that returns nothing is marked : void, one that may also return null uses ?string, and a parameter that accepts one of several types is written like int|float.
<?php
declare(strict_types=1);
function fullPrice(float $price, int $quantity = 1): float
{
return $price * $quantity;
}
function greet(string $name): string
{
return "Hello, $name!";
}
echo greet("Aysel"), "\n";
echo fullPrice(2.5, 4), "\n";
echo fullPrice(7.5), "\n";Hello, Aysel! 10 7.5
declare(strict_types=1); on the first line of a file turns on strict mode. In this mode PHP does not let you pass the string "5" to an int parameter and throws a TypeError. Without strict mode, PHP tries to convert the value, and mistakes stay hidden. The only exception: an int can always be passed to a float parameter.
function add($a, $b)
{
return $a + $b;
}
echo add("5", "3"); // 8, works by accidentdeclare(strict_types=1);
function add(int $a, int $b): int
{
return $a + $b;
}
echo add("5", "3"); // TypeErrorDefault, named and variadic arguments
If a parameter has a default value, you can leave it out of the call. Since PHP 8 there are named arguments: by writing bold: true you skip the parameters in between and pass the one you need by its name. ...$numbers collects any number of arguments into an array, and in a call, ...$marks “unpacks” an array into separate arguments.
<?php
function makeTag(string $text, string $tag = "p", bool $bold = false): string
{
$inner = $bold ? "<b>$text</b>" : $text;
return "<$tag>$inner</$tag>";
}
function average(float ...$numbers): float
{
return array_sum($numbers) / count($numbers);
}
echo makeTag("Hello"), "\n";
echo makeTag("Title", "h1"), "\n";
echo makeTag("Warning", bold: true), "\n";
echo average(4, 5, 3), "\n";
$marks = [5, 5, 4, 4];
echo average(...$marks), "\n";<p>Hello</p> <h1>Title</h1> <p><b>Warning</b></p> 4 4.5
Anonymous functions and closures
In PHP a function is a value: you can store a nameless function in a variable and call it later. To see an outside variable, an anonymous function “captures” it with use ($rate) — such a function is called a closure. An arrow function fn($x) => ... (PHP 7.4) is shorter: it consists of a single expression and captures outside variables automatically.
<?php
$rate = 1.7;
$toAzn = function (float $usd) use ($rate): float {
return $usd * $rate;
};
$toUsd = fn(float $azn): float => round($azn / $rate, 2);
echo $toAzn(10), "\n";
echo $toUsd(34), "\n";
function makeCounter(): Closure
{
$count = 0;
return function () use (&$count): int {
return ++$count;
};
}
$next = makeCounter();
$next();
$next();
echo $next(), "\n";17 20 3
use ($rate) captures a copy of the value. use (&$count) refers to the variable itself, so the counter grows with every call and lives on after makeCounter has finished. Arrow functions always capture by value and cannot change an outside variable.
Functions inside array functions
Closures are used most of all with array functions. array_map transforms every element, array_filter keeps the elements that pass a test, array_reduce “folds” an array into one value, and usort sorts using your comparison function. Below we add 18% VAT to prices and rank students by score.
<?php
$prices = [12, 45, 7, 30];
$withVat = array_map(fn($p) => $p * 1.18, $prices);
echo implode(", ", $withVat), "\n";
$expensive = array_filter($prices, fn($p) => $p > 10);
print_r($expensive);
$total = array_reduce($prices, fn($sum, $p) => $sum + $p, 0);
echo $total, "\n";
$students = [
["name" => "Murad", "score" => 78],
["name" => "Leyla", "score" => 95],
["name" => "Elvin", "score" => 86],
];
usort($students, fn($a, $b) => $b["score"] <=> $a["score"]);
echo implode(", ", array_column($students, "name")), "\n";14.16, 53.1, 8.26, 35.4
Array
(
[0] => 12
[1] => 45
[3] => 30
)
94
Leyla, Elvin, MuradKey points
- Write parameter and return types;
declare(strict_types=1)forbids automatic conversions and exposes mistakes. - Parameters with default values can be left out, and named arguments (
bold: true) let you skip the ones in between. ...$argsgathers arguments into an array, and...$arrayin a call unpacks an array into arguments.- A closure captures outside variables with
use(by reference with&), while an arrow functionfncaptures them automatically, by value. array_map,array_filter,array_reduceandusortwith closures process arrays briefly and clearly;array_filterkeeps the keys.
Check yourself
10 questions. Every correct answer earns XP.
declare(strict_types=1);. What happens if we call function add(int $a, int $b): int as add("5", "3")?