I've used both array_keys($obj) !== range(0, count($obj) - 1) and array_values($arr) !== $arr (which are duals of each other, although the second is cheaper than the first) but both fail for very large arrays. Interestingly enough I encountered this while trying to figure out why json_encode was failing for a large array, so I wrote my own replacement for it which failed as well (and this was where it failed).
In essence, array_keys and array_values are both very costly operations (since they build a whole new array of size roughly that of the original).
The following function is more robust than the methods provided above, but not perfect:
function array_type( $obj ){
$last_key = -1;
$type = 'index';
foreach( $obj as $key => $val ){
if( !is_int( $key ) ){
return 'assoc';
}
if( $key !== $last_key + 1 ){
$type = 'sparse';
}
$last_key = $key;
}
return $type;
}
Note that if I wrote foreach( array_keys( $obj ) as $key ){ } then it would fail just as quickly as the earlier described methods.
Also note that if you don't care to differentiate sparse arrays from associative arrays you can simply return 'assoc' for !is_int($key) and $key.
Finally, while this might seem much less "elegant" than a lot of "solutions" on this page, in practice it is vastly more efficient. Almost any associative array will be detected instantly. Only indexed arrays will get checked exhaustively, and the methods outlined above not only check indexed arrays exhaustively, they duplicate them.