xref: /PHP-8.4/ext/standard/type.c (revision d45eb46c)
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: Rasmus Lerdorf <rasmus@php.net>                              |
14    +----------------------------------------------------------------------+
15 */
16 
17 #include "php.h"
18 
19 /* {{{ Returns the type of the variable */
PHP_FUNCTION(gettype)20 PHP_FUNCTION(gettype)
21 {
22 	zval *arg;
23 	zend_string *type;
24 
25 	ZEND_PARSE_PARAMETERS_START(1, 1)
26 		Z_PARAM_ZVAL(arg)
27 	ZEND_PARSE_PARAMETERS_END();
28 
29 	type = zend_zval_get_legacy_type(arg);
30 	if (EXPECTED(type)) {
31 		RETURN_INTERNED_STR(type);
32 	} else {
33 		RETURN_STRING("unknown type");
34 	}
35 }
36 /* }}} */
37 
38 /* {{{ Returns the type of the variable resolving class names */
PHP_FUNCTION(get_debug_type)39 PHP_FUNCTION(get_debug_type)
40 {
41 	zval *arg;
42 	const char *name;
43 
44 	ZEND_PARSE_PARAMETERS_START(1, 1)
45 		Z_PARAM_ZVAL(arg)
46 	ZEND_PARSE_PARAMETERS_END();
47 
48 	switch (Z_TYPE_P(arg)) {
49 		case IS_NULL:
50 			RETURN_INTERNED_STR(ZSTR_KNOWN(ZEND_STR_NULL_LOWERCASE));
51 		case IS_FALSE:
52 		case IS_TRUE:
53 			RETURN_INTERNED_STR(ZSTR_KNOWN(ZEND_STR_BOOL));
54 		case IS_LONG:
55 			RETURN_INTERNED_STR(ZSTR_KNOWN(ZEND_STR_INT));
56 		case IS_DOUBLE:
57 			RETURN_INTERNED_STR(ZSTR_KNOWN(ZEND_STR_FLOAT));
58 		case IS_STRING:
59 			RETURN_INTERNED_STR(ZSTR_KNOWN(ZEND_STR_STRING));
60 		case IS_ARRAY:
61 			RETURN_INTERNED_STR(ZSTR_KNOWN(ZEND_STR_ARRAY));
62 		case IS_OBJECT:
63 			if (Z_OBJ_P(arg)->ce->ce_flags & ZEND_ACC_ANON_CLASS) {
64 				name = ZSTR_VAL(Z_OBJ_P(arg)->ce->name);
65 				RETURN_NEW_STR(zend_string_init(name, strlen(name), 0));
66 			} else {
67 				RETURN_STR_COPY(Z_OBJ_P(arg)->ce->name);
68 			}
69 		case IS_RESOURCE:
70 			name = zend_rsrc_list_get_rsrc_type(Z_RES_P(arg));
71 			if (name) {
72 				RETURN_NEW_STR(zend_strpprintf(0, "resource (%s)", name));
73 			} else {
74 				RETURN_INTERNED_STR(ZSTR_KNOWN(ZEND_STR_CLOSED_RESOURCE));
75 			}
76 		default:
77 			RETURN_INTERNED_STR(ZSTR_KNOWN(ZEND_STR_UNKNOWN));
78 	}
79 }
80 /* }}} */
81 
82 
83 /* {{{ Set the type of the variable */
PHP_FUNCTION(settype)84 PHP_FUNCTION(settype)
85 {
86 	zval *var;
87 	zend_string *type;
88 	zval tmp, *ptr;
89 
90 	ZEND_PARSE_PARAMETERS_START(2, 2)
91 		Z_PARAM_ZVAL(var)
92 		Z_PARAM_STR(type)
93 	ZEND_PARSE_PARAMETERS_END();
94 
95 	ZEND_ASSERT(Z_ISREF_P(var));
96 	if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(var)))) {
97 		ZVAL_COPY(&tmp, Z_REFVAL_P(var));
98 		ptr = &tmp;
99 	} else {
100 		ptr = Z_REFVAL_P(var);
101 	}
102 	if (zend_string_equals_ci(type, ZSTR_KNOWN(ZEND_STR_INTEGER))) {
103 		convert_to_long(ptr);
104 	} else if (zend_string_equals_ci(type, ZSTR_KNOWN(ZEND_STR_INT))) {
105 		convert_to_long(ptr);
106 	} else if (zend_string_equals_ci(type, ZSTR_KNOWN(ZEND_STR_FLOAT))) {
107 		convert_to_double(ptr);
108 	} else if (zend_string_equals_ci(type, ZSTR_KNOWN(ZEND_STR_DOUBLE))) { /* deprecated */
109 		convert_to_double(ptr);
110 	} else if (zend_string_equals_ci(type, ZSTR_KNOWN(ZEND_STR_STRING))) {
111 		convert_to_string(ptr);
112 	} else if (zend_string_equals_ci(type, ZSTR_KNOWN(ZEND_STR_ARRAY))) {
113 		convert_to_array(ptr);
114 	} else if (zend_string_equals_ci(type, ZSTR_KNOWN(ZEND_STR_OBJECT))) {
115 		convert_to_object(ptr);
116 	} else if (zend_string_equals_ci(type, ZSTR_KNOWN(ZEND_STR_BOOL))) {
117 		convert_to_boolean(ptr);
118 	} else if (zend_string_equals_ci(type, ZSTR_KNOWN(ZEND_STR_BOOLEAN))) {
119 		convert_to_boolean(ptr);
120 	} else if (zend_string_equals_ci(type, ZSTR_KNOWN(ZEND_STR_NULL_LOWERCASE))) {
121 		convert_to_null(ptr);
122 	} else {
123 		if (ptr == &tmp) {
124 			zval_ptr_dtor(&tmp);
125 		}
126 		if (zend_string_equals_ci(type, ZSTR_KNOWN(ZEND_STR_RESOURCE))) {
127 			zend_value_error("Cannot convert to resource type");
128 		} else {
129 			zend_argument_value_error(2, "must be a valid type");
130 		}
131 		RETURN_THROWS();
132 	}
133 
134 	if (ptr == &tmp) {
135 		zend_try_assign_typed_ref(Z_REF_P(var), &tmp);
136 	}
137 	RETVAL_TRUE;
138 }
139 /* }}} */
140 
141 /* {{{ Get the integer value of a variable using the optional base for the conversion */
PHP_FUNCTION(intval)142 PHP_FUNCTION(intval)
143 {
144 	zval *num;
145 	zend_long base = 10;
146 
147 	ZEND_PARSE_PARAMETERS_START(1, 2)
148 		Z_PARAM_ZVAL(num)
149 		Z_PARAM_OPTIONAL
150 		Z_PARAM_LONG(base)
151 	ZEND_PARSE_PARAMETERS_END();
152 
153 	if (Z_TYPE_P(num) != IS_STRING || base == 10) {
154 		RETVAL_LONG(zval_get_long(num));
155 		return;
156 	}
157 
158 
159 	if (base == 0 || base == 2) {
160 		char *strval = Z_STRVAL_P(num);
161 		size_t strlen = Z_STRLEN_P(num);
162 
163 		while (isspace(*strval) && strlen) {
164 			strval++;
165 			strlen--;
166 		}
167 
168 		/* Length of 3+ covers "0b#" and "-0b" (which results in 0) */
169 		if (strlen > 2) {
170 			int offset = 0;
171 			if (strval[0] == '-' || strval[0] == '+') {
172 				offset = 1;
173 			}
174 
175 			if (strval[offset] == '0' && (strval[offset + 1] == 'b' || strval[offset + 1] == 'B')) {
176 				char *tmpval;
177 				strlen -= 2; /* Removing "0b" */
178 				tmpval = emalloc(strlen + 1);
179 
180 				/* Place the unary symbol at pos 0 if there was one */
181 				if (offset) {
182 					tmpval[0] = strval[0];
183 				}
184 
185 				/* Copy the data from after "0b" to the end of the buffer */
186 				memcpy(tmpval + offset, strval + offset + 2, strlen - offset);
187 				tmpval[strlen] = 0;
188 
189 				RETVAL_LONG(ZEND_STRTOL(tmpval, NULL, 2));
190 				efree(tmpval);
191 				return;
192 			}
193 		}
194 	}
195 
196 	RETVAL_LONG(ZEND_STRTOL(Z_STRVAL_P(num), NULL, base));
197 }
198 /* }}} */
199 
200 /* {{{ Get the float value of a variable */
PHP_FUNCTION(floatval)201 PHP_FUNCTION(floatval)
202 {
203 	zval *num;
204 
205 	ZEND_PARSE_PARAMETERS_START(1, 1)
206 		Z_PARAM_ZVAL(num)
207 	ZEND_PARSE_PARAMETERS_END();
208 
209 	RETURN_DOUBLE(zval_get_double(num));
210 }
211 /* }}} */
212 
213 /* {{{ Get the boolean value of a variable */
PHP_FUNCTION(boolval)214 PHP_FUNCTION(boolval)
215 {
216 	zval *value;
217 
218 	ZEND_PARSE_PARAMETERS_START(1, 1)
219 		Z_PARAM_ZVAL(value)
220 	ZEND_PARSE_PARAMETERS_END();
221 
222 	RETURN_BOOL(zend_is_true(value));
223 }
224 /* }}} */
225 
226 /* {{{ Get the string value of a variable */
PHP_FUNCTION(strval)227 PHP_FUNCTION(strval)
228 {
229 	zval *value;
230 
231 	ZEND_PARSE_PARAMETERS_START(1, 1)
232 		Z_PARAM_ZVAL(value)
233 	ZEND_PARSE_PARAMETERS_END();
234 
235 	RETVAL_STR(zval_get_string(value));
236 }
237 /* }}} */
238 
php_is_type(INTERNAL_FUNCTION_PARAMETERS,int type)239 static inline void php_is_type(INTERNAL_FUNCTION_PARAMETERS, int type)
240 {
241 	zval *arg;
242 
243 	ZEND_PARSE_PARAMETERS_START(1, 1)
244 		Z_PARAM_ZVAL(arg)
245 	ZEND_PARSE_PARAMETERS_END();
246 
247 	if (Z_TYPE_P(arg) == type) {
248 		if (type == IS_RESOURCE) {
249 			const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(arg));
250 			if (!type_name) {
251 				RETURN_FALSE;
252 			}
253 		}
254 		RETURN_TRUE;
255 	} else {
256 		RETURN_FALSE;
257 	}
258 }
259 
260 
261 /* {{{ Returns true if variable is null
262    Warning: This function is special-cased by zend_compile.c and so is usually bypassed */
PHP_FUNCTION(is_null)263 PHP_FUNCTION(is_null)
264 {
265 	php_is_type(INTERNAL_FUNCTION_PARAM_PASSTHRU, IS_NULL);
266 }
267 /* }}} */
268 
269 /* {{{ Returns true if variable is a resource
270    Warning: This function is special-cased by zend_compile.c and so is usually bypassed */
PHP_FUNCTION(is_resource)271 PHP_FUNCTION(is_resource)
272 {
273 	php_is_type(INTERNAL_FUNCTION_PARAM_PASSTHRU, IS_RESOURCE);
274 }
275 /* }}} */
276 
277 /* {{{ Returns true if variable is a boolean
278    Warning: This function is special-cased by zend_compile.c and so is usually bypassed */
PHP_FUNCTION(is_bool)279 PHP_FUNCTION(is_bool)
280 {
281 	zval *arg;
282 
283 	ZEND_PARSE_PARAMETERS_START(1, 1)
284 		Z_PARAM_ZVAL(arg)
285 	ZEND_PARSE_PARAMETERS_END();
286 
287 	RETURN_BOOL(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE);
288 }
289 /* }}} */
290 
291 /* {{{ Returns true if variable is an integer
292    Warning: This function is special-cased by zend_compile.c and so is usually bypassed */
PHP_FUNCTION(is_int)293 PHP_FUNCTION(is_int)
294 {
295 	php_is_type(INTERNAL_FUNCTION_PARAM_PASSTHRU, IS_LONG);
296 }
297 /* }}} */
298 
299 /* {{{ Returns true if variable is float point
300    Warning: This function is special-cased by zend_compile.c and so is usually bypassed */
PHP_FUNCTION(is_float)301 PHP_FUNCTION(is_float)
302 {
303 	php_is_type(INTERNAL_FUNCTION_PARAM_PASSTHRU, IS_DOUBLE);
304 }
305 /* }}} */
306 
307 /* {{{ Returns true if variable is a string
308    Warning: This function is special-cased by zend_compile.c and so is usually bypassed */
PHP_FUNCTION(is_string)309 PHP_FUNCTION(is_string)
310 {
311 	php_is_type(INTERNAL_FUNCTION_PARAM_PASSTHRU, IS_STRING);
312 }
313 /* }}} */
314 
315 /* {{{ Returns true if variable is an array
316    Warning: This function is special-cased by zend_compile.c and so is usually bypassed */
PHP_FUNCTION(is_array)317 PHP_FUNCTION(is_array)
318 {
319 	php_is_type(INTERNAL_FUNCTION_PARAM_PASSTHRU, IS_ARRAY);
320 }
321 /* }}} */
322 
323 /* {{{ Returns true if $array is an array whose keys are all numeric, sequential, and start at 0 */
PHP_FUNCTION(array_is_list)324 PHP_FUNCTION(array_is_list)
325 {
326 	HashTable *array;
327 
328 	ZEND_PARSE_PARAMETERS_START(1, 1)
329 		Z_PARAM_ARRAY_HT(array)
330 	ZEND_PARSE_PARAMETERS_END();
331 
332 	RETURN_BOOL(zend_array_is_list(array));
333 }
334 /* }}} */
335 
336 /* {{{ Returns true if variable is an object
337    Warning: This function is special-cased by zend_compile.c and so is usually bypassed */
PHP_FUNCTION(is_object)338 PHP_FUNCTION(is_object)
339 {
340 	php_is_type(INTERNAL_FUNCTION_PARAM_PASSTHRU, IS_OBJECT);
341 }
342 /* }}} */
343 
_zend_is_numeric(zval * return_value,zval * arg)344 static inline void _zend_is_numeric(zval *return_value, zval *arg)
345 {
346 	switch (Z_TYPE_P(arg)) {
347 		case IS_LONG:
348 		case IS_DOUBLE:
349 			RETURN_TRUE;
350 			break;
351 
352 		case IS_STRING:
353 			if (is_numeric_string(Z_STRVAL_P(arg), Z_STRLEN_P(arg), NULL, NULL, 0)) {
354 				RETURN_TRUE;
355 			} else {
356 				RETURN_FALSE;
357 			}
358 			break;
359 
360 		default:
361 			RETURN_FALSE;
362 			break;
363 	}
364 }
365 
366 /* {{{ Returns true if value is a number or a numeric string */
PHP_FUNCTION(is_numeric)367 PHP_FUNCTION(is_numeric)
368 {
369 	zval *arg;
370 
371 	ZEND_PARSE_PARAMETERS_START(1, 1)
372 		Z_PARAM_ZVAL(arg)
373 	ZEND_PARSE_PARAMETERS_END();
374 
375 	_zend_is_numeric(return_value, arg);
376 }
377 /* }}} */
378 
379 ZEND_FRAMELESS_FUNCTION(is_numeric, 1)
380 {
381 	zval *arg;
382 
383 	Z_FLF_PARAM_ZVAL(1, arg);
384 
385 	_zend_is_numeric(return_value, arg);
386 }
387 
388 /* {{{ Returns true if value is a scalar */
PHP_FUNCTION(is_scalar)389 PHP_FUNCTION(is_scalar)
390 {
391 	zval *arg;
392 
393 	ZEND_PARSE_PARAMETERS_START(1, 1)
394 		Z_PARAM_ZVAL(arg)
395 	ZEND_PARSE_PARAMETERS_END();
396 
397 	switch (Z_TYPE_P(arg)) {
398 		case IS_FALSE:
399 		case IS_TRUE:
400 		case IS_DOUBLE:
401 		case IS_LONG:
402 		case IS_STRING:
403 			RETURN_TRUE;
404 			break;
405 
406 		default:
407 			RETURN_FALSE;
408 			break;
409 	}
410 }
411 /* }}} */
412 
413 /* {{{ Returns true if var is callable. */
PHP_FUNCTION(is_callable)414 PHP_FUNCTION(is_callable)
415 {
416 	zval *var, *callable_name = NULL;
417 	zend_string *name;
418 	bool retval;
419 	bool syntax_only = 0;
420 	int check_flags = 0;
421 
422 	ZEND_PARSE_PARAMETERS_START(1, 3)
423 		Z_PARAM_ZVAL(var)
424 		Z_PARAM_OPTIONAL
425 		Z_PARAM_BOOL(syntax_only)
426 		Z_PARAM_ZVAL(callable_name)
427 	ZEND_PARSE_PARAMETERS_END();
428 
429 	if (syntax_only) {
430 		check_flags |= IS_CALLABLE_CHECK_SYNTAX_ONLY;
431 	}
432 	if (ZEND_NUM_ARGS() > 2) {
433 		retval = zend_is_callable_ex(var, NULL, check_flags, &name, NULL, NULL);
434 		ZEND_TRY_ASSIGN_REF_STR(callable_name, name);
435 	} else {
436 		retval = zend_is_callable_ex(var, NULL, check_flags, NULL, NULL, NULL);
437 	}
438 
439 	RETURN_BOOL(retval);
440 }
441 /* }}} */
442 
443 /* {{{ Returns true if var is iterable (array or instance of Traversable). */
PHP_FUNCTION(is_iterable)444 PHP_FUNCTION(is_iterable)
445 {
446 	zval *var;
447 
448 	ZEND_PARSE_PARAMETERS_START(1, 1)
449 		Z_PARAM_ZVAL(var)
450 	ZEND_PARSE_PARAMETERS_END();
451 
452 	RETURN_BOOL(zend_is_iterable(var));
453 }
454 /* }}} */
455 
456 /* {{{ Returns true if var is countable (array or instance of Countable). */
PHP_FUNCTION(is_countable)457 PHP_FUNCTION(is_countable)
458 {
459 	zval *var;
460 
461 	ZEND_PARSE_PARAMETERS_START(1, 1)
462 		Z_PARAM_ZVAL(var)
463 	ZEND_PARSE_PARAMETERS_END();
464 
465 	RETURN_BOOL(zend_is_countable(var));
466 }
467 /* }}} */
468