Created
July 2, 2011 04:20
-
-
Save dg/1059734 to your computer and use it in GitHub Desktop.
Nette\ObjectTrait
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* This file is part of the Nette Framework (http://nette.org) | |
* | |
* Copyright (c) 2004, 2011 David Grudl (http://davidgrudl.com) | |
* | |
* For the full copyright and license information, please view | |
* the file license.txt that was distributed with this source code. | |
*/ | |
namespace Nette; | |
use Nette; | |
/** | |
* Nette\Object is the common trait for all instantiable classes. | |
* | |
* It defines some handful methods and enhances object core of PHP: | |
* - access to undeclared members throws exceptions | |
* - support for conventional properties with getters and setters | |
* - support for event raising functionality | |
* - ability to add new methods to class (extension methods) | |
* | |
* Properties is a syntactic sugar which allows access public getter and setter | |
* methods as normal object variables. A property is defined by a getter method | |
* or setter method (no setter method means read-only property). | |
* <code> | |
* $val = $obj->label; // equivalent to $val = $obj->getLabel(); | |
* $obj->label = 'Nette'; // equivalent to $obj->setLabel('Nette'); | |
* </code> | |
* Property names are case-sensitive, and they are written in the camelCaps | |
* or PascalCaps. | |
* | |
* Event functionality is provided by declaration of property named 'on{Something}' | |
* Multiple handlers are allowed. | |
* <code> | |
* public $onClick; // declaration in class | |
* $this->onClick[] = 'callback'; // attaching event handler | |
* if (!empty($this->onClick)) ... // are there any handlers? | |
* $this->onClick($sender, $arg); // raises the event with arguments | |
* </code> | |
* | |
* Adding method to class (i.e. to all instances) works similar to JavaScript | |
* prototype property. The syntax for adding a new method is: | |
* <code> | |
* MyClass::extensionMethod('newMethod', function(MyClass $obj, $arg, ...) { ... }); | |
* $obj = new MyClass; | |
* $obj->newMethod($x); | |
* </code> | |
* | |
* @author David Grudl | |
* | |
* @property-read string $class | |
* @property-read Nette\Reflection\ClassType $reflection | |
*/ | |
trait ObjectTrait | |
{ | |
/** @var array */ | |
private static $methods; | |
/** | |
* Access to reflection. | |
* @return Nette\Reflection\ClassType | |
*/ | |
public /**/static/**/ function getReflection() | |
{ | |
return new Reflection\ClassType(/*5.2*$this*//**/get_called_class()/**/); | |
} | |
/** | |
* Call to undefined method. | |
* @param string method name | |
* @param array arguments | |
* @return mixed | |
* @throws MemberAccessException | |
*/ | |
public function __call($name, $args) | |
{ | |
$class = new Reflection\ClassType($this); | |
if ($name === '') { | |
throw new MemberAccessException("Call to class '$class->name' method without name."); | |
} | |
// event functionality | |
if ($class->hasEventProperty($name)) { | |
if (is_array($list = $this->$name) || $list instanceof \Traversable) { | |
foreach ($list as $handler) { | |
callback($handler)->invokeArgs($args); | |
} | |
} | |
return NULL; | |
} | |
// extension methods | |
if ($cb = $class->getExtensionMethod($name)) { | |
array_unshift($args, $this); | |
return $cb->invokeArgs($args); | |
} | |
throw new MemberAccessException("Call to undefined method $class->name::$name()."); | |
} | |
/** | |
* Call to undefined static method. | |
* @param string method name (in lower case!) | |
* @param array arguments | |
* @return mixed | |
* @throws MemberAccessException | |
*/ | |
public static function __callStatic($name, $args) | |
{ | |
throw new MemberAccessException("Call to undefined static method $class::$name()."); | |
} | |
/** | |
* Adding method to class. | |
* @param string method name | |
* @param mixed callback or closure | |
* @return mixed | |
*/ | |
public static function extensionMethod($name, $callback = NULL) | |
{ | |
if (strpos($name, '::') === FALSE) { | |
$class = get_called_class(); | |
} else { | |
list($class, $name) = explode('::', $name); | |
} | |
$class = new Reflection\ClassType($class); | |
if ($callback === NULL) { | |
return $class->getExtensionMethod($name); | |
} else { | |
$class->setExtensionMethod($name, $callback); | |
} | |
} | |
/** | |
* Returns property value. Do not call directly. | |
* @param string property name | |
* @return mixed property value | |
* @throws MemberAccessException if the property is not defined. | |
*/ | |
public function &__get($name) | |
{ | |
$class = get_class($this); | |
if ($name === '') { | |
throw new MemberAccessException("Cannot read a class '$class' property without name."); | |
} | |
if (!isset(self::$methods[$class])) { | |
// get_class_methods returns ONLY PUBLIC methods of objects | |
// but returns static methods too (nothing doing...) | |
// and is much faster than reflection | |
// (works good since 5.0.4) | |
self::$methods[$class] = array_flip(get_class_methods($class)); | |
} | |
// property getter support | |
$name[0] = $name[0] & "\xDF"; // case-sensitive checking, capitalize first character | |
$m = 'get' . $name; | |
if (isset(self::$methods[$class][$m])) { | |
// ampersands: | |
// - uses &__get() because declaration should be forward compatible (e.g. with Nette\Utils\Html) | |
// - doesn't call &$this->$m because user could bypass property setter by: $x = & $obj->property; $x = 'new value'; | |
$val = $this->$m(); | |
return $val; | |
} | |
$m = 'is' . $name; | |
if (isset(self::$methods[$class][$m])) { | |
$val = $this->$m(); | |
return $val; | |
} | |
$type = isset(self::$methods[$class]['set' . $name]) ? 'a write-only' : 'an undeclared'; | |
$name = func_get_arg(1); | |
throw new MemberAccessException("Cannot read $type property $class::\$$name."); | |
} | |
/** | |
* Sets value of a property. Do not call directly. | |
* @param string property name | |
* @param mixed property value | |
* @return void | |
* @throws MemberAccessException if the property is not defined or is read-only | |
*/ | |
public function __set($name, $value) | |
{ | |
$class = get_class($this); | |
if ($name === '') { | |
throw new MemberAccessException("Cannot write to a class '$class' property without name."); | |
} | |
if (!isset(self::$methods[$class])) { | |
self::$methods[$class] = array_flip(get_class_methods($class)); | |
} | |
// property setter support | |
$name[0] = $name[0] & "\xDF"; // case-sensitive checking, capitalize first character | |
$m = 'set' . $name; | |
if (isset(self::$methods[$class][$m])) { | |
$this->$m($value); | |
return; | |
} | |
$type = isset(self::$methods[$class]['get' . $name]) || isset(self::$methods[$class]['is' . $name]) | |
? 'a read-only' : 'an undeclared'; | |
$name = func_get_arg(1); | |
throw new MemberAccessException("Cannot write to $type property $class::\$$name."); | |
} | |
/** | |
* Is property defined? | |
* @param string property name | |
* @return bool | |
*/ | |
public function __isset($name) | |
{ | |
if ($name === '') { | |
return FALSE; | |
} | |
$class = get_class($this); | |
if (!isset(self::$methods[$class])) { | |
self::$methods[$class] = array_flip(get_class_methods($class)); | |
} | |
$name[0] = $name[0] & "\xDF"; | |
return isset(self::$methods[$class]['get' . $name]) || isset(self::$methods[$class]['is' . $name]); | |
} | |
/** | |
* Access to undeclared property. | |
* @param string property name | |
* @return void | |
* @throws MemberAccessException | |
*/ | |
public function __unset($name) | |
{ | |
$class = get_class($this); | |
throw new MemberAccessException("Cannot unset the property $class::\$$name."); | |
} | |
} |
Pěkné! Kdypak se dočkáme podpory 5.4 přímo v Nette?
Nette v 5.4 bezproblémů funguje.
Špatně jsem se vyjádřil, měl jsem na mysli právě třeba traity přímo v Nette.
Až nadejde správný čas.
Properties is a syntactic sugar which allows access public getter and setter methods as normal object variables.
LOL! Mně skutečně fascinuje, jak se jedna část PHP komunity snaží udělat z PHP Ruby, a druhá bastardní Javu :)
Chtěl jsi říct Delphi (property) a .NET (extension method), ne?
Ne, mně to spíš evokovalo attr_accessor
(http://www.ruby-doc.org/core/classes/Module.html#M000478)
Jasně, existuje to v každém druhém jazyce.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage: