#ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" #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 { 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; /* BBCode exception object */ zend_class_entry *bbcode_exception_ce; /* Define all constants */ #ifdef old_code_dropped GEN(TYPE_ARG) /*BBCODE_TYPE_ARG*/\ GEN(TYPE_NOARG) /*BBCODE_TYPE_NOARG*/\ 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*/\ GEN(QUOTE_SIMPLE) /*BBCODE_ARG_SINGLE_QUOTE*/\ GEN(QUOTE_HTML) /*BBCODE_ARG_HTML_QUOTE*/\ GEN(QUOTE_ESCAPE) /*BBCODE_ARG_QUOTE_ESCAPING*/\ \ /* Corrections */ \ GEN(CORRECT_AUTO) /*BBCODE_AUTO_CORRECT*/\ GEN(CORRECT_REOPEN) /*BBCODE_CORRECT_REOPEN_TAGS*/\ GEN(CORRECT_NOTREE) /*BBCODE_DISABLE_TREE_BUILD*/\ \ /* Smileys */ \ GEN(SMILEY_ON) /*BBCODE_DEFAULT_SMILEYS_ON*/\ GEN(SMILEY_CI) /*BBCODE_SMILEYS_CASE_INSENSITIVE*/\ \ /* Flags */ \ GEN(CLOSE_AUTO) /*BBCODE_FLAGS_ONE_OPEN_PER_LEVEL*/\ GEN(REMOVE_EMPTY) /*BBCODE_FLAGS_REMOVE_IF_EMPTY*/\ GEN(REMOVE_CONTENT) /*BBCODE_FLAGS_CDATA_NOT_ALLOWED*/\ GEN(REMOVE_REOPEN) /*BBCODE_FLAGS_DENY_REOPEN_CHILD*/ /* Generate enum version */ #define BBCODE_GEN_ENUM(NAME) BBCODE_ ## NAME, /* 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) }; /* Set default flag * 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)); } /* }}} */ /* {{{ 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); //zend_object_std_dtor(&obj->std TSRMLS_CC); /*if (obj->flag) { efree(obj->flag); }*/ //efree(obj); } /* }}} */ /* {{{ 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) { efree(obj->flag); }*/ //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) { /* Fetch pointer on old obj */ zend_object *oldobj = Z_OBJ_P(obj); /* Create pointer on nex obj */ zend_object *newobj = bbcode_create(oldobj->ce); /* Call members cloning function */ zend_objects_clone_members(newobj, oldobj); /* Retrieve from zend object old bbcode */ bbcode_object *bboldobj = Z_BBCODE_P(obj); /* Retrieve from zend object new bbcode */ bbcode_object *bbnewobj = bbcode_fetch(newobj); /* Restore object member values */ //TODO: deal with the other one (tag|smiley) Z_LVAL(bbnewobj->flag) = Z_LVAL(bboldobj->flag); /* Return the new object */ return newobj; } /* }}} */ /* {{{ 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 = (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(&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; obj->std.handlers = &bbcode_handlers; Z_LVAL(obj->flag) = 0; return &obj->std; } /* }}} */ /* {{{ 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) { HashTable *tag = NULL; HashTable *smiley = NULL; zend_long flag = BBCODE_DEFAULT_FLAG; /*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; } /* 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) { Z_LVAL(obj->flag) = flag; } } /* }}} */ /* {{{ 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, parse) { */ 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); } /* }}} */ /* {{{ bbcode_methods[] * * Every BBCode visible function must have an entry in bbcode_methods[]. */ 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, parse, bbcode_parse_arginfo, ZEND_ACC_PUBLIC) PHP_FE_END }; /* }}} */ /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(bbcode) { /* If you have INI entries, uncomment these lines REGISTER_INI_ENTRIES(); */ /* Tmp zend class entry*/ zend_class_entry tmp_ce; /* Init BBCode class entry */ INIT_CLASS_ENTRY(tmp_ce, "BBCode", bbcode_methods); /* Register BBCode class entry */ bbcode_ce = zend_register_internal_class(&tmp_ce TSRMLS_CC); bbcode_ce->create_object = bbcode_create; /* Generate const */ BBCODE_DEF(BBCODE_GEN_CONST) /* Init BBCodeException class entry */ INIT_CLASS_ENTRY(tmp_ce, "BBCodeException", NULL); /* Register BBCodeException class entry */ bbcode_exception_ce = zend_register_internal_class_ex(&tmp_ce, zend_exception_get_default(TSRMLS_C) TSRMLS_CC); return SUCCESS; } /* }}} */ /* {{{ PHP_MSHUTDOWN_FUNCTION */ PHP_MSHUTDOWN_FUNCTION(bbcode) { /* uncomment this line if you have INI entries UNREGISTER_INI_ENTRIES(); */ return SUCCESS; } /* }}} */ /* {{{ PHP_MINFO_FUNCTION */ PHP_MINFO_FUNCTION(bbcode) { php_info_print_table_start(); php_info_print_table_header(2, PHP_BBCODE_NAME " support", "enabled"); php_info_print_table_row(2, "Extension version", PHP_BBCODE_VERSION); php_info_print_table_end(); /* Remove comments if you have entries in php.ini DISPLAY_INI_ENTRIES(); */ } /* }}} */ /* {{{ bbcode_module_entry */ zend_module_entry bbcode_module_entry = { STANDARD_MODULE_HEADER, PHP_BBCODE_NAME, NULL, /*bbcode_functions,*/ PHP_MINIT(bbcode), PHP_MSHUTDOWN(bbcode), NULL, NULL, PHP_MINFO(bbcode), PHP_BBCODE_VERSION, STANDARD_MODULE_PROPERTIES }; /* }}} */ #ifdef COMPILE_DL_BBCODE #ifdef ZTS ZEND_TSRMLS_CACHE_DEFINE() #endif ZEND_GET_MODULE(bbcode) #endif /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */