Skip to content

Commit 6abc6ba

Browse files
committed
[ci skip] Migrate intl error conventions docs to Markdown
1 parent 1c53afb commit 6abc6ba

File tree

2 files changed

+129
-115
lines changed

2 files changed

+129
-115
lines changed

ext/intl/ERROR.CONVENTIONS

-115
This file was deleted.

ext/intl/ERROR_CONVENTIONS.md

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

0 commit comments

Comments
 (0)