5810aae38435468b394260e30b05e66298035a6a
[bbcode] / bbcode.c
1
2 #ifdef HAVE_CONFIG_H
3 #include "config.h"
4 #endif
5
6 #include "php.h"
7 #include "php_ini.h"
8 #include "ext/standard/info.h"
9 #include "Zend/zend_exceptions.h"
10 #include "php_bbcode.h"
11
12 #include <stdbool.h>
13
14 /* If you declare any globals in php_bbcode.h uncomment this:
15 ZEND_DECLARE_MODULE_GLOBALS(bbcode)
16 */
17
18 typedef struct _bbcode_object {
19 HashTable *tag;
20 HashTable *smiley;
21 zval flag;
22 zend_object std;
23 } bbcode_object;
24
25 /* True global resources - no need for thread safety here */
26 zend_object_handlers bbcode_handlers;
27
28 /* BBCode object */
29 zend_class_entry *bbcode_ce;
30
31 /* BBCode exception object */
32 zend_class_entry *bbcode_exception_ce;
33
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*/\
39
40 #endif
41
42 #define BBCODE_DEF(GEN) \
43 /* Types */ \
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*/\
47 \
48 /* Quotes */ \
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*/\
53 \
54 /* Corrections */ \
55 GEN(CORRECT_AUTO) /*BBCODE_AUTO_CORRECT*/\
56 GEN(CORRECT_REOPEN) /*BBCODE_CORRECT_REOPEN_TAGS*/\
57 GEN(CORRECT_NOTREE) /*BBCODE_DISABLE_TREE_BUILD*/\
58 \
59 /* Smileys */ \
60 GEN(SMILEY_ON) /*BBCODE_DEFAULT_SMILEYS_ON*/\
61 GEN(SMILEY_CI) /*BBCODE_SMILEYS_CASE_INSENSITIVE*/\
62 \
63 /* Flags */ \
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*/
68
69
70 /* Generate enum version */
71 #define BBCODE_GEN_ENUM(NAME) BBCODE_ ## NAME,
72
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);
75
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"\
89 ) \
90 ) \
91 ) \
92 ) \
93 ) \
94 ) \
95 ) \
96 ) \
97 ) \
98 ) \
99 )
100
101 /* All flags enum set */
102 enum {
103 BBCODE_DEF(BBCODE_GEN_ENUM)
104 };
105
106 /* Set default flag
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
109
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);
115 /* }}} */
116
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));
121 }
122 /* }}} */
123
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)))
127 /* }}} */
128
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);
134
135 //zend_object_std_dtor(&obj->std TSRMLS_CC);
136
137 /*if (obj->flag) {
138 efree(obj->flag);
139 }*/
140
141 //efree(obj);
142 }
143 /* }}} */
144
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) {
148
149 /*if (obj->flag) {
150 efree(obj->flag);
151 }*/
152
153 //efree(obj);
154 zend_object_std_dtor((zend_object *)obj);
155 }
156 /* }}} */
157
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) {
161
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);
166
167 /* Call members cloning function */
168 zend_objects_clone_members(newobj, oldobj);
169
170 /* Retrieve from zend object old bbcode */
171 bbcode_object *bboldobj = Z_BBCODE_P(obj);
172
173 /* Retrieve from zend object new bbcode */
174 bbcode_object *bbnewobj = bbcode_fetch(newobj);
175
176 /* Restore object member values */
177 //TODO: deal with the other one (tag|smiley)
178 Z_LVAL(bbnewobj->flag) = Z_LVAL(bboldobj->flag);
179
180 /* Return the new object */
181 return newobj;
182 }
183 /* }}} */
184
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));
189
190 zend_object_std_init(&obj->std, ce TSRMLS_CC);
191 object_properties_init(&obj->std, ce TSRMLS_CC);
192
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;
198
199 obj->std.handlers = &bbcode_handlers;
200
201 Z_LVAL(obj->flag) = 0;
202
203 return &obj->std;
204 }
205 /* }}} */
206
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, 1)
212 ZEND_ARG_ARRAY_INFO(0, smiley, 1)
213 // ZEND_ARG_INFO(pass_by_ref, name)
214 ZEND_ARG_INFO(0, flag)
215 ZEND_END_ARG_INFO()
216 /* }}} */
217
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)
223 ZEND_END_ARG_INFO()
224 /* }}} */
225
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 */
230 zend_long idx;
231 /* Key string */
232 zend_string *key;
233 /* Value */
234 zval *val;
235
236 /* Key position */
237 int pos = 0;
238
239 /* Has a root */
240 int hasRoot = 0;
241
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 */
245 if (!key) {
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);
248 /* Return failure */
249 return 0;
250 }
251
252 /* Check that value exists */
253 if (val == NULL) {
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);
256 /* Return failure */
257 return 0;
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));
262 /* Return failure */
263 return 0;
264 }
265
266 /* Store child tag array */
267 HashTable *ctag = Z_ARRVAL_P(val);
268 /* Child key numeric value */
269 zend_long cidx;
270 /* Child key string */
271 zend_string *ckey;
272 /* Child value */
273 zval *cval;
274
275 /* Child key position */
276 int cpos = 0, type;
277
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;
280
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 */
284 if (!ckey) {
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);
287 /* Return failure */
288 return 0;
289 }
290
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);
295 /* Return failure */
296 return 0;
297 /* Check if current ckey is type */
298 } else if (strcmp("type", ZSTR_VAL(ckey)) == 0) {
299 /* Extract type */
300 type = Z_LVAL_P(cval);
301
302 /* Check that current type is valid */
303 if (
304 type != BBCODE_TYPE_ROOT &&
305 type != BBCODE_TYPE_MULTI &&
306 type != BBCODE_TYPE_SINGLE
307 ) {
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);
310 /* Return failure */
311 return 0;
312 /* Check if root */
313 } else if (type == BBCODE_TYPE_ROOT) {
314 /* Grow hasRoot */
315 hasRoot++;
316 }
317
318 /* Set hasType */
319 hasType = true;
320 /* Check if current key is parent */
321 } else if (strcmp("parent", ZSTR_VAL(ckey)) == 0) {
322 /* Check that value exists */
323 if (cval == NULL) {
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);
326 /* Return failure */
327 return 0;
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));
332 /* Return failure */
333 return 0;
334 }
335
336 /* Parent value */
337 zval *pval;
338
339 /* Child key position */
340 int ppos = 0;
341
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);
348 /* Return failure */
349 return 0;
350 }
351
352 /* Tag key */
353 zend_string *tkey;
354
355 /* Reset found flag */
356 bool found = false;
357
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*/
363 found = true;
364 }
365 } ZEND_HASH_FOREACH_END();
366
367 /* Check if we found the key */
368 if (!found) {
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);
371 /* Return failure */
372 return 0;
373 }
374
375 /* Grow ppos */
376 ppos++;
377 } ZEND_HASH_FOREACH_END();
378
379 /* Set hasParent */
380 hasParent = true;
381 /* Check if current key is child */
382 } else if (strcmp("child", ZSTR_VAL(ckey)) == 0) {
383 /* Check that value exists */
384 if (cval == NULL) {
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);
387 /* Return failure */
388 return 0;
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));
393 /* Return failure */
394 return 0;
395 }
396
397 /* Child value */
398 zval *ccval;
399
400 /* Child key position */
401 int ccpos = 0;
402
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);
409 /* Return failure */
410 return 0;
411 }
412
413 /* Tag key */
414 zend_string *tkey;
415
416 /* Reset found flag */
417 bool found = false;
418
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*/
424 found = true;
425 }
426 } ZEND_HASH_FOREACH_END();
427
428 /* Check if we found the key */
429 if (!found) {
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);
432 /* Return failure */
433 return 0;
434 }
435
436 /* Grow ccpos */
437 ccpos++;
438 } ZEND_HASH_FOREACH_END();
439
440 /* Set hasChild */
441 hasChild = true;
442 /* Check if current key is open */
443 } else if (strcmp("open", ZSTR_VAL(ckey)) == 0) {
444 /* Set hasOpen */
445 hasOpen = true;
446 /* Check if current key is close */
447 } else if (strcmp("close", ZSTR_VAL(ckey)) == 0) {
448 /* Set hasClose */
449 hasClose = true;
450 /* Check if current key is default */
451 } else if (strcmp("default", ZSTR_VAL(ckey)) == 0) {
452 /* Set hasDefault */
453 hasDefault = true;
454 /* Check if current key is arg */
455 } else if (strcmp("arg", ZSTR_VAL(ckey)) == 0) {
456 /* Set hasArg */
457 hasArg = true;
458 /* Check if current key is unknown */
459 } else {
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);
462 /* Return failure */
463 return 0;
464 }
465
466 /* Grow cpos */
467 cpos++;
468 } ZEND_HASH_FOREACH_END();
469
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);
479 }
480
481 /* Check if entry has no type */
482 if (!hasType) {
483 /* Type zval */
484 zval tval;
485
486 /* Type key string */
487 zend_string *tkey = zend_string_init("type", strlen("type"), 0);
488
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));
493
494 /* Grow hasRoot */
495 hasRoot++;
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 */
501 } else {
502 /* Set tval value to multi */
503 ZVAL_LONG(&tval, (type = BBCODE_TYPE_MULTI));
504 }
505
506 /* Add new type key */
507 ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), tkey, &tval) != NULL);
508
509 /* Free type key */
510 zend_string_release(tkey);
511
512 /* Set hasType */
513 hasType = true;
514 }
515
516 /* Check for root type */
517 if (type == BBCODE_TYPE_ROOT) {
518 /* Check if has parent */
519 if (hasParent) {
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);
522 /* Return failure */
523 return 0;
524 }
525
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
529 if (!hasChild) {
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 ?
532 }
533 /* Check for multi type */
534 } else if (type == BBCODE_TYPE_MULTI) {
535 /* Check if has open entry */
536 if (!hasOpen) {
537 /* Key string */
538 zend_string *mkey = zend_string_init("open", strlen("open"), 0);
539
540 /* Value */
541 zval mval;
542
543 /* Tag */
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));
546 if (hasArg) {
547 memcpy(ZSTR_VAL(mtag)+(1+ZSTR_LEN(key))*sizeof(char), "%s>", strlen("%s>"));
548 } else {
549 memcpy(ZSTR_VAL(mtag)+(1+ZSTR_LEN(key))*sizeof(char), ">", strlen(">"));
550 }
551
552 /* Set value */
553 ZVAL_STRING(&mval, ZSTR_VAL(mtag));
554
555 /* Add new key */
556 ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), mkey, &mval) != NULL);
557
558 /* Free tag */
559 zend_string_release(mtag);
560
561 /* Free key */
562 zend_string_release(mkey);
563 }
564
565 /* Check if has close entry */
566 if (!hasClose) {
567 /* Key string */
568 zend_string *mkey = zend_string_init("close", strlen("close"), 0);
569
570 /* Value */
571 zval mval;
572
573 /* Tag */
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(">"));
577
578 /* Set value */
579 ZVAL_STRING(&mval, ZSTR_VAL(mtag));
580
581 /* Add new key */
582 ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), mkey, &mval) != NULL);
583
584 /* Free tag */
585 zend_string_release(mtag);
586
587 /* Free key */
588 zend_string_release(mkey);
589 }
590
591 /* Check if has not default */
592 if (!hasDefault) {
593 /* Key string */
594 zend_string *mkey = zend_string_init("default", strlen("default"), 0);
595
596 /* Value */
597 zval mval;
598
599 /* Set value */
600 ZVAL_STRING(&mval, "%s");
601
602 /* Add new key */
603 ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), mkey, &mval) != NULL);
604
605 /* Free key */
606 zend_string_release(mkey);
607 }
608
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)
610
611 /* Check for single type */
612 } else if (type == BBCODE_TYPE_SINGLE) {
613 /* Check if has open entry */
614 if (hasOpen) {
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);
617 /* Return failure */
618 return 0;
619 }
620
621 /* Check if has close entry */
622 if (hasClose) {
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);
625 /* Return failure */
626 return 0;
627 }
628
629 /* Check if has not default */
630 if (!hasDefault) {
631 /* Key string */
632 zend_string *skey = zend_string_init("default", strlen("default"), 0);
633
634 /* Value */
635 zval sval;
636
637 /* Tag */
638 zend_string *stag;
639
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);
643 if (hasArg) {
644 memcpy(ZSTR_VAL(stag)+strlen("<img src=\"%s\"")*sizeof(char), "%s/>", strlen("%s/>"));
645 } else {
646 memcpy(ZSTR_VAL(stag)+strlen("<img src=\"%s\"")*sizeof(char), "/>", strlen("/>"));
647 }
648 } else {
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));
651 if (hasArg) {
652 memcpy(ZSTR_VAL(stag)+(1+ZSTR_LEN(key))*sizeof(char), "%s/>", strlen("%s/>"));
653 } else {
654 memcpy(ZSTR_VAL(stag)+(1+ZSTR_LEN(key))*sizeof(char), "/>", strlen("/>"));
655 }
656 }
657
658 /* Set value */
659 ZVAL_STRING(&sval, ZSTR_VAL(stag));
660
661 /* Add new key */
662 ZEND_ASSERT(zend_hash_add(Z_ARR_P(val), skey, &sval) != NULL);
663
664 /* Free tag */
665 zend_string_release(stag);
666
667 /* Free key */
668 zend_string_release(skey);
669 }
670
671 /* Check if has child */
672 if (hasChild) {
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);
675 /* Return failure */
676 return 0;
677 }
678
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)
680 }
681
682 #ifndef DEBUG
683 printf("key=>%s\n", ZSTR_VAL(key));
684 fflush(NULL);
685 php_debug_zval_dump(val, 0);
686 fflush(NULL);
687 #endif
688
689 /* Grow pos */
690 pos++;
691 } ZEND_HASH_FOREACH_END();
692
693 /* Check if not one unique root */
694 if (hasRoot != 1) {
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);
697 /* Return failure */
698 return 0;
699 }
700
701 /* Return success */
702 return 1;
703 }
704 /* }}} */
705
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 */
710 zend_long idx;
711 /* Key string */
712 zend_string *key;
713 /* Value */
714 zval *val;
715
716 /* Key position */
717 int pos = 0;
718
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 */
722 if (key == NULL) {
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);
725 /* Return failure */
726 return 0;
727 }
728
729 /* Check that value exists */
730 if (val == NULL) {
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);
733 /* Return failure */
734 return 0;
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);
739 /* Return failure */
740 return 0;
741 }
742
743 /* Grow pos */
744 pos++;
745 } ZEND_HASH_FOREACH_END();
746
747 /* Return success */
748 return 1;
749 }
750 /* }}} */
751
752 /* {{{ PHP_METHOD(BBCode, __construct) {
753 */
754 PHP_METHOD(BBCode, __construct) {
755 HashTable *tag = NULL;
756 HashTable *smiley = NULL;
757 zend_long flag = BBCODE_DEFAULT_FLAG;
758
759 /*TODO: init tag with [
760 '' => [ ? ]
761 ]*/
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 ?
765 * */
766
767 /* Retrieve the pointer to this object */
768 bbcode_object *obj = Z_BBCODE_P(getThis());
769
770 /* Verify that the pointer is leading somewhere */
771 assert(obj != NULL);
772
773 /* Set default values */
774 obj->tag = tag;
775 obj->smiley = smiley;
776 Z_LVAL(obj->flag) = flag;
777
778 /* Parse parameters */
779 ZEND_PARSE_PARAMETERS_START(1, 3)
780 Z_PARAM_ARRAY_HT(tag)
781 Z_PARAM_ARRAY_HT(smiley)
782 Z_PARAM_LONG(flag)
783 ZEND_PARSE_PARAMETERS_END();
784
785 /*if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "h|hl", &tag, &smiley, &flag) == FAILURE) {
786 return;
787 }*/
788
789 /* Save tag argument hash in current object */
790 //TODO: check tag structure
791 if (bbcode_check_tag(tag)) {
792 obj->tag = tag;
793 } else {
794 zend_hash_destroy(tag);
795 zend_hash_destroy(smiley);
796 ZVAL_NULL(getThis() TSRMLS_CC);
797 return;
798 }
799
800 /* Save smiley argument hash in current object if provided */
801 //TODO: check smiley structure
802 if (smiley) {
803 if (bbcode_check_smiley(smiley)) {
804 obj->smiley = smiley;
805 } else {
806 zend_hash_destroy(tag);
807 zend_hash_destroy(smiley);
808 ZVAL_NULL(getThis() TSRMLS_CC);
809 return;
810 }
811 }
812
813 /* Save flag argument long in current object if provided */
814 if (flag) {
815 Z_LVAL(obj->flag) = flag;
816 }
817 }
818 /* }}} */
819
820 /* {{{ PHP_METHOD(BBCode, __destruct) {
821 */
822 PHP_METHOD(BBCode, __destruct) {
823 /* Retrieve the pointer to this object */
824 bbcode_object *obj = Z_BBCODE_P(getThis());
825
826 /* Verify that the pointer is leading somewhere */
827 assert(obj != NULL);
828
829 /* Free tag hashtable */
830 if (obj->tag) {
831 efree(obj->tag);
832 }
833
834 /* Free smiley hashtable */
835 if (obj->smiley) {
836 efree(obj->smiley);
837 }
838 }
839 /* }}} */
840
841 /* { { { PHP_METHOD(BBCode, parse) {
842 */
843 PHP_METHOD(BBCode, parse) {
844 zend_string *str = NULL;
845
846 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "S", &str) == FAILURE) {
847 return;
848 }
849
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
852 //TODO: d
853 //TODO: apply the smiley parsing on string
854
855 RETURN_STR(str);
856 }
857 /* }}} */
858
859 /* {{{ bbcode_methods[]
860 *
861 * Every BBCode visible function must have an entry in bbcode_methods[].
862 */
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)
867 PHP_FE_END
868 };
869 /* }}} */
870
871 /* {{{ PHP_MINIT_FUNCTION
872 */
873 PHP_MINIT_FUNCTION(bbcode)
874 {
875 /* If you have INI entries, uncomment these lines
876 REGISTER_INI_ENTRIES();
877 */
878
879 /* Tmp zend class entry*/
880 zend_class_entry tmp_ce;
881
882 /* Init BBCode class entry */
883 INIT_CLASS_ENTRY(tmp_ce, "BBCode", bbcode_methods);
884
885 /* Register BBCode class entry */
886 bbcode_ce = zend_register_internal_class(&tmp_ce TSRMLS_CC);
887 bbcode_ce->create_object = bbcode_create;
888
889 /* Generate const */
890 BBCODE_DEF(BBCODE_GEN_CONST)
891
892 /* Init BBCodeException class entry */
893 INIT_CLASS_ENTRY(tmp_ce, "BBCodeException", NULL);
894
895 /* Register BBCodeException class entry */
896 bbcode_exception_ce = zend_register_internal_class_ex(&tmp_ce, zend_exception_get_default(TSRMLS_C) TSRMLS_CC);
897
898 return SUCCESS;
899 }
900 /* }}} */
901
902 /* {{{ PHP_MSHUTDOWN_FUNCTION
903 */
904 PHP_MSHUTDOWN_FUNCTION(bbcode)
905 {
906 /* uncomment this line if you have INI entries
907 UNREGISTER_INI_ENTRIES();
908 */
909 return SUCCESS;
910 }
911 /* }}} */
912
913 /* {{{ PHP_MINFO_FUNCTION
914 */
915 PHP_MINFO_FUNCTION(bbcode)
916 {
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();
921
922 /* Remove comments if you have entries in php.ini
923 DISPLAY_INI_ENTRIES();
924 */
925 }
926 /* }}} */
927
928 /* {{{ bbcode_module_entry
929 */
930 zend_module_entry bbcode_module_entry = {
931 STANDARD_MODULE_HEADER,
932 PHP_BBCODE_NAME,
933 NULL, /*bbcode_functions,*/
934 PHP_MINIT(bbcode),
935 PHP_MSHUTDOWN(bbcode),
936 NULL,
937 NULL,
938 PHP_MINFO(bbcode),
939 PHP_BBCODE_VERSION,
940 STANDARD_MODULE_PROPERTIES
941 };
942 /* }}} */
943
944 #ifdef COMPILE_DL_BBCODE
945 #ifdef ZTS
946 ZEND_TSRMLS_CACHE_DEFINE()
947 #endif
948 ZEND_GET_MODULE(bbcode)
949 #endif
950
951 /*
952 * Local variables:
953 * tab-width: 4
954 * c-basic-offset: 4
955 * End:
956 * vim600: noet sw=4 ts=4 fdm=marker
957 * vim<600: noet sw=4 ts=4
958 */