diff --git a/.gitignore b/.gitignore index 113a004..94ecd63 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,7 @@ tests/*.log tests/*.php tests/*.sh tests/*.out +!tests/*.inc.php # Ignore C-related temporary files *.o diff --git a/tests/check-func-refl.inc.php b/tests/check-func-refl.inc.php new file mode 100644 index 0000000..42f2b17 --- /dev/null +++ b/tests/check-func-refl.inc.php @@ -0,0 +1,31 @@ + $replacement) { + $patterns[] = '{\b'.preg_quote($search).'\b}'; + $replacements[] = $replacement; + } + + return preg_replace($patterns, $replacements, $input); +} + +foreach ($functions as $phpFunctionName => $timecopFunctionName) { + echo sprintf("Checking %s vs %s\n", $phpFunctionName, $timecopFunctionName); + + $phpFunction = new \ReflectionFunction($phpFunctionName); + $timecopFunction = new \ReflectionFunction($timecopFunctionName); + + $timecopFunctionNormalised = replace_words($timecopFunction, [ + $timecopFunction->getName() => $phpFunction->getName(), + $timecopFunction->getExtensionName() => $phpFunction->getExtensionName(), + ]); + + if ((string) $phpFunction !== $timecopFunctionNormalised) { + echo "php: ", $phpFunction, "\n"; + echo "timecop: ", $timecopFunctionNormalised, "\n"; + } +} diff --git a/tests/func_002.phpt b/tests/func_002.phpt index f04d33b..43f45d9 100644 --- a/tests/func_002.phpt +++ b/tests/func_002.phpt @@ -3,6 +3,7 @@ Test for timecop_mktime() --SKIPIF-- getMessage(); } --EXPECT-- -timecop_strtotime(): Argument #1 ($time) must be of type string, null given +timecop_strtotime(): Argument #1 ($datetime) must be of type string, null given diff --git a/tests/refl_001.phpt b/tests/refl_001.phpt new file mode 100644 index 0000000..cb9e297 --- /dev/null +++ b/tests/refl_001.phpt @@ -0,0 +1,53 @@ +--TEST-- +Check reflection data for overridden methods +--SKIPIF-- + $replacement) { + $patterns[] = '{\b'.preg_quote($search).'\b}'; + $replacements[] = $replacement; + } + + return preg_replace($patterns, $replacements, $input); +} + +foreach (['DateTime', 'DateTimeImmutable'] as $className) { + foreach (['__construct', 'createFromFormat'] as $method) { + echo sprintf("Checking %1\$s::%2\$s vs Timecop%1\$s::%2\$s\n", $className, $method); + + $phpMethod = new \ReflectionMethod($className, $method); + $timecopMethod = new \ReflectionMethod('Timecop'.$className, $method); + + $timecopMethodNormalised = replace_words($timecopMethod, [ + $timecopMethod->getName() => $phpMethod->getName(), + $timecopMethod->getExtensionName() => $phpMethod->getExtensionName(), + ', overwrites '.$className => '', + ', prototype '.$className => '', + ]); + + if ((string) $phpMethod !== $timecopMethodNormalised) { + echo "php: ", $phpMethod, "\n"; + echo "timecop: ", $timecopMethodNormalised, "\n"; + } + } +} + +--EXPECT-- +Checking DateTime::__construct vs TimecopDateTime::__construct +Checking DateTime::createFromFormat vs TimecopDateTime::createFromFormat +Checking DateTimeImmutable::__construct vs TimecopDateTimeImmutable::__construct +Checking DateTimeImmutable::createFromFormat vs TimecopDateTimeImmutable::createFromFormat diff --git a/tests/refl_002.phpt b/tests/refl_002.phpt new file mode 100644 index 0000000..1a3479c --- /dev/null +++ b/tests/refl_002.phpt @@ -0,0 +1,46 @@ +--TEST-- +Check reflection data for overridden functions +--SKIPIF-- + 'timecop_time', + 'date' => 'timecop_date', + 'gmdate' => 'timecop_gmdate', + 'idate' => 'timecop_idate', + 'getdate' => 'timecop_getdate', + 'localtime' => 'timecop_localtime', + 'strtotime' => 'timecop_strtotime', + 'strftime' => 'timecop_strftime', + 'gmstrftime' => 'timecop_gmstrftime', + 'date_create' => 'timecop_date_create', + 'date_create_from_format' => 'timecop_date_create_from_format', + 'date_create_immutable' => 'timecop_date_create_immutable', + 'date_create_immutable_from_format' => 'timecop_date_create_immutable_from_format', +]; + +require __DIR__.'/check-func-refl.inc.php'; + +--EXPECT-- +Checking time vs timecop_time +Checking date vs timecop_date +Checking gmdate vs timecop_gmdate +Checking idate vs timecop_idate +Checking getdate vs timecop_getdate +Checking localtime vs timecop_localtime +Checking strtotime vs timecop_strtotime +Checking strftime vs timecop_strftime +Checking gmstrftime vs timecop_gmstrftime +Checking date_create vs timecop_date_create +Checking date_create_from_format vs timecop_date_create_from_format +Checking date_create_immutable vs timecop_date_create_immutable +Checking date_create_immutable_from_format vs timecop_date_create_immutable_from_format diff --git a/tests/refl_003.phpt b/tests/refl_003.phpt new file mode 100644 index 0000000..1ca57a2 --- /dev/null +++ b/tests/refl_003.phpt @@ -0,0 +1,24 @@ +--TEST-- +Check reflection data for overridden functions +--SKIPIF-- + 'timecop_microtime', + 'gettimeofday' => 'timecop_gettimeofday', +]; + +require __DIR__.'/check-func-refl.inc.php'; + +--EXPECT-- +Checking microtime vs timecop_microtime +Checking gettimeofday vs timecop_gettimeofday diff --git a/tests/refl_004.phpt b/tests/refl_004.phpt new file mode 100644 index 0000000..df406bf --- /dev/null +++ b/tests/refl_004.phpt @@ -0,0 +1,22 @@ +--TEST-- +Check reflection data for overridden functions +--SKIPIF-- + 'timecop_unixtojd', +]; + +require __DIR__.'/check-func-refl.inc.php'; + +--EXPECT-- +Checking unixtojd vs timecop_unixtojd diff --git a/tests/refl_005.phpt b/tests/refl_005.phpt new file mode 100644 index 0000000..106b2e3 --- /dev/null +++ b/tests/refl_005.phpt @@ -0,0 +1,25 @@ +--TEST-- +Check reflection data for overridden functions (On PHP 5.6 timecop_mktime() does not implement the $is_dst parameter) +--SKIPIF-- + 'timecop_mktime', + 'gmmktime' => 'timecop_gmmktime', +]; + +require __DIR__.'/check-func-refl.inc.php'; + +--EXPECT-- +Checking mktime vs timecop_mktime +Checking gmmktime vs timecop_gmmktime diff --git a/timecop_php7.c b/timecop_php7.c index 556ecf4..1f6b588 100644 --- a/timecop_php7.c +++ b/timecop_php7.c @@ -91,7 +91,11 @@ static const struct timecop_override_class_entry timecop_override_class_table[] {NULL, NULL, NULL, NULL} }; +#if PHP_VERSION_ID >= 80000 +#include "timecop_php8_arginfo.h" +#else #include "timecop_php7_arginfo.h" +#endif /* {{{ timecop_functions[] */ const zend_function_entry timecop_functions[] = { @@ -137,7 +141,7 @@ static zend_function_entry timecop_funcs_timecop[] = { static zend_function_entry timecop_funcs_date[] = { PHP_ME(TimecopDateTime, __construct, arginfo_class_TimecopDateTime___construct, ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) - PHP_ME_MAPPING(createFromFormat, timecop_date_create_from_format, arginfo_timecop_date_create_from_format, + PHP_ME_MAPPING(createFromFormat, timecop_date_create_from_format, arginfo_class_TimecopDateTime_createFromFormat, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) {NULL, NULL, NULL} }; @@ -151,7 +155,7 @@ static zend_function_entry timecop_funcs_orig_date[] = { static zend_function_entry timecop_funcs_immutable[] = { PHP_ME(TimecopDateTimeImmutable, __construct, arginfo_class_TimecopDateTimeImmutable___construct, ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) - PHP_ME_MAPPING(createFromFormat, timecop_date_create_immutable_from_format, arginfo_timecop_date_create_immutable_from_format, + PHP_ME_MAPPING(createFromFormat, timecop_date_create_immutable_from_format, arginfo_class_TimecopDateTimeImmutable_createFromFormat, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) {NULL, NULL, NULL} }; @@ -977,6 +981,19 @@ PHP_FUNCTION(timecop_time) PHP_FUNCTION(timecop_mktime) { zend_long hou = 0, min = 0, sec = 0, mon = 0, day = 0, yea = 0; + zend_bool min_is_null = 1, sec_is_null = 1, mon_is_null = 1, day_is_null = 1, yea_is_null = 1; + +#if PHP_VERSION_ID >= 80000 + ZEND_PARSE_PARAMETERS_START(1, 6) + Z_PARAM_LONG(hou) + Z_PARAM_OPTIONAL + Z_PARAM_LONG_OR_NULL(min, min_is_null) + Z_PARAM_LONG_OR_NULL(sec, sec_is_null) + Z_PARAM_LONG_OR_NULL(mon, mon_is_null) + Z_PARAM_LONG_OR_NULL(day, day_is_null) + Z_PARAM_LONG_OR_NULL(yea, yea_is_null) + ZEND_PARSE_PARAMETERS_END(); +#else ZEND_PARSE_PARAMETERS_START(0, 6) Z_PARAM_OPTIONAL Z_PARAM_LONG(hou) @@ -986,6 +1003,7 @@ PHP_FUNCTION(timecop_mktime) Z_PARAM_LONG(day) Z_PARAM_LONG(yea) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); +#endif TIMECOP_CALL_MKTIME("mktime", "date"); } @@ -996,6 +1014,19 @@ PHP_FUNCTION(timecop_mktime) PHP_FUNCTION(timecop_gmmktime) { zend_long hou = 0, min = 0, sec = 0, mon = 0, day = 0, yea = 0; + zend_bool min_is_null = 1, sec_is_null = 1, mon_is_null = 1, day_is_null = 1, yea_is_null = 1; + +#if PHP_VERSION_ID >= 80000 + ZEND_PARSE_PARAMETERS_START(1, 6) + Z_PARAM_LONG(hou) + Z_PARAM_OPTIONAL + Z_PARAM_LONG_OR_NULL(min, min_is_null) + Z_PARAM_LONG_OR_NULL(sec, sec_is_null) + Z_PARAM_LONG_OR_NULL(mon, mon_is_null) + Z_PARAM_LONG_OR_NULL(day, day_is_null) + Z_PARAM_LONG_OR_NULL(yea, yea_is_null) + ZEND_PARSE_PARAMETERS_END(); +#else ZEND_PARSE_PARAMETERS_START(0, 6) Z_PARAM_OPTIONAL Z_PARAM_LONG(hou) @@ -1005,6 +1036,7 @@ PHP_FUNCTION(timecop_gmmktime) Z_PARAM_LONG(day) Z_PARAM_LONG(yea) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); +#endif TIMECOP_CALL_MKTIME("gmmktime", "gmdate"); } diff --git a/timecop_php7_arginfo.h b/timecop_php7_arginfo.h index 76a5120..251a386 100644 --- a/timecop_php7_arginfo.h +++ b/timecop_php7_arginfo.h @@ -101,7 +101,11 @@ ZEND_END_ARG_INFO() // timecop_date_create() ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_date_create, 0, 0, 0) ZEND_ARG_INFO(0, time) +#if PHP_VERSION_ID >= 70100 + ZEND_ARG_INFO(0, timezone) +#else ZEND_ARG_INFO(0, object) +#endif ZEND_END_ARG_INFO() // timecop_date_create_from_format() @@ -118,6 +122,9 @@ ZEND_END_ARG_INFO() // TimecopDateTime::__construct() #define arginfo_class_TimecopDateTime___construct arginfo_timecop_date_create +// TimecopDateTime::createFromFormat() +#define arginfo_class_TimecopDateTime_createFromFormat arginfo_timecop_date_create_from_format + // timecop_date_create_immutable() #define arginfo_timecop_date_create_immutable arginfo_timecop_date_create @@ -126,3 +133,6 @@ ZEND_END_ARG_INFO() // TimecopDateTimeImmutable::__construct #define arginfo_class_TimecopDateTimeImmutable___construct arginfo_class_TimecopDateTime___construct + +// TimecopDateTimeImmutable::createFromFormat +#define arginfo_class_TimecopDateTimeImmutable_createFromFormat arginfo_timecop_date_create_immutable_from_format diff --git a/timecop_php8_arginfo.h b/timecop_php8_arginfo.h new file mode 100644 index 0000000..82d67d8 --- /dev/null +++ b/timecop_php8_arginfo.h @@ -0,0 +1,157 @@ +/* +MIT License + +Copyright (c) 2012-2017 Yoshio HANAWA +Copyright (c) 2021 Wider Plan Ltd + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_freeze, 0, 0, 1) + ZEND_ARG_INFO(0, timestamp) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_travel, 0, 0, 1) + ZEND_ARG_INFO(0, timestamp) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_scale, 0, 0, 1) + ZEND_ARG_INFO(0, scale) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_timecop_return, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_timecop_time, 0, 0, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_timecop_mktime, 0, 1, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, hour, IS_LONG, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, minute, IS_LONG, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, second, IS_LONG, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, month, IS_LONG, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, day, IS_LONG, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, year, IS_LONG, 1, "null") +ZEND_END_ARG_INFO() + +#define arginfo_timecop_gmmktime arginfo_timecop_mktime + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_timecop_date, 0, 1, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, format, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timestamp, IS_LONG, 1, "null") +ZEND_END_ARG_INFO() + +#define arginfo_timecop_gmdate arginfo_timecop_date + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_timecop_idate, 0, 1, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, format, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timestamp, IS_LONG, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_timecop_getdate, 0, 0, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timestamp, IS_LONG, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_timecop_localtime, 0, 0, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timestamp, IS_LONG, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, associative, _IS_BOOL, 0, "false") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_timecop_strtotime, 0, 1, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, baseTimestamp, IS_LONG, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_timecop_strftime, 0, 1, MAY_BE_STRING|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, format, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timestamp, IS_LONG, 1, "null") +ZEND_END_ARG_INFO() + +#define arginfo_timecop_gmstrftime arginfo_timecop_strftime + +#ifdef HAVE_GETTIMEOFDAY +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_timecop_microtime, 0, 0, MAY_BE_STRING|MAY_BE_DOUBLE) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, as_float, _IS_BOOL, 0, "false") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_timecop_gettimeofday, 0, 0, MAY_BE_ARRAY|MAY_BE_DOUBLE) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, as_float, _IS_BOOL, 0, "false") +ZEND_END_ARG_INFO() +#endif + +ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_unixtojd, 0, 0, 0) + ZEND_ARG_INFO(0, timestamp) +ZEND_END_ARG_INFO() + +// timecop_date_create() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_timecop_date_create, 0, 0, DateTime, MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, datetime, IS_STRING, 0, "\"now\"") + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, timezone, DateTimeZone, 1, "null") +ZEND_END_ARG_INFO() + +// timecop_date_create_from_format() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_timecop_date_create_from_format, 0, 2, DateTime, MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, format, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0) + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, timezone, DateTimeZone, 1, "null") +ZEND_END_ARG_INFO() + +// TimecopDateTime::__construct() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_TimecopDateTime___construct, 0, 0, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, datetime, IS_STRING, 0, "\"now\"") + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, timezone, DateTimeZone, 1, "null") +ZEND_END_ARG_INFO() + +// TimecopDateTime::createFromFormat() +#if PHP_VERSION_ID >= 80100 +#define arginfo_class_TimecopDateTime_createFromFormat arginfo_timecop_date_create_from_format +#else +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_TimecopDateTime_createFromFormat, 0, 0, 2) + ZEND_ARG_TYPE_INFO(0, format, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0) + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, timezone, DateTimeZone, 1, "null") +ZEND_END_ARG_INFO() +#endif + +// timecop_date_create_immutable() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_timecop_date_create_immutable, 0, 0, DateTimeImmutable, MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, datetime, IS_STRING, 0, "\"now\"") + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, timezone, DateTimeZone, 1, "null") +ZEND_END_ARG_INFO() + +// timecop_date_create_immutable_from_format() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_timecop_date_create_immutable_from_format, 0, 2, DateTimeImmutable, MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, format, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0) + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, timezone, DateTimeZone, 1, "null") +ZEND_END_ARG_INFO() + +// TimecopDateTimeImmutable::__construct +#define arginfo_class_TimecopDateTimeImmutable___construct arginfo_class_TimecopDateTime___construct + +// TimecopDateTimeImmutable::createFromFormat +#if PHP_VERSION_ID >= 80100 +#define arginfo_class_TimecopDateTimeImmutable_createFromFormat arginfo_timecop_date_create_immutable_from_format +#else +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_TimecopDateTimeImmutable_createFromFormat, 0, 0, 2) + ZEND_ARG_TYPE_INFO(0, format, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, datetime, IS_STRING, 0) + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, timezone, DateTimeZone, 1, "null") +ZEND_END_ARG_INFO() +#endif