Referência do PHP na chave da matriz

PHP:

$a = array("key" => 23); var_dump($a); $c = &$a["key"]; var_dump($a); unset($c); var_dump($a); 

Saída:

 array(1) { ["key"]=> int(23) } array(1) { ["key"]=> &int(23) } array(1) { ["key"]=> int(23) } 

No segundo despejo, o valor de “chave” é mostrado como uma referência. Por que é que? Se eu fizer o mesmo com uma variável normal em vez de uma chave de matriz, isso não acontece.

Minha única explicação seria que as chaves da matriz geralmente sejam armazenadas como referências e, enquanto houver apenas uma input na tabela de símbolos, ela é mostrada como um escalar no despejo.

Internamente, arrays de PHP são hashmaps (ou dictionarys, ou HashTables ou o que você quer chamar). Mesmo uma matriz numericamente indexada é implementada como uma tabela de hash, que é um zval , assim como qualquer outro.
No entanto, o que você está vendo é comportamento esperado, o que é explicado aqui e aqui .

Basicamente, o que a sua matriz parece interna é o seguinte:

 typedef struct _zval_struct { zvalue_value value; zend_uint refcount__gc; zend_uchar type; zend_uchar is_ref__gc; } zval; //zval_value: typedef union _zvalue_value { long lval; double dval; struct { char *val; int len; } str; HashTable *ht; zend_object_value obj; } zvalue_value; 

No caso de uma matriz, o tipo zval.type será definido para indicar que o valor zval é uma matriz e, assim, o membro zval_value.ht será usado.
O que acontece quando você escreve $c = &$a['key'] é que o zval que é atribuído a $a['key'] será atualizado: zval.refcount__gc será incrementado e is_ref__gc será configurado para 1. Simplesmente porque o valor não é copiado, mas o valor é usado por mais de 1 variável: significando que esse valor é uma referência. Depois de unset($c); , a refcount é diminuída e a referência é perdida e, portanto, is_ref está definido como 0 .

Agora, para o grande: por que você não vê o mesmo quando usa variables ​​escalares regulares? Bem, isso é porque uma matriz é um HashTable, completo com seu próprio, interno, ref-counting ( zval_ptr_dtor ). Uma vez que uma matriz em si está vazia, ela também deve ser destruída. Ao criar uma referência a um valor de matriz, e você desarmar a matriz, o zval deve ser GC’ed. Mas isso significa que você tem uma referência a um zval destruído ao redor.
Portanto, o zval na matriz também é alterado para uma referência: uma referência pode ser excluída com segurança. Então, se você fizesse isso:

 $foo = array(123); $bar = &$foo[0]; unset($foo[0]); echo $bar, PHP_EOL; 

Seu código ainda funcionará como esperado: $foo[0] já não existe, mas $bar agora é a única referência existente para 123.

Esta é apenas uma explicação real, real, curta e incompleta, mas google os internos do PHP, e como o gerenciamento de memory funciona, como as referências são tratadas internamente e como o coletor de lixo usa os membros refcount e refcount para gerenciar a memory.
Preste especial atenção aos mecanismos internos, como copy-on-write, e (ao pesquisar o primeiro link que forneci aqui), procure o snippet que se pareça com isto:

 $ref = &$array; foreach ($ref as $val) {} 

Porque trata de algumas peculiaridades em termos de referências e matrizes.