]> granicus.if.org Git - php/commitdiff
Major rework of the basic api which provides:
authorSterling Hughes <sterling@php.net>
Tue, 21 Nov 2000 12:12:19 +0000 (12:12 +0000)
committerSterling Hughes <sterling@php.net>
Tue, 21 Nov 2000 12:12:19 +0000 (12:12 +0000)
        - All Sablotron errors are now caught meaning nothing is
          ever outputted directly to the screen allowing you to
          catch all errors.
        - A mechanism is provided for you to have an error function
          which recieves all sablotron errors.
        - All of the basic functions re-use a single processor increasing
          performance (especially with high loads).
        - Added a bunch of comments, more to come (this way other people
          can easily modify my source).

@ Added the xslt_set_error_handler() function to the Sablotron extension.
@ (Sterling)

@ Improved Sablotron's error handling system allowing you to catch all
@ errors before they are outputted to the screen. (Sterling)

ext/sablot/php_sablot.h
ext/sablot/sablot.c

index 3879965668f733159c96a465e838b30f52505739..fff7ddab6c748e0c51db4a17c52d40d451244ef4 100644 (file)
@@ -32,34 +32,53 @@ extern zend_module_entry sablot_module_entry;
 #define PHP_SABLOT_API
 #endif
 
+/* Module functions */
 PHP_MINIT_FUNCTION(sablot);
+PHP_MSHUTDOWN_FUNCTION(sablot);
 PHP_MINFO_FUNCTION(sablot);
+
+/* Output transformation functions */
 PHP_FUNCTION(xslt_output_begintransform);
 PHP_FUNCTION(xslt_output_endtransform);
+
+/* Basic transformation functions */
 PHP_FUNCTION(xslt_transform);
 PHP_FUNCTION(xslt_process);
+
+/* Advanced API transformation functions */
 PHP_FUNCTION(xslt_create);
 PHP_FUNCTION(xslt_run);
-PHP_FUNCTION(xslt_fetch_result);
-PHP_FUNCTION(xslt_openlog);
-PHP_FUNCTION(xslt_closelog);
 PHP_FUNCTION(xslt_set_sax_handler);
+PHP_FUNCTION(xslt_set_error_handler);
+PHP_FUNCTION(xslt_fetch_result);
 PHP_FUNCTION(xslt_free);
+
+/* Error Handling functions */
 PHP_FUNCTION(xslt_error);
 PHP_FUNCTION(xslt_errno);
+PHP_FUNCTION(xslt_openlog);
+PHP_FUNCTION(xslt_closelog);
 
+/* Sablotron error structure */
 struct _php_sablot_error {
        char *key;
        char *value;
        struct _php_sablot_error *next;
 };
-
 typedef struct _php_sablot_error php_sablot_error;
 
