/*
$Id: array.c,v 1.10 2005/05/24 09:54:57 dk Exp $
Implemenmtation of PHP::TieHash and PHP::TieArray methods
*/
#include "PHP.h"
#ifdef __cplusplus
extern "C" {
#endif
XS(PHP_ArrayHandle_new)
{
dXSARGS;
STRLEN na;
zval * array;
if ( items != 1)
croak("PHP::ArrayHandle::new: 1 parameter expected");
SP -= items;
MAKE_STD_ZVAL( array);
array_init( array);
XPUSHs( sv_2mortal( Entity_create( SvPV( ST(0), na), array)));
PUTBACK;
ZVAL_DELREF( array);
return;
}
XS( PHP_TieHash_EXISTS)
{
dXSARGS;
char * key;
STRLEN na, klen;
zval * array;
#define METHOD "PHP::TieHash::EXISTS"
if ( items != 2)
croak("%s: expect 2 parameters", METHOD);
if (( array = SV2ZANY( ST(0))) == NULL)
croak("%s: (%s) is not a PHP array", METHOD, SvPV(ST(0), na));
key = SvPV( ST(1), klen);
DEBUG("exists 0x%x->{%s}", array, key);
SP -= items;
PUTBACK;
return XSRETURN_IV( zend_hash_exists( HASH_OF(array), key, klen + 1));
#undef METHOD
}
XS( PHP_TieHash_FETCH)
{
dXSARGS;
char * key;
STRLEN na, klen;
zval * array, **zobj;
SV * retsv;
#define METHOD "PHP::TieHash::FETCH"
if ( items != 2)
croak("%s: expect 2 parameters", METHOD);
if (( array = SV2ZANY( ST(0))) == NULL)
croak("%s: (%s) is not a PHP array", METHOD, SvPV(ST(0), na));
key = SvPV( ST(1), klen);
DEBUG("fetch 0x%x->{%s}", array, key);
SP -= items;
if ( zend_hash_find( HASH_OF(array), key, klen + 1, (void**) &zobj) == FAILURE) {
XPUSHs( &PL_sv_undef);
PUTBACK;
return;
}
if ( !( retsv = zval2sv( *zobj))) {
warn("%s: value cannot be converted\n", METHOD);
retsv = &PL_sv_undef;
}
retsv = sv_2mortal( retsv);
XPUSHs( retsv);
#undef METHOD
PUTBACK;
return;
}
XS( PHP_TieHash_STORE)
{
dXSARGS;
char * key;
STRLEN na, klen;
zval * array, *zobj;
SV * val;
#define METHOD "PHP::TieHash::STORE"
if ( items != 3)
croak("%s: expect 3 parameters", METHOD);
if (( array = SV2ZANY( ST(0))) == NULL)
croak("%s: (%s) is not a PHP array", METHOD, SvPV(ST(0), na));
key = SvPV( ST(1), klen);
DEBUG("store 0x%x->{%s}=%s", array, key, SvPV( ST(2), na));
MAKE_STD_ZVAL( zobj);
zobj-> type = IS_NULL;
if ( !sv2zval( val = ST(2), zobj, -1)) {
zval_ptr_dtor( &zobj);
croak("%s: scalar (%s) type=%d cannot be converted",
METHOD, SvPV( val, na), SvTYPE( val));
}
if ( zend_hash_update(
HASH_OF(array), key, klen + 1,
(void *)&zobj, sizeof(zval *), NULL
) == FAILURE) {
zval_ptr_dtor( &zobj);
croak("%s: failed", METHOD);
}
#undef METHOD
SP -= items;
PUTBACK;
XSRETURN_EMPTY;
}
XS( PHP_TieHash_DELETE)
{
dXSARGS;
char * key;
STRLEN na, klen;
zval * array;
#define METHOD "PHP::TieHash::DELETE"
if ( items != 2)
croak("%s: expect 2 parameters", METHOD);
if (( array = SV2ZANY( ST(0))) == NULL)
croak("%s: (%s) is not a PHP array", METHOD, SvPV(ST(0), na));
key = SvPV( ST(1), klen);
DEBUG("delete 0x%x->{%s}", array, key);
SP -= items;
PUTBACK;
zend_hash_del( HASH_OF(array), key, klen + 1);
XSRETURN_EMPTY;
#undef METHOD
}
XS( PHP_TieHash_CLEAR)
{
dXSARGS;
STRLEN na;
zval * array;
#define METHOD "PHP::TieHash::CLEAR"
if ( items != 1)
croak("%s: expect 1 parameter", METHOD);
if (( array = SV2ZANY( ST(0))) == NULL)
croak("%s: (%s) is not a PHP array", METHOD, SvPV(ST(0), na));
DEBUG("clear 0x%x", array);
SP -= items;
PUTBACK;
zend_hash_clean( HASH_OF(array));
XSRETURN_EMPTY;
#undef METHOD
}
/* for internal use by FIRSTKEY and NEXTKEY - construct return value and advance zhash ptr */
static SV *
do_zenum(
char * method,
zval * array,
HashPosition * hpos
) {
SV * ret;
int rettype, klen;
unsigned long numkey;
char * key;
if ( ( rettype = zend_hash_get_current_key_ex( HASH_OF(array),
&key, &klen, &numkey, 0, hpos)) == HASH_KEY_NON_EXISTANT) {
DEBUG( "%s: enum stop", method);
return &PL_sv_undef;
}
if ( rettype == HASH_KEY_IS_STRING) {
ret = newSVpvn( key, klen - 1);
DEBUG( "%s: enum %s", method, key);
} else {
ret = newSViv( numkey);
DEBUG( "%s: enum index %d", method, numkey);
}
return sv_2mortal( ret);
}
XS( PHP_TieHash_FIRSTKEY)
{
dXSARGS;
zval * array;
STRLEN na;
SV * hash_position, * perl_obj;
HashPosition hpos_buf, *hpos;
#define METHOD "PHP::TieHash::FIRSTKEY"
if ( items != 1)
croak("%s: expect 1 parameter", METHOD);
if (( array = SV2ZANY( perl_obj = ST(0))) == NULL)
croak("%s: (%s) is not a PHP array", METHOD, SvPV( perl_obj, na));
DEBUG("firstkey 0x%x", array);
hash_position = newSV( sizeof( HashPosition));
sv_setpvn( hash_position, ( char *) &hpos_buf, sizeof( hpos_buf));
hpos = ( HashPosition*) SvPV( hash_position, na);
hv_store((HV *) SvRV( perl_obj), "__ENUM__", 8, hash_position, 0);
zend_hash_internal_pointer_reset_ex( HASH_OF(array), hpos);
SP -= items;
XPUSHs( do_zenum( METHOD, array, hpos));
PUTBACK;
#undef METHOD
return;
}
XS( PHP_TieHash_NEXTKEY)
{
dXSARGS;
zval * array;
STRLEN na;
SV ** hash_position, * perl_obj;
HashPosition *hpos;
#define METHOD "PHP::TieHash::NEXTKEY"
if ( items != 2)
croak("%s: expect 2 parameters", METHOD);
if (( array = SV2ZANY( perl_obj = ST(0))) == NULL)
croak("%s: (%s) is not a PHP array", METHOD, SvPV( perl_obj, na));
DEBUG("nextkey 0x%x", array);
if ( !( hash_position = hv_fetch(( HV *) SvRV( perl_obj), "__ENUM__", 8, 0)))
croak("%s: Internal inconsistency", METHOD);
hpos = ( HashPosition*) SvPV( *hash_position, na);
zend_hash_move_forward_ex( HASH_OF(array), hpos);
SP -= items;
XPUSHs( do_zenum( METHOD, array, hpos));
PUTBACK;
#undef METHOD
return;
}
XS( PHP_TieArray_EXISTS)
{
dXSARGS;
long key;
STRLEN na;
zval * array;
#define METHOD "PHP::TieArray::EXISTS"
if ( items != 2)
croak("%s: expect 2 parameters", METHOD);
if (( array = SV2ZANY( ST(0))) == NULL)
croak("%s: (%s) is not a PHP array", METHOD, SvPV(ST(0), na));
key = SvIV( ST(1));
DEBUG("exists 0x%x->[%d]", array, key);
SP -= items;
PUTBACK;
return XSRETURN_IV( zend_hash_index_exists( HASH_OF(array), key));
#undef METHOD
}
XS( PHP_TieArray_FETCH)
{
dXSARGS;
long key;
STRLEN na;
zval * array, **zobj;
SV * retsv;
#define METHOD "PHP::TieArray::FETCH"
if ( items != 2)
croak("%s: expect 2 parameters", METHOD);
if (( array = SV2ZANY( ST(0))) == NULL)
croak("%s: (%s) is not a PHP array", METHOD, SvPV(ST(0), na));
key = SvIV( ST(1));
DEBUG("fetch 0x%x->[%d]", array, key);
SP -= items;
if ( zend_hash_index_find( HASH_OF(array), key, (void**) &zobj) == FAILURE) {
XPUSHs( &PL_sv_undef);
PUTBACK;
return;
}
if ( !( retsv = zval2sv( *zobj))) {
warn("%s: value cannot be converted\n", METHOD);
retsv = &PL_sv_undef;
}
if ( retsv != &PL_sv_undef)
retsv = sv_2mortal( retsv);
XPUSHs( retsv);
#undef METHOD
PUTBACK;
return;
}
XS( PHP_TieArray_STORE)
{
dXSARGS;
long key;
STRLEN na;
zval * array, *zobj;
SV * val;
#define METHOD "PHP::TieArray::STORE"
if ( items != 3)
croak("%s: expect 3 parameters", METHOD);
if (( array = SV2ZANY( ST(0))) == NULL)
croak("%s: (%s) is not a PHP array", METHOD, SvPV(ST(0), na));
key = SvIV( ST(1));
DEBUG("store 0x%x->[%d]=%s", array, key, SvPV( ST(2), na));
MAKE_STD_ZVAL( zobj);
zobj-> type = IS_NULL;
if ( !sv2zval( val = ST(2), zobj, -1)) {
zval_ptr_dtor( &zobj);
croak("%s: scalar (%s) type=%d cannot be converted",
METHOD, SvPV( val, na), SvTYPE( val));
}
if ( zend_hash_index_update(
HASH_OF(array), key,
(void *)&zobj, sizeof(zval *), NULL
) == FAILURE) {
zval_ptr_dtor( &zobj);
croak("%s: failed", METHOD);
}
#undef METHOD
SP -= items;
PUTBACK;
XSRETURN_EMPTY;
}
XS( PHP_TieArray_DELETE)
{
dXSARGS;
long key;
STRLEN na;
zval * array;
#define METHOD "PHP::TieArray::DELETE"
if ( items != 2)
croak("%s: expect 2 parameters", METHOD);
if (( array = SV2ZANY( ST(0))) == NULL)
croak("%s: (%s) is not a PHP array", METHOD, SvPV(ST(0), na));
key = SvIV( ST(1));
DEBUG("delete 0x%x->[%d]", array, key);
SP -= items;
PUTBACK;
zend_hash_index_del( HASH_OF(array), key);
XSRETURN_EMPTY;
#undef METHOD
}
/*
Retrieve index of the last item in the array; return -1 if the array is empty .
*/
static long
array_last_index( HashTable * array)
{
int klen;
char * key;
HashPosition hp;
unsigned long numkey;
long last = -1;
zend_hash_internal_pointer_reset_ex( array, &hp);
while ( 1) {
switch( zend_hash_get_current_key_ex( array,
&key, &klen, &numkey, 0, &hp))
{
case HASH_KEY_NON_EXISTANT:
return last;
case HASH_KEY_IS_LONG:
if ( last < (long) numkey) last = numkey;
break;
}
zend_hash_move_forward_ex( array, &hp);
}
}
XS( PHP_TieArray_FETCHSIZE)
{
dXSARGS;
STRLEN na;
zval * array;
#define METHOD "PHP::TieArray::FETCHSIZE"
if ( items != 1)
croak("%s: expect 1 parameter", METHOD);
if (( array = SV2ZANY( ST(0))) == NULL)
croak("%s: (%s) is not a PHP array", METHOD, SvPV(ST(0), na));
DEBUG("fetchsize 0x%x", array);
SP -= items;
PUTBACK;
XSRETURN_IV( 1 + array_last_index( HASH_OF(array)));
#undef METHOD
}
XS( PHP_TieArray_PUSH)
{
dXSARGS;
STRLEN na;
long i, pos;
zval * array, * zobj;
SV * val;
#define METHOD "PHP::TieArray::PUSH"
if ( items < 1)
croak("%s: expect at least 1 parameter", METHOD);
if (( array = SV2ZANY( ST(0))) == NULL)
croak("%s: (%s) is not a PHP array", METHOD, SvPV(ST(0), na));
pos = array_last_index( HASH_OF(array));
DEBUG("push 0x%x %d items after %d", array, items - 1, pos);
for ( i = 1; i < items; i++) {
DEBUG("push %s", SvPV( ST(i), na));
MAKE_STD_ZVAL( zobj);
zobj-> type = IS_NULL;
if ( !sv2zval( val = ST(i), zobj, -1)) {
zval_ptr_dtor( &zobj);
croak("%s: scalar (%s) type=%d cannot be converted",
METHOD, SvPV( val, na), SvTYPE( val));
}
if ( zend_hash_index_update(
HASH_OF(array), pos + i,
(void *)&zobj, sizeof(zval *), NULL
) == FAILURE) {
zval_ptr_dtor( &zobj);
croak("%s: failed", METHOD);
}
}
SP -= items;
PUTBACK;
XSRETURN_IV( pos + items);
#undef METHOD
}
XS( PHP_TieArray_POP)
{
dXSARGS;
STRLEN na;
zval * array, **zobj;
SV * retsv;
long pos;
#define METHOD "PHP::TieArray::POP"
if ( items != 1)
croak("%s: expect 1 parameters", METHOD);
if (( array = SV2ZANY( ST(0))) == NULL)
croak("%s: (%s) is not a PHP array", METHOD, SvPV(ST(0), na));
pos = array_last_index( HASH_OF(array));
DEBUG("pop 0x%x at %d", array, pos);
SP -= items;
if ( pos == -1) { /* empty array */
XPUSHs( &PL_sv_undef);
PUTBACK;
return;
}
if ( zend_hash_index_find( HASH_OF(array), pos, (void**) &zobj) == FAILURE) {
XPUSHs( &PL_sv_undef);
PUTBACK;
return;
}
if ( !( retsv = zval2sv( *zobj))) {
warn("%s: value cannot be converted\n", METHOD);
retsv = &PL_sv_undef;
}
zend_hash_index_del( HASH_OF(array), pos);
if ( retsv != &PL_sv_undef)
retsv = sv_2mortal( retsv);
XPUSHs( retsv);
#undef METHOD
PUTBACK;
return;
}
void
register_PHP_Array()
{
newXS( "PHP::ArrayHandle::new", PHP_ArrayHandle_new, "PHP::ArrayHandle");
newXS( "PHP::TieHash::EXISTS", PHP_TieHash_EXISTS, "PHP::TieHash");
newXS( "PHP::TieHash::FETCH", PHP_TieHash_FETCH, "PHP::TieHash");
newXS( "PHP::TieHash::STORE", PHP_TieHash_STORE, "PHP::TieHash");
newXS( "PHP::TieHash::DELETE", PHP_TieHash_DELETE, "PHP::TieHash");
newXS( "PHP::TieHash::CLEAR", PHP_TieHash_CLEAR, "PHP::TieHash");
newXS( "PHP::TieHash::FIRSTKEY",PHP_TieHash_FIRSTKEY, "PHP::TieHash");
newXS( "PHP::TieHash::NEXTKEY", PHP_TieHash_NEXTKEY, "PHP::TieHash");
newXS( "PHP::TieArray::FETCHSIZE",PHP_TieArray_FETCHSIZE,"PHP::TieArray");
newXS( "PHP::TieArray::EXISTS", PHP_TieArray_EXISTS, "PHP::TieArray");
newXS( "PHP::TieArray::FETCH", PHP_TieArray_FETCH, "PHP::TieArray");
newXS( "PHP::TieArray::STORE", PHP_TieArray_STORE, "PHP::TieArray");
newXS( "PHP::TieArray::DELETE", PHP_TieArray_DELETE, "PHP::TieArray");
newXS( "PHP::TieArray::CLEAR", PHP_TieHash_CLEAR, "PHP::TieArray");
newXS( "PHP::TieArray::PUSH", PHP_TieArray_PUSH, "PHP::TieArray");
newXS( "PHP::TieArray::POP", PHP_TieArray_POP, "PHP::TieArray");
}
#ifdef __cplusplus
}
#endif