This RFC proposes to add a fallthrough; marker to switch to prevent accidental fallthrough, helping to avoid bugs while making intentional fallthrough visible and explicit.
Empty case bodies, usually for a list of matches, will still require no fallthrough;.
Implicit fallthrough is a source of bugs and potential confusion. Many languages found ways to clarify developer intent. When match was introduced fallthrough was omitted explicitly deliberately to avoid these issues, while php-src itself uses ZEND_FALLTHROUGH in its C code to allow for compiler warnings.
We propose to emit a E_DEPRECATED or E_WARNING if a non-empty case or default body falls through to the next case without an explicit fallthrough;.
<?php switch ($mode) { case 'create': // Non-empty case falls through to the next case, terminate the case with "fallthrough;" if this is intentional. audit('create'); case 'update': persist($payload); break; } switch ($mode) { case 'create': audit('create'); fallthrough; // Indicate the the fallthrough is intentional. case 'update': persist($payload); break; } switch ($extension) { case 'jpg': // No fallthrough needed here, intent is obvious case 'jpeg': return 'image/jpeg'; case 'png': return 'image/png'; } ?>
During compilation, each non-empty case / default body that is followed by another case in source order is checked.
If its last statement is not one of the following, the compiler emits an E_DEPRECATED or E_WARNING:
break; / break …;continue; / continue …;return; / return …;throw …;goto …;fallthrough;The warning text used by the proof of concept is:
Non-empty case falls through to the next case, terminate the case with "fallthrough;" if this is intentional. [ This behavior is deprecated in PHP 8.6 and will emit an E_WARNING in PHP 9.0. ]
Empty cases remain valid and continue to be used for grouped labels. They do not require fallthrough;.
<?php switch ($token) { case T_STRING: $value = normalize($value); fallthrough; case T_NAME_QUALIFIED: $result[] = validate($value); break; } switch ($extension) { case 'jpg': case 'jpeg': return 'image/jpeg'; case 'png': return 'image/png'; } ?>
The proof of concept implements fallthrough; as a (useless) read of the fallthrough constant. This enables using this construct in older PHP versions when paired with a one-line polyfill:
This keeps migration friction low for libraries, tooling, and mixed-version codebases.
The desired diagnostic level for that situation is left to a proposed secondary vote.
Empty case bodies fall through implicitly.
<?php switch ($extension) { case 'jpg': case 'jpeg': return 'image/jpeg'; case 'png': return 'image/png'; } ?>
PHP allows, but discourages, the default: to be in the non-final position in the switch. Fallthrough rules apply as if this would be a case …: statement.
<?php switch ($extension) { case 'bin': default: // Uncommon but allowed, no fallthrough needed case 'binary': return 'application/octet-stream'; case 'jpg': case 'jpeg': return 'image/jpeg'; } switch ($extension) { default: log('unknown extension'); fallthrough; // Fallthrough needed here case 'bin': case 'binary': return 'application/octet-stream'; case 'jpg': case 'jpeg': return 'image/jpeg'; } ?>
-Wimplicit-fallthrough compiler warning and C23's [[fallthrough]] attribute. This is the closest equivalent to what this RFC proposes.fallthrough is a reserved keyword; switch does not fall through by default.
Code that previously fell through silently from a non-empty case will now emit an E_DEPRECATED or E_WARNING. This is comparable to the existing warning for continue; targeting switch(): legal code remains legal, but likely mistakes become visible.
The proof of concept exposes fallthrough as a global constant. Whether the final implementation keeps that representation or uses a dedicated contextual/compiler-recognized form is an implementation detail; the source-level syntax proposed by this RFC is fallthrough;.
PHP 8.6.
Tooling gets a canonical, machine-readable marker for intentional fallthrough instead of relying on comments like /* fallthrough */ or project-specific conventions. Because fallthrough; is source-compatible with older runtimes via a trivial polyfill, analyzers and coding standards can adopt it immediately.
Existing usages and rules around fallthrough, excerpt:
None.
None.
None.
None.
Primary Vote requiring a 2/3 majority to accept the RFC:
Secondary vote requiring a simple majority, breaking ties towards E_WARNING:
Proof of concept:
If accepted, this section will be updated with the merge target version, implementing commit(s), and manual entry.
This RFC deliberately does not make implicit fallthrough an error and does not redesign switch. The goal is to keep migration cost low.