Is this switch behavior normal?

11

The case

It turns out that doing some tests I have found something unusual behavior or that I had not experienced before.

The issue is that the switch function allows comparisons to be made consecutively of the type:

//...
switch ($i) {
    case 0:
    case 1:
    case 'a':
    echo "...";
    break;
//...

Similar examples appear in the php manual .

The theory is that if the value of $i is 0 , 1 , or a should activate the echo .

In the case I show:

  • The values 0 or a would return 0 .
  • The values 1 or b would return 1 .
  • The values 2 or c would return 2 .

But I do not know what motive causes the expected results with the following example:

class Test {
    const A = 0;
    const B = 1;
    const C = 2;

    public function select($value)
    {
        switch($value) {
            case 'a':
            case self::A:
                return self::A;
                break;
            case 'b':
            case self::B:
                return self::B;
                break;
            case 'c':
            case self::C:
                return self::C;
                break;
            default:
                return 3;
        }
    }
}

$test = new Test();
echo $test->select('b');

A functional example in a Sandbox .

The problem

  

All integer return their correct value, but strings always return 0 .

The question

Why is it that the values with string do not return the value that corresponds to it?

    
asked by OscarR 14.05.2018 в 12:08
source

2 answers

5

The problem comes from a conversion of types, in the documentation we can find the following:

Comparison Operators :

  

If a number is compared to a string or the comparison involves numeric strings, then each string is converted into a number and the comparison is made numerically. These rules also apply to the switch statement. The type conversion does not take place when the comparison is === or! == since this involves comparing the type as well as the value.

What's happening?

First let's see what happens when we force the String% co_from% to number

$value = 'b';
echo (int) $value; // 0

Yes we see the Conversion of string to numbers We see the following:

  

The value is given by the initial part of the string. If the string starts with a valid numeric data, this will be the value used. Otherwise, the value will be 0 (zero) .

Therefore if we review the b and the results the example:

class Test {
    const A = 0;
    const B = 1;
    const C = 2;

    public function select($value)
    {  
      switch($value) {
        case 'a':  // a == b : false
        case self::A:  // 0 == 0 : true
            return self::A;
            break;
        case 'b':  // b == b : true
        case self::B:  // 1 == 0 : false
            return self::B;
            break;
        case 'c': // c == b : false
        case self::C: // 3 == 0 : false
            return self::C;
            break;
        default:
            return 3;
      }
    }
}

How to solve it?

For me the most appropriate way is to force strings in the case, since we could receive numbers as a string and if we force types these strict comparison will be false ( switch ).

Example:

    switch($value) {
        case 'a':  // a == b : false
        case (string) self::A:  // b == 0 : false
            return self::A;
            break;
        case 'b':  // b == b : true
        case (string) self::B:  // b == 0 : false
            return self::B;
            break;
    }
    
answered by 14.05.2018 / 13:17
source
4

The problem may be that, according to the documentation , the switch / case statement works with flexible comparisons. with what this supposes when comparing chains.

You can see a little more information in this answer

One way to solve it is to "force" you to make strict comparisons:

 class Test {
        const A = 0;
        const B = 1;
        const C = 2;

        public function select($value)
        {

            switch($value) {
                case 'a':
                case $value === self::A:
                    return self::A;
                    break;
                case 'b':

                case $value === self::B:
                    return self::B;
                    break;
                case 'c':
                case $value === self::C:
                    return self::C;
                    break;
                default:
                    return 3;
            }
        }
    }

    $test = new Test();
    echo $test->select('c');
    
answered by 14.05.2018 в 12:49