xref: /php-src/ext/standard/assert.c (revision 433ed097)
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 PHPAPI zend_class_entry *assertion_error_ce;
40 
PHP_INI_MH(OnChangeCallback)41 static PHP_INI_MH(OnChangeCallback) /* {{{ */
42 {
43 	if (EG(current_execute_data)) {
44 		if (Z_TYPE(ASSERTG(callback)) != IS_UNDEF) {
45 			zval_ptr_dtor(&ASSERTG(callback));
46 			ZVAL_UNDEF(&ASSERTG(callback));
47 		}
48 		if (new_value && (Z_TYPE(ASSERTG(callback)) != IS_UNDEF || ZSTR_LEN(new_value))) {
49 			ZVAL_STR_COPY(&ASSERTG(callback), new_value);
50 		}
51 	} else {
52 		if (ASSERTG(cb)) {
53 			pefree(ASSERTG(cb), 1);
54 		}
55 		if (new_value && ZSTR_LEN(new_value)) {
56 			ASSERTG(cb) = pemalloc(ZSTR_LEN(new_value) + 1, 1);
57 			memcpy(ASSERTG(cb), ZSTR_VAL(new_value), ZSTR_LEN(new_value));
58 			ASSERTG(cb)[ZSTR_LEN(new_value)] = '\0';
59 		} else {
60 			ASSERTG(cb) = NULL;
61 		}
62 	}
63 	return SUCCESS;
64 }
65 /* }}} */
66 
67 PHP_INI_BEGIN()
68 	 STD_PHP_INI_BOOLEAN("assert.active",		"1",	PHP_INI_ALL,	OnUpdateBool,		active,	 			zend_assert_globals,		assert_globals)
69 	 STD_PHP_INI_BOOLEAN("assert.bail",		"0",	PHP_INI_ALL,	OnUpdateBool,		bail,	 			zend_assert_globals,		assert_globals)
70 	 STD_PHP_INI_BOOLEAN("assert.warning",	"1",	PHP_INI_ALL,	OnUpdateBool,		warning, 			zend_assert_globals,		assert_globals)
71 	 PHP_INI_ENTRY("assert.callback",		NULL,	PHP_INI_ALL,	OnChangeCallback)
72 	 STD_PHP_INI_BOOLEAN("assert.exception",	"1",	PHP_INI_ALL,	OnUpdateBool,		exception, 			zend_assert_globals,		assert_globals)
PHP_INI_END()73 PHP_INI_END()
74 
75 static void php_assert_init_globals(zend_assert_globals *assert_globals_p) /* {{{ */
76 {
77 	ZVAL_UNDEF(&assert_globals_p->callback);
78 	assert_globals_p->cb = NULL;
79 }
80 /* }}} */
81 
PHP_MINIT_FUNCTION(assert)82 PHP_MINIT_FUNCTION(assert) /* {{{ */
83 {
84 	ZEND_INIT_MODULE_GLOBALS(assert, php_assert_init_globals, NULL);
85 
86 	REGISTER_INI_ENTRIES();
87 
88 	return SUCCESS;
89 }
90 /* }}} */
91 
PHP_MSHUTDOWN_FUNCTION(assert)92 PHP_MSHUTDOWN_FUNCTION(assert) /* {{{ */
93 {
94 	if (ASSERTG(cb)) {
95 		pefree(ASSERTG(cb), 1);
96 		ASSERTG(cb) = NULL;
97 	}
98 	return SUCCESS;
99 }
100 /* }}} */
101 
PHP_RSHUTDOWN_FUNCTION(assert)102 PHP_RSHUTDOWN_FUNCTION(assert) /* {{{ */
103 {
104 	if (Z_TYPE(ASSERTG(callback)) != IS_UNDEF) {
105 		zval_ptr_dtor(&ASSERTG(callback));
106 		ZVAL_UNDEF(&ASSERTG(callback));
107 	}
108 
109 	return SUCCESS;
110 }
111 /* }}} */
112 
PHP_MINFO_FUNCTION(assert)113 PHP_MINFO_FUNCTION(assert) /* {{{ */
114 {
115 	DISPLAY_INI_ENTRIES();
116 }
117 /* }}} */
118 
119 /* {{{ Checks if assertion is false */
PHP_FUNCTION(assert)120 PHP_FUNCTION(assert)
121 {
122 	zval *assertion;
123 	zend_string *description_str = NULL;
124 	zend_object *description_obj = NULL;
125 
126 	if (!ASSERTG(active)) {
127 		RETURN_TRUE;
128 	}
129 
130 	ZEND_PARSE_PARAMETERS_START(1, 2)
131 		Z_PARAM_ZVAL(assertion)
132 		Z_PARAM_OPTIONAL
133 		Z_PARAM_OBJ_OF_CLASS_OR_STR_OR_NULL(description_obj, zend_ce_throwable, description_str)
134 	ZEND_PARSE_PARAMETERS_END();
135 
136 	if (zend_is_true(assertion)) {
137 		RETURN_TRUE;
138 	}
139 
140 	if (description_obj) {
141 		GC_ADDREF(description_obj);
142 		zend_throw_exception_internal(description_obj);
143 		RETURN_THROWS();
144 	}
145 
146 	if (Z_TYPE(ASSERTG(callback)) == IS_UNDEF && ASSERTG(cb)) {
147 		ZVAL_STRING(&ASSERTG(callback), ASSERTG(cb));
148 	}
149 
150 	if (Z_TYPE(ASSERTG(callback)) != IS_UNDEF) {
151 		zval args[4];
152 		zval retval;
153 		uint32_t lineno = zend_get_executed_lineno();
154 		const char *filename = zend_get_executed_filename();
155 
156 		ZVAL_STRING(&args[0], SAFE_STRING(filename));
157 		ZVAL_LONG(&args[1], lineno);
158 		ZVAL_NULL(&args[2]);
159 
160 		ZVAL_FALSE(&retval);
161 
162 		if (description_str) {
163 			ZVAL_STR(&args[3], description_str);
164 			call_user_function(NULL, NULL, &ASSERTG(callback), &retval, 4, args);
165 		} else {
166 			call_user_function(NULL, NULL, &ASSERTG(callback), &retval, 3, args);
167 		}
168 
169 		zval_ptr_dtor(&args[0]);
170 		zval_ptr_dtor(&retval);
171 	}
172 
173 	if (ASSERTG(exception)) {
174 		zend_throw_exception(assertion_error_ce, description_str ? ZSTR_VAL(description_str) : NULL, E_ERROR);
175 		if (ASSERTG(bail)) {
176 			/* When bail is turned on, the exception will not be caught. */
177 			zend_exception_error(EG(exception), E_ERROR);
178 		}
179 	} else if (ASSERTG(warning)) {
180 		php_error_docref(NULL, E_WARNING, "%s failed", description_str ? ZSTR_VAL(description_str) : "Assertion failed");
181 	}
182 
183 	if (ASSERTG(bail)) {
184 		zend_throw_unwind_exit();
185 		RETURN_THROWS();
186 	} else {
187 		RETURN_FALSE;
188 	}
189 }
190 /* }}} */
191 
192 /* {{{ Set/get the various assert flags */
PHP_FUNCTION(assert_options)193 PHP_FUNCTION(assert_options)
194 {
195 	zval *value = NULL;
196 	zend_long what;
197 	bool oldint;
198 	int ac = ZEND_NUM_ARGS();
199 	zend_string *key;
200 
201 	ZEND_PARSE_PARAMETERS_START(1, 2)
202 		Z_PARAM_LONG(what)
203 		Z_PARAM_OPTIONAL
204 		Z_PARAM_ZVAL(value)
205 	ZEND_PARSE_PARAMETERS_END();
206 
207 	switch (what) {
208 	case PHP_ASSERT_ACTIVE:
209 		oldint = ASSERTG(active);
210 		if (ac == 2) {
211 			zend_string *value_str = zval_try_get_string(value);
212 			if (UNEXPECTED(!value_str)) {
213 				RETURN_THROWS();
214 			}
215 
216 			key = zend_string_init("assert.active", sizeof("assert.active")-1, 0);
217 			zend_alter_ini_entry_ex(key, value_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0);
218 			zend_string_release_ex(key, 0);
219 			zend_string_release_ex(value_str, 0);
220 		}
221 		RETURN_LONG(oldint);
222 		break;
223 
224 	case PHP_ASSERT_BAIL:
225 		oldint = ASSERTG(bail);
226 		if (ac == 2) {
227 			zend_string *value_str = zval_try_get_string(value);
228 			if (UNEXPECTED(!value_str)) {
229 				RETURN_THROWS();
230 			}
231 
232 			key = zend_string_init("assert.bail", sizeof("assert.bail")-1, 0);
233 			zend_alter_ini_entry_ex(key, value_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0);
234 			zend_string_release_ex(key, 0);
235 			zend_string_release_ex(value_str, 0);
236 		}
237 		RETURN_LONG(oldint);
238 		break;
239 
240 	case PHP_ASSERT_WARNING:
241 		oldint = ASSERTG(warning);
242 		if (ac == 2) {
243 			zend_string *value_str = zval_try_get_string(value);
244 			if (UNEXPECTED(!value_str)) {
245 				RETURN_THROWS();
246 			}
247 
248 			key = zend_string_init("assert.warning", sizeof("assert.warning")-1, 0);
249 			zend_alter_ini_entry_ex(key, value_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0);
250 			zend_string_release_ex(key, 0);
251 			zend_string_release_ex(value_str, 0);
252 		}
253 		RETURN_LONG(oldint);
254 		break;
255 
256 	case PHP_ASSERT_CALLBACK:
257 		if (Z_TYPE(ASSERTG(callback)) != IS_UNDEF) {
258 			ZVAL_COPY(return_value, &ASSERTG(callback));
259 		} else if (ASSERTG(cb)) {
260 			RETVAL_STRING(ASSERTG(cb));
261 		} else {
262 			RETVAL_NULL();
263 		}
264 
265 		if (ac == 2) {
266 			zval_ptr_dtor(&ASSERTG(callback));
267 			if (Z_TYPE_P(value) == IS_NULL) {
268 				ZVAL_UNDEF(&ASSERTG(callback));
269 			} else {
270 				ZVAL_COPY(&ASSERTG(callback), value);
271 			}
272 		}
273 		return;
274 
275 	case PHP_ASSERT_EXCEPTION:
276 		oldint = ASSERTG(exception);
277 		if (ac == 2) {
278 			zend_string *val = zval_try_get_string(value);
279 			if (UNEXPECTED(!val)) {
280 				RETURN_THROWS();
281 			}
282 
283 			key = zend_string_init("assert.exception", sizeof("assert.exception")-1, 0);
284 			zend_alter_ini_entry_ex(key, val, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0);
285 			zend_string_release_ex(val, 0);
286 			zend_string_release_ex(key, 0);
287 		}
288 		RETURN_LONG(oldint);
289 		break;
290 
291 	default:
292 		zend_argument_value_error(1, "must be an ASSERT_* constant");
293 		RETURN_THROWS();
294 	}
295 }
296 /* }}} */
297