Quolonel Questions Quolonel Questions - 3 months ago 7
PHP Question

Userland implementation of PHP array key casting

How can I implement PHP array key casting logic in PHP? One naive implementation could be the following.

function castKey($key) {
$array = [$key => null];
return key($array);
}


However, it seems wasteful to allocate a redundant array. I'd prefer to accurately emulate the casting logic. Of course it would be useful to see and understand the source code but I am not sure where to find this.

Empirically I have observed any scalar value can be stored as a key using the following casting rules.


  • Integers are stored verbatim.

  • Strings are stored as strings unless they look like an integer. I do not know exactly how PHP determines that a string looks like an integer; whether it is just
    ^\d+$
    or something more complex.

  • Booleans are cast to integer.

  • Floats are cast to integer.


Answer

A non-optimized version of the PHP array key casting logic can be found here: https://github.com/php/php-src/blob/dbf63cb98d642236da9b9b5c751f0d4f8223c77f/Zend/zend_API.c#L1630

The logic for detecting a numeric string (as far as array keys are concerned) can be found here: https://github.com/php/php-src/blob/16160386982a86e6ec7969c6c89707d38228f19e/Zend/zend_hash.c#L2442

So a userland implementation would look something like this:

function castKey($key) {
    if (is_int($key)) {
        return $key;
    }
    if (is_string($key)) {
        // Only keys of the form 0|-?[1-9][0-9]* in the
        // supported integer range are converted.
        $int = filter_var($key, FILTER_VALIDATE_INT);
        if (false !== $int && '-0' !== $key) {
            return $int;
        }
        return $key;
    }
    if (null === $key) {
        return "";
    }
    if (is_bool($key) || is_double($key)) {
        return (int) $key;
    }
    if (is_resource($key)) {
        $id = (int) $key;
        trigger_error("Resource ID#$id used as offset, casting to integer ($id)");
        return $id;
    }
    throw new Exception('Illegal offset type');
}
Comments