Bug #62059 [Com]: ArrayObject and isset are not friends.

From: Date: Wed, 13 May 2015 13:31:22 +0000
Subject: Bug #62059 [Com]: ArrayObject and isset are not friends.
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-192651@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=62059&edit=1 ID: 62059 Comment by: contact dot 01834e2c at renegade334 dot me dot uk Reported by: julien at palard dot fr Summary: ArrayObject and isset are not friends. Status: Open Type: Bug Package: SPL related Operating System: Linux 2.6.32-5-amd64 PHP Version: Irrelevant Block user comment: N Private report: N New Comment: This has only been partially fixed. When isset($ArrayObject['foo']['bar']) is called, Zend doesn't call spl_array_has_dimension_ex(). Instead, it puts in a call to spl_array_get_dimension_ptr() with type=BP_VAR_IS. This value of type means that it doesn't generate a notice if 'foo' isn't a valid index. However, it is worth pointing out that at a Zend level, the native offsetGet method is never actually called in this case. However, it IS called if isset() is called on an instance of a child class, which has overwritten the offsetGet method with a function that calls parent::offsetGet(). Consider the following: <?php class MyArrayObject extends ArrayObject { public function offsetGet($index) { // ... return parent::offsetGet($index); } } $ArrayObject = new ArrayObject; $MyArrayObject = new MyArrayObject; isset($ArrayObject['foo']['bar']); isset($MyArrayObject['foo']['bar']); ?> In the first case, ArrayObject::offsetGet() is NOT actually being called - Zend takes a shortcut by going straight from spl_array_read_dimension_ex() to spl_array_get_dimension_ptr(), without calling the offsetGet method. In the second case, spl_array_read_dimension_ex() detects the presence of the overwritten offsetGet method, and calls it. This DOES call the native offsetGet method, which didn't happen in the case before. The native offsetGet method calls spl_array_read_dimension_ex() again, BUT this time, the call is made with type=BP_VAR_R (not BP_VAR_IS as before). This means that spl_array_get_dimension_ptr() DOES generate a notice if the index does not exist. In other words: - isset($ArrayObject['foo']['bar']) DOES NOT generate a notice, as the native offsetGet method is never actually called by Zend. - isset($MyArrayObject['foo']['bar']) DOES generate a notice, as it specifically calls the native offsetGet method. This is inconsistent behaviour, and should be addressed. $ ~/builds/php7/bin/php --version PHP 7.0.0-dev (cli) (built: May 13 2015 14:51:36) Copyright (c) 1997-2015 The PHP Group Zend Engine v3.0.0-dev, Copyright (c) 1998-2015 Zend Technologies Previous Comments: ------------------------------------------------------------------------ [2015-01-10 22:19:16] ajf at ajf dot me Hey, it's not the ->__isset() magic method that isset() calls for indexes (array access). __isset() is used for checking property existence. You're probably thinking of ArrayAccess's ->offsetExists(), which ArrayObject does implement. Also, this bug isn't really about ArrayObject per se, it's about ArrayAccess's offsetExists() not working with isset(). ------------------------------------------------------------------------ [2014-09-03 13:08:03] php at mandark dot fr Seems fix in PHP 5.5.16 ------------------------------------------------------------------------ [2013-05-23 20:01:54] spneacy at gmail dot com Confirmed in version 5.4.6 When using version 5.4.9, it appears to have been incidentally (or silently) fixed. ------------------------------------------------------------------------ [2012-10-23 23:05:33] roberts at x0 dot lv Confirmed present in 5.3.3-7 and 5.4.7-7, and 5.4.8-1 ------------------------------------------------------------------------ [2012-05-18 15:22:30] julien at palard dot fr Description: ------------ The doc state that isset() is not a function, so that it can do weird things like deep inspection, without raising a NOTICE about 'b' being undefined : $a = array(); isset($a['b']['c']); But if $a is an ArrayObject, the deep inspection does not work and a "Undefined index 'b'" NOTICE is triggered. But I found a __isset() in the documentation, (http://www.php.net/manual/en/language.oop5.overloading.php#object.isset) it have a very concise documentation but it seems a sufficient convention to make this work : I think that isset should call __isset() from left to right returning FALSE at the 1st non set member, like this : $a = SomeObject(); isset($a['b']['c']['d']['e']); should call : $a->__isset('b'), that should return FALSE, and stop here, wihout NOTICE. Test script: --------------- echo "isset on array, work without notice\n"; $array = array(); var_dump(isset($array['a']['b'])); echo "isset on arrayobject, should work as ArrayObject implements __isset\n"; $arrayobject = new ArrayObject(); var_dump(isset($arrayobject['a']['b'])); Expected result: ---------------- isset on array, work without notice bool(false) isset on arrayobject, should work as ArrayObject implements __isset bool(false) Actual result: -------------- isset on array, work without notice bool(false) isset on arrayobject, should work as ArrayObject implements __isset PHP Notice: Undefined index: a in test.php on line 10 Notice: Undefined index: a in test.php on line 10 bool(false) ------------------------------------------------------------------------ -- Edit this bug report at https://bugs.php.net/bug.php?id=62059&edit=1

« previous php.bugs (#192651) next »