1 : <?php
2 : /**
3 : * TComponent, TPropertyValue classes
4 : *
5 : * @author Qiang Xue <qiang.xue@gmail.com>
6 : * @link http://www.pradosoft.com/
7 : * @copyright Copyright © 2005 PradoSoft
8 : * @license http://www.pradosoft.com/license/
9 : * @version $Id: TComponent.php 2108 2007-08-05 18:08:33Z xue $
10 : * @package System
11 : */
12 :
13 : /**
14 : * TComponent class
15 : *
16 : * TComponent is the base class for all PRADO components.
17 : * TComponent implements the protocol of defining, using properties and events.
18 : *
19 : * A property is defined by a getter method, and/or a setter method.
20 : * Properties can be accessed in the way like accessing normal object members.
21 : * Reading or writing a property will cause the invocation of the corresponding
22 : * getter or setter method, e.g.,
23 : * <code>
24 : * $a=$this->Text; // equivalent to $a=$this->getText();
25 : * $this->Text='abc'; // equivalent to $this->setText('abc');
26 : * </code>
27 : * The signatures of getter and setter methods are as follows,
28 : * <code>
29 : * // getter, defines a readable property 'Text'
30 : * function getText() { ... }
31 : * // setter, defines a writable property 'Text', with $value being the value to be set to the property
32 : * function setText($value) { ... }
33 : * </code>
34 : * Property names are case-insensitive. It is recommended that they are written
35 : * in the format of concatenated words, with the first letter of each word
36 : * capitalized (e.g. DisplayMode, ItemStyle).
37 : *
38 : * An event is defined by the presence of a method whose name starts with 'on'.
39 : * The event name is the method name and is thus case-insensitive.
40 : * An event can be attached with one or several methods (called event handlers).
41 : * An event can be raised by calling {@link raiseEvent} method, upon which
42 : * the attached event handlers will be invoked automatically in the order they
43 : * are attached to the event. Event handlers must have the following signature,
44 : * <code>
45 : * function eventHandlerFuncName($sender,$param) { ... }
46 : * </code>
47 : * where $sender refers to the object who is responsible for the raising of the event,
48 : * and $param refers to a structure that may contain event-specific information.
49 : * To raise an event (assuming named as 'Click') of a component, use
50 : * <code>
51 : * $component->raiseEvent('OnClick');
52 : * </code>
53 : * To attach an event handler to an event, use one of the following ways,
54 : * <code>
55 : * $component->OnClick=$callback; // or $component->OnClick->add($callback);
56 : * $$component->attachEventHandler('OnClick',$callback);
57 : * </code>
58 : * The first two ways make use of the fact that $component->OnClick refers to
59 : * the event handler list {@link TList} for the 'OnClick' event.
60 : * The variable $callback contains the definition of the event handler that can
61 : * be either a string referring to a global function name, or an array whose
62 : * first element refers to an object and second element a method name/path that
63 : * is reachable by the object, e.g.
64 : * - 'buttonClicked' : buttonClicked($sender,$param);
65 : * - array($object,'buttonClicked') : $object->buttonClicked($sender,$param);
66 : * - array($object,'MainContent.SubmitButton.buttonClicked') :
67 : * $object->MainContent->SubmitButton->buttonClicked($sender,$param);
68 : *
69 : * @author Qiang Xue <qiang.xue@gmail.com>
70 : * @version $Id: TComponent.php 2108 2007-08-05 18:08:33Z xue $
71 : * @package System
72 : * @since 3.0
73 : */
74 : class TComponent
75 : {
76 : /**
77 : * @var array event handler lists
78 : */
79 : private $_e=array();
80 :
81 : /**
82 : * Returns a property value or an event handler list by property or event name.
83 : * Do not call this method. This is a PHP magic method that we override
84 : * to allow using the following syntax to read a property:
85 : * <code>
86 : * $value=$component->PropertyName;
87 : * </code>
88 : * and to obtain the event handler list for an event,
89 : * <code>
90 : * $eventHandlerList=$component->EventName;
91 : * </code>
92 : * @param string the property name or the event name
93 : * @return mixed the property value or the event handler list
94 : * @throws TInvalidOperationException if the property/event is not defined.
95 : */
96 : public function __get($name)
97 : {
98 32 : $getter='get'.$name;
99 32 : if(method_exists($this,$getter))
100 32 : {
101 : // getting a property
102 29 : return $this->$getter();
103 : }
104 4 : else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
105 4 : {
106 : // getting an event (handler list)
107 0 : $name=strtolower($name);
108 0 : if(!isset($this->_e[$name]))
109 0 : $this->_e[$name]=new TList;
110 0 : return $this->_e[$name];
111 : }
112 : else
113 : {
114 4 : throw new TInvalidOperationException('component_property_undefined',get_class($this),$name);
115 : }
116 : }
117 :
118 : /**
119 : * Sets value of a component property.
120 : * Do not call this method. This is a PHP magic method that we override
121 : * to allow using the following syntax to set a property or attach an event handler.
122 : * <code>
123 : * $this->PropertyName=$value;
124 : * $this->EventName=$handler;
125 : * </code>
126 : * @param string the property name or event name
127 : * @param mixed the property value or event handler
128 : * @throws TInvalidOperationException If the property is not defined or read-only.
129 : */
130 : public function __set($name,$value)
131 : {
132 28 : $setter='set'.$name;
133 28 : if(method_exists($this,$setter))
134 28 : {
135 28 : $this->$setter($value);
136 27 : }
137 1 : else if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
138 1 : {
139 0 : $this->attachEventHandler($name,$value);
140 0 : }
141 1 : else if(method_exists($this,'get'.$name))
142 1 : {
143 0 : throw new TInvalidOperationException('component_property_readonly',get_class($this),$name);
144 : }
145 : else
146 : {
147 1 : throw new TInvalidOperationException('component_property_undefined',get_class($this),$name);
148 : }
149 27 : }
150 :
151 : /**
152 : * Determines whether a property is defined.
153 : * A property is defined if there is a getter or setter method
154 : * defined in the class. Note, property names are case-insensitive.
155 : * @param string the property name
156 : * @return boolean whether the property is defined
157 : */
158 : public function hasProperty($name)
159 : {
160 2 : return method_exists($this,'get'.$name) || method_exists($this,'set'.$name);
161 : }
162 :
163 : /**
164 : * Determines whether a property can be read.
165 : * A property can be read if the class has a getter method
166 : * for the property name. Note, property name is case-insensitive.
167 : * @param string the property name
168 : * @return boolean whether the property can be read
169 : */
170 : public function canGetProperty($name)
171 : {
172 2 : return method_exists($this,'get'.$name);
173 : }
174 :
175 : /**
176 : * Determines whether a property can be set.
177 : * A property can be written if the class has a setter method
178 : * for the property name. Note, property name is case-insensitive.
179 : * @param string the property name
180 : * @return boolean whether the property can be written
181 : */
182 : public function canSetProperty($name)
183 : {
184 1 : return method_exists($this,'set'.$name);
185 : }
186 :
187 : /**
188 : * Evaluates a property path.
189 : * A property path is a sequence of property names concatenated by '.' character.
190 : * For example, 'Parent.Page' refers to the 'Page' property of the component's
191 : * 'Parent' property value (which should be a component also).
192 : * @param string property path
193 : * @return mixed the property path value
194 : */
195 : public function getSubProperty($path)
196 : {
197 4 : $object=$this;
198 4 : foreach(explode('.',$path) as $property)
199 4 : $object=$object->$property;
200 4 : return $object;
201 : }
202 :
203 : /**
204 : * Sets a value to a property path.
205 : * A property path is a sequence of property names concatenated by '.' character.
206 : * For example, 'Parent.Page' refers to the 'Page' property of the component's
207 : * 'Parent' property value (which should be a component also).
208 : * @param string property path
209 : * @param mixed the property path value
210 : */
211 : public function setSubProperty($path,$value)
212 : {
213 2 : $object=$this;
214 2 : if(($pos=strrpos($path,'.'))===false)
215 2 : $property=$path;
216 : else
217 : {
218 2 : $object=$this->getSubProperty(substr($path,0,$pos));
219 2 : $property=substr($path,$pos+1);
220 : }
221 2 : $object->$property=$value;
222 2 : }
223 :
224 : /**
225 : * Determines whether an event is defined.
226 : * An event is defined if the class has a method whose name is the event name prefixed with 'on'.
227 : * Note, event name is case-insensitive.
228 : * @param string the event name
229 : * @return boolean
230 : */
231 : public function hasEvent($name)
232 : {
233 11 : return strncasecmp($name,'on',2)===0 && method_exists($this,$name);
234 : }
235 :
236 : /**
237 : * @return boolean whether an event has been attached one or several handlers
238 : */
239 : public function hasEventHandler($name)
240 : {
241 1 : $name=strtolower($name);
242 1 : return isset($this->_e[$name]) && $this->_e[$name]->getCount()>0;
243 : }
244 :
245 : /**
246 : * Returns the list of attached event handlers for an event.
247 : * @return TList list of attached event handlers for an event
248 : * @throws TInvalidOperationException if the event is not defined
249 : */
250 : public function getEventHandlers($name)
251 : {
252 9 : if(strncasecmp($name,'on',2)===0 && method_exists($this,$name))
253 9 : {
254 9 : $name=strtolower($name);
255 9 : if(!isset($this->_e[$name]))
256 9 : $this->_e[$name]=new TList;
257 9 : return $this->_e[$name];
258 : }
259 : else
260 2 : throw new TInvalidOperationException('component_event_undefined',get_class($this),$name);
261 : }
262 :
263 : /**
264 : * Attaches an event handler to an event.
265 : *
266 : * The handler must be a valid PHP callback, i.e., a string referring to
267 : * a global function name, or an array containing two elements with
268 : * the first element being an object and the second element a method name
269 : * of the object. In Prado, you can also use method path to refer to
270 : * an event handler. For example, array($object,'Parent.buttonClicked')
271 : * uses a method path that refers to the method $object->Parent->buttonClicked(...).
272 : *
273 : * The event handler must be of the following signature,
274 : * <code>
275 : * function handlerName($sender,$param) {}
276 : * </code>
277 : * where $sender represents the object that raises the event,
278 : * and $param is the event parameter.
279 : *
280 : * This is a convenient method to add an event handler.
281 : * It is equivalent to {@link getEventHandlers}($name)->add($handler).
282 : * For complete management of event handlers, use {@link getEventHandlers}
283 : * to get the event handler list first, and then do various
284 : * {@link TList} operations to append, insert or remove
285 : * event handlers. You may also do these operations like
286 : * getting and setting properties, e.g.,
287 : * <code>
288 : * $component->OnClick[]=array($object,'buttonClicked');
289 : * $component->OnClick->insertAt(0,array($object,'buttonClicked'));
290 : * </code>
291 : * which are equivalent to the following
292 : * <code>
293 : * $component->getEventHandlers('OnClick')->add(array($object,'buttonClicked'));
294 : * $component->getEventHandlers('OnClick')->insertAt(0,array($object,'buttonClicked'));
295 : * </code>
296 : *
297 : * @param string the event name
298 : * @param callback the event handler
299 : * @throws TInvalidOperationException if the event does not exist
300 : */
301 : public function attachEventHandler($name,$handler)
302 : {
303 9 : $this->getEventHandlers($name)->add($handler);
304 9 : }
305 :
306 : /**
307 : * Detaches an existing event handler.
308 : * This method is the opposite of {@link attachEventHandler}.
309 : * @param string event name
310 : * @param callback the event handler to be removed
311 : * @return boolean if the removal is successful
312 : */
313 : public function detachEventHandler($name,$handler)
314 : {
315 0 : if($this->hasEventHandler($name))
316 0 : {
317 : try
318 : {
319 0 : $this->getEventHandlers($name)->remove($handler);
320 0 : return true;
321 : }
322 : catch(Exception $e)
323 : {
324 : }
325 : }
326 0 : return false;
327 : }
328 :
329 : /**
|