1: <?php
2: /**
3: * This file contains classes implementing attribute collection feature.
4: *
5: * @author Qiang Xue <qiang.xue@gmail.com>
6: * @link http://www.yiiframework.com/
7: * @copyright 2008-2013 Yii Software LLC
8: * @license http://www.yiiframework.com/license/
9: */
10:
11:
12: /**
13: * CAttributeCollection implements a collection for storing attribute names and values.
14: *
15: * Besides all functionalities provided by {@link CMap}, CAttributeCollection
16: * allows you to get and set attribute values like getting and setting
17: * properties. For example, the following usages are all valid for a
18: * CAttributeCollection object:
19: * <pre>
20: * $collection->text='text'; // same as: $collection->add('text','text');
21: * echo $collection->text; // same as: echo $collection->itemAt('text');
22: * </pre>
23: *
24: * The case sensitivity of attribute names can be toggled by setting the
25: * {@link caseSensitive} property of the collection.
26: *
27: * @author Qiang Xue <qiang.xue@gmail.com>
28: * @package system.collections
29: * @since 1.0
30: */
31: class CAttributeCollection extends CMap
32: {
33: /**
34: * @var boolean whether the keys are case-sensitive. Defaults to false.
35: */
36: public $caseSensitive=false;
37:
38: /**
39: * Returns a property value or an event handler list by property or event name.
40: * This method overrides the parent implementation by returning
41: * a key value if the key exists in the collection.
42: * @param string $name the property name or the event name
43: * @return mixed the property value or the event handler list
44: * @throws CException if the property/event is not defined.
45: */
46: public function __get($name)
47: {
48: if($this->contains($name))
49: return $this->itemAt($name);
50: else
51: return parent::__get($name);
52: }
53:
54: /**
55: * Sets value of a component property.
56: * This method overrides the parent implementation by adding a new key value
57: * to the collection.
58: * @param string $name the property name or event name
59: * @param mixed $value the property value or event handler
60: * @throws CException If the property is not defined or read-only.
61: */
62: public function __set($name,$value)
63: {
64: $this->add($name,$value);
65: }
66:
67: /**
68: * Checks if a property value is null.
69: * This method overrides the parent implementation by checking
70: * if the key exists in the collection and contains a non-null value.
71: * @param string $name the property name or the event name
72: * @return boolean whether the property value is null
73: */
74: public function __isset($name)
75: {
76: if($this->contains($name))
77: return $this->itemAt($name)!==null;
78: else
79: return parent::__isset($name);
80: }
81:
82: /**
83: * Sets a component property to be null.
84: * This method overrides the parent implementation by clearing
85: * the specified key value.
86: * @param string $name the property name or the event name
87: */
88: public function __unset($name)
89: {
90: $this->remove($name);
91: }
92:
93: /**
94: * Returns the item with the specified key.
95: * This overrides the parent implementation by converting the key to lower case first if {@link caseSensitive} is false.
96: * @param mixed $key the key
97: * @return mixed the element at the offset, null if no element is found at the offset
98: */
99: public function itemAt($key)
100: {
101: if($this->caseSensitive)
102: return parent::itemAt($key);
103: else
104: return parent::itemAt(strtolower($key));
105: }
106:
107: /**
108: * Adds an item into the map.
109: * This overrides the parent implementation by converting the key to lower case first if {@link caseSensitive} is false.
110: * @param mixed $key key
111: * @param mixed $value value
112: */
113: public function add($key,$value)
114: {
115: if($this->caseSensitive)
116: parent::add($key,$value);
117: else
118: parent::add(strtolower($key),$value);
119: }
120:
121: /**
122: * Removes an item from the map by its key.
123: * This overrides the parent implementation by converting the key to lower case first if {@link caseSensitive} is false.
124: * @param mixed $key the key of the item to be removed
125: * @return mixed the removed value, null if no such key exists.
126: */
127: public function remove($key)
128: {
129: if($this->caseSensitive)
130: return parent::remove($key);
131: else
132: return parent::remove(strtolower($key));
133: }
134:
135: /**
136: * Returns whether the specified is in the map.
137: * This overrides the parent implementation by converting the key to lower case first if {@link caseSensitive} is false.
138: * @param mixed $key the key
139: * @return boolean whether the map contains an item with the specified key
140: */
141: public function contains($key)
142: {
143: if($this->caseSensitive)
144: return parent::contains($key);
145: else
146: return parent::contains(strtolower($key));
147: }
148:
149: /**
150: * Determines whether a property is defined.
151: * This method overrides parent implementation by returning true
152: * if the collection contains the named key.
153: * @param string $name the property name
154: * @return boolean whether the property is defined
155: */
156: public function hasProperty($name)
157: {
158: return $this->contains($name) || parent::hasProperty($name);
159: }
160:
161: /**
162: * Determines whether a property can be read.
163: * This method overrides parent implementation by returning true
164: * if the collection contains the named key.
165: * @param string $name the property name
166: * @return boolean whether the property can be read
167: */
168: public function canGetProperty($name)
169: {
170: return $this->contains($name) || parent::canGetProperty($name);
171: }
172:
173: /**
174: * Determines whether a property can be set.
175: * This method overrides parent implementation by always returning true
176: * because you can always add a new value to the collection.
177: * @param string $name the property name
178: * @return boolean true
179: */
180: public function canSetProperty($name)
181: {
182: return true;
183: }
184:
185: /**
186: * Merges iterable data into the map.
187: *
188: * Existing elements in the map will be overwritten if their keys are the same as those in the source.
189: * If the merge is recursive, the following algorithm is performed:
190: * <ul>
191: * <li>the map data is saved as $a, and the source data is saved as $b;</li>
192: * <li>if $a and $b both have an array indexed at the same string key, the arrays will be merged using this algorithm;</li>
193: * <li>any integer-indexed elements in $b will be appended to $a and reindexed accordingly;</li>
194: * <li>any string-indexed elements in $b will overwrite elements in $a with the same index;</li>
195: * </ul>
196: *
197: * @param mixed $data the data to be merged with, must be an array or object implementing Traversable
198: * @param boolean $recursive whether the merging should be recursive.
199: *
200: * @throws CException If data is neither an array nor an iterator.
201: */
202: public function mergeWith($data,$recursive=true)
203: {
204: if(!$this->caseSensitive && (is_array($data) || $data instanceof Traversable))
205: {
206: $d=array();
207: foreach($data as $key=>$value)
208: $d[strtolower($key)]=$value;
209: return parent::mergeWith($d,$recursive);
210: }
211: parent::mergeWith($data,$recursive);
212: }
213: }
214: