diff --git a/NEWS b/NEWS index 7bacef200c357..535060d48ea4a 100644 --- a/NEWS +++ b/NEWS @@ -20,6 +20,7 @@ PHP NEWS php.exe). (Michele Locati) . Implemented "Convert numeric keys in object/array casts" RFC, fixes bugs #53838, #61655, #66173, #70925, #72254, etc. (Andrea) + . Added is_countable() to check if the parameter is safe to pass to count(). (Craig Duncan) - Date: . Fixed bug #69587 (DateInterval properties and isset). (jhdxr) diff --git a/UPGRADING b/UPGRADING index 462c75b6aea29..cfbb73ad115ce 100644 --- a/UPGRADING +++ b/UPGRADING @@ -77,6 +77,7 @@ PHP 7.2 UPGRADE NOTES . Simplified password hashing API updated to support Argon2i hashes when PHP is compiled with libargon2 (https://wiki.php.net/rfc/argon2_password_hash). . proc_nice() is now supported on Windows platforms. + . Added is_countable() to check if the parameter is safe to pass to count(). ======================================== 3. Changes in SAPI modules diff --git a/ext/standard/array.c b/ext/standard/array.c index 2c511498810ce..e4650720f4d6a 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -773,6 +773,38 @@ PHPAPI zend_long php_count_recursive(zval *array, zend_long mode) /* {{{ */ } /* }}} */ +/* {{{ proto bool is_countable(mixed var) + Returns true if variable is countable using count() */ +PHP_FUNCTION(is_countable) +{ + zval *array; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(array) + ZEND_PARSE_PARAMETERS_END(); + + switch (Z_TYPE_P(array)) { + case IS_ARRAY: + RETURN_TRUE; + return; + case IS_OBJECT: + /* first, we check if the handler is defined */ + if (Z_OBJ_HT_P(array)->count_elements) { + RETURN_TRUE; + return; + } + /* if not check if the object implements Countable */ + if (instanceof_function(Z_OBJCE_P(array), spl_ce_Countable)) { + RETURN_TRUE; + return; + } + default: + RETURN_FALSE; + break; + } +} +/* }}} */ + /* {{{ proto int count(mixed var [, int mode]) Count the number of elements in a variable (usually an array) */ PHP_FUNCTION(count) diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 9001d6dac33e0..e00c949af4f0c 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -224,6 +224,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_ksort, 0, 0, 1) ZEND_ARG_INFO(0, sort_flags) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO(arginfo_is_countable, 0) + ZEND_ARG_INFO(0, var) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_count, 0, 0, 1) ZEND_ARG_INFO(0, var) ZEND_ARG_INFO(0, mode) @@ -3311,6 +3315,7 @@ const zend_function_entry basic_functions[] = { /* {{{ */ PHP_FE(shuffle, arginfo_shuffle) PHP_FE(array_walk, arginfo_array_walk) PHP_FE(array_walk_recursive, arginfo_array_walk_recursive) + PHP_FE(is_countable, arginfo_is_countable) PHP_FE(count, arginfo_count) PHP_FE(end, arginfo_end) PHP_FE(prev, arginfo_prev) diff --git a/ext/standard/php_array.h b/ext/standard/php_array.h index 302f0ddc6731c..9e64ba6b5b1d3 100644 --- a/ext/standard/php_array.h +++ b/ext/standard/php_array.h @@ -40,6 +40,7 @@ PHP_FUNCTION(uasort); PHP_FUNCTION(uksort); PHP_FUNCTION(array_walk); PHP_FUNCTION(array_walk_recursive); +PHP_FUNCTION(is_countable); PHP_FUNCTION(count); PHP_FUNCTION(end); PHP_FUNCTION(prev); diff --git a/ext/standard/tests/array/is_countable.phpt b/ext/standard/tests/array/is_countable.phpt new file mode 100644 index 0000000000000..e107f01198155 --- /dev/null +++ b/ext/standard/tests/array/is_countable.phpt @@ -0,0 +1,36 @@ +--TEST-- +Check if variables can be counted +--FILE-- + [], + "null" => null, + "string" => "test", + "integer" => 123, + "float" => 3.14, + "true" => true, + "false" => false, + "object" => (object) [], + "simplexml" => new ArrayObject(["one", "two", "three"]), + "countable" => new class implements Countable { function count() { return 1; }}, +]; + +foreach ($types as $type => $value) { + echo "{$type}: "; + var_dump(is_countable($value)); +} + +?> +--EXPECTF-- + +array: bool(true) +null: bool(false) +string: bool(false) +integer: bool(false) +float: bool(false) +true: bool(false) +false: bool(false) +object: bool(false) +simplexml: bool(true) +countable: bool(true)