1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette;
9:
10: use Nette\Utils\Callback;
11: use Nette\Utils\ObjectMixin;
12:
13:
14: 15: 16: 17: 18: 19: 20: 21:
22: trait SmartObject
23: {
24:
25: 26: 27: 28:
29: public function __call($name, $args)
30: {
31: $class = get_class($this);
32: $isProp = ObjectMixin::hasProperty($class, $name);
33:
34: if ($name === '') {
35: throw new MemberAccessException("Call to class '$class' method without name.");
36:
37: } elseif ($isProp === 'event') {
38: if (is_array($this->$name) || $this->$name instanceof \Traversable) {
39: foreach ($this->$name as $handler) {
40: Callback::invokeArgs($handler, $args);
41: }
42: } elseif ($this->$name !== null) {
43: throw new UnexpectedValueException("Property $class::$$name must be array or null, " . gettype($this->$name) . ' given.');
44: }
45:
46: } elseif ($isProp && $this->$name instanceof \Closure) {
47: trigger_error("Invoking closure in property via \$obj->$name() is deprecated" . ObjectMixin::getSource(), E_USER_DEPRECATED);
48: return call_user_func_array($this->$name, $args);
49:
50: } elseif (($methods = &ObjectMixin::getMethods($class)) && isset($methods[$name]) && is_array($methods[$name])) {
51: trigger_error("Magic methods such as $class::$name() are deprecated" . ObjectMixin::getSource(), E_USER_DEPRECATED);
52: list($op, $rp, $type) = $methods[$name];
53: if (count($args) !== ($op === 'get' ? 0 : 1)) {
54: throw new InvalidArgumentException("$class::$name() expects " . ($op === 'get' ? 'no' : '1') . ' argument, ' . count($args) . ' given.');
55:
56: } elseif ($type && $args && !ObjectMixin::checkType($args[0], $type)) {
57: throw new InvalidArgumentException("Argument passed to $class::$name() must be $type, " . gettype($args[0]) . ' given.');
58: }
59:
60: if ($op === 'get') {
61: return $rp->getValue($this);
62: } elseif ($op === 'set') {
63: $rp->setValue($this, $args[0]);
64: } elseif ($op === 'add') {
65: $val = $rp->getValue($this);
66: $val[] = $args[0];
67: $rp->setValue($this, $val);
68: }
69: return $this;
70:
71: } elseif ($cb = ObjectMixin::getExtensionMethod($class, $name)) {
72: trigger_error("Extension methods such as $class::$name() are deprecated" . ObjectMixin::getSource(), E_USER_DEPRECATED);
73: return Callback::invoke($cb, $this, ...$args);
74:
75: } else {
76: ObjectMixin::strictCall($class, $name);
77: }
78: }
79:
80:
81: 82: 83: 84:
85: public static function __callStatic($name, $args)
86: {
87: ObjectMixin::strictStaticCall(get_called_class(), $name);
88: }
89:
90:
91: 92: 93: 94:
95: public function &__get($name)
96: {
97: $class = get_class($this);
98: $uname = ucfirst($name);
99:
100: if ($prop = ObjectMixin::getMagicProperty($class, $name)) {
101: if (!($prop & 0b0001)) {
102: throw new MemberAccessException("Cannot read a write-only property $class::\$$name.");
103: }
104: $m = ($prop & 0b0010 ? 'get' : 'is') . $uname;
105: if ($prop & 0b0100) {
106: return $this->$m();
107: } else {
108: $val = $this->$m();
109: return $val;
110: }
111:
112: } elseif ($name === '') {
113: throw new MemberAccessException("Cannot read a class '$class' property without name.");
114:
115: } elseif (($methods = &ObjectMixin::getMethods($class)) && isset($methods[$m = 'get' . $uname]) || isset($methods[$m = 'is' . $uname])) {
116: trigger_error("Use $m() or add annotation @property for $class::\$$name" . ObjectMixin::getSource(), E_USER_DEPRECATED);
117: if ($methods[$m] === 0) {
118: $methods[$m] = (new \ReflectionMethod($class, $m))->returnsReference();
119: }
120: if ($methods[$m] === true) {
121: return $this->$m();
122: } else {
123: $val = $this->$m();
124: return $val;
125: }
126:
127: } elseif (isset($methods[$name])) {
128: trigger_error("Accessing methods as properties via \$obj->$name is deprecated" . ObjectMixin::getSource(), E_USER_DEPRECATED);
129: $val = Callback::closure($this, $name);
130: return $val;
131:
132: } elseif (isset($methods['set' . $uname])) {
133: throw new MemberAccessException("Cannot read a write-only property $class::\$$name.");
134:
135: } else {
136: ObjectMixin::strictGet($class, $name);
137: }
138: }
139:
140:
141: 142: 143: 144:
145: public function __set($name, $value)
146: {
147: $class = get_class($this);
148: $uname = ucfirst($name);
149:
150: if (ObjectMixin::hasProperty($class, $name)) {
151: $this->$name = $value;
152:
153: } elseif ($prop = ObjectMixin::getMagicProperty($class, $name)) {
154: if (!($prop & 0b1000)) {
155: throw new MemberAccessException("Cannot write to a read-only property $class::\$$name.");
156: }
157: $this->{'set' . $name}($value);
158:
159: } elseif ($name === '') {
160: throw new MemberAccessException("Cannot write to a class '$class' property without name.");
161:
162: } elseif (($methods = &ObjectMixin::getMethods($class)) && isset($methods[$m = 'set' . $uname])) {
163: trigger_error("Use $m() or add annotation @property for $class::\$$name" . ObjectMixin::getSource(), E_USER_DEPRECATED);
164: $this->$m($value);
165:
166: } elseif (isset($methods['get' . $uname]) || isset($methods['is' . $uname])) {
167: throw new MemberAccessException("Cannot write to a read-only property $class::\$$name.");
168:
169: } else {
170: ObjectMixin::strictSet($class, $name);
171: }
172: }
173:
174:
175: 176: 177: 178:
179: public function __unset($name)
180: {
181: $class = get_class($this);
182: if (!ObjectMixin::hasProperty($class, $name)) {
183: throw new MemberAccessException("Cannot unset the property $class::\$$name.");
184: }
185: }
186:
187:
188: 189: 190:
191: public function __isset($name)
192: {
193: $uname = ucfirst($name);
194: return ObjectMixin::getMagicProperty(get_class($this), $name)
195: || ($name !== '' && ($methods = ObjectMixin::getMethods(get_class($this))) && (isset($methods['get' . $uname]) || isset($methods['is' . $uname])));
196: }
197:
198:
199: 200: 201: 202:
203: public static function getReflection()
204: {
205: trigger_error(get_called_class() . '::getReflection() is deprecated' . ObjectMixin::getSource(), E_USER_DEPRECATED);
206: $class = class_exists(Reflection\ClassType::class) ? Reflection\ClassType::class : \ReflectionClass::class;
207: return new $class(get_called_class());
208: }
209:
210:
211: 212: 213: 214:
215: public static function extensionMethod($name, $callback = null)
216: {
217: if (strpos($name, '::') === false) {
218: $class = get_called_class();
219: } else {
220: list($class, $name) = explode('::', $name);
221: $class = (new \ReflectionClass($class))->getName();
222: }
223: trigger_error("Extension methods such as $class::$name() are deprecated" . ObjectMixin::getSource(), E_USER_DEPRECATED);
224: if ($callback === null) {
225: return ObjectMixin::getExtensionMethod($class, $name);
226: } else {
227: ObjectMixin::setExtensionMethod($class, $name, $callback);
228: }
229: }
230: }
231: