TWeakList::contains()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 1
b 0
f 0
1
<?php
2
3
/**
4
 * TWeakList class
5
 *
6
 * @author Brad Anderson <[email protected]>
7
 * @link https://github.com/pradosoft/prado
8
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
9
 */
10
11
namespace Prado\Collections;
12
13
use Prado\Exceptions\TInvalidOperationException;
14
use Prado\Exceptions\TInvalidDataTypeException;
15
use Prado\Exceptions\TInvalidDataValueException;
16
use Prado\Prado;
17
use Prado\TEventHandler;
18
use Prado\TPropertyValue;
19
use ArrayAccess;
20
use Closure;
21
use Traversable;
22
use WeakReference;
23
24
/**
25
 * TWeakList class
26
 *
27
 * TWeakList implements an integer-indexed collection class with objects kept as
28
 * WeakReference.  Closure are treated as function PHP types rather than as objects.
29
 *
30
 * Objects in the TWeakList are encoded into WeakReference when saved, and the objects
31
 * restored on retrieval.  When an object becomes unset in the application/system
32
 * and its WeakReference invalidated, it can be removed from the TWeakList or have
33
 * a null in place of the object, depending on the mode.  The mode can be set during
34
 * {@see __construct Construct}.  The default mode of the TWeakList is to maintain
35
 * a list of only valid objects -where the count and item locations can change when
36
 * an item is invalidated-.  The other mode is to retain the place of invalidated
37
 * objects and replace the object with null -maintaining the count and item locations-.
38
 *
39
 * List items do not need to be objects.  TWeakList is similar to TList except list
40
 * items that are objects (except Closure and IWeakRetainable) are stored as WeakReference.
41
 * List items that are arrays are recursively traversed for replacement of objects
42
 * with WeakReference before storing.  In this way, TWeakList will not retain objects
43
 * (incrementing their use/reference counter) that it contains.  Only primary list
44
 * items are tracked with the WeakMap, and objects in arrays has no effect on the whole.
45
 * If an object in an array is invalidated, it well be replaced by "null".  Arrays
46
 * in the TWeakList are kept regardless of the use/reference count of contained objects.
47
 *
48
 * When searching by a {@see \Prado\TEventHandler} object, it will only find itself and
49
 * will not match on its {@see \Prado\TEventHandler::getHandler}.  However, if searching
50
 * for a callable handler, it will first match direct callable handlers in the list,
51
 * and then search for matching TEventHandlers' Handler regardless of the data.
52
 *
53
 * {@see \Prado\Collections\TWeakCollectionTrait} implements a PHP 8 WeakMap used to track any changes
54
 * in WeakReference objects in the TWeakList and optionally scrubs the list of invalid
55
 * objects on any changes to the WeakMap.
56
 *
57
 * Note that any objects or objects in arrays will be lost if they are not otherwise
58
 * retained in other parts of the application.  The only exception is a PHP Closure.
59
 * Closures are stored without WeakReference so anonymous functions can be stored
60
 * without risk of deletion if it is the only reference.  Closures act similarly to
61
 * a PHP data type rather than an object.
62
 *
63
 * @author Brad Anderson <[email protected]>
64
 * @since 4.3.0
65
 */
