Statement on glibc/iconv Vulnerability

match

(PHP 8)

La valutazione dei rami dell'espressione match basata su un controllo di identità di un valore. Analogamente a un'istruzione switch, un' espressione match ha un'espressione soggetto che viene confrontata con più alternative. A differenza di switch, restituirà un valore molto simile alle espressioni ternarie. A differenza di switch, il confronto è un controllo di identità (===) piuttosto che un controllo di uguaglianza debole (==). Le espressioni di corrispondenza sono disponibili a partire da PHP 8.0.0.

Example #1 Structure of a match expression

<?php
$return_value
= match (subject_expression) {
single_conditional_expression => return_expression,
conditional_expression1, conditional_expression2 => return_expression,
};
?>

Nota: Non è necessario utilizzare il risultato di un'espressione match.

Nota: Un'espressione match deve essere terminata da un punto e virgola ;.

L'espressione match è simile a un' istruzione switch ma presenta alcune differenze fondamentali:

  • Un braccio match confronta i valori rigorosamente (===) invece che vagamente come fa l'istruzione switch.
  • Un'espressione match restituisce un valore.
  • Le braccia match non ricadono in casi successivi come fanno le istruzioni switch.
  • Un'espressione match deve essere esaustiva.

Come le istruzioni switch, le espressioni match vengono eseguite braccio di confronto per braccio di confronto. All'inizio non viene eseguito alcun codice. Le espressioni condizionali vengono valutate solo se tutte le espressioni condizionali precedenti non sono riuscite a corrispondere all'espressione del soggetto. Verrà valutata solo l'espressione di ritorno corrispondente all'espressione condizionale corrispondente. Per esempio:

<?php
$result
= match ($x) {
foo() => ...,
$this->bar() => ..., // bar() non viene chiamata se foo() === $x
$this->baz => beep(), // beep() non è chiamata a meno che $x === $this->baz
// etc.
};
?>

I bracci delle espressioni match possono contenere più espressioni separate da una virgola. Questo è un OR logico, ed è una scorciatoia per più bracci di corrispondenza con lo stesso lato destro.

<?php
$result
= match ($x) {
// Questo braccio di corrispondenza:
$a, $b, $c => 5,
// È equivalente a questi tre bracci di corrispondenza:
$a => 5,
$b => 5,
$c => 5,
};
?>

Un caso speciale è il pattern default. Questo modello corrisponde a tutto ciò che non era stato precedentemente trovato. Per esempio:

<?php
$expressionResult
= match ($condition) {
1, 2 => foo(),
3, 4 => bar(),
default =>
baz(),
};
?>

Nota: Più pattern predefiniti solleveranno un errore E_FATAL_ERROR.

Un'espressione match deve essere esaustiva. Se l' espressione del soggetto non è gestita da alcun braccio di corrispondenza, viene lanciato un UnhandledMatchError.

Example #2 Esempio di un'espressione di corrispondenza non gestita

<?php
$condition
= 5;

try {
match (
$condition) {
1, 2 => foo(),
3, 4 => bar(),
};
} catch (
\UnhandledMatchError $e) {
var_dump($e);
}
?>

Il precedente esempio visualizzerà:

object(UnhandledMatchError)#1 (7) {
  ["message":protected]=>
  string(33) "Unhandled match value of type int"
  ["string":"Error":private]=>
  string(0) ""
  ["code":protected]=>
  int(0)
  ["file":protected]=>
  string(9) "/in/ICgGK"
  ["line":protected]=>
  int(6)
  ["trace":"Error":private]=>
  array(0) {
  }
  ["previous":"Error":private]=>
  NULL
}

Utilizzo di espressioni di corrispondenza per gestire i controlli non di identità

È possibile utilizzare un'espressione match per gestire casi condizionali non di identità utilizzando true come espressione del soggetto.

Example #3 Utilizzo di espressioni di corrispondenza generalizzate per ramificare su intervalli interi

<?php

$age
= 23;

$result = match (true) {
$age >= 65 => 'senior',
$age >= 25 => 'adult',
$age >= 18 => 'young adult',
default =>
'kid',
};

var_dump($result);
?>

Il precedente esempio visualizzerà:

string(11) "young adult"

Example #4 Utilizzo di espressioni di corrispondenza generalizzate per ramificare sul contenuto della stringa

<?php

$text
= 'Bienvenue chez nous';

$result = match (true) {
str_contains($text, 'Welcome') || str_contains($text, 'Hello') => 'en',
str_contains($text, 'Bienvenue') || str_contains($text, 'Bonjour') => 'fr',
// ...
};

var_dump($result);
?>

Il precedente esempio visualizzerà:

string(2) "fr"
add a note

User Contributed Notes 9 notes

up
76
darius dot restivan at gmail dot com
3 years ago
This will allow for a nicer FizzBuzz solution:

<?php

function fizzbuzz($num) {
print match (
0) {
$num % 15 => "FizzBuzz" . PHP_EOL,
$num % 3 => "Fizz" . PHP_EOL,
$num % 5 => "Buzz" . PHP_EOL,
default =>
$num . PHP_EOL,
};
}