+
+/* Sablotron Handle */
 typedef struct {
-       long index;
+
+    /* Error Handling */
+    zval *errorHandler;
+       php_sablot_error *errors;
+       php_sablot_error errors_start;
        int last_errno;
-       php_sablot_error *errors, errors_start;
+       
+    /* SAX Handling */
+       long index;
        zval *startDocHandler;
        zval *startElementHandler;
        zval *endElementHandler;
@@ -69,37 +88,43 @@ typedef struct {
        zval *PIHandler;
        zval *charactersHandler;
        zval *endDocHandler;
+       
+       /* Sablotron Related */
        SablotHandle p;
+
 } php_sablot;
 
+
+/* Sablotron Globals */
 typedef struct {
-       char *output_transform_file;
-       int le_sablot;
-       int last_errno;
-       int processor;
+    zval *errorHandler;
+    SablotHandle processor;
+    php_sablot_error *errors;
+    php_sablot_error errors_start;
+       char *output_transform_file; /* For output transformations */
+       int last_errno;              /* Global last_errno, if no handle is found */
 } php_sablot_globals;
 
 
 #ifdef ZTS
 #define SABLOTG(v) (sablot_globals->v)
 #define SABLOTLS_FETCH() php_sablot_globals *sablot_globals = ts_resource(sablot_globals_id)
+#define SABLOTG_HANDLE (*sablot_globals)
 #else
 #define SABLOTG(v) (sablot_globals.v)
+#define SABLOTG_HANDLE sablot_globals
 #define SABLOTLS_FETCH()
 #endif
 
 #else
-
 #define phpext_sablot_ptr NULL
-
 #endif
 
 #endif
 
-
 /*
  * Local variables:
  * tab-width: 4
  * c-basic-offset: 4
  * End:
- */
+ */
\ No newline at end of file
index 4833f37c72434239e618641ce28249f4550ceafa..d7b5ac3affb174565454999b439c84e2f35f5e0c 100644 (file)
@@ -31,6 +31,8 @@
 #include "ext/standard/php_output.h"
 #include "php_sablot.h"
 
+static int le_sablot;
+
 /* Functions related to PHP's list handling */
 static void _php_sablot_free_processor(zend_rsrc_list_entry *rsrc);
 
@@ -50,27 +52,59 @@ static SAX_RETURN _php_sablot_sax_endDoc(void *);
 /* Error Handling Functions */
 static MH_ERROR _php_sablot_make_code(void *, SablotHandle, int, unsigned short, unsigned short);
 static MH_ERROR _php_sablot_error(void *, SablotHandle, MH_ERROR, MH_LEVEL, char **);
+static void _php_sablot_standard_error(php_sablot_error *, php_sablot_error, int, int);
 
 /* PHP Utility Functions */
 static void _php_sablot_ht_char(HashTable *, char **);
 static zval *_php_sablot_string_zval(const char *);
 static zval *_php_sablot_resource_zval(long);
 
+/* Macro's */
+
 /* Free macros */
 #define S_FREE(__var) if (__var) efree(__var);
 #define FUNCH_FREE(__var) if (__var) zval_del_ref(&(__var));           
 
-/* ERROR Macro */
-#define SEND_ERROR_INFO(__error, __errlevel) \
-       php_printf("A %s has occurred, sending all relevant information\n", __error); \
-       handle->errors = handle->errors_start.next; \
-       while (handle->errors) { \
-               php_printf("%s: %s\n", handle->errors->key, handle->errors->value); \
-               handle->errors = handle->errors->next; \
-       } \
-       php_error(__errlevel, "");
-
-
+/* ERROR Macros */
+
+#define SABLOT_FREE_ERROR_HANDLE(__handle) \
+    if ((__handle).errors) { \
+        (__handle).errors = (__handle).errors_start.next; \
+        while ((__handle).errors) { \
+            S_FREE((__handle).errors->key); \
+            S_FREE((__handle).errors->value); \
+            (__handle).errors = (__handle).errors->next; \
+        } \
+        S_FREE((__handle).errors); \
+    }
+
+
+/* Sablotron Basic Api macro's */
+#define SABLOT_BASIC_CREATE_PROCESSOR() \
+    if (SABLOTG(processor) == NULL) { \
+        int ret = 0; \
+        \
+        ret = SablotCreateProcessor(&SABLOTG(processor)); \
+        if (ret) { \
+            SABLOTG(last_errno) = ret; \
+            return; \
+        } \
+        \
+        ret = SablotRegHandler(SABLOTG(processor), HLR_MESSAGE, (void *)&mh, (void *)NULL); \
+        if (ret) { \
+            SABLOTG(last_errno) = ret; \
+            return; \
+        } \
+    }
+
+#define SABLOT_BASIC_HANDLE SABLOTG(processor)
+
+/**
+ * SAX Handler structure, this defines the different functions to be
+ * called when Sablotron's internal expat parser reaches the 
+ * different callbacks.  These are the same as the functions which are
+ * defined for expat.
+ */
 static SAXHandler sax = {
        _php_sablot_sax_startDoc,
        _php_sablot_sax_startElement,
@@ -83,14 +117,17 @@ static SAXHandler sax = {
        _php_sablot_sax_endDoc
 };
 
+
+/**
+ * Message Handler structure for use when Sablotron
+ * reports that something has gone wrong.
+ */
 static MessageHandler mh = {
        _php_sablot_make_code,
        _php_sablot_error,
        _php_sablot_error
 };
 
-/* {{{ Begin PHP Extension code */
-
 #ifdef ZTS
 int sablot_globals_id;
 #else
@@ -112,6 +149,7 @@ function_entry sablot_functions[] = {
        PHP_FE(xslt_openlog,                  NULL)
        PHP_FE(xslt_closelog,                 NULL)
        PHP_FE(xslt_set_sax_handler,          NULL)
+       PHP_FE(xslt_set_error_handler,        NULL)
        PHP_FE(xslt_free,                     NULL)
        PHP_FE(xslt_error,                    NULL)
        PHP_FE(xslt_errno,                    NULL)
@@ -122,7 +160,7 @@ zend_module_entry sablot_module_entry = {
        "sablot",
        sablot_functions,
        PHP_MINIT(sablot),
-       NULL,
+       PHP_MSHUTDOWN(sablot),
        NULL,
        NULL,
        PHP_MINFO(sablot),
@@ -133,37 +171,43 @@ zend_module_entry sablot_module_entry = {
 ZEND_GET_MODULE(sablot)
 #endif
 
-/* }}} */
-
 
-/* {{{ MINIT and MINFO Functions */
+/* MINIT and MINFO Functions */
 PHP_MINIT_FUNCTION(sablot)
 {
-       SABLOTLS_FETCH();
-       SABLOTG(le_sablot) = zend_register_list_destructors_ex(_php_sablot_free_processor, NULL, "sablotron", module_number);
+    le_sablot = zend_register_list_destructors_ex(_php_sablot_free_processor, NULL, "Sablotron XSLT", module_number);
+
        return SUCCESS;
 }
 
+PHP_MSHUTDOWN_FUNCTION(sablot)
+{
+    SABLOTLS_FETCH();
+    
+    if (SABLOTG(processor)) {
+        SablotUnregHandler(SABLOTG(processor), HLR_MESSAGE, NULL, NULL);
+        SablotDestroyProcessor(SABLOTG(processor));
+    }
+    
+    return SUCCESS;
+}
+
 PHP_MINFO_FUNCTION(sablot)
 {
        php_info_print_table_start();
-       php_info_print_table_row(2, "Sablotron support", "enabled");
+       php_info_print_table_row(2, "Sablotron XSLT support", "enabled");
        php_info_print_table_end();
 
 }
-/* }}} */
-
-/* }}} */
-
-/* {{{ Begin Extension function */
-
-/* {{{ Begin Output Transformation functions */
 
 /* {{{ proto void xslt_output_begintransform(string file)
    Begin filtering of all data that is being printed out through the XSL file given by the file parameter. */
 PHP_FUNCTION(xslt_output_begintransform)
 {
+    /* The name of the file to pass the output through */
        zval **file;
+       
+       /* needed for the output transformation file name */
        SABLOTLS_FETCH();
        
        if (ZEND_NUM_ARGS() != 1 ||
@@ -172,9 +216,15 @@ PHP_FUNCTION(xslt_output_begintransform)
        }
        convert_to_string_ex(file);
        
+       /* If the buffer exists, free it */
        S_FREE(SABLOTG(output_transform_file));
-               
+       
        SABLOTG(output_transform_file) = estrndup(Z_STRVAL_PP(file), Z_STRLEN_PP(file));
+
+    /**
+     * Start output buffering, NULL signifies that no "user-space" output function
+     * will be used.
+     */
        php_start_ob_buffer(NULL);
 }
 /* }}} */
@@ -186,45 +236,81 @@ PHP_FUNCTION(xslt_output_endtransform)
        char *tRes   = NULL,
             *buffer = NULL;
        int ret = 0;
+       
+       /* Fetch both the output globals and the sablotron globals */
        OLS_FETCH();
        SABLOTLS_FETCH();
 
+    /** 
+     * Make sure that we don't have more than one output buffer going on
+     * at the same time.
+     */
        if (OG(nesting_level) == 0) {
                RETURN_NULL();
        }
 
        buffer = estrndup(OG(active_ob_buffer).buffer, OG(active_ob_buffer).text_length);
 
+    /* Nake sure there is data to send */
        if (strlen(buffer)) {
                char *args[] = {"/_xmlinput", buffer,
                                "/_output",   NULL};
                
-               ret = SablotProcess(SABLOTG(output_transform_file), "arg:/_xmlinput", "arg:/_output", NULL, args, &tRes);
-       }
+               SABLOT_BASIC_CREATE_PROCESSOR();
+               
+               ret = SablotRunProcessor(SABLOT_BASIC_HANDLE, SABLOTG(output_transform_file), 
+                                        "arg:/_xmlinput", "arg:/_output", NULL, args);
+               
+               if (ret) {
+                   SABLOTG(last_errno) = ret;
+                   S_FREE(SABLOTG(output_transform_file));
+                   RETURN_NULL();
+               }
+               
+               ret = SablotGetResultArg(SABLOT_BASIC_HANDLE, "arg:/_output", &tRes);
+               
+               if (ret) {
+                   SABLOTG(last_errno) = ret;
+                   S_FREE(SABLOTG(output_transform_file));
+                   RETURN_NULL();
+               }
+    }
 
+    /**
+     * A non-zero return means error, save this error in the global
+     * errno (for xslt_errno() and xslt_error()) free the output
+     * transformation file and null.
+     */ 
        if (ret) {
                SABLOTG(last_errno) = ret;
                S_FREE(SABLOTG(output_transform_file));
-               return;
+               RETURN_NULL();
        }
-               
-       php_end_ob_buffer(0);
+       
+       /**
+        * If there is a buffer, end output buffering and clear the
+        * current output buffer (so we don't send data twice) 
+        */
+       if (tRes)
+           php_end_ob_buffer(0);
        
        PUTS(tRes);
+       
        S_FREE(SABLOTG(output_transform_file));
        S_FREE(buffer);
 
-       if (tRes) {
-               SablotFree(tRes);
-       } else {
-               php_end_ob_buffer(0);
-       }
+    /**
+     * If there exists a result, free that result
+     * otherwise just end output buffering and send the
+     * data.
+     */
+       if (tRes)
+           SablotFree(tRes);
+       else
+               php_end_ob_buffer(1);
 }
 /* }}} */
 
-/* }}} */
-/* {{{ Begin Simple API */
-
 /* {{{ proto bool xslt_transform(string xslt_uri, string transform_uri, string result_uri[, array xslt_params[, array xslt_args[, string &result]]])
    Transform an XML document, transform_uri, with an XSL stylesheet, xslt_uri with parameters, xslt_params, into the Result buffer, result_uri, xslt_args defines the variables in xslt_uri, transform_uri and result_uri. */
 PHP_FUNCTION(xslt_transform)
@@ -235,11 +321,14 @@ PHP_FUNCTION(xslt_transform)
             **xslt_params, 
                 **xslt_args, 
                 **result;
+
        char **args   = NULL,
             **params = NULL,
             *tResult = NULL;
+
        int argc = ZEND_NUM_ARGS(), 
            ret  = 0;
+
        SABLOTLS_FETCH();
        
        if (argc < 3 || argc > 6 ||
@@ -248,24 +337,45 @@ PHP_FUNCTION(xslt_transform)
        }
        multi_convert_to_string_ex(3, xslt_uri, transform_uri, result_uri);
        
+       /**
+        * if there are more than 3 function arguments, inspect the value of 
+        * the third argument and decide whether or not there are Sablotron
+        * parameters.
+        */
        if (argc > 3) {
-               if (Z_TYPE_PP(xslt_params) != IS_LONG || Z_LVAL_PP(xslt_params) != 0) {
+               if (Z_TYPE_PP(xslt_params) != IS_LONG || Z_LVAL_PP(xslt_params) != 0 ||
+                   Z_TYPE_PP(xslt_params) != IS_NULL) {
                        int numelems, 
                            size;
                        HashTable *ar = HASH_OF(*xslt_params);
 
+            /**
+             * Allocate 2 times the number of elements in
+             * the array, since with associative arrays in PHP
+             * keys are not counted.
+             */
                        numelems = zend_hash_num_elements(ar);
                        size = (numelems * 2 + 1) * sizeof(char *);
                        
                        params = (char **)emalloc(size+1);
                        memset((char *)params, 0, size);
                        
+                       /**
+                        * Translate a PHP array (HashTable *) into a 
+                        * Sablotron array (char **).
+                        */
                        _php_sablot_ht_char(ar, params);
                }
        }
        
+       /**
+        * If there are more than 4 function arguments, inspect the value
+        * of the 4 argument and decide whether or not there are Sablotron
+        * arguments.
+        */
        if (argc > 4) {
-               if (Z_TYPE_PP(xslt_args) != IS_LONG || Z_LVAL_PP(xslt_args) != 0) {
+               if (Z_TYPE_PP(xslt_args) != IS_LONG || Z_LVAL_PP(xslt_args) != 0 ||
+                   Z_TYPE_PP(xslt_args) != IS_NULL) {
                        int numelems, 
                            size;
                        HashTable *ar = HASH_OF(*xslt_args);
@@ -280,21 +390,33 @@ PHP_FUNCTION(xslt_transform)
                }
        }
        
-       ret = SablotProcess(Z_STRVAL_PP(xslt_uri), Z_STRVAL_PP(transform_uri),
-                           Z_STRVAL_PP(result_uri), params, args, &tResult);
-       
+       SABLOT_BASIC_CREATE_PROCESSOR();
+       ret = SablotRunProcessor(SABLOT_BASIC_HANDLE, Z_STRVAL_PP(xslt_uri), 
+                                Z_STRVAL_PP(transform_uri), Z_STRVAL_PP(result_uri), 
+                                params, args);
+
        if (ret) {
                SABLOTG(last_errno) = ret;
                
                S_FREE(params);
                S_FREE(args);
-               if (tResult) {
-                       SablotFree(tResult);
-               }
                
                RETURN_FALSE;
-       } else { RETVAL_TRUE; }
-
+       }
+       
+       ret = SablotGetResultArg(SABLOT_BASIC_HANDLE, Z_STRVAL_PP(result_uri), &tResult);
+       
+       if (ret) {
+           SABLOTG(last_errno) = ret;
+           
+           S_FREE(params);
+           S_FREE(args);
+           
+           if (tResult)
+               SablotFree(tResult);
+    
+        RETURN_FALSE;
+    } else { RETVAL_TRUE; }
 
        if (tResult &&
            argc == 6) {
@@ -327,22 +449,48 @@ PHP_FUNCTION(xslt_process)
                WRONG_PARAM_COUNT;
        }
        multi_convert_to_string_ex(2, xslt, input);
-
+       
+       SABLOT_BASIC_CREATE_PROCESSOR();
+       
+       /**
+        * If we have more than three arguments that means we have
+        * a base!
+        */
        if (argc > 3) {
-               convert_to_string_ex(base);
-               ret = SablotProcessStringsWithBase(Z_STRVAL_PP(xslt), Z_STRVAL_PP(input), &tRes, Z_STRVAL_PP(base));
-       } else {
-               ret = SablotProcessStrings(Z_STRVAL_PP(xslt), Z_STRVAL_PP(input), &tRes);
+           convert_to_string_ex(base);
+           
+           SablotSetBase(SABLOT_BASIC_HANDLE, Z_STRVAL_PP(base));
+       }
+       
+       /**
+        * Need to declare args here (to lazy to actually allocate
+        * it with emalloc() ;)
+        */
+       {
+           char *args[] = {"/_stylesheet", Z_STRVAL_PP(xslt),
+                           "/_xmlinput", Z_STRVAL_PP(input),
+                           "/_output", NULL,
+                           NULL};
+           
+           ret = SablotRunProcessor(SABLOT_BASIC_HANDLE, "arg:/_stylesheet", 
+                                    "arg:/_xmlinput", "arg:/_output", 
+                                    NULL, args);
        }
        
        if (ret) {
-               SABLOTG(last_errno) = ret;
-               if (tRes) {
-                       SablotFree(tRes);
-               }
-               RETURN_FALSE;
+           SABLOTG(last_errno) = ret;
+           RETURN_FALSE;
        }
-
+       
+       SablotGetResultArg(SABLOT_BASIC_HANDLE, "arg:/_output", &tRes);
+       if (ret) {
+           SABLOTG(last_errno) = ret;
+           RETURN_FALSE;
+           
+           if (tRes)
+               SablotFree(tRes);
+       }
+       
        if (tRes) {
                ZVAL_STRING(*result, tRes, 1);
                SablotFree(tRes);
@@ -351,9 +499,6 @@ PHP_FUNCTION(xslt_process)
 }
 /* }}} */
 
-/* }}} */
-
-/* {{{ Begin Advanced Resource API */
 
 /* {{{ proto resource xslt_create(void)
    Create a new XSL processor and return a resource identifier. */
@@ -393,7 +538,7 @@ PHP_FUNCTION(xslt_create)
                RETURN_FALSE;
        }
        
-       ZEND_REGISTER_RESOURCE(return_value, handle, SABLOTG(le_sablot));
+       ZEND_REGISTER_RESOURCE(return_value, handle, le_sablot);
        handle->index = Z_LVAL_P(return_value);
 }
 /* }}} */
@@ -420,7 +565,7 @@ PHP_FUNCTION(xslt_run)
            zend_get_parameters_ex(argc, &xh, &xslt_file, &data_file, &xslt_result, &xslt_params, &xslt_args) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
-       ZEND_FETCH_RESOURCE(handle, php_sablot *, xh, -1, "PHP-Sablotron handle", SABLOTG(le_sablot));
+       ZEND_FETCH_RESOURCE(handle, php_sablot *, xh, -1, "PHP-Sablotron handle", le_sablot);
        multi_convert_to_string_ex(2, xslt_file, data_file);
        
        if (argc == 3) {
@@ -500,7 +645,7 @@ PHP_FUNCTION(xslt_openlog)
            zend_get_parameters_ex(argc, &xh, &logfile, &opt_loglevel) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
-       ZEND_FETCH_RESOURCE(handle, php_sablot *, xh, -1, "PHP-Sablotron", SABLOTG(le_sablot));
+       ZEND_FETCH_RESOURCE(handle, php_sablot *, xh, -1, "PHP-Sablotron", le_sablot);
        convert_to_string_ex(logfile);
        
        if (argc > 2) {
@@ -533,7 +678,7 @@ PHP_FUNCTION(xslt_closelog)
            zend_get_parameters_ex(1, &xh) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
-       ZEND_FETCH_RESOURCE(handle, php_sablot *, xh, -1, "PHP-Sablotron", SABLOTG(le_sablot));
+       ZEND_FETCH_RESOURCE(handle, php_sablot *, xh, -1, "PHP-Sablotron", le_sablot);
        
        ret = SablotSetLog(handle->p, (const char *)NULL, 1);
        
@@ -561,7 +706,7 @@ PHP_FUNCTION(xslt_fetch_result)
            zend_get_parameters_ex(argc, &xh, &result_name) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
-       ZEND_FETCH_RESOURCE(handle, php_sablot *, xh, -1, "PHP-Sablotron Handle", SABLOTG(le_sablot));
+       ZEND_FETCH_RESOURCE(handle, php_sablot *, xh, -1, "PHP-Sablotron Handle", le_sablot);
        
        if (argc > 1) {
                convert_to_string_ex(result_name);
@@ -603,7 +748,7 @@ PHP_FUNCTION(xslt_free)
                WRONG_PARAM_COUNT;
        }
        
-       ZEND_FETCH_RESOURCE(handle, php_sablot *, xh, -1, "PHP-Sablotron Handle", SABLOTG(le_sablot));
+       ZEND_FETCH_RESOURCE(handle, php_sablot *, xh, -1, "PHP-Sablotron Handle", le_sablot);
        zend_list_delete(Z_LVAL_PP(xh));
 }
 /* }}} */
@@ -626,7 +771,7 @@ PHP_FUNCTION(xslt_set_sax_handler)
            zend_get_parameters_ex(2, &xh, &handlers) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
-       ZEND_FETCH_RESOURCE(handle, php_sablot *, xh, -1, "PHP-Sablotron Handle", SABLOTG(le_sablot));
+       ZEND_FETCH_RESOURCE(handle, php_sablot *, xh, -1, "PHP-Sablotron Handle", le_sablot);
        
        handlers_list = HASH_OF(*handlers);
        
@@ -692,7 +837,7 @@ PHP_FUNCTION(xslt_error)
        }
        
        if (argc) {
-               ZEND_FETCH_RESOURCE(handle, php_sablot *, xh, -1, "PHP-Sablotron Handle", SABLOTG(le_sablot));
+               ZEND_FETCH_RESOURCE(handle, php_sablot *, xh, -1, "PHP-Sablotron Handle", le_sablot);
 
                if (handle->errors) {
                        if (array_init(return_value) == FAILURE) {
@@ -735,7 +880,7 @@ PHP_FUNCTION(xslt_errno)
        }
        
        if (argc) {
-               ZEND_FETCH_RESOURCE(handle, php_sablot *, xh, -1, "PHP-Sablotron Handle", SABLOTG(le_sablot));
+               ZEND_FETCH_RESOURCE(handle, php_sablot *, xh, -1, "PHP-Sablotron Handle", le_sablot);
                RETURN_LONG(handle->last_errno);
        } else {
                RETURN_LONG(SABLOTG(last_errno));
@@ -743,13 +888,40 @@ PHP_FUNCTION(xslt_errno)
 }
 /* }}} */
 
+/* {{{ proto void xslt_set_error_handler([int xh, ] string error_handler_name)
+   Set a error handler (either per handle or global) */
+PHP_FUNCTION(xslt_set_error_handler)
+{
+    zval **arg1, **arg2;
+    php_sablot *handle;
+    int argc = ZEND_NUM_ARGS();
+    
+    if (argc > 2 || argc < 1 ||
+        zend_get_parameters_ex(argc, &arg1, &arg2) == FAILURE) {
+        WRONG_PARAM_COUNT;
+    }
+    
+    if (argc > 1) {
+        convert_to_string_ex(arg2);
+        ZEND_FETCH_RESOURCE(handle, php_sablot *, arg1, -1, "PHP-Sablotron Handle", le_sablot);
+        
+        if (!handle->errorHandler)
+            MAKE_STD_ZVAL(handle->errorHandler);
+         
+        *handle->errorHandler = **arg2;
+        zval_copy_ctor(handle->errorHandler);
+    } else {
+        convert_to_string_ex(arg1);
+        
+        if (!SABLOTG(errorHandler))
+            MAKE_STD_ZVAL(SABLOTG(errorHandler));
+        
+        *SABLOTG(errorHandler) = **arg1;
+        zval_copy_ctor(SABLOTG(errorHandler));
+    }
+}
 /* }}} */
 
-/* }}} */
-
-/* {{{ BEGIN HANDLER FUNCTIONS */
-
-/* {{{ Begin SAX Handler functions */
 
 /* {{{ _php_sablot_handler_pair()
    Set the handler functions from a two item array */
@@ -990,68 +1162,160 @@ static MH_ERROR _php_sablot_make_code(void *userData, SablotHandle p, int severi
    Handle Sablotron errors */
 static MH_ERROR _php_sablot_error(void *userData, SablotHandle p, MH_ERROR code, MH_LEVEL level, char **fields)
 {
-       php_sablot *handle = (php_sablot *)userData;
-
-       char *sep;
-
-       int idx, 
-           len;
-
-       if (!fields) {
-               if (code) {
-                       handle->last_errno = (int)code;
-               }
-               return(0);
-       }       
-       
-       if (handle->errors) {
-               handle->errors = handle->errors_start.next;
-               while (handle->errors) {
-                       S_FREE(handle->errors->key);
-                       S_FREE(handle->errors->value);
-                       handle->errors = handle->errors->next;
-               }
-               S_FREE(handle->errors);
-       }
-
-       handle->errors_start.next = NULL;
-       handle->errors = &(handle->errors_start);
-
-       while (fields && *fields) {
-               handle->errors->next = (php_sablot_error *)emalloc(sizeof(php_sablot_error));
-               handle->errors = handle->errors->next;
-       
-               sep = strchr(fields[0], ':');
-               
-               idx = sep - fields[0];
-               len = strlen(fields[0]);
-               
-               handle->errors->key   = emalloc(idx+1);
-               handle->errors->value = emalloc((len - idx) + 1);
-               memcpy(handle->errors->key, fields[0], idx);
-               memcpy(handle->errors->value, fields[0] + idx + 1, len - idx - 1);
-               
-               handle->errors->next = NULL;
-               fields++;
-       }
-       handle->last_errno = (int)code;
-       
-       switch (code) {
-               case MH_LEVEL_CRITICAL:
-                       SEND_ERROR_INFO("Fatal Error", E_CORE);
-                       return (0);
-               case MH_LEVEL_ERROR:
-                       SEND_ERROR_INFO("Serious Error", E_ERROR);
-                       return (0);
-               case MH_LEVEL_WARN:
-                       SEND_ERROR_INFO("Warning", E_WARNING);
-                       break;
-       }
+    zval **argv = NULL,
+          *errorHandler;
+    
+    php_sablot_error *errors,
+                      errors_start;
+    php_sablot       *handle = NULL;
+    
+    char *sep = NULL;
+    
+    int isAdvanced = 0,
+        argc = 0,
+        idx,
+        len;
+    
+    if (userData == NULL) {
+        SABLOT_FREE_ERROR_HANDLE(SABLOTG_HANDLE);
+        
+        SABLOTG(errors_start).next = NULL;
+        SABLOTG(errors) = &SABLOTG(errors_start);
+        
+        errors        = SABLOTG(errors);
+        errorHandler  = SABLOTG(errorHandler);
+    } else {
+        handle = (php_sablot *)userData;
+        
+        SABLOT_FREE_ERROR_HANDLE(*handle);
+        
+        handle->errors_start.next = NULL;
+        handle->errors = &errors_start;
+        
+        errors        = handle->errors;
+        errorHandler  = handle->errorHandler;
+        
+        isAdvanced   = 1;
+    }
+
+
+    while (fields && *fields) {
+        errors->next = (php_sablot_error *)emalloc(sizeof(php_sablot_error));
+        errors = errors->next;
+        
+        sep = strchr(fields[0], ':');
+        
+        idx = sep - fields[0];
+        len = strlen(fields[0]);
+        
+        errors->key   = emalloc(idx+1);
+        errors->value = emalloc((len - idx) + 1);
+        
+        memcpy(errors->key, fields[0], idx);
+        memcpy(errors->value, fields[0] + idx + 1, len - idx - 1);
+        errors->value[len - idx - 1] = '\0';
+        
+        errors->next = NULL;
+        fields++;
+    }
+    
+    if (isAdvanced)
+        handle->last_errno = (int)code;
+    else
+        SABLOTG(last_errno) = (int)code;
+    
+    if (errorHandler) {
+        zval *retval;
+        int i;
+        ELS_FETCH();
+        
+        MAKE_STD_ZVAL(retval);
+        
+        argc = isAdvanced ? 4 : 3;
+        argv = emalloc(argc * sizeof(zval *));
+        
+        MAKE_STD_ZVAL(argv[0]);
+        MAKE_STD_ZVAL(argv[1]);
+        MAKE_STD_ZVAL(argv[2]);
+        
+        if (isAdvanced) {
+            MAKE_STD_ZVAL(argv[3]);
+
+            ZVAL_RESOURCE(argv[0], handle->index);
+            ZVAL_LONG(argv[1], code);
+            ZVAL_LONG(argv[2], level);
+            
+            array_init(argv[3]);
+            errors = handle->errors_start.next;
+            while (errors->next) {
+                add_assoc_string(argv[3], errors->key, errors->value, 1);
+                errors = errors->next;
+            }
+        } else {
+            ZVAL_LONG(argv[0], code);
+            ZVAL_LONG(argv[1], level);
+            
+            array_init(argv[2]);
+            errors = SABLOTG(errors_start).next;
+            while (errors) {
+                add_assoc_string(argv[2], errors->key, errors->value, 1);
+                errors = errors->next;
+            }
+        }
+
+        if (call_user_function(EG(function_table), NULL, errorHandler, retval, argc, argv) == FAILURE) {
+            php_error(E_WARNING, "Sorry, couldn't call %s error handler", Z_STRVAL_P(errorHandler));
+        }
+
+        zval_dtor(retval);
+        efree(retval);
+    } else {
+        _php_sablot_standard_error(errors, isAdvanced ? handle->errors_start : SABLOTG(errors_start), code, level);
+    }
 
        return(0);
+
 }
 /* }}} */
 
+static void _php_sablot_standard_error(php_sablot_error *errors, php_sablot_error errors_start, int code, int level)
+{
+    int len = 0,
+        pos = 0;
+    char *errstr = NULL;
+    SABLOTLS_FETCH();
+    
+    errors = errors_start.next;
+    
+    while (errors) {
+        len = pos + strlen(errors->key) + sizeof(": ") + strlen(errors->value) + sizeof("\n");
+        
+        /** 
+         * Could be a problem, I just hate looping through strings
+         * more than I have to ;-)
+         */
+        if (pos)
+            errstr = erealloc(errstr, len);
+        else
+            errstr = emalloc(len+1);
+        
+        sprintf(errstr + pos, "%s: %s\n", errors->key, errors->value);
+        
+        pos = len;
+        errors = errors->next;
+    }
+    
+    switch (level) {
+        case MH_LEVEL_CRITICAL:
+        case MH_LEVEL_ERROR:  
+            php_error(E_ERROR, errstr);
+            break;
+        case MH_LEVEL_WARN:
+            php_error(E_WARNING, errstr);
+            break;
+    }
+}
+
 /* }}} */
 
 /* {{{ List Handling functions */
@@ -1077,16 +1341,7 @@ static void _php_sablot_free_processor(zend_rsrc_list_entry *rsrc)
        FUNCH_FREE(handle->charactersHandler);
        FUNCH_FREE(handle->endDocHandler);
 
-       if (handle->errors) {
-               handle->errors = handle->errors_start.next;
-               while (handle->errors) {
-                       S_FREE(handle->errors->key);
-                       S_FREE(handle->errors->value);
-                       handle->errors = handle->errors->next;
-               }
-               S_FREE(handle->errors);
-       }
-       S_FREE(handle);
+    SABLOT_FREE_ERROR_HANDLE(*handle);
 }
 /* }}} */