8

How come a case option in a switch statement that does not contain a break automatically forwards to a next case without check?

try {
    switch($param) {
        case "created":
            if(!($value instanceof \DateTime))
                throw new \Exception("\DateTime expected, ".gettype($value)." given for self::$param");
        case "Creator":
            if(!($value instanceof \Base\User)) {
                throw new \Exception(get_class($value)." given. \Base\User expected for self::\$Creator");                  
            }
        default:
            $this->$param = $value;
            break;
    }
} catch(Exception $e) {
    echo $e->getMessage();
}

If the param is "created" it will do the check in the created-case, which is good. When the check is succesful, I want the code to continue to the default option, that's why there is no break;. But instead it continues to "Creator" while $param != "Creator"!

I do know how to solve this (just add the default code in my case "created"), but I don't like to repeat that code too often. My actual question is: Why does it continue with the "Creator" case while the case is not "Creator".

Rene Terstegen
  • 7,911
  • 18
  • 52
  • 74

6 Answers6

18

Fallthrough was an intentional design feature for allowing code like:

switch ($command) {
  case "exit":
  case "quit":
    quit();
    break;
  case "reset":
    stop();
  case "start":
    start();
    break;
}

It's designed so that execution runs down from case to case.

default is a case like any other, except that jumping there happens if no other case was triggered. It is not by any means a "do this after running the actual selected case" instruction. In your example, you could consider:

  switch($param) {
    case "created":
        if(!($value instanceof \DateTime))
            throw new \Exception("\DateTime expected, ".gettype($value)." given for self::$param");
        break;
    case "Creator":
        if(!($value instanceof \Base\User)) {
            throw new \Exception(get_class($value)." given. \Base\User expected for self::\$Creator");                  
        }
        break;
}

$this->$param = $value;

The rule of thumb here is, if it doesn't depend on the switch, move it out of the switch.

Victor Nicollet
  • 24,361
  • 4
  • 58
  • 89
  • I would add that - while fall-through is sometimes usefull - it's better to not use it where both cases do something instead of just falling through (Good: `case "created": case "creator": case "something_else": do_stuff();` Bad: `case "created": do_stuff(); case "creator": do_Second_stuff(); case "something_else": do_remaining_stuff();`) –  Nov 11 '10 at 14:57
  • @dbemerlin: it's indeed quite dangerous, though acceptable if the cases are short or in languages which support explicit fallthrough (like C#). – Victor Nicollet Nov 11 '10 at 15:10
  • Best is to add a comment to say fallthrough is intentional. – Hammerite Nov 11 '10 at 15:27
2

In PHP 8 we have match, similar with switch expression but is significantly shorter:

  • it doesn't require a break statement
  • it can combine different arms into one using a comma
  • it returns a value, so you only have to assign value once

An example:

$message = match ($statusCode) {
    200, 300 => null,
    400 => 'not found',
    500 => 'server error',
    default => 'unknown status code',
};

Here's its switch equivalent:

switch ($statusCode) {
    case 200:
    case 300:
        $message = null;
        break;
    case 400:
        $message = 'not found';
        break;
    case 500:
        $message = 'server error';
        break;
    default:
        $message = 'unknown status code';
        break;
}

reference : https://stitcher.io/blog/php-8-match-or-switch

Muhammad Dyas Yaskur
  • 6,914
  • 10
  • 48
  • 73
1

Because that's how it's done in C.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
1

Perhaps this will enlighten you:

Jump Table Switch Case question

Community
  • 1
  • 1
bcosca
  • 17,371
  • 5
  • 40
  • 51
0

To answer your "actual question": Why does it continue with the "Creator" case while the case is not "Creator".

Because you do not have break. Without it, it will continue to the cases below it. The only solution I can think of is putting your default code to the cases, and add break.

Also, you don't need break on the default case since its the last case in the switch block.

Ruel
  • 15,438
  • 7
  • 38
  • 49
0

I don't really see what you want.

  1. If you want to run the default stuff in all cases, just put it after the switch.
  2. If you want to run the default stuff only in the "created" case and in the default case, swap the position of the "created" and "Creator" sections and put a break after the first.
  3. If you want that code to only run if Creator or created matches, then get rid of the switch statement and use an if/else OR use a flag and a following if statement.

All the tools are there.

caveman
  • 1,755
  • 1
  • 14
  • 19
  • 1
    It's not about what I want, it is about why a switch statement is working like this. – Rene Terstegen Nov 11 '10 at 14:58
  • I apologize that I misunderstood the question. History is the answer. Like most anything in programming, somebody said so. This is how they said. There's some history there (basically that C started it), but that is the syntax. – caveman Nov 11 '10 at 15:37