xref: /aosp_15_r20/external/protobuf/php/REFCOUNTING.md (revision 1b3f573f81763fcece89efc2b6a5209149e44ab8)
1*1b3f573fSAndroid Build Coastguard Worker
2*1b3f573fSAndroid Build Coastguard Worker# Refcounting Tips
3*1b3f573fSAndroid Build Coastguard Worker
4*1b3f573fSAndroid Build Coastguard WorkerOne of the trickiest parts of the C extension for PHP is getting the refcounting
5*1b3f573fSAndroid Build Coastguard Workerright.  These are some notes about the basics of what you should know,
6*1b3f573fSAndroid Build Coastguard Workerespecially if you're not super familiar with PHP's C API.
7*1b3f573fSAndroid Build Coastguard Worker
8*1b3f573fSAndroid Build Coastguard WorkerThese notes cover the same general material as [the Memory Management chapter of
9*1b3f573fSAndroid Build Coastguard Workerthe PHP internal's
10*1b3f573fSAndroid Build Coastguard Workerbook](https://www.phpinternalsbook.com/php7/zvals/memory_management.html), but
11*1b3f573fSAndroid Build Coastguard Workercalls out some points that were not immediately clear to me.
12*1b3f573fSAndroid Build Coastguard Worker
13*1b3f573fSAndroid Build Coastguard Worker##  Zvals
14*1b3f573fSAndroid Build Coastguard Worker
15*1b3f573fSAndroid Build Coastguard WorkerIn the PHP C API, the `zval` type is roughly analogous to a variable in PHP, eg:
16*1b3f573fSAndroid Build Coastguard Worker
17*1b3f573fSAndroid Build Coastguard Worker```php
18*1b3f573fSAndroid Build Coastguard Worker    // Think of $a as a "zval".
19*1b3f573fSAndroid Build Coastguard Worker    $a = [];
20*1b3f573fSAndroid Build Coastguard Worker```
21*1b3f573fSAndroid Build Coastguard Worker
22*1b3f573fSAndroid Build Coastguard WorkerThe equivalent PHP C code would be:
23*1b3f573fSAndroid Build Coastguard Worker
24*1b3f573fSAndroid Build Coastguard Worker```c
25*1b3f573fSAndroid Build Coastguard Worker    zval a;
26*1b3f573fSAndroid Build Coastguard Worker    ZVAL_NEW_ARR(&a);  // Allocates and assigns a new array.
27*1b3f573fSAndroid Build Coastguard Worker```
28*1b3f573fSAndroid Build Coastguard Worker
29*1b3f573fSAndroid Build Coastguard WorkerPHP is reference counted, so each variable -- and thus each zval -- will have a
30*1b3f573fSAndroid Build Coastguard Workerreference on whatever it points to (unless its holding a data type that isn't
31*1b3f573fSAndroid Build Coastguard Workerrefcounted at all, like numbers). Since the zval owns a reference, it must be
32*1b3f573fSAndroid Build Coastguard Workerexplicitly destroyed in order to release this reference.
33*1b3f573fSAndroid Build Coastguard Worker
34*1b3f573fSAndroid Build Coastguard Worker```c
35*1b3f573fSAndroid Build Coastguard Worker    zval a;
36*1b3f573fSAndroid Build Coastguard Worker    ZVAL_NEW_ARR(&a);
37*1b3f573fSAndroid Build Coastguard Worker
38*1b3f573fSAndroid Build Coastguard Worker    // The destructor for a zval, this must be called or the ref will be leaked.
39*1b3f573fSAndroid Build Coastguard Worker    zval_ptr_dtor(&a);
40*1b3f573fSAndroid Build Coastguard Worker```
41*1b3f573fSAndroid Build Coastguard Worker
42*1b3f573fSAndroid Build Coastguard WorkerWhenever you see a `zval`, you can assume it owns a ref (or is storing a
43*1b3f573fSAndroid Build Coastguard Workernon-refcounted type). If you see a `zval*`, which is also quite common, then
44*1b3f573fSAndroid Build Coastguard Workerthis is *pointing to* something that owns a ref, but it does not own a ref
45*1b3f573fSAndroid Build Coastguard Workeritself.
46*1b3f573fSAndroid Build Coastguard Worker
47*1b3f573fSAndroid Build Coastguard WorkerThe [`ZVAL_*` family of
48*1b3f573fSAndroid Build Coastguard Workermacros](https://github.com/php/php-src/blob/4030a00e8b6453aff929362bf9b25c193f72c94a/Zend/zend_types.h#L883-L1109)
49*1b3f573fSAndroid Build Coastguard Workerinitializes a `zval` from a specific value type.  A few examples:
50*1b3f573fSAndroid Build Coastguard Worker
51*1b3f573fSAndroid Build Coastguard Worker* `ZVAL_NULL(&zv)`: initializes the value to `null`
52*1b3f573fSAndroid Build Coastguard Worker* `ZVAL_LONG(&zv, 5)`: initializes a `zend_long` (integer) value
53*1b3f573fSAndroid Build Coastguard Worker* `ZVAL_ARR(&zv, arr)`: initializes a `zend_array*` value (refcounted)
54*1b3f573fSAndroid Build Coastguard Worker* `ZVAL_OBJ(&zv, obj)`: initializes a `zend_object*` value (refcounted)
55*1b3f573fSAndroid Build Coastguard Worker
56*1b3f573fSAndroid Build Coastguard WorkerNote that all of our custom objects (messages, repeated fields, descriptors,
57*1b3f573fSAndroid Build Coastguard Workeretc) are `zend_object*`.
58*1b3f573fSAndroid Build Coastguard Worker
59*1b3f573fSAndroid Build Coastguard WorkerThe variants that initialize from a refcounted type do *not* increase the
60*1b3f573fSAndroid Build Coastguard Workerrefcount. This makes them suitable for initializing from a newly-created object:
61*1b3f573fSAndroid Build Coastguard Worker
62*1b3f573fSAndroid Build Coastguard Worker```c
63*1b3f573fSAndroid Build Coastguard Worker    zval zv;
64*1b3f573fSAndroid Build Coastguard Worker    ZVAL_OBJ(&zv, CreateObject());
65*1b3f573fSAndroid Build Coastguard Worker```
66*1b3f573fSAndroid Build Coastguard Worker
67*1b3f573fSAndroid Build Coastguard WorkerOnce in a while, we want to initialize a `zval` while also increasing the
68*1b3f573fSAndroid Build Coastguard Workerreference count. For this we can use `ZVAL_OBJ_COPY()`:
69*1b3f573fSAndroid Build Coastguard Worker
70*1b3f573fSAndroid Build Coastguard Worker```c
71*1b3f573fSAndroid Build Coastguard Workerzend_object *some_global;
72*1b3f573fSAndroid Build Coastguard Worker
73*1b3f573fSAndroid Build Coastguard Workervoid GetGlobal(zval *zv) {
74*1b3f573fSAndroid Build Coastguard Worker    // We want to create a new ref to an existing object.
75*1b3f573fSAndroid Build Coastguard Worker    ZVAL_OBJ_COPY(zv, some_global);
76*1b3f573fSAndroid Build Coastguard Worker}
77*1b3f573fSAndroid Build Coastguard Worker```
78*1b3f573fSAndroid Build Coastguard Worker
79*1b3f573fSAndroid Build Coastguard Worker## Transferring references
80*1b3f573fSAndroid Build Coastguard Worker
81*1b3f573fSAndroid Build Coastguard WorkerA `zval`'s ref must be released at some point. While `zval_ptr_dtor()` is the
82*1b3f573fSAndroid Build Coastguard Workersimplest way of releasing a ref, it is not the most common (at least in our code
83*1b3f573fSAndroid Build Coastguard Workerbase). More often, we are returning the `zval` back to PHP from C.
84*1b3f573fSAndroid Build Coastguard Worker
85*1b3f573fSAndroid Build Coastguard Worker```c
86*1b3f573fSAndroid Build Coastguard Worker    zval zv;
87*1b3f573fSAndroid Build Coastguard Worker    InitializeOurZval(&zv);
88*1b3f573fSAndroid Build Coastguard Worker    // Returns the value of zv to the caller and donates our ref.
89*1b3f573fSAndroid Build Coastguard Worker    RETURN_COPY_VALUE(&zv);
90*1b3f573fSAndroid Build Coastguard Worker```
91*1b3f573fSAndroid Build Coastguard Worker
92*1b3f573fSAndroid Build Coastguard WorkerThe `RETURN_COPY_VALUE()` macro (standard in PHP 8.x, and polyfilled in earlier
93*1b3f573fSAndroid Build Coastguard Workerversions) is the most common way we return a value back to PHP, because it
94*1b3f573fSAndroid Build Coastguard Workerdonates our `zval`'s refcount to the caller, and thus saves us from needing to
95*1b3f573fSAndroid Build Coastguard Workerdestroy our `zval` explicitly. This is ideal when we have a full `zval` to
96*1b3f573fSAndroid Build Coastguard Workerreturn.
97*1b3f573fSAndroid Build Coastguard Worker
98*1b3f573fSAndroid Build Coastguard WorkerOnce in a while we have a `zval*` to return instead. For example when we parse
99*1b3f573fSAndroid Build Coastguard Workerparameters to our function and ask for a `zval`, PHP will give us pointers to
100*1b3f573fSAndroid Build Coastguard Workerthe existing `zval` structures instead of creating new ones.
101*1b3f573fSAndroid Build Coastguard Worker
102*1b3f573fSAndroid Build Coastguard Worker```c
103*1b3f573fSAndroid Build Coastguard Worker    zval *val;
104*1b3f573fSAndroid Build Coastguard Worker    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &val) == FAILURE) {
105*1b3f573fSAndroid Build Coastguard Worker      return;
106*1b3f573fSAndroid Build Coastguard Worker    }
107*1b3f573fSAndroid Build Coastguard Worker    // Returns a copy of this zval, adding a ref in the process.
108*1b3f573fSAndroid Build Coastguard Worker    RETURN_COPY(val);
109*1b3f573fSAndroid Build Coastguard Worker```
110*1b3f573fSAndroid Build Coastguard Worker
111*1b3f573fSAndroid Build Coastguard WorkerWhen we use `RETURN_COPY`, the refcount is increased; this is perfect for
112*1b3f573fSAndroid Build Coastguard Workerreturning a `zval*` when we do not own a ref on it.
113