1

I would like to get values for a collection of values:

>>> class Foo() {}
>>> $v = (object)[42];

>>> $a = [1, 1, 2, 3, 5, Foo::class, 'o_o', $v]
>>> $b = [1, 1, 2, 3, 5, Foo::class, 'o_o', $v]

>>> $data[$a] = 42;
>>> echo $data[$b]
42

I tried with SplObjectStorage, but I need to convert $a into an object and in this case $a != $b because they are different instances:

$s = new SplObjectStorage()
$s[$a] = 42
echo $s[$b]
UnexpectedValueException with message 'Object not found'

How can I achieve this in PHP?

In Python I would have used:

>>> a = (1, 1, 2, 3, 5, Foo, 'o_o', hashable_object)
>>> b = (1, 1, 2, 3, 5, Foo, 'o_o', hashable_object)    
>>> data[a] = 42
>>> print(data[b])
42 

EDIT

One not very efficient working solution would be:

>>> class Foo() {}
>>> $v = (object)[42];

>>> $a = [1, 1, 2, 3, 5, Foo::class, 'o_o', $v]
>>> $b = [1, 1, 2, 3, 5, Foo::class, 'o_o', $v]

>>> $data[serialize($a)] = 42;
>>> echo $data[serialize($b)]
42
nowox
  • 25,978
  • 39
  • 143
  • 293
  • That because the `$a` and `$b` are not the same (notice that `$a !== $b` as you mention) - so they not represent same key on the `SplObjectStorage` - so I guess it is the expected output – dWinder Nov 25 '18 at 13:45
  • Indeed, it is the expected output, but I don't know how to answer the question though. – nowox Nov 25 '18 at 13:45
  • Why do you think it is not efficient? – revo Nov 25 '18 at 15:10

2 Answers2

1

According to php manual arrays and objects cannot be used as array keys.

What you could do is this:

>>> class Foo {}
>>> $test = new \stdClass();
>>> $test->{implode([1, 1, 2, 3, 5, Foo::class, 'o_o'])} = 42;
>>> $test->{implode([1, 1, 2, 3, 5, Foo::class, 'o_o'])};
=> 42

What i was going to do is this:

$test->something = [ 42 => [1, 1, 2, 3, 5, Foo::class, 'o_o']];
array_search([1, 1, 2, 3, 5, Foo::class, 'o_o'], $test->something, true);
=> 42

I hope it helps.

Vaggelis Ksps
  • 308
  • 4
  • 13
  • Not sure it is more efficient than using `serialize` – nowox Nov 25 '18 at 13:57
  • 1
    According @Peon 's comment [here](https://stackoverflow.com/questions/15218827/serialize-or-implode) Implode 2 elements: 1.8937180042267 seconds, Implode 4 elements: 2.4380650520325 seconds, Implode 8 elements: 3.6058769226074 seconds Not exactly two times, but close to it, while serialize changes with the same progression type, but slower rate. So i think serialize is faster and it is up to you man and always add the full path of the function for better performance. For example \array_search() – Vaggelis Ksps Nov 25 '18 at 14:05
0

By looking at the PHP doc I found '\Ds\Map' available from PECL. With this you can write:

$map = new \Ds\Map();

$v = (object)[41];

$a = [1, 1, 2, 3, 5, 'o_o', $v];
$b = [1, 1, 2, 3, 5, 'o_o', $v];

$map[$a] = 42;

var_dump($map[$b]);

I also looked at the implementation:

https://github.com/php-ds/ext-ds/blob/master/src/ds/ds_htable.c

The zval is hashed with this function

static uint32_t get_array_hash(zval *array)
{
    uint32_t                   hash;
    php_serialize_data_t       var_hash;
    smart_str                  buffer = {0};

    PHP_VAR_SERIALIZE_INIT(var_hash);
    php_var_serialize(&buffer, array, &var_hash);
    PHP_VAR_SERIALIZE_DESTROY(var_hash);

    smart_str_0(&buffer);

    if (buffer.s) {
        hash = get_string_hash(buffer.s);
        zend_string_free(buffer.s);
    } else {
        hash = 0;
    }

    return hash;
}

So behind the scene it is the serialize function that is used. I feel a bit sad about this. So your inefficient function will be not much inefficient at the end.

nowox
  • 25,978
  • 39
  • 143
  • 293