Skip to content

Add array|int and object-of-class|int types to stubs #6081

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 13 commits into from
32 changes: 29 additions & 3 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -221,15 +221,21 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_error(int error_code,
case ZPP_ERROR_WRONG_CLASS_OR_NULL:
zend_wrong_parameter_class_or_null_error(num, name, arg);
break;
case ZPP_ERROR_WRONG_ARG:
zend_wrong_parameter_type_error(num, expected_type, arg);
break;
case ZPP_ERROR_WRONG_CLASS_OR_STRING:
zend_wrong_parameter_class_or_string_error(num, name, arg);
break;
case ZPP_ERROR_WRONG_CLASS_OR_STRING_OR_NULL:
zend_wrong_parameter_class_or_string_or_null_error(num, name, arg);
break;
case ZPP_ERROR_WRONG_CLASS_OR_LONG:
zend_wrong_parameter_class_or_long_error(num, name, arg);
break;
case ZPP_ERROR_WRONG_CLASS_OR_LONG_OR_NULL:
zend_wrong_parameter_class_or_long_or_null_error(num, name, arg);
break;
case ZPP_ERROR_WRONG_ARG:
zend_wrong_parameter_type_error(num, expected_type, arg);
break;
case ZPP_ERROR_UNEXPECTED_EXTRA_NAMED:
zend_unexpected_extra_named_error();
break;
Expand Down Expand Up @@ -280,6 +286,26 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_null_error(u
}
/* }}} */

ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_long_error(uint32_t num, const char *name, zval *arg) /* {{{ */
{
if (EG(exception)) {
return;
}

zend_argument_type_error(num, "must be of type %s|int, %s given", name, zend_zval_type_name(arg));
}
/* }}} */

ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_long_or_null_error(uint32_t num, const char *name, zval *arg) /* {{{ */
{
if (EG(exception)) {
return;
}

zend_argument_type_error(num, "must be of type %s|int|null, %s given", name, zend_zval_type_name(arg));
}
/* }}} */

ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_string_error(uint32_t num, const char *name, zval *arg) /* {{{ */
{
if (EG(exception)) {
Expand Down
114 changes: 109 additions & 5 deletions Zend/zend_API.h
Original file line number Diff line number Diff line change
Expand Up @@ -1209,6 +1209,8 @@ static zend_always_inline zval *zend_try_array_init(zval *zv)
_(Z_EXPECTED_STRING_OR_NULL, "of type ?string") \
_(Z_EXPECTED_ARRAY, "of type array") \
_(Z_EXPECTED_ARRAY_OR_NULL, "of type ?array") \
_(Z_EXPECTED_ARRAY_OR_LONG, "of type array|int") \
_(Z_EXPECTED_ARRAY_OR_LONG_OR_NULL, "of type array|int|null") \
_(Z_EXPECTED_ITERABLE, "of type iterable") \
_(Z_EXPECTED_ITERABLE_OR_NULL, "of type ?iterable") \
_(Z_EXPECTED_FUNC, "a valid callback") \
Expand Down Expand Up @@ -1248,6 +1250,8 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_error(int error_code,
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_type_error(uint32_t num, zend_expected_type expected_type, zval *arg);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_error(uint32_t num, const char *name, zval *arg);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_null_error(uint32_t num, const char *name, zval *arg);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_long_error(uint32_t num, const char *name, zval *arg);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_long_or_null_error(uint32_t num, const char *name, zval *arg);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_string_error(uint32_t num, const char *name, zval *arg);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_parameter_class_or_string_or_null_error(uint32_t num, const char *name, zval *arg);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_wrong_callback_error(uint32_t num, char *error);
Expand All @@ -1261,11 +1265,13 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
#define ZPP_ERROR_WRONG_CALLBACK 2
#define ZPP_ERROR_WRONG_CLASS 3
#define ZPP_ERROR_WRONG_CLASS_OR_NULL 4
#define ZPP_ERROR_WRONG_ARG 5
#define ZPP_ERROR_WRONG_COUNT 6
#define ZPP_ERROR_WRONG_CLASS_OR_STRING 7
#define ZPP_ERROR_WRONG_CLASS_OR_STRING_OR_NULL 8
#define ZPP_ERROR_UNEXPECTED_EXTRA_NAMED 9
#define ZPP_ERROR_WRONG_CLASS_OR_STRING 5
#define ZPP_ERROR_WRONG_CLASS_OR_STRING_OR_NULL 6
#define ZPP_ERROR_WRONG_CLASS_OR_LONG 7
#define ZPP_ERROR_WRONG_CLASS_OR_LONG_OR_NULL 8
#define ZPP_ERROR_WRONG_ARG 9
#define ZPP_ERROR_WRONG_COUNT 10
#define ZPP_ERROR_UNEXPECTED_EXTRA_NAMED 11

#define ZEND_PARSE_PARAMETERS_START_EX(flags, min_num_args, max_num_args) do { \
const int _flags = (flags); \
Expand Down Expand Up @@ -1530,6 +1536,20 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
#define Z_PARAM_ARRAY_HT_OR_NULL(dest) \
Z_PARAM_ARRAY_HT_EX(dest, 1, 0)

#define Z_PARAM_ARRAY_HT_OR_LONG_EX(dest_ht, dest_long, is_null, allow_null) \
Z_PARAM_PROLOGUE(0, 0); \
if (UNEXPECTED(!zend_parse_arg_array_ht_or_long(_arg, &dest_ht, &dest_long, &is_null, allow_null))) { \
_expected_type = allow_null ? Z_EXPECTED_ARRAY_OR_LONG_OR_NULL : Z_EXPECTED_ARRAY_OR_LONG; \
_error_code = ZPP_ERROR_WRONG_ARG; \
break; \
}

#define Z_PARAM_ARRAY_HT_OR_LONG(dest_ht, dest_long) \
Z_PARAM_ARRAY_HT_OR_LONG_EX(dest_ht, dest_long, _dummy, 0)

#define Z_PARAM_ARRAY_HT_OR_LONG_OR_NULL(dest_ht, dest_long, is_null) \
Z_PARAM_ARRAY_HT_OR_LONG_EX(dest_ht, dest_long, is_null, 1)

/* old "H" */
#define Z_PARAM_ARRAY_OR_OBJECT_HT_EX2(dest, check_null, deref, separate) \
Z_PARAM_PROLOGUE(deref, separate); \
Expand Down Expand Up @@ -1638,6 +1658,44 @@ ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *
#define Z_PARAM_OBJECT_OF_CLASS_OR_NULL(dest, _ce) \
Z_PARAM_OBJECT_OF_CLASS_EX(dest, _ce, 1, 0)

/* The same as Z_PARAM_OBJECT_OF_CLASS_EX2 except that dest is a zend_object rather than a zval */
#define Z_PARAM_OBJ_OF_CLASS_EX2(dest, _ce, check_null, deref, separate) \
Z_PARAM_PROLOGUE(deref, separate); \
if (UNEXPECTED(!zend_parse_arg_obj(_arg, &dest, _ce, check_null))) { \
if (_ce) { \
_error = ZSTR_VAL((_ce)->name); \
_error_code = check_null ? ZPP_ERROR_WRONG_CLASS_OR_NULL : ZPP_ERROR_WRONG_CLASS; \
break; \
} else { \
_expected_type = check_null ? Z_EXPECTED_OBJECT_OR_NULL : Z_EXPECTED_OBJECT; \
_error_code = ZPP_ERROR_WRONG_ARG; \
break; \
} \
}

#define Z_PARAM_OBJ_OF_CLASS_EX(dest, _ce, check_null, separate) \
Z_PARAM_OBJ_OF_CLASS_EX2(dest, _ce, check_null, separate, separate)

#define Z_PARAM_OBJ_OF_CLASS(dest, _ce) \
Z_PARAM_OBJ_OF_CLASS_EX(dest, _ce, 0, 0)

#define Z_PARAM_OBJ_OF_CLASS_OR_NULL(dest, _ce) \
Z_PARAM_OBJ_OF_CLASS_EX(dest, _ce, 1, 0)

#define Z_PARAM_OBJ_OF_CLASS_OR_LONG_EX(dest_obj, _ce, dest_long, is_null, allow_null) \
Z_PARAM_PROLOGUE(0, 0); \
if (UNEXPECTED(!zend_parse_arg_obj_or_long(_arg, &dest_obj, _ce, &dest_long, &is_null, allow_null))) { \
_error = ZSTR_VAL((_ce)->name); \
_error_code = allow_null ? ZPP_ERROR_WRONG_CLASS_OR_LONG_OR_NULL : ZPP_ERROR_WRONG_CLASS_OR_LONG; \
break; \
}

#define Z_PARAM_OBJ_OF_CLASS_OR_LONG(dest_obj, _ce, dest_long) \
Z_PARAM_OBJ_OF_CLASS_OR_LONG_EX(dest_obj, _ce, dest_long, _dummy, 0)

#define Z_PARAM_OBJ_OF_CLASS_OR_LONG_OR_NULL(dest_obj, _ce, dest_long, is_null) \
Z_PARAM_OBJ_OF_CLASS_OR_LONG_EX(dest_obj, _ce, dest_long, is_null, 1)

/* old "p" */
#define Z_PARAM_PATH_EX2(dest, dest_len, check_null, deref, separate) \
Z_PARAM_PROLOGUE(deref, separate); \
Expand Down Expand Up @@ -1991,6 +2049,29 @@ static zend_always_inline bool zend_parse_arg_array_ht(zval *arg, HashTable **de
return 1;
}

static zend_always_inline bool zend_parse_arg_array_ht_or_long(
zval *arg, HashTable **dest_ht, zend_long *dest_long, zend_bool *is_null, bool allow_null
) {
if (allow_null) {
*is_null = 0;
}

if (EXPECTED(Z_TYPE_P(arg) == IS_ARRAY)) {
*dest_ht = Z_ARRVAL_P(arg);
} else if (EXPECTED(Z_TYPE_P(arg) == IS_LONG)) {
*dest_ht = NULL;
*dest_long = Z_LVAL_P(arg);
} else if (allow_null && EXPECTED(Z_TYPE_P(arg) == IS_NULL)) {
*dest_ht = NULL;
*is_null = 1;
} else {
*dest_ht = NULL;
return zend_parse_arg_long_slow(arg, dest_long);
}

return 1;
}

static zend_always_inline bool zend_parse_arg_object(zval *arg, zval **dest, zend_class_entry *ce, bool check_null)
{
if (EXPECTED(Z_TYPE_P(arg) == IS_OBJECT) &&
Expand All @@ -2017,6 +2098,29 @@ static zend_always_inline bool zend_parse_arg_obj(zval *arg, zend_object **dest,
return 1;
}

static zend_always_inline bool zend_parse_arg_obj_or_long(
zval *arg, zend_object **dest_obj, zend_class_entry *ce, zend_long *dest_long, zend_bool *is_null, bool allow_null
) {
if (allow_null) {
*is_null = 0;
}

if (EXPECTED(Z_TYPE_P(arg) == IS_OBJECT) && EXPECTED(instanceof_function(Z_OBJCE_P(arg), ce) != 0)) {
*dest_obj = Z_OBJ_P(arg);
} else if (EXPECTED(Z_TYPE_P(arg) == IS_LONG)) {
*dest_obj = NULL;
*dest_long = Z_LVAL_P(arg);
} else if (allow_null && EXPECTED(Z_TYPE_P(arg) == IS_NULL)) {
*dest_obj = NULL;
*is_null = 1;
} else {
*dest_obj = NULL;
return zend_parse_arg_long_slow(arg, dest_long);
}

return 1;
}

static zend_always_inline bool zend_parse_arg_resource(zval *arg, zval **dest, bool check_null)
{
if (EXPECTED(Z_TYPE_P(arg) == IS_RESOURCE)) {
Expand Down
35 changes: 25 additions & 10 deletions ext/date/php_date.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "ext/standard/php_math.h"
#include "php_date.h"
#include "zend_interfaces.h"
#include "zend_exceptions.h"
#include "lib/timelib.h"
#include "lib/timelib_private.h"
#ifndef PHP_WIN32
Expand Down Expand Up @@ -4117,13 +4118,11 @@ PHP_METHOD(DatePeriod, __construct)
timelib_time *clone;
zend_error_handling error_handling;

zend_replace_error_handling(EH_THROW, NULL, &error_handling);
if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "OOl|l", &start, date_ce_interface, &interval, date_ce_interval, &recurrences, &options) == FAILURE) {
if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "OOO|l", &start, date_ce_interface, &interval, date_ce_interval, &end, date_ce_interface, &options) == FAILURE) {
if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "s|l", &isostr, &isostr_len, &options) == FAILURE) {
php_error_docref(NULL, E_WARNING, "This constructor accepts either (DateTimeInterface, DateInterval, int) OR (DateTimeInterface, DateInterval, DateTime) OR (string) as arguments.");
zend_restore_error_handling(&error_handling);
return;
zend_type_error("DatePeriod::__construct() accepts (DateTimeInterface, DateInterval, int [, int]), or (DateTimeInterface, DateInterval, DateTime [, int]), or (string [, int]) as arguments");
RETURN_THROWS();
}
}
}
Expand All @@ -4132,15 +4131,30 @@ PHP_METHOD(DatePeriod, __construct)
dpobj->current = NULL;

if (isostr) {
zend_replace_error_handling(EH_THROW, NULL, &error_handling);
date_period_initialize(&(dpobj->start), &(dpobj->end), &(dpobj->interval), &recurrences, isostr, isostr_len);
zend_restore_error_handling(&error_handling);
if (EG(exception)) {
RETURN_THROWS();
}

if (dpobj->start == NULL) {
php_error_docref(NULL, E_WARNING, "The ISO interval '%s' did not contain a start date.", isostr);
zend_string *func = get_active_function_or_method_name();
zend_throw_error(zend_ce_exception, "%s(): ISO interval must contain a start date, \"%s\" given", ZSTR_VAL(func), isostr);
zend_string_release(func);
RETURN_THROWS();
}
if (dpobj->interval == NULL) {
php_error_docref(NULL, E_WARNING, "The ISO interval '%s' did not contain an interval.", isostr);
zend_string *func = get_active_function_or_method_name();
zend_throw_error(zend_ce_exception, "%s(): ISO interval must contain an interval, \"%s\" given", ZSTR_VAL(func), isostr);
zend_string_release(func);
RETURN_THROWS();
}
if (dpobj->end == NULL && recurrences == 0) {
php_error_docref(NULL, E_WARNING, "The ISO interval '%s' did not contain an end date or a recurrence count.", isostr);
zend_string *func = get_active_function_or_method_name();
zend_throw_error(zend_ce_exception, "%s(): ISO interval must contain an end date or a recurrence count, \"%s\" given", ZSTR_VAL(func), isostr);
zend_string_release(func);
RETURN_THROWS();
}

if (dpobj->start) {
Expand Down Expand Up @@ -4179,7 +4193,10 @@ PHP_METHOD(DatePeriod, __construct)
}

if (dpobj->end == NULL && recurrences < 1) {
php_error_docref(NULL, E_WARNING, "The recurrence count '%d' is invalid. Needs to be > 0", (int) recurrences);
zend_string *func = get_active_function_or_method_name();
zend_throw_error(zend_ce_exception, "%s(): Recurrence count must be greater than 0", ZSTR_VAL(func));
zend_string_release(func);
RETURN_THROWS();
}

/* options */
Expand All @@ -4189,8 +4206,6 @@ PHP_METHOD(DatePeriod, __construct)
dpobj->recurrences = recurrences + dpobj->include_start_date;

dpobj->initialized = 1;

zend_restore_error_handling(&error_handling);
}
/* }}} */

Expand Down
15 changes: 8 additions & 7 deletions ext/date/tests/DatePeriod_wrong_constructor.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ Havard Eide <[email protected]>
date.timezone=UTC
--FILE--
<?php
new DatePeriod();

try {
new DatePeriod();
} catch (TypeError $exception) {
echo $exception->getMessage() . "\n";
}
?>
--EXPECTF--
Fatal error: Uncaught Exception: DatePeriod::__construct(): This constructor accepts either (DateTimeInterface, DateInterval, int) OR (DateTimeInterface, DateInterval, DateTime) OR (string) as arguments. in %s:%d
Stack trace:
#0 %s(%d): DatePeriod->__construct()
#1 {main}
thrown in %s on line %d
--EXPECT--
DatePeriod::__construct() accepts (DateTimeInterface, DateInterval, int [, int]), or (DateTimeInterface, DateInterval, DateTime [, int]), or (string [, int]) as arguments
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ try {
}

try {
new DatePeriod(new DateTime('yesterday'), new DateInterval('P1D'),-1);
new DatePeriod(new DateTime('yesterday'), new DateInterval('P1D'), -1);
} catch (Exception $exception) {
echo $exception->getMessage(), "\n";
}

?>
--EXPECT--
DatePeriod::__construct(): The recurrence count '0' is invalid. Needs to be > 0
DatePeriod::__construct(): The recurrence count '-1' is invalid. Needs to be > 0
DatePeriod::__construct(): Recurrence count must be greater than 0
DatePeriod::__construct(): Recurrence count must be greater than 0
7 changes: 2 additions & 5 deletions ext/date/tests/bug44562.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@ Bug #44562 (Creating instance of DatePeriod crashes)
<?php
date_default_timezone_set('Europe/Oslo');

try
{
try {
$dp = new DatePeriod('2D');
}
catch ( Exception $e )
{
} catch (Exception $e) {
echo $e->getMessage(), "\n";
}

Expand Down
29 changes: 29 additions & 0 deletions ext/date/tests/date_period_bad_iso_format.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
--TEST--
Test bad ISO date formats passed to DatePeriod constructor
--FILE--
<?php

try {
$perid = new DatePeriod("R4");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$perid = new DatePeriod("R4");
$period = new DatePeriod("R4");

Or drop the variable :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I don't even know why I added the variables in the first place... :) (I do know indeed: copy-pasted from date_interval_bad_format_leak.phpt)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You copied my typo :P

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed them during applying the changes in this PR :)

} catch (Exception $e) {
echo $e->getMessage(), "\n";
}

try {
$perid = new DatePeriod("R4/2012-07-01T00:00:00Z");
} catch (Exception $e) {
echo $e->getMessage(), "\n";
}


try {
$perid = new DatePeriod("2012-07-01T00:00:00Z/P7D");
} catch (Exception $e) {
echo $e->getMessage(), "\n";
}

?>
--EXPECT--
DatePeriod::__construct(): ISO interval must contain a start date, "R4" given
DatePeriod::__construct(): ISO interval must contain an interval, "R4/2012-07-01T00:00:00Z" given
DatePeriod::__construct(): ISO interval must contain an end date or a recurrence count, "2012-07-01T00:00:00Z/P7D" given
Loading