66
class TWeakList extends TList implements IWeakCollection, ICollectionFilter
67
{
68
	use TWeakCollectionTrait;
69
70
	/** @var ?bool Should invalid WeakReference automatically be deleted from the list.
71
	 *    Default True.
72
	 */
73
	private ?bool $_discardInvalid = null;
74
75
	/** @var int The number of TEventHandlers in the list */
76
	private int $_eventHandlerCount = 0;
77
78
	/**
79
	 * Constructor.
80
	 * Initializes the weak list with an array or an iterable object.
81
	 * @param null|array|\Iterator $data The initial data. Default is null, meaning no initialization.
82
	 * @param ?bool $readOnly Whether the list is read-only. Default is null.
83
	 * @param ?bool $discardInvalid Whether the list is scrubbed of invalid WeakReferences.
84
	 *   Default is null for the opposite of $readOnly.  Thus, Read Only lists retain
85
	 *   invalid WeakReference; and Mutable lists scrub invalid WeakReferences.
86
	 * @throws TInvalidDataTypeException If data is not null and neither an array nor an iterator.
87
	 */
88
	public function __construct($data = null, $readOnly = null, $discardInvalid = null)
89
	{
90
		parent::__construct($data, $readOnly);
91
		$this->setDiscardInvalid($discardInvalid);
92
	}
93
94
	/**
95
	 * Cloning a TWeakList requires cloning the WeakMap
96
	 */
97
	public function __clone()
98
	{
99
		$this->weakClone();
100
		parent::__clone();
101
	}
102
103
	/**
104
	 * Waking up a TWeakList requires creating the WeakMap.  No items are saved in
105
	 * TWeakList so only initialization of the WeakMap is required.
106
	 */
107
	public function __wakeup()
108
	{
109
		if ($this->_discardInvalid) {
110
			$this->weakStart();
111
		}
112
		parent::__wakeup();
113
	}
114
115
	/**
116
	 * This is a custom function for adding objects to the weak map.  Specifically,
117
	 * if the object being added is a TEventHandler, we use the {@see \Prado\TEventHandler::getHandlerObject}
118
	 * object instead of the TEventHandler itself.
119
	 * @param object $object The object to add to the managed weak map.
120
	 * @since 4.3.0
121
	 */
122
	protected function weakCustomAdd(object $object)
123
	{
124
		if ($object instanceof TEventHandler) {
125
			$object = $object->getHandlerObject();
126
			$this->_eventHandlerCount++;
127
		}
128
		return $this->weakAdd($object);
0 ignored issues
show
Bug introduced by
It seems like $object can also be of type null; however, parameter $object of Prado\Collections\TWeakList::weakAdd() does only seem to accept object, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

128
		return $this->weakAdd(/** @scrutinizer ignore-type */ $object);
Loading history...
129
	}
130
131
	/**
132
	 * This is a custom function for removing objects to the weak map.  Specifically,
133
	 * if the object being removed is a TEventHandler, we use the {@see \Prado\TEventHandler::getHandlerObject}
134
	 * object instead of the TEventHandler itself.
135
	 * @param object $object The object to remove to the managed weak map.
136
	 * @since 4.3.0
137
	 */
138
	protected function weakCustomRemove(object $object)
139
	{
140
		if ($object instanceof TEventHandler) {
141
			$object = $object->getHandlerObject();
142
			$this->_eventHandlerCount--;
143
		}
144
		return $this->weakRemove($object);
0 ignored issues
show
Bug introduced by
It seems like $object can also be of type null; however, parameter $object of Prado\Collections\TWeakList::weakRemove() does only seem to accept object, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

144
		return $this->weakRemove(/** @scrutinizer ignore-type */ $object);
Loading history...
145
	}
146
147
	/**
148
	 * Converts the $item callable that has WeakReference rather than the actual object
149
	 * back into a regular callable.
150
	 * @param mixed &$item
151
	 */
152
	public static function filterItemForOutput(&$item): void
153
	{
154
		if (is_array($item)) {
155
			foreach (array_keys($item) as $key) {
156
				static::filterItemForOutput($item[$key]);
157
			}
158
		} elseif ($item instanceof Traversable && $item instanceof ArrayAccess) {
159
			foreach ($item as $key => $element) {
160
				static::filterItemForOutput($element);
161
				$item[$key] = $element;
162
			}
163
		} elseif (is_object($item)) {
164
			if ($item instanceof WeakReference) {
165
				$item = $item->get();
166
			} elseif (($item instanceof TEventHandler) && !$item->hasHandler()) {
167
				$item = null;
168
			}
169
		}
170
	}
171
172
	/**
173
	 * Converts the $item object and objects in an array into their WeakReference version
174
	 * for storage.  Closure[s] are not converted into WeakReference and so act like a
175
	 * basic PHP type.  Closures are added to the the WeakMap cache but has no weak
176
	 * effect because the TWeakList maintains references to Closure[s] preventing their
177
	 * invalidation.
178
	 * @param mixed &$item object to convert into a WeakReference where needed.
179
	 */
180
	public static function filterItemForInput(&$item): void
181
	{
182
		if (is_array($item)) {
183
			foreach (array_keys($item) as $key) {
184
				static::filterItemForInput($item[$key]);
185
			}
186
		} elseif ($item instanceof Traversable && $item instanceof ArrayAccess) {
187
			foreach ($item as $key => $element) {
188
				static::filterItemForInput($element);
189
				$item[$key] = $element;
190
			}
191
		} elseif (is_object($item) && !($item instanceof WeakReference) && !($item instanceof Closure) && !($item instanceof IWeakRetainable)) {
192
			$item = WeakReference::create($item);
193
		}
194
	}
195
196
	/**
197
	 * When a change in the WeakMap is detected, scrub the list of invalid WeakReference.
198
	 */
199
	protected function scrubWeakReferences(): void
200
	{
201
		if (!$this->getDiscardInvalid() || !$this->weakChanged()) {
202
			return;
203
		}
204
		for ($i = $this->_c - 1; $i >= 0; $i--) {
205
			if (is_object($this->_d[$i])) {
206
				$object = $this->_d[$i];
207
				if ($isEventHandler = ($object instanceof TEventHandler)) {
208
					$object = $object->getHandlerObject(true);
209
				}
210
				if (($object instanceof WeakReference) && $object->get() === null) {
211
					$this->_c--;
212
					if ($i === $this->_c) {
213
						array_pop($this->_d);
214
					} else {
215
						array_splice($this->_d, $i, 1);
216
					}
217
					if ($isEventHandler) {
218
						$this->_eventHandlerCount--;
219
					}
220
				}
221
			}
222
		}
223
		$this->weakResetCount();
224
	}
225
226
	/**
227
	 * @return bool Does the TWeakList scrub invalid WeakReference.
228
	 */
229
	public function getDiscardInvalid(): bool
230
	{
231
		$this->collapseDiscardInvalid();
232
		return $this->_discardInvalid;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_discardInvalid could return the type null which is incompatible with the type-hinted return boolean. Consider adding an additional type-check to rule them out.
Loading history...
233
	}
234
235
	/**
236
	 * Ensures that DiscardInvalid is set.
237
	 */
238
	protected function collapseDiscardInvalid()
239
	{
240
		if ($this->_discardInvalid === null) {
241
			$this->setDiscardInvalid(!$this->getReadOnly());
242
		}
243
	}
244
245
	/**
246
	 * @param bool $value Sets the TWeakList scrubbing of invalid WeakReference.
247
	 */
248
	public function setDiscardInvalid($value): void
249
	{
250
		if ($value === $this->_discardInvalid) {
251
			return;
252
		}
253
		if ($this->_discardInvalid !== null && !Prado::isCallingSelf()) {
254
			throw new TInvalidOperationException('weak_no_set_discard_invalid', $this::class);
255
		}
256
		$value = TPropertyValue::ensureBoolean($value);
257
		if ($value && !$this->_discardInvalid) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_discardInvalid of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
258
			$this->weakStart();
259
			for ($i = $this->_c - 1; $i >= 0; $i--) {
260
				if (is_object($this->_d[$i])) {
261
					$object = $this->_d[$i];
262
					if ($isEventHandler = ($object instanceof TEventHandler)) {
263
						$object = $object->getHandlerObject(true);
264
					}
265
					if ($object instanceof WeakReference) {
266
						$object = $object->get();
267
					}
268
					if ($object === null) {
269
						$this->_c--;	//on read only, parent::removeAt won't remove for scrub.
270
						if ($i === $this->_c) {
271
							array_pop($this->_d);
272
						} else {
273
							array_splice($this->_d, $i, 1);
274
						}
275
						if ($isEventHandler) {
276
							$this->_eventHandlerCount--;
277
						}
278
					} else {
279
						$this->weakAdd($object);
280
					}
281
				}
282
			}
283
		} elseif (!$value && $this->_discardInvalid) {
284
			$this->weakStop();
285
		}
286
		$this->_discardInvalid = $value;
287
	}
