Completed
Pull Request — master (#578)
by Fabio
19:18 queued 07:13
created

TComponent::_getZappableSleepProps()   B

Complexity

Conditions 5
Paths 16

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 11
rs 8.8571
cc 5
eloc 9
nc 16
nop 1
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 &copy; 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) {
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

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.

Loading history...
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'));
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
422
			array_push($fx,TComponent::GLOBAL_RAISE_EVENT_LISTENER);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
423
		}
424
425
		$this->_listeningenabled=true;
426
427
		$this->dyListen($fx);
0 ignored issues
show
Bug introduced by
The method dyListen() does not exist on TComponent. Did you maybe mean listen()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
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'));
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
456
			array_push($fx,TComponent::GLOBAL_RAISE_EVENT_LISTENER);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
457
		}
458
459
		$this->_listeningenabled=false;
460
461
		$this->dyUnlisten($fx);
0 ignored issues
show
Bug introduced by
The method dyUnlisten() does not exist on TComponent. Did you maybe mean listen()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
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
		throw new TApplicationException('component_method_undefined',get_class($this),$method);
559
	}
560
561
562
	/**
563
	 * Returns a property value or an event handler list by property or event name.
564
	 * Do not call this method. This is a PHP magic method that we override
565
	 * to allow using the following syntax to read a property:
566
	 * <code>
567
	 * $value=$component->PropertyName;
568
	 * $value=$component->jsPropertyName; // return JavaScript literal
569
	 * </code>
570
	 * and to obtain the event handler list for an event,
571
	 * <code>
572
	 * $eventHandlerList=$component->EventName;
573
	 * </code>
574
	 * This will also return the global event handler list when specifing an 'fx'
575
	 * event,
576
	 * <code>
577
	 * $globalEventHandlerList=$component->fxEventName;
578
	 * </code>
579
	 * When behaviors are enabled, this will return the behavior of a specific
580
	 * name, a property of a behavior, or an object 'on' event defined by the behavior.
581
	 * @param string the property name or the event name
582
	 * @return mixed the property value or the event handler list as {@link TPriorityList}
583
	 * @throws TInvalidOperationException if the property/event is not defined.
584
	 */
585
	public function __get($name)
586
	{
587
		if(method_exists($this,$getter='get'.$name))
588
		{
589
			// getting a property
590
			return $this->$getter();
591
		}
592
		else if(method_exists($this,$jsgetter='getjs'.$name))
593
		{
594
			// getting a javascript property
595
			return (string)$this->$jsgetter();
596
		}
597
		else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
598
		{
599
			// getting an event (handler list)
600
			$name=strtolower($name);
601
			if(!isset($this->_e[$name]))
602
				$this->_e[$name]=new TPriorityList;
603
			return $this->_e[$name];
604
		}
605
		else if(strncasecmp($name,'fx',2)===0)
606
		{
607
			// getting a global event (handler list)
608
			$name=strtolower($name);
609
			if(!isset(self::$_ue[$name]))
610
				self::$_ue[$name]=new TPriorityList;
611
			return self::$_ue[$name];
612
		}
613
		else if($this->_behaviorsenabled)
614
		{
615
			// getting a behavior property/event (handler list)
616
			if(isset($this->_m[$name]))
617
				return $this->_m[$name];
618
			else if($this->_m!==null)
619
			{
620
				foreach($this->_m->toArray() as $behavior)
621
				{
622
					if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&
623
						(property_exists($behavior,$name)||$behavior->canGetProperty($name)||$behavior->hasEvent($name)))
624
						return $behavior->$name;
625
				}
626
			}
627
		}
628
		throw new TInvalidOperationException('component_property_undefined',get_class($this),$name);
629
	}
630
631
	/**
632
	 * Sets value of a component property.
633
	 * Do not call this method. This is a PHP magic method that we override
634
	 * to allow using the following syntax to set a property or attach an event handler.
635
	 * <code>
636
	 * $this->PropertyName=$value;
637
	 * $this->jsPropertyName=$value; // $value will be treated as a JavaScript literal
638
	 * $this->EventName=$handler;
639
	 * $this->fxEventName=$handler; //global event listener
640
	 * </code>
641
	 * When behaviors are enabled, this will also set a behaviors properties and events.
642
	 * @param string the property name or event name
643
	 * @param mixed the property value or event handler
644
	 * @throws TInvalidOperationException If the property is not defined or read-only.
645
	 */
646
	public function __set($name,$value)
647
	{
648
		if(method_exists($this,$setter='set'.$name))
649
		{
650
			if(strncasecmp($name,'js',2)===0&&$value&&!($value instanceof TJavaScriptLiteral))
651
				$value = new TJavaScriptLiteral($value);
652
			return $this->$setter($value);
653
		}
654
		else if(method_exists($this,$jssetter='setjs'.$name))
655
		{
656
			if($value&&!($value instanceof TJavaScriptString))
657
				$value=new TJavaScriptString($value);
658
			return $this->$jssetter($value);
659
		}
660
		else if((strncasecmp($name,'on',2)===0&&method_exists($this,$name))||strncasecmp($name,'fx',2)===0)
661
		{
662
			return $this->attachEventHandler($name,$value);
663
		}
664
		else if($this->_m!==null&&$this->_m->getCount()>0&&$this->_behaviorsenabled)
665
		{
666
			$sets=0;
667
			foreach($this->_m->toArray() as $behavior)
668
			{
669
				if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&
670
					(property_exists($behavior,$name)||$behavior->canSetProperty($name)||$behavior->hasEvent($name))) {
671
					$behavior->$name=$value;
672
					$sets++;
673
				}
674
			}
675
			if($sets)return $value;
676
677
		}
678
679
		if(method_exists($this,'get'.$name)||method_exists($this,'getjs'.$name))
680
		{
681
			throw new TInvalidOperationException('component_property_readonly',get_class($this),$name);
682
		}
683
		else
684
		{
685
			throw new TInvalidOperationException('component_property_undefined',get_class($this),$name);
686
		}
687
	}
688
689
	/**
690
	 * Checks if a property value is null, there are no events in the object
691
	 * event list or global event list registered under the name, and, if
692
	 * behaviors are enabled,
693
	 * Do not call this method. This is a PHP magic method that we override
694
	 * to allow using isset() to detect if a component property is set or not.
695
	 * This also works for global events.  When behaviors are enabled, it
696
	 * will check for a behavior of the specified name, and also check
697
	 * the behavior for events and properties.
698
	 * @param string the property name or the event name
699
	 * @since 3.2.3
700
	 */
701
	public function __isset($name)
702
	{
703
		if(method_exists($this,$getter='get'.$name))
704
			return $this->$getter()!==null;
705
		else if(method_exists($this,$jsgetter='getjs'.$name))
706
			return $this->$jsgetter()!==null;
707
		else if(strncasecmp($name,'on',2)===0&&method_exists($this,$name))
708
		{
709
			$name=strtolower($name);
710
			return isset($this->_e[$name])&&$this->_e[$name]->getCount();
711
		}
712
		else if(strncasecmp($name,'fx',2)===0)
713
		{
714
			$name=strtolower($name);
715
			return isset(self::$_ue[$name])&&self::$_ue[$name]->getCount();
716
		}
717
		else if($this->_m!==null&&$this->_m->getCount()>0&&$this->_behaviorsenabled)
718
		{
719
			if(isset($this->_m[$name]))
720
				return true;
721
			foreach($this->_m->toArray() as $behavior)
722
			{
723
				if((!($behavior instanceof IBehavior)||$behavior->getEnabled()))
724
					return isset($behavior->$name);
725
			}
726
727
		}
728
		else
729
			return false;
730
	}
731
732
	/**
733
	 * Sets a component property to be null.  Clears the object or global
734
	 * events. When enabled, loops through all behaviors and unsets the
735
	 * property or event.
736
	 * Do not call this method. This is a PHP magic method that we override
737
	 * to allow using unset() to set a component property to be null.
738
	 * @param string the property name or the event name
739
	 * @throws TInvalidOperationException if the property is read only.
740
	 * @since 3.2.3
741
	 */
742
	public function __unset($name)
743
	{
744
		if(method_exists($this,$setter='set'.$name))
745
			$this->$setter(null);
746
		else if(method_exists($this,$jssetter='setjs'.$name))
747
			$this->$jssetter(null);
748
		else if(strncasecmp($name,'on',2)===0&&method_exists($this,$name))
749
			$this->_e[strtolower($name)]->clear();
750
		else if(strncasecmp($name,'fx',2)===0)
751
			$this->getEventHandlers($name)->remove(array($this, $name));
752
		else if($this->_m!==null&&$this->_m->getCount()>0&&$this->_behaviorsenabled)
753
		{
754
			if(isset($this->_m[$name]))
755
				$this->detachBehavior($name);
756
			else {
757
				$unset=0;
758
				foreach($this->_m->toArray() as $behavior)
759
				{
760
					if((!($behavior instanceof IBehavior)||$behavior->getEnabled())) {
761
						unset($behavior->$name);
762
						$unset++;
763
					}
764
				}
765
				if(!$unset&&method_exists($this,'get'.$name))
766
					throw new TInvalidOperationException('component_property_readonly',get_class($this),$name);
767
			}
768
		} else if(method_exists($this,'get'.$name))
769
			throw new TInvalidOperationException('component_property_readonly',get_class($this),$name);
770
	}
771
772
	/**
773
	 * Determines whether a property is defined.
774
	 * A property is defined if there is a getter or setter method
775
	 * defined in the class. Note, property names are case-insensitive.
776
	 * @param string the property name
777
	 * @return boolean whether the property is defined
778
	 */
