Skip to content

Commit b5eb544

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 24fb724 commit b5eb544

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
@@ -8369,53 +8369,38 @@ ZEND_VM_COLD_CONST_HANDLER(190, ZEND_COUNT, CONST|TMPVAR|CV, UNUSED)
83698369
{
83708370
USE_OPLINE
83718371
zval *op1;
8372-
zend_long count;
8372+
zend_long count = 1;
83738373

83748374
SAVE_OPLINE();
83758375
op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
8376-
while (1) {
8377-
if (Z_TYPE_P(op1) == IS_ARRAY) {
8378-
count = zend_array_count(Z_ARRVAL_P(op1));
8379-
break;
8380-
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
8381-
zend_object *zobj = Z_OBJ_P(op1);
8382-
8383-
/* first, we check if the handler is defined */
8384-
if (zobj->handlers->count_elements) {
8385-
if (SUCCESS == zobj->handlers->count_elements(zobj, &count)) {
8386-
break;
8387-
}
8388-
if (UNEXPECTED(EG(exception))) {
8389-
count = 0;
8390-
break;
8391-
}
8392-
}
8393-
8394-
/* if not and the object implements Countable we call its count() method */
8395-
if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
8396-
zval retval;
83978376

8398-
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
8399-
count = zval_get_long(&retval);
8400-
zval_ptr_dtor(&retval);
8401-
break;
8402-
}
8377+
if ((OP1_TYPE & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
8378+
op1 = Z_REFVAL_P(op1);
8379+
}
84038380

8404-
/* If There's no handler and it doesn't implement Countable then add a warning */
8405-
count = 1;
8406-
} else if ((OP1_TYPE & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
8407-
op1 = Z_REFVAL_P(op1);
8408-
continue;
8409-
} else if (Z_TYPE_P(op1) <= IS_NULL) {
8410-
if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
8411-
ZVAL_UNDEFINED_OP1();
8412-
}
8413-
count = 0;
8381+
if (Z_TYPE_P(op1) <= IS_NULL) {
8382+
/* Intentionally not converted to an exception */
8383+
zend_error(E_DEPRECATED, "%s(): Passing null is deprecated", opline->extended_value ? "sizeof" : "count");
8384+
if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
8385+
ZVAL_UNDEFINED_OP1();
8386+
}
8387+
count = 0;
8388+
} else if (Z_TYPE_P(op1) == IS_ARRAY) {
8389+
count = zend_array_count(Z_ARRVAL_P(op1));
8390+
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
8391+
zend_object *zobj = Z_OBJ_P(op1);
8392+
/* if the object implements Countable we call its count() method */
8393+
if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
8394+
zval retval;
8395+
8396+
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
8397+
count = zval_get_long(&retval);
8398+
zval_ptr_dtor(&retval);
84148399
} else {
8415-
count = 1;
8400+
zend_type_error("Parameter must be an array or an object that implements Countable");
84168401
}
8417-
zend_error(E_WARNING, "%s(): Parameter must be an array or an object that implements Countable", opline->extended_value ? "sizeof" : "count");
8418-
break;
8402+
} else {
8403+
zend_type_error("Parameter must be an array or an object that implements Countable");
84198404
}
84208405

84218406
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
@@ -9211,53 +9211,38 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COUNT_SPEC_CONST_
92119211
{
92129212
USE_OPLINE
92139213
zval *op1;
9214-
zend_long count;
9214+
zend_long count = 1;
92159215

92169216
SAVE_OPLINE();
92179217
op1 = RT_CONSTANT(opline, opline->op1);
9218-
while (1) {
9219-
if (Z_TYPE_P(op1) == IS_ARRAY) {
9220-
count = zend_array_count(Z_ARRVAL_P(op1));
9221-
break;
9222-
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
9223-
zend_object *zobj = Z_OBJ_P(op1);
92249218

9225-
/* first, we check if the handler is defined */
9226-
if (zobj->handlers->count_elements) {
9227-
if (SUCCESS == zobj->handlers->count_elements(zobj, &count)) {
9228-
break;
9229-
}
9230-
if (UNEXPECTED(EG(exception))) {
9231-
count = 0;
9232-
break;
9233-
}
9234-
}
9235-
9236-
/* if not and the object implements Countable we call its count() method */
9237-
if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
9238-
zval retval;
9219+
if ((IS_CONST & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
9220+
op1 = Z_REFVAL_P(op1);
9221+
}
92399222

9240-
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
9241-
count = zval_get_long(&retval);
9242-
zval_ptr_dtor(&retval);
9243-
break;
9244-
}
9223+
if (Z_TYPE_P(op1) <= IS_NULL) {
9224+
/* Intentionally not converted to an exception */
9225+
zend_error(E_DEPRECATED, "%s(): Passing null is deprecated", opline->extended_value ? "sizeof" : "count");
9226+
if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
9227+
ZVAL_UNDEFINED_OP1();
9228+
}
9229+
count = 0;
9230+
} else if (Z_TYPE_P(op1) == IS_ARRAY) {
9231+
count = zend_array_count(Z_ARRVAL_P(op1));
9232+
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
9233+
zend_object *zobj = Z_OBJ_P(op1);
9234+
/* if the object implements Countable we call its count() method */
9235+
if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
9236+
zval retval;
92459237

9246-
/* If There's no handler and it doesn't implement Countable then add a warning */
9247-
count = 1;
9248-
} else if ((IS_CONST & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
9249-
op1 = Z_REFVAL_P(op1);
9250-
continue;
9251-
} else if (Z_TYPE_P(op1) <= IS_NULL) {
9252-
if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
9253-
ZVAL_UNDEFINED_OP1();
9254-
}
9255-
count = 0;
9238+
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
9239+
count = zval_get_long(&retval);
9240+
zval_ptr_dtor(&retval);
92569241
} else {
9257-
count = 1;
9242+
zend_type_error("Parameter must be an array or an object that implements Countable");
92589243
}
9259-
zend_error(E_WARNING, "%s(): Parameter must be an array or an object that implements Countable", opline->extended_value ? "sizeof" : "count");
9260-
break;
9244+
} else {
9245+
zend_type_error("Parameter must be an array or an object that implements Countable");
92619246
}
92629247

92639248
ZVAL_LONG(EX_VAR(opline->result.var), count);
@@ -16233,53 +16218,38 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COUNT_SPEC_TMPVAR_UNUSED_HANDL
1623316218
{
1623416219
USE_OPLINE
1623516220
zval *op1;
16236-
zend_long count;
16221+
zend_long count = 1;
1623716222

1623816223
SAVE_OPLINE();
1623916224
op1 = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC);
16240-
while (1) {
16241-
if (Z_TYPE_P(op1) == IS_ARRAY) {
16242-
count = zend_array_count(Z_ARRVAL_P(op1));
16243-
break;
16244-
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
16245-
zend_object *zobj = Z_OBJ_P(op1);
16246-
16247-
/* first, we check if the handler is defined */
16248-
if (zobj->handlers->count_elements) {
16249-
if (SUCCESS == zobj->handlers->count_elements(zobj, &count)) {
16250-
break;
16251-
}
16252-
if (UNEXPECTED(EG(exception))) {
16253-
count = 0;
16254-
break;
16255-
}
16256-
}
1625716225

16258-
/* if not and the object implements Countable we call its count() method */
16259-
if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
16260-
zval retval;
16226+
if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
16227+
op1 = Z_REFVAL_P(op1);
16228+
}
1626116229

16262-
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
16263-
count = zval_get_long(&retval);
16264-
zval_ptr_dtor(&retval);
16265-
break;
16266-
}
16230+
if (Z_TYPE_P(op1) <= IS_NULL) {
16231+
/* Intentionally not converted to an exception */
16232+
zend_error(E_DEPRECATED, "%s(): Passing null is deprecated", opline->extended_value ? "sizeof" : "count");
16233+
if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
16234+
ZVAL_UNDEFINED_OP1();
16235+
}
16236+
count = 0;
16237+
} else if (Z_TYPE_P(op1) == IS_ARRAY) {
16238+
count = zend_array_count(Z_ARRVAL_P(op1));
16239+
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
16240+
zend_object *zobj = Z_OBJ_P(op1);
16241+
/* if the object implements Countable we call its count() method */
16242+
if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
16243+
zval retval;
1626716244

16268-
/* If There's no handler and it doesn't implement Countable then add a warning */
16269-
count = 1;
16270-
} else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
16271-
op1 = Z_REFVAL_P(op1);
16272-
continue;
16273-
} else if (Z_TYPE_P(op1) <= IS_NULL) {
16274-
if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
16275-
ZVAL_UNDEFINED_OP1();
16276-
}
16277-
count = 0;
16245+
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
16246+
count = zval_get_long(&retval);
16247+
zval_ptr_dtor(&retval);
1627816248
} else {
16279-
count = 1;
16249+
zend_type_error("Parameter must be an array or an object that implements Countable");
1628016250
}
16281-
zend_error(E_WARNING, "%s(): Parameter must be an array or an object that implements Countable", opline->extended_value ? "sizeof" : "count");
16282-
break;
16251+
} else {
16252+
zend_type_error("Parameter must be an array or an object that implements Countable");
1628316253
}
1628416254

