PHP Array Keys on 32-bit Versus 64-bit Systems
PHP arrays are interesting beasts. They incorporate a merged set of functionality drawn from both indexed and associative array data structures, which are distinct and separate things in most other programming languages - where an indexed array is just called an array, while an associative array might be called a hash table or similar. The underlying implementations for these data structures are of course very different, and one might argue that having indexed and associative arrays as distinct language features is a not-completely-necessary inheritance from C and C-like languages where memory management is explicit and people must thus think more carefully about data structure implementation.
The point to all this is that there exist a range of implicit rules as to when a PHP array behaves more like it is indexed versus more like it is associative, and as for all such things there are edge cases that will jump up and bite you if you are not paying careful attention. The basic rule is that this behavior depends on whether or not your array keys are numeric:
// An indexed array declared explicitly. $indexed = array( 0 => 'a', 1 => 'b', 2 => 'c', ); // This is exactly the same as the array above - the 0, 1, 2 indexes are assumed. $implicitly_indexed = array('a', 'b', 'c'); // An associative array with non-integer keys. $associative = array( 'x' => 'a', 'y' => 'b', 'z' => 'c', );
While developers generally keep arrays either wholly indexed or wholly associative - for the sake of sanity if nothing else - it is actually the case that you can mix the two. In an array with numeric and non-numeric keys, the individual keys follow the respective rules for indexed versus non-indexed behavior when it matters. Some array operations like array_merge() differentiate between numeric and non-numeric keys, for example:
$example_1 = array( 1 => 'a', 5 => 'b', 12 => 'c', 'x' => 'y', ); $example_2 = array( 2 => 'r', 7 => 'q', 11 => 's', 'aa' => 'zz', ); $result = array_merge($example_1, $example_2); /* The output will have renumbered integer indexes: $result = array( 0 => 'a', 1 => 'b', 2 => 'c', 'x' => 'y', 3 => 'r', 4 => 'q', 5 => 's', 'aa' => 'zz', ); */
The fun then starts with the question of what counts as a numeric index, as for some cases a string can be considered numeric. For example, these two arrays are essentially the same - they are both treated as indexed arrays:
$indexed = array( 0 => 'a', 1 => 'b', 2 => 'c', ); $treated_as_indexed = array( '0' => 'a', '1' => 'b', '2' => 'c', );
But only string representations of integers work this way:
$example = array( // 0 is indexed. 0 => 'a', // '1' is indexed: integer string representations are treated as though numeric. '1' => 'b', // '2.5' is associative: only integer string representations are treated as indexed. '2.5' => 'c', // 'str' is associative: all other strings are associative indexes. 'str' => 'd', );
It's a little more complicated than that, however. More accurately, only strings representing integers up to PHP_INT_MAX - i.e. 2147483647 on a 32-bit system and 9223372036854775807 on a 64-bit system - will be treated as if numeric. So we have this:
$example = array( // Indexed on both 32-bit and 64-bit systems. '2147483647' => 'a', // Associative on a 32-bit system, indexed on a 64-bit system. '2147483648' => 'b', );
So let us say that you are integrating with a partner who likes to use large numbers as unique IDs in their system - bigger than PHP_INT_MAX on a 32-bit system, but smaller than PHP_INT_MAX on a 64-bit system. Brightcove does this, for example. Suppose that you naively write code that looks something like this:
// Two arrays of objects obtained from the partner, indexed by their large // string-representation-of-integer unique ids, e.g. '1841237631249'. $array_1 = array( $id1 => $obj1, $id2 => $obj2, ... ); $array_2 = array( $id11 => $obj11, $id12 => $obj12, ... ); // And merge them. Will these keys be treated as indexed and so re-indexed or // will they be treated as associative and not re-indexed? $merged = array_merge($array_1, $array_2); // Will we find the key that we're looking for here? That depends on whether // this is a 32-bit or 64-bit system. if (array_key_exists($check_this_id, $merged) { // Do something. }
If you start work in a 32-bit OS and then switch to a 64-bit OS mid-way through the project, this can produce some very ugly bugs.