1The intl extension has particular conventions regarding error reporting. 2These conventions are enumerated in this document. 3 4:: The last error is always stored globally. 5 6The global error code can be obtained in userland with intl_get_error_code(). 7This is a U_* error code defined by ICU, but it does not have necessarily to be 8returned obtained after a call to an ICU function. That is to say, the internal 9PHP wrapper functions can set these error codes when appropriate. For instance, 10in response to bad arguments (e.g. zend_parse_parameters() failure), the PHP 11wrapper function should set the global error code to U_ILLEGAL_ARGUMENT_ERROR). 12 13The error code (an integer) can be converter to the corresponding enum name 14string in userland with intl_error_name(). 15 16The associated message can be obtained with intl_get_error_message(). This is a 17message set by the PHP wrapping code, not by ICU. The message should include the 18name of the function that failed in order to make debugging easier (though if 19you activate warnings with intl.error_level or exceptions with 20intl.use_exceptions you get more fine-grained information about where the 21error occurred). 22 23The internal PHP code can set the global last error with: 24void intl_error_set_code(intl_error* err, UErrorCode err_code); 25void intl_error_set_custom_msg(intl_error* err, char* msg, int copyMsg); 26void intl_error_set(intl_error* err, UErrorCode code, char* msg, int copyMsg); 27 28and by passing NULL as the first parameter. The last function is a combination 29of the first two. If the message is not a static buffer, copyMsg should be 1. 30This makes the message string be copied and freed when no longer needed. There's 31no way to pass ownership of the string without it being copied. 32 33 34:: The last is ALSO stored in the object whose method call triggered the error, 35 unless the error is due to bad arguments, in which case only the global error 36 should be set 37 38Objects store an intl_error structed in their private data. For instance: 39typedef struct { 40 zend_object zo; 41 intl_error err; 42 Calendar* ucal; 43} Calendar_object; 44 45The global error and the object error can be SIMULTANEOUSLY set with these 46functions: 47void intl_errors_set_custom_msg(intl_error* err, char* msg, int copyMsg); 48void intl_errors_set_code(intl_error* err, UErrorCode err_code); 49void intl_errors_set(intl_error* err, UErrorCode code, char* msg, int copyMsg); 50 51by passing a pointer to the object's intl_error structed as the first parameter. 52Node the extra 's' in the functions' names ('errors', not 'error'). 53 54Static methods should only set the global error. 55 56 57:: Intl classes that can be instantiated should provide ::getErrorCode() and 58 getErrorMessage() methods 59 60These methods are used to retrieve the error codes stored in the object's 61private intl_error structured and mirror the global intl_get_error_code() and 62intl_get_error_message(). 63 64 65:: Intl methods and functions should return FALSE on error (even argument 66 parsing errors), not NULL. Constructors and factory methods are the 67 exception; these should return NULL, not FALSE. 68 69Not that constructors in Intl generally (always?) don't throws exceptions. 70They instead destroy the object to that the result of new IntlClass() can 71be NULL. This may be surprising. 72 73 74:: Intl functions and methods should reset the global error before doing 75 anything else (even parse the arguments); instance methods should also reset 76 the object's private error 77 78Errors should be lost after a function call. This is different from the way 79ICU operates, where functions return immediately if an error is set. 80 81Error resetting can be done with: 82void intl_error_reset(NULL); /* reset global error */ 83void intl_errors_reset(intl_error* err ); /* reset global and object error */ 84 85In practice, intl_errors_reset() is not used because most classes have also 86plain functions mapped to the same internal functions as their instance methods. 87Fetching of the object is done with zend_parse_method_parameters() instead of 88directly using getThis(). Therefore, no reference to object is obtained until 89the arguments are fully parsed. Without a reference to the object, there's no 90way to reset the object's internal error code. Instead, resetting of the 91object's internal error code is done upon fetching the object from its zval. 92 93Example: 94U_CFUNC PHP_FUNCTION(breakiter_set_text) 95{ 96 /* ... variable declations ... */ 97 BREAKITER_METHOD_INIT_VARS; /* macro also resets global error */ 98 object = getThis(); 99 100 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", 101 &text, &text_len) == FAILURE) { 102 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, 103 "breakiter_set_text: bad arguments", 0); 104 RETURN_FALSE; 105 } 106 107 /* ... */ 108 109 BREAKITER_METHOD_FETCH_OBJECT; /* macro also resets object's error */ 110 111 /* ... */ 112} 113 114Implementations of ::getErrorCode() and ::getErrorMessage() should not reset the 115object's error code. 116