Coverage Report

Created: 2026-07-25 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/standard/assert.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright © The PHP Group and Contributors.                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to the Modified BSD License that is      |
6
   | bundled with this package in the file LICENSE, and is available      |
7
   | through the World Wide Web at <https://www.php.net/license/>.        |
8
   |                                                                      |
9
   | SPDX-License-Identifier: BSD-3-Clause                                |
10
   +----------------------------------------------------------------------+
11
   | Author: Thies C. Arntzen <thies@thieso.net>                          |
12
   +----------------------------------------------------------------------+
13
*/
14
15
/* {{{ includes */
16
#include "php.h"
17
#include "php_assert.h"
18
#include "php_ini.h"
19
#include "zend_exceptions.h"
20
/* }}} */
21
22
ZEND_BEGIN_MODULE_GLOBALS(assert)
23
  zval callback;
24
  char *cb;
25
  bool active;
26
  bool bail;
27
  bool warning;
28
  bool exception;
29
ZEND_END_MODULE_GLOBALS(assert)
30
31
ZEND_DECLARE_MODULE_GLOBALS(assert)
32
33
6.87k
#define ASSERTG(v) ZEND_MODULE_GLOBALS_ACCESSOR(assert, v)
34
35
PHPAPI zend_class_entry *assertion_error_ce;
36
37
/* Hack to pass a custom stage for the our OnModify handler so that a deprecation warning does not get emitted
38
 * when an option is modified via assert_option() function */
39
307
#define ZEND_INI_STAGE_ASSERT_OPTIONS (1<<6)
40
41
static inline bool php_must_emit_ini_deprecation(int stage)
42
153
{
43
153
  return stage != ZEND_INI_STAGE_DEACTIVATE && stage != ZEND_INI_STAGE_SHUTDOWN && stage != ZEND_INI_STAGE_ASSERT_OPTIONS;
44
153
}
45
46
static PHP_INI_MH(OnChangeCallback) /* {{{ */
47
16
{
48
16
  if (EG(current_execute_data)) {
49
0
    if (Z_TYPE(ASSERTG(callback)) != IS_UNDEF) {
50
0
      zval_ptr_dtor(&ASSERTG(callback));
51
0
      ZVAL_UNDEF(&ASSERTG(callback));
52
0
    }
53
0
    if (new_value && (Z_TYPE(ASSERTG(callback)) != IS_UNDEF || ZSTR_LEN(new_value))) {
54
0
      if (php_must_emit_ini_deprecation(stage)) {
55
0
        php_error_docref(NULL, E_DEPRECATED, "assert.callback INI setting is deprecated");
56
0
      }
57
0
      ZVAL_STR_COPY(&ASSERTG(callback), new_value);
58
0
    }
59
16
  } else {
60
16
    if (ASSERTG(cb)) {
61
0
      pefree(ASSERTG(cb), 1);
62
0
    }
63
16
    if (new_value && ZSTR_LEN(new_value)) {
64
0
      if (php_must_emit_ini_deprecation(stage)) {
65
0
        php_error_docref(NULL, E_DEPRECATED, "assert.callback INI setting is deprecated");
66
0
      }
67
0
      ASSERTG(cb) = pemalloc(ZSTR_LEN(new_value) + 1, 1);
68
0
      memcpy(ASSERTG(cb), ZSTR_VAL(new_value), ZSTR_LEN(new_value));
69
0
      ASSERTG(cb)[ZSTR_LEN(new_value)] = '\0';
70
16
    } else {
71
16
      ASSERTG(cb) = NULL;
72
16
    }
73
16
  }
74
16
  return SUCCESS;
75
16
}
76
/* }}} */
77
78
static PHP_INI_MH(OnUpdateActiveBool)
79
16
{
80
16
  bool *p = ZEND_INI_GET_ADDR();
81
16
  *p = zend_ini_parse_bool(new_value);
82
16
  if (php_must_emit_ini_deprecation(stage) && !*p) {
83
0
    php_error_docref(NULL, E_DEPRECATED, "assert.active INI setting is deprecated");
84
0
  }
85
16
  return SUCCESS;
86
16
}
87
88
static PHP_INI_MH(OnUpdateBailBool)
89
50
{
90
50
  bool *p = ZEND_INI_GET_ADDR();
91
50
  *p = zend_ini_parse_bool(new_value);
92
50
  if (php_must_emit_ini_deprecation(stage) && *p) {
93
0
    php_error_docref(NULL, E_DEPRECATED, "assert.bail INI setting is deprecated");
94
0
  }
95
50
  return SUCCESS;
96
50
}
97
98
static PHP_INI_MH(OnUpdateExceptionBool)
99
71
{
100
71
  bool *p = ZEND_INI_GET_ADDR();
101
71
  *p = zend_ini_parse_bool(new_value);
102
71
  if (php_must_emit_ini_deprecation(stage) && !*p) {
103
0
    php_error_docref(NULL, E_DEPRECATED, "assert.exception INI setting is deprecated");
104
0
  }
105
71
  return SUCCESS;
106
71
}
107
108
109
static PHP_INI_MH(OnUpdateWarningBool)
110
16
{
111
16
  bool *p = ZEND_INI_GET_ADDR();
112
16
  *p = zend_ini_parse_bool(new_value);
113
16
  if (php_must_emit_ini_deprecation(stage) && !*p) {
114
0
    php_error_docref(NULL, E_DEPRECATED, "assert.warning INI setting is deprecated");
115
0
  }
116
16
  return SUCCESS;
117
16
}
118
119
120
PHP_INI_BEGIN()
121
   STD_PHP_INI_BOOLEAN("assert.active",    "1",  PHP_INI_ALL, OnUpdateActiveBool,   active,       zend_assert_globals,    assert_globals)
