Skip to content

Commit 1df1a42

Browse files
committed
Improve count() in regards to argument checks
Add ZPP checks for the first argument to ensure it is an array or a Countable object. Due to earlier discussion NULL is special cased and deprecated. Also throw a ValueError on invalid mode values.
1 parent 404872c commit 1df1a42

23 files changed

+835
-845
lines changed

Zend/tests/generators/errors/count_error.phpt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ try {
1111
count($gen);
1212
} catch (Exception $e) {
1313
echo $e;
14-
}
14+
} catch (\TypeError $e) {
15+
echo $e->getMessage() . "\n";
16+
}
1517

1618
?>
17-
--EXPECTF--
18-
Warning: count(): Parameter must be an array or an object that implements Countable in %s on line %d
19+
--EXPECT--
20+
Parameter must be an array or an object that implements Countable

Zend/zend_vm_def.h

Lines changed: 25 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8352,53 +8352,38 @@ ZEND_VM_COLD_CONST_HANDLER(190, ZEND_COUNT, CONST|TMPVAR|CV, UNUSED)
83528352
{
83538353
USE_OPLINE
83548354
zval *op1;
8355-
zend_long count;
8355+
zend_long count = 1;
83568356

83578357
SAVE_OPLINE();
83588358
op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
8359-
while (1) {
8360-
if (Z_TYPE_P(op1) == IS_ARRAY) {
8361-
count = zend_array_count(Z_ARRVAL_P(op1));
8362-
break;
8363-
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
8364-
zend_object *zobj = Z_OBJ_P(op1);
8365-
8366-
/* first, we check if the handler is defined */
8367-
if (zobj->handlers->count_elements) {
8368-
if (SUCCESS == zobj->handlers->count_elements(zobj, &count)) {
8369-
break;
8370-
}
8371-
if (UNEXPECTED(EG(exception))) {
8372-
count = 0;
8373-
break;
8374-
}
8375-
}
8376-
8377-
/* if not and the object implements Countable we call its count() method */
8378-
if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
8379-
zval retval;
83808359

8381-
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
8382-
count = zval_get_long(&retval);
8383-
zval_ptr_dtor(&retval);
8384-
break;
8385-
}
8360+
if ((OP1_TYPE & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
8361+
op1 = Z_REFVAL_P(op1);
8362+
}
83868363

8387-
/* If There's no handler and it doesn't implement Countable then add a warning */
8388-
count = 1;
8389-
} else if ((OP1_TYPE & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
8390-
op1 = Z_REFVAL_P(op1);
8391-
continue;
8392-
} else if (Z_TYPE_P(op1) <= IS_NULL) {
8393-
if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
8394-
ZVAL_UNDEFINED_OP1();
8395-
}
8396-
count = 0;
8364+
if (Z_TYPE_P(op1) <= IS_NULL) {
8365+
/* Intentionally not converted to an exception */
8366+
zend_error(E_DEPRECATED, "%s(): Passing null is deprecated", opline->extended_value ? "sizeof" : "count");
8367+
if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
8368+
ZVAL_UNDEFINED_OP1();
8369+
}
8370+
count = 0;
8371+
} else if (Z_TYPE_P(op1) == IS_ARRAY) {
8372+
count = zend_array_count(Z_ARRVAL_P(op1));
8373+
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
8374+
zend_object *zobj = Z_OBJ_P(op1);
8375+
/* if the object implements Countable we call its count() method */
8376+
if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
8377+
zval retval;
8378+
8379+
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
8380+
count = zval_get_long(&retval);
8381+
zval_ptr_dtor(&retval);
83978382
} else {
8398-
count = 1;
8383+
zend_type_error("Parameter must be an array or an object that implements Countable");
83998384
}
8400-
zend_error(E_WARNING, "%s(): Parameter must be an array or an object that implements Countable", opline->extended_value ? "sizeof" : "count");
8401-
break;
8385+
} else {
8386+
zend_type_error("Parameter must be an array or an object that implements Countable");
84028387
}
84038388

84048389
ZVAL_LONG(EX_VAR(opline->result.var), count);

Zend/zend_vm_execute.h

Lines changed: 72 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -9194,53 +9194,38 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COUNT_SPEC_CONST_
91949194
{
91959195
USE_OPLINE
91969196
zval *op1;
9197-
zend_long count;
9197+
zend_long count = 1;
91989198

91999199
SAVE_OPLINE();
92009200
op1 = RT_CONSTANT(opline, opline->op1);
9201-
while (1) {
9202-
if (Z_TYPE_P(op1) == IS_ARRAY) {
9203-
count = zend_array_count(Z_ARRVAL_P(op1));
9204-
break;
9205-
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
9206-
zend_object *zobj = Z_OBJ_P(op1);
92079201

9208-
/* first, we check if the handler is defined */
9209-
if (zobj->handlers->count_elements) {
9210-
if (SUCCESS == zobj->handlers->count_elements(zobj, &count)) {
9211-
break;
9212-
}
9213-
if (UNEXPECTED(EG(exception))) {
9214-
count = 0;
9215-
break;
9216-
}
9217-
}
9218-
9219-
/* if not and the object implements Countable we call its count() method */
9220-
if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
9221-
zval retval;
9202+
if ((IS_CONST & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
9203+
op1 = Z_REFVAL_P(op1);
9204+
}
92229205

9223-
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
9224-
count = zval_get_long(&retval);
9225-
zval_ptr_dtor(&retval);
9226-
break;
9227-
}
9206+
if (Z_TYPE_P(op1) <= IS_NULL) {
9207+
/* Intentionally not converted to an exception */
9208+
zend_error(E_DEPRECATED, "%s(): Passing null is deprecated", opline->extended_value ? "sizeof" : "count");
9209+
if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
9210+
ZVAL_UNDEFINED_OP1();
9211+
}
9212+
count = 0;
9213+
} else if (Z_TYPE_P(op1) == IS_ARRAY) {
9214+
count = zend_array_count(Z_ARRVAL_P(op1));
9215+
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
9216+
zend_object *zobj = Z_OBJ_P(op1);
9217+
/* if the object implements Countable we call its count() method */
9218+
if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
9219+
zval retval;
92289220

9229-
/* If There's no handler and it doesn't implement Countable then add a warning */
9230-
count = 1;
9231-
} else if ((IS_CONST & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
9232-
op1 = Z_REFVAL_P(op1);
9233-
continue;
9234-
} else if (Z_TYPE_P(op1) <= IS_NULL) {
9235-
if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
9236-
ZVAL_UNDEFINED_OP1();
9237-
}
9238-
count = 0;
9221+
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
9222+
count = zval_get_long(&retval);
9223+
zval_ptr_dtor(&retval);
92399224
} else {
9240-
count = 1;
9225+
zend_type_error("Parameter must be an array or an object that implements Countable");
92419226
}
9242-
zend_error(E_WARNING, "%s(): Parameter must be an array or an object that implements Countable", opline->extended_value ? "sizeof" : "count");
9243-
break;
9227+
} else {
9228+
zend_type_error("Parameter must be an array or an object that implements Countable");
92449229
}
92459230

92469231
ZVAL_LONG(EX_VAR(opline->result.var), count);
@@ -16216,53 +16201,38 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COUNT_SPEC_TMPVAR_UNUSED_HANDL
1621616201
{
1621716202
USE_OPLINE
1621816203
zval *op1;
16219-
zend_long count;
16204+
zend_long count = 1;
1622016205

1622116206
SAVE_OPLINE();
1622216207
op1 = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC);
16223-
while (1) {
16224-
if (Z_TYPE_P(op1) == IS_ARRAY) {
16225-
count = zend_array_count(Z_ARRVAL_P(op1));
16226-
break;
16227-
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
16228-
zend_object *zobj = Z_OBJ_P(op1);
16229-
16230-
/* first, we check if the handler is defined */
16231-
if (zobj->handlers->count_elements) {
16232-
if (SUCCESS == zobj->handlers->count_elements(zobj, &count)) {
16233-
break;
16234-
}
16235-
if (UNEXPECTED(EG(exception))) {
16236-
count = 0;
16237-
break;
16238-
}
16239-
}
1624016208

16241-
/* if not and the object implements Countable we call its count() method */
16242-
if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
16243-
zval retval;
16209+
if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
16210+
op1 = Z_REFVAL_P(op1);
16211+
}
1624416212

16245-
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
16246-
count = zval_get_long(&retval);
16247-
zval_ptr_dtor(&retval);
16248-
break;
16249-
}
16213+
if (Z_TYPE_P(op1) <= IS_NULL) {
16214+
/* Intentionally not converted to an exception */
16215+
zend_error(E_DEPRECATED, "%s(): Passing null is deprecated", opline->extended_value ? "sizeof" : "count");
16216+
if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
16217+
ZVAL_UNDEFINED_OP1();
16218+
}
16219+
count = 0;
16220+
} else if (Z_TYPE_P(op1) == IS_ARRAY) {
16221+
count = zend_array_count(Z_ARRVAL_P(op1));
16222+
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
16223+
zend_object *zobj = Z_OBJ_P(op1);
16224+
/* if the object implements Countable we call its count() method */
16225+
if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
16226+
zval retval;
1625016227

16251-
/* If There's no handler and it doesn't implement Countable then add a warning */
16252-
count = 1;
16253-
} else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
16254-
op1 = Z_REFVAL_P(op1);
16255-
continue;
16256-
} else if (Z_TYPE_P(op1) <= IS_NULL) {
16257-
if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
16258-
ZVAL_UNDEFINED_OP1();
16259-
}
16260-
count = 0;
16228+
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
16229+
count = zval_get_long(&retval);
16230+
zval_ptr_dtor(&retval);
1626116231
} else {
16262-
count = 1;
16232+
zend_type_error("Parameter must be an array or an object that implements Countable");
1626316233
}
16264-
zend_error(E_WARNING, "%s(): Parameter must be an array or an object that implements Countable", opline->extended_value ? "sizeof" : "count");
16265-
break;
16234+
} else {
16235+
zend_type_error("Parameter must be an array or an object that implements Countable");
1626616236
}
1626716237

1626816238
ZVAL_LONG(EX_VAR(opline->result.var), count);
@@ -44736,53 +44706,38 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COUNT_SPEC_CV_UNUSED_HANDLER(Z
4473644706
{
4473744707
USE_OPLINE
4473844708
zval *op1;
44739-
zend_long count;
44709+
zend_long count = 1;
4474044710

4474144711
SAVE_OPLINE();
4474244712
op1 = EX_VAR(opline->op1.var);
44743-
while (1) {
44744-
if (Z_TYPE_P(op1) == IS_ARRAY) {
44745-
count = zend_array_count(Z_ARRVAL_P(op1));
44746-
break;
44747-
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
44748-
zend_object *zobj = Z_OBJ_P(op1);
44749-
44750-
/* first, we check if the handler is defined */
44751-
if (zobj->handlers->count_elements) {
44752-
if (SUCCESS == zobj->handlers->count_elements(zobj, &count)) {
44753-
break;
44754-
}
44755-
if (UNEXPECTED(EG(exception))) {
44756-
count = 0;
44757-
break;
44758-
}
44759-
}
4476044713

44761-
/* if not and the object implements Countable we call its count() method */
44762-
if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
44763-
zval retval;
44714+
if ((IS_CV & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
44715+
op1 = Z_REFVAL_P(op1);
44716+
}
4476444717

44765-
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
44766-
count = zval_get_long(&retval);
44767-
zval_ptr_dtor(&retval);
44768-
break;
44769-
}
44718+
if (Z_TYPE_P(op1) <= IS_NULL) {
44719+
/* Intentionally not converted to an exception */
44720+
zend_error(E_DEPRECATED, "%s(): Passing null is deprecated", opline->extended_value ? "sizeof" : "count");
44721+
if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
44722+
ZVAL_UNDEFINED_OP1();
44723+
}
44724+
count = 0;
44725+
} else if (Z_TYPE_P(op1) == IS_ARRAY) {
44726+
count = zend_array_count(Z_ARRVAL_P(op1));
44727+
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
44728+
zend_object *zobj = Z_OBJ_P(op1);
44729+
/* if the object implements Countable we call its count() method */
44730+
if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
44731+
zval retval;
4477044732

44771-
/* If There's no handler and it doesn't implement Countable then add a warning */
44772-
count = 1;
44773-
} else if ((IS_CV & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
44774-
op1 = Z_REFVAL_P(op1);
44775-
continue;
44776-
} else if (Z_TYPE_P(op1) <= IS_NULL) {
44777-
if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
44778-
ZVAL_UNDEFINED_OP1();
44779-
}
44780-
count = 0;
44733+
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
44734+
count = zval_get_long(&retval);
44735+
zval_ptr_dtor(&retval);
4478144736
} else {
44782-
count = 1;
44737+
zend_type_error("Parameter must be an array or an object that implements Countable");
4478344738
}
44784-
zend_error(E_WARNING, "%s(): Parameter must be an array or an object that implements Countable", opline->extended_value ? "sizeof" : "count");
44785-
break;
44739+
} else {
44740+
zend_type_error("Parameter must be an array or an object that implements Countable");
4478644741
}
4478744742

4478844743
ZVAL_LONG(EX_VAR(opline->result.var), count);

0 commit comments

Comments
 (0)