288
289
	/**
290
	 * Returns an iterator for traversing the items in the list.
291
	 * This method is required by the interface \IteratorAggregate.
292
	 * All invalid WeakReference[s] are optionally removed from the iterated list.
293
	 * @return \Iterator an iterator for traversing the items in the list.
294
	 */
295
	public function getIterator(): \Iterator
296
	{
297
		return new \ArrayIterator($this->toArray());
298
	}
299
300
	/**
301
	 * All invalid WeakReference[s] are optionally removed from the list before counting.
302
	 * @return int the number of items in the list
303
	 */
304
	public function getCount(): int
305
	{
306
		$this->scrubWeakReferences();
307
		return parent::getCount();
308
	}
309
310
	/**
311
	 * Returns the item at the specified offset.
312
	 * This method is exactly the same as {@see offsetGet}.
313
	 * All invalid WeakReference[s] are optionally removed from the list before indexing.
314
	 * @param int $index the index of the item
315
	 * @throws TInvalidDataValueException if the index is out of the range
316
	 * @return mixed the item at the index
317
	 */
318
	public function itemAt($index)
319
	{
320
		$this->scrubWeakReferences();
321
		$item = parent::itemAt($index);
322
		$this->filterItemForOutput($item);
323
		return $item;
324
	}
325
326
	/**
327
	 * Appends an item at the end of the list.
328
	 * All invalid WeakReference[s] are optionally removed from the list before adding
329
	 * for proper indexing.
330
	 * @param mixed $item new item
331
	 * @throws TInvalidOperationException if the list is read-only
332
	 * @return int the zero-based index at which the item is added
333
	 */
