Ensuring a Well-Structured Switch Command in PHP
In PHP, the switch
statement is a powerful tool for controlling program flow, especially when dealing with multiple conditional branches. However, achieving a clean and efficient switch has its own snags. This post explores essential tips and practices for checking the quality of a ‘switch’ command to ensure code quality and optimize its performance.
Missing Default Entry
The first aspect to consider is whether the switch
statement includes a default
case. A default
acts as a fallback option, ensuring that the code executes when none of the defined case
match.
default
should always be present. In fact, the command match
, which is an alternative version of switch
, throws an exception when it cannot match any of the case
, and the default
is missing.
<?php switch($a) { case 0: echo 'A'; break; case 1: echo 'A'; break; } ?>
default
serve different purposes :
- Handle all other cases as one. This is when a finite list of cases is available, and anything outside that list can be handled in a single manner.
- Raise an error if ever is it reached. In this situation, the context preceding the
switch
ensures that a finite list of cases is handled. Then, whenever a case outside the legit ones reach the command, an exception should be raised.
<?php switch($a) { case 0: echo 'A'; break; case 1: echo 'A'; break; default: throw new MyException('This should be zero or one'); } ?>
Always ensure that switch has a default.
Duplicate Cases
It’s rewarded to examine a switch
statement for duplicate cases. PHP only process the first case that it encounters, ignoring the others. This leads to dead code, with some of the branches in the switch to be ignored and never used.
<?php switch($a) { case 'gif': // doSomething() and break case 'jpeg': case 'jpg': // doSomething() and break case 'png': // doSomething() and break case 'gif': // doSomething() and break default: throw new MyException('This should be an image format'); } ?>
Duplicates happen often in very long list of cases.
They also happen when the actual value is hidden by constant names, or also, by PHP’s type juggling. Indeed, switch
uses a relaxed comparison style, and some literals may actually be the same.
<?php // $a in [null, false, '', 0] switch($a) { // Most of the cases ends here, except 0 // because it is the first encountered case '': print 'Empty string';break; // 0 goes here case 0: print 'Zero';break; case false: print 'false';break; case null: print 'null';break; default: throw new MyException('This should be empty'); } ?>
Carefully review the cases values and remove the duplicates.
Duplicate Code Blocks
Another common issue is the repetition of code blocks in different case branches. This redundancy can bloat your switch
and make it challenging to update in the future. Consider refactoring your code to eliminate duplication and merge the cases.
<?php switch($a) { case 'john': $b = 0; break; case 'henry': $b = 10; break; // duplicate of case 'john' case 'julia': $b = 0; break; case 'jeroen': $b = 30; break; default: throw new MyException('This is an unexpected name'); } ?>
Usage of Enumeration Cases
PHP has built-in support for enumerations. One of the important advantages of enumerations is their finite nature. They represent a full collection of options, and no other option shall exist. This makes it easy to check if a switch
is covering all the possible situations.
<?php enum formats { case GIF; case JPG; case PNG; } switch($a) { case formats::GIF: // doSomething() and break; case formats::PNG: // doSomething() and break; // We are missing one case : JPG! // Unknown cases triggers an exception lazily (only if reached) case formats::TIFF: // doSomething() and break; default: throw new MyException('Unknown image format'); } ?>
Optimization with Simple Switch
PHP 7.2 introduced an optimisation for switch
.
Until then, PHP would review each cases one after the other, and stop as soon as it finds a matching value. With PHP 7.2 and later, PHP sets up a lookup table for the case values, when they are simple literals. Then, PHP jumps immediately to the right case, and bypass all other non-matching cases.
<?php switch($a) { case 1: // doSomething() and break; case 2: // doSomething() and break; default: throw new MyException('Unknown image format'); } ?>
Simple switch happens when all the cases are simple values, that can be directly compared to a incoming variable.
If the switch has a mix of expressions and simple values, it is recommended to separate the simple literals in a first switch
, and put the others in a second switch.
<?php switch($a) { case 1: // doSomething() and break; case 2: // doSomething() and break; default: switch($a) { case $b + 2: // doSomething() and break; case strtolower($a): // doSomething() and break; default: throw new MyException('Unknown case'); } } ?>
It might also be more convenient to process them in a different manner, with a previous condition, for example.
Cunning little switch
In conclusion, a well-structured switch
statement in PHP can greatly enhance your code’s clarity, maintainability, and performance. By checking for missing default entries, eliminating duplicate cases and code blocks, leveraging enumeration-like structures, and optimizing complex switches, you can ensure that your switch
statements remain an efficient and effective part of any PHP codebase.
Happy PHP code auditing