====== PHP RFC: Explicit fallthrough for ''switch'' ====== * Version: 0.1 * Date: 2026-04-21 * Author: Volker Dusch , Tim Düsterhus * 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;''. ===== 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;''. 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 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. ==== Prior Art ==== * C/C++: The ''-Wimplicit-fallthrough'' compiler warning and C23's [[https://en.cppreference.com/c/language/attributes/fallthrough|[[fallthrough]]]] attribute. This is the closest equivalent to what this RFC proposes. * Go: ''fallthrough'' is 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: * [[https://cs.symfony.com/doc/rules/control_structure/no_break_comment.html]] * [[https://www.php-fig.org/per/coding-style/#52-switch-case-match]] * [[https://github.com/squizlabs/PHP_CodeSniffer/blob/master/src/Standards/PSR2/Sniffs/ControlStructures/SwitchDeclarationSniff.php#L196]] * [[https://github.com/nikic/PHP-Fuzzer/blob/23e440d79d353d5f42c9e41a50f7b4f930b85c00/src/Mutation/Mutator.php#L290]] * [[https://github.com/wikimedia/mediawiki-extensions-InlineComments/blob/dd5d2e291cb5c20920b9ba61965ed594ce7f3ea3/src/AnnotationTreeHandler.php#L128]] ==== 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: * Yes * No * Abstain Secondary vote requiring a simple majority, breaking ties towards E_WARNING: * E_WARNING * E_DEPRECATED * Abstain ===== Patches and Tests ===== Proof of concept: * https://github.com/php/php-src/pull/21821 ===== Implementation ===== If accepted, this section will be updated with the merge target version, implementing commit(s), and manual entry. ===== References ===== * [[https://www.php.net/manual/en/control-structures.switch.php#:~:text=Note%3A%20Technically%20the%20default%20case%20may%20be%20listed%20in%20any%20order.%20It]] * [[https://wiki.php.net/rfc/match_expression_v2]] * [[https://wiki.php.net/rfc/continue_on_switch_deprecation]] * [[https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wimplicit-fallthrough_003d]] * [[https://clang.llvm.org/docs/AttributeReference.html#fallthrough]] * [[https://checkstyle.org/checks/coding/fallthrough.html#FallThrough]] * [[https://docs.swift.org/swift-book/documentation/the-swift-programming-language/statements#Fallthrough-Statement]] * [[https://go.dev/ref/spec#Fallthrough_statements]] ===== 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.