779
	public function hasProperty($name)
780
	{
781
		return $this->canGetProperty($name)||$this->canSetProperty($name);
782
	}
783
784
	/**
785
	 * Determines whether a property can be read.
786
	 * A property can be read if the class has a getter method
787
	 * for the property name. Note, property name is case-insensitive.
788
	 * This also checks for getjs.  When enabled, it loops through all
789
	 * active behaviors for the get property when undefined by the object.
790
	 * @param string the property name
791
	 * @return boolean whether the property can be read
792
	 */
793
	public function canGetProperty($name)
794
	{
795
		if(method_exists($this,'get'.$name)||method_exists($this,'getjs'.$name))
796
			return true;
797
		else if($this->_m!==null&&$this->_behaviorsenabled)
798
		{
799
			foreach($this->_m->toArray() as $behavior)
800
			{
801
				if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->canGetProperty($name))
802
					return true;
803
			}
804
		}
805
		return false;
806
	}
807
808
	/**
809
	 * Determines whether a property can be set.
810
	 * A property can be written if the class has a setter method
811
	 * for the property name. Note, property name is case-insensitive.
812
	 * This also checks for setjs.  When enabled, it loops through all
813
	 * active behaviors for the set property when undefined by the object.
814
	 * @param string the property name
815
	 * @return boolean whether the property can be written
816
	 */
817
	public function canSetProperty($name)
818
	{
819
		if(method_exists($this,'set'.$name)||method_exists($this,'setjs'.$name))
820
			return true;
821
		else if($this->_m!==null&&$this->_behaviorsenabled)
822
		{
823
			foreach($this->_m->toArray() as $behavior)
824
			{
825
				if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->canSetProperty($name))
826
					return true;
827
			}
828
		}
829
		return false;
830
	}
831
832
	/**
833
	 * Evaluates a property path.
834
	 * A property path is a sequence of property names concatenated by '.' character.
835
	 * For example, 'Parent.Page' refers to the 'Page' property of the component's
836
	 * 'Parent' property value (which should be a component also).
837
	 * When a property is not defined by an object, this also loops through all
838
	 * active behaviors of the object.
839
	 * @param string property path
840
	 * @return mixed the property path value
841
	 */
842
	public function getSubProperty($path)
843
	{
844
		$object=$this;
845
		foreach(explode('.',$path) as $property)
846
			$object=$object->$property;
847
		return $object;
848
	}
849
850
	/**
851
	 * Sets a value to a property path.
852
	 * A property path is a sequence of property names concatenated by '.' character.
853
	 * For example, 'Parent.Page' refers to the 'Page' property of the component's
854
	 * 'Parent' property value (which should be a component also).
855
	 * When a property is not defined by an object, this also loops through all
856
	 * active behaviors of the object.
857
	 * @param string property path
858
	 * @param mixed the property path value
859
	 */
860
	public function setSubProperty($path,$value)
861
	{
862
		$object=$this;
863
		if(($pos=strrpos($path,'.'))===false)
864
			$property=$path;
865
		else
866
		{
867
			$object=$this->getSubProperty(substr($path,0,$pos));
868
			$property=substr($path,$pos+1);
869
		}
870
		$object->$property=$value;
871
	}
872
873
	/**
874
	 * Determines whether an event is defined.
875
	 * An event is defined if the class has a method whose name is the event name
876
	 * prefixed with 'on', 'fx', or 'dy'.
877
	 * Every object responds to every 'fx' and 'dy' event as they are in a universally
878
	 * accepted event space.  'on' event must be declared by the object.
879
	 * When enabled, this will loop through all active behaviors for 'on' events
880
	 * defined by the behavior.
881
	 * Note, event name is case-insensitive.
882
	 * @param string the event name
883
	 * @return boolean
884
	 */
885
	public function hasEvent($name)
886
	{
887
		if((strncasecmp($name,'on',2)===0&&method_exists($this,$name))||strncasecmp($name,'fx',2)===0||strncasecmp($name,'dy',2)===0)
888
			return true;
889
890
		else if($this->_m!==null&&$this->_behaviorsenabled)
891
		{
892
			foreach($this->_m->toArray() as $behavior)
893
			{
894
				if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->hasEvent($name))
895
					return true;
896
			}
897
		}
898
		return false;
899
	}
900
901
	/**
902
	 * Checks if an event has any handlers.  This function also checks through all
903
	 * the behaviors for 'on' events when behaviors are enabled.
904
	 * 'dy' dynamic events are not handled by this function.
905
	 * @param string the event name
906
	 * @return boolean whether an event has been attached one or several handlers
907
	 */
908
	public function hasEventHandler($name)
909
	{
910
		$name=strtolower($name);
911
		if(strncasecmp($name,'fx',2)===0)
912
			return isset(self::$_ue[$name])&&self::$_ue[$name]->getCount()>0;
913
		else
914
		{
915
			if(isset($this->_e[$name])&&$this->_e[$name]->getCount()>0)
916
				return true;
917
			else if($this->_m!==null&&$this->_behaviorsenabled) {
918
				foreach($this->_m->toArray() as $behavior)
919
				{
920
					if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->hasEventHandler($name))
921
						return true;
922
				}
923
			}
924
		}
925
		return false;
926
	}
927
928
	/**
929
	 * Returns the list of attached event handlers for an 'on' or 'fx' event.   This function also
930
	 * checks through all the behaviors for 'on' event lists when behaviors are enabled.
931
	 * @return TPriorityList list of attached event handlers for an event
932
	 * @throws TInvalidOperationException if the event is not defined
933
	 */
934
	public function getEventHandlers($name)
935
	{
936
		if(strncasecmp($name,'on',2)===0&&method_exists($this,$name))
937
		{
938
			$name=strtolower($name);
939
			if(!isset($this->_e[$name]))
940
				$this->_e[$name]=new TPriorityList;
941
			return $this->_e[$name];
942
		}
943
		else if(strncasecmp($name,'fx',2)===0)
944
		{
945
			$name=strtolower($name);
946
			if(!isset(self::$_ue[$name]))
947
				self::$_ue[$name]=new TPriorityList;
948
			return self::$_ue[$name];
949
		}
950
		else if($this->_m!==null&&$this->_behaviorsenabled)
951
		{
952
			foreach($this->_m->toArray() as $behavior)
953
			{
954
				if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->hasEvent($name))
955
					return $behavior->getEventHandlers($name);
956
			}
957
		}
958
		throw new TInvalidOperationException('component_event_undefined',get_class($this),$name);
959
	}
960
961
	/**
962
	 * Attaches an event handler to an event.
963
	 *
964
	 * The handler must be a valid PHP callback, i.e., a string referring to
965
	 * a global function name, or an array containing two elements with
966
	 * the first element being an object and the second element a method name
967
	 * of the object. In Prado, you can also use method path to refer to
968
	 * an event handler. For example, array($object,'Parent.buttonClicked')
969
	 * uses a method path that refers to the method $object->Parent->buttonClicked(...).
970
	 *
971
	 * The event handler must be of the following signature,
972
	 * <code>
973
	 * function handlerName($sender, $param) {}
974
	 * function handlerName($sender, $param, $name) {}
975
	 * </code>
976
	 * where $sender represents the object that raises the event,
977
	 * and $param is the event parameter. $name refers to the event name
978
	 * being handled.
979
	 *
980
	 * This is a convenient method to add an event handler.
981
	 * It is equivalent to {@link getEventHandlers}($name)->add($handler).
982
	 * For complete management of event handlers, use {@link getEventHandlers}
983
	 * to get the event handler list first, and then do various
984
	 * {@link TPriorityList} operations to append, insert or remove
985
	 * event handlers. You may also do these operations like
986
	 * getting and setting properties, e.g.,
987
	 * <code>
988
	 * $component->OnClick[]=array($object,'buttonClicked');
989
	 * $component->OnClick->insertAt(0,array($object,'buttonClicked'));
990
	 * </code>
991
	 * which are equivalent to the following
992
	 * <code>
993
	 * $component->getEventHandlers('OnClick')->add(array($object,'buttonClicked'));
994
	 * $component->getEventHandlers('OnClick')->insertAt(0,array($object,'buttonClicked'));
995
	 * </code>
996
	 *
997
	 * Due to the nature of {@link getEventHandlers}, any active behaviors defining
998
	 * new 'on' events, this method will pass through to the behavior transparently.
999
	 *
1000
	 * @param string the event name
1001
	 * @param callback the event handler
1002
	 * @param numeric|null the priority of the handler, defaults to null which translates into the
1003
	 * default priority of 10.0 within {@link TPriorityList}
1004
	 * @throws TInvalidOperationException if the event does not exist
1005
	 */
1006
	public function attachEventHandler($name,$handler,$priority=null)
1007
	{
1008
		$this->getEventHandlers($name)->add($handler,$priority);
1009
	}
1010
1011
	/**
1012
	 * Detaches an existing event handler.
1013
	 * This method is the opposite of {@link attachEventHandler}.  It will detach
1014
	 * any 'on' events definedb by an objects active behaviors as well.
1015
	 * @param string event name
1016
	 * @param callback the event handler to be removed
1017
	 * @param numeric|false|null the priority of the handler, defaults to false which translates
1018
	 * to an item of any priority within {@link TPriorityList}; null means the default priority
1019
	 * @return boolean if the removal is successful
1020
	 */
1021
	public function detachEventHandler($name,$handler,$priority=false)
1022
	{
1023
		if($this->hasEventHandler($name))
1024
		{
1025
			try
1026
			{
1027
				$this->getEventHandlers($name)->remove($handler,$priority);
1028
				return true;
1029
			}
1030
			catch(Exception $e)
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1031
			{
1032
			}
1033
		}
1034
		return false;
1035
	}
1036
1037
	/**
1038
	 * Raises an event.  This raises both inter-object 'on' events and global 'fx' events.
1039
	 * This method represents the happening of an event and will
1040
	 * invoke all attached event handlers for the event in {@link TPriorityList} order.
1041
	 * This method does not handle intra-object/behavior dynamic 'dy' events.
1042
	 *
1043
	 * There are ways to handle event responses.  By defailt {@link EVENT_RESULT_FILTER},
1044
	 * all event responses are stored in an array, filtered for null responses, and returned.
1045
	 * If {@link EVENT_RESULT_ALL} is specified, all returned results will be stored along
1046
	 * with the sender and param in an array
1047
	 * <code>
1048
	 * 		$result[] = array('sender'=>$sender,'param'=>$param,'response'=>$response);
1049
	 * </code>
1050
	 *
1051
	 * If {@link EVENT_RESULT_FEED_FORWARD} is specified, then each handler result is then
1052
	 * fed forward as the parameters for the next event.  This allows for events to filter data
1053
	 * directly by affecting the event parameters
1054
	 *
1055
	 * If a callable function is set in the response type or the post function filter is specified then the
1056
	 * result of each called event handler is post processed by the callable function.  Used in
1057
	 * combination with {@link EVENT_RESULT_FEED_FORWARD}, any event (and its result) can be chained.
1058
	 *
1059
	 * When raising a global 'fx' event, registered handlers in the global event list for
1060
	 * {@link GLOBAL_RAISE_EVENT_LISTENER} are always added into the set of event handlers.  In this way,
1061
	 * these global events are always raised for every global 'fx' event.  The registered handlers for global
1062
	 * raiseEvent events have priorities.  Any registered global raiseEvent event handlers with a priority less than zero
1063
	 * are added before the main event handlers being raised and any registered global raiseEvent event handlers
1064
	 * with a priority equal or greater than zero are added after the main event handlers being raised.  In this way
1065
	 * all {@link GLOBAL_RAISE_EVENT_LISTENER} handlers are always called for every raised 'fx' event.
1066
	 *
1067
	 * Behaviors may implement the following functions:
1068
	 * <code>
1069
	 *	public function dyPreRaiseEvent($name,$sender,$param,$responsetype,$postfunction[, $chain]) {
1070
	 *  	return $name; //eg, the event name may be filtered/changed
1071
	 *  }
1072
	 *	public function dyIntraRaiseEventTestHandler($handler,$sender,$param,$name[, $chain]) {
1073
	 *  	return true; //should this particular handler be executed?  true/false
1074
	 *  }
1075
	 *  public function dyIntraRaiseEventPostHandler($name,$sender,$param,$handler,$response[, $chain]) {
1076
	 *		//contains the per handler response
1077
	 *  }
1078
	 *  public function dyPostRaiseEvent($responses,$name,$sender,$param,$responsetype,$postfunction[, $chain]) {
1079
	 *		return $responses;
1080
	 *  }
1081
	 * </code>
1082
	 * to be executed when raiseEvent is called.  The 'intra' dynamic events are called per handler in
1083
	 * the handler loop.
1084
	 *
1085
	 * dyPreRaiseEvent has the effect of being able to change the event being raised.  This intra
1086
	 * object/behavior event returns the name of the desired event to be raised.  It will pass through
1087
	 * if no dynamic event is specified, or if the original event name is returned.
1088
	 * dyIntraRaiseEventTestHandler returns true or false as to whether a specific handler should be
1089
	 * called for a specific raised event (and associated event arguments)
1090
	 * dyIntraRaiseEventPostHandler does not return anything.  This allows behaviors to access the results
1091
	 * of an event handler in the per handler loop.
1092
	 * dyPostRaiseEvent returns the responses.  This allows for any post processing of the event
1093
	 * results from the sum of all event handlers
1094
	 *
1095
	 * When handling a catch-all {@link __dycall}, the method name is the name of the event
1096
	 * and the parameters are the sender, the param, and then the name of the event.
1097
	 *
1098
	 * @param string the event name
1099
	 * @param mixed the event sender object
1100
	 * @param TEventParameter the event parameter
1101
	 * @param numeric how the results of the event are tabulated.  default: {@link EVENT_RESULT_FILTER}  The default filters out
1102
	 *		null responses. optional
1103
	 * @param function any per handler filtering of the response result needed is passed through
1104
	 *		this if not null. default: null.  optional
1105
	 * @return mixed the results of the event
1106
	 * @throws TInvalidOperationException if the event is undefined
1107
	 * @throws TInvalidDataValueException If an event handler is invalid
1108
	 */
1109
	public function raiseEvent($name,$sender,$param,$responsetype=null,$postfunction=null)
1110
	{
1111
		$p=$param;
0 ignored issues
show
Unused Code introduced by
$p is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1112
		if(is_callable($responsetype))
1113
		{
1114
			$postfunction=$responsetype;
1115
			$responsetype=null;
1116
		}
1117
1118
		if($responsetype===null)
1119
			$responsetype=TEventResults::EVENT_RESULT_FILTER;
1120
1121
		$name=strtolower($name);
1122
		$responses=array();
1123
1124
		$name=$this->dyPreRaiseEvent($name,$sender,$param,$responsetype,$postfunction);
0 ignored issues
show
Bug introduced by
The method dyPreRaiseEvent() does not exist on TComponent. Did you maybe mean raiseEvent()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1125
1126
		if($this->hasEventHandler($name)||$this->hasEventHandler(TComponent::GLOBAL_RAISE_EVENT_LISTENER))
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
1127
		{
1128
			$handlers=$this->getEventHandlers($name);
1129
			$handlerArray=$handlers->toArray();
1130
			if(strncasecmp($name,'fx',2)===0&&$this->hasEventHandler(TComponent::GLOBAL_RAISE_EVENT_LISTENER))
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
1131
			{
1132
				$globalhandlers=$this->getEventHandlers(TComponent::GLOBAL_RAISE_EVENT_LISTENER);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
1133
				$handlerArray=array_merge($globalhandlers->toArrayBelowPriority(0),$handlerArray,$globalhandlers->toArrayAbovePriority(0));
1134
			}
1135
			$response=null;
0 ignored issues
show
Unused Code introduced by
$response is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1136
			foreach($handlerArray as $handler)
1137
			{
1138
				if($this->dyIntraRaiseEventTestHandler($handler,$sender,$param,$name)===false)
0 ignored issues
show
Bug introduced by
The method dyIntraRaiseEventTestHandler() does not exist on TComponent. Did you maybe mean raiseEvent()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1139
					continue;
1140
1141
				if(is_string($handler))
1142
				{
1143
					if(($pos=strrpos($handler,'.'))!==false)
1144
					{
1145
						$object=$this->getSubProperty(substr($handler,0,$pos));
1146
						$method=substr($handler,$pos+1);
1147
						if(method_exists($object,$method)||strncasecmp($method,'dy',2)===0||strncasecmp($method,'fx',2)===0)
1148
						{
1149
							if($method=='__dycall')
1150
								$response=$object->__dycall($name,array($sender,$param,$name));
1151
							else
1152
								$response=$object->$method($sender,$param,$name);
1153
						}
1154
						else
1155
							throw new TInvalidDataValueException('component_eventhandler_invalid',get_class($this),$name,$handler);
1156
					}
1157
					else
1158
						$response=call_user_func($handler,$sender,$param,$name);
1159
				}
1160
				else if(is_callable($handler,true))
1161
				{
1162
					list($object,$method)=$handler;
1163
					if(is_string($object))
1164
						$response=call_user_func($handler,$sender,$param,$name);
1165
					else
1166
					{
1167
						if(($pos=strrpos($method,'.'))!==false)
1168
						{
1169
							$object=$this->getSubProperty(substr($method,0,$pos));
1170
							$method=substr($method,$pos+1);
1171
						}
1172
						if(method_exists($object,$method)||strncasecmp($method,'dy',2)===0||strncasecmp($method,'fx',2)===0)
1173
						{
1174
							if($method=='__dycall')
1175
								$response=$object->__dycall($name,array($sender,$param,$name));
1176
							else
1177
								$response=$object->$method($sender,$param,$name);
1178
						}
1179
						else
1180
							throw new TInvalidDataValueException('component_eventhandler_invalid',get_class($this),$name,$handler[1]);
1181
					}
1182
				}
1183
				else
1184
					throw new TInvalidDataValueException('component_eventhandler_invalid',get_class($this),$name,gettype($handler));
1185
1186
				$this->dyIntraRaiseEventPostHandler($name,$sender,$param,$handler,$response);
0 ignored issues
show
Bug introduced by
The method dyIntraRaiseEventPostHandler() does not exist on TComponent. Did you maybe mean raiseEvent()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1187
1188
				if($postfunction)
1189
					$response=call_user_func_array($postfunction,array($sender,$param,$this,$response));
1190
1191
				if($responsetype&TEventResults::EVENT_RESULT_ALL)
1192
					$responses[]=array('sender'=>$sender,'param'=>$param,'response'=>$response);
1193
				else
1194
					$responses[]=$response;
1195
1196
				if($response!==null&&($responsetype&TEventResults::EVENT_RESULT_FEED_FORWARD))
1197
					$param=$response;
1198
1199
			}
1200
		}
1201
		else if(strncasecmp($name,'on',2)===0&&!$this->hasEvent($name))
1202
			throw new TInvalidOperationException('component_event_undefined',get_class($this),$name);
1203
1204
		if($responsetype&TEventResults::EVENT_RESULT_FILTER)
1205
			$responses=array_filter($responses);
1206
1207
		$responses=$this->dyPostRaiseEvent($responses,$name,$sender,$param,$responsetype,$postfunction);
0 ignored issues
show
Bug introduced by
The method dyPostRaiseEvent() does not exist on TComponent. Did you maybe mean raiseEvent()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1208
1209
		return $responses;
1210
	}
1211
1212
	/**
1213
	 * Evaluates a PHP expression in the context of this control.
1214
	 *
1215
	 * Behaviors may implement the function:
1216
	 * <code>
1217
	 *	public function dyEvaluateExpressionFilter($expression, $chain) {
1218
	 * 		return $chain->dyEvaluateExpressionFilter(str_replace('foo', 'bar', $expression)); //example
1219
	 * }
1220
	 * </code>
1221
	 * to be executed when evaluateExpression is called.  All attached behaviors are notified through
1222
	 * dyEvaluateExpressionFilter.  The chaining is important in this function due to the filtering
1223
	 * pass-through effect.
1224
	 *
1225
	 * @param string PHP expression
1226
	 * @return mixed the expression result
1227
	 * @throws TInvalidOperationException if the expression is invalid
1228
	 */
1229
	public function evaluateExpression($expression)
1230
	{
1231
		$expression=$this->dyEvaluateExpressionFilter($expression);
0 ignored issues
show
Bug introduced by
The method dyEvaluateExpressionFilter() does not exist on TComponent. Did you maybe mean evaluateExpression()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1232
		try
1233
		{
1234
			if(eval("\$result=$expression;")===false)
0 ignored issues
show
Coding Style introduced by
It is generally not recommended to use eval unless absolutely required.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
1235
				throw new Exception('');
1236
			return $result;
0 ignored issues
show
Bug introduced by
The variable $result does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
1237
		}
1238
		catch(Exception $e)
1239
		{
1240
			throw new TInvalidOperationException('component_expression_invalid',get_class($this),$expression,$e->getMessage());
1241
		}
1242
	}
1243
1244
	/**
1245
	 * Evaluates a list of PHP statements.
1246
	 *
1247
	 * Behaviors may implement the function:
1248
	 * <code>
1249
	 *	public function dyEvaluateStatementsFilter($statements, $chain) {
1250
	 * 		return $chain->dyEvaluateStatementsFilter(str_replace('foo', 'bar', $statements)); //example
1251
	 * }
1252
	 * </code>
1253
	 * to be executed when evaluateStatements is called.  All attached behaviors are notified through
1254
	 * dyEvaluateStatementsFilter.  The chaining is important in this function due to the filtering
1255
	 * pass-through effect.
1256
	 *
1257
	 * @param string PHP statements
1258
	 * @return string content echoed or printed by the PHP statements
1259
	 * @throws TInvalidOperationException if the statements are invalid
1260
	 */
1261
	public function evaluateStatements($statements)
1262
	{
1263
		$statements=$this->dyEvaluateStatementsFilter($statements);
0 ignored issues
show
Bug introduced by
The method dyEvaluateStatementsFilter() does not exist on TComponent. Did you maybe mean evaluateStatements()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1264
		try
1265
		{
1266
			ob_start();
1267
			if(eval($statements)===false)
0 ignored issues
show
Coding Style introduced by
It is generally not recommended to use eval unless absolutely required.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
1268
				throw new Exception('');
1269
			$content=ob_get_contents();
1270
			ob_end_clean();
1271
			return $content;
1272
		}
1273
		catch(Exception $e)
1274
		{
1275
			throw new TInvalidOperationException('component_statements_invalid',get_class($this),$statements,$e->getMessage());
1276
		}
1277
	}
1278
1279
	/**
1280
	 * This method is invoked after the component is instantiated by a template.
1281
	 * When this method is invoked, the component's properties have been initialized.
1282
	 * The default implementation of this method will invoke
1283
	 * the potential parent component's {@link addParsedObject}.
1284
	 * This method can be overridden.
1285
	 *
1286
	 * Behaviors may implement the function:
1287
	 * <code>
1288
	 *	public function dyCreatedOnTemplate($parent, $chain) {
1289
	 * 		return $chain->dyCreatedOnTemplate($parent); //example
1290
	 *  }
1291
	 * </code>
1292
	 * to be executed when createdOnTemplate is called.  All attached behaviors are notified through
1293
	 * dyCreatedOnTemplate.
1294
	 *
1295
	 * @param TComponent potential parent of this control
1296
	 * @see addParsedObject
1297
	 */
1298
	public function createdOnTemplate($parent)
1299
	{
1300
		$parent=$this->dyCreatedOnTemplate($parent);
0 ignored issues
show
Bug introduced by
The method dyCreatedOnTemplate() does not exist on TComponent. Did you maybe mean createdOnTemplate()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1301
		$parent->addParsedObject($this);
1302
	}
1303
1304
	/**
1305
	 * Processes an object that is created during parsing template.
1306
	 * The object can be either a component or a static text string.
1307
	 * This method can be overridden to customize the handling of newly created objects in template.
1308
	 * Only framework developers and control developers should use this method.
1309
	 *
1310
	 * Behaviors may implement the function:
1311
	 * <code>
1312
	 *	public function dyAddParsedObject($object[, $chain]) {
1313
	 *  }
1314
	 * </code>
1315
	 * to be executed when addParsedObject is called.  All attached behaviors are notified through
1316
	 * dyAddParsedObject.
1317
	 *
1318
	 * @param string|TComponent text string or component parsed and instantiated in template
1319
	 * @see createdOnTemplate
1320
	 */
1321
	public function addParsedObject($object)
1322
	{
1323
		$this->dyAddParsedObject($object);
0 ignored issues
show
Bug introduced by
The method dyAddParsedObject() does not exist on TComponent. Did you maybe mean addParsedObject()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1324
	}
1325
1326
1327
	/**
1328
	 *	This is the method registered for all instanced objects should a class behavior be added after
1329
	 * the class is instanced.  Only when the class to which the behavior is being added is in this
1330
	 * object's class hierarchy, via {@link getClassHierarchy}, is the behavior added to this instance.
1331
	 * @param $sender the application
1332
	 * @param $param TClassBehaviorEventParameter
1333
	 * @since 3.2.3
1334
	 */
1335
	public function fxAttachClassBehavior($sender,$param) {
1336
		if(in_array($param->getClass(),$this->getClassHierarchy(true)))
1337
			return $this->attachBehavior($param->getName(),$param->getBehavior(),$param->getPriority());
1338
	}
1339
1340
1341
	/**
1342
	 *	This is the method registered for all instanced objects should a class behavior be removed after
1343
	 * the class is instanced.  Only when the class to which the behavior is being added is in this
1344
	 * object's class hierarchy, via {@link getClassHierarchy}, is the behavior removed from this instance.
1345
	 * @param $sender the application
1346
	 * @param $param TClassBehaviorEventParameter
1347
	 * @since 3.2.3
1348
	 */
1349
	public function fxDetachClassBehavior($sender,$param) {
1350
		if(in_array($param->getClass(),$this->getClassHierarchy(true)))
1351
			return $this->detachBehavior($param->getName(),$param->getPriority());
1352
	}
1353
1354
1355
	/**
1356
	 *	This will add a class behavior to all classes instanced (that are listening) and future newly instanced objects.
1357
	 * This registers the behavior for future instances and pushes the changes to all the instances that are listening as well.
1358
	 * The universal class behaviors are stored in an inverted stack with the latest class behavior being at the first position in the array.
1359
	 * This is done so class behaviors are added last first.
1360
	 * @param string name the key of the class behavior
1361
	 * @param object|string class behavior or name of the object behavior per instance
1362
	 * @param string|class string of class or class on which to attach this behavior.  Defaults to null which will error
1363
	 *	but more important, if this is on PHP 5.3 it will use Late Static Binding to derive the class
1364
	 * it should extend.
1365
	 * <code>
1366
	 * TPanel::attachClassBehavior('javascripts', (new TJsPanelBehavior())->init($this));
1367
	 * </code>
1368
	 * @param numeric|null priority of behavior, default: null the default priority of the {@link TPriorityList}  Optional.
1369
	 * @throws TInvalidOperationException if the class behavior is being added to a {@link TComponent}; due to recursion.
1370
	 * @throws TInvalidOperationException if the class behavior is already defined
1371
	 * @since 3.2.3
1372
	 */
1373
	public static function attachClassBehavior($name,$behavior,$class=null,$priority=null) {
1374
		if(!$class&&function_exists('get_called_class'))
1375
			$class=get_called_class();
1376
		if(!$class)
1377
			throw new TInvalidOperationException('component_no_class_provided_nor_late_binding');
1378
1379
		if(!is_string($name))
1380
			$name=get_class($name);
1381
		$class=strtolower($class);
1382
		if($class==='tcomponent')
1383
			throw new TInvalidOperationException('component_no_tcomponent_class_behaviors');
1384
		if(empty(self::$_um[$class]))
1385
			self::$_um[$class]=array();
1386
		if(isset(self::$_um[$class][$name]))
1387
			throw new TInvalidOperationException('component_class_behavior_defined',$class,$name);
1388
		$param=new TClassBehaviorEventParameter($class,$name,$behavior,$priority);
1389
		self::$_um[$class]=array($name=>$param)+self::$_um[$class];
1390
		$behaviorObject=is_string($behavior)?new $behavior:$behavior;
1391
		return $behaviorObject->raiseEvent('fxAttachClassBehavior',null,$param);
1392
	}
1393
1394
1395
	/**
1396
	 *	This will remove a behavior from a class.  It unregisters it from future instances and
1397
	 * pulls the changes from all the instances that are listening as well.
1398
	 * PHP 5.3 uses Late Static Binding to derive the static class upon which this method is called.
1399
	 * @param $name the key of the class behavior
1400
	 * @param $class string class on which to attach this behavior.  Defaults to null.
1401
	 * @param $priority numeric|null|false priority.  false is any priority, null is default
1402
	 *		{@link TPriorityList} priority, and numeric is a specific priority.
1403
	 * @throws Exception if the the class cannot be derived from Late Static Binding and is not
1404
	 * not supplied as a parameter.
1405
	 * @since 3.2.3
1406
	 */
1407
	public static function detachClassBehavior($name,$class=null,$priority=false) {
1408
		if(!$class&&function_exists('get_called_class'))
1409
			$class=get_called_class();
1410
		if(!$class)
1411
			throw new TInvalidOperationException('component_no_class_provided_nor_late_binding');
1412
1413
		$class=strtolower($class);
1414
		if(!is_string($name))
1415
			$name=get_class($name);
1416
		if(empty(self::$_um[$class])||!isset(self::$_um[$class][$name]))
1417
			return false;
1418
		$param=self::$_um[$class][$name];
1419
		$behavior=$param->getBehavior();
1420
		unset(self::$_um[$class][$name]);
1421
		$behaviorObject=is_string($behavior)?new $behavior:$behavior;
1422
		return $behaviorObject->raiseEvent('fxDetachClassBehavior',null,$param);
1423
	}
1424
1425
	/**
1426
	 * Returns the named behavior object.
1427
	 * The name 'asa' stands for 'as a'.
1428
	 * @param string the behavior name
1429
	 * @return IBehavior the behavior object, or null if the behavior does not exist
1430
	 * @since 3.2.3
1431
	 */
1432
	public function asa($behaviorname)
1433
	{
1434
		return isset($this->_m[$behaviorname])?$this->_m[$behaviorname]:null;
1435
	}
1436
1437
	/**
1438
	 * Returns whether or not the object or any of the behaviors are of a particular class.
1439
	 * The name 'isa' stands for 'is a'.  This first checks if $this is an instanceof the class.
1440
	 * It then checks each Behavior.  If a behavior implements {@link IInstanceCheck},
1441
	 * then the behavior can determine what it is an instanceof.  If this behavior function returns true,
1442
	 * then this method returns true.  If the behavior instance checking function returns false,
1443
	 * then no further checking is performed as it is assumed to be correct.
1444
	 *
1445
	 * If the behavior instance check function returns nothing or null or the behavior
1446
	 * doesn't implement the {@link IInstanceCheck} interface, then the default instanceof occurs.
1447
	 * The default isa behavior is to check if the behavior is an instanceof the class.
1448
	 *
1449
	 * The behavior {@link IInstanceCheck} is to allow a behavior to have the host object
1450
	 * act as a completely different object.
1451
	 *
1452
	 * @param class or string
1453
	 * @return boolean whether or not the object or a behavior is an instance of a particular class
1454
	 * @since 3.2.3
1455
	 */
1456
	public function isa($class)
1457
	{
1458
		if($this instanceof $class)
1459
			return true;
1460
		if($this->_m!==null&&$this->_behaviorsenabled)
1461
			foreach($this->_m->toArray() as $behavior){
1462
				if(($behavior instanceof IBehavior)&&!$behavior->getEnabled())
1463
					continue;
1464
1465
				$check = null;
1466
				if(($behavior->isa('IInstanceCheck'))&&$check=$behavior->isinstanceof($class,$this))
1467
					return true;
1468
				if($check===null&&($behavior->isa($class)))
1469
					return true;
1470
			}
1471
		return false;
1472
	}
1473
1474
	/**
1475
	 * Attaches a list of behaviors to the component.
1476
	 * Each behavior is indexed by its name and should be an instance of
1477
	 * {@link IBehavior}, a string specifying the behavior class, or a
1478
	 * {@link TClassBehaviorEventParameter}.
1479
	 * @param array list of behaviors to be attached to the component
1480
	 * @since 3.2.3
1481
	 */
1482
	public function attachBehaviors($behaviors)
1483
	{
1484
		foreach($behaviors as $name=>$behavior)
1485
			if($behavior instanceof TClassBehaviorEventParameter)
1486
				$this->attachBehavior($behavior->getName(),$behavior->getBehavior(),$behavior->getPriority());
1487
			else
1488
				$this->attachBehavior($name,$behavior);
1489
	}
1490
1491
	/**
1492
	 * Detaches select behaviors from the component.
1493
	 * Each behavior is indexed by its name and should be an instance of
1494
	 * {@link IBehavior}, a string specifying the behavior class, or a
1495
	 * {@link TClassBehaviorEventParameter}.
1496
	 * @param array list of behaviors to be detached from the component
1497
	 * @since 3.2.3
1498
	 */
1499
	public function detachBehaviors($behaviors)
1500
	{
1501
		if($this->_m!==null)
1502
		{
1503
			foreach($behaviors as $name=>$behavior)
1504
				if($behavior instanceof TClassBehaviorEventParameter)
1505
					$this->detachBehavior($behavior->getName(),$behavior->getPriority());
1506
				else
1507
					$this->detachBehavior(is_string($behavior)?$behavior:$name);
1508
		}
1509
	}
1510
1511
	/**
1512
	 * Detaches all behaviors from the component.
1513
	 * @since 3.2.3
1514
	 */
1515
	public function clearBehaviors()
1516
	{
1517
		if($this->_m!==null)
1518
		{
1519
			foreach($this->_m->toArray() as $name=>$behavior)
1520
				$this->detachBehavior($name);
1521
			$this->_m=null;
1522
		}
1523
	}
1524
1525
	/**
1526
	 * Attaches a behavior to this component.
1527
	 * This method will create the behavior object based on the given
1528
	 * configuration. After that, the behavior object will be initialized
1529
	 * by calling its {@link IBehavior::attach} method.
1530
	 *
1531
	 * Already attached behaviors may implement the function:
1532
	 * <code>
1533
	 *	public function dyAttachBehavior($name,$behavior[, $chain]) {
1534
	 *  }
1535
	 * </code>
1536
	 * to be executed when attachBehavior is called.  All attached behaviors are notified through
1537
	 * dyAttachBehavior.
1538
	 *
1539
	 * @param string the behavior's name. It should uniquely identify this behavior.
1540
	 * @param mixed the behavior configuration. This is passed as the first
1541
	 * parameter to {@link YiiBase::createComponent} to create the behavior object.
1542
	 * @return IBehavior the behavior object
1543
	 * @since 3.2.3
1544
	 */
1545
	public function attachBehavior($name,$behavior,$priority=null)
1546
	{
1547
		if(is_string($behavior))
1548
			$behavior=Prado::createComponent($behavior);
1549
		if(!($behavior instanceof IBaseBehavior))
1550
			throw new TInvalidDataTypeException('component_not_a_behavior',get_class($behavior));
1551
		if($behavior instanceof IBehavior)
1552
			$behavior->setEnabled(true);
1553
		if($this->_m===null)
1554
			$this->_m=new TPriorityMap;
1555
		$behavior->attach($this);
1556
		$this->dyAttachBehavior($name,$behavior);
0 ignored issues
show
Bug introduced by
The method dyAttachBehavior() does not exist on TComponent. Did you maybe mean attachBehavior()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1557
		$this->_m->add($name,$behavior,$priority);
1558
		return $behavior;
1559
	}
1560
1561
	/**
1562
	 * Detaches a behavior from the component.
1563
	 * The behavior's {@link IBehavior::detach} method will be invoked.
1564
	 *
1565
	 * Behaviors may implement the function:
1566
	 * <code>
1567
	 *	public function dyDetachBehavior($name,$behavior[, $chain]) {
1568
	 *  }
1569
	 * </code>
1570
	 * to be executed when detachBehavior is called.  All attached behaviors are notified through
1571
	 * dyDetachBehavior.
1572
	 *
1573
	 * @param string the behavior's name. It uniquely identifies the behavior.
1574
	 * @param numeric the behavior's priority. This defaults to false, aka any priority.
1575
	 * @return IBehavior the detached behavior. Null if the behavior does not exist.
1576
	 * @since 3.2.3
1577
	 */
1578
	public function detachBehavior($name,$priority=false)
1579
	{
1580
		if($this->_m!=null&&isset($this->_m[$name]))
1581
		{
1582
			$this->_m[$name]->detach($this);
1583
			$behavior=$this->_m->itemAt($name);
1584
			$this->_m->remove($name,$priority);
1585
			$this->dyDetachBehavior($name,$behavior);
0 ignored issues
show
Bug introduced by
The method dyDetachBehavior() does not exist on TComponent. Did you maybe mean detachBehavior()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1586
			return $behavior;
1587
		}
1588
	}
1589
1590
	/**
1591
	 * Enables all behaviors attached to this component independent of the behaviors
1592
	 *
1593
	 * Behaviors may implement the function:
1594
	 * <code>
1595
	 *	public function dyEnableBehaviors($name,$behavior[, $chain]) {
1596
	 *  }
1597
	 * </code>
1598
	 * to be executed when enableBehaviors is called.  All attached behaviors are notified through
1599
	 * dyEnableBehaviors.
1600
	 * @since 3.2.3
1601
	 */
1602
	public function enableBehaviors()
1603
	{
1604
		if(!$this->_behaviorsenabled)
1605
		{
1606
			$this->_behaviorsenabled=true;
1607
			$this->dyEnableBehaviors();
0 ignored issues
show
Bug introduced by
The method dyEnableBehaviors() does not exist on TComponent. Did you maybe mean enableBehaviors()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1608
		}
1609
	}
1610
1611
	/**
1612
	 * Disables all behaviors attached to this component independent of the behaviors
1613
	 *
1614
	 * Behaviors may implement the function:
1615
	 * <code>
1616
	 *	public function dyDisableBehaviors($name,$behavior[, $chain]) {
1617
	 *  }
1618
	 * </code>
1619
	 * to be executed when disableBehaviors is called.  All attached behaviors are notified through
1620
	 * dyDisableBehaviors.
1621
	 * @since 3.2.3
1622
	 */
1623
	public function disableBehaviors()
1624
	{
1625
		if($this->_behaviorsenabled)
1626
		{
1627
			$this->dyDisableBehaviors();
0 ignored issues
show
Bug introduced by
The method dyDisableBehaviors() does not exist on TComponent. Did you maybe mean disableBehaviors()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1628
			$this->_behaviorsenabled=false;
1629
		}
1630
	}
1631
1632
1633
	/**
1634
	 * Returns if all the behaviors are turned on or off for the object.
1635
	 * @return boolean whether or not all behaviors are enabled (true) or not (false)
1636
	 * @since 3.2.3
1637
	 */
1638
	public function getBehaviorsEnabled()
1639
	{
1640
		return $this->_behaviorsenabled;
1641
	}
1642
1643
	/**
1644
	 * Enables an attached object behavior.  This cannot enable or disable whole class behaviors.
1645
	 * A behavior is only effective when it is enabled.
1646
	 * A behavior is enabled when first attached.
1647
	 *
1648
	 * Behaviors may implement the function:
1649
	 * <code>
1650
	 *	public function dyEnableBehavior($name,$behavior[, $chain]) {
1651
	 *  }
1652
	 * </code>
1653
	 * to be executed when enableBehavior is called.  All attached behaviors are notified through
1654
	 * dyEnableBehavior.
1655
	 *
1656
	 * @param string the behavior's name. It uniquely identifies the behavior.
1657
	 * @since 3.2.3
1658
	 */
1659
	public function enableBehavior($name)
1660
	{
1661
		if($this->_m!=null&&isset($this->_m[$name])){
1662
			if($this->_m[$name] instanceof IBehavior) {
1663
				$this->_m[$name]->setEnabled(true);
1664
				$this->dyEnableBehavior($name,$this->_m[$name]);
0 ignored issues
show
Bug introduced by
The method dyEnableBehavior() does not exist on TComponent. Did you maybe mean enableBehavior()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1665
				return true;
1666
			}
1667
			return false;
1668
		}
1669
		return null;
1670
	}
1671
1672
	/**
1673
	 * Disables an attached behavior.  This cannot enable or disable whole class behaviors.
1674
	 * A behavior is only effective when it is enabled.
1675
	 *
1676
	 * Behaviors may implement the function:
1677
	 * <code>
1678
	 *	public function dyDisableBehavior($name,$behavior[, $chain]) {
1679
	 *  }
1680
	 * </code>
1681
	 * to be executed when disableBehavior is called.  All attached behaviors are notified through
1682
	 * dyDisableBehavior.
1683
	 *
1684
	 * @param string the behavior's name. It uniquely identifies the behavior.
1685
	 * @since 3.2.3
1686
	 */
1687
	public function disableBehavior($name)
1688
	{
1689
		if($this->_m!=null&&isset($this->_m[$name])){
1690
			if($this->_m[$name] instanceof IBehavior) {
1691
				$this->_m[$name]->setEnabled(false);
1692
				$this->dyDisableBehavior($name,$this->_m[$name]);
0 ignored issues
show
Bug introduced by
The method dyDisableBehavior() does not exist on TComponent. Did you maybe mean disableBehavior()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1693
				return true;
1694
			}
1695
			return false;
1696
		}
1697
		return null;
1698
	}
1699
1700
	/**
1701
	 * Returns an array with the names of all variables of that object that should be serialized.
1702
	 * Do not call this method. This is a PHP magic method that will be called automatically
1703
	 * prior to any serialization.
1704
	 */
1705
	public function __sleep()
1706
	{
1707
		$a = (array)$this;
1708
		$a = array_keys($a);
1709
		$exprops = array();
1710
		$this->_getZappableSleepProps($exprops);
1711
		return array_diff($a, $exprops);
1712
	}
1713
1714
	/**
1715
	 * Returns an array with the names of all variables of this object that should NOT be serialized
1716
	 * because their value is the default one or useless to be cached for the next page loads.
1717
	 * Reimplement in derived classes to add new variables, but remember to  also to call the parent
1718
	 * implementation first.
1719
	 */
1720
	protected function _getZappableSleepProps(&$exprops)
1721
	{
1722
		if($this->_listeningenabled===false)
1723
			$exprops[] = "\0TComponent\0_listeningenabled";
1724
		if($this->_behaviorsenabled===true)
1725
			$exprops[] = "\0TComponent\0_behaviorsenabled";
1726
		if ($this->_e===array())
1727
			$exprops[] = "\0TComponent\0_e";
1728
		if ($this->_m===null)
1729
			$exprops[] = "\0TComponent\0_m";
1730
	}
1731
}
1732
1733
1734
/**
1735
 * IDynamicMethods interface.
1736
 * IDynamicMethods marks an object to receive undefined global or dynamic events.
1737
 *
1738
 * @author Brad Anderson <[email protected]>
1739
 * @version $Id$
1740
 * @package System
1741
 * @since 3.2.3
1742
 */
1743
interface IDynamicMethods
1744
{
1745
	public function __dycall($method,$args);
0 ignored issues
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
Coding Style introduced by
Method name "IDynamicMethods::__dycall" is invalid; only PHP magic methods should be prefixed with a double underscore
Loading history...
1746
}
1747
1748
1749
1750
/**
1751
 * TClassBehaviorEventParameter class.
1752
 * TClassBehaviorEventParameter is the parameter sent with the class behavior changes.
1753
 *
1754
 * @author Brad Anderson <[email protected]>
1755
 * @version $Id$
1756
 * @package System
1757
 * @since 3.2.3
1758
 */
1759
class TClassBehaviorEventParameter extends TEventParameter
1760
{
1761
	private $_class;
1762
	private $_name;
1763
	private $_behavior;
1764
	private $_priority;
1765
1766
	/**
1767
	 * Holds the parameters for the Class Behavior Events
1768
	 *	@param string $class this is the class to get the behavior
1769
	 *	@param string $name the name of the behavior
1770
	 *	@param object $behavior this is the behavior to implement the class behavior
1771
	 */
1772
	public function __construct($class,$name,$behavior,$priority)
1773
	{
1774
		$this->_class=$class;
1775
		$this->_name=$name;
1776
		$this->_behavior=$behavior;
1777
		$this->_priority=$priority;
1778
	}
1779
1780
	/**
1781
	 * This is the class to get the behavior
1782
	 * @return string the class to get the behavior
1783
	 */
1784
	public function getClass()
1785
	{
1786
		return $this->_class;
1787
	}
1788
1789
	/**
1790
	 * name of the behavior
1791
	 * @return string the name to get the behavior
1792
	 */
1793
	public function getName()
1794
	{
1795
		return $this->_name;
1796
	}
1797
1798
	/**
1799
	 * This is the behavior which the class is to get
1800
	 * @return object the behavior to implement
1801
	 */
1802
	public function getBehavior()
1803
	{
1804
		return $this->_behavior;
1805
	}
1806
1807
	/**
1808
	 * This is the priority which the behavior is to get
1809
	 * @return numeric the priority of the behavior
1810
	 */
1811
	public function getPriority()
1812
	{
1813
		return $this->_priority;
1814
	}
1815
}
1816
1817
1818
/**
1819
 * TEnumerable class.
1820
 * TEnumerable is the base class for all enumerable types.
1821
 * To define an enumerable type, extend TEnumberable and define string constants.
1822
 * Each constant represents an enumerable value.
1823
 * The constant name must be the same as the constant value.
1824
 * For example,
1825
 * <code>
1826
 * class TTextAlign extends TEnumerable
1827
 * {
1828
 *     const Left='Left';
1829
 *     const Right='Right';
1830
 * }
1831
 * </code>
1832
 * Then, one can use the enumerable values such as TTextAlign::Left and
1833
 * TTextAlign::Right.
1834
 *
1835
 * @author Qiang Xue <[email protected]>
1836
 * @package System
1837
 * @since 3.0
1838
 */
1839
class TEnumerable implements Iterator
1840
{
1841
	private $_enums=array();
1842
1843
	public function __construct() {
1844
		$reflection=new ReflectionClass($this);
1845
		$this->_enums=$reflection->getConstants();
1846
	}
1847
1848
	public function current() {
1849
		return current($this->_enums);
1850
	}
1851
1852
	public function key() {
1853
		return key($this->_enums);
1854
	}
1855
1856
	public function next() {
1857
		return next($this->_enums);
1858
	}
1859
1860
	public function rewind() {
1861
		reset($this->_enums);
1862
	}
1863
1864
	public function valid() {
1865
		return $this->current()!==false;
1866
	}
1867
}
1868
1869
/**
1870
 * TPropertyValue class
1871
 *
1872
 * TPropertyValue is a utility class that provides static methods
1873
 * to convert component property values to specific types.
1874
 *
1875
 * TPropertyValue is commonly used in component setter methods to ensure
1876
 * the new property value is of specific type.
1877
 * For example, a boolean-typed property setter method would be as follows,
1878
 * <code>
1879
 * function setPropertyName($value) {
1880
 *     $value=TPropertyValue::ensureBoolean($value);
1881
 *     // $value is now of boolean type
1882
 * }
1883
 * </code>
1884
 *
1885
 * Properties can be of the following types with specific type conversion rules:
1886
 * - string: a boolean value will be converted to 'true' or 'false'.
1887
 * - boolean: string 'true' (case-insensitive) will be converted to true,
1888
 *            string 'false' (case-insensitive) will be converted to false.
1889
 * - integer
1890
 * - float
1891
 * - array: string starting with '(' and ending with ')' will be considered as
1892
 *          as an array expression and will be evaluated. Otherwise, an array
1893
 *          with the value to be ensured is returned.
1894
 * - object
1895
 * - enum: enumerable type, represented by an array of strings.
1896
 *
1897
 * @author Qiang Xue <[email protected]>
1898
 * @package System
1899
 * @since 3.0
1900
 */
1901
class TPropertyValue
1902
{
1903
	/**
1904
	 * Converts a value to boolean type.
1905
	 * Note, string 'true' (case-insensitive) will be converted to true,
1906
	 * string 'false' (case-insensitive) will be converted to false.
1907
	 * If a string represents a non-zero number, it will be treated as true.
1908
	 * @param mixed the value to be converted.
1909
	 * @return boolean
1910
	 */
1911
	public static function ensureBoolean($value)
1912
	{
1913
		if (is_string($value))
1914
			return strcasecmp($value,'true')==0 || $value!=0;
1915
		else
1916
			return (boolean)$value;
1917
	}
1918
1919
	/**
1920
	 * Converts a value to string type.
1921
	 * Note, a boolean value will be converted to 'true' if it is true
1922
	 * and 'false' if it is false.
1923
	 * @param mixed the value to be converted.
1924
	 * @return string
1925
	 */
1926
	public static function ensureString($value)
1927
	{
1928
		if (TJavaScript::isJsLiteral($value))
1929
			return $value;
1930
		if (is_bool($value))
1931
			return $value?'true':'false';
1932
		else
1933
			return (string)$value;
1934
	}
1935
1936
	/**
1937
	 * Converts a value to integer type.
1938
	 * @param mixed the value to be converted.
1939
	 * @return integer
1940
	 */
1941
	public static function ensureInteger($value)
1942
	{
1943
		return (integer)$value;
1944
	}
1945
1946
	/**
1947
	 * Converts a value to float type.
1948
	 * @param mixed the value to be converted.
1949
	 * @return float
1950
	 */
1951
	public static function ensureFloat($value)
1952
	{
1953
		return (float)$value;
1954
	}
1955
1956
	/**
1957
	 * Converts a value to array type. If the value is a string and it is
1958
	 * in the form (a,b,c) then an array consisting of each of the elements
1959
	 * will be returned. If the value is a string and it is not in this form
1960
	 * then an array consisting of just the string will be returned. If the value
1961
	 * is not a string then
1962
	 * @param mixed the value to be converted.
1963
	 * @return array
1964
	 */
1965
	public static function ensureArray($value)
1966
	{
1967
		if(is_string($value))
1968
		{
1969
			$value = trim($value);
1970
			$len = strlen($value);
1971
			if ($len >= 2 && $value[0] == '(' && $value[$len-1] == ')')
1972
			{
1973
				eval('$array=array'.$value.';');
0 ignored issues
show
Coding Style introduced by
It is generally not recommended to use eval unless absolutely required.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
1974
				return $array;
0 ignored issues
show
Bug introduced by
The variable $array does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
1975
			}
1976
			else
1977
				return $len>0?array($value):array();
1978
		}
1979
		else
1980
			return (array)$value;
1981
	}
1982
1983
	/**
1984
	 * Converts a value to object type.
1985
	 * @param mixed the value to be converted.
1986
	 * @return object
1987
	 */
1988
	public static function ensureObject($value)
1989
	{
1990
		return (object)$value;
1991
	}
1992
1993
	/**
1994
	 * Converts a value to enum type.
1995
	 *
1996
	 * This method checks if the value is of the specified enumerable type.
1997
	 * A value is a valid enumerable value if it is equal to the name of a constant
1998
	 * in the specified enumerable type (class).
1999
	 * For more details about enumerable, see {@link TEnumerable}.
2000
	 *
2001
	 * For backward compatibility, this method also supports sanity
2002
	 * check of a string value to see if it is among the given list of strings.
2003
	 * @param mixed the value to be converted.
2004
	 * @param mixed class name of the enumerable type, or array of valid enumeration values. If this is not an array,
2005
	 * the method considers its parameters are of variable length, and the second till the last parameters are enumeration values.
2006
	 * @return string the valid enumeration value
2007
	 * @throws TInvalidDataValueException if the original value is not in the string array.
2008
	 */
2009
	public static function ensureEnum($value,$enums)
2010
	{
2011
		static $types=array();
2012
		if(func_num_args()===2 && is_string($enums))
2013
		{
2014
			if(!isset($types[$enums]))
2015
				$types[$enums]=new ReflectionClass($enums);
2016
			if($types[$enums]->hasConstant($value))
2017
				return $value;
2018
			else
2019
				throw new TInvalidDataValueException(
2020
					'propertyvalue_enumvalue_invalid',$value,
2021
						implode(' | ',$types[$enums]->getConstants()));
2022
		}
2023
		else if(!is_array($enums))
2024
		{
2025
			$enums=func_get_args();
2026
			array_shift($enums);
2027
		}
2028
		if(in_array($value,$enums,true))
2029
			return $value;
2030
		else
2031
			throw new TInvalidDataValueException('propertyvalue_enumvalue_invalid',$value,implode(' | ',$enums));
2032
	}
2033
2034
	/**
2035
	 * Converts the value to 'null' if the given value is empty
2036
	 * @param mixed value to be converted
2037
	 * @return mixed input or NULL if input is empty
2038
	 */
2039
	public static function ensureNullIfEmpty($value)
2040
	{
2041
		return empty($value)?null:$value;
2042
	}
2043
}
2044
2045
/**
2046
 * TEventParameter class.
2047
 * TEventParameter is the base class for all event parameter classes.
2048
 *
2049
 * @author Qiang Xue <[email protected]>
2050
 * @package System
2051
 * @since 3.0
2052
 */
2053
class TEventParameter extends TComponent
2054
{
2055
}
2056
2057
class TEventResults extends TEnumerable {
2058
	const EVENT_RESULT_FEED_FORWARD=1;
2059
	const EVENT_RESULT_FILTER=2;
2060
	const EVENT_RESULT_ALL=4;
2061
}
2062
2063
/**
2064
 * TComponentReflection class.
2065
 *
2066
 * TComponentReflection provides functionalities to inspect the public/protected
2067
 * properties, events and methods defined in a class.
2068
 *
2069
 * The following code displays the properties and events defined in {@link TDataGrid},
2070
 * <code>
2071
 *   $reflection=new TComponentReflection('TDataGrid');
2072
 *   Prado::varDump($reflection->getProperties());
2073
 *   Prado::varDump($reflection->getEvents());
2074
 * </code>
2075
 *
2076
 * @author Qiang Xue <[email protected]>
2077
 * @package System
2078
 * @since 3.0
2079
 */
