@@ -117,3 +117,155 @@ $handle = spl_object_id($instance);
117117$objectEntry = Core::$executor->objectStore[$handle];
118118var_dump($objectEntry);
119119```
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