From 93d687aa9391e333498bd219fb17ce623d3c51f7 Mon Sep 17 00:00:00 2001 From: Brett Lawson Date: Wed, 30 Mar 2016 21:06:57 -0700 Subject: [PATCH] PCBC-360, PCBC-376: Major refactor for PHP 7 and reentrant transcoders. This includes major changes to most components to support PHP 7. This also includes a major change to the operation handling flow to correct issues when callbacks (ex: transcoders) call back into the library. Change-Id: I90c68f250863bf723b8d6327d8a77c2e2e7fa3ae --- bucket.c | 1290 +++++++++++++++++++++++++++--------------- bucket.h | 14 +- cas.c | 29 +- cas.h | 6 +- cluster.c | 152 +++-- cluster.h | 11 +- config.m4 | 1 + config.w32 | 1 + couchbase.c | 17 +- couchbase.h | 2 - exception.c | 66 ++- exception.h | 20 +- metadoc.c | 73 +-- metadoc.h | 15 +- opcookie.c | 49 ++ opcookie.h | 29 + package2.xml | 2 +- paramparser.h | 128 +++-- phphelpers.h | 16 - tests/BucketTest.php | 66 ++- transcoding.c | 110 ++-- transcoding.h | 9 +- zap.h | 480 ++++++++++++++++ 23 files changed, 1788 insertions(+), 798 deletions(-) create mode 100644 opcookie.c create mode 100644 opcookie.h delete mode 100644 phphelpers.h create mode 100644 zap.h diff --git a/bucket.c b/bucket.c index 05c9dd6..7223baf 100644 --- a/bucket.c +++ b/bucket.c @@ -3,275 +3,490 @@ #include "exception.h" #include "datainfo.h" #include "paramparser.h" -#include "phphelpers.h" +#include "zap.h" #include "bucket.h" #include "cas.h" #include "metadoc.h" #include "transcoding.h" +#include "opcookie.h" -#define PCBC_CHECK_ZVAL(v,t,m) \ +#define _PCBC_CHECK_ZVAL(v,t,m) \ if (v && Z_TYPE_P(v) != t) { \ throw_pcbc_exception(m, LCB_EINVAL); \ RETURN_NULL(); \ } +#define PCBC_CHECK_ZVAL_STRING(v, m) \ + _PCBC_CHECK_ZVAL(v, IS_STRING, m) +#define PCBC_CHECK_ZVAL_LONG(v, m) \ + _PCBC_CHECK_ZVAL(v, IS_LONG, m) +#define PCBC_CHECK_ZVAL_CAS(v, m) \ + _PCBC_CHECK_ZVAL(v, IS_RESOURCE, m) +#define PCBC_CHECK_ZVAL_BOOL(v, m) \ + if (v && zap_zval_is_bool(v)) { \ + throw_pcbc_exception(m, LCB_EINVAL); \ + RETURN_NULL(); \ + } -typedef struct { - int mapped; - int remaining; - zval *retval; - bucket_object *owner; -} bopcookie; - -bopcookie * bopcookie_init(bucket_object *bucketobj, zval *return_value, int ismapped) { - bopcookie *cookie = emalloc(sizeof(bopcookie)); - cookie->owner = bucketobj; - cookie->retval = return_value; - if (ismapped) { - array_init(cookie->retval); - } else { - ZVAL_NULL(cookie->retval); - } - return cookie; -} +#define PHP_THISOBJ() zap_fetch_this(bucket_object) -void bopcookie_destroy(bopcookie *cookie) { - efree(cookie); +zap_class_entry bucket_class; +zend_class_entry *bucket_ce; + +zap_FREEOBJ_FUNC(bucket_free_storage) +{ + bucket_object *obj = zap_get_object(bucket_object, object); + + zapval_destroy(obj->encoder); + zapval_destroy(obj->decoder); + zapval_destroy(obj->prefix); + + zend_object_std_dtor(&obj->std TSRMLS_CC); + zap_free_object_storage(obj); } -zval * bopcookie_get_doc(const bopcookie *cookie, const char *key, uint key_len) { - // Maximum server keylength is currently 250 - static char tmpstr[251]; +zap_CREATEOBJ_FUNC(bucket_create_handler) +{ + bucket_object *obj = zap_alloc_object_storage(bucket_object, type); + + zend_object_std_init(&obj->std, type TSRMLS_CC); + zap_object_properties_init(&obj->std, type); - zval *doc; - if (Z_TYPE_P(cookie->retval) == IS_ARRAY && key != NULL) { - MAKE_STD_ZVAL(doc); - ZVAL_NULL(doc); + zapval_alloc_empty_string(obj->encoder); + zapval_alloc_empty_string(obj->decoder); + zapval_alloc_empty_string(obj->prefix); - memcpy(tmpstr, key, key_len); - tmpstr[key_len] = '\0'; - add_assoc_zval_ex(cookie->retval, tmpstr, key_len + 1, doc); - } else { - doc = cookie->retval; - } - return doc; + obj->conn = NULL; + + return zap_finalize_object(obj, &bucket_class); } -void bopcookie_error(const bopcookie *cookie, bucket_object *data, zval *doc, - lcb_error_t error TSRMLS_DC) { - zval *zerror = create_lcb_exception(error TSRMLS_CC); - if (Z_TYPE_P(cookie->retval) == IS_ARRAY) { - metadoc_from_error(doc, zerror TSRMLS_CC); - zval_ptr_dtor(&zerror); - } else { - data->error = zerror; - } +static zval * bop_get_return_doc(zval *return_value, zapval *key, int is_mapped) +{ + zval *doc = return_value; + if (is_mapped) { + if (!zap_zval_is_array(return_value)) { + array_init(return_value); + } + { + char tmpstr[251]; + HashTable *htretval = Z_ARRVAL_P(return_value); + uint key_len = zapval_strlen_p(key); + zapval new_doc; + zapval_alloc_null(new_doc); + + memcpy(tmpstr, zapval_strval_p(key), key_len); + tmpstr[key_len] = '\0'; + + doc = zap_hash_str_add( + htretval, tmpstr, key_len, zapval_zvalptr(new_doc)); + } + } + return doc; } -zend_object_handlers bucket_handlers; +typedef struct { + opcookie_res header; + zapval key; + zapval bytes; + zapval flags; + zapval datatype; + zapval cas; +} opcookie_get_res; -void bucket_free_storage(void *object TSRMLS_DC) +static void get_callback(lcb_t instance, const void *cookie, lcb_error_t error, + const lcb_get_resp_t *resp) { - bucket_object *obj = (bucket_object *)object; + opcookie_get_res *result = ecalloc(1, sizeof(opcookie_get_res)); + TSRMLS_FETCH(); - zend_hash_destroy(obj->std.properties); - FREE_HASHTABLE(obj->std.properties); + result->header.err = error; + zapval_alloc_stringl( + result->key, resp->v.v0.key, resp->v.v0.nkey); + zapval_alloc_stringl( + result->bytes, resp->v.v0.bytes, resp->v.v0.nbytes); + zapval_alloc_long(result->flags,resp->v.v0.flags); + zapval_alloc_long(result->datatype, resp->v.v0.datatype); + alloc_cas(result->cas, resp->v.v0.cas TSRMLS_CC); - zval_ptr_dtor(&obj->encoder); - zval_ptr_dtor(&obj->decoder); - zval_ptr_dtor(&obj->prefix); + opcookie_push((opcookie*)cookie, &result->header); +} + +static lcb_error_t proc_get_results(bucket_object *bucket, zval *return_value, + opcookie *cookie, int is_mapped TSRMLS_DC) +{ + opcookie_get_res *res; + lcb_error_t err = LCB_SUCCESS; + + // If we are not mapped, we need to throw any op errors + if (is_mapped == 0) { + err = opcookie_get_first_error(cookie); + } + + if (err == LCB_SUCCESS) { + FOREACH_OPCOOKIE_RES(opcookie_get_res, res, cookie) { + zval *doc = bop_get_return_doc( + return_value, &res->key, is_mapped); + + if (res->header.err == LCB_SUCCESS) { + zapval value; + zapval_alloc_null(value); + pcbc_decode_value(bucket, + &value, &res->bytes, &res->flags, &res->datatype TSRMLS_CC); + + make_metadoc(doc, &value, &res->flags, &res->cas TSRMLS_CC); + zapval_destroy(value); + } else { + make_metadoc_error(doc, res->header.err TSRMLS_CC); + } + } + } - efree(obj); + FOREACH_OPCOOKIE_RES(opcookie_get_res, res, cookie) { + zapval_destroy(res->key); + zapval_destroy(res->bytes); + zapval_destroy(res->flags); + zapval_destroy(res->datatype); + zapval_destroy(res->cas); + } + + return err; } -zend_object_value bucket_create_handler(zend_class_entry *type TSRMLS_DC) +typedef struct { + opcookie_res header; + zapval key; +} opcookie_unlock_res; + +static void unlock_callback(lcb_t instance, const void *cookie, + lcb_error_t error, const lcb_unlock_resp_t *resp) { - zend_object_value retval; - - bucket_object *obj = (bucket_object *)emalloc(sizeof(bucket_object)); - memset(obj, 0, sizeof(bucket_object)); - obj->std.ce = type; - obj->conn = NULL; - - MAKE_STD_ZVAL(obj->encoder); - ZVAL_EMPTY_STRING(obj->encoder); - MAKE_STD_ZVAL(obj->decoder); - ZVAL_EMPTY_STRING(obj->decoder); - MAKE_STD_ZVAL(obj->prefix); - ZVAL_EMPTY_STRING(obj->prefix); - - ALLOC_HASHTABLE(obj->std.properties); - zend_hash_init(obj->std.properties, 0, NULL, ZVAL_PTR_DTOR, 0); - phlp_object_properties_init(&obj->std, type); - - retval.handle = zend_objects_store_put(obj, - (zend_objects_store_dtor_t)zend_objects_destroy_object, - (zend_objects_free_object_storage_t)bucket_free_storage, - NULL TSRMLS_CC); - retval.handlers = &bucket_handlers; - - return retval; + opcookie_unlock_res *result = ecalloc(1, sizeof(opcookie_unlock_res)); + TSRMLS_FETCH(); + + result->header.err = error; + zapval_alloc_stringl( + result->key, resp->v.v0.key, resp->v.v0.nkey); + + opcookie_push((opcookie*)cookie, &result->header); } -static void get_callback(lcb_t instance, const void *cookie, lcb_error_t error, - const lcb_get_resp_t *resp) +static lcb_error_t proc_unlock_results(bucket_object *bucket, zval *return_value, + opcookie *cookie, int is_mapped TSRMLS_DC) { - bopcookie *op = (bopcookie*)cookie; - bucket_object *data = op->owner; - zval *doc = bopcookie_get_doc(cookie, resp->v.v0.key, resp->v.v0.nkey); - TSRMLS_FETCH(); - - if (error == LCB_SUCCESS) { - if (metadoc_from_bytes(data, doc, resp->v.v0.bytes, resp->v.v0.nbytes, - resp->v.v0.cas, resp->v.v0.flags, resp->v.v0.datatype TSRMLS_CC) == FAILURE) { - bopcookie_error(cookie, data, doc, LCB_ERROR TSRMLS_CC); - return; - } - } else { - bopcookie_error(cookie, data, doc, error TSRMLS_CC); - } + opcookie_unlock_res *res; + lcb_error_t err = LCB_SUCCESS; + + // If we are not mapped, we need to throw any op errors + if (is_mapped == 0) { + err = opcookie_get_first_error(cookie); + } + + if (err == LCB_SUCCESS) { + FOREACH_OPCOOKIE_RES(opcookie_unlock_res, res, cookie) { + zval *doc = bop_get_return_doc( + return_value, &res->key, is_mapped); + + if (res->header.err == LCB_SUCCESS) { + make_metadoc(doc, NULL, NULL, NULL TSRMLS_CC); + } else { + make_metadoc_error(doc, res->header.err TSRMLS_CC); + } + } + } + + FOREACH_OPCOOKIE_RES(opcookie_unlock_res, res, cookie) { + zapval_destroy(res->key); + } + + return err; } +typedef struct { + opcookie_res header; + zapval key; + zapval cas; +} opcookie_store_res; static void store_callback(lcb_t instance, const void *cookie, lcb_storage_t operation, lcb_error_t error, const lcb_store_resp_t *resp) { - bopcookie *op = (bopcookie*)cookie; - bucket_object *data = op->owner; - zval *doc = bopcookie_get_doc(cookie, resp->v.v0.key, resp->v.v0.nkey); - TSRMLS_FETCH(); - - if (error == LCB_SUCCESS) { - if (metadoc_from_bytes(data, doc, NULL, 0, resp->v.v0.cas, 0, 0 TSRMLS_CC) == FAILURE) { - bopcookie_error(cookie, data, doc, LCB_ERROR TSRMLS_CC); - return; - } - } else { - bopcookie_error(cookie, data, doc, error TSRMLS_CC); - } + opcookie_store_res *result = ecalloc(1, sizeof(opcookie_store_res)); + TSRMLS_FETCH(); + + result->header.err = error; + zapval_alloc_stringl( + result->key, resp->v.v0.key, resp->v.v0.nkey); + alloc_cas(result->cas, resp->v.v0.cas TSRMLS_CC); + + opcookie_push((opcookie*)cookie, &result->header); } +static void remove_callback(lcb_t instance, const void *cookie, + lcb_error_t error, const lcb_remove_resp_t *resp) { + opcookie_store_res *result = ecalloc(1, sizeof(opcookie_store_res)); + TSRMLS_FETCH(); + + result->header.err = error; + zapval_alloc_stringl( + result->key, resp->v.v0.key, resp->v.v0.nkey); + alloc_cas(result->cas, resp->v.v0.cas TSRMLS_CC); + + opcookie_push((opcookie*)cookie, &result->header); +} + +static void touch_callback(lcb_t instance, const void *cookie, + lcb_error_t error, const lcb_touch_resp_t *resp) { + opcookie_store_res *result = ecalloc(1, sizeof(opcookie_store_res)); + TSRMLS_FETCH(); + + result->header.err = error; + zapval_alloc_stringl( + result->key, resp->v.v0.key, resp->v.v0.nkey); + alloc_cas(result->cas, resp->v.v0.cas TSRMLS_CC); + + opcookie_push((opcookie*)cookie, &result->header); +} + +static lcb_error_t proc_store_results(bucket_object *bucket, zval *return_value, + opcookie *cookie, int is_mapped TSRMLS_DC) +{ + opcookie_store_res *res; + lcb_error_t err = LCB_SUCCESS; + + // If we are not mapped, we need to throw any op errors + if (is_mapped == 0) { + err = opcookie_get_first_error(cookie); + } + + if (err == LCB_SUCCESS) { + FOREACH_OPCOOKIE_RES(opcookie_store_res, res, cookie) { + zval *doc = bop_get_return_doc( + return_value, &res->key, is_mapped); + + if (res->header.err == LCB_SUCCESS) { + make_metadoc(doc, NULL, NULL, &res->cas TSRMLS_CC); + } else { + make_metadoc_error(doc, res->header.err TSRMLS_CC); + } + } + } + + FOREACH_OPCOOKIE_RES(opcookie_store_res, res, cookie) { + zapval_destroy(res->key); + zapval_destroy(res->cas); + } + + return err; +} +#define proc_remove_results proc_store_results +#define proc_touch_results proc_store_results + +typedef struct { + opcookie_res header; + zapval key; + zapval value; + zapval cas; +} opcookie_arithmetic_res; + static void arithmetic_callback(lcb_t instance, const void *cookie, lcb_error_t error, const lcb_arithmetic_resp_t *resp) { - bopcookie *op = (bopcookie*)cookie; - bucket_object *data = op->owner; - zval *doc = bopcookie_get_doc(cookie, resp->v.v0.key, resp->v.v0.nkey); - TSRMLS_FETCH(); - - if (error == LCB_SUCCESS) { - if (metadoc_from_long(doc, resp->v.v0.value, resp->v.v0.cas, 0, 0 TSRMLS_CC) == FAILURE) { - bopcookie_error(cookie, data, doc, LCB_ERROR TSRMLS_CC); - return; - } - } else { - bopcookie_error(cookie, data, doc, error TSRMLS_CC); - } + opcookie_arithmetic_res *result = ecalloc(1, sizeof(opcookie_arithmetic_res)); + TSRMLS_FETCH(); + + result->header.err = error; + zapval_alloc_stringl( + result->key, resp->v.v0.key, resp->v.v0.nkey); + zapval_alloc_long(result->value, resp->v.v0.value); + alloc_cas(result->cas, resp->v.v0.cas TSRMLS_CC); + + opcookie_push((opcookie*)cookie, &result->header); } -static void remove_callback(lcb_t instance, const void *cookie, - lcb_error_t error, const lcb_remove_resp_t *resp) { - bopcookie *op = (bopcookie*)cookie; - bucket_object *data = op->owner; - zval *doc = bopcookie_get_doc(cookie, resp->v.v0.key, resp->v.v0.nkey); - TSRMLS_FETCH(); - - if (error == LCB_SUCCESS) { - if (metadoc_create(doc, NULL, resp->v.v0.cas, 0, 0 TSRMLS_CC) == FAILURE) { - bopcookie_error(cookie, data, doc, LCB_ERROR TSRMLS_CC); - return; - } - } else { - bopcookie_error(cookie, data, doc, error TSRMLS_CC); - } +static lcb_error_t proc_arithmetic_results(bucket_object *bucket, zval *return_value, + opcookie *cookie, int is_mapped TSRMLS_DC) +{ + opcookie_arithmetic_res *res; + lcb_error_t err = LCB_SUCCESS; + + // If we are not mapped, we need to throw any op errors + if (is_mapped == 0) { + err = opcookie_get_first_error(cookie); + } + + if (err == LCB_SUCCESS) { + FOREACH_OPCOOKIE_RES(opcookie_arithmetic_res, res, cookie) { + zval *doc = bop_get_return_doc( + return_value, &res->key, is_mapped); + + if (res->header.err == LCB_SUCCESS) { + make_metadoc(doc, &res->value, NULL, &res->cas TSRMLS_CC); + } else { + make_metadoc_error(doc, res->header.err TSRMLS_CC); + } + } + } + + FOREACH_OPCOOKIE_RES(opcookie_arithmetic_res, res, cookie) { + zapval_destroy(res->key); + zapval_destroy(res->value); + zapval_destroy(res->cas); + } + + return err; } -static void touch_callback(lcb_t instance, const void *cookie, - lcb_error_t error, const lcb_touch_resp_t *resp) { - bopcookie *op = (bopcookie*)cookie; - bucket_object *data = op->owner; - zval *doc = bopcookie_get_doc(cookie, resp->v.v0.key, resp->v.v0.nkey); - TSRMLS_FETCH(); - - if (error == LCB_SUCCESS) { - if (metadoc_create(doc, NULL, resp->v.v0.cas, 0, 0 TSRMLS_CC) == FAILURE) { - bopcookie_error(cookie, data, doc, LCB_ERROR TSRMLS_CC); - return; - } - } else { - bopcookie_error(cookie, data, doc, error TSRMLS_CC); - } +typedef struct { + opcookie_res header; + zapval key; +} opcookie_durability_res; + +static void durability_callback(lcb_t instance, const void *cookie, + lcb_error_t error, const lcb_durability_resp_t *resp) { + opcookie_durability_res *result = ecalloc(1, sizeof(opcookie_durability_res)); + TSRMLS_FETCH(); + + result->header.err = error; + zapval_alloc_stringl( + result->key, resp->v.v0.key, resp->v.v0.nkey); + + opcookie_push((opcookie*)cookie, &result->header); + +} + +static lcb_error_t proc_durability_results(bucket_object *bucket, zval *return_value, + opcookie *cookie, int is_mapped TSRMLS_DC) +{ + opcookie_durability_res *res; + lcb_error_t err = LCB_SUCCESS; + + // If we are not mapped, we need to throw any op errors + if (is_mapped == 0) { + err = opcookie_get_first_error(cookie); + } + + if (err == LCB_SUCCESS) { + FOREACH_OPCOOKIE_RES(opcookie_durability_res, res, cookie) { + zval *doc = bop_get_return_doc( + return_value, &res->key, is_mapped); + + if (res->header.err == LCB_SUCCESS) { + make_metadoc(doc, NULL, NULL, NULL TSRMLS_CC); + } else { + make_metadoc_error(doc, res->header.err TSRMLS_CC); + } + } + } + + FOREACH_OPCOOKIE_RES(opcookie_durability_res, res, cookie) { + zapval_destroy(res->key); + } + + return err; } +typedef struct { + opcookie_res header; + lcb_U16 rflags; + zapval row; +} opcookie_n1qlrow_res; + static void n1qlrow_callback(lcb_t instance, int ignoreme, const lcb_RESPN1QL *resp) { - bopcookie *op = (bopcookie*)resp->cookie; - bucket_object *data = op->owner; - zval *doc = bopcookie_get_doc(op, NULL, 0); - zval **results, *rowdata; + opcookie_n1qlrow_res *result = ecalloc(1, sizeof(opcookie_n1qlrow_res)); TSRMLS_FETCH(); - if (resp->rflags & LCB_RESP_F_FINAL) - { - MAKE_STD_ZVAL(rowdata); - ZVAL_STRINGL(rowdata, resp->row, resp->nrow, 1); + result->header.err = resp->rc; + result->rflags = resp->rflags; + zapval_alloc_stringl( + result->row, resp->row, resp->nrow); + + opcookie_push((opcookie*)resp->cookie, &result->header); +} - add_assoc_zval(doc, "meta", rowdata); - return; +static lcb_error_t proc_n1qlrow_results(bucket_object *bucket, zval *return_value, + opcookie *cookie TSRMLS_DC) +{ + opcookie_n1qlrow_res *res; + lcb_error_t err = LCB_SUCCESS; + + // Any error should cause everything to fail... for now? + err = opcookie_get_first_error(cookie); + + if (err == LCB_SUCCESS) { + zval *results_array = NULL; + zapval zResults; + zapval_alloc_array(zResults); + array_init(return_value); + results_array = zap_hash_str_add(Z_ARRVAL_P(return_value), "results", 7, + zapval_zvalptr(zResults)); + + FOREACH_OPCOOKIE_RES(opcookie_n1qlrow_res, res, cookie) { + if (res->rflags & LCB_RESP_F_FINAL) { + zap_hash_str_add(Z_ARRVAL_P(return_value), "meta", 4, + zapval_zvalptr(res->row)); + zapval_addref(res->row); + } else { + zap_hash_next_index_insert( + Z_ARRVAL_P(results_array), zapval_zvalptr(res->row)); + zapval_addref(res->row); + } + } } - zend_hash_find(Z_ARRVAL_P(doc), "results", 8, (void**)&results); + FOREACH_OPCOOKIE_RES(opcookie_n1qlrow_res, res, cookie) { + zapval_destroy(res->row); + } - MAKE_STD_ZVAL(rowdata); - ZVAL_STRINGL(rowdata, resp->row, resp->nrow, 1); - add_next_index_zval(*results, rowdata); + return err; } +typedef struct { + opcookie_res header; + zapval bytes; +} opcookie_http_res; + static void http_complete_callback(lcb_http_request_t request, lcb_t instance, const void *cookie, lcb_error_t error, const lcb_http_resp_t *resp) { - bopcookie *op = (bopcookie*)cookie; - bucket_object *data = op->owner; - zval *doc = bopcookie_get_doc(cookie, NULL, 0); - TSRMLS_FETCH(); + opcookie_http_res *result = ecalloc(1, sizeof(opcookie_http_res)); + TSRMLS_FETCH(); - if (error == LCB_SUCCESS) { - ZVAL_STRINGL(doc, resp->v.v0.bytes, resp->v.v0.nbytes, 1); - } else { - bopcookie_error(cookie, data, NULL, error TSRMLS_CC); - } -} + result->header.err = error; + zapval_alloc_stringl( + result->bytes, resp->v.v0.bytes, resp->v.v0.nbytes); -static void durability_callback(lcb_t instance, const void *cookie, - lcb_error_t error, const lcb_durability_resp_t *resp) { - bopcookie *op = (bopcookie*)cookie; - bucket_object *data = op->owner; - zval *doc = bopcookie_get_doc(cookie, resp->v.v0.key, resp->v.v0.nkey); - TSRMLS_FETCH(); - - if (error == LCB_SUCCESS) { - ZVAL_TRUE(doc); - } else { - bopcookie_error(cookie, data, doc, error TSRMLS_CC); - } + opcookie_push((opcookie*)cookie, &result->header); } -static int pcbc_wait(bucket_object *obj TSRMLS_DC) +static lcb_error_t proc_http_results(bucket_object *bucket, zval *return_value, + opcookie *cookie TSRMLS_DC) { - lcb_t instance = obj->conn->lcb; - obj->error = NULL; - - lcb_wait(instance); + opcookie_http_res *res; + lcb_error_t err = LCB_SUCCESS; + + // Any error should cause everything to fail... for now? + err = opcookie_get_first_error(cookie); + + if (err == LCB_SUCCESS) { + int has_value = 0; + FOREACH_OPCOOKIE_RES(opcookie_http_res, res, cookie) { + if (has_value == 0) { + zap_zval_zval_p(return_value, zapval_zvalptr(res->bytes), 1, 0); + has_value = 1; + } else { + err = LCB_ERROR; + break; + } + } + } - if (obj->error) { - zend_throw_exception_object(obj->error TSRMLS_CC); - obj->error = NULL; - return 0; - } + FOREACH_OPCOOKIE_RES(opcookie_http_res, res, cookie) { + zapval_destroy(res->bytes); + } - return 1; + return err; } -zend_class_entry *bucket_ce; - PHP_METHOD(Bucket, __construct) { bucket_object *data = PHP_THISOBJ(); @@ -292,6 +507,16 @@ PHP_METHOD(Bucket, __construct) RETURN_NULL(); } + if (zap_zval_is_undef(zdsn)) { + zdsn = NULL; + } + if (zap_zval_is_undef(zname)) { + zname = NULL; + } + if (zap_zval_is_undef(zpassword)) { + zpassword = NULL; + } + if (zdsn) { if (Z_TYPE_P(zdsn) == IS_STRING) { dsn = estrndup(Z_STRVAL_P(zdsn), Z_STRLEN_P(zdsn)); @@ -356,6 +581,7 @@ PHP_METHOD(Bucket, __construct) } lcb_set_get_callback(instance, get_callback); + lcb_set_unlock_callback(instance, unlock_callback); lcb_set_store_callback(instance, store_callback); lcb_set_arithmetic_callback(instance, arithmetic_callback); lcb_set_remove_callback(instance, remove_callback); @@ -406,8 +632,6 @@ PHP_METHOD(Bucket, __construct) efree(connkey); } - - // insert($id, $doc {, $expiry, $groupid}) : MetaDoc PHP_METHOD(Bucket, insert) { @@ -416,13 +640,15 @@ PHP_METHOD(Bucket, insert) lcb_store_cmd_t **cmds = NULL; int ii, num_cmds; pcbc_pp_state pp_state; - zval *zid, *zvalue, *zexpiry, *zflags, *zgroupid; - bopcookie *cookie; + pcbc_pp_id id; + zval *zvalue, *zexpiry, *zflags, *zgroupid; + opcookie *cookie; + lcb_error_t err; // Note that groupid is experimental here and should not be used. if (pcbc_pp_begin(ZEND_NUM_ARGS() TSRMLS_CC, &pp_state, "id|value|expiry,flags,groupid", - &zid, &zvalue, &zexpiry, &zflags, &zgroupid) != SUCCESS) + &id, &zvalue, &zexpiry, &zflags, &zgroupid) != SUCCESS) { throw_pcbc_exception("Invalid arguments.", LCB_EINVAL); RETURN_NULL(); @@ -434,17 +660,16 @@ PHP_METHOD(Bucket, insert) memset(cmd, 0, sizeof(lcb_store_cmd_t) * num_cmds); for (ii = 0; pcbc_pp_next(&pp_state); ++ii) { - PCBC_CHECK_ZVAL(zid, IS_STRING, "id must be a string"); - PCBC_CHECK_ZVAL(zexpiry, IS_LONG, "expiry must be an integer"); - PCBC_CHECK_ZVAL(zflags, IS_LONG, "flags must be an integer"); - PCBC_CHECK_ZVAL(zgroupid, IS_STRING, "groupid must be a string"); + PCBC_CHECK_ZVAL_LONG(zexpiry, "expiry must be an integer"); + PCBC_CHECK_ZVAL_LONG(zflags, "flags must be an integer"); + PCBC_CHECK_ZVAL_STRING(zgroupid, "groupid must be a string"); cmd[ii].version = 0; cmd[ii].v.v0.operation = LCB_ADD; - cmd[ii].v.v0.key = Z_STRVAL_P(zid); - cmd[ii].v.v0.nkey = Z_STRLEN_P(zid); + cmd[ii].v.v0.key = id.str; + cmd[ii].v.v0.nkey = id.len; - pcbc_zval_to_bytes(data, zvalue, &cmd[ii].v.v0.bytes, &cmd[ii].v.v0.nbytes, + pcbc_encode_value(data, zvalue, &cmd[ii].v.v0.bytes, &cmd[ii].v.v0.nbytes, &cmd[ii].v.v0.flags, &cmd[ii].v.v0.datatype TSRMLS_CC); if (zexpiry) { @@ -461,18 +686,28 @@ PHP_METHOD(Bucket, insert) cmds[ii] = &cmd[ii]; } - cookie = bopcookie_init(data, return_value, pcbc_pp_ismapped(&pp_state)); + cookie = opcookie_init(); - lcb_store(data->conn->lcb, cookie, + err = lcb_store(data->conn->lcb, cookie, num_cmds, (const lcb_store_cmd_t*const*)cmds); - pcbc_wait(data TSRMLS_CC); - bopcookie_destroy(cookie); + if (err == LCB_SUCCESS) { + lcb_wait(data->conn->lcb); + + err = proc_store_results(data, return_value, + cookie, pcbc_pp_ismapped(&pp_state) TSRMLS_CC); + } + + opcookie_destroy(cookie); for (ii = 0; ii < num_cmds; ++ii) { efree((void*)cmds[ii]->v.v0.bytes); } - efree(cmds); - efree(cmd); + efree(cmds); + efree(cmd); + + if (err != LCB_SUCCESS) { + throw_lcb_exception(err); + } } // upsert($id, $doc {, $expiry, $groupid}) : MetaDoc @@ -483,13 +718,15 @@ PHP_METHOD(Bucket, upsert) lcb_store_cmd_t **cmds = NULL; int ii, num_cmds; pcbc_pp_state pp_state; - zval *zid, *zvalue, *zexpiry, *zflags, *zgroupid; - bopcookie *cookie; + zval *zvalue, *zexpiry, *zflags, *zgroupid; + pcbc_pp_id id; + opcookie *cookie; + lcb_error_t err; // Note that groupid is experimental here and should not be used. if (pcbc_pp_begin(ZEND_NUM_ARGS() TSRMLS_CC, &pp_state, "id|value|expiry,flags,groupid", - &zid, &zvalue, &zexpiry, &zflags, &zgroupid) != SUCCESS) + &id, &zvalue, &zexpiry, &zflags, &zgroupid) != SUCCESS) { throw_pcbc_exception("Invalid arguments.", LCB_EINVAL); RETURN_NULL(); @@ -501,17 +738,16 @@ PHP_METHOD(Bucket, upsert) memset(cmd, 0, sizeof(lcb_store_cmd_t) * num_cmds); for (ii = 0; pcbc_pp_next(&pp_state); ++ii) { - PCBC_CHECK_ZVAL(zid, IS_STRING, "id must be a string"); - PCBC_CHECK_ZVAL(zexpiry, IS_LONG, "expiry must be an integer"); - PCBC_CHECK_ZVAL(zflags, IS_LONG, "flags must be an integer"); - PCBC_CHECK_ZVAL(zgroupid, IS_STRING, "groupid must be a string"); + PCBC_CHECK_ZVAL_LONG(zexpiry, "expiry must be an integer"); + PCBC_CHECK_ZVAL_LONG(zflags, "flags must be an integer"); + PCBC_CHECK_ZVAL_STRING(zgroupid, "groupid must be a string"); cmd[ii].version = 0; cmd[ii].v.v0.operation = LCB_SET; - cmd[ii].v.v0.key = Z_STRVAL_P(zid); - cmd[ii].v.v0.nkey = Z_STRLEN_P(zid); + cmd[ii].v.v0.key = id.str; + cmd[ii].v.v0.nkey = id.len; - pcbc_zval_to_bytes(data, zvalue, &cmd[ii].v.v0.bytes, &cmd[ii].v.v0.nbytes, + pcbc_encode_value(data, zvalue, &cmd[ii].v.v0.bytes, &cmd[ii].v.v0.nbytes, &cmd[ii].v.v0.flags, &cmd[ii].v.v0.datatype TSRMLS_CC); if (zexpiry) { @@ -528,18 +764,28 @@ PHP_METHOD(Bucket, upsert) cmds[ii] = &cmd[ii]; } - cookie = bopcookie_init(data, return_value, pcbc_pp_ismapped(&pp_state)); + cookie = opcookie_init(); - lcb_store(data->conn->lcb, cookie, + err = lcb_store(data->conn->lcb, cookie, num_cmds, (const lcb_store_cmd_t*const*)cmds); - pcbc_wait(data TSRMLS_CC); - bopcookie_destroy(cookie); - for (ii = 0; ii < num_cmds; ++ii) { - efree((void*)cmds[ii]->v.v0.bytes); - } - efree(cmds); - efree(cmd); + if (err == LCB_SUCCESS) { + lcb_wait(data->conn->lcb); + + err = proc_store_results(data, return_value, + cookie, pcbc_pp_ismapped(&pp_state) TSRMLS_CC); + } + + opcookie_destroy(cookie); + for (ii = 0; ii < num_cmds; ++ii) { + efree((void*)cmds[ii]->v.v0.bytes); + } + efree(cmds); + efree(cmd); + + if (err != LCB_SUCCESS) { + throw_lcb_exception(err); + } } // save($id, $doc {, $cas, $expiry, $groupid}) : MetaDoc @@ -550,13 +796,15 @@ PHP_METHOD(Bucket, replace) lcb_store_cmd_t **cmds = NULL; int ii, num_cmds; pcbc_pp_state pp_state; - zval *zid, *zvalue, *zcas, *zexpiry, *zflags, *zgroupid; - bopcookie *cookie; + pcbc_pp_id id; + zval *zvalue, *zcas, *zexpiry, *zflags, *zgroupid; + opcookie *cookie; + lcb_error_t err; // Note that groupid is experimental here and should not be used. if (pcbc_pp_begin(ZEND_NUM_ARGS() TSRMLS_CC, &pp_state, "id|value|cas,expiry,flags,groupid", - &zid, &zvalue, &zcas, &zexpiry, &zflags, &zgroupid) != SUCCESS) + &id, &zvalue, &zcas, &zexpiry, &zflags, &zgroupid) != SUCCESS) { throw_pcbc_exception("Invalid arguments.", LCB_EINVAL); RETURN_NULL(); @@ -568,18 +816,17 @@ PHP_METHOD(Bucket, replace) memset(cmd, 0, sizeof(lcb_store_cmd_t) * num_cmds); for (ii = 0; pcbc_pp_next(&pp_state); ++ii) { - PCBC_CHECK_ZVAL(zid, IS_STRING, "id must be a string"); - PCBC_CHECK_ZVAL(zcas, IS_RESOURCE, "cas must be a CAS resource"); - PCBC_CHECK_ZVAL(zexpiry, IS_LONG, "expiry must be an integer"); - PCBC_CHECK_ZVAL(zflags, IS_LONG, "flags must be an integer"); - PCBC_CHECK_ZVAL(zgroupid, IS_STRING, "groupid must be a string"); + PCBC_CHECK_ZVAL_CAS(zcas, "cas must be a CAS resource"); + PCBC_CHECK_ZVAL_LONG(zexpiry, "expiry must be an integer"); + PCBC_CHECK_ZVAL_LONG(zflags, "flags must be an integer"); + PCBC_CHECK_ZVAL_STRING(zgroupid, "groupid must be a string"); cmd[ii].version = 0; cmd[ii].v.v0.operation = LCB_REPLACE; - cmd[ii].v.v0.key = Z_STRVAL_P(zid); - cmd[ii].v.v0.nkey = Z_STRLEN_P(zid); + cmd[ii].v.v0.key = id.str; + cmd[ii].v.v0.nkey = id.len; - pcbc_zval_to_bytes(data, zvalue, &cmd[ii].v.v0.bytes, &cmd[ii].v.v0.nbytes, + pcbc_encode_value(data, zvalue, &cmd[ii].v.v0.bytes, &cmd[ii].v.v0.nbytes, &cmd[ii].v.v0.flags, &cmd[ii].v.v0.datatype TSRMLS_CC); if (zcas) { @@ -599,18 +846,28 @@ PHP_METHOD(Bucket, replace) cmds[ii] = &cmd[ii]; } - cookie = bopcookie_init(data, return_value, pcbc_pp_ismapped(&pp_state)); + cookie = opcookie_init(); - lcb_store(data->conn->lcb, cookie, + err = lcb_store(data->conn->lcb, cookie, num_cmds, (const lcb_store_cmd_t*const*)cmds); - pcbc_wait(data TSRMLS_CC); - bopcookie_destroy(cookie); + if (err == LCB_SUCCESS) { + lcb_wait(data->conn->lcb); + + err = proc_store_results(data, return_value, + cookie, pcbc_pp_ismapped(&pp_state) TSRMLS_CC); + } + + opcookie_destroy(cookie); for (ii = 0; ii < num_cmds; ++ii) { efree((void*)cmds[ii]->v.v0.bytes); } - efree(cmds); - efree(cmd); + efree(cmds); + efree(cmd); + + if (err != LCB_SUCCESS) { + throw_lcb_exception(err); + } } // append($id, $doc {, $cas, $groupid}) : MetaDoc @@ -621,12 +878,14 @@ PHP_METHOD(Bucket, append) lcb_store_cmd_t **cmds = NULL; int ii, num_cmds; pcbc_pp_state pp_state; - zval *zid, *zvalue, *zcas, *zgroupid; - bopcookie *cookie; + pcbc_pp_id id; + zval *zvalue, *zcas, *zgroupid; + opcookie *cookie; + lcb_error_t err; // Note that groupid is experimental here and should not be used. if (pcbc_pp_begin(ZEND_NUM_ARGS() TSRMLS_CC, &pp_state, "id|value|cas,groupid", - &zid, &zvalue, &zcas, &zgroupid) != SUCCESS) + &id, &zvalue, &zcas, &zgroupid) != SUCCESS) { throw_pcbc_exception("Invalid arguments.", LCB_EINVAL); RETURN_NULL(); @@ -638,16 +897,15 @@ PHP_METHOD(Bucket, append) memset(cmd, 0, sizeof(lcb_store_cmd_t) * num_cmds); for (ii = 0; pcbc_pp_next(&pp_state); ++ii) { - PCBC_CHECK_ZVAL(zid, IS_STRING, "id must be a string"); - PCBC_CHECK_ZVAL(zcas, IS_RESOURCE, "cas must be a CAS resource"); - PCBC_CHECK_ZVAL(zgroupid, IS_STRING, "groupid must be a string"); + PCBC_CHECK_ZVAL_CAS(zcas, "cas must be a CAS resource"); + PCBC_CHECK_ZVAL_STRING(zgroupid, "groupid must be a string"); cmd[ii].version = 0; cmd[ii].v.v0.operation = LCB_APPEND; - cmd[ii].v.v0.key = Z_STRVAL_P(zid); - cmd[ii].v.v0.nkey = Z_STRLEN_P(zid); + cmd[ii].v.v0.key = id.str; + cmd[ii].v.v0.nkey = id.len; - pcbc_zval_to_bytes(data, zvalue, &cmd[ii].v.v0.bytes, &cmd[ii].v.v0.nbytes, + pcbc_encode_value(data, zvalue, &cmd[ii].v.v0.bytes, &cmd[ii].v.v0.nbytes, &cmd[ii].v.v0.flags, &cmd[ii].v.v0.datatype TSRMLS_CC); if (zcas) { @@ -664,18 +922,28 @@ PHP_METHOD(Bucket, append) cmds[ii] = &cmd[ii]; } - cookie = bopcookie_init(data, return_value, pcbc_pp_ismapped(&pp_state)); + cookie = opcookie_init(); - lcb_store(data->conn->lcb, cookie, + err = lcb_store(data->conn->lcb, cookie, num_cmds, (const lcb_store_cmd_t*const*)cmds); - pcbc_wait(data TSRMLS_CC); - bopcookie_destroy(cookie); + if (err == LCB_SUCCESS) { + lcb_wait(data->conn->lcb); + + err = proc_store_results(data, return_value, + cookie, pcbc_pp_ismapped(&pp_state) TSRMLS_CC); + } + + opcookie_destroy(cookie); for (ii = 0; ii < num_cmds; ++ii) { efree((void*)cmds[ii]->v.v0.bytes); } - efree(cmds); - efree(cmd); + efree(cmds); + efree(cmd); + + if (err != LCB_SUCCESS) { + throw_lcb_exception(err); + } } // append($id, $doc {, $cas, $groupid}) : MetaDoc @@ -686,13 +954,15 @@ PHP_METHOD(Bucket, prepend) lcb_store_cmd_t **cmds = NULL; int ii, num_cmds; pcbc_pp_state pp_state; - zval *zid, *zvalue, *zcas, *zgroupid; - bopcookie *cookie; + pcbc_pp_id id; + zval *zvalue, *zcas, *zgroupid; + opcookie *cookie; + lcb_error_t err; // Note that groupid is experimental here and should not be used. if (pcbc_pp_begin(ZEND_NUM_ARGS() TSRMLS_CC, &pp_state, "id|value|cas,groupid", - &zid, &zvalue, &zcas, &zgroupid) != SUCCESS) + &id, &zvalue, &zcas, &zgroupid) != SUCCESS) { throw_pcbc_exception("Invalid arguments.", LCB_EINVAL); RETURN_NULL(); @@ -704,16 +974,15 @@ PHP_METHOD(Bucket, prepend) memset(cmd, 0, sizeof(lcb_store_cmd_t) * num_cmds); for (ii = 0; pcbc_pp_next(&pp_state); ++ii) { - PCBC_CHECK_ZVAL(zid, IS_STRING, "id must be a string"); - PCBC_CHECK_ZVAL(zcas, IS_RESOURCE, "cas must be a CAS resource"); - PCBC_CHECK_ZVAL(zgroupid, IS_STRING, "groupid must be a string"); + PCBC_CHECK_ZVAL_CAS(zcas, "cas must be a CAS resource"); + PCBC_CHECK_ZVAL_STRING(zgroupid, "groupid must be a string"); cmd[ii].version = 0; cmd[ii].v.v0.operation = LCB_PREPEND; - cmd[ii].v.v0.key = Z_STRVAL_P(zid); - cmd[ii].v.v0.nkey = Z_STRLEN_P(zid); + cmd[ii].v.v0.key = id.str; + cmd[ii].v.v0.nkey = id.len; - pcbc_zval_to_bytes(data, zvalue, &cmd[ii].v.v0.bytes, &cmd[ii].v.v0.nbytes, + pcbc_encode_value(data, zvalue, &cmd[ii].v.v0.bytes, &cmd[ii].v.v0.nbytes, &cmd[ii].v.v0.flags, &cmd[ii].v.v0.datatype TSRMLS_CC); if (zcas) { @@ -730,18 +999,28 @@ PHP_METHOD(Bucket, prepend) cmds[ii] = &cmd[ii]; } - cookie = bopcookie_init(data, return_value, pcbc_pp_ismapped(&pp_state)); + cookie = opcookie_init(); - lcb_store(data->conn->lcb, cookie, + err = lcb_store(data->conn->lcb, cookie, num_cmds, (const lcb_store_cmd_t*const*)cmds); - pcbc_wait(data TSRMLS_CC); - bopcookie_destroy(cookie); + if (err == LCB_SUCCESS) { + lcb_wait(data->conn->lcb); + + err = proc_store_results(data, return_value, + cookie, pcbc_pp_ismapped(&pp_state) TSRMLS_CC); + } + + opcookie_destroy(cookie); for (ii = 0; ii < num_cmds; ++ii) { efree((void*)cmds[ii]->v.v0.bytes); } - efree(cmds); - efree(cmd); + efree(cmds); + efree(cmd); + + if (err != LCB_SUCCESS) { + throw_lcb_exception(err); + } } // remove($id {, $cas, $groupid}) : MetaDoc @@ -752,12 +1031,14 @@ PHP_METHOD(Bucket, remove) lcb_remove_cmd_t **cmds = NULL; int ii, num_cmds; pcbc_pp_state pp_state; - bopcookie *cookie; - zval *zid, *zcas, *zgroupid; + pcbc_pp_id id; + opcookie *cookie; + zval *zcas, *zgroupid; + lcb_error_t err; // Note that groupid is experimental here and should not be used. if (pcbc_pp_begin(ZEND_NUM_ARGS() TSRMLS_CC, &pp_state, "id||cas,groupid", - &zid, &zcas, &zgroupid) != SUCCESS) + &id, &zcas, &zgroupid) != SUCCESS) { throw_pcbc_exception("Invalid arguments.", LCB_EINVAL); RETURN_NULL(); @@ -769,13 +1050,12 @@ PHP_METHOD(Bucket, remove) memset(cmd, 0, sizeof(lcb_remove_cmd_t) * num_cmds); for (ii = 0; pcbc_pp_next(&pp_state); ++ii) { - PCBC_CHECK_ZVAL(zid, IS_STRING, "id must be a string"); - PCBC_CHECK_ZVAL(zcas, IS_RESOURCE, "cas must be a CAS resource"); - PCBC_CHECK_ZVAL(zgroupid, IS_STRING, "groupid must be a string"); + PCBC_CHECK_ZVAL_CAS(zcas, "cas must be a CAS resource"); + PCBC_CHECK_ZVAL_STRING(zgroupid, "groupid must be a string"); cmd[ii].version = 0; - cmd[ii].v.v0.key = Z_STRVAL_P(zid); - cmd[ii].v.v0.nkey = Z_STRLEN_P(zid); + cmd[ii].v.v0.key = id.str; + cmd[ii].v.v0.nkey = id.len; if (zcas) { cmd[ii].v.v0.cas = cas_retrieve(zcas TSRMLS_CC); @@ -788,15 +1068,25 @@ PHP_METHOD(Bucket, remove) cmds[ii] = &cmd[ii]; } - cookie = bopcookie_init(data, return_value, pcbc_pp_ismapped(&pp_state)); + cookie = opcookie_init(); - lcb_remove(data->conn->lcb, cookie, + err = lcb_remove(data->conn->lcb, cookie, num_cmds, (const lcb_remove_cmd_t*const*)cmds); - pcbc_wait(data TSRMLS_CC); - bopcookie_destroy(cookie); - efree(cmds); - efree(cmd); + if (err == LCB_SUCCESS) { + lcb_wait(data->conn->lcb); + + err = proc_remove_results(data, return_value, + cookie, pcbc_pp_ismapped(&pp_state) TSRMLS_CC); + } + + opcookie_destroy(cookie); + efree(cmds); + efree(cmd); + + if (err != LCB_SUCCESS) { + throw_lcb_exception(err); + } } // touch($id {, $lock, $groupid}) : MetaDoc @@ -807,13 +1097,15 @@ PHP_METHOD(Bucket, touch) lcb_touch_cmd_t **cmds = NULL; int ii, num_cmds; pcbc_pp_state pp_state; - zval *zid, *zexpiry, *zgroupid; - bopcookie *cookie; + pcbc_pp_id id; + zval *zexpiry, *zgroupid; + opcookie *cookie; + lcb_error_t err; // Note that groupid is experimental here and should not be used. if (pcbc_pp_begin(ZEND_NUM_ARGS() TSRMLS_CC, &pp_state, "id|expiry|groupid", - &zid, &zexpiry, &zgroupid) != SUCCESS) + &id, &zexpiry, &zgroupid) != SUCCESS) { throw_pcbc_exception("Invalid arguments.", LCB_EINVAL); RETURN_NULL(); @@ -825,13 +1117,12 @@ PHP_METHOD(Bucket, touch) memset(cmd, 0, sizeof(lcb_touch_cmd_t) * num_cmds); for (ii = 0; pcbc_pp_next(&pp_state); ++ii) { - PCBC_CHECK_ZVAL(zid, IS_STRING, "id must be a string"); - PCBC_CHECK_ZVAL(zexpiry, IS_LONG, "expiry must be an integer"); - PCBC_CHECK_ZVAL(zgroupid, IS_STRING, "groupid must be a string"); + PCBC_CHECK_ZVAL_LONG(zexpiry, "expiry must be an integer"); + PCBC_CHECK_ZVAL_STRING(zgroupid, "groupid must be a string"); cmd[ii].version = 0; - cmd[ii].v.v0.key = Z_STRVAL_P(zid); - cmd[ii].v.v0.nkey = Z_STRLEN_P(zid); + cmd[ii].v.v0.key = id.str; + cmd[ii].v.v0.nkey = id.len; cmd[ii].v.v0.exptime = Z_LVAL_P(zexpiry); if (zgroupid) { cmd[ii].v.v0.hashkey = Z_STRVAL_P(zgroupid); @@ -841,15 +1132,25 @@ PHP_METHOD(Bucket, touch) cmds[ii] = &cmd[ii]; } - cookie = bopcookie_init(data, return_value, pcbc_pp_ismapped(&pp_state)); + cookie = opcookie_init(); - lcb_touch(data->conn->lcb, cookie, + err = lcb_touch(data->conn->lcb, cookie, num_cmds, (const lcb_touch_cmd_t*const*)cmds); - pcbc_wait(data TSRMLS_CC); - bopcookie_destroy(cookie); + if (err == LCB_SUCCESS) { + lcb_wait(data->conn->lcb); + + err = proc_touch_results(data, return_value, + cookie, pcbc_pp_ismapped(&pp_state) TSRMLS_CC); + } + + opcookie_destroy(cookie); efree(cmds); efree(cmd); + + if (err != LCB_SUCCESS) { + throw_lcb_exception(err); + } } // get($id {, $lock, $groupid}) : MetaDoc @@ -860,13 +1161,15 @@ PHP_METHOD(Bucket, get) lcb_get_cmd_t **cmds = NULL; int ii, num_cmds; pcbc_pp_state pp_state; - zval *zid, *zlock, *zexpiry, *zgroupid; - bopcookie *cookie; + pcbc_pp_id id; + zval *zlock, *zexpiry, *zgroupid; + opcookie *cookie; + lcb_error_t err; // Note that groupid is experimental here and should not be used. if (pcbc_pp_begin(ZEND_NUM_ARGS() TSRMLS_CC, &pp_state, "id||lockTime,expiry,groupid", - &zid, &zlock, &zexpiry, &zgroupid) != SUCCESS) + &id, &zlock, &zexpiry, &zgroupid) != SUCCESS) { throw_pcbc_exception("Invalid arguments.", LCB_EINVAL); RETURN_NULL(); @@ -878,14 +1181,13 @@ PHP_METHOD(Bucket, get) memset(cmd, 0, sizeof(lcb_get_cmd_t) * num_cmds); for (ii = 0; pcbc_pp_next(&pp_state); ++ii) { - PCBC_CHECK_ZVAL(zid, IS_STRING, "id must be a string"); - PCBC_CHECK_ZVAL(zlock, IS_LONG, "lock must be an integer"); - PCBC_CHECK_ZVAL(zexpiry, IS_LONG, "expiry must be an integer"); - PCBC_CHECK_ZVAL(zgroupid, IS_STRING, "groupid must be a string"); + PCBC_CHECK_ZVAL_LONG(zlock, "lock must be an integer"); + PCBC_CHECK_ZVAL_LONG(zexpiry, "expiry must be an integer"); + PCBC_CHECK_ZVAL_STRING(zgroupid, "groupid must be a string"); cmd[ii].version = 0; - cmd[ii].v.v0.key = Z_STRVAL_P(zid); - cmd[ii].v.v0.nkey = Z_STRLEN_P(zid); + cmd[ii].v.v0.key = id.str; + cmd[ii].v.v0.nkey = id.len; if (zexpiry) { cmd[ii].v.v0.lock = 0; cmd[ii].v.v0.exptime = Z_LVAL_P(zexpiry); @@ -901,15 +1203,25 @@ PHP_METHOD(Bucket, get) cmds[ii] = &cmd[ii]; } - cookie = bopcookie_init(data, return_value, pcbc_pp_ismapped(&pp_state)); + cookie = opcookie_init(); - lcb_get(data->conn->lcb, cookie, + err = lcb_get(data->conn->lcb, cookie, num_cmds, (const lcb_get_cmd_t*const*)cmds); - pcbc_wait(data TSRMLS_CC); - bopcookie_destroy(cookie); + if (err == LCB_SUCCESS) { + lcb_wait(data->conn->lcb); + + err = proc_get_results(data, return_value, + cookie, pcbc_pp_ismapped(&pp_state) TSRMLS_CC); + } + + opcookie_destroy(cookie); efree(cmds); efree(cmd); + + if (err != LCB_SUCCESS) { + throw_lcb_exception(err); + } } // get($id {, $lock, $groupid}) : MetaDoc @@ -920,13 +1232,15 @@ PHP_METHOD(Bucket, getFromReplica) lcb_get_replica_cmd_t **cmds = NULL; int ii, num_cmds; pcbc_pp_state pp_state; - zval *zid, *zindex, *zgroupid; - bopcookie *cookie; + pcbc_pp_id id; + zval *zindex, *zgroupid; + opcookie *cookie; + lcb_error_t err; // Note that groupid is experimental here and should not be used. if (pcbc_pp_begin(ZEND_NUM_ARGS() TSRMLS_CC, &pp_state, "id||index,groupid", - &zid, &zindex, &zgroupid) != SUCCESS) + &id, &zindex, &zgroupid) != SUCCESS) { throw_pcbc_exception("Invalid arguments.", LCB_EINVAL); RETURN_NULL(); @@ -938,13 +1252,12 @@ PHP_METHOD(Bucket, getFromReplica) memset(cmd, 0, sizeof(lcb_get_replica_cmd_t) * num_cmds); for (ii = 0; pcbc_pp_next(&pp_state); ++ii) { - PCBC_CHECK_ZVAL(zid, IS_STRING, "id must be a string"); - PCBC_CHECK_ZVAL(zindex, IS_LONG, "index must be an integer"); - PCBC_CHECK_ZVAL(zgroupid, IS_STRING, "groupid must be a string"); + PCBC_CHECK_ZVAL_LONG(zindex, "index must be an integer"); + PCBC_CHECK_ZVAL_STRING(zgroupid, "groupid must be a string"); cmd[ii].version = 1; - cmd[ii].v.v1.key = Z_STRVAL_P(zid); - cmd[ii].v.v1.nkey = Z_STRLEN_P(zid); + cmd[ii].v.v1.key = id.str; + cmd[ii].v.v1.nkey = id.len; if (zindex) { cmd[ii].v.v1.index = Z_LVAL_P(zindex); if (cmd[ii].v.v1.index >= 0) { @@ -961,15 +1274,25 @@ PHP_METHOD(Bucket, getFromReplica) cmds[ii] = &cmd[ii]; } - cookie = bopcookie_init(data, return_value, pcbc_pp_ismapped(&pp_state)); + cookie = opcookie_init(); - lcb_get_replica(data->conn->lcb, cookie, + err = lcb_get_replica(data->conn->lcb, cookie, num_cmds, (const lcb_get_replica_cmd_t*const*)cmds); - pcbc_wait(data TSRMLS_CC); - bopcookie_destroy(cookie); - efree(cmds); - efree(cmd); + if (err == LCB_SUCCESS) { + lcb_wait(data->conn->lcb); + + err = proc_get_results(data, return_value, + cookie, pcbc_pp_ismapped(&pp_state) TSRMLS_CC); + } + + opcookie_destroy(cookie); + efree(cmds); + efree(cmd); + + if (err != LCB_SUCCESS) { + throw_lcb_exception(err); + } } // unlock($id {, $cas, $groupid}) : MetaDoc @@ -980,13 +1303,15 @@ PHP_METHOD(Bucket, unlock) lcb_unlock_cmd_t **cmds = NULL; int ii, num_cmds; pcbc_pp_state pp_state; - zval *zid, *zcas, *zgroupid; - bopcookie *cookie; + pcbc_pp_id id; + zval *zcas, *zgroupid; + opcookie *cookie; + lcb_error_t err; // Note that groupid is experimental here and should not be used. if (pcbc_pp_begin(ZEND_NUM_ARGS() TSRMLS_CC, &pp_state, "id||cas,groupid", - &zid, &zcas, &zgroupid) != SUCCESS) + &id, &zcas, &zgroupid) != SUCCESS) { throw_pcbc_exception("Invalid arguments.", LCB_EINVAL); RETURN_NULL(); @@ -998,13 +1323,12 @@ PHP_METHOD(Bucket, unlock) memset(cmd, 0, sizeof(lcb_unlock_cmd_t) * num_cmds); for (ii = 0; pcbc_pp_next(&pp_state); ++ii) { - PCBC_CHECK_ZVAL(zid, IS_STRING, "id must be a string"); - PCBC_CHECK_ZVAL(zcas, IS_RESOURCE, "cas must be a CAS resource"); - PCBC_CHECK_ZVAL(zgroupid, IS_STRING, "groupid must be a string"); + PCBC_CHECK_ZVAL_CAS(zcas, "cas must be a CAS resource"); + PCBC_CHECK_ZVAL_STRING(zgroupid, "groupid must be a string"); cmd[ii].version = 0; - cmd[ii].v.v0.key = Z_STRVAL_P(zid); - cmd[ii].v.v0.nkey = Z_STRLEN_P(zid); + cmd[ii].v.v0.key = id.str; + cmd[ii].v.v0.nkey = id.len; if (zcas) { cmd[ii].v.v0.cas = cas_retrieve(zcas TSRMLS_CC); } @@ -1016,15 +1340,25 @@ PHP_METHOD(Bucket, unlock) cmds[ii] = &cmd[ii]; } - cookie = bopcookie_init(data, return_value, pcbc_pp_ismapped(&pp_state)); + cookie = opcookie_init(); - lcb_unlock(data->conn->lcb, cookie, + err = lcb_unlock(data->conn->lcb, cookie, num_cmds, (const lcb_unlock_cmd_t*const*)cmds); - pcbc_wait(data TSRMLS_CC); - bopcookie_destroy(cookie); - efree(cmds); - efree(cmd); + if (err == LCB_SUCCESS) { + lcb_wait(data->conn->lcb); + + err = proc_unlock_results(data, return_value, + cookie, pcbc_pp_ismapped(&pp_state) TSRMLS_CC); + } + + opcookie_destroy(cookie); + efree(cmds); + efree(cmd); + + if (err != LCB_SUCCESS) { + throw_lcb_exception(err); + } } // counter($id, $delta {, $initial, $expiry}) : MetaDoc @@ -1035,13 +1369,15 @@ PHP_METHOD(Bucket, counter) lcb_arithmetic_cmd_t **cmds = NULL; int ii, num_cmds; pcbc_pp_state pp_state; - zval *zid, *zdelta, *zinitial, *zexpiry, *zgroupid; - bopcookie *cookie; + pcbc_pp_id id; + zval *zdelta, *zinitial, *zexpiry, *zgroupid; + opcookie *cookie; + lcb_error_t err; // Note that groupid is experimental here and should not be used. if (pcbc_pp_begin(ZEND_NUM_ARGS() TSRMLS_CC, &pp_state, "id|delta|initial,expiry,groupid", - &zid, &zdelta, &zinitial, &zexpiry, &zgroupid) != SUCCESS) + &id, &zdelta, &zinitial, &zexpiry, &zgroupid) != SUCCESS) { throw_pcbc_exception("Invalid arguments.", LCB_EINVAL); RETURN_NULL(); @@ -1053,15 +1389,14 @@ PHP_METHOD(Bucket, counter) memset(cmd, 0, sizeof(lcb_arithmetic_cmd_t) * num_cmds); for (ii = 0; pcbc_pp_next(&pp_state); ++ii) { - PCBC_CHECK_ZVAL(zid, IS_STRING, "id must be a string"); - PCBC_CHECK_ZVAL(zdelta, IS_LONG, "delta must be an integer"); - PCBC_CHECK_ZVAL(zinitial, IS_LONG, "initial must be an integer"); - PCBC_CHECK_ZVAL(zexpiry, IS_LONG, "expiry must be an integer"); - PCBC_CHECK_ZVAL(zgroupid, IS_STRING, "groupid must be a string"); + PCBC_CHECK_ZVAL_LONG(zdelta, "delta must be an integer"); + PCBC_CHECK_ZVAL_LONG(zinitial, "initial must be an integer"); + PCBC_CHECK_ZVAL_LONG(zexpiry, "expiry must be an integer"); + PCBC_CHECK_ZVAL_STRING(zgroupid, "groupid must be a string"); cmd[ii].version = 0; - cmd[ii].v.v0.key = Z_STRVAL_P(zid); - cmd[ii].v.v0.nkey = Z_STRLEN_P(zid); + cmd[ii].v.v0.key = id.str; + cmd[ii].v.v0.nkey = id.len; cmd[ii].v.v0.delta = Z_LVAL_P(zdelta); if (zinitial) { cmd[ii].v.v0.initial = Z_LVAL_P(zinitial); @@ -1078,24 +1413,110 @@ PHP_METHOD(Bucket, counter) cmds[ii] = &cmd[ii]; } - cookie = bopcookie_init(data, return_value, pcbc_pp_ismapped(&pp_state)); + cookie = opcookie_init(); - lcb_arithmetic(data->conn->lcb, cookie, + err = lcb_arithmetic(data->conn->lcb, cookie, num_cmds, (const lcb_arithmetic_cmd_t*const*)cmds); - pcbc_wait(data TSRMLS_CC); - bopcookie_destroy(cookie); - efree(cmds); - efree(cmd); + if (err == LCB_SUCCESS) { + lcb_wait(data->conn->lcb); + + err = proc_arithmetic_results(data, return_value, + cookie, pcbc_pp_ismapped(&pp_state) TSRMLS_CC); + } + + opcookie_destroy(cookie); + efree(cmds); + efree(cmd); + + if (err != LCB_SUCCESS) { + throw_lcb_exception(err); + } +} + +PHP_METHOD(Bucket, durability) +{ + bucket_object *data = PHP_THISOBJ(); + lcb_durability_cmd_t *cmd = NULL; + lcb_durability_opts_t opts = { 0 }; + lcb_durability_cmd_t **cmds = NULL; + int ii, num_cmds; + pcbc_pp_state pp_state; + pcbc_pp_id id; + zval *zcas, *zgroupid, *zpersist, *zreplica; + opcookie *cookie; + lcb_error_t err; + + if (pcbc_pp_begin(ZEND_NUM_ARGS() TSRMLS_CC, &pp_state, + "id||cas,groupid,persist_to,replicate_to", + &id, &zcas, &zgroupid, &zpersist, &zreplica) != SUCCESS) + { + throw_pcbc_exception("Invalid arguments.", LCB_EINVAL); + RETURN_NULL(); + } + + num_cmds = pcbc_pp_keycount(&pp_state); + cmd = emalloc(sizeof(lcb_durability_cmd_t) * num_cmds); + cmds = emalloc(sizeof(lcb_durability_cmd_t*) * num_cmds); + memset(cmd, 0, sizeof(lcb_durability_cmd_t) * num_cmds); + + for (ii = 0; pcbc_pp_next(&pp_state); ++ii) { + PCBC_CHECK_ZVAL_CAS(zcas, "cas must be a CAS resource"); + PCBC_CHECK_ZVAL_STRING(zgroupid, "groupid must be a string"); + PCBC_CHECK_ZVAL_LONG(zpersist, "persist_to must be an integer"); + PCBC_CHECK_ZVAL_LONG(zreplica, "replicate_to must be an integer"); + + cmd[ii].version = 0; + cmd[ii].v.v0.key = id.str; + cmd[ii].v.v0.nkey = id.len; + if (zcas) { + cmd[ii].v.v0.cas = cas_retrieve(zcas TSRMLS_CC); + } + if (zgroupid) { + cmd[ii].v.v0.hashkey = Z_STRVAL_P(zgroupid); + cmd[ii].v.v0.nhashkey = Z_STRLEN_P(zgroupid); + } + + // These are written through each iteration, but only used once. + if (zpersist) { + opts.v.v0.persist_to = (lcb_U16)Z_LVAL_P(zpersist); + } + if (zreplica) { + opts.v.v0.replicate_to = (lcb_U16)Z_LVAL_P(zreplica); + } + + cmds[ii] = &cmd[ii]; + } + + cookie = opcookie_init(); + + err = lcb_durability_poll(data->conn->lcb, cookie, &opts, + num_cmds, (const lcb_durability_cmd_t*const*)cmds); + + if (err == LCB_SUCCESS) { + lcb_wait(data->conn->lcb); + + err = proc_durability_results(data, return_value, + cookie, pcbc_pp_ismapped(&pp_state) TSRMLS_CC); + } + + opcookie_destroy(cookie); + efree(cmds); + efree(cmd); + + if (err != LCB_SUCCESS) { + throw_lcb_exception(err); + } } PHP_METHOD(Bucket, n1ql_request) { bucket_object *data = PHP_THISOBJ(); lcb_CMDN1QL cmd = { 0 }; - bopcookie *cookie; + opcookie *cookie; zval *zbody, *zadhoc; - zval *zResults; + zapval zResults; + lcb_error_t err; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &zbody, &zadhoc) == FAILURE) { @@ -1103,43 +1524,46 @@ PHP_METHOD(Bucket, n1ql_request) RETURN_NULL(); } - PCBC_CHECK_ZVAL(zbody, IS_STRING, "body must be a string"); - PCBC_CHECK_ZVAL(zadhoc, IS_BOOL, "adhoc must be a bool"); + PCBC_CHECK_ZVAL_STRING(zbody, "body must be a string"); + PCBC_CHECK_ZVAL_BOOL(zadhoc, "adhoc must be a bool"); cmd.callback = n1qlrow_callback; cmd.content_type = "application/json"; cmd.query = Z_STRVAL_P(zbody); cmd.nquery = Z_STRLEN_P(zbody); - if (Z_BVAL_P(zadhoc) == 0) { + if (zap_zval_boolval(zadhoc) == 0) { cmd.cmdflags |= LCB_CMDN1QL_F_PREPCACHE; } - cookie = bopcookie_init(data, return_value, 0); + cookie = opcookie_init(); - // Setup basic structure - MAKE_STD_ZVAL(zResults); - array_init(zResults); + // Execute query + err = lcb_n1ql_query(data->conn->lcb, cookie, &cmd); - array_init(return_value); - add_assoc_zval(return_value, "results", zResults); + if (err == LCB_SUCCESS) { + lcb_wait(data->conn->lcb); - // Execute query - lcb_n1ql_query(data->conn->lcb, cookie, &cmd); - pcbc_wait(data TSRMLS_CC); + err = proc_n1qlrow_results(data, return_value, cookie TSRMLS_CC); + } - bopcookie_destroy(cookie); + opcookie_destroy(cookie); + + if (err != LCB_SUCCESS) { + throw_lcb_exception(err); + } } PHP_METHOD(Bucket, http_request) { bucket_object *data = PHP_THISOBJ(); lcb_http_cmd_t cmd = { 0 }; - bopcookie *cookie; + opcookie *cookie; lcb_http_type_t type; lcb_http_method_t method; const char *contenttype; zval *ztype, *zmethod, *zpath, *zbody, *zcontenttype; + lcb_error_t err; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zzzzz", &ztype, &zmethod, &zpath, &zbody, &zcontenttype) == FAILURE) { @@ -1189,77 +1613,21 @@ PHP_METHOD(Bucket, http_request) cmd.v.v0.chunked = 0; cmd.v.v0.content_type = contenttype; - cookie = bopcookie_init(data, return_value, 0); + cookie = opcookie_init(); - lcb_make_http_request(data->conn->lcb, cookie, type, &cmd, NULL); - pcbc_wait(data TSRMLS_CC); + err = lcb_make_http_request(data->conn->lcb, cookie, type, &cmd, NULL); - bopcookie_destroy(cookie); -} - - -PHP_METHOD(Bucket, durability) -{ - bucket_object *data = PHP_THISOBJ(); - lcb_durability_cmd_t *cmd = NULL; - lcb_durability_opts_t opts = { 0 }; - lcb_durability_cmd_t **cmds = NULL; - int ii, num_cmds; - pcbc_pp_state pp_state; - zval *zid, *zcas, *zgroupid, *zpersist, *zreplica; - bopcookie *cookie; + if (err == LCB_SUCCESS) { + lcb_wait(data->conn->lcb); - if (pcbc_pp_begin(ZEND_NUM_ARGS() TSRMLS_CC, &pp_state, - "id||cas,groupid,persist_to,replicate_to", - &zid, &zcas, &zgroupid, &zpersist, &zreplica) != SUCCESS) - { - throw_pcbc_exception("Invalid arguments.", LCB_EINVAL); - RETURN_NULL(); + err = proc_http_results(data, return_value, cookie TSRMLS_CC); } - num_cmds = pcbc_pp_keycount(&pp_state); - cmd = emalloc(sizeof(lcb_durability_cmd_t) * num_cmds); - cmds = emalloc(sizeof(lcb_durability_cmd_t*) * num_cmds); - memset(cmd, 0, sizeof(lcb_durability_cmd_t) * num_cmds); + opcookie_destroy(cookie); - for (ii = 0; pcbc_pp_next(&pp_state); ++ii) { - PCBC_CHECK_ZVAL(zid, IS_STRING, "id must be a string"); - PCBC_CHECK_ZVAL(zcas, IS_RESOURCE, "cas must be a CAS resource"); - PCBC_CHECK_ZVAL(zgroupid, IS_STRING, "groupid must be a string"); - PCBC_CHECK_ZVAL(zpersist, IS_LONG, "persist_to must be an integer"); - PCBC_CHECK_ZVAL(zreplica, IS_LONG, "replicate_to must be an integer"); - - cmd[ii].version = 0; - cmd[ii].v.v0.key = Z_STRVAL_P(zid); - cmd[ii].v.v0.nkey = Z_STRLEN_P(zid); - if (zcas) { - cmd[ii].v.v0.cas = cas_retrieve(zcas TSRMLS_CC); - } - if (zgroupid) { - cmd[ii].v.v0.hashkey = Z_STRVAL_P(zgroupid); - cmd[ii].v.v0.nhashkey = Z_STRLEN_P(zgroupid); - } - - // These are written through each iteration, but only used once. - if (zpersist) { - opts.v.v0.persist_to = (lcb_U16)Z_LVAL_P(zpersist); - } - if (zreplica) { - opts.v.v0.replicate_to = (lcb_U16)Z_LVAL_P(zreplica); - } - - cmds[ii] = &cmd[ii]; - } - - cookie = bopcookie_init(data, return_value, pcbc_pp_ismapped(&pp_state)); - - lcb_durability_poll(data->conn->lcb, cookie, &opts, - num_cmds, (const lcb_durability_cmd_t*const*)cmds); - pcbc_wait(data TSRMLS_CC); - - bopcookie_destroy(cookie); - efree(cmds); - efree(cmd); + if (err != LCB_SUCCESS) { + throw_lcb_exception(err); + } } @@ -1272,13 +1640,11 @@ PHP_METHOD(Bucket, setTranscoder) RETURN_NULL(); } - zval_ptr_dtor(&data->encoder); - MAKE_STD_ZVAL(data->encoder); - ZVAL_ZVAL(data->encoder, zencoder, 1, NULL); + zapval_destroy(data->encoder); + zapval_alloc_zval(data->encoder, zencoder, 1, 0); - zval_ptr_dtor(&data->decoder); - MAKE_STD_ZVAL(data->decoder); - ZVAL_ZVAL(data->decoder, zdecoder, 1, NULL); + zapval_destroy(data->decoder); + zapval_alloc_zval(data->decoder, zdecoder, 1, 0); RETURN_NULL(); } @@ -1339,15 +1705,11 @@ zend_function_entry bucket_methods[] = { }; void couchbase_init_bucket(INIT_FUNC_ARGS) { - zend_class_entry ce; - - INIT_CLASS_ENTRY(ce, "_CouchbaseBucket", bucket_methods); - ce.create_object = bucket_create_handler; - bucket_ce = zend_register_internal_class(&ce TSRMLS_CC); - - memcpy(&bucket_handlers, - zend_get_std_object_handlers(), sizeof(zend_object_handlers)); - bucket_handlers.clone_obj = NULL; + zap_init_class_entry(&bucket_class, "_CouchbaseBucket", + bucket_methods); + bucket_class.create_obj = bucket_create_handler; + bucket_class.free_obj = bucket_free_storage; + bucket_ce = zap_register_internal_class(&bucket_class, bucket_object); } void couchbase_shutdown_bucket(SHUTDOWN_FUNC_ARGS) { diff --git a/bucket.h b/bucket.h index 2a33d46..2601441 100644 --- a/bucket.h +++ b/bucket.h @@ -3,15 +3,17 @@ #include #include "couchbase.h" +#include "zap.h" typedef struct bucket_object { - zend_object std; - zval *error; - zval *encoder; - zval *decoder; - zval *prefix; + zap_ZEND_OBJECT_START - pcbc_lcb *conn; + zapval encoder; + zapval decoder; + zapval prefix; + pcbc_lcb *conn; + + zap_ZEND_OBJECT_END } bucket_object; #endif // BUCKET_H_ diff --git a/cas.c b/cas.c index d0e6269..daf7e74 100644 --- a/cas.c +++ b/cas.c @@ -1,9 +1,10 @@ #include #include +#include "cas.h" int le_cas; -static void cas_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) +static zap_DTORRES_FUNC(cas_dtor) { lcb_cas_t *cas_data = (lcb_cas_t*)rsrc->ptr; if (cas_data) { @@ -12,24 +13,20 @@ static void cas_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) } void couchbase_init_cas(INIT_FUNC_ARGS) { - le_cas = zend_register_list_destructors_ex(cas_dtor, NULL, "CouchbaseCAS", module_number); + le_cas = zend_register_list_destructors_ex(cas_dtor, NULL, "CouchbaseCAS", module_number); } lcb_cas_t cas_retrieve(zval * zcas TSRMLS_DC) { - lcb_cas_t *cas = 0; - ZEND_FETCH_RESOURCE_NO_RETURN(cas, lcb_cas_t*, &zcas, -1, "CouchbaseCAS", le_cas); - if (cas) { - return *cas; - } else { - return 0; - } + lcb_cas_t *cas = (lcb_cas_t*)zap_fetch_resource(zcas, "CouchbaseCAS", le_cas); + if (cas) { + return *cas; + } else { + return 0; + } } -zval * cas_create(lcb_cas_t value TSRMLS_DC) { - zval *cas; - void *cas_data = emalloc(sizeof(lcb_cas_t)); - *((lcb_cas_t*)cas_data) = value; - MAKE_STD_ZVAL(cas); - ZEND_REGISTER_RESOURCE(cas, cas_data, le_cas); - return cas; +void cas_create(zapval *casout, lcb_cas_t value TSRMLS_DC) { + void *cas_data = emalloc(sizeof(lcb_cas_t)); + *((lcb_cas_t*)cas_data) = value; + zapval_alloc_res(*casout, cas_data, le_cas); } diff --git a/cas.h b/cas.h index dd56a8a..878f18d 100644 --- a/cas.h +++ b/cas.h @@ -3,9 +3,13 @@ #include #include +#include "zap.h" void couchbase_init_cas(INIT_FUNC_ARGS); lcb_cas_t cas_retrieve(zval * zcas TSRMLS_DC); -zval * cas_create(lcb_cas_t value TSRMLS_DC); +void cas_create(zapval *casout, lcb_cas_t value TSRMLS_DC); + +#define alloc_cas(z, v) \ + cas_create(&z, v) #endif // CAS_H_ diff --git a/cluster.c b/cluster.c index f32700f..93cb934 100644 --- a/cluster.c +++ b/cluster.c @@ -1,96 +1,83 @@ -#include #include "couchbase.h" -#include "phphelpers.h" +#include "ext/standard/php_var.h" #include "exception.h" +#include "zap.h" #include "cluster.h" -#include "metadoc.h" +#include "opcookie.h" -zend_object_handlers cluster_handlers; +zap_class_entry cluster_class; +zend_class_entry *cluster_ce; + +#define PHP_THISOBJ() zap_fetch_this(cluster_object) -void cluster_free_storage(void *object TSRMLS_DC) +zap_FREEOBJ_FUNC(cluster_free_storage) { - cluster_object *obj = (cluster_object *)object; + cluster_object *obj = zap_get_object(cluster_object, object); - zend_hash_destroy(obj->std.properties); - FREE_HASHTABLE(obj->std.properties); + if (obj->lcb != NULL) { + lcb_destroy(obj->lcb); + obj->lcb = NULL; + } - efree(obj); + zend_object_std_dtor(&obj->std TSRMLS_CC); + zap_free_object_storage(obj); } -zend_object_value cluster_create_handler(zend_class_entry *type TSRMLS_DC) +zap_CREATEOBJ_FUNC(cluster_create_handler) { - zend_object_value retval; - - cluster_object *obj = (cluster_object *)emalloc(sizeof(cluster_object)); - memset(obj, 0, sizeof(cluster_object)); - obj->std.ce = type; + cluster_object *obj = zap_alloc_object_storage(cluster_object, type); - ALLOC_HASHTABLE(obj->std.properties); - zend_hash_init(obj->std.properties, 0, NULL, ZVAL_PTR_DTOR, 0); - phlp_object_properties_init(&obj->std, type); + zend_object_std_init(&obj->std, type TSRMLS_CC); + zap_object_properties_init(&obj->std, type); - retval.handle = zend_objects_store_put(obj, NULL, - cluster_free_storage, NULL TSRMLS_CC); - retval.handlers = &cluster_handlers; + obj->lcb = NULL; - return retval; + return zap_finalize_object(obj, &cluster_class); } typedef struct { - zval *retval; - cluster_object *owner; -} copcookie; - -copcookie * copcookie_init(cluster_object *clusterobj, zval *return_value) { - copcookie *cookie = emalloc(sizeof(copcookie)); - cookie->owner = clusterobj; - cookie->retval = return_value; - ZVAL_NULL(cookie->retval); - return cookie; -} - -void ccookie_error(const copcookie *cookie, cluster_object *data, zval *doc, - lcb_error_t error TSRMLS_DC) { - zval *zerror = create_lcb_exception(error TSRMLS_CC); - if (Z_TYPE_P(cookie->retval) == IS_ARRAY) { - metadoc_from_error(doc, zerror TSRMLS_CC); - } else { - data->error = zerror; - } -} + opcookie_res header; + zapval bytes; +} opcookie_http_res; static void http_complete_callback(lcb_http_request_t request, lcb_t instance, const void *cookie, lcb_error_t error, const lcb_http_resp_t *resp) { - cluster_object *data = (cluster_object*)lcb_get_cookie(instance); - zval *doc = ((copcookie*)cookie)->retval; - TSRMLS_FETCH(); + opcookie_http_res *result = ecalloc(1, sizeof(opcookie_http_res)); + TSRMLS_FETCH(); - if (error == LCB_SUCCESS) { - ZVAL_STRINGL(doc, resp->v.v0.bytes, resp->v.v0.nbytes, 1); - } else { - ccookie_error(cookie, data, NULL, error TSRMLS_CC); - } + result->header.err = error; + zapval_alloc_stringl( + result->bytes, resp->v.v0.bytes, resp->v.v0.nbytes); + + opcookie_push((opcookie*)cookie, &result->header); } -static int pcbc_wait(cluster_object *obj TSRMLS_DC) +static lcb_error_t proc_http_results(cluster_object *cluster, zval *return_value, + opcookie *cookie TSRMLS_DC) { - lcb_t instance = obj->lcb; - obj->error = NULL; - - lcb_wait(instance); - - if (obj->error) { - zend_throw_exception_object(obj->error TSRMLS_CC); - obj->error = NULL; - return 0; - } - - return 1; + opcookie_http_res *res; + lcb_error_t err = LCB_SUCCESS; + + // Any error should cause everything to fail... for now? + err = opcookie_get_first_error(cookie); + + if (err == LCB_SUCCESS) { + // TODO: This could leak with multiple results... It also copies + // which might not be needed... + FOREACH_OPCOOKIE_RES(opcookie_http_res, res, cookie) { + zap_zval_stringl_p(return_value, + zapval_strval_p(&res->bytes), zapval_strlen_p(&res->bytes)) + } + } + + FOREACH_OPCOOKIE_RES(opcookie_http_res, res, cookie) { + zapval_destroy(res->bytes); + } + + return err; } -zend_class_entry *cluster_ce; - PHP_METHOD(Cluster, __construct) { cluster_object *data = PHP_THISOBJ(); @@ -187,11 +174,12 @@ PHP_METHOD(Cluster, http_request) { cluster_object *data = PHP_THISOBJ(); lcb_http_cmd_t cmd = { 0 }; - copcookie *cookie; + opcookie *cookie; lcb_http_type_t type; lcb_http_method_t method; const char *contenttype; zval *ztype, *zmethod, *zpath, *zbody, *zcontenttype; + lcb_error_t err; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zzzzz", &ztype, &zmethod, &zpath, &zbody, &zcontenttype) == FAILURE) { @@ -240,12 +228,21 @@ PHP_METHOD(Cluster, http_request) cmd.v.v0.chunked = 0; cmd.v.v0.content_type = contenttype; - cookie = copcookie_init(data, return_value); + cookie = opcookie_init(); + + err = lcb_make_http_request(data->lcb, cookie, type, &cmd, NULL); + + if (err == LCB_SUCCESS) { + lcb_wait(data->lcb); + + err = proc_http_results(data, return_value, cookie TSRMLS_CC); + } - lcb_make_http_request(data->lcb, cookie, type, &cmd, NULL); - pcbc_wait(data TSRMLS_CC); + opcookie_destroy(cookie); - efree(cookie); + if (err != LCB_SUCCESS) { + throw_lcb_exception(err); + } } zend_function_entry cluster_methods[] = { @@ -256,12 +253,9 @@ zend_function_entry cluster_methods[] = { }; void couchbase_init_cluster(INIT_FUNC_ARGS) { - zend_class_entry ce; - INIT_CLASS_ENTRY(ce, "_CouchbaseCluster", cluster_methods); - ce.create_object = cluster_create_handler; - cluster_ce = zend_register_internal_class(&ce TSRMLS_CC); - - memcpy(&cluster_handlers, - zend_get_std_object_handlers(), sizeof(zend_object_handlers)); - cluster_handlers.clone_obj = NULL; + zap_init_class_entry(&cluster_class, "_CouchbaseCluster", + cluster_methods); + cluster_class.create_obj = cluster_create_handler; + cluster_class.free_obj = cluster_free_storage; + cluster_ce = zap_register_internal_class(&cluster_class, cluster_object); } diff --git a/cluster.h b/cluster.h index 180dade..e2dce91 100644 --- a/cluster.h +++ b/cluster.h @@ -1,13 +1,16 @@ #ifndef CLUSTER_H_ #define CLUSTER_H_ -#include #include +#include "couchbase.h" +#include "zap.h" typedef struct cluster_object { - zend_object std; - lcb_t lcb; - zval *error; + zap_ZEND_OBJECT_START + + lcb_t lcb; + + zap_ZEND_OBJECT_END } cluster_object; #endif // CLUSTER_H_ diff --git a/config.m4 b/config.m4 index 480e520..e4c1903 100644 --- a/config.m4 +++ b/config.m4 @@ -40,6 +40,7 @@ if test "$PHP_COUCHBASE" != "no"; then couchbase.c \ exception.c \ metadoc.c \ + opcookie.c \ transcoding.c \ fastlz/fastlz.c \ , $ext_shared) diff --git a/config.w32 b/config.w32 index a962884..2f01f12 100644 --- a/config.w32 +++ b/config.w32 @@ -8,6 +8,7 @@ if (PHP_COUCHBASE != "no") { "couchbase.c " + "exception.c " + "metadoc.c " + + "opcookie.c " + "transcoding.c "; if (CHECK_LIB("libcouchbase.lib", "couchbase", PHP_COUCHBASE) && diff --git a/couchbase.c b/couchbase.c index 8e63199..def397f 100644 --- a/couchbase.c +++ b/couchbase.c @@ -3,6 +3,7 @@ #include "metadoc.h" #include "phpstubstr.h" #include "fastlz/fastlz.h" +#include "zap.h" #if HAVE_ZLIB #include @@ -150,10 +151,10 @@ PHP_FUNCTION(couchbase_zlib_compress) compress((uint8_t*)dataOut + 4, &dataOutSize, dataIn, dataSize); *(uint32_t*)dataOut = dataSize; - RETURN_STRINGL(dataOut, 4 + dataOutSize, 0); + zap_zval_stringl_p(return_value, dataOut, 4 + dataOutSize); + efree(dataOut); #else zend_throw_exception(NULL, "The zlib library was not available when the couchbase extension was built.", 0 TSRMLS_CC); - RETURN_NULL(); #endif } @@ -175,10 +176,10 @@ PHP_FUNCTION(couchbase_zlib_decompress) dataOut = emalloc(dataOutSize); uncompress(dataOut, &dataOutSize, (uint8_t*)dataIn + 4, dataSize - 4); - RETURN_STRINGL(dataOut, dataOutSize, 0); + zap_zval_stringl_p(return_value, dataOut, dataOutSize); + efree(dataOut); #else zend_throw_exception(NULL, "The zlib library was not available when the couchbase extension was built.", 0 TSRMLS_CC); - RETURN_NULL(); #endif } @@ -200,7 +201,9 @@ PHP_FUNCTION(couchbase_fastlz_compress) dataOutSize = fastlz_compress(dataIn, dataSize, (uint8_t*)dataOut + 4); *(uint32_t*)dataOut = dataSize; - RETURN_STRINGL(dataOut, 4 + dataOutSize, 0); + zap_zval_stringl_p(return_value, dataOut, 4 + dataOutSize); + + efree(dataOut); } PHP_FUNCTION(couchbase_fastlz_decompress) @@ -221,7 +224,9 @@ PHP_FUNCTION(couchbase_fastlz_decompress) dataOutSize = fastlz_decompress( (uint8_t*)dataIn + 4, dataSize - 4, dataOut, dataOutSize); - RETURN_STRINGL(dataOut, dataOutSize, 0); + zap_zval_stringl_p(return_value, dataOut, dataOutSize); + + efree(dataOut); } static zend_function_entry couchbase_functions[] = { diff --git a/couchbase.h b/couchbase.h index 0a91c01..669ee82 100644 --- a/couchbase.h +++ b/couchbase.h @@ -41,8 +41,6 @@ ZEND_EXTERN_MODULE_GLOBALS(couchbase) #define PCBCG(v) (couchbase_globals.v) #endif -#define PHP_THISOBJ() zend_object_store_get_object(getThis() TSRMLS_CC) - void couchbase_init_exceptions(INIT_FUNC_ARGS); void couchbase_init_cluster(INIT_FUNC_ARGS); void couchbase_init_bucket(INIT_FUNC_ARGS); diff --git a/exception.c b/exception.c index 9e41cd1..1ee2df3 100644 --- a/exception.c +++ b/exception.c @@ -1,48 +1,52 @@ #include "couchbase.h" #include +#include "zap.h" zend_class_entry *default_exception_ce; zend_class_entry *cb_exception_ce; -zval * create_exception(zend_class_entry *exception_ce, const char *message, long code TSRMLS_DC) { - zend_class_entry *default_exception_ce = zend_exception_get_default(TSRMLS_C); - zval *ex; - - MAKE_STD_ZVAL(ex); - object_init_ex(ex, exception_ce); - - if (message) { - zend_update_property_string(default_exception_ce, ex, "message", sizeof("message")-1, message TSRMLS_CC); - } - if (code) { - zend_update_property_long(default_exception_ce, ex, "code", sizeof("code")-1, code TSRMLS_CC); - } - - return ex; +void make_exception(zapval *ex, zend_class_entry *exception_ce, const char *message, long code TSRMLS_DC) { + zapval_alloc(*ex); + object_init_ex(zapval_zvalptr_p(ex), cb_exception_ce); + + if (message) { + zend_update_property_string( + cb_exception_ce, + zapval_zvalptr_p(ex), + "message", sizeof("message")-1, + message TSRMLS_CC); + } + if (code) { + zend_update_property_long( + cb_exception_ce, + zapval_zvalptr_p(ex), + "code", sizeof("code")-1, + code TSRMLS_CC); + } } -zval * create_pcbc_exception(const char *message, long code TSRMLS_DC) { - return create_exception(cb_exception_ce, message, code TSRMLS_CC); +void make_pcbc_exception(zapval *ex, const char *message, long code TSRMLS_DC) { + make_exception(ex, cb_exception_ce, message, code TSRMLS_CC); } -zval * create_lcb_exception(long code TSRMLS_DC) { - const char *str = lcb_strerror(NULL, (lcb_error_t)code); - return create_exception(cb_exception_ce, str, code TSRMLS_CC); +void make_lcb_exception(zapval *ex, long code, const char *msg TSRMLS_DC) { + if (msg) { + return make_exception(ex, cb_exception_ce, msg, code TSRMLS_CC); + } else { + const char *str = lcb_strerror(NULL, (lcb_error_t)code); + return make_exception(ex, cb_exception_ce, str, code TSRMLS_CC); + } } #define setup(var, name, parent) \ - do { \ - zend_class_entry cbe; \ - INIT_CLASS_ENTRY(cbe, name, NULL); \ - var = zend_register_internal_class_ex(&cbe, parent, NULL TSRMLS_CC); \ - } while(0) + do { \ + zend_class_entry cbe; \ + INIT_CLASS_ENTRY(cbe, name, NULL); \ + var = zap_zend_register_internal_class_ex(&cbe, parent); \ + } while(0) void couchbase_init_exceptions(INIT_FUNC_ARGS) { -#if ZEND_MODULE_API_NO >= 20060613 - default_exception_ce = (zend_class_entry *)zend_exception_get_default(TSRMLS_C); -#else - default_exception_ce = (zend_class_entry *)zend_exception_get_default(); -#endif + default_exception_ce = (zend_class_entry *)zap_zend_exception_get_default(); - setup(cb_exception_ce, "CouchbaseException", default_exception_ce); + setup(cb_exception_ce, "CouchbaseException", default_exception_ce); } diff --git a/exception.h b/exception.h index 3491b98..406bc74 100644 --- a/exception.h +++ b/exception.h @@ -1,14 +1,20 @@ #ifndef EXCEPTION_H_ #define EXCEPTION_H_ -zval * create_exception(zend_class_entry *exception_ce, const char *message, long code TSRMLS_DC); -zval * create_pcbc_exception(const char *message, long code TSRMLS_DC); -zval * create_lcb_exception(long code TSRMLS_DC); +#include "zap.h" -#define throw_pcbc_exception(message, code) \ - zend_throw_exception_object(create_pcbc_exception(message, code TSRMLS_CC) TSRMLS_CC); -#define throw_lcb_exception(code) \ - zend_throw_exception_object(create_lcb_exception(code TSRMLS_CC) TSRMLS_CC); +void make_exception(zapval *ex, zend_class_entry *exception_ce, const char *message, long code TSRMLS_DC); +void make_pcbc_exception(zapval *ex, const char *message, long code TSRMLS_DC); +void make_lcb_exception(zapval *ex, long code, const char *msg TSRMLS_DC); + +#define throw_pcbc_exception(message, code) { \ + zapval zerror; \ + make_pcbc_exception(&zerror, message, code TSRMLS_CC); \ + zap_throw_exception_object(zerror); } +#define throw_lcb_exception(code) { \ + zapval zerror; \ + make_lcb_exception(&zerror, code, NULL TSRMLS_CC); \ + zap_throw_exception_object(zerror); } extern zend_class_entry *default_exception_ce; extern zend_class_entry *cb_exception_ce; diff --git a/metadoc.c b/metadoc.c index 1e3f918..488b80c 100644 --- a/metadoc.c +++ b/metadoc.c @@ -1,6 +1,7 @@ #include "bucket.h" #include "cas.h" #include "transcoding.h" +#include "exception.h" zend_class_entry *metadoc_ce; @@ -20,53 +21,35 @@ void couchbase_init_metadoc(INIT_FUNC_ARGS) { zend_declare_property_null(metadoc_ce, "cas", strlen("cas"), ZEND_ACC_PUBLIC TSRMLS_CC); } +int make_metadoc_error(zval *doc, lcb_error_t err TSRMLS_DC) +{ + zapval zerror; -int metadoc_from_error(zval *doc, zval *zerror TSRMLS_DC) { - object_init_ex(doc, metadoc_ce); - zend_update_property(metadoc_ce, doc, "error", sizeof("error") - 1, zerror TSRMLS_CC); - return SUCCESS; -} - -int metadoc_create(zval *doc, zval *value, lcb_cas_t cas, - lcb_uint32_t flags, lcb_uint8_t datatype TSRMLS_DC) { - zval *zcas, *zflags; - - object_init_ex(doc, metadoc_ce); - - if (value) { - zend_update_property(metadoc_ce, doc, "value", sizeof("value") - 1, value TSRMLS_CC); - } - - MAKE_STD_ZVAL(zflags); - ZVAL_LONG(zflags, flags); - zend_update_property(metadoc_ce, doc, "flags", sizeof("flags") - 1, zflags TSRMLS_CC); - zval_ptr_dtor(&zflags); - - zcas = cas_create(cas TSRMLS_CC); - zend_update_property(metadoc_ce, doc, "cas", sizeof("cas") - 1, zcas TSRMLS_CC); - zval_ptr_dtor(&zcas); + object_init_ex(doc, metadoc_ce); + make_lcb_exception(&zerror, err, NULL TSRMLS_CC); + zend_update_property(metadoc_ce, doc, "error", sizeof("error") - 1, + zapval_zvalptr(zerror) TSRMLS_CC); - return SUCCESS; + zapval_destroy(zerror); + return SUCCESS; } -int metadoc_from_long(zval *doc, lcb_U64 value, - lcb_cas_t cas, lcb_uint32_t flags, lcb_uint8_t datatype TSRMLS_DC) { - zval *zvalue; - MAKE_STD_ZVAL(zvalue); - ZVAL_LONG(zvalue, (long)value); - - return metadoc_create(doc, zvalue, cas, flags, datatype TSRMLS_CC); -} - -int metadoc_from_bytes(bucket_object *obj, zval *doc, const void *bytes, - lcb_size_t nbytes, lcb_cas_t cas, lcb_uint32_t flags, - lcb_uint8_t datatype TSRMLS_DC) { - int retval; - zval *zvalue; - pcbc_bytes_to_zval(obj, &zvalue, bytes, nbytes, flags, datatype TSRMLS_CC); - - retval = metadoc_create(doc, zvalue, cas, flags, datatype TSRMLS_CC); - - zval_ptr_dtor(&zvalue); - return retval; +int make_metadoc(zval *doc, zapval *value, zapval *flags, zapval *cas TSRMLS_DC) +{ + object_init_ex(doc, metadoc_ce); + + if (value) { + zend_update_property(metadoc_ce, doc, + "value", sizeof("value") - 1, zapval_zvalptr_p(value) TSRMLS_CC); + } + if (flags) { + zend_update_property(metadoc_ce, doc, + "flags", sizeof("flags") - 1, zapval_zvalptr_p(flags) TSRMLS_CC); + } + if (cas) { + zend_update_property(metadoc_ce, doc, + "cas", sizeof("cas") - 1, zapval_zvalptr_p(cas) TSRMLS_CC); + } + + return SUCCESS; } diff --git a/metadoc.h b/metadoc.h index b5083b2..283c793 100644 --- a/metadoc.h +++ b/metadoc.h @@ -7,16 +7,7 @@ void couchbase_init_metadoc(INIT_FUNC_ARGS); -int metadoc_create(zval *doc, zval *value, lcb_cas_t cas, - lcb_uint32_t flags, lcb_uint8_t datatype TSRMLS_DC); +int make_metadoc(zval *doc, zapval *value, zapval *flags, zapval *cas TSRMLS_DC); +int make_metadoc_error(zval *doc, lcb_error_t err TSRMLS_DC); -int metadoc_from_error(zval *doc, zval *zerror TSRMLS_DC); - -int metadoc_from_long(zval *doc, lcb_U64 value, - lcb_cas_t cas, lcb_uint32_t flags, lcb_uint8_t datatype TSRMLS_DC); - -int metadoc_from_bytes(bucket_object *obj, zval *doc, const void *bytes, - lcb_size_t nbytes, lcb_cas_t cas, lcb_uint32_t flags, - lcb_uint8_t datatype TSRMLS_DC); - -#endif // METADOC_H_ \ No newline at end of file +#endif // METADOC_H_ diff --git a/opcookie.c b/opcookie.c new file mode 100644 index 0000000..0150c01 --- /dev/null +++ b/opcookie.c @@ -0,0 +1,49 @@ +#include "opcookie.h" +#include "exception.h" +#include "metadoc.h" +#include "zap.h" + +opcookie * opcookie_init() +{ + return ecalloc(1, sizeof(opcookie)); +} + +void opcookie_destroy(opcookie *cookie) { + opcookie_res *iter = cookie->res_head; + while (iter != NULL) { + opcookie_res *cur = iter; + iter = cur->next; + efree(cur); + } + efree(cookie); +} + +lcb_error_t opcookie_get_first_error(opcookie *cookie) +{ + return cookie->first_error; +} + +void opcookie_push(opcookie *cookie, opcookie_res *res) +{ + if (cookie->res_head == NULL) { + cookie->res_head = res; + cookie->res_tail = res; + } else { + cookie->res_tail->next = res; + cookie->res_tail = res; + } + res->next = NULL; + + if (res->err != LCB_SUCCESS && cookie->first_error == LCB_SUCCESS) { + cookie->first_error = res->err; + } +} + +opcookie_res * opcookie_next_res(opcookie *cookie, opcookie_res *cur) +{ + if (cur == NULL) { + return cookie->res_head; + } else { + return cur->next; + } +} diff --git a/opcookie.h b/opcookie.h new file mode 100644 index 0000000..1104bec --- /dev/null +++ b/opcookie.h @@ -0,0 +1,29 @@ +#ifndef OPCOOKIE_H_ +#define OPCOOKIE_H_ + +#include +#include "couchbase.h" +#include "zap.h" + +typedef struct { + void *next; + lcb_error_t err; +} opcookie_res; + +typedef struct { + opcookie_res *res_head; + opcookie_res *res_tail; + lcb_error_t first_error; +} opcookie; + +opcookie * opcookie_init(); +void opcookie_destroy(opcookie *cookie); +void opcookie_push(opcookie *cookie, opcookie_res *res); +lcb_error_t opcookie_get_first_error(opcookie *cookie); +opcookie_res * opcookie_next_res(opcookie *cookie, opcookie_res *cur); + +#define FOREACH_OPCOOKIE_RES(Type, Res, cookie) \ + Res = NULL; \ + while ((Res = (Type*)opcookie_next_res(cookie, (opcookie_res*)Res)) != NULL) + +#endif /* OPCOOKIE_H_ */ diff --git a/package2.xml b/package2.xml index 44eb77c..986b2f6 100644 --- a/package2.xml +++ b/package2.xml @@ -60,7 +60,7 @@ - + diff --git a/paramparser.h b/paramparser.h index 3d38452..0b63c5c 100644 --- a/paramparser.h +++ b/paramparser.h @@ -3,10 +3,15 @@ #define PCBC_PP_MAX_ARGS 10 +typedef struct { + char *str; + uint len; +} pcbc_pp_id; + typedef struct { char name[16]; zval **ptr; - zval *val; + zapval val; } pcbc_pp_state_arg; typedef struct { @@ -15,30 +20,29 @@ typedef struct { int arg_opt; int arg_named; int cur_idx; - zval *zids; - zval tmpzid; + zapval zids; HashPosition hash_pos; } pcbc_pp_state; // assumes first parameter in the spec is the ids (`id|`). int pcbc_pp_begin(int param_count TSRMLS_DC, pcbc_pp_state *state, const char *spec, ...) { - zval **args[PCBC_PP_MAX_ARGS]; + zapval args[PCBC_PP_MAX_ARGS]; char arg_name[16]; const char *spec_iter = spec; char *arg_iter = arg_name; int arg_type = 0; int arg_num = 0; - zval *znamed; + zapval *znamed; int arg_unnamed; int ii; va_list vl; va_start(vl,spec); - if (_zend_get_parameters_array_ex(param_count, args TSRMLS_CC) != SUCCESS) { + if (zap_get_parameters_array_ex(param_count, args) != SUCCESS) { return FAILURE; } - state->zids = *args[0]; + state->zids = args[0]; state->cur_idx = 0; state->arg_req = 0; state->arg_opt = 0; @@ -49,14 +53,24 @@ int pcbc_pp_begin(int param_count TSRMLS_DC, pcbc_pp_state *state, const char *s if (arg_iter != arg_name) { pcbc_pp_state_arg *arg = &state->args[arg_num]; *arg_iter = 0; - + + // First arguement (id) is a special case... + if (arg_num == 0) { + // First arguement (id) is a special case... + if (strcmp(arg_name, "id") != 0) { + php_printf("First argument must be ID.\n"); + return FAILURE; + } + } + memcpy(arg->name, arg_name, 16); + arg->ptr = va_arg(vl, zval**); - if (arg_num > 0 && arg_num < param_count) { - arg->val = *args[arg_num]; + if (arg_num > 0 && arg_num < param_count && arg_type < 2) { + arg->val = args[arg_num]; } else { - arg->val = NULL; + zapval_undef(arg->val); } if (arg_type == 0) { @@ -87,16 +101,18 @@ int pcbc_pp_begin(int param_count TSRMLS_DC, pcbc_pp_state *state, const char *s } while(1); if (param_count < state->arg_req) { + // TODO: This should not printf... php_printf("Less than required number of args.\n"); return FAILURE; } arg_unnamed = state->arg_req + state->arg_opt; + if (param_count > arg_unnamed) { - znamed = *args[arg_unnamed]; + znamed = &args[arg_unnamed]; // Ensure that it is an options array! - if (Z_TYPE_P(znamed) != IS_ARRAY) { + if (!zapval_is_array(*znamed)) { php_printf("Options argument must be an associative array.\n"); return FAILURE; } @@ -109,38 +125,43 @@ int pcbc_pp_begin(int param_count TSRMLS_DC, pcbc_pp_state *state, const char *s pcbc_pp_state_arg *arg = &state->args[aii]; if (znamed) { - HashTable *htoptions = Z_ARRVAL_P(znamed); - zval **zvalue; + HashTable *htoptions = zapval_arrval(*znamed); + zval *zvalue = zap_hash_str_find( + htoptions, arg->name, strlen(arg->name)); - if (zend_hash_find(htoptions, arg->name, strlen(arg->name)+1, - (void**)&zvalue) == SUCCESS) { - arg->val = *zvalue; + if (zvalue) { + arg->val = zapval_from_zvalptr(zvalue); } else { - arg->val = NULL; + zapval_undef(arg->val); } } else { - arg->val = NULL; + zapval_undef(arg->val); } } - if (Z_TYPE_P(state->zids) == IS_ARRAY) { + if (zapval_is_array(state->zids)) { // If this is an array, make sure its internal pointer is the start. - HashTable *hash = Z_ARRVAL_P(state->zids); + HashTable *hash = zapval_arrval(state->zids); zend_hash_internal_pointer_reset_ex(hash, &state->hash_pos); + } else if (zapval_is_string(state->zids)) { + // Nothing to configure for basic string + } else { + // Definitely an error + return FAILURE; } return SUCCESS; } int pcbc_pp_ismapped(pcbc_pp_state *state) { - return Z_TYPE_P(state->zids) != IS_STRING; + return !zapval_is_string(state->zids); } int pcbc_pp_keycount(pcbc_pp_state *state) { - if (Z_TYPE_P(state->zids) == IS_STRING) { + if (zapval_is_string(state->zids)) { return 1; - } else if (Z_TYPE_P(state->zids) == IS_ARRAY) { - return zend_hash_num_elements(Z_ARRVAL_P(state->zids)); + } else if (zapval_is_array(state->zids)) { + return zend_hash_num_elements(zapval_arrval(state->zids)); } else { return 0; } @@ -149,58 +170,69 @@ int pcbc_pp_keycount(pcbc_pp_state *state) { int pcbc_pp_next(pcbc_pp_state *state) { int ii; int arg_total = state->arg_req + state->arg_opt + state->arg_named; + pcbc_pp_id *id_ptr = (pcbc_pp_id*)state->args[0].ptr; // Set everything to 'base' values for (ii = 1; ii < arg_total; ++ii) { - *(state->args[ii].ptr) = state->args[ii].val; + if (zapval_is_undef(state->args[ii].val)) { + *(state->args[ii].ptr) = NULL; + } else { + *(state->args[ii].ptr) = zapval_zvalptr(state->args[ii].val); + } } - if (Z_TYPE_P(state->zids) == IS_ARRAY) { - HashTable *hash = Z_ARRVAL_P(state->zids); - zval **data; + if (zapval_is_array(state->zids)) { + HashTable *hash = zapval_arrval(state->zids); + zapval *data; + zend_ulong keyidx, key_type; char *keystr; - uint keystr_len, key_type; - ulong keyidx; + uint keystr_len; - if (zend_hash_get_current_data_ex(hash, (void**) &data, &state->hash_pos) != SUCCESS) { + data = zap_hash_get_current_data_ex(hash, &state->hash_pos); + if (data == 0) { return 0; } - key_type = zend_hash_get_current_key_ex(hash, &keystr, &keystr_len, &keyidx, 0, &state->hash_pos); + key_type = zap_hash_str_get_current_key_ex( + hash, &keystr, &keystr_len, &keyidx, &state->hash_pos); if (key_type == HASH_KEY_IS_STRING) { - ZVAL_STRINGL(&state->tmpzid, keystr, keystr_len-1, 0); - *(state->args[0].ptr) = &state->tmpzid; + id_ptr->str = keystr; + id_ptr->len = keystr_len; - if (Z_TYPE_PP(data) == IS_ARRAY) { - HashTable *htdata = Z_ARRVAL_PP(data); - zval **zvalue; + if (zapval_is_array(*data)) { + HashTable *htdata = zapval_arrval(*data); + zval *zvalue; for (ii = 1; ii < arg_total; ++ii) { pcbc_pp_state_arg * arg = &state->args[ii]; - if (zend_hash_find(htdata, arg->name, strlen(arg->name)+1, - (void**)&zvalue) == SUCCESS) { - *(arg->ptr) = *zvalue; + zvalue = zap_hash_str_find( + htdata, arg->name, strlen(arg->name)); + if (zvalue != NULL) { + *(arg->ptr) = zvalue; } } } } else if (key_type == HASH_KEY_IS_LONG) { - *(state->args[0].ptr) = *data; + id_ptr->str = zapval_strval_p(data); + id_ptr->len = zapval_strlen_p(data); } zend_hash_move_forward_ex(hash, &state->hash_pos); return 1; - } else { + } else if (zapval_is_string(state->zids)) { if (state->cur_idx > 0) { return 0; } - *(state->args[0].ptr) = state->zids; + id_ptr->str = zapval_strval_p(&state->zids); + id_ptr->len = zapval_strlen_p(&state->zids); state->cur_idx++; return 1; + } else { + // Invalid type for state->zids + return 0; } - - return 0; } -#endif /* PARAMPARSER_H_ */ +#endif diff --git a/phphelpers.h b/phphelpers.h deleted file mode 100644 index 17e4213..0000000 --- a/phphelpers.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef PHPHELPERS_H_ -#define PHPHELPERS_H_ - -#include - -#if PHP_VERSION_ID >= 50400 -#define phlp_object_properties_init object_properties_init -#else -static void phlp_object_properties_init(zend_object *obj, zend_class_entry* type) { - zval *tmp; - zend_hash_copy(obj->properties, &type->default_properties, - (copy_ctor_func_t)zval_add_ref, (void *)&tmp, sizeof(zval *)); -} -#endif - -#endif // PHPHELPERS_H_ diff --git a/tests/BucketTest.php b/tests/BucketTest.php index 7ebe5bf..b33bfe1 100644 --- a/tests/BucketTest.php +++ b/tests/BucketTest.php @@ -24,7 +24,7 @@ function testBadBucket() { $this->wrapException(function() use($h) { $h->openBucket('bad_bucket'); - }, 'CouchbaseException', 25); + }, 'CouchbaseException'); } /** @@ -284,6 +284,50 @@ function testNoKeyUpsert($b) { } */ + /** + * @test + * Test recursive transcoder functions + */ + function testRecursiveTranscode() { + global $recursive_transcoder_bucket; + global $recursive_transcoder_key2; + global $recursive_transcoder_key3; + + $h = new CouchbaseCluster($this->testDsn); + $b = $h->openBucket(); + + $key1 = $this->makeKey('basicUpsertKey1'); + $key2 = $this->makeKey('basicUpsertKey2'); + $key3 = $this->makeKey('basicUpsertKey3'); + + // Set up a transcoder that upserts key2 when it sees key1 + $recursive_transcoder_bucket = $b; + $recursive_transcoder_key2 = $key2; + $recursive_transcoder_key3 = $key3; + $b->setTranscoder( + 'recursive_transcoder_encoder', // defined at bottom of file + 'recursive_transcoder_decoder'); // defined at bottom of file + + // Upsert key1, transcoder should set key2 + $res = $b->upsert($key1, 'key1'); + $this->assertValidMetaDoc($res, 'cas'); + + // Check key1 was upserted + $res = $b->get($key1); + $this->assertValidMetaDoc($res, 'cas'); + $this->assertEquals($res->value, 'key1'); + + // Check key2 was upserted, trasncoder should set key3 + $res = $b->get($key2); + $this->assertValidMetaDoc($res, 'cas'); + $this->assertEquals($res->value, 'key2'); + + // Check key3 was upserted + $res = $b->get($key3); + $this->assertValidMetaDoc($res, 'cas'); + $this->assertEquals($res->value, 'key3'); + } + /** * @test * Test all option values to make sure they save/load @@ -345,3 +389,23 @@ function testConfigCache() { } } + +function recursive_transcoder_encoder($value) { + global $recursive_transcoder_bucket; + global $recursive_transcoder_key2; + if ($value == 'key1') { + $recursive_transcoder_bucket->upsert( + $recursive_transcoder_key2, 'key2'); + } + return couchbase_default_encoder($value); +} +function recursive_transcoder_decoder($bytes, $flags, $datatype) { + global $recursive_transcoder_bucket; + global $recursive_transcoder_key3; + $value = couchbase_default_decoder($bytes, $flags, $datatype); + if ($value == 'key2') { + $recursive_transcoder_bucket->upsert( + $recursive_transcoder_key3, 'key3'); + } + return $value; +} diff --git a/transcoding.c b/transcoding.c index 64da086..bf1eabb 100644 --- a/transcoding.c +++ b/transcoding.c @@ -1,70 +1,70 @@ #include "transcoding.h" +#include "zap.h" -int pcbc_bytes_to_zval(bucket_object *obj, zval **zvalue, const void *bytes, - lcb_size_t nbytes, lcb_uint32_t flags, lcb_uint8_t datatype TSRMLS_DC) { - zval zbytes, zflags, zdatatype; - zval *zparams[] = { &zbytes, &zflags, &zdatatype }; +int pcbc_decode_value(bucket_object *bucket, zapval *zvalue, + zapval *zbytes, zapval *zflags, zapval *zdatatype TSRMLS_DC) { + zapval zparams[] = { *zbytes, *zflags, *zdatatype }; - INIT_ZVAL(zbytes); - if (nbytes > 0) { - ZVAL_STRINGL(&zbytes, bytes, nbytes, 0); - } else { - ZVAL_STRINGL(&zbytes, "", 0, 0); - } + if (call_user_function(CG(function_table), NULL, + zapval_zvalptr(bucket->decoder), zapval_zvalptr_p(zvalue), + 3, zparams TSRMLS_CC) != SUCCESS) + { + return FAILURE; + } - INIT_ZVAL(zflags); - ZVAL_LONG(&zflags, flags); - - INIT_ZVAL(zdatatype); - ZVAL_LONG(&zdatatype, datatype); - - MAKE_STD_ZVAL(*zvalue); - if (call_user_function(CG(function_table), NULL, obj->decoder, *zvalue, - 3, zparams TSRMLS_CC) != SUCCESS) { - return FAILURE; - } - - return SUCCESS; + return SUCCESS; } -int pcbc_zval_to_bytes(bucket_object *obj, zval *value, - const void **bytes, lcb_size_t *nbytes, lcb_uint32_t *flags, - lcb_uint8_t *datatype TSRMLS_DC) { - zval zretval, **zpbytes, **zpflags, **zpdatatype; - zval *zparams[] = { value }; - HashTable *retval; +int pcbc_encode_value(bucket_object *bucket, zval *value, + const void **bytes, lcb_size_t *nbytes, lcb_uint32_t *flags, + lcb_uint8_t *datatype TSRMLS_DC) { + zapval *zpbytes, *zpflags, *zpdatatype; + zapval zretval; + HashTable *retval; + + zapval_alloc_null(zretval); - if (call_user_function(CG(function_table), NULL, obj->encoder, &zretval, - 1, zparams TSRMLS_CC) != SUCCESS) { - return FAILURE; - } + if (call_user_function(CG(function_table), NULL, + zapval_zvalptr(bucket->encoder), zapval_zvalptr(zretval), + 1, zapvalptr_from_zvalptr(value) TSRMLS_CC) != SUCCESS) { + zapval_destroy(zretval); + return FAILURE; + } - retval = Z_ARRVAL(zretval); + if (!zapval_is_array(zretval)) { + zapval_destroy(zretval); + return FAILURE; + } - if (zend_hash_num_elements(retval) != 3) { - return FAILURE; - } + retval = zapval_arrval(zretval); - zend_hash_index_find(retval, 0, (void**)&zpbytes); - zend_hash_index_find(retval, 1, (void**)&zpflags); - zend_hash_index_find(retval, 2, (void**)&zpdatatype); + if (zend_hash_num_elements(retval) != 3) { + zapval_destroy(zretval); + return FAILURE; + } - if (Z_TYPE_PP(zpbytes) != IS_STRING) { - return FAILURE; - } - if (Z_TYPE_PP(zpflags) != IS_LONG) { - return FAILURE; - } - if (Z_TYPE_PP(zpdatatype) != IS_LONG) { - return FAILURE; - } + zpbytes = zap_hash_index_find(retval, 0); + zpflags = zap_hash_index_find(retval, 1); + zpdatatype = zap_hash_index_find(retval, 2); - *nbytes = Z_STRLEN_PP(zpbytes); - *bytes = estrndup(Z_STRVAL_PP(zpbytes), *nbytes); - *flags = Z_LVAL_PP(zpflags); - *datatype = (lcb_uint8_t)Z_LVAL_PP(zpdatatype); + if (!zapval_is_string_p(zpbytes)) { + zapval_destroy(zretval); + return FAILURE; + } + if (!zapval_is_long_p(zpflags)) { + zapval_destroy(zretval); + return FAILURE; + } + if (!zapval_is_long_p(zpdatatype)) { + zapval_destroy(zretval); + return FAILURE; + } - zval_dtor(&zretval); + *nbytes = zapval_strlen_p(zpbytes); + *bytes = estrndup(zapval_strval_p(zpbytes), *nbytes); + *flags = zapval_lval_p(zpflags); + *datatype = (lcb_uint8_t)zapval_lval_p(zpdatatype); - return SUCCESS; + zapval_destroy(zretval); + return SUCCESS; } diff --git a/transcoding.h b/transcoding.h index e86c578..9e255d8 100644 --- a/transcoding.h +++ b/transcoding.h @@ -3,13 +3,14 @@ #include #include +#include "zap.h" #include "bucket.h" -int pcbc_bytes_to_zval(bucket_object *obj, zval **zvalue, const void *bytes, - lcb_size_t nbytes, lcb_uint32_t flags, lcb_uint8_t datatype TSRMLS_DC); +int pcbc_decode_value(bucket_object *bucket, zapval *zvalue, + zapval *zbytes, zapval *zflags, zapval *zdatatype TSRMLS_DC); -int pcbc_zval_to_bytes(bucket_object *obj, zval *value, +int pcbc_encode_value(bucket_object *bucket, zval *value, const void **bytes, lcb_size_t *nbytes, lcb_uint32_t *flags, lcb_uint8_t *datatype TSRMLS_DC); -#endif // TRANSCODING_H_ \ No newline at end of file +#endif // TRANSCODING_H_ diff --git a/zap.h b/zap.h new file mode 100644 index 0000000..6a1e3ef --- /dev/null +++ b/zap.h @@ -0,0 +1,480 @@ +/** + * Zend Abstractions for Php + */ + +#ifndef ZAP_H_ +#define ZAP_H_ + +#include + +typedef struct _zap_class_entry { +#if PHP_VERSION_ID >= 70000 + zend_object* (*create_obj)(zend_class_entry *class_type); + zend_object_free_obj_t free_obj; + zend_object_dtor_obj_t dtor_obj; + zend_object_clone_obj_t clone_obj; +#else + zend_object_value (*create_obj)(zend_class_entry *type TSRMLS_DC); + zend_objects_free_object_storage_t free_obj; + zend_objects_store_dtor_t dtor_obj; + zend_objects_store_clone_t clone_obj; +#endif + + struct { + zend_class_entry ce; + zend_object_handlers handlers; + } _internal; +} zap_class_entry; + +#define zap_init_class_entry(cls, name, methods) \ + memset((cls), 0, sizeof(zap_class_entry));\ + INIT_CLASS_ENTRY((cls)->_internal.ce, name, methods) + +#if PHP_VERSION_ID >= 70000 +static zend_class_entry * _zap_register_internal_class(zap_class_entry *cls, int offset TSRMLS_DC) { + zend_class_entry *ce; + + memcpy(&cls->_internal.handlers, + zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + cls->_internal.handlers.offset = offset; + + if (cls->free_obj != NULL) { + cls->_internal.handlers.free_obj = cls->free_obj; + } + if (cls->dtor_obj != NULL) { + cls->_internal.handlers.dtor_obj = cls->dtor_obj; + } + if (cls->clone_obj != NULL) { + cls->_internal.handlers.clone_obj = cls->clone_obj; + } + + cls->_internal.ce.create_object = cls->create_obj; + ce = zend_register_internal_class(&cls->_internal.ce TSRMLS_CC); + return ce; +} +#define zap_register_internal_class(cls, Type) \ + _zap_register_internal_class(cls, XtOffsetOf(struct Type, std) TSRMLS_CC) + +static inline zend_object * _zap_finalize_object(zend_object *std, zap_class_entry* cls) { + std->handlers = &(cls)->_internal.handlers; + return std; +} +#define zap_finalize_object(obj, cls) \ + _zap_finalize_object(&obj->std, cls) + +#else + +static zend_class_entry * _zap_register_internal_class(zap_class_entry *cls TSRMLS_DC) { + zend_class_entry *ce; + + memcpy(&cls->_internal.handlers, + zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + + cls->_internal.ce.create_object = cls->create_obj; + ce = zend_register_internal_class(&cls->_internal.ce TSRMLS_CC); + return ce; +} +#define zap_register_internal_class(cls, Type) \ + _zap_register_internal_class(cls TSRMLS_CC) + +static inline zend_object_value _zap_finalize_object(void *obj, zap_class_entry* cls TSRMLS_DC) { + zend_object_value retval; + retval.handle = zend_objects_store_put( + obj, cls->dtor_obj, cls->free_obj, cls->clone_obj TSRMLS_CC); + retval.handlers = &(cls)->_internal.handlers; + return retval; +} +#define zap_finalize_object(obj, cls) \ + _zap_finalize_object(obj, cls TSRMLS_CC) + +#endif + + +#if PHP_VERSION_ID >= 70000 + +#define zap_DTORRES_FUNC(name) \ + void name(zend_resource *rsrc TSRMLS_DC) + +#define zap_fetch_resource(z, name, le) \ + zend_fetch_resource_ex(z, name, le) + +#else + +#define zap_DTORRES_FUNC(name) \ + void name(zend_rsrc_list_entry *rsrc TSRMLS_DC) + +#define zap_fetch_resource(z, name, le) \ + zend_fetch_resource(&z TSRMLS_CC, -1, name, NULL, 1, le) + +#endif + +#if PHP_VERSION_ID >= 70000 + +#define zap_ZEND_OBJECT_START +#define zap_ZEND_OBJECT_END zend_object std; + +#define zap_get_object(Type, object) ((struct Type *)((char*)(object) - XtOffsetOf(struct Type, std))) +#define zap_fetch_this(Type) zap_get_object(Type, Z_OBJ_P(getThis())) + +#define zap_CREATEOBJ_FUNC(name) \ + zend_object * name(zend_class_entry *type TSRMLS_DC) +#define zap_FREEOBJ_FUNC(name) \ + void name(zend_object *object TSRMLS_DC) + +#define zap_alloc_object_storage(Type, type) \ + ((Type *)ecalloc(1, sizeof(Type) + zend_object_properties_size(type))) +#define zap_free_object_storage(o) + +#else + +#define zap_ZEND_OBJECT_START zend_object std; +#define zap_ZEND_OBJECT_END + +#define zap_get_object(Type, object) ((Type*)object) +#define zap_fetch_this(Type) ((Type*)zend_object_store_get_object(getThis() TSRMLS_CC)) + +#define zap_CREATEOBJ_FUNC(name) \ + zend_object_value name(zend_class_entry *type TSRMLS_DC) +#define zap_FREEOBJ_FUNC(name) \ + void name(void *object TSRMLS_DC) + +#define zap_alloc_object_storage(Type, type) \ + ((Type *)ecalloc(1, sizeof(Type))) +#define zap_free_object_storage(o) efree(o) + +#endif + +#if PHP_VERSION_ID >= 70000 +typedef zval zapval; + +#define zapval_zvalptr(v) (&v) +#define zapval_zvalptr_p(v) (v) + +#define zapvalptr_from_zvalptr(v) (v) +#define zapval_from_zvalptr(v) (*v) + +// These all set a zval* to something +#define zap_zval_undef_p(v) \ + ZVAL_UNDEF(v) +#define zap_zval_null_p(v) \ + ZVAL_NULL(v) +#define zap_zval_bool_p(v, b) \ + if (b) { ZVAL_TRUE(v); } else { ZVAL_FALSE(v); } +#define zap_zval_empty_string_p(v) \ + ZVAL_EMPTY_STRING(v) +#define zap_zval_stringl_p(v, b, nb) \ + if (b != NULL || nb > 0) { ZVAL_STRINGL(v, b, nb); } \ + else { ZVAL_EMPTY_STRING(v); } +#define zap_zval_long_p(v, n) \ + ZVAL_LONG(v, n) +#define zap_zval_array_p(v) \ + array_init(v) +#define zap_zval_zval_p(v, z, copy, dtor) \ + ZVAL_ZVAL(v, z, copy, dtor) +#define zap_zval_res_p(v, ptr, type) \ + ZVAL_RES(v, zend_register_resource(ptr, type)) + +#define zap_zval_is_undef(v) \ + (Z_TYPE_P(v) == IS_UNDEF) +#define zap_zval_is_bool(v) \ + (Z_TYPE_P(v) == IS_TRUE || Z_TYPE_P(v) == IS_FALSE) +#define zap_zval_is_array(v) \ + (Z_TYPE_P(v) == IS_ARRAY) + +#define zap_zval_boolval(v) \ + (Z_TYPE_P(v) == IS_TRUE ? 1 : 0) + +// These all set a zapval to something +#define zapval_undef(v) \ + zap_zval_undef_p(&v) +#define zapval_null(v) \ + zap_zval_null_p(&v) +#define zapval_empty_string(v) \ + zap_zval_empty_string_p(&v) +#define zapval_stringl(v, b, nb) \ + zap_zval_stringl_p(&v, b, nb) +#define zapval_long(v, n) \ + zap_zval_long_p(&v, n) +#define zapval_array(v) \ + zap_zval_array_p(&v) +#define zapval_zval(v, z, copy, dtor) \ + zap_zval_zval_p(&v, z, copy, dtor) +#define zapval_res(v, ptr, type) \ + zap_zval_res_p(&v, ptr, type) + +// These all allocate and set a zapval to something +#define zapval_alloc(v) \ + zapval_undef(v) +#define zapval_addref(v) \ + Z_TRY_ADDREF(v) +#define zapval_destroy(v) \ + zval_ptr_dtor(&v) + +#define zapval_is_undef(v) (Z_TYPE(v) == IS_UNDEF) +#define zapval_is_bool(v) (Z_TYPE(v) == IS_TRUE || Z_TYPE(v) == IS_FALSE) +#define zapval_is_string(v) (Z_TYPE(v) == IS_STRING) +#define zapval_is_long(v) (Z_TYPE(v) == IS_LONG) +#define zapval_is_array(v) (Z_TYPE(v) == IS_ARRAY) + +#define zapval_arrval(v) Z_ARRVAL(v) + +#define zapval_strlen_p(v) Z_STRLEN_P(v) +#define zapval_strval_p(v) Z_STRVAL_P(v) +#define zapval_lval_p(v) Z_LVAL_P(v) + +#define zap_throw_exception_object(o) zend_throw_exception_object(&o TSRMLS_CC) + +// int param_count, zapval *args +#define zap_get_parameters_array_ex(param_count, args) \ + _zend_get_parameters_array_ex(param_count, args TSRMLS_CC) + +#define zap_call_user_function(o, f, r, np, p) \ + call_user_function(CG(function_table), o, &f, r, np, p TSRMLS_CC) + +#define zap_hash_str_add(ht, k, nk, hv) \ + zend_hash_str_add(ht, k, nk, hv) +#define zap_hash_next_index_insert(ht, hv) \ + zend_hash_next_index_insert(ht, hv) +#define zap_hash_str_find(ht, k, nk) \ + zend_hash_str_find(ht, k, nk) +#define zap_hash_index_find(ht, i) \ + zend_hash_index_find(ht, i) +#define zap_hash_get_current_data_ex(ht, pos) \ + zend_hash_get_current_data_ex(ht, pos) + +static inline int zap_hash_str_get_current_key_ex(HashTable *ht, char **str, + uint *len, zend_ulong *num_index, HashPosition *pos) { + zend_string *zstr = NULL; + int key_type = zend_hash_get_current_key_ex(ht, &zstr, num_index, pos); + if (zstr != NULL) { + *str = zstr->val; + *len = zstr->len; + } else { + *str = NULL; + *len = 0; + } + return key_type; +} + +#else +typedef zval* zapval; + +#define zapval_zvalptr(v) (v) +#define zapval_zvalptr_p(v) (*v) + +#define zapvalptr_from_zvalptr(v) (&v) +#define zapval_from_zvalptr(v) (v) + +// TODO: v=NULL will leak memory! + +// These all set a zval* to something +#define zap_zval_undef_p(v) \ + v = NULL; +#define zap_zval_null_p(v) \ + ZVAL_NULL(v) +#define zap_zval_bool_p(v, b) \ + if (b) { ZVAL_TRUE(v); } else { ZVAL_FALSE(v); } +#define zap_zval_empty_string_p(v) \ + ZVAL_EMPTY_STRING(v) +#define zap_zval_stringl_p(v, b, nb) \ + if (b != NULL || nb > 0) { ZVAL_STRINGL(v, b, nb, 1); } \ + else { ZVAL_EMPTY_STRING(v); } +#define zap_zval_long_p(v, n) \ + ZVAL_LONG(v, n) +#define zap_zval_array_p(v) \ + array_init(v) +#define zap_zval_zval_p(v, z, copy, dtor) \ + ZVAL_ZVAL(v, z, copy, dtor) +#define zap_zval_res_p(v, ptr, type) \ + ZEND_REGISTER_RESOURCE(v, ptr, type) + +#define zap_zval_is_undef(v) \ + (v == NULL) +#define zap_zval_is_bool(v) \ + (Z_TYPE_P(v) == IS_BOOL) +#define zap_zval_is_array(v) \ + (Z_TYPE_P(v) == IS_ARRAY) + +#define zap_zval_boolval(v) \ + Z_BVAL_P(v) + +// These all set a zapval to something +#define zapval_undef(v) \ + zap_zval_undef_p(v) +#define zapval_null(v) \ + zap_zval_null_p(v) +#define zapval_empty_string(v) \ + zap_zval_empty_string_p(v) +#define zapval_stringl(v, b, nb) \ + zap_zval_stringl_p(v, b, nb) +#define zapval_long(v, n) \ + zap_zval_long_p(v, n) +#define zapval_array(v) \ + zap_zval_array_p(v) +#define zapval_zval(v, z, copy, dtor) \ + zap_zval_zval_p(v, z, copy, dtor) +#define zapval_res(v, ptr, type) \ + zap_zval_res_p(v, ptr, type) + +// These all allocate and set a zapval to something +#define zapval_alloc(v) \ + MAKE_STD_ZVAL(v) +#define zapval_addref(v) \ + Z_ADDREF_P(v) +#define zapval_destroy(v) \ + zval_ptr_dtor(&v) + +#define zapval_is_undef(v) (v == NULL) +#define zapval_is_bool(v) (Z_TYPE_P(v) == IS_BOOL) +#define zapval_is_string(v) (Z_TYPE_P(v) == IS_STRING) +#define zapval_is_long(v) (Z_TYPE_P(v) == IS_LONG) +#define zapval_is_array(v) (Z_TYPE_P(v) == IS_ARRAY) + +#define zapval_arrval(v) Z_ARRVAL_P(v) + +#define zapval_strlen_p(v) Z_STRLEN_PP(v) +#define zapval_strval_p(v) Z_STRVAL_PP(v) +#define zapval_lval_p(v) Z_LVAL_PP(v) + +#define zap_throw_exception_object(o) zend_throw_exception_object(o TSRMLS_CC) + +static inline int _zap_get_parameters_array_ex(int param_count, zapval *args TSRMLS_DC) +{ + if (param_count <= 16) { + int i; + zval **_args[16]; + int retval = _zend_get_parameters_array_ex(param_count, _args TSRMLS_CC); + for (i = 0; i < param_count; ++i) { + args[i] = *_args[i]; + } + return retval; + } else { + int i; + zval ***_args = emalloc(param_count * sizeof(zval**)); + int retval = _zend_get_parameters_array_ex(param_count, _args TSRMLS_CC); + for (i = 0; i < param_count; ++i) { + args[i] = *_args[i]; + } + efree(_args); + return retval; + } +} +#define zap_get_parameters_array_ex(param_count, args) \ + _zap_get_parameters_array_ex(param_count, args TSRMLS_CC) + +#define zap_call_user_function(o, f, r, np, p) \ + call_user_function(CG(function_table), o, f, r, np, p TSRMLS_CC) + +static inline zval * _zap_hash_str_add(HashTable *ht, char *key, size_t len, zval *pData) { + if (zend_hash_add(ht, key, len + 1, (void*)&pData, sizeof(zval**), NULL) != SUCCESS) { + return NULL; + } + return pData; +} +#define zap_hash_str_add(ht, k, nk, hv) \ + _zap_hash_str_add(ht, k, nk, hv) + +static inline zval * _zap_hash_next_index_insert(HashTable *ht, zval *pData) { + if (zend_hash_next_index_insert( + ht, (void*)&pData, sizeof(zval**), NULL) != SUCCESS) { + return NULL; + } + return pData; +} +#define zap_hash_next_index_insert(ht, hv) \ + _zap_hash_next_index_insert(ht, hv) + +static inline zval * _zap_hash_str_find(HashTable *ht, char *key, size_t len) { + zval **result = NULL; + if (zend_hash_find(ht, key, len+1, (void**)&result) != SUCCESS) { + return NULL; + } + return *result; +} +#define zap_hash_str_find(ht, k, nk) \ + _zap_hash_str_find(ht, k, nk) + +static inline zapval * _zap_hash_index_find(HashTable *ht, ulong i) { + zval **result; + if (zend_hash_index_find(ht, i, (void**)&result) != SUCCESS) { + return NULL; + } + return result; +} +#define zap_hash_index_find(ht, i) \ + _zap_hash_index_find(ht, i) + +static inline zapval * _zap_hash_get_current_data_ex(HashTable *ht, HashPosition *pos) { + zval **result; + if (zend_hash_get_current_data_ex(ht, (void**)&result, pos) != SUCCESS) { + return NULL; + } + return result; +} +#define zap_hash_get_current_data_ex(ht, pos) \ + _zap_hash_get_current_data_ex(ht, pos) + +static inline int zap_hash_str_get_current_key_ex(HashTable *ht, char **str, + uint *len, zend_ulong *num_index, HashPosition *pos) { + uint len_out = 0; + int key_type = zend_hash_get_current_key_ex(ht, str, &len_out, num_index, 0, pos); + if (len != NULL) { + *len = len_out - 1; + } + return key_type; +} + +#endif + +#define zapval_alloc_null(v) \ + zapval_alloc(v); \ + zapval_null(v) +#define zapval_alloc_empty_string(v) \ + zapval_alloc(v); \ + zapval_empty_string(v) +#define zapval_alloc_stringl(v, b, nb) \ + zapval_alloc(v); \ + zapval_stringl(v, b, nb) +#define zapval_alloc_long(v, n) \ + zapval_alloc(v); \ + zapval_long(v, n) +#define zapval_alloc_array(v) \ + zapval_alloc(v); \ + zapval_array(v) +#define zapval_alloc_zval(v, z, copy, dtor) \ + zapval_alloc(v); \ + zapval_zval(v, z, copy, dtor) +#define zapval_alloc_res(v, ptr, type) \ + zapval_alloc(v); \ + zapval_res(v, ptr, type) + +#define zapval_is_undef_p(v) zapval_is_undef(*v) +#define zapval_is_bool_p(v) zapval_is_bool(*v) +#define zapval_is_string_p(v) zapval_is_string(*v) +#define zapval_is_long_p(v) zapval_is_long(*v) + +#if PHP_VERSION_ID >= 50400 +#define zap_object_properties_init object_properties_init +#else +static void zap_object_properties_init(zend_object *obj, zend_class_entry* type) { + zval *tmp; + ALLOC_HASHTABLE(obj->properties); + zend_hash_init(obj->properties, 0, NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(obj->properties, &type->default_properties, + (copy_ctor_func_t)zval_add_ref, (void *)&tmp, sizeof(zval *)); +} +#endif + +#if ZEND_MODULE_API_NO >= 20151012 +#define zap_zend_register_internal_class_ex(ce, parent_ce) zend_register_internal_class_ex(ce, parent_ce TSRMLS_CC) +#else +#define zap_zend_register_internal_class_ex(ce, parent_ce) zend_register_internal_class_ex(ce, parent_ce, NULL TSRMLS_CC) +#endif + +#if ZEND_MODULE_API_NO >= 20060613 +#define zap_zend_exception_get_default() zend_exception_get_default(TSRMLS_C) +#else +#define zap_zend_exception_get_default() zend_exception_get_default() +#endif + +#endif // ZAP_H_