Browse Source

finished Vector.

tags/2.0.0-beta
Qiang Xue 14 years ago
parent
commit
3ba68632b2
  1. 152
      framework/base/Component.php
  2. 4
      framework/base/Event.php
  3. 25
      framework/base/Exception.php
  4. 345
      framework/base/Vector.php
  5. 95
      framework/base/VectorIterator.php

152
framework/base/Component.php

@ -5,7 +5,7 @@ namespace yii\base;
class Component
{
private $_e;
private $_m;
private $_b;
/**
* Returns a property value, an event handler list or a behavior based on its name.
@ -23,26 +23,27 @@ class Component
public function __get($name)
{
$getter = 'get' . $name;
if(method_exists($this, $getter))
if (method_exists($this, $getter)) { // read property, e.g. getName()
return $this->$getter();
else if(method_exists($this, $name) && strncasecmp($name, 'on', 2)===0)
{
}
elseif (method_exists($this, $name) && strncasecmp($name, 'on', 2) === 0) { // event, e.g. onClick()
$name = strtolower($name);
if(!isset($this->_e[$name]))
$this->_e[$name]=new yii\collections\Vector;
if (!isset($this->_e[$name])) {
$this->_e[$name] = new \yii\collections\Vector;
}
return $this->_e[$name];
}
else if(isset($this->_m[$name]))
return $this->_m[$name];
else if(is_array($this->_m)) // has attached behaviors
{
foreach($this->_m as $object)
{
if($object->getEnabled() && $object->canGetProperty($name))
elseif (isset($this->_b[$name])) { // behavior
return $this->_b[$name];
}
elseif (is_array($this->_b)) { // a behavior property
foreach ($this->_b as $object) {
if ($object->getEnabled() && $object->canGetProperty($name)) {
return $object->$name;
}
}
throw new Exception('Undefined property: ' . get_class($this) . '.' $name);
}
throw new Exception('Getting unknown property: ' . get_class($this) . '.' $name);
}
/**
@ -61,27 +62,29 @@ class Component
public function __set($name, $value)
{
$setter = 'set' . $name;
if(method_exists($this,$setter))
if (method_exists($this, $setter)) {
return $this->$setter($value);
else if(method_exists($this,$name) && strncasecmp($name,'on',2)===0)
{
}
elseif (method_exists($this, $name) && strncasecmp($name, 'on', 2) === 0) {
$name = strtolower($name);
if(!isset($this->_e[$name]))
$this->_e[$name]=new yii\collections\Vector;
if (!isset($this->_e[$name])) {
$this->_e[$name] = new \yii\collections\Vector;
}
return $this->_e[$name]->add($value);
}
else if(is_array($this->_m))
{
foreach($this->_m as $object)
{
if($object->getEnabled() && $object->canSetProperty($name))
elseif (is_array($this->_b)) {
foreach ($this->_b as $object) {
if ($object->getEnabled() && $object->canSetProperty($name)) {
return $object->$name = $value;
}
}
if(method_exists($this,'get'.$name))
throw new Exception('Unable to set read-only property: ' . get_class($this) . '.' $name);
else
throw new Exception('Undefined property: ' . get_class($this) . '.' $name);
}
if (method_exists($this, 'get' . $name)) {
throw new Exception('Setting read-only property: ' . get_class($this) . '.' $name);
}
else {
throw new Exception('Setting unknown property: ' . get_class($this) . '.' $name);
}
}
/**
@ -94,22 +97,22 @@ class Component
public function __isset($name)
{
$getter = 'get' . $name;
if(method_exists($this,$getter))
if (method_exists($this, $getter)) { // property is not null
return $this->$getter() !== null;
else if(method_exists($this,$name) && strncasecmp($name,'on',2)===0)
{
}
elseif (method_exists($this, $name) && strncasecmp($name, 'on', 2) === 0) { // has event handler
$name = strtolower($name);
return isset($this->_e[$name]) && $this->_e[$name]->getCount();
}
else if(isset($this->_m[$name]))
return true;
else if(is_array($this->_m))
{
foreach($this->_m as $object)
{
if($object->getEnabled() && $object->canGetProperty($name))
elseif (isset($this->_b[$name])) { // has behavior
return true;
}
elseif (is_array($this->_b)) {
foreach ($this->_b as $object) {
if ($object->getEnabled() && $object->canGetProperty($name)) {
return $object->$name !== null;
}
}
}
return false;
}
@ -125,22 +128,25 @@ class Component
public function __unset($name)
{
$setter = 'set' . $name;
if(method_exists($this,$setter))
if (method_exists($this, $setter)) {
$this->$setter(null);
else if(method_exists($this,$name) && strncasecmp($name,'on',2)===0)
}
elseif (method_exists($this, $name) && strncasecmp($name, 'on', 2) === 0) {
unset($this->_e[strtolower($name)]);
else if(isset($this->_m[$name]))
}
elseif (isset($this->_b[$name])) {
$this->detachBehavior($name);
else if(is_array($this->_m))
{
foreach($this->_m as $object)
}
elseif (is_array($this->_b)) {
foreach ($this->_b as $object)
{
if($object->getEnabled() && $object->canSetProperty($name))
if ($object->getEnabled() && $object->canSetProperty($name)) {
return $object->$name = null;
}
}
}
elseif (method_exists($this, 'get' . $name))
throw new Exception('Unable to unset read-only property: ' . get_class($this) . '.' $name);
throw new Exception('Unsetting read-only property: ' . get_class($this) . '.' $name);
}
/**
@ -154,16 +160,21 @@ class Component
*/
public function __call($name, $parameters)
{
if($this->_m!==null)
if ($this->_b !== null)
{
foreach($this->_m as $object)
foreach ($this->_b as $object)
{
if($object->getEnabled() && method_exists($object,$name))
if ($object->getEnabled() && method_exists($object, $name)) {
return call_user_func_array(array($object, $name), $parameters);
}
}
if($this->canGetProperty($name) && $this->$name instanceof \Closure)
return call_user_func_array($this->$name, $parameters);
}
if ($this->canGetProperty($name)) {
$func = $this->$name;
if ($func instanceof \Closure) {
return call_user_func_array($func, $parameters);
}
}
throw new Exception('Unknown method: ' . get_class($this) . '::' $name . '()');
}
@ -176,7 +187,7 @@ class Component
*/
public function asa($behavior)
{
return isset($this->_m[$behavior]) ? $this->_m[$behavior] : null;
return isset($this->_b[$behavior]) ? $this->_b[$behavior] : null;
}
/**
@ -206,11 +217,11 @@ class Component
*/
public function detachBehaviors()
{
if($this->_m!==null)
if ($this->_b !== null)
{
foreach($this->_m as $name=>$behavior)
foreach ($this->_b as $name => $behavior)
$this->detachBehavior($name);
$this->_m=null;
$this->_b = null;
}
}
@ -231,7 +242,7 @@ class Component
$behavior = Yii::createComponent($behavior);
$behavior->setEnabled(true);
$behavior->attach($this);
return $this->_m[$name]=$behavior;
return $this->_b[$name] = $behavior;
}
/**
@ -243,11 +254,11 @@ class Component
*/
public function detachBehavior($name)
{
if(isset($this->_m[$name]))
if (isset($this->_b[$name]))
{
$this->_m[$name]->detach($this);
$behavior=$this->_m[$name];
unset($this->_m[$name]);
$this->_b[$name]->detach($this);
$behavior = $this->_b[$name];
unset($this->_b[$name]);
return $behavior;
}
}
@ -258,9 +269,9 @@ class Component
*/
public function enableBehaviors()
{
if($this->_m!==null)
if ($this->_b !== null)
{
foreach($this->_m as $behavior)
foreach ($this->_b as $behavior)
$behavior->setEnabled(true);
}
}
@ -271,9 +282,9 @@ class Component
*/
public function disableBehaviors()
{
if($this->_m!==null)
if ($this->_b !== null)
{
foreach($this->_m as $behavior)
foreach ($this->_b as $behavior)
$behavior->setEnabled(false);
}
}
@ -287,8 +298,8 @@ class Component
*/
public function enableBehavior($name)
{
if(isset($this->_m[$name]))
$this->_m[$name]->setEnabled(true);
if (isset($this->_b[$name]))
$this->_b[$name]->setEnabled(true);
}
/**
@ -299,8 +310,8 @@ class Component
*/
public function disableBehavior($name)
{
if(isset($this->_m[$name]))
$this->_m[$name]->setEnabled(false);
if (isset($this->_b[$name]))
$this->_b[$name]->setEnabled(false);
}
/**
@ -308,13 +319,14 @@ class Component
* A property is defined if there is a getter or setter method
* defined in the class. Note, property names are case-insensitive.
* @param string $name the property name
* @param boolean $checkVar whether to check class properties. If false, only properties declared by getters will be checked.
* @return boolean whether the property is defined
* @see canGetProperty
* @see canSetProperty
*/
public function hasProperty($name)
public function hasProperty($name, $checkVar = true)
{
return method_exists($this,'get'.$name) || method_exists($this,'set'.$name);
return method_exists($this, 'get' . $name) || method_exists($this, 'set' . $name) || property_exists($this, $name);
}
/**

4
framework/base/Event.php

@ -3,7 +3,7 @@
namespace yii\base;
/**
* CEvent is the base class for all event classes.
* Event is the base class for all event classes.
*
* It encapsulates the parameters associated with an event.
* The {@link sender} property describes who raises the event.
@ -16,7 +16,7 @@ namespace yii\base;
* @package system.base
* @since 1.0
*/
class CEvent extends CComponent
class Event extends Component
{
/**
* @var object the sender of this event

25
framework/base/Exception.php

@ -0,0 +1,25 @@
<?php
namespace yii\base;
/**
* Exception class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008-2011 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
/**
* Exception represents a generic exception for all purposes.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @version $Id: CException.php 2799 2011-01-01 19:31:13Z qiang.xue $
* @package system.base
* @since 1.0
*/
class Exception extends \Exception
{
}

345
framework/base/Vector.php

@ -0,0 +1,345 @@
<?php
/**
* Vector class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008-2012 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* Vector implements an integer-indexed collection class.
*
* You can access, append, insert, remove an item from the vector
* by calling methods such as [[itemAt]], [[add]], [[insertAt]],
* [[remove]] and [[removeAt]].
*
* To get the number of the items in the vector, use [[getCount]].
*
* Because Vector implements a set of SPL interfaces, it can be used
* like a regular PHP array as follows,
*
* ~~~php
* $vector[] = $item; // append new item at the end
* $vector[$index] = $item; // set new item at $index
* unset($vector[$index]); // remove the item at $index
* if (isset($vector[$index])) // if the vector has an item at $index
* foreach ($vector as $index=>$item) // traverse each item in the vector
* $n = count($vector); // count the number of items
* ~~~
*
* Note that if you plan to extend Vector by performing additional operations
* with each addition or removal of an item (e.g. performing type check),
* please make sure you override [[insertAt]] and [[removeAt]].
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Vector extends Component implements \IteratorAggregate, \ArrayAccess, \Countable
{
/**
* @var boolean whether this vector is read-only or not.
* If the vector is read-only, adding or moving items will throw an exception.
*/
public $readOnly;
/**
* @var array internal data storage
*/
private $_d = array();
/**
* @var integer number of items
*/
private $_c = 0;
/**
* Constructor.
* Initializes the vector with an array or an iterable object.
* @param mixed $data the initial data to be populated into the vector.
* This can be an array or an iterable object. If null, the vector will be set as empty.
* @param boolean $readOnly whether the vector should be marked as read-only.
* @throws Exception if data is not well formed (neither an array nor an iterable object)
*/
public function __construct($data = array(), $readOnly = false)
{
if ($data !== array()) {
$this->copyFrom($data);
}
$this->readOnly = $readOnly;
}
/**
* Returns an iterator for traversing the items in the vector.
* This method is required by the SPL interface `IteratorAggregate`.
* It will be implicitly called when you use `foreach` to traverse the vector.
* @return Iterator an iterator for traversing the items in the vector.
*/
public function getIterator()
{
return new VectorIterator($this->_d);
}
/**
* Returns the number of items in the vector.
* This method is required by the SPL `Countable` interface.
* It will be implicitly called when you use `count($vector)`.
* @return integer number of items in the vector.
*/
public function count()
{
return $this->getCount();
}
/**
* Returns the number of items in the vector.
* @return integer the number of items in the vector
*/
public function getCount()
{
return $this->_c;
}
/**
* Returns the item at the specified index.
* @param integer $index the index of the item
* @return mixed the item at the index
* @throws Exception if the index is out of range
*/
public function itemAt($index)
{
if (isset($this->_d[$index])) {
return $this->_d[$index];
}
elseif ($index >= 0 && $index < $this->_c) { // in case the value is null
return $this->_d[$index];
}
throw new Exception('Index out of range: ' . $index);
}
/**
* Appends an item at the end of the vector.
* @param mixed $item new item
* @return integer the zero-based index at which the item is added
* @throws Exception if the vector is read-only.
*/
public function add($item)
{
$this->insertAt($this->_c, $item);
return $this->_c-1;
}
/**
* Inserts an item at the specified position.
* Original item at the position and the following items will be moved
* one step towards the end.
* @param integer $index the specified position.
* @param mixed $item new item to be inserted into the vector
* @throws Exception if the index specified is out of range, or the vector is read-only.
*/
public function insertAt($index, $item)
{
if (!$this->readOnly) {
if ($index === $this->_c) {
$this->_d[$this->_c++] = $item;
}
elseif ($index >= 0 && $index < $this->_c) {
array_splice($this->_d, $index, 0, array($item));
$this->_c++;
}
throw new Exception('Index out of range: ' . $index);
}
throw new Exception('Vector is read only.');
}
/**
* Removes an item from the vector.
* The vector will search for the item, and the first item found
* will be removed from the vector.
* @param mixed $item the item to be removed.
* @return mixed the index at which the item is being removed, or false
* if the item cannot be found in the vector.
* @throws Exception if the vector is read only.
*/
public function remove($item)
{
if (($index = $this->indexOf($item)) >= 0)
{
$this->removeAt($index);
return $index;
}
else
return false;
}
/**
* Removes an item at the specified position.
* @param integer $index the index of the item to be removed.
* @return mixed the removed item.
* @throws Exception if the index is out of range, or the vector is read only.
*/
public function removeAt($index)
{
if (!$this->readOnly) {
if ($index >= 0 && $index < $this->_c) {
$this->_c--;
if ($index === $this->_c) {
return array_pop($this->_d);
}
else {
$item = $this->_d[$index];
array_splice($this->_d, $index, 1);
return $item;
}
}
throw new Exception('Index out of range: ' . $index);
}
throw new Exception('Vector is read only.');
}
/**
* Removes all items from the vector.
*/
public function clear()
{
for ($i = $this->_c-1;$i >= 0;--$i) {
$this->removeAt($i);
}
}
/**
* Returns a value indicating whether the vector contains the specified item.
* Note that the search is based on strict PHP comparison.
* @param mixed $item the item
* @return boolean whether the vector contains the item
*/
public function contains($item)
{
return $this->indexOf($item) >= 0;
}
/**
* Returns the index of the specified item in the vector.
* The index is zero-based. If the item is not found in the vector, -1 will be returned.
* Note that the search is based on strict PHP comparison.
* @param mixed $item the item
* @return integer the index of the item in the vector (0 based), -1 if not found.
*/
public function indexOf($item)
{
$index = array_search($item, $this->_d, true);
return $index === false ? -1 : $index;
}
/**
* Returns the vector as a PHP array.
* @return array the items in the vector.
*/
public function toArray()
{
return $this->_d;
}
/**
* Copies iterable data into the vector.
* Note, existing data in the vector will be cleared first.
* @param mixed $data the data to be copied from, must be an array or an object implementing `Traversable`
* @throws Exception if data is neither an array nor an object implementing `Traversable`.
*/
public function copyFrom($data)
{
if (is_array($data) || ($data instanceof Traversable)) {
if ($this->_c > 0) {
$this->clear();
}
if ($data instanceof self) {
$data = $data->_d;
}
foreach ($data as $item) {
$this->add($item);
}
}
throw new Exception('Data must be either an array or an object implementing Traversable.');
}
/**
* Merges iterable data into the vector.
* New items will be appended to the end of the existing items.
* @param mixed $data the data to be merged with, must be an array or an object implementing `Traversable`
* @throws Exception if data is neither an array nor an object implementing `Traversable`.
*/
public function mergeWith($data)
{
if (is_array($data) || ($data instanceof Traversable)) {
if ($data instanceof Vector) {
$data = $data->_d;
}
foreach ($data as $item) {
$this->add($item);
}
}
throw new Exception('Data must be either an array or an object implementing Traversable.');
}
/**
* Returns a value indicating whether there is an item at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `isset($vector[$index])`.
* @param integer $offset the offset to be checked
* @return boolean whether there is an item at the specified offset.
*/
public function offsetExists($offset)
{
return $offset >= 0 && $offset < $this->_c;
}
/**
* Returns the item at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `$value = $vector[$index];`.
* @param integer $offset the offset to retrieve item.
* @return mixed the item at the offset
* @throws Exception if the offset is out of range
*/
public function offsetGet($offset)
{
return $this->itemAt($offset);
}
/**
* Sets the item at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `$vector[$index] = $value;`.
* If the offset is null or equal to the number of the existing items,
* the new item will be appended to the vector.
* Otherwise, the existing item at the offset will be replaced with the new item.
* @param integer $offset the offset to set item
* @param mixed $item the item value
* @throws Exception if the offset is out of range, or the vector is read only.
*/
public function offsetSet($offset, $item)
{
if ($offset === null || $offset === $this->_c) {
$this->insertAt($this->_c, $item);
}
else {
$this->removeAt($offset);
$this->insertAt($offset, $item);
}
}
/**
* Unsets the item at the specified offset.
* This method is required by the SPL interface `ArrayAccess`.
* It is implicitly called when you use something like `unset($vector[$index])`.
* This is equivalent to [[removeAt]].
* @param integer $offset the offset to unset item
* @throws Exception if the offset is out of range, or the vector is read only.
*/
public function offsetUnset($offset)
{
$this->removeAt($offset);
}
}

