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