PHP RFC: Explicit fallthrough for ''switch''
- Version: 0.1
- Date: 2026-04-21
- Author: Volker Dusch volker@tideways-gmbh.com, Tim Düsterhus tim@tideways-gmbh.com
- Status: Draft
- Target Version: PHP 8.6
- Implementation: tbd
Introduction
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'; } ?>
Proposal
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.
Examples
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'; } ?>
Prior Art
- C/C++: The
-Wimplicit-fallthroughcompiler warning and C23's [[fallthrough]] attribute. This is the closest equivalent to what this RFC proposes. - Go:
fallthroughis a reserved keyword; switch does not fall through by default. - C#: Requires explicit flow control (break, goto case, return, etc.) at the end of every non-empty case — implicit fallthrough is a compile error.
- Swift: Has a fallthrough keyword; switch cases don't fall through by default.
- Rust: match has no fallthrough concept at all.
Backward Incompatible Changes
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;.
Proposed PHP Version(s)
PHP 8.6.
RFC Impact
To the Ecosystem
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:
To Existing Extensions
None.
To SAPIs
None.
Open Issues
None.
Future Scope
None.
Voting Choices
Primary Vote requiring a 2/3 majority to accept the RFC:
Secondary vote requiring a simple majority, breaking ties towards E_WARNING:
Patches and Tests
Proof of concept:
Implementation
If accepted, this section will be updated with the merge target version, implementing commit(s), and manual entry.
References
Rejected Features
Changing ''switch'' semantics
This RFC deliberately does not make implicit fallthrough an error and does not redesign switch. The goal is to keep migration cost low.
Changelog
- 2026-04-21: Initial draft.