334
	public function add($item)
335
	{
336
		$this->collapseDiscardInvalid();
337
		$this->scrubWeakReferences();
338
		if (is_object($item)) {
339
			$this->weakCustomAdd($item);
340
		}
341
		$this->filterItemForInput($item);
342
		parent::insertAt($this->_c, $item);
343
		return $this->_c - 1;
344
	}
345
346
	/**
347
	 * Inserts an item at the specified position.
348
	 * Original item at the position and the next items
349
	 * will be moved one step towards the end.
350
	 * All invalid WeakReference[s] are optionally removed from the list before indexing.
351
	 * @param int $index the specified position.
352
	 * @param mixed $item new item
353
	 * @throws TInvalidDataValueException If the index specified exceeds the bound
354
	 * @throws TInvalidOperationException if the list is read-only
355
	 */
356
	public function insertAt($index, $item)
357
	{
358
		$this->collapseDiscardInvalid();
359
		$this->scrubWeakReferences();
360
		if (is_object($item)) {
361
			$this->weakCustomAdd($item);
362
		}
363
		$this->filterItemForInput($item);
364
		parent::insertAt($index, $item);
365
	}
366
367
	/**
368
	 * Removes an item from the list.
369
	 * The list will first search for the item.
370
	 * The first item found will be removed from the list.
371
	 * All invalid WeakReference[s] are optionally removed from the list before indexing.
372
	 * @param mixed $item the item to be removed.
373
	 * @throws TInvalidDataValueException If the item does not exist
374
	 * @throws TInvalidOperationException if the list is read-only
375
	 * @return int the index at which the item is being removed
376
	 */
377
	public function remove($item)
378
	{
379
		if (!$this->getReadOnly()) {
380
			if (($index = $this->indexOf($item)) !== -1) {
381
				if (is_object($item)) {
382
					$this->weakCustomRemove($item);
383
				}
384
				parent::removeAt($index);
385
				return $index;
386
			} else {
387
				throw new TInvalidDataValueException('list_item_inexistent');
388
			}
389
		} else {
390
			throw new TInvalidOperationException('list_readonly', get_class($this));
391
		}
392
	}
393
394
	/**
395
	 * Removes an item at the specified position.
396
	 * All invalid WeakReference[s] are optionally removed from the list before indexing.
397
	 * @param int $index the index of the item to be removed.
398
	 * @throws TInvalidDataValueException If the index specified exceeds the bound
399
	 * @throws TInvalidOperationException if the list is read-only
400
	 * @return mixed the removed item.
401
	 */
402
	public function removeAt($index)
