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