rfc:switch-case-fallthrough

PHP RFC: Explicit fallthrough for ''switch''

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:

<?php
 
if (!defined('fallthrough')) define('fallthrough', null);
 
?>

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-fallthrough compiler warning and C23's [[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:

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:

Emit a diagnostic on implicit fallthrough as outlined in the RFC?
Real name Yes No Abstain
Final result: 0 0 0
This poll has been closed.

Secondary vote requiring a simple majority, breaking ties towards E_WARNING:

Which diagnostic should an implicit switch falltrough emit?
Real name E_WARNING E_DEPRECATED Abstain
Final result: 0 0 0
This poll has been closed.

Patches and Tests

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.
rfc/switch-case-fallthrough.txt · Last modified: by timwolla