1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13:
14:
15: 16: 17: 18: 19: 20: 21: 22:
23: abstract class NCollection extends ArrayObject implements ICollection
24: {
25:
26: private $itemType;
27:
28:
29: private $checkFunc;
30:
31:
32: private $frozen = FALSE;
33:
34:
35:
36: 37: 38: 39: 40:
41: public function __construct($arr = NULL, $type = NULL)
42: {
43: if (substr($type, 0, 1) === ':') {
44: $this->itemType = substr($type, 1);
45: $this->checkFunc = 'is_' . $this->itemType;
46: } else {
47: $this->itemType = $type;
48: }
49:
50: if ($arr !== NULL) {
51: $this->import($arr);
52: }
53: }
54:
55:
56:
57: 58: 59: 60: 61: 62:
63: public function append($item)
64: {
65: $this->beforeAdd($item);
66: parent::append($item);
67: }
68:
69:
70:
71: 72: 73: 74: 75: 76:
77: public function remove($item)
78: {
79: $this->updating();
80: $index = $this->search($item);
81: if ($index === FALSE) {
82: return FALSE;
83: } else {
84: parent::offsetUnset($index);
85: return TRUE;
86: }
87: }
88:
89:
90:
91: 92: 93: 94: 95: 96:
97: protected function search($item)
98: {
99: return array_search($item, $this->getArrayCopy(), TRUE);
100: }
101:
102:
103:
104: 105: 106: 107: 108:
109: public function clear()
110: {
111: $this->updating();
112: parent::exchangeArray(array());
113: }
114:
115:
116:
117: 118: 119: 120: 121:
122: public function contains($item)
123: {
124: return $this->search($item) !== FALSE;
125: }
126:
127:
128:
129: 130: 131: 132: 133: 134:
135: public function import($arr)
136: {
137: if (!(is_array($arr) || $arr instanceof Traversable)) {
138: throw new InvalidArgumentException("Argument must be traversable.");
139: }
140:
141: $this->clear();
142: foreach ($arr as $item) {
143: $this->offsetSet(NULL, $item);
144: }
145: }
146:
147:
148:
149: 150: 151: 152:
153: public function getItemType()
154: {
155: return $this->itemType;
156: }
157:
158:
159:
160: 161: 162:
163: public function setReadOnly()
164: {
165: throw new DeprecatedException(__METHOD__ . '() is deprecated; use freeze() instead.');
166: }
167:
168:
169:
170: 171: 172:
173: public function isReadOnly()
174: {
175: throw new DeprecatedException(__METHOD__ . '() is deprecated; use isFrozen() instead.');
176: }
177:
178:
179:
180:
181:
182:
183:
184: 185: 186: 187: 188: 189:
190: protected function beforeAdd($item)
191: {
192: $this->updating();
193:
194: if ($this->itemType !== NULL) {
195: if ($this->checkFunc === NULL) {
196: if (!($item instanceof $this->itemType)) {
197: throw new InvalidArgumentException("Item must be '$this->itemType' object.");
198: }
199: } else {
200: $fnc = $this->checkFunc;
201: if (!$fnc($item)) {
202: throw new InvalidArgumentException("Item must be $this->itemType type.");
203: }
204: }
205: }
206: }
207:
208:
209:
210:
211:
212:
213:
214: 215: 216: 217:
218: public function getIterator()
219: {
220: return new ArrayIterator($this->getArrayCopy());
221: }
222:
223:
224:
225: 226: 227:
228: public function exchangeArray($array)
229: {
230: throw new NotSupportedException('Use ' . __CLASS__ . '::import()');
231: }
232:
233:
234:
235: 236: 237: 238: 239:
240: protected function setArray($array)
241: {
242: parent::exchangeArray($array);
243: return $this;
244: }
245:
246:
247:
248:
249:
250:
251:
252: 253: 254:
255: public function getReflection()
256: {
257: return new NClassReflection($this);
258: }
259:
260:
261:
262: 263: 264: 265:
266: public function __call($name, $args)
267: {
268: return NObjectMixin::call($this, $name, $args);
269: }
270:
271:
272:
273: 274: 275: 276:
277: public static function __callStatic($name, $args)
278: {
279: $class = __CLASS__;
280: throw new MemberAccessException("Call to undefined static method $class::$name().");
281: }
282:
283:
284:
285: 286: 287: 288:
289: public function &__get($name)
290: {
291: return NObjectMixin::get($this, $name);
292: }
293:
294:
295:
296: 297: 298: 299:
300: public function __set($name, $value)
301: {
302: return NObjectMixin::set($this, $name, $value);
303: }
304:
305:
306:
307: 308: 309: 310: 311:
312: public function __isset($name)
313: {
314: return NObjectMixin::has($this, $name);
315: }
316:
317:
318:
319: 320: 321: 322:
323: public function __unset($name)
324: {
325: throw new MemberAccessException("Cannot unset the property {$this->reflection->name}::\$$name.");
326: }
327:
328:
329:
330:
331:
332:
333:
334: 335: 336: 337:
338: public function freeze()
339: {
340: $this->frozen = TRUE;
341: }
342:
343:
344:
345: 346: 347: 348:
349: final public function isFrozen()
350: {
351: return $this->frozen;
352: }
353:
354:
355:
356: 357: 358: 359:
360: public function __clone()
361: {
362: $this->frozen = FALSE;
363: }
364:
365:
366:
367: 368: 369:
370: protected function updating()
371: {
372: if ($this->frozen) {
373: $class = get_class($this);
374: throw new InvalidStateException("Cannot modify a frozen object '$class'.");
375: }
376: }
377:
378: }
379: