Understanding PHP zend_array: Packed Array vs. Hash Array Internal Structure

This article explains how PHP's zend_array is implemented as either a packed array or a hash array, detailing the underlying struct fields, the role of the Bucket container, and how different key patterns affect memory layout and performance.

php Courses
php Courses
php Courses
Understanding PHP zend_array: Packed Array vs. Hash Array Internal Structure

PHP stores its array data in a structure called zend_array, which can be represented internally as either a packed array (when all keys are sequential integers) or a hash array (when keys are non‑sequential or non‑numeric).

The core definition of zend_array (also typedefed as HashTable) is:

typedef struct _zend_array HashTable;
struct _zend_array {
    zend_refcounted_h gc;
    union {
        struct { ZEND_ENDIAN_LOHI_4(zend_uchar flags, zend_uchar _unused, zend_uchar nIteratorsCount, zend_uchar _unused2) } v;
        uint32_t flags;
    } u;
    uint32_t nTableMask;
    Bucket *arData;
    uint32_t nNumUsed;
    uint32_t nNumOfElements;
    uint32_t nTableSize;
    uint32_t nInternalPointer;
    zend_long nNextFreeElement;
    dtor_func_t pDestructor;
};

Key fields include: gc: reference‑counted header used for memory management. flags and the union u: store hash‑table state and iterator count. nTableSize: total number of Bucket slots. nNumUsed: how many slots have ever been used (including deleted ones). nNumOfElements: number of currently valid elements. nNextFreeElement: the next numeric key to assign for packed arrays.

Each element of the array is stored in a Bucket struct:

typedef struct _Bucket {
    zval val;               // element value (16 bytes)
    zend_ulong h;          // hash of the key
    zend_string *key;      // pointer to the key string (used only for hash arrays)
} Bucket;

Both packed and hash arrays ultimately store their data in Bucket entries; the difference lies in how the key is interpreted. For a packed array the key is implicit (0, 1, 2, …) and the h field equals the bucket index. For a hash array the key may be any string or integer, and h holds the hashed value.

Example of a packed array: $a = array(1, 2, 3); // packed array In this case nTableSize equals the number of buckets (e.g., 8), nNumUsed and nNumOfElements are both 3, nNextFreeElement is 3, and the index array size is fixed at 2 because it is not used.

Example of a hash array: $c = array('x'=>1, 'y'=>2, 'z'=>3, 'a'=>0); // hash array Here the hash table size may be 8, nNumUsed and nNumOfElements are 4, nTableMask is –8, and nNextFreeElement remains 0 because numeric keys are not automatically generated.

Because packed arrays avoid the auxiliary index array, they save memory (approximately (nTableSize‑2) * sizeof(uint32_t) bytes) and provide faster direct bucket access, making them the preferred representation when keys are sequential integers.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendPHPArraysdata-structuresphp-internalszend_array
php Courses
Written by

php Courses

php中文网's platform for the latest courses and technical articles, helping PHP learners advance quickly.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.