8 #include "ext/standard/info.h" 
   9 #include "Zend/zend_exceptions.h" 
  10 #include "php_bbcode.h" 
  14 /* If you declare any globals in php_bbcode.h uncomment this: 
  15 ZEND_DECLARE_MODULE_GLOBALS(bbcode) 
  18 typedef struct _bbcode_object 
{ 
  25 /* True global resources - no need for thread safety here */ 
  26 zend_object_handlers bbcode_handlers
; 
  29 zend_class_entry 
*bbcode_ce
; 
  31 /* BBCode exception object */ 
  32 zend_class_entry 
*bbcode_exception_ce
; 
  34 /* Define all constants */ 
  35 #ifdef old_code_dropped 
  36         GEN(TYPE_ARG
) /*BBCODE_TYPE_ARG*/\
 
  37         GEN(TYPE_NOARG
) /*BBCODE_TYPE_NOARG*/\
 
  38         GEN(TYPE_OPTARG
) /*BBCODE_TYPE_OPTARG*/\
 
  42 #define BBCODE_DEF(GEN) \ 
  44         GEN(TYPE_ROOT) /*BBCODE_TYPE_ROOT*/\ 
  45         GEN(TYPE_MULTI) /*BBCODE_TYPE_ARG|BBCODE_TYPE_OPTARG|BBCODE_TYPE_NOARG*/\ 
  46         GEN(TYPE_SINGLE) /*BBCODE_TYPE_SINGLE*/\ 
  49         GEN(QUOTE_DOUBLE) /*BBCODE_ARG_DOUBLE_QUOTE*/\ 
  50         GEN(QUOTE_SIMPLE) /*BBCODE_ARG_SINGLE_QUOTE*/\ 
  51         GEN(QUOTE_HTML) /*BBCODE_ARG_HTML_QUOTE*/\ 
  52         GEN(QUOTE_ESCAPE) /*BBCODE_ARG_QUOTE_ESCAPING*/\ 
  55         GEN(CORRECT_AUTO) /*BBCODE_AUTO_CORRECT*/\ 
  56         GEN(CORRECT_REOPEN) /*BBCODE_CORRECT_REOPEN_TAGS*/\ 
  57         GEN(CORRECT_NOTREE) /*BBCODE_DISABLE_TREE_BUILD*/\ 
  60         GEN(SMILEY_ON) /*BBCODE_DEFAULT_SMILEYS_ON*/\ 
  61         GEN(SMILEY_CI) /*BBCODE_SMILEYS_CASE_INSENSITIVE*/\ 
  64         GEN(CLOSE_AUTO) /*BBCODE_FLAGS_ONE_OPEN_PER_LEVEL*/\ 
  65         GEN(REMOVE_EMPTY) /*BBCODE_FLAGS_REMOVE_IF_EMPTY*/\ 
  66         GEN(REMOVE_CONTENT) /*BBCODE_FLAGS_CDATA_NOT_ALLOWED*/\ 
  67         GEN(REMOVE_REOPEN) /*BBCODE_FLAGS_DENY_REOPEN_CHILD*/ 
  70 /* Generate enum version */ 
  71 #define BBCODE_GEN_ENUM(NAME) BBCODE_ ## NAME, 
  73 /* Generate zend const version */ 
  74 #define BBCODE_GEN_CONST(NAME) zend_declare_class_constant_long(bbcode_ce, #NAME, sizeof(#NAME) - 1, BBCODE_ ## NAME TSRMLS_CC); 
  76 /* Defined in /usr/include/php/Zend/zend_types.h +382 */ 
  77 #define BBCODE_GET_TYPE(TYPE) (TYPE==IS_UNDEF)?"undef":( \ 
  78                         (TYPE==IS_NULL)?"null":( \ 
  79                                 (TYPE==IS_FALSE)?"false":( \ 
  80                                         (TYPE==IS_TRUE)?"true":( \ 
  81                                                 (TYPE==IS_LONG)?"long":( \ 
  82                                                         (TYPE==IS_DOUBLE)?"double":( \ 
  83                                                                 (TYPE==IS_STRING)?"string":( \ 
  84                                                                         (TYPE==IS_ARRAY)?"array":( \ 
  85                                                                                 (TYPE==IS_OBJECT)?"object":( \ 
  86                                                                                         (TYPE==IS_RESOURCE)?"resource":( \ 
  87                                                                                                 (TYPE==IS_REFERENCE)?"reference":( \ 
  88                                                                                                         (TYPE==IS_CONSTANT_AST)?"constant":"unknown"\ 
 101 /* All flags enum set */ 
 103         BBCODE_DEF(BBCODE_GEN_ENUM
) 
 107  * TODO: Set it in an ini ? */ 
 108 #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 
 110 /* {{{ Function prototypes (bbcode_destroy|bbcode_free|bbcode_clone|bbcode_create) */ 
 111 static void bbcode_destroy(bbcode_object 
*obj TSRMLS_DC
); 
 112 static void bbcode_free(bbcode_object 
*obj TSRMLS_DC
); 
 113 static zend_object 
*bbcode_clone(zval 
*obj TSRMLS_DC
); 
 114 static zend_object 
*bbcode_create(zend_class_entry 
*ce TSRMLS_DC
); 
 117 /* {{{ static inline bbcode_object* bbcode_fetch(zend_object *obj TSRMLS_DC) { 
 118  * BBCode object fetch function */ 
 119 static inline bbcode_object
* bbcode_fetch(zend_object 
*obj TSRMLS_DC
) { 
 120         return (bbcode_object 
*)((char *)obj 
- XtOffsetOf(bbcode_object
, std
)); 
 124 /* {{{ Z_BBCODE_P(zv) bbcode_fetch(Z_OBJ_P((zv))) 
 125  * BBCode object pointer fetch macro */ 
 126 #define Z_BBCODE_P(zv) bbcode_fetch(Z_OBJ_P((zv))) 
 129 /* {{{ static void bbcode_destroy(bbcode_object *obj TSRMLS_DC) { 
 130  * BBCode object destroy call */ 
 131 static void bbcode_destroy(bbcode_object 
*obj TSRMLS_DC
) { 
 132         //zend_objects_destroy_object(&obj->std); 
 133         zend_objects_destroy_object((zend_object 
*)obj
); 
 135         //zend_object_std_dtor(&obj->std TSRMLS_CC); 
 145 /* {{{ static void bbcode_free(bbcode_object *obj TSRMLS_DC) { 
 146  * BBCode object free call */ 
 147 static void bbcode_free(bbcode_object 
*obj TSRMLS_DC
) { 
 154         zend_object_std_dtor((zend_object 
*)obj
); 
 158 /* {{{ static zend_object *bbcode_clone(zval *obj TSRMLS_DC) { 
 159  * BBCode object clone call */ 
 160 static zend_object 
*bbcode_clone(zval 
*obj TSRMLS_DC
) { 
 162         /* Fetch pointer on old obj */ 
 163         zend_object 
*oldobj 
= Z_OBJ_P(obj
); 
 164         /* Create pointer on nex obj */ 
 165         zend_object 
*newobj 
= bbcode_create(oldobj
->ce
); 
 167         /* Call members cloning function */ 
 168         zend_objects_clone_members(newobj
, oldobj
); 
 170         /* Retrieve from zend object old bbcode */ 
 171         bbcode_object 
*bboldobj 
= Z_BBCODE_P(obj
); 
 173         /* Retrieve from zend object new bbcode */ 
 174         bbcode_object 
*bbnewobj 
= bbcode_fetch(newobj
); 
 176         /* Restore object member values */ 
 177         //TODO: deal with the other one (tag|smiley) 
 178         Z_LVAL(bbnewobj
->flag
) = Z_LVAL(bboldobj
->flag
); 
 180         /* Return the new object */ 
 185 /* {{{ static zend_object *bbcode_create(zend_class_entry *ce TSRMLS_DC) { 
 186  * BBCode object create call */ 
 187 static zend_object 
*bbcode_create(zend_class_entry 
*ce TSRMLS_DC
) { 
 188         bbcode_object 
*obj 
= (bbcode_object 
*)ecalloc(1, sizeof(bbcode_object
) + zend_object_properties_size(ce
));//emalloc(sizeof(bbcode_object)); 
 190         zend_object_std_init(&obj
->std
, ce TSRMLS_CC
); 
 191         object_properties_init(&obj
->std
, ce TSRMLS_CC
); 
 193         memcpy(&bbcode_handlers
, zend_get_std_object_handlers(), sizeof(zend_object_handlers
)); 
 194         bbcode_handlers
.offset 
= XtOffsetOf(bbcode_object
, std
); 
 195         bbcode_handlers
.dtor_obj 
= (zend_object_dtor_obj_t
) bbcode_destroy
; 
 196         bbcode_handlers
.free_obj 
= (zend_object_free_obj_t
) bbcode_free
; 
 197         bbcode_handlers
.clone_obj 
= (zend_object_clone_obj_t
) bbcode_clone
; 
 199         obj
->std
.handlers 
= &bbcode_handlers
; 
 201         Z_LVAL(obj
->flag
) = 0; 
 207 /* {{{ ZEND_BEGIN_ARG_INFO_EX(bbcode_construct_arginfo) */ 
 208 // ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args) 
 209 ZEND_BEGIN_ARG_INFO_EX(bbcode_construct_arginfo
, 0, 0, 1) 
 210         // ZEND_ARG_ARRAY_INFO(pass_by_ref, name, allow_null) 
 211         ZEND_ARG_ARRAY_INFO(0, tag
, 0) 
 212         ZEND_ARG_ARRAY_INFO(0, smiley
, 0) 
 213         // ZEND_ARG_INFO(pass_by_ref, name) 
 214         ZEND_ARG_INFO(0, flag
) 
 218 /* {{{ ZEND_BEGIN_ARG_INFO_EX(bbcode_parse_arginfo) */ 
 219 // ZEND_BEGIN_ARG_INFO_EX(name, _unused, return_reference, required_num_args) 
 220 ZEND_BEGIN_ARG_INFO_EX(bbcode_parse_arginfo
, 0, 0, 1) 
 221         // ZEND_ARG_INFO(pass_by_ref, name) 
 222         ZEND_ARG_INFO(0, str
) 
 226 /* { { { static zend_bool *bbcode_check_tag(HashTable *tag TSRMLS_DC) { 
 227  * Check that tag hash is valid */ 
 228 static zend_bool 
bbcode_check_tag(HashTable 
*tag TSRMLS_DC
) { 
 229         /* Key numeric value */ 
 242         /* Iterate on each key val until hash end is reached */ 
 243         ZEND_HASH_FOREACH_KEY_VAL(tag
, idx
, key
, val
) { 
 244                 /* Check if key is a string */ 
 246                         /* Display error about long key */ 
 247                         php_error_docref(NULL TSRMLS_CC
, E_ERROR
, "Key %ld[%d] from argument tag is not an expected string", idx
, pos
); 
 252                 /* Check that value exists */ 
 254                         /* Display error about long key */ 
 255                         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
); 
 258                 /* Check that value is an array */ 
 259                 } else if (Z_TYPE_P(val
) != IS_ARRAY
) { 
 260                         /* Display error about long key */ 
 261                         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
)); 
 266                 /* Store child tag array */ 
 267                 HashTable 
*ctag 
= Z_ARRVAL_P(val
); 
 268                 /* Child key numeric value */ 
 270                 /* Child key string */ 
 275                 /* Child key position */ 
 278                 /* Detect if entry has a type, parent, child, open, close, default or arg key to display error if mandatory field is missing */ 
 279                 bool hasType 
= false, hasParent 
= false, hasChild 
= false, hasOpen 
= false, hasClose 
= false, hasDefault 
= false, hasArg 
= false; 
 281                 /* Iterate on each ckey cval until hash end is reached */ 
 282                 ZEND_HASH_FOREACH_KEY_VAL(ctag
, cidx
, ckey
, cval
) { 
 283                         /* Check if ckey is a string */ 
 285                                 /* Display error about long ckey */ 
 286                                 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
); 
 291                         /* Check if ckey is empty */ 
 292                         if (ZSTR_LEN(ckey
) == 0) { 
 293                                 /* Display error about long key */ 
 294                                 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
); 
 297                         /* Check if current ckey is type */ 
 298                         } else if (strcmp("type", ZSTR_VAL(ckey
)) == 0) { 
 300                                 type 
= Z_LVAL_P(cval
); 
 302                                 /* Check that current type is valid */ 
 304                                         type 
!= BBCODE_TYPE_ROOT 
&& 
 305                                         type 
!= BBCODE_TYPE_MULTI 
&& 
 306                                         type 
!= BBCODE_TYPE_SINGLE
 
 308                                         /* Display error about unexpected type value */ 
 309                                         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
); 
 313                                 } else if (type 
== BBCODE_TYPE_ROOT
) { 
 320                         /* Check if current key is parent */ 
 321                         } else if (strcmp("parent", ZSTR_VAL(ckey
)) == 0) { 
 322                                 /* Check that value exists */ 
 324                                         /* Display error about long key */ 
 325                                         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
); 
 328                                 /* Check that value is an array */ 
 329                                 } else if (Z_TYPE_P(cval
) != IS_ARRAY
) { 
 330                                         /* Display error about long key */ 
 331                                         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
)); 
 339                                 /* Child key position */ 
 342                                 /* Iterate on each val until hash end is reached */ 
 343                                 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(cval
), pval
) { 
 344                                         /* Check that parent val is a string */ 
 345                                         if (Z_TYPE_P(pval
) != IS_STRING
) { 
 346                                                 /* Display error about not string */ 
 347                                                 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
); 
 355                                         /* Reset found flag */ 
 358                                         /* Iterate on each key until hash end is reached */ 
 359                                         ZEND_HASH_FOREACH_STR_KEY(tag
, tkey
) { 
 360                                                 /* Check that tkey is a string and is identical */ 
 361                                                 if (tkey 
!= NULL 
&& strcmp(Z_STRVAL_P(pval
), ZSTR_VAL(tkey
)) == 0) { 
 362                                                         /* We found parent value in tag keys*/ 
 365                                         } ZEND_HASH_FOREACH_END(); 
 367                                         /* Check if we found the key */ 
 369                                                 /* Display error about long key */ 
 370                                                 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
); 
 377                                 } ZEND_HASH_FOREACH_END(); 
 381                         /* Check if current key is child */ 
 382                         } else if (strcmp("child", ZSTR_VAL(ckey
)) == 0) { 
 383                                 /* Check that value exists */ 
 385                                         /* Display error about long key */ 
 386                                         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
); 
 389                                 /* Check that value is an array */ 
 390                                 } else if (Z_TYPE_P(cval
) != IS_ARRAY
) { 
 391                                         /* Display error about long key */ 
 392                                         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
)); 
 400                                 /* Child key position */ 
 403                                 /* Iterate on each val until hash end is reached */ 
 404                                 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(cval
), ccval
) { 
 405                                         /* Check that child val is a string */ 
 406                                         if (Z_TYPE_P(ccval
) != IS_STRING
) { 
 407                                                 /* Display error about not string */ 
 408                                                 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
); 
 416                                         /* Reset found flag */ 
 419                                         /* Iterate on each key until hash end is reached */ 
 420                                         ZEND_HASH_FOREACH_STR_KEY(tag
, tkey
) { 
 421                                                 /* Check that tkey is a string and is identical */ 
 422                                                 if (tkey 
!= NULL 
&& strcmp(Z_STRVAL_P(ccval
), ZSTR_VAL(tkey
)) == 0) { 
 423                                                         /* We found child value in tag keys*/ 
 426                                         } ZEND_HASH_FOREACH_END(); 
 428                                         /* Check if we found the key */ 
 430                                                 /* Display error about long key */ 
 431                                                 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
); 
 438                                 } ZEND_HASH_FOREACH_END(); 
 442                         /* Check if current key is open */ 
 443                         } else if (strcmp("open", ZSTR_VAL(ckey
)) == 0) { 
 446                         /* Check if current key is close */ 
 447                         } else if (strcmp("close", ZSTR_VAL(ckey
)) == 0) { 
 450                         /* Check if current key is default */ 
 451                         } else if (strcmp("default", ZSTR_VAL(ckey
)) == 0) { 
 454                         /* Check if current key is arg */ 
 455                         } else if (strcmp("arg", ZSTR_VAL(ckey
)) == 0) { 
 458                         /* Check if current key is unknown */ 
 460                                 /* Display error about long key */ 
 461                                 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
); 
 468                 } ZEND_HASH_FOREACH_END(); 
 470                 /* Check if val array is initialized */ 
 471                 /*XXX: this step is required to workaround zend engine half initialized hashtable when val array is empty that trigger a segfault*/ 
 472                 if ((GC_FLAGS(Z_ARR_P(val
)) & HASH_FLAG_INITIALIZED
) != HASH_FLAG_INITIALIZED
) { 
 473                         /* Allocate hash table */ 
 474                         /* XXX: this is required to avoid segfault in zend_hash_add when the array is empty */ 
 475                         ALLOC_HASHTABLE(Z_ARR_P(val
)); 
 476                         /* Init hash table */ 
 477                         zend_hash_init(Z_ARR_P(val
), 0, NULL
, ZVAL_PTR_DTOR
, 0); 
 478                         zend_hash_copy(Z_ARR_P(val
), ctag
, (copy_ctor_func_t
) zval_add_ref
); 
 481                 /* Check if entry has no type */ 
 486                         /* Type key string */ 
 487                         zend_string 
*tkey 
= zend_string_init("type", strlen("type"), 0); 
 489                         /* Check if key is '' */ 
 490                         if (ZSTR_LEN(key
) == 0) { 
 491                                 /* Set tval value to root */ 
 492                                 ZVAL_LONG(&tval
, (type 
= BBCODE_TYPE_ROOT
)); 
 496                         /* Check if key is img */ 
 497                         } else if (strcmp("img", ZSTR_VAL(key
)) == 0) { 
 498                                 /* Set tval value to single */ 
 499                                 ZVAL_LONG(&tval
, (type 
= BBCODE_TYPE_SINGLE
)); 
 500                         /* Handle other key as multi */ 
 502                                 /* Set tval value to multi */ 
 503                                 ZVAL_LONG(&tval
, (type 
= BBCODE_TYPE_MULTI
)); 
 506                         /* Add new type key */ 
 507                         ZEND_ASSERT(zend_hash_add(Z_ARR_P(val
), tkey
, &tval
) != NULL
); 
 510                         zend_string_release(tkey
); 
 516                 /* Check for root type */ 
 517                 if (type 
== BBCODE_TYPE_ROOT
) { 
 518                         /* Check if has parent */ 
 520                                 /* Display error about parent entry */ 
 521                                 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
); 
 526                         /* TODO: Compute child here */ 
 527                         //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) 
 528                         //TODO: display error if type=root and child was not provided| XXX: we can compute that shit here for lazy morons 
 530                                 //TODO compute child by looping on each non root element and element with no parent or with root in parent 
 531                                 //XXX: it seems that a better approach would be to save root element en push each element one by one ? 
 533                 /* Check for multi type */ 
 534                 } else if (type 
== BBCODE_TYPE_MULTI
) { 
 535                         /* Check if has open entry */ 
 538                                 zend_string 
*mkey 
= zend_string_init("open", strlen("open"), 0); 
 544                                 zend_string 
*mtag 
= zend_string_init("<>", strlen("<>") + ZSTR_LEN(key
) + (hasArg
?strlen("%s"):0), 0); 
 545                                 memcpy(ZSTR_VAL(mtag
)+sizeof(char), ZSTR_VAL(key
), ZSTR_LEN(key
)); 
 547                                         memcpy(ZSTR_VAL(mtag
)+(1+ZSTR_LEN(key
))*sizeof(char), "%s>", strlen("%s>")); 
 549                                         memcpy(ZSTR_VAL(mtag
)+(1+ZSTR_LEN(key
))*sizeof(char), ">", strlen(">")); 
 553                                 ZVAL_STRING(&mval
, ZSTR_VAL(mtag
)); 
 556                                 ZEND_ASSERT(zend_hash_add(Z_ARR_P(val
), mkey
, &mval
) != NULL
); 
 559                                 zend_string_release(mtag
); 
 562                                 zend_string_release(mkey
); 
 565                         /* Check if has close entry */ 
 568                                 zend_string 
*mkey 
= zend_string_init("close", strlen("close"), 0); 
 574                                 zend_string 
*mtag 
= zend_string_init("</>", strlen("</>") + ZSTR_LEN(key
), 0); 
 575                                 memcpy(ZSTR_VAL(mtag
)+2*sizeof(char), ZSTR_VAL(key
), ZSTR_LEN(key
)); 
 576                                 memcpy(ZSTR_VAL(mtag
)+(2+ZSTR_LEN(key
))*sizeof(char), ">", strlen(">")); 
 579                                 ZVAL_STRING(&mval
, ZSTR_VAL(mtag
)); 
 582                                 ZEND_ASSERT(zend_hash_add(Z_ARR_P(val
), mkey
, &mval
) != NULL
); 
 585                                 zend_string_release(mtag
); 
 588                                 zend_string_release(mkey
); 
 591                         /* Check if has not default */ 
 594                                 zend_string 
*mkey 
= zend_string_init("default", strlen("default"), 0); 
 600                                 ZVAL_STRING(&mval
, "%s"); 
 603                                 ZEND_ASSERT(zend_hash_add(Z_ARR_P(val
), mkey
, &mval
) != NULL
); 
 606                                 zend_string_release(mkey
); 
 609                         //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) 
 611                 /* Check for single type */ 
 612                 } else if (type 
== BBCODE_TYPE_SINGLE
) { 
 613                         /* Check if has open entry */ 
 615                                 /* Display error about open entry */ 
 616                                 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
); 
 621                         /* Check if has close entry */ 
 623                                 /* Display error about close entry */ 
 624                                 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
); 
 629                         /* Check if has not default */ 
 632                                 zend_string 
*skey 
= zend_string_init("default", strlen("default"), 0); 
 640                                 /* Check if jey is img */ 
 641                                 if (strcmp("img", ZSTR_VAL(key
)) == 0) { 
 642                                         stag 
= zend_string_init("<img src=\"%s\"/>", strlen("<img src=\"%s\"/>") + (hasArg
?strlen("%s"):0), 0); 
 644                                                 memcpy(ZSTR_VAL(stag
)+strlen("<img src=\"%s\"")*sizeof(char), "%s/>", strlen("%s/>")); 
 646                                                 memcpy(ZSTR_VAL(stag
)+strlen("<img src=\"%s\"")*sizeof(char), "/>", strlen("/>")); 
 649                                         stag 
= zend_string_init("</>", strlen("</>") + ZSTR_LEN(key
) + (hasArg
?strlen("%s"):0), 0); 
 650                                         memcpy(ZSTR_VAL(stag
)+sizeof(char), ZSTR_VAL(key
), ZSTR_LEN(key
)); 
 652                                                 memcpy(ZSTR_VAL(stag
)+(1+ZSTR_LEN(key
))*sizeof(char), "%s/>", strlen("%s/>")); 
 654                                                 memcpy(ZSTR_VAL(stag
)+(1+ZSTR_LEN(key
))*sizeof(char), "/>", strlen("/>")); 
 659                                 ZVAL_STRING(&sval
, ZSTR_VAL(stag
)); 
 662                                 ZEND_ASSERT(zend_hash_add(Z_ARR_P(val
), skey
, &sval
) != NULL
); 
 665                                 zend_string_release(stag
); 
 668                                 zend_string_release(skey
); 
 671                         /* Check if has child */ 
 673                                 /* Display error about child entry */ 
 674                                 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
); 
 679                         //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) 
 683                 printf("key=>%s\n", ZSTR_VAL(key
)); 
 685                 php_debug_zval_dump(val
, 0); 
 691         } ZEND_HASH_FOREACH_END(); 
 693         /* Check if not one unique root */ 
 695                         /* Display error about multiple root */ 
 696                         php_error_docref(NULL TSRMLS_CC
, E_ERROR
, "Has %d BBCODE::TYPE_ROOT entry, require one unique", hasRoot
); 
 706 /* {{{ static zend_bool *bbcode_check_smiley(HashTable *smiley TSRMLS_DC) { 
 707  * Check that smiley hash is valid */ 
 708 static zend_bool 
bbcode_check_smiley(HashTable 
*smiley TSRMLS_DC
) { 
 709         /* Key numeric value */ 
 719         /* Iterate on each key val until hash end is reached */ 
 720         ZEND_HASH_FOREACH_KEY_VAL(smiley
, idx
, key
, val
) { 
 721                 /* Check that key is a string */ 
 723                         /* Display error about long key */ 
 724                         php_error_docref(NULL TSRMLS_CC
, E_ERROR
, "Key %ld[%d] from argument smiley is not an expected string", idx
, pos
); 
 729                 /* Check that value exists */ 
 731                         /* Display error about long key */ 
 732                         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
); 
 735                 /* Check that value is not an array */ 
 736                 } else if (Z_TYPE_P(val
) == IS_ARRAY
) { 
 737                         /* Display error about long key */ 
 738                         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
); 
 745         } ZEND_HASH_FOREACH_END(); 
 752 /* {{{ PHP_METHOD(BBCode, __construct) { 
 754 PHP_METHOD(BBCode
, __construct
) { 
 755         HashTable 
*tag 
= NULL
; 
 756         HashTable 
*smiley 
= NULL
; 
 757         zend_long flag 
= BBCODE_DEFAULT_FLAG
; 
 759 /*TODO: init tag with [ 
 762 /* TODO: init smiley with default list 
 763  * TODO: use prefix in an ini parameter ? ini_setable ? 
 764  * TODO: make sure it's not really a global and per-app param ? 
 767         /* Retrieve the pointer to this object */ 
 768         bbcode_object 
*obj 
= Z_BBCODE_P(getThis()); 
 770         /* Verify that the pointer is leading somewhere */ 
 773         /* Set default values */ 
 775         obj
->smiley 
= smiley
; 
 776         Z_LVAL(obj
->flag
) = flag
; 
 778         /* Parse parameters */ 
 779         ZEND_PARSE_PARAMETERS_START(1, 3) 
 780                 Z_PARAM_ARRAY_HT(tag
) 
 781                 Z_PARAM_ARRAY_HT(smiley
) 
 783         ZEND_PARSE_PARAMETERS_END(); 
 785         /*if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "h|hl", &tag, &smiley, &flag) == FAILURE) { 
 789         /* Save tag argument hash in current object */ 
 790         //TODO: check tag structure 
 791         if (bbcode_check_tag(tag
)) { 
 794                 zend_hash_destroy(tag
); 
 795                 zend_hash_destroy(smiley
); 
 796                 ZVAL_NULL(getThis() TSRMLS_CC
); 
 800         /* Save smiley argument hash in current object if provided */ 
 801         //TODO: check smiley structure 
 803                 if (bbcode_check_smiley(smiley
)) { 
 804                         obj
->smiley 
= smiley
; 
 806                         zend_hash_destroy(tag
); 
 807                         zend_hash_destroy(smiley
); 
 808                         ZVAL_NULL(getThis() TSRMLS_CC
); 
 813         /* Save flag argument long in current object if provided */ 
 815                 Z_LVAL(obj
->flag
) = flag
; 
 820 /* {{{ PHP_METHOD(BBCode, __destruct) { 
 822 PHP_METHOD(BBCode
, __destruct
) { 
 823         /* Retrieve the pointer to this object */ 
 824         bbcode_object 
*obj 
= Z_BBCODE_P(getThis()); 
 826         /* Verify that the pointer is leading somewhere */ 
 829         /* Free tag hashtable */ 
 834         /* Free smiley hashtable */ 
 841 /* { { { PHP_METHOD(BBCode, parse) { 
 843 PHP_METHOD(BBCode
, parse
) { 
 844         zend_string 
*str 
= NULL
; 
 846         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC
, "S", &str
) == FAILURE
) { 
 850         //TODO: create a tree for storing result 
 851         //TODO: apply recursively search of next possible tags (or a closing parent tag ?) regexp and store result inside 
 853         //TODO: apply the smiley parsing on string 
 859 /* {{{ bbcode_methods[] 
 861  * Every BBCode visible function must have an entry in bbcode_methods[]. 
 863 const zend_function_entry bbcode_methods
[] = { 
 864         PHP_ME(BBCode
, __construct
, bbcode_construct_arginfo
, ZEND_ACC_PUBLIC
|ZEND_ACC_CTOR
) 
 865         PHP_ME(BBCode
, __destruct
, NULL
, ZEND_ACC_PUBLIC
|ZEND_ACC_DTOR
) 
 866         PHP_ME(BBCode
, parse
, bbcode_parse_arginfo
, ZEND_ACC_PUBLIC
) 
 871 /* {{{ PHP_MINIT_FUNCTION 
 873 PHP_MINIT_FUNCTION(bbcode
) 
 875         /* If you have INI entries, uncomment these lines 
 876         REGISTER_INI_ENTRIES(); 
 879         /* Tmp zend class entry*/ 
 880         zend_class_entry tmp_ce
; 
 882         /* Init BBCode class entry */ 
 883         INIT_CLASS_ENTRY(tmp_ce
, "BBCode", bbcode_methods
); 
 885         /* Register BBCode class entry */ 
 886         bbcode_ce 
= zend_register_internal_class(&tmp_ce TSRMLS_CC
); 
 887         bbcode_ce
->create_object 
= bbcode_create
; 
 890         BBCODE_DEF(BBCODE_GEN_CONST
) 
 892         /* Init BBCodeException class entry */ 
 893         INIT_CLASS_ENTRY(tmp_ce
, "BBCodeException", NULL
); 
 895         /* Register BBCodeException class entry */ 
 896         bbcode_exception_ce 
= zend_register_internal_class_ex(&tmp_ce
, zend_exception_get_default(TSRMLS_C
) TSRMLS_CC
); 
 902 /* {{{ PHP_MSHUTDOWN_FUNCTION 
 904 PHP_MSHUTDOWN_FUNCTION(bbcode
) 
 906         /* uncomment this line if you have INI entries 
 907         UNREGISTER_INI_ENTRIES(); 
 913 /* {{{ PHP_MINFO_FUNCTION 
 915 PHP_MINFO_FUNCTION(bbcode
) 
 917         php_info_print_table_start(); 
 918         php_info_print_table_header(2, PHP_BBCODE_NAME 
" support", "enabled"); 
 919         php_info_print_table_row(2, "Extension version", PHP_BBCODE_VERSION
); 
 920         php_info_print_table_end(); 
 922         /* Remove comments if you have entries in php.ini 
 923         DISPLAY_INI_ENTRIES(); 
 928 /* {{{ bbcode_module_entry 
 930 zend_module_entry bbcode_module_entry 
= { 
 931         STANDARD_MODULE_HEADER
, 
 933         NULL
, /*bbcode_functions,*/ 
 935         PHP_MSHUTDOWN(bbcode
), 
 940         STANDARD_MODULE_PROPERTIES
 
 944 #ifdef COMPILE_DL_BBCODE 
 946 ZEND_TSRMLS_CACHE_DEFINE() 
 948 ZEND_GET_MODULE(bbcode
) 
 956  * vim600: noet sw=4 ts=4 fdm=marker 
 957  * vim<600: noet sw=4 ts=4