1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10:
11:
12: namespace Nette;
13:
14: use Nette;
15:
16:
17: 18: 19: 20: 21:
22: final class ObjectMixin
23: {
24:
25: private static $methods;
26:
27:
28: private static $props;
29:
30:
31: 32: 33:
34: final public function __construct()
35: {
36: throw new StaticClassException;
37: }
38:
39:
40: 41: 42: 43: 44: 45: 46: 47:
48: public static function call($_this, $name, $args)
49: {
50: $class = get_class($_this);
51: $isProp = self::hasProperty($class, $name);
52:
53: if ($name === '') {
54: throw new MemberAccessException("Call to class '$class' method without name.");
55:
56: } elseif ($isProp === 'event') {
57: if (is_array($_this->$name) || $_this->$name instanceof \Traversable) {
58: foreach ($_this->$name as $handler) {
59: Nette\Callback::create($handler)->invokeArgs($args);
60: }
61: } elseif ($_this->$name !== NULL) {
62: throw new UnexpectedValueException("Property $class::$$name must be array or NULL, " . gettype($_this->$name) ." given.");
63: }
64:
65: } elseif ($cb = Reflection\ClassType::from($_this)->getExtensionMethod($name)) {
66: array_unshift($args, $_this);
67: return $cb->invokeArgs($args);
68:
69: } else {
70: throw new MemberAccessException("Call to undefined method $class::$name().");
71: }
72: }
73:
74:
75: 76: 77: 78: 79: 80: 81: 82:
83: public static function callProperty($_this, $name, $args)
84: {
85: if (strlen($name) > 3) {
86: $op = substr($name, 0, 3);
87: $prop = strtolower($name[3]) . substr($name, 4);
88: if ($op === 'add' && self::hasProperty(get_class($_this), $prop.'s')) {
89: $_this->{$prop.'s'}[] = $args[0];
90: return $_this;
91:
92: } elseif ($op === 'set' && self::hasProperty(get_class($_this), $prop)) {
93: $_this->$prop = $args[0];
94: return $_this;
95:
96: } elseif ($op === 'get' && self::hasProperty(get_class($_this), $prop)) {
97: return $_this->$prop;
98: }
99: }
100: return self::call($_this, $name, $args);
101: }
102:
103:
104: 105: 106: 107: 108: 109: 110: 111:
112: public static function callStatic($class, $method, $args)
113: {
114: throw new MemberAccessException("Call to undefined static method $class::$method().");
115: }
116:
117:
118: 119: 120: 121: 122: 123: 124:
125: public static function & get($_this, $name)
126: {
127: $class = get_class($_this);
128: $uname = ucfirst($name);
129:
130: if (!isset(self::$methods[$class])) {
131: self::$methods[$class] = array_flip(get_class_methods($class));
132: }
133:
134: if ($name === '') {
135: throw new MemberAccessException("Cannot read a class '$class' property without name.");
136:
137: } elseif (isset(self::$methods[$class][$m = 'get' . $uname]) || isset(self::$methods[$class][$m = 'is' . $uname])) {
138: $val = $_this->$m();
139: return $val;
140:
141: } elseif (isset(self::$methods[$class][$name])) {
142: $val = Callback::create($_this, $name);
143: return $val;
144:
145: } else {
146: $type = isset(self::$methods[$class]['set' . $uname]) ? 'a write-only' : 'an undeclared';
147: throw new MemberAccessException("Cannot read $type property $class::\$$name.");
148: }
149: }
150:
151:
152: 153: 154: 155: 156: 157: 158: 159:
160: public static function set($_this, $name, $value)
161: {
162: $class = get_class($_this);
163: $uname = ucfirst($name);
164:
165: if (!isset(self::$methods[$class])) {
166: self::$methods[$class] = array_flip(get_class_methods($class));
167: }
168:
169: if ($name === '') {
170: throw new MemberAccessException("Cannot write to a class '$class' property without name.");
171:
172: } elseif (self::hasProperty($class, $name)) {
173: $_this->$name = $value;
174:
175: } elseif (isset(self::$methods[$class][$m = 'set' . $uname])) {
176: $_this->$m($value);
177:
178: } else {
179: $type = isset(self::$methods[$class]['get' . $uname]) || isset(self::$methods[$class]['is' . $uname])
180: ? 'a read-only' : 'an undeclared';
181: throw new MemberAccessException("Cannot write to $type property $class::\$$name.");
182: }
183: }
184:
185:
186: 187: 188: 189: 190: 191: 192:
193: public static function remove($_this, $name)
194: {
195: $class = get_class($_this);
196: if (!self::hasProperty($class, $name)) {
197: throw new MemberAccessException("Cannot unset the property $class::\$$name.");
198: }
199: }
200:
201:
202: 203: 204: 205: 206: 207:
208: public static function has($_this, $name)
209: {
210: $class = get_class($_this);
211: $name = ucfirst($name);
212: if (!isset(self::$methods[$class])) {
213: self::$methods[$class] = array_flip(get_class_methods($class));
214: }
215: return $name !== '' && (isset(self::$methods[$class]['get' . $name]) || isset(self::$methods[$class]['is' . $name]));
216: }
217:
218:
219: 220: 221: 222:
223: private static function hasProperty($class, $name)
224: {
225: $prop = & self::$props[$class][$name];
226: if ($prop === NULL) {
227: $prop = FALSE;
228: try {
229: $rp = new \ReflectionProperty($class, $name);
230: if ($name === $rp->getName() && $rp->isPublic() && !$rp->isStatic()) {
231: $prop = preg_match('#^on[A-Z]#', $name) ? 'event' : TRUE;
232: }
233: } catch (\ReflectionException $e) {}
234: }
235: return $prop;
236: }
237:
238: }
239: