Passed
Push — master ( e1e446...1b5c12 )
by Fabio
06:00
created

TBehavior::events()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
/**
3
 * TBehavior class file.
4
 *
5
 * @author Qiang Xue <[email protected]>
6
 * @link https://github.com/pradosoft/prado
7
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
8
 */
9
10
namespace Prado\Util;
11
12
use Prado\Exceptions\TInvalidOperationException;
13
use Prado\TComponent;
14
use Prado\TPropertyValue;
15
use Prado\Util\TCallChain;
16
17
use WeakReference;
18
19
/**
20
 * TBehavior is the base class for behaviors that extend owner components with new
21
 * state information, properties, run time methods, and process modification.  Each
22
 * instance of the TBehavior can only have one owner.  This tracks behavior event
23
 * handlers attachment status on its owner.
24
 *
25
 * TBehavior is one of two types of behaviors in PRADO. The other type is the
26
 * {@link TClassBehavior} where each instance can have multiple owners and is
27
 * is designed to not retain per object information (acting stateless).
28
 *
29
 * Behaviors can be attached to instanced objects, with {@link TComponent::attachbehavior},
30
 * or to each class, the parent classes, interfaces, and first level traits used by
31 26
 * the class(es) with {@link TComponent::attachClassBehavior}. Class-wide behaviors
32
 * cannot be attached to the root class {@link TComponent} but can attach to any subclass.
33 26
 * All new components with an attached class behavior will receive the behavior on
34
 * instancing.  Instances of a class will receive the class behavior if they are
35
 * {@link TComponent::listen}ing.
36
 *
37
 * TComponent clones instanced IBehavior when applied to whole classes.
38
 *
39
 * TBehavior is a subclass of {@link TBaseBehavior} that implements the core
40
 * behavior functionality.  See {@link IBehavior} for implementation details.
41
 *
42
 * @author Brad Anderson <[email protected]>
43 26
 * @since 3.2.3
44
 */
