1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Thies C. Arntzen <thies@thieso.net> |
14 +----------------------------------------------------------------------+
15 */
16
17 /* {{{ includes */
18 #include "php.h"
19 #include "php_assert.h"
20 #include "php_ini.h"
21 #include "zend_exceptions.h"
22 /* }}} */
23
24 ZEND_BEGIN_MODULE_GLOBALS(assert)
25 zval callback;
26 char *cb;
27 bool active;
28 bool bail;
29 bool warning;
30 bool exception;
31 ZEND_END_MODULE_GLOBALS(assert)
32
33 ZEND_DECLARE_MODULE_GLOBALS(assert)
34
35 #define ASSERTG(v) ZEND_MODULE_GLOBALS_ACCESSOR(assert, v)
36
37 #define SAFE_STRING(s) ((s)?(s):"")
38
39 enum {
40 ASSERT_ACTIVE=1,
41 ASSERT_CALLBACK,
42 ASSERT_BAIL,
43 ASSERT_WARNING,
44 ASSERT_EXCEPTION
45 };
46
47 PHPAPI zend_class_entry *assertion_error_ce;
48
PHP_INI_MH(OnChangeCallback)49 static PHP_INI_MH(OnChangeCallback) /* {{{ */
50 {
51 if (EG(current_execute_data)) {
52 if (Z_TYPE(ASSERTG(callback)) != IS_UNDEF) {
53 zval_ptr_dtor(&ASSERTG(callback));
54 ZVAL_UNDEF(&ASSERTG(callback));
55 }
56 if (new_value && (Z_TYPE(ASSERTG(callback)) != IS_UNDEF || ZSTR_LEN(new_value))) {
57 ZVAL_STR_COPY(&ASSERTG(callback), new_value);
58 }
59 } else {
60 if (ASSERTG(cb)) {
61 pefree(ASSERTG(cb), 1);
62 }
63 if (new_value && ZSTR_LEN(new_value)) {
64 ASSERTG(cb) = pemalloc(ZSTR_LEN(new_value) + 1, 1);
65 memcpy(ASSERTG(cb), ZSTR_VAL(new_value), ZSTR_LEN(new_value));
66 ASSERTG(cb)[ZSTR_LEN(new_value)] = '\0';
67 } else {
68 ASSERTG(cb) = NULL;
69 }
70 }
71 return SUCCESS;
72 }
73 /* }}} */
74
75 PHP_INI_BEGIN()
76 STD_PHP_INI_BOOLEAN("assert.active", "1", PHP_INI_ALL, OnUpdateBool, active, zend_assert_globals, assert_globals)
77 STD_PHP_INI_BOOLEAN("assert.bail", "0", PHP_INI_ALL, OnUpdateBool, bail, zend_assert_globals, assert_globals)
78 STD_PHP_INI_BOOLEAN("assert.warning", "1", PHP_INI_ALL, OnUpdateBool, warning, zend_assert_globals, assert_globals)
79 PHP_INI_ENTRY("assert.callback", NULL, PHP_INI_ALL, OnChangeCallback)
80 STD_PHP_INI_BOOLEAN("assert.exception", "1", PHP_INI_ALL, OnUpdateBool, exception, zend_assert_globals, assert_globals)
PHP_INI_END()81 PHP_INI_END()
82
83 static void php_assert_init_globals(zend_assert_globals *assert_globals_p) /* {{{ */
84 {
85 ZVAL_UNDEF(&assert_globals_p->callback);
86 assert_globals_p->cb = NULL;
87 }
88 /* }}} */
89
PHP_MINIT_FUNCTION(assert)90 PHP_MINIT_FUNCTION(assert) /* {{{ */
91 {
92 ZEND_INIT_MODULE_GLOBALS(assert, php_assert_init_globals, NULL);
93
94 REGISTER_INI_ENTRIES();
95
96 REGISTER_LONG_CONSTANT("ASSERT_ACTIVE", ASSERT_ACTIVE, CONST_CS|CONST_PERSISTENT);
97 REGISTER_LONG_CONSTANT("ASSERT_CALLBACK", ASSERT_CALLBACK, CONST_CS|CONST_PERSISTENT);
98 REGISTER_LONG_CONSTANT("ASSERT_BAIL", ASSERT_BAIL, CONST_CS|CONST_PERSISTENT);
99 REGISTER_LONG_CONSTANT("ASSERT_WARNING", ASSERT_WARNING, CONST_CS|CONST_PERSISTENT);
100 REGISTER_LONG_CONSTANT("ASSERT_EXCEPTION", ASSERT_EXCEPTION, CONST_CS|CONST_PERSISTENT);
101
102 return SUCCESS;
103 }
104 /* }}} */
105
PHP_MSHUTDOWN_FUNCTION(assert)106 PHP_MSHUTDOWN_FUNCTION(assert) /* {{{ */
107 {
108 if (ASSERTG(cb)) {
109 pefree(ASSERTG(cb), 1);
110 ASSERTG(cb) = NULL;
111 }
112 return SUCCESS;
113 }
114 /* }}} */
115
PHP_RSHUTDOWN_FUNCTION(assert)116 PHP_RSHUTDOWN_FUNCTION(assert) /* {{{ */
117 {
118 if (Z_TYPE(ASSERTG(callback)) != IS_UNDEF) {
119 zval_ptr_dtor(&ASSERTG(callback));
120 ZVAL_UNDEF(&ASSERTG(callback));
121 }
122
123 return SUCCESS;
124 }
125 /* }}} */
126
PHP_MINFO_FUNCTION(assert)127 PHP_MINFO_FUNCTION(assert) /* {{{ */
128 {
129 DISPLAY_INI_ENTRIES();
130 }
131 /* }}} */
132
133 /* {{{ Checks if assertion is false */
PHP_FUNCTION(assert)134 PHP_FUNCTION(assert)
135 {
136 zval *assertion;
137 zend_string *description_str = NULL;
138 zend_object *description_obj = NULL;
139
140 if (!ASSERTG(active)) {
141 RETURN_TRUE;
142 }
143
144 ZEND_PARSE_PARAMETERS_START(1, 2)
145 Z_PARAM_ZVAL(assertion)
146 Z_PARAM_OPTIONAL
147 Z_PARAM_OBJ_OF_CLASS_OR_STR_OR_NULL(description_obj, zend_ce_throwable, description_str)
148 ZEND_PARSE_PARAMETERS_END();
149
150 if (zend_is_true(assertion)) {
151 RETURN_TRUE;
152 }
153
154 if (description_obj) {
155 GC_ADDREF(description_obj);
156 zend_throw_exception_internal(description_obj);
157 RETURN_THROWS();
158 }
159
160 if (Z_TYPE(ASSERTG(callback)) == IS_UNDEF && ASSERTG(cb)) {
161 ZVAL_STRING(&ASSERTG(callback), ASSERTG(cb));
162 }
163
164 if (Z_TYPE(ASSERTG(callback)) != IS_UNDEF) {
165 zval args[4];
166 zval retval;
167 uint32_t lineno = zend_get_executed_lineno();
168 const char *filename = zend_get_executed_filename();
169
170 ZVAL_STRING(&args[0], SAFE_STRING(filename));
171 ZVAL_LONG(&args[1], lineno);
172 ZVAL_NULL(&args[2]);
173
174 ZVAL_FALSE(&retval);
175
176 if (description_str) {
177 ZVAL_STR(&args[3], description_str);
178 call_user_function(NULL, NULL, &ASSERTG(callback), &retval, 4, args);
179 } else {
180 call_user_function(NULL, NULL, &ASSERTG(callback), &retval, 3, args);
181 }
182
183 zval_ptr_dtor(&args[0]);
184 zval_ptr_dtor(&retval);
185 }
186
187 if (ASSERTG(exception)) {
188 zend_throw_exception(assertion_error_ce, description_str ? ZSTR_VAL(description_str) : NULL, E_ERROR);
189 if (ASSERTG(bail)) {
190 /* When bail is turned on, the exception will not be caught. */
191 zend_exception_error(EG(exception), E_ERROR);
192 }
193 } else if (ASSERTG(warning)) {
194 php_error_docref(NULL, E_WARNING, "%s failed", description_str ? ZSTR_VAL(description_str) : "Assertion failed");
195 }
196
197 if (ASSERTG(bail)) {
198 if (EG(exception)) {
199 /* The callback might have thrown. Use E_WARNING to print the
200 * exception so we can avoid bailout and use unwind_exit. */
201 zend_exception_error(EG(exception), E_WARNING);
202 }
203 zend_throw_unwind_exit();
204 RETURN_THROWS();
205 } else {
206 RETURN_FALSE;
207 }
208 }
209 /* }}} */
210
211 /* {{{ Set/get the various assert flags */
PHP_FUNCTION(assert_options)212 PHP_FUNCTION(assert_options)
213 {
214 zval *value = NULL;
215 zend_long what;
216 bool oldint;
217 int ac = ZEND_NUM_ARGS();
218 zend_string *key;
219
220 ZEND_PARSE_PARAMETERS_START(1, 2)
221 Z_PARAM_LONG(what)
222 Z_PARAM_OPTIONAL
223 Z_PARAM_ZVAL(value)
224 ZEND_PARSE_PARAMETERS_END();
225
226 switch (what) {
227 case ASSERT_ACTIVE:
228 oldint = ASSERTG(active);
229 if (ac == 2) {
230 zend_string *value_str = zval_try_get_string(value);
231 if (UNEXPECTED(!value_str)) {
232 RETURN_THROWS();
233 }
234
235 key = zend_string_init("assert.active", sizeof("assert.active")-1, 0);
236 zend_alter_ini_entry_ex(key, value_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0);
237 zend_string_release_ex(key, 0);
238 zend_string_release_ex(value_str, 0);
239 }
240 RETURN_LONG(oldint);
241 break;
242
243 case ASSERT_BAIL:
244 oldint = ASSERTG(bail);
245 if (ac == 2) {
246 zend_string *value_str = zval_try_get_string(value);
247 if (UNEXPECTED(!value_str)) {
248 RETURN_THROWS();
249 }
250
251 key = zend_string_init("assert.bail", sizeof("assert.bail")-1, 0);
252 zend_alter_ini_entry_ex(key, value_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0);
253 zend_string_release_ex(key, 0);
254 zend_string_release_ex(value_str, 0);
255 }
256 RETURN_LONG(oldint);
257 break;
258
259 case ASSERT_WARNING:
260 oldint = ASSERTG(warning);
261 if (ac == 2) {
262 zend_string *value_str = zval_try_get_string(value);
263 if (UNEXPECTED(!value_str)) {
264 RETURN_THROWS();
265 }
266
267 key = zend_string_init("assert.warning", sizeof("assert.warning")-1, 0);
268 zend_alter_ini_entry_ex(key, value_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0);
269 zend_string_release_ex(key, 0);
270 zend_string_release_ex(value_str, 0);
271 }
272 RETURN_LONG(oldint);
273 break;
274
275 case ASSERT_CALLBACK:
276 if (Z_TYPE(ASSERTG(callback)) != IS_UNDEF) {
277 ZVAL_COPY(return_value, &ASSERTG(callback));
278 } else if (ASSERTG(cb)) {
279 RETVAL_STRING(ASSERTG(cb));
280 } else {
281 RETVAL_NULL();
282 }
283
284 if (ac == 2) {
285 zval_ptr_dtor(&ASSERTG(callback));
286 if (Z_TYPE_P(value) == IS_NULL) {
287 ZVAL_UNDEF(&ASSERTG(callback));
288 } else {
289 ZVAL_COPY(&ASSERTG(callback), value);
290 }
291 }
292 return;
293
294 case ASSERT_EXCEPTION:
295 oldint = ASSERTG(exception);
296 if (ac == 2) {
297 zend_string *val = zval_try_get_string(value);
298 if (UNEXPECTED(!val)) {
299 RETURN_THROWS();
300 }
301
302 key = zend_string_init("assert.exception", sizeof("assert.exception")-1, 0);
303 zend_alter_ini_entry_ex(key, val, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0);
304 zend_string_release_ex(val, 0);
305 zend_string_release_ex(key, 0);
306 }
307 RETURN_LONG(oldint);
308 break;
309
310 default:
311 zend_argument_value_error(1, "must be an ASSERT_* constant");
312 RETURN_THROWS();
313 }
314 }
315 /* }}} */
316