95
framework/base/VectorIterator.php

@ -0,0 +1,95 @@
<?php
/**
* VectorIterator class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008-2012 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* VectorIterator implements the SPL `Iterator` interface for [[Vector]].
*
* It allows [[Vector]] to return a new iterator for data traversing purpose.
* You normally do not use this class directly.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class VectorIterator implements \Iterator
{
/**
* @var array the data to be iterated through
*/
private $_d;
/**
* @var integer index of the current item
*/
private $_i;
/**
* @var integer count of the data items
*/
private $_c;
/**
* Constructor.
* @param array $data the data to be iterated through
*/
public function __construct(&$data)
{
$this->_d = &$data;
$this->_i = 0;
$this->_c = count($this->_d);
}
/**
* Rewinds the index of the current item.
* This method is required by the SPL interface `Iterator`.
*/
public function rewind()
{
$this->_i = 0;
}
/**
* Returns the key of the current item.
* This method is required by the SPL interface `Iterator`.
* @return integer the key of the current item
*/
public function key()
{
return $this->_i;
}
/**
* Returns the current item.
* This method is required by the SPL interface `Iterator`.
* @return mixed the current item
*/
public function current()
{
return $this->_d[$this->_i];
}
/**
* Moves the internal pointer to the next item.
* This method is required by the SPL interface `Iterator`.
*/
public function next()
{
$this->_i++;
}
/**
* Returns a value indicating whether there is an item at current position.
* This method is required by the SPL interface `Iterator`.
* @return boolean whether there is an item at current position.
*/
public function valid()
{
return $this->_i < $this->_c;
}
}
Loading…
Cancel
Save