web-dev-qa-db-fra.com

"La modification indirecte d'un élément surchargé de SplFixedArray n'a aucun effet"

Pourquoi ce qui suit

$a = new SplFixedArray(5);
$a[0] = array(1, 2, 3);
$a[0][0] = 12345; // here
var_dump($a);

produit

Notice: Indirect modification of overloaded element of SplFixedArray has no effect in <file> on line <indicated>

Est-ce un bug? Comment traitez-vous avec SplFixedArrays multidimensionnelle alors? Des solutions de contournement?

24
Desmond Hume

Premièrement, le problème est lié à toutes les classes qui implémentent ArrayAccess. Ce n'est pas un problème spécial de SplFixedArray uniquement.


Lorsque vous accédez aux éléments de SplFixedArray à l'aide de l'opérateur [], il ne se comporte pas exactement comme un tableau. En interne, sa méthode offsetGet() est appelée et renverra dans votre cas un tableau - mais pas une référence à ce tableau. Cela signifie que toutes les modifications que vous apportez sur $a[0] seront perdues à moins que vous ne les sauvegardiez:

Solution de contournement:

$a = new SplFixedArray(5);
$a[0] = array(1, 2, 3); 
// get element
$element = $a[0];
// modify it
$element[0] = 12345;
// store the element again
$a[0] = $element;

var_dump($a);

Voici un exemple utilisant un scalaire qui échoue également - juste pour vous montrer qu'il n'est pas lié uniquement à des éléments de tableau.

38
hek2mgl

Ceci est effectivement réparable si vous slapez un & devant offsetGet (en supposant que vous ayez accès aux éléments internes de votre implémentation ArrayAccess):

class Dict implements IDict {
    private $_data = [];

    /**
     * @param mixed $offset
     * @return bool
     */
    public function offsetExists($offset) {
        return array_key_exists(self::hash($offset), $this->_data);
    }

    /**
     * @param mixed $offset
     * @return mixed
     */
    public function &offsetGet($offset) {
        return $this->_data[self::hash($offset)];
    }

    /**
     * @param mixed $var
     * @return string
     */
    private static function hash($var) {
        return is_object($var) ? spl_object_hash($var) : json_encode($var,JSON_UNESCAPED_SLASHES);
    }

    /**
     * @param mixed $offset
     * @param mixed $value
     */
    public function offsetSet($offset, $value) {
        $this->_data[self::hash($offset)] = $value;
    }

    /**
     * @param mixed $offset
     */
    public function offsetUnset($offset) {
        unset($this->_data[self::hash($offset)]);
    }
}
4
mpen

En ajoutant mon expérience avec la même erreur, au cas où cela aiderait quelqu'un:

J'ai récemment importé mon code dans un framework avec une tolérance d'erreur faible (Laravel). En conséquence, mon code lève maintenant une exception lorsque j'essaie de récupérer une valeur d'un tableau associatif à l'aide d'une clé inexistante. Afin de résoudre ce problème, j'ai essayé d'implémenter mon propre dictionnaire à l'aide de l'interface ArrayAccess. Cela fonctionne bien, mais la syntaxe suivante échoue:

$myDict = new Dictionary();
$myDict[] = 123;
$myDict[] = 456;

Et dans le cas d'une carte multiple:

$properties = new Dictionary();
$properties['colours'] = new Dictionary();
$properties['colours'][] = 'red';
$properties['colours'][] = 'blue';

J'ai réussi à résoudre le problème avec l'implémentation suivante:

<?php

use ArrayAccess;

/**
 * Class Dictionary
 *
 * DOES NOT THROW EXCEPTIONS, RETURNS NULL IF KEY IS EMPTY
 *
 * @package fnxProdCrawler
 */
class Dictionary implements ArrayAccess
{
    // FOR MORE INFO SEE: http://alanstorm.com/php_array_access

    protected $dict;

    function __construct()
    {
        $this->dict = [];
    }

    // INTERFACE IMPLEMENTATION - ArrayAccess
    public function offsetExists($key)
    {
        return array_key_exists($key, $this->dict);
    }
    public function offsetGet($key)
    {
        if ($this->offsetExists($key))
            return $this->dict[$key];
        else
            return null;
    }
    public function offsetSet($key, $value)
    {
        // NOTE: THIS IS THE FIX FOR THE ISSUE "Indirect modification of overloaded element of SplFixedArray has no effect"
        // NOTE: WHEN APPENDING AN ARRAY (E.G. myArr[] = 5) THE KEY IS NULL, SO WE TEST FOR THIS CONDITION BELOW, AND VOILA

        if (is_null($key))
        {
            $this->dict[] = $value;
        }
        else
        {
            $this->dict[$key] = $value;
        }
    }
    public function offsetUnset($key)
    {
        unset($this->dict[$key]);
    }
}

J'espère que ça aide.

0
mils