Skip to content

PHP: unequal numeric strings (and byte arrays) are compared as equal #1217

Open
@generalmimon

Description

@generalmimon

Reproducible example:

meta:
  id: str_eq
instances:
  eq_strings:
    value: '"7" == "07"'
  eq_byte_arrays:
    value: '[0x37] == [0x30, 0x37]'

As of kaitai-io/kaitai_struct_compiler@12dbc32, it is translated to PHP as follows:

        protected $_m_eqStrings;
        public function eqStrings() {
            if ($this->_m_eqStrings !== null)
                return $this->_m_eqStrings;
            $this->_m_eqStrings = "7" == "07";
            return $this->_m_eqStrings;
        }
        protected $_m_eqByteArrays;
        public function eqByteArrays() {
            if ($this->_m_eqByteArrays !== null)
                return $this->_m_eqByteArrays;
            $this->_m_eqByteArrays = "\x37" == "\x30\x37";
            return $this->_m_eqByteArrays;
        }

Both eq_strings and eq_byte_arrays should evaluate to false, but they evaluate to true in the generated PHP parser. The reason is written in PHP documentation:

If both operands are numeric strings, or one operand is a number and the other one is a numeric string, then the comparison is done numerically. These rules also apply to the switch statement. The type conversion does not take place when the comparison is === or !== as this involves comparing the type as well as the value.

The solution would be to use the strict equality operator === instead, which does not perform type juggling. The catch I can foresee is that simply switching entirely to === would break expressions like 2.0 == 2 (which the KS expression language apparently allows), since float and int are different types (var_dump(2.0 === 2) is bool(false)).

We should also pay attention to this part:

These rules also apply to the switch statement.

This means that we run into the same problem with type switching, because this .ksy snippet...

seq:
  - id: a
    type:
      switch-on: '"7"'
      cases:
        '"07"': s1

... is translated into a switch statement:

        private function _read() {
            switch ("7") {
                case "07":
                    $this->_m_a = $this->_io->readS1();
                    break;
            }
        }

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions