Completed
Push — master ( 01d827...d129b1 )
by Fabio
16:20
created

TComponent::disableBehavior()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 12
rs 9.2
cc 4
eloc 8
nc 3
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
		// don't thrown an exception for __magicMethods() or any other weird methods natively implemented by php
559
		if (!method_exists($this, $method)) {
560
			throw new TApplicationException('component_method_undefined',get_class($this),$method);
561
		}
562
	}
563
564
565
	/**
566
	 * Returns a property value or an event handler list by property or event name.
567
	 * Do not call this method. This is a PHP magic method that we override
568
	 * to allow using the following syntax to read a property:
569
	 * <code>
570
	 * $value=$component->PropertyName;
571
	 * $value=$component->jsPropertyName; // return JavaScript literal
572
	 * </code>
573
	 * and to obtain the event handler list for an event,
574
	 * <code>
575
	 * $eventHandlerList=$component->EventName;
576
	 * </code>
577
	 * This will also return the global event handler list when specifing an 'fx'
578
	 * event,
579
	 * <code>
580
	 * $globalEventHandlerList=$component->fxEventName;
581
	 * </code>
582
	 * When behaviors are enabled, this will return the behavior of a specific
583
	 * name, a property of a behavior, or an object 'on' event defined by the behavior.
584
	 * @param string the property name or the event name
585
	 * @return mixed the property value or the event handler list as {@link TPriorityList}
586
	 * @throws TInvalidOperationException if the property/event is not defined.
587
	 */
588
	public function __get($name)
589
	{
590
		if(method_exists($this,$getter='get'.$name))
591
		{
592
			// getting a property
593
			return $this->$getter();
594
		}
595
		else if(method_exists($this,$jsgetter='getjs'.$name))
596
		{
597
			// getting a javascript property
598
			return (string)$this->$jsgetter();
599
		}
600
		else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
601
		{
602
			// getting an event (handler list)
603
			$name=strtolower($name);
604
			if(!isset($this->_e[$name]))
605
				$this->_e[$name]=new TPriorityList;
606
			return $this->_e[$name];
607
		}
608
		else if(strncasecmp($name,'fx',2)===0)
609
		{
610
			// getting a global event (handler list)
611
			$name=strtolower($name);
612
			if(!isset(self::$_ue[$name]))
613
				self::$_ue[$name]=new TPriorityList;
614
			return self::$_ue[$name];
615
		}
616
		else if($this->_behaviorsenabled)
617
		{
618
			// getting a behavior property/event (handler list)
619
			if(isset($this->_m[$name]))
620
				return $this->_m[$name];
621
			else if($this->_m!==null)
622
			{
623
				foreach($this->_m->toArray() as $behavior)
624
				{
625
					if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&
626
						(property_exists($behavior,$name)||$behavior->canGetProperty($name)||$behavior->hasEvent($name)))
627
						return $behavior->$name;
628
				}
629
			}
630
		}
631
		throw new TInvalidOperationException('component_property_undefined',get_class($this),$name);
632
	}
633
634
	/**
635
	 * Sets value of a component property.
636
	 * Do not call this method. This is a PHP magic method that we override
637
	 * to allow using the following syntax to set a property or attach an event handler.
638
	 * <code>
639
	 * $this->PropertyName=$value;
640
	 * $this->jsPropertyName=$value; // $value will be treated as a JavaScript literal
641
	 * $this->EventName=$handler;
642
	 * $this->fxEventName=$handler; //global event listener
643
	 * </code>
644
	 * When behaviors are enabled, this will also set a behaviors properties and events.
645
	 * @param string the property name or event name
646
	 * @param mixed the property value or event handler
647
	 * @throws TInvalidOperationException If the property is not defined or read-only.
648
	 */
649
	public function __set($name,$value)
650
	{
651
		if(method_exists($this,$setter='set'.$name))
652
		{
653
			if(strncasecmp($name,'js',2)===0&&$value&&!($value instanceof TJavaScriptLiteral))
654
				$value = new TJavaScriptLiteral($value);
655
			return $this->$setter($value);
656
		}
657
		else if(method_exists($this,$jssetter='setjs'.$name))
658
		{
659
			if($value&&!($value instanceof TJavaScriptString))
660
				$value=new TJavaScriptString($value);
661
			return $this->$jssetter($value);
662
		}
663
		else if((strncasecmp($name,'on',2)===0&&method_exists($this,$name))||strncasecmp($name,'fx',2)===0)
664
		{
665
			return $this->attachEventHandler($name,$value);
666
		}
667
		else if($this->_m!==null&&$this->_m->getCount()>0&&$this->_behaviorsenabled)
668
		{
669
			$sets=0;
670
			foreach($this->_m->toArray() as $behavior)
671
			{
672
				if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&
673
					(property_exists($behavior,$name)||$behavior->canSetProperty($name)||$behavior->hasEvent($name))) {
674
					$behavior->$name=$value;
675
					$sets++;
676
				}
677
			}
678
			if($sets)return $value;
679
680
		}
681
682
		if(method_exists($this,'get'.$name)||method_exists($this,'getjs'.$name))
683
		{
684
			throw new TInvalidOperationException('component_property_readonly',get_class($this),$name);
685
		}
686
		else
687
		{
688
			throw new TInvalidOperationException('component_property_undefined',get_class($this),$name);
689
		}
690
	}
691
692
	/**
693
	 * Checks if a property value is null, there are no events in the object
694
	 * event list or global event list registered under the name, and, if
695
	 * behaviors are enabled,
696
	 * Do not call this method. This is a PHP magic method that we override
697
	 * to allow using isset() to detect if a component property is set or not.
698
	 * This also works for global events.  When behaviors are enabled, it
699
	 * will check for a behavior of the specified name, and also check
700
	 * the behavior for events and properties.
701
	 * @param string the property name or the event name
702
	 * @since 3.2.3
703
	 */
704
	public function __isset($name)
705
	{
706
		if(method_exists($this,$getter='get'.$name))
707
			return $this->$getter()!==null;
708
		else if(method_exists($this,$jsgetter='getjs'.$name))
709
			return $this->$jsgetter()!==null;
710
		else if(strncasecmp($name,'on',2)===0&&method_exists($this,$name))
711
		{
712
			$name=strtolower($name);
713
			return isset($this->_e[$name])&&$this->_e[$name]->getCount();
714
		}
715
		else if(strncasecmp($name,'fx',2)===0)
716
		{
717
			$name=strtolower($name);
718
			return isset(self::$_ue[$name])&&self::$_ue[$name]->getCount();
719
		}
720
		else if($this->_m!==null&&$this->_m->getCount()>0&&$this->_behaviorsenabled)
721
		{
722
			if(isset($this->_m[$name]))
723
				return true;
724
			foreach($this->_m->toArray() as $behavior)
725
			{
726
				if((!($behavior instanceof IBehavior)||$behavior->getEnabled()))
727
					return isset($behavior->$name);
728
			}
729
730
		}
731
		else
732
			return false;
733
	}
734
735
	/**
736
	 * Sets a component property to be null.  Clears the object or global
737
	 * events. When enabled, loops through all behaviors and unsets the
738
	 * property or event.
739
	 * Do not call this method. This is a PHP magic method that we override
740
	 * to allow using unset() to set a component property to be null.
741
	 * @param string the property name or the event name
742
	 * @throws TInvalidOperationException if the property is read only.
743
	 * @since 3.2.3
744
	 */
745
	public function __unset($name)
746
	{
747
		if(method_exists($this,$setter='set'.$name))
748
			$this->$setter(null);
749
		else if(method_exists($this,$jssetter='setjs'.$name))
750
			$this->$jssetter(null);
751
		else if(strncasecmp($name,'on',2)===0&&method_exists($this,$name))
752
			$this->_e[strtolower($name)]->clear();
753
		else if(strncasecmp($name,'fx',2)===0)
754
			$this->getEventHandlers($name)->remove(array($this, $name));
755
		else if($this->_m!==null&&$this->_m->getCount()>0&&$this->_behaviorsenabled)
756
		{
757
			if(isset($this->_m[$name]))
758
				$this->detachBehavior($name);
759
			else {
760
				$unset=0;
761
				foreach($this->_m->toArray() as $behavior)
762
				{
763
					if((!($behavior instanceof IBehavior)||$behavior->getEnabled())) {
764
						unset($behavior->$name);
765
						$unset++;
766
					}
767
				}
768
				if(!$unset&&method_exists($this,'get'.$name))
769
					throw new TInvalidOperationException('component_property_readonly',get_class($this),$name);
770
			}
771
		} else if(method_exists($this,'get'.$name))
772
			throw new TInvalidOperationException('component_property_readonly',get_class($this),$name);
773
	}
774
775
	/**
776
	 * Determines whether a property is defined.
777
	 * A property is defined if there is a getter or setter method
778
	 * defined in the class. Note, property names are case-insensitive.
779
	 * @param string the property name
780
	 * @return boolean whether the property is defined
781
	 */
782
	public function hasProperty($name)
783
	{
784
		return $this->canGetProperty($name)||$this->canSetProperty($name);
785
	}
