129

Is it possible to use a numeric string like "123" as a key in a PHP array, without it being converted to an integer?

$blah = array('123' => 1);
var_dump($blah);

prints

array(1) {
  [123]=>
  int(1)
}

I want

array(1) {
  ["123"]=>
  int(1)
}
Dogbert
  • 212,659
  • 41
  • 396
  • 397
  • 9
    Since PHP is loosely typed, `"123"` == `123` for almost every purpose. What's the reason you want it specifically as a string (and having an int is bad)? – ircmaxell Nov 04 '10 at 19:31
  • 23
    Reason that comes to my mind relates to array functions like `array_merge` *"If the input arrays have the same string keys, then the later value for that key will overwrite the previous one. If, **however, the arrays contain numeric keys**, the later value will not overwrite the original value, but will be appended."* – ficuscr Nov 05 '14 at 17:51
  • 9
    Another example where numeric strings as array keys is problematic: [`asort`](http://php.net/manual/en/function.asort.php) – swenedo Jan 13 '15 at 22:02
  • 2
    Possible duplicate of [How can I force PHP to use strings for array keys?](http://stackoverflow.com/questions/3445953/how-can-i-force-php-to-use-strings-for-array-keys) – nawfal Nov 18 '15 at 08:53
  • @nawfal: I'd be tempted to close these the other way around. And/or maybe have a mod merge the answers. Or possibly just [let them be.](http://blog.stackoverflow.com/2010/11/dr-strangedupe-or-how-i-learned-to-stop-worrying-and-love-duplication/) – Ilmari Karonen Nov 19 '15 at 00:04
  • 1
    This poses a problem for me too when I later array_merge two arrays with specific number strings as keys, as array_merge resets these keys. – DDecoene Jan 13 '16 at 16:38
  • 5
    Another use case: unit testing JSON data transition. Converting such an array to JSON and back won't let you assert that both, the original and the result are exactly the same. – David Feb 03 '16 at 14:39
  • 1
    @ircmaxell I have a library that is doing strict checks on array keys. – Peter Chaula Jun 08 '17 at 12:58
  • I was curious if `array_change_key_case($array)` would work but alas the internal rules are unhackable (directly). – mickmackusa Nov 20 '20 at 02:12
  • @swenedo [`asort()` is just fine](https://3v4l.org/4a5I4), same output since PHP 4.3. – Walf Feb 24 '21 at 12:39

11 Answers11

101

No; no it's not:

From the manual:

A key may be either an integer or a string. If a key is the standard representation of an integer, it will be interpreted as such (i.e. "8" will be interpreted as 8, while "08" will be interpreted as "08").

Addendum

Because of the comments below, I thought it would be fun to point out that the behaviour is similar but not identical to JavaScript object keys.

foo = { '10' : 'bar' };

foo['10']; // "bar"
foo[10]; // "bar"
foo[012]; // "bar"
foo['012']; // undefined!
Hamish
  • 22,860
  • 8
  • 53
  • 67
  • 5
    It looks like there is a way, actually! Do you disagree with this answer? http://stackoverflow.com/a/35180513/247696 – Flimm Apr 19 '16 at 11:48
  • 1
    How?!.. foo[012] return "bar" – Himanshu Oct 24 '18 at 06:05
  • 4
    @Himanshu: because php interprets numbers beginning with 0 as octal. so 012 is octal 10. – Yeasir Arafat Majumder Apr 16 '20 at 02:25
  • Yes its possibe using this code: $dtmf_arr = array(); foreach ($_POST['dtmf'] as $val) { $dtmf_arr['"'.$val.'"'] = $dtmf[$val]; } – Darshit Mendapara Jun 25 '21 at 04:46
  • Furthermore, there are plenty of ways to work around this, such as using array addition with `+` instead of merge, or the many inbuilt functions that preserve keys. If you need the key as a string again when reading from the array, casting it to a string is guaranteed to return the original value. – Walf Dec 01 '22 at 00:28
  • JavaScript behaves much more reasonably – it only allows single type (string) as object keys so casting everything to string is fine. PHP casts keys differently based on the string contents – that is just cursed. – Jan Tojnar Jun 15 '23 at 10:52
59

Yes, it is possible by array-casting an stdClass object:

$data =  new stdClass;
$data->{"12"} = 37;
$data = (array) $data;
var_dump( $data );

That gives you (up to PHP version 7.1):

array(1) {
  ["12"]=>
  int(37)
}

(Update: My original answer showed a more complicated way by using json_decode() and json_encode() which is not necessary.)

Note the comment: It's unfortunately not possible to reference the value directly: $data['12'] will result in a notice.

Update:
From PHP 7.2 on it is also possible to use a numeric string as key to reference the value:

var_dump( $data['12'] ); // int 32
David
  • 803
  • 6
  • 13
  • 3
    Direct access of the value with a string key doesn't work, though. Add this line to your example: `echo $data['12'];`. It will give the error, "Notice: Undefined offset: 12 in - on line 5". – Mr. Lance E Sloan May 29 '16 at 09:47
  • Yes, you're right. Assuming it was possible sometimes in the past. – David Jun 01 '16 at 11:55
  • 1
    when U use laravel dd($data) it will crash :P – Kamil Kiełczewski Feb 28 '17 at 22:41
  • Upvoting for the cool solution, but it's relatively complicated and I fear that as soon as some bozo that can do something about it notices the answer, it will be marked as a bug. – LGT Mar 11 '17 at 12:44
  • 2
    In PHP 7.2.0RC2 the behaviour is the same as before. – dev0 Sep 19 '17 at 14:41
  • It can't be referenced?! Waves hand: "This is not the fix you are looking for". – ChristoKiwi Apr 03 '18 at 00:12
  • 2
    Apparently `array_key_exists` won't find it in the older versions either. – Don't Panic Aug 29 '18 at 23:15
  • on php7.2.4, `array_key_exists('12',$data)` returns `true` – Brockley John Feb 10 '19 at 16:49
  • 1
    This does only work for >5.0 <7.1.33 – Tomáš Fejfar Jul 04 '22 at 13:31
  • 1
    My use case is months where I want '01' through '12' as keys in many places, but the reverse order in one place. The original order works now (12/2022), but array_reverse() does not work on objects and really breaks on the original array. A nasty solution could be `array_flip(array_reverse(array_flip($original)))` as I have only the (unique) month names as values. – Adam P. Dec 12 '22 at 12:10
13

My workaround is:

$id = 55;
$array = array(
  " $id" => $value
);

The space char (prepend) is a good solution because keep the int conversion:

foreach( $array as $key => $value ) {
  echo $key;
}

You'll see 55 as int.

Undolog
  • 562
  • 2
  • 9
  • 20
12

If you need to use a numeric key in a php data structure, an object will work. And objects preserve order, so you can iterate.

$obj = new stdClass();
$key = '3';
$obj->$key = 'abc';
steampowered
  • 11,809
  • 12
  • 78
  • 98
  • This is a very good suggestion. I am writing framework code and faced with someone passing an array that could have either "accidental" indexing: array('this', 'that') or "associative" indexing: array(123=>array('this', 'that')). Now, thanks to you, I can just typehint ;) +1 –  Dec 08 '13 at 09:09
  • But is it possible to use a numeric string like "123" as a key in a PHP array, without it being converted to an integer? – Flimm Apr 19 '16 at 11:45
  • @Flimm no that is not possible, which is why I offer my solution. – steampowered Apr 19 '16 at 12:21
  • @Flimm that answer seems to contradict the [PHP manual](http://php.net/manual/en/language.types.array.php): _Strings containing valid integers will be cast to the integer type. E.g. the key "8" will actually be stored under 8. On the other hand "08" will not be cast, as it isn't a valid decimal integer._, although I have not tested his answer. – steampowered Apr 19 '16 at 13:04
  • That sentence is under the section "Specifying with `array()`, so I'm assuming that *in that context* strings specifying valid integers will be cast to the integer type. But it turns out there are other ways of creating arrays, where that doesn't happen, such as in David's answer, which I have tested. – Flimm Apr 19 '16 at 13:09
  • @Flimm it sounds risky to me to build code based on tested behavior which the manual says the data structure may not support. To me, this is akin to depending upon javascript engines to support object property order, which is not officially supported and is implemented differently in different versions and different platforms. In other words, David's answer may not work in other versions of PHP, and it may stop working if you upgrade PHP in the future. – steampowered Apr 19 '16 at 13:29
  • And now most array functions are unavailable to you. – Walf Feb 24 '21 at 12:40
5

You can typecast the key to a string but it will eventually be converted to an integer due to PHP's loose-typing. See for yourself:

$x=array((string)123=>'abc');
var_dump($x);
$x[123]='def';
var_dump($x);

From the PHP manual:

A key may be either an integer or a string . If a key is the standard representation of an integer , it will be interpreted as such (i.e. "8" will be interpreted as 8, while "08" will be interpreted as "08"). Floats in key are truncated to integer . The indexed and associative array types are the same type in PHP, which can both contain integer and string indices.

Christian Giupponi
  • 7,408
  • 11
  • 68
  • 113
bcosca
  • 17,371
  • 5
  • 40
  • 51
  • 1
    The conversion is not due to loose typing; php determines whether the string *looks* numeric and then converts it. – Ja͢ck Apr 09 '13 at 07:10
  • 1
    So are you saying it's not possible? [This answer](http://stackoverflow.com/a/35180513/247696) shows that there is a way to use use a string that looks like an integer as a key in an array. – Flimm Apr 19 '16 at 11:53
  • IMHO the problem is the PHP interpreter. It's not even possible to imagine to have a language that mixes strings and integers as array keys. The best solution? As proposed by Undolog http://stackoverflow.com/a/15413637/1977778 the best solution is to use a trailing space... Sadly. – sentenza May 19 '16 at 14:22
  • @sentenza: It _**is**_ possible to imagine, especially since PHP allows a mixture of strings and integers as the keys of an array: `[42 => 'answer', 'snafu' => 'fubar']` – Mr. Lance E Sloan May 29 '16 at 09:34
  • @LS yep. I know that PHP allows you to do so, but if you consider the keys within an hypothetical generic type system it won't match any *mixed type* that embraces strings and numbers at the same time. The **loose typing** applied to the keys of an associative array is simply error prone. – sentenza May 31 '16 at 08:47
3

I had this problem trying to merge arrays which had both string and integer keys. It was important that the integers would also be handled as string since these were names for input fields (as in shoe sizes etc,..)

When I used $data = array_merge($data, $extra); PHP would 're-order' the keys. In an attempt doing the ordering, the integer keys (I tried with 6 - '6'- "6" even (string)"6" as keys) got renamed from 0 to n ... If you think about it, in most cases this would be the desired behaviour.

You can work around this by using $data = $data + $extra; instead. Pretty straight forward, but I didn't think of it at first ^^.

Brainfeeder
  • 2,604
  • 2
  • 19
  • 37
  • 1
    The exact same problem led me to this page, but I have to say that this is not answer to OP's question. – Flimm Apr 19 '16 at 11:50
  • @Flimm True. But searching for an answer led me to this page. I figured my solution could be a help for other Googlers :) – Brainfeeder Apr 19 '16 at 11:53
1

As workaround, you can encode PHP array into json object, with JSON_FORCE_OBJECT option.

i.e., This example:

     $a = array('foo','bar','baz');
     echo "RESULT: ", json_encode($a, JSON_FORCE_OBJECT);

will result in:

     RESULT: {"0" : "foo", "1": "bar", "2" : "baz"}
GigiManco
  • 67
  • 10
  • A caveat to this is that it will also force all child arrays to be objects which may not produce the intended output, see [the difference between `json_encode($a, JSON_FORCE_OBJECT)` and `json_encode((object) $a)`](https://3v4l.org/2F02S). – Walf Dec 01 '22 at 00:23
0

I ran into this problem on an array with both '0' and '' as keys. It meant that I couldn't check my array keys with either == or ===.

$array=array(''=>'empty', '0'=>'zero', '1'=>'one');
echo "Test 1\n";
foreach ($array as $key=>$value) {
    if ($key == '') { // Error - wrongly finds '0' as well
        echo "$value\n";
    }
}
echo "Test 2\n";
foreach ($array as $key=>$value) {
    if ($key === '0') { // Error - doesn't find '0'
        echo "$value\n";
    }
}

The workaround is to cast the array keys back to strings before use.

echo "Test 3\n";
foreach ($array as $key=>$value) {
    if ((string)$key == '') { // Cast back to string - fixes problem
        echo "$value\n";
    }
}
echo "Test 4\n";
foreach ($array as $key=>$value) {
    if ((string)$key === '0') { // Cast back to string - fixes problem
        echo "$value\n";
    }
}
fisharebest
  • 1,300
  • 10
  • 16
0

Strings containing valid integers will be cast to the integer type. E.g. the key "8" will actually be stored under 8. On the other hand "08" will not be cast, as it isn't a valid decimal integer.

WRONG

I have a casting function which handles sequential to associative array casting,

$array_assoc = cast($arr,'array_assoc');

$array_sequential = cast($arr,'array_sequential');

$obj = cast($arr,'object');

$json = cast($arr,'json');



function cast($var, $type){

    $orig_type = gettype($var);

    if($orig_type == 'string'){

        if($type == 'object'){
            $temp = json_decode($var);
        } else if($type == 'array'){
            $temp = json_decode($var, true);
        }
        if(isset($temp) && json_last_error() == JSON_ERROR_NONE){
            return $temp;
        }
    }
    if(@settype($var, $type)){
        return $var;
    }
    switch( $orig_type ) {

        case 'array' :

            if($type == 'array_assoc'){

                $obj = new stdClass;
                foreach($var as $key => $value){
                    $obj->{$key} = $value;
                }
                return (array) $obj;

            } else if($type == 'array_sequential'){

                return array_values($var);

            } else if($type == 'json'){

                return json_encode($var);
            }
        break;
    }
    return null; // or trigger_error
}
TarranJones
  • 4,084
  • 2
  • 38
  • 55
0

Regarding @david solution, please note that when you try to access the string values in the associative array, the numbers will not work. My guess is that they are casted to integers behind the scenes (when accessing the array) and no value is found. Accessing the values as integers won't work either. But you can use array_shift() to get the values or iterate the array.

$data = new stdClass;
$data->{"0"} = "Zero";
$data->{"1"} = "One";
$data->{"A"} = "A";
$data->{"B"} = "B";

$data = (array)$data;

var_dump($data);
/*
Note the key "0" is correctly saved as a string:
array(3) {
  ["0"]=>
  string(4) "Zero"
  ["A"]=>
  string(1) "A"
  ["B"]=>
  string(1) "B"
}
*/

//Now let's access the associative array via the values 
//given from var_dump() above:
var_dump($data["0"]); // NULL -> Expected string(1) "0"
var_dump($data[0]); // NULL (as expected)
var_dump($data["1"]); // NULL -> Expected string(1) "1"
var_dump($data[1]); // NULL (as expected)
var_dump($data["A"]); // string(1) "A" (as expected)
var_dump($data["B"]); // string(1) "B" (as expected)
Nico Schefer
  • 317
  • 3
  • 4
-1

I had this problem while trying to sort an array where I needed the sort key to be a hex sha1. When a resulting sha1 value has no letters, PHP turns the key into an integer. But I needed to sort the array on the relative order of the strings. So I needed to find a way to force the key to be a string without changing the sorting order.

Looking at the ASCII chart (https://en.wikipedia.org/wiki/ASCII) the exclamation point sorts just about the same as space and certainly lower than all numbers and letters.

So I appended an exclamation point at the end of the key string.

for(...) {

    $database[$sha.'!'] = array($sha,$name,$age);
}

ksort($database);
$row = reset($database);
$topsha = $row[0];
drchuck
  • 4,415
  • 3
  • 27
  • 30
  • So are you saying it's not possible to use a numeric string like "123" as a key in a PHP array, without it being converted to an integer? – Flimm Apr 19 '16 at 11:52
  • No - it only treats the key that is all numbers as an integer when sorting the array using ksort() - it is never converted to an integer - just compared as one during sorting. – drchuck Apr 20 '16 at 18:14