403
	{
404
		$this->scrubWeakReferences();
405
		$item = parent::removeAt($index);
406
		$this->filterItemForOutput($item);
407
		if (is_object($item)) {
408
			$this->weakCustomRemove($item);
409
		}
410
		return $item;
411
	}
412
413
	/**
414
	 * Removes all items in the list and resets the Weak Cache.
415
	 * @throws TInvalidOperationException if the list is read-only
416
	 */
417
	public function clear(): void
418
	{
419
		$c = $this->_c;
420
		for ($i = $this->_c - 1; $i >= 0; --$i) {
421
			parent::removeAt($i);
422
		}
423
		if ($c) {
424
			$this->weakRestart();
425
		}
426
	}
427
428
	/**
429
	 * @param mixed $item the item
430
	 * @return bool whether the list contains the item
431
	 */
432
	public function contains($item): bool
433
	{
434
		return $this->indexOf($item) !== -1;
435
	}
436
437
	/**
438
	 * All invalid WeakReference[s] are optionally removed from the list before indexing.
439
	 * @param mixed $item the item
440
	 * @return int the index of the item in the list (0 based), -1 if not found.
441
	 */
442
	public function indexOf($item)
443
	{
444
		$this->scrubWeakReferences();
445
		$this->filterItemForInput($item);
446
		if (($index = parent::indexOf($item)) === -1 && $this->_eventHandlerCount) {
447
			$index = false;
448
			foreach ($this->_d as $index => $dItem) {
449
				if (($dItem instanceof TEventHandler) && $dItem->isSameHandler($item, true)) {
450
					break;
451
				}
452
				$index = false;
453
			}
454
		}
455
		if ($index === false) {
456
			return -1;
457
		} else {
458
			return $index;
459
		}
460
	}
461
462
	/**
463
	 * Finds the base item.  If found, the item is inserted before it.
464
	 * All invalid WeakReference[s] are optionally removed from the list before indexing.
465
	 * @param mixed $baseitem the base item which will be pushed back by the second parameter
466
	 * @param mixed $item the item
467
	 * @throws TInvalidDataValueException if the base item is not within this list
468
	 * @throws TInvalidOperationException if the list is read-only
469
	 * @return int the index where the item is inserted
470
	 */
471
	public function insertBefore($baseitem, $item)
472
	{
473
		if (!$this->getReadOnly()) {
474
			if (($index = $this->indexOf($baseitem)) === -1) {
475
				throw new TInvalidDataValueException('list_item_inexistent');
476
			}
477
			if (is_object($item)) {
478
				$this->weakCustomAdd($item);
479
			}
480
			$this->filterItemForInput($item);
481
			parent::insertAt($index, $item);
482
			return $index;
483
		} else {
484
			throw new TInvalidOperationException('list_readonly', get_class($this));
485
		}
486
	}
487
488
	/**
489
	 * Finds the base item.  If found, the item is inserted after it.
490
	 * All invalid WeakReference[s] are optionally removed from the list before indexing.
491
	 * @param mixed $baseitem the base item which comes before the second parameter when added to the list
492
	 * @param mixed $item the item
493
	 * @throws TInvalidDataValueException if the base item is not within this list
494
	 * @throws TInvalidOperationException if the list is read-only
495
	 * @return int the index where the item is inserted
496
	 */
497
	public function insertAfter($baseitem, $item)
498
	{
499
		if (!$this->getReadOnly()) {
500
			if (($index = $this->indexOf($baseitem)) === -1) {
501
				throw new TInvalidDataValueException('list_item_inexistent');
502
			}
503
			if (is_object($item)) {
504
				$this->weakCustomAdd($item);
505
			}
506
			$this->filterItemForInput($item);
507
			parent::insertAt($index + 1, $item);
508
			return $index + 1;
509
		} else {
510
			throw new TInvalidOperationException('list_readonly', get_class($this));
511
		}
512
	}
513
514
	/**
515
	 * All invalid WeakReference[s] are optionally removed from the list.
516
	 * @return array the list of items in array
517
	 */
518
	public function toArray(): array
519
	{
520
		$this->scrubWeakReferences();
521
		$items = $this->_d;
522
		$this->filterItemForOutput($items);
523
		return $items;
524
	}