786
787
	/**
788
	 * Determines whether a property can be read.
789
	 * A property can be read if the class has a getter method
790
	 * for the property name. Note, property name is case-insensitive.
791
	 * This also checks for getjs.  When enabled, it loops through all
792
	 * active behaviors for the get property when undefined by the object.
793
	 * @param string the property name
794
	 * @return boolean whether the property can be read
795
	 */
796
	public function canGetProperty($name)
797
	{
798
		if(method_exists($this,'get'.$name)||method_exists($this,'getjs'.$name))
799
			return true;
800
		else if($this->_m!==null&&$this->_behaviorsenabled)
801
		{
802
			foreach($this->_m->toArray() as $behavior)
803
			{
804
				if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->canGetProperty($name))
805
					return true;
806
			}
807
		}
808
		return false;
809
	}
810
811
	/**
812
	 * Determines whether a property can be set.
813
	 * A property can be written if the class has a setter method
814
	 * for the property name. Note, property name is case-insensitive.
815
	 * This also checks for setjs.  When enabled, it loops through all
816
	 * active behaviors for the set property when undefined by the object.
817
	 * @param string the property name
818
	 * @return boolean whether the property can be written
819
	 */
820
	public function canSetProperty($name)
821
	{
822
		if(method_exists($this,'set'.$name)||method_exists($this,'setjs'.$name))
823
			return true;
824
		else if($this->_m!==null&&$this->_behaviorsenabled)
825
		{
826
			foreach($this->_m->toArray() as $behavior)
827
			{
828
				if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->canSetProperty($name))
829
					return true;
830
			}
831
		}
832
		return false;
833
	}
834
835
	/**
836
	 * Evaluates a property path.
837
	 * A property path is a sequence of property names concatenated by '.' character.
838
	 * For example, 'Parent.Page' refers to the 'Page' property of the component's
839
	 * 'Parent' property value (which should be a component also).
840
	 * When a property is not defined by an object, this also loops through all
841
	 * active behaviors of the object.
842
	 * @param string property path
843
	 * @return mixed the property path value
844
	 */
845
	public function getSubProperty($path)
846
	{
847
		$object=$this;
848
		foreach(explode('.',$path) as $property)
849
			$object=$object->$property;
850
		return $object;
851
	}
852
853
	/**
854
	 * Sets a value to a property path.
855
	 * A property path is a sequence of property names concatenated by '.' character.
856
	 * For example, 'Parent.Page' refers to the 'Page' property of the component's
857
	 * 'Parent' property value (which should be a component also).
858
	 * When a property is not defined by an object, this also loops through all
859
	 * active behaviors of the object.
860
	 * @param string property path
861
	 * @param mixed the property path value
862
	 */
863
	public function setSubProperty($path,$value)
864
	{
865
		$object=$this;
866
		if(($pos=strrpos($path,'.'))===false)
867
			$property=$path;
868
		else
869
		{
870
			$object=$this->getSubProperty(substr($path,0,$pos));
871
			$property=substr($path,$pos+1);
872
		}
873
		$object->$property=$value;
874
	}
875
876
	/**
877
	 * Determines whether an event is defined.
878
	 * An event is defined if the class has a method whose name is the event name
879
	 * prefixed with 'on', 'fx', or 'dy'.
880
	 * Every object responds to every 'fx' and 'dy' event as they are in a universally
881
	 * accepted event space.  'on' event must be declared by the object.
882
	 * When enabled, this will loop through all active behaviors for 'on' events
883
	 * defined by the behavior.
884
	 * Note, event name is case-insensitive.
885
	 * @param string the event name
886
	 * @return boolean
887
	 */
888
	public function hasEvent($name)
889
	{
890
		if((strncasecmp($name,'on',2)===0&&method_exists($this,$name))||strncasecmp($name,'fx',2)===0||strncasecmp($name,'dy',2)===0)
891
			return true;
892
893
		else if($this->_m!==null&&$this->_behaviorsenabled)
894
		{
895
			foreach($this->_m->toArray() as $behavior)
896
			{
897
				if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->hasEvent($name))
898
					return true;
899
			}
900
		}
901
		return false;
902
	}
903
904
	/**
905
	 * Checks if an event has any handlers.  This function also checks through all
906
	 * the behaviors for 'on' events when behaviors are enabled.
907
	 * 'dy' dynamic events are not handled by this function.
908
	 * @param string the event name
909
	 * @return boolean whether an event has been attached one or several handlers
910
	 */
911
	public function hasEventHandler($name)
912
	{
913
		$name=strtolower($name);
914
		if(strncasecmp($name,'fx',2)===0)
915
			return isset(self::$_ue[$name])&&self::$_ue[$name]->getCount()>0;
916
		else
917
		{
918
			if(isset($this->_e[$name])&&$this->_e[$name]->getCount()>0)
919
				return true;
920
			else if($this->_m!==null&&$this->_behaviorsenabled) {
921
				foreach($this->_m->toArray() as $behavior)
922
				{
923
					if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->hasEventHandler($name))
924
						return true;
925
				}
926
			}
927
		}
928
		return false;
929
	}
930
931
	/**
932
	 * Returns the list of attached event handlers for an 'on' or 'fx' event.   This function also
933
	 * checks through all the behaviors for 'on' event lists when behaviors are enabled.
934
	 * @return TPriorityList list of attached event handlers for an event
935
	 * @throws TInvalidOperationException if the event is not defined
936
	 */
937
	public function getEventHandlers($name)
938
	{
939
		if(strncasecmp($name,'on',2)===0&&method_exists($this,$name))
940
		{
941
			$name=strtolower($name);
942
			if(!isset($this->_e[$name]))
943
				$this->_e[$name]=new TPriorityList;
944
			return $this->_e[$name];
945
		}
946
		else if(strncasecmp($name,'fx',2)===0)
947
		{
948
			$name=strtolower($name);
949
			if(!isset(self::$_ue[$name]))
950
				self::$_ue[$name]=new TPriorityList;
951
			return self::$_ue[$name];
952
		}
953
		else if($this->_m!==null&&$this->_behaviorsenabled)
954
		{
955
			foreach($this->_m->toArray() as $behavior)
956
			{
957
				if((!($behavior instanceof IBehavior)||$behavior->getEnabled())&&$behavior->hasEvent($name))
958
					return $behavior->getEventHandlers($name);
959
			}
960
		}
961
		throw new TInvalidOperationException('component_event_undefined',get_class($this),$name);
962
	}
963
964
	/**
965
	 * Attaches an event handler to an event.
966
	 *
967
	 * The handler must be a valid PHP callback, i.e., a string referring to
968
	 * a global function name, or an array containing two elements with
969
	 * the first element being an object and the second element a method name
970
	 * of the object. In Prado, you can also use method path to refer to
971
	 * an event handler. For example, array($object,'Parent.buttonClicked')
972
	 * uses a method path that refers to the method $object->Parent->buttonClicked(...).
973
	 *
974
	 * The event handler must be of the following signature,
975
	 * <code>
976
	 * function handlerName($sender, $param) {}
977
	 * function handlerName($sender, $param, $name) {}
978
	 * </code>
979
	 * where $sender represents the object that raises the event,
980
	 * and $param is the event parameter. $name refers to the event name
981
	 * being handled.
982
	 *
983
	 * This is a convenient method to add an event handler.
984
	 * It is equivalent to {@link getEventHandlers}($name)->add($handler).
985
	 * For complete management of event handlers, use {@link getEventHandlers}
986
	 * to get the event handler list first, and then do various
987
	 * {@link TPriorityList} operations to append, insert or remove
988
	 * event handlers. You may also do these operations like
989
	 * getting and setting properties, e.g.,
990
	 * <code>
991
	 * $component->OnClick[]=array($object,'buttonClicked');
992
	 * $component->OnClick->insertAt(0,array($object,'buttonClicked'));
993
	 * </code>
994
	 * which are equivalent to the following
995
	 * <code>
996
	 * $component->getEventHandlers('OnClick')->add(array($object,'buttonClicked'));
997
	 * $component->getEventHandlers('OnClick')->insertAt(0,array($object,'buttonClicked'));
998
	 * </code>
999
	 *
1000
	 * Due to the nature of {@link getEventHandlers}, any active behaviors defining
1001
	 * new 'on' events, this method will pass through to the behavior transparently.
1002
	 *
1003
	 * @param string the event name
1004
	 * @param callback the event handler
1005
	 * @param numeric|null the priority of the handler, defaults to null which translates into the
1006
	 * default priority of 10.0 within {@link TPriorityList}
1007
	 * @throws TInvalidOperationException if the event does not exist
1008
	 */
1009
	public function attachEventHandler($name,$handler,$priority=null)
1010
	{
1011
		$this->getEventHandlers($name)->add($handler,$priority);
1012
	}
1013
1014
	/**
1015
	 * Detaches an existing event handler.
1016
	 * This method is the opposite of {@link attachEventHandler}.  It will detach
1017
	 * any 'on' events definedb by an objects active behaviors as well.
1018
	 * @param string event name
1019
	 * @param callback the event handler to be removed
1020
	 * @param numeric|false|null the priority of the handler, defaults to false which translates
1021
	 * to an item of any priority within {@link TPriorityList}; null means the default priority
1022
	 * @return boolean if the removal is successful
1023
	 */
