Skip to content

Commit aa831fd

Browse files
authored
Add object handlers section for PHP 7 (#123)
Add chapter for objects in PHP 7 and document object handlers.
1 parent d23f273 commit aa831fd

File tree

3 files changed

+148
-1
lines changed

3 files changed

+148
-1
lines changed

Book/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ PHP 7 and PHP 8
1515
php7/memory_management.rst
1616
php7/zend_engine.rst
1717
php7/debugging.rst
18+
php7/classes_objects.rst
1819

1920
..
2021
php7/hashtables.rst
21-
php7/classes_objects.rst
2222
php7/prerequisites.rst
2323
php7/php_first_look.rst
2424
php7/managing_memory.rst

Book/php7/classes_objects.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Classes and objects
2+
===================
3+
4+
This chapter covers the rather complex internals of PHP's object orientation system.
5+
6+
Contents:
7+
8+
.. toctree::
9+
:maxdepth: 2
10+
11+
classes_objects/object_handlers.rst
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
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

Comments
 (0)