X-Git-Url: https://git.rapsys.eu/bbcode/blobdiff_plain/72cacff6db32c7def98fd82e2abd60c74ba6913a..6c4c81d7f5790de40252709342d896ce3f50cf0a:/bbcode.c?ds=inline diff --git a/bbcode.c b/bbcode.c index 3a9ac76..5810aae 100644 --- a/bbcode.c +++ b/bbcode.c @@ -9,20 +9,22 @@ #include "Zend/zend_exceptions.h" #include "php_bbcode.h" +#include + /* If you declare any globals in php_bbcode.h uncomment this: ZEND_DECLARE_MODULE_GLOBALS(bbcode) */ typedef struct _bbcode_object { - /*zval *tag; - zval *smiley; - zval *flag;*/ - zend_long flag; - zend_object_handlers bbcode_handlers; + HashTable *tag; + HashTable *smiley; + zval flag; zend_object std; } bbcode_object; /* True global resources - no need for thread safety here */ +zend_object_handlers bbcode_handlers; + /* BBCode object */ zend_class_entry *bbcode_ce; @@ -30,13 +32,18 @@ zend_class_entry *bbcode_ce; zend_class_entry *bbcode_exception_ce; /* Define all constants */ -#define BBCODE_DEF(GEN) \ - /* Types */ \ +#ifdef old_code_dropped GEN(TYPE_ARG) /*BBCODE_TYPE_ARG*/\ GEN(TYPE_NOARG) /*BBCODE_TYPE_NOARG*/\ - GEN(TYPE_SINGLE) /*BBCODE_TYPE_SINGLE*/\ GEN(TYPE_OPTARG) /*BBCODE_TYPE_OPTARG*/\ + +#endif + +#define BBCODE_DEF(GEN) \ + /* Types */ \ GEN(TYPE_ROOT) /*BBCODE_TYPE_ROOT*/\ + GEN(TYPE_MULTI) /*BBCODE_TYPE_ARG|BBCODE_TYPE_OPTARG|BBCODE_TYPE_NOARG*/\ + GEN(TYPE_SINGLE) /*BBCODE_TYPE_SINGLE*/\ \ /* Quotes */ \ GEN(QUOTE_DOUBLE) /*BBCODE_ARG_DOUBLE_QUOTE*/\ @@ -66,6 +73,31 @@ zend_class_entry *bbcode_exception_ce; /* Generate zend const version */ #define BBCODE_GEN_CONST(NAME) zend_declare_class_constant_long(bbcode_ce, #NAME, sizeof(#NAME) - 1, BBCODE_ ## NAME TSRMLS_CC); +/* Defined in /usr/include/php/Zend/zend_types.h +382 */ +#define BBCODE_GET_TYPE(TYPE) (TYPE==IS_UNDEF)?"undef":( \ + (TYPE==IS_NULL)?"null":( \ + (TYPE==IS_FALSE)?"false":( \ + (TYPE==IS_TRUE)?"true":( \ + (TYPE==IS_LONG)?"long":( \ + (TYPE==IS_DOUBLE)?"double":( \ + (TYPE==IS_STRING)?"string":( \ + (TYPE==IS_ARRAY)?"array":( \ + (TYPE==IS_OBJECT)?"object":( \ + (TYPE==IS_RESOURCE)?"resource":( \ + (TYPE==IS_REFERENCE)?"reference":( \ + (TYPE==IS_CONSTANT_AST)?"constant":"unknown"\ + ) \ + ) \ + ) \ + ) \ + ) \ + ) \ + ) \ + ) \ + ) \ + ) \ + ) + /* All flags enum set */ enum { BBCODE_DEF(BBCODE_GEN_ENUM) @@ -75,12 +107,27 @@ enum { * TODO: Set it in an ini ? */ #define BBCODE_DEFAULT_FLAG BBCODE_QUOTE_DOUBLE|BBCODE_QUOTE_SIMPLE|BBCODE_QUOTE_ESCAPE|BBCODE_CORRECT_AUTO|BBCODE_CORRECT_REOPEN|BBCODE_SMILEY_ON|BBCODE_CLOSE_AUTO|BBCODE_REMOVE_EMPTY +/* {{{ Function prototypes (bbcode_destroy|bbcode_free|bbcode_clone|bbcode_create) */ static void bbcode_destroy(bbcode_object *obj TSRMLS_DC); static void bbcode_free(bbcode_object *obj TSRMLS_DC); static zend_object *bbcode_clone(zval *obj TSRMLS_DC); static zend_object *bbcode_create(zend_class_entry *ce TSRMLS_DC); +/* }}} */ + +/* {{{ static inline bbcode_object* bbcode_fetch(zend_object *obj TSRMLS_DC) { + * BBCode object fetch function */ +static inline bbcode_object* bbcode_fetch(zend_object *obj TSRMLS_DC) { + return (bbcode_object *)((char *)obj - XtOffsetOf(bbcode_object, std)); +} +/* }}} */ -/* BBCode object destroy call */ +/* {{{ Z_BBCODE_P(zv) bbcode_fetch(Z_OBJ_P((zv))) + * BBCode object pointer fetch macro */ +#define Z_BBCODE_P(zv) bbcode_fetch(Z_OBJ_P((zv))) +/* }}} */ + +/* {{{ static void bbcode_destroy(bbcode_object *obj TSRMLS_DC) { + * BBCode object destroy call */ static void bbcode_destroy(bbcode_object *obj TSRMLS_DC) { //zend_objects_destroy_object(&obj->std); zend_objects_destroy_object((zend_object *)obj); @@ -93,8 +140,10 @@ static void bbcode_destroy(bbcode_object *obj TSRMLS_DC) { //efree(obj); } +/* }}} */ -/* BBCode object free call */ +/* {{{ static void bbcode_free(bbcode_object *obj TSRMLS_DC) { + * BBCode object free call */ static void bbcode_free(bbcode_object *obj TSRMLS_DC) { /*if (obj->flag) { @@ -104,153 +153,706 @@ static void bbcode_free(bbcode_object *obj TSRMLS_DC) { //efree(obj); zend_object_std_dtor((zend_object *)obj); } +/* }}} */ +/* {{{ static zend_object *bbcode_clone(zval *obj TSRMLS_DC) { + * BBCode object clone call */ static zend_object *bbcode_clone(zval *obj TSRMLS_DC) { - bbcode_object *oldobj = (bbcode_object *)((char *)obj - XtOffsetOf(bbcode_object, std)); - - bbcode_object *newobj = ecalloc(1, sizeof(bbcode_object) + zend_object_properties_size(Z_OBJCE_P(obj))); + /* Fetch pointer on old obj */ + zend_object *oldobj = Z_OBJ_P(obj); + /* Create pointer on nex obj */ + zend_object *newobj = bbcode_create(oldobj->ce); - zend_object_std_init(&newobj->std, Z_OBJCE_P(obj) TSRMLS_CC); - object_properties_init(&newobj->std, Z_OBJCE_P(obj) TSRMLS_CC); + /* Call members cloning function */ + zend_objects_clone_members(newobj, oldobj); - memcpy(&newobj->bbcode_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); - newobj->bbcode_handlers.offset = XtOffsetOf(bbcode_object, std); - newobj->bbcode_handlers.dtor_obj = (zend_object_dtor_obj_t) bbcode_destroy; - newobj->bbcode_handlers.free_obj = (zend_object_free_obj_t) bbcode_free; - newobj->bbcode_handlers.clone_obj = (zend_object_clone_obj_t) bbcode_clone; + /* Retrieve from zend object old bbcode */ + bbcode_object *bboldobj = Z_BBCODE_P(obj); - newobj->std.handlers = &(newobj->bbcode_handlers); + /* Retrieve from zend object new bbcode */ + bbcode_object *bbnewobj = bbcode_fetch(newobj); - zend_objects_clone_members(&newobj->std, &oldobj->std); + /* Restore object member values */ + //TODO: deal with the other one (tag|smiley) + Z_LVAL(bbnewobj->flag) = Z_LVAL(bboldobj->flag); -#if 0 - bbcode_object *oldobj = (bbcode_object *)((char *)obj - XtOffsetOf(bbcode_object, std)); - zend_object *newobj_val = bbcode_create(Z_OBJCE_P(obj) TSRMLS_CC); - bbcode_object *newobj = (bbcode_object *)((char *)newobj_val - XtOffsetOf(bbcode_object, std)); - - zend_objects_clone_members(&newobj->std, &oldobj->std); -#endif - - /*XXX: clone flag member*/ - newobj->flag = oldobj->flag; - - /* TODO: clone the members of bbcode_object - newobj->buffer = oldobj->buffer; - newobj->length = oldobj->length; - - if (oldobj->flag) { - newobj->buffer = emalloc(oldobj->length); - memcpy(newobj->buffer, oldobj->buffer, oldobj->length); - }*/ - - return &newobj->std; + /* Return the new object */ + return newobj; } +/* }}} */ -#if 0 -static zend_object_value bbcode_clone(zval *obj TSRMLS_DC) -{ - bbcode_object *oldobj = zend_object_store_get_object(obj TSRMLS_CC); - zend_object_value newobj_val = array_buffer_create_object(Z_OBJCE_P(obj) TSRMLS_CC); - bbcode_object *newobj = zend_object_store_get_object_by_handle(newobj_val.handle TSRMLS_CC); - - zend_objects_clone_members(&newobj->std, &oldobj->std); - - /* TODO: clone the members of bbcode_object - newobj->buffer = oldobj->buffer; - newobj->length = oldobj->length; - - if (oldobj->flag) { - newobj->buffer = emalloc(oldobj->length); - memcpy(newobj->buffer, oldobj->buffer, oldobj->length); - }*/ - - return newobj_val; -} -#endif - +/* {{{ static zend_object *bbcode_create(zend_class_entry *ce TSRMLS_DC) { + * BBCode object create call */ static zend_object *bbcode_create(zend_class_entry *ce TSRMLS_DC) { - bbcode_object *obj = ecalloc(1, sizeof(bbcode_object) + zend_object_properties_size(ce));//emalloc(sizeof(bbcode_object)); - obj->flag = 0; - //memset(obj, 0, sizeof(bbcode_object)); + bbcode_object *obj = (bbcode_object *)ecalloc(1, sizeof(bbcode_object) + zend_object_properties_size(ce));//emalloc(sizeof(bbcode_object)); zend_object_std_init(&obj->std, ce TSRMLS_CC); object_properties_init(&obj->std, ce TSRMLS_CC); - /**/ - - memcpy(&obj->bbcode_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); - obj->bbcode_handlers.offset = XtOffsetOf(bbcode_object, std); - obj->bbcode_handlers.dtor_obj = (zend_object_dtor_obj_t) bbcode_destroy; - obj->bbcode_handlers.free_obj = (zend_object_free_obj_t) bbcode_free; - obj->bbcode_handlers.clone_obj = (zend_object_clone_obj_t) bbcode_clone; - //bbcode_handlers.offset = XtOffsetOf(bbcode_object, std); - //bbcode_handlers.dtor_obj = (zend_object_dtor_obj_t) bbcode_destroy; - //bbcode_handlers.free_obj = (zend_object_free_obj_t) bbcode_free; - //bbcode_handlers.clone_obj = (zend_object_clone_obj_t) bbcode_clone; - + memcpy(&bbcode_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + bbcode_handlers.offset = XtOffsetOf(bbcode_object, std); + bbcode_handlers.dtor_obj = (zend_object_dtor_obj_t) bbcode_destroy; + bbcode_handlers.free_obj = (zend_object_free_obj_t) bbcode_free; + bbcode_handlers.clone_obj = (zend_object_clone_obj_t) bbcode_clone; - /*retval.handle = zend_object_store_put( - obj, - (zend_objects_store_dtor_t) zend_objects_destroy_object, - (zend_objects_free_object_storage_t) bbcode_free, - NULL TSRMLS_CC - ); - retval.handlers = &bbcode_handlers; + obj->std.handlers = &bbcode_handlers; - return retval;*/ - - obj->std.handlers = &(obj->bbcode_handlers); + Z_LVAL(obj->flag) = 0; return &obj->std; } +/* }}} */ -static inline bbcode_object* bbcode_fetch(zend_object *obj) { - return (bbcode_object *)((char *)obj - XtOffsetOf(bbcode_object, std)); -} - -/* {{{ PHP_METHOD(BBCode::__construct) { - */ +/* {{{ ZEND_BEGIN_ARG_INFO_EX(bbcode_construct_arginfo) */ +// ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args) ZEND_BEGIN_ARG_INFO_EX(bbcode_construct_arginfo, 0, 0, 1) + // ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) ZEND_ARG_ARRAY_INFO(0, tag, 1) ZEND_ARG_ARRAY_INFO(0, smiley, 1) + // ZEND_ARG_INFO(pass_by_ref, name) ZEND_ARG_INFO(0, flag) ZEND_END_ARG_INFO() +/* }}} */ +/* {{{ ZEND_BEGIN_ARG_INFO_EX(bbcode_parse_arginfo) */ +// ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args) +ZEND_BEGIN_ARG_INFO_EX(bbcode_parse_arginfo, 0, 0, 1) + // ZEND_ARG_INFO(pass_by_ref, name) + ZEND_ARG_INFO(0, str) +ZEND_END_ARG_INFO() +/* }}} */ + +/* { { { static zend_bool *bbcode_check_tag(HashTable *tag TSRMLS_DC) { + * Check that tag hash is valid */ +static zend_bool bbcode_check_tag(HashTable *tag TSRMLS_DC) { + /* Key numeric value */ + zend_long idx; + /* Key string */ + zend_string *key; + /* Value */ + zval *val; + + /* Key position */ + int pos = 0; + + /* Has a root */ + int hasRoot = 0; + + /* Iterate on each key val until hash end is reached */ + ZEND_HASH_FOREACH_KEY_VAL(tag, idx, key, val) { + /* Check if key is a string */ + if (!key) { + /* Display error about long key */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Key %ld[%d] from argument tag is not an expected string", idx, pos); + /* Return failure */ + return 0; + } + + /* Check that value exists */ + if (val == NULL) { + /* Display error about long key */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Value for key %s[%d] from argument tag is NULL instead of expected array", ZSTR_VAL(key), pos); + /* Return failure */ + return 0; + /* Check that value is an array */ + } else if (Z_TYPE_P(val) != IS_ARRAY) { + /* Display error about long key */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Value for key %s[%d] from argument tag is %s[%d] instead of expected array", ZSTR_VAL(key), pos, BBCODE_GET_TYPE(Z_TYPE_P(val)), Z_TYPE_P(val)); + /* Return failure */ + return 0; + } + + /* Store child tag array */ + HashTable *ctag = Z_ARRVAL_P(val); + /* Child key numeric value */ + zend_long cidx; + /* Child key string */ + zend_string *ckey; + /* Child value */ + zval *cval; + + /* Child key position */ + int cpos = 0, type; + + /* Detect if entry has a type, parent, child, open, close, default or arg key to display error if mandatory field is missing */ + bool hasType = false, hasParent = false, hasChild = false, hasOpen = false, hasClose = false, hasDefault = false, hasArg = false; + + /* Iterate on each ckey cval until hash end is reached */ + ZEND_HASH_FOREACH_KEY_VAL(ctag, cidx, ckey, cval) { + /* Check if ckey is a string */ + if (!ckey) { + /* Display error about long ckey */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Key %s[%d]/%ld[%d] from argument tag is not an expected string", ZSTR_VAL(key), pos, cidx, cpos); + /* Return failure */ + return 0; + } + + /* Check if ckey is empty */ + if (ZSTR_LEN(ckey) == 0) { + /* Display error about long key */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Key %s[%d]/%s[%d] from argument tag is empty not one of expected type|parent|child|open|close|default string", ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); + /* Return failure */ + return 0; + /* Check if current ckey is type */ + } else if (strcmp("type", ZSTR_VAL(ckey)) == 0) { + /* Extract type */ + type = Z_LVAL_P(cval); + + /* Check that current type is valid */ + if ( + type != BBCODE_TYPE_ROOT && + type != BBCODE_TYPE_MULTI && + type != BBCODE_TYPE_SINGLE + ) { + /* Display error about unexpected type value */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Value '%d' for key %s[%d]/%s[%d] from argument tag is not one of expected BBCODE::TYPE_(ROOT|MULTI|SINGLE) constant", type, ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); + /* Return failure */ + return 0; + /* Check if root */ + } else if (type == BBCODE_TYPE_ROOT) { + /* Grow hasRoot */ + hasRoot++; + } + + /* Set hasType */ + hasType = true; + /* Check if current key is parent */ + } else if (strcmp("parent", ZSTR_VAL(ckey)) == 0) { + /* Check that value exists */ + if (cval == NULL) { + /* Display error about long key */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Value for key %s[%d]/%s[%d] from argument tag is NULL instead of expected array", ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); + /* Return failure */ + return 0; + /* Check that value is an array */ + } else if (Z_TYPE_P(cval) != IS_ARRAY) { + /* Display error about long key */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Value for key %s[%d]/%s[%d] from argument tag is %s[%d] instead of expected array", ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos, BBCODE_GET_TYPE(Z_TYPE_P(cval)), Z_TYPE_P(cval)); + /* Return failure */ + return 0; + } + + /* Parent value */ + zval *pval; + + /* Child key position */ + int ppos = 0; + + /* Iterate on each val until hash end is reached */ + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(cval), pval) { + /* Check that parent val is a string */ + if (Z_TYPE_P(pval) != IS_STRING) { + /* Display error about not string */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Parent value %s[%d] for key %s[%d]/%s[%d] is not an expected string", Z_STRVAL_P(pval), ppos, ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); + /* Return failure */ + return 0; + } + + /* Tag key */ + zend_string *tkey; + + /* Reset found flag */ + bool found = false; + + /* Iterate on each key until hash end is reached */ + ZEND_HASH_FOREACH_STR_KEY(tag, tkey) { + /* Check that tkey is a string and is identical */ + if (tkey != NULL && strcmp(Z_STRVAL_P(pval), ZSTR_VAL(tkey)) == 0) { + /* We found parent value in tag keys*/ + found = true; + } + } ZEND_HASH_FOREACH_END(); + + /* Check if we found the key */ + if (!found) { + /* Display error about long key */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Parent value %s for key %s[%d]/%s[%d] is not present in tag keys", Z_STRVAL_P(pval), ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); + /* Return failure */ + return 0; + } + + /* Grow ppos */ + ppos++; + } ZEND_HASH_FOREACH_END(); + + /* Set hasParent */ + hasParent = true; + /* Check if current key is child */ + } else if (strcmp("child", ZSTR_VAL(ckey)) == 0) { + /* Check that value exists */ + if (cval == NULL) { + /* Display error about long key */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Value for key %s[%d]/%s[%d] from argument tag is NULL instead of expected array", ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); + /* Return failure */ + return 0; + /* Check that value is an array */ + } else if (Z_TYPE_P(cval) != IS_ARRAY) { + /* Display error about long key */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Value for key %s[%d]/%s[%d] from argument tag is %s[%d] instead of expected array", ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos, BBCODE_GET_TYPE(Z_TYPE_P(cval)), Z_TYPE_P(cval)); + /* Return failure */ + return 0; + } + + /* Child value */ + zval *ccval; + + /* Child key position */ + int ccpos = 0; + + /* Iterate on each val until hash end is reached */ + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(cval), ccval) { + /* Check that child val is a string */ + if (Z_TYPE_P(ccval) != IS_STRING) { + /* Display error about not string */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Child value %s[%d] for key %s[%d]/%s[%d] is not an expected string", Z_STRVAL_P(ccval), ccpos, ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); + /* Return failure */ + return 0; + } + + /* Tag key */ + zend_string *tkey; + + /* Reset found flag */ + bool found = false; + + /* Iterate on each key until hash end is reached */ + ZEND_HASH_FOREACH_STR_KEY(tag, tkey) { + /* Check that tkey is a string and is identical */ + if (tkey != NULL && strcmp(Z_STRVAL_P(ccval), ZSTR_VAL(tkey)) == 0) { + /* We found child value in tag keys*/ + found = true; + } + } ZEND_HASH_FOREACH_END(); + + /* Check if we found the key */ + if (!found) { + /* Display error about long key */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Child value %s for key %s[%d]/%s[%d] is not present in tag keys", Z_STRVAL_P(ccval), ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); + /* Return failure */ + return 0; + } + + /* Grow ccpos */ + ccpos++; + } ZEND_HASH_FOREACH_END(); + + /* Set hasChild */ + hasChild = true; + /* Check if current key is open */ + } else if (strcmp("open", ZSTR_VAL(ckey)) == 0) { + /* Set hasOpen */ + hasOpen = true; + /* Check if current key is close */ + } else if (strcmp("close", ZSTR_VAL(ckey)) == 0) { + /* Set hasClose */ + hasClose = true; + /* Check if current key is default */ + } else if (strcmp("default", ZSTR_VAL(ckey)) == 0) { + /* Set hasDefault */ + hasDefault = true; + /* Check if current key is arg */ + } else if (strcmp("arg", ZSTR_VAL(ckey)) == 0) { + /* Set hasArg */ + hasArg = true; + /* Check if current key is unknown */ + } else { + /* Display error about long key */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Key %s[%d]/%s[%d] from argument tag is not one of expected type|parent|child|open|close|default string", ZSTR_VAL(key), pos, ZSTR_VAL(ckey), cpos); + /* Return failure */ + return 0; + } + + /* Grow cpos */ + cpos++; + } ZEND_HASH_FOREACH_END(); + + /* Check if val array is initialized */ + /*XXX: this step is required to workaround zend engine half initialized hashtable when val array is empty that trigger a segfault*/ + if ((GC_FLAGS(Z_ARR_P(val)) & HASH_FLAG_INITIALIZED) != HASH_FLAG_INITIALIZED) { + /* Allocate hash table */ + /* XXX: this is required to avoid segfault in zend_hash_add when the array is empty */ + ALLOC_HASHTABLE(Z_ARR_P(val)); + /* Init hash table */ + zend_hash_init(Z_ARR_P(val), 0, NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(Z_ARR_P(val), ctag, (copy_ctor_func_t) zval_add_ref); + } + + /* Check if entry has no type */ + if (!hasType) { + /* Type zval */ + zval tval; + + /* Type key string */ + zend_string *tkey = zend_string_init("type", strlen("type"), 0); + + /* Check if key is '' */ + if (ZSTR_LEN(key) == 0) { + /* Set tval value to root */ + ZVAL_LONG(&tval, (type = BBCODE_TYPE_ROOT)); + + /* Grow hasRoot */ + hasRoot++; + /* Check if key is img */ + } else if (strcmp("img", ZSTR_VAL(key)) == 0) { + /* Set tval value to single */ + ZVAL_LONG(&tval, (type = BBCODE_TYPE_SINGLE)); + /* Handle other key as multi */ + } else { + /* Set tval value to multi */ + ZVAL_LONG(&tval, (type = BBCODE_TYPE_MULTI)); + } + + /* Add new type key */ + ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), tkey, &tval) != NULL); + + /* Free type key */ + zend_string_release(tkey); + + /* Set hasType */ + hasType = true; + } + + /* Check for root type */ + if (type == BBCODE_TYPE_ROOT) { + /* Check if has parent */ + if (hasParent) { + /* Display error about parent entry */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Key %s[%d] from argument tag of type BBCODE::TYPE_ROOT cannot have a parent entry", ZSTR_VAL(key), pos); + /* Return failure */ + return 0; + } + + /* TODO: Compute child here */ + //TODO: set child if not provided for ROOT (has to loop on each possible type and check if a parent array is present with this tag inside, if none set to all possible tag except '' one) + //TODO: display error if type=root and child was not provided| XXX: we can compute that shit here for lazy morons + if (!hasChild) { + //TODO compute child by looping on each non root element and element with no parent or with root in parent + //XXX: it seems that a better approach would be to save root element en push each element one by one ? + } + /* Check for multi type */ + } else if (type == BBCODE_TYPE_MULTI) { + /* Check if has open entry */ + if (!hasOpen) { + /* Key string */ + zend_string *mkey = zend_string_init("open", strlen("open"), 0); + + /* Value */ + zval mval; + + /* Tag */ + zend_string *mtag = zend_string_init("<>", strlen("<>") + ZSTR_LEN(key) + (hasArg?strlen("%s"):0), 0); + memcpy(ZSTR_VAL(mtag)+sizeof(char), ZSTR_VAL(key), ZSTR_LEN(key)); + if (hasArg) { + memcpy(ZSTR_VAL(mtag)+(1+ZSTR_LEN(key))*sizeof(char), "%s>", strlen("%s>")); + } else { + memcpy(ZSTR_VAL(mtag)+(1+ZSTR_LEN(key))*sizeof(char), ">", strlen(">")); + } + + /* Set value */ + ZVAL_STRING(&mval, ZSTR_VAL(mtag)); + + /* Add new key */ + ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), mkey, &mval) != NULL); + + /* Free tag */ + zend_string_release(mtag); + + /* Free key */ + zend_string_release(mkey); + } + + /* Check if has close entry */ + if (!hasClose) { + /* Key string */ + zend_string *mkey = zend_string_init("close", strlen("close"), 0); + + /* Value */ + zval mval; + + /* Tag */ + zend_string *mtag = zend_string_init("", strlen("") + ZSTR_LEN(key), 0); + memcpy(ZSTR_VAL(mtag)+2*sizeof(char), ZSTR_VAL(key), ZSTR_LEN(key)); + memcpy(ZSTR_VAL(mtag)+(2+ZSTR_LEN(key))*sizeof(char), ">", strlen(">")); + + /* Set value */ + ZVAL_STRING(&mval, ZSTR_VAL(mtag)); + + /* Add new key */ + ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), mkey, &mval) != NULL); + + /* Free tag */ + zend_string_release(mtag); + + /* Free key */ + zend_string_release(mkey); + } + + /* Check if has not default */ + if (!hasDefault) { + /* Key string */ + zend_string *mkey = zend_string_init("default", strlen("default"), 0); + + /* Value */ + zval mval; + + /* Set value */ + ZVAL_STRING(&mval, "%s"); + + /* Add new key */ + ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), mkey, &mval) != NULL); + + /* Free key */ + zend_string_release(mkey); + } + + //TODO: set parent&child if not provided for ARG|OPTARG|NOARG|SINGLE (has to loop on each possible type and check if no child array or present in child array if available) + + /* Check for single type */ + } else if (type == BBCODE_TYPE_SINGLE) { + /* Check if has open entry */ + if (hasOpen) { + /* Display error about open entry */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Key %s[%d] from argument tag of type BBCODE::TYPE_SINGLE cannot have an open entry", ZSTR_VAL(key), pos); + /* Return failure */ + return 0; + } + + /* Check if has close entry */ + if (hasClose) { + /* Display error about close entry */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Key %s[%d] from argument tag of type BBCODE::TYPE_SINGLE cannot have a close entry", ZSTR_VAL(key), pos); + /* Return failure */ + return 0; + } + + /* Check if has not default */ + if (!hasDefault) { + /* Key string */ + zend_string *skey = zend_string_init("default", strlen("default"), 0); + + /* Value */ + zval sval; + + /* Tag */ + zend_string *stag; + + /* Check if jey is img */ + if (strcmp("img", ZSTR_VAL(key)) == 0) { + stag = zend_string_init("", strlen("") + (hasArg?strlen("%s"):0), 0); + if (hasArg) { + memcpy(ZSTR_VAL(stag)+strlen("", strlen("%s/>")); + } else { + memcpy(ZSTR_VAL(stag)+strlen("", strlen("/>")); + } + } else { + stag = zend_string_init("", strlen("") + ZSTR_LEN(key) + (hasArg?strlen("%s"):0), 0); + memcpy(ZSTR_VAL(stag)+sizeof(char), ZSTR_VAL(key), ZSTR_LEN(key)); + if (hasArg) { + memcpy(ZSTR_VAL(stag)+(1+ZSTR_LEN(key))*sizeof(char), "%s/>", strlen("%s/>")); + } else { + memcpy(ZSTR_VAL(stag)+(1+ZSTR_LEN(key))*sizeof(char), "/>", strlen("/>")); + } + } + + /* Set value */ + ZVAL_STRING(&sval, ZSTR_VAL(stag)); + + /* Add new key */ + ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), skey, &sval) != NULL); + + /* Free tag */ + zend_string_release(stag); + + /* Free key */ + zend_string_release(skey); + } + + /* Check if has child */ + if (hasChild) { + /* Display error about child entry */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Key %s[%d] from argument tag of type BBCODE::TYPE_SINGLE cannot have a child entry", ZSTR_VAL(key), pos); + /* Return failure */ + return 0; + } + + //TODO: set parent if not provided for ARG|OPTARG|NOARG|SINGLE (has to loop on each possible type and check if no child array or present in child array if available) + } + +#ifndef DEBUG + printf("key=>%s\n", ZSTR_VAL(key)); + fflush(NULL); + php_debug_zval_dump(val, 0); + fflush(NULL); +#endif + + /* Grow pos */ + pos++; + } ZEND_HASH_FOREACH_END(); + + /* Check if not one unique root */ + if (hasRoot != 1) { + /* Display error about multiple root */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Has %d BBCODE::TYPE_ROOT entry, require one unique", hasRoot); + /* Return failure */ + return 0; + } + /* Return success */ + return 1; +} +/* }}} */ + +/* {{{ static zend_bool *bbcode_check_smiley(HashTable *smiley TSRMLS_DC) { + * Check that smiley hash is valid */ +static zend_bool bbcode_check_smiley(HashTable *smiley TSRMLS_DC) { + /* Key numeric value */ + zend_long idx; + /* Key string */ + zend_string *key; + /* Value */ + zval *val; + + /* Key position */ + int pos = 0; + + /* Iterate on each key val until hash end is reached */ + ZEND_HASH_FOREACH_KEY_VAL(smiley, idx, key, val) { + /* Check that key is a string */ + if (key == NULL) { + /* Display error about long key */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Key %ld[%d] from argument smiley is not an expected string", idx, pos); + /* Return failure */ + return 0; + } + + /* Check that value exists */ + if (val == NULL) { + /* Display error about long key */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Value for key %s[%d] from argument smiley is NULL instead of expected array", ZSTR_VAL(key), pos); + /* Return failure */ + return 0; + /* Check that value is not an array */ + } else if (Z_TYPE_P(val) == IS_ARRAY) { + /* Display error about long key */ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Value for key %s[%d] from argument smiley is an array instead of expected string", ZSTR_VAL(key), pos); + /* Return failure */ + return 0; + } + + /* Grow pos */ + pos++; + } ZEND_HASH_FOREACH_END(); + + /* Return success */ + return 1; +} +/* }}} */ + +/* {{{ PHP_METHOD(BBCode, __construct) { + */ PHP_METHOD(BBCode, __construct) { - zend_array *tag; - zend_array *smiley; + HashTable *tag = NULL; + HashTable *smiley = NULL; zend_long flag = BBCODE_DEFAULT_FLAG; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|al", &tag, &smiley, &flag) == FAILURE) { +/*TODO: init tag with [ + '' => [ ? ] +]*/ +/* TODO: init smiley with default list + * TODO: use prefix in an ini parameter ? ini_setable ? + * TODO: make sure it's not really a global and per-app param ? + * */ + + /* Retrieve the pointer to this object */ + bbcode_object *obj = Z_BBCODE_P(getThis()); + + /* Verify that the pointer is leading somewhere */ + assert(obj != NULL); + + /* Set default values */ + obj->tag = tag; + obj->smiley = smiley; + Z_LVAL(obj->flag) = flag; + + /* Parse parameters */ + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_ARRAY_HT(tag) + Z_PARAM_ARRAY_HT(smiley) + Z_PARAM_LONG(flag) + ZEND_PARSE_PARAMETERS_END(); + + /*if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "h|hl", &tag, &smiley, &flag) == FAILURE) { + return; + }*/ + + /* Save tag argument hash in current object */ + //TODO: check tag structure + if (bbcode_check_tag(tag)) { + obj->tag = tag; + } else { + zend_hash_destroy(tag); + zend_hash_destroy(smiley); + ZVAL_NULL(getThis() TSRMLS_CC); return; } - bbcode_object *obj = bbcode_fetch(Z_OBJ_P(getThis())); - assert(obj != NULL); + /* Save smiley argument hash in current object if provided */ + //TODO: check smiley structure + if (smiley) { + if (bbcode_check_smiley(smiley)) { + obj->smiley = smiley; + } else { + zend_hash_destroy(tag); + zend_hash_destroy(smiley); + ZVAL_NULL(getThis() TSRMLS_CC); + return; + } + } + /* Save flag argument long in current object if provided */ if (flag) { - obj->flag = (zend_long) flag; + Z_LVAL(obj->flag) = flag; } - - //obj = zend_object_store_get_object(getThis() TSRMLS_CC); - //if (error) { - //ZVAL_NULL(this); - //} } /* }}} */ /* {{{ PHP_METHOD(BBCode, __destruct) { */ PHP_METHOD(BBCode, __destruct) { + /* Retrieve the pointer to this object */ + bbcode_object *obj = Z_BBCODE_P(getThis()); + + /* Verify that the pointer is leading somewhere */ + assert(obj != NULL); + + /* Free tag hashtable */ + if (obj->tag) { + efree(obj->tag); + } + + /* Free smiley hashtable */ + if (obj->smiley) { + efree(obj->smiley); + } } /* }}} */ -/* {{{ PHP_METHOD(BBCode, __toString) { +/* { { { PHP_METHOD(BBCode, parse) { */ -PHP_METHOD(BBCode, __toString) { - //TODO: +PHP_METHOD(BBCode, parse) { + zend_string *str = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "S", &str) == FAILURE) { + return; + } + + //TODO: create a tree for storing result + //TODO: apply recursively search of next possible tags (or a closing parent tag ?) regexp and store result inside + //TODO: d + //TODO: apply the smiley parsing on string + + RETURN_STR(str); } /* }}} */ @@ -261,7 +863,7 @@ PHP_METHOD(BBCode, __toString) { const zend_function_entry bbcode_methods[] = { PHP_ME(BBCode, __construct, bbcode_construct_arginfo, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) PHP_ME(BBCode, __destruct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_DTOR) - PHP_ME(BBCode, __toString, NULL, ZEND_ACC_PUBLIC) + PHP_ME(BBCode, parse, bbcode_parse_arginfo, ZEND_ACC_PUBLIC) PHP_FE_END }; /* }}} */