2080
class TComponentReflection extends TComponent
2081
{
2082
	private $_className;
2083
	private $_properties=array();
2084
	private $_events=array();
2085
	private $_methods=array();
2086
2087
	/**
2088
	 * Constructor.
2089
	 * @param object|string the component instance or the class name
2090
	 * @throws TInvalidDataTypeException if the object is not a component
2091
	 */
2092
	public function __construct($component)
2093
	{
2094
		if(is_string($component) && class_exists($component,false))
2095
			$this->_className=$component;
2096
		else if(is_object($component))
2097
			$this->_className=get_class($component);
2098
		else
2099
			throw new TInvalidDataTypeException('componentreflection_class_invalid');
2100
		$this->reflect();
2101
	}
2102
2103
	private function isPropertyMethod($method)
2104
	{
2105
		$methodName=$method->getName();
2106
		return $method->getNumberOfRequiredParameters()===0
2107
				&& strncasecmp($methodName,'get',3)===0
2108
				&& isset($methodName[3]);
2109
	}
2110
2111
	private function isEventMethod($method)
2112
	{
2113
		$methodName=$method->getName();
2114
		return strncasecmp($methodName,'on',2)===0
2115
				&& isset($methodName[2]);
2116
	}
2117
2118
	private function reflect()
2119
	{
2120
		$class=new ReflectionClass($this->_className);
2121
		$properties=array();
2122
		$events=array();
2123
		$methods=array();
2124
		$isComponent=is_subclass_of($this->_className,'TComponent') || strcasecmp($this->_className,'TComponent')===0;
2125
		foreach($class->getMethods() as $method)
2126
		{
2127
			if($method->isPublic() || $method->isProtected())
2128
			{
2129
				$methodName=$method->getName();
2130
				if(!$method->isStatic() && $isComponent)
2131
				{
2132
					if($this->isPropertyMethod($method))
2133
						$properties[substr($methodName,3)]=$method;
2134
					else if($this->isEventMethod($method))
2135
					{
2136
						$methodName[0]='O';
2137
						$events[$methodName]=$method;
2138
					}
2139
				}
2140
				if(strncmp($methodName,'__',2)!==0)
2141
					$methods[$methodName]=$method;
2142
			}
2143
		}
2144
		$reserved=array();
2145
		ksort($properties);
2146
		foreach($properties as $name=>$method)
2147
		{
2148
			$this->_properties[$name]=array(
2149
				'type'=>$this->determinePropertyType($method),
2150
				'readonly'=>!$class->hasMethod('set'.$name),
2151
				'protected'=>$method->isProtected(),
2152
				'class'=>$method->getDeclaringClass()->getName(),
2153
				'comments'=>$method->getDocComment()
2154
			);
2155
			$reserved['get'.strtolower($name)]=1;
2156
			$reserved['set'.strtolower($name)]=1;
2157
		}
2158
		ksort($events);
2159
		foreach($events as $name=>$method)
2160
		{
2161
			$this->_events[$name]=array(
2162
				'class'=>$method->getDeclaringClass()->getName(),
2163
				'protected'=>$method->isProtected(),
2164
				'comments'=>$method->getDocComment()
2165
			);
2166
			$reserved[strtolower($name)]=1;
2167
		}
2168
		ksort($methods);
2169
		foreach($methods as $name=>$method)
2170
		{
2171
			if(!isset($reserved[strtolower($name)]))
2172
				$this->_methods[$name]=array(
2173
					'class'=>$method->getDeclaringClass()->getName(),
2174
					'protected'=>$method->isProtected(),
2175
					'static'=>$method->isStatic(),
2176
					'comments'=>$method->getDocComment()
2177
				);
2178
		}
2179
	}
2180
2181
	/**
2182
	 * Determines the property type.
2183
	 * This method uses the doc comment to determine the property type.
2184
	 * @param ReflectionMethod
2185
	 * @return string the property type, '{unknown}' if type cannot be determined from comment
2186
	 */
2187
	protected function determinePropertyType($method)
2188
	{
2189
		$comment=$method->getDocComment();
2190
		if(preg_match('/@return\\s+(.*?)\\s+/',$comment,$matches))
2191
			return $matches[1];
2192
		else
2193
			return '{unknown}';
2194
	}
2195
2196
	/**
2197
	 * @return string class name of the component
2198
	 */
2199
	public function getClassName()
2200
	{
2201
		return $this->_className;
2202
	}
2203
2204
	/**
2205
	 * @return array list of component properties. Array keys are property names.
2206
	 * Each array element is of the following structure:
2207
	 * [type]=>property type,
2208
	 * [readonly]=>whether the property is read-only,
2209
	 * [protected]=>whether the method is protected or not
2210
	 * [class]=>the class where the property is inherited from,
2211
	 * [comments]=>comments	associated with the property.
2212
	 */
2213
	public function getProperties()
2214
	{
2215
		return $this->_properties;
2216
	}
2217
2218
	/**
2219
	 * @return array list of component events. Array keys are event names.
2220
	 * Each array element is of the following structure:
2221
	 * [protected]=>whether the event is protected or not
2222
	 * [class]=>the class where the event is inherited from.
2223
	 * [comments]=>comments associated with the event.
2224
	 */
2225
	public function getEvents()
2226
	{
2227
		return $this->_events;
2228
	}
2229
2230
	/**
2231
	 * @return array list of public/protected methods. Array keys are method names.
2232
	 * Each array element is of the following structure:
2233
	 * [protected]=>whether the method is protected or not
2234
	 * [static]=>whether the method is static or not
2235
	 * [class]=>the class where the property is inherited from,
2236
	 * [comments]=>comments associated with the event.
2237
	 */
2238
	public function getMethods()
2239
	{
2240
		return $this->_methods;
2241
	}
2242
}
2243
2244
/**
2245
 * IBaseBehavior interface is the base behavior class from which all other
2246
 * behaviors types are derived
2247
 *
2248
 * @author Brad Anderson <[email protected]>
2249
 * @version $Id$
2250
 * @package System
2251
 * @since 3.2.3
2252
 */
2253
interface IBaseBehavior {
2254
	/**
2255
	 * Attaches the behavior object to the component.
2256
	 * @param CComponent the component that this behavior is to be attached to.
2257
	 */
2258
	public function attach($component);
0 ignored issues
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
2259
	/**
2260
	 * Detaches the behavior object from the component.
2261
	 * @param CComponent the component that this behavior is to be detached from.
2262
	 */
2263
	public function detach($component);
0 ignored issues
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
2264
}
2265
2266
/**
2267
 * IBehavior interfaces is implemented by instance behavior classes.
2268
 *
2269
 * A behavior is a way to enhance a component with additional methods and
2270
 * events that are defined in the behavior class and not available in the
2271
 * class.  Objects may signal behaviors through dynamic events.
2272
 *
2273
 * @author Brad Anderson <[email protected]>
2274
 * @version $Id$
2275
 * @package System
2276
 * @since 3.2.3
2277
 */
2278
interface IBehavior extends IBaseBehavior
2279
{
2280
	/**
2281
	 * @return boolean whether this behavior is enabled
2282
	 */
2283
	public function getEnabled();
2284
	/**
2285
	 * @param boolean whether this behavior is enabled
2286
	 */
2287
	public function setEnabled($value);
0 ignored issues
show
Documentation introduced by
For interfaces and abstract methods it is generally a good practice to add a @return annotation even if it is just @return void or @return null, so that implementors know what to do in the overridden method.

For interface and abstract methods, it is impossible to infer the return type from the immediate code. In these cases, it is generally advisible to explicitly annotate these methods with a @return doc comment to communicate to implementors of these methods what they are expected to return.

Loading history...
2288
}
2289
2290
2291
/**
2292
 * IClassBehavior interface is implements behaviors across all instances of
2293
 * a particular class
2294
 *
2295
 * Any calls to functions not present in the original object but to behaviors
2296
 * derived from this class, will have inserted as the first argument parameter
2297
 * the object containing the behavior.
2298
 *
2299
 * For example:
2300
 * <code>
2301
 * $objWithClassBehavior->MethodOfClassBehavior(1, 20);
2302
 * </code>
2303
 * will be acted within the class behavior like this:
2304
 * <code>
2305
 * public function MethodOfClassBehavior($object, $firstParam, $secondParam){
2306
 *      // $object === $objWithClassBehavior, $firstParam === 1, $secondParam === 20
2307
 * }
2308
 * </code>
2309
 *
2310
 * This also holds for 'dy' events as well.  For dynamic events, method arguments would be:
2311
 * <code>
2312
 * public function dyMethodOfClassBehavior($object, $firstParam, $secondParam, $callchain){
2313
 *      // $object === $objWithClassBehavior, $firstParam === 1, $secondParam === 20, $callchain instanceof {@link TCallChain}
2314
 * }
2315
 * </code>
2316
 *
2317
 * @author Brad Anderson <[email protected]>
2318
 * @version $Id$
2319
 * @package System
2320
 * @since 3.2.3
2321
 */
2322
interface IClassBehavior extends IBaseBehavior {
2323
}
2324
2325
2326
/**
2327
 * IInstanceCheck This interface allows objects to determine their own
2328
 * 'instanceof' results when {@link TComponent::isa} is called.  This is
2329
 * important with behaviors because behaviors may want to look like
2330
 * particular objects other than themselves.
2331
 *
2332
 * @author Brad Anderson <[email protected]>
2333
 * @version $Id$
2334
 * @package System
2335
 * @since 3.2.3
2336
 */
2337
interface IInstanceCheck {
2338
	/**
2339
	 * The method checks $this or, if needed, the parameter $instance is of type
2340
	 * class.  In the case of a Class Behavior, the instance to which the behavior
2341
	 * is attached may be important to determine if $this is an instance
2342
	 * of a particular class.
2343
	 * @param class|string the component that this behavior is checking if it is an instanceof.
2344
	 * @param object the object which the behavior is attached to.  default: null
2345
	 * @return boolean|null if the this or the instance is of type class.  When null, no information could be derived and
2346
	 * the default mechanisms take over.
2347
	 */
2348
	public function isinstanceof($class,$instance=null);
2349
}
2350
2351
/**
2352
 * TJavaScriptLiteral class that encloses string literals that are not
2353
 * supposed to be escaped by {@link TJavaScript::encode() }
2354
 *
2355
 * Since Prado 3.2 all the data that gets sent clientside inside a javascript statement
2356
 * is encoded by default to avoid any kind of injection.
2357
 * Sometimes there's the need to bypass this encoding and send raw javascript code.
2358
 * To ensure that a string doesn't get encoded by {@link TJavaScript::encode() },
2359
 * construct a new TJavaScriptLiteral:
2360
 * <code>
2361
 * // a javascript test string
2362
 * $js="alert('hello')";
2363
 * // the string in $raw will not be encoded when sent clientside inside a javascript block
2364
 * $raw=new TJavaScriptLiteral($js);
2365
 * // shortened form
2366
 * $raw=_js($js);
2367
 * </code>
2368
 *
2369
 * @package System
2370
 * @since 3.2.0
2371
 */
2372
class TJavaScriptLiteral
2373
{
2374
	protected $_s;
2375
2376
	public function __construct($s)
2377
	{
2378
		$this->_s = $s;
2379
	}
2380
2381
	public function __toString()
2382
	{
2383
		return (string)$this->_s;
2384
	}
2385
2386
	public function toJavaScriptLiteral()
2387
	{
2388
		return $this->__toString();
2389
	}
2390
}
2391
2392
/**
2393
 * TJavaScriptString class is an internal class that marks strings that will be
2394
 * forcibly encoded when rendered inside a javascript block
2395
 *
2396
 * @package System
2397
 * @since 3.2.0
2398
 */
2399
class TJavaScriptString extends TJavaScriptLiteral
2400
{
2401
	public function toJavaScriptLiteral()
2402
	{
2403
		return TJavaScript::jsonEncode((string)$this->_s,JSON_HEX_QUOT | JSON_HEX_APOS | JSON_HEX_TAG);
2404
	}
2405
}
2406
2407