Coverage Report

Created: 2025-06-13 06:43

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