xref: /PHP-8.1/ext/standard/assert.c (revision 01b3fc03)
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 		zend_throw_unwind_exit();
199 		RETURN_THROWS();
200 	} else {
201 		RETURN_FALSE;
202 	}
203 }
204 /* }}} */
205 
206 /* {{{ Set/get the various assert flags */
PHP_FUNCTION(assert_options)207 PHP_FUNCTION(assert_options)
208 {
209 	zval *value = NULL;
210 	zend_long what;
211 	bool oldint;
212 	int ac = ZEND_NUM_ARGS();
213 	zend_string *key;
214 
215 	ZEND_PARSE_PARAMETERS_START(1, 2)
216 		Z_PARAM_LONG(what)
217 		Z_PARAM_OPTIONAL
218 		Z_PARAM_ZVAL(value)
219 	ZEND_PARSE_PARAMETERS_END();
220 
221 	switch (what) {
222 	case ASSERT_ACTIVE:
223 		oldint = ASSERTG(active);
224 		if (ac == 2) {
225 			zend_string *value_str = zval_try_get_string(value);
226 			if (UNEXPECTED(!value_str)) {
227 				RETURN_THROWS();
228 			}
229 
230 			key = zend_string_init("assert.active", sizeof("assert.active")-1, 0);
231 			zend_alter_ini_entry_ex(key, value_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0);
232 			zend_string_release_ex(key, 0);
233 			zend_string_release_ex(value_str, 0);
234 		}
235 		RETURN_LONG(oldint);
236 		break;
237 
238 	case ASSERT_BAIL:
239 		oldint = ASSERTG(bail);
240 		if (ac == 2) {
241 			zend_string *value_str = zval_try_get_string(value);
242 			if (UNEXPECTED(!value_str)) {
243 				RETURN_THROWS();
244 			}
245 
246 			key = zend_string_init("assert.bail", sizeof("assert.bail")-1, 0);
247 			zend_alter_ini_entry_ex(key, value_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0);
248 			zend_string_release_ex(key, 0);
249 			zend_string_release_ex(value_str, 0);
250 		}
251 		RETURN_LONG(oldint);
252 		break;
253 
254 	case ASSERT_WARNING:
255 		oldint = ASSERTG(warning);
256 		if (ac == 2) {
257 			zend_string *value_str = zval_try_get_string(value);
258 			if (UNEXPECTED(!value_str)) {
259 				RETURN_THROWS();
260 			}
261 
262 			key = zend_string_init("assert.warning", sizeof("assert.warning")-1, 0);
263 			zend_alter_ini_entry_ex(key, value_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0);
264 			zend_string_release_ex(key, 0);
265 			zend_string_release_ex(value_str, 0);
266 		}
267 		RETURN_LONG(oldint);
268 		break;
269 
270 	case ASSERT_CALLBACK:
271 		if (Z_TYPE(ASSERTG(callback)) != IS_UNDEF) {
272 			ZVAL_COPY(return_value, &ASSERTG(callback));
273 		} else if (ASSERTG(cb)) {
274 			RETVAL_STRING(ASSERTG(cb));
275 		} else {
276 			RETVAL_NULL();
277 		}
278 
279 		if (ac == 2) {
280 			zval_ptr_dtor(&ASSERTG(callback));
281 			if (Z_TYPE_P(value) == IS_NULL) {
282 				ZVAL_UNDEF(&ASSERTG(callback));
283 			} else {
284 				ZVAL_COPY(&ASSERTG(callback), value);
285 			}
286 		}
287 		return;
288 
289 	case ASSERT_EXCEPTION:
290 		oldint = ASSERTG(exception);
291 		if (ac == 2) {
292 			zend_string *val = zval_try_get_string(value);
293 			if (UNEXPECTED(!val)) {
294 				RETURN_THROWS();
295 			}
296 
297 			key = zend_string_init("assert.exception", sizeof("assert.exception")-1, 0);
298 			zend_alter_ini_entry_ex(key, val, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0);
299 			zend_string_release_ex(val, 0);
300 			zend_string_release_ex(key, 0);
301 		}
302 		RETURN_LONG(oldint);
303 		break;
304 
305 	default:
306 		zend_argument_value_error(1, "must be an ASSERT_* constant");
307 		RETURN_THROWS();
308 	}
309 }
310 /* }}} */
311