zval ubiquity
The ubiquitous zval is represented by val_ptr and val_rc.
val_ptr
val_ptr holds a zval*, given by PHP from a function call, or from inside an array. val_ptr does not do automatic reference counting on its content in constructor or destructor. There is no declared destructor function. val_ptr merely copies a zval* to its only internal member value, zval* p_, for further inspection and/or work.
The main externally used purpose of val_ptr, is to pass along zval* without reference counting, and read its enclose value type, and extract its encloses PHP types without altering the value within, as most functions are delared as () const;
The val_ptr does have some protected methods which will increment its reference count. These are used by some methods of the val_rc class, htab_ptr and htab_rw class, which declared as "friends".
There are also some static methods, inlined as declared in the val_ptr header. These are string_bind, object_bind, and array_bind. They so not increment their stored values reference count. This is because where used the increment happens only on success of the operation, usually a write to a HashTable, or in setting an object property. As this is mostly successful, the code really ought to increment the reference count, and undo only on unexpected failure. But it is to allow the use of "throwaway" temporary plain zval structure, increment not done, and also no need for a destructor with a check for reference decrement.
The author decided to indulge further and return a boolean value from string_bind, object_bind, and array_bind to be true if value is reference counted.
If the contained zval* is a nullptr, isNull() returns true, and ok() returns false. Method is_nullptr() returns true for the specific case of containing a nullptr.
// How zval temporary is used
void obj_ptr::property(str_ptr key, str_ptr value)
{
zval temp = {0};
// No rc++, because zval is thrown away on exit.
val_ptr::string_bind(&temp, value);
property(key, val_ptr(&temp));
}
// How zval temporary is assigned a specific type
void htab_rw::push_back(zend_string* zs)
{
zval tmp = {0};
bool refct = val_ptr::string_bind(&tmp,zs);
if (zend_hash_next_index_insert(ht_, &tmp))
{
// avoid double checks
if (refct) GC_ADDREF(zs);
}
}
// the static try_addref methods are type specific and also likely to be inlined
//
static void str_rc::try_addref(zend_string* zs)
{
if (GC_TYPE_INFO(zs) & IS_STR_INTERNED)
{
return;
}
GC_ADDREF(zs);
}
These methods are for inner part of the workings of some operators and classes of zpp, and should hardly ever be used externally by other C++ class code.
var_rc
var_rc does not inherited from val_ptr. Rather than holding a zval*, it holds a full zval structure. Which can hold anything PHP can put in it. It uses val_ptr methods in its internals, because, it can pass a pointer to its own zval to construct a val_ptr to do much of its work.
A major purpose of val_rc, is to allow the assignment of raw PHP values, handle reference counting for values returned from functions that are otherwise garbage collected, and sometime return a mixed value back to PHP.
'var_rc', like 'var_ptr' has methods to query the contained PHP type, and attempt to return common PHP handle types asked for, namely zobject(), zarray(), zstr(). It returns a nullptr if the type is not agreeable, does not throw an exception.