Mantis - Quercus
Viewing Issue Advanced Details
3230 major always 01-09-09 07:41 01-15-09 18:10
haplo  
ferg  
normal  
closed 3.2.1  
fixed  
none    
none 4.0.0  
0003230: instanceof does not include interface hierarchy
Hi everyone,

i am trying hard to get PRADO running on resin and i found a nasty bug.

The Traversable interface is extended by the IteratorAggregate, but if i implement the IteratorAggregate interface (i.e. IteratorAggregateImpl ) the following test fails!


$impl = new IteratorAggregateImpl();
$impl instanceof Traversable; // is false on quercus and true on apache

Try this code on quercus and apache.


<?php

class MyMap implements IteratorAggregate,ArrayAccess,Countable
{
/**
* @var array internal data storage
*/
private $_d=array();
/**
* @var boolean whether this list is read-only
*/
private $_r=false;

/**
* Constructor.
* Initializes the list with an array or an iterable object.
* @param array|Iterator the intial data. Default is null, meaning no initialization.
* @param boolean whether the list is read-only
* @throws TInvalidDataTypeException If data is not null and neither an array nor an iterator.
*/
public function __construct($data=null,$readOnly=false)
{
if($data!==null) {
$this->copyFrom($data);
}
$this->setReadOnly($readOnly);
}

/**
* @return boolean whether this map is read-only or not. Defaults to false.
*/
public function getReadOnly()
{
return $this->_r;
}

/**
* @param boolean whether this list is read-only or not
*/
protected function setReadOnly($value)
{
$this->_r=$value ;
}

/**
* Returns an iterator for traversing the items in the list.
* This method is required by the interface IteratorAggregate.
* @return Iterator an iterator for traversing the items in the list.
*/
public function getIterator()
{
return new TMapIterator($this->_d);
}

/**
* Returns the number of items in the map.
* This method is required by Countable interface.
* @return integer number of items in the map.
*/
public function count()
{
return $this->getCount();
}

/**
* @return integer the number of items in the map
*/
public function getCount()
{
return count($this->_d);
}

/**
* @return array the key list
*/
public function getKeys()
{
return array_keys($this->_d);
}

/**
* Returns the item with the specified key.
* This method is exactly the same as {@link offsetGet}.
* @param mixed the key
* @return mixed the element at the offset, null if no element is found at the offset
*/
public function itemAt($key)
{
return isset($this->_d[$key]) ? $this->_d[$key] : null;
}

/**
* Adds an item into the map.
* Note, if the specified key already exists, the old value will be overwritten.
* @param mixed key
* @param mixed value
* @throws TInvalidOperationException if the map is read-only
*/
public function add($key,$value)
{
if(!$this->_r)
$this->_d[$key]=$value;
}

/**
* Removes an item from the map by its key.
* @param mixed the key of the item to be removed
* @return mixed the removed value, null if no such key exists.
* @throws TInvalidOperationException if the map is read-only
*/
public function remove($key)
{
if(!$this->_r)
{
if(isset($this->_d[$key]) || array_key_exists($key,$this->_d))
{
$value=$this->_d[$key];
unset($this->_d[$key]);
return $value;
}
else
return null;
}
}

/**
* Removes all items in the map.
*/
public function clear()
{
foreach(array_keys($this->_d) as $key)
$this->remove($key);
}

/**
* @param mixed the key
* @return boolean whether the map contains an item with the specified key
*/
public function contains($key)
{
return isset($this->_d[$key]) || array_key_exists($key,$this->_d);
}

/**
* @return array the list of items in array
*/
public function toArray()
{
return $this->_d;
}

/**
* Copies iterable data into the map.
* Note, existing data in the map will be cleared first.
* @param mixed the data to be copied from, must be an array or object implementing Traversable
* @throws TInvalidDataTypeException If data is neither an array nor an iterator.
*/
public function copyFrom($data)
{
if(is_array($data) || $data instanceof IteratorAggregate || $data instanceof Iterator)
{
if($this->getCount()>0) {
$this->clear();
}
foreach($data as $key=>$value) {
$this->add($key,$value);
}
}
}

/**
* Merges iterable data into the map.
* Existing data in the map will be kept and overwritten if the keys are the same.
* @param mixed the data to be merged with, must be an array or object implementing Traversable
* @throws TInvalidDataTypeException If data is neither an array nor an iterator.
*/
public function mergeWith($data)
{
if(is_array($data) || $data instanceof IteratorAggregate || $data instanceof Iterator)
{
foreach($data as $key=>$value)
$this->add($key,$value);
}
else if($data!==null)
throw new TInvalidDataTypeException('map_data_not_iterable');
}

/**
* Returns whether there is an element at the specified offset.
* This method is required by the interface ArrayAccess.
* @param mixed the offset to check on
* @return boolean
*/
public function offsetExists($offset)
{
return $this->contains($offset);
}

/**
* Returns the element at the specified offset.
* This method is required by the interface ArrayAccess.
* @param integer the offset to retrieve element.
* @return mixed the element at the offset, null if no element is found at the offset
*/
public function offsetGet($offset)
{
return $this->itemAt($offset);
}

/**
* Sets the element at the specified offset.
* This method is required by the interface ArrayAccess.
* @param integer the offset to set element
* @param mixed the element value
*/
public function offsetSet($offset,$item)
{
$this->add($offset,$item);
}

/**
* Unsets the element at the specified offset.
* This method is required by the interface ArrayAccess.
* @param mixed the offset to unset element
*/
public function offsetUnset($offset)
{
$this->remove($offset);
}
}

