diff --git a/dibi/dibi.php b/dibi/dibi.php index 2477374b..7907b58b 100644 --- a/dibi/dibi.php +++ b/dibi/dibi.php @@ -32,6 +32,7 @@ if (version_compare(PHP_VERSION , '5.1.0', '<')) { // libraries +require_once __FILE__ . '/../libs/NObject.php'; require_once __FILE__ . '/../libs/driver.php'; require_once __FILE__ . '/../libs/resultset.php'; require_once __FILE__ . '/../libs/translator.php'; diff --git a/dibi/libs/NObject.php b/dibi/libs/NObject.php new file mode 100644 index 00000000..a4e14d6b --- /dev/null +++ b/dibi/libs/NObject.php @@ -0,0 +1,229 @@ + + * $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 +{ +} diff --git a/dibi/libs/driver.php b/dibi/libs/driver.php index f20bbf2f..2cfd8d20 100644 --- a/dibi/libs/driver.php +++ b/dibi/libs/driver.php @@ -26,7 +26,7 @@ * * @version $Revision$ $Date$ */ -abstract class DibiDriver +abstract class DibiDriver extends NObject { /** * Current connection configuration @@ -300,15 +300,4 @@ abstract class DibiDriver abstract public function applyLimit(&$sql, $limit, $offset = 0); - - /**#@+ - * Access to undeclared property - * @throws Exception - */ - private function &__get($name) { throw new Exception("Access to undeclared property: " . get_class($this) . "::$$name"); } - private function __set($name, $value) { throw new Exception("Access to undeclared property: " . get_class($this) . "::$$name"); } - private function __unset($name) { throw new Exception("Access to undeclared property: " . get_class($this) . "::$$name"); } - /**#@-*/ - - } // class DibiDriver diff --git a/dibi/libs/logger.php b/dibi/libs/logger.php index 8954594e..26abfb8b 100644 --- a/dibi/libs/logger.php +++ b/dibi/libs/logger.php @@ -26,7 +26,7 @@ * * @version $Revision$ $Date$ */ -final class DibiLogger +final class DibiLogger extends NObject { /** @var string Name of the file where SQL errors should be logged */ private $file; @@ -102,15 +102,4 @@ final class DibiLogger fclose($handle); } - - - /**#@+ - * Access to undeclared property - * @throws Exception - */ - private function &__get($name) { throw new Exception("Access to undeclared property: " . get_class($this) . "::$$name"); } - private function __set($name, $value) { throw new Exception("Access to undeclared property: " . get_class($this) . "::$$name"); } - private function __unset($name) { throw new Exception("Access to undeclared property: " . get_class($this) . "::$$name"); } - /**#@-*/ - } diff --git a/dibi/libs/resultset.php b/dibi/libs/resultset.php index 67421728..1654d701 100644 --- a/dibi/libs/resultset.php +++ b/dibi/libs/resultset.php @@ -35,7 +35,7 @@ * * @version $Revision$ $Date$ */ -abstract class DibiResult implements IteratorAggregate, Countable +abstract class DibiResult extends NObject implements IteratorAggregate, Countable { /** * Describes columns types @@ -492,16 +492,6 @@ abstract class DibiResult implements IteratorAggregate, Countable /** end required Countable functions */ - - /**#@+ - * Access to undeclared property - * @throws Exception - */ - private function &__get($name) { throw new Exception("Access to undeclared property: " . get_class($this) . "::$$name"); } - private function __set($name, $value) { throw new Exception("Access to undeclared property: " . get_class($this) . "::$$name"); } - private function __unset($name) { throw new Exception("Access to undeclared property: " . get_class($this) . "::$$name"); } - /**#@-*/ - } // class DibiResult diff --git a/dibi/libs/translator.php b/dibi/libs/translator.php index 8ac48343..158ecf63 100644 --- a/dibi/libs/translator.php +++ b/dibi/libs/translator.php @@ -26,7 +26,7 @@ * * @version $Revision$ $Date$ */ -final class DibiTranslator +final class DibiTranslator extends NObject { /** @var string */ public $sql; @@ -412,14 +412,4 @@ final class DibiTranslator } - - /**#@+ - * Access to undeclared property - * @throws Exception - */ - private function &__get($name) { throw new Exception("Access to undeclared property: " . get_class($this) . "::$$name"); } - private function __set($name, $value) { throw new Exception("Access to undeclared property: " . get_class($this) . "::$$name"); } - private function __unset($name) { throw new Exception("Access to undeclared property: " . get_class($this) . "::$$name"); } - /**#@-*/ - } // class DibiParser