#34934 [Bgs]: offsetExists not called in derived class

From: Date: Tue, 25 Oct 2005 03:38:57 +0000
Subject: #34934 [Bgs]: offsetExists not called in derived class
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-87010@lists.php.net to get a copy of this message
 ID:               34934
 User updated by:  adove at booyahnetworks dot com
 Reported By:      adove at booyahnetworks dot com
 Status:           Bogus
 Bug Type:         SPL related
 Operating System: WinXP, Linux
 PHP Version:      5.0.5
 Assigned To:      dmitry
 New Comment:

Ok, isset works as expected. IMHO, the behavior should be made
consistent across the one array_key_exists example that works.


Previous Comments:
------------------------------------------------------------------------

[2005-10-24 09:01:00] dmitry@php.net

After discussion with Marcus and Andi, this fix is reverted and bug
marked as bogus. array_... functions were not designed to work with
ArrayAccess interface, ao array_key_exists() shouldn't call
ofssetExists() at all.
You should use isset() instead of it.

------------------------------------------------------------------------

[2005-10-21 17:21:08] dmitry@php.net

Fixed in CVS HEAD and PHP_5_1.

------------------------------------------------------------------------

[2005-10-21 07:42:19] adove at booyahnetworks dot com

Here's a full code sample to replicate. Note I stripped out the method
comments. If you want them I can repost.

<?php
class MyArray extends ArrayObject
{
    function __construct(
        $mValue = array()
        )
    {
        // Convert our value.        
        if(is_array($mValue))
        {
            $mValue = &$this->recurs($mValue);
        }
        elseif(is_object($mValue) && ($mValue instanceof ArrayObject))
        {
            // Get the raw array from our ArrayObject for handling.
            $mValue = &$this->recurs($mValue->getArrayCopy(),
$constFlags);
        }
        elseif(null == $mValue)
        {
            $mValue = array();
        }
        elseif(!is_object($mValue))
        {
            // Turn primative into an indexed array.
            $mValue = array($mValue);
        }
        // else - it's an object, enum its public properites.
        
        parent::__construct($mValue);
    }
    
    protected function explodeKeyPath($mKeyPath)
    {
        return explode("/", trim($mKeyPath, "/"));
    }
    
    function offsetSet($mKey, $mValue) 
    {
        $mKey = trim($mKey, "/");
        if(stristr($mKey, "/"))
        {
            // A path was requested. Start out pointing to ourself.
	        $mRetval = $this;
	        
	        $aKeyPathParts = $this->explodeKeyPath($mKey);
                
            // Need to take the LAST path off. That's what we assign
our value too. 
            $strLastPathPart = array_pop($aKeyPathParts);
	            
	        foreach($aKeyPathParts as $strPathPart)
	        {
	            // We know we our data are either a primative type or an
ArrayAccess. 
	            if(($mRetval instanceof ArrayAccess) && 
	                array_key_exists($strPathPart, $mRetval)
	                )
	            {
	                $mRetval = $mRetval[$strPathPart];
	            }
	            else
	            {
                    // Need a new empty one of us and then point to
it!
                    $mRetval[$strPathPart] = 
                        $this->newArrayObject($strPathPart, array(),
$this->getFlags());
	                $mRetval = $mRetval[$strPathPart];
	            }
	        }
            
            // Now put our value!
            $mRetval[$strLastPathPart] = $mValue;
        }
        else
        {
	        if(is_array($mValue))
	        {
	            // Need to create a new instance of us!
	            $mValue = $this->newArrayObject($mKey, $mValue,
$this->getFlags());
	        }
	        
            parent::offsetSet($mKey, $mValue);
        }
    }

    function offsetGet($mKey) 
    { 
        $mRetval = null;
        
        $mKey = trim($mKey, "/");
        if(stristr($mKey, "/"))
        {
            // A path was requested. Start out pointing to ourself.
	        $mRetval = $this;
	        
	        $aKeyPathParts = $this->explodeKeyPath($mKey);
	            
	        foreach($aKeyPathParts as $strPathPart)
	        {
	            // We know we our data are either a primative type or an
ArrayAccess. 
	            if(($mRetval instanceof ArrayAccess) && 
	                array_key_exists($strPathPart, $mRetval)
	                )
	            {
	                $mRetval = $mRetval[$strPathPart];
	            }
	            else
	            {
                    $mRetval = null;
	                
	                break;
	            }
	        }
        }
        else
        {
            $mRetval = parent::offsetGet($mKey);
        }
        
        return $mRetval; 
    } 
    
    function offsetUnset($mKey) 
    { 
        $mKey = trim($mKey, "/");
        if(stristr($mKey, "/"))
        {
            // A path was requested. Start out pointing to ourself.
	        $oArray = $this;
	        
	        $aKeyPathParts = $this->explodeKeyPath($mKey);
                
            // Need to take the LAST path off. That's what we unset. 
            $strLastPathPart = array_pop($aKeyPathParts);
	            
	        foreach($aKeyPathParts as $strPathPart)
	        {
	            // We know we our data are either a primative type or an
ArrayAccess. 
	            if(($oArray instanceof ArrayAccess) && 
	                array_key_exists($strPathPart, $oArray)
	                )
	            {
	                $oArray = $oArray[$strPathPart];
	            }
	            else
	            {
                    // Hmmmm... bad path... Die. Nothing will happen
below.
                    break;
	            }
	        }
            
            // Now unset our value!
            if(($oArray instanceof ArrayAccess) && 
                array_key_exists($strLastPathPart, $oArray)
                )
            {
                unset($oArray[$strLastPathPart]);
            }
            // else - Bad path.. do nothing...
        }
        else
        {
            parent::offsetUnset($mKey);
        }
    } 

    function offsetExists($mKey) 
    { 
        $bRetval = false;
        
        $mKey = trim($mKey, "/");
        if(stristr($mKey, "/"))
        {
            // A path was asked for. Start at our root.
            $mPathData = $this;
            
	        $aKeyPathParts = $this->explodeKeyPath($mKey);
	            
	        foreach($aKeyPathParts as $strPathPart)
	        {
	            // We know we our data are either a primative type
(non-array) 
	            // or an ArrayAccess. 
	            if(($mPathData instanceof ArrayAccess) && 
	                array_key_exists($strPathPart, $mPathData)
	                )
	            {
                    // So far so good... True will remain unless a
latter path 
                    // part is invalid. 
	                $bRetval = true;
	                
	                // Set for next loop.
	                $mPathData = $mPathData[$strPathPart];
	            }
	            else
	            {
	                // Nope, bad path.
	                $bRetval = false;
	                break;
	            }
	        }
        }
        else
        {
            $bRetval = parent::offsetExists($mKey);
        }
        
        return $bRetval;
    } 
    
    protected function &recurs(
        $aData
        )
    {
        $aRetval = $aData;
        
        foreach($aRetval as $mKey => &$mValue)
        {
            // If we have a primative array or MyArray-based instance 
            // convert them to us... Buhahahahahah!!!  
            if(is_array($mValue) || 
                (is_object($mValue) && ($mValue instanceof
ArrayObject))
                )
            {
                $aRetval[$mKey] = new MyArray($mValue);
            }
            // else - leave it alone!
        }
        
        return $aRetval;
    }
}

$a = array(
    "test" => array(
        "one" => "dunno",
        "two" => array(
            "peekabo" => "do you see me?",
            "anyone" => array("there")
            )
        )
    );
$oArray = new ArrayObject($a);
$oMyArray = new MyArray($a);

$bWorks1 = array_key_exists("test", $oArray);
$bWorks2 = array_key_exists("test", $oMyArray);

$bShouldWorkButDoesNot = array_key_exists("test/two/peekabo",
$oMyArray);
$bDirectWorks = $oMyArray->offsetExists("test/two/peekabo");
$bDirectBadPathWorksToo =
$oMyArray->offsetExists("test/two/notfound");
?>

------------------------------------------------------------------------

[2005-10-20 19:30:56] tony2001@php.net

Provide a short but complete reproduce script then.
The code you've pasted in the report is not complete - there is no
declaration of MyArray class.

------------------------------------------------------------------------

[2005-10-20 19:28:17] adove at booyahnetworks dot com

Same behavior in both the latest 5.0.5 and 5.1...

------------------------------------------------------------------------

The remainder of the comments for this report are too long. To view
the rest of the comments, please view the bug report online at
    http://bugs.php.net/34934

-- 
Edit this bug report at http://bugs.php.net/?id=34934&edit=1


Thread (8 messages)

« previous php.bugs (#87010) next »