HashTable* Writing values, class zpp::htab_rw

The PHP scripting language requires that Arrays with more than one reference, that is assigned to more than one variable, be copy on write. If a write occurs the copy is made of the array written to, so that again its reference count value is 1. Otherwise the contents of arrays are shared, only for that content just prior to the last write operation.

Using zpp::htab_rw

htab_rw is also a htab_ptr , with read methods, and adds all write and array modification methods. It is constructed using another source of a HashTable, and is not itself reference counted. The source class will have its HashTable checked, and if it has a reference count of greater than one, the HashTable pointer will be duplicated, and the new copy with reference count of 1 is stationed in its place.

Its maybe worth looking at the current implementation of copy-on-write

/**
 * static class functions of htab\_rc.

 * Copy on Write operation (cowop)
 * Ensures argument inout is a writeable HashTable with reference count of 1.
 * The owning object loses its access to the original, so try\_decref must be called here.
 * 
 * Zend engine has a global static zend_array structure.
 * - zend_empty_array. Its reference count is 2, and marked as immutable.
 * Return true if the pointer reference was changed.
 */
bool htab_rc::cowop(HashTable*& inout, size_t init)
{
	HashTable* used = inout;
	if ( (used == nullptr)
	   ||(used == const_cast<HashTable*>(&zend_empty_array)))
	{
		HashTable* newht = zend_new_array(init);
		inout = newht;
		return true;
	}
	if (GC_REFCOUNT(used) > 1) 
	{
		inout = zend_array_dup(used);
		htab_rc::try_decref(used);
	    return true;
	}
	return false;
}
/**
 * htab_rw will ensure the source class donating the HashTable* does
 * a copy-write array operation if the reference count is greater than
 * 1.  The size argument is used to make a new array if HashTable* is null 
 * or immutable. Fair Warning: This will replace value inside any zval.
 * htab_rw is then usable as a write agent acting on behalf of the donating source.
 */


htab_rw(htab_rc& mgr, size_t init);
htab_rw(val_rc& mgr, size_t init);
htab_rw(val_ptr mgr, size_t init);
htab_rw(zval* p, size_t init);

htab_rw(HashTable* h);

Obscure side-effects of setting zval value, especially as value in array.

The write methods of htab_rw, all call a specialist zval bind method, to ensure that particular flags in the zval are unset for immutable values. Otherwise some PHP value deletion code may be run on them, leading to a crash.

These static bind methods are part of the val_ptr class.

For example, the common PHP macro ZVAL_ARR(zval*, HashTable*) sets the type flags in an optimized fashion, using the bit mask IS_ARRAY_EX, not just IS_ARRAY, making it both reference counted and collectable. We don't want this for immutable values. Same idea applies for immutable objects or interned strings. A comment in PHP source zend_types.h "we should never set just Z_TYPE, we should set Z_TYPE_INFO". The u1 member of zval is a union structure, with the size of uint32_t (type_info), the type itself is uint8_t, and type_flags are uint8_t. So the macro Z_TYPE_INFO_P(zval*, int) conveniently sets all the bits of this union.

//should be done on zeroed zval
/**
 * The ZVAL_STR macro now also does the equivalent of this.
 * 
		Z_STR_P(tmp,s);
        if ((GC_FLAGS(s) & IS_STR_INTERNED))
    	{
    		Z_TYPE_INFO_P(tmp, IS_STRING);
    		//Z_TYPE_FLAGS_P(tmp) = 0; 
	    }
	    else {
	    	Z_TYPE_INFO_P(tmp, IS_STRING_EX);
	    }

 */
void val_ptr::array_bind(zval* tmp, HashTable* ht);
void val_ptr::string_bind(zval* tmp, zend_string* s);
void val_ptr::object_bind(zval* temp, zend_object* obj);