|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* TComponent, TPropertyValue classes |
|
4
|
|
|
* |
|
5
|
|
|
* @author Qiang Xue <[email protected]> |
|
6
|
|
|
* |
|
7
|
|
|
* Global Events, intra-object events, Class behaviors, expanded behaviors |
|
8
|
|
|
* @author Brad Anderson <[email protected]> |
|
9
|
|
|
* |
|
10
|
|
|
* @link https://github.com/pradosoft/prado |
|
11
|
|
|
* @copyright Copyright © 2005-2016 The PRADO Group |
|
12
|
|
|
* @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT |
|
13
|
|
|
* @package System |
|
14
|
|
|
*/ |
|
15
|
|
|
|
|
16
|
|
|
/** |
|
17
|
|
|
* TComponent class |
|
18
|
|
|
* |
|
19
|
|
|
* TComponent is the base class for all PRADO components. |
|
20
|
|
|
* TComponent implements the protocol of defining, using properties, behaviors, |
|
21
|
|
|
* and events. |
|
22
|
|
|
* |
|
23
|
|
|
* A property is defined by a getter method, and/or a setter method. |
|
24
|
|
|
* Properties can be accessed in the way like accessing normal object members. |
|
25
|
|
|
* Reading or writing a property will cause the invocation of the corresponding |
|
26
|
|
|
* getter or setter method, e.g., |
|
27
|
|
|
* <code> |
|
28
|
|
|
* $a=$this->Text; // equivalent to $a=$this->getText(); |
|
29
|
|
|
* $this->Text='abc'; // equivalent to $this->setText('abc'); |
|
30
|
|
|
* </code> |
|
31
|
|
|
* The signatures of getter and setter methods are as follows, |
|
32
|
|
|
* <code> |
|
33
|
|
|
* // getter, defines a readable property 'Text' |
|
34
|
|
|
* function getText() { ... } |
|
35
|
|
|
* // setter, defines a writable property 'Text', with $value being the value to be set to the property |
|
36
|
|
|
* function setText($value) { ... } |
|
37
|
|
|
* </code> |
|
38
|
|
|
* Property names are case-insensitive. It is recommended that they are written |
|
39
|
|
|
* in the format of concatenated words, with the first letter of each word |
|
40
|
|
|
* capitalized (e.g. DisplayMode, ItemStyle). |
|
41
|
|
|
* |
|
42
|
|
|
* Javascript Get and Set |
|
43
|
|
|
* |
|
44
|
|
|
* Since Prado 3.2 a new class of javascript-friendly properties have been introduced |
|
45
|
|
|
* to better deal with potential security problems like cross-site scripting issues. |
|
46
|
|
|
* All the data that gets sent clientside inside a javascript block is now encoded by default. |
|
47
|
|
|
* Sometimes there's the need to bypass this encoding and be able to send raw javascript code. |
|
48
|
|
|
* This new class of javascript-friendly properties are identified by their name |
|
49
|
|
|
* starting with 'js' (case insensitive): |
|
50
|
|
|
* <code> |
|
51
|
|
|
* // getter, defines a readable property 'Text' |
|
52
|
|
|
* function getJsText() { ... } |
|
53
|
|
|
* // setter, defines a writable property 'Text', with $value being the value to be set to the property |
|
54
|
|
|
* function setJsText(TJavaScriptLiteral $value) { ... } |
|
55
|
|
|
* </code> |
|
56
|
|
|
* Js-friendly properties can be accessed using both their Js-less name and their Js-enabled name: |
|
57
|
|
|
* <code> |
|
58
|
|
|
* // set some simple text as property value |
|
59
|
|
|
* $component->Text = 'text'; |
|
60
|
|
|
* // set some javascript code as property value |
|
61
|
|
|
* $component->JsText = 'raw javascript'; |
|
62
|
|
|
* </code> |
|
63
|
|
|
* In the first case, the property value will automatically gets encoded when sent |
|
64
|
|
|
* clientside inside a javascript block. |
|
65
|
|
|
* In the second case, the property will be 'marked' as being a safe javascript |
|
66
|
|
|
* statement and will not be encoded when rendered inside a javascript block. |
|
67
|
|
|
* This special handling makes use of the {@link TJavaScriptLiteral} class. |
|
68
|
|
|
* |
|
69
|
|
|
* Events |
|
70
|
|
|
* |
|
71
|
|
|
* An event is defined by the presence of a method whose name starts with 'on'. |
|
72
|
|
|
* The event name is the method name and is thus case-insensitive. |
|
73
|
|
|
* An event can be attached with one or several methods (called event handlers). |
|
74
|
|
|
* An event can be raised by calling {@link raiseEvent} method, upon which |
|
75
|
|
|
* the attached event handlers will be invoked automatically in the order they |
|
76
|
|
|
* are attached to the event. Event handlers must have the following signature, |
|
77
|
|
|
* <code> |
|
78
|
|
|
* function eventHandlerFuncName($sender,$param) { ... } |
|
79
|
|
|
* </code> |
|
80
|
|
|
* where $sender refers to the object who is responsible for the raising of the event, |
|
81
|
|
|
* and $param refers to a structure that may contain event-specific information. |
|
82
|
|
|
* To raise an event (assuming named as 'Click') of a component, use |
|
83
|
|
|
* <code> |
|
84
|
|
|
* $component->raiseEvent('OnClick'); |
|
85
|
|
|
* $component->raiseEvent('OnClick', $this, $param); |
|
86
|
|
|
* </code> |
|
87
|
|
|
* To attach an event handler to an event, use one of the following ways, |
|
88
|
|
|
* <code> |
|
89
|
|
|
* $component->OnClick=$callback; // or $component->OnClick->add($callback); |
|
90
|
|
|
* $component->attachEventHandler('OnClick',$callback); |
|
91
|
|
|
* </code> |
|
92
|
|
|
* The first two ways make use of the fact that $component->OnClick refers to |
|
93
|
|
|
* the event handler list {@link TPriorityList} for the 'OnClick' event. |
|
94
|
|
|
* The variable $callback contains the definition of the event handler that can |
|
95
|
|
|
* be either a string referring to a global function name, or an array whose |
|
96
|
|
|
* first element refers to an object and second element a method name/path that |
|
97
|
|
|
* is reachable by the object, e.g. |
|
98
|
|
|
* - 'buttonClicked' : buttonClicked($sender,$param); |
|
99
|
|
|
* - array($object,'buttonClicked') : $object->buttonClicked($sender,$param); |
|
100
|
|
|
* - array($object,'MainContent.SubmitButton.buttonClicked') : |
|
101
|
|
|
* $object->MainContent->SubmitButton->buttonClicked($sender,$param); |
|
102
|
|
|
* |
|
103
|
|
|
* With the addition of behaviors, a more expansive event model is needed. There |
|
104
|
|
|
* are two new event types (global and dynamic events) as well as a more comprehensive |
|
105
|
|
|
* behavior model that includes class wide behaviors. |
|
106
|
|
|
* |
|
107
|
|
|
* A global event is defined by all events whose name starts with 'fx'. |
|
108
|
|
|
* The event name is potentially a method name and is thus case-insensitive. All 'fx' events |
|
109
|
|
|
* are valid as the whole 'fx' event/method space is global in nature. Any object may patch into |
|
110
|
|
|
* any global event by defining that event as a method. Global events have priorities |
|
111
|
|
|
* just like 'on' events; so as to be able to order the event execution. Due to the |
|
112
|
|
|
* nature of all events which start with 'fx' being valid, in effect, every object |
|
113
|
|
|
* has every 'fx' global event. It is simply an issue of tapping into the desired |
|
114
|
|
|
* global event. |
|
115
|
|
|
* |
|
116
|
|
|
* A global event that starts with 'fx' can be called even if the object does not |
|
117
|
|
|
* implement the method of the global event. A call to a non-existing 'fx' method |
|
118
|
|
|
* will, at minimal, function and return null. If a method argument list has a first |
|
119
|
|
|
* parameter, it will be returned instead of null. This allows filtering and chaining. |
|
120
|
|
|
* 'fx' methods do not automatically install and uninstall. To install and uninstall an |
|
121
|
|
|
* object's global event listeners, call the object's {@link listen} and |
|
122
|
|
|
* {@link unlisten} methods, respectively. An object may auto-install its global event |
|
123
|
|
|
* during {@link __construct} by overriding {@link getAutoGlobalListen} and returning true. |
|
124
|
|
|
* |
|
125
|
|
|
* As of PHP version 5.3, nulled objects without code references will still continue to persist |
|
126
|
|
|
* in the global event queue because {@link __destruct} is not automatically called. In the common |
|
127
|
|
|
* __destruct method, if an object is listening to global events, then {@link unlisten} is called. |
|
128
|
|
|
* {@link unlisten} is required to be manually called before an object is |
|
129
|
|
|
* left without references if it is currently listening to any global events. This includes |
|
130
|
|
|
* class wide behaviors. |
|
131
|
|
|
* |
|
132
|
|
|
* An object that contains a method that starts with 'fx' will have those functions |
|
133
|
|
|
* automatically receive those events of the same name after {@link listen} is called on the object. |
|
134
|
|
|
* |
|
135
|
|
|
* An object may listen to a global event without defining an 'fx' method of the same name by |
|
136
|
|
|
* adding an object method to the global event list. For example |
|
137
|
|
|
* <code> |
|
138
|
|
|
* $component->fxGlobalCheck=$callback; // or $component->OnClick->add($callback); |
|
139
|
|
|
* $component->attachEventHandler('fxGlobalCheck',array($object, 'someMethod')); |
|
140
|
|
|
* </code> |
|
141
|
|
|
* |
|
142
|
|
|
* Events between Objects and their behaviors, Dynamic Events |
|
143
|
|
|
* |
|
144
|
|
|
* An intra-object/behavior event is defined by methods that start with 'dy'. Just as with |
|
145
|
|
|
* 'fx' global events, every object has every dynamic event. Any call to a method that |
|
146
|
|
|
* starts with 'dy' will be handled, regardless of whether it is implemented. These |
|
147
|
|
|
* events are for communicating with attached behaviors. |
|
148
|
|
|
* |
|
149
|
|
|
* Dynamic events can be used in a variety of ways. They can be used to tell behaviors |
|
150
|
|
|
* when a non-behavior method is called. Dynamic events could be used as data filters. |
|
151
|
|
|
* They could also be used to specify when a piece of code is to be run, eg. should the |
|
152
|
|
|
* loop process be performed on a particular piece of data. In this way, some control |
|
153
|
|
|
* is handed to the behaviors over the process and/or data. |
|
154
|
|
|
* |
|
155
|
|
|
* If there are no handlers for an 'fx' or 'dy' event, it will return the first |
|
156
|
|
|
* parameter of the argument list. If there are no arguments, these events |
|
157
|
|
|
* will return null. If there are handlers an 'fx' method will be called directly |
|
158
|
|
|
* within the object. Global 'fx' events are triggered by calling {@link raiseEvent}. |
|
159
|
|
|
* For dynamic events where there are behaviors that respond to the dynamic events, a |
|
160
|
|
|
* {@link TCallChain} is developed. A call chain allows the behavior dynamic event |
|
161
|
|
|
* implementations to call further implementing behaviors within a chain. |
|
162
|
|
|
* |
|
163
|
|
|
* If an object implements {@link IDynamicMethods}, all global and object dynamic |
|
164
|
|
|
* events will be sent to {@link __dycall}. In the case of global events, all |
|
165
|
|
|
* global events will trigger this method. In the case of behaviors, all undefined |
|
166
|
|
|
* dynamic events which are called will be passed through to this method. |
|
167
|
|
|
* |
|
168
|
|
|
* |
|
169
|
|
|
* Behaviors |
|
170
|
|
|
* |
|
171
|
|
|
* There are two types of behaviors. There are individual object behaviors and |
|
172
|
|
|
* there are class wide behaviors. Class behaviors depend upon object behaviors. |
|
173
|
|
|
* |
|
174
|
|
|
* When a new class implements {@link IBehavior} or {@link IClassBehavior} or |
|
175
|
|
|
* extends {@link TBehavior} or {@link TClassBehavior}, it may be added to an |
|
176
|
|
|
* object by calling the object's {@link attachBehavior}. The behaviors associated |
|
177
|
|
|
* name can then be used to {@link enableBehavior} or {@link disableBehavior} |
|
178
|
|
|
* the specific behavior. |
|
179
|
|
|
* |
|
180
|
|
|
* All behaviors may be turned on and off via {@link enableBehaviors} and |
|
181
|
|
|
* {@link disableBehaviors}, respectively. To check if behaviors are on or off |
|
182
|
|
|
* a call to {@link getBehaviorsEnabled} will provide the variable. |
|
183
|
|
|
* |
|
184
|
|
|
* Attaching and detaching whole sets of behaviors is done using |
|
185
|
|
|
* {@link attachBehaviors} and {@link detachBehaviors}. {@link clearBehaviors} |
|
186
|
|
|
* removes all of an object's behaviors. |
|
187
|
|
|
* |
|
188
|
|
|
* {@link asa} returns a behavior of a specific name. {@link isa} is the |
|
189
|
|
|
* behavior inclusive function that acts as the PHP operator {@link instanceof}. |
|
190
|
|
|
* A behavior could provide the functionality of a specific class thus causing |
|
191
|
|
|
* the host object to act similarly to a completely different class. A behavior |
|
192
|
|
|
* would then implement {@link IInstanceCheck} to provide the identity of the |
|
193
|
|
|
* different class. |
|
194
|
|
|
* |
|
195
|
|
|
* Class behaviors are similar to object behaviors except that the class behavior |
|
196
|
|
|
* is the implementation for all instances of the class. A class behavior |
|
197
|
|
|
* will have the object upon which is being called be prepended to the parameter |
|
198
|
|
|
* list. This way the object is known across the class behavior implementation. |
|
199
|
|
|
* |
|
200
|
|
|
* Class behaviors are attached using {@link attachClassBehavior} and detached |
|
201
|
|
|
* using {@link detachClassBehavior}. Class behaviors are important in that |
|
202
|
|
|
* they will be applied to all new instances of a particular class. In this way |
|
203
|
|
|
* class behaviors become default behaviors to a new instances of a class in |
|
204
|
|
|
* {@link __construct}. Detaching a class behavior will remove the behavior |
|
205
|
|
|
* from the default set of behaviors created for an object when the object |
|
206
|
|
|
* is instanced. |
|
207
|
|
|
* |
|
208
|
|
|
* Class behaviors are also added to all existing instances via the global 'fx' |
|
209
|
|
|
* event mechanism. When a new class behavior is added, the event |
|
210
|
|
|
* {@link fxAttachClassBehavior} is raised and all existing instances that are |
|
211
|
|
|
* listening to this global event (primarily after {@link listen} is called) |
|
212
|
|
|
* will have this new behavior attached. A similar process is used when |
|
213
|
|
|
* detaching class behaviors. Any objects listening to the global 'fx' event |
|
214
|
|
|
* {@link fxDetachClassBehavior} will have a class behavior removed. |
|
215
|
|
|
* |
|
216
|
|
|
* Dynamic Intra-Object Events |
|
217
|
|
|
* |
|
218
|
|
|
* Dynamic events start with 'dy'. This mechanism is used to allow objects |
|
219
|
|
|
* to communicate with their behaviors directly. The entire 'dy' event space |
|
220
|
|
|
* is valid. All attached, enabled behaviors that implement a dynamic event |
|
221
|
|
|
* are called when the host object calls the dynamic event. If there is no |
|
222
|
|
|
* implementation or behaviors, this returns null when no parameters are |
|
223
|
|
|
* supplied and will return the first parameter when there is at least one |
|
224
|
|
|
* parameter in the dynamic event. |
|
225
|
|
|
* <code> |
|
226
|
|
|
* null == $this->dyBehaviorEvent(); |
|
227
|
|
|
* 5 == $this->dyBehaviorEvent(5); //when no behaviors implement this dynamic event |
|
228
|
|
|
* </code> |
|
229
|
|
|
* |
|
230
|
|
|
* Dynamic events can be chained together within behaviors to allow for data |
|
231
|
|
|
* filtering. Dynamic events are implemented within behaviors by defining the |
|
232
|
|
|
* event as a method. |
|
233
|
|
|
* <code> |
|
234
|
|
|
* class TObjectBehavior extends TBehavior { |
|
235
|
|
|
* public function dyBehaviorEvent($param1, $callchain) { |
|
236
|
|
|
* //Do something, eg: $param1 += 13; |
|
237
|
|
|
* return $callchain->dyBehaviorEvent($param1); |
|
238
|
|
|
* } |
|
239
|
|
|
* } |
|
240
|
|
|
* </code> |
|
241
|
|
|
* This implementation of a behavior and dynamic event will flow through to the |
|
242
|
|
|
* next behavior implementing the dynamic event. The first parameter is always |
|
243
|
|
|
* return when it is supplied. Otherwise a dynamic event returns null. |
|
244
|
|
|
* |
|
245
|
|
|
* In the case of a class behavior, the object is also prepended to the dynamic |
|
246
|
|
|
* event. |
|
247
|
|
|
* <code> |
|
248
|
|
|
* class TObjectClassBehavior extends TClassBehavior { |
|
249
|
|
|
* public function dyBehaviorEvent($hostobject, $param1, $callchain) { |
|
250
|
|
|
* //Do something, eg: $param1 += $hostobject->getNumber(); |
|
251
|
|
|
* return $callchain->dyBehaviorEvent($param1); |
|
252
|
|
|
* } |
|
253
|
|
|
* } |
|
254
|
|
|
* </code> |
|
255
|
|
|
* When calling a dynamic event, only the parameters are passed. The host object |
|
256
|
|
|
* and the call chain are built into the framework. |
|
257
|
|
|
* |
|
258
|
|
|
* Global Event and Dynamic event catching |
|
259
|
|
|
* |
|
260
|
|
|
* Given that all global 'fx' events and dynamic 'dy' events are valid and |
|
261
|
|
|
* operational, there is a mechanism for catching events called that are not |
|
262
|
|
|
* implemented (similar to the built-in PHP method {@link __call}). When |
|
263
|
|
|
* a dynamic or global event is called but a behavior does not implement it, |
|
264
|
|
|
* yet desires to know when an undefined dynamic event is run, the behavior |
|
265
|
|
|
* implements the interface {@link IDynamicMethods} and method {@link __dycall}. |
|
266
|
|
|
* |
|
267
|
|
|
* In the case of dynamic events, {@link __dycall} is supplied with the method |
|
268
|
|
|
* name and its parameters. When a global event is raised, via {@link raiseEvent}, |
|
269
|
|
|
* the method is the event name and the parameters are supplied. |
|
270
|
|
|
* |
|
271
|
|
|
* When implemented, this catch-all mechanism is called for event global event event |
|
272
|
|
|
* when implemented outside of a behavior. Within a behavior, it will also be called |
|
273
|
|
|
* when the object to which the behavior is attached calls any unimplemented dynamic |
|
274
|
|
|
* event. This is the fall-back mechanism for informing a class and/or behavior |
|
275
|
|
|
* of when an global and/or undefined dynamic event is executed. |
|
276
|
|
|
* |
|
277
|
|
|
* @author Qiang Xue <[email protected]> |
|
278
|
|
|
* @author Brad Anderson <[email protected]> |
|
279
|
|
|
* @package System |
|
280
|
|
|
* @since 3.0 |
|
281
|
|
|
*/ |
|
282
|
|
|
class TComponent |
|
283
|
|
|
{ |
|
284
|
|
|
/** |
|
285
|
|
|
* @var array event handler lists |
|
286
|
|
|
*/ |
|
287
|
|
|
private $_e=array(); |
|
288
|
|
|
|
|
289
|
|
|
/** |
|
290
|
|
|
* @var boolean if listening is enabled. Automatically turned on or off in |
|
291
|
|
|
* constructor according to {@link getAutoGlobalListen}. Default false, off |
|
292
|
|
|
*/ |
|
293
|
|
|
private $_listeningenabled=false; |
|
294
|
|
|
|
|
295
|
|
|
/** |
|
296
|
|
|
* @var array static registered global event handler lists |
|
297
|
|
|
*/ |
|
298
|
|
|
private static $_ue=array(); |
|
299
|
|
|
|
|
300
|
|
|
/** |
|
301
|
|
|
* @var boolean if object behaviors are on or off. default true, on |
|
302
|
|
|
*/ |
|
303
|
|
|
private $_behaviorsenabled=true; |
|
304
|
|
|
|
|
305
|
|
|
/** |
|
306
|
|
|
* @var TPriorityMap list of object behaviors |
|
307
|
|
|
*/ |
|
308
|
|
|
private $_m=null; |
|
309
|
|
|
|
|
310
|
|
|
/** |
|
311
|
|
|
* @var array static global class behaviors, these behaviors are added upon instantiation of a class |
|
312
|
|
|
*/ |
|
313
|
|
|
private static $_um=array(); |
|
314
|
|
|
|
|
315
|
|
|
|
|
316
|
|
|
/** |
|
317
|
|
|
* @const string the name of the global {@link raiseEvent} listener |
|
318
|
|
|
*/ |
|
319
|
|
|
const GLOBAL_RAISE_EVENT_LISTENER='fxGlobalListener'; |
|
320
|
|
|
|
|
321
|
|
|
|
|
322
|
|
|
/** |
|
323
|
|
|
* The common __construct |
|
324
|
|
|
* If desired by the new object, this will auto install and listen to global event functions |
|
325
|
|
|
* as defined by the object via 'fx' methods. This also attaches any predefined behaviors. |
|
326
|
|
|
* This function installs all class behaviors in a class hierarchy from the deepest subclass |
|
327
|
|
|
* through each parent to the top most class, TComponent. |
|
328
|
|
|
*/ |
|
329
|
|
|
public function __construct() { |
|
330
|
|
|
if($this->getAutoGlobalListen()) |
|
331
|
|
|
$this->listen(); |
|
332
|
|
|
|
|
333
|
|
|
$classes=array_reverse($this->getClassHierarchy(true)); |
|
334
|
|
|
foreach($classes as $class) { |
|
335
|
|
|
if(isset(self::$_um[$class])) |
|
336
|
|
|
$this->attachBehaviors(self::$_um[$class]); |
|
337
|
|
|
} |
|
338
|
|
|
} |
|
339
|
|
|
|
|
340
|
|
|
|
|
341
|
|
|
/** |
|
342
|
|
|
* Tells TComponent whether or not to automatically listen to global events. |
|
343
|
|
|
* Defaults to false because PHP variable cleanup is affected if this is true. |
|
344
|
|
|
* When unsetting a variable that is listening to global events, {@link unlisten} |
|
345
|
|
|
* must explicitly be called when cleaning variables allocation or else the global |
|
346
|
|
|
* event registry will contain references to the old object. This is true for PHP 5.4 |
|
347
|
|
|
* |
|
348
|
|
|
* Override this method by a subclass to change the setting. When set to true, this |
|
349
|
|
|
* will enable {@link __construct} to call {@link listen}. |
|
350
|
|
|
* |
|
351
|
|
|
* @return boolean whether or not to auto listen to global events during {@link __construct}, default false |
|
352
|
|
|
*/ |
|
353
|
|
|
public function getAutoGlobalListen() { |
|
354
|
|
|
return false; |
|
355
|
|
|
} |
|
356
|
|
|
|
|
357
|
|
|
|
|
358
|
|
|
/** |
|
359
|
|
|
* The common __destruct |
|
360
|
|
|
* This unlistens from the global event routines if listening |
|
361
|
|
|
* |
|
362
|
|
|
* PHP 5.3 does not __destruct objects when they are nulled and thus unlisten must be |
|
363
|
|
|
* called must be explicitly called. |
|
364
|
|
|
*/ |
|
365
|
|
|
public function __destruct() { |
|
366
|
|
|
if($this->_listeningenabled) |
|
367
|
|
|
$this->unlisten(); |
|
368
|
|
|
} |
|
369
|
|
|
|
|
370
|
|
|
|
|
371
|
|
|
/** |
|
372
|
|
|
* This utility function is a private array filter method. The array values |
|
373
|
|
|
* that start with 'fx' are filtered in. |
|
374
|
|
|
*/ |
|
375
|
|
|
private function filter_prado_fx($name) { |
|
|
|
|
|
|
376
|
|
|
return strncasecmp($name,'fx',2)===0; |
|
377
|
|
|
} |
|
378
|
|
|
|
|
379
|
|
|
|
|
380
|
|
|
/** |
|
381
|
|
|
* This returns an array of the class name and the names of all its parents. The base object first, |
|
382
|
|
|
* {@link TComponent}, and the deepest subclass is last. |
|
383
|
|
|
* @param boolean optional should the names be all lowercase true/false |
|
384
|
|
|
* @return array array of strings being the class hierarchy of $this. |
|
385
|
|
|
*/ |
|
386
|
|
|
public function getClassHierarchy($lowercase = false) |
|
387
|
|
|
{ |
|
388
|
|
|
$class=get_class($this); |
|
389
|
|
|
$classes=array($class); |
|
390
|
|
|
while($class=get_parent_class($class)){array_unshift($classes,$class);} |
|
391
|
|
|
if($lowercase) |
|
392
|
|
|
return array_map('strtolower',$classes); |
|
393
|
|
|
return $classes; |
|
394
|
|
|
} |
|
395
|
|
|
|
|
396
|
|
|
|
|
397
|
|
|
/** |
|
398
|
|
|
* This adds an object's fx event handlers into the global broadcaster to listen into any |
|
399
|
|
|
* broadcast global events called through {@link raiseEvent} |
|
400
|
|
|
* |
|
401
|
|
|
* Behaviors may implement the function: |
|
402
|
|
|
* <code> |
|
403
|
|
|
* public function dyListen($globalEvents[, $chain]) { |
|
404
|
|
|
* $this->listen(); //eg |
|
405
|
|
|
* } |
|
406
|
|
|
* </code> |
|
407
|
|
|
* to be executed when listen is called. All attached behaviors are notified through dyListen. |
|
408
|
|
|
* |
|
409
|
|
|
* @return numeric the number of global events that were registered to the global event registry |
|
410
|
|
|
*/ |
|
411
|
|
|
public function listen() { |
|
412
|
|
|
if($this->_listeningenabled) |
|
413
|
|
|
return; |
|
414
|
|
|
|
|
415
|
|
|
$fx=array_filter(get_class_methods($this),array($this,'filter_prado_fx')); |
|
416
|
|
|
|
|
417
|
|
|
foreach($fx as $func) |
|
418
|
|
|
$this->attachEventHandler($func,array($this,$func)); |
|
419
|
|
|
|
|
420
|
|
|
if(is_a($this,'IDynamicMethods')) { |
|
421
|
|
|
$this->attachEventHandler(TComponent::GLOBAL_RAISE_EVENT_LISTENER,array($this,'__dycall')); |
|
|
|
|
|
|
422
|
|
|
array_push($fx,TComponent::GLOBAL_RAISE_EVENT_LISTENER); |
|
|
|
|
|
|
423
|
|
|
} |
|
424
|
|
|
|
|
425
|
|
|
$this->_listeningenabled=true; |
|
426
|
|
|
|
|
427
|
|
|
$this->dyListen($fx); |
|
|
|
|
|
|
428
|
|
|
|
|
429
|
|
|
return count($fx); |
|
430
|
|
|
} |
|
431
|
|
|
|
|
432
|
|
|
/** |
|
433
|
|
|
* this removes an object's fx events from the global broadcaster |
|
434
|
|
|
* |
|
435
|
|
|
* Behaviors may implement the function: |
|
436
|
|
|
* <code> |
|
437
|
|
|
* public function dyUnlisten($globalEvents[, $chain]) { |
|
438
|
|
|
* $this->behaviorUnlisten(); //eg |
|
439
|
|
|
* } |
|
440
|
|
|
* </code> |
|
441
|
|
|
* to be executed when listen is called. All attached behaviors are notified through dyUnlisten. |
|
442
|
|
|
* |
|
443
|
|
|
* @return numeric the number of global events that were unregistered from the global event registry |
|
444
|
|
|
*/ |
|
445
|
|
|
public function unlisten() { |
|
446
|
|
|
if(!$this->_listeningenabled) |
|
447
|
|
|
return; |
|
448
|
|
|
|
|
449
|
|
|
$fx=array_filter(get_class_methods($this),array($this,'filter_prado_fx')); |
|
450
|
|
|
|
|
451
|
|
|
foreach($fx as $func) |
|
452
|
|
|
$this->detachEventHandler($func,array($this,$func)); |
|
453
|
|
|
|
|
454
|
|
|
if(is_a($this,'IDynamicMethods')) { |
|
455
|
|
|
$this->detachEventHandler(TComponent::GLOBAL_RAISE_EVENT_LISTENER,array($this,'__dycall')); |
|
|
|
|
|
|
456
|
|
|
array_push($fx,TComponent::GLOBAL_RAISE_EVENT_LISTENER); |
|
|
|
|
|
|
457
|
|
|
} |
|
458
|
|
|
|
|
459
|
|
|
$this->_listeningenabled=false; |
|
460
|
|
|
|
|
461
|
|
|
$this->dyUnlisten($fx); |
|
|
|
|
|
|
462
|
|
|
|
|
463
|
|
|
return count($fx); |
|
464
|
|
|
} |
|
465
|
|
|
|
|
466
|
|
|
/** |
|
467
|
|
|
* Gets the state of listening to global events |
|
468
|
|
|
* @return boolean is Listening to global broadcast enabled |
|
469
|
|
|
*/ |
|
470
|
|
|
public function getListeningToGlobalEvents() |
|
471
|
|
|
{ |
|
472
|
|
|
return $this->_listeningenabled; |
|
473
|
|
|
} |
|
474
|
|
|
|
|
475
|
|
|
|
|
476
|
|
|
/** |
|
477
|
|
|
* Calls a method. |
|
478
|
|
|
* Do not call this method directly. This is a PHP magic method that we override |
|
479
|
|
|
* to allow behaviors, dynamic events (intra-object/behavior events), |
|
480
|
|
|
* undefined dynamic and global events, and |
|
481
|
|
|
* to allow using the following syntax to call a property setter or getter. |
|
482
|
|
|
* <code> |
|
483
|
|
|
* $this->getPropertyName($value); // if there's a $this->getjsPropertyName() method |
|
484
|
|
|
* $this->setPropertyName($value); // if there's a $this->setjsPropertyName() method |
|
485
|
|
|
* </code> |
|
486
|
|
|
* |
|
487
|
|
|
* Additional object behaviors override class behaviors. |
|
488
|
|
|
* dynamic and global events do not fail even if they aren't implemented. |
|
489
|
|
|
* Any intra-object/behavior dynamic events that are not implemented by the behavior |
|
490
|
|
|
* return the first function paramater or null when no parameters are specified. |
|
491
|
|
|
* |
|
492
|
|
|
* @param string method name that doesn't exist and is being called on the object |
|
493
|
|
|
* @param mixed method parameters |
|
494
|
|
|
* @throws TInvalidOperationException If the property is not defined or read-only or |
|
495
|
|
|
* method is undefined |
|
496
|
|
|
* @return mixed result of the method call, or false if 'fx' or 'dy' function but |
|
497
|
|
|
* is not found in the class, otherwise it runs |
|
498
|
|
|
*/ |
|
499
|
|
|
public function __call($method, $args) |
|
500
|
|
|
{ |
|
501
|
|
|
$getset=substr($method,0,3); |
|
502
|
|
|
if(($getset=='get')||($getset=='set')) |
|
503
|
|
|
{ |
|
504
|
|
|
$propname=substr($method,3); |
|
505
|
|
|
$jsmethod=$getset.'js'.$propname; |
|
506
|
|
|
if(method_exists($this,$jsmethod)) |
|
507
|
|
|
{ |
|
508
|
|
|
if(count($args)>0) |
|
509
|
|
|
if($args[0]&&!($args[0] instanceof TJavaScriptString)) |
|
510
|
|
|
$args[0]=new TJavaScriptString($args[0]); |
|
511
|
|
|
return call_user_func_array(array($this,$jsmethod),$args); |
|
512
|
|
|
} |
|
513
|
|
|
|
|
514
|
|
|
if (($getset=='set')&&method_exists($this,'getjs'.$propname)) |
|
515
|
|
|
throw new TInvalidOperationException('component_property_readonly',get_class($this),$method); |
|
516
|
|
|
} |
|
517
|
|
|
|
|
518
|
|
|
if($this->_m!==null&&$this->_behaviorsenabled) |
|
519
|
|
|
{ |
|
520
|
|
|
if(strncasecmp($method,'dy',2)===0) |
|
521
|
|
|
{ |
|
522
|
|
|
$callchain=new TCallChain($method); |
|
523
|
|
|
foreach($this->_m->toArray() as $behavior) |
|
524
|
|
|
{ |
|
525
|
|
|
if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&(method_exists($behavior,$method)||($behavior instanceof IDynamicMethods))) |
|
526
|
|
|
{ |
|
527
|
|
|
$behavior_args=$args; |
|
528
|
|
|
if($behavior instanceof IClassBehavior) |
|
529
|
|
|
array_unshift($behavior_args,$this); |
|
530
|
|
|
$callchain->addCall(array($behavior,$method),$behavior_args); |
|
531
|
|
|
} |
|
532
|
|
|
|
|
533
|
|
|
} |
|
534
|
|
|
if($callchain->getCount()>0) |
|
535
|
|
|
return call_user_func_array(array($callchain,'call'),$args); |
|
536
|
|
|
} |
|
537
|
|
|
else |
|
538
|
|
|
{ |
|
539
|
|
|
foreach($this->_m->toArray() as $behavior) |
|
540
|
|
|
{ |
|
541
|
|
|
if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&method_exists($behavior,$method)) |
|
542
|
|
|
{ |
|
543
|
|
|
if($behavior instanceof IClassBehavior) |
|
544
|
|
|
array_unshift($args,$this); |
|
545
|
|
|
return call_user_func_array(array($behavior,$method),$args); |
|
546
|
|
|
} |
|
547
|
|
|
} |
|
548
|
|
|
} |
|
549
|
|
|
} |
|
550
|
|
|
|
|
551
|
|
|
if(strncasecmp($method,'dy',2)===0||strncasecmp($method,'fx',2)===0) |
|
552
|
|
|
{ |
|
553
|
|
|
if($this instanceof IDynamicMethods) |
|
554
|
|
|
return $this->__dycall($method,$args); |
|
555
|
|
|
return isset($args[0])?$args[0]:null; |
|
556
|
|
|
} |
|
557
|
|
|
|
|
558
|
|
|
// don't thrown an exception for __magicMethods() or any other weird methods natively implemented by php |
|
559
|
|
|
if (!method_exists($this, $method)) { |
|
560
|
|
|
throw new TApplicationException('component_method_undefined',get_class($this),$method); |
|
561
|
|
|
} |
|
562
|
|
|
} |
|
563
|
|
|
|
|
564
|
|
|
|
|
565
|
|
|
/** |
|
566
|
|
|
* Returns a property value or an event handler list by property or event name. |
|
567
|
|
|
* Do not call this method. This is a PHP magic method that we override |
|
568
|
|
|
* to allow using the following syntax to read a property: |
|
569
|
|
|
* <code> |
|
570
|
|
|
* $value=$component->PropertyName; |
|
571
|
|
|
* $value=$component->jsPropertyName; // return JavaScript literal |
|
572
|
|
|
* </code> |
|
573
|
|
|
* and to obtain the event handler list for an event, |
|
574
|
|
|
* <code> |
|
575
|
|
|
* $eventHandlerList=$component->EventName; |
|
576
|
|
|
* </code> |
|
577
|
|
|
* This will also return the global event handler list when specifing an 'fx' |
|
578
|
|
|
* event, |
|
579
|
|
|
* <code> |
|
580
|
|
|
* $globalEventHandlerList=$component->fxEventName; |
|
581
|
|
|
* </code> |
|
582
|
|
|
* When behaviors are enabled, this will return the behavior of a specific |
|
583
|
|
|
* name, a property of a behavior, or an object 'on' event defined by the behavior. |
|
584
|
|
|
* @param string the property name or the event name |
|
585
|
|
|
* @return mixed the property value or the event handler list as {@link TPriorityList} |
|
586
|
|
|
* @throws TInvalidOperationException if the property/event is not defined. |
|
587
|
|
|
*/ |
|
588
|
|
|
public function __get($name) |
|
589
|
|
|
{ |
|
590
|
|
|
if(method_exists($this,$getter='get'.$name)) |
|
591
|
|
|
{ |
|
592
|
|
|
// getting a property |
|
593
|
|
|
return $this->$getter(); |
|
594
|
|
|
} |
|
595
|
|
|
else if(method_exists($this,$jsgetter='getjs'.$name)) |
|
596
|
|
|
{ |
|
597
|
|
|
// getting a javascript property |
|
598
|
|
|
return (string)$this->$jsgetter(); |
|
599
|
|
|
} |
|
600
|
|
|
else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name)) |
|
601
|
|
|
{ |
|
602
|
|
|
// getting an event (handler list) |
|
603
|
|
|
$name=strtolower($name); |
|
604
|
|
|
if(!isset($this->_e[$name])) |
|
605
|
|
|
$this->_e[$name]=new TPriorityList; |
|
606
|
|
|
return $this->_e[$name]; |
|
607
|
|
|
} |
|
608
|
|
|
else if(strncasecmp($name,'fx',2)===0) |
|
609
|
|
|
{ |
|
610
|
|
|
// getting a global event (handler list) |
|
611
|
|
|
$name=strtolower($name); |
|
612
|
|
|
if(!isset(self::$_ue[$name])) |
|
613
|
|
|
self::$_ue[$name]=new TPriorityList; |
|
614
|
|
|
return self::$_ue[$name]; |
|
615
|
|
|
} |
|
616
|
|
|
else if($this->_behaviorsenabled) |
|
617
|
|
|
{ |
|
618
|
|
|
// getting a behavior property/event (handler list) |
|
619
|
|
|
if(isset($this->_m[$name])) |
|
620
|
|
|
return $this->_m[$name]; |
|
621
|
|
|
else if($this->_m!==null) |
|
622
|
|
|
{ |
|
623
|
|
|
foreach($this->_m->toArray() as $behavior) |
|
624
|
|
|
{ |
|
625
|
|
|
if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&& |
|
626
|
|
|
(property_exists($behavior,$name)||$behavior->canGetProperty($name)||$behavior->hasEvent($name))) |
|
627
|
|
|
return $behavior->$name; |
|
628
|
|
|
} |
|
629
|
|
|
} |
|
630
|
|
|
} |
|
631
|
|
|
throw new TInvalidOperationException('component_property_undefined',get_class($this),$name); |
|
632
|
|
|
} |
|
633
|
|
|
|
|
634
|
|
|
/** |
|
635
|
|
|
* Sets value of a component property. |
|
636
|
|
|
* Do not call this method. This is a PHP magic method that we override |
|
637
|
|
|
* to allow using the following syntax to set a property or attach an event handler. |
|
638
|
|
|
* <code> |
|
639
|
|
|
* $this->PropertyName=$value; |
|
640
|
|
|
* $this->jsPropertyName=$value; // $value will be treated as a JavaScript literal |
|
641
|
|
|
* $this->EventName=$handler; |
|
642
|
|
|
* $this->fxEventName=$handler; //global event listener |
|
643
|
|
|
* </code> |
|
644
|
|
|
* When behaviors are enabled, this will also set a behaviors properties and events. |
|
645
|
|
|
* @param string the property name or event name |
|
646
|
|
|
* @param mixed the property value or event handler |
|
647
|
|
|
* @throws TInvalidOperationException If the property is not defined or read-only. |
|
648
|
|
|
*/ |
|
649
|
|
|
public function __set($name,$value) |
|
650
|
|
|
{ |
|
651
|
|
|
if(method_exists($this,$setter='set'.$name)) |
|
652
|
|
|
{ |
|
653
|
|
|
if(strncasecmp($name,'js',2)===0&&$value&&!($value instanceof TJavaScriptLiteral)) |
|
654
|
|
|
$value = new TJavaScriptLiteral($value); |
|
655
|
|
|
return $this->$setter($value); |
|
656
|
|
|
} |
|
657
|
|
|
else if(method_exists($this,$jssetter='setjs'.$name)) |
|
658
|
|
|
{ |
|
659
|
|
|
if($value&&!($value instanceof TJavaScriptString)) |
|
660
|
|
|
$value=new TJavaScriptString($value); |
|
661
|
|
|
return $this->$jssetter($value); |
|
662
|
|
|
} |
|
663
|
|
|
else if((strncasecmp($name,'on',2)===0&&method_exists($this,$name))||strncasecmp($name,'fx',2)===0) |
|
664
|
|
|
{ |
|
665
|
|
|
return $this->attachEventHandler($name,$value); |
|
666
|
|
|
} |
|
667
|
|
|
else if($this->_m!==null&&$this->_m->getCount()>0&&$this->_behaviorsenabled) |
|
668
|
|
|
{ |
|
669
|
|
|
$sets=0; |
|
670
|
|
|
foreach($this->_m->toArray() as $behavior) |
|
671
|
|
|
{ |
|
672
|
|
|
if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&& |
|
673
|
|
|
(property_exists($behavior,$name)||$behavior->canSetProperty($name)||$behavior->hasEvent($name))) { |
|
674
|
|
|
$behavior->$name=$value; |
|
675
|
|
|
$sets++; |
|
676
|
|
|
} |
|
677
|
|
|
} |
|
678
|
|
|
if($sets)return $value; |
|
679
|
|
|
|
|
680
|
|
|
} |
|
681
|
|
|
|
|
682
|
|
|
if(method_exists($this,'get'.$name)||method_exists($this,'getjs'.$name)) |
|
683
|
|
|
{ |
|
684
|
|
|
throw new TInvalidOperationException('component_property_readonly',get_class($this),$name); |
|
685
|
|
|
} |
|
686
|
|
|
else |
|
687
|
|
|
{ |
|
688
|
|
|
throw new TInvalidOperationException('component_property_undefined',get_class($this),$name); |
|
689
|
|
|
} |
|
690
|
|
|
} |
|
691
|
|
|
|
|
692
|
|
|
/** |
|
693
|
|
|
* Checks if a property value is null, there are no events in the object |
|
694
|
|
|
* event list or global event list registered under the name, and, if |
|
695
|
|
|
* behaviors are enabled, |
|
696
|
|
|
* Do not call this method. This is a PHP magic method that we override |
|
697
|
|
|
* to allow using isset() to detect if a component property is set or not. |
|
698
|
|
|
* This also works for global events. When behaviors are enabled, it |
|
699
|
|
|
* will check for a behavior of the specified name, and also check |
|
700
|
|
|
* the behavior for events and properties. |
|
701
|
|
|
* @param string the property name or the event name |
|
702
|
|
|
* @since 3.2.3 |
|
703
|
|
|
*/ |
|
704
|
|
|
public function __isset($name) |
|
705
|
|
|
{ |
|
706
|
|
|
if(method_exists($this,$getter='get'.$name)) |
|
707
|
|
|
return $this->$getter()!==null; |
|
708
|
|
|
else if(method_exists($this,$jsgetter='getjs'.$name)) |
|
709
|
|
|
return $this->$jsgetter()!==null; |
|
710
|
|
|
else if(strncasecmp($name,'on',2)===0&&method_exists($this,$name)) |
|
711
|
|
|
{ |
|
712
|
|
|
$name=strtolower($name); |
|
713
|
|
|
return isset($this->_e[$name])&&$this->_e[$name]->getCount(); |
|
714
|
|
|
} |
|
715
|
|
|
else if(strncasecmp($name,'fx',2)===0) |
|
716
|
|
|
{ |
|
717
|
|
|
$name=strtolower($name); |
|
718
|
|
|
return isset(self::$_ue[$name])&&self::$_ue[$name]->getCount(); |
|
719
|
|
|
} |
|
720
|
|
|
else if($this->_m!==null&&$this->_m->getCount()>0&&$this->_behaviorsenabled) |
|
721
|
|
|
{ |
|
722
|
|
|
if(isset($this->_m[$name])) |
|
723
|
|
|
return true; |
|
724
|
|
|
foreach($this->_m->toArray() as $behavior) |
|
725
|
|
|
{ |
|
726
|
|
|
if((!($behavior instanceof IBehavior)||$behavior->getEnabled())) |
|
727
|
|
|
return isset($behavior->$name); |
|
728
|
|
|
} |
|
729
|
|
|
|
|
730
|
|
|
} |
|
731
|
|
|
else |
|
732
|
|
|
return false; |
|
733
|
|
|
} |
|
734
|
|
|
|
|
735
|
|
|
/** |
|
736
|
|
|
* Sets a component property to be null. Clears the object or global |
|
737
|
|
|
* events. When enabled, loops through all behaviors and unsets the |
|
738
|
|
|
* property or event. |
|
739
|
|
|
* Do not call this method. This is a PHP magic method that we override |
|
740
|
|
|
* to allow using unset() to set a component property to be null. |
|
741
|
|
|
* @param string the property name or the event name |
|
742
|
|
|
* @throws TInvalidOperationException if the property is read only. |
|
743
|
|
|
* @since 3.2.3 |
|
744
|
|
|
*/ |
|
745
|
|
|
public function __unset($name) |
|
746
|
|
|
{ |
|
747
|
|
|
if(method_exists($this,$setter='set'.$name)) |
|
748
|
|
|
$this->$setter(null); |
|
749
|
|
|
else if(method_exists($this,$jssetter='setjs'.$name)) |
|
750
|
|
|
$this->$jssetter(null); |
|
751
|
|
|
else if(strncasecmp($name,'on',2)===0&&method_exists($this,$name)) |
|
752
|
|
|
$this->_e[strtolower($name)]->clear(); |
|
753
|
|
|
else if(strncasecmp($name,'fx',2)===0) |
|
754
|
|
|
$this->getEventHandlers($name)->remove(array($this, $name)); |
|
755
|
|
|
else if($this->_m!==null&&$this->_m->getCount()>0&&$this->_behaviorsenabled) |
|
756
|
|
|
{ |
|
757
|
|
|
if(isset($this->_m[$name])) |
|
758
|
|
|
$this->detachBehavior($name); |
|
759
|
|
|
else { |
|
760
|
|
|
$unset=0; |
|
761
|
|
|
foreach($this->_m->toArray() as $behavior) |
|
762
|
|
|
{ |
|
763
|
|
|
if((!($behavior instanceof IBehavior)||$behavior->getEnabled())) { |
|
764
|
|
|
unset($behavior->$name); |
|
765
|
|
|
$unset++; |
|
766
|
|
|
} |
|
767
|
|
|
} |
|
768
|
|
|
if(!$unset&&method_exists($this,'get'.$name)) |
|
769
|
|
|
throw new TInvalidOperationException('component_property_readonly',get_class($this),$name); |
|
770
|
|
|
} |
|
771
|
|
|
} else if(method_exists($this,'get'.$name)) |
|
772
|
|
|
throw new TInvalidOperationException('component_property_readonly',get_class($this),$name); |
|
773
|
|
|
} |
|
774
|
|
|
|
|
775
|
|
|
/** |
|
776
|
|
|
* Determines whether a property is defined. |
|
777
|
|
|
* A property is defined if there is a getter or setter method |
|
778
|
|
|
* defined in the class. Note, property names are case-insensitive. |
|
779
|
|
|
* @param string the property name |
|
780
|
|
|
* @return boolean whether the property is defined |
|
781
|
|
|
*/ |
|
782
|
|
|
public function hasProperty($name) |
|
783
|
|
|
{ |
|
784
|
|
|
return $this->canGetProperty($name)||$this->canSetProperty($name); |
|
785
|
|
|
} |
|
786
|
|
|
|
|
787
|
|
|
/** |
|
788
|
|
|
* Determines whether a property can be read. |
|
789
|
|
|
* A property can be read if the class has a getter method |
|
790
|
|
|
* for the property name. Note, property name is case-insensitive. |
|
791
|
|
|
* This also checks for getjs. When enabled, it loops through all |
|
792
|
|
|
* active behaviors for the get property when undefined by the object. |
|
793
|
|
|
* @param string the property name |
|
794
|
|
|
* @return boolean whether the property can be read |
|
795
|
|
|
*/ |
|
796
|
|
|
public function canGetProperty($name) |
|
797
|
|
|
{ |
|
798
|
|
|
if(method_exists($this,'get'.$name)||method_exists($this,'getjs'.$name)) |
|
799
|
|
|
return true; |
|
800
|
|
|
else if($this->_m!==null&&$this->_behaviorsenabled) |
|
801
|
|
|
{ |
|
802
|
|
|
foreach($this->_m->toArray() as $behavior) |
|
803
|
|
|
{ |
|
804
|
|
|
if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->canGetProperty($name)) |
|
805
|
|
|
return true; |
|
806
|
|
|
} |
|
807
|
|
|
} |
|
808
|
|
|
return false; |
|
809
|
|
|
} |
|
810
|
|
|
|
|
811
|
|
|
/** |
|
812
|
|
|
* Determines whether a property can be set. |
|
813
|
|
|
* A property can be written if the class has a setter method |
|
814
|
|
|
* for the property name. Note, property name is case-insensitive. |
|
815
|
|
|
* This also checks for setjs. When enabled, it loops through all |
|
816
|
|
|
* active behaviors for the set property when undefined by the object. |
|
817
|
|
|
* @param string the property name |
|
818
|
|
|
* @return boolean whether the property can be written |
|
819
|
|
|
*/ |
|
820
|
|
|
public function canSetProperty($name) |
|
821
|
|
|
{ |
|
822
|
|
|
if(method_exists($this,'set'.$name)||method_exists($this,'setjs'.$name)) |
|
823
|
|
|
return true; |
|
824
|
|
|
else if($this->_m!==null&&$this->_behaviorsenabled) |
|
825
|
|
|
{ |
|
826
|
|
|
foreach($this->_m->toArray() as $behavior) |
|
827
|
|
|
{ |
|
828
|
|
|
if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->canSetProperty($name)) |
|
829
|
|
|
return true; |
|
830
|
|
|
} |
|
831
|
|
|
} |
|
832
|
|
|
return false; |
|
833
|
|
|
} |
|
834
|
|
|
|
|
835
|
|
|
/** |
|
836
|
|
|
* Evaluates a property path. |
|
837
|
|
|
* A property path is a sequence of property names concatenated by '.' character. |
|
838
|
|
|
* For example, 'Parent.Page' refers to the 'Page' property of the component's |
|
839
|
|
|
* 'Parent' property value (which should be a component also). |
|
840
|
|
|
* When a property is not defined by an object, this also loops through all |
|
841
|
|
|
* active behaviors of the object. |
|
842
|
|
|
* @param string property path |
|
843
|
|
|
* @return mixed the property path value |
|
844
|
|
|
*/ |
|
845
|
|
|
public function getSubProperty($path) |
|
846
|
|
|
{ |
|
847
|
|
|
$object=$this; |
|
848
|
|
|
foreach(explode('.',$path) as $property) |
|
849
|
|
|
$object=$object->$property; |
|
850
|
|
|
return $object; |
|
851
|
|
|
} |
|
852
|
|
|
|
|
853
|
|
|
/** |
|
854
|
|
|
* Sets a value to a property path. |
|
855
|
|
|
* A property path is a sequence of property names concatenated by '.' character. |
|
856
|
|
|
* For example, 'Parent.Page' refers to the 'Page' property of the component's |
|
857
|
|
|
* 'Parent' property value (which should be a component also). |
|
858
|
|
|
* When a property is not defined by an object, this also loops through all |
|
859
|
|
|
* active behaviors of the object. |
|
860
|
|
|
* @param string property path |
|
861
|
|
|
* @param mixed the property path value |
|
862
|
|
|
*/ |
|
863
|
|
|
public function setSubProperty($path,$value) |
|
864
|
|
|
{ |
|
865
|
|
|
$object=$this; |
|
866
|
|
|
if(($pos=strrpos($path,'.'))===false) |
|
867
|
|
|
$property=$path; |
|
868
|
|
|
else |
|
869
|
|
|
{ |
|
870
|
|
|
$object=$this->getSubProperty(substr($path,0,$pos)); |
|
871
|
|
|
$property=substr($path,$pos+1); |
|
872
|
|
|
} |
|
873
|
|
|
$object->$property=$value; |
|
874
|
|
|
} |
|
875
|
|
|
|
|
876
|
|
|
/** |
|
877
|
|
|
* Determines whether an event is defined. |
|
878
|
|
|
* An event is defined if the class has a method whose name is the event name |
|
879
|
|
|
* prefixed with 'on', 'fx', or 'dy'. |
|
880
|
|
|
* Every object responds to every 'fx' and 'dy' event as they are in a universally |
|
881
|
|
|
* accepted event space. 'on' event must be declared by the object. |
|
882
|
|
|
* When enabled, this will loop through all active behaviors for 'on' events |
|
883
|
|
|
* defined by the behavior. |
|
884
|
|
|
* Note, event name is case-insensitive. |
|
885
|
|
|
* @param string the event name |
|
886
|
|
|
* @return boolean |
|
887
|
|
|
*/ |
|
888
|
|
|
public function hasEvent($name) |
|
889
|
|
|
{ |
|
890
|
|
|
if((strncasecmp($name,'on',2)===0&&method_exists($this,$name))||strncasecmp($name,'fx',2)===0||strncasecmp($name,'dy',2)===0) |
|
891
|
|
|
return true; |
|
892
|
|
|
|
|
893
|
|
|
else if($this->_m!==null&&$this->_behaviorsenabled) |
|
894
|
|
|
{ |
|
895
|
|
|
foreach($this->_m->toArray() as $behavior) |
|
896
|
|
|
{ |
|
897
|
|
|
if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->hasEvent($name)) |
|
898
|
|
|
return true; |
|
899
|
|
|
} |
|
900
|
|
|
} |
|
901
|
|
|
return false; |
|
902
|
|
|
} |
|
903
|
|
|
|
|
904
|
|
|
/** |
|
905
|
|
|
* Checks if an event has any handlers. This function also checks through all |
|
906
|
|
|
* the behaviors for 'on' events when behaviors are enabled. |
|
907
|
|
|
* 'dy' dynamic events are not handled by this function. |
|
908
|
|
|
* @param string the event name |
|
909
|
|
|
* @return boolean whether an event has been attached one or several handlers |
|
910
|
|
|
*/ |
|
911
|
|
|
public function hasEventHandler($name) |
|
912
|
|
|
{ |
|
913
|
|
|
$name=strtolower($name); |
|
914
|
|
|
if(strncasecmp($name,'fx',2)===0) |
|
915
|
|
|
return isset(self::$_ue[$name])&&self::$_ue[$name]->getCount()>0; |
|
916
|
|
|
else |
|
917
|
|
|
{ |
|
918
|
|
|
if(isset($this->_e[$name])&&$this->_e[$name]->getCount()>0) |
|
919
|
|
|
return true; |
|
920
|
|
|
else if($this->_m!==null&&$this->_behaviorsenabled) { |
|
921
|
|
|
foreach($this->_m->toArray() as $behavior) |
|
922
|
|
|
{ |
|
923
|
|
|
if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->hasEventHandler($name)) |
|
924
|
|
|
return true; |
|
925
|
|
|
} |
|
926
|
|
|
} |
|
927
|
|
|
} |
|
928
|
|
|
return false; |
|
929
|
|
|
} |
|
930
|
|
|
|
|
931
|
|
|
/** |
|
932
|
|
|
* Returns the list of attached event handlers for an 'on' or 'fx' event. This function also |
|
933
|
|
|
* checks through all the behaviors for 'on' event lists when behaviors are enabled. |
|
934
|
|
|
* @return TPriorityList list of attached event handlers for an event |
|
935
|
|
|
* @throws TInvalidOperationException if the event is not defined |
|
936
|
|
|
*/ |
|
937
|
|
|
public function getEventHandlers($name) |
|
938
|
|
|
{ |
|
939
|
|
|
if(strncasecmp($name,'on',2)===0&&method_exists($this,$name)) |
|
940
|
|
|
{ |
|
941
|
|
|
$name=strtolower($name); |
|
942
|
|
|
if(!isset($this->_e[$name])) |
|
943
|
|
|
$this->_e[$name]=new TPriorityList; |
|
944
|
|
|
return $this->_e[$name]; |
|
945
|
|
|
} |
|
946
|
|
|
else if(strncasecmp($name,'fx',2)===0) |
|
947
|
|
|
{ |
|
948
|
|
|
$name=strtolower($name); |
|
949
|
|
|
if(!isset(self::$_ue[$name])) |
|
950
|
|
|
self::$_ue[$name]=new TPriorityList; |
|
951
|
|
|
return self::$_ue[$name]; |
|
952
|
|
|
} |
|
953
|
|
|
else if($this->_m!==null&&$this->_behaviorsenabled) |
|
954
|
|
|
{ |
|
955
|
|
|
foreach($this->_m->toArray() as $behavior) |
|
956
|
|
|
{ |
|
957
|
|
|
if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->hasEvent($name)) |
|
958
|
|
|
return $behavior->getEventHandlers($name); |
|
959
|
|
|
} |
|
960
|
|
|
} |
|
961
|
|
|
throw new TInvalidOperationException('component_event_undefined',get_class($this),$name); |
|
962
|
|
|
} |
|
963
|
|
|
|
|
964
|
|
|
/** |
|
965
|
|
|
* Attaches an event handler to an event. |
|
966
|
|
|
* |
|
967
|
|
|
* The handler must be a valid PHP callback, i.e., a string referring to |
|
968
|
|
|
* a global function name, or an array containing two elements with |
|
969
|
|
|
* the first element being an object and the second element a method name |
|
970
|
|
|
* of the object. In Prado, you can also use method path to refer to |
|
971
|
|
|
* an event handler. For example, array($object,'Parent.buttonClicked') |
|
972
|
|
|
* uses a method path that refers to the method $object->Parent->buttonClicked(...). |
|
973
|
|
|
* |
|
974
|
|
|
* The event handler must be of the following signature, |
|
975
|
|
|
* <code> |
|
976
|
|
|
* function handlerName($sender, $param) {} |
|
977
|
|
|
* function handlerName($sender, $param, $name) {} |
|
978
|
|
|
* </code> |
|
979
|
|
|
* where $sender represents the object that raises the event, |
|
980
|
|
|
* and $param is the event parameter. $name refers to the event name |
|
981
|
|
|
* being handled. |
|
982
|
|
|
* |
|
983
|
|
|
* This is a convenient method to add an event handler. |
|
984
|
|
|
* It is equivalent to {@link getEventHandlers}($name)->add($handler). |
|
985
|
|
|
* For complete management of event handlers, use {@link getEventHandlers} |
|
986
|
|
|
* to get the event handler list first, and then do various |
|
987
|
|
|
* {@link TPriorityList} operations to append, insert or remove |
|
988
|
|
|
* event handlers. You may also do these operations like |
|
989
|
|
|
* getting and setting properties, e.g., |
|
990
|
|
|
* <code> |
|
991
|
|
|
* $component->OnClick[]=array($object,'buttonClicked'); |
|
992
|
|
|
* $component->OnClick->insertAt(0,array($object,'buttonClicked')); |
|
993
|
|
|
* </code> |
|
994
|
|
|
* which are equivalent to the following |
|
995
|
|
|
* <code> |
|
996
|
|
|
* $component->getEventHandlers('OnClick')->add(array($object,'buttonClicked')); |
|
997
|
|
|
* $component->getEventHandlers('OnClick')->insertAt(0,array($object,'buttonClicked')); |
|
998
|
|
|
* </code> |
|
999
|
|
|
* |
|
1000
|
|
|
* Due to the nature of {@link getEventHandlers}, any active behaviors defining |
|
1001
|
|
|
* new 'on' events, this method will pass through to the behavior transparently. |
|
1002
|
|
|
* |
|
1003
|
|
|
* @param string the event name |
|
1004
|
|
|
* @param callback the event handler |
|
1005
|
|
|
* @param numeric|null the priority of the handler, defaults to null which translates into the |
|
1006
|
|
|
* default priority of 10.0 within {@link TPriorityList} |
|
1007
|
|
|
* @throws TInvalidOperationException if the event does not exist |
|
1008
|
|
|
*/ |
|
1009
|
|
|
public function attachEventHandler($name,$handler,$priority=null) |
|
1010
|
|
|
{ |
|
1011
|
|
|
$this->getEventHandlers($name)->add($handler,$priority); |
|
1012
|
|
|
} |
|
1013
|
|
|
|
|
1014
|
|
|
/** |
|
1015
|
|
|
* Detaches an existing event handler. |
|
1016
|
|
|
* This method is the opposite of {@link attachEventHandler}. It will detach |
|
1017
|
|
|
* any 'on' events definedb by an objects active behaviors as well. |
|
1018
|
|
|
* @param string event name |
|
1019
|
|
|
* @param callback the event handler to be removed |
|
1020
|
|
|
* @param numeric|false|null the priority of the handler, defaults to false which translates |
|
1021
|
|
|
* to an item of any priority within {@link TPriorityList}; null means the default priority |
|
1022
|
|
|
* @return boolean if the removal is successful |
|
1023
|
|
|
*/ |
|
1024
|
|
|
public function detachEventHandler($name,$handler,$priority=false) |
|
1025
|
|
|
{ |
|
1026
|
|
|
if($this->hasEventHandler($name)) |
|
1027
|
|
|
{ |
|
1028
|
|
|
try |
|
1029
|
|
|
{ |
|
1030
|
|
|
$this->getEventHandlers($name)->remove($handler,$priority); |
|
1031
|
|
|
return true; |
|
1032
|
|
|
} |
|
1033
|
|
|
catch(Exception $e) |
|
|
|
|
|
|
1034
|
|
|
{ |
|
1035
|
|
|
} |
|
1036
|
|
|
} |
|
1037
|
|
|
return false; |
|
1038
|
|
|
} |
|
1039
|
|
|
|
|
1040
|
|
|
/** |
|
1041
|
|
|
* Raises an event. This raises both inter-object 'on' events and global 'fx' events. |
|
1042
|
|
|
* This method represents the happening of an event and will |
|
1043
|
|
|
* invoke all attached event handlers for the event in {@link TPriorityList} order. |
|
1044
|
|
|
* This method does not handle intra-object/behavior dynamic 'dy' events. |
|
1045
|
|
|
* |
|
1046
|
|
|
* There are ways to handle event responses. By defailt {@link EVENT_RESULT_FILTER}, |
|
1047
|
|
|
* all event responses are stored in an array, filtered for null responses, and returned. |
|
1048
|
|
|
* If {@link EVENT_RESULT_ALL} is specified, all returned results will be stored along |
|
1049
|
|
|
* with the sender and param in an array |
|
1050
|
|
|
* <code> |
|
1051
|
|
|
* $result[] = array('sender'=>$sender,'param'=>$param,'response'=>$response); |
|
1052
|
|
|
* </code> |
|
1053
|
|
|
* |
|
1054
|
|
|
* If {@link EVENT_RESULT_FEED_FORWARD} is specified, then each handler result is then |
|
1055
|
|
|
* fed forward as the parameters for the next event. This allows for events to filter data |
|
1056
|
|
|
* directly by affecting the event parameters |
|
1057
|
|
|
* |
|
1058
|
|
|
* If a callable function is set in the response type or the post function filter is specified then the |
|
1059
|
|
|
* result of each called event handler is post processed by the callable function. Used in |
|
1060
|
|
|
* combination with {@link EVENT_RESULT_FEED_FORWARD}, any event (and its result) can be chained. |
|
1061
|
|
|
* |
|
1062
|
|
|
* When raising a global 'fx' event, registered handlers in the global event list for |
|
1063
|
|
|
* {@link GLOBAL_RAISE_EVENT_LISTENER} are always added into the set of event handlers. In this way, |
|
1064
|
|
|
* these global events are always raised for every global 'fx' event. The registered handlers for global |
|
1065
|
|
|
* raiseEvent events have priorities. Any registered global raiseEvent event handlers with a priority less than zero |
|
1066
|
|
|
* are added before the main event handlers being raised and any registered global raiseEvent event handlers |
|
1067
|
|
|
* with a priority equal or greater than zero are added after the main event handlers being raised. In this way |
|
1068
|
|
|
* all {@link GLOBAL_RAISE_EVENT_LISTENER} handlers are always called for every raised 'fx' event. |
|
1069
|
|
|
* |
|
1070
|
|
|
* Behaviors may implement the following functions: |
|
1071
|
|
|
* <code> |
|
1072
|
|
|
* public function dyPreRaiseEvent($name,$sender,$param,$responsetype,$postfunction[, $chain]) { |
|
1073
|
|
|
* return $name; //eg, the event name may be filtered/changed |
|
1074
|
|
|
* } |
|
1075
|
|
|
* public function dyIntraRaiseEventTestHandler($handler,$sender,$param,$name[, $chain]) { |
|
1076
|
|
|
* return true; //should this particular handler be executed? true/false |
|
1077
|
|
|
* } |
|
1078
|
|
|
* public function dyIntraRaiseEventPostHandler($name,$sender,$param,$handler,$response[, $chain]) { |
|
1079
|
|
|
* //contains the per handler response |
|
1080
|
|
|
* } |
|
1081
|
|
|
* public function dyPostRaiseEvent($responses,$name,$sender,$param,$responsetype,$postfunction[, $chain]) { |
|
1082
|
|
|
* return $responses; |
|
1083
|
|
|
* } |
|
1084
|
|
|
* </code> |
|
1085
|
|
|
* to be executed when raiseEvent is called. The 'intra' dynamic events are called per handler in |
|
1086
|
|
|
* the handler loop. |
|
1087
|
|
|
* |
|
1088
|
|
|
* dyPreRaiseEvent has the effect of being able to change the event being raised. This intra |
|
1089
|
|
|
* object/behavior event returns the name of the desired event to be raised. It will pass through |
|
1090
|
|
|
* if no dynamic event is specified, or if the original event name is returned. |
|
1091
|
|
|
* dyIntraRaiseEventTestHandler returns true or false as to whether a specific handler should be |
|
1092
|
|
|
* called for a specific raised event (and associated event arguments) |
|
1093
|
|
|
* dyIntraRaiseEventPostHandler does not return anything. This allows behaviors to access the results |
|
1094
|
|
|
* of an event handler in the per handler loop. |
|
1095
|
|
|
* dyPostRaiseEvent returns the responses. This allows for any post processing of the event |
|
1096
|
|
|
* results from the sum of all event handlers |
|
1097
|
|
|
* |
|
1098
|
|
|
* When handling a catch-all {@link __dycall}, the method name is the name of the event |
|
1099
|
|
|
* and the parameters are the sender, the param, and then the name of the event. |
|
1100
|
|
|
* |
|
1101
|
|
|
* @param string the event name |
|
1102
|
|
|
* @param mixed the event sender object |
|
1103
|
|
|
* @param TEventParameter the event parameter |
|
1104
|
|
|
* @param numeric how the results of the event are tabulated. default: {@link EVENT_RESULT_FILTER} The default filters out |
|
1105
|
|
|
* null responses. optional |
|
1106
|
|
|
* @param function any per handler filtering of the response result needed is passed through |
|
1107
|
|
|
* this if not null. default: null. optional |
|
1108
|
|
|
* @return mixed the results of the event |
|
1109
|
|
|
* @throws TInvalidOperationException if the event is undefined |
|
1110
|
|
|
* @throws TInvalidDataValueException If an event handler is invalid |
|
1111
|
|
|
*/ |
|
1112
|
|
|
public function raiseEvent($name,$sender,$param,$responsetype=null,$postfunction=null) |
|
1113
|
|
|
{ |
|
1114
|
|
|
$p=$param; |
|
|
|
|
|
|
1115
|
|
|
if(is_callable($responsetype)) |
|
1116
|
|
|
{ |
|
1117
|
|
|
$postfunction=$responsetype; |
|
1118
|
|
|
$responsetype=null; |
|
1119
|
|
|
} |
|
1120
|
|
|
|
|
1121
|
|
|
if($responsetype===null) |
|
1122
|
|
|
$responsetype=TEventResults::EVENT_RESULT_FILTER; |
|
1123
|
|
|
|
|
1124
|
|
|
$name=strtolower($name); |
|
1125
|
|
|
$responses=array(); |
|
1126
|
|
|
|
|
1127
|
|
|
$name=$this->dyPreRaiseEvent($name,$sender,$param,$responsetype,$postfunction); |
|
|
|
|
|
|
1128
|
|
|
|
|
1129
|
|
|
if($this->hasEventHandler($name)||$this->hasEventHandler(TComponent::GLOBAL_RAISE_EVENT_LISTENER)) |
|
|
|
|
|
|
1130
|
|
|
{ |
|
1131
|
|
|
$handlers=$this->getEventHandlers($name); |
|
1132
|
|
|
$handlerArray=$handlers->toArray(); |
|
1133
|
|
|
if(strncasecmp($name,'fx',2)===0&&$this->hasEventHandler(TComponent::GLOBAL_RAISE_EVENT_LISTENER)) |
|
|
|
|
|
|
1134
|
|
|
{ |
|
1135
|
|
|
$globalhandlers=$this->getEventHandlers(TComponent::GLOBAL_RAISE_EVENT_LISTENER); |
|
|
|
|
|
|
1136
|
|
|
$handlerArray=array_merge($globalhandlers->toArrayBelowPriority(0),$handlerArray,$globalhandlers->toArrayAbovePriority(0)); |
|
1137
|
|
|
} |
|
1138
|
|
|
$response=null; |
|
|
|
|
|
|
1139
|
|
|
foreach($handlerArray as $handler) |
|
1140
|
|
|
{ |
|
1141
|
|
|
if($this->dyIntraRaiseEventTestHandler($handler,$sender,$param,$name)===false) |
|
|
|
|
|
|
1142
|
|
|
continue; |
|
1143
|
|
|
|
|
1144
|
|
|
if(is_string($handler)) |
|
1145
|
|
|
{ |
|
1146
|
|
|
if(($pos=strrpos($handler,'.'))!==false) |
|
1147
|
|
|
{ |
|
1148
|
|
|
$object=$this->getSubProperty(substr($handler,0,$pos)); |
|
1149
|
|
|
$method=substr($handler,$pos+1); |
|
1150
|
|
|
if(method_exists($object,$method)||strncasecmp($method,'dy',2)===0||strncasecmp($method,'fx',2)===0) |
|
1151
|
|
|
{ |
|
1152
|
|
|
if($method=='__dycall') |
|
1153
|
|
|
$response=$object->__dycall($name,array($sender,$param,$name)); |
|
1154
|
|
|
else |
|
1155
|
|
|
$response=$object->$method($sender,$param,$name); |
|
1156
|
|
|
} |
|
1157
|
|
|
else |
|
1158
|
|
|
throw new TInvalidDataValueException('component_eventhandler_invalid',get_class($this),$name,$handler); |
|
1159
|
|
|
} |
|
1160
|
|
|
else |
|
1161
|
|
|
$response=call_user_func($handler,$sender,$param,$name); |
|
1162
|
|
|
} |
|
1163
|
|
|
else if(is_callable($handler,true)) |
|
1164
|
|
|
{ |
|
1165
|
|
|
list($object,$method)=$handler; |
|
1166
|
|
|
if(is_string($object)) |
|
1167
|
|
|
$response=call_user_func($handler,$sender,$param,$name); |
|
1168
|
|
|
else |
|
1169
|
|
|
{ |
|
1170
|
|
|
if(($pos=strrpos($method,'.'))!==false) |
|
1171
|
|
|
{ |
|
1172
|
|
|
$object=$this->getSubProperty(substr($method,0,$pos)); |
|
1173
|
|
|
$method=substr($method,$pos+1); |
|
1174
|
|
|
} |
|
1175
|
|
|
if(method_exists($object,$method)||strncasecmp($method,'dy',2)===0||strncasecmp($method,'fx',2)===0) |
|
1176
|
|
|
{ |
|
1177
|
|
|
if($method=='__dycall') |
|
1178
|
|
|
$response=$object->__dycall($name,array($sender,$param,$name)); |
|
1179
|
|
|
else |
|
1180
|
|
|
$response=$object->$method($sender,$param,$name); |
|
1181
|
|
|
} |
|
1182
|
|
|
else |
|
1183
|
|
|
throw new TInvalidDataValueException('component_eventhandler_invalid',get_class($this),$name,$handler[1]); |
|
1184
|
|
|
} |
|
1185
|
|
|
} |
|
1186
|
|
|
else |
|
1187
|
|
|
throw new TInvalidDataValueException('component_eventhandler_invalid',get_class($this),$name,gettype($handler)); |
|
1188
|
|
|
|
|
1189
|
|
|
$this->dyIntraRaiseEventPostHandler($name,$sender,$param,$handler,$response); |
|
|
|
|
|
|
1190
|
|
|
|
|
1191
|
|
|
if($postfunction) |
|
1192
|
|
|
$response=call_user_func_array($postfunction,array($sender,$param,$this,$response)); |
|
1193
|
|
|
|
|
1194
|
|
|
if($responsetype&TEventResults::EVENT_RESULT_ALL) |
|
1195
|
|
|
$responses[]=array('sender'=>$sender,'param'=>$param,'response'=>$response); |
|
1196
|
|
|
else |
|
1197
|
|
|
$responses[]=$response; |
|
1198
|
|
|
|
|
1199
|
|
|
if($response!==null&&($responsetype&TEventResults::EVENT_RESULT_FEED_FORWARD)) |
|
1200
|
|
|
$param=$response; |
|
1201
|
|
|
|
|
1202
|
|
|
} |
|
1203
|
|
|
} |
|
1204
|
|
|
else if(strncasecmp($name,'on',2)===0&&!$this->hasEvent($name)) |
|
1205
|
|
|
throw new TInvalidOperationException('component_event_undefined',get_class($this),$name); |
|
1206
|
|
|
|
|
1207
|
|
|
if($responsetype&TEventResults::EVENT_RESULT_FILTER) |
|
1208
|
|
|
$responses=array_filter($responses); |
|
1209
|
|
|
|
|
1210
|
|
|
$responses=$this->dyPostRaiseEvent($responses,$name,$sender,$param,$responsetype,$postfunction); |
|
|
|
|
|
|
1211
|
|
|
|
|
1212
|
|
|
return $responses; |
|
1213
|
|
|
} |
|
1214
|
|
|
|
|
1215
|
|
|
/** |
|
1216
|
|
|
* Evaluates a PHP expression in the context of this control. |
|
1217
|
|
|
* |
|
1218
|
|
|
* Behaviors may implement the function: |
|
1219
|
|
|
* <code> |
|
1220
|
|
|
* public function dyEvaluateExpressionFilter($expression, $chain) { |
|
1221
|
|
|
* return $chain->dyEvaluateExpressionFilter(str_replace('foo', 'bar', $expression)); //example |
|
1222
|
|
|
* } |
|
1223
|
|
|
* </code> |
|
1224
|
|
|
* to be executed when evaluateExpression is called. All attached behaviors are notified through |
|
1225
|
|
|
* dyEvaluateExpressionFilter. The chaining is important in this function due to the filtering |
|
1226
|
|
|
* pass-through effect. |
|
1227
|
|
|
* |
|
1228
|
|
|
* @param string PHP expression |
|
1229
|
|
|
* @return mixed the expression result |
|
1230
|
|
|
* @throws TInvalidOperationException if the expression is invalid |
|
1231
|
|
|
*/ |
|
1232
|
|
|
public function evaluateExpression($expression) |
|
1233
|
|
|
{ |
|
1234
|
|
|
$expression=$this->dyEvaluateExpressionFilter($expression); |
|
|
|
|
|
|
1235
|
|
|
try |
|
1236
|
|
|
{ |
|
1237
|
|
|
if(eval("\$result=$expression;")===false) |
|
|
|
|
|
|
1238
|
|
|
throw new Exception(''); |
|
1239
|
|
|
return $result; |
|
|
|
|
|
|
1240
|
|
|
} |
|
1241
|
|
|
catch(Exception $e) |
|
1242
|
|
|
{ |
|
1243
|
|
|
throw new TInvalidOperationException('component_expression_invalid',get_class($this),$expression,$e->getMessage()); |
|
1244
|
|
|
} |
|
1245
|
|
|
} |
|
1246
|
|
|
|
|
1247
|
|
|
/** |
|
1248
|
|
|
* Evaluates a list of PHP statements. |
|
1249
|
|
|
* |
|
1250
|
|
|
* Behaviors may implement the function: |
|
1251
|
|
|
* <code> |
|
1252
|
|
|
* public function dyEvaluateStatementsFilter($statements, $chain) { |
|
1253
|
|
|
* return $chain->dyEvaluateStatementsFilter(str_replace('foo', 'bar', $statements)); //example |
|
1254
|
|
|
* } |
|
1255
|
|
|
* </code> |
|
1256
|
|
|
* to be executed when evaluateStatements is called. All attached behaviors are notified through |
|
1257
|
|
|
* dyEvaluateStatementsFilter. The chaining is important in this function due to the filtering |
|
1258
|
|
|
* pass-through effect. |
|
1259
|
|
|
* |
|
1260
|
|
|
* @param string PHP statements |
|
1261
|
|
|
* @return string content echoed or printed by the PHP statements |
|
1262
|
|
|
* @throws TInvalidOperationException if the statements are invalid |
|
1263
|
|
|
*/ |
|
1264
|
|
|
public function evaluateStatements($statements) |
|
1265
|
|
|
{ |
|
1266
|
|
|
$statements=$this->dyEvaluateStatementsFilter($statements); |
|
|
|
|
|
|
1267
|
|
|
try |
|
1268
|
|
|
{ |
|
1269
|
|
|
ob_start(); |
|
1270
|
|
|
if(eval($statements)===false) |
|
|
|
|
|
|
1271
|
|
|
throw new Exception(''); |
|
1272
|
|
|
$content=ob_get_contents(); |
|
1273
|
|
|
ob_end_clean(); |
|
1274
|
|
|
return $content; |
|
1275
|
|
|
} |
|
1276
|
|
|
catch(Exception $e) |
|
1277
|
|
|
{ |
|
1278
|
|
|
throw new TInvalidOperationException('component_statements_invalid',get_class($this),$statements,$e->getMessage()); |
|
1279
|
|
|
} |
|
1280
|
|
|
} |
|
1281
|
|
|
|
|
1282
|
|
|
/** |
|
1283
|
|
|
* This method is invoked after the component is instantiated by a template. |
|
1284
|
|
|
* When this method is invoked, the component's properties have been initialized. |
|
1285
|
|
|
* The default implementation of this method will invoke |
|
1286
|
|
|
* the potential parent component's {@link addParsedObject}. |
|
1287
|
|
|
* This method can be overridden. |
|
1288
|
|
|
* |
|
1289
|
|
|
* Behaviors may implement the function: |
|
1290
|
|
|
* <code> |
|
1291
|
|
|
* public function dyCreatedOnTemplate($parent, $chain) { |
|
1292
|
|
|
* return $chain->dyCreatedOnTemplate($parent); //example |
|
1293
|
|
|
* } |
|
1294
|
|
|
* </code> |
|
1295
|
|
|
* to be executed when createdOnTemplate is called. All attached behaviors are notified through |
|
1296
|
|
|
* dyCreatedOnTemplate. |
|
1297
|
|
|
* |
|
1298
|
|
|
* @param TComponent potential parent of this control |
|
1299
|
|
|
* @see addParsedObject |
|
1300
|
|
|
*/ |
|
1301
|
|
|
public function createdOnTemplate($parent) |
|
1302
|
|
|
{ |
|
1303
|
|
|
$parent=$this->dyCreatedOnTemplate($parent); |
|
|
|
|
|
|
1304
|
|
|
$parent->addParsedObject($this); |
|
1305
|
|
|
} |
|
1306
|
|
|
|
|
1307
|
|
|
/** |
|
1308
|
|
|
* Processes an object that is created during parsing template. |
|
1309
|
|
|
* The object can be either a component or a static text string. |
|
1310
|
|
|
* This method can be overridden to customize the handling of newly created objects in template. |
|
1311
|
|
|
* Only framework developers and control developers should use this method. |
|
1312
|
|
|
* |
|
1313
|
|
|
* Behaviors may implement the function: |
|
1314
|
|
|
* <code> |
|
1315
|
|
|
* public function dyAddParsedObject($object[, $chain]) { |
|
1316
|
|
|
* } |
|
1317
|
|
|
* </code> |
|
1318
|
|
|
* to be executed when addParsedObject is called. All attached behaviors are notified through |
|
1319
|
|
|
* dyAddParsedObject. |
|
1320
|
|
|
* |
|
1321
|
|
|
* @param string|TComponent text string or component parsed and instantiated in template |
|
1322
|
|
|
* @see createdOnTemplate |
|
1323
|
|
|
*/ |
|
1324
|
|
|
public function addParsedObject($object) |
|
1325
|
|
|
{ |
|
1326
|
|
|
$this->dyAddParsedObject($object); |
|
|
|
|
|
|
1327
|
|
|
} |
|
1328
|
|
|
|
|
1329
|
|
|
|
|
1330
|
|
|
/** |
|
1331
|
|
|
* This is the method registered for all instanced objects should a class behavior be added after |
|
1332
|
|
|
* the class is instanced. Only when the class to which the behavior is being added is in this |
|
1333
|
|
|
* object's class hierarchy, via {@link getClassHierarchy}, is the behavior added to this instance. |
|
1334
|
|
|
* @param $sender the application |
|
1335
|
|
|
* @param $param TClassBehaviorEventParameter |
|
1336
|
|
|
* @since 3.2.3 |
|
1337
|
|
|
*/ |
|
1338
|
|
|
public function fxAttachClassBehavior($sender,$param) { |
|
1339
|
|
|
if(in_array($param->getClass(),$this->getClassHierarchy(true))) |
|
1340
|
|
|
return $this->attachBehavior($param->getName(),$param->getBehavior(),$param->getPriority()); |
|
1341
|
|
|
} |
|
1342
|
|
|
|
|
1343
|
|
|
|
|
1344
|
|
|
/** |
|
1345
|
|
|
* This is the method registered for all instanced objects should a class behavior be removed after |
|
1346
|
|
|
* the class is instanced. Only when the class to which the behavior is being added is in this |
|
1347
|
|
|
* object's class hierarchy, via {@link getClassHierarchy}, is the behavior removed from this instance. |
|
1348
|
|
|
* @param $sender the application |
|
1349
|
|
|
* @param $param TClassBehaviorEventParameter |
|
1350
|
|
|
* @since 3.2.3 |
|
1351
|
|
|
*/ |
|
1352
|
|
|
public function fxDetachClassBehavior($sender,$param) { |
|
1353
|
|
|
if(in_array($param->getClass(),$this->getClassHierarchy(true))) |
|
1354
|
|
|
return $this->detachBehavior($param->getName(),$param->getPriority()); |
|
1355
|
|
|
} |
|
1356
|
|
|
|
|
1357
|
|
|
|
|
1358
|
|
|
/** |
|
1359
|
|
|
* This will add a class behavior to all classes instanced (that are listening) and future newly instanced objects. |
|
1360
|
|
|
* This registers the behavior for future instances and pushes the changes to all the instances that are listening as well. |
|
1361
|
|
|
* The universal class behaviors are stored in an inverted stack with the latest class behavior being at the first position in the array. |
|
1362
|
|
|
* This is done so class behaviors are added last first. |
|
1363
|
|
|
* @param string name the key of the class behavior |
|
1364
|
|
|
* @param object|string class behavior or name of the object behavior per instance |
|
1365
|
|
|
* @param string|class string of class or class on which to attach this behavior. Defaults to null which will error |
|
1366
|
|
|
* but more important, if this is on PHP 5.3 it will use Late Static Binding to derive the class |
|
1367
|
|
|
* it should extend. |
|
1368
|
|
|
* <code> |
|
1369
|
|
|
* TPanel::attachClassBehavior('javascripts', (new TJsPanelBehavior())->init($this)); |
|
1370
|
|
|
* </code> |
|
1371
|
|
|
* @param numeric|null priority of behavior, default: null the default priority of the {@link TPriorityList} Optional. |
|
1372
|
|
|
* @throws TInvalidOperationException if the class behavior is being added to a {@link TComponent}; due to recursion. |
|
1373
|
|
|
* @throws TInvalidOperationException if the class behavior is already defined |
|
1374
|
|
|
* @since 3.2.3 |
|
1375
|
|
|
*/ |
|
1376
|
|
|
public static function attachClassBehavior($name,$behavior,$class=null,$priority=null) { |
|
1377
|
|
|
if(!$class&&function_exists('get_called_class')) |
|
1378
|
|
|
$class=get_called_class(); |
|
1379
|
|
|
if(!$class) |
|
1380
|
|
|
throw new TInvalidOperationException('component_no_class_provided_nor_late_binding'); |
|
1381
|
|
|
|
|
1382
|
|
|
if(!is_string($name)) |
|
1383
|
|
|
$name=get_class($name); |
|
1384
|
|
|
$class=strtolower($class); |
|
1385
|
|
|
if($class==='tcomponent') |
|
1386
|
|
|
throw new TInvalidOperationException('component_no_tcomponent_class_behaviors'); |
|
1387
|
|
|
if(empty(self::$_um[$class])) |
|
1388
|
|
|
self::$_um[$class]=array(); |
|
1389
|
|
|
if(isset(self::$_um[$class][$name])) |
|
1390
|
|
|
throw new TInvalidOperationException('component_class_behavior_defined',$class,$name); |
|
1391
|
|
|
$param=new TClassBehaviorEventParameter($class,$name,$behavior,$priority); |
|
1392
|
|
|
self::$_um[$class]=array($name=>$param)+self::$_um[$class]; |
|
1393
|
|
|
$behaviorObject=is_string($behavior)?new $behavior:$behavior; |
|
1394
|
|
|
return $behaviorObject->raiseEvent('fxAttachClassBehavior',null,$param); |
|
1395
|
|
|
} |
|
1396
|
|
|
|
|
1397
|
|
|
|
|
1398
|
|
|
/** |
|
1399
|
|
|
* This will remove a behavior from a class. It unregisters it from future instances and |
|
1400
|
|
|
* pulls the changes from all the instances that are listening as well. |
|
1401
|
|
|
* PHP 5.3 uses Late Static Binding to derive the static class upon which this method is called. |
|
1402
|
|
|
* @param $name the key of the class behavior |
|
1403
|
|
|
* @param $class string class on which to attach this behavior. Defaults to null. |
|
1404
|
|
|
* @param $priority numeric|null|false priority. false is any priority, null is default |
|
1405
|
|
|
* {@link TPriorityList} priority, and numeric is a specific priority. |
|
1406
|
|
|
* @throws Exception if the the class cannot be derived from Late Static Binding and is not |
|
1407
|
|
|
* not supplied as a parameter. |
|
1408
|
|
|
* @since 3.2.3 |
|
1409
|
|
|
*/ |
|
1410
|
|
|
public static function detachClassBehavior($name,$class=null,$priority=false) { |
|
1411
|
|
|
if(!$class&&function_exists('get_called_class')) |
|
1412
|
|
|
$class=get_called_class(); |
|
1413
|
|
|
if(!$class) |
|
1414
|
|
|
throw new TInvalidOperationException('component_no_class_provided_nor_late_binding'); |
|
1415
|
|
|
|
|
1416
|
|
|
$class=strtolower($class); |
|
1417
|
|
|
if(!is_string($name)) |
|
1418
|
|
|
$name=get_class($name); |
|
1419
|
|
|
if(empty(self::$_um[$class])||!isset(self::$_um[$class][$name])) |
|
1420
|
|
|
return false; |
|
1421
|
|
|
$param=self::$_um[$class][$name]; |
|
1422
|
|
|
$behavior=$param->getBehavior(); |
|
1423
|
|
|
unset(self::$_um[$class][$name]); |
|
1424
|
|
|
$behaviorObject=is_string($behavior)?new $behavior:$behavior; |
|
1425
|
|
|
return $behaviorObject->raiseEvent('fxDetachClassBehavior',null,$param); |
|
1426
|
|
|
} |
|
1427
|
|
|
|
|
1428
|
|
|
/** |
|
1429
|
|
|
* Returns the named behavior object. |
|
1430
|
|
|
* The name 'asa' stands for 'as a'. |
|
1431
|
|
|
* @param string the behavior name |
|
1432
|
|
|
* @return IBehavior the behavior object, or null if the behavior does not exist |
|
1433
|
|
|
* @since 3.2.3 |
|
1434
|
|
|
*/ |
|
1435
|
|
|
public function asa($behaviorname) |
|
1436
|
|
|
{ |
|
1437
|
|
|
return isset($this->_m[$behaviorname])?$this->_m[$behaviorname]:null; |
|
1438
|
|
|
} |
|
1439
|
|
|
|
|
1440
|
|
|
/** |
|
1441
|
|
|
* Returns whether or not the object or any of the behaviors are of a particular class. |
|
1442
|
|
|
* The name 'isa' stands for 'is a'. This first checks if $this is an instanceof the class. |
|
1443
|
|
|
* It then checks each Behavior. If a behavior implements {@link IInstanceCheck}, |
|
1444
|
|
|
* then the behavior can determine what it is an instanceof. If this behavior function returns true, |
|
1445
|
|
|
* then this method returns true. If the behavior instance checking function returns false, |
|
1446
|
|
|
* then no further checking is performed as it is assumed to be correct. |
|
1447
|
|
|
* |
|
1448
|
|
|
* If the behavior instance check function returns nothing or null or the behavior |
|
1449
|
|
|
* doesn't implement the {@link IInstanceCheck} interface, then the default instanceof occurs. |
|
1450
|
|
|
* The default isa behavior is to check if the behavior is an instanceof the class. |
|
1451
|
|
|
* |
|
1452
|
|
|
* The behavior {@link IInstanceCheck} is to allow a behavior to have the host object |
|
1453
|
|
|
* act as a completely different object. |
|
1454
|
|
|
* |
|
1455
|
|
|
* @param class or string |
|
1456
|
|
|
* @return boolean whether or not the object or a behavior is an instance of a particular class |
|
1457
|
|
|
* @since 3.2.3 |
|
1458
|
|
|
*/ |
|
1459
|
|
|
public function isa($class) |
|
1460
|
|
|
{ |
|
1461
|
|
|
if($this instanceof $class) |
|
1462
|
|
|
return true; |
|
1463
|
|
|
if($this->_m!==null&&$this->_behaviorsenabled) |
|
1464
|
|
|
foreach($this->_m->toArray() as $behavior){ |
|
1465
|
|
|
if(($behavior instanceof IBehavior)&&!$behavior->getEnabled()) |
|
1466
|
|
|
continue; |
|
1467
|
|
|
|
|
1468
|
|
|
$check = null; |
|
1469
|
|
|
if(($behavior->isa('IInstanceCheck'))&&$check=$behavior->isinstanceof($class,$this)) |
|
1470
|
|
|
return true; |
|
1471
|
|
|
if($check===null&&($behavior->isa($class))) |
|
1472
|
|
|
return true; |
|
1473
|
|
|
} |
|
1474
|
|
|
return false; |
|
1475
|
|
|
} |
|
1476
|
|
|
|
|
1477
|
|
|
/** |
|
1478
|
|
|
* Attaches a list of behaviors to the component. |
|
1479
|
|
|
* Each behavior is indexed by its name and should be an instance of |
|
1480
|
|
|
* {@link IBehavior}, a string specifying the behavior class, or a |
|
1481
|
|
|
* {@link TClassBehaviorEventParameter}. |
|
1482
|
|
|
* @param array list of behaviors to be attached to the component |
|
1483
|
|
|
* @since 3.2.3 |
|
1484
|
|
|
*/ |
|
1485
|
|
|
public function attachBehaviors($behaviors) |
|
1486
|
|
|
{ |
|
1487
|
|
|
foreach($behaviors as $name=>$behavior) |
|
1488
|
|
|
if($behavior instanceof TClassBehaviorEventParameter) |
|
1489
|
|
|
$this->attachBehavior($behavior->getName(),$behavior->getBehavior(),$behavior->getPriority()); |
|
1490
|
|
|
else |
|
1491
|
|
|
$this->attachBehavior($name,$behavior); |
|
1492
|
|
|
} |
|
1493
|
|
|
|
|
1494
|
|
|
/** |
|
1495
|
|
|
* Detaches select behaviors from the component. |
|
1496
|
|
|
* Each behavior is indexed by its name and should be an instance of |
|
1497
|
|
|
* {@link IBehavior}, a string specifying the behavior class, or a |
|
1498
|
|
|
* {@link TClassBehaviorEventParameter}. |
|
1499
|
|
|
* @param array list of behaviors to be detached from the component |
|
1500
|
|
|
* @since 3.2.3 |
|
1501
|
|
|
*/ |
|
1502
|
|
|
public function detachBehaviors($behaviors) |
|
1503
|
|
|
{ |
|
1504
|
|
|
if($this->_m!==null) |
|
1505
|
|
|
{ |
|
1506
|
|
|
foreach($behaviors as $name=>$behavior) |
|
1507
|
|
|
if($behavior instanceof TClassBehaviorEventParameter) |
|
1508
|
|
|
$this->detachBehavior($behavior->getName(),$behavior->getPriority()); |
|
1509
|
|
|
else |
|
1510
|
|
|
$this->detachBehavior(is_string($behavior)?$behavior:$name); |
|
1511
|
|
|
} |
|
1512
|
|
|
} |
|
1513
|
|
|
|
|
1514
|
|
|
/** |
|
1515
|
|
|
* Detaches all behaviors from the component. |
|
1516
|
|
|
* @since 3.2.3 |
|
1517
|
|
|
*/ |
|
1518
|
|
|
public function clearBehaviors() |
|
1519
|
|
|
{ |
|
1520
|
|
|
if($this->_m!==null) |
|
1521
|
|
|
{ |
|
1522
|
|
|
foreach($this->_m->toArray() as $name=>$behavior) |
|
1523
|
|
|
$this->detachBehavior($name); |
|
1524
|
|
|
$this->_m=null; |
|
1525
|
|
|
} |
|
1526
|
|
|
} |
|
1527
|
|
|
|
|
1528
|
|
|
/** |
|
1529
|
|
|
* Attaches a behavior to this component. |
|
1530
|
|
|
* This method will create the behavior object based on the given |
|
1531
|
|
|
* configuration. After that, the behavior object will be initialized |
|
1532
|
|
|
* by calling its {@link IBehavior::attach} method. |
|
1533
|
|
|
* |
|
1534
|
|
|
* Already attached behaviors may implement the function: |
|
1535
|
|
|
* <code> |
|
1536
|
|
|
* public function dyAttachBehavior($name,$behavior[, $chain]) { |
|
1537
|
|
|
* } |
|
1538
|
|
|
* </code> |
|
1539
|
|
|
* to be executed when attachBehavior is called. All attached behaviors are notified through |
|
1540
|
|
|
* dyAttachBehavior. |
|
1541
|
|
|
* |
|
1542
|
|
|
* @param string the behavior's name. It should uniquely identify this behavior. |
|
1543
|
|
|
* @param mixed the behavior configuration. This is passed as the first |
|
1544
|
|
|
* parameter to {@link YiiBase::createComponent} to create the behavior object. |
|
1545
|
|
|
* @return IBehavior the behavior object |
|
1546
|
|
|
* @since 3.2.3 |
|
1547
|
|
|
*/ |
|
1548
|
|
|
public function attachBehavior($name,$behavior,$priority=null) |
|
1549
|
|
|
{ |
|
1550
|
|
|
if(is_string($behavior)) |
|
1551
|
|
|
$behavior=Prado::createComponent($behavior); |
|
1552
|
|
|
if(!($behavior instanceof IBaseBehavior)) |
|
1553
|
|
|
throw new TInvalidDataTypeException('component_not_a_behavior',get_class($behavior)); |
|
1554
|
|
|
if($behavior instanceof IBehavior) |
|
1555
|
|
|
$behavior->setEnabled(true); |
|
1556
|
|
|
if($this->_m===null) |
|
1557
|
|
|
$this->_m=new TPriorityMap; |
|
1558
|
|
|
$behavior->attach($this); |
|
1559
|
|
|
$this->dyAttachBehavior($name,$behavior); |
|
|
|
|
|
|
1560
|
|
|
$this->_m->add($name,$behavior,$priority); |
|
1561
|
|
|
return $behavior; |
|
1562
|
|
|
} |
|
1563
|
|
|
|
|
1564
|
|
|
/** |
|
1565
|
|
|
* Detaches a behavior from the component. |
|
1566
|
|
|
* The behavior's {@link IBehavior::detach} method will be invoked. |
|
1567
|
|
|
* |
|
1568
|
|
|
* Behaviors may implement the function: |
|
1569
|
|
|
* <code> |
|
1570
|
|
|
* public function dyDetachBehavior($name,$behavior[, $chain]) { |
|
1571
|
|
|
* } |
|
1572
|
|
|
* </code> |
|
1573
|
|
|
* to be executed when detachBehavior is called. All attached behaviors are notified through |
|
1574
|
|
|
* dyDetachBehavior. |
|
1575
|
|
|
* |
|
1576
|
|
|
* @param string the behavior's name. It uniquely identifies the behavior. |
|
1577
|
|
|
* @param numeric the behavior's priority. This defaults to false, aka any priority. |
|
1578
|
|
|
* @return IBehavior the detached behavior. Null if the behavior does not exist. |
|
1579
|
|
|
* @since 3.2.3 |
|
1580
|
|
|
*/ |
|
1581
|
|
|
public function detachBehavior($name,$priority=false) |
|
1582
|
|
|
{ |
|
1583
|
|
|
if($this->_m!=null&&isset($this->_m[$name])) |
|
1584
|
|
|
{ |
|
1585
|
|
|
$this->_m[$name]->detach($this); |
|
1586
|
|
|
$behavior=$this->_m->itemAt($name); |
|
1587
|
|
|
$this->_m->remove($name,$priority); |
|
1588
|
|
|
$this->dyDetachBehavior($name,$behavior); |
|
|
|
|
|
|
1589
|
|
|
return $behavior; |
|
1590
|
|
|
} |
|
1591
|
|
|
} |
|
1592
|
|
|
|
|
1593
|
|
|
/** |
|
1594
|
|
|
* Enables all behaviors attached to this component independent of the behaviors |
|
1595
|
|
|
* |
|
1596
|
|
|
* Behaviors may implement the function: |
|
1597
|
|
|
* <code> |
|
1598
|
|
|
* public function dyEnableBehaviors($name,$behavior[, $chain]) { |
|
1599
|
|
|
* } |
|
1600
|
|
|
* </code> |
|
1601
|
|
|
* to be executed when enableBehaviors is called. All attached behaviors are notified through |
|
1602
|
|
|
* dyEnableBehaviors. |
|
1603
|
|
|
* @since 3.2.3 |
|
1604
|
|
|
*/ |
|
1605
|
|
|
public function enableBehaviors() |
|
1606
|
|
|
{ |
|
1607
|
|
|
if(!$this->_behaviorsenabled) |
|
1608
|
|
|
{ |
|
1609
|
|
|
$this->_behaviorsenabled=true; |
|
1610
|
|
|
$this->dyEnableBehaviors(); |
|
|
|
|
|
|
1611
|
|
|
} |
|
1612
|
|
|
} |
|
1613
|
|
|
|
|
1614
|
|
|
/** |
|
1615
|
|
|
* Disables all behaviors attached to this component independent of the behaviors |
|
1616
|
|
|
* |
|
1617
|
|
|
* Behaviors may implement the function: |
|
1618
|
|
|
* <code> |
|
1619
|
|
|
* public function dyDisableBehaviors($name,$behavior[, $chain]) { |
|
1620
|
|
|
* } |
|
1621
|
|
|
* </code> |
|
1622
|
|
|
* to be executed when disableBehaviors is called. All attached behaviors are notified through |
|
1623
|
|
|
* dyDisableBehaviors. |
|
1624
|
|
|
* @since 3.2.3 |
|
1625
|
|
|
*/ |
|
1626
|
|
|
public function disableBehaviors() |
|
1627
|
|
|
{ |
|
1628
|
|
|
if($this->_behaviorsenabled) |
|
1629
|
|
|
{ |
|
1630
|
|
|
$this->dyDisableBehaviors(); |
|
|
|
|
|
|
1631
|
|
|
$this->_behaviorsenabled=false; |
|
1632
|
|
|
} |
|
1633
|
|
|
} |
|
1634
|
|
|
|
|
1635
|
|
|
|
|
1636
|
|
|
/** |
|
1637
|
|
|
* Returns if all the behaviors are turned on or off for the object. |
|
1638
|
|
|
* @return boolean whether or not all behaviors are enabled (true) or not (false) |
|
1639
|
|
|
* @since 3.2.3 |
|
1640
|
|
|
*/ |
|
1641
|
|
|
public function getBehaviorsEnabled() |
|
1642
|
|
|
{ |
|
1643
|
|
|
return $this->_behaviorsenabled; |
|
1644
|
|
|
} |
|
1645
|
|
|
|
|
1646
|
|
|
/** |
|
1647
|
|
|
* Enables an attached object behavior. This cannot enable or disable whole class behaviors. |
|
1648
|
|
|
* A behavior is only effective when it is enabled. |
|
1649
|
|
|
* A behavior is enabled when first attached. |
|
1650
|
|
|
* |
|
1651
|
|
|
* Behaviors may implement the function: |
|
1652
|
|
|
* <code> |
|
1653
|
|
|
* public function dyEnableBehavior($name,$behavior[, $chain]) { |
|
1654
|
|
|
* } |
|
1655
|
|
|
* </code> |
|
1656
|
|
|
* to be executed when enableBehavior is called. All attached behaviors are notified through |
|
1657
|
|
|
* dyEnableBehavior. |
|
1658
|
|
|
* |
|
1659
|
|
|
* @param string the behavior's name. It uniquely identifies the behavior. |
|
1660
|
|
|
* @since 3.2.3 |
|
1661
|
|
|
*/ |
|
1662
|
|
|
public function enableBehavior($name) |
|
1663
|
|
|
{ |
|
1664
|
|
|
if($this->_m!=null&&isset($this->_m[$name])){ |
|
1665
|
|
|
if($this->_m[$name] instanceof IBehavior) { |
|
1666
|
|
|
$this->_m[$name]->setEnabled(true); |
|
1667
|
|
|
$this->dyEnableBehavior($name,$this->_m[$name]); |
|
|
|
|
|
|
1668
|
|
|
return true; |
|
1669
|
|
|
} |
|
1670
|
|
|
return false; |
|
1671
|
|
|
} |
|
1672
|
|
|
return null; |
|
1673
|
|
|
} |
|
1674
|
|
|
|
|
1675
|
|
|
/** |
|
1676
|
|
|
* Disables an attached behavior. This cannot enable or disable whole class behaviors. |
|
1677
|
|
|
* A behavior is only effective when it is enabled. |
|
1678
|
|
|
* |
|
1679
|
|
|
* Behaviors may implement the function: |
|
1680
|
|
|
* <code> |
|
1681
|
|
|
* public function dyDisableBehavior($name,$behavior[, $chain]) { |
|
1682
|
|
|
* } |
|
1683
|
|
|
* </code> |
|
1684
|
|
|
* to be executed when disableBehavior is called. All attached behaviors are notified through |
|
1685
|
|
|
* dyDisableBehavior. |
|
1686
|
|
|
* |
|
1687
|
|
|
* @param string the behavior's name. It uniquely identifies the behavior. |
|
1688
|
|
|
* @since 3.2.3 |
|
1689
|
|
|
*/ |
|
1690
|
|
|
public function disableBehavior($name) |
|
1691
|
|
|
{ |
|
1692
|
|
|
if($this->_m!=null&&isset($this->_m[$name])){ |
|
1693
|
|
|
if($this->_m[$name] instanceof IBehavior) { |
|
1694
|
|
|
$this->_m[$name]->setEnabled(false); |
|
1695
|
|
|
$this->dyDisableBehavior($name,$this->_m[$name]); |
|
|
|
|
|
|
1696
|
|
|
return true; |
|
1697
|
|
|
} |
|
1698
|
|
|
return false; |
|
1699
|
|
|
} |
|
1700
|
|
|
return null; |
|
1701
|
|
|
} |
|
1702
|
|
|
|
|
1703
|
|
|
/** |
|
1704
|
|
|
* Returns an array with the names of all variables of that object that should be serialized. |
|
1705
|
|
|
* Do not call this method. This is a PHP magic method that will be called automatically |
|
1706
|
|
|
* prior to any serialization. |
|
1707
|
|
|
*/ |
|
1708
|
|
|
public function __sleep() |
|
1709
|
|
|
{ |
|
1710
|
|
|
$a = (array)$this; |
|
1711
|
|
|
$a = array_keys($a); |
|
1712
|
|
|
$exprops = array(); |
|
1713
|
|
|
$this->_getZappableSleepProps($exprops); |
|
1714
|
|
|
return array_diff($a, $exprops); |
|
1715
|
|
|
} |
|
1716
|
|
|
|
|
1717
|
|
|
/** |
|
1718
|
|
|
* Returns an array with the names of all variables of this object that should NOT be serialized |
|
1719
|
|
|
* because their value is the default one or useless to be cached for the next page loads. |
|
1720
|
|
|
* Reimplement in derived classes to add new variables, but remember to also to call the parent |
|
1721
|
|
|
* implementation first. |
|
1722
|
|
|
*/ |
|
1723
|
|
|
protected function _getZappableSleepProps(&$exprops) |
|
1724
|
|
|
{ |
|
1725
|
|
|
if($this->_listeningenabled===false) |
|
1726
|
|
|
$exprops[] = "\0TComponent\0_listeningenabled"; |
|
1727
|
|
|
if($this->_behaviorsenabled===true) |
|
1728
|
|
|
$exprops[] = "\0TComponent\0_behaviorsenabled"; |
|
1729
|
|
|
if ($this->_e===array()) |
|
1730
|
|
|
$exprops[] = "\0TComponent\0_e"; |
|
1731
|
|
|
if ($this->_m===null) |
|
1732
|
|
|
$exprops[] = "\0TComponent\0_m"; |
|
1733
|
|
|
} |
|
1734
|
|
|
} |
|
1735
|
|
|
|
|
1736
|
|
|
|
|
1737
|
|
|
/** |
|
1738
|
|
|
* IDynamicMethods interface. |
|
1739
|
|
|
* IDynamicMethods marks an object to receive undefined global or dynamic events. |
|
1740
|
|
|
* |
|
1741
|
|
|
* @author Brad Anderson <[email protected]> |
|
1742
|
|
|
* @version $Id$ |
|
1743
|
|
|
* @package System |
|
1744
|
|
|
* @since 3.2.3 |
|
1745
|
|
|
*/ |
|
1746
|
|
|
interface IDynamicMethods |
|
1747
|
|
|
{ |
|
1748
|
|
|
public function __dycall($method,$args); |
|
|
|
|
|
|
1749
|
|
|
} |
|
1750
|
|
|
|
|
1751
|
|
|
|
|
1752
|
|
|
|
|
1753
|
|
|
/** |
|
1754
|
|
|
* TClassBehaviorEventParameter class. |
|
1755
|
|
|
* TClassBehaviorEventParameter is the parameter sent with the class behavior changes. |
|
1756
|
|
|
* |
|
1757
|
|
|
* @author Brad Anderson <[email protected]> |
|
1758
|
|
|
* @version $Id$ |
|
1759
|
|
|
* @package System |
|
1760
|
|
|
* @since 3.2.3 |
|
1761
|
|
|
*/ |
|
1762
|
|
|
class TClassBehaviorEventParameter extends TEventParameter |
|
1763
|
|
|
{ |
|
1764
|
|
|
private $_class; |
|
1765
|
|
|
private $_name; |
|
1766
|
|
|
private $_behavior; |
|
1767
|
|
|
private $_priority; |
|
1768
|
|
|
|
|
1769
|
|
|
/** |
|
1770
|
|
|
* Holds the parameters for the Class Behavior Events |
|
1771
|
|
|
* @param string $class this is the class to get the behavior |
|
1772
|
|
|
* @param string $name the name of the behavior |
|
1773
|
|
|
* @param object $behavior this is the behavior to implement the class behavior |
|
1774
|
|
|
*/ |
|
1775
|
|
|
public function __construct($class,$name,$behavior,$priority) |
|
1776
|
|
|
{ |
|
1777
|
|
|
$this->_class=$class; |
|
1778
|
|
|
$this->_name=$name; |
|
1779
|
|
|
$this->_behavior=$behavior; |
|
1780
|
|
|
$this->_priority=$priority; |
|
1781
|
|
|
} |
|
1782
|
|
|
|
|
1783
|
|
|
/** |
|
1784
|
|
|
* This is the class to get the behavior |
|
1785
|
|
|
* @return string the class to get the behavior |
|
1786
|
|
|
*/ |
|
1787
|
|
|
public function getClass() |
|
1788
|
|
|
{ |
|
1789
|
|
|
return $this->_class; |
|
1790
|
|
|
} |
|
1791
|
|
|
|
|
1792
|
|
|
/** |
|
1793
|
|
|
* name of the behavior |
|
1794
|
|
|
* @return string the name to get the behavior |
|
1795
|
|
|
*/ |
|
1796
|
|
|
public function getName() |
|
1797
|
|
|
{ |
|
1798
|
|
|
return $this->_name; |
|
1799
|
|
|
} |
|
1800
|
|
|
|
|
1801
|
|
|
/** |
|
1802
|
|
|
* This is the behavior which the class is to get |
|
1803
|
|
|
* @return object the behavior to implement |
|
1804
|
|
|
*/ |
|
1805
|
|
|
public function getBehavior() |
|
1806
|
|
|
{ |
|
1807
|
|
|
return $this->_behavior; |
|
1808
|
|
|
} |
|
1809
|
|
|
|
|
1810
|
|
|
/** |
|
1811
|
|
|
* This is the priority which the behavior is to get |
|
1812
|
|
|
* @return numeric the priority of the behavior |
|
1813
|
|
|
*/ |
|
1814
|
|
|
public function getPriority() |
|
1815
|
|
|
{ |
|
1816
|
|
|
return $this->_priority; |
|
1817
|
|
|
} |
|
1818
|
|
|
} |
|
1819
|
|
|
|
|
1820
|
|
|
|
|
1821
|
|
|
/** |
|
1822
|
|
|
* TEnumerable class. |
|
1823
|
|
|
* TEnumerable is the base class for all enumerable types. |
|
1824
|
|
|
* To define an enumerable type, extend TEnumberable and define string constants. |
|
1825
|
|
|
* Each constant represents an enumerable value. |
|
1826
|
|
|
* The constant name must be the same as the constant value. |
|
1827
|
|
|
* For example, |
|
1828
|
|
|
* <code> |
|
1829
|
|
|
* class TTextAlign extends TEnumerable |
|
1830
|
|
|
* { |
|
1831
|
|
|
* const Left='Left'; |
|
1832
|
|
|
* const Right='Right'; |
|
1833
|
|
|
* } |
|
1834
|
|
|
* </code> |
|
1835
|
|
|
* Then, one can use the enumerable values such as TTextAlign::Left and |
|
1836
|
|
|
* TTextAlign::Right. |
|
1837
|
|
|
* |
|
1838
|
|
|
* @author Qiang Xue <[email protected]> |
|
1839
|
|
|
* @package System |
|
1840
|
|
|
* @since 3.0 |
|
1841
|
|
|
*/ |
|
1842
|
|
|
class TEnumerable implements Iterator |
|
1843
|
|
|
{ |
|
1844
|
|
|
private $_enums=array(); |
|
1845
|
|
|
|
|
1846
|
|
|
public function __construct() { |
|
1847
|
|
|
$reflection=new ReflectionClass($this); |
|
1848
|
|
|
$this->_enums=$reflection->getConstants(); |
|
1849
|
|
|
} |
|
1850
|
|
|
|
|
1851
|
|
|
public function current() { |
|
1852
|
|
|
return current($this->_enums); |
|
1853
|
|
|
} |
|
1854
|
|
|
|
|
1855
|
|
|
public function key() { |
|
1856
|
|
|
return key($this->_enums); |
|
1857
|
|
|
} |
|
1858
|
|
|
|
|
1859
|
|
|
public function next() { |
|
1860
|
|
|
return next($this->_enums); |
|
1861
|
|
|
} |
|
1862
|
|
|
|
|
1863
|
|
|
public function rewind() { |
|
1864
|
|
|
reset($this->_enums); |
|
1865
|
|
|
} |
|
1866
|
|
|
|
|
1867
|
|
|
public function valid() { |
|
1868
|
|
|
return $this->current()!==false; |
|
1869
|
|
|
} |
|
1870
|
|
|
} |
|
1871
|
|
|
|
|
1872
|
|
|
/** |
|
1873
|
|
|
* TPropertyValue class |
|
1874
|
|
|
* |
|
1875
|
|
|
* TPropertyValue is a utility class that provides static methods |
|
1876
|
|
|
* to convert component property values to specific types. |
|
1877
|
|
|
* |
|
1878
|
|
|
* TPropertyValue is commonly used in component setter methods to ensure |
|
1879
|
|
|
* the new property value is of specific type. |
|
1880
|
|
|
* For example, a boolean-typed property setter method would be as follows, |
|
1881
|
|
|
* <code> |
|
1882
|
|
|
* function setPropertyName($value) { |
|
1883
|
|
|
* $value=TPropertyValue::ensureBoolean($value); |
|
1884
|
|
|
* // $value is now of boolean type |
|
1885
|
|
|
* } |
|
1886
|
|
|
* </code> |
|
1887
|
|
|
* |
|
1888
|
|
|
* Properties can be of the following types with specific type conversion rules: |
|
1889
|
|
|
* - string: a boolean value will be converted to 'true' or 'false'. |
|
1890
|
|
|
* - boolean: string 'true' (case-insensitive) will be converted to true, |
|
1891
|
|
|
* string 'false' (case-insensitive) will be converted to false. |
|
1892
|
|
|
* - integer |
|
1893
|
|
|
* - float |
|
1894
|
|
|
* - array: string starting with '(' and ending with ')' will be considered as |
|
1895
|
|
|
* as an array expression and will be evaluated. Otherwise, an array |
|
1896
|
|
|
* with the value to be ensured is returned. |
|
1897
|
|
|
* - object |
|
1898
|
|
|
* - enum: enumerable type, represented by an array of strings. |
|
1899
|
|
|
* |
|
1900
|
|
|
* @author Qiang Xue <[email protected]> |
|
1901
|
|
|
* @package System |
|
1902
|
|
|
* @since 3.0 |
|
1903
|
|
|
*/ |
|
1904
|
|
|
class TPropertyValue |
|
1905
|
|
|
{ |
|
1906
|
|
|
/** |
|
1907
|
|
|
* Converts a value to boolean type. |
|
1908
|
|
|
* Note, string 'true' (case-insensitive) will be converted to true, |
|
1909
|
|
|
* string 'false' (case-insensitive) will be converted to false. |
|
1910
|
|
|
* If a string represents a non-zero number, it will be treated as true. |
|
1911
|
|
|
* @param mixed the value to be converted. |
|
1912
|
|
|
* @return boolean |
|
1913
|
|
|
*/ |
|
1914
|
|
|
public static function ensureBoolean($value) |
|
1915
|
|
|
{ |
|
1916
|
|
|
if (is_string($value)) |
|
1917
|
|
|
return strcasecmp($value,'true')==0 || $value!=0; |
|
1918
|
|
|
else |
|
1919
|
|
|
return (boolean)$value; |
|
1920
|
|
|
} |
|
1921
|
|
|
|
|
1922
|
|
|
/** |
|
1923
|
|
|
* Converts a value to string type. |
|
1924
|
|
|
* Note, a boolean value will be converted to 'true' if it is true |
|
1925
|
|
|
* and 'false' if it is false. |
|
1926
|
|
|
* @param mixed the value to be converted. |
|
1927
|
|
|
* @return string |
|
1928
|
|
|
*/ |
|
1929
|
|
|
public static function ensureString($value) |
|
1930
|
|
|
{ |
|
1931
|
|
|
if (TJavaScript::isJsLiteral($value)) |
|
1932
|
|
|
return $value; |
|
1933
|
|
|
if (is_bool($value)) |
|
1934
|
|
|
return $value?'true':'false'; |
|
1935
|
|
|
else |
|
1936
|
|
|
return (string)$value; |
|
1937
|
|
|
} |
|
1938
|
|
|
|
|
1939
|
|
|
/** |
|
1940
|
|
|
* Converts a value to integer type. |
|
1941
|
|
|
* @param mixed the value to be converted. |
|
1942
|
|
|
* @return integer |
|
1943
|
|
|
*/ |
|
1944
|
|
|
public static function ensureInteger($value) |
|
1945
|
|
|
{ |
|
1946
|
|
|
return (integer)$value; |
|
1947
|
|
|
} |
|
1948
|
|
|
|
|
1949
|
|
|
/** |
|
1950
|
|
|
* Converts a value to float type. |
|
1951
|
|
|
* @param mixed the value to be converted. |
|
1952
|
|
|
* @return float |
|
1953
|
|
|
*/ |
|
1954
|
|
|
public static function ensureFloat($value) |
|
1955
|
|
|
{ |
|
1956
|
|
|
return (float)$value; |
|
1957
|
|
|
} |
|
1958
|
|
|
|
|
1959
|
|
|
/** |
|
1960
|
|
|
* Converts a value to array type. If the value is a string and it is |
|
1961
|
|
|
* in the form (a,b,c) then an array consisting of each of the elements |
|
1962
|
|
|
* will be returned. If the value is a string and it is not in this form |
|
1963
|
|
|
* then an array consisting of just the string will be returned. If the value |
|
1964
|
|
|
* is not a string then |
|
1965
|
|
|
* @param mixed the value to be converted. |
|
1966
|
|
|
* @return array |
|
1967
|
|
|
*/ |
|
1968
|
|
|
public static function ensureArray($value) |
|
1969
|
|
|
{ |
|
1970
|
|
|
if(is_string($value)) |
|
1971
|
|
|
{ |
|
1972
|
|
|
$value = trim($value); |
|
1973
|
|
|
$len = strlen($value); |
|
1974
|
|
|
if ($len >= 2 && $value[0] == '(' && $value[$len-1] == ')') |
|
1975
|
|
|
{ |
|
1976
|
|
|
eval('$array=array'.$value.';'); |
|
|
|
|
|
|
1977
|
|
|
return $array; |
|
|
|
|
|
|
1978
|
|
|
} |
|
1979
|
|
|
else |
|
1980
|
|
|
return $len>0?array($value):array(); |
|
1981
|
|
|
} |
|
1982
|
|
|
else |
|
1983
|
|
|
return (array)$value; |
|
1984
|
|
|
} |
|
1985
|
|
|
|
|
1986
|
|
|
/** |
|
1987
|
|
|
* Converts a value to object type. |
|
1988
|
|
|
* @param mixed the value to be converted. |
|
1989
|
|
|
* @return object |
|
1990
|
|
|
*/ |
|
1991
|
|
|
public static function ensureObject($value) |
|
1992
|
|
|
{ |
|
1993
|
|
|
return (object)$value; |
|
1994
|
|
|
} |
|
1995
|
|
|
|
|
1996
|
|
|
/** |
|
1997
|
|
|
* Converts a value to enum type. |
|
1998
|
|
|
* |
|
1999
|
|
|
* This method checks if the value is of the specified enumerable type. |
|
2000
|
|
|
* A value is a valid enumerable value if it is equal to the name of a constant |
|
2001
|
|
|
* in the specified enumerable type (class). |
|
2002
|
|
|
* For more details about enumerable, see {@link TEnumerable}. |
|
2003
|
|
|
* |
|
2004
|
|
|
* For backward compatibility, this method also supports sanity |
|
2005
|
|
|
* check of a string value to see if it is among the given list of strings. |
|
2006
|
|
|
* @param mixed the value to be converted. |
|
2007
|
|
|
* @param mixed class name of the enumerable type, or array of valid enumeration values. If this is not an array, |
|
2008
|
|
|
* the method considers its parameters are of variable length, and the second till the last parameters are enumeration values. |
|
2009
|
|
|
* @return string the valid enumeration value |
|
2010
|
|
|
* @throws TInvalidDataValueException if the original value is not in the string array. |
|
2011
|
|
|
*/ |
|
2012
|
|
|
public static function ensureEnum($value,$enums) |
|
2013
|
|
|
{ |
|
2014
|
|
|
static $types=array(); |
|
2015
|
|
|
if(func_num_args()===2 && is_string($enums)) |
|
2016
|
|
|
{ |
|
2017
|
|
|
if(!isset($types[$enums])) |
|
2018
|
|
|
$types[$enums]=new ReflectionClass($enums); |
|
2019
|
|
|
if($types[$enums]->hasConstant($value)) |
|
2020
|
|
|
return $value; |
|
2021
|
|
|
else |
|
2022
|
|
|
throw new TInvalidDataValueException( |
|
2023
|
|
|
'propertyvalue_enumvalue_invalid',$value, |
|
2024
|
|
|
implode(' | ',$types[$enums]->getConstants())); |
|
2025
|
|
|
} |
|
2026
|
|
|
else if(!is_array($enums)) |
|
2027
|
|
|
{ |
|
2028
|
|
|
$enums=func_get_args(); |
|
2029
|
|
|
array_shift($enums); |
|
2030
|
|
|
} |
|
2031
|
|
|
if(in_array($value,$enums,true)) |
|
2032
|
|
|
return $value; |
|
2033
|
|
|
else |
|
2034
|
|
|
throw new TInvalidDataValueException('propertyvalue_enumvalue_invalid',$value,implode(' | ',$enums)); |
|
2035
|
|
|
} |
|
2036
|
|
|
|
|
2037
|
|
|
/** |
|
2038
|
|
|
* Converts the value to 'null' if the given value is empty |
|
2039
|
|
|
* @param mixed value to be converted |
|
2040
|
|
|
* @return mixed input or NULL if input is empty |
|
2041
|
|
|
*/ |
|
2042
|
|
|
public static function ensureNullIfEmpty($value) |
|
2043
|
|
|
{ |
|
2044
|
|
|
return empty($value)?null:$value; |
|
2045
|
|
|
} |
|
2046
|
|
|
} |
|
2047
|
|
|
|
|
2048
|
|
|
/** |
|
2049
|
|
|
* TEventParameter class. |
|
2050
|
|
|
* TEventParameter is the base class for all event parameter classes. |
|
2051
|
|
|
* |
|
2052
|
|
|
* @author Qiang Xue <[email protected]> |
|
2053
|
|
|
* @package System |
|
2054
|
|
|
* @since 3.0 |
|
2055
|
|
|
*/ |
|
2056
|
|
|
class TEventParameter extends TComponent |
|
2057
|
|
|
{ |
|
2058
|
|
|
} |
|
2059
|
|
|
|
|
2060
|
|
|
class TEventResults extends TEnumerable { |
|
2061
|
|
|
const EVENT_RESULT_FEED_FORWARD=1; |
|
2062
|
|
|
const EVENT_RESULT_FILTER=2; |
|
2063
|
|
|
const EVENT_RESULT_ALL=4; |
|
2064
|
|
|
} |
|
2065
|
|
|
|
|
2066
|
|
|
/** |
|
2067
|
|
|
* TComponentReflection class. |
|
2068
|
|
|
* |
|
2069
|
|
|
* TComponentReflection provides functionalities to inspect the public/protected |
|
2070
|
|
|
* properties, events and methods defined in a class. |
|
2071
|
|
|
* |
|
2072
|
|
|
* The following code displays the properties and events defined in {@link TDataGrid}, |
|
2073
|
|
|
* <code> |
|
2074
|
|
|
* $reflection=new TComponentReflection('TDataGrid'); |
|
2075
|
|
|
* Prado::varDump($reflection->getProperties()); |
|
2076
|
|
|
* Prado::varDump($reflection->getEvents()); |
|
2077
|
|
|
* </code> |
|
2078
|
|
|
* |
|
2079
|
|
|
* @author Qiang Xue <[email protected]> |
|
2080
|
|
|
* @package System |
|
2081
|
|
|
* @since 3.0 |
|
2082
|
|
|
*/ |
|
2083
|
|
|
class TComponentReflection extends TComponent |
|
2084
|
|
|
{ |
|
2085
|
|
|
private $_className; |
|
2086
|
|
|
private $_properties=array(); |
|
2087
|
|
|
private $_events=array(); |
|
2088
|
|
|
private $_methods=array(); |
|
2089
|
|
|
|
|
2090
|
|
|
/** |
|
2091
|
|
|
* Constructor. |
|
2092
|
|
|
* @param object|string the component instance or the class name |
|
2093
|
|
|
* @throws TInvalidDataTypeException if the object is not a component |
|
2094
|
|
|
*/ |
|
2095
|
|
|
public function __construct($component) |
|
2096
|
|
|
{ |
|
2097
|
|
|
if(is_string($component) && class_exists($component,false)) |
|
2098
|
|
|
$this->_className=$component; |
|
2099
|
|
|
else if(is_object($component)) |
|
2100
|
|
|
$this->_className=get_class($component); |
|
2101
|
|
|
else |
|
2102
|
|
|
throw new TInvalidDataTypeException('componentreflection_class_invalid'); |
|
2103
|
|
|
$this->reflect(); |
|
2104
|
|
|
} |
|
2105
|
|
|
|
|
2106
|
|
|
private function isPropertyMethod($method) |
|
2107
|
|
|
{ |
|
2108
|
|
|
$methodName=$method->getName(); |
|
2109
|
|
|
return $method->getNumberOfRequiredParameters()===0 |
|
2110
|
|
|
&& strncasecmp($methodName,'get',3)===0 |
|
2111
|
|
|
&& isset($methodName[3]); |
|
2112
|
|
|
} |
|
2113
|
|
|
|
|
2114
|
|
|
private function isEventMethod($method) |
|
2115
|
|
|
{ |
|
2116
|
|
|
$methodName=$method->getName(); |
|
2117
|
|
|
return strncasecmp($methodName,'on',2)===0 |
|
2118
|
|
|
&& isset($methodName[2]); |
|
2119
|
|
|
} |
|
2120
|
|
|
|
|
2121
|
|
|
private function reflect() |
|
2122
|
|
|
{ |
|
2123
|
|
|
$class=new ReflectionClass($this->_className); |
|
2124
|
|
|
$properties=array(); |
|
2125
|
|
|
$events=array(); |
|
2126
|
|
|
$methods=array(); |
|
2127
|
|
|
$isComponent=is_subclass_of($this->_className,'TComponent') || strcasecmp($this->_className,'TComponent')===0; |
|
2128
|
|
|
foreach($class->getMethods() as $method) |
|
2129
|
|
|
{ |
|
2130
|
|
|
if($method->isPublic() || $method->isProtected()) |
|
2131
|
|
|
{ |
|
2132
|
|
|
$methodName=$method->getName(); |
|
2133
|
|
|
if(!$method->isStatic() && $isComponent) |
|
2134
|
|
|
{ |
|
2135
|
|
|
if($this->isPropertyMethod($method)) |
|
2136
|
|
|
$properties[substr($methodName,3)]=$method; |
|
2137
|
|
|
else if($this->isEventMethod($method)) |
|
2138
|
|
|
{ |
|
2139
|
|
|
$methodName[0]='O'; |
|
2140
|
|
|
$events[$methodName]=$method; |
|
2141
|
|
|
} |
|
2142
|
|
|
} |
|
2143
|
|
|
if(strncmp($methodName,'__',2)!==0) |
|
2144
|
|
|
$methods[$methodName]=$method; |
|
2145
|
|
|
} |
|
2146
|
|
|
} |
|
2147
|
|
|
$reserved=array(); |
|
2148
|
|
|
ksort($properties); |
|
2149
|
|
|
foreach($properties as $name=>$method) |
|
2150
|
|
|
{ |
|
2151
|
|
|
$this->_properties[$name]=array( |
|
2152
|
|
|
'type'=>$this->determinePropertyType($method), |
|
2153
|
|
|
'readonly'=>!$class->hasMethod('set'.$name), |
|
2154
|
|
|
'protected'=>$method->isProtected(), |
|
2155
|
|
|
'class'=>$method->getDeclaringClass()->getName(), |
|
2156
|
|
|
'comments'=>$method->getDocComment() |
|
2157
|
|
|
); |
|
2158
|
|
|
$reserved['get'.strtolower($name)]=1; |
|
2159
|
|
|
$reserved['set'.strtolower($name)]=1; |
|
2160
|
|
|
} |
|
2161
|
|
|
ksort($events); |
|
2162
|
|
|
foreach($events as $name=>$method) |
|
2163
|
|
|
{ |
|
2164
|
|
|
$this->_events[$name]=array( |
|
2165
|
|
|
'class'=>$method->getDeclaringClass()->getName(), |
|
2166
|
|
|
'protected'=>$method->isProtected(), |
|
2167
|
|
|
'comments'=>$method->getDocComment() |
|
2168
|
|
|
); |
|
2169
|
|
|
$reserved[strtolower($name)]=1; |
|
2170
|
|
|
} |
|
2171
|
|
|
ksort($methods); |
|
2172
|
|
|
foreach($methods as $name=>$method) |
|
2173
|
|
|
{ |
|
2174
|
|
|
if(!isset($reserved[strtolower($name)])) |
|
2175
|
|
|
$this->_methods[$name]=array( |
|
2176
|
|
|
'class'=>$method->getDeclaringClass()->getName(), |
|
2177
|
|
|
'protected'=>$method->isProtected(), |
|
2178
|
|
|
'static'=>$method->isStatic(), |
|
2179
|
|
|
'comments'=>$method->getDocComment() |
|
2180
|
|
|
); |
|
2181
|
|
|
} |
|
2182
|
|
|
} |
|
2183
|
|
|
|
|
2184
|
|
|
/** |
|
2185
|
|
|
* Determines the property type. |
|
2186
|
|
|
* This method uses the doc comment to determine the property type. |
|
2187
|
|
|
* @param ReflectionMethod |
|
2188
|
|
|
* @return string the property type, '{unknown}' if type cannot be determined from comment |
|
2189
|
|
|
*/ |
|
2190
|
|
|
protected function determinePropertyType($method) |
|
2191
|
|
|
{ |
|
2192
|
|
|
$comment=$method->getDocComment(); |
|
2193
|
|
|
if(preg_match('/@return\\s+(.*?)\\s+/',$comment,$matches)) |
|
2194
|
|
|
return $matches[1]; |
|
2195
|
|
|
else |
|
2196
|
|
|
return '{unknown}'; |
|
2197
|
|
|
} |
|
2198
|
|
|
|
|
2199
|
|
|
/** |
|
2200
|
|
|
* @return string class name of the component |
|
2201
|
|
|
*/ |
|
2202
|
|
|
public function getClassName() |
|
2203
|
|
|
{ |
|
2204
|
|
|
return $this->_className; |
|
2205
|
|
|
} |
|
2206
|
|
|
|
|
2207
|
|
|
/** |
|
2208
|
|
|
* @return array list of component properties. Array keys are property names. |
|
2209
|
|
|
* Each array element is of the following structure: |
|
2210
|
|
|
* [type]=>property type, |
|
2211
|
|
|
* [readonly]=>whether the property is read-only, |
|
2212
|
|
|
* [protected]=>whether the method is protected or not |
|
2213
|
|
|
* [class]=>the class where the property is inherited from, |
|
2214
|
|
|
* [comments]=>comments associated with the property. |
|
2215
|
|
|
*/ |
|
2216
|
|
|
public function getProperties() |
|
2217
|
|
|
{ |
|
2218
|
|
|
return $this->_properties; |
|
2219
|
|
|
} |
|
2220
|
|
|
|
|
2221
|
|
|
/** |
|
2222
|
|
|
* @return array list of component events. Array keys are event names. |
|
2223
|
|
|
* Each array element is of the following structure: |
|
2224
|
|
|
* [protected]=>whether the event is protected or not |
|
2225
|
|
|
* [class]=>the class where the event is inherited from. |
|
2226
|
|
|
* [comments]=>comments associated with the event. |
|
2227
|
|
|
*/ |
|
2228
|
|
|
public function getEvents() |
|
2229
|
|
|
{ |
|
2230
|
|
|
return $this->_events; |
|
2231
|
|
|
} |
|
2232
|
|
|
|
|
2233
|
|
|
/** |
|
2234
|
|
|
* @return array list of public/protected methods. Array keys are method names. |
|
2235
|
|
|
* Each array element is of the following structure: |
|
2236
|
|
|
* [protected]=>whether the method is protected or not |
|
2237
|
|
|
* [static]=>whether the method is static or not |
|
2238
|
|
|
* [class]=>the class where the property is inherited from, |
|
2239
|
|
|
* [comments]=>comments associated with the event. |
|
2240
|
|
|
*/ |
|
2241
|
|
|
public function getMethods() |
|
2242
|
|
|
{ |
|
2243
|
|
|
return $this->_methods; |
|
2244
|
|
|
} |
|
2245
|
|
|
} |
|
2246
|
|
|
|
|
2247
|
|
|
/** |
|
2248
|
|
|
* IBaseBehavior interface is the base behavior class from which all other |
|
2249
|
|
|
* behaviors types are derived |
|
2250
|
|
|
* |
|
2251
|
|
|
* @author Brad Anderson <[email protected]> |
|
2252
|
|
|
* @version $Id$ |
|
2253
|
|
|
* @package System |
|
2254
|
|
|
* @since 3.2.3 |
|
2255
|
|
|
*/ |
|
2256
|
|
|
interface IBaseBehavior { |
|
2257
|
|
|
/** |
|
2258
|
|
|
* Attaches the behavior object to the component. |
|
2259
|
|
|
* @param CComponent the component that this behavior is to be attached to. |
|
2260
|
|
|
*/ |
|
2261
|
|
|
public function attach($component); |
|
|
|
|
|
|
2262
|
|
|
/** |
|
2263
|
|
|
* Detaches the behavior object from the component. |
|
2264
|
|
|
* @param CComponent the component that this behavior is to be detached from. |
|
2265
|
|
|
*/ |
|
2266
|
|
|
public function detach($component); |
|
|
|
|
|
|
2267
|
|
|
} |
|
2268
|
|
|
|
|
2269
|
|
|
/** |
|
2270
|
|
|
* IBehavior interfaces is implemented by instance behavior classes. |
|
2271
|
|
|
* |
|
2272
|
|
|
* A behavior is a way to enhance a component with additional methods and |
|
2273
|
|
|
* events that are defined in the behavior class and not available in the |
|
2274
|
|
|
* class. Objects may signal behaviors through dynamic events. |
|
2275
|
|
|
* |
|
2276
|
|
|
* @author Brad Anderson <[email protected]> |
|
2277
|
|
|
* @version $Id$ |
|
2278
|
|
|
* @package System |
|
2279
|
|
|
* @since 3.2.3 |
|
2280
|
|
|
*/ |
|
2281
|
|
|
interface IBehavior extends IBaseBehavior |
|
2282
|
|
|
{ |
|
2283
|
|
|
/** |
|
2284
|
|
|
* @return boolean whether this behavior is enabled |
|
2285
|
|
|
*/ |
|
2286
|
|
|
public function getEnabled(); |
|
2287
|
|
|
/** |
|
2288
|
|
|
* @param boolean whether this behavior is enabled |
|
2289
|
|
|
*/ |
|
2290
|
|
|
public function setEnabled($value); |
|
|
|
|
|
|
2291
|
|
|
} |
|
2292
|
|
|
|
|
2293
|
|
|
|
|
2294
|
|
|
/** |
|
2295
|
|
|
* IClassBehavior interface is implements behaviors across all instances of |
|
2296
|
|
|
* a particular class |
|
2297
|
|
|
* |
|
2298
|
|
|
* Any calls to functions not present in the original object but to behaviors |
|
2299
|
|
|
* derived from this class, will have inserted as the first argument parameter |
|
2300
|
|
|
* the object containing the behavior. |
|
2301
|
|
|
* |
|
2302
|
|
|
* For example: |
|
2303
|
|
|
* <code> |
|
2304
|
|
|
* $objWithClassBehavior->MethodOfClassBehavior(1, 20); |
|
2305
|
|
|
* </code> |
|
2306
|
|
|
* will be acted within the class behavior like this: |
|
2307
|
|
|
* <code> |
|
2308
|
|
|
* public function MethodOfClassBehavior($object, $firstParam, $secondParam){ |
|
2309
|
|
|
* // $object === $objWithClassBehavior, $firstParam === 1, $secondParam === 20 |
|
2310
|
|
|
* } |
|
2311
|
|
|
* </code> |
|
2312
|
|
|
* |
|
2313
|
|
|
* This also holds for 'dy' events as well. For dynamic events, method arguments would be: |
|
2314
|
|
|
* <code> |
|
2315
|
|
|
* public function dyMethodOfClassBehavior($object, $firstParam, $secondParam, $callchain){ |
|
2316
|
|
|
* // $object === $objWithClassBehavior, $firstParam === 1, $secondParam === 20, $callchain instanceof {@link TCallChain} |
|
2317
|
|
|
* } |
|
2318
|
|
|
* </code> |
|
2319
|
|
|
* |
|
2320
|
|
|
* @author Brad Anderson <[email protected]> |
|
2321
|
|
|
* @version $Id$ |
|
2322
|
|
|
* @package System |
|
2323
|
|
|
* @since 3.2.3 |
|
2324
|
|
|
*/ |
|
2325
|
|
|
interface IClassBehavior extends IBaseBehavior { |
|
2326
|
|
|
} |
|
2327
|
|
|
|
|
2328
|
|
|
|
|
2329
|
|
|
/** |
|
2330
|
|
|
* IInstanceCheck This interface allows objects to determine their own |
|
2331
|
|
|
* 'instanceof' results when {@link TComponent::isa} is called. This is |
|
2332
|
|
|
* important with behaviors because behaviors may want to look like |
|
2333
|
|
|
* particular objects other than themselves. |
|
2334
|
|
|
* |
|
2335
|
|
|
* @author Brad Anderson <[email protected]> |
|
2336
|
|
|
* @version $Id$ |
|
2337
|
|
|
* @package System |
|
2338
|
|
|
* @since 3.2.3 |
|
2339
|
|
|
*/ |
|
2340
|
|
|
interface IInstanceCheck { |
|
2341
|
|
|
/** |
|
2342
|
|
|
* The method checks $this or, if needed, the parameter $instance is of type |
|
2343
|
|
|
* class. In the case of a Class Behavior, the instance to which the behavior |
|
2344
|
|
|
* is attached may be important to determine if $this is an instance |
|
2345
|
|
|
* of a particular class. |
|
2346
|
|
|
* @param class|string the component that this behavior is checking if it is an instanceof. |
|
2347
|
|
|
* @param object the object which the behavior is attached to. default: null |
|
2348
|
|
|
* @return boolean|null if the this or the instance is of type class. When null, no information could be derived and |
|
2349
|
|
|
* the default mechanisms take over. |
|
2350
|
|
|
*/ |
|
2351
|
|
|
public function isinstanceof($class,$instance=null); |
|
2352
|
|
|
} |
|
2353
|
|
|
|
|
2354
|
|
|
/** |
|
2355
|
|
|
* TJavaScriptLiteral class that encloses string literals that are not |
|
2356
|
|
|
* supposed to be escaped by {@link TJavaScript::encode() } |
|
2357
|
|
|
* |
|
2358
|
|
|
* Since Prado 3.2 all the data that gets sent clientside inside a javascript statement |
|
2359
|
|
|
* is encoded by default to avoid any kind of injection. |
|
2360
|
|
|
* Sometimes there's the need to bypass this encoding and send raw javascript code. |
|
2361
|
|
|
* To ensure that a string doesn't get encoded by {@link TJavaScript::encode() }, |
|
2362
|
|
|
* construct a new TJavaScriptLiteral: |
|
2363
|
|
|
* <code> |
|
2364
|
|
|
* // a javascript test string |
|
2365
|
|
|
* $js="alert('hello')"; |
|
2366
|
|
|
* // the string in $raw will not be encoded when sent clientside inside a javascript block |
|
2367
|
|
|
* $raw=new TJavaScriptLiteral($js); |
|
2368
|
|
|
* // shortened form |
|
2369
|
|
|
* $raw=_js($js); |
|
2370
|
|
|
* </code> |
|
2371
|
|
|
* |
|
2372
|
|
|
* @package System |
|
2373
|
|
|
* @since 3.2.0 |
|
2374
|
|
|
*/ |
|
2375
|
|
|
class TJavaScriptLiteral |
|
2376
|
|
|
{ |
|
2377
|
|
|
protected $_s; |
|
2378
|
|
|
|
|
2379
|
|
|
public function __construct($s) |
|
2380
|
|
|
{ |
|
2381
|
|
|
$this->_s = $s; |
|
2382
|
|
|
} |
|
2383
|
|
|
|
|
2384
|
|
|
public function __toString() |
|
2385
|
|
|
{ |
|
2386
|
|
|
return (string)$this->_s; |
|
2387
|
|
|
} |
|
2388
|
|
|
|
|
2389
|
|
|
public function toJavaScriptLiteral() |
|
2390
|
|
|
{ |
|
2391
|
|
|
return $this->__toString(); |
|
2392
|
|
|
} |
|
2393
|
|
|
} |
|
2394
|
|
|
|
|
2395
|
|
|
/** |
|
2396
|
|
|
* TJavaScriptString class is an internal class that marks strings that will be |
|
2397
|
|
|
* forcibly encoded when rendered inside a javascript block |
|
2398
|
|
|
* |
|
2399
|
|
|
* @package System |
|
2400
|
|
|
* @since 3.2.0 |
|
2401
|
|
|
*/ |
|
2402
|
|
|
class TJavaScriptString extends TJavaScriptLiteral |
|
2403
|
|
|
{ |
|
2404
|
|
|
public function toJavaScriptLiteral() |
|
2405
|
|
|
{ |
|
2406
|
|
|
return TJavaScript::jsonEncode((string)$this->_s,JSON_HEX_QUOT | JSON_HEX_APOS | JSON_HEX_TAG); |
|
2407
|
|
|
} |
|
2408
|
|
|
} |
|
2409
|
|
|
|
|
2410
|
|
|
|
This check looks for method names that are not written in camelCase.
In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes
databaseConnectionSeeker.