/**
* TMapIterator class
*
* TMapIterator implements Iterator interface.
*
* TMapIterator is used by TMap. It allows TMap to return a new iterator
* for traversing the items in the map.
*
* @author Qiang Xue
* @version $Id: TMap.php 1398 2006-09-08 19:31:03Z xue $
* @package System.Collections
* @since 3.0
*/
class TMapIterator implements Iterator
{
/**
* @var array the data to be iterated through
*/
private $_d;
/**
* @var array list of keys in the map
*/
private $_keys;
/**
* @var mixed current key
*/
private $_key;

/**
* Constructor.
* @param array the data to be iterated through
*/
public function __construct(&$data)
{
$this->_d=&$data;
$this->_keys=array_keys($data);
}

/**
* Rewinds internal array pointer.
* This method is required by the interface Iterator.
*/
public function rewind()
{
$this->_key=reset($this->_keys);
}

/**
* Returns the key of the current array element.
* This method is required by the interface Iterator.
* @return mixed the key of the current array element
*/
public function key()
{
return $this->_key;
}

/**
* Returns the current array element.
* This method is required by the interface Iterator.
* @return mixed the current array element
*/
public function current()
{
return $this->_d[$this->_key];
}

/**
* Moves the internal pointer to the next array element.
* This method is required by the interface Iterator.
*/
public function next()
{
$this->_key=next($this->_keys);
}

/**
* Returns whether there is an element at current position.
* This method is required by the interface Iterator.
* @return boolean
*/
public function valid()
{
return $this->_key!==false;
}
}

$array = new MyMap();

echo ($array instanceof Traversable ? ' MyMap is instance of Traversable ' : ' MyMap is NOT instance of Traversable ' );
echo ($array instanceof IteratorAggregate ? ' MyMap is instance of IteratorAggregate ' : ' MyMap is NOT instance of IteratorAggregate ' );
echo ($array instanceof Iterator ? ' MyMap is instance of Iterator ' : ' MyMap is NOT instance of Iterator ' );

The result on quercus is:

MyMap is NOT instance of Traversable
MyMap is instance of IteratorAggregate
MyMap is NOT instance of Iterator

The result on apache:

MyMap is instance of Traversable
MyMap is instance of IteratorAggregate
MyMap is NOT instance of Iterator

i used the resin 3.2.1 binary from the website

Notes
(0003742)
ferg   
01-15-09 18:10   
php/4as6