Skip to content

Commit cbece37

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.
1 parent 53e0331 commit cbece37

22 files changed

+799
-832
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: 28 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8442,53 +8442,41 @@ ZEND_VM_COLD_CONST_HANDLER(190, ZEND_COUNT, CONST|TMPVAR|CV, UNUSED)
84428442
{
84438443
USE_OPLINE
84448444
zval *op1;
8445-
zend_long count;
8445+
zend_long count = 1;
84468446

84478447
SAVE_OPLINE();
84488448
op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
8449-
while (1) {
8450-
if (Z_TYPE_P(op1) == IS_ARRAY) {
8451-
count = zend_array_count(Z_ARRVAL_P(op1));
8452-
break;
8453-
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
8454-
zend_object *zobj = Z_OBJ_P(op1);
8455-
8456-
/* first, we check if the handler is defined */
8457-
if (zobj->handlers->count_elements) {
8458-
if (SUCCESS == zobj->handlers->count_elements(zobj, &count)) {
8459-
break;
8460-
}
8461-
if (UNEXPECTED(EG(exception))) {
8462-
count = 0;
8463-
break;
8464-
}
8465-
}
84668449

8467-
/* if not and the object implements Countable we call its count() method */
8468-
if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
8469-
zval retval;
8470-
8471-
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
8472-
count = zval_get_long(&retval);
8473-
zval_ptr_dtor(&retval);
8474-
break;
8475-
}
8450+
if ((OP1_TYPE & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
8451+
op1 = Z_REFVAL_P(op1);
8452+
}
84768453

8477-
/* If There's no handler and it doesn't implement Countable then add a warning */
8478-
count = 1;
8479-
} else if ((OP1_TYPE & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
8480-
op1 = Z_REFVAL_P(op1);
8481-
continue;
8482-
} else if (Z_TYPE_P(op1) <= IS_NULL) {
8483-
if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
8484-
ZVAL_UNDEFINED_OP1();
8485-
}
8486-
count = 0;
8454+
if (Z_TYPE_P(op1) <= IS_NULL) {
8455+
/* Intentionally not converted to an exception */
8456+
zend_error(E_DEPRECATED, "%s(): Passing null is deprecated", opline->extended_value ? "sizeof" : "count");
8457+
if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
8458+
ZVAL_UNDEFINED_OP1();
8459+
}
8460+
count = 0;
8461+
} else if (Z_TYPE_P(op1) == IS_ARRAY) {
8462+
count = zend_array_count(Z_ARRVAL_P(op1));
8463+
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
8464+
zend_object *zobj = Z_OBJ_P(op1);
8465+
/* First, check if the handler is defined as it is faster */
8466+
if (zobj->handlers->count_elements) {
8467+
zobj->handlers->count_elements(zobj, &count);
8468+
/* Otherwise check if the object implements Countable and call its count() method */
8469+
} else if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
8470+
zval retval;
8471+
8472+
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
8473+
count = zval_get_long(&retval);
8474+
zval_ptr_dtor(&retval);
84878475
} else {
8488-
count = 1;
8476+
zend_type_error("Parameter must be an array or an object that implements Countable");
84898477
}
8490-
zend_error(E_WARNING, "%s(): Parameter must be an array or an object that implements Countable", opline->extended_value ? "sizeof" : "count");
8491-
break;
8478+
} else {
8479+
zend_type_error("Parameter must be an array or an object that implements Countable");
84928480
}
84938481

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

Zend/zend_vm_execute.h

Lines changed: 81 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -9259,53 +9259,41 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COUNT_SPEC_CONST_
92599259
{
92609260
USE_OPLINE
92619261
zval *op1;
9262-
zend_long count;
9262+
zend_long count = 1;
92639263

92649264
SAVE_OPLINE();
92659265
op1 = RT_CONSTANT(opline, opline->op1);
9266-
while (1) {
9267-
if (Z_TYPE_P(op1) == IS_ARRAY) {
9268-
count = zend_array_count(Z_ARRVAL_P(op1));
9269-
break;
9270-
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
9271-
zend_object *zobj = Z_OBJ_P(op1);
9272-
9273-
/* first, we check if the handler is defined */
9274-
if (zobj->handlers->count_elements) {
9275-
if (SUCCESS == zobj->handlers->count_elements(zobj, &count)) {
9276-
break;
9277-
}
9278-
if (UNEXPECTED(EG(exception))) {
9279-
count = 0;
9280-
break;
9281-
}
9282-
}
92839266

9284-
/* if not and the object implements Countable we call its count() method */
9285-
if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
9286-
zval retval;
9267+
if ((IS_CONST & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
9268+
op1 = Z_REFVAL_P(op1);
9269+
}
92879270

9288-
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
9289-
count = zval_get_long(&retval);
9290-
zval_ptr_dtor(&retval);
9291-
break;
9292-
}
9271+
if (Z_TYPE_P(op1) <= IS_NULL) {
9272+
/* Intentionally not converted to an exception */
9273+
zend_error(E_DEPRECATED, "%s(): Passing null is deprecated", opline->extended_value ? "sizeof" : "count");
9274+
if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
9275+
ZVAL_UNDEFINED_OP1();
9276+
}
9277+
count = 0;
9278+
} else if (Z_TYPE_P(op1) == IS_ARRAY) {
9279+
count = zend_array_count(Z_ARRVAL_P(op1));
9280+
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
9281+
zend_object *zobj = Z_OBJ_P(op1);
9282+
/* First, check if the handler is defined as it is faster */
9283+
if (zobj->handlers->count_elements) {
9284+
zobj->handlers->count_elements(zobj, &count);
9285+
/* Otherwise check if the object implements Countable and call its count() method */
9286+
} else if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
9287+
zval retval;
92939288

9294-
/* If There's no handler and it doesn't implement Countable then add a warning */
9295-
count = 1;
9296-
} else if ((IS_CONST & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
9297-
op1 = Z_REFVAL_P(op1);
9298-
continue;
9299-
} else if (Z_TYPE_P(op1) <= IS_NULL) {
9300-
if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
9301-
ZVAL_UNDEFINED_OP1();
9302-
}
9303-
count = 0;
9289+
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
9290+
count = zval_get_long(&retval);
9291+
zval_ptr_dtor(&retval);
93049292
} else {
9305-
count = 1;
9293+
zend_type_error("Parameter must be an array or an object that implements Countable");
93069294
}
9307-
zend_error(E_WARNING, "%s(): Parameter must be an array or an object that implements Countable", opline->extended_value ? "sizeof" : "count");
9308-
break;
9295+
} else {
9296+
zend_type_error("Parameter must be an array or an object that implements Countable");
93099297
}
93109298

93119299
ZVAL_LONG(EX_VAR(opline->result.var), count);
@@ -16338,53 +16326,41 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COUNT_SPEC_TMPVAR_UNUSED_HANDL
1633816326
{
1633916327
USE_OPLINE
1634016328
zval *op1;
16341-
zend_long count;
16329+
zend_long count = 1;
1634216330

1634316331
SAVE_OPLINE();
1634416332
op1 = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC);
16345-
while (1) {
16346-
if (Z_TYPE_P(op1) == IS_ARRAY) {
16347-
count = zend_array_count(Z_ARRVAL_P(op1));
16348-
break;
16349-
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
16350-
zend_object *zobj = Z_OBJ_P(op1);
1635116333

16352-
/* first, we check if the handler is defined */
16353-
if (zobj->handlers->count_elements) {
16354-
if (SUCCESS == zobj->handlers->count_elements(zobj, &count)) {
16355-
break;
16356-
}
16357-
if (UNEXPECTED(EG(exception))) {
16358-
count = 0;
16359-
break;
16360-
}
16361-
}
16362-
16363-
/* if not and the object implements Countable we call its count() method */
16364-
if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
16365-
zval retval;
16334+
if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
16335+
op1 = Z_REFVAL_P(op1);
16336+
}
1636616337

16367-
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
16368-
count = zval_get_long(&retval);
16369-
zval_ptr_dtor(&retval);
16370-
break;
16371-
}
16338+
if (Z_TYPE_P(op1) <= IS_NULL) {
16339+
/* Intentionally not converted to an exception */
16340+
zend_error(E_DEPRECATED, "%s(): Passing null is deprecated", opline->extended_value ? "sizeof" : "count");
16341+
if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
16342+
ZVAL_UNDEFINED_OP1();
16343+
}
16344+
count = 0;
16345+
} else if (Z_TYPE_P(op1) == IS_ARRAY) {
16346+
count = zend_array_count(Z_ARRVAL_P(op1));
16347+
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
16348+
zend_object *zobj = Z_OBJ_P(op1);
16349+
/* First, check if the handler is defined as it is faster */
16350+
if (zobj->handlers->count_elements) {
16351+
zobj->handlers->count_elements(zobj, &count);
16352+
/* Otherwise check if the object implements Countable and call its count() method */
16353+
} else if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
16354+
zval retval;
1637216355

16373-
/* If There's no handler and it doesn't implement Countable then add a warning */
16374-
count = 1;
16375-
} else if (((IS_TMP_VAR|IS_VAR) & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
16376-
op1 = Z_REFVAL_P(op1);
16377-
continue;
16378-
} else if (Z_TYPE_P(op1) <= IS_NULL) {
16379-
if ((IS_TMP_VAR|IS_VAR) == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
16380-
ZVAL_UNDEFINED_OP1();
16381-
}
16382-
count = 0;
16356+
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
16357+
count = zval_get_long(&retval);
16358+
zval_ptr_dtor(&retval);
1638316359
} else {
16384-
count = 1;
16360+
zend_type_error("Parameter must be an array or an object that implements Countable");
1638516361
}
16386-
zend_error(E_WARNING, "%s(): Parameter must be an array or an object that implements Countable", opline->extended_value ? "sizeof" : "count");
16387-
break;
16362+
} else {
16363+
zend_type_error("Parameter must be an array or an object that implements Countable");
1638816364
}
1638916365

1639016366
ZVAL_LONG(EX_VAR(opline->result.var), count);
@@ -45185,53 +45161,41 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COUNT_SPEC_CV_UNUSED_HANDLER(Z
4518545161
{
4518645162
USE_OPLINE
4518745163
zval *op1;
45188-
zend_long count;
45164+
zend_long count = 1;
4518945165

4519045166
SAVE_OPLINE();
4519145167
op1 = EX_VAR(opline->op1.var);
45192-
while (1) {
45193-
if (Z_TYPE_P(op1) == IS_ARRAY) {
45194-
count = zend_array_count(Z_ARRVAL_P(op1));
45195-
break;
45196-
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
45197-
zend_object *zobj = Z_OBJ_P(op1);
4519845168

45199-
/* first, we check if the handler is defined */
45200-
if (zobj->handlers->count_elements) {
45201-
if (SUCCESS == zobj->handlers->count_elements(zobj, &count)) {
45202-
break;
45203-
}
45204-
if (UNEXPECTED(EG(exception))) {
45205-
count = 0;
45206-
break;
45207-
}
45208-
}
45209-
45210-
/* if not and the object implements Countable we call its count() method */
45211-
if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
45212-
zval retval;
45169+
if ((IS_CV & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
45170+
op1 = Z_REFVAL_P(op1);
45171+
}
4521345172

45214-
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
45215-
count = zval_get_long(&retval);
45216-
zval_ptr_dtor(&retval);
45217-
break;
45218-
}
45173+
if (Z_TYPE_P(op1) <= IS_NULL) {
45174+
/* Intentionally not converted to an exception */
45175+
zend_error(E_DEPRECATED, "%s(): Passing null is deprecated", opline->extended_value ? "sizeof" : "count");
45176+
if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
45177+
ZVAL_UNDEFINED_OP1();
45178+
}
45179+
count = 0;
45180+
} else if (Z_TYPE_P(op1) == IS_ARRAY) {
45181+
count = zend_array_count(Z_ARRVAL_P(op1));
45182+
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
45183+
zend_object *zobj = Z_OBJ_P(op1);
45184+
/* First, check if the handler is defined as it is faster */
45185+
if (zobj->handlers->count_elements) {
45186+
zobj->handlers->count_elements(zobj, &count);
45187+
/* Otherwise check if the object implements Countable and call its count() method */
45188+
} else if (zend_class_implements_interface(zobj->ce, zend_ce_countable)) {
45189+
zval retval;
4521945190

45220-
/* If There's no handler and it doesn't implement Countable then add a warning */
45221-
count = 1;
45222-
} else if ((IS_CV & (IS_VAR|IS_CV)) != 0 && Z_TYPE_P(op1) == IS_REFERENCE) {
45223-
op1 = Z_REFVAL_P(op1);
45224-
continue;
45225-
} else if (Z_TYPE_P(op1) <= IS_NULL) {
45226-
if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(op1) == IS_UNDEF)) {
45227-
ZVAL_UNDEFINED_OP1();
45228-
}
45229-
count = 0;
45191+
zend_call_method_with_0_params(zobj, NULL, NULL, "count", &retval);
45192+
count = zval_get_long(&retval);
45193+
zval_ptr_dtor(&retval);
4523045194
} else {
45231-
count = 1;
45195+
zend_type_error("Parameter must be an array or an object that implements Countable");
4523245196
}
45233-
zend_error(E_WARNING, "%s(): Parameter must be an array or an object that implements Countable", opline->extended_value ? "sizeof" : "count");
45234-
break;
45197+
} else {
45198+
zend_type_error("Parameter must be an array or an object that implements Countable");
4523545199
}
4523645200

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

0 commit comments

Comments
 (0)