* $val = $obj->Label; // equivalent to $val = $obj->getLabel(); * $obj->Label = 'Nette'; // equivalent to $obj->setLabel('Nette'); * * Property names are case-sensitive, and they are written in the PascalCaps, * in contrast to camelCaps used by normal members. * * Adding method to class (i.e. to all instances) works similar to JavaScript * prototype property. The syntax for adding a new method is: * * function MyClass_prototype_newMethod(MyClass $obj, $arg, ...) { ... } * $obj = new MyClass; * $obj->newMethod($x); // equivalent to MyClass_prototype_newMethod($obj, $x); * */ abstract class NObject { /** * Returns the name of the class of this object * * @return string */ final public function getClass() { return get_class($this); } /** * Access to reflection * * @return ReflectionClass */ final public function getReflection() { return new ReflectionClass(get_class($this)); } /** * Return hash id for given object * * @return string 32 hexa chars */ final public function getHashId() { return spl_object_hash($this); } /** * Call to undefined method * * @param string method name * @param array arguments * @return mixed * @throws BadMethodCallException */ protected function __call($name, $args) { // object prototypes support Class__method() // (or use class Class__method { static function ... } with autoloading?) $cl = $class = get_class($this); do { if (function_exists($nm = $cl . '_prototype_' . $name)) { array_unshift($args, $this); return call_user_func_array($nm, $args); } } while ($cl = get_parent_class($cl)); throw new BadMethodCallException("Call to undefined method $class::$name()"); } /** * Returns property value. Do not call directly. * * @param string property name * @return mixed property value or the event handler list * @throws NPropertyException if the property is not defined. */ protected function &__get($name) { if ($name === '') { throw new NPropertyException('Property name must be a non-empty string'); } // property getter support $class = get_class($this); $m = 'get' . $name; if (self::isCallable($class, $m)) { // ampersands: // - using &__get() because declaration should be forward compatible (e.g. with NHtml) // - not using &$this->$m because user could bypass property setter by: $x = & $obj->property; $x = 'new value'; $val = $this->$m(); return $val; } else { throw new NPropertyException("Cannot read an undeclared property $class::\$$name"); } } /** * Sets value of a property. Do not call directly. * * @param string property name * @param mixed property value * @return void * @throws NPropertyException if the property is not defined or is read-only */ protected function __set($name, $value) { if ($name === '') { throw new NPropertyException('Property name must be a non-empty string'); } // property setter support $class = get_class($this); if (self::isCallable($class, 'get' . $name)) { $m = 'set' . $name; if (self::isCallable($class, $m)) { $this->$m($value); } else { throw new NPropertyException("Cannot assign to a read-only property $class::\$$name"); } } else { throw new NPropertyException("Cannot assign to an undeclared property $class::\$$name"); } } /** * Is property defined? * * @param string property name * @return bool */ protected function __isset($name) { return $name !== '' && self::isCallable(get_class($this), 'get' . $name); } /** * Access to undeclared property * * @param string property name * @return void * @throws NPropertyException */ protected function __unset($name) { $class = get_class($this); throw new NPropertyException("Cannot unset an declared or undeclared property $class::\$$name"); } /** * Is method defined? Case sensitive, filters protected & private, doesn't recognize static methods (works good since 5.0.4) * * @param string class name * @param string method name * @return bool */ private static function isCallable($c, $m) { static $cache; if (!isset($cache[$c])) { $cache[$c] = array_flip(get_class_methods($c)); } return isset($cache[$c][$m]); } } /** * Occurs on unsuccessful attempts to get or set the value of a property */ class NPropertyException extends Exception { } }