Skip to content

Commit 08703fa

Browse files
committed
Add a manual about object extensions API #20
1 parent d53d142 commit 08703fa

File tree

1 file changed

+152
-0
lines changed

1 file changed

+152
-0
lines changed

README.md

+152
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,155 @@ $handle = spl_object_id($instance);
117117
$objectEntry = Core::$executor->objectStore[$handle];
118118
var_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

Comments
 (0)