@@ -117,3 +117,155 @@ $handle = spl_object_id($instance);
117
117
$objectEntry = Core::$executor->objectStore[$handle];
118
118
var_dump($objectEntry);
119
119
```
120
+
121
+ Object Extensions API
122
+ ---------------------
123
+
124
+ With the help of ` z-engine ` library it is possible to overload standard operators for your classes without diving deep
125
+ into the PHP engine implementation. For example, let's say you want to define native matrix operators and use it:
126
+
127
+ ``` php
128
+ <?php
129
+
130
+ use ZEngine\ClassExtension\ObjectCastInterface;
131
+ use ZEngine\ClassExtension\ObjectCompareValuesInterface;
132
+ use ZEngine\ClassExtension\ObjectCreateInterface;
133
+ use ZEngine\ClassExtension\ObjectCreateTrait;
134
+ use ZEngine\ClassExtension\ObjectDoOperationInterface;
135
+
136
+ class Matrix implements
137
+ ObjectCreateInterface,
138
+ ObjectCompareValuesInterface,
139
+ ObjectDoOperationInterface,
140
+ ObjectCastInterface
141
+ {
142
+ use ObjectCreateTrait;
143
+
144
+ // ...
145
+ }
146
+ $a = new Matrix([10, 20, 30]);
147
+ $b = new Matrix([1, 2, 3]);
148
+ $c = $a + $b; // Matrix([11, 22, 33])
149
+ $c *= 2; // Matrix([22, 44, 66])
150
+ ```
151
+
152
+ There are two ways of activating custom handlers.
153
+ First way is to implement several system interfaces like
154
+ ` ObjectCastInterface ` , ` ObjectCompareValuesInterface ` , ` ObjectCreateInterface ` and ` ObjectDoOperationInterface ` . After
155
+ that you should create an instance of ` ReflectionClass ` provided by this package and call ` installExtensionHandlers `
156
+ method to install extensions:
157
+
158
+ ``` php
159
+ use ZEngine\Reflection\ReflectionClass as ReflectionClassEx;
160
+
161
+ // ... initialization logic
162
+
163
+ $refClass = new ReflectionClassEx(Matrix::class);
164
+ $refClass->installExtensionHandlers();
165
+ ```
166
+
167
+ if you don't have an access to the code (eg. vendor), then you can still have an ability to define custom handlers.
168
+ You need to define callbacks as closures explicitly and assign them via ` set***Handler() ` methods in the
169
+ ` ReflectionClass ` .
170
+
171
+ ``` php
172
+ use ZEngine\ClassExtension\ObjectCreateTrait;
173
+ use ZEngine\Reflection\ReflectionClass as ReflectionClassEx;
174
+
175
+ $refClass = new ReflectionClassEx(Matrix::class);
176
+ $handler = Closure::fromCallable([ObjectCreateTrait::class, '__init']);
177
+ $refClass->setCreateObjectHandler($handler);
178
+ $refClass->setCompareValuesHandler(function ($left, $right) {
179
+ if (is_object($left)) {
180
+ $left = spl_object_id($left);
181
+ }
182
+ if (is_object($right)) {
183
+ $right = spl_object_id($right);
184
+ }
185
+
186
+ // Just for example, object with bigger object_id is considered bigger that object with smaller object_id
187
+ return $left <=> $right;
188
+ });
189
+ ```
190
+
191
+ Library provides following interfaces:
192
+
193
+ First one is ` ObjectCastInterface ` which provides a hook for handling casting a class instance to scalars. Typical
194
+ examples are following: 1) explicit ` $value = (int) $objectInstance ` or implicit: ` $value = 10 + $objectInstance; ` in
195
+ the case when ` do_operation ` handler is not installed. Please note, that this handler doesn't handle casting to ` array `
196
+ type as it is implemented in a different way.
197
+
198
+ ``` php
199
+ <?php
200
+
201
+ /**
202
+ * Interface ObjectCastInterface allows to cast given object to scalar values, like integer, floats, etc
203
+ */
204
+ interface ObjectCastInterface
205
+ {
206
+ /**
207
+ * Performs casting of given object to another value
208
+ *
209
+ * @param object $instance Instance of object that should be casted
210
+ * @param int $typeTo Type of casting, @see ReflectionValue::IS_* constants
211
+ *
212
+ * @return mixed Casted value
213
+ */
214
+ public static function __cast(object $instance, int $typeTo);
215
+ }
216
+ ```
217
+
218
+ Next ` ObjectCompareValuesInterface ` interface is used to control the comparison logic. For example, you can compare
219
+ two objects or even compare object with scalar values: ` if ($object > 10 || $object < $anotherObject) `
220
+
221
+ ``` php
222
+ <?php
223
+
224
+ /**
225
+ * Interface ObjectCompareValuesInterface allows to perform comparison of objects
226
+ */
227
+ interface ObjectCompareValuesInterface
228
+ {
229
+ /**
230
+ * Performs comparison of given object with another value
231
+ *
232
+ * @param mixed $one First side of operation
233
+ * @param mixed $another Another side of operation
234
+ *
235
+ * @return int Result of comparison: 1 is greater, -1 is less, 0 is equal
236
+ */
237
+ public static function __compare($one, $another): int;
238
+ }
239
+ ```
240
+ Handler should check arguments (one of them should be an instance of your class) and return integer result -1..1. Where
241
+ 1 is greater, -1 is less and 0 is equal.
242
+
243
+ The interface ` ObjectDoOperationInterface ` is the most powerful one because it gives you control over math operators
244
+ applied to your object (such as ADD, SUB, MUL, DIV, POW, etc).
245
+
246
+ ``` php
247
+ <?php
248
+
249
+ /**
250
+ * Interface ObjectDoOperationInterface allows to perform math operations (aka operator overloading) on object
251
+ */
252
+ interface ObjectDoOperationInterface
253
+ {
254
+ /**
255
+ * Performs casting of given object to another value
256
+ *
257
+ * @param int $opCode Operation code
258
+ * @param mixed $left left side of operation
259
+ * @param mixed $right Right side of operation
260
+ *
261
+ * @return mixed Result of operation value
262
+ */
263
+ public static function __doOperation(int $opCode, $left, $right);
264
+ }
265
+ ```
266
+ This handler receives an opcode (see ` OpCode::* ` constants) and two arguments (one of them is an instance of class) and
267
+ returns a value for that operation. In this handler you can return a new instance of your object to have a chain of
268
+ immutable instances of objects.
269
+
270
+ Important reminder: you ** MUST** install the ` create_object ` handler first in order to install hooks in runtime. Also
271
+ you can not install the ` create_object ` handler for the object if it is internal one.
0 commit comments