http://qiita.com/items/41e1376c41d8c15e8894 がきっかけで少々ソース読んでみた。
手元に転がってたのはPHP5.2.12。結構古い。
ext/standard/array.c
/* {{{ proto array array_splice(array input, int offset [, int length [, array replacement]])
Removes the elements designated by offset and length and replace them with supplied array */
PHP_FUNCTION(array_splice)
{
中略
/* Perform splice */
new_hash = php_splice(Z_ARRVAL_P(array), offset, length,
repl, repl_num,
&Z_ARRVAL_P(return_value));
/* Replace input array's hashtable with the new one */
zend_hash_destroy(Z_ARRVAL_P(array));
if (Z_ARRVAL_P(array) == &EG(symbol_table)) {
zend_reset_all_cv(&EG(symbol_table) TSRMLS_CC);
}
*Z_ARRVAL_P(array) = *new_hash;
FREE_HASHTABLE(new_hash);
/* Clean up */
if (argc == 4)
efree(repl);
efree(args);
/* }}} */
array_spliceでやってるのは引数に応じた前処理と結果取得後の配列参照差し替え処理で、実体はphp_spliceにある。コメントだけ訳して貼り付け。
ext/standard/array.c
HashTable* php_splice(HashTable *in_hash, int offset, int length, zval ***list, int list_count, HashTable **removed) /* {{{ */
{
HashTable *out_hash = NULL; /* Output hashtable */
int num_in, /* Number of entries in the input hashtable */
pos, /* Current position in the hashtable */
i; /* Loop counter */
Bucket *p; /* Pointer to hash bucket */
zval *entry; /* Hash entry */
略
/* 出力用ハッシュを生成し初期化する */
ALLOC_HASHTABLE(out_hash);
zend_hash_init(out_hash, 0, NULL, ZVAL_PTR_DTOR, 0);
/* 入力ハッシュの先頭からオフセット位置まで、出力ハッシュへエントリをコピーする */
for (pos=0, p=in_hash->pListHead; pos<offset && p ; pos++, p=p->pListNext) {
/* エントリを取得し参照カウントを増加させる */
entry = *((zval **)p->pData);
entry->refcount++;
/* キー種別に応じて出力ハッシュを更新する */
if (p->nKeyLength)
zend_hash_quick_update(out_hash, p->arKey, p->nKeyLength, p->h, &entry, sizeof(zval *), NULL);
else
zend_hash_next_index_insert(out_hash, &entry, sizeof(zval *), NULL);
}
/* 削除要素保存用のエントリが存在すれば(注:array_spliceからの呼び出しの場合、常に存在する。array_unshift/array_popの内部でもphp_spliceは使われていて、これらでは不要)オフセット+指定位置までのエントリをコピーする */
if (removed != NULL) {
for ( ; pos<offset+length && p; pos++, p=p->pListNext) {
entry = *((zval **)p->pData);
entry->refcount++;
if (p->nKeyLength)
zend_hash_quick_update(*removed, p->arKey, p->nKeyLength, p->h, &entry, sizeof(zval *), NULL);
else
zend_hash_next_index_insert(*removed, &entry, sizeof(zval *), NULL);
}
} else /* 無ければ単純にエントリをスキップする */
for ( ; pos<offset+length && p; pos++, p=p->pListNext);
/* 挿入するエントリがあれば… */
if (list != NULL) {
/* それぞれについて新たなzvalを生成し、エントリをそれにコピーし、出力ハッシュへのコピーを行う */
for (i=0; i<list_count; i++) {
entry = *list[i];
entry->refcount++;
zend_hash_next_index_insert(out_hash, &entry, sizeof(zval *), NULL);
}
}
/* 残りの入力ハッシュエントリを出力ハッシュへコピーする */
for ( ; p ; p=p->pListNext) {
entry = *((zval **)p->pData);
entry->refcount++;
if (p->nKeyLength)
zend_hash_quick_update(out_hash, p->arKey, p->nKeyLength, p->h, &entry, sizeof(zval *), NULL);
else
zend_hash_next_index_insert(out_hash, &entry, sizeof(zval *), NULL);
}
zend_hash_internal_pointer_reset(out_hash);
return out_hash;
}
/* }}} */
コメントが全て。ともかく、array_splice()の実装は新たなハッシュを作成してそいつに既存要素の参照をコピー、必要なら新要素を追加した上で元ハッシュを破棄してる。という時点で元記事の要望満たすのきっついなぁ。