En effet, la documentation du langage définit l’interface \arrayAccess de la manière suivante :

Interface to provide accessing objects as arrays.

Il est donc très clair à mes yeux que le but de cette interface n’est pas de permettre de faire passer un objet pour un tableau, mais juste de permettre l’utilisation de l’opérateur [] sur un objet puisqu’il est l’interface privilégiée pour modifier le contenu d’un tableau ou y accéder.

On m’a alors fait remarquer qu’il était assez paradoxal qu’il soit possible d’écrire le code suivant :

$object[] = 'foo';

Alors que le code qui suit, censé être fonctionnellement identique, provoque une erreur :

array_push($object, 'foo');

Sauf qu’en fait, les deux codes n’ont strictement rien à voir.

La documentation de PHP indique à propos de array_push() :

array_push() treats array as a stack, and pushes the passed variables onto the end of array.

En bon français, cette fonction ajoute donc l’élément qu’elle reçoit comme second argument dans le tableau qu’elle reçoit en premier argument.

Lorsqu’un développeur l’utilise, c’est donc qu’il a besoin de ce comportement pour que son code génère le résultat qu’il désire.

Admettons maintenant que PHP autorise une instance d’une classe définissant \arrayAccess à être utilisé comme premier argument de cette fonction, et que le code de la fonction offsetSet() cette classe est le suivant :

class foo implements arrayAccess
{
   protected $array = array();

   […]

   public function offsetSet($offset, $value)
   {
      if ($offset === null)
      {
         array_unshift($this->array, $value);
      }
      else
      {
         $this->array[$offset] = $value;
      }
   }

   […]

}

Avec un tel code, $object[] = 'foo' n’ajoute donc plus la valeur à la fin de l’objet, mais au début, et si l’on admet toujours qu’il est possible d’utiliser un objet implémentant \arrayAccess avec la fonction array_push(), le code array_push($object, 'foo') fera la même chose, avec le paradoxe que cela implique.

Et j’ajoute que je suis bien en peine de définir précisément la notion de début et de fin pour un objet, puisque conceptuellement, il peut s’agir de tout et de n’importe quoi.

Ainsi, si code $train[] = new wagon() peut avoir du sens, je ne suis pas certain du sens à donner à $soap[] = new request(), par exemple.

En effet, on pourrait supposer que ce code ajouterait la requête à la fin de la queue des requêtes que le client SOAP doit envoyer au serveur.

Cependant, si nous imaginons maintenant qu’une requête dispose d’un niveau de priorité, on pourrait tout aussi bien dire que le code $soap[] = {new request()}->setHightPriority() ajoute la requête au début de la queue.

En clair, le comportement de la méthode arrayAccess::offsetSet() dépend entièrement de la volonté de son développeur.

En conséquence, la fonction array_push() n’a aucune garantie que si elle y fait appel, la valeur qu’elle reçoit en second argument sera effectivement ajoutée à la fin de l’objet, quoique cela puisse alors signifier.

Éric m’a alors fait remarquer que je remets alors en cause le principe des interfaces.

Que nenni.

La documentation de PHP indique à propos de la méthode \arrayAccess::offsetSet() :

Assigns a value to the specified offset.

Elle n’impose donc pas que si la clef est nulle, alors la valeur doive être obligatoirement ajoutée à la fin, et heureusement, puisque la fin n’a pas forcément de signification dans le cadre d’un objet.

Le développeur est donc totalement libre de faire ce qu’il veut, car l’interface \arrayAccess ne définit pas un contrat précis à propos du comportement que doivent avoir les méthodes dont elle impose la définition.

Il ne serait donc pas raisonnable pour la fonction array_push() de se baser sur une implémentation de \arrayAccess::offsetSet() pour faire ce pour quoi elle a été conçue.

Maintenant, je ne nie absolument pas qu’il serait extrêmement intéressant de pouvoir utiliser un objet avec les fonctions de la famille array_*.

Cependant, il faudrait pour cela que PHP dispose d’une interface moins générique et plus contraignante qui imposerait par exemple au développeur la définition d’une méthode \php\proxy\array::array_push() qui permettrait à l’objet d’avoir un comportement identique à celui d’un tableau lorsqu’il est utilisé avec la fonction éponyme.

Et il faudrait évidemment avoir la même chose pour toutes les méthodes relatives au tableau.