45 26
class TBehavior extends TBaseBehavior implements IBehavior
46 26
{
47
	/** @var ?WeakReference The owner component of the behavior. Default: null */
48
	private ?WeakReference $_owner = null;
49 26
50
	/** @var bool Are the handlers installed on the owner. Default: false */
51
	private bool $_handlersInstalled = false;
52
53
	/**
54
	 * This resets the owner and _handlersInstalled flag on cloning.
55
	 * @since 4.2.3
56
	 */
57
	public function __clone()
58 16
	{
59
		$this->_owner = null;
60 16
		$this->_handlersInstalled = false;
61
		parent::__clone();
62
	}
63 16
64 16
	/**
65
	 * Attaches the behavior object to the new owner component.  This is normally called
66
	 * by the new owner when attaching a behavior, by {@link TComponent::attachBehavior},
67
	 * and not directly called.
68
	 *
69 2
	 * The default implementation will set the {@link getOwner Owner} property and
70
	 * synchronized the behavior event handlers (cached in {@link eventsLog}) with
71 2
	 * the owner.  Make sure you call the parent implementation if you override this method.
72
	 * @param TComponent $owner The owner component that this behavior is being attached.
73
	 * @throws TInvalidOperationException When attaching a behavior that is already
74
	 *   attached to an owner or the owner isn't a TComponent.
75
	 */
76
	public function attach($owner)
77 24
	{
78
		if ($this->_owner !== null) {
79 24
			throw new TInvalidOperationException('behavior_has_owner', $this->getName());
80
		}
81
		if (!($owner instanceof TComponent)) {
0 ignored issues
show
introduced by
$owner is always a sub-type of Prado\TComponent.
Loading history...
82
			throw new TInvalidOperationException('behavior_bad_attach_component', $this->getName());
83
		}
84
		$this->_owner = WeakReference::create($owner);
85 26
		parent::attach($owner);
86
	}
87 26
88 26
	/**
89
	 * Detaches the behavior object from the owner component.  This is normally called
90
	 * by the owner when detaching a behavior, by {@link TComponent::detachBehavior},
91
	 * and not directly called.
92
	 *
93
	 * The default implementation detaches the behavior event handlers (cached in {@link
94
	 * eventsLog}) and resets the owner. Make sure you call this parent implementation
95
	 * if you override this method.
96
	 * @param TComponent $owner The component this behavior is being detached from.
97
	 * @throws TInvalidOperationException When detaching without an owner or from a
98
	 *   component that isn't the behaviors owner.
99
	 */
100
	public function detach($owner)
101
	{
102
		if ($this->_owner === null) {
103
			throw new TInvalidOperationException('behavior_detach_without_owner', $this->getName());
104
		}
105
		if (!$owner || $this->getOwner() !== $owner) {
0 ignored issues
show
introduced by
$owner is of type Prado\TComponent, thus it always evaluated to true.
Loading history...
106
			throw new TInvalidOperationException('behavior_detach_wrong_owner', $this->getName());
107
		}
108
		parent::detach($owner);
109
		$this->_owner = null;
110
		$this->_name = null;
111
	}
112
113
	/**
114
	 * This class stores the owner as a WeakReference.  This method retrieves and re-references
115
	 * the owner.
116
	 * @return ?object The owner component that this behavior is attached to.
117
	 *   Default null before the behavior is attached to an owner.
118
	 */
119
	public function getOwner(): ?object
120
	{
121
		if ($this->_owner) {
122
			return $this->_owner->get();
123
		}
124
		return null;
125
	}
126
127
	/**
128
	 * This returns an array of the one owner that the TBehavior is attached to.
129
	 * TClassBehavior could return multiple owners, but this type of behavior only
130
	 *  has one.
131
	 * @return array An array of the owner component.
132
	 * @since 4.2.3
133
	 */
134
	public function getOwners(): array
135
	{
136
		if ($owner = $this->getOwner()) {
137
			return [$owner];
138
		}
139
		return [];
140
	}
141
142
	/**
143
	 * @return bool Is the behavior attached to an owner.
144
	 * @since 4.2.3
145
	 */
146
	public function hasOwner(): bool
147
	{
148
		return $this->_owner !== null;
149
	}
150
151
	/**
152
	 * @param object $component The object to check if its the owner of the behavior.
153
	 * @return bool Is the object an owner of the behavior.
154
	 * @since 4.2.3
155
	 */
156
	public function isOwner(object $component): bool
157
	{
158
		return $this->_owner && $this->getOwner() === $component;
159
	}
160
161
	/**
162
	 * This dynamic event method tracks the "behaviors enabled" status of an owner. Subclasses
163
	 * can call this method (as "parent::dyEnableBehaviors()") without the $chain and it will
164
	 * delegate the $chain continuation to the subclass implementation. At this point,
165
	 * the behaviors are already enabled in the owner.
166
	 * @param ?TCallChain $chain The chain of dynamic events being raised.  Default
167
	 *   is null for no chain continuation.
168
	 * @since 4.2.3
169
	 */
170
	public function dyEnableBehaviors(?TCallChain $chain = null)
171
	{
172
		$this->syncEventHandlers();
173
		if ($chain) {
174
			$chain->dyEnableBehaviors();
175
		}
176
	}
177
178
	/**
179
	 * This dynamic event method tracks the "behaviors disabled" status of an owner. Subclasses
180
	 * can call this parent method (as "parent::dyDisableBehaviors()") without the $chain
181
	 * and it will delegate the $chain continuation to the subclass implementation.
182
	 * @param ?TCallChain $chain The chain of dynamic events being raised.  Default
183
	 *   is null for no chain continuation.
184
	 * @since 4.2.3
185
	 */
186
	public function dyDisableBehaviors(?TCallChain $chain = null)
187
	{
188
		$this->syncEventHandlers();
189
		if ($chain) {
190
			$chain->dyDisableBehaviors();
191
		}
192
	}
193
194
	/**
195
	 * This gets the attachment status of the behavior event handlers on the owner
196
	 * component.
197
	 * @param ?TComponent $component The component to check the attachment status.
198
	 *   Default null for the IBehavior owner status.  This must match the owner if/when
199
	 *   provided.
200
	 * @return bool Are the behavior handlers attached to the owner events.
201
	 * @since 4.2.3
202
	 */
203
	protected function getHandlersStatus(?TComponent $component = null): ?bool
204
	{
205
		if ($component && $this->getOwner() !== $component) {
206
			return null;
207
		}
208
		return $this->_handlersInstalled;
209
	}
210
211
	/**
212
	 * This sets the attachment status of the behavior handlers on the owner. It only
213
	 * returns true when there is a change in status.
214
	 * @param TComponent $component The owner of the behavior.
215
	 * @param bool $attach "true" to attach the handlers or "false" to detach.
216
	 * @return bool Is there a change in the attachment status on the owner.
217
	 * @since 4.2.3
218
	 */
219
	protected function setHandlersStatus(TComponent $component, bool $attach): bool
220
	{
221
		if ($this->getOwner() !== $component) {
222
			return false;
223
		}
224
		if($attach ^ $this->_handlersInstalled) {
225
			$this->_handlersInstalled = $attach;
226
			return true;
227
		}
228
		return false;
229
	}
230
231
	/**
232
	 * Returns an array with the names of all variables of this object that should
233
	 * NOT be serialized because their value is the default one or useless to be cached
234
	 * for the next page loads.  Reimplement in derived classes to add new variables,
235
	 * but remember to also to call the parent implementation first.
236
	 * @param array $exprops by reference
237
	 * @since 4.2.3
238
	 */
239
	protected function _getZappableSleepProps(&$exprops)
240
	{
241
		parent::_getZappableSleepProps($exprops);
242
		$exprops[] = "\0" . __CLASS__ . "\0_owner";
243
		$exprops[] = "\0" . __CLASS__ . "\0_handlersInstalled";
244
	}
245
}
246