for (
$i = 0; $i <=100; $i++)
{
fizzbuzz($i);
}
up
63
Anonymous
2 years ago
<?php
function days_in_month(string $month, $year): int
{
return match(
strtolower(substr($month, 0, 3))) {
'jan' => 31,
'feb' => is_leap($year) ? 29 : 28,
'mar' => 31,
'apr' => 30,
'may' => 31,
'jun' => 30,
'jul' => 31,
'aug' => 31,
'sep' => 30,
'oct' => 31,
'nov' => 30,
'dec' => 31,
default => throw new
InvalidArgumentException("Bogus month"),
};
}
?>

can be more concisely written as

<?php
function days_in_month(string $month, $year): int
{
return match(
strtolower(substr($month, 0, 3))) {
'apr', 'jun', 'sep', 'nov' => 30,
'jan', 'mar', 'may', 'jul', 'aug', 'oct', 'dec' => 31,
'feb' => is_leap($year) ? 29 : 28,
default => throw new
InvalidArgumentException("Bogus month"),
};
}
?>
up
5
Sbastien
1 year ago
I use match instead of storing PDOStatement::rowCount() result and chaining if/elseif conditions or use the ugly switch/break :

<?php

$sql
= <<<SQL
INSERT INTO ...
ON DUPLICATE KEY UPDATE ...
SQL;

$upkeep = $pdo->prepare($sql);

$count_untouched = 0;
$count_inserted = 0;
$count_updated = 0;

foreach (
$data as $record) {
$upkeep->execute($record);
match (
$upkeep->rowCount()) {
0 => $count_untouched++,
1 => $count_inserted++,
2 => $count_updated++,
};
}

echo
"Untouched rows : {$count_untouched}\r\n";
echo
"Inserted rows : {$count_inserted}\r\n";
echo
"Updated rows : {$count_updated}\r\n";
up
54
Hayley Watson
3 years ago
As well as being similar to a switch, match expressions can be thought of as enhanced lookup tables — for when a simple array lookup isn't enough without extra handling of edge cases, but a full switch statement would be overweight.

For a familiar example, the following
<?php

function days_in_month(string $month): int
{
static
$lookup = [
'jan' => 31,
'feb' => 0,
'mar' => 31,
'apr' => 30,
'may' => 31,
'jun' => 30,
'jul' => 31,
'aug' => 31,
'sep' => 30,
'oct' => 31,
'nov' => 30,
'dec' => 31
];

$name = strtolower(substr($name, 0, 3));

if(isset(
$lookup[$name])) {
if(
$name == 'feb') {
return
is_leap($year) ? 29 : 28;
} else {
return
$lookup[$name];
}
}
throw new
InvalidArgumentException("Bogus month");
}

?>

with the fiddly stuff at the end, can be replaced by

<?php
function days_in_month(string $month): int
{
return match(
strtolower(substr($month, 0, 3))) {
'jan' => 31,
'feb' => is_leap($year) ? 29 : 28,
'mar' => 31,
'apr' => 30,
'may' => 31,
'jun' => 30,
'jul' => 31,
'aug' => 31,
'sep' => 30,
'oct' => 31,
'nov' => 30,
'dec' => 31,
default => throw new
InvalidArgumentException("Bogus month"),
};
}
?>

Which also takes advantage of "throw" being handled as of PHP 8.0 as an expression instead of a statement.
up
5
thomas at zuschneid dot de
1 year ago
While match allows chaining multiple conditions with ",", like:
<?php
$result
= match ($source) {
cond1, cond2 => val1,
default =>
val2
};
?>
it seems not valid to chain conditions with default, like:
<?php
$result
= match ($source) {
cond1 => val1,
cond2, default => val2
};
?>
up
10
php at joren dot dev
2 years ago
If you want to execute multiple return expressions when matching a conditional expression, you can do so by stating all return expressions inside an array.

<?php
$countries
= ['Belgium', 'Netherlands'];
$spoken_languages = [
'Dutch' => false,
'French' => false,
'German' => false,
'English' => false,
];

foreach (
$countries as $country) {
match(
$country) {
'Belgium' => [
$spoken_languages['Dutch'] = true,
$spoken_languages['French'] = true,
$spoken_languages['German'] = true,
],
'Netherlands' => $spoken_languages['Dutch'] = true,
'Germany' => $spoken_languages['German'] = true,
'United Kingdom' => $spoken_languages['English'] = true,
};
}

var_export($spoken_languages);
// array ( 'Dutch' => true, 'French' => true, 'German' => true, 'English' => false, )

?>
up
5
mark at manngo dot net
2 years ago
While you can’t polyfill a language construct, you can mimic the basic behaviour with a simple array.

Using example 2 above:

<?php
$food
= 'apple';
$return_value = match ($food) {
'apple' => 'This food is an apple',
'bar' => 'This food is a bar',
'cake' => 'This food is a cake',
};
print
$return_value;
?>

… you can get something similar with:

<?php
$food
= 'apple';
$return_value = [
'apple' => 'This food is an apple',
'bar' => 'This food is a bar',
'cake' => 'This food is a cake',
][
$food];
print
$return_value;
?>
up
1
tolga dot ulas at tolgaulas dot com
1 month ago
Yes it currently does not support code blocks but this hack works:

match ($foo){
'bar'=>(function(){
echo "bar";
})(),
default => (function(){
echo "baz";
})()
};
up
-1
tm
2 years ago
If you are using a match expression for non-identity checks as described above make sure whatever you are using is actually returning `true` on success.

Quite often you rely on truthy vs. falsy when using if conditions and that will not work for match (for example `preg_match`). Casting to bool will solve this issue.
To Top