В статье Что нового в PHP 7.2 мы рассмотрели основные изменения новой версии php, релиз которой ожидается в конце ноября. Сегодня мы рассмотрим небольшую, но полезную оптимизацию оператора switch
, добавленную в php 7.2. Эта статья является вольным переводом статьи PHP 7.2’s «switch» optimisations от Дерика Ретанса, автора Xdebug и других полезных инструментов для php.
Как было до PHP 7.2
До версии 7.2, php выполнял выражения case
по порядку. Рассмотрим следующий простой пример:
$cc = "no"; switch ( $cc ) { case 'de': echo "DEUTSCH"; break; case 'en': echo "English"; break; case 'nl': echo "Nederlands"; break; case 'no': echo "Norsk"; break; default: echo "unknown"; break; }
Для интерпретатора php выполнение этого кода аналогично следующему:
$cc = "no"; if ($cc == 'de') { echo "DEUTSCH"; } elseif ($cc == 'en') { echo "English"; } elseif ($cc == 'nl') { echo "Nederlands"; } elseif ($cc == 'no') { echo "Norsk"; } else { echo "unknown"; break; }
Да, php сравнивает переменную $cc
с каждым значением case
по очереди. Это означает, что если наиболее часто используемое значение case
находится в конце списка, то код тратит процессорное время в пустую.
Оптимизация оператора switch в PHP 7.2
В php 7.2 добавилась оптимизация, преобразующая такую последовательность if-ов в таблицу переходов. Стоит заметить, что это работает, если все значения case
являются только строками, или только числами. Если в условиях case
есть значения разных типов, то оператор switch
будет работать так же, как и в предыдущих версиях php.
Так как php не поддерживает оператор goto
, рассмотрим как это работает с помощью следующего псевдокода:
$cc = "no"; $_table = [ "de" => 1, "en" => 2, "nl" => 3, "no" => 4 ]; if (gettype($cc) == 'string') { if (array_key_exists($cc, $_table)) { goto "jmp_{$_table[$cc]}"; } else { goto jmp_default; } } else { /* do original if/else if/else sequence */ } jmp_1: echo "DEUTSCH"; goto end; jmp_2: echo "English"; goto end; jmp_3: echo "Nederlands"; goto end; jmp_4: echo "Norsk"; goto end; jmp_default: echo "unknown"; end;
В этом примере видно, что если значение $cc
является строкой, то будет использована таблица переходов $_table
для немедленного перехода к выполнению кода, соответствующего нужно условию case
. Если $cc
— не строка, будут выполнены обычные сравнения if/else
.
Заключение
Данная оптимизация является очень полезным обновлением, особенно если в коде используются длинные списки switch/case
.
Протестировать новую версию php можно с помощью официального docker-контейнера PHP.
switch вообще лучше избегать. ООП же.
Даже в ООП должно быть место, где выбирается конкретная реализация. И тут без условных операторов не обойтись.