phparraysobjectrecursionlowercase

Converting keys of an array/object-tree to lowercase


I am currently optimizing a PHP application and found one function being called around 10-20k times, so I'd thought I'd start optimization there:

function keysToLower($obj)
{
    if (!is_object($obj) && !is_array($obj))
        return $obj;

    foreach ($obj as $key => $element) {
        $element = keysToLower($element);
        if (is_object($obj)) {
            $obj->{strtolower($key)} = $element;
            if (!ctype_lower($key))
                unset($obj->{$key});
        } elseif (is_array($obj) && ctype_upper($key)) {
           $obj[strtolower($key)] = $element;
           unset($obj[$key]);
        }
    }
    return $obj;
}

Most of the time is spent in recursive calls (which are quite slow in PHP), but I don't see any way to convert it to a loop. How can I do this?


Solution

  • Foreach is using an internal copy that is then traversed. Try it without:

    function keysToLower($obj)
    {
        $type = (int) is_object($obj) - (int) is_array($obj);
        if ($type === 0) return $obj;
        reset($obj);
        while (($key = key($obj)) !== null)
        {
            $element = keysToLower(current($obj));
            switch ($type)
            {
            case 1:
                if (!is_int($key) && $key !== ($keyLowercase = strtolower($key)))
                {
                    unset($obj->{$key});
                    $key = $keyLowercase;
                }
                $obj->{$key} = $element;
                break;
            case -1:
                if (!is_int($key) && $key !== ($keyLowercase = strtolower($key)))
                {
                    unset($obj[$key]);
                    $key = $keyLowercase;
                }
                $obj[$key] = $element;
                break;
            }
            next($obj);
        }
        return $obj;
    }
    

    Or use references to avoid that a copy is used:

    function &keysToLower(&$obj)
    {
        $type = (int) is_object($obj) - (int) is_array($obj);
        if ($type === 0) return $obj;
        foreach ($obj as $key => &$val)
        {
            $element = keysToLower($val);
            switch ($type)
            {
            case 1:
                if (!is_int($key) && $key !== ($keyLowercase = strtolower($key)))
                {
                    unset($obj->{$key});
                    $key = $keyLowercase;
                }
                $obj->{$key} = $element;
                break;
            case -1:
                if (!is_int($key) && $key !== ($keyLowercase = strtolower($key)))
                {
                    unset($obj[$key]);
                    $key = $keyLowercase;
                }
                $obj[$key] = $element;
                break;
            }
        }
        return $obj;
    }