1628516255
ZVAL_LONG(EX_VAR(opline->result.var), count);
@@ -44797,53 +44767,38 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COUNT_SPEC_CV_UNUSED_HANDLER(Z
4479744767
{
4479844768
USE_OPLINE
4479944769
zval *op1;
44800-
zend_long count;
44770+
zend_long count = 1;
4480144771

4480244772
SAVE_OPLINE();
4480344773
op1 = EX_VAR(opline->op1.var);
44804-
while (1) {
44805-
if (Z_TYPE_P(op1) == IS_ARRAY) {
44806-
count = zend_array_count(Z_ARRVAL_P(op1));
44807-
break;
44808-
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
44809-
zend_object *zobj = Z_OBJ_P(op1);
44810-
44811-
/* first, we check if the handler is defined */
44812-
if (zobj->handlers->count_elements) {
44813-
if (SUCCESS == zobj->handlers->count_elements(zobj, &count)) {
44814-
break;
44815-
}
44816-
if (UNEXPECTED(EG(exception))) {
44817-
count = 0;
44818-
break;
44819-
}
44820-
}
4482144774

44822-
/* if not and the object implements Countable we call its count() method */
44823-
if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
44824-
zval retval;
44775+
if ((IS_CV & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
44776+
op1 = Z_REFVAL_P(op1);
44777+
}
4482544778

44826-
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
44827-
count = zval_get_long(&retval);
44828-
zval_ptr_dtor(&retval);
44829-
break;
44830-
}
44779+
if (Z_TYPE_P(op1) <= IS_NULL) {
44780+
/* Intentionally not converted to an exception */
44781+
zend_error(E_DEPRECATED, "%s(): Passing null is deprecated", opline->extended_value ? "sizeof" : "count");
44782+
if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
44783+
ZVAL_UNDEFINED_OP1();
44784+
}
44785+
count = 0;
44786+
} else if (Z_TYPE_P(op1) == IS_ARRAY) {
44787+
count = zend_array_count(Z_ARRVAL_P(op1));
44788+
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
44789+
zend_object *zobj = Z_OBJ_P(op1);
44790+
/* if the object implements Countable we call its count() method */
44791+
if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
44792+
zval retval;
4483144793

44832-
/* If There's no handler and it doesn't implement Countable then add a warning */
44833-
count = 1;
44834-
} else if ((IS_CV & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
44835-
op1 = Z_REFVAL_P(op1);
44836-
continue;
44837-
} else if (Z_TYPE_P(op1) <= IS_NULL) {
44838-
if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
44839-
ZVAL_UNDEFINED_OP1();
44840-
}
44841-
count = 0;
44794+
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
44795+
count = zval_get_long(&retval);
44796+
zval_ptr_dtor(&retval);
4484244797
} else {
44843-
count = 1;
44798+
zend_type_error("Parameter must be an array or an object that implements Countable");
4484444799
}
44845-
zend_error(E_WARNING, "%s(): Parameter must be an array or an object that implements Countable", opline->extended_value ? "sizeof" : "count");
44846-
break;
44800+
} else {
44801+
zend_type_error("Parameter must be an array or an object that implements Countable");
4484744802
}
4484844803

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

0 commit comments

Comments
 (0)