1024
	public function detachEventHandler($name,$handler,$priority=false)
1025
	{
1026
		if($this->hasEventHandler($name))
1027
		{
1028
			try
1029
			{
1030
				$this->getEventHandlers($name)->remove($handler,$priority);
1031
				return true;
1032
			}
1033
			catch(Exception $e)
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1034
			{
1035
			}
1036
		}
1037
		return false;
1038
	}
1039
1040
	/**
1041
	 * Raises an event.  This raises both inter-object 'on' events and global 'fx' events.
1042
	 * This method represents the happening of an event and will
1043
	 * invoke all attached event handlers for the event in {@link TPriorityList} order.
1044
	 * This method does not handle intra-object/behavior dynamic 'dy' events.
1045
	 *
1046
	 * There are ways to handle event responses.  By defailt {@link EVENT_RESULT_FILTER},
1047
	 * all event responses are stored in an array, filtered for null responses, and returned.
1048
	 * If {@link EVENT_RESULT_ALL} is specified, all returned results will be stored along
1049
	 * with the sender and param in an array
1050
	 * <code>
1051
	 * 		$result[] = array('sender'=>$sender,'param'=>$param,'response'=>$response);
1052
	 * </code>
1053
	 *
1054
	 * If {@link EVENT_RESULT_FEED_FORWARD} is specified, then each handler result is then
1055
	 * fed forward as the parameters for the next event.  This allows for events to filter data
1056
	 * directly by affecting the event parameters
1057
	 *
1058
	 * If a callable function is set in the response type or the post function filter is specified then the
1059
	 * result of each called event handler is post processed by the callable function.  Used in
1060
	 * combination with {@link EVENT_RESULT_FEED_FORWARD}, any event (and its result) can be chained.
1061
	 *
1062
	 * When raising a global 'fx' event, registered handlers in the global event list for
1063
	 * {@link GLOBAL_RAISE_EVENT_LISTENER} are always added into the set of event handlers.  In this way,
1064
	 * these global events are always raised for every global 'fx' event.  The registered handlers for global
1065
	 * raiseEvent events have priorities.  Any registered global raiseEvent event handlers with a priority less than zero
1066
	 * are added before the main event handlers being raised and any registered global raiseEvent event handlers
1067
	 * with a priority equal or greater than zero are added after the main event handlers being raised.  In this way
1068
	 * all {@link GLOBAL_RAISE_EVENT_LISTENER} handlers are always called for every raised 'fx' event.
1069
	 *
1070
	 * Behaviors may implement the following functions:
1071
	 * <code>
1072
	 *	public function dyPreRaiseEvent($name,$sender,$param,$responsetype,$postfunction[, $chain]) {
1073
	 *  	return $name; //eg, the event name may be filtered/changed
1074
	 *  }
1075
	 *	public function dyIntraRaiseEventTestHandler($handler,$sender,$param,$name[, $chain]) {
1076
	 *  	return true; //should this particular handler be executed?  true/false
1077
	 *  }
1078
	 *  public function dyIntraRaiseEventPostHandler($name,$sender,$param,$handler,$response[, $chain]) {
1079
	 *		//contains the per handler response
1080
	 *  }
1081
	 *  public function dyPostRaiseEvent($responses,$name,$sender,$param,$responsetype,$postfunction[, $chain]) {
1082
	 *		return $responses;
1083
	 *  }
1084
	 * </code>
1085
	 * to be executed when raiseEvent is called.  The 'intra' dynamic events are called per handler in
1086
	 * the handler loop.
1087
	 *
1088
	 * dyPreRaiseEvent has the effect of being able to change the event being raised.  This intra
1089
	 * object/behavior event returns the name of the desired event to be raised.  It will pass through
1090
	 * if no dynamic event is specified, or if the original event name is returned.
1091
	 * dyIntraRaiseEventTestHandler returns true or false as to whether a specific handler should be
1092
	 * called for a specific raised event (and associated event arguments)
1093
	 * dyIntraRaiseEventPostHandler does not return anything.  This allows behaviors to access the results
1094
	 * of an event handler in the per handler loop.
1095
	 * dyPostRaiseEvent returns the responses.  This allows for any post processing of the event
1096
	 * results from the sum of all event handlers
1097
	 *
1098
	 * When handling a catch-all {@link __dycall}, the method name is the name of the event
1099
	 * and the parameters are the sender, the param, and then the name of the event.
1100
	 *
1101
	 * @param string the event name
1102
	 * @param mixed the event sender object
1103
	 * @param TEventParameter the event parameter
1104
	 * @param numeric how the results of the event are tabulated.  default: {@link EVENT_RESULT_FILTER}  The default filters out
1105
	 *		null responses. optional
1106
	 * @param function any per handler filtering of the response result needed is passed through
1107
	 *		this if not null. default: null.  optional
1108
	 * @return mixed the results of the event
1109
	 * @throws TInvalidOperationException if the event is undefined
1110
	 * @throws TInvalidDataValueException If an event handler is invalid
1111
	 */
1112
	public function raiseEvent($name,$sender,$param,$responsetype=null,$postfunction=null)
1113
	{
1114
		$p=$param;
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...
1115
		if(is_callable($responsetype))
1116
		{
1117
			$postfunction=$responsetype;
1118
			$responsetype=null;
1119
		}
1120
1121
		if($responsetype===null)
1122
			$responsetype=TEventResults::EVENT_RESULT_FILTER;
1123
1124
		$name=strtolower($name);
1125
		$responses=array();
1126
1127
		$name=$this->dyPreRaiseEvent($name,$sender,$param,$responsetype,$postfunction);
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...
1128
1129
		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...
1130
		{
1131
			$handlers=$this->getEventHandlers($name);
1132
			$handlerArray=$handlers->toArray();
1133
			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...
1134
			{
1135
				$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...
1136
				$handlerArray=array_merge($globalhandlers->toArrayBelowPriority(0),$handlerArray,$globalhandlers->toArrayAbovePriority(0));
1137
			}
1138
			$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...
1139
			foreach($handlerArray as $handler)
1140
			{
1141
				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...
1142
					continue;
1143
1144
				if(is_string($handler))
1145
				{
1146
					if(($pos=strrpos($handler,'.'))!==false)
1147
					{
1148
						$object=$this->getSubProperty(substr($handler,0,$pos));
1149
						$method=substr($handler,$pos+1);
1150
						if(method_exists($object,$method)||strncasecmp($method,'dy',2)===0||strncasecmp($method,'fx',2)===0)
1151
						{
1152
							if($method=='__dycall')
1153
								$response=$object->__dycall($name,array($sender,$param,$name));
1154
							else
1155
								$response=$object->$method($sender,$param,$name);
1156
						}
1157
						else
1158
							throw new TInvalidDataValueException('component_eventhandler_invalid',get_class($this),$name,$handler);
1159
					}
1160
					else
1161
						$response=call_user_func($handler,$sender,$param,$name);
1162
				}
1163
				else if(is_callable($handler,true))
1164
				{
1165
					list($object,$method)=$handler;
1166
					if(is_string($object))
1167
						$response=call_user_func($handler,$sender,$param,$name);
1168
					else
1169
					{
1170
						if(($pos=strrpos($method,'.'))!==false)
1171
						{
1172
							$object=$this->getSubProperty(substr($method,0,$pos));
1173
							$method=substr($method,$pos+1);
1174
						}
1175
						if(method_exists($object,$method)||strncasecmp($method,'dy',2)===0||strncasecmp($method,'fx',2)===0)
1176
						{
1177
							if($method=='__dycall')
1178
								$response=$object->__dycall($name,array($sender,$param,$name));
1179
							else
1180
								$response=$object->$method($sender,$param,$name);
1181
						}
1182
						else
1183
							throw new TInvalidDataValueException('component_eventhandler_invalid',get_class($this),$name,$handler[1]);
1184
					}
1185
				}
1186
				else
1187
					throw new TInvalidDataValueException('component_eventhandler_invalid',get_class($this),$name,gettype($handler));
1188
1189
				$this->dyIntraRaiseEventPostHandler($name,$sender,$param,$handler,$response);
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...
1190
1191
				if($postfunction)
1192
					$response=call_user_func_array($postfunction,array($sender,$param,$this,$response));
1193
1194
				if($responsetype&TEventResults::EVENT_RESULT_ALL)
1195
					$responses[]=array('sender'=>$sender,'param'=>$param,'response'=>$response);
1196
				else
1197
					$responses[]=$response;
1198
1199
				if($response!==null&&($responsetype&TEventResults::EVENT_RESULT_FEED_FORWARD))
1200
					$param=$response;
1201
1202
			}
1203
		}
1204
		else if(strncasecmp($name,'on',2)===0&&!$this->hasEvent($name))
1205
			throw new TInvalidOperationException('component_event_undefined',get_class($this),$name);
1206
1207
		if($responsetype&TEventResults::EVENT_RESULT_FILTER)
1208
			$responses=array_filter($responses);
1209
1210
		$responses=$this->dyPostRaiseEvent($responses,$name,$sender,$param,$responsetype,$postfunction);
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...
1211
1212
		return $responses;
1213
	}
1214
1215
	/**
1216
	 * Evaluates a PHP expression in the context of this control.
1217
	 *
1218
	 * Behaviors may implement the function:
1219
	 * <code>
1220
	 *	public function dyEvaluateExpressionFilter($expression, $chain) {
1221
	 * 		return $chain->dyEvaluateExpressionFilter(str_replace('foo', 'bar', $expression)); //example
1222
	 * }
1223
	 * </code>
1224
	 * to be executed when evaluateExpression is called.  All attached behaviors are notified through
1225
	 * dyEvaluateExpressionFilter.  The chaining is important in this function due to the filtering
1226
	 * pass-through effect.
1227
	 *
1228
	 * @param string PHP expression
1229
	 * @return mixed the expression result
1230
	 * @throws TInvalidOperationException if the expression is invalid
1231
	 */
1232
	public function evaluateExpression($expression)
1233
	{
1234
		$expression=$this->dyEvaluateExpressionFilter($expression);
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...
1235
		try
1236
		{
1237
			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...
1238
				throw new Exception('');
1239
			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...
1240
		}
1241
		catch(Exception $e)
1242
		{
1243
			throw new TInvalidOperationException('component_expression_invalid',get_class($this),$expression,$e->getMessage());
1244
		}
1245
	}
1246
1247
	/**
1248
	 * Evaluates a list of PHP statements.
1249
	 *
1250
	 * Behaviors may implement the function:
1251
	 * <code>
1252
	 *	public function dyEvaluateStatementsFilter($statements, $chain) {
1253
	 * 		return $chain->dyEvaluateStatementsFilter(str_replace('foo', 'bar', $statements)); //example
1254
	 * }
1255
	 * </code>
1256
	 * to be executed when evaluateStatements is called.  All attached behaviors are notified through
1257
	 * dyEvaluateStatementsFilter.  The chaining is important in this function due to the filtering
1258
	 * pass-through effect.
1259
	 *
1260
	 * @param string PHP statements
1261
	 * @return string content echoed or printed by the PHP statements
1262
	 * @throws TInvalidOperationException if the statements are invalid
1263
	 */
1264
	public function evaluateStatements($statements)
1265
	{
1266
		$statements=$this->dyEvaluateStatementsFilter($statements);
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...
1267
		try
1268
		{
1269
			ob_start();
1270
			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...
1271
				throw new Exception('');
1272
			$content=ob_get_contents();
1273
			ob_end_clean();
1274
			return $content;
1275
		}
1276
		catch(Exception $e)
1277
		{
1278
			throw new TInvalidOperationException('component_statements_invalid',get_class($this),$statements,$e->getMessage());
1279
		}
1280
	}
1281
1282
	/**
1283
	 * This method is invoked after the component is instantiated by a template.
1284
	 * When this method is invoked, the component's properties have been initialized.
1285
	 * The default implementation of this method will invoke
1286
	 * the potential parent component's {@link addParsedObject}.
1287
	 * This method can be overridden.
1288
	 *
1289
	 * Behaviors may implement the function:
1290
	 * <code>
1291
	 *	public function dyCreatedOnTemplate($parent, $chain) {
1292
	 * 		return $chain->dyCreatedOnTemplate($parent); //example
1293
	 *  }
1294
	 * </code>
1295
	 * to be executed when createdOnTemplate is called.  All attached behaviors are notified through
1296
	 * dyCreatedOnTemplate.
1297
	 *
1298
	 * @param TComponent potential parent of this control
1299
	 * @see addParsedObject
1300
	 */
1301
	public function createdOnTemplate($parent)
1302
	{
1303
		$parent=$this->dyCreatedOnTemplate($parent);
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...
1304
		$parent->addParsedObject($this);
1305
	}
1306
1307
	/**
1308
	 * Processes an object that is created during parsing template.
1309
	 * The object can be either a component or a static text string.
1310
	 * This method can be overridden to customize the handling of newly created objects in template.
1311
	 * Only framework developers and control developers should use this method.
1312
	 *
1313
	 * Behaviors may implement the function:
1314
	 * <code>
1315
	 *	public function dyAddParsedObject($object[, $chain]) {
1316
	 *  }
1317
	 * </code>
1318
	 * to be executed when addParsedObject is called.  All attached behaviors are notified through
1319
	 * dyAddParsedObject.
1320
	 *
1321
	 * @param string|TComponent text string or component parsed and instantiated in template
1322
	 * @see createdOnTemplate
1323
	 */
1324
	public function addParsedObject($object)
1325
	{
1326
		$this->dyAddParsedObject($object);
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...
1327
	}
1328
1329
1330
	/**
1331
	 *	This is the method registered for all instanced objects should a class behavior be added after
1332
	 * the class is instanced.  Only when the class to which the behavior is being added is in this
1333
	 * object's class hierarchy, via {@link getClassHierarchy}, is the behavior added to this instance.
1334
	 * @param $sender the application
1335
	 * @param $param TClassBehaviorEventParameter
1336
	 * @since 3.2.3
1337
	 */
1338
	public function fxAttachClassBehavior($sender,$param) {
1339
		if(in_array($param->getClass(),$this->getClassHierarchy(true)))
1340
			return $this->attachBehavior($param->getName(),$param->getBehavior(),$param->getPriority());
1341
	}
1342
1343
1344
	/**
1345
	 *	This is the method registered for all instanced objects should a class behavior be removed after
1346
	 * the class is instanced.  Only when the class to which the behavior is being added is in this
1347
	 * object's class hierarchy, via {@link getClassHierarchy}, is the behavior removed from this instance.
1348
	 * @param $sender the application
1349
	 * @param $param TClassBehaviorEventParameter
1350
	 * @since 3.2.3
1351
	 */
1352
	public function fxDetachClassBehavior($sender,$param) {
1353
		if(in_array($param->getClass(),$this->getClassHierarchy(true)))
1354
			return $this->detachBehavior($param->getName(),$param->getPriority());
1355
	}
1356
1357
1358
	/**
1359
	 *	This will add a class behavior to all classes instanced (that are listening) and future newly instanced objects.
1360
	 * This registers the behavior for future instances and pushes the changes to all the instances that are listening as well.
1361
	 * The universal class behaviors are stored in an inverted stack with the latest class behavior being at the first position in the array.
1362
	 * This is done so class behaviors are added last first.
1363
	 * @param string name the key of the class behavior
1364
	 * @param object|string class behavior or name of the object behavior per instance
1365
	 * @param string|class string of class or class on which to attach this behavior.  Defaults to null which will error
1366
	 *	but more important, if this is on PHP 5.3 it will use Late Static Binding to derive the class
1367
	 * it should extend.
1368
	 * <code>
1369
	 * TPanel::attachClassBehavior('javascripts', (new TJsPanelBehavior())->init($this));
1370
	 * </code>
1371
	 * @param numeric|null priority of behavior, default: null the default priority of the {@link TPriorityList}  Optional.
1372
	 * @throws TInvalidOperationException if the class behavior is being added to a {@link TComponent}; due to recursion.
1373
	 * @throws TInvalidOperationException if the class behavior is already defined
1374
	 * @since 3.2.3
1375
	 */
1376
	public static function attachClassBehavior($name,$behavior,$class=null,$priority=null) {
1377
		if(!$class&&function_exists('get_called_class'))
1378
			$class=get_called_class();
1379
		if(!$class)
1380
			throw new TInvalidOperationException('component_no_class_provided_nor_late_binding');
1381
1382
		if(!is_string($name))
1383
			$name=get_class($name);
1384
		$class=strtolower($class);
1385
		if($class==='tcomponent')
1386
			throw new TInvalidOperationException('component_no_tcomponent_class_behaviors');
1387
		if(empty(self::$_um[$class]))
1388
			self::$_um[$class]=array();
1389
		if(isset(self::$_um[$class][$name]))
1390
			throw new TInvalidOperationException('component_class_behavior_defined',$class,$name);
1391
		$param=new TClassBehaviorEventParameter($class,$name,$behavior,$priority);
1392
		self::$_um[$class]=array($name=>$param)+self::$_um[$class];
1393
		$behaviorObject=is_string($behavior)?new $behavior:$behavior;
1394
		return $behaviorObject->raiseEvent('fxAttachClassBehavior',null,$param);
1395
	}
1396
1397
1398
	/**
1399
	 *	This will remove a behavior from a class.  It unregisters it from future instances and
1400
	 * pulls the changes from all the instances that are listening as well.
1401
	 * PHP 5.3 uses Late Static Binding to derive the static class upon which this method is called.
1402
	 * @param $name the key of the class behavior
1403
	 * @param $class string class on which to attach this behavior.  Defaults to null.
1404
	 * @param $priority numeric|null|false priority.  false is any priority, null is default
1405
	 *		{@link TPriorityList} priority, and numeric is a specific priority.
1406
	 * @throws Exception if the the class cannot be derived from Late Static Binding and is not
1407
	 * not supplied as a parameter.
1408
	 * @since 3.2.3
1409
	 */
1410
	public static function detachClassBehavior($name,$class=null,$priority=false) {
1411
		if(!$class&&function_exists('get_called_class'))
1412
			$class=get_called_class();
1413
		if(!$class)
1414
			throw new TInvalidOperationException('component_no_class_provided_nor_late_binding');
1415
1416
		$class=strtolower($class);
1417
		if(!is_string($name))
1418
			$name=get_class($name);
1419
		if(empty(self::$_um[$class])||!isset(self::$_um[$class][$name]))
1420
			return false;
1421
		$param=self::$_um[$class][$name];
1422
		$behavior=$param->getBehavior();
1423
		unset(self::$_um[$class][$name]);
1424
		$behaviorObject=is_string($behavior)?new $behavior:$behavior;
1425
		return $behaviorObject->raiseEvent('fxDetachClassBehavior',null,$param);
1426
	}
1427
1428
	/**
1429
	 * Returns the named behavior object.
1430
	 * The name 'asa' stands for 'as a'.
1431
	 * @param string the behavior name
1432
	 * @return IBehavior the behavior object, or null if the behavior does not exist
1433
	 * @since 3.2.3
1434
	 */
1435
	public function asa($behaviorname)
1436
	{
1437
		return isset($this->_m[$behaviorname])?$this->_m[$behaviorname]:null;
1438
	}
1439
1440
	/**
1441
	 * Returns whether or not the object or any of the behaviors are of a particular class.
1442
	 * The name 'isa' stands for 'is a'.  This first checks if $this is an instanceof the class.
1443
	 * It then checks each Behavior.  If a behavior implements {@link IInstanceCheck},
1444
	 * then the behavior can determine what it is an instanceof.  If this behavior function returns true,
1445
	 * then this method returns true.  If the behavior instance checking function returns false,
1446
	 * then no further checking is performed as it is assumed to be correct.
1447
	 *
1448
	 * If the behavior instance check function returns nothing or null or the behavior
1449
	 * doesn't implement the {@link IInstanceCheck} interface, then the default instanceof occurs.
1450
	 * The default isa behavior is to check if the behavior is an instanceof the class.
1451
	 *
1452
	 * The behavior {@link IInstanceCheck} is to allow a behavior to have the host object
1453
	 * act as a completely different object.
1454
	 *
1455
	 * @param class or string
1456
	 * @return boolean whether or not the object or a behavior is an instance of a particular class
1457
	 * @since 3.2.3
1458
	 */
1459
	public function isa($class)
1460
	{
1461
		if($this instanceof $class)
1462
			return true;
1463
		if($this->_m!==null&&$this->_behaviorsenabled)
1464
			foreach($this->_m->toArray() as $behavior){
1465
				if(($behavior instanceof IBehavior)&&!$behavior->getEnabled())
1466
					continue;
1467
1468
				$check = null;
1469
				if(($behavior->isa('IInstanceCheck'))&&$check=$behavior->isinstanceof($class,$this))
1470
					return true;
1471
				if($check===null&&($behavior->isa($class)))
1472
					return true;
1473
			}
1474
		return false;
1475
	}
1476
1477
	/**
1478
	 * Attaches a list of behaviors to the component.
1479
	 * Each behavior is indexed by its name and should be an instance of
1480
	 * {@link IBehavior}, a string specifying the behavior class, or a
1481
	 * {@link TClassBehaviorEventParameter}.
1482
	 * @param array list of behaviors to be attached to the component
1483
	 * @since 3.2.3
1484
	 */
1485
	public function attachBehaviors($behaviors)
1486
	{
1487
		foreach($behaviors as $name=>$behavior)
1488
			if($behavior instanceof TClassBehaviorEventParameter)
1489
				$this->attachBehavior($behavior->getName(),$behavior->getBehavior(),$behavior->getPriority());
1490
			else
1491
				$this->attachBehavior($name,$behavior);
1492
	}
1493
1494
	/**
1495
	 * Detaches select behaviors from the component.
1496
	 * Each behavior is indexed by its name and should be an instance of
1497
	 * {@link IBehavior}, a string specifying the behavior class, or a
1498
	 * {@link TClassBehaviorEventParameter}.
1499
	 * @param array list of behaviors to be detached from the component
1500
	 * @since 3.2.3
1501
	 */
1502
	public function detachBehaviors($behaviors)
1503
	{
1504
		if($this->_m!==null)
1505
		{
1506
			foreach($behaviors as $name=>$behavior)
1507
				if($behavior instanceof TClassBehaviorEventParameter)
1508
					$this->detachBehavior($behavior->getName(),$behavior->getPriority());
1509
				else
1510
					$this->detachBehavior(is_string($behavior)?$behavior:$name);
1511
		}
1512
	}
1513
1514
	/**
1515
	 * Detaches all behaviors from the component.
1516
	 * @since 3.2.3
1517
	 */
1518
	public function clearBehaviors()
1519
	{
1520
		if($this->_m!==null)
1521
		{
1522
			foreach($this->_m->toArray() as $name=>$behavior)
1523
				$this->detachBehavior($name);
1524
			$this->_m=null;
1525
		}
1526
	}
1527
1528
	/**
1529
	 * Attaches a behavior to this component.
1530
	 * This method will create the behavior object based on the given
1531
	 * configuration. After that, the behavior object will be initialized
1532
	 * by calling its {@link IBehavior::attach} method.
1533
	 *
1534
	 * Already attached behaviors may implement the function:
1535
	 * <code>
1536
	 *	public function dyAttachBehavior($name,$behavior[, $chain]) {
1537
	 *  }
1538
	 * </code>
1539
	 * to be executed when attachBehavior is called.  All attached behaviors are notified through
1540
	 * dyAttachBehavior.
1541
	 *
1542
	 * @param string the behavior's name. It should uniquely identify this behavior.
1543
	 * @param mixed the behavior configuration. This is passed as the first
1544
	 * parameter to {@link YiiBase::createComponent} to create the behavior object.
1545
	 * @return IBehavior the behavior object
1546
	 * @since 3.2.3
1547
	 */
1548
	public function attachBehavior($name,$behavior,$priority=null)
1549
	{
1550
		if(is_string($behavior))
1551
			$behavior=Prado::createComponent($behavior);
1552
		if(!($behavior instanceof IBaseBehavior))
1553
			throw new TInvalidDataTypeException('component_not_a_behavior',get_class($behavior));
1554
		if($behavior instanceof IBehavior)
1555
			$behavior->setEnabled(true);
1556
		if($this->_m===null)
1557
			$this->_m=new TPriorityMap;
1558
		$behavior->attach($this);
1559
		$this->dyAttachBehavior($name,$behavior);
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...
1560
		$this->_m->add($name,$behavior,$priority);
1561
		return $behavior;
1562
	}
1563
1564
	/**
1565
	 * Detaches a behavior from the component.
1566
	 * The behavior's {@link IBehavior::detach} method will be invoked.
1567
	 *
1568
	 * Behaviors may implement the function:
1569
	 * <code>
1570
	 *	public function dyDetachBehavior($name,$behavior[, $chain]) {
1571
	 *  }
1572
	 * </code>
1573
	 * to be executed when detachBehavior is called.  All attached behaviors are notified through
1574
	 * dyDetachBehavior.
1575
	 *
1576
	 * @param string the behavior's name. It uniquely identifies the behavior.
1577
	 * @param numeric the behavior's priority. This defaults to false, aka any priority.
1578
	 * @return IBehavior the detached behavior. Null if the behavior does not exist.
1579
	 * @since 3.2.3
1580
	 */
1581
	public function detachBehavior($name,$priority=false)
1582
	{
1583
		if($this->_m!=null&&isset($this->_m[$name]))
1584
		{
1585
			$this->_m[$name]->detach($this);
1586
			$behavior=$this->_m->itemAt($name);
1587
			$this->_m->remove($name,$priority);
1588
			$this->dyDetachBehavior($name,$behavior);
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...
1589
			return $behavior;
1590
		}
1591
	}
1592
1593
	/**
1594
	 * Enables all behaviors attached to this component independent of the behaviors
1595
	 *
1596
	 * Behaviors may implement the function:
1597
	 * <code>
1598
	 *	public function dyEnableBehaviors($name,$behavior[, $chain]) {
1599
	 *  }
1600
	 * </code>
1601
	 * to be executed when enableBehaviors is called.  All attached behaviors are notified through
1602
	 * dyEnableBehaviors.
1603
	 * @since 3.2.3
1604
	 */
1605
	public function enableBehaviors()
1606
	{
1607
		if(!$this->_behaviorsenabled)
1608
		{
1609
			$this->_behaviorsenabled=true;
1610
			$this->dyEnableBehaviors();
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...
1611
		}
1612
	}
1613
1614
	/**
1615
	 * Disables all behaviors attached to this component independent of the behaviors
1616
	 *
1617
	 * Behaviors may implement the function:
1618
	 * <code>
1619
	 *	public function dyDisableBehaviors($name,$behavior[, $chain]) {
1620
	 *  }
1621
	 * </code>
1622
	 * to be executed when disableBehaviors is called.  All attached behaviors are notified through
1623
	 * dyDisableBehaviors.
1624
	 * @since 3.2.3
1625
	 */
1626
	public function disableBehaviors()
1627
	{
1628
		if($this->_behaviorsenabled)
1629
		{
1630
			$this->dyDisableBehaviors();
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...
1631
			$this->_behaviorsenabled=false;
1632
		}
1633
	}
1634
1635
1636
	/**
1637
	 * Returns if all the behaviors are turned on or off for the object.
1638
	 * @return boolean whether or not all behaviors are enabled (true) or not (false)
1639
	 * @since 3.2.3
1640
	 */
1641
	public function getBehaviorsEnabled()
1642
	{
1643
		return $this->_behaviorsenabled;
1644
	}
1645
1646
	/**
1647
	 * Enables an attached object behavior.  This cannot enable or disable whole class behaviors.
1648
	 * A behavior is only effective when it is enabled.
1649
	 * A behavior is enabled when first attached.
1650
	 *
1651
	 * Behaviors may implement the function:
1652
	 * <code>
1653
	 *	public function dyEnableBehavior($name,$behavior[, $chain]) {
1654
	 *  }
1655
	 * </code>
1656
	 * to be executed when enableBehavior is called.  All attached behaviors are notified through
1657
	 * dyEnableBehavior.
1658
	 *
1659
	 * @param string the behavior's name. It uniquely identifies the behavior.
1660
	 * @since 3.2.3
1661
	 */
1662
	public function enableBehavior($name)
1663
	{
1664
		if($this->_m!=null&&isset($this->_m[$name])){
1665
			if($this->_m[$name] instanceof IBehavior) {
1666
				$this->_m[$name]->setEnabled(true);
1667
				$this->dyEnableBehavior($name,$this->_m[$name]);
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...
1668
				return true;
1669
			}
1670
			return false;
1671
		}
1672
		return null;
1673
	}
1674
1675
	/**
1676
	 * Disables an attached behavior.  This cannot enable or disable whole class behaviors.
1677
	 * A behavior is only effective when it is enabled.
1678
	 *
1679
	 * Behaviors may implement the function:
1680
	 * <code>
1681
	 *	public function dyDisableBehavior($name,$behavior[, $chain]) {
1682
	 *  }
1683
	 * </code>
1684
	 * to be executed when disableBehavior is called.  All attached behaviors are notified through
1685
	 * dyDisableBehavior.
1686
	 *
1687
	 * @param string the behavior's name. It uniquely identifies the behavior.
1688
	 * @since 3.2.3
1689
	 */
1690
	public function disableBehavior($name)
1691
	{
1692
		if($this->_m!=null&&isset($this->_m[$name])){
1693
			if($this->_m[$name] instanceof IBehavior) {
1694
				$this->_m[$name]->setEnabled(false);
1695
				$this->dyDisableBehavior($name,$this->_m[$name]);
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...
1696
				return true;
1697
			}
1698
			return false;
1699
		}
1700
		return null;
1701
	}
1702
1703
	/**
1704
	 * Returns an array with the names of all variables of that object that should be serialized.
1705
	 * Do not call this method. This is a PHP magic method that will be called automatically
1706
	 * prior to any serialization.
1707
	 */
1708
	public function __sleep()
1709
	{
1710
		$a = (array)$this;
1711
		$a = array_keys($a);
1712
		$exprops = array();
1713
		$this->_getZappableSleepProps($exprops);
1714
		return array_diff($a, $exprops);
1715
	}
1716
1717
	/**
1718
	 * Returns an array with the names of all variables of this object that should NOT be serialized
1719
	 * because their value is the default one or useless to be cached for the next page loads.
1720
	 * Reimplement in derived classes to add new variables, but remember to  also to call the parent
1721
	 * implementation first.
1722
	 */
1723
	protected function _getZappableSleepProps(&$exprops)
1724
	{
1725
		if($this->_listeningenabled===false)
1726
			$exprops[] = "\0TComponent\0_listeningenabled";
1727
		if($this->_behaviorsenabled===true)
1728
			$exprops[] = "\0TComponent\0_behaviorsenabled";
1729
		if ($this->_e===array())
1730
			$exprops[] = "\0TComponent\0_e";
1731
		if ($this->_m===null)
1732
			$exprops[] = "\0TComponent\0_m";
1733
	}
1734
}
1735
1736
1737
/**
1738
 * IDynamicMethods interface.
1739
 * IDynamicMethods marks an object to receive undefined global or dynamic events.
1740
 *
1741
 * @author Brad Anderson <[email protected]>
1742
 * @version $Id$
1743
 * @package System
1744
 * @since 3.2.3
1745
 */
1746
interface IDynamicMethods
1747
{
1748
	public function __dycall($method,$args);
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...
1749
}
1750
1751
1752
1753
/**
1754
 * TClassBehaviorEventParameter class.
1755
 * TClassBehaviorEventParameter is the parameter sent with the class behavior changes.
1756
 *
1757
 * @author Brad Anderson <[email protected]>
1758
 * @version $Id$
1759
 * @package System
1760
 * @since 3.2.3
1761
 */
1762
class TClassBehaviorEventParameter extends TEventParameter
1763
{
1764
	private $_class;
1765
	private $_name;
1766
	private $_behavior;
1767
	private $_priority;
1768
1769
	/**
1770
	 * Holds the parameters for the Class Behavior Events
1771
	 *	@param string $class this is the class to get the behavior
1772
	 *	@param string $name the name of the behavior
1773
	 *	@param object $behavior this is the behavior to implement the class behavior
1774
	 */
1775
	public function __construct($class,$name,$behavior,$priority)
1776
	{
1777
		$this->_class=$class;
1778
		$this->_name=$name;
1779
		$this->_behavior=$behavior;
1780
		$this->_priority=$priority;
1781
	}
1782
1783
	/**
1784
	 * This is the class to get the behavior
1785
	 * @return string the class to get the behavior
1786
	 */
1787
	public function getClass()
1788
	{
1789
		return $this->_class;
1790
	}
1791
1792
	/**
1793
	 * name of the behavior
1794
	 * @return string the name to get the behavior
1795
	 */
1796
	public function getName()
1797
	{
1798
		return $this->_name;
1799
	}
1800
1801
	/**
1802
	 * This is the behavior which the class is to get
1803
	 * @return object the behavior to implement
1804
	 */
1805
	public function getBehavior()
1806
	{
1807
		return $this->_behavior;
1808
	}
1809
1810
	/**
1811
	 * This is the priority which the behavior is to get
1812
	 * @return numeric the priority of the behavior
1813
	 */
1814
	public function getPriority()
1815
	{
1816
		return $this->_priority;
1817
	}
1818
}
1819
1820
1821
/**
1822
 * TEnumerable class.
1823
 * TEnumerable is the base class for all enumerable types.
1824
 * To define an enumerable type, extend TEnumberable and define string constants.
1825
 * Each constant represents an enumerable value.
1826
 * The constant name must be the same as the constant value.
1827
 * For example,
1828
 * <code>
1829
 * class TTextAlign extends TEnumerable
1830
 * {
1831
 *     const Left='Left';
1832
 *     const Right='Right';
1833
 * }
1834
 * </code>
1835
 * Then, one can use the enumerable values such as TTextAlign::Left and
1836
 * TTextAlign::Right.
1837
 *
1838
 * @author Qiang Xue <[email protected]>
1839
 * @package System
1840
 * @since 3.0
1841
 */
1842
class TEnumerable implements Iterator
1843
{
1844
	private $_enums=array();
1845
1846
	public function __construct() {
1847
		$reflection=new ReflectionClass($this);
1848
		$this->_enums=$reflection->getConstants();
1849
	}
1850
1851
	public function current() {
1852
		return current($this->_enums);
1853
	}
1854
1855
	public function key() {
1856
		return key($this->_enums);
1857
	}
1858
1859
	public function next() {
1860
		return next($this->_enums);
1861
	}
1862
1863
	public function rewind() {
1864
		reset($this->_enums);
1865
	}
1866
1867
	public function valid() {
1868
		return $this->current()!==false;
1869
	}
1870
}
1871
1872
/**
1873
 * TPropertyValue class
1874
 *
1875
 * TPropertyValue is a utility class that provides static methods
1876
 * to convert component property values to specific types.
1877
 *
1878
 * TPropertyValue is commonly used in component setter methods to ensure
1879
 * the new property value is of specific type.
1880
 * For example, a boolean-typed property setter method would be as follows,
1881
 * <code>
1882
 * function setPropertyName($value) {
1883
 *     $value=TPropertyValue::ensureBoolean($value);
1884
 *     // $value is now of boolean type
1885
 * }
1886
 * </code>
1887
 *
1888
 * Properties can be of the following types with specific type conversion rules:
1889
 * - string: a boolean value will be converted to 'true' or 'false'.
1890
 * - boolean: string 'true' (case-insensitive) will be converted to true,
1891
 *            string 'false' (case-insensitive) will be converted to false.
1892
 * - integer
1893
 * - float
1894
 * - array: string starting with '(' and ending with ')' will be considered as
1895
 *          as an array expression and will be evaluated. Otherwise, an array
1896
 *          with the value to be ensured is returned.
1897
 * - object
1898
 * - enum: enumerable type, represented by an array of strings.
1899
 *
1900
 * @author Qiang Xue <[email protected]>
1901
 * @package System
1902
 * @since 3.0
1903
 */
1904
class TPropertyValue
1905
{
1906
	/**
1907
	 * Converts a value to boolean type.
1908
	 * Note, string 'true' (case-insensitive) will be converted to true,
1909
	 * string 'false' (case-insensitive) will be converted to false.
1910
	 * If a string represents a non-zero number, it will be treated as true.
1911
	 * @param mixed the value to be converted.
1912
	 * @return boolean
1913
	 */
1914
	public static function ensureBoolean($value)
1915
	{
1916
		if (is_string($value))
1917
			return strcasecmp($value,'true')==0 || $value!=0;
1918
		else
1919
			return (boolean)$value;
1920
	}
1921
1922
	/**
1923
	 * Converts a value to string type.
1924
	 * Note, a boolean value will be converted to 'true' if it is true
1925
	 * and 'false' if it is false.
1926
	 * @param mixed the value to be converted.
1927
	 * @return string
1928
	 */
1929
	public static function ensureString($value)
1930
	{
1931
		if (TJavaScript::isJsLiteral($value))
1932
			return $value;
1933
		if (is_bool($value))
1934
			return $value?'true':'false';
1935
		else
1936
			return (string)$value;
1937
	}
1938
1939
	/**
1940
	 * Converts a value to integer type.
1941
	 * @param mixed the value to be converted.
1942
	 * @return integer
1943
	 */
1944
	public static function ensureInteger($value)
1945
	{
1946
		return (integer)$value;
1947
	}
1948
1949
	/**
1950
	 * Converts a value to float type.
1951
	 * @param mixed the value to be converted.
1952
	 * @return float
1953
	 */
1954
	public static function ensureFloat($value)
1955
	{
1956
		return (float)$value;
1957
	}
1958
1959
	/**
1960
	 * Converts a value to array type. If the value is a string and it is
1961
	 * in the form (a,b,c) then an array consisting of each of the elements
1962
	 * will be returned. If the value is a string and it is not in this form
1963
	 * then an array consisting of just the string will be returned. If the value
1964
	 * is not a string then
1965
	 * @param mixed the value to be converted.
1966
	 * @return array
1967
	 */
1968
	public static function ensureArray($value)
1969
	{
1970
		if(is_string($value))
1971
		{
1972
			$value = trim($value);
1973
			$len = strlen($value);
1974
			if ($len >= 2 && $value[0] == '(' && $value[$len-1] == ')')
1975
			{
1976
				eval('$array=array'.$value.';');
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...
1977
				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...
1978
			}
1979
			else
1980
				return $len>0?array($value):array();
1981
		}
1982
		else
1983
			return (array)$value;
1984
	}
1985
1986
	/**
1987
	 * Converts a value to object type.
1988
	 * @param mixed the value to be converted.
1989
	 * @return object
1990
	 */
1991
	public static function ensureObject($value)
1992
	{
1993
		return (object)$value;
1994
	}
1995
1996
	/**
1997
	 * Converts a value to enum type.
1998
	 *
1999
	 * This method checks if the value is of the specified enumerable type.
2000
	 * A value is a valid enumerable value if it is equal to the name of a constant
2001
	 * in the specified enumerable type (class).
2002
	 * For more details about enumerable, see {@link TEnumerable}.
2003
	 *
2004
	 * For backward compatibility, this method also supports sanity
2005
	 * check of a string value to see if it is among the given list of strings.
2006
	 * @param mixed the value to be converted.
2007
	 * @param mixed class name of the enumerable type, or array of valid enumeration values. If this is not an array,
2008
	 * the method considers its parameters are of variable length, and the second till the last parameters are enumeration values.
2009
	 * @return string the valid enumeration value
2010
	 * @throws TInvalidDataValueException if the original value is not in the string array.
2011
	 */
2012
	public static function ensureEnum($value,$enums)
2013
	{
2014
		static $types=array();
2015
		if(func_num_args()===2 && is_string($enums))
2016
		{
2017
			if(!isset($types[$enums]))
2018
				$types[$enums]=new ReflectionClass($enums);
2019
			if($types[$enums]->hasConstant($value))
2020
				return $value;
2021
			else
2022
				throw new TInvalidDataValueException(
2023
					'propertyvalue_enumvalue_invalid',$value,
2024
						implode(' | ',$types[$enums]->getConstants()));
2025
		}
2026
		else if(!is_array($enums))
2027
		{
2028
			$enums=func_get_args();
2029
			array_shift($enums);
2030
		}
2031
		if(in_array($value,$enums,true))
2032
			return $value;
2033
		else
2034
			throw new TInvalidDataValueException('propertyvalue_enumvalue_invalid',$value,implode(' | ',$enums));
2035
	}
2036
2037
	/**
2038
	 * Converts the value to 'null' if the given value is empty
2039
	 * @param mixed value to be converted
2040
	 * @return mixed input or NULL if input is empty
2041
	 */
2042
	public static function ensureNullIfEmpty($value)
2043
	{
2044
		return empty($value)?null:$value;
2045
	}
2046
}
2047
2048
/**
2049
 * TEventParameter class.
2050
 * TEventParameter is the base class for all event parameter classes.
2051
 *
2052
 * @author Qiang Xue <[email protected]>
2053
 * @package System
2054
 * @since 3.0
2055
 */
2056
class TEventParameter extends TComponent
2057
{
2058
}
2059
2060
class TEventResults extends TEnumerable {
2061
	const EVENT_RESULT_FEED_FORWARD=1;
2062
	const EVENT_RESULT_FILTER=2;
2063
	const EVENT_RESULT_ALL=4;
2064
}
2065
2066
/**
2067
 * TComponentReflection class.
2068
 *
2069
 * TComponentReflection provides functionalities to inspect the public/protected
2070
 * properties, events and methods defined in a class.
2071
 *
2072
 * The following code displays the properties and events defined in {@link TDataGrid},
2073
 * <code>
2074
 *   $reflection=new TComponentReflection('TDataGrid');
2075
 *   Prado::varDump($reflection->getProperties());
2076
 *   Prado::varDump($reflection->getEvents());
2077
 * </code>
2078
 *
2079
 * @author Qiang Xue <[email protected]>
2080
 * @package System
2081
 * @since 3.0
2082
 */
2083
class TComponentReflection extends TComponent
2084
{
2085
	private $_className;
2086
	private $_properties=array();
2087
	private $_events=array();
2088
	private $_methods=array();
2089
2090
	/**
2091
	 * Constructor.
2092
	 * @param object|string the component instance or the class name
2093
	 * @throws TInvalidDataTypeException if the object is not a component
2094
	 */
2095
	public function __construct($component)
2096
	{
2097
		if(is_string($component) && class_exists($component,false))
2098
			$this->_className=$component;
2099
		else if(is_object($component))
2100
			$this->_className=get_class($component);
2101
		else
2102
			throw new TInvalidDataTypeException('componentreflection_class_invalid');
2103
		$this->reflect();
2104
	}
2105
2106
	private function isPropertyMethod($method)
2107
	{
2108
		$methodName=$method->getName();
2109
		return $method->getNumberOfRequiredParameters()===0
2110
				&& strncasecmp($methodName,'get',3)===0
2111
				&& isset($methodName[3]);
2112
	}
2113
2114
	private function isEventMethod($method)
2115
	{
2116
		$methodName=$method->getName();
2117
		return strncasecmp($methodName,'on',2)===0
2118
				&& isset($methodName[2]);
2119
	}
2120
2121
	private function reflect()
2122
	{
2123
		$class=new ReflectionClass($this->_className);
2124
		$properties=array();
2125
		$events=array();
2126
		$methods=array();
2127
		$isComponent=is_subclass_of($this->_className,'TComponent') || strcasecmp($this->_className,'TComponent')===0;
2128
		foreach($class->getMethods() as $method)
2129
		{
2130
			if($method->isPublic() || $method->isProtected())
2131
			{
2132
				$methodName=$method->getName();
2133
				if(!$method->isStatic() && $isComponent)
2134
				{
2135
					if($this->isPropertyMethod($method))
2136
						$properties[substr($methodName,3)]=$method;
2137
					else if($this->isEventMethod($method))
2138
					{
2139
						$methodName[0]='O';
2140
						$events[$methodName]=$method;
2141
					}
2142
				}
2143
				if(strncmp($methodName,'__',2)!==0)
2144
					$methods[$methodName]=$method;
2145
			}
2146
		}
2147
		$reserved=array();
2148
		ksort($properties);
2149
		foreach($properties as $name=>$method)
2150
		{
2151
			$this->_properties[$name]=array(
2152
				'type'=>$this->determinePropertyType($method),
2153
				'readonly'=>!$class->hasMethod('set'.$name),
2154
				'protected'=>$method->isProtected(),
2155
				'class'=>$method->getDeclaringClass()->getName(),
2156
				'comments'=>$method->getDocComment()
2157
			);
2158
			$reserved['get'.strtolower($name)]=1;
2159
			$reserved['set'.strtolower($name)]=1;
2160
		}
2161
		ksort($events);
2162
		foreach($events as $name=>$method)
2163
		{
2164
			$this->_events[$name]=array(
2165
				'class'=>$method->getDeclaringClass()->getName(),
2166
				'protected'=>$method->isProtected(),
2167
				'comments'=>$method->getDocComment()
2168
			);
2169
			$reserved[strtolower($name)]=1;
2170
		}
2171
		ksort($methods);
2172
		foreach($methods as $name=>$method)
2173
		{
2174
			if(!isset($reserved[strtolower($name)]))
2175
				$this->_methods[$name]=array(
2176
					'class'=>$method->getDeclaringClass()->getName(),
2177
					'protected'=>$method->isProtected(),
2178
					'static'=>$method->isStatic(),
2179
					'comments'=>$method->getDocComment()
2180
				);
2181
		}
2182
	}
2183
2184
	/**
2185
	 * Determines the property type.
2186
	 * This method uses the doc comment to determine the property type.
2187
	 * @param ReflectionMethod
2188
	 * @return string the property type, '{unknown}' if type cannot be determined from comment
2189
	 */
2190
	protected function determinePropertyType($method)
2191
	{
2192
		$comment=$method->getDocComment();
2193
		if(preg_match('/@return\\s+(.*?)\\s+/',$comment,$matches))
2194
			return $matches[1];
2195
		else
2196
			return '{unknown}';
2197
	}
2198
2199
	/**
2200
	 * @return string class name of the component
2201
	 */
2202
	public function getClassName()
2203
	{
2204
		return $this->_className;
2205
	}
2206
2207
	/**
2208
	 * @return array list of component properties. Array keys are property names.
2209
	 * Each array element is of the following structure:
2210
	 * [type]=>property type,
2211
	 * [readonly]=>whether the property is read-only,
2212
	 * [protected]=>whether the method is protected or not
2213
	 * [class]=>the class where the property is inherited from,
2214
	 * [comments]=>comments	associated with the property.
2215
	 */
2216
	public function getProperties()
2217
	{
2218
		return $this->_properties;
2219
	}
2220
2221
	/**
2222
	 * @return array list of component events. Array keys are event names.
2223
	 * Each array element is of the following structure:
2224
	 * [protected]=>whether the event is protected or not
2225
	 * [class]=>the class where the event is inherited from.
2226
	 * [comments]=>comments associated with the event.
2227
	 */
2228
	public function getEvents()
2229
	{
2230
		return $this->_events;
2231
	}
2232
2233
	/**
2234
	 * @return array list of public/protected methods. Array keys are method names.
2235
	 * Each array element is of the following structure:
2236
	 * [protected]=>whether the method is protected or not
2237
	 * [static]=>whether the method is static or not
2238
	 * [class]=>the class where the property is inherited from,
2239
	 * [comments]=>comments associated with the event.
2240
	 */
2241
	public function getMethods()
2242
	{
2243
		return $this->_methods;
2244
	}
2245
}
2246
2247
/**
2248
 * IBaseBehavior interface is the base behavior class from which all other
2249
 * behaviors types are derived
2250
 *
2251
 * @author Brad Anderson <[email protected]>
2252
 * @version $Id$
2253
 * @package System
2254
 * @since 3.2.3
2255
 */
2256
interface IBaseBehavior {
2257
	/**
2258
	 * Attaches the behavior object to the component.
2259
	 * @param CComponent the component that this behavior is to be attached to.
2260
	 */
2261
	public function attach($component);
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...
2262
	/**
2263
	 * Detaches the behavior object from the component.
2264
	 * @param CComponent the component that this behavior is to be detached from.
2265
	 */
2266
	public function detach($component);
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...
2267
}
2268
2269
/**
2270
 * IBehavior interfaces is implemented by instance behavior classes.
2271
 *
2272
 * A behavior is a way to enhance a component with additional methods and
2273
 * events that are defined in the behavior class and not available in the
2274
 * class.  Objects may signal behaviors through dynamic events.
2275
 *
2276
 * @author Brad Anderson <[email protected]>
2277
 * @version $Id$
2278
 * @package System
2279
 * @since 3.2.3
2280
 */
2281
interface IBehavior extends IBaseBehavior
2282
{
2283
	/**
2284
	 * @return boolean whether this behavior is enabled
2285
	 */
2286
	public function getEnabled();
2287
	/**
2288
	 * @param boolean whether this behavior is enabled
2289
	 */
2290
	public function setEnabled($value);
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...
2291
}
2292
2293
2294
/**
2295
 * IClassBehavior interface is implements behaviors across all instances of
2296
 * a particular class
2297
 *
2298
 * Any calls to functions not present in the original object but to behaviors
2299
 * derived from this class, will have inserted as the first argument parameter
2300
 * the object containing the behavior.
2301
 *
2302
 * For example:
2303
 * <code>
2304
 * $objWithClassBehavior->MethodOfClassBehavior(1, 20);
2305
 * </code>
2306
 * will be acted within the class behavior like this:
2307
 * <code>
2308
 * public function MethodOfClassBehavior($object, $firstParam, $secondParam){
2309
 *      // $object === $objWithClassBehavior, $firstParam === 1, $secondParam === 20
2310
 * }
2311
 * </code>
2312
 *
2313
 * This also holds for 'dy' events as well.  For dynamic events, method arguments would be:
2314
 * <code>
2315
 * public function dyMethodOfClassBehavior($object, $firstParam, $secondParam, $callchain){
2316
 *      // $object === $objWithClassBehavior, $firstParam === 1, $secondParam === 20, $callchain instanceof {@link TCallChain}
2317
 * }
2318
 * </code>
2319
 *
2320
 * @author Brad Anderson <[email protected]>
2321
 * @version $Id$
2322
 * @package System
2323
 * @since 3.2.3
2324
 */
2325
interface IClassBehavior extends IBaseBehavior {
2326
}
2327
2328
2329
/**
2330
 * IInstanceCheck This interface allows objects to determine their own
2331
 * 'instanceof' results when {@link TComponent::isa} is called.  This is
2332
 * important with behaviors because behaviors may want to look like
2333
 * particular objects other than themselves.
2334
 *
2335
 * @author Brad Anderson <[email protected]>
2336
 * @version $Id$
2337
 * @package System
2338
 * @since 3.2.3
2339
 */
2340
interface IInstanceCheck {
2341
	/**
2342
	 * The method checks $this or, if needed, the parameter $instance is of type
2343
	 * class.  In the case of a Class Behavior, the instance to which the behavior
2344
	 * is attached may be important to determine if $this is an instance
2345
	 * of a particular class.
2346
	 * @param class|string the component that this behavior is checking if it is an instanceof.
2347
	 * @param object the object which the behavior is attached to.  default: null
2348
	 * @return boolean|null if the this or the instance is of type class.  When null, no information could be derived and
2349
	 * the default mechanisms take over.
2350
	 */
2351
	public function isinstanceof($class,$instance=null);
2352
}
2353
2354
/**
2355
 * TJavaScriptLiteral class that encloses string literals that are not
2356
 * supposed to be escaped by {@link TJavaScript::encode() }
2357
 *
2358
 * Since Prado 3.2 all the data that gets sent clientside inside a javascript statement
2359
 * is encoded by default to avoid any kind of injection.
2360
 * Sometimes there's the need to bypass this encoding and send raw javascript code.
2361
 * To ensure that a string doesn't get encoded by {@link TJavaScript::encode() },
2362
 * construct a new TJavaScriptLiteral:
2363
 * <code>
2364
 * // a javascript test string
2365
 * $js="alert('hello')";
2366
 * // the string in $raw will not be encoded when sent clientside inside a javascript block
2367
 * $raw=new TJavaScriptLiteral($js);
2368
 * // shortened form
2369
 * $raw=_js($js);
2370
 * </code>
2371
 *
2372
 * @package System
2373
 * @since 3.2.0
2374
 */
2375
class TJavaScriptLiteral
2376
{
2377
	protected $_s;
2378
2379
	public function __construct($s)
2380
	{
2381
		$this->_s = $s;
2382
	}
2383
2384
	public function __toString()
2385
	{
2386
		return (string)$this->_s;
2387
	}
2388
2389
	public function toJavaScriptLiteral()
2390
	{
2391
		return $this->__toString();
2392
	}
2393
}
2394
2395
/**
2396
 * TJavaScriptString class is an internal class that marks strings that will be
2397
 * forcibly encoded when rendered inside a javascript block
2398
 *
2399
 * @package System
2400
 * @since 3.2.0
2401
 */
2402
class TJavaScriptString extends TJavaScriptLiteral
2403
{
2404
	public function toJavaScriptLiteral()
2405
	{
2406
		return TJavaScript::jsonEncode((string)$this->_s,JSON_HEX_QUOT | JSON_HEX_APOS | JSON_HEX_TAG);
2407
	}
2408
}
2409
2410