525
526
	/**
527
	 * Copies iterable data into the list.
528
	 * Note, existing data in the list will be cleared first.
529
	 * @param mixed $data the data to be copied from, must be an array or object implementing Traversable
530
	 * @throws TInvalidDataTypeException If data is neither an array nor a Traversable.
531
	 */
532
	public function copyFrom($data): void
533
	{
534
		if (is_array($data) || ($data instanceof Traversable)) {
535
			if ($this->_c > 0) {
536
				$this->clear();
537
			}
538
			foreach ($data as $item) {
539
				if (is_object($item)) {
540
					$this->weakCustomAdd($item);
541
				}
542
				$this->filterItemForInput($item);
543
				parent::insertAt($this->_c, $item);
544
			}
545
		} elseif ($data !== null) {
546
			throw new TInvalidDataTypeException('list_data_not_iterable');
547
		}
548
	}
549
550
	/**
551
	 * Merges iterable data into the map.
552
	 * New data will be appended to the end of the existing data.
553
	 * @param mixed $data the data to be merged with, must be an array or object implementing Traversable
554
	 * @throws TInvalidDataTypeException If data is neither an array nor an iterator.
555
	 */
556
	public function mergeWith($data): void
557
	{
558
		if (is_array($data) || ($data instanceof Traversable)) {
559
			foreach ($data as $item) {
560
				if (is_object($item)) {
561
					$this->weakCustomAdd($item);
562
				}
563
				$this->filterItemForInput($item);
564
				parent::insertAt($this->_c, $item);
565
			}
566
		} elseif ($data !== null) {
567
			throw new TInvalidDataTypeException('list_data_not_iterable');
568
		}
569
	}
570
571
	/**
572
	 * Returns whether there is an item at the specified offset.
573
	 * This method is required by the interface \ArrayAccess.
574
	 * All invalid WeakReference[s] are optionally removed from the list before indexing.
575
	 * @param int $offset the offset to check on
576
	 * @return bool
577
	 */
578
	public function offsetExists($offset): bool
579
	{
580
		return ($offset >= 0 && $offset < $this->getCount());
581
	}
582
583
	/**
584
	 * Sets the item at the specified offset.
585
	 * This method is required by the interface \ArrayAccess.
586
	 * All invalid WeakReference[s] are optionally removed from the list before indexing.
587
	 * @param int $offset the offset to set item
588
	 * @param mixed $item the item value
589
	 */
590
	public function offsetSet($offset, $item): void
591
	{
592
		$this->scrubWeakReferences();
593
		if ($offset === null || $offset === $this->_c) {
594
			if (is_object($item)) {
595
				$this->weakCustomAdd($item);
596
			}
597
			$this->filterItemForInput($item);
598
			parent::insertAt($this->_c, $item);
599
		} else {
600
			$removed = parent::removeAt($offset);
601
			$this->filterItemForOutput($removed);
602
			if (is_object($removed)) {
603
				$this->weakCustomRemove($removed);
604
			}
605
			if (is_object($item)) {
606
				$this->weakCustomAdd($item);
607
			}
608
			$this->filterItemForInput($item);
609
			parent::insertAt($offset, $item);
610
		}
611
	}
612
613
	/**
614
	 * Returns an array with the names of all variables of this object that should
615
	 * NOT be serialized because their value is the default one or useless to be cached
616
	 * for the next page loads.  Reimplement in derived classes to add new variables,
617
	 * but remember to  also to call the parent implementation first.
618
	 * Due to being weak, the TWeakList is not serialized.  The count is artificially
619
	 * made zero so the parent has no values to save.
620
	 * @param array $exprops by reference
621
	 */
622
	protected function _getZappableSleepProps(&$exprops)
623
	{
624
		$c = $this->_c;
625
		$this->_c = 0;
626
		parent::_getZappableSleepProps($exprops);
627
		$this->_c = $c;
628
629
		$this->_weakZappableSleepProps($exprops);
630
		if ($this->_discardInvalid === null) {
631
			$exprops[] = "\0" . __CLASS__ . "\0_discardInvalid";
632
		}
633
	}
634
}
635