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