From 55c4ac1bfe565f1ca7f537fdd8b7a201be28e581 Mon Sep 17 00:00:00 2001 From: xue <> Date: Thu, 10 Nov 2005 12:47:19 +0000 Subject: Initial import of prado framework --- framework/TComponent.php | 535 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 535 insertions(+) create mode 100644 framework/TComponent.php (limited to 'framework/TComponent.php') diff --git a/framework/TComponent.php b/framework/TComponent.php new file mode 100644 index 00000000..36a06632 --- /dev/null +++ b/framework/TComponent.php @@ -0,0 +1,535 @@ + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ $Date: $ + * @package System + */ + +/** + * TComponent class + * + * TComponent is the base class for all PRADO components. + * TComponent implements the protocol of defining, using properties and events. + * + * A property is defined by a getter method, and/or a setter method. + * Properties can be accessed in the way like accessing normal object members. + * Reading or writing a property will cause the invocation of the corresponding + * getter or setter method, e.g., + * + * $a=$this->Text; // equivalent to $a=$this->getText(); + * $this->Text='abc'; // equivalent to $this->setText('abc'); + * + * The signatures of getter and setter methods are as follows, + * + * // getter, defines a readable property 'Text' + * function getText() { ... } + * // setter, defines a writable property 'Text', with $value being the value to be set to the property + * function setText($value) { ... } + * + * Property names are case-insensitive. It is recommended that they are written + * in the format of concatenated words, with the first letter of each word + * capitalized (e.g. DisplayMode, ItemStyle). + * + * An event is defined by the presence of a method whose name is the event name prefixed with 'on'. + * The event name is case-insensitive. + * An event can be attached with one or several methods (called event handlers). + * An event can be raised by calling {@link raiseEvent} method, upon which + * the attached event handlers will be invoked automatically in the order they + * are attached to the event. Event handlers must have the following signature, + * + * function eventHandlerFuncName($sender,$param) { ... } + * + * where $sender refers to the object who is responsible for the raising of the event, + * and $param refers to a structure that may contain event-specific information. + * To raise an event (assuming named as 'Click') of a component, use + * + * $component->raiseEvent('Click'); + * + * To attach an event handler to an event, use one of the following ways, + * + * $component->Click=$callback; // or $component->Click->add($callback); + * $$component->attachEventHandler('Click',$callback); + * + * The first two ways make use of the fact that $component->Click refers to + * the event handler list {@link TList} for the 'Click' event. + * The variable $callback contains the definition of the event handler that can + * be either a string referring to a global function name, or an array whose + * first element refers to an object and second element a method name/path that + * is reachable by the object, e.g. + * - 'buttonClicked' : buttonClicked($sender,$param); + * - array($object,'buttonClicked') : $object->buttonClicked($sender,$param); + * - array($object,'MainContent.SubmitButton.buttonClicked') : + * $object->MainContent->SubmitButton->buttonClicked($sender,$param); + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System + * @since 3.0 + */ +class TComponent +{ + /** + * @var array event handler lists + */ + private $_e=array(); + + /** + * Constructor. + */ + public function __construct() + { + } + + /** + * Destructor. + */ + public function __destruct() + { + } + + /** + * Returns a property value or an event handler list by property or event name. + * Do not call this method. This is a PHP magic method that we override + * to allow using the following syntax to read a property: + * + * $value=$component->PropertyName; + * + * and to obtain the event handler list for an event, + * + * $eventHandlerList=$component->EventName; + * + * @param string the property name or the event name + * @return mixed the property value or the event handler list + * @throws TInvalidOperationException if the property/event is not defined. + */ + public function __get($name) + { + $getter='get'.$name; + if(method_exists($this,$getter)) + { + // getting a property + return $this->$getter(); + } + else if(method_exists($this,'on'.$name)) + { + // getting an event (handler list) + $name=strtolower($name); + if(!isset($this->_e[$name])) + $this->_e[$name]=new TList; + return $this->_e[$name]; + } + else + { + throw new TInvalidOperationException('component_property_undefined',get_class($this),$name); + } + } + + /** + * Sets value of a component property. + * Do not call this method. This is a PHP magic method that we override + * to allow using the following syntax to set a property or attach an event handler. + * + * $this->PropertyName=$value; + * $this->EventName=$handler; + * + * @param string the property name or event name + * @param mixed the property value or event handler + * @throws TInvalidOperationException If the property is not defined or read-only. + */ + public function __set($name,$value) + { + $setter='set'.$name; + if(method_exists($this,$setter)) + { + $this->$setter($value); + } + else if(method_exists($this,'on'.$name)) + { + $this->attachEventHandler($name,$value); + } + else if(method_exists($this,'get'.$name)) + { + throw new TInvalidOperationException('component_property_readonly',get_class($this),$name); + } + else + { + throw new TInvalidOperationException('component_property_undefined',get_class($this),$name); + } + } + + /** + * Determines whether a property is defined. + * A property is defined if there is a getter or setter method + * defined in the class. Note, property names are case-insensitive. + * @param string the property name + * @return boolean whether the property is defined + */ + final public function hasProperty($name) + { + return method_exists($this,'get'.$name) || method_exists($this,'set'.$name); + } + + /** + * Determines whether a property can be read. + * A property can be read if the class has a getter method + * for the property name. Note, property name is case-insensitive. + * @param string the property name + * @return boolean whether the property can be read + */ + final public function canGetProperty($name) + { + return method_exists($this,'get'.$name); + } + + /** + * Determines whether a property can be set. + * A property can be written if the class has a setter method + * for the property name. Note, property name is case-insensitive. + * @param string the property name + * @return boolean whether the property can be written + */ + final public function canSetProperty($name) + { + return method_exists($this,'set'.$name); + } + + /** + * Evaluates a property path. + * A property path is a sequence of property names concatenated by '.' character. + * For example, 'Parent.Page' refers to the 'Page' property of the component's + * 'Parent' property value (which should be a component also). + * @param string property path + * @return mixed the property path value + */ + public function getPropertyByPath($path) + { + $object=$this; + foreach(explode('.',$path) as $property) + $object=$object->$property; + return $object; + } + + /** + * Sets a value to a property path. + * A property path is a sequence of property names concatenated by '.' character. + * For example, 'Parent.Page' refers to the 'Page' property of the component's + * 'Parent' property value (which should be a component also). + * @param string property path + * @param mixed the property path value + */ + public function setPropertyByPath($path,$value) + { + $object=$this; + if(($pos=strrpos($path,'.'))===false) + $property=$path; + else + { + $object=$this->getPropertyByPath(substr($path,0,$pos)); + $property=substr($path,$pos+1); + } + $object->$property=$value; + } + + /** + * Determines whether an event is defined. + * An event is defined if the class has a method whose name is the event name prefixed with 'on'. + * Note, event name is case-insensitive. + * @param string the event name + * @return boolean + */ + public function hasEvent($name) + { + return method_exists($this,'on'.$name); + } + + /** + * @return boolean whether an event has been attached one or several handlers + */ + public function hasEventHandler($name) + { + $name=strtolower($name); + return isset($this->_e[$name]) && $this->_e[$name]->getCount()>0; + } + + /** + * Returns the list of attached event handlers for an event. + * @return TList list of attached event handlers for an event + * @throws TInvalidOperationException if the event is not defined + */ + public function getEventHandlers($name) + { + if(method_exists($this,'on'.$name)) + { + $name=strtolower($name); + if(!isset($this->_e[$name])) + $this->_e[$name]=new TList; + return $this->_e[$name]; + } + else + throw new TInvalidOperationException('component_event_undefined',get_class($this),$name); + } + + /** + * Attaches an event handler to an event. + * + * The handler must be a valid PHP callback, i.e., a string referring to + * a global function name, or an array containing two elements with + * the first element being an object and the second element a method name + * of the object. In Prado, you can also use method path to refer to + * an event handler. For example, array($object,'Parent.buttonClicked') + * uses a method path that refers to the method $object->Parent->buttonClicked(...). + * + * The event handler must be of the following signature, + * + * function handlerName($sender,$param) {} + * + * where $sender represents the object that raises the event, + * and $param is the event parameter. + * + * This is a convenient method to add an event handler. + * It is equivalent to {@link getEventHandlers}($name)->add($handler). + * For complete management of event handlers, use {@link getEventHandlers} + * to get the event handler list first, and then do various + * {@link TList} operations to append, insert or remove + * event handlers. You may also do these operations like + * getting and setting properties, e.g., + * + * $component->Click[]=array($object,'buttonClicked'); + * $component->Click->addAt(0,array($object,'buttonClicked')); + * + * which are equivalent to the following + * + * $component->getEventHandlers('Click')->add(array($object,'buttonClicked')); + * $component->getEventHandlers('Click')->addAt(0,array($object,'buttonClicked')); + * + * + * @param string the event name + * @param callback the event handler + * @throws TInvalidOperationException if the event does not exist + */ + public function attachEventHandler($name,$handler) + { + $this->getEventHandlers($name)->add($handler); + } + + /** + * Raises an event. + * This method represents the happening of an event and will + * invoke all attached event handlers for the event. + * @param string the event name + * @param mixed the event sender object + * @param TEventParameter the event parameter + * @throws TInvalidOperationException if the event is undefined + * @throws TInvalidDataValueException If an event handler is invalid + */ + public function raiseEvent($name,$sender,$param) + { + $name=strtolower($name); + if(isset($this->_e[$name])) + { + foreach($this->_e[$name] as $handler) + { + if(is_string($handler)) + { + call_user_func($handler,$sender,$param); + } + else if(is_callable($handler,true)) + { + // an array: 0 - object, 1 - method name/path + list($object,$method)=$handler; + if(($pos=strrpos($method,'.'))!==false) + { + $object=$this->getPropertyByPath(substr($method,0,$pos)); + $method=substr($method,$pos+1); + } + $object->$method($sender,$param); + } + else + throw new TInvalidDataValueException('component_event_handler_invalid',$handler); + } + } + else if(!$this->hasEvent($name)) + throw new TInvalidOperationException('component_event_undefined',get_class($this),$name); + } + + /** + * Evaluates a PHP expression in the context of this control. + * @return mixed the expression result + * @throws TInvalidOperationException if the expression is invalid + */ + public function evaluateExpression($expression) + { + try + { + if(eval("\$result=$expression;")===false) + throw new Exception(''); + return $result; + } + catch(Exception $e) + { + throw new TInvalidOperationException('component_expression_invalid',$expression,$e->getMessage()); + } + } + + /** + * Evaluates a list of PHP statements. + * @param string PHP statements + * @return string content echoed or printed by the PHP statements + * @throw TInvalidOperationException if the statements are invalid + */ + public function evaluateStatements($statements) + { + try + { + ob_start(); + if(eval($statements)===false) + throw new Exception(''); + $content=ob_get_contents(); + ob_end_clean(); + return $content; + } + catch(Exception $e) + { + throw new TInvalidOperationException('component_statements_invalid',$statements,$e->getMessage()); + } + } +} + +/** + * TPropertyValue class + * + * TPropertyValue is a utility class that provides static methods + * to convert component property values to specific types. + * + * TPropertyValue is commonly used in component setter methods to ensure + * the new property value is of specific type. + * For example, a boolean-typed property setter method would be as follows, + * + * function setPropertyName($value) { + * $value=TPropertyValue::ensureBoolean($value); + * // $value is now of boolean type + * } + * + * + * Properties can be of the following types with specific type conversion rules: + * - string: a boolean value will be converted to 'true' or 'false'. + * - boolean: string 'true' (case-insensitive) will be converted to true, + * string 'false' (case-insensitive) will be converted to false. + * - integer + * - float + * - array + * - object + * - enum: enumerable type, represented by an array of strings. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System + * @since 3.0 + */ +class TPropertyValue +{ + /** + * Converts a value to boolean type. + * Note, string 'true' (case-insensitive) will be converted to true, + * string 'false' (case-insensitive) will be converted to false. + * @param mixed the value to be converted. + * @return boolean + */ + public static function ensureBoolean($value) + { + if (is_string($value)) + return strcasecmp($value,'true')==0; + else + return (boolean)$value; + } + + /** + * Converts a value to string type. + * Note, a boolean value will be converted to 'true' if it is true + * and 'false' if it is false. + * @param mixed the value to be converted. + * @return string + */ + public static function ensureString($value) + { + if (is_bool($value)) + return $value?'true':'false'; + else + return (string)$value; + } + + /** + * Converts a value to integer type. + * @param mixed the value to be converted. + * @return integer + */ + public static function ensureInteger($value) + { + return (integer)$value; + } + + /** + * Converts a value to float type. + * @param mixed the value to be converted. + * @return float + */ + public static function ensureFloat($value) + { + return (float)$value; + } + + /** + * Converts a value to array type. + * @param mixed the value to be converted. + * @return array + */ + public static function ensureArray($value) + { + return (array)$value; + } + + /** + * Converts a value to object type. + * @param mixed the value to be converted. + * @return object + */ + public static function ensureObject($value) + { + return (object)$value; + } + + /** + * Converts a value to enum type. + * @param mixed the value to be converted. + * @param array array of strings representing the enum type. + * @return string + * @throws TInvalidDataValueException if the original value is not in the string array. + */ + public static function ensureEnum($value,$enum) + { + if(($index=array_search($value,$enum))!==false) + return $enum[$index]; + else + throw new TInvalidDataValueException('propertyvalue_enumvalue_invalid',$value,implode(',',$enum)); + } +} + +/** + * TEventParameter class. + * TEventParameter is the base class for all event parameter classes. + * + * @author Qiang Xue + * @version $Revision: $ $Date: $ + * @package System + * @since 3.0 + */ +class TEventParameter extends TComponent +{ +} + +?> \ No newline at end of file -- cgit v1.2.3