diff --git a/README.md b/README.md index c20ba24..cea67c1 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,23 @@ public CSS3Processor::__construct(void) : CSS3Processor * Throws exceptions on errors. +``` +public CSS3Processor::setNotifier(int $type, callable $callback) : bool +``` + +* Registers a callback for the given token `$type` that will be called + during tokenisation. The callback gets an info array of the current + token and context. +* All return values from the callback are discarded. +* The parameter `$type` can be set to any of the `extcss3` Type Constants + listed below. +* If a Modifier is registered for the same token type, the Notifier is + allways called bevore the Modifier. +* Multiple notifier callbacks per token type are possible. +* Returns `true` on success. +* Throws exceptions on errors. + + ``` public CSS3Processor::setModifier(int $type, callable $callback) : bool ``` diff --git a/extcss3/intern.c b/extcss3/intern.c index 0c73772..d47ac44 100644 --- a/extcss3/intern.c +++ b/extcss3/intern.c @@ -40,6 +40,11 @@ extcss3_decl *extcss3_create_decl(void) return (extcss3_decl *)calloc(1, sizeof(extcss3_decl)); } +extcss3_sig *extcss3_create_signal(void) +{ + return (extcss3_sig *)calloc(1, sizeof(extcss3_sig)); +} + /* ==================================================================================================== */ void extcss3_release_intern(extcss3_intern *intern) @@ -86,6 +91,10 @@ void extcss3_release_intern(extcss3_intern *intern) } } + if (intern->notifier.base != NULL) { + extcss3_release_signals_list(&intern->notifier); + } + free(intern); } @@ -248,6 +257,36 @@ void extcss3_release_decls_list(extcss3_decl *list) extcss3_release_decl(list); } +void extcss3_release_signal(extcss3_not *notifier, extcss3_sig *signal) +{ + if (signal == NULL) { + return; + } + + if ((signal->callable != NULL) && (notifier != NULL) && (notifier->destructor != NULL)) { + notifier->destructor(signal->callable); + } + + free(signal); +} + +void extcss3_release_signals_list(extcss3_not *notifier) +{ + extcss3_sig *next, *list = notifier->base; + + if (list == NULL) { + return; + } + + while (list->next != NULL) { + next = list->next->next; + extcss3_release_signal(notifier, list->next); + list->next = next; + } + + extcss3_release_signal(notifier, list); +} + /* ==================================================================================================== */ bool extcss3_set_css_string(extcss3_intern *intern, char *css, size_t len, unsigned int *error) @@ -298,6 +337,36 @@ bool extcss3_set_css_string(extcss3_intern *intern, char *css, size_t len, unsig return EXTCSS3_SUCCESS; } +bool extcss3_set_notifier(extcss3_intern *intern, unsigned int type, void *callable, unsigned int *error) +{ + extcss3_sig *signal; + + if ((intern == NULL) || (intern->notifier.destructor == NULL) || (intern->notifier.callback == NULL) || (callable == NULL)) { + *error = EXTCSS3_ERR_NULL_PTR; + + return EXTCSS3_FAILURE; + } + + if ((signal = extcss3_create_signal()) == NULL) { + *error = EXTCSS3_ERR_MEMORY; + + return EXTCSS3_FAILURE; + } + + signal->type = type; + signal->callable = callable; + + if (intern->notifier.base == NULL) { + intern->notifier.base = signal; + } else { + intern->notifier.last->next = signal; + } + + intern->notifier.last = signal; + + return EXTCSS3_SUCCESS; +} + bool extcss3_set_modifier(extcss3_intern *intern, unsigned int type, void *callable, unsigned int *error) { if ((intern == NULL) || (intern->modifier.destructor == NULL) || (intern->modifier.callback == NULL) || (callable == NULL)) { diff --git a/extcss3/intern.h b/extcss3/intern.h index afdf93f..f96aaf3 100644 --- a/extcss3/intern.h +++ b/extcss3/intern.h @@ -25,8 +25,11 @@ void extcss3_release_rules_list(extcss3_rule *list); void extcss3_release_block(extcss3_block *block); void extcss3_release_decl(extcss3_decl *decl); void extcss3_release_decls_list(extcss3_decl *list); +void extcss3_release_signal(extcss3_not *notifier, extcss3_sig *signal); +void extcss3_release_signals_list(extcss3_not *notifier); bool extcss3_set_css_string(extcss3_intern *intern, char *css, size_t len, unsigned int *error); +bool extcss3_set_notifier(extcss3_intern *intern, unsigned int type, void *callable, unsigned int *error); bool extcss3_set_modifier(extcss3_intern *intern, unsigned int type, void *callable, unsigned int *error); bool extcss3_set_vendor_string(extcss3_intern *intern, char *name, size_t len, unsigned int *error); diff --git a/extcss3/tokenizer/tokenizer.c b/extcss3/tokenizer/tokenizer.c index 0501e7c..a244821 100644 --- a/extcss3/tokenizer/tokenizer.c +++ b/extcss3/tokenizer/tokenizer.c @@ -69,7 +69,7 @@ bool extcss3_tokenize(extcss3_intern *intern, unsigned int *error) } else if ((intern->base_token = token = extcss3_create_token()) == NULL) { return _extcss3_cleanup_tokenizer(*error = EXTCSS3_ERR_MEMORY, NULL, false, false); } else if ( - EXTCSS3_HAS_MODIFIER(intern) && + ((intern->notifier.base != NULL) || EXTCSS3_HAS_MODIFIER(intern)) && ((intern->base_ctxt = intern->last_ctxt = extcss3_create_ctxt()) == NULL) ) { return _extcss3_cleanup_tokenizer(*error = EXTCSS3_ERR_MEMORY, intern, true, false); @@ -396,6 +396,7 @@ static inline bool _extcss3_next_char(extcss3_intern *intern, unsigned int *erro static inline bool _extcss3_token_add(extcss3_intern *intern, extcss3_token *token, unsigned int *error) { extcss3_token *prev; + extcss3_sig *signal; if ((token->prev = intern->last_token) != NULL) { intern->last_token->next = token; @@ -431,6 +432,24 @@ static inline bool _extcss3_token_add(extcss3_intern *intern, extcss3_token *tok /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ if (intern->base_ctxt != NULL) { + /* Calling notifier(s) */ + if (((signal = intern->notifier.base) != NULL) && (intern->notifier.callback != NULL)) { + while (signal != NULL) { + if (signal->type == token->type) { + intern->notifier.callback(intern, signal); + + if ((token->user.str != NULL) || (token->user.len != 0)) { + *error = EXTCSS3_ERR_INV_VALUE; + + return EXTCSS3_FAILURE; + } + } + + signal = signal->next; + } + } + + /* Calling modifier */ if (EXTCSS3_TYPE_IS_MODIFIABLE(token->type) && (intern->modifier.callback != NULL)) { intern->modifier.callback(intern); diff --git a/extcss3/types.h b/extcss3/types.h index 625326a..9bf5d74 100644 --- a/extcss3/types.h +++ b/extcss3/types.h @@ -15,6 +15,7 @@ #define EXTCSS3_ERR_BYTES_CORRUPTION ((unsigned int)2) #define EXTCSS3_ERR_NULL_PTR ((unsigned int)3) #define EXTCSS3_ERR_INV_PARAM ((unsigned int)4) +#define EXTCSS3_ERR_INV_VALUE ((unsigned int)5) /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ @@ -125,6 +126,10 @@ typedef struct _extcss3_ctxt extcss3_ctxt; typedef struct _extcss3_vendor extcss3_vendor; +typedef struct _extcss3_sig extcss3_sig; + +typedef struct _extcss3_not extcss3_not; + typedef struct _extcss3_mod extcss3_mod; typedef struct _extcss3_decl extcss3_decl; @@ -181,6 +186,23 @@ struct _extcss3_vendor extcss3_vendor *next; }; +struct _extcss3_sig +{ + unsigned int type; + extcss3_sig *next; + + void *callable; +}; + +struct _extcss3_not +{ + extcss3_sig *base; + extcss3_sig *last; + + void (*callback)(extcss3_intern *intern, extcss3_sig *signal); + void (*destructor)(void *callable); +}; + struct _extcss3_mod { void *string; @@ -242,6 +264,7 @@ struct _extcss3_intern extcss3_vendor *base_vendor; extcss3_vendor *last_vendor; + extcss3_not notifier; extcss3_mod modifier; }; diff --git a/php_extcss3.c b/php_extcss3.c index e564f3f..bd970c1 100644 --- a/php_extcss3.c +++ b/php_extcss3.c @@ -62,6 +62,9 @@ static inline void php_extcss3_throw_exception(unsigned int error) case EXTCSS3_ERR_INV_PARAM: zend_throw_exception(zend_ce_exception, "extcss3: Invalid paramenter or parameter type given", EXTCSS3_ERR_INV_PARAM); break; + case EXTCSS3_ERR_INV_VALUE: + zend_throw_exception(zend_ce_exception, "extcss3: Invalid, unallowed or unexpected value found", EXTCSS3_ERR_INV_VALUE); + break; default: zend_throw_exception(zend_ce_exception, "extcss3: Unknown internal error", 0); } @@ -138,6 +141,32 @@ static inline void php_extcss3_make_data_array(extcss3_intern *intern, zval *dat zval_ptr_dtor(&empty); } +static void php_extcss3_notifier_callback(extcss3_intern *intern, extcss3_sig *signal) +{ + zval data, retval, *args, *callable; + + if ((intern == NULL) || (intern->last_token == NULL) || (signal == NULL)) { + return; + } + + if ((callable = signal->callable) == NULL) { + return; + } + + php_extcss3_make_data_array(intern, &data); + + args = safe_emalloc(sizeof(zval), 1, 0); + ZVAL_COPY(&args[0], &data); + + call_user_function_ex(EG(function_table), NULL, callable, &retval, 1, args, 0, NULL); + + zval_ptr_dtor(&data); + zval_ptr_dtor(&args[0]); + zval_ptr_dtor(&retval); + + efree(args); +} + static void php_extcss3_modifier_callback(extcss3_intern *intern) { zval data, retval, *args, *callable = NULL; @@ -191,9 +220,9 @@ static void php_extcss3_modifier_callback(extcss3_intern *intern) efree(args); } -static void php_extcss3_modifier_destructor(void *modifier) +static void php_extcss3_callable_destructor(void *callable) { - zval *ptr = (zval *)modifier; + zval *ptr = (zval *)callable; if (ptr == NULL) { return; @@ -232,8 +261,11 @@ PHP_METHOD(CSS3Processor, __construct) if (intern == NULL) { php_extcss3_throw_exception(EXTCSS3_ERR_MEMORY); } else { + intern->notifier.callback = php_extcss3_notifier_callback; + intern->notifier.destructor = php_extcss3_callable_destructor; + intern->modifier.callback = php_extcss3_modifier_callback; - intern->modifier.destructor = php_extcss3_modifier_destructor; + intern->modifier.destructor = php_extcss3_callable_destructor; object->intern = intern; } @@ -276,6 +308,40 @@ PHP_METHOD(CSS3Processor, setModifier) RETURN_TRUE; } +PHP_METHOD(CSS3Processor, setNotifier) +{ + extcss3_object *object = extcss3_object_fetch(Z_OBJ_P(getThis())); + extcss3_intern *intern = object->intern; + zval *callable, *copy; + size_t type; + unsigned int error = 0; + + if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "lz", &type, &callable)) { + return; + } else if (intern == NULL) { + php_extcss3_throw_exception(EXTCSS3_ERR_NULL_PTR); + return; + } + + copy = (zval *)ecalloc(1, sizeof(zval)); + + if (copy == NULL) { + php_extcss3_throw_exception(EXTCSS3_ERR_MEMORY); + return; + } + + ZVAL_COPY(copy, callable); + + if (EXTCSS3_SUCCESS != extcss3_set_notifier(intern, type, copy, &error)) { + intern->notifier.destructor(copy); + + php_extcss3_throw_exception(error); + return; + } + + RETURN_TRUE; +} + PHP_METHOD(CSS3Processor, dump) { extcss3_object *object = extcss3_object_fetch(Z_OBJ_P(getThis())); @@ -359,6 +425,7 @@ PHP_METHOD(CSS3Processor, minify) zend_function_entry extcss3_methods[] = { PHP_ME(CSS3Processor, __construct, arginfo_EXTCSS3_void, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) PHP_ME(CSS3Processor, setModifier, arginfo_EXTCSS3_setModifier, ZEND_ACC_PUBLIC) + PHP_ME(CSS3Processor, setNotifier, arginfo_EXTCSS3_setModifier, ZEND_ACC_PUBLIC) PHP_ME(CSS3Processor, dump, arginfo_EXTCSS3_dump, ZEND_ACC_PUBLIC) PHP_ME(CSS3Processor, minify, arginfo_EXTCSS3_minify, ZEND_ACC_PUBLIC) PHP_FE_END diff --git a/tests/setNotifier.phpt b/tests/setNotifier.phpt new file mode 100644 index 0000000..73c8bf1 --- /dev/null +++ b/tests/setNotifier.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test CSS3Processor::setNotifier() method +--FILE-- +setNotifier(CSS3Processor::TYPE_AT_KEYWORD, ['Test_Notifier', 'A'])); +var_dump($oProcessor->setNotifier(CSS3Processor::TYPE_AT_KEYWORD, ['Test_Notifier', 'B'])); + +$oProcessor->dump('@charset "UTF-8";'); + +?> +===DONE=== +--EXPECT-- +bool(true) +bool(true) +string(11) "A: @charset" +string(11) "B: @charset" +===DONE===