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