|
| 1 | +Object handlers |
| 2 | +=============== |
| 3 | + |
| 4 | +Nearly all operations on objects in PHP go through object handlers and every magic method or magic interface is |
| 5 | +implemented with an object or class handler internally. Furthermore there are quite a few handlers which are not exposed |
| 6 | +to userland PHP. For example internal classes can have custom comparison and cast behavior. |
| 7 | + |
| 8 | +As the number of different object handlers is rather large we can only discuss examples (using the typed array |
| 9 | +implementation from the last section) for a few of them. For all the others only a short description is provided. |
| 10 | + |
| 11 | +An Overview |
| 12 | +----------- |
| 13 | + |
| 14 | +Here are all the object handlers with their signature and a small description. |
| 15 | + |
| 16 | +.. c:member:: |
| 17 | + zval *read_property(zend_object *object, zend_string *member, int type, void **cache_slot, zval *rv) |
| 18 | + zval *write_property(zend_object *object, zend_string *member, zval *value, void **cache_slot) |
| 19 | + int has_property(zend_object *zobj, zend_string *name, int has_set_exists, void **cache_slot) |
| 20 | + void unset_property(zend_object *zobj, zend_string *name, void **cache_slot) |
| 21 | + zval *get_property_ptr_ptr(zend_object *zobj, zend_string *name, int type, void **cache_slot) |
| 22 | +
|
| 23 | + These handlers correspond to the ``__get``, ``__set``, ``__isset`` and ``__unset`` methods. ``get_property_ptr_ptr`` |
| 24 | + is the internal equivalent of ``__get`` returning by reference. ``cache_slot`` is used to store the property |
| 25 | + offset and ``zend_property_info``. ``zval *rv`` in ``read_property`` provides a place for temporary ``zval``s that |
| 26 | + are not stored in the object itself, like results of calls to ``__get``. |
| 27 | + |
| 28 | +.. c:member:: |
| 29 | + zval *read_dimension(zend_object *object, zval *offset, int type, zval *rv) |
| 30 | + void write_dimension(zend_object *object, zval *offset, zval *value) |
| 31 | + int has_dimension(zend_object *object, zval *offset, int check_empty) |
| 32 | + void unset_dimension(zend_object *object, zval *offset) |
| 33 | +
|
| 34 | + This set of handlers is the internal representation of the ``ArrayAccess`` interface. ``zval *rv`` in |
| 35 | + ``read_dimension`` is used for temporary values returned from ``offsetGet`` and ``offsetExists``. |
| 36 | + |
| 37 | +.. c:member:: |
| 38 | + HashTable *get_properties(zend_object *zobj) |
| 39 | + HashTable *get_debug_info(zend_object *object, int *is_temp) |
| 40 | +
|
| 41 | + Used to get the object properties as a hashtable. The former is more general purpose, for example it is also used |
| 42 | + for the ``get_object_vars`` function. The latter on the other hand is used exclusively to display properties in |
| 43 | + debugging functions like ``var_dump``. So even if your object does not provide any formal properties you can still |
| 44 | + have a meaningful debug output. |
| 45 | + |
| 46 | +.. c:member:: |
| 47 | + zend_function *get_method(zend_object **obj_ptr, zend_string *method_name, const zval *key) |
| 48 | +
|
| 49 | + The ``get_method`` handler fetches the ``zend_function`` used to call a certain method. Either the ``method_name`` |
| 50 | + or ``key`` must be passed. ``key`` is assumed to be lower case. |
| 51 | + |
| 52 | +.. c:member:: |
| 53 | + zend_function *get_constructor(zend_object *zobj) |
| 54 | +
|
| 55 | + Like ``get_method``, but getting the constructor function. The most common reason to override this handler is to |
| 56 | + disallow manual construction by throwing an error in the handler. |
| 57 | + |
| 58 | +.. c:member:: |
| 59 | + int count_elements(zend_object *object, zend_long *count) |
| 60 | +
|
| 61 | + This is just the internal way of implementing the ``Countable::count`` method. The function returns a |
| 62 | + ``zend_result`` and assigns the value to the ``zend_long *count`` pointer. |
| 63 | + |
| 64 | +.. FIXME: Change return type of count_elements to zend_result to make it more obvious the count is not returned? |
| 65 | +
|
| 66 | +.. c:member:: |
| 67 | + int compare(zval *o1, zval *o2) |
| 68 | + int cast_object(zend_object *readobj, zval *writeobj, int type) |
| 69 | +
|
| 70 | + Internal classes have the ability to implement a custom compare behavior and override casting behavior for all |
| 71 | + types. Userland classes on the other hand only have the ability to override object to string casting through |
| 72 | + ``__toString``. |
| 73 | + |
| 74 | +.. c:member:: |
| 75 | + int get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only) |
| 76 | +
|
| 77 | + This handler is invoked when the object is used as a function, i.e. it is the internal version of ``__invoke``. |
| 78 | + The name derives from the fact that its main use is for the implementation of closures (the ``Closure`` class). |
| 79 | + |
| 80 | +.. c:member:: |
| 81 | + zend_string *get_class_name(const zend_object *zobj) |
| 82 | +
|
| 83 | + This handler is used to get the class name from an object. There should be little reason to overwrite it. The only |
| 84 | + occasion that I can think of where this would be necessary is if you choose to create a custom object structure that |
| 85 | + does *not* contain the standard ``zend_object`` as a substructure. (This is entirely possible, but not usually done.) |
| 86 | + |
| 87 | +.. c:member:: |
| 88 | + zend_object *clone_obj(zend_object *old_object) |
| 89 | + HashTable *get_gc(zend_object *zobj, zval **table, int *n) |
| 90 | +
|
| 91 | + You already know the ``clone_obj`` handler, so I'll jump right to ``get_gc``: This handler should return all |
| 92 | + variables that are held by the object, so cyclic dependencies can be properly collected. If the object doesn't |
| 93 | + maintain a property hashmap (because it doesn't store any dynamic properties) it can use ``table`` to store a |
| 94 | + pointer directly into the list of zvals, along with a count of properties. |
| 95 | + |
| 96 | +.. c:member:: |
| 97 | + void dtor_obj(zend_object *object) |
| 98 | + void free_obj(zend_object *object) |
| 99 | +
|
| 100 | + ``dtor_obj`` is called before ``free_obj``. The object must remain in a valid state after dtor_obj finishes running. |
| 101 | + Unlike ``free_obj``, it is run prior to deactivation of the executor during shutdown, which allows user code to run. |
| 102 | + This handler is not guaranteed to be called (e.g. on fatal error), and as such should not be used to release |
| 103 | + resources or deallocate memory. Furthermore, releasing resources in this handler can break detection of memory |
| 104 | + leaks, as cycles may be broken early. ``dtor_obj`` should be used only to call user destruction hooks, such as |
| 105 | + ``__destruct``. |
| 106 | + |
| 107 | + ``free_obj`` should release any resources the object holds, without freeing the object structure itself. The object |
| 108 | + does not need to be in a valid state after ``free_obj`` finishes running. ``free_obj`` will always be invoked, even |
| 109 | + if the object leaks or a fatal error occurs. However, during shutdown it may be called once the executor is no |
| 110 | + longer active, in which case execution of user code may be skipped. |
| 111 | + |
| 112 | +.. c:member:: |
| 113 | + int do_operation(zend_uchar opcode, zval *result, zval *op1, zval *op2) |
| 114 | +
|
| 115 | + ``do_operation`` is an optional handler that will be invoked for various arithmetic and binary operations on |
| 116 | + instances of the given class. This allows for operator overloading semantics to be implemented for custom classes. |
| 117 | + Examples for overloadable operators are ``+``, ``-``, ``*``, ``/``, ``++``, ``--``, ``!``. |
| 118 | + |
| 119 | +.. c:member:: |
| 120 | + int compare(zval *object1, zval *object2) |
| 121 | +
|
| 122 | + The ``compare`` handler is a required handler that computes equality of the given object and another value. Note |
| 123 | + that the other value isn't necessarily an object of the same class, or even an object at all. The handler should |
| 124 | + return negative numbers if the lhs is smaller, 0 if they are equal, or a positive number is the lhs is larger. If |
| 125 | + the values are uncomparable ``ZEND_UNCOMPARABLE`` should be returned. |
| 126 | + |
| 127 | +.. c:member:: |
| 128 | + zend_array *get_properties_for(zend_object *object, zend_prop_purpose purpose) |
| 129 | +
|
| 130 | + The ``get_properties_for`` can be used to customize the list of object properties returned for various purposes. |
| 131 | + The purposes are defined in ``zend_prop_purpose``, which currently entails ``print_r``, ``var_dump``, the |
| 132 | + ``(array)`` cast, ``serialize``, ``var_export`` and ``json_encode``. |
0 commit comments