122
   STD_PHP_INI_BOOLEAN("assert.bail",      "0",  PHP_INI_ALL, OnUpdateBailBool,   bail,       zend_assert_globals,    assert_globals)
123
   STD_PHP_INI_BOOLEAN("assert.warning",   "1",  PHP_INI_ALL, OnUpdateWarningBool,    warning,      zend_assert_globals,    assert_globals)
124
   PHP_INI_ENTRY("assert.callback",        NULL, PHP_INI_ALL, OnChangeCallback)
125
   STD_PHP_INI_BOOLEAN("assert.exception", "1",  PHP_INI_ALL, OnUpdateExceptionBool,    exception,      zend_assert_globals,    assert_globals)
126
PHP_INI_END()
127
128
static void php_assert_init_globals(zend_assert_globals *assert_globals_p) /* {{{ */
129
16
{
130
16
  ZVAL_UNDEF(&assert_globals_p->callback);
131
16
  assert_globals_p->cb = NULL;
132
16
}
133
/* }}} */
134
135
PHP_MINIT_FUNCTION(assert) /* {{{ */
136
16
{
137
16
  ZEND_INIT_MODULE_GLOBALS(assert, php_assert_init_globals, NULL);
138
139
16
  REGISTER_INI_ENTRIES();
140
141
16
  return SUCCESS;
142
16
}
143
/* }}} */
144
145
PHP_MSHUTDOWN_FUNCTION(assert) /* {{{ */
146
0
{
147
0
  if (ASSERTG(cb)) {
148
0
    pefree(ASSERTG(cb), 1);
149
0
    ASSERTG(cb) = NULL;
150
0
  }
151
0
  return SUCCESS;
152
0
}
153
/* }}} */
154
155
PHP_RSHUTDOWN_FUNCTION(assert) /* {{{ */
156
300k
{
157
300k
  if (Z_TYPE(ASSERTG(callback)) != IS_UNDEF) {
158
18
    zval_ptr_dtor(&ASSERTG(callback));
159
18
    ZVAL_UNDEF(&ASSERTG(callback));
160
18
  }
161
162
300k
  return SUCCESS;
163
300k
}
164
/* }}} */
165
166
PHP_MINFO_FUNCTION(assert) /* {{{ */
167
4
{
168
4
  DISPLAY_INI_ENTRIES();
169
4
}
170
/* }}} */
171
172
/* {{{ Checks if assertion is false */
173
PHP_FUNCTION(assert)
174
3.67k
{
175
3.67k
  zval *assertion;
176
3.67k
  zend_string *description_str = NULL;
177
3.67k
  zend_object *description_obj = NULL;
178
179
  /* EG(assertions) <= 0 is only reachable by dynamic calls to assert(),
180
   * since calls known at compile time will skip the entire call when
181
   * assertions are disabled.
182
   */
183
3.67k
  if (!ASSERTG(active) || EG(assertions) <= 0) {
184
0
    RETURN_TRUE;
185
0
  }
186
187
11.0k
  ZEND_PARSE_PARAMETERS_START(1, 2)
188
14.7k
    Z_PARAM_ZVAL(assertion)
189
14.7k
    Z_PARAM_OPTIONAL
190
18.2k
    Z_PARAM_OBJ_OF_CLASS_OR_STR_OR_NULL(description_obj, zend_ce_throwable, description_str)
191
18.2k
  ZEND_PARSE_PARAMETERS_END();
192
193
3.67k
  if (zend_is_true(assertion)) {
194
2.89k
    RETURN_TRUE;
195
2.89k
  }
196
197
783
  if (description_obj) {
198
13
    GC_ADDREF(description_obj);
199
13
    zend_throw_exception_internal(description_obj);
200
13
    RETURN_THROWS();
201
13
  }
202
203
770
  if (Z_TYPE(ASSERTG(callback)) == IS_UNDEF && ASSERTG(cb)) {
204
0
    ZVAL_STRING(&ASSERTG(callback), ASSERTG(cb));
205
0
  }
206
207
770
  if (Z_TYPE(ASSERTG(callback)) != IS_UNDEF) {
208
18
    zval args[4];
209
18
    zval retval;
210
18
    uint32_t lineno = zend_get_executed_lineno();
211
18
    zend_string *filename = zend_get_executed_filename_ex();
212
18
    if (UNEXPECTED(!filename)) {
213
0
      filename = ZSTR_KNOWN(ZEND_STR_UNKNOWN_CAPITALIZED);
214
0
    }
215
216
18
    ZVAL_STR(&args[0], filename);
217
18
    ZVAL_LONG(&args[1], lineno);
218
18
    ZVAL_NULL(&args[2]);
219
220
18
    ZVAL_FALSE(&retval);
221
222
18
    if (description_str) {
223
18
      ZVAL_STR(&args[3], description_str);
224
18
      call_user_function(NULL, NULL, &ASSERTG(callback), &retval, 4, args);
225
18
    } else {
226
0
      call_user_function(NULL, NULL, &ASSERTG(callback), &retval, 3, args);
227
0
    }
228
229
18
    zval_ptr_dtor(&retval);
230
18
  }
231
232
770
  if (ASSERTG(exception)) {
233
757
    zend_throw_exception(assertion_error_ce, description_str ? ZSTR_VAL(description_str) : NULL, E_ERROR);
234
757
    if (ASSERTG(bail)) {
235
      /* When bail is turned on, the exception will not be caught. */
236
4
      zend_exception_error(EG(exception), E_ERROR);
237
4
    }
238
757
  } else if (ASSERTG(warning)) {
239
13
    php_error_docref(NULL, E_WARNING, "%s failed", description_str ? ZSTR_VAL(description_str) : "Assertion");
240
13
  }
241
242
770
  if (ASSERTG(bail)) {
243
14
    if (EG(exception)) {
244
      /* The callback might have thrown. Use E_WARNING to print the
245
       * exception so we can avoid bailout and use unwind_exit. */
246
10
      zend_exception_error(EG(exception), E_WARNING);
247
10
    }
248
14
    if (!EG(exception)) {
249
14
      zend_throw_unwind_exit();
250
14
    }
251
14
    RETURN_THROWS();
252
756
  } else {
253
756
    RETURN_FALSE;
254
756
  }
255
770
}
256
/* }}} */
257
258
/* {{{ Set/get the various assert flags */
259
PHP_FUNCTION(assert_options)
260
66
{
261
66
  zval *value = NULL;
262
66
  zend_long what;
263
66
  bool oldint;
264
66
  uint32_t ac = ZEND_NUM_ARGS();
265
66
  zend_string *key;
266
267
198
  ZEND_PARSE_PARAMETERS_START(1, 2)
268
264
    Z_PARAM_LONG(what)
269
65
    Z_PARAM_OPTIONAL
270
256
    Z_PARAM_ZVAL(value)
271
256
  ZEND_PARSE_PARAMETERS_END();
272
273
65
  switch (what) {
274
0
  case PHP_ASSERT_ACTIVE:
275
0
    oldint = ASSERTG(active);
276
0
    if (ac == 2) {
277
0
      zend_string *value_str = zval_try_get_string(value);
278
0
      if (UNEXPECTED(!value_str)) {
279
0
        RETURN_THROWS();
280
0
      }
281
282
0
      key = ZSTR_INIT_LITERAL("assert.active", 0);
283
0
      zend_alter_ini_entry_ex(key, value_str, PHP_INI_USER, ZEND_INI_STAGE_ASSERT_OPTIONS, 0);
284
0
      zend_string_release_ex(key, 0);
285
0
      zend_string_release_ex(value_str, 0);
286
0
    }
287
0
    RETURN_LONG(oldint);
288
289
17
  case PHP_ASSERT_BAIL:
290
17
    oldint = ASSERTG(bail);
291
17
    if (ac == 2) {
292
17
      zend_string *value_str = zval_try_get_string(value);
293
17
      if (UNEXPECTED(!value_str)) {
294
0
        RETURN_THROWS();
295
0
      }
296
297
17
      key = ZSTR_INIT_LITERAL("assert.bail", 0);
298
17
      zend_alter_ini_entry_ex(key, value_str, PHP_INI_USER, ZEND_INI_STAGE_ASSERT_OPTIONS, 0);
299
17
      zend_string_release_ex(key, 0);
300
17
      zend_string_release_ex(value_str, 0);
301
17
    }
302
17
    RETURN_LONG(oldint);
303
304
0
  case PHP_ASSERT_WARNING:
305
0
    oldint = ASSERTG(warning);
306
0
    if (ac == 2) {
307
0
      zend_string *value_str = zval_try_get_string(value);
308
0
      if (UNEXPECTED(!value_str)) {
309
0
        RETURN_THROWS();
310
0
      }
311
312
0
      key = ZSTR_INIT_LITERAL("assert.warning", 0);
313
0
      zend_alter_ini_entry_ex(key, value_str, PHP_INI_USER, ZEND_INI_STAGE_ASSERT_OPTIONS, 0);
314
0
      zend_string_release_ex(key, 0);
315
0
      zend_string_release_ex(value_str, 0);
316
0
    }
317
0
    RETURN_LONG(oldint);
318
319
18
  case PHP_ASSERT_CALLBACK:
320
18
    if (Z_TYPE(ASSERTG(callback)) != IS_UNDEF) {
321
0
      ZVAL_COPY(return_value, &ASSERTG(callback));
322
18
    } else if (ASSERTG(cb)) {
323
0
      RETVAL_STRING(ASSERTG(cb));
324
18
    } else {
325
18
      RETVAL_NULL();
326
18
    }
327
328
18
    if (ac == 2) {
329
18
      zval_ptr_dtor(&ASSERTG(callback));
330
18
      if (Z_TYPE_P(value) == IS_NULL) {
331
0
        ZVAL_UNDEF(&ASSERTG(callback));
332
18
      } else {
333
18
        ZVAL_COPY(&ASSERTG(callback), value);
334
18
      }
335
18
    }
336
18
    return;
337
338
29
  case PHP_ASSERT_EXCEPTION:
339
29
    oldint = ASSERTG(exception);
340
29
    if (ac == 2) {
341
28
      zend_string *val = zval_try_get_string(value);
342
28
      if (UNEXPECTED(!val)) {
343
0
        RETURN_THROWS();
344
0
      }
345
346
28
      key = ZSTR_INIT_LITERAL("assert.exception", 0);
347
28
      zend_alter_ini_entry_ex(key, val, PHP_INI_USER, ZEND_INI_STAGE_ASSERT_OPTIONS, 0);
348
28
      zend_string_release_ex(val, 0);
349
28
      zend_string_release_ex(key, 0);
350
28
    }
351
29
    RETURN_LONG(oldint);
352
353
1
  default:
354
1
    zend_argument_value_error(1, "must be an ASSERT_* constant");
355
    RETURN_THROWS();
356
65
  }
357
65
}
358
/* }}} */