]> Raphaƫl G. Git Repositories - bbcode/blobdiff - bbcode.c
Reset allow_null for args
[bbcode] / bbcode.c
index 3a9ac76e1dabae597fa97d05d17f5385a1828bae..5810aae38435468b394260e30b05e66298035a6a 100644 (file)
--- a/bbcode.c
+++ b/bbcode.c
@@ -9,20 +9,22 @@
 #include "Zend/zend_exceptions.h"
 #include "php_bbcode.h"
 
+#include <stdbool.h>
+
 /* 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("<img src=\"%s\"/>", strlen("<img src=\"%s\"/>") + (hasArg?strlen("%s"):0), 0);
+                                       if (hasArg) {
+                                               memcpy(ZSTR_VAL(stag)+strlen("<img src=\"%s\"")*sizeof(char), "%s/>", strlen("%s/>"));
+                                       } else {
+                                               memcpy(ZSTR_VAL(stag)+strlen("<img src=\"%s\"")*sizeof(char), "/>", 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
 };
 /* }}} */