From 19b4feac518d1ca3313c5be2e76d51f62d11ce22 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 8 Nov 2024 11:00:17 +0000 Subject: [PATCH 1/7] Add PHP 8.4 build --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 742843c..bb61d39 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,6 +25,7 @@ jobs: - '8.1' - '8.2' - '8.3' + - '8.4' php-zts: - nts - ts @@ -33,6 +34,8 @@ jobs: php-zts: 'ts' - php-version: '8.3' php-zts: 'ts' + - php-version: '8.4' + php-zts: 'ts' steps: - name: Setup PHP uses: shivammathur/setup-php@v2 From f81a2dc38ecc3b04ab21a30b3b8dcd42bb29e597 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 8 Nov 2024 12:17:53 +0000 Subject: [PATCH 2/7] [WIP] Attributes added to these functions causes segfault --- timecop_php7.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/timecop_php7.c b/timecop_php7.c index ff416dd..132479b 100644 --- a/timecop_php7.c +++ b/timecop_php7.c @@ -112,7 +112,10 @@ const zend_function_entry timecop_functions[] = { PHP_FE(timecop_getdate, arginfo_timecop_getdate) PHP_FE(timecop_localtime, arginfo_timecop_localtime) PHP_FE(timecop_strtotime, arginfo_timecop_strtotime) -#if PHP_VERSION_ID >= 80100 +#if PHP_VERSION_ID >= 80400 + ZEND_RAW_FENTRY("timecop_strftime", zif_timecop_strftime, arginfo_timecop_strftime, ZEND_ACC_DEPRECATED, NULL, NULL) + ZEND_RAW_FENTRY("timecop_gmstrftime", zif_timecop_gmstrftime, arginfo_timecop_gmstrftime, ZEND_ACC_DEPRECATED, NULL, NULL) +#elif PHP_VERSION_ID >= 80100 PHP_DEP_FE(timecop_strftime, arginfo_timecop_strftime) PHP_DEP_FE(timecop_gmstrftime, arginfo_timecop_gmstrftime) #else @@ -435,12 +438,12 @@ static int timecop_func_override() TIMECOP_ASSERT(zf_ovrd->type == ZEND_INTERNAL_FUNCTION); zend_hash_str_add_mem(CG(function_table), p->save_func, strlen(p->save_func), - zf_orig, sizeof(zend_internal_function)); + zf_orig, sizeof(zend_function)); function_add_ref(zf_orig); FIX_FUNCTION_ARG_INFO_DTOR(zf_orig); zend_hash_str_update_mem(CG(function_table), p->orig_func, strlen(p->orig_func), - zf_ovrd, sizeof(zend_internal_function)); + zf_ovrd, sizeof(zend_function)); function_add_ref(zf_ovrd); p++; @@ -509,12 +512,12 @@ static int timecop_class_override() zend_hash_str_add_mem(&ce_orig->function_table, p->save_method, strlen(p->save_method), - zf_orig, sizeof(zend_internal_function)); + zf_orig, sizeof(zend_function)); function_add_ref(zf_orig); zf_new = zend_hash_str_update_mem(&ce_orig->function_table, p->orig_method, strlen(p->orig_method), - zf_ovrd, sizeof(zend_internal_function)); + zf_ovrd, sizeof(zend_function)); function_add_ref(zf_ovrd); TIMECOP_ASSERT(zf_new != NULL); @@ -547,7 +550,7 @@ static int timecop_func_override_clear() FIX_FUNCTION_ARG_INFO_DTOR(zf_ovld); zend_hash_str_update_mem(CG(function_table), p->orig_func, strlen(p->orig_func), - zf_orig, sizeof(zend_internal_function)); + zf_orig, sizeof(zend_function)); function_add_ref(zf_orig); FIX_FUNCTION_ARG_INFO_DTOR(zf_orig); @@ -586,7 +589,7 @@ static int timecop_class_override_clear() } zend_hash_str_update_mem(&ce_orig->function_table, p->orig_method, strlen(p->orig_method), - zf_orig, sizeof(zend_internal_function)); + zf_orig, sizeof(zend_function)); function_add_ref(zf_orig); zend_hash_str_del(&ce_orig->function_table, p->save_method, strlen(p->save_method)); From 5d6f3d8a39f49889c2616920d630fd000ae4cb59 Mon Sep 17 00:00:00 2001 From: Sylvain MSL Date: Thu, 20 Mar 2025 15:24:55 +0100 Subject: [PATCH 3/7] Try support php 8.4 --- Dockerfile | 46 ++++ README.md | 21 +- config.m4 | 2 + php_timecop.h | 35 +-- tc_timeval.dep | 1 + timecop_php7.dep | 96 ++++++++ timecop_php8.c | 582 +++++++++++++++++++++++++++++++++++++++++++++++ timecop_php8.dep | 95 ++++++++ 8 files changed, 842 insertions(+), 36 deletions(-) create mode 100644 Dockerfile create mode 100644 tc_timeval.dep create mode 100644 timecop_php7.dep create mode 100644 timecop_php8.c create mode 100644 timecop_php8.dep diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..adae8e4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,46 @@ +FROM ubuntu:24.04 + +LABEL "com.talkspirit.maintainer"="Olivier RICARD " + +# Let the conatiner know that there is no tty +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get upgrade -y && apt-get install -y build-essential debhelper devscripts cron software-properties-common wget zsh curl vim zsh git supervisor jq libjq-dev -y + +# MongoDB shell tools +RUN wget -qO - https://www.mongodb.org/static/pgp/server-6.0.asc | apt-key add - +RUN echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/6.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-6.0.list +RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php -y -u && apt-get update + +# geoip https://github.com/maxmind/GeoIP2-php + +RUN apt-get update && apt-get install -y mongodb-mongosh mongodb-org-tools php8.4-dev php8.4-fpm php8.4-mongodb php8.4-gd php8.4-curl php8.4-cli php8.4-soap php8.4-apcu php8.4-opcache php8.4-intl php8.4-mbstring php8.4-redis php8.4-dom php8.4-zip php8.4-imagick php8.4-bcmath php8.4-mysql && \ +echo "date.timezone=${PHP_TIMEZONE:-Europe/Paris}" > /etc/php/8.4/cli/conf.d/date_timezone.ini && \ +echo "date.timezone=${PHP_TIMEZONE:-Europe/Paris}" > /etc/php/8.4/fpm/conf.d/date_timezone.ini + +RUN wget http://pear.php.net/go-pear.phar && php go-pear.phar + +# tools +RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && \ + curl -sS http://gordalina.github.io/cachetool/downloads/cachetool.phar -o /usr/local/bin/cachetool.phar && \ + wget https://get.symfony.com/cli/installer -O - | bash && \ + mv /root/.symfony5/bin/symfony /usr/local/bin/symfony && \ + curl -fLSs https://circle.ci/cli | bash + +RUN sed -i -e "s/upload_max_filesize\s*=\s*2M/upload_max_filesize = 100M/g" /etc/php/8.4/cli/php.ini && \ + sed -i -e "s/post_max_size\s*=\s*8M/post_max_size = 100M/g" /etc/php/8.4/cli/php.ini && \ + sed -i -e "s/;daemonize\s*=\s*yes/daemonize = no/g" /etc/php/8.4/fpm/php-fpm.conf && \ + sed -i -e "s/listen = \/run\/php\/php8.4-fpm.sock/;listen = \/run\/php\/php8.4-fpm.sock\nlisten = 0:9000/g" /etc/php/8.4/fpm/pool.d/www.conf && \ + sed -i -e "s/upload_max_filesize\s*=\s*2M/upload_max_filesize = 100M/g" /etc/php/8.4/cli/php.ini + +RUN apt install php8.4-phpdbg + +COPY . /tmp/install/php-timecop + +# Install timecop & jq +RUN cd /tmp/install/php-timecop && \ + phpize && \ + ./configure && \ + make && \ + make install && \ + echo "extension=timecop.so" >> /etc/php/8.4/cli/php.ini \ No newline at end of file diff --git a/README.md b/README.md index 830cc4f..0e7aaab 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ extension=timecop.so ## SYSTEM REQUIREMENTS - OS: Linux, macOS -- PHP: 5.6.x - 8.3.x +- PHP: 8.0.x - 8.4.x - SAPI: Apache, CLI - Other SAPIs are not tested, but there is no SAPI-dependent code. - non-ZTS(recommended), ZTS @@ -54,13 +54,13 @@ extension=timecop.so - `gettimeofday()` - `unixtojd()` - `DateTime::_construct()` - - `DateTime::createFromFormat()` (PHP >= 5.3.0) - - `DateTimeImmutable::_construct()` (PHP >= 5.5.0) - - `DateTimeImmutable::createFromFormat()` (PHP >= 5.5.0) + - `DateTime::createFromFormat()` + - `DateTimeImmutable::_construct()` + - `DateTimeImmutable::createFromFormat()` - `date_create()` - - `date_create_from_format()` (PHP >= 5.3.0) - - `date_create_immutable()` (PHP >= 5.5.0) - - `date_create_immutable_from_format()` (PHP >= 5.5.0) + - `date_create_from_format()` + - `date_create_immutable()` + - `date_create_immutable_from_format()` - Rewrite value of the following global variables when the time has been moved. - `$_SERVER['REQUEST_TIME']` @@ -106,6 +106,12 @@ var_dump((new DateTime())->format("c")); // string(25) "2017-01-01T00:00:05+00:0 ## CHANGELOG +### version 1.7.0, 2024/03/20 +- Support PHP 8.4 +- Drop support for PHP 5.x and 7.x +- Fix segmentation faults in PHP 8.4 +- Improved memory management + ### version 1.6.0, 2024/02/09 - Support PHP 8.3 @@ -228,6 +234,7 @@ The MIT License Copyright (c) 2012-2017 Yoshio HANAWA Copyright (c) 2019-2024 Wider Plan Ltd +Copyright (c) 2024 Sylvain Filteau 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: diff --git a/config.m4 b/config.m4 index 3c890ca..be358e5 100644 --- a/config.m4 +++ b/config.m4 @@ -37,6 +37,8 @@ if test "$PHP_TIMECOP" != "no"; then if test "$PHP_MAJOR_VERSION" -eq 5; then PHP_NEW_EXTENSION(timecop, timecop_php5.c tc_timeval.c, $ext_shared) + elif test "$PHP_MAJOR_VERSION" -ge 8; then + PHP_NEW_EXTENSION(timecop, timecop_php8.c tc_timeval.c, $ext_shared) else PHP_NEW_EXTENSION(timecop, timecop_php7.c tc_timeval.c, $ext_shared) fi diff --git a/php_timecop.h b/php_timecop.h index e57d232..831a2aa 100644 --- a/php_timecop.h +++ b/php_timecop.h @@ -2,6 +2,8 @@ MIT License Copyright (c) 2012-2017 Yoshio HANAWA +Copyright (c) 2019-2024 Wider Plan Ltd +Copyright (c) 2024 Sylvain Filteau Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -25,7 +27,7 @@ SOFTWARE. #ifndef PHP_TIMECOP_H #define PHP_TIMECOP_H -#define PHP_TIMECOP_VERSION "1.6.0" +#define PHP_TIMECOP_VERSION "1.7.0" extern zend_module_entry timecop_module_entry; #define phpext_timecop_ptr &timecop_module_entry @@ -79,22 +81,13 @@ PHP_FUNCTION(timecop_gettimeofday); PHP_FUNCTION(timecop_unixtojd); PHP_FUNCTION(timecop_date_create); PHP_FUNCTION(timecop_date_create_from_format); -#if PHP_VERSION_ID >= 50500 PHP_FUNCTION(timecop_date_create_immutable); PHP_FUNCTION(timecop_date_create_immutable_from_format); -#endif -#if !defined(PHP_VERSION_ID) || PHP_VERSION_ID < 50300 -PHP_FUNCTION(date_timestamp_set); -PHP_FUNCTION(date_timestamp_get); -#endif PHP_METHOD(TimecopDateTime, __construct); PHP_METHOD(TimecopOrigDateTime, __construct); - -#if PHP_VERSION_ID >= 50500 PHP_METHOD(TimecopDateTimeImmutable, __construct); PHP_METHOD(TimecopOrigDateTimeImmutable, __construct); -#endif PHP_METHOD(Timecop, freeze); PHP_METHOD(Timecop, travel); @@ -108,20 +101,12 @@ typedef enum timecop_mode_t { ZEND_BEGIN_MODULE_GLOBALS(timecop) long func_override; long sync_request_time; -#if PHP_VERSION_ID >= 70000 zval orig_request_time; -#else - zval *orig_request_time; -#endif timecop_mode_t timecop_mode; tc_timeval freezed_time; tc_timeval travel_origin; tc_timeval travel_offset; -#if PHP_VERSION_ID >= 70000 zend_long scaling_factor; -#else - long scaling_factor; -#endif zend_class_entry *ce_DateTimeZone; zend_class_entry *ce_DateTimeInterface; zend_class_entry *ce_DateTime; @@ -211,17 +196,9 @@ struct timecop_override_class_entry { #define TSRMLS_FETCH() #endif -#if PHP_VERSION_ID >= 70000 -# define TIMECOP_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(timecop, v) -# if defined(ZTS) && defined(COMPILE_DL_TIMECOP) - ZEND_TSRMLS_CACHE_EXTERN(); -# endif -#else -# ifdef ZTS -# define TIMECOP_G(v) TSRMG(timecop_globals_id, zend_timecop_globals *, v) -# else -# define TIMECOP_G(v) (timecop_globals.v) -# endif +#define TIMECOP_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(timecop, v) +#if defined(ZTS) && defined(COMPILE_DL_TIMECOP) + ZEND_TSRMLS_CACHE_EXTERN(); #endif #endif /* PHP_TIMECOP_H */ diff --git a/tc_timeval.dep b/tc_timeval.dep new file mode 100644 index 0000000..cccbb73 --- /dev/null +++ b/tc_timeval.dep @@ -0,0 +1 @@ +tc_timeval.lo: /app/tc_timeval.c /app/tc_timeval.h diff --git a/timecop_php7.dep b/timecop_php7.dep new file mode 100644 index 0000000..9baec60 --- /dev/null +++ b/timecop_php7.dep @@ -0,0 +1,96 @@ +timecop_php7.lo: /app/timecop_php7.c /app/config.h \ + /usr/include/php/20240924/main/php.h \ + /usr/include/php/20240924/main/php_version.h \ + /usr/include/php/20240924/Zend/zend.h \ + /usr/include/php/20240924/Zend/zend_types.h \ + /usr/include/php/20240924/Zend/zend_portability.h \ + /usr/include/php/20240924/Zend/zend_config.h \ + /usr/include/php/20240924/main/../main/php_config.h \ + /usr/include/php/20240924/Zend/../TSRM/TSRM.h \ + /usr/include/php/20240924/main/php_config.h \ + /usr/include/php/20240924/Zend/zend_range_check.h \ + /usr/include/php/20240924/Zend/zend_long.h \ + /usr/include/php/20240924/Zend/zend_map_ptr.h \ + /usr/include/php/20240924/Zend/zend_errors.h \ + /usr/include/php/20240924/Zend/zend_alloc.h \ + /usr/include/php/20240924/Zend/zend_alloc_sizes.h \ + /usr/include/php/20240924/Zend/zend_llist.h \ + /usr/include/php/20240924/Zend/zend_string.h \ + /usr/include/php/20240924/Zend/zend_gc.h \ + /usr/include/php/20240924/Zend/zend_hrtime.h \ + /usr/include/php/20240924/Zend/zend_hash.h \ + /usr/include/php/20240924/Zend/zend_sort.h \ + /usr/include/php/20240924/Zend/zend_ast.h \ + /usr/include/php/20240924/Zend/zend_variables.h \ + /usr/include/php/20240924/Zend/zend_iterators.h \ + /usr/include/php/20240924/Zend/zend_stream.h \ + /usr/include/php/20240924/Zend/zend_smart_str_public.h \ + /usr/include/php/20240924/Zend/zend_smart_string_public.h \ + /usr/include/php/20240924/Zend/zend_signal.h \ + /usr/include/php/20240924/Zend/zend_max_execution_timer.h \ + /usr/include/php/20240924/Zend/zend_object_handlers.h \ + /usr/include/php/20240924/Zend/zend_property_hooks.h \ + /usr/include/php/20240924/Zend/zend_lazy_objects.h \ + /usr/include/php/20240924/Zend/zend_types.h \ + /usr/include/php/20240924/Zend/zend_operators.h \ + /usr/include/php/20240924/Zend/zend_strtod.h \ + /usr/include/php/20240924/Zend/zend_multiply.h \ + /usr/include/php/20240924/Zend/zend_sort.h \ + /usr/include/php/20240924/main/php_compat.h \ + /usr/include/php/20240924/main/php_config.h \ + /usr/include/php/20240924/Zend/zend_API.h \ + /usr/include/php/20240924/Zend/zend_modules.h \ + /usr/include/php/20240924/Zend/zend.h \ + /usr/include/php/20240924/Zend/zend_compile.h \ + /usr/include/php/20240924/Zend/zend_frameless_function.h \ + /usr/include/php/20240924/Zend/zend_globals.h \ + /usr/include/php/20240924/Zend/zend_globals_macros.h \ + /usr/include/php/20240924/Zend/zend_atomic.h \ + /usr/include/php/20240924/Zend/zend_stack.h \ + /usr/include/php/20240924/Zend/zend_ptr_stack.h \ + /usr/include/php/20240924/Zend/zend_objects.h \ + /usr/include/php/20240924/Zend/zend_objects_API.h \ + /usr/include/php/20240924/Zend/zend_float.h \ + /usr/include/php/20240924/Zend/zend_multibyte.h \ + /usr/include/php/20240924/Zend/zend_arena.h \ + /usr/include/php/20240924/Zend/zend_call_stack.h \ + /usr/include/php/20240924/Zend/zend_vm_opcodes.h \ + /usr/include/php/20240924/Zend/zend_build.h \ + /usr/include/php/20240924/Zend/zend_list.h \ + /usr/include/php/20240924/Zend/zend_execute.h \ + /usr/include/php/20240924/Zend/zend_type_info.h \ + /usr/include/php/20240924/main/build-defs.h \ + /usr/include/php/20240924/Zend/zend_hash.h \ + /usr/include/php/20240924/Zend/zend_alloc.h \ + /usr/include/php/20240924/Zend/zend_stack.h \ + /usr/include/php/20240924/main/snprintf.h \ + /usr/include/php/20240924/main/spprintf.h \ + /usr/include/php/20240924/Zend/zend_smart_str_public.h \ + /usr/include/php/20240924/Zend/zend_smart_string_public.h \ + /usr/include/php/20240924/main/php_syslog.h \ + /usr/include/php/20240924/main/php.h \ + /usr/include/php/20240924/main/php_output.h \ + /usr/include/php/20240924/main/php_streams.h \ + /usr/include/php/20240924/Zend/zend_stream.h \ + /usr/include/php/20240924/main/streams/php_stream_context.h \ + /usr/include/php/20240924/main/streams/php_stream_filter_api.h \ + /usr/include/php/20240924/main/streams/php_stream_transport.h \ + /usr/include/php/20240924/main/streams/php_stream_plain_wrapper.h \ + /usr/include/php/20240924/main/streams/php_stream_glob_wrapper.h \ + /usr/include/php/20240924/main/streams/php_stream_userspace.h \ + /usr/include/php/20240924/main/streams/php_stream_mmap.h \ + /usr/include/php/20240924/main/php_memory_streams.h \ + /usr/include/php/20240924/main/fopen_wrappers.h \ + /usr/include/php/20240924/main/php_globals.h \ + /usr/include/php/20240924/Zend/zend_globals.h \ + /usr/include/php/20240924/main/php_ini.h \ + /usr/include/php/20240924/Zend/zend_ini.h \ + /usr/include/php/20240924/Zend/zend_virtual_cwd.h \ + /usr/include/php/20240924/TSRM/TSRM.h \ + /usr/include/php/20240924/Zend/zend_constants.h \ + /usr/include/php/20240924/main/php_reentrancy.h \ + /usr/include/php/20240924/main/php_ini.h \ + /usr/include/php/20240924/ext/standard/info.h /app/php_timecop.h \ + /usr/include/php/20240924/Zend/zend_interfaces.h \ + /usr/include/php/20240924/Zend/zend_API.h /app/tc_timeval.h \ + /app/timecop_php8_arginfo.h diff --git a/timecop_php8.c b/timecop_php8.c new file mode 100644 index 0000000..f477511 --- /dev/null +++ b/timecop_php8.c @@ -0,0 +1,582 @@ +/* +MIT License + +Copyright (c) 2012-2017 Yoshio HANAWA +Copyright (c) 2019-2024 Wider Plan Ltd +Copyright (c) 2024 Sylvain Filteau + +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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" + +#include "php_timecop.h" + +#ifdef ZFS +#include "TSRM.h" +#endif + +ZEND_DECLARE_MODULE_GLOBALS(timecop) + +static void timecop_globals_ctor(zend_timecop_globals *globals) { + /* Initialize your global struct */ + globals->func_override = 1; + globals->sync_request_time = 1; + ZVAL_NULL(&globals->orig_request_time); + globals->timecop_mode = TIMECOP_MODE_REALTIME; + globals->freezed_time.sec = 0; + globals->freezed_time.usec = 0; + globals->travel_origin.sec = 0; + globals->travel_origin.usec = 0; + globals->travel_offset.sec = 0; + globals->travel_offset.usec = 0; + globals->scaling_factor = 1; + globals->ce_DateTimeZone = NULL; + globals->ce_DateTimeInterface = NULL; + globals->ce_DateTime = NULL; + globals->ce_TimecopDateTime = NULL; + globals->ce_DateTimeImmutable = NULL; + globals->ce_TimecopDateTimeImmutable = NULL; +} + +static const struct timecop_override_func_entry timecop_override_func_table[] = { + TIMECOP_OFE("time"), + TIMECOP_OFE("mktime"), + TIMECOP_OFE("gmmktime"), + TIMECOP_OFE("date"), + TIMECOP_OFE("gmdate"), + TIMECOP_OFE("idate"), + TIMECOP_OFE("getdate"), + TIMECOP_OFE("localtime"), + TIMECOP_OFE("strtotime"), + TIMECOP_OFE("strftime"), + TIMECOP_OFE("gmstrftime"), +#ifdef HAVE_GETTIMEOFDAY + TIMECOP_OFE("microtime"), + TIMECOP_OFE("gettimeofday"), +#endif + TIMECOP_OFE("unixtojd"), + TIMECOP_OFE("date_create"), + TIMECOP_OFE("date_create_from_format"), + TIMECOP_OFE("date_create_immutable"), + TIMECOP_OFE("date_create_immutable_from_format"), + {NULL, NULL, NULL} +}; + +static const struct timecop_override_class_entry timecop_override_class_table[] = { + TIMECOP_OCE("datetime", "__construct"), + TIMECOP_OCE("datetime", "createfromformat"), + TIMECOP_OCE("datetimeimmutable", "__construct"), + TIMECOP_OCE("datetimeimmutable", "createfromformat"), + {NULL, NULL, NULL, NULL} +}; + +#include "timecop_php8_arginfo.h" + +/* {{{ timecop_functions[] */ +const zend_function_entry timecop_functions[] = { + PHP_FE(timecop_freeze, arginfo_timecop_freeze) + PHP_FE(timecop_travel, arginfo_timecop_travel) + PHP_FE(timecop_scale, arginfo_timecop_scale) + PHP_FE(timecop_return, arginfo_timecop_return) + PHP_FE(timecop_time, arginfo_timecop_time) + PHP_FE(timecop_mktime, arginfo_timecop_mktime) + PHP_FE(timecop_gmmktime, arginfo_timecop_gmmktime) + PHP_FE(timecop_date, arginfo_timecop_date) + PHP_FE(timecop_gmdate, arginfo_timecop_gmdate) + PHP_FE(timecop_idate, arginfo_timecop_idate) + PHP_FE(timecop_getdate, arginfo_timecop_getdate) + PHP_FE(timecop_localtime, arginfo_timecop_localtime) + PHP_FE(timecop_strtotime, arginfo_timecop_strtotime) +#if PHP_VERSION_ID >= 80400 + ZEND_RAW_FENTRY("timecop_strftime", zif_timecop_strftime, arginfo_timecop_strftime, ZEND_ACC_DEPRECATED, NULL, NULL) + ZEND_RAW_FENTRY("timecop_gmstrftime", zif_timecop_gmstrftime, arginfo_timecop_gmstrftime, ZEND_ACC_DEPRECATED, NULL, NULL) +#elif PHP_VERSION_ID >= 80100 + PHP_DEP_FE(timecop_strftime, arginfo_timecop_strftime) + PHP_DEP_FE(timecop_gmstrftime, arginfo_timecop_gmstrftime) +#else + PHP_FE(timecop_strftime, arginfo_timecop_strftime) + PHP_FE(timecop_gmstrftime, arginfo_timecop_gmstrftime) +#endif +#ifdef HAVE_GETTIMEOFDAY + PHP_FE(timecop_microtime, arginfo_timecop_microtime) + PHP_FE(timecop_gettimeofday, arginfo_timecop_gettimeofday) +#endif + PHP_FE(timecop_unixtojd, arginfo_timecop_unixtojd) + PHP_FE(timecop_date_create, arginfo_timecop_date_create) + PHP_FE(timecop_date_create_from_format, arginfo_timecop_date_create_from_format) + PHP_FE(timecop_date_create_immutable, arginfo_timecop_date_create_immutable) + PHP_FE(timecop_date_create_immutable_from_format, arginfo_timecop_date_create_immutable_from_format) + {NULL, NULL, NULL} +}; +/* }}} */ + +/* declare method parameters, */ + +/* each method can have its own parameters and visibility */ +static zend_function_entry timecop_funcs_timecop[] = { + PHP_ME_MAPPING(freeze, timecop_freeze, arginfo_timecop_travel, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + PHP_ME_MAPPING(travel, timecop_travel, arginfo_timecop_travel, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + PHP_ME_MAPPING(scale, timecop_scale, arginfo_timecop_scale, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + PHP_ME_MAPPING(return, timecop_return, arginfo_timecop_return, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + {NULL, NULL, NULL} +}; + +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_class_TimecopDateTime_createFromFormat, + ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + {NULL, NULL, NULL} +}; + +static zend_function_entry timecop_funcs_orig_date[] = { + PHP_ME(TimecopOrigDateTime, __construct, arginfo_class_TimecopDateTime___construct, + ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) + {NULL, NULL, NULL} +}; + +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_class_TimecopDateTimeImmutable_createFromFormat, + ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + {NULL, NULL, NULL} +}; + +static zend_function_entry timecop_funcs_orig_immutable[] = { + PHP_ME(TimecopOrigDateTimeImmutable, __construct, arginfo_class_TimecopDateTimeImmutable___construct, + ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) + {NULL, NULL, NULL} +}; + +#define MKTIME_NUM_ARGS 6 + +#define TIMECOP_CALL_FUNCTION(func_name, index_to_fill_timestamp) \ + _timecop_call_function(INTERNAL_FUNCTION_PARAM_PASSTHRU, ORIG_FUNC_NAME(func_name), index_to_fill_timestamp); + +#define TIMECOP_CALL_MKTIME(mktime_func_name, date_func_name) \ + _timecop_call_mktime(INTERNAL_FUNCTION_PARAM_PASSTHRU, ORIG_FUNC_NAME(mktime_func_name), ORIG_FUNC_NAME(date_func_name)); + +static void timecop_globals_ctor(zend_timecop_globals *globals); + +static int register_timecop_classes(); +static int timecop_func_override(); +static int timecop_class_override(); +static int timecop_func_override_clear(); +static int timecop_class_override_clear(); + +static int update_request_time(zend_long unixtime); +static int restore_request_time(); + +static int fill_mktime_params(zval *fill_params, const char *date_function_name, int from); +static int get_formatted_mock_time(zval *time, zval *timezone_obj, zval *retval_time, zval *retval_timezone); +static long get_mock_fraction(zval *time, zval *timezone_obj); + +static void _timecop_call_function(INTERNAL_FUNCTION_PARAMETERS, const char *function_name, int index_to_fill_timestamp); +static void _timecop_call_mktime(INTERNAL_FUNCTION_PARAMETERS, const char *mktime_function_name, const char *date_function_name); + +static int get_mock_timeval(tc_timeval *fixed, const tc_timeval *now); +static inline zend_long mock_timestamp(); + +static int get_timeval_from_datetime(tc_timeval *tp, zval *dt); +static int get_current_time(tc_timeval *now); + +static void _timecop_orig_datetime_constructor(INTERNAL_FUNCTION_PARAMETERS, int immutable); +static inline void _timecop_date_create(INTERNAL_FUNCTION_PARAMETERS, int immutable); +static inline void _timecop_datetime_constructor(INTERNAL_FUNCTION_PARAMETERS, int immutable); +static void _timecop_datetime_constructor_ex(INTERNAL_FUNCTION_PARAMETERS, zval *obj, int immutable); + +static inline zval* _call_php_method_with_0_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr); +static inline zval* _call_php_method_with_1_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, zval *arg1); +static inline zval* _call_php_method_with_2_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, zval *arg1, zval *arg2); +static inline zval* _call_php_method(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, int param_count, zval* arg1, zval* arg2); +static void _call_php_function_with_3_params(const char *function_name, zval *retval_ptr, zval *arg1, zval *arg2, zval *arg3); +static inline void _call_php_function_with_params(const char *function_name, zval *retval_ptr, uint32_t param_count, zval params[]); + +static const zend_module_dep timecop_module_deps[] = { + ZEND_MOD_REQUIRED("Date") + ZEND_MOD_END +}; + +/* {{{ timecop_module_entry + */ +zend_module_entry timecop_module_entry = { + STANDARD_MODULE_HEADER_EX, + NULL, + timecop_module_deps, + "timecop", + timecop_functions, + PHP_MINIT(timecop), + PHP_MSHUTDOWN(timecop), + PHP_RINIT(timecop), + PHP_RSHUTDOWN(timecop), + PHP_MINFO(timecop), +#if ZEND_MODULE_API_NO >= 20010901 + PHP_TIMECOP_VERSION, +#endif + STANDARD_MODULE_PROPERTIES +}; +/* }}} */ + +#ifdef COMPILE_DL_TIMECOP +# ifdef ZTS + ZEND_TSRMLS_CACHE_DEFINE(); +# endif +ZEND_GET_MODULE(timecop) +#endif + +/* {{{ PHP_INI + */ +PHP_INI_BEGIN() + STD_PHP_INI_ENTRY("timecop.func_override", "1", + PHP_INI_SYSTEM, OnUpdateLong, func_override, zend_timecop_globals, timecop_globals) + STD_PHP_INI_ENTRY("timecop.sync_request_time", "1", + PHP_INI_SYSTEM, OnUpdateLong, sync_request_time, zend_timecop_globals, timecop_globals) +PHP_INI_END() +/* }}} */ + +/* {{{ PHP_MINIT_FUNCTION + */ +PHP_MINIT_FUNCTION(timecop) +{ + ZEND_INIT_MODULE_GLOBALS(timecop, timecop_globals_ctor, NULL); + REGISTER_INI_ENTRIES(); + register_timecop_classes(); + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +PHP_MSHUTDOWN_FUNCTION(timecop) +{ + UNREGISTER_INI_ENTRIES(); + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_RINIT_FUNCTION(timecop) */ +PHP_RINIT_FUNCTION(timecop) +{ +#if defined(COMPILE_DL_TIMECOP) && defined(ZTS) + ZEND_TSRMLS_CACHE_UPDATE(); +#endif + + if (TIMECOP_G(func_override)) { + if (SUCCESS != timecop_func_override() || + SUCCESS != timecop_class_override()) { + return FAILURE; + } + } + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_RSHUTDOWN_FUNCTION(timecop) */ +PHP_RSHUTDOWN_FUNCTION(timecop) +{ + if (TIMECOP_G(func_override)) { + timecop_func_override_clear(); + timecop_class_override_clear(); + } + + if (Z_TYPE(TIMECOP_G(orig_request_time)) != IS_NULL) { + restore_request_time(); + } + + TIMECOP_G(timecop_mode) = TIMECOP_MODE_REALTIME; + TIMECOP_G(scaling_factor) = 1; + + return SUCCESS; +} +/* }}} */ + + +/* {{{ PHP_MINFO_FUNCTION + */ +PHP_MINFO_FUNCTION(timecop) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "timecop", "enabled"); + php_info_print_table_row(2, "Version", PHP_TIMECOP_VERSION); + php_info_print_table_end(); + + DISPLAY_INI_ENTRIES(); +} +/* }}} */ + +static int register_timecop_classes() +{ + zend_class_entry ce; + zend_class_entry *tmp, *date_ce, *timezone_ce, *immutable_ce, *interface_ce; + + date_ce = zend_hash_str_find_ptr(CG(class_table), "datetime", sizeof("datetime")-1); + if (date_ce == NULL) { + /* DateTime must be initialized before */ + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't find class %s.", "DateTime"); + return SUCCESS; + } + + timezone_ce = zend_hash_str_find_ptr(CG(class_table), "datetimezone", sizeof("datetimezone")-1); + if (timezone_ce == NULL) { + /* DateTime must be initialized before */ + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't find class %s.", "DateTimeZone"); + return SUCCESS; + } + + immutable_ce = zend_hash_str_find_ptr(CG(class_table), "datetimeimmutable", sizeof("datetimeimmutable")-1); + if (immutable_ce == NULL) { + /* DateTimeImmutable must be initialized before */ + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't find class %s.", "DateTimeImmutable"); + return SUCCESS; + } + + interface_ce = zend_hash_str_find_ptr(CG(class_table), "datetimeinterface", sizeof("datetimeinterface")-1); + if (interface_ce == NULL) { + /* DateTimeInterface must be initialized before */ + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't find interface %s.", "DateTimeInterface"); + return SUCCESS; + } + + INIT_CLASS_ENTRY(ce, "Timecop", timecop_funcs_timecop); + zend_register_internal_class(&ce); + + TIMECOP_G(ce_DateTimeZone) = timezone_ce; + TIMECOP_G(ce_DateTimeInterface) = interface_ce; + + /* replace DateTime */ + INIT_CLASS_ENTRY(ce, "TimecopDateTime", timecop_funcs_date); + tmp = zend_register_internal_class_ex(&ce, date_ce); + tmp->create_object = date_ce->create_object; + + TIMECOP_G(ce_DateTime) = date_ce; + TIMECOP_G(ce_TimecopDateTime) = tmp; + + INIT_CLASS_ENTRY(ce, "TimecopOrigDateTime", timecop_funcs_orig_date); + tmp = zend_register_internal_class_ex(&ce, date_ce); + tmp->create_object = date_ce->create_object; + + /* replace DateTimeImmutable */ + INIT_CLASS_ENTRY(ce, "TimecopDateTimeImmutable", timecop_funcs_immutable); + tmp = zend_register_internal_class_ex(&ce, immutable_ce); + tmp->create_object = immutable_ce->create_object; + + TIMECOP_G(ce_DateTimeImmutable) = immutable_ce; + TIMECOP_G(ce_TimecopDateTimeImmutable) = tmp; + + INIT_CLASS_ENTRY(ce, "TimecopOrigDateTimeImmutable", timecop_funcs_orig_immutable); + tmp = zend_register_internal_class_ex(&ce, immutable_ce); + tmp->create_object = immutable_ce->create_object; + + return SUCCESS; +} + +static int timecop_func_override() +{ + const struct timecop_override_func_entry *p; + zend_function *zf_orig, *zf_ovrd, *zf_save; + + p = &(timecop_override_func_table[0]); + while (p->orig_func != NULL) { + zf_orig = zend_hash_str_find_ptr(CG(function_table), p->orig_func, strlen(p->orig_func)); + if (zf_orig == NULL) { + /* Do nothing. Because some functions are introduced by optional extensions. */ + p++; + continue; + } + + zf_ovrd = zend_hash_str_find_ptr(CG(function_table), p->ovrd_func, strlen(p->ovrd_func)); + if (zf_ovrd == NULL) { + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't find function %s.", p->ovrd_func); + p++; + continue; + } + + zf_save = zend_hash_str_find_ptr(CG(function_table), p->save_func, strlen(p->save_func)); + if (zf_save != NULL) { + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't create function %s because already exists.", + p->save_func); + p++; + continue; + } + + TIMECOP_ASSERT(zf_orig->type == ZEND_INTERNAL_FUNCTION); + TIMECOP_ASSERT(zf_ovrd->type == ZEND_INTERNAL_FUNCTION); + + zend_hash_str_add_mem(CG(function_table), p->save_func, strlen(p->save_func), + zf_orig, sizeof(zend_function)); + function_add_ref(zf_orig); + + FIX_FUNCTION_ARG_INFO_DTOR(zf_orig); + zend_hash_str_update_mem(CG(function_table), p->orig_func, strlen(p->orig_func), + zf_ovrd, sizeof(zend_function)); + function_add_ref(zf_ovrd); + + p++; + } + return SUCCESS; +} + +static int timecop_class_override() +{ + const struct timecop_override_class_entry *p; + zend_class_entry *ce_orig, *ce_ovrd; + zend_function *zf_orig, *zf_ovrd, *zf_save, *zf_new; + + p = &(timecop_override_class_table[0]); + while (p->orig_class != NULL) { + ce_orig = zend_hash_str_find_ptr(CG(class_table), p->orig_class, strlen(p->orig_class)); + if (ce_orig == NULL) { + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't find class %s.", p->orig_class); + p++; + continue; + } + + ce_ovrd = zend_hash_str_find_ptr(CG(class_table), p->ovrd_class, strlen(p->ovrd_class)); + if (ce_ovrd == NULL) { + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't find class %s.", p->ovrd_class); + p++; + continue; + } + + zf_orig = zend_hash_str_find_ptr(&ce_orig->function_table, + p->orig_method, strlen(p->orig_method)); + if (zf_orig == NULL) { + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't find method %s::%s.", + p->orig_class, p->orig_method); + p++; + continue; + } + + zf_ovrd = zend_hash_str_find_ptr(&ce_ovrd->function_table, + p->orig_method, strlen(p->orig_method)); + if (zf_ovrd == NULL) { + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't find method %s::%s.", + p->ovrd_class, p->orig_method); + p++; + continue; + } + + zf_save = zend_hash_str_find_ptr(&ce_orig->function_table, + p->save_method, strlen(p->save_method)); + if (zf_save != NULL) { + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't create method %s::%s because already exists.", + p->orig_class, p->save_method); + p++; + continue; + } + + TIMECOP_ASSERT(zf_orig->type == ZEND_INTERNAL_FUNCTION); + TIMECOP_ASSERT(ce_orig->type & ZEND_INTERNAL_CLASS); + TIMECOP_ASSERT(zf_ovrd->type == ZEND_INTERNAL_FUNCTION); + TIMECOP_ASSERT(ce_ovrd->type & ZEND_INTERNAL_CLASS); + + zend_hash_str_add_mem(&ce_orig->function_table, + p->save_method, strlen(p->save_method), + zf_orig, sizeof(zend_function)); + function_add_ref(zf_orig); + + zf_new = zend_hash_str_update_mem(&ce_orig->function_table, + p->orig_method, strlen(p->orig_method), + zf_ovrd, sizeof(zend_function)); + function_add_ref(zf_ovrd); + + TIMECOP_ASSERT(zf_new != NULL); + TIMECOP_ASSERT(zf_new != zf_orig); + + if (strcmp(p->orig_method, "__construct") == 0) { + ce_orig->constructor = zf_new; + } + p++; + } + return SUCCESS; +} + +/* clear function overriding. */ +static int timecop_func_override_clear() +{ + const struct timecop_override_func_entry *p; + zend_function *zf_orig, *zf_ovld; + + p = &(timecop_override_func_table[0]); + while (p->orig_func != NULL) { + zf_orig = zend_hash_str_find_ptr(CG(function_table), + p->save_func, strlen(p->save_func)); + zf_ovld = zend_hash_str_find_ptr(CG(function_table), + p->orig_func, strlen(p->orig_func)); + if (zf_orig == NULL || zf_ovld == NULL) { + p++; + continue; + } + + FIX_FUNCTION_ARG_INFO_DTOR(zf_ovld); + zend_hash_str_update_mem(CG(function_table), p->orig_func, strlen(p->orig_func), + zf_orig, sizeof(zend_function)); + function_add_ref(zf_orig); + + FIX_FUNCTION_ARG_INFO_DTOR(zf_orig); + zend_hash_str_del(CG(function_table), p->save_func, strlen(p->save_func)); + + p++; + } + return SUCCESS; +} + +static int timecop_class_override_clear() +{ + const struct timecop_override_class_entry *p; + zend_class_entry *ce_orig; + zend_function *zf_orig; + + p = &(timecop_override_class_table[0]); + while (p->orig_class != NULL) { + ce_orig = zend_hash_str_find_ptr(CG(class_table), + p->orig_class, strlen(p->orig_class)); + if (ce_orig == NULL) { + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't find class %s.", p->orig_class); + p++; + continue; + } + + zf_orig = zend_hash_str_find_ptr(&ce_orig->function_table, + p->save_method, strlen(p->save_method)); + if (zf_orig == NULL) { + php_error_docref("https://github.com/ diff --git a/timecop_php8.dep b/timecop_php8.dep new file mode 100644 index 0000000..de4e7a8 --- /dev/null +++ b/timecop_php8.dep @@ -0,0 +1,95 @@ +timecop_php8.lo: /home/sylvain/projects/php-timecop/timecop_php8.c \ + /home/sylvain/projects/php-timecop/config.h \ + /usr/include/php/20230831/main/php.h \ + /usr/include/php/20230831/main/php_version.h \ + /usr/include/php/20230831/Zend/zend.h \ + /usr/include/php/20230831/Zend/zend_types.h \ + /usr/include/php/20230831/Zend/zend_portability.h \ + /usr/include/php/20230831/Zend/zend_config.h \ + /usr/include/php/20230831/main/../main/php_config.h \ + /usr/include/php/20230831/Zend/../TSRM/TSRM.h \ + /usr/include/php/20230831/main/php_config.h \ + /usr/include/php/20230831/Zend/zend_range_check.h \ + /usr/include/php/20230831/Zend/zend_long.h \ + /usr/include/php/20230831/Zend/zend_map_ptr.h \ + /usr/include/php/20230831/Zend/zend_errors.h \ + /usr/include/php/20230831/Zend/zend_alloc.h \ + /usr/include/php/20230831/Zend/zend.h \ + /usr/include/php/20230831/Zend/zend_alloc_sizes.h \ + /usr/include/php/20230831/Zend/zend_llist.h \ + /usr/include/php/20230831/Zend/zend_string.h \ + /usr/include/php/20230831/Zend/zend_hash.h \ + /usr/include/php/20230831/Zend/zend_sort.h \ + /usr/include/php/20230831/Zend/zend_ast.h \ + /usr/include/php/20230831/Zend/zend_gc.h \ + /usr/include/php/20230831/Zend/zend_hrtime.h \ + /usr/include/php/20230831/Zend/zend_variables.h \ + /usr/include/php/20230831/Zend/zend_iterators.h \ + /usr/include/php/20230831/Zend/zend_stream.h \ + /usr/include/php/20230831/Zend/zend_smart_str_public.h \ + /usr/include/php/20230831/Zend/zend_smart_string_public.h \ + /usr/include/php/20230831/Zend/zend_signal.h \ + /usr/include/php/20230831/Zend/zend_max_execution_timer.h \ + /usr/include/php/20230831/Zend/zend_object_handlers.h \ + /usr/include/php/20230831/Zend/zend_operators.h \ + /usr/include/php/20230831/Zend/zend_strtod.h \ + /usr/include/php/20230831/Zend/zend_multiply.h \ + /usr/include/php/20230831/Zend/zend_sort.h \ + /usr/include/php/20230831/main/php_compat.h \ + /usr/include/php/20230831/main/php_config.h \ + /usr/include/php/20230831/Zend/zend_API.h \ + /usr/include/php/20230831/Zend/zend_modules.h \ + /usr/include/php/20230831/Zend/zend_compile.h \ + /usr/include/php/20230831/Zend/zend_globals.h \ + /usr/include/php/20230831/Zend/zend_globals_macros.h \ + /usr/include/php/20230831/Zend/zend_atomic.h \ + /usr/include/php/20230831/Zend/zend_stack.h \ + /usr/include/php/20230831/Zend/zend_ptr_stack.h \ + /usr/include/php/20230831/Zend/zend_objects.h \ + /usr/include/php/20230831/Zend/zend_objects_API.h \ + /usr/include/php/20230831/Zend/zend_float.h \ + /usr/include/php/20230831/Zend/zend_multibyte.h \ + /usr/include/php/20230831/Zend/zend_arena.h \ + /usr/include/php/20230831/Zend/zend_call_stack.h \ + /usr/include/php/20230831/Zend/zend_vm_opcodes.h \ + /usr/include/php/20230831/Zend/zend_build.h \ + /usr/include/php/20230831/Zend/zend_list.h \ + /usr/include/php/20230831/Zend/zend_execute.h \ + /usr/include/php/20230831/Zend/zend_type_info.h \ + /usr/include/php/20230831/main/build-defs.h \ + /usr/include/php/20230831/Zend/zend_hash.h \ + /usr/include/php/20230831/Zend/zend_alloc.h \ + /usr/include/php/20230831/Zend/zend_stack.h \ + /usr/include/php/20230831/main/snprintf.h \ + /usr/include/php/20230831/main/spprintf.h \ + /usr/include/php/20230831/Zend/zend_smart_str_public.h \ + /usr/include/php/20230831/Zend/zend_smart_string_public.h \ + /usr/include/php/20230831/main/php_syslog.h \ + /usr/include/php/20230831/main/php.h \ + /usr/include/php/20230831/main/php_output.h \ + /usr/include/php/20230831/main/php_streams.h \ + /usr/include/php/20230831/Zend/zend_stream.h \ + /usr/include/php/20230831/main/streams/php_stream_context.h \ + /usr/include/php/20230831/main/streams/php_stream_filter_api.h \ + /usr/include/php/20230831/main/streams/php_stream_transport.h \ + /usr/include/php/20230831/main/streams/php_stream_plain_wrapper.h \ + /usr/include/php/20230831/main/streams/php_stream_glob_wrapper.h \ + /usr/include/php/20230831/main/streams/php_stream_userspace.h \ + /usr/include/php/20230831/main/streams/php_stream_mmap.h \ + /usr/include/php/20230831/main/php_memory_streams.h \ + /usr/include/php/20230831/main/fopen_wrappers.h \ + /usr/include/php/20230831/main/php_globals.h \ + /usr/include/php/20230831/Zend/zend_globals.h \ + /usr/include/php/20230831/main/php_ini.h \ + /usr/include/php/20230831/Zend/zend_ini.h \ + /usr/include/php/20230831/Zend/zend_virtual_cwd.h \ + /usr/include/php/20230831/TSRM/TSRM.h \ + /usr/include/php/20230831/Zend/zend_constants.h \ + /usr/include/php/20230831/main/php_reentrancy.h \ + /usr/include/php/20230831/main/php_ini.h \ + /usr/include/php/20230831/ext/standard/info.h \ + /home/sylvain/projects/php-timecop/php_timecop.h \ + /usr/include/php/20230831/Zend/zend_interfaces.h \ + /usr/include/php/20230831/Zend/zend_API.h \ + /home/sylvain/projects/php-timecop/tc_timeval.h \ + /home/sylvain/projects/php-timecop/timecop_php8_arginfo.h From 761239cbb254823ed512bd170f307f152c4bdc3e Mon Sep 17 00:00:00 2001 From: Sylvain MSL Date: Thu, 20 Mar 2025 15:48:09 +0100 Subject: [PATCH 4/7] Only support PHP 8.4+ --- timecop_php7.dep | 96 ---- timecop_php8.c | 1092 +++++++++++++++++++++++++++++++++++++++++++++- timecop_php8.dep | 191 ++++---- 3 files changed, 1187 insertions(+), 192 deletions(-) delete mode 100644 timecop_php7.dep diff --git a/timecop_php7.dep b/timecop_php7.dep deleted file mode 100644 index 9baec60..0000000 --- a/timecop_php7.dep +++ /dev/null @@ -1,96 +0,0 @@ -timecop_php7.lo: /app/timecop_php7.c /app/config.h \ - /usr/include/php/20240924/main/php.h \ - /usr/include/php/20240924/main/php_version.h \ - /usr/include/php/20240924/Zend/zend.h \ - /usr/include/php/20240924/Zend/zend_types.h \ - /usr/include/php/20240924/Zend/zend_portability.h \ - /usr/include/php/20240924/Zend/zend_config.h \ - /usr/include/php/20240924/main/../main/php_config.h \ - /usr/include/php/20240924/Zend/../TSRM/TSRM.h \ - /usr/include/php/20240924/main/php_config.h \ - /usr/include/php/20240924/Zend/zend_range_check.h \ - /usr/include/php/20240924/Zend/zend_long.h \ - /usr/include/php/20240924/Zend/zend_map_ptr.h \ - /usr/include/php/20240924/Zend/zend_errors.h \ - /usr/include/php/20240924/Zend/zend_alloc.h \ - /usr/include/php/20240924/Zend/zend_alloc_sizes.h \ - /usr/include/php/20240924/Zend/zend_llist.h \ - /usr/include/php/20240924/Zend/zend_string.h \ - /usr/include/php/20240924/Zend/zend_gc.h \ - /usr/include/php/20240924/Zend/zend_hrtime.h \ - /usr/include/php/20240924/Zend/zend_hash.h \ - /usr/include/php/20240924/Zend/zend_sort.h \ - /usr/include/php/20240924/Zend/zend_ast.h \ - /usr/include/php/20240924/Zend/zend_variables.h \ - /usr/include/php/20240924/Zend/zend_iterators.h \ - /usr/include/php/20240924/Zend/zend_stream.h \ - /usr/include/php/20240924/Zend/zend_smart_str_public.h \ - /usr/include/php/20240924/Zend/zend_smart_string_public.h \ - /usr/include/php/20240924/Zend/zend_signal.h \ - /usr/include/php/20240924/Zend/zend_max_execution_timer.h \ - /usr/include/php/20240924/Zend/zend_object_handlers.h \ - /usr/include/php/20240924/Zend/zend_property_hooks.h \ - /usr/include/php/20240924/Zend/zend_lazy_objects.h \ - /usr/include/php/20240924/Zend/zend_types.h \ - /usr/include/php/20240924/Zend/zend_operators.h \ - /usr/include/php/20240924/Zend/zend_strtod.h \ - /usr/include/php/20240924/Zend/zend_multiply.h \ - /usr/include/php/20240924/Zend/zend_sort.h \ - /usr/include/php/20240924/main/php_compat.h \ - /usr/include/php/20240924/main/php_config.h \ - /usr/include/php/20240924/Zend/zend_API.h \ - /usr/include/php/20240924/Zend/zend_modules.h \ - /usr/include/php/20240924/Zend/zend.h \ - /usr/include/php/20240924/Zend/zend_compile.h \ - /usr/include/php/20240924/Zend/zend_frameless_function.h \ - /usr/include/php/20240924/Zend/zend_globals.h \ - /usr/include/php/20240924/Zend/zend_globals_macros.h \ - /usr/include/php/20240924/Zend/zend_atomic.h \ - /usr/include/php/20240924/Zend/zend_stack.h \ - /usr/include/php/20240924/Zend/zend_ptr_stack.h \ - /usr/include/php/20240924/Zend/zend_objects.h \ - /usr/include/php/20240924/Zend/zend_objects_API.h \ - /usr/include/php/20240924/Zend/zend_float.h \ - /usr/include/php/20240924/Zend/zend_multibyte.h \ - /usr/include/php/20240924/Zend/zend_arena.h \ - /usr/include/php/20240924/Zend/zend_call_stack.h \ - /usr/include/php/20240924/Zend/zend_vm_opcodes.h \ - /usr/include/php/20240924/Zend/zend_build.h \ - /usr/include/php/20240924/Zend/zend_list.h \ - /usr/include/php/20240924/Zend/zend_execute.h \ - /usr/include/php/20240924/Zend/zend_type_info.h \ - /usr/include/php/20240924/main/build-defs.h \ - /usr/include/php/20240924/Zend/zend_hash.h \ - /usr/include/php/20240924/Zend/zend_alloc.h \ - /usr/include/php/20240924/Zend/zend_stack.h \ - /usr/include/php/20240924/main/snprintf.h \ - /usr/include/php/20240924/main/spprintf.h \ - /usr/include/php/20240924/Zend/zend_smart_str_public.h \ - /usr/include/php/20240924/Zend/zend_smart_string_public.h \ - /usr/include/php/20240924/main/php_syslog.h \ - /usr/include/php/20240924/main/php.h \ - /usr/include/php/20240924/main/php_output.h \ - /usr/include/php/20240924/main/php_streams.h \ - /usr/include/php/20240924/Zend/zend_stream.h \ - /usr/include/php/20240924/main/streams/php_stream_context.h \ - /usr/include/php/20240924/main/streams/php_stream_filter_api.h \ - /usr/include/php/20240924/main/streams/php_stream_transport.h \ - /usr/include/php/20240924/main/streams/php_stream_plain_wrapper.h \ - /usr/include/php/20240924/main/streams/php_stream_glob_wrapper.h \ - /usr/include/php/20240924/main/streams/php_stream_userspace.h \ - /usr/include/php/20240924/main/streams/php_stream_mmap.h \ - /usr/include/php/20240924/main/php_memory_streams.h \ - /usr/include/php/20240924/main/fopen_wrappers.h \ - /usr/include/php/20240924/main/php_globals.h \ - /usr/include/php/20240924/Zend/zend_globals.h \ - /usr/include/php/20240924/main/php_ini.h \ - /usr/include/php/20240924/Zend/zend_ini.h \ - /usr/include/php/20240924/Zend/zend_virtual_cwd.h \ - /usr/include/php/20240924/TSRM/TSRM.h \ - /usr/include/php/20240924/Zend/zend_constants.h \ - /usr/include/php/20240924/main/php_reentrancy.h \ - /usr/include/php/20240924/main/php_ini.h \ - /usr/include/php/20240924/ext/standard/info.h /app/php_timecop.h \ - /usr/include/php/20240924/Zend/zend_interfaces.h \ - /usr/include/php/20240924/Zend/zend_API.h /app/tc_timeval.h \ - /app/timecop_php8_arginfo.h diff --git a/timecop_php8.c b/timecop_php8.c index f477511..d7235e8 100644 --- a/timecop_php8.c +++ b/timecop_php8.c @@ -579,4 +579,1094 @@ static int timecop_class_override_clear() zf_orig = zend_hash_str_find_ptr(&ce_orig->function_table, p->save_method, strlen(p->save_method)); if (zf_orig == NULL) { - php_error_docref("https://github.com/ + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't find method %s::%s.", + p->orig_class, p->save_method); + p++; + continue; + } + + zend_hash_str_update_mem(&ce_orig->function_table, p->orig_method, strlen(p->orig_method), + zf_orig, sizeof(zend_function)); + function_add_ref(zf_orig); + + zend_hash_str_del(&ce_orig->function_table, p->save_method, strlen(p->save_method)); + + if (strcmp(p->orig_method, "__construct") == 0) { + ce_orig->constructor = zf_orig; + } + p++; + } + return SUCCESS; +} + +static int update_request_time(zend_long unixtime) +{ + zval *server_vars, *request_time, tmp; + + server_vars = zend_hash_str_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER")-1); + if (server_vars != NULL && Z_TYPE_P(server_vars) == IS_ARRAY) { + request_time = zend_hash_str_find(Z_ARRVAL_P(server_vars), "REQUEST_TIME", sizeof("REQUEST_TIME")-1); + if (request_time != NULL) { + if (Z_TYPE(TIMECOP_G(orig_request_time)) == IS_NULL) { + ZVAL_COPY_VALUE(&TIMECOP_G(orig_request_time), request_time); + } + } + ZVAL_LONG(&tmp, unixtime); + zend_hash_str_update(Z_ARRVAL_P(server_vars), + "REQUEST_TIME", sizeof("REQUEST_TIME")-1, + &tmp); + } + + return SUCCESS; +} + +static int restore_request_time() +{ + zval *server_vars; + + server_vars = zend_hash_str_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER")-1); + if (Z_TYPE(TIMECOP_G(orig_request_time)) != IS_NULL && + server_vars != NULL && Z_TYPE_P(server_vars) == IS_ARRAY) { + zend_hash_str_update(Z_ARRVAL_P(server_vars), + "REQUEST_TIME", sizeof("REQUEST_TIME")-1, + &TIMECOP_G(orig_request_time)); + ZVAL_NULL(&TIMECOP_G(orig_request_time)); + } + + return SUCCESS; +} + +static int fill_mktime_params(zval *fill_params, const char *date_function_name, int from) +{ + char *formats[MKTIME_NUM_ARGS] = {"H", "i", "s", "n", "j", "Y"}; + zval format, timestamp; + int i; + + ZVAL_LONG(×tamp, mock_timestamp()); + + for (i = from; i < MKTIME_NUM_ARGS; i++) { + ZVAL_STRING(&format, formats[i]); + call_php_function_with_2_params(date_function_name, &fill_params[i], &format, ×tamp); + zval_ptr_dtor(&format); + } + + return MKTIME_NUM_ARGS; +} + +/* + * get_formatted_mock_time() : return formatted mock time like "2000-12-30 01:02:03.456000" + * + * pseudo code: + * + * function get_formatted_mock_time($time, $timezone_obj) { + * if ($time === null || $time === false || $time === "") { + * $time = "now"; + * } + * $now = get_mock_timeval(); + * if ($timezone_obj) { + * // save default timezone + * $zonename = $timezone_obj->getName() + * $orig_zonename = date_default_timezone_get(); + * date_default_timezone_set($zonename); + * } + * $fixed_sec = strtotime($time, $now->sec); + * if ($timezone_obj && $orig_zonename) { + * // restore default timezone + * date_default_timezone_set($orig_zonename); + * } + * if ($fixed_sec === FALSE) { + * return false; + * } + * $fixed_usec = get_mock_fraction($time, $timezone_obj); + * if ($fixed_usec === -1) { + * $fixed_usec = $now->usec; + * } + * $dt = date_create($time, $timezone_obj); + * $dt->setTimestamp($fixed_sec); + * $format = sprintf("Y-m-d H:i:s.%06d", $fixed_usec); + * $formatted_time = $dt->format($format); + * return $formatted_time; + * } + */ +static int get_formatted_mock_time(zval *time, zval *timezone_obj, zval *retval_time, zval *retval_timezone) +{ + zval fixed_sec, orig_zonename; + zval now_timestamp, str_now; + tc_timeval now; + zend_long fixed_usec; + + if (TIMECOP_G(timecop_mode) == TIMECOP_MODE_REALTIME) { + ZVAL_FALSE(retval_time); + ZVAL_NULL(retval_timezone); + return -1; + } + + if (time == NULL || Z_TYPE_P(time) == IS_NULL || + Z_TYPE_P(time) == IS_FALSE || + (Z_TYPE_P(time) == IS_STRING && Z_STRLEN_P(time) == 0)) { + ZVAL_STRING(&str_now, "now"); + time = &str_now; + } + + get_mock_timeval(&now, NULL); + + // @todo Restore removed timezone handling code? https://github.com/kiddivouchers/php-timecop/pull/6 + + ZVAL_LONG(&now_timestamp, now.sec); + call_php_function_with_2_params(ORIG_FUNC_NAME("strtotime"), &fixed_sec, time, &now_timestamp); + + if (Z_TYPE(fixed_sec) == IS_FALSE) { + ZVAL_FALSE(retval_time); + ZVAL_NULL(retval_timezone); + return -1; + } + + fixed_usec = get_mock_fraction(time, timezone_obj); + if (fixed_usec == -1) { + fixed_usec = now.usec; + } + + { + zval dt; + zval format_str; + + char buf[64]; + + call_php_function_with_2_params(ORIG_FUNC_NAME("date_create"), &dt, time, timezone_obj); + if (Z_TYPE(dt) == IS_FALSE) { + ZVAL_FALSE(retval_time); + ZVAL_NULL(retval_timezone); + return -1; + } + sprintf(buf, "Y-m-d H:i:s.%06ld", fixed_usec); + ZVAL_STRING(&format_str, buf); + call_php_method_with_1_params(&dt, TIMECOP_G(ce_DateTime), "settimestamp", NULL, &fixed_sec); + call_php_method_with_0_params(&dt, TIMECOP_G(ce_DateTime), "gettimezone", retval_timezone); + call_php_method_with_1_params(&dt, TIMECOP_G(ce_DateTime), "format", retval_time, &format_str); + zval_ptr_dtor(&fixed_sec); + zval_ptr_dtor(&format_str); + zval_ptr_dtor(&dt); + } + + if (time == &str_now) { + zval_ptr_dtor(&str_now); + } + return 0; +} + +/* + * get_mock_fraction() + * + * pseudo code: + * + * function get_mock_fraction($time, $timezone_obj) { + * $dt1 = date_create($time, $timezone_obj); + * usleep(1); + * $dt2 = date_create($time, $timezone_obj); + * $usec1 = $dt1->format("u"); + * $usec2 = $dt2->format("u"); + * if ($usec1 === $usec2) { + * $fixed_usec = $usec1; + * } else { + * $fixed_usec = -1; + * } + * return $fixed_usec; + * } + */ +static long get_mock_fraction(zval *time, zval *timezone_obj TSRMLS_DC) +{ + zval dt1, dt2, usec1, usec2; + zend_long fixed_usec; + zval u_str, sleep_usec; + + call_php_function_with_2_params(ORIG_FUNC_NAME("date_create"), &dt1, time, timezone_obj); + if (Z_TYPE(dt1) == IS_FALSE) { + return -1; + } + + ZVAL_LONG(&sleep_usec, 1); + call_php_function_with_1_params("usleep", NULL, &sleep_usec); + + call_php_function_with_2_params(ORIG_FUNC_NAME("date_create"), &dt2, time, timezone_obj); + if (Z_TYPE(dt2) == IS_FALSE) { + zval_ptr_dtor(&dt1); + return -1; + } + ZVAL_STRING(&u_str, "u"); + call_php_method_with_1_params(&dt1, TIMECOP_G(ce_DateTime), "format", &usec1, &u_str); + call_php_method_with_1_params(&dt2, TIMECOP_G(ce_DateTime), "format", &usec2, &u_str); + convert_to_long(&usec1); + convert_to_long(&usec2); + + if (Z_LVAL(usec1) == Z_LVAL(usec2)) { + fixed_usec = Z_LVAL(usec1); + } else { + fixed_usec = -1; + } + zval_ptr_dtor(&dt1); + zval_ptr_dtor(&dt2); + zval_ptr_dtor(&u_str); + + return fixed_usec; +} + + +static void _timecop_call_function(INTERNAL_FUNCTION_PARAMETERS, const char *function_name, int index_to_fill_timestamp) +{ + zval *params; + uint32_t param_count; + + param_count = MAX(ZEND_NUM_ARGS(), index_to_fill_timestamp+1); + params = (zval *)safe_emalloc(param_count, sizeof(zval), 0); + + if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), params) == FAILURE) { + efree(params); + return; + } + + param_count = ZEND_NUM_ARGS(); + if (param_count == index_to_fill_timestamp) { + ZVAL_LONG(¶ms[param_count], mock_timestamp()); + param_count++; + } + + _call_php_function_with_params(function_name, return_value, param_count, params); + + efree(params); +} + +/* {{{ _timecop_call_mktime - timecop_(gm)mktime helper */ +static void _timecop_call_mktime(INTERNAL_FUNCTION_PARAMETERS, const char *mktime_function_name, const char *date_function_name) +{ + zval *params; + uint32_t param_count; + + int i; + + param_count = MAX(ZEND_NUM_ARGS(), MKTIME_NUM_ARGS); + params = (zval *)safe_emalloc(param_count, sizeof(zval), 0); + + if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), params) == FAILURE) { + efree(params); + zend_throw_error(NULL, "Cannot get arguments for calling"); + return; + } + + param_count = ZEND_NUM_ARGS(); + if (param_count < MKTIME_NUM_ARGS) { + fill_mktime_params(params, date_function_name, param_count); + param_count = MKTIME_NUM_ARGS; + } + + if (ZEND_NUM_ARGS() == 0) { + php_error_docref(NULL, E_DEPRECATED, "You should be using the time() function instead"); + } + + _call_php_function_with_params(mktime_function_name, return_value, param_count, params); + + for (i = ZEND_NUM_ARGS(); i < MKTIME_NUM_ARGS; i++) { + zval_ptr_dtor(¶ms[i]); + } + efree(params); +} +/* }}} */ + +/* {{{ proto int timecop_freeze(long timestamp) + Time travel to specified timestamp and freeze */ +PHP_FUNCTION(timecop_freeze) +{ + zval *dt; + zend_long timestamp; + tc_timeval freezed_tv; + + if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "O", &dt, TIMECOP_G(ce_DateTimeInterface)) != FAILURE) { + get_timeval_from_datetime(&freezed_tv, dt); + } else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "l", ×tamp) != FAILURE) { + freezed_tv.sec = timestamp; + freezed_tv.usec = 0; + } else { + php_error_docref(NULL, E_WARNING, "This function accepts either (DateTimeInterface) OR (int) as arguments."); + RETURN_FALSE; + } + + TIMECOP_G(timecop_mode) = TIMECOP_MODE_FREEZE; + TIMECOP_G(freezed_time) = freezed_tv; + + if (TIMECOP_G(sync_request_time)){ + update_request_time(freezed_tv.sec); + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto int timecop_travel(long timestamp) + Time travel to specified timestamp */ +PHP_FUNCTION(timecop_travel) +{ + zval *dt; + zend_long timestamp; + tc_timeval now, mock_tv; + + if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "O", &dt, TIMECOP_G(ce_DateTimeInterface)) != FAILURE) { + get_timeval_from_datetime(&mock_tv, dt); + } else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "l", ×tamp) != FAILURE) { + mock_tv.sec = timestamp; + mock_tv.usec = 0; + } else { + php_error_docref(NULL, E_WARNING, "This function accepts either (DateTimeInterface) OR (int) as arguments."); + RETURN_FALSE; + } + + TIMECOP_G(timecop_mode) = TIMECOP_MODE_TRAVEL; + get_current_time(&now); + tc_timeval_sub(&TIMECOP_G(travel_offset), &mock_tv, &now); + TIMECOP_G(travel_origin) = now; + + if (TIMECOP_G(sync_request_time)){ + update_request_time(mock_tv.sec); + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto int timecop_scale(long scale) + Accelerate time with specified scale */ +PHP_FUNCTION(timecop_scale) +{ + zend_long scale; + tc_timeval now, mock_time; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &scale) == FAILURE) { + RETURN_FALSE; + } + if (scale < 0) { + RETURN_FALSE; + } + get_current_time(&now); + get_mock_timeval(&mock_time, &now); + TIMECOP_G(timecop_mode) = TIMECOP_MODE_TRAVEL; + TIMECOP_G(travel_origin) = now; + tc_timeval_sub(&TIMECOP_G(travel_offset), &mock_time, &now); + TIMECOP_G(scaling_factor) = scale; + + if (TIMECOP_G(sync_request_time)){ + update_request_time(mock_time.sec); + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto int timecop_return(void) + Return to Time travel to specified timestamp */ +PHP_FUNCTION(timecop_return) +{ + TIMECOP_G(timecop_mode) = TIMECOP_MODE_REALTIME; + + if (TIMECOP_G(sync_request_time)){ + restore_request_time(); + } + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto int timecop_time(void) + Return virtual timestamp */ +PHP_FUNCTION(timecop_time) +{ + RETURN_LONG(mock_timestamp()); +} +/* }}} */ + +/* {{{ proto int timecop_mktime([int hour [, int min [, int sec [, int mon [, int day [, int year]]]]]]) + Get UNIX timestamp for a date */ +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) + Z_PARAM_LONG(min) + Z_PARAM_LONG(sec) + Z_PARAM_LONG(mon) + Z_PARAM_LONG(day) + Z_PARAM_LONG(yea) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); +#endif + + TIMECOP_CALL_MKTIME("mktime", "date"); +} +/* }}} */ + +/* {{{ proto int timecop_gmmktime([int hour [, int min [, int sec [, int mon [, int day [, int year]]]]]]) + Get UNIX timestamp for a GMT date */ +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) + Z_PARAM_LONG(min) + Z_PARAM_LONG(sec) + Z_PARAM_LONG(mon) + Z_PARAM_LONG(day) + Z_PARAM_LONG(yea) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); +#endif + + TIMECOP_CALL_MKTIME("gmmktime", "gmdate"); +} +/* }}} */ + +/* {{{ proto string timecop_date(string format [, long timestamp]) + Format a local date/time */ +PHP_FUNCTION(timecop_date) +{ + zend_string *format; + zend_long ts; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(format) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(ts) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + TIMECOP_CALL_FUNCTION("date", 1); +} +/* }}} */ + +/* {{{ proto string timecop_gmdate(string format [, long timestamp]) + Format a GMT date/time */ +PHP_FUNCTION(timecop_gmdate) +{ + zend_string *format; + zend_long ts; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(format) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(ts) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + TIMECOP_CALL_FUNCTION("gmdate", 1); +} +/* }}} */ + +/* {{{ proto int timecop_idate(string format [, int timestamp]) + Format a local time/date as integer */ +PHP_FUNCTION(timecop_idate) +{ + zend_string *format; + zend_long ts; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(format) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(ts) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + TIMECOP_CALL_FUNCTION("idate", 1); +} +/* }}} */ + +/* {{{ proto array timecop_getdate([int timestamp]) + Get date/time information */ +PHP_FUNCTION(timecop_getdate) +{ + zend_long ts; + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(ts) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + TIMECOP_CALL_FUNCTION("getdate", 0); +} +/* }}} */ + +/* {{{ proto array timecop_localtime([int timestamp [, bool associative_array]]) + Returns the results of the C system call localtime as an associative array if + the associative_array argument is set to 1 other wise it is a regular array */ +PHP_FUNCTION(timecop_localtime) +{ + zend_long timestamp; + zend_bool associative; + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(timestamp) + Z_PARAM_BOOL(associative) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + TIMECOP_CALL_FUNCTION("localtime", 0); +} +/* }}} */ + +/* {{{ proto int timecop_strtotime(string time [, int now ]) + Convert string representation of date and time to a timestamp */ +PHP_FUNCTION(timecop_strtotime) +{ + zend_string *times; + zend_long preset_ts; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(times) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(preset_ts) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + TIMECOP_CALL_FUNCTION("strtotime", 1); +} +/* }}} */ + +/* {{{ proto string timecop_strftime(string format [, int timestamp]) + Format a local time/date according to locale settings */ +PHP_FUNCTION(timecop_strftime) +{ + zend_string *format; + zend_long timestamp; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(format) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(timestamp) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + TIMECOP_CALL_FUNCTION("strftime", 1); +} +/* }}} */ + +/* {{{ proto string timecop_gmstrftime(string format [, int timestamp]) + Format a GMT/UCT time/date according to locale settings */ +PHP_FUNCTION(timecop_gmstrftime) +{ + zend_string *format; + zend_long timestamp = 0; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(format) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(timestamp) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + TIMECOP_CALL_FUNCTION("gmstrftime", 1); +} +/* }}} */ + +/* + * get_mock_timeval(fixed, now) + * + * + * delta + * |<----------------------->| + * travel_offset delta * scaling_factor + * |<------------->|<------------------------------------------------->| + * ==@===============@=========@=========================================@== + * ^ ^ ^ + * | | | + * travel_origin orig_time traveled_time + * + * + * delta = orig_time - travel_origin + * traveled_time = travel_origin + travel_offset + delta * scaling_factor + */ +static int get_mock_timeval(tc_timeval *fixed, const tc_timeval *now) +{ + if (TIMECOP_G(timecop_mode) == TIMECOP_MODE_FREEZE) { + *fixed = TIMECOP_G(freezed_time); + } else if (TIMECOP_G(timecop_mode) == TIMECOP_MODE_TRAVEL) { + tc_timeval delta, origin = TIMECOP_G(travel_origin); + zend_long scale = TIMECOP_G(scaling_factor); + if (now == NULL) { + get_current_time(&delta); + } else { + delta = *now; + } + tc_timeval_sub(&delta, &delta, &origin); + tc_timeval_mul(&delta, &delta, scale); + tc_timeval_add(fixed, &origin, &TIMECOP_G(travel_offset)); + tc_timeval_add(fixed, fixed, &delta); + } else { + if (now == NULL) { + get_current_time(fixed); + } else { + *fixed = *now; + } + } + return 0; +} + +static zend_long mock_timestamp() +{ + tc_timeval tv; + get_mock_timeval(&tv, NULL); + return tv.sec; +} + +static int get_timeval_from_datetime(tc_timeval *tp, zval *dt) +{ + zval sec, usec; + zval u_str; + + call_php_method_with_0_params(dt, Z_OBJCE_P(dt), "gettimestamp", &sec); + ZVAL_STRING(&u_str, "u"); + call_php_method_with_1_params(dt, Z_OBJCE_P(dt), "format", &usec, &u_str); + zval_ptr_dtor(&u_str); + convert_to_long(&usec); + + tp->sec = Z_LVAL(sec); + tp->usec = Z_LVAL(usec); + + return 0; +} + +static int get_current_time(tc_timeval *now) +{ + int ret = 0; +#if HAVE_GETTIMEOFDAY + struct timeval tv; + ret = gettimeofday(&tv, NULL); + if (ret == 0) { + now->sec = (zend_long)tv.tv_sec; + now->usec = (zend_long)tv.tv_usec; + } +#else + time_t ts = time(NULL); + if (ts == -1) { + ret = -1; + } else { + now->sec = (zend_long)ts; + now->usec = 0; + } +#endif + return ret; +} + +#ifdef HAVE_GETTIMEOFDAY + +#define MICRO_IN_SEC 1000000.00 +#define SEC_IN_MIN 60 + +static void _timecop_gettimeofday(INTERNAL_FUNCTION_PARAMETERS, int mode) +{ + zend_bool get_as_float = 0; + tc_timeval fixed; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &get_as_float) == FAILURE) { + RETURN_FALSE; + } + + if (get_mock_timeval(&fixed, NULL)) { + RETURN_FALSE; + } + + if (get_as_float) { + RETURN_DOUBLE((double)(fixed.sec + fixed.usec / MICRO_IN_SEC)); + } + if (mode) { + zval zv_offset, zv_dst, format, timestamp; + zend_long offset = 0, is_dst = 0; + + ZVAL_LONG(×tamp, fixed.sec); + + /* offset */ + ZVAL_STRING(&format, "Z"); + call_php_function_with_2_params(ORIG_FUNC_NAME("date"), &zv_offset, &format, ×tamp); + convert_to_long(&zv_offset); + offset = Z_LVAL(zv_offset); + zval_ptr_dtor(&zv_offset); + zval_ptr_dtor(&format); + + /* is_dst */ + ZVAL_STRING(&format, "I"); + call_php_function_with_2_params(ORIG_FUNC_NAME("date"), &zv_dst, &format, ×tamp); + convert_to_long(&zv_dst); + is_dst = Z_LVAL(zv_dst); + zval_ptr_dtor(&zv_dst); + zval_ptr_dtor(&format); + + array_init(return_value); + add_assoc_long(return_value, "sec", fixed.sec); + add_assoc_long(return_value, "usec", fixed.usec); + add_assoc_long(return_value, "minuteswest", -offset / SEC_IN_MIN); + add_assoc_long(return_value, "dsttime", is_dst); + } else { + char ret[100]; + snprintf(ret, 100, "%.8F %ld", fixed.usec / MICRO_IN_SEC, fixed.sec); + RETURN_STRING(ret); + } +} + +/* {{{ proto mixed microtime([bool get_as_float]) + Returns either a string or a float containing the current time in seconds and microseconds */ +PHP_FUNCTION(timecop_microtime) +{ + _timecop_gettimeofday(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto array gettimeofday([bool get_as_float]) + Returns the current time as array */ +PHP_FUNCTION(timecop_gettimeofday) +{ + _timecop_gettimeofday(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ +#endif + +/* {{{ proto int timecop_unixtojd([int timestamp]) + Convert UNIX timestamp to Julian Day */ +PHP_FUNCTION(timecop_unixtojd) +{ + TIMECOP_CALL_FUNCTION("unixtojd", 0); +} +/* }}} */ + +static void _timecop_orig_datetime_constructor(INTERNAL_FUNCTION_PARAMETERS, int immutable) +{ + zval *arg1 = NULL, *arg2 = NULL; + zend_class_entry *real_ce; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|zz", &arg1, &arg2) == FAILURE) { + RETURN_FALSE; + } + + if (immutable) { + real_ce = TIMECOP_G(ce_DateTimeImmutable); + } else { + real_ce = TIMECOP_G(ce_DateTime); + } + + call_php_method_with_2_params(getThis(), real_ce, ORIG_FUNC_NAME("__construct"), NULL, arg1, arg2); +} + +static inline void _timecop_datetime_constructor(INTERNAL_FUNCTION_PARAMETERS, int immutable) +{ + _timecop_datetime_constructor_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, getThis(), immutable); +} + +static inline void _timecop_date_create(INTERNAL_FUNCTION_PARAMETERS, int immutable) +{ + _timecop_datetime_constructor_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, NULL, immutable); +} + +static void _timecop_datetime_constructor_ex(INTERNAL_FUNCTION_PARAMETERS, zval *obj, int immutable) +{ + zval orig_time, *orig_timezone = NULL; + zval fixed_time, fixed_timezone, *arg1, *arg2; + char *orig_time_str = NULL; + size_t orig_time_len = 0; + const char *real_func; + zend_class_entry *real_ce; + +#if PHP_VERSION_ID >= 80100 + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_STRING(orig_time_str, orig_time_len) + Z_PARAM_OBJECT_OF_CLASS_OR_NULL(orig_timezone, TIMECOP_G(ce_DateTimeZone)) + ZEND_PARSE_PARAMETERS_END(); + + ZVAL_STRINGL(&orig_time, orig_time_str, orig_time_len); +#else + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sO!", &orig_time_str, &orig_time_len, &orig_timezone, TIMECOP_G(ce_DateTimeZone)) == FAILURE) { + RETURN_FALSE; + } + + if (orig_time_str == NULL) { + ZVAL_NULL(&orig_time); + } else { + ZVAL_STRINGL(&orig_time, orig_time_str, orig_time_len); + } +#endif + + if (immutable) { + real_func = ORIG_FUNC_NAME("date_create_immutable"); + real_ce = TIMECOP_G(ce_DateTimeImmutable); + } else { + real_func = ORIG_FUNC_NAME("date_create"); + real_ce = TIMECOP_G(ce_DateTime); + } + + if (get_formatted_mock_time(&orig_time, orig_timezone, &fixed_time, &fixed_timezone TSRMLS_CC) == 0) { + arg1 = &fixed_time; + arg2 = &fixed_timezone; + } else { + arg1 = &orig_time; + arg2 = orig_timezone; + } + if (obj == NULL) { + call_php_function_with_2_params(real_func, return_value, arg1, arg2); + } else { + call_php_method_with_2_params(obj, real_ce, ORIG_FUNC_NAME("__construct"), NULL, arg1, arg2); + } + zval_ptr_dtor(&orig_time); + zval_ptr_dtor(&fixed_time); + zval_ptr_dtor(&fixed_timezone); +} + +/* {{{ proto DateTime timecop_date_create([string time[, DateTimeZone object]]) + Returns new DateTime object initialized with traveled time */ +PHP_FUNCTION(timecop_date_create) +{ + _timecop_date_create(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +static void _timecop_date_create_from_format(INTERNAL_FUNCTION_PARAMETERS, int immutable) +{ + zval *orig_timezone = NULL; + zval orig_format, orig_time, fixed_format, fixed_time, new_format, new_time; + zval dt, now_timestamp, tmp; + char *orig_format_str, *orig_time_str; + size_t orig_format_len, orig_time_len; + tc_timeval now; + char buf[64]; + const char *real_func; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|O!", &orig_format_str, &orig_format_len, &orig_time_str, &orig_time_len, &orig_timezone, TIMECOP_G(ce_DateTimeZone)) == FAILURE) { + RETURN_FALSE; + } + + ZVAL_STRINGL(&orig_format, orig_format_str, orig_format_len); + ZVAL_STRINGL(&orig_time, orig_time_str, orig_time_len); + + call_php_function_with_3_params(ORIG_FUNC_NAME("date_create_from_format"), &dt, &orig_format, &orig_time, orig_timezone); + if (Z_TYPE(dt) == IS_FALSE) { + RETURN_FALSE; + } + + if (memchr(orig_format_str, '!', orig_format_len) || + memchr(orig_format_str, '|', orig_format_len)) { + + if (immutable) { + call_php_function_with_3_params(ORIG_FUNC_NAME("date_create_immutable_from_format"), &dt, &orig_format, &orig_time, orig_timezone); + } + + zval_ptr_dtor(&orig_format); + zval_ptr_dtor(&orig_time); + RETURN_ZVAL(&dt, 1, 1); + } + + get_mock_timeval(&now, NULL); + + ZVAL_LONG(&now_timestamp, now.sec); + call_php_method_with_1_params(&dt, TIMECOP_G(ce_DateTime), "settimestamp", NULL, &now_timestamp); + sprintf(buf, "Y-m-d H:i:s.%06ld ", now.usec); + ZVAL_STRING(&tmp, buf); + call_php_method_with_1_params(&dt, TIMECOP_G(ce_DateTime), "format", &fixed_time, &tmp); + zval_ptr_dtor(&tmp); + + if (memchr(orig_format_str, 'g', orig_format_len) || + memchr(orig_format_str, 'h', orig_format_len) || + memchr(orig_format_str, 'G', orig_format_len) || + memchr(orig_format_str, 'H', orig_format_len) || + memchr(orig_format_str, 'i', orig_format_len) || + memchr(orig_format_str, 's', orig_format_len)) { + ZVAL_STRING(&fixed_format, "Y-m-d ??:??:??.??????"); + } else if (memchr(orig_format_str, 'Y', orig_format_len) || + memchr(orig_format_str, 'y', orig_format_len) || + memchr(orig_format_str, 'F', orig_format_len) || + memchr(orig_format_str, 'M', orig_format_len) || + memchr(orig_format_str, 'm', orig_format_len) || + memchr(orig_format_str, 'n', orig_format_len) || + memchr(orig_format_str, 'd', orig_format_len) || + memchr(orig_format_str, 'j', orig_format_len) || + memchr(orig_format_str, 'D', orig_format_len) || + memchr(orig_format_str, 'l', orig_format_len) || + memchr(orig_format_str, 'U', orig_format_len)) { + ZVAL_STRING(&fixed_format, "Y-m-d H:i:s.??????"); + } else if (memchr(orig_format_str, 'u', orig_format_len)) { + // https://bugs.php.net/bug.php?id=78603 +#if PHP_VERSION_ID >= 70300 + ZVAL_STRING(&fixed_format, "Y-m-d ??:??:??.??????"); +#elif PHP_VERSION_ID >= 70100 + ZVAL_STRING(&fixed_format, "Y-m-d H:i:s.u"); +#else + ZVAL_STRING(&fixed_format, "Y-m-d H:i:s.??????"); +#endif + } else { +#if PHP_VERSION_ID >= 70100 + ZVAL_STRING(&fixed_format, "Y-m-d H:i:s.u"); +#else + ZVAL_STRING(&fixed_format, "Y-m-d H:i:s.??????"); +#endif + } + + ZVAL_STRING(&tmp, "%s %s"); + call_php_function_with_3_params("sprintf", &new_format, &tmp, &fixed_format, &orig_format); + call_php_function_with_3_params("sprintf", &new_time, &tmp, &fixed_time, &orig_time); + zval_ptr_dtor(&tmp); + + if (immutable) { + real_func = ORIG_FUNC_NAME("date_create_immutable_from_format"); + } else { + real_func = ORIG_FUNC_NAME("date_create_from_format"); + } + call_php_function_with_3_params(real_func, return_value, &new_format, &new_time, orig_timezone); + + zval_ptr_dtor(&dt); + zval_ptr_dtor(&orig_format); + zval_ptr_dtor(&orig_time); + zval_ptr_dtor(&fixed_format); + zval_ptr_dtor(&fixed_time); + zval_ptr_dtor(&new_format); + zval_ptr_dtor(&new_time); +} + +/* {{{ proto DateTime timecop_date_create_from_format(string format, string time[, DateTimeZone object]) + Returns new DateTime object initialized with traveled time */ +PHP_FUNCTION(timecop_date_create_from_format) +{ + _timecop_date_create_from_format(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto DateTimeImmutable timecop_date_create_immutable([string time[, DateTimeZone object]]) + Returns new DateTimeImmutable object initialized with traveled time */ +PHP_FUNCTION(timecop_date_create_immutable) +{ + _timecop_date_create(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ proto DateTimeImmutable timecop_date_create_immutable_from_format(string format, string time[, DateTimeZone object]) + Returns new DateTimeImmutable object initialized with traveled time */ +PHP_FUNCTION(timecop_date_create_immutable_from_format) +{ + _timecop_date_create_from_format(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ proto TimecopDateTime::__construct([string time[, DateTimeZone object]]) + Creates new TimecopDateTime object */ +PHP_METHOD(TimecopDateTime, __construct) +{ + _timecop_datetime_constructor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto TimecopOrigDateTime::__construct([string time[, DateTimeZone object]]) + Creates new TimecopOrigDateTime object */ +PHP_METHOD(TimecopOrigDateTime, __construct) +{ + _timecop_orig_datetime_constructor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); +} +/* }}} */ + +/* {{{ proto TimecopDateTimeImmutable::__construct([string time[, DateTimeZone object]]) + Creates new TimecopDateTimeImmutable object */ +PHP_METHOD(TimecopDateTimeImmutable, __construct) +{ + _timecop_datetime_constructor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +/* {{{ proto TimecopOrigDateTimeImmutable::__construct([string time[, DateTimeZone object]]) + Creates new TimecopOrigDateTimeImmutable object */ +PHP_METHOD(TimecopOrigDateTimeImmutable, __construct) +{ + _timecop_orig_datetime_constructor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); +} +/* }}} */ + +static inline zval* _call_php_method_with_0_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr) +{ + return _call_php_method(object_pp, obj_ce, method_name, retval_ptr, 0, NULL, NULL); +} + +static inline zval* _call_php_method_with_1_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, zval *arg1) +{ + int nparams = 1; + if (arg1 == NULL) { + nparams = 0; + } + return _call_php_method(object_pp, obj_ce, method_name, retval_ptr, nparams, arg1, NULL); +} + +static inline zval* _call_php_method_with_2_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, zval *arg1, zval *arg2) +{ + int nparams = 2; + if (arg1 == NULL) { + nparams = 0; + } else if (arg2 == NULL) { + nparams = 1; + } + return _call_php_method(object_pp, obj_ce, method_name, retval_ptr, nparams, arg1, arg2); +} + +static inline zval* _call_php_method(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, int param_count, zval* arg1, zval* arg2) +{ + return zend_call_method( +#if PHP_MAJOR_VERSION >= 8 + object_pp == NULL ? NULL : Z_OBJ_P(object_pp), +#else + object_pp, +#endif + obj_ce, + NULL, + method_name, + strlen(method_name), + retval_ptr, + param_count, + arg1, + arg2 + ); +} + +static inline void _call_php_function_with_3_params(const char *function_name, zval *retval_ptr, zval *arg1, zval *arg2, zval *arg3) +{ + if (arg3 == NULL) { + call_php_function_with_2_params(function_name, retval_ptr, arg1, arg2); + } else { + zval params[3]; + ZVAL_COPY(¶ms[0], arg1); + ZVAL_COPY(¶ms[1], arg2); + ZVAL_COPY(¶ms[2], arg3); + _call_php_function_with_params(function_name, retval_ptr, 3, params); + zval_ptr_dtor(¶ms[0]); + zval_ptr_dtor(¶ms[1]); + zval_ptr_dtor(¶ms[2]); + } +} + +static inline void _call_php_function_with_params(const char *function_name, zval *retval_ptr, uint32_t param_count, zval *params) +{ + zval callable; + ZVAL_STRING(&callable, function_name); + + call_user_function(EG(function_table), NULL, &callable, retval_ptr, param_count, params); + + zval_ptr_dtor(&callable); +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + diff --git a/timecop_php8.dep b/timecop_php8.dep index de4e7a8..7d27572 100644 --- a/timecop_php8.dep +++ b/timecop_php8.dep @@ -1,95 +1,96 @@ -timecop_php8.lo: /home/sylvain/projects/php-timecop/timecop_php8.c \ - /home/sylvain/projects/php-timecop/config.h \ - /usr/include/php/20230831/main/php.h \ - /usr/include/php/20230831/main/php_version.h \ - /usr/include/php/20230831/Zend/zend.h \ - /usr/include/php/20230831/Zend/zend_types.h \ - /usr/include/php/20230831/Zend/zend_portability.h \ - /usr/include/php/20230831/Zend/zend_config.h \ - /usr/include/php/20230831/main/../main/php_config.h \ - /usr/include/php/20230831/Zend/../TSRM/TSRM.h \ - /usr/include/php/20230831/main/php_config.h \ - /usr/include/php/20230831/Zend/zend_range_check.h \ - /usr/include/php/20230831/Zend/zend_long.h \ - /usr/include/php/20230831/Zend/zend_map_ptr.h \ - /usr/include/php/20230831/Zend/zend_errors.h \ - /usr/include/php/20230831/Zend/zend_alloc.h \ - /usr/include/php/20230831/Zend/zend.h \ - /usr/include/php/20230831/Zend/zend_alloc_sizes.h \ - /usr/include/php/20230831/Zend/zend_llist.h \ - /usr/include/php/20230831/Zend/zend_string.h \ - /usr/include/php/20230831/Zend/zend_hash.h \ - /usr/include/php/20230831/Zend/zend_sort.h \ - /usr/include/php/20230831/Zend/zend_ast.h \ - /usr/include/php/20230831/Zend/zend_gc.h \ - /usr/include/php/20230831/Zend/zend_hrtime.h \ - /usr/include/php/20230831/Zend/zend_variables.h \ - /usr/include/php/20230831/Zend/zend_iterators.h \ - /usr/include/php/20230831/Zend/zend_stream.h \ - /usr/include/php/20230831/Zend/zend_smart_str_public.h \ - /usr/include/php/20230831/Zend/zend_smart_string_public.h \ - /usr/include/php/20230831/Zend/zend_signal.h \ - /usr/include/php/20230831/Zend/zend_max_execution_timer.h \ - /usr/include/php/20230831/Zend/zend_object_handlers.h \ - /usr/include/php/20230831/Zend/zend_operators.h \ - /usr/include/php/20230831/Zend/zend_strtod.h \ - /usr/include/php/20230831/Zend/zend_multiply.h \ - /usr/include/php/20230831/Zend/zend_sort.h \ - /usr/include/php/20230831/main/php_compat.h \ - /usr/include/php/20230831/main/php_config.h \ - /usr/include/php/20230831/Zend/zend_API.h \ - /usr/include/php/20230831/Zend/zend_modules.h \ - /usr/include/php/20230831/Zend/zend_compile.h \ - /usr/include/php/20230831/Zend/zend_globals.h \ - /usr/include/php/20230831/Zend/zend_globals_macros.h \ - /usr/include/php/20230831/Zend/zend_atomic.h \ - /usr/include/php/20230831/Zend/zend_stack.h \ - /usr/include/php/20230831/Zend/zend_ptr_stack.h \ - /usr/include/php/20230831/Zend/zend_objects.h \ - /usr/include/php/20230831/Zend/zend_objects_API.h \ - /usr/include/php/20230831/Zend/zend_float.h \ - /usr/include/php/20230831/Zend/zend_multibyte.h \ - /usr/include/php/20230831/Zend/zend_arena.h \ - /usr/include/php/20230831/Zend/zend_call_stack.h \ - /usr/include/php/20230831/Zend/zend_vm_opcodes.h \ - /usr/include/php/20230831/Zend/zend_build.h \ - /usr/include/php/20230831/Zend/zend_list.h \ - /usr/include/php/20230831/Zend/zend_execute.h \ - /usr/include/php/20230831/Zend/zend_type_info.h \ - /usr/include/php/20230831/main/build-defs.h \ - /usr/include/php/20230831/Zend/zend_hash.h \ - /usr/include/php/20230831/Zend/zend_alloc.h \ - /usr/include/php/20230831/Zend/zend_stack.h \ - /usr/include/php/20230831/main/snprintf.h \ - /usr/include/php/20230831/main/spprintf.h \ - /usr/include/php/20230831/Zend/zend_smart_str_public.h \ - /usr/include/php/20230831/Zend/zend_smart_string_public.h \ - /usr/include/php/20230831/main/php_syslog.h \ - /usr/include/php/20230831/main/php.h \ - /usr/include/php/20230831/main/php_output.h \ - /usr/include/php/20230831/main/php_streams.h \ - /usr/include/php/20230831/Zend/zend_stream.h \ - /usr/include/php/20230831/main/streams/php_stream_context.h \ - /usr/include/php/20230831/main/streams/php_stream_filter_api.h \ - /usr/include/php/20230831/main/streams/php_stream_transport.h \ - /usr/include/php/20230831/main/streams/php_stream_plain_wrapper.h \ - /usr/include/php/20230831/main/streams/php_stream_glob_wrapper.h \ - /usr/include/php/20230831/main/streams/php_stream_userspace.h \ - /usr/include/php/20230831/main/streams/php_stream_mmap.h \ - /usr/include/php/20230831/main/php_memory_streams.h \ - /usr/include/php/20230831/main/fopen_wrappers.h \ - /usr/include/php/20230831/main/php_globals.h \ - /usr/include/php/20230831/Zend/zend_globals.h \ - /usr/include/php/20230831/main/php_ini.h \ - /usr/include/php/20230831/Zend/zend_ini.h \ - /usr/include/php/20230831/Zend/zend_virtual_cwd.h \ - /usr/include/php/20230831/TSRM/TSRM.h \ - /usr/include/php/20230831/Zend/zend_constants.h \ - /usr/include/php/20230831/main/php_reentrancy.h \ - /usr/include/php/20230831/main/php_ini.h \ - /usr/include/php/20230831/ext/standard/info.h \ - /home/sylvain/projects/php-timecop/php_timecop.h \ - /usr/include/php/20230831/Zend/zend_interfaces.h \ - /usr/include/php/20230831/Zend/zend_API.h \ - /home/sylvain/projects/php-timecop/tc_timeval.h \ - /home/sylvain/projects/php-timecop/timecop_php8_arginfo.h +timecop_php8.lo: /app/timecop_php8.c /app/config.h \ + /usr/include/php/20240924/main/php.h \ + /usr/include/php/20240924/main/php_version.h \ + /usr/include/php/20240924/Zend/zend.h \ + /usr/include/php/20240924/Zend/zend_types.h \ + /usr/include/php/20240924/Zend/zend_portability.h \ + /usr/include/php/20240924/Zend/zend_config.h \ + /usr/include/php/20240924/main/../main/php_config.h \ + /usr/include/php/20240924/Zend/../TSRM/TSRM.h \ + /usr/include/php/20240924/main/php_config.h \ + /usr/include/php/20240924/Zend/zend_range_check.h \ + /usr/include/php/20240924/Zend/zend_long.h \ + /usr/include/php/20240924/Zend/zend_map_ptr.h \ + /usr/include/php/20240924/Zend/zend_errors.h \ + /usr/include/php/20240924/Zend/zend_alloc.h \ + /usr/include/php/20240924/Zend/zend_alloc_sizes.h \ + /usr/include/php/20240924/Zend/zend_llist.h \ + /usr/include/php/20240924/Zend/zend_string.h \ + /usr/include/php/20240924/Zend/zend_gc.h \ + /usr/include/php/20240924/Zend/zend_hrtime.h \ + /usr/include/php/20240924/Zend/zend_hash.h \ + /usr/include/php/20240924/Zend/zend_sort.h \ + /usr/include/php/20240924/Zend/zend_ast.h \ + /usr/include/php/20240924/Zend/zend_variables.h \ + /usr/include/php/20240924/Zend/zend_iterators.h \ + /usr/include/php/20240924/Zend/zend_stream.h \ + /usr/include/php/20240924/Zend/zend_smart_str_public.h \ + /usr/include/php/20240924/Zend/zend_smart_string_public.h \ + /usr/include/php/20240924/Zend/zend_signal.h \ + /usr/include/php/20240924/Zend/zend_max_execution_timer.h \ + /usr/include/php/20240924/Zend/zend_object_handlers.h \ + /usr/include/php/20240924/Zend/zend_property_hooks.h \ + /usr/include/php/20240924/Zend/zend_lazy_objects.h \ + /usr/include/php/20240924/Zend/zend_types.h \ + /usr/include/php/20240924/Zend/zend_operators.h \ + /usr/include/php/20240924/Zend/zend_strtod.h \ + /usr/include/php/20240924/Zend/zend_multiply.h \ + /usr/include/php/20240924/Zend/zend_sort.h \ + /usr/include/php/20240924/main/php_compat.h \ + /usr/include/php/20240924/main/php_config.h \ + /usr/include/php/20240924/Zend/zend_API.h \ + /usr/include/php/20240924/Zend/zend_modules.h \ + /usr/include/php/20240924/Zend/zend.h \ + /usr/include/php/20240924/Zend/zend_compile.h \ + /usr/include/php/20240924/Zend/zend_frameless_function.h \ + /usr/include/php/20240924/Zend/zend_globals.h \ + /usr/include/php/20240924/Zend/zend_globals_macros.h \ + /usr/include/php/20240924/Zend/zend_atomic.h \ + /usr/include/php/20240924/Zend/zend_stack.h \ + /usr/include/php/20240924/Zend/zend_ptr_stack.h \ + /usr/include/php/20240924/Zend/zend_objects.h \ + /usr/include/php/20240924/Zend/zend_objects_API.h \ + /usr/include/php/20240924/Zend/zend_float.h \ + /usr/include/php/20240924/Zend/zend_multibyte.h \ + /usr/include/php/20240924/Zend/zend_arena.h \ + /usr/include/php/20240924/Zend/zend_call_stack.h \ + /usr/include/php/20240924/Zend/zend_vm_opcodes.h \ + /usr/include/php/20240924/Zend/zend_build.h \ + /usr/include/php/20240924/Zend/zend_list.h \ + /usr/include/php/20240924/Zend/zend_execute.h \ + /usr/include/php/20240924/Zend/zend_type_info.h \ + /usr/include/php/20240924/main/build-defs.h \ + /usr/include/php/20240924/Zend/zend_hash.h \ + /usr/include/php/20240924/Zend/zend_alloc.h \ + /usr/include/php/20240924/Zend/zend_stack.h \ + /usr/include/php/20240924/main/snprintf.h \ + /usr/include/php/20240924/main/spprintf.h \ + /usr/include/php/20240924/Zend/zend_smart_str_public.h \ + /usr/include/php/20240924/Zend/zend_smart_string_public.h \ + /usr/include/php/20240924/main/php_syslog.h \ + /usr/include/php/20240924/main/php.h \ + /usr/include/php/20240924/main/php_output.h \ + /usr/include/php/20240924/main/php_streams.h \ + /usr/include/php/20240924/Zend/zend_stream.h \ + /usr/include/php/20240924/main/streams/php_stream_context.h \ + /usr/include/php/20240924/main/streams/php_stream_filter_api.h \ + /usr/include/php/20240924/main/streams/php_stream_transport.h \ + /usr/include/php/20240924/main/streams/php_stream_plain_wrapper.h \ + /usr/include/php/20240924/main/streams/php_stream_glob_wrapper.h \ + /usr/include/php/20240924/main/streams/php_stream_userspace.h \ + /usr/include/php/20240924/main/streams/php_stream_mmap.h \ + /usr/include/php/20240924/main/php_memory_streams.h \ + /usr/include/php/20240924/main/fopen_wrappers.h \ + /usr/include/php/20240924/main/php_globals.h \ + /usr/include/php/20240924/Zend/zend_globals.h \ + /usr/include/php/20240924/main/php_ini.h \ + /usr/include/php/20240924/Zend/zend_ini.h \ + /usr/include/php/20240924/Zend/zend_virtual_cwd.h \ + /usr/include/php/20240924/TSRM/TSRM.h \ + /usr/include/php/20240924/Zend/zend_constants.h \ + /usr/include/php/20240924/main/php_reentrancy.h \ + /usr/include/php/20240924/main/php_ini.h \ + /usr/include/php/20240924/ext/standard/info.h /app/php_timecop.h \ + /usr/include/php/20240924/Zend/zend_interfaces.h \ + /usr/include/php/20240924/Zend/zend_API.h /app/tc_timeval.h \ + /app/timecop_php8_arginfo.h From 6f6b1195232fc8b532c334f698f3dc36df6855e1 Mon Sep 17 00:00:00 2001 From: Sylvain MSL Date: Fri, 21 Mar 2025 10:49:10 +0100 Subject: [PATCH 5/7] Remove support for strftime gmstrftime --- .gitignore | 1 + Dockerfile | 2 +- php_timecop.h | 6 --- tc_timeval.dep | 3 +- tests/func_010.phpt | 32 ------------ tests/func_011.phpt | 32 ------------ tests/func_override_010.phpt | 32 ------------ tests/func_override_011.phpt | 32 ------------ tests/issue_009.phpt | 2 +- tests/refl_002.phpt | 4 -- timecop_php8.c | 98 ------------------------------------ timecop_php8.dep | 11 ++-- timecop_php8_arginfo.h | 15 ------ 13 files changed, 12 insertions(+), 258 deletions(-) delete mode 100644 tests/func_010.phpt delete mode 100644 tests/func_011.phpt delete mode 100644 tests/func_override_010.phpt delete mode 100644 tests/func_override_011.phpt diff --git a/.gitignore b/.gitignore index 94ecd63..661a7a4 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,4 @@ tests/*.out .#* .DS_Store Thumbs.db +.vscode diff --git a/Dockerfile b/Dockerfile index adae8e4..e127af2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,7 +33,7 @@ RUN sed -i -e "s/upload_max_filesize\s*=\s*2M/upload_max_filesize = 100M/g" /etc sed -i -e "s/listen = \/run\/php\/php8.4-fpm.sock/;listen = \/run\/php\/php8.4-fpm.sock\nlisten = 0:9000/g" /etc/php/8.4/fpm/pool.d/www.conf && \ sed -i -e "s/upload_max_filesize\s*=\s*2M/upload_max_filesize = 100M/g" /etc/php/8.4/cli/php.ini -RUN apt install php8.4-phpdbg +RUN apt install -y php8.4-phpdbg gdb COPY . /tmp/install/php-timecop diff --git a/php_timecop.h b/php_timecop.h index 831a2aa..d3ef3c5 100644 --- a/php_timecop.h +++ b/php_timecop.h @@ -137,11 +137,7 @@ ZEND_END_MODULE_GLOBALS(timecop) * Trick for guarding the multi-referenced internal function from function destructor on PHP 7.2.0+ * See: https://github.com/hnw/php-timecop/issues/29#issuecomment-332171527 */ -#if PHP_VERSION_ID >= 70200 # define FIX_FUNCTION_ARG_INFO_DTOR(zend_func) zend_func->common.arg_info = NULL; -#else -# define FIX_FUNCTION_ARG_INFO_DTOR(zend_func) -#endif struct timecop_override_func_entry { char *orig_func; @@ -188,13 +184,11 @@ struct timecop_override_class_entry { */ /* Redeclare macros as no-ops which were removed in PHP 8. */ -#if PHP_VERSION_ID >= 80000 #define TSRMLS_D void #define TSRMLS_DC #define TSRMLS_C #define TSRMLS_CC #define TSRMLS_FETCH() -#endif #define TIMECOP_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(timecop, v) #if defined(ZTS) && defined(COMPILE_DL_TIMECOP) diff --git a/tc_timeval.dep b/tc_timeval.dep index cccbb73..46602ba 100644 --- a/tc_timeval.dep +++ b/tc_timeval.dep @@ -1 +1,2 @@ -tc_timeval.lo: /app/tc_timeval.c /app/tc_timeval.h +tc_timeval.lo: /home/sylvain/projects/php-timecop/tc_timeval.c \ + /home/sylvain/projects/php-timecop/tc_timeval.h diff --git a/tests/func_010.phpt b/tests/func_010.phpt deleted file mode 100644 index 64440f1..0000000 --- a/tests/func_010.phpt +++ /dev/null @@ -1,32 +0,0 @@ ---TEST-- -Test for timecop_strftime ---SKIPIF-- -= 80100, - function (\Closure $callback) { - return decorateIgnoreDeprecation( - 'Function strftime() is deprecated', - decorateIgnoreDeprecation( - 'Function timecop_strftime() is deprecated', - $callback - ) - ); - }, - function () { - var_dump(timecop_strftime("%Y-%m-%d %H:%M:%S")); - } -)); ---EXPECT-- -string(19) "2012-02-29 01:23:45" diff --git a/tests/func_011.phpt b/tests/func_011.phpt deleted file mode 100644 index 8f2f27e..0000000 --- a/tests/func_011.phpt +++ /dev/null @@ -1,32 +0,0 @@ ---TEST-- -Test for timecop_gmstrftime ---SKIPIF-- -= 80100, - function (\Closure $callback) { - return decorateIgnoreDeprecation( - 'Function gmstrftime() is deprecated', - decorateIgnoreDeprecation( - 'Function timecop_gmstrftime() is deprecated', - $callback - ) - ); - }, - function () { - var_dump(timecop_gmstrftime("%Y-%m-%d %H:%M:%S")); - } -)); ---EXPECT-- -string(19) "2012-02-29 01:23:45" diff --git a/tests/func_override_010.phpt b/tests/func_override_010.phpt deleted file mode 100644 index df43b63..0000000 --- a/tests/func_override_010.phpt +++ /dev/null @@ -1,32 +0,0 @@ ---TEST-- -Function overrideing test for strftime ---SKIPIF-- -= 80100, - function (\Closure $callback) { - return decorateIgnoreDeprecation( - 'Function strftime() is deprecated', - decorateIgnoreDeprecation( - 'Function timecop_strftime() is deprecated', - $callback - ) - ); - }, - function () { - var_dump(strftime("%Y-%m-%d %H:%M:%S")); - } -)); ---EXPECT-- -string(19) "2012-02-29 01:23:45" diff --git a/tests/func_override_011.phpt b/tests/func_override_011.phpt deleted file mode 100644 index 85772fe..0000000 --- a/tests/func_override_011.phpt +++ /dev/null @@ -1,32 +0,0 @@ ---TEST-- -Function overrideing test for gmstrftime ---SKIPIF-- -= 80100, - function (\Closure $callback) { - return decorateIgnoreDeprecation( - 'Function gmstrftime() is deprecated', - decorateIgnoreDeprecation( - 'Function timecop_gmstrftime() is deprecated', - $callback - ) - ); - }, - function () { - var_dump(gmstrftime("%Y-%m-%d %H:%M:%S")); - } -)); ---EXPECT-- -string(19) "2012-02-29 01:23:45" diff --git a/tests/issue_009.phpt b/tests/issue_009.phpt index e71f071..6147cbb 100644 --- a/tests/issue_009.phpt +++ b/tests/issue_009.phpt @@ -13,7 +13,7 @@ timecop.func_override=1 setTime(0, 0, 0); diff --git a/tests/refl_002.phpt b/tests/refl_002.phpt index 1a3479c..76e9b3a 100644 --- a/tests/refl_002.phpt +++ b/tests/refl_002.phpt @@ -20,8 +20,6 @@ $functions = [ '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', @@ -38,8 +36,6 @@ 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 diff --git a/timecop_php8.c b/timecop_php8.c index d7235e8..34297bd 100644 --- a/timecop_php8.c +++ b/timecop_php8.c @@ -71,8 +71,6 @@ static const struct timecop_override_func_entry timecop_override_func_table[] = TIMECOP_OFE("getdate"), TIMECOP_OFE("localtime"), TIMECOP_OFE("strtotime"), - TIMECOP_OFE("strftime"), - TIMECOP_OFE("gmstrftime"), #ifdef HAVE_GETTIMEOFDAY TIMECOP_OFE("microtime"), TIMECOP_OFE("gettimeofday"), @@ -110,16 +108,6 @@ const zend_function_entry timecop_functions[] = { PHP_FE(timecop_getdate, arginfo_timecop_getdate) PHP_FE(timecop_localtime, arginfo_timecop_localtime) PHP_FE(timecop_strtotime, arginfo_timecop_strtotime) -#if PHP_VERSION_ID >= 80400 - ZEND_RAW_FENTRY("timecop_strftime", zif_timecop_strftime, arginfo_timecop_strftime, ZEND_ACC_DEPRECATED, NULL, NULL) - ZEND_RAW_FENTRY("timecop_gmstrftime", zif_timecop_gmstrftime, arginfo_timecop_gmstrftime, ZEND_ACC_DEPRECATED, NULL, NULL) -#elif PHP_VERSION_ID >= 80100 - PHP_DEP_FE(timecop_strftime, arginfo_timecop_strftime) - PHP_DEP_FE(timecop_gmstrftime, arginfo_timecop_gmstrftime) -#else - PHP_FE(timecop_strftime, arginfo_timecop_strftime) - PHP_FE(timecop_gmstrftime, arginfo_timecop_gmstrftime) -#endif #ifdef HAVE_GETTIMEOFDAY PHP_FE(timecop_microtime, arginfo_timecop_microtime) PHP_FE(timecop_gettimeofday, arginfo_timecop_gettimeofday) @@ -234,9 +222,7 @@ zend_module_entry timecop_module_entry = { PHP_RINIT(timecop), PHP_RSHUTDOWN(timecop), PHP_MINFO(timecop), -#if ZEND_MODULE_API_NO >= 20010901 PHP_TIMECOP_VERSION, -#endif STANDARD_MODULE_PROPERTIES }; /* }}} */ @@ -989,7 +975,6 @@ 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 @@ -999,17 +984,6 @@ PHP_FUNCTION(timecop_mktime) 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) - Z_PARAM_LONG(min) - Z_PARAM_LONG(sec) - Z_PARAM_LONG(mon) - Z_PARAM_LONG(day) - Z_PARAM_LONG(yea) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); -#endif TIMECOP_CALL_MKTIME("mktime", "date"); } @@ -1022,7 +996,6 @@ 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 @@ -1032,17 +1005,6 @@ PHP_FUNCTION(timecop_gmmktime) 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) - Z_PARAM_LONG(min) - Z_PARAM_LONG(sec) - Z_PARAM_LONG(mon) - Z_PARAM_LONG(day) - Z_PARAM_LONG(yea) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); -#endif TIMECOP_CALL_MKTIME("gmmktime", "gmdate"); } @@ -1143,38 +1105,6 @@ PHP_FUNCTION(timecop_strtotime) } /* }}} */ -/* {{{ proto string timecop_strftime(string format [, int timestamp]) - Format a local time/date according to locale settings */ -PHP_FUNCTION(timecop_strftime) -{ - zend_string *format; - zend_long timestamp; - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STR(format) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(timestamp) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - TIMECOP_CALL_FUNCTION("strftime", 1); -} -/* }}} */ - -/* {{{ proto string timecop_gmstrftime(string format [, int timestamp]) - Format a GMT/UCT time/date according to locale settings */ -PHP_FUNCTION(timecop_gmstrftime) -{ - zend_string *format; - zend_long timestamp = 0; - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STR(format) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(timestamp) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - TIMECOP_CALL_FUNCTION("gmstrftime", 1); -} -/* }}} */ - /* * get_mock_timeval(fixed, now) * @@ -1381,7 +1311,6 @@ static void _timecop_datetime_constructor_ex(INTERNAL_FUNCTION_PARAMETERS, zval const char *real_func; zend_class_entry *real_ce; -#if PHP_VERSION_ID >= 80100 ZEND_PARSE_PARAMETERS_START(0, 2) Z_PARAM_OPTIONAL Z_PARAM_STRING(orig_time_str, orig_time_len) @@ -1389,17 +1318,6 @@ static void _timecop_datetime_constructor_ex(INTERNAL_FUNCTION_PARAMETERS, zval ZEND_PARSE_PARAMETERS_END(); ZVAL_STRINGL(&orig_time, orig_time_str, orig_time_len); -#else - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sO!", &orig_time_str, &orig_time_len, &orig_timezone, TIMECOP_G(ce_DateTimeZone)) == FAILURE) { - RETURN_FALSE; - } - - if (orig_time_str == NULL) { - ZVAL_NULL(&orig_time); - } else { - ZVAL_STRINGL(&orig_time, orig_time_str, orig_time_len); - } -#endif if (immutable) { real_func = ORIG_FUNC_NAME("date_create_immutable"); @@ -1498,20 +1416,9 @@ static void _timecop_date_create_from_format(INTERNAL_FUNCTION_PARAMETERS, int i memchr(orig_format_str, 'U', orig_format_len)) { ZVAL_STRING(&fixed_format, "Y-m-d H:i:s.??????"); } else if (memchr(orig_format_str, 'u', orig_format_len)) { - // https://bugs.php.net/bug.php?id=78603 -#if PHP_VERSION_ID >= 70300 ZVAL_STRING(&fixed_format, "Y-m-d ??:??:??.??????"); -#elif PHP_VERSION_ID >= 70100 - ZVAL_STRING(&fixed_format, "Y-m-d H:i:s.u"); -#else - ZVAL_STRING(&fixed_format, "Y-m-d H:i:s.??????"); -#endif } else { -#if PHP_VERSION_ID >= 70100 ZVAL_STRING(&fixed_format, "Y-m-d H:i:s.u"); -#else - ZVAL_STRING(&fixed_format, "Y-m-d H:i:s.??????"); -#endif } ZVAL_STRING(&tmp, "%s %s"); @@ -1619,11 +1526,7 @@ static inline zval* _call_php_method_with_2_params(zval *object_pp, zend_class_e static inline zval* _call_php_method(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, int param_count, zval* arg1, zval* arg2) { return zend_call_method( -#if PHP_MAJOR_VERSION >= 8 object_pp == NULL ? NULL : Z_OBJ_P(object_pp), -#else - object_pp, -#endif obj_ce, NULL, method_name, @@ -1669,4 +1572,3 @@ static inline void _call_php_function_with_params(const char *function_name, zva * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */ - diff --git a/timecop_php8.dep b/timecop_php8.dep index 7d27572..b0d2449 100644 --- a/timecop_php8.dep +++ b/timecop_php8.dep @@ -1,4 +1,5 @@ -timecop_php8.lo: /app/timecop_php8.c /app/config.h \ +timecop_php8.lo: /home/sylvain/projects/php-timecop/timecop_php8.c \ + /home/sylvain/projects/php-timecop/config.h \ /usr/include/php/20240924/main/php.h \ /usr/include/php/20240924/main/php_version.h \ /usr/include/php/20240924/Zend/zend.h \ @@ -90,7 +91,9 @@ timecop_php8.lo: /app/timecop_php8.c /app/config.h \ /usr/include/php/20240924/Zend/zend_constants.h \ /usr/include/php/20240924/main/php_reentrancy.h \ /usr/include/php/20240924/main/php_ini.h \ - /usr/include/php/20240924/ext/standard/info.h /app/php_timecop.h \ + /usr/include/php/20240924/ext/standard/info.h \ + /home/sylvain/projects/php-timecop/php_timecop.h \ /usr/include/php/20240924/Zend/zend_interfaces.h \ - /usr/include/php/20240924/Zend/zend_API.h /app/tc_timeval.h \ - /app/timecop_php8_arginfo.h + /usr/include/php/20240924/Zend/zend_API.h \ + /home/sylvain/projects/php-timecop/tc_timeval.h \ + /home/sylvain/projects/php-timecop/timecop_php8_arginfo.h diff --git a/timecop_php8_arginfo.h b/timecop_php8_arginfo.h index f03caea..602de63 100644 --- a/timecop_php8_arginfo.h +++ b/timecop_php8_arginfo.h @@ -78,13 +78,6 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_timecop_strtotime, 0, 1, MAY_BE_ 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") @@ -119,11 +112,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_TimecopDateTime___construct, 0, 0, 0) ZEND_END_ARG_INFO() // TimecopDateTime::createFromFormat() -#if PHP_VERSION_ID >= 80100 ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_TimecopDateTime_createFromFormat, 0, 2, DateTime, MAY_BE_FALSE) -#else -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_TimecopDateTime_createFromFormat, 0, 0, 2) -#endif 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") @@ -146,11 +135,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_TimecopDateTimeImmutable___construct arginfo_class_TimecopDateTime___construct // TimecopDateTimeImmutable::createFromFormat -#if PHP_VERSION_ID >= 80100 ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_TimecopDateTimeImmutable_createFromFormat, 0, 2, DateTimeImmutable, MAY_BE_FALSE) -#else -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_TimecopDateTimeImmutable_createFromFormat, 0, 0, 2) -#endif 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") From 64b961065134ec74287e8e8575a342e2278383cd Mon Sep 17 00:00:00 2001 From: Sylvain MSL Date: Fri, 21 Mar 2025 11:45:23 +0100 Subject: [PATCH 6/7] Remove support for php7 and php5 --- Dockerfile | 46 -- README.md | 7 +- timecop_php5.c | 1733 ---------------------------------------- timecop_php7.c | 1673 -------------------------------------- timecop_php7_arginfo.h | 138 ---- 5 files changed, 3 insertions(+), 3594 deletions(-) delete mode 100644 Dockerfile delete mode 100644 timecop_php5.c delete mode 100644 timecop_php7.c delete mode 100644 timecop_php7_arginfo.h diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index e127af2..0000000 --- a/Dockerfile +++ /dev/null @@ -1,46 +0,0 @@ -FROM ubuntu:24.04 - -LABEL "com.talkspirit.maintainer"="Olivier RICARD " - -# Let the conatiner know that there is no tty -ENV DEBIAN_FRONTEND=noninteractive - -RUN apt-get update && apt-get upgrade -y && apt-get install -y build-essential debhelper devscripts cron software-properties-common wget zsh curl vim zsh git supervisor jq libjq-dev -y - -# MongoDB shell tools -RUN wget -qO - https://www.mongodb.org/static/pgp/server-6.0.asc | apt-key add - -RUN echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/6.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-6.0.list -RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php -y -u && apt-get update - -# geoip https://github.com/maxmind/GeoIP2-php - -RUN apt-get update && apt-get install -y mongodb-mongosh mongodb-org-tools php8.4-dev php8.4-fpm php8.4-mongodb php8.4-gd php8.4-curl php8.4-cli php8.4-soap php8.4-apcu php8.4-opcache php8.4-intl php8.4-mbstring php8.4-redis php8.4-dom php8.4-zip php8.4-imagick php8.4-bcmath php8.4-mysql && \ -echo "date.timezone=${PHP_TIMEZONE:-Europe/Paris}" > /etc/php/8.4/cli/conf.d/date_timezone.ini && \ -echo "date.timezone=${PHP_TIMEZONE:-Europe/Paris}" > /etc/php/8.4/fpm/conf.d/date_timezone.ini - -RUN wget http://pear.php.net/go-pear.phar && php go-pear.phar - -# tools -RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && \ - curl -sS http://gordalina.github.io/cachetool/downloads/cachetool.phar -o /usr/local/bin/cachetool.phar && \ - wget https://get.symfony.com/cli/installer -O - | bash && \ - mv /root/.symfony5/bin/symfony /usr/local/bin/symfony && \ - curl -fLSs https://circle.ci/cli | bash - -RUN sed -i -e "s/upload_max_filesize\s*=\s*2M/upload_max_filesize = 100M/g" /etc/php/8.4/cli/php.ini && \ - sed -i -e "s/post_max_size\s*=\s*8M/post_max_size = 100M/g" /etc/php/8.4/cli/php.ini && \ - sed -i -e "s/;daemonize\s*=\s*yes/daemonize = no/g" /etc/php/8.4/fpm/php-fpm.conf && \ - sed -i -e "s/listen = \/run\/php\/php8.4-fpm.sock/;listen = \/run\/php\/php8.4-fpm.sock\nlisten = 0:9000/g" /etc/php/8.4/fpm/pool.d/www.conf && \ - sed -i -e "s/upload_max_filesize\s*=\s*2M/upload_max_filesize = 100M/g" /etc/php/8.4/cli/php.ini - -RUN apt install -y php8.4-phpdbg gdb - -COPY . /tmp/install/php-timecop - -# Install timecop & jq -RUN cd /tmp/install/php-timecop && \ - phpize && \ - ./configure && \ - make && \ - make install && \ - echo "extension=timecop.so" >> /etc/php/8.4/cli/php.ini \ No newline at end of file diff --git a/README.md b/README.md index 0e7aaab..5b101a5 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ extension=timecop.so ## SYSTEM REQUIREMENTS - OS: Linux, macOS -- PHP: 8.0.x - 8.4.x +- PHP: 8.4.x+ - SAPI: Apache, CLI - Other SAPIs are not tested, but there is no SAPI-dependent code. - non-ZTS(recommended), ZTS @@ -48,8 +48,6 @@ extension=timecop.so - `getdate()` - `localtime()` - `strtotime()` - - `strftime()` - - `gmstrftime()` - `microtime()` - `gettimeofday()` - `unixtojd()` @@ -108,9 +106,10 @@ var_dump((new DateTime())->format("c")); // string(25) "2017-01-01T00:00:05+00:0 ### version 1.7.0, 2024/03/20 - Support PHP 8.4 -- Drop support for PHP 5.x and 7.x +- Drop support for PHP 5.x to 8.3.x - Fix segmentation faults in PHP 8.4 - Improved memory management +- Drop support for `strftime` and `gmstrftime` ### version 1.6.0, 2024/02/09 - Support PHP 8.3 diff --git a/timecop_php5.c b/timecop_php5.c deleted file mode 100644 index f1e4d7d..0000000 --- a/timecop_php5.c +++ /dev/null @@ -1,1733 +0,0 @@ -/* -MIT License - -Copyright (c) 2012-2017 Yoshio HANAWA - -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. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "php.h" -#include "php_ini.h" -#include "ext/standard/info.h" - -#if !defined(PHP_VERSION_ID) || PHP_VERSION_ID < 50300 -#include "ext/date/lib/timelib.h" -#endif - -#include "php_timecop.h" - -#ifdef ZFS -#include "TSRM.h" -#endif - -ZEND_DECLARE_MODULE_GLOBALS(timecop) - -static void timecop_globals_ctor(zend_timecop_globals *globals TSRMLS_DC) { - /* Initialize your global struct */ - globals->func_override = 1; - globals->sync_request_time = 1; - globals->orig_request_time = NULL; - globals->timecop_mode = TIMECOP_MODE_REALTIME; - globals->freezed_time.sec = 0; - globals->freezed_time.usec = 0; - globals->travel_origin.sec = 0; - globals->travel_origin.usec = 0; - globals->travel_offset.sec = 0; - globals->travel_offset.usec = 0; - globals->scaling_factor = 1; - globals->ce_DateTimeZone = NULL; - globals->ce_DateTimeInterface = NULL; - globals->ce_DateTime = NULL; - globals->ce_TimecopDateTime = NULL; - globals->ce_DateTimeImmutable = NULL; - globals->ce_TimecopDateTimeImmutable = NULL; -} - -static const struct timecop_override_func_entry timecop_override_func_table[] = { - TIMECOP_OFE("time"), - TIMECOP_OFE("mktime"), - TIMECOP_OFE("gmmktime"), - TIMECOP_OFE("date"), - TIMECOP_OFE("gmdate"), - TIMECOP_OFE("idate"), - TIMECOP_OFE("getdate"), - TIMECOP_OFE("localtime"), - TIMECOP_OFE("strtotime"), - TIMECOP_OFE("strftime"), - TIMECOP_OFE("gmstrftime"), -#ifdef HAVE_GETTIMEOFDAY - TIMECOP_OFE("microtime"), - TIMECOP_OFE("gettimeofday"), -#endif - TIMECOP_OFE("unixtojd"), - TIMECOP_OFE("date_create"), -#if PHP_VERSION_ID >= 50300 - TIMECOP_OFE("date_create_from_format"), -#endif -#if PHP_VERSION_ID >= 50500 - TIMECOP_OFE("date_create_immutable"), - TIMECOP_OFE("date_create_immutable_from_format"), -#endif - {NULL, NULL, NULL} -}; - -static const struct timecop_override_class_entry timecop_override_class_table[] = { - TIMECOP_OCE("datetime", "__construct"), -#if PHP_VERSION_ID >= 50300 - TIMECOP_OCE("datetime", "createfromformat"), -#endif -#if PHP_VERSION_ID >= 50500 - TIMECOP_OCE("datetimeimmutable", "__construct"), - TIMECOP_OCE("datetimeimmutable", "createfromformat"), -#endif - {NULL, NULL, NULL, NULL} -}; - -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_INFO(arginfo_timecop_time, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_mktime, 0, 0, 0) - ZEND_ARG_INFO(0, hour) - ZEND_ARG_INFO(0, min) - ZEND_ARG_INFO(0, sec) - ZEND_ARG_INFO(0, mon) - ZEND_ARG_INFO(0, day) - ZEND_ARG_INFO(0, year) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_gmmktime, 0, 0, 0) - ZEND_ARG_INFO(0, hour) - ZEND_ARG_INFO(0, min) - ZEND_ARG_INFO(0, sec) - ZEND_ARG_INFO(0, mon) - ZEND_ARG_INFO(0, day) - ZEND_ARG_INFO(0, year) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_date, 0, 0, 1) - ZEND_ARG_INFO(0, format) - ZEND_ARG_INFO(0, timestamp) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_gmdate, 0, 0, 1) - ZEND_ARG_INFO(0, format) - ZEND_ARG_INFO(0, timestamp) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_idate, 0, 0, 1) - ZEND_ARG_INFO(0, format) - ZEND_ARG_INFO(0, timestamp) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_getdate, 0, 0, 0) - ZEND_ARG_INFO(0, timestamp) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_localtime, 0, 0, 0) - ZEND_ARG_INFO(0, timestamp) - ZEND_ARG_INFO(0, associative_array) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_strtotime, 0, 0, 1) - ZEND_ARG_INFO(0, time) - ZEND_ARG_INFO(0, now) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_strftime, 0, 0, 1) - ZEND_ARG_INFO(0, format) - ZEND_ARG_INFO(0, timestamp) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_gmstrftime, 0, 0, 1) - ZEND_ARG_INFO(0, format) - ZEND_ARG_INFO(0, timestamp) -ZEND_END_ARG_INFO() - -#ifdef HAVE_GETTIMEOFDAY -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_microtime, 0, 0, 0) - ZEND_ARG_INFO(0, get_as_float) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_gettimeofday, 0, 0, 0) - ZEND_ARG_INFO(0, get_as_float) -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() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_date_create, 0, 0, 0) - ZEND_ARG_INFO(0, time) - ZEND_ARG_INFO(0, object) -ZEND_END_ARG_INFO() - -#if PHP_VERSION_ID >= 50300 -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_date_create_from_format, 0, 0, 2) - ZEND_ARG_INFO(0, format) - ZEND_ARG_INFO(0, time) - ZEND_ARG_INFO(0, object) -ZEND_END_ARG_INFO() -#endif - -#if !defined(PHP_VERSION_ID) || PHP_VERSION_ID < 50300 -ZEND_BEGIN_ARG_INFO_EX(arginfo_date_timestamp_set, 0, 0, 2) - ZEND_ARG_INFO(0, object) - ZEND_ARG_INFO(0, unixtimestamp) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_date_timestamp_get, 0, 0, 1) - ZEND_ARG_INFO(0, object) -ZEND_END_ARG_INFO() -#endif - -/* {{{ timecop_functions[] */ -const zend_function_entry timecop_functions[] = { - PHP_FE(timecop_freeze, arginfo_timecop_freeze) - PHP_FE(timecop_travel, arginfo_timecop_travel) - PHP_FE(timecop_scale, arginfo_timecop_scale) - PHP_FE(timecop_return, arginfo_timecop_return) - PHP_FE(timecop_time, arginfo_timecop_time) - PHP_FE(timecop_mktime, arginfo_timecop_mktime) - PHP_FE(timecop_gmmktime, arginfo_timecop_gmmktime) - PHP_FE(timecop_date, arginfo_timecop_date) - PHP_FE(timecop_gmdate, arginfo_timecop_gmdate) - PHP_FE(timecop_idate, arginfo_timecop_idate) - PHP_FE(timecop_getdate, arginfo_timecop_getdate) - PHP_FE(timecop_localtime, arginfo_timecop_localtime) - PHP_FE(timecop_strtotime, arginfo_timecop_strtotime) - PHP_FE(timecop_strftime, arginfo_timecop_strftime) - PHP_FE(timecop_gmstrftime, arginfo_timecop_gmstrftime) -#ifdef HAVE_GETTIMEOFDAY - PHP_FE(timecop_microtime, arginfo_timecop_microtime) - PHP_FE(timecop_gettimeofday, arginfo_timecop_gettimeofday) -#endif - PHP_FE(timecop_unixtojd, arginfo_timecop_unixtojd) - PHP_FE(timecop_date_create, arginfo_timecop_date_create) -#if PHP_VERSION_ID >= 50300 - PHP_FE(timecop_date_create_from_format, arginfo_timecop_date_create_from_format) -#endif -#if PHP_VERSION_ID >= 50500 - PHP_FE(timecop_date_create_immutable, arginfo_timecop_date_create) - PHP_FE(timecop_date_create_immutable_from_format, arginfo_timecop_date_create_from_format) -#endif -#if !defined(PHP_VERSION_ID) || PHP_VERSION_ID < 50300 - PHP_FE(date_timestamp_set, arginfo_date_timestamp_set) - PHP_FE(date_timestamp_get, arginfo_date_timestamp_get) -#endif - {NULL, NULL, NULL} -}; -/* }}} */ - -/* declare method parameters, */ - -/* each method can have its own parameters and visibility */ -static zend_function_entry timecop_funcs_timecop[] = { - PHP_ME_MAPPING(freeze, timecop_freeze, arginfo_timecop_travel, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - PHP_ME_MAPPING(travel, timecop_travel, arginfo_timecop_travel, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - PHP_ME_MAPPING(scale, timecop_scale, arginfo_timecop_scale, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - PHP_ME_MAPPING(return, timecop_return, arginfo_timecop_return, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - {NULL, NULL, NULL} -}; - -static zend_function_entry timecop_funcs_date[] = { - PHP_ME(TimecopDateTime, __construct, arginfo_timecop_date_create, - ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) -#if PHP_VERSION_ID >= 50300 - PHP_ME_MAPPING(createFromFormat, timecop_date_create_from_format, arginfo_timecop_date_create_from_format, - ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) -#endif - {NULL, NULL, NULL} -}; - -static zend_function_entry timecop_funcs_orig_date[] = { - PHP_ME(TimecopOrigDateTime, __construct, arginfo_timecop_date_create, - ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} -}; - -#if PHP_VERSION_ID >= 50500 -static zend_function_entry timecop_funcs_immutable[] = { - PHP_ME(TimecopDateTimeImmutable, __construct, arginfo_timecop_date_create, - ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) - PHP_ME_MAPPING(createFromFormat, timecop_date_create_immutable_from_format, arginfo_timecop_date_create_from_format, - ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - {NULL, NULL, NULL} -}; - -static zend_function_entry timecop_funcs_orig_immutable[] = { - PHP_ME(TimecopOrigDateTimeImmutable, __construct, arginfo_timecop_date_create, - ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} -}; -#endif - -#define MKTIME_NUM_ARGS 6 - -#define TIMECOP_CALL_FUNCTION(func_name, index_to_fill_timestamp) \ - _timecop_call_function(INTERNAL_FUNCTION_PARAM_PASSTHRU, ORIG_FUNC_NAME(func_name), index_to_fill_timestamp); - -#define TIMECOP_CALL_MKTIME(mktime_func_name, date_func_name) \ - _timecop_call_mktime(INTERNAL_FUNCTION_PARAM_PASSTHRU, ORIG_FUNC_NAME(mktime_func_name), ORIG_FUNC_NAME(date_func_name)); - -#if !defined(PHP_VERSION_ID) || PHP_VERSION_ID < 50300 -#define DATE_CHECK_INITIALIZED(member, class_name) \ - if (!(member)) { \ - php_error_docref(NULL TSRMLS_CC, E_WARNING, "The " #class_name " object has not been correctly initialized by its constructor"); \ - RETURN_FALSE; \ - } - -typedef struct _php_date_obj php_date_obj; - -struct _php_date_obj { - zend_object std; - timelib_time *time; -}; -#endif - -static void timecop_globals_ctor(zend_timecop_globals *globals TSRMLS_DC); - -static int register_timecop_classes(TSRMLS_D); -static int timecop_func_override(TSRMLS_D); -static int timecop_class_override(TSRMLS_D); -static int timecop_func_override_clear(TSRMLS_D); -static int timecop_class_override_clear(TSRMLS_D); - -static int update_request_time(long unixtime TSRMLS_DC); -static int restore_request_time(TSRMLS_D); - -static int fill_mktime_params(zval ***params, const char *date_function_name, int from TSRMLS_DC); -static int get_formatted_mock_time(zval *time, zval *timezone_obj, zval **retval_time, zval **retval_timezone TSRMLS_DC); -static long get_mock_fraction(zval *time, zval *timezone_obj TSRMLS_DC); - -static void _timecop_call_function(INTERNAL_FUNCTION_PARAMETERS, const char *function_name, int index_to_fill_timestamp); -static void _timecop_call_mktime(INTERNAL_FUNCTION_PARAMETERS, const char *mktime_function_name, const char *date_function_name); - -static int get_mock_timeval(tc_timeval *fixed, const tc_timeval *now TSRMLS_DC); -static inline long mock_timestamp(); - -static int get_timeval_from_datetime(tc_timeval *tp, zval *dt TSRMLS_DC); -static int get_current_time(tc_timeval *now TSRMLS_DC); - -static void _timecop_orig_datetime_constructor(INTERNAL_FUNCTION_PARAMETERS, int immutable); -static inline void _timecop_date_create(INTERNAL_FUNCTION_PARAMETERS, int immutable); -static inline void _timecop_datetime_constructor(INTERNAL_FUNCTION_PARAMETERS, int immutable); -static void _timecop_datetime_constructor_ex(INTERNAL_FUNCTION_PARAMETERS, zval *obj, int immutable); - -static inline zval* _call_php_method_with_0_params(zval **object_pp, zend_class_entry *obj_ce, const char *method_name, zval **retval_ptr_ptr TSRMLS_DC); -static inline zval* _call_php_method_with_1_params(zval **object_pp, zend_class_entry *obj_ce, const char *method_name, zval **retval_ptr_ptr, zval *arg1 TSRMLS_DC); -static inline zval* _call_php_method_with_2_params(zval **object_pp, zend_class_entry *obj_ce, const char *method_name, zval **retval_ptr_ptr, zval *arg1, zval *arg2 TSRMLS_DC); -static inline zval* _call_php_method(zval **object_pp, zend_class_entry *obj_ce, const char *method_name, zval **retval_ptr_ptr, int param_count, zval* arg1, zval* arg2 TSRMLS_DC); -static void _call_php_function_with_3_params(const char *function_name, zval **retval_ptr_ptr, zval *arg1, zval *arg2, zval *arg3 TSRMLS_DC); -static inline void _call_php_function_with_params(const char *function_name, zval **retval_ptr_ptr, zend_uint param_count, zval **params[] TSRMLS_DC); - -/* {{{ timecop_module_entry - */ -zend_module_entry timecop_module_entry = { -#if ZEND_MODULE_API_NO >= 20010901 - STANDARD_MODULE_HEADER, -#endif - "timecop", - timecop_functions, - PHP_MINIT(timecop), - PHP_MSHUTDOWN(timecop), - PHP_RINIT(timecop), - PHP_RSHUTDOWN(timecop), - PHP_MINFO(timecop), -#if ZEND_MODULE_API_NO >= 20010901 - PHP_TIMECOP_VERSION, -#endif - STANDARD_MODULE_PROPERTIES -}; -/* }}} */ - -#ifdef COMPILE_DL_TIMECOP -ZEND_GET_MODULE(timecop) -#endif - -/* {{{ PHP_INI - */ -PHP_INI_BEGIN() - STD_PHP_INI_ENTRY("timecop.func_override", "1", - PHP_INI_SYSTEM, OnUpdateLong, func_override, zend_timecop_globals, timecop_globals) - STD_PHP_INI_ENTRY("timecop.sync_request_time", "1", - PHP_INI_SYSTEM, OnUpdateLong, sync_request_time, zend_timecop_globals, timecop_globals) -PHP_INI_END() -/* }}} */ - -/* {{{ PHP_MINIT_FUNCTION - */ -PHP_MINIT_FUNCTION(timecop) -{ - ZEND_INIT_MODULE_GLOBALS(timecop, timecop_globals_ctor, NULL); - REGISTER_INI_ENTRIES(); - register_timecop_classes(TSRMLS_C); - return SUCCESS; -} -/* }}} */ - -/* {{{ PHP_MSHUTDOWN_FUNCTION - */ -PHP_MSHUTDOWN_FUNCTION(timecop) -{ - UNREGISTER_INI_ENTRIES(); - return SUCCESS; -} -/* }}} */ - -/* {{{ PHP_RINIT_FUNCTION(timecop) */ -PHP_RINIT_FUNCTION(timecop) -{ - if (TIMECOP_G(func_override)) { - if (SUCCESS != timecop_func_override(TSRMLS_C) || - SUCCESS != timecop_class_override(TSRMLS_C)) { - return FAILURE; - } - } - - return SUCCESS; -} -/* }}} */ - -/* {{{ PHP_RSHUTDOWN_FUNCTION(timecop) */ -PHP_RSHUTDOWN_FUNCTION(timecop) -{ - if (TIMECOP_G(func_override)) { - timecop_func_override_clear(TSRMLS_C); - timecop_class_override_clear(TSRMLS_C); - } - - if (TIMECOP_G(orig_request_time)) { - restore_request_time(TSRMLS_C); - } - - TIMECOP_G(timecop_mode) = TIMECOP_MODE_REALTIME; - TIMECOP_G(scaling_factor) = 1; - - return SUCCESS; -} -/* }}} */ - - -/* {{{ PHP_MINFO_FUNCTION - */ -PHP_MINFO_FUNCTION(timecop) -{ - php_info_print_table_start(); - php_info_print_table_header(2, "timecop", "enabled"); - php_info_print_table_row(2, "Version", PHP_TIMECOP_VERSION); - php_info_print_table_end(); - - DISPLAY_INI_ENTRIES(); -} -/* }}} */ - -static int register_timecop_classes(TSRMLS_D) -{ - zend_class_entry **pce; - zend_class_entry ce; - zend_class_entry *tmp, *date_ce, *timezone_ce, *immutable_ce = NULL, *interface_ce = NULL; - - if (zend_hash_find(CG(class_table), "datetime", sizeof("datetime"), (void **) &pce) == FAILURE) { - /* DateTime must be initialized before */ - php_error_docref("https://github.com/hnw/php-timecop" TSRMLS_CC, E_WARNING, - "timecop couldn't find class %s.", "DateTime"); - return SUCCESS; - } - date_ce = *pce; - - if (zend_hash_find(CG(class_table), "datetimezone", sizeof("datetimezone"), (void **) &pce) == FAILURE) { - /* DateTimeImmutable must be initialized before */ - php_error_docref("https://github.com/hnw/php-timecop" TSRMLS_CC, E_WARNING, - "timecop couldn't find class %s.", "DateTimeZone"); - return SUCCESS; - } - timezone_ce = *pce; - -#if PHP_VERSION_ID >= 50500 - if (zend_hash_find(CG(class_table), "datetimeimmutable", sizeof("datetimeimmutable"), (void **) &pce) == FAILURE) { - /* DateTimeImmutable must be initialized before */ - php_error_docref("https://github.com/hnw/php-timecop" TSRMLS_CC, E_WARNING, - "timecop couldn't find class %s.", "DateTimeImmutable"); - return SUCCESS; - } - immutable_ce = *pce; - - if (zend_hash_find(CG(class_table), "datetimeinterface", sizeof("datetimeinterface"), (void **) &pce) == FAILURE) { - /* DateTimeInterface must be initialized before */ - php_error_docref("https://github.com/hnw/php-timecop" TSRMLS_CC, E_WARNING, - "timecop couldn't find interface %s.", "DateTimeInterface"); - return SUCCESS; - } - interface_ce = *pce; -#endif - - INIT_CLASS_ENTRY(ce, "Timecop", timecop_funcs_timecop); - zend_register_internal_class(&ce TSRMLS_CC); - - TIMECOP_G(ce_DateTimeZone) = timezone_ce; - if (interface_ce) { - TIMECOP_G(ce_DateTimeInterface) = interface_ce; - } else { - TIMECOP_G(ce_DateTimeInterface) = date_ce; - } - - /* replace DateTime */ - INIT_CLASS_ENTRY(ce, "TimecopDateTime", timecop_funcs_date); - tmp = zend_register_internal_class_ex(&ce, date_ce, NULL TSRMLS_CC); - tmp->create_object = date_ce->create_object; - - TIMECOP_G(ce_DateTime) = date_ce; - TIMECOP_G(ce_TimecopDateTime) = tmp; - - INIT_CLASS_ENTRY(ce, "TimecopOrigDateTime", timecop_funcs_orig_date); - tmp = zend_register_internal_class_ex(&ce, date_ce, NULL TSRMLS_CC); - tmp->create_object = date_ce->create_object; - -#if PHP_VERSION_ID >= 50500 - /* replace DateTimeImmutable */ - INIT_CLASS_ENTRY(ce, "TimecopDateTimeImmutable", timecop_funcs_immutable); - tmp = zend_register_internal_class_ex(&ce, immutable_ce, NULL TSRMLS_CC); - tmp->create_object = immutable_ce->create_object; - - TIMECOP_G(ce_DateTimeImmutable) = immutable_ce; - TIMECOP_G(ce_TimecopDateTimeImmutable) = tmp; - - INIT_CLASS_ENTRY(ce, "TimecopOrigDateTimeImmutable", timecop_funcs_orig_immutable); - tmp = zend_register_internal_class_ex(&ce, immutable_ce, NULL TSRMLS_CC); - tmp->create_object = immutable_ce->create_object; -#endif - - return SUCCESS; -} - -static int timecop_func_override(TSRMLS_D) -{ - const struct timecop_override_func_entry *p; - zend_function *zf_orig, *zf_ovrd, *zf_save; - - p = &(timecop_override_func_table[0]); - while (p->orig_func != NULL) { - if (zend_hash_find(EG(function_table), p->orig_func, strlen(p->orig_func)+1, - (void **)&zf_orig) != SUCCESS) { - /* Do nothing. Because some functions are introduced by optional extensions. */ - p++; - continue; - } - if (zend_hash_find(EG(function_table), p->ovrd_func, strlen(p->ovrd_func)+1, - (void **)&zf_ovrd) != SUCCESS) { - php_error_docref("https://github.com/hnw/php-timecop" TSRMLS_CC, E_WARNING, - "timecop couldn't find function %s.", p->ovrd_func); - p++; - continue; - } - if (zend_hash_find(EG(function_table), p->save_func, strlen(p->save_func)+1, - (void **)&zf_save) == SUCCESS) { - php_error_docref("https://github.com/hnw/php-timecop" TSRMLS_CC, E_WARNING, - "timecop couldn't create function %s because already exists.", - p->save_func); - p++; - continue; - } - - TIMECOP_ASSERT(zf_orig->type == ZEND_INTERNAL_FUNCTION); - TIMECOP_ASSERT(zf_ovrd->type == ZEND_INTERNAL_FUNCTION); - - zend_hash_add(EG(function_table), p->save_func, strlen(p->save_func)+1, - zf_orig, sizeof(zend_function), NULL); - function_add_ref(zf_orig); - - zend_hash_update(EG(function_table), p->orig_func, strlen(p->orig_func)+1, - zf_ovrd, sizeof(zend_function), NULL); - function_add_ref(zf_ovrd); - - p++; - } - return SUCCESS; -} - -static int timecop_class_override(TSRMLS_D) -{ - const struct timecop_override_class_entry *p; - zend_class_entry **pce_ovrd, **pce_orig, *ce_ovrd, *ce_orig; - zend_function *zf_orig, *zf_ovrd, *zf_save, *zf_new; - - p = &(timecop_override_class_table[0]); - while (p->orig_class != NULL) { - if (zend_hash_find(EG(class_table), p->ovrd_class, strlen(p->ovrd_class)+1 , - (void **)&pce_ovrd) != SUCCESS) { - php_error_docref("https://github.com/hnw/php-timecop" TSRMLS_CC, E_WARNING, - "timecop couldn't find class %s.", p->ovrd_class); - p++; - continue; - } - if (zend_hash_find(EG(class_table), p->orig_class, strlen(p->orig_class)+1, - (void **)&pce_orig) != SUCCESS) { - php_error_docref("https://github.com/hnw/php-timecop" TSRMLS_CC, E_WARNING, - "timecop couldn't find function %s.", p->orig_class); - p++; - continue; - } - - ce_ovrd = *pce_ovrd; - ce_orig = *pce_orig; - - if (zend_hash_find(&ce_orig->function_table, p->orig_method, strlen(p->orig_method)+1, - (void **)&zf_orig) != SUCCESS) { - php_error_docref("https://github.com/hnw/php-timecop" TSRMLS_CC, E_WARNING, - "timecop couldn't find method %s::%s.", - p->orig_class, p->orig_method); - p++; - continue; - } - if (zend_hash_find(&ce_ovrd->function_table, p->orig_method, strlen(p->orig_method)+1, - (void **)&zf_ovrd) != SUCCESS) { - php_error_docref("https://github.com/hnw/php-timecop" TSRMLS_CC, E_WARNING, - "timecop couldn't find method %s::%s.", - p->ovrd_class, p->orig_method); - p++; - continue; - } - if (zend_hash_find(&ce_orig->function_table, p->save_method, strlen(p->save_method)+1, - (void **)&zf_save) == SUCCESS) { - php_error_docref("https://github.com/hnw/php-timecop" TSRMLS_CC, E_WARNING, - "timecop couldn't create method %s::%s because already exists.", - p->orig_class, p->save_method); - p++; - continue; - } - - TIMECOP_ASSERT(zf_orig->type == ZEND_INTERNAL_FUNCTION); - TIMECOP_ASSERT(ce_orig->type & ZEND_INTERNAL_CLASS); - - zend_hash_add(&ce_orig->function_table, p->save_method, strlen(p->save_method)+1, - zf_orig, sizeof(zend_function), NULL); - function_add_ref(zf_orig); - - TIMECOP_ASSERT(zf_ovrd->type == ZEND_INTERNAL_FUNCTION); - TIMECOP_ASSERT(ce_ovrd->type & ZEND_INTERNAL_CLASS); - - zend_hash_update(&ce_orig->function_table, p->orig_method, strlen(p->orig_method)+1, - zf_ovrd, sizeof(zend_function), (void**)&zf_new); - function_add_ref(zf_new); - - //TIMECOP_ASSERT(zf_new != NULL); - //TIMECOP_ASSERT(zf_new != zf_orig); - - if (strcmp(p->orig_method, "__construct") == 0) { - ce_orig->constructor = zf_new; - } - p++; - } - return SUCCESS; -} - -/* clear function overriding. */ -static int timecop_func_override_clear(TSRMLS_D) -{ - const struct timecop_override_func_entry *p; - zend_function *zf_orig; - - p = &(timecop_override_func_table[0]); - while (p->orig_func != NULL) { - if (zend_hash_find(EG(function_table), p->save_func, strlen(p->save_func)+1, - (void **)&zf_orig) != SUCCESS) { - p++; - continue; - } - - zend_hash_update(EG(function_table), p->orig_func, strlen(p->orig_func)+1, - zf_orig, sizeof(zend_function), NULL); - function_add_ref(zf_orig); - - zend_hash_del(EG(function_table), p->save_func, strlen(p->save_func)+1); - - p++; - } - return SUCCESS; -} - -static int timecop_class_override_clear(TSRMLS_D) -{ - const struct timecop_override_class_entry *p; - zend_class_entry **pce_orig, *ce_orig; - zend_function *zf_orig; - - p = &(timecop_override_class_table[0]); - while (p->orig_class != NULL) { - if (zend_hash_find(EG(class_table), p->orig_class, strlen(p->orig_class)+1, - (void **)&pce_orig) != SUCCESS) { - php_error_docref("https://github.com/hnw/php-timecop" TSRMLS_CC, E_WARNING, - "timecop couldn't find class %s.", p->orig_class); - p++; - continue; - } - ce_orig = *pce_orig; - - if (zend_hash_find(&ce_orig->function_table, p->save_method, strlen(p->save_method)+1, - (void **)&zf_orig) != SUCCESS) { - php_error_docref("https://github.com/hnw/php-timecop" TSRMLS_CC, E_WARNING, - "timecop couldn't find method %s::%s.", - p->orig_class, p->save_method); - p++; - continue; - } - - zend_hash_update(&ce_orig->function_table, p->orig_method, strlen(p->orig_method)+1, - zf_orig, sizeof(zend_function), NULL); - function_add_ref(zf_orig); - - zend_hash_del(&ce_orig->function_table, p->save_method, strlen(p->save_method)+1); - - if (strcmp(p->orig_method, "__construct") == 0) { - ce_orig->constructor = zf_orig; - } - p++; - } - return SUCCESS; -} - -static int update_request_time(long unixtime TSRMLS_DC) -{ - zval **server_vars; - zval **request_time; - zval *tmp; - - if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **) &server_vars) == SUCCESS && - Z_TYPE_PP(server_vars) == IS_ARRAY && - zend_hash_find(Z_ARRVAL_PP(server_vars), "REQUEST_TIME", sizeof("REQUEST_TIME"), (void **) &request_time) == SUCCESS) { - if (TIMECOP_G(orig_request_time) == NULL) { - MAKE_STD_ZVAL(TIMECOP_G(orig_request_time)); - ZVAL_ZVAL(TIMECOP_G(orig_request_time), *request_time, 1, 0); - } - MAKE_STD_ZVAL(tmp); - ZVAL_LONG(tmp, unixtime); - add_assoc_zval(*server_vars, "REQUEST_TIME", tmp); - } - - return SUCCESS; -} - -static int restore_request_time(TSRMLS_D) -{ - zval **server_vars; - zval **request_time; - zval *orig_request_time; - orig_request_time = TIMECOP_G(orig_request_time); - - if (orig_request_time && - zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **) &server_vars) == SUCCESS && - Z_TYPE_PP(server_vars) == IS_ARRAY && - zend_hash_find(Z_ARRVAL_PP(server_vars), "REQUEST_TIME", sizeof("REQUEST_TIME"), (void **) &request_time) == SUCCESS) { - add_assoc_zval(*server_vars, "REQUEST_TIME", orig_request_time); - TIMECOP_G(orig_request_time) = NULL; - } - return SUCCESS; -} - -static int fill_mktime_params(zval ***params, const char *date_function_name, int from TSRMLS_DC) -{ - int i; - char *formats[MKTIME_NUM_ARGS] = {"H", "i", "s", "n", "j", "Y"}; - zval format, timestamp, *retval_ptr; - - INIT_ZVAL(timestamp); - ZVAL_LONG(×tamp, mock_timestamp(TSRMLS_C)); - INIT_ZVAL(format); - - for (i = from; i < MKTIME_NUM_ARGS; i++) { - ZVAL_STRING(&format, formats[i], 0); - - call_php_function_with_2_params(date_function_name, &retval_ptr, &format, ×tamp); - if (retval_ptr) { - ZVAL_ZVAL(*params[i], retval_ptr, 1, 1); - } - } - - return MKTIME_NUM_ARGS; -} - -/* - * get_formatted_mock_time() : return formatted mock time like "2000-12-30 01:02:03.456000" - * - * pseudo code: - * - * function get_formatted_mock_time($time, $timezone_obj) { - * if ($time === null || $time === false || $time === "") { - * $time = "now"; - * } - * $now = get_mock_timeval(); - * if ($timezone_obj) { - * // save default timezone - * $zonename = $timezone_obj->getName(); - * $orig_zonename = date_default_timezone_get(); - * date_default_timezone_set($zonename); - * } - * $fixed_sec = strtotime($time, $now->sec); - * if ($timezone_obj && $orig_zonename) { - * // restore default timezone - * date_default_timezone_set($orig_zonename); - * } - * if ($fixed_sec === FALSE) { - * return false; - * } - * $fixed_usec = get_mock_fraction($time, $timezone_obj); - * if ($fixed_usec === -1) { - * $fixed_usec = $now->usec; - * } - * $dt = date_create($time, $timezone_obj); - * $orig_sec = $dt->getTimestamp(); - * if ($orig_sec != $fixed_sec) { - * // workaround against PHP Bug #62896 - * $dt->setTimestamp($fixed_sec); - * } - * $format = sprintf("Y-m-d H:i:s.%06d", $fixed_usec); - * $formatted_time = $dt->format($format); - * return $formatted_time; - * } - */ -static int get_formatted_mock_time(zval *time, zval *timezone_obj, zval **retval_time, zval **retval_timezone TSRMLS_DC) -{ - zval *fixed_sec, *orig_sec, *orig_zonename; - zval str_now, now_timestamp; - tc_timeval now; - long fixed_usec; - - if (TIMECOP_G(timecop_mode) == TIMECOP_MODE_REALTIME) { - MAKE_STD_ZVAL(*retval_time); - ZVAL_FALSE(*retval_time); - MAKE_STD_ZVAL(*retval_timezone); - ZVAL_NULL(*retval_timezone); - return -1; - } - - if (time == NULL || Z_TYPE_P(time) == IS_NULL || - (Z_TYPE_P(time) == IS_BOOL && !Z_BVAL_P(time)) || - (Z_TYPE_P(time) == IS_STRING && Z_STRLEN_P(time) == 0)) { - INIT_ZVAL(str_now); - ZVAL_STRING(&str_now, "now", 0); - time = &str_now; - } - - get_mock_timeval(&now, NULL TSRMLS_CC); - - // @todo Restore removed timezone handling code? https://github.com/kiddivouchers/php-timecop/pull/6 - - INIT_ZVAL(now_timestamp); - ZVAL_LONG(&now_timestamp, now.sec); - call_php_function_with_2_params(ORIG_FUNC_NAME("strtotime"), &fixed_sec, time, &now_timestamp); - - if (Z_TYPE_P(fixed_sec) == IS_BOOL && !Z_BVAL_P(fixed_sec)) { - /* $fixed_sec === false */ - MAKE_STD_ZVAL(*retval_time); - ZVAL_FALSE(*retval_time); - MAKE_STD_ZVAL(*retval_timezone); - ZVAL_NULL(*retval_timezone); - return -1; - } - - fixed_usec = get_mock_fraction(time, timezone_obj TSRMLS_CC); - if (fixed_usec == -1) { - fixed_usec = now.usec; - } - - { - zval *dt; - zval format_str; - char buf[64]; - - call_php_function_with_2_params(ORIG_FUNC_NAME("date_create"), &dt, time, timezone_obj); - if (Z_TYPE_P(dt) == IS_BOOL && !Z_BVAL_P(dt)) { - MAKE_STD_ZVAL(*retval_time); - ZVAL_FALSE(*retval_time); - MAKE_STD_ZVAL(*retval_timezone); - ZVAL_NULL(*retval_timezone); - return -1; - } - - sprintf(buf, "Y-m-d H:i:s.%06ld", fixed_usec); - INIT_ZVAL(format_str); - ZVAL_STRING(&format_str, buf, 0); - call_php_function_with_1_params("date_timestamp_get", &orig_sec, dt); - if (Z_TYPE_P(orig_sec) == IS_LONG && - Z_TYPE_P(fixed_sec) == IS_LONG && - Z_LVAL_P(orig_sec) != Z_LVAL_P(fixed_sec)) { - call_php_function_with_2_params("date_timestamp_set", NULL, dt, fixed_sec); - } - - call_php_method_with_0_params(&dt, TIMECOP_G(ce_DateTime), "gettimezone", retval_timezone); - call_php_method_with_1_params(&dt, TIMECOP_G(ce_DateTime), "format", retval_time, &format_str); - - zval_ptr_dtor(&dt); - } - - if (fixed_sec) { - zval_ptr_dtor(&fixed_sec); - } - - return 0; -} - -/* - * get_mock_fraction() - * - * pseudo code: - * - * function get_mock_fraction($time, $timezone_obj) { - * $dt1 = date_create($time, $timezone_obj); - * $dt2 = date_create($time, $timezone_obj); - * $usec1 = $dt1->format("u"); - * $usec2 = $dt2->format("u"); - * if ($usec1 === $usec2) { - * $fixed_usec = $usec1; - * } else { - * $fixed_usec = -1; - * } - * return $fixed_usec; - * } - */ -static long get_mock_fraction(zval *time, zval *timezone_obj TSRMLS_DC) -{ - zval *dt1, *dt2, *usec1, *usec2; - long fixed_usec; - zval u_str; - - call_php_function_with_2_params(ORIG_FUNC_NAME("date_create"), &dt1, time, timezone_obj); - if (Z_TYPE_P(dt1) == IS_BOOL && !Z_BVAL_P(dt1)) { - return -1; - } - call_php_function_with_2_params(ORIG_FUNC_NAME("date_create"), &dt2, time, timezone_obj); - if (Z_TYPE_P(dt2) == IS_BOOL && !Z_BVAL_P(dt2)) { - zval_ptr_dtor(&dt1); - return -1; - } - INIT_ZVAL(u_str); - ZVAL_STRING(&u_str, "u", 0); - call_php_method_with_1_params(&dt1, TIMECOP_G(ce_DateTime), "format", &usec1, &u_str); - call_php_method_with_1_params(&dt2, TIMECOP_G(ce_DateTime), "format", &usec2, &u_str); - convert_to_long(usec1); - convert_to_long(usec2); - - if (Z_LVAL_P(usec1) == Z_LVAL_P(usec2)) { - fixed_usec = Z_LVAL_P(usec1); - } else { - fixed_usec = -1; - } - zval_ptr_dtor(&dt1); - zval_ptr_dtor(&dt2); - zval_ptr_dtor(&usec1); - zval_ptr_dtor(&usec2); - - return fixed_usec; -} - -static void _timecop_call_function(INTERNAL_FUNCTION_PARAMETERS, const char *function_name, int index_to_fill_timestamp) -{ - int params_size; - zval ***params, *zp, filled_timestamp, *retval_ptr; - zend_uint argc; - - params_size = MAX(ZEND_NUM_ARGS(), index_to_fill_timestamp+1); - params = (zval ***)safe_emalloc(sizeof(zval **), params_size, 0); - - if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), params) == FAILURE) { - efree(params); - return; - } - argc = ZEND_NUM_ARGS(); - - if (ZEND_NUM_ARGS() == index_to_fill_timestamp) { - INIT_ZVAL(filled_timestamp); - ZVAL_LONG(&filled_timestamp, mock_timestamp(TSRMLS_C)); - zp = &filled_timestamp; - - params[argc] = &zp; - argc++; - } - - _call_php_function_with_params(function_name, &retval_ptr, argc, params TSRMLS_CC); - - efree(params); - - if (retval_ptr) { - RETVAL_ZVAL(retval_ptr, 1, 1); - } -} - -/* {{{ _timecop_call_mktime - timecop_(gm)mktime helper */ -static void _timecop_call_mktime(INTERNAL_FUNCTION_PARAMETERS, const char *mktime_function_name, const char *date_function_name) -{ - int params_size; - zval ***params, *filled_value[MKTIME_NUM_ARGS], *retval_ptr; - zend_uint argc; - int i; - - params_size = MAX(ZEND_NUM_ARGS(), MKTIME_NUM_ARGS); - params = (zval ***)safe_emalloc(sizeof(zval **), params_size, 0); - - if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), params) == FAILURE) { - efree(params); - return; - } - argc = ZEND_NUM_ARGS(); - - for (i = argc; i < MKTIME_NUM_ARGS; i++) { - ALLOC_INIT_ZVAL(filled_value[i]); - params[i] = &filled_value[i]; - } - fill_mktime_params(params, date_function_name, argc TSRMLS_CC); - - if (ZEND_NUM_ARGS() == 0) { - php_error_docref(NULL TSRMLS_CC, E_STRICT, "You should be using the time() function instead"); - } - - _call_php_function_with_params(mktime_function_name, &retval_ptr, params_size, params TSRMLS_CC); - - for (i = argc; i < MKTIME_NUM_ARGS; i++) { - zval_ptr_dtor(&filled_value[i]); - } - efree(params); - - if (retval_ptr) { - RETVAL_ZVAL(retval_ptr, 1, 1); - } -} -/* }}} */ - -/* {{{ proto int timecop_freeze(long timestamp) - Time travel to specified timestamp and freeze */ -PHP_FUNCTION(timecop_freeze) -{ - zval *dt; - long timestamp; - tc_timeval freezed_tv; - - if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "O", &dt, TIMECOP_G(ce_DateTimeInterface)) != FAILURE) { - get_timeval_from_datetime(&freezed_tv, dt TSRMLS_CC); - } else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "l", ×tamp) != FAILURE) { - freezed_tv.sec = timestamp; - freezed_tv.usec = 0; - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "This function accepts either (DateTimeInterface) OR (int) as arguments."); - RETURN_FALSE; - } - - TIMECOP_G(timecop_mode) = TIMECOP_MODE_FREEZE; - TIMECOP_G(freezed_time) = freezed_tv; - - if (TIMECOP_G(sync_request_time)){ - update_request_time(freezed_tv.sec TSRMLS_CC); - } - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto int timecop_travel(long timestamp) - Time travel to specified timestamp */ -PHP_FUNCTION(timecop_travel) -{ - zval *dt; - long timestamp; - tc_timeval now, mock_tv; - - if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "O", &dt, TIMECOP_G(ce_DateTimeInterface)) != FAILURE) { - get_timeval_from_datetime(&mock_tv, dt TSRMLS_CC); - } else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "l", ×tamp) != FAILURE) { - mock_tv.sec = timestamp; - mock_tv.usec = 0; - } else { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "This function accepts either (DateTimeInterface) OR (int) as arguments."); - RETURN_FALSE; - } - - TIMECOP_G(timecop_mode) = TIMECOP_MODE_TRAVEL; - get_current_time(&now TSRMLS_CC); - tc_timeval_sub(&TIMECOP_G(travel_offset), &mock_tv, &now); - TIMECOP_G(travel_origin) = now; - - if (TIMECOP_G(sync_request_time)){ - update_request_time(mock_tv.sec TSRMLS_CC); - } - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto int timecop_scale(long scale) - Accelerate time with specified scale */ -PHP_FUNCTION(timecop_scale) -{ - long scale; - tc_timeval now, mock_time; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &scale) == FAILURE) { - RETURN_FALSE; - } - if (scale < 0) { - RETURN_FALSE; - } - get_current_time(&now TSRMLS_CC); - get_mock_timeval(&mock_time, &now TSRMLS_CC); - TIMECOP_G(timecop_mode) = TIMECOP_MODE_TRAVEL; - TIMECOP_G(travel_origin) = now; - tc_timeval_sub(&TIMECOP_G(travel_offset), &mock_time, &now); - TIMECOP_G(scaling_factor) = scale; - - if (TIMECOP_G(sync_request_time)){ - update_request_time(mock_time.sec TSRMLS_CC); - } - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto int timecop_return(void) - Return to Time travel to specified timestamp */ -PHP_FUNCTION(timecop_return) -{ - TIMECOP_G(timecop_mode) = TIMECOP_MODE_REALTIME; - - if (TIMECOP_G(sync_request_time)){ - restore_request_time(TSRMLS_C); - } - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto int timecop_time(void) - Return virtual timestamp */ -PHP_FUNCTION(timecop_time) -{ - RETURN_LONG(mock_timestamp(TSRMLS_C)); -} -/* }}} */ - -/* {{{ proto int timecop_mktime([int hour [, int min [, int sec [, int mon [, int day [, int year]]]]]]) - Get UNIX timestamp for a date */ -PHP_FUNCTION(timecop_mktime) -{ - TIMECOP_CALL_MKTIME("mktime", "date"); -} -/* }}} */ - -/* {{{ proto int timecop_gmmktime([int hour [, int min [, int sec [, int mon [, int day [, int year]]]]]]) - Get UNIX timestamp for a GMT date */ -PHP_FUNCTION(timecop_gmmktime) -{ - TIMECOP_CALL_MKTIME("gmmktime", "gmdate"); -} -/* }}} */ - -/* {{{ proto string timecop_date(string format [, long timestamp]) - Format a local date/time */ -PHP_FUNCTION(timecop_date) -{ - TIMECOP_CALL_FUNCTION("date", 1); -} -/* }}} */ - -/* {{{ proto string timecop_gmdate(string format [, long timestamp]) - Format a GMT date/time */ -PHP_FUNCTION(timecop_gmdate) -{ - TIMECOP_CALL_FUNCTION("gmdate", 1); -} -/* }}} */ - -/* {{{ proto int timecop_idate(string format [, int timestamp]) - Format a local time/date as integer */ -PHP_FUNCTION(timecop_idate) -{ - TIMECOP_CALL_FUNCTION("idate", 1); -} -/* }}} */ - -/* {{{ proto array timecop_getdate([int timestamp]) - Get date/time information */ -PHP_FUNCTION(timecop_getdate) -{ - TIMECOP_CALL_FUNCTION("getdate", 0); -} -/* }}} */ - -/* {{{ proto array timecop_localtime([int timestamp [, bool associative_array]]) - Returns the results of the C system call localtime as an associative array if - the associative_array argument is set to 1 other wise it is a regular array */ -PHP_FUNCTION(timecop_localtime) -{ - TIMECOP_CALL_FUNCTION("localtime", 0); -} -/* }}} */ - -/* {{{ proto int timecop_strtotime(string time [, int now ]) - Convert string representation of date and time to a timestamp */ -PHP_FUNCTION(timecop_strtotime) -{ - TIMECOP_CALL_FUNCTION("strtotime", 1); -} -/* }}} */ - -/* {{{ proto string timecop_strftime(string format [, int timestamp]) - Format a local time/date according to locale settings */ -PHP_FUNCTION(timecop_strftime) -{ - TIMECOP_CALL_FUNCTION("strftime", 1); -} -/* }}} */ - -/* {{{ proto string timecop_gmstrftime(string format [, int timestamp]) - Format a GMT/UCT time/date according to locale settings */ -PHP_FUNCTION(timecop_gmstrftime) -{ - TIMECOP_CALL_FUNCTION("gmstrftime", 1); -} -/* }}} */ - -/* - * get_mock_timeval(fixed, now) - * - * - * delta - * |<----------------------->| - * travel_offset delta * scaling_factor - * |<------------->|<------------------------------------------------->| - * ==@===============@=========@=========================================@== - * ^ ^ ^ - * | | | - * travel_origin orig_time traveled_time - * - * - * delta = orig_time - travel_origin - * traveled_time = travel_origin + travel_offset + delta * scaling_factor - */ -static int get_mock_timeval(tc_timeval *fixed, const tc_timeval *now TSRMLS_DC) -{ - if (TIMECOP_G(timecop_mode) == TIMECOP_MODE_FREEZE) { - *fixed = TIMECOP_G(freezed_time); - } else if (TIMECOP_G(timecop_mode) == TIMECOP_MODE_TRAVEL) { - tc_timeval delta, origin = TIMECOP_G(travel_origin); - long scale = TIMECOP_G(scaling_factor); - if (now == NULL) { - get_current_time(&delta TSRMLS_CC); - } else { - delta = *now; - } - tc_timeval_sub(&delta, &delta, &origin); - tc_timeval_mul(&delta, &delta, scale); - tc_timeval_add(fixed, &origin, &TIMECOP_G(travel_offset)); - tc_timeval_add(fixed, fixed, &delta); - } else { - if (now == NULL) { - get_current_time(fixed TSRMLS_CC); - } else { - *fixed = *now; - } - } - return 0; -} - -static inline long mock_timestamp(TSRMLS_D) -{ - tc_timeval tv; - get_mock_timeval(&tv, NULL TSRMLS_CC); - return tv.sec; -} - -static int get_timeval_from_datetime(tc_timeval *tp, zval *dt TSRMLS_DC) -{ - zval *sec, *usec; - zval u_str; - - call_php_function_with_1_params("date_timestamp_get", &sec, dt); - INIT_ZVAL(u_str); - ZVAL_STRING(&u_str, "u", 0); - call_php_method_with_1_params(&dt, Z_OBJCE_P(dt), "format", &usec, &u_str); - convert_to_long(usec); - - tp->sec = Z_LVAL_P(sec); - tp->usec = Z_LVAL_P(usec); - - zval_ptr_dtor(&sec); - zval_ptr_dtor(&usec); - - return 0; -} - -static int get_current_time(tc_timeval *now TSRMLS_DC) -{ - int ret = 0; -#if HAVE_GETTIMEOFDAY - struct timeval tv; - ret = gettimeofday(&tv, NULL); - if (ret == 0) { - now->sec = (long)tv.tv_sec; - now->usec = (long)tv.tv_usec; - } -#else - time_t ts = time(NULL); - if (ts == -1) { - ret = -1; - } else { - now->sec = (long)ts; - now->usec = 0; - } -#endif - return ret; -} - -#ifdef HAVE_GETTIMEOFDAY - -#define MICRO_IN_SEC 1000000.00 -#define SEC_IN_MIN 60 - -static void _timecop_gettimeofday(INTERNAL_FUNCTION_PARAMETERS, int mode) -{ - zend_bool get_as_float = 0; - tc_timeval fixed; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &get_as_float) == FAILURE) { - return; - } - - get_mock_timeval(&fixed, NULL TSRMLS_CC); - - if (get_as_float) { - RETURN_DOUBLE((double)(fixed.sec + fixed.usec / MICRO_IN_SEC)); - } - if (mode) { - zval *zv_offset, *zv_dst, format, timestamp; - long offset = 0, is_dst = 0; - - INIT_ZVAL(timestamp); - ZVAL_LONG(×tamp, fixed.sec); - - /* offset */ - INIT_ZVAL(format); - ZVAL_STRING(&format, "Z", 0); - call_php_function_with_2_params(ORIG_FUNC_NAME("date"), &zv_offset, &format, ×tamp); - convert_to_long(zv_offset); - offset = Z_LVAL_P(zv_offset); - zval_ptr_dtor(&zv_offset); - - /* is_dst */ - ZVAL_STRING(&format, "I", 0); - call_php_function_with_2_params(ORIG_FUNC_NAME("date"), &zv_dst, &format, ×tamp); - convert_to_long(zv_dst); - is_dst = Z_LVAL_P(zv_dst); - zval_ptr_dtor(&zv_dst); - - array_init(return_value); - add_assoc_long(return_value, "sec", fixed.sec); - add_assoc_long(return_value, "usec", fixed.usec); - add_assoc_long(return_value, "minuteswest", -offset / SEC_IN_MIN); - add_assoc_long(return_value, "dsttime", is_dst); - } else { - char ret[100]; - snprintf(ret, 100, "%.8F %ld", fixed.usec / MICRO_IN_SEC, fixed.sec); - RETURN_STRING(ret, 1); - } -} - -/* {{{ proto mixed microtime([bool get_as_float]) - Returns either a string or a float containing the current time in seconds and microseconds */ -PHP_FUNCTION(timecop_microtime) -{ - _timecop_gettimeofday(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); -} -/* }}} */ - -/* {{{ proto array gettimeofday([bool get_as_float]) - Returns the current time as array */ -PHP_FUNCTION(timecop_gettimeofday) -{ - _timecop_gettimeofday(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); -} -/* }}} */ -#endif - -/* {{{ proto int timecop_unixtojd([int timestamp]) - Convert UNIX timestamp to Julian Day */ -PHP_FUNCTION(timecop_unixtojd) -{ - TIMECOP_CALL_FUNCTION("unixtojd", 0); -} -/* }}} */ - -static void _timecop_orig_datetime_constructor(INTERNAL_FUNCTION_PARAMETERS, int immutable) -{ - zval *arg1 = NULL, *arg2 = NULL; - zend_class_entry *real_ce; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|zz", &arg1, &arg2) == FAILURE) { - RETURN_FALSE; - } - - if (immutable) { - real_ce = TIMECOP_G(ce_DateTimeImmutable); - } else { - real_ce = TIMECOP_G(ce_DateTime); - } - - call_php_method_with_2_params(&getThis(), real_ce, ORIG_FUNC_NAME("__construct"), NULL, arg1, arg2); -} - -static inline void _timecop_datetime_constructor(INTERNAL_FUNCTION_PARAMETERS, int immutable) -{ - _timecop_datetime_constructor_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, getThis(), immutable); -} - -static inline void _timecop_date_create(INTERNAL_FUNCTION_PARAMETERS, int immutable) -{ - _timecop_datetime_constructor_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, NULL, immutable); -} - -static void _timecop_datetime_constructor_ex(INTERNAL_FUNCTION_PARAMETERS, zval *obj, int immutable) -{ - zval orig_time, *orig_timezone = NULL; - zval *fixed_time, *fixed_timezone, *dt, *arg1, *arg2; - char *orig_time_str = NULL; - int orig_time_len = 0; - const char *real_func; - zend_class_entry *real_ce; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sO!", &orig_time_str, &orig_time_len, &orig_timezone, TIMECOP_G(ce_DateTimeZone)) == FAILURE) { - RETURN_FALSE; - } - - INIT_ZVAL(orig_time); - if (orig_time_str == NULL) { - ZVAL_NULL(&orig_time); - } else { - ZVAL_STRINGL(&orig_time, orig_time_str, orig_time_len, 0); - } - if (immutable) { - real_func = ORIG_FUNC_NAME("date_create_immutable"); - real_ce = TIMECOP_G(ce_DateTimeImmutable); - } else { - real_func = ORIG_FUNC_NAME("date_create"); - real_ce = TIMECOP_G(ce_DateTime); - } - - if (get_formatted_mock_time(&orig_time, orig_timezone, &fixed_time, &fixed_timezone TSRMLS_CC) == 0) { - arg1 = fixed_time; - arg2 = fixed_timezone; - } else { - arg1 = &orig_time; - arg2 = orig_timezone; - } - if (obj == NULL) { - call_php_function_with_2_params(real_func, &dt, arg1, arg2); - } else { - call_php_method_with_2_params(&obj, real_ce, ORIG_FUNC_NAME("__construct"), NULL, arg1, arg2); - } - - zval_ptr_dtor(&fixed_time); - zval_ptr_dtor(&fixed_timezone); - - if (obj == NULL) { - RETURN_ZVAL(dt, 1, 1); - } -} - -/* {{{ proto DateTime timecop_date_create([string time[, DateTimeZone object]]) - Returns new DateTime object initialized with traveled time */ -PHP_FUNCTION(timecop_date_create) -{ - _timecop_date_create(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); -} -/* }}} */ - -#if PHP_VERSION_ID >= 50300 -static void _timecop_date_create_from_format(INTERNAL_FUNCTION_PARAMETERS, int immutable) -{ - zval *orig_timezone = NULL; - zval orig_format, orig_time, fixed_format, *fixed_time, *new_format, *new_time; - zval *dt, *new_dt, now_timestamp, tmp; - char *orig_format_str, *orig_time_str; - int orig_format_len, orig_time_len; - tc_timeval now; - char buf[64]; - const char *real_func; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|O!", &orig_format_str, &orig_format_len, &orig_time_str, &orig_time_len, &orig_timezone, TIMECOP_G(ce_DateTimeZone)) == FAILURE) { - RETURN_FALSE; - } - - INIT_ZVAL(orig_format); - ZVAL_STRINGL(&orig_format, orig_format_str, orig_format_len, 0); - INIT_ZVAL(orig_time); - ZVAL_STRINGL(&orig_time, orig_time_str, orig_time_len, 0); - - call_php_function_with_3_params(ORIG_FUNC_NAME("date_create_from_format"), &dt, &orig_format, &orig_time, orig_timezone); - if (Z_TYPE_P(dt) == IS_BOOL && !Z_BVAL_P(dt)) { - RETURN_FALSE; - } - - if (memchr(orig_format_str, '!', orig_format_len) || - memchr(orig_format_str, '|', orig_format_len)) { - - if (immutable) { - call_php_function_with_3_params(ORIG_FUNC_NAME("date_create_immutable_from_format"), &dt, &orig_format, &orig_time, orig_timezone); - } - - RETURN_ZVAL(dt, 1, 1); - } - - get_mock_timeval(&now, NULL TSRMLS_CC); - - INIT_ZVAL(now_timestamp); - ZVAL_LONG(&now_timestamp, now.sec); - call_php_method_with_1_params(&dt, TIMECOP_G(ce_DateTime), "settimestamp", NULL, &now_timestamp); - sprintf(buf, "Y-m-d H:i:s.%06ld ", now.usec); - INIT_ZVAL(tmp); - ZVAL_STRINGL(&tmp, buf, strlen(buf), 0); - call_php_method_with_1_params(&dt, TIMECOP_G(ce_DateTime), "format", &fixed_time, &tmp); - - INIT_ZVAL(fixed_format); - if (memchr(orig_format_str, 'g', orig_format_len) || - memchr(orig_format_str, 'h', orig_format_len) || - memchr(orig_format_str, 'G', orig_format_len) || - memchr(orig_format_str, 'H', orig_format_len) || - memchr(orig_format_str, 'i', orig_format_len) || - memchr(orig_format_str, 's', orig_format_len)) { - ZVAL_STRING(&fixed_format, "Y-m-d ??:??:??.??????", 0); - } else if (memchr(orig_format_str, 'Y', orig_format_len) || - memchr(orig_format_str, 'y', orig_format_len) || - memchr(orig_format_str, 'F', orig_format_len) || - memchr(orig_format_str, 'M', orig_format_len) || - memchr(orig_format_str, 'm', orig_format_len) || - memchr(orig_format_str, 'n', orig_format_len) || - memchr(orig_format_str, 'd', orig_format_len) || - memchr(orig_format_str, 'j', orig_format_len) || - memchr(orig_format_str, 'D', orig_format_len) || - memchr(orig_format_str, 'l', orig_format_len) || - memchr(orig_format_str, 'U', orig_format_len)) { - ZVAL_STRING(&fixed_format, "Y-m-d H:i:s.??????", 0); - } else { - ZVAL_STRING(&fixed_format, "Y-m-d H:i:s.??????", 0); - } - - ZVAL_STRING(&tmp, "%s %s", 0); - call_php_function_with_3_params("sprintf", &new_format, &tmp, &fixed_format, &orig_format); - call_php_function_with_3_params("sprintf", &new_time, &tmp, fixed_time, &orig_time); - - if (immutable) { - real_func = ORIG_FUNC_NAME("date_create_immutable_from_format"); - } else { - real_func = ORIG_FUNC_NAME("date_create_from_format"); - } - call_php_function_with_3_params(real_func, &new_dt, new_format, new_time, orig_timezone); - - zval_ptr_dtor(&dt); - zval_ptr_dtor(&fixed_time); - zval_ptr_dtor(&new_format); - zval_ptr_dtor(&new_time); - RETURN_ZVAL(new_dt, 1, 1); -} - -/* {{{ proto DateTime timecop_date_create_from_format(string format, string time[, DateTimeZone object]) - Returns new DateTime object initialized with traveled time */ -PHP_FUNCTION(timecop_date_create_from_format) -{ - _timecop_date_create_from_format(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); -} -/* }}} */ -#endif - -#if PHP_VERSION_ID >= 50500 -/* {{{ proto DateTimeImmutable timecop_date_create_immutable([string time[, DateTimeZone object]]) - Returns new DateTimeImmutable object initialized with traveled time */ -PHP_FUNCTION(timecop_date_create_immutable) -{ - _timecop_date_create(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); -} -/* }}} */ - -/* {{{ proto DateTimeImmutable timecop_date_create_immutable_from_format(string format, string time[, DateTimeZone object]) - Returns new DateTimeImmutable object initialized with traveled time */ -PHP_FUNCTION(timecop_date_create_immutable_from_format) -{ - _timecop_date_create_from_format(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); -} -/* }}} */ -#endif - -/* {{{ proto TimecopDateTime::__construct([string time[, DateTimeZone object]]) - Creates new TimecopDateTime object */ -PHP_METHOD(TimecopDateTime, __construct) -{ - _timecop_datetime_constructor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); -} -/* }}} */ - -/* {{{ proto TimecopOrigDateTime::__construct([string time[, DateTimeZone object]]) - Creates new TimecopOrigDateTime object */ -PHP_METHOD(TimecopOrigDateTime, __construct) -{ - _timecop_orig_datetime_constructor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); -} -/* }}} */ - -#if PHP_VERSION_ID >= 50500 -/* {{{ proto TimecopDateTimeImmutable::__construct([string time[, DateTimeZone object]]) - Creates new TimecopDateTimeImmutable object */ -PHP_METHOD(TimecopDateTimeImmutable, __construct) -{ - _timecop_datetime_constructor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); -} -/* }}} */ - -/* {{{ proto TimecopOrigDateTimeImmutable::__construct([string time[, DateTimeZone object]]) - Creates new TimecopOrigDateTimeImmutable object */ -PHP_METHOD(TimecopOrigDateTimeImmutable, __construct) -{ - _timecop_orig_datetime_constructor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); -} -/* }}} */ -#endif - -#if !defined(PHP_VERSION_ID) || PHP_VERSION_ID < 50300 -/* {{{ proto DateTime date_timestamp_set(DateTime object, long unixTimestamp) - Sets the date and time based on an Unix timestamp. -*/ -PHP_FUNCTION(date_timestamp_set) -{ - zval *object; - php_date_obj *dateobj; - long timestamp; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", &object, TIMECOP_G(ce_DateTime), ×tamp) == FAILURE) { - RETURN_FALSE; - } - dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC); - DATE_CHECK_INITIALIZED(dateobj->time, DateTime); - timelib_unixtime2local(dateobj->time, (timelib_sll)timestamp); - timelib_update_ts(dateobj->time, NULL); - - RETURN_ZVAL(object, 1, 0); -} -/* }}} */ - -/* {{{ proto long date_timestamp_get(DateTime object) - Gets the Unix timestamp. -*/ -PHP_FUNCTION(date_timestamp_get) -{ - zval *object; - php_date_obj *dateobj; - long timestamp; - int error; - - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &object, TIMECOP_G(ce_DateTime)) == FAILURE) { - RETURN_FALSE; - } - dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC); - DATE_CHECK_INITIALIZED(dateobj->time, DateTime); - timelib_update_ts(dateobj->time, NULL); - - timestamp = timelib_date_to_int(dateobj->time, &error); - if (error) { - RETURN_FALSE; - } else { - RETVAL_LONG(timestamp); - } -} -/* }}} */ -#endif - -static inline zval* _call_php_method_with_0_params(zval **object_pp, zend_class_entry *obj_ce, const char *method_name, zval **retval_ptr_ptr TSRMLS_DC) -{ - return _call_php_method(object_pp, obj_ce, method_name, retval_ptr_ptr, 0, NULL, NULL TSRMLS_CC); -} - -static inline zval* _call_php_method_with_1_params(zval **object_pp, zend_class_entry *obj_ce, const char *method_name, zval **retval_ptr_ptr, zval *arg1 TSRMLS_DC) -{ - int nparams = 1; - if (arg1 == NULL) { - nparams = 0; - } - return _call_php_method(object_pp, obj_ce, method_name, retval_ptr_ptr, nparams, arg1, NULL TSRMLS_CC); -} - -static inline zval* _call_php_method_with_2_params(zval **object_pp, zend_class_entry *obj_ce, const char *method_name, zval **retval_ptr_ptr, zval *arg1, zval *arg2 TSRMLS_DC) -{ - int nparams = 2; - if (arg1 == NULL) { - nparams = 0; - } else if (arg2 == NULL) { - nparams = 1; - } - return _call_php_method(object_pp, obj_ce, method_name, retval_ptr_ptr, nparams, arg1, arg2 TSRMLS_CC); -} - -static inline zval* _call_php_method(zval **object_pp, zend_class_entry *obj_ce, const char *method_name, zval **retval_ptr_ptr, int param_count, zval* arg1, zval* arg2 TSRMLS_DC) -{ - return zend_call_method(object_pp, obj_ce, NULL, method_name, strlen(method_name), retval_ptr_ptr, param_count, arg1, arg2 TSRMLS_CC); -} - -static void _call_php_function_with_3_params(const char *function_name, zval **retval_ptr_ptr, zval *arg1, zval *arg2, zval *arg3 TSRMLS_DC) -{ - if (arg3 == NULL) { - call_php_function_with_2_params(function_name, retval_ptr_ptr, arg1, arg2 TSRMLS_CC); - } else { - zval *zps[3] = {arg1, arg2, arg3}; - zval **params[3] = {&zps[0], &zps[1], &zps[2]}; - _call_php_function_with_params(function_name, retval_ptr_ptr, 3, params TSRMLS_CC); - } -} - -static inline void _call_php_function_with_params(const char *function_name, zval **retval_ptr_ptr, zend_uint param_count, zval **params[] TSRMLS_DC) -{ - zval callable; - - INIT_ZVAL(callable); - ZVAL_STRING(&callable, function_name, 0); - - call_user_function_ex(EG(function_table), NULL, &callable, retval_ptr_ptr, param_count, params, 1, NULL TSRMLS_CC); -} - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: noet sw=4 ts=4 fdm=marker - * vim<600: noet sw=4 ts=4 - */ diff --git a/timecop_php7.c b/timecop_php7.c deleted file mode 100644 index 132479b..0000000 --- a/timecop_php7.c +++ /dev/null @@ -1,1673 +0,0 @@ -/* -MIT License - -Copyright (c) 2012-2017 Yoshio HANAWA - -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. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "php.h" -#include "php_ini.h" -#include "ext/standard/info.h" - -#include "php_timecop.h" - -#ifdef ZFS -#include "TSRM.h" -#endif - -ZEND_DECLARE_MODULE_GLOBALS(timecop) - -static void timecop_globals_ctor(zend_timecop_globals *globals) { - /* Initialize your global struct */ - globals->func_override = 1; - globals->sync_request_time = 1; - ZVAL_NULL(&globals->orig_request_time); - globals->timecop_mode = TIMECOP_MODE_REALTIME; - globals->freezed_time.sec = 0; - globals->freezed_time.usec = 0; - globals->travel_origin.sec = 0; - globals->travel_origin.usec = 0; - globals->travel_offset.sec = 0; - globals->travel_offset.usec = 0; - globals->scaling_factor = 1; - globals->ce_DateTimeZone = NULL; - globals->ce_DateTimeInterface = NULL; - globals->ce_DateTime = NULL; - globals->ce_TimecopDateTime = NULL; - globals->ce_DateTimeImmutable = NULL; - globals->ce_TimecopDateTimeImmutable = NULL; -} - -static const struct timecop_override_func_entry timecop_override_func_table[] = { - TIMECOP_OFE("time"), - TIMECOP_OFE("mktime"), - TIMECOP_OFE("gmmktime"), - TIMECOP_OFE("date"), - TIMECOP_OFE("gmdate"), - TIMECOP_OFE("idate"), - TIMECOP_OFE("getdate"), - TIMECOP_OFE("localtime"), - TIMECOP_OFE("strtotime"), - TIMECOP_OFE("strftime"), - TIMECOP_OFE("gmstrftime"), -#ifdef HAVE_GETTIMEOFDAY - TIMECOP_OFE("microtime"), - TIMECOP_OFE("gettimeofday"), -#endif - TIMECOP_OFE("unixtojd"), - TIMECOP_OFE("date_create"), - TIMECOP_OFE("date_create_from_format"), - TIMECOP_OFE("date_create_immutable"), - TIMECOP_OFE("date_create_immutable_from_format"), - {NULL, NULL, NULL} -}; - -static const struct timecop_override_class_entry timecop_override_class_table[] = { - TIMECOP_OCE("datetime", "__construct"), - TIMECOP_OCE("datetime", "createfromformat"), - TIMECOP_OCE("datetimeimmutable", "__construct"), - TIMECOP_OCE("datetimeimmutable", "createfromformat"), - {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[] = { - PHP_FE(timecop_freeze, arginfo_timecop_freeze) - PHP_FE(timecop_travel, arginfo_timecop_travel) - PHP_FE(timecop_scale, arginfo_timecop_scale) - PHP_FE(timecop_return, arginfo_timecop_return) - PHP_FE(timecop_time, arginfo_timecop_time) - PHP_FE(timecop_mktime, arginfo_timecop_mktime) - PHP_FE(timecop_gmmktime, arginfo_timecop_gmmktime) - PHP_FE(timecop_date, arginfo_timecop_date) - PHP_FE(timecop_gmdate, arginfo_timecop_gmdate) - PHP_FE(timecop_idate, arginfo_timecop_idate) - PHP_FE(timecop_getdate, arginfo_timecop_getdate) - PHP_FE(timecop_localtime, arginfo_timecop_localtime) - PHP_FE(timecop_strtotime, arginfo_timecop_strtotime) -#if PHP_VERSION_ID >= 80400 - ZEND_RAW_FENTRY("timecop_strftime", zif_timecop_strftime, arginfo_timecop_strftime, ZEND_ACC_DEPRECATED, NULL, NULL) - ZEND_RAW_FENTRY("timecop_gmstrftime", zif_timecop_gmstrftime, arginfo_timecop_gmstrftime, ZEND_ACC_DEPRECATED, NULL, NULL) -#elif PHP_VERSION_ID >= 80100 - PHP_DEP_FE(timecop_strftime, arginfo_timecop_strftime) - PHP_DEP_FE(timecop_gmstrftime, arginfo_timecop_gmstrftime) -#else - PHP_FE(timecop_strftime, arginfo_timecop_strftime) - PHP_FE(timecop_gmstrftime, arginfo_timecop_gmstrftime) -#endif -#ifdef HAVE_GETTIMEOFDAY - PHP_FE(timecop_microtime, arginfo_timecop_microtime) - PHP_FE(timecop_gettimeofday, arginfo_timecop_gettimeofday) -#endif - PHP_FE(timecop_unixtojd, arginfo_timecop_unixtojd) - PHP_FE(timecop_date_create, arginfo_timecop_date_create) - PHP_FE(timecop_date_create_from_format, arginfo_timecop_date_create_from_format) - PHP_FE(timecop_date_create_immutable, arginfo_timecop_date_create_immutable) - PHP_FE(timecop_date_create_immutable_from_format, arginfo_timecop_date_create_immutable_from_format) - {NULL, NULL, NULL} -}; -/* }}} */ - -/* declare method parameters, */ - -/* each method can have its own parameters and visibility */ -static zend_function_entry timecop_funcs_timecop[] = { - PHP_ME_MAPPING(freeze, timecop_freeze, arginfo_timecop_travel, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - PHP_ME_MAPPING(travel, timecop_travel, arginfo_timecop_travel, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - PHP_ME_MAPPING(scale, timecop_scale, arginfo_timecop_scale, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - PHP_ME_MAPPING(return, timecop_return, arginfo_timecop_return, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - {NULL, NULL, NULL} -}; - -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_class_TimecopDateTime_createFromFormat, - ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - {NULL, NULL, NULL} -}; - -static zend_function_entry timecop_funcs_orig_date[] = { - PHP_ME(TimecopOrigDateTime, __construct, arginfo_class_TimecopDateTime___construct, - ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} -}; - -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_class_TimecopDateTimeImmutable_createFromFormat, - ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - {NULL, NULL, NULL} -}; - -static zend_function_entry timecop_funcs_orig_immutable[] = { - PHP_ME(TimecopOrigDateTimeImmutable, __construct, arginfo_class_TimecopDateTimeImmutable___construct, - ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} -}; - -#define MKTIME_NUM_ARGS 6 - -#define TIMECOP_CALL_FUNCTION(func_name, index_to_fill_timestamp) \ - _timecop_call_function(INTERNAL_FUNCTION_PARAM_PASSTHRU, ORIG_FUNC_NAME(func_name), index_to_fill_timestamp); - -#define TIMECOP_CALL_MKTIME(mktime_func_name, date_func_name) \ - _timecop_call_mktime(INTERNAL_FUNCTION_PARAM_PASSTHRU, ORIG_FUNC_NAME(mktime_func_name), ORIG_FUNC_NAME(date_func_name)); - -static void timecop_globals_ctor(zend_timecop_globals *globals); - -static int register_timecop_classes(); -static int timecop_func_override(); -static int timecop_class_override(); -static int timecop_func_override_clear(); -static int timecop_class_override_clear(); - -static int update_request_time(zend_long unixtime); -static int restore_request_time(); - -static int fill_mktime_params(zval *fill_params, const char *date_function_name, int from); -static int get_formatted_mock_time(zval *time, zval *timezone_obj, zval *retval_time, zval *retval_timezone); -static long get_mock_fraction(zval *time, zval *timezone_obj); - -static void _timecop_call_function(INTERNAL_FUNCTION_PARAMETERS, const char *function_name, int index_to_fill_timestamp); -static void _timecop_call_mktime(INTERNAL_FUNCTION_PARAMETERS, const char *mktime_function_name, const char *date_function_name); - -static int get_mock_timeval(tc_timeval *fixed, const tc_timeval *now); -static inline zend_long mock_timestamp(); - -static int get_timeval_from_datetime(tc_timeval *tp, zval *dt); -static int get_current_time(tc_timeval *now); - -static void _timecop_orig_datetime_constructor(INTERNAL_FUNCTION_PARAMETERS, int immutable); -static inline void _timecop_date_create(INTERNAL_FUNCTION_PARAMETERS, int immutable); -static inline void _timecop_datetime_constructor(INTERNAL_FUNCTION_PARAMETERS, int immutable); -static void _timecop_datetime_constructor_ex(INTERNAL_FUNCTION_PARAMETERS, zval *obj, int immutable); - -static inline zval* _call_php_method_with_0_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr); -static inline zval* _call_php_method_with_1_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, zval *arg1); -static inline zval* _call_php_method_with_2_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, zval *arg1, zval *arg2); -static inline zval* _call_php_method(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, int param_count, zval* arg1, zval* arg2); -static void _call_php_function_with_3_params(const char *function_name, zval *retval_ptr, zval *arg1, zval *arg2, zval *arg3); -static inline void _call_php_function_with_params(const char *function_name, zval *retval_ptr, uint32_t param_count, zval params[]); - -static const zend_module_dep timecop_module_deps[] = { - ZEND_MOD_REQUIRED("Date") - ZEND_MOD_END -}; - -/* {{{ timecop_module_entry - */ -zend_module_entry timecop_module_entry = { - STANDARD_MODULE_HEADER_EX, - NULL, - timecop_module_deps, - "timecop", - timecop_functions, - PHP_MINIT(timecop), - PHP_MSHUTDOWN(timecop), - PHP_RINIT(timecop), - PHP_RSHUTDOWN(timecop), - PHP_MINFO(timecop), -#if ZEND_MODULE_API_NO >= 20010901 - PHP_TIMECOP_VERSION, -#endif - STANDARD_MODULE_PROPERTIES -}; -/* }}} */ - -#ifdef COMPILE_DL_TIMECOP -# ifdef ZTS - ZEND_TSRMLS_CACHE_DEFINE(); -# endif -ZEND_GET_MODULE(timecop) -#endif - -/* {{{ PHP_INI - */ -PHP_INI_BEGIN() - STD_PHP_INI_ENTRY("timecop.func_override", "1", - PHP_INI_SYSTEM, OnUpdateLong, func_override, zend_timecop_globals, timecop_globals) - STD_PHP_INI_ENTRY("timecop.sync_request_time", "1", - PHP_INI_SYSTEM, OnUpdateLong, sync_request_time, zend_timecop_globals, timecop_globals) -PHP_INI_END() -/* }}} */ - -/* {{{ PHP_MINIT_FUNCTION - */ -PHP_MINIT_FUNCTION(timecop) -{ - ZEND_INIT_MODULE_GLOBALS(timecop, timecop_globals_ctor, NULL); - REGISTER_INI_ENTRIES(); - register_timecop_classes(); - - return SUCCESS; -} -/* }}} */ - -/* {{{ PHP_MSHUTDOWN_FUNCTION - */ -PHP_MSHUTDOWN_FUNCTION(timecop) -{ - UNREGISTER_INI_ENTRIES(); - - return SUCCESS; -} -/* }}} */ - -/* {{{ PHP_RINIT_FUNCTION(timecop) */ -PHP_RINIT_FUNCTION(timecop) -{ -#if defined(COMPILE_DL_TIMECOP) && defined(ZTS) - ZEND_TSRMLS_CACHE_UPDATE(); -#endif - - if (TIMECOP_G(func_override)) { - if (SUCCESS != timecop_func_override() || - SUCCESS != timecop_class_override()) { - return FAILURE; - } - } - - return SUCCESS; -} -/* }}} */ - -/* {{{ PHP_RSHUTDOWN_FUNCTION(timecop) */ -PHP_RSHUTDOWN_FUNCTION(timecop) -{ - if (TIMECOP_G(func_override)) { - timecop_func_override_clear(); - timecop_class_override_clear(); - } - - if (Z_TYPE(TIMECOP_G(orig_request_time)) == IS_NULL) { - restore_request_time(); - } - - TIMECOP_G(timecop_mode) = TIMECOP_MODE_REALTIME; - TIMECOP_G(scaling_factor) = 1; - - return SUCCESS; -} -/* }}} */ - - -/* {{{ PHP_MINFO_FUNCTION - */ -PHP_MINFO_FUNCTION(timecop) -{ - php_info_print_table_start(); - php_info_print_table_header(2, "timecop", "enabled"); - php_info_print_table_row(2, "Version", PHP_TIMECOP_VERSION); - php_info_print_table_end(); - - DISPLAY_INI_ENTRIES(); -} -/* }}} */ - -static int register_timecop_classes() -{ - zend_class_entry ce; - zend_class_entry *tmp, *date_ce, *timezone_ce, *immutable_ce, *interface_ce; - - date_ce = zend_hash_str_find_ptr(CG(class_table), "datetime", sizeof("datetime")-1); - if (date_ce == NULL) { - /* DateTime must be initialized before */ - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't find class %s.", "DateTime"); - return SUCCESS; - } - - timezone_ce = zend_hash_str_find_ptr(CG(class_table), "datetimezone", sizeof("datetimezone")-1); - if (timezone_ce == NULL) { - /* DateTime must be initialized before */ - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't find class %s.", "DateTimeZone"); - return SUCCESS; - } - - immutable_ce = zend_hash_str_find_ptr(CG(class_table), "datetimeimmutable", sizeof("datetimeimmutable")-1); - if (immutable_ce == NULL) { - /* DateTimeImmutable must be initialized before */ - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't find class %s.", "DateTimeImmutable"); - return SUCCESS; - } - - interface_ce = zend_hash_str_find_ptr(CG(class_table), "datetimeinterface", sizeof("datetimeinterface")-1); - if (interface_ce == NULL) { - /* DateTimeInterface must be initialized before */ - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't find interface %s.", "DateTimeInterface"); - return SUCCESS; - } - - INIT_CLASS_ENTRY(ce, "Timecop", timecop_funcs_timecop); - zend_register_internal_class(&ce); - - TIMECOP_G(ce_DateTimeZone) = timezone_ce; - TIMECOP_G(ce_DateTimeInterface) = interface_ce; - - /* replace DateTime */ - INIT_CLASS_ENTRY(ce, "TimecopDateTime", timecop_funcs_date); - tmp = zend_register_internal_class_ex(&ce, date_ce); - tmp->create_object = date_ce->create_object; - - TIMECOP_G(ce_DateTime) = date_ce; - TIMECOP_G(ce_TimecopDateTime) = tmp; - - INIT_CLASS_ENTRY(ce, "TimecopOrigDateTime", timecop_funcs_orig_date); - tmp = zend_register_internal_class_ex(&ce, date_ce); - tmp->create_object = date_ce->create_object; - - /* replace DateTimeImmutable */ - INIT_CLASS_ENTRY(ce, "TimecopDateTimeImmutable", timecop_funcs_immutable); - tmp = zend_register_internal_class_ex(&ce, immutable_ce); - tmp->create_object = immutable_ce->create_object; - - TIMECOP_G(ce_DateTimeImmutable) = immutable_ce; - TIMECOP_G(ce_TimecopDateTimeImmutable) = tmp; - - INIT_CLASS_ENTRY(ce, "TimecopOrigDateTimeImmutable", timecop_funcs_orig_immutable); - tmp = zend_register_internal_class_ex(&ce, immutable_ce); - tmp->create_object = immutable_ce->create_object; - - return SUCCESS; -} - -static int timecop_func_override() -{ - const struct timecop_override_func_entry *p; - zend_function *zf_orig, *zf_ovrd, *zf_save; - - p = &(timecop_override_func_table[0]); - while (p->orig_func != NULL) { - zf_orig = zend_hash_str_find_ptr(CG(function_table), p->orig_func, strlen(p->orig_func)); - if (zf_orig == NULL) { - /* Do nothing. Because some functions are introduced by optional extensions. */ - p++; - continue; - } - - zf_ovrd = zend_hash_str_find_ptr(CG(function_table), p->ovrd_func, strlen(p->ovrd_func)); - if (zf_ovrd == NULL) { - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't find function %s.", p->ovrd_func); - p++; - continue; - } - - zf_save = zend_hash_str_find_ptr(CG(function_table), p->save_func, strlen(p->save_func)); - if (zf_save != NULL) { - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't create function %s because already exists.", - p->save_func); - p++; - continue; - } - - TIMECOP_ASSERT(zf_orig->type == ZEND_INTERNAL_FUNCTION); - TIMECOP_ASSERT(zf_ovrd->type == ZEND_INTERNAL_FUNCTION); - - zend_hash_str_add_mem(CG(function_table), p->save_func, strlen(p->save_func), - zf_orig, sizeof(zend_function)); - function_add_ref(zf_orig); - - FIX_FUNCTION_ARG_INFO_DTOR(zf_orig); - zend_hash_str_update_mem(CG(function_table), p->orig_func, strlen(p->orig_func), - zf_ovrd, sizeof(zend_function)); - function_add_ref(zf_ovrd); - - p++; - } - return SUCCESS; -} - -static int timecop_class_override() -{ - const struct timecop_override_class_entry *p; - zend_class_entry *ce_orig, *ce_ovrd; - zend_function *zf_orig, *zf_ovrd, *zf_save, *zf_new; - - p = &(timecop_override_class_table[0]); - while (p->orig_class != NULL) { - ce_orig = zend_hash_str_find_ptr(CG(class_table), p->orig_class, strlen(p->orig_class)); - if (ce_orig == NULL) { - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't find class %s.", p->orig_class); - p++; - continue; - } - - ce_ovrd = zend_hash_str_find_ptr(CG(class_table), p->ovrd_class, strlen(p->ovrd_class)); - if (ce_ovrd == NULL) { - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't find class %s.", p->ovrd_class); - p++; - continue; - } - - zf_orig = zend_hash_str_find_ptr(&ce_orig->function_table, - p->orig_method, strlen(p->orig_method)); - if (zf_orig == NULL) { - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't find method %s::%s.", - p->orig_class, p->orig_method); - p++; - continue; - } - - zf_ovrd = zend_hash_str_find_ptr(&ce_ovrd->function_table, - p->orig_method, strlen(p->orig_method)); - if (zf_ovrd == NULL) { - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't find method %s::%s.", - p->ovrd_class, p->orig_method); - p++; - continue; - } - - zf_save = zend_hash_str_find_ptr(&ce_orig->function_table, - p->save_method, strlen(p->save_method)); - if (zf_save != NULL) { - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't create method %s::%s because already exists.", - p->orig_class, p->save_method); - p++; - continue; - } - - TIMECOP_ASSERT(zf_orig->type == ZEND_INTERNAL_FUNCTION); - TIMECOP_ASSERT(ce_orig->type & ZEND_INTERNAL_CLASS); - TIMECOP_ASSERT(zf_ovrd->type == ZEND_INTERNAL_FUNCTION); - TIMECOP_ASSERT(ce_ovrd->type & ZEND_INTERNAL_CLASS); - - zend_hash_str_add_mem(&ce_orig->function_table, - p->save_method, strlen(p->save_method), - zf_orig, sizeof(zend_function)); - function_add_ref(zf_orig); - - zf_new = zend_hash_str_update_mem(&ce_orig->function_table, - p->orig_method, strlen(p->orig_method), - zf_ovrd, sizeof(zend_function)); - function_add_ref(zf_ovrd); - - TIMECOP_ASSERT(zf_new != NULL); - TIMECOP_ASSERT(zf_new != zf_orig); - - if (strcmp(p->orig_method, "__construct") == 0) { - ce_orig->constructor = zf_new; - } - p++; - } - return SUCCESS; -} - -/* clear function overriding. */ -static int timecop_func_override_clear() -{ - const struct timecop_override_func_entry *p; - zend_function *zf_orig, *zf_ovld; - - p = &(timecop_override_func_table[0]); - while (p->orig_func != NULL) { - zf_orig = zend_hash_str_find_ptr(CG(function_table), - p->save_func, strlen(p->save_func)); - zf_ovld = zend_hash_str_find_ptr(CG(function_table), - p->orig_func, strlen(p->orig_func)); - if (zf_orig == NULL || zf_ovld == NULL) { - p++; - continue; - } - - FIX_FUNCTION_ARG_INFO_DTOR(zf_ovld); - zend_hash_str_update_mem(CG(function_table), p->orig_func, strlen(p->orig_func), - zf_orig, sizeof(zend_function)); - function_add_ref(zf_orig); - - FIX_FUNCTION_ARG_INFO_DTOR(zf_orig); - zend_hash_str_del(CG(function_table), p->save_func, strlen(p->save_func)); - - p++; - } - return SUCCESS; -} - -static int timecop_class_override_clear() -{ - const struct timecop_override_class_entry *p; - zend_class_entry *ce_orig; - zend_function *zf_orig; - - p = &(timecop_override_class_table[0]); - while (p->orig_class != NULL) { - ce_orig = zend_hash_str_find_ptr(CG(class_table), - p->orig_class, strlen(p->orig_class)); - if (ce_orig == NULL) { - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't find class %s.", p->orig_class); - p++; - continue; - } - - zf_orig = zend_hash_str_find_ptr(&ce_orig->function_table, - p->save_method, strlen(p->save_method)); - if (zf_orig == NULL) { - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't find method %s::%s.", - p->orig_class, p->save_method); - p++; - continue; - } - - zend_hash_str_update_mem(&ce_orig->function_table, p->orig_method, strlen(p->orig_method), - zf_orig, sizeof(zend_function)); - function_add_ref(zf_orig); - - zend_hash_str_del(&ce_orig->function_table, p->save_method, strlen(p->save_method)); - - if (strcmp(p->orig_method, "__construct") == 0) { - ce_orig->constructor = zf_orig; - } - p++; - } - return SUCCESS; -} - -static int update_request_time(zend_long unixtime) -{ - zval *server_vars, *request_time, tmp; - - server_vars = zend_hash_str_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER")-1); - if (server_vars != NULL && Z_TYPE_P(server_vars) == IS_ARRAY) { - request_time = zend_hash_str_find(Z_ARRVAL_P(server_vars), "REQUEST_TIME", sizeof("REQUEST_TIME")-1); - if (request_time != NULL) { - if (Z_TYPE(TIMECOP_G(orig_request_time)) == IS_NULL) { - ZVAL_COPY_VALUE(&TIMECOP_G(orig_request_time), request_time); - } - } - ZVAL_LONG(&tmp, unixtime); - zend_hash_str_update(Z_ARRVAL_P(server_vars), - "REQUEST_TIME", sizeof("REQUEST_TIME")-1, - &tmp); - } - - return SUCCESS; -} - -static int restore_request_time() -{ - zval *server_vars; - - server_vars = zend_hash_str_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER")-1); - if (Z_TYPE(TIMECOP_G(orig_request_time)) != IS_NULL && - server_vars != NULL && Z_TYPE_P(server_vars) == IS_ARRAY) { - zend_hash_str_update(Z_ARRVAL_P(server_vars), - "REQUEST_TIME", sizeof("REQUEST_TIME")-1, - &TIMECOP_G(orig_request_time)); - ZVAL_NULL(&TIMECOP_G(orig_request_time)); - } - - return SUCCESS; -} - -static int fill_mktime_params(zval *fill_params, const char *date_function_name, int from) -{ - char *formats[MKTIME_NUM_ARGS] = {"H", "i", "s", "n", "j", "Y"}; - zval format, timestamp; - int i; - - ZVAL_LONG(×tamp, mock_timestamp()); - - for (i = from; i < MKTIME_NUM_ARGS; i++) { - ZVAL_STRING(&format, formats[i]); - call_php_function_with_2_params(date_function_name, &fill_params[i], &format, ×tamp); - zval_ptr_dtor(&format); - } - - return MKTIME_NUM_ARGS; -} - -/* - * get_formatted_mock_time() : return formatted mock time like "2000-12-30 01:02:03.456000" - * - * pseudo code: - * - * function get_formatted_mock_time($time, $timezone_obj) { - * if ($time === null || $time === false || $time === "") { - * $time = "now"; - * } - * $now = get_mock_timeval(); - * if ($timezone_obj) { - * // save default timezone - * $zonename = $timezone_obj->getName() - * $orig_zonename = date_default_timezone_get(); - * date_default_timezone_set($zonename); - * } - * $fixed_sec = strtotime($time, $now->sec); - * if ($timezone_obj && $orig_zonename) { - * // restore default timezone - * date_default_timezone_set($orig_zonename); - * } - * if ($fixed_sec === FALSE) { - * return false; - * } - * $fixed_usec = get_mock_fraction($time, $timezone_obj); - * if ($fixed_usec === -1) { - * $fixed_usec = $now->usec; - * } - * $dt = date_create($time, $timezone_obj); - * $dt->setTimestamp($fixed_sec); - * $format = sprintf("Y-m-d H:i:s.%06d", $fixed_usec); - * $formatted_time = $dt->format($format); - * return $formatted_time; - * } - */ -static int get_formatted_mock_time(zval *time, zval *timezone_obj, zval *retval_time, zval *retval_timezone) -{ - zval fixed_sec, orig_zonename; - zval now_timestamp, str_now; - tc_timeval now; - zend_long fixed_usec; - - if (TIMECOP_G(timecop_mode) == TIMECOP_MODE_REALTIME) { - ZVAL_FALSE(retval_time); - ZVAL_NULL(retval_timezone); - return -1; - } - - if (time == NULL || Z_TYPE_P(time) == IS_NULL || - Z_TYPE_P(time) == IS_FALSE || - (Z_TYPE_P(time) == IS_STRING && Z_STRLEN_P(time) == 0)) { - ZVAL_STRING(&str_now, "now"); - time = &str_now; - } - - get_mock_timeval(&now, NULL); - - // @todo Restore removed timezone handling code? https://github.com/kiddivouchers/php-timecop/pull/6 - - ZVAL_LONG(&now_timestamp, now.sec); - call_php_function_with_2_params(ORIG_FUNC_NAME("strtotime"), &fixed_sec, time, &now_timestamp); - - if (Z_TYPE(fixed_sec) == IS_FALSE) { - ZVAL_FALSE(retval_time); - ZVAL_NULL(retval_timezone); - return -1; - } - - fixed_usec = get_mock_fraction(time, timezone_obj); - if (fixed_usec == -1) { - fixed_usec = now.usec; - } - - { - zval dt; - zval format_str; - - char buf[64]; - - call_php_function_with_2_params(ORIG_FUNC_NAME("date_create"), &dt, time, timezone_obj); - if (Z_TYPE(dt) == IS_FALSE) { - ZVAL_FALSE(retval_time); - ZVAL_NULL(retval_timezone); - return -1; - } - sprintf(buf, "Y-m-d H:i:s.%06ld", fixed_usec); - ZVAL_STRING(&format_str, buf); - call_php_method_with_1_params(&dt, TIMECOP_G(ce_DateTime), "settimestamp", NULL, &fixed_sec); - call_php_method_with_0_params(&dt, TIMECOP_G(ce_DateTime), "gettimezone", retval_timezone); - call_php_method_with_1_params(&dt, TIMECOP_G(ce_DateTime), "format", retval_time, &format_str); - zval_ptr_dtor(&fixed_sec); - zval_ptr_dtor(&format_str); - zval_ptr_dtor(&dt); - } - - if (time == &str_now) { - zval_ptr_dtor(&str_now); - } - return 0; -} - -/* - * get_mock_fraction() - * - * pseudo code: - * - * function get_mock_fraction($time, $timezone_obj) { - * $dt1 = date_create($time, $timezone_obj); - * usleep(1); - * $dt2 = date_create($time, $timezone_obj); - * $usec1 = $dt1->format("u"); - * $usec2 = $dt2->format("u"); - * if ($usec1 === $usec2) { - * $fixed_usec = $usec1; - * } else { - * $fixed_usec = -1; - * } - * return $fixed_usec; - * } - */ -static long get_mock_fraction(zval *time, zval *timezone_obj TSRMLS_DC) -{ - zval dt1, dt2, usec1, usec2; - zend_long fixed_usec; - zval u_str, sleep_usec; - - call_php_function_with_2_params(ORIG_FUNC_NAME("date_create"), &dt1, time, timezone_obj); - if (Z_TYPE(dt1) == IS_FALSE) { - return -1; - } - - ZVAL_LONG(&sleep_usec, 1); - call_php_function_with_1_params("usleep", NULL, &sleep_usec); - - call_php_function_with_2_params(ORIG_FUNC_NAME("date_create"), &dt2, time, timezone_obj); - if (Z_TYPE(dt2) == IS_FALSE) { - zval_ptr_dtor(&dt1); - return -1; - } - ZVAL_STRING(&u_str, "u"); - call_php_method_with_1_params(&dt1, TIMECOP_G(ce_DateTime), "format", &usec1, &u_str); - call_php_method_with_1_params(&dt2, TIMECOP_G(ce_DateTime), "format", &usec2, &u_str); - convert_to_long(&usec1); - convert_to_long(&usec2); - - if (Z_LVAL(usec1) == Z_LVAL(usec2)) { - fixed_usec = Z_LVAL(usec1); - } else { - fixed_usec = -1; - } - zval_ptr_dtor(&dt1); - zval_ptr_dtor(&dt2); - zval_ptr_dtor(&u_str); - - return fixed_usec; -} - - -static void _timecop_call_function(INTERNAL_FUNCTION_PARAMETERS, const char *function_name, int index_to_fill_timestamp) -{ - zval *params; - uint32_t param_count; - - param_count = MAX(ZEND_NUM_ARGS(), index_to_fill_timestamp+1); - params = (zval *)safe_emalloc(param_count, sizeof(zval), 0); - - if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), params) == FAILURE) { - efree(params); - return; - } - - param_count = ZEND_NUM_ARGS(); - if (param_count == index_to_fill_timestamp) { - ZVAL_LONG(¶ms[param_count], mock_timestamp()); - param_count++; - } - - _call_php_function_with_params(function_name, return_value, param_count, params); - - efree(params); -} - -/* {{{ _timecop_call_mktime - timecop_(gm)mktime helper */ -static void _timecop_call_mktime(INTERNAL_FUNCTION_PARAMETERS, const char *mktime_function_name, const char *date_function_name) -{ - zval *params; - uint32_t param_count; - - int i; - - param_count = MAX(ZEND_NUM_ARGS(), MKTIME_NUM_ARGS); - params = (zval *)safe_emalloc(param_count, sizeof(zval), 0); - - if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), params) == FAILURE) { - efree(params); - zend_throw_error(NULL, "Cannot get arguments for calling"); - return; - } - - param_count = ZEND_NUM_ARGS(); - if (param_count < MKTIME_NUM_ARGS) { - fill_mktime_params(params, date_function_name, param_count); - param_count = MKTIME_NUM_ARGS; - } - - if (ZEND_NUM_ARGS() == 0) { - php_error_docref(NULL, E_DEPRECATED, "You should be using the time() function instead"); - } - - _call_php_function_with_params(mktime_function_name, return_value, param_count, params); - - for (i = ZEND_NUM_ARGS(); i < MKTIME_NUM_ARGS; i++) { - zval_ptr_dtor(¶ms[i]); - } - efree(params); -} -/* }}} */ - -/* {{{ proto int timecop_freeze(long timestamp) - Time travel to specified timestamp and freeze */ -PHP_FUNCTION(timecop_freeze) -{ - zval *dt; - zend_long timestamp; - tc_timeval freezed_tv; - - if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "O", &dt, TIMECOP_G(ce_DateTimeInterface)) != FAILURE) { - get_timeval_from_datetime(&freezed_tv, dt); - } else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "l", ×tamp) != FAILURE) { - freezed_tv.sec = timestamp; - freezed_tv.usec = 0; - } else { - php_error_docref(NULL, E_WARNING, "This function accepts either (DateTimeInterface) OR (int) as arguments."); - RETURN_FALSE; - } - - TIMECOP_G(timecop_mode) = TIMECOP_MODE_FREEZE; - TIMECOP_G(freezed_time) = freezed_tv; - - if (TIMECOP_G(sync_request_time)){ - update_request_time(freezed_tv.sec); - } - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto int timecop_travel(long timestamp) - Time travel to specified timestamp */ -PHP_FUNCTION(timecop_travel) -{ - zval *dt; - zend_long timestamp; - tc_timeval now, mock_tv; - - if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "O", &dt, TIMECOP_G(ce_DateTimeInterface)) != FAILURE) { - get_timeval_from_datetime(&mock_tv, dt); - } else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "l", ×tamp) != FAILURE) { - mock_tv.sec = timestamp; - mock_tv.usec = 0; - } else { - php_error_docref(NULL, E_WARNING, "This function accepts either (DateTimeInterface) OR (int) as arguments."); - RETURN_FALSE; - } - - TIMECOP_G(timecop_mode) = TIMECOP_MODE_TRAVEL; - get_current_time(&now); - tc_timeval_sub(&TIMECOP_G(travel_offset), &mock_tv, &now); - TIMECOP_G(travel_origin) = now; - - if (TIMECOP_G(sync_request_time)){ - update_request_time(mock_tv.sec); - } - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto int timecop_scale(long scale) - Accelerate time with specified scale */ -PHP_FUNCTION(timecop_scale) -{ - zend_long scale; - tc_timeval now, mock_time; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &scale) == FAILURE) { - RETURN_FALSE; - } - if (scale < 0) { - RETURN_FALSE; - } - get_current_time(&now); - get_mock_timeval(&mock_time, &now); - TIMECOP_G(timecop_mode) = TIMECOP_MODE_TRAVEL; - TIMECOP_G(travel_origin) = now; - tc_timeval_sub(&TIMECOP_G(travel_offset), &mock_time, &now); - TIMECOP_G(scaling_factor) = scale; - - if (TIMECOP_G(sync_request_time)){ - update_request_time(mock_time.sec); - } - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto int timecop_return(void) - Return to Time travel to specified timestamp */ -PHP_FUNCTION(timecop_return) -{ - TIMECOP_G(timecop_mode) = TIMECOP_MODE_REALTIME; - - if (TIMECOP_G(sync_request_time)){ - restore_request_time(); - } - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto int timecop_time(void) - Return virtual timestamp */ -PHP_FUNCTION(timecop_time) -{ - RETURN_LONG(mock_timestamp()); -} -/* }}} */ - -/* {{{ proto int timecop_mktime([int hour [, int min [, int sec [, int mon [, int day [, int year]]]]]]) - Get UNIX timestamp for a date */ -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) - Z_PARAM_LONG(min) - Z_PARAM_LONG(sec) - Z_PARAM_LONG(mon) - Z_PARAM_LONG(day) - Z_PARAM_LONG(yea) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); -#endif - - TIMECOP_CALL_MKTIME("mktime", "date"); -} -/* }}} */ - -/* {{{ proto int timecop_gmmktime([int hour [, int min [, int sec [, int mon [, int day [, int year]]]]]]) - Get UNIX timestamp for a GMT date */ -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) - Z_PARAM_LONG(min) - Z_PARAM_LONG(sec) - Z_PARAM_LONG(mon) - Z_PARAM_LONG(day) - Z_PARAM_LONG(yea) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); -#endif - - TIMECOP_CALL_MKTIME("gmmktime", "gmdate"); -} -/* }}} */ - -/* {{{ proto string timecop_date(string format [, long timestamp]) - Format a local date/time */ -PHP_FUNCTION(timecop_date) -{ - zend_string *format; - zend_long ts; - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STR(format) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(ts) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - TIMECOP_CALL_FUNCTION("date", 1); -} -/* }}} */ - -/* {{{ proto string timecop_gmdate(string format [, long timestamp]) - Format a GMT date/time */ -PHP_FUNCTION(timecop_gmdate) -{ - zend_string *format; - zend_long ts; - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STR(format) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(ts) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - TIMECOP_CALL_FUNCTION("gmdate", 1); -} -/* }}} */ - -/* {{{ proto int timecop_idate(string format [, int timestamp]) - Format a local time/date as integer */ -PHP_FUNCTION(timecop_idate) -{ - zend_string *format; - zend_long ts; - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STR(format) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(ts) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - TIMECOP_CALL_FUNCTION("idate", 1); -} -/* }}} */ - -/* {{{ proto array timecop_getdate([int timestamp]) - Get date/time information */ -PHP_FUNCTION(timecop_getdate) -{ - zend_long ts; - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(ts) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - TIMECOP_CALL_FUNCTION("getdate", 0); -} -/* }}} */ - -/* {{{ proto array timecop_localtime([int timestamp [, bool associative_array]]) - Returns the results of the C system call localtime as an associative array if - the associative_array argument is set to 1 other wise it is a regular array */ -PHP_FUNCTION(timecop_localtime) -{ - zend_long timestamp; - zend_bool associative; - ZEND_PARSE_PARAMETERS_START(0, 2) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(timestamp) - Z_PARAM_BOOL(associative) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - TIMECOP_CALL_FUNCTION("localtime", 0); -} -/* }}} */ - -/* {{{ proto int timecop_strtotime(string time [, int now ]) - Convert string representation of date and time to a timestamp */ -PHP_FUNCTION(timecop_strtotime) -{ - zend_string *times; - zend_long preset_ts; - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STR(times) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(preset_ts) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - TIMECOP_CALL_FUNCTION("strtotime", 1); -} -/* }}} */ - -/* {{{ proto string timecop_strftime(string format [, int timestamp]) - Format a local time/date according to locale settings */ -PHP_FUNCTION(timecop_strftime) -{ - zend_string *format; - zend_long timestamp; - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STR(format) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(timestamp) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - TIMECOP_CALL_FUNCTION("strftime", 1); -} -/* }}} */ - -/* {{{ proto string timecop_gmstrftime(string format [, int timestamp]) - Format a GMT/UCT time/date according to locale settings */ -PHP_FUNCTION(timecop_gmstrftime) -{ - zend_string *format; - zend_long timestamp = 0; - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STR(format) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(timestamp) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - TIMECOP_CALL_FUNCTION("gmstrftime", 1); -} -/* }}} */ - -/* - * get_mock_timeval(fixed, now) - * - * - * delta - * |<----------------------->| - * travel_offset delta * scaling_factor - * |<------------->|<------------------------------------------------->| - * ==@===============@=========@=========================================@== - * ^ ^ ^ - * | | | - * travel_origin orig_time traveled_time - * - * - * delta = orig_time - travel_origin - * traveled_time = travel_origin + travel_offset + delta * scaling_factor - */ -static int get_mock_timeval(tc_timeval *fixed, const tc_timeval *now) -{ - if (TIMECOP_G(timecop_mode) == TIMECOP_MODE_FREEZE) { - *fixed = TIMECOP_G(freezed_time); - } else if (TIMECOP_G(timecop_mode) == TIMECOP_MODE_TRAVEL) { - tc_timeval delta, origin = TIMECOP_G(travel_origin); - zend_long scale = TIMECOP_G(scaling_factor); - if (now == NULL) { - get_current_time(&delta); - } else { - delta = *now; - } - tc_timeval_sub(&delta, &delta, &origin); - tc_timeval_mul(&delta, &delta, scale); - tc_timeval_add(fixed, &origin, &TIMECOP_G(travel_offset)); - tc_timeval_add(fixed, fixed, &delta); - } else { - if (now == NULL) { - get_current_time(fixed); - } else { - *fixed = *now; - } - } - return 0; -} - -static zend_long mock_timestamp() -{ - tc_timeval tv; - get_mock_timeval(&tv, NULL); - return tv.sec; -} - -static int get_timeval_from_datetime(tc_timeval *tp, zval *dt) -{ - zval sec, usec; - zval u_str; - - call_php_method_with_0_params(dt, Z_OBJCE_P(dt), "gettimestamp", &sec); - ZVAL_STRING(&u_str, "u"); - call_php_method_with_1_params(dt, Z_OBJCE_P(dt), "format", &usec, &u_str); - zval_ptr_dtor(&u_str); - convert_to_long(&usec); - - tp->sec = Z_LVAL(sec); - tp->usec = Z_LVAL(usec); - - return 0; -} - -static int get_current_time(tc_timeval *now) -{ - int ret = 0; -#if HAVE_GETTIMEOFDAY - struct timeval tv; - ret = gettimeofday(&tv, NULL); - if (ret == 0) { - now->sec = (zend_long)tv.tv_sec; - now->usec = (zend_long)tv.tv_usec; - } -#else - time_t ts = time(NULL); - if (ts == -1) { - ret = -1; - } else { - now->sec = (zend_long)ts; - now->usec = 0; - } -#endif - return ret; -} - -#ifdef HAVE_GETTIMEOFDAY - -#define MICRO_IN_SEC 1000000.00 -#define SEC_IN_MIN 60 - -static void _timecop_gettimeofday(INTERNAL_FUNCTION_PARAMETERS, int mode) -{ - zend_bool get_as_float = 0; - tc_timeval fixed; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &get_as_float) == FAILURE) { - RETURN_FALSE; - } - - if (get_mock_timeval(&fixed, NULL)) { - RETURN_FALSE; - } - - if (get_as_float) { - RETURN_DOUBLE((double)(fixed.sec + fixed.usec / MICRO_IN_SEC)); - } - if (mode) { - zval zv_offset, zv_dst, format, timestamp; - zend_long offset = 0, is_dst = 0; - - ZVAL_LONG(×tamp, fixed.sec); - - /* offset */ - ZVAL_STRING(&format, "Z"); - call_php_function_with_2_params(ORIG_FUNC_NAME("date"), &zv_offset, &format, ×tamp); - convert_to_long(&zv_offset); - offset = Z_LVAL(zv_offset); - zval_ptr_dtor(&zv_offset); - zval_ptr_dtor(&format); - - /* is_dst */ - ZVAL_STRING(&format, "I"); - call_php_function_with_2_params(ORIG_FUNC_NAME("date"), &zv_dst, &format, ×tamp); - convert_to_long(&zv_dst); - is_dst = Z_LVAL(zv_dst); - zval_ptr_dtor(&zv_dst); - zval_ptr_dtor(&format); - - array_init(return_value); - add_assoc_long(return_value, "sec", fixed.sec); - add_assoc_long(return_value, "usec", fixed.usec); - add_assoc_long(return_value, "minuteswest", -offset / SEC_IN_MIN); - add_assoc_long(return_value, "dsttime", is_dst); - } else { - char ret[100]; - snprintf(ret, 100, "%.8F %ld", fixed.usec / MICRO_IN_SEC, fixed.sec); - RETURN_STRING(ret); - } -} - -/* {{{ proto mixed microtime([bool get_as_float]) - Returns either a string or a float containing the current time in seconds and microseconds */ -PHP_FUNCTION(timecop_microtime) -{ - _timecop_gettimeofday(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); -} -/* }}} */ - -/* {{{ proto array gettimeofday([bool get_as_float]) - Returns the current time as array */ -PHP_FUNCTION(timecop_gettimeofday) -{ - _timecop_gettimeofday(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); -} -/* }}} */ -#endif - -/* {{{ proto int timecop_unixtojd([int timestamp]) - Convert UNIX timestamp to Julian Day */ -PHP_FUNCTION(timecop_unixtojd) -{ - TIMECOP_CALL_FUNCTION("unixtojd", 0); -} -/* }}} */ - -static void _timecop_orig_datetime_constructor(INTERNAL_FUNCTION_PARAMETERS, int immutable) -{ - zval *arg1 = NULL, *arg2 = NULL; - zend_class_entry *real_ce; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|zz", &arg1, &arg2) == FAILURE) { - RETURN_FALSE; - } - - if (immutable) { - real_ce = TIMECOP_G(ce_DateTimeImmutable); - } else { - real_ce = TIMECOP_G(ce_DateTime); - } - - call_php_method_with_2_params(getThis(), real_ce, ORIG_FUNC_NAME("__construct"), NULL, arg1, arg2); -} - -static inline void _timecop_datetime_constructor(INTERNAL_FUNCTION_PARAMETERS, int immutable) -{ - _timecop_datetime_constructor_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, getThis(), immutable); -} - -static inline void _timecop_date_create(INTERNAL_FUNCTION_PARAMETERS, int immutable) -{ - _timecop_datetime_constructor_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, NULL, immutable); -} - -static void _timecop_datetime_constructor_ex(INTERNAL_FUNCTION_PARAMETERS, zval *obj, int immutable) -{ - zval orig_time, *orig_timezone = NULL; - zval fixed_time, fixed_timezone, *arg1, *arg2; - char *orig_time_str = NULL; - size_t orig_time_len = 0; - const char *real_func; - zend_class_entry *real_ce; - -#if PHP_VERSION_ID >= 80100 - ZEND_PARSE_PARAMETERS_START(0, 2) - Z_PARAM_OPTIONAL - Z_PARAM_STRING(orig_time_str, orig_time_len) - Z_PARAM_OBJECT_OF_CLASS_OR_NULL(orig_timezone, TIMECOP_G(ce_DateTimeZone)) - ZEND_PARSE_PARAMETERS_END(); - - ZVAL_STRINGL(&orig_time, orig_time_str, orig_time_len); -#else - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sO!", &orig_time_str, &orig_time_len, &orig_timezone, TIMECOP_G(ce_DateTimeZone)) == FAILURE) { - RETURN_FALSE; - } - - if (orig_time_str == NULL) { - ZVAL_NULL(&orig_time); - } else { - ZVAL_STRINGL(&orig_time, orig_time_str, orig_time_len); - } -#endif - - if (immutable) { - real_func = ORIG_FUNC_NAME("date_create_immutable"); - real_ce = TIMECOP_G(ce_DateTimeImmutable); - } else { - real_func = ORIG_FUNC_NAME("date_create"); - real_ce = TIMECOP_G(ce_DateTime); - } - - if (get_formatted_mock_time(&orig_time, orig_timezone, &fixed_time, &fixed_timezone TSRMLS_CC) == 0) { - arg1 = &fixed_time; - arg2 = &fixed_timezone; - } else { - arg1 = &orig_time; - arg2 = orig_timezone; - } - if (obj == NULL) { - call_php_function_with_2_params(real_func, return_value, arg1, arg2); - } else { - call_php_method_with_2_params(obj, real_ce, ORIG_FUNC_NAME("__construct"), NULL, arg1, arg2); - } - zval_ptr_dtor(&orig_time); - zval_ptr_dtor(&fixed_time); - zval_ptr_dtor(&fixed_timezone); -} - -/* {{{ proto DateTime timecop_date_create([string time[, DateTimeZone object]]) - Returns new DateTime object initialized with traveled time */ -PHP_FUNCTION(timecop_date_create) -{ - _timecop_date_create(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); -} -/* }}} */ - -static void _timecop_date_create_from_format(INTERNAL_FUNCTION_PARAMETERS, int immutable) -{ - zval *orig_timezone = NULL; - zval orig_format, orig_time, fixed_format, fixed_time, new_format, new_time; - zval dt, now_timestamp, tmp; - char *orig_format_str, *orig_time_str; - size_t orig_format_len, orig_time_len; - tc_timeval now; - char buf[64]; - const char *real_func; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|O!", &orig_format_str, &orig_format_len, &orig_time_str, &orig_time_len, &orig_timezone, TIMECOP_G(ce_DateTimeZone)) == FAILURE) { - RETURN_FALSE; - } - - ZVAL_STRINGL(&orig_format, orig_format_str, orig_format_len); - ZVAL_STRINGL(&orig_time, orig_time_str, orig_time_len); - - call_php_function_with_3_params(ORIG_FUNC_NAME("date_create_from_format"), &dt, &orig_format, &orig_time, orig_timezone); - if (Z_TYPE(dt) == IS_FALSE) { - RETURN_FALSE; - } - - if (memchr(orig_format_str, '!', orig_format_len) || - memchr(orig_format_str, '|', orig_format_len)) { - - if (immutable) { - call_php_function_with_3_params(ORIG_FUNC_NAME("date_create_immutable_from_format"), &dt, &orig_format, &orig_time, orig_timezone); - } - - zval_ptr_dtor(&orig_format); - zval_ptr_dtor(&orig_time); - RETURN_ZVAL(&dt, 1, 1); - } - - get_mock_timeval(&now, NULL); - - ZVAL_LONG(&now_timestamp, now.sec); - call_php_method_with_1_params(&dt, TIMECOP_G(ce_DateTime), "settimestamp", NULL, &now_timestamp); - sprintf(buf, "Y-m-d H:i:s.%06ld ", now.usec); - ZVAL_STRING(&tmp, buf); - call_php_method_with_1_params(&dt, TIMECOP_G(ce_DateTime), "format", &fixed_time, &tmp); - zval_ptr_dtor(&tmp); - - if (memchr(orig_format_str, 'g', orig_format_len) || - memchr(orig_format_str, 'h', orig_format_len) || - memchr(orig_format_str, 'G', orig_format_len) || - memchr(orig_format_str, 'H', orig_format_len) || - memchr(orig_format_str, 'i', orig_format_len) || - memchr(orig_format_str, 's', orig_format_len)) { - ZVAL_STRING(&fixed_format, "Y-m-d ??:??:??.??????"); - } else if (memchr(orig_format_str, 'Y', orig_format_len) || - memchr(orig_format_str, 'y', orig_format_len) || - memchr(orig_format_str, 'F', orig_format_len) || - memchr(orig_format_str, 'M', orig_format_len) || - memchr(orig_format_str, 'm', orig_format_len) || - memchr(orig_format_str, 'n', orig_format_len) || - memchr(orig_format_str, 'd', orig_format_len) || - memchr(orig_format_str, 'j', orig_format_len) || - memchr(orig_format_str, 'D', orig_format_len) || - memchr(orig_format_str, 'l', orig_format_len) || - memchr(orig_format_str, 'U', orig_format_len)) { - ZVAL_STRING(&fixed_format, "Y-m-d H:i:s.??????"); - } else if (memchr(orig_format_str, 'u', orig_format_len)) { - // https://bugs.php.net/bug.php?id=78603 -#if PHP_VERSION_ID >= 70300 - ZVAL_STRING(&fixed_format, "Y-m-d ??:??:??.??????"); -#elif PHP_VERSION_ID >= 70100 - ZVAL_STRING(&fixed_format, "Y-m-d H:i:s.u"); -#else - ZVAL_STRING(&fixed_format, "Y-m-d H:i:s.??????"); -#endif - } else { -#if PHP_VERSION_ID >= 70100 - ZVAL_STRING(&fixed_format, "Y-m-d H:i:s.u"); -#else - ZVAL_STRING(&fixed_format, "Y-m-d H:i:s.??????"); -#endif - } - - ZVAL_STRING(&tmp, "%s %s"); - call_php_function_with_3_params("sprintf", &new_format, &tmp, &fixed_format, &orig_format); - call_php_function_with_3_params("sprintf", &new_time, &tmp, &fixed_time, &orig_time); - zval_ptr_dtor(&tmp); - - if (immutable) { - real_func = ORIG_FUNC_NAME("date_create_immutable_from_format"); - } else { - real_func = ORIG_FUNC_NAME("date_create_from_format"); - } - call_php_function_with_3_params(real_func, return_value, &new_format, &new_time, orig_timezone); - - zval_ptr_dtor(&dt); - zval_ptr_dtor(&orig_format); - zval_ptr_dtor(&orig_time); - zval_ptr_dtor(&fixed_format); - zval_ptr_dtor(&fixed_time); - zval_ptr_dtor(&new_format); - zval_ptr_dtor(&new_time); -} - -/* {{{ proto DateTime timecop_date_create_from_format(string format, string time[, DateTimeZone object]) - Returns new DateTime object initialized with traveled time */ -PHP_FUNCTION(timecop_date_create_from_format) -{ - _timecop_date_create_from_format(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); -} -/* }}} */ - -/* {{{ proto DateTimeImmutable timecop_date_create_immutable([string time[, DateTimeZone object]]) - Returns new DateTimeImmutable object initialized with traveled time */ -PHP_FUNCTION(timecop_date_create_immutable) -{ - _timecop_date_create(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); -} -/* }}} */ - -/* {{{ proto DateTimeImmutable timecop_date_create_immutable_from_format(string format, string time[, DateTimeZone object]) - Returns new DateTimeImmutable object initialized with traveled time */ -PHP_FUNCTION(timecop_date_create_immutable_from_format) -{ - _timecop_date_create_from_format(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); -} -/* }}} */ - -/* {{{ proto TimecopDateTime::__construct([string time[, DateTimeZone object]]) - Creates new TimecopDateTime object */ -PHP_METHOD(TimecopDateTime, __construct) -{ - _timecop_datetime_constructor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); -} -/* }}} */ - -/* {{{ proto TimecopOrigDateTime::__construct([string time[, DateTimeZone object]]) - Creates new TimecopOrigDateTime object */ -PHP_METHOD(TimecopOrigDateTime, __construct) -{ - _timecop_orig_datetime_constructor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); -} -/* }}} */ - -/* {{{ proto TimecopDateTimeImmutable::__construct([string time[, DateTimeZone object]]) - Creates new TimecopDateTimeImmutable object */ -PHP_METHOD(TimecopDateTimeImmutable, __construct) -{ - _timecop_datetime_constructor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); -} -/* }}} */ - -/* {{{ proto TimecopOrigDateTimeImmutable::__construct([string time[, DateTimeZone object]]) - Creates new TimecopOrigDateTimeImmutable object */ -PHP_METHOD(TimecopOrigDateTimeImmutable, __construct) -{ - _timecop_orig_datetime_constructor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); -} -/* }}} */ - -static inline zval* _call_php_method_with_0_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr) -{ - return _call_php_method(object_pp, obj_ce, method_name, retval_ptr, 0, NULL, NULL); -} - -static inline zval* _call_php_method_with_1_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, zval *arg1) -{ - int nparams = 1; - if (arg1 == NULL) { - nparams = 0; - } - return _call_php_method(object_pp, obj_ce, method_name, retval_ptr, nparams, arg1, NULL); -} - -static inline zval* _call_php_method_with_2_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, zval *arg1, zval *arg2) -{ - int nparams = 2; - if (arg1 == NULL) { - nparams = 0; - } else if (arg2 == NULL) { - nparams = 1; - } - return _call_php_method(object_pp, obj_ce, method_name, retval_ptr, nparams, arg1, arg2); -} - -static inline zval* _call_php_method(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, int param_count, zval* arg1, zval* arg2) -{ - return zend_call_method( -#if PHP_MAJOR_VERSION >= 8 - object_pp == NULL ? NULL : Z_OBJ_P(object_pp), -#else - object_pp, -#endif - obj_ce, - NULL, - method_name, - strlen(method_name), - retval_ptr, - param_count, - arg1, - arg2 - ); -} - -static inline void _call_php_function_with_3_params(const char *function_name, zval *retval_ptr, zval *arg1, zval *arg2, zval *arg3) -{ - if (arg3 == NULL) { - call_php_function_with_2_params(function_name, retval_ptr, arg1, arg2); - } else { - zval params[3]; - ZVAL_COPY(¶ms[0], arg1); - ZVAL_COPY(¶ms[1], arg2); - ZVAL_COPY(¶ms[2], arg3); - _call_php_function_with_params(function_name, retval_ptr, 3, params); - zval_ptr_dtor(¶ms[0]); - zval_ptr_dtor(¶ms[1]); - zval_ptr_dtor(¶ms[2]); - } -} - -static inline void _call_php_function_with_params(const char *function_name, zval *retval_ptr, uint32_t param_count, zval *params) -{ - zval callable; - ZVAL_STRING(&callable, function_name); - - call_user_function(EG(function_table), NULL, &callable, retval_ptr, param_count, params); - - zval_ptr_dtor(&callable); -} - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: noet sw=4 ts=4 fdm=marker - * vim<600: noet sw=4 ts=4 - */ diff --git a/timecop_php7_arginfo.h b/timecop_php7_arginfo.h deleted file mode 100644 index 251a386..0000000 --- a/timecop_php7_arginfo.h +++ /dev/null @@ -1,138 +0,0 @@ -/* -MIT License - -Copyright (c) 2012-2017 Yoshio HANAWA - -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_INFO(arginfo_timecop_time, 0) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_mktime, 0, 0, 0) - ZEND_ARG_INFO(0, hour) - ZEND_ARG_INFO(0, min) - ZEND_ARG_INFO(0, sec) - ZEND_ARG_INFO(0, mon) - ZEND_ARG_INFO(0, day) - ZEND_ARG_INFO(0, year) -ZEND_END_ARG_INFO() - -#define arginfo_timecop_gmmktime arginfo_timecop_mktime - -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_date, 0, 0, 1) - ZEND_ARG_INFO(0, format) - ZEND_ARG_INFO(0, timestamp) -ZEND_END_ARG_INFO() - -#define arginfo_timecop_gmdate arginfo_timecop_date - -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_idate, 0, 0, 1) - ZEND_ARG_INFO(0, format) - ZEND_ARG_INFO(0, timestamp) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_getdate, 0, 0, 0) - ZEND_ARG_INFO(0, timestamp) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_localtime, 0, 0, 0) - ZEND_ARG_INFO(0, timestamp) - ZEND_ARG_INFO(0, associative_array) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_strtotime, 0, 0, 1) - ZEND_ARG_INFO(0, time) - ZEND_ARG_INFO(0, now) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_strftime, 0, 0, 1) - ZEND_ARG_INFO(0, format) - ZEND_ARG_INFO(0, timestamp) -ZEND_END_ARG_INFO() - -#define arginfo_timecop_gmstrftime arginfo_timecop_strftime - -#ifdef HAVE_GETTIMEOFDAY -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_microtime, 0, 0, 0) - ZEND_ARG_INFO(0, get_as_float) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_gettimeofday, 0, 0, 0) - ZEND_ARG_INFO(0, get_as_float) -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_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() -ZEND_BEGIN_ARG_INFO_EX(arginfo_timecop_date_create_from_format, 0, 0, 2) - ZEND_ARG_INFO(0, format) - ZEND_ARG_INFO(0, time) -#if PHP_VERSION_ID >= 70200 - ZEND_ARG_OBJ_INFO(0, object, DateTimeZone, 1) -#else - ZEND_ARG_INFO(0, object) -#endif -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 - -// timecop_date_create_immutable_from_format() -#define arginfo_timecop_date_create_immutable_from_format arginfo_timecop_date_create_from_format - -// 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 From da7d4b9ea0df5aea7e1fd2c483af1dc3df4e0ea5 Mon Sep 17 00:00:00 2001 From: Sylvain MSL Date: Fri, 21 Mar 2025 14:08:42 +0100 Subject: [PATCH 7/7] chore(tws\backendenv): Remove support for php < 8.4 and with *strftime --- .github/workflows/build.yml | 64 +- README.md | 1 - config.m4 | 8 +- php_timecop.h | 361 ++-- timecop_php8.c | 3097 ++++++++++++++++++----------------- timecop_php8_arginfo.h | 80 +- 6 files changed, 1773 insertions(+), 1838 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bb61d39..b63e78a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,42 +8,20 @@ on: types: [created] jobs: build: - name: PHP ${{ matrix.php-version }} (ZTS ${{ matrix.php-zts }}) - runs-on: ubuntu-22.04 + name: PHP ${{ matrix.php-version }} + runs-on: ubuntu-24.04 timeout-minutes: 30 strategy: fail-fast: false matrix: php-version: - - '5.6' - - '7.0' - - '7.1' - - '7.2' - - '7.3' - - '7.4' - - '8.0' - - '8.1' - - '8.2' - - '8.3' - '8.4' - php-zts: - - nts - - ts - exclude: - - php-version: '5.6' - php-zts: 'ts' - - php-version: '8.3' - php-zts: 'ts' - - php-version: '8.4' - php-zts: 'ts' steps: - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php-version }} coverage: none - env: - phpts: ${{ matrix.php-zts }} - name: PHP version run: | php --version @@ -58,42 +36,4 @@ jobs: ./configure make make test REPORT_EXIT_STATUS=1 NO_INTERACTION=1 TESTS="--show-all" - - name: Save artifact (NTS) - uses: actions/upload-artifact@v4 - if: matrix.php-zts == 'nts' - with: - name: timecop_${{ env.PHP_API }}.so - path: modules/timecop.so - if-no-files-found: error - - name: Save artifact (TS) - uses: actions/upload-artifact@v4 - if: matrix.php-zts == 'ts' - with: - name: timecop_${{ env.PHP_API }}_zts.so - path: modules/timecop.so - if-no-files-found: error - package: - name: Package for download - runs-on: ubuntu-22.04 - timeout-minutes: 10 - needs: build - steps: - - name: Download artifacts - uses: actions/download-artifact@v4 - - name: Combine artifacts - run: | - mkdir -p output - - for dir in timecop_*.so; do - # Move/rename from timecop.so to timecop_PHPAPI.so - mv "${dir}/timecop.so" "output/${dir}" - done - cd output - sha256sum *.so > SHA256SUM - - name: Save artifact - uses: actions/upload-artifact@v4 - with: - name: timecop - path: output/* - if-no-files-found: error diff --git a/README.md b/README.md index 5b101a5..cf485fe 100644 --- a/README.md +++ b/README.md @@ -233,7 +233,6 @@ The MIT License Copyright (c) 2012-2017 Yoshio HANAWA Copyright (c) 2019-2024 Wider Plan Ltd -Copyright (c) 2024 Sylvain Filteau 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: diff --git a/config.m4 b/config.m4 index be358e5..8e3056e 100644 --- a/config.m4 +++ b/config.m4 @@ -35,11 +35,5 @@ if test "$PHP_TIMECOP" != "no"; then AC_MSG_RESULT([$PHP_VERSION]) fi - if test "$PHP_MAJOR_VERSION" -eq 5; then - PHP_NEW_EXTENSION(timecop, timecop_php5.c tc_timeval.c, $ext_shared) - elif test "$PHP_MAJOR_VERSION" -ge 8; then - PHP_NEW_EXTENSION(timecop, timecop_php8.c tc_timeval.c, $ext_shared) - else - PHP_NEW_EXTENSION(timecop, timecop_php7.c tc_timeval.c, $ext_shared) - fi + PHP_NEW_EXTENSION(timecop, timecop_php8.c tc_timeval.c, $ext_shared) fi diff --git a/php_timecop.h b/php_timecop.h index d3ef3c5..a84661f 100644 --- a/php_timecop.h +++ b/php_timecop.h @@ -24,184 +24,185 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef PHP_TIMECOP_H -#define PHP_TIMECOP_H - -#define PHP_TIMECOP_VERSION "1.7.0" - -extern zend_module_entry timecop_module_entry; -#define phpext_timecop_ptr &timecop_module_entry - -#ifdef PHP_WIN32 -# define PHP_TIMECOP_API __declspec(dllexport) -#elif defined(__GNUC__) && __GNUC__ >= 4 -# define PHP_TIMECOP_API __attribute__ ((visibility("default"))) -#else -# define PHP_TIMECOP_API -#endif - -#ifdef ZTS -#include "TSRM.h" -#endif - -#ifndef PHP_WIN32 -#include -#else -#include "win32/time.h" -#endif - -#include "Zend/zend_interfaces.h" -#include "tc_timeval.h" - -PHP_MINIT_FUNCTION(timecop); -PHP_MSHUTDOWN_FUNCTION(timecop); -PHP_RINIT_FUNCTION(timecop); -PHP_RSHUTDOWN_FUNCTION(timecop); -PHP_MINFO_FUNCTION(timecop); - -PHP_FUNCTION(timecop_freeze); -PHP_FUNCTION(timecop_travel); -PHP_FUNCTION(timecop_scale); -PHP_FUNCTION(timecop_return); -PHP_FUNCTION(timecop_time); -PHP_FUNCTION(timecop_mktime); -PHP_FUNCTION(timecop_gmmktime); -PHP_FUNCTION(timecop_date); -PHP_FUNCTION(timecop_gmdate); -PHP_FUNCTION(timecop_idate); -PHP_FUNCTION(timecop_getdate); -PHP_FUNCTION(timecop_localtime); -PHP_FUNCTION(timecop_strtotime); -PHP_FUNCTION(timecop_strftime); -PHP_FUNCTION(timecop_gmstrftime); -#ifdef HAVE_GETTIMEOFDAY -PHP_FUNCTION(timecop_microtime); -PHP_FUNCTION(timecop_gettimeofday); -#endif -PHP_FUNCTION(timecop_unixtojd); -PHP_FUNCTION(timecop_date_create); -PHP_FUNCTION(timecop_date_create_from_format); -PHP_FUNCTION(timecop_date_create_immutable); -PHP_FUNCTION(timecop_date_create_immutable_from_format); - -PHP_METHOD(TimecopDateTime, __construct); -PHP_METHOD(TimecopOrigDateTime, __construct); -PHP_METHOD(TimecopDateTimeImmutable, __construct); -PHP_METHOD(TimecopOrigDateTimeImmutable, __construct); - -PHP_METHOD(Timecop, freeze); -PHP_METHOD(Timecop, travel); - -typedef enum timecop_mode_t { - TIMECOP_MODE_REALTIME, - TIMECOP_MODE_FREEZE, - TIMECOP_MODE_TRAVEL -} timecop_mode_t; - -ZEND_BEGIN_MODULE_GLOBALS(timecop) - long func_override; - long sync_request_time; - zval orig_request_time; - timecop_mode_t timecop_mode; - tc_timeval freezed_time; - tc_timeval travel_origin; - tc_timeval travel_offset; - zend_long scaling_factor; - zend_class_entry *ce_DateTimeZone; - zend_class_entry *ce_DateTimeInterface; - zend_class_entry *ce_DateTime; - zend_class_entry *ce_TimecopDateTime; - zend_class_entry *ce_DateTimeImmutable; - zend_class_entry *ce_TimecopDateTimeImmutable; -ZEND_END_MODULE_GLOBALS(timecop) - -#if ZEND_DEBUG -# define TIMECOP_ASSERT(c) assert(c) -#else -# define TIMECOP_ASSERT(c) -#endif /* ZEND_DEBUG */ - -#define SAVE_FUNC_PREFIX "timecop_orig_" -#define OVRD_FUNC_PREFIX "timecop_" - -#define OVRD_CLASS_PREFIX "timecop" - -#define ORIG_FUNC_NAME(fname) \ - (TIMECOP_G(func_override) ? (SAVE_FUNC_PREFIX fname) : fname) - -#define TIMECOP_OFE(fname) {fname, OVRD_FUNC_PREFIX fname, SAVE_FUNC_PREFIX fname} -#define TIMECOP_OCE(cname, mname) \ - {cname, mname, OVRD_CLASS_PREFIX cname, SAVE_FUNC_PREFIX mname} - -/* - * Trick for guarding the multi-referenced internal function from function destructor on PHP 7.2.0+ - * See: https://github.com/hnw/php-timecop/issues/29#issuecomment-332171527 - */ -# define FIX_FUNCTION_ARG_INFO_DTOR(zend_func) zend_func->common.arg_info = NULL; - -struct timecop_override_func_entry { - char *orig_func; - char *ovrd_func; - char *save_func; -}; - -struct timecop_override_class_entry { - char *orig_class; - char *orig_method; - char *ovrd_class; - char *save_method; -}; - -#define call_php_method_with_0_params(obj, ce, method_name, retval) \ - _call_php_method_with_0_params(obj, ce, method_name, retval TSRMLS_CC) - -#define call_php_method_with_1_params(obj, ce, method_name, retval, arg1) \ - _call_php_method_with_1_params(obj, ce, method_name, retval, arg1 TSRMLS_CC) - -#define call_php_method_with_2_params(obj, ce, method_name, retval, arg1, arg2) \ - _call_php_method_with_2_params(obj, ce, method_name, retval, arg1, arg2 TSRMLS_CC) - -#define call_php_function_with_0_params(function_name, retval) \ - _call_php_method_with_0_params(NULL, NULL, function_name, retval TSRMLS_CC) - -#define call_php_function_with_1_params(function_name, retval, arg1) \ - _call_php_method_with_1_params(NULL, NULL, function_name, retval, arg1 TSRMLS_CC) - -#define call_php_function_with_2_params(function_name, retval, arg1, arg2) \ - _call_php_method_with_2_params(NULL, NULL, function_name, retval, arg1, arg2 TSRMLS_CC) - -#define call_php_function_with_3_params(function_name, retval, arg1, arg2, arg3) \ - _call_php_function_with_3_params(function_name, retval, arg1, arg2, arg3 TSRMLS_CC) - -/* In every utility function you add that needs to use variables - in php_timecop_globals, call TSRMLS_FETCH(); after declaring other - variables used by that function, or better yet, pass in TSRMLS_CC - after the last function argument and declare your utility function - with TSRMLS_DC after the last declared argument. Always refer to - the globals in your function as TIMECOP_G(variable). You are - encouraged to rename these macros something shorter, see - examples in any other php module directory. -*/ - -/* Redeclare macros as no-ops which were removed in PHP 8. */ -#define TSRMLS_D void -#define TSRMLS_DC -#define TSRMLS_C -#define TSRMLS_CC -#define TSRMLS_FETCH() - -#define TIMECOP_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(timecop, v) -#if defined(ZTS) && defined(COMPILE_DL_TIMECOP) - ZEND_TSRMLS_CACHE_EXTERN(); -#endif - -#endif /* PHP_TIMECOP_H */ - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: noet sw=4 ts=4 fdm=marker - * vim<600: noet sw=4 ts=4 + #ifndef PHP_TIMECOP_H + #define PHP_TIMECOP_H + + #define PHP_TIMECOP_VERSION "1.7.0" + + extern zend_module_entry timecop_module_entry; + #define phpext_timecop_ptr &timecop_module_entry + + #ifdef PHP_WIN32 + # define PHP_TIMECOP_API __declspec(dllexport) + #elif defined(__GNUC__) && __GNUC__ >= 4 + # define PHP_TIMECOP_API __attribute__ ((visibility("default"))) + #else + # define PHP_TIMECOP_API + #endif + + #ifdef ZTS + #include "TSRM.h" + #endif + + #ifndef PHP_WIN32 + #include + #else + #include "win32/time.h" + #endif + + #include "Zend/zend_interfaces.h" + #include "tc_timeval.h" + + PHP_MINIT_FUNCTION(timecop); + PHP_MSHUTDOWN_FUNCTION(timecop); + PHP_RINIT_FUNCTION(timecop); + PHP_RSHUTDOWN_FUNCTION(timecop); + PHP_MINFO_FUNCTION(timecop); + + PHP_FUNCTION(timecop_freeze); + PHP_FUNCTION(timecop_travel); + PHP_FUNCTION(timecop_scale); + PHP_FUNCTION(timecop_return); + PHP_FUNCTION(timecop_time); + PHP_FUNCTION(timecop_mktime); + PHP_FUNCTION(timecop_gmmktime); + PHP_FUNCTION(timecop_date); + PHP_FUNCTION(timecop_gmdate); + PHP_FUNCTION(timecop_idate); + PHP_FUNCTION(timecop_getdate); + PHP_FUNCTION(timecop_localtime); + PHP_FUNCTION(timecop_strtotime); + PHP_FUNCTION(timecop_strftime); + PHP_FUNCTION(timecop_gmstrftime); + #ifdef HAVE_GETTIMEOFDAY + PHP_FUNCTION(timecop_microtime); + PHP_FUNCTION(timecop_gettimeofday); + #endif + PHP_FUNCTION(timecop_unixtojd); + PHP_FUNCTION(timecop_date_create); + PHP_FUNCTION(timecop_date_create_from_format); + PHP_FUNCTION(timecop_date_create_immutable); + PHP_FUNCTION(timecop_date_create_immutable_from_format); + + PHP_METHOD(TimecopDateTime, __construct); + PHP_METHOD(TimecopOrigDateTime, __construct); + PHP_METHOD(TimecopDateTimeImmutable, __construct); + PHP_METHOD(TimecopOrigDateTimeImmutable, __construct); + + PHP_METHOD(Timecop, freeze); + PHP_METHOD(Timecop, travel); + + typedef enum timecop_mode_t { + TIMECOP_MODE_REALTIME, + TIMECOP_MODE_FREEZE, + TIMECOP_MODE_TRAVEL + } timecop_mode_t; + + ZEND_BEGIN_MODULE_GLOBALS(timecop) + long func_override; + long sync_request_time; + zval orig_request_time; + timecop_mode_t timecop_mode; + tc_timeval freezed_time; + tc_timeval travel_origin; + tc_timeval travel_offset; + zend_long scaling_factor; + zend_class_entry *ce_DateTimeZone; + zend_class_entry *ce_DateTimeInterface; + zend_class_entry *ce_DateTime; + zend_class_entry *ce_TimecopDateTime; + zend_class_entry *ce_DateTimeImmutable; + zend_class_entry *ce_TimecopDateTimeImmutable; + ZEND_END_MODULE_GLOBALS(timecop) + + #if ZEND_DEBUG + # define TIMECOP_ASSERT(c) assert(c) + #else + # define TIMECOP_ASSERT(c) + #endif /* ZEND_DEBUG */ + + #define SAVE_FUNC_PREFIX "timecop_orig_" + #define OVRD_FUNC_PREFIX "timecop_" + + #define OVRD_CLASS_PREFIX "timecop" + + #define ORIG_FUNC_NAME(fname) \ + (TIMECOP_G(func_override) ? (SAVE_FUNC_PREFIX fname) : fname) + + #define TIMECOP_OFE(fname) {fname, OVRD_FUNC_PREFIX fname, SAVE_FUNC_PREFIX fname} + #define TIMECOP_OCE(cname, mname) \ + {cname, mname, OVRD_CLASS_PREFIX cname, SAVE_FUNC_PREFIX mname} + + /* + * Trick for guarding the multi-referenced internal function from function destructor on PHP 7.2.0+ + * See: https://github.com/hnw/php-timecop/issues/29#issuecomment-332171527 + */ + # define FIX_FUNCTION_ARG_INFO_DTOR(zend_func) zend_func->common.arg_info = NULL; + + struct timecop_override_func_entry { + char *orig_func; + char *ovrd_func; + char *save_func; + }; + + struct timecop_override_class_entry { + char *orig_class; + char *orig_method; + char *ovrd_class; + char *save_method; + }; + + #define call_php_method_with_0_params(obj, ce, method_name, retval) \ + _call_php_method_with_0_params(obj, ce, method_name, retval TSRMLS_CC) + + #define call_php_method_with_1_params(obj, ce, method_name, retval, arg1) \ + _call_php_method_with_1_params(obj, ce, method_name, retval, arg1 TSRMLS_CC) + + #define call_php_method_with_2_params(obj, ce, method_name, retval, arg1, arg2) \ + _call_php_method_with_2_params(obj, ce, method_name, retval, arg1, arg2 TSRMLS_CC) + + #define call_php_function_with_0_params(function_name, retval) \ + _call_php_method_with_0_params(NULL, NULL, function_name, retval TSRMLS_CC) + + #define call_php_function_with_1_params(function_name, retval, arg1) \ + _call_php_method_with_1_params(NULL, NULL, function_name, retval, arg1 TSRMLS_CC) + + #define call_php_function_with_2_params(function_name, retval, arg1, arg2) \ + _call_php_method_with_2_params(NULL, NULL, function_name, retval, arg1, arg2 TSRMLS_CC) + + #define call_php_function_with_3_params(function_name, retval, arg1, arg2, arg3) \ + _call_php_function_with_3_params(function_name, retval, arg1, arg2, arg3 TSRMLS_CC) + + /* In every utility function you add that needs to use variables + in php_timecop_globals, call TSRMLS_FETCH(); after declaring other + variables used by that function, or better yet, pass in TSRMLS_CC + after the last function argument and declare your utility function + with TSRMLS_DC after the last declared argument. Always refer to + the globals in your function as TIMECOP_G(variable). You are + encouraged to rename these macros something shorter, see + examples in any other php module directory. */ + + /* Redeclare macros as no-ops which were removed in PHP 8. */ + #define TSRMLS_D void + #define TSRMLS_DC + #define TSRMLS_C + #define TSRMLS_CC + #define TSRMLS_FETCH() + + #define TIMECOP_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(timecop, v) + #if defined(ZTS) && defined(COMPILE_DL_TIMECOP) + ZEND_TSRMLS_CACHE_EXTERN(); + #endif + + #endif /* PHP_TIMECOP_H */ + + /* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + \ No newline at end of file diff --git a/timecop_php8.c b/timecop_php8.c index 34297bd..7389eb4 100644 --- a/timecop_php8.c +++ b/timecop_php8.c @@ -24,1551 +24,1552 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "php.h" -#include "php_ini.h" -#include "ext/standard/info.h" - -#include "php_timecop.h" - -#ifdef ZFS -#include "TSRM.h" -#endif - -ZEND_DECLARE_MODULE_GLOBALS(timecop) - -static void timecop_globals_ctor(zend_timecop_globals *globals) { - /* Initialize your global struct */ - globals->func_override = 1; - globals->sync_request_time = 1; - ZVAL_NULL(&globals->orig_request_time); - globals->timecop_mode = TIMECOP_MODE_REALTIME; - globals->freezed_time.sec = 0; - globals->freezed_time.usec = 0; - globals->travel_origin.sec = 0; - globals->travel_origin.usec = 0; - globals->travel_offset.sec = 0; - globals->travel_offset.usec = 0; - globals->scaling_factor = 1; - globals->ce_DateTimeZone = NULL; - globals->ce_DateTimeInterface = NULL; - globals->ce_DateTime = NULL; - globals->ce_TimecopDateTime = NULL; - globals->ce_DateTimeImmutable = NULL; - globals->ce_TimecopDateTimeImmutable = NULL; -} - -static const struct timecop_override_func_entry timecop_override_func_table[] = { - TIMECOP_OFE("time"), - TIMECOP_OFE("mktime"), - TIMECOP_OFE("gmmktime"), - TIMECOP_OFE("date"), - TIMECOP_OFE("gmdate"), - TIMECOP_OFE("idate"), - TIMECOP_OFE("getdate"), - TIMECOP_OFE("localtime"), - TIMECOP_OFE("strtotime"), -#ifdef HAVE_GETTIMEOFDAY - TIMECOP_OFE("microtime"), - TIMECOP_OFE("gettimeofday"), -#endif - TIMECOP_OFE("unixtojd"), - TIMECOP_OFE("date_create"), - TIMECOP_OFE("date_create_from_format"), - TIMECOP_OFE("date_create_immutable"), - TIMECOP_OFE("date_create_immutable_from_format"), - {NULL, NULL, NULL} -}; - -static const struct timecop_override_class_entry timecop_override_class_table[] = { - TIMECOP_OCE("datetime", "__construct"), - TIMECOP_OCE("datetime", "createfromformat"), - TIMECOP_OCE("datetimeimmutable", "__construct"), - TIMECOP_OCE("datetimeimmutable", "createfromformat"), - {NULL, NULL, NULL, NULL} -}; - -#include "timecop_php8_arginfo.h" - -/* {{{ timecop_functions[] */ -const zend_function_entry timecop_functions[] = { - PHP_FE(timecop_freeze, arginfo_timecop_freeze) - PHP_FE(timecop_travel, arginfo_timecop_travel) - PHP_FE(timecop_scale, arginfo_timecop_scale) - PHP_FE(timecop_return, arginfo_timecop_return) - PHP_FE(timecop_time, arginfo_timecop_time) - PHP_FE(timecop_mktime, arginfo_timecop_mktime) - PHP_FE(timecop_gmmktime, arginfo_timecop_gmmktime) - PHP_FE(timecop_date, arginfo_timecop_date) - PHP_FE(timecop_gmdate, arginfo_timecop_gmdate) - PHP_FE(timecop_idate, arginfo_timecop_idate) - PHP_FE(timecop_getdate, arginfo_timecop_getdate) - PHP_FE(timecop_localtime, arginfo_timecop_localtime) - PHP_FE(timecop_strtotime, arginfo_timecop_strtotime) -#ifdef HAVE_GETTIMEOFDAY - PHP_FE(timecop_microtime, arginfo_timecop_microtime) - PHP_FE(timecop_gettimeofday, arginfo_timecop_gettimeofday) -#endif - PHP_FE(timecop_unixtojd, arginfo_timecop_unixtojd) - PHP_FE(timecop_date_create, arginfo_timecop_date_create) - PHP_FE(timecop_date_create_from_format, arginfo_timecop_date_create_from_format) - PHP_FE(timecop_date_create_immutable, arginfo_timecop_date_create_immutable) - PHP_FE(timecop_date_create_immutable_from_format, arginfo_timecop_date_create_immutable_from_format) - {NULL, NULL, NULL} -}; -/* }}} */ - -/* declare method parameters, */ - -/* each method can have its own parameters and visibility */ -static zend_function_entry timecop_funcs_timecop[] = { - PHP_ME_MAPPING(freeze, timecop_freeze, arginfo_timecop_travel, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - PHP_ME_MAPPING(travel, timecop_travel, arginfo_timecop_travel, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - PHP_ME_MAPPING(scale, timecop_scale, arginfo_timecop_scale, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - PHP_ME_MAPPING(return, timecop_return, arginfo_timecop_return, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) - {NULL, NULL, NULL} -}; - -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_class_TimecopDateTime_createFromFormat, - ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - {NULL, NULL, NULL} -}; - -static zend_function_entry timecop_funcs_orig_date[] = { - PHP_ME(TimecopOrigDateTime, __construct, arginfo_class_TimecopDateTime___construct, - ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} -}; - -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_class_TimecopDateTimeImmutable_createFromFormat, - ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - {NULL, NULL, NULL} -}; - -static zend_function_entry timecop_funcs_orig_immutable[] = { - PHP_ME(TimecopOrigDateTimeImmutable, __construct, arginfo_class_TimecopDateTimeImmutable___construct, - ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) - {NULL, NULL, NULL} -}; - -#define MKTIME_NUM_ARGS 6 - -#define TIMECOP_CALL_FUNCTION(func_name, index_to_fill_timestamp) \ - _timecop_call_function(INTERNAL_FUNCTION_PARAM_PASSTHRU, ORIG_FUNC_NAME(func_name), index_to_fill_timestamp); - -#define TIMECOP_CALL_MKTIME(mktime_func_name, date_func_name) \ - _timecop_call_mktime(INTERNAL_FUNCTION_PARAM_PASSTHRU, ORIG_FUNC_NAME(mktime_func_name), ORIG_FUNC_NAME(date_func_name)); - -static void timecop_globals_ctor(zend_timecop_globals *globals); - -static int register_timecop_classes(); -static int timecop_func_override(); -static int timecop_class_override(); -static int timecop_func_override_clear(); -static int timecop_class_override_clear(); - -static int update_request_time(zend_long unixtime); -static int restore_request_time(); - -static int fill_mktime_params(zval *fill_params, const char *date_function_name, int from); -static int get_formatted_mock_time(zval *time, zval *timezone_obj, zval *retval_time, zval *retval_timezone); -static long get_mock_fraction(zval *time, zval *timezone_obj); - -static void _timecop_call_function(INTERNAL_FUNCTION_PARAMETERS, const char *function_name, int index_to_fill_timestamp); -static void _timecop_call_mktime(INTERNAL_FUNCTION_PARAMETERS, const char *mktime_function_name, const char *date_function_name); - -static int get_mock_timeval(tc_timeval *fixed, const tc_timeval *now); -static inline zend_long mock_timestamp(); - -static int get_timeval_from_datetime(tc_timeval *tp, zval *dt); -static int get_current_time(tc_timeval *now); - -static void _timecop_orig_datetime_constructor(INTERNAL_FUNCTION_PARAMETERS, int immutable); -static inline void _timecop_date_create(INTERNAL_FUNCTION_PARAMETERS, int immutable); -static inline void _timecop_datetime_constructor(INTERNAL_FUNCTION_PARAMETERS, int immutable); -static void _timecop_datetime_constructor_ex(INTERNAL_FUNCTION_PARAMETERS, zval *obj, int immutable); - -static inline zval* _call_php_method_with_0_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr); -static inline zval* _call_php_method_with_1_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, zval *arg1); -static inline zval* _call_php_method_with_2_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, zval *arg1, zval *arg2); -static inline zval* _call_php_method(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, int param_count, zval* arg1, zval* arg2); -static void _call_php_function_with_3_params(const char *function_name, zval *retval_ptr, zval *arg1, zval *arg2, zval *arg3); -static inline void _call_php_function_with_params(const char *function_name, zval *retval_ptr, uint32_t param_count, zval params[]); - -static const zend_module_dep timecop_module_deps[] = { - ZEND_MOD_REQUIRED("Date") - ZEND_MOD_END -}; - -/* {{{ timecop_module_entry - */ -zend_module_entry timecop_module_entry = { - STANDARD_MODULE_HEADER_EX, - NULL, - timecop_module_deps, - "timecop", - timecop_functions, - PHP_MINIT(timecop), - PHP_MSHUTDOWN(timecop), - PHP_RINIT(timecop), - PHP_RSHUTDOWN(timecop), - PHP_MINFO(timecop), - PHP_TIMECOP_VERSION, - STANDARD_MODULE_PROPERTIES -}; -/* }}} */ - -#ifdef COMPILE_DL_TIMECOP -# ifdef ZTS - ZEND_TSRMLS_CACHE_DEFINE(); -# endif -ZEND_GET_MODULE(timecop) -#endif - -/* {{{ PHP_INI - */ -PHP_INI_BEGIN() - STD_PHP_INI_ENTRY("timecop.func_override", "1", - PHP_INI_SYSTEM, OnUpdateLong, func_override, zend_timecop_globals, timecop_globals) - STD_PHP_INI_ENTRY("timecop.sync_request_time", "1", - PHP_INI_SYSTEM, OnUpdateLong, sync_request_time, zend_timecop_globals, timecop_globals) -PHP_INI_END() -/* }}} */ - -/* {{{ PHP_MINIT_FUNCTION - */ -PHP_MINIT_FUNCTION(timecop) -{ - ZEND_INIT_MODULE_GLOBALS(timecop, timecop_globals_ctor, NULL); - REGISTER_INI_ENTRIES(); - register_timecop_classes(); - - return SUCCESS; -} -/* }}} */ - -/* {{{ PHP_MSHUTDOWN_FUNCTION - */ -PHP_MSHUTDOWN_FUNCTION(timecop) -{ - UNREGISTER_INI_ENTRIES(); - - return SUCCESS; -} -/* }}} */ - -/* {{{ PHP_RINIT_FUNCTION(timecop) */ -PHP_RINIT_FUNCTION(timecop) -{ -#if defined(COMPILE_DL_TIMECOP) && defined(ZTS) - ZEND_TSRMLS_CACHE_UPDATE(); -#endif - - if (TIMECOP_G(func_override)) { - if (SUCCESS != timecop_func_override() || - SUCCESS != timecop_class_override()) { - return FAILURE; - } - } - - return SUCCESS; -} -/* }}} */ - -/* {{{ PHP_RSHUTDOWN_FUNCTION(timecop) */ -PHP_RSHUTDOWN_FUNCTION(timecop) -{ - if (TIMECOP_G(func_override)) { - timecop_func_override_clear(); - timecop_class_override_clear(); - } - - if (Z_TYPE(TIMECOP_G(orig_request_time)) != IS_NULL) { - restore_request_time(); - } - - TIMECOP_G(timecop_mode) = TIMECOP_MODE_REALTIME; - TIMECOP_G(scaling_factor) = 1; - - return SUCCESS; -} -/* }}} */ - - -/* {{{ PHP_MINFO_FUNCTION - */ -PHP_MINFO_FUNCTION(timecop) -{ - php_info_print_table_start(); - php_info_print_table_header(2, "timecop", "enabled"); - php_info_print_table_row(2, "Version", PHP_TIMECOP_VERSION); - php_info_print_table_end(); - - DISPLAY_INI_ENTRIES(); -} -/* }}} */ - -static int register_timecop_classes() -{ - zend_class_entry ce; - zend_class_entry *tmp, *date_ce, *timezone_ce, *immutable_ce, *interface_ce; - - date_ce = zend_hash_str_find_ptr(CG(class_table), "datetime", sizeof("datetime")-1); - if (date_ce == NULL) { - /* DateTime must be initialized before */ - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't find class %s.", "DateTime"); - return SUCCESS; - } - - timezone_ce = zend_hash_str_find_ptr(CG(class_table), "datetimezone", sizeof("datetimezone")-1); - if (timezone_ce == NULL) { - /* DateTime must be initialized before */ - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't find class %s.", "DateTimeZone"); - return SUCCESS; - } - - immutable_ce = zend_hash_str_find_ptr(CG(class_table), "datetimeimmutable", sizeof("datetimeimmutable")-1); - if (immutable_ce == NULL) { - /* DateTimeImmutable must be initialized before */ - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't find class %s.", "DateTimeImmutable"); - return SUCCESS; - } - - interface_ce = zend_hash_str_find_ptr(CG(class_table), "datetimeinterface", sizeof("datetimeinterface")-1); - if (interface_ce == NULL) { - /* DateTimeInterface must be initialized before */ - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't find interface %s.", "DateTimeInterface"); - return SUCCESS; - } - - INIT_CLASS_ENTRY(ce, "Timecop", timecop_funcs_timecop); - zend_register_internal_class(&ce); - - TIMECOP_G(ce_DateTimeZone) = timezone_ce; - TIMECOP_G(ce_DateTimeInterface) = interface_ce; - - /* replace DateTime */ - INIT_CLASS_ENTRY(ce, "TimecopDateTime", timecop_funcs_date); - tmp = zend_register_internal_class_ex(&ce, date_ce); - tmp->create_object = date_ce->create_object; - - TIMECOP_G(ce_DateTime) = date_ce; - TIMECOP_G(ce_TimecopDateTime) = tmp; - - INIT_CLASS_ENTRY(ce, "TimecopOrigDateTime", timecop_funcs_orig_date); - tmp = zend_register_internal_class_ex(&ce, date_ce); - tmp->create_object = date_ce->create_object; - - /* replace DateTimeImmutable */ - INIT_CLASS_ENTRY(ce, "TimecopDateTimeImmutable", timecop_funcs_immutable); - tmp = zend_register_internal_class_ex(&ce, immutable_ce); - tmp->create_object = immutable_ce->create_object; - - TIMECOP_G(ce_DateTimeImmutable) = immutable_ce; - TIMECOP_G(ce_TimecopDateTimeImmutable) = tmp; - - INIT_CLASS_ENTRY(ce, "TimecopOrigDateTimeImmutable", timecop_funcs_orig_immutable); - tmp = zend_register_internal_class_ex(&ce, immutable_ce); - tmp->create_object = immutable_ce->create_object; - - return SUCCESS; -} - -static int timecop_func_override() -{ - const struct timecop_override_func_entry *p; - zend_function *zf_orig, *zf_ovrd, *zf_save; - - p = &(timecop_override_func_table[0]); - while (p->orig_func != NULL) { - zf_orig = zend_hash_str_find_ptr(CG(function_table), p->orig_func, strlen(p->orig_func)); - if (zf_orig == NULL) { - /* Do nothing. Because some functions are introduced by optional extensions. */ - p++; - continue; - } - - zf_ovrd = zend_hash_str_find_ptr(CG(function_table), p->ovrd_func, strlen(p->ovrd_func)); - if (zf_ovrd == NULL) { - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't find function %s.", p->ovrd_func); - p++; - continue; - } - - zf_save = zend_hash_str_find_ptr(CG(function_table), p->save_func, strlen(p->save_func)); - if (zf_save != NULL) { - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't create function %s because already exists.", - p->save_func); - p++; - continue; - } - - TIMECOP_ASSERT(zf_orig->type == ZEND_INTERNAL_FUNCTION); - TIMECOP_ASSERT(zf_ovrd->type == ZEND_INTERNAL_FUNCTION); - - zend_hash_str_add_mem(CG(function_table), p->save_func, strlen(p->save_func), - zf_orig, sizeof(zend_function)); - function_add_ref(zf_orig); - - FIX_FUNCTION_ARG_INFO_DTOR(zf_orig); - zend_hash_str_update_mem(CG(function_table), p->orig_func, strlen(p->orig_func), - zf_ovrd, sizeof(zend_function)); - function_add_ref(zf_ovrd); - - p++; - } - return SUCCESS; -} - -static int timecop_class_override() -{ - const struct timecop_override_class_entry *p; - zend_class_entry *ce_orig, *ce_ovrd; - zend_function *zf_orig, *zf_ovrd, *zf_save, *zf_new; - - p = &(timecop_override_class_table[0]); - while (p->orig_class != NULL) { - ce_orig = zend_hash_str_find_ptr(CG(class_table), p->orig_class, strlen(p->orig_class)); - if (ce_orig == NULL) { - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't find class %s.", p->orig_class); - p++; - continue; - } - - ce_ovrd = zend_hash_str_find_ptr(CG(class_table), p->ovrd_class, strlen(p->ovrd_class)); - if (ce_ovrd == NULL) { - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't find class %s.", p->ovrd_class); - p++; - continue; - } - - zf_orig = zend_hash_str_find_ptr(&ce_orig->function_table, - p->orig_method, strlen(p->orig_method)); - if (zf_orig == NULL) { - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't find method %s::%s.", - p->orig_class, p->orig_method); - p++; - continue; - } - - zf_ovrd = zend_hash_str_find_ptr(&ce_ovrd->function_table, - p->orig_method, strlen(p->orig_method)); - if (zf_ovrd == NULL) { - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't find method %s::%s.", - p->ovrd_class, p->orig_method); - p++; - continue; - } - - zf_save = zend_hash_str_find_ptr(&ce_orig->function_table, - p->save_method, strlen(p->save_method)); - if (zf_save != NULL) { - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't create method %s::%s because already exists.", - p->orig_class, p->save_method); - p++; - continue; - } - - TIMECOP_ASSERT(zf_orig->type == ZEND_INTERNAL_FUNCTION); - TIMECOP_ASSERT(ce_orig->type & ZEND_INTERNAL_CLASS); - TIMECOP_ASSERT(zf_ovrd->type == ZEND_INTERNAL_FUNCTION); - TIMECOP_ASSERT(ce_ovrd->type & ZEND_INTERNAL_CLASS); - - zend_hash_str_add_mem(&ce_orig->function_table, - p->save_method, strlen(p->save_method), - zf_orig, sizeof(zend_function)); - function_add_ref(zf_orig); - - zf_new = zend_hash_str_update_mem(&ce_orig->function_table, - p->orig_method, strlen(p->orig_method), - zf_ovrd, sizeof(zend_function)); - function_add_ref(zf_ovrd); - - TIMECOP_ASSERT(zf_new != NULL); - TIMECOP_ASSERT(zf_new != zf_orig); - - if (strcmp(p->orig_method, "__construct") == 0) { - ce_orig->constructor = zf_new; - } - p++; - } - return SUCCESS; -} - -/* clear function overriding. */ -static int timecop_func_override_clear() -{ - const struct timecop_override_func_entry *p; - zend_function *zf_orig, *zf_ovld; - - p = &(timecop_override_func_table[0]); - while (p->orig_func != NULL) { - zf_orig = zend_hash_str_find_ptr(CG(function_table), - p->save_func, strlen(p->save_func)); - zf_ovld = zend_hash_str_find_ptr(CG(function_table), - p->orig_func, strlen(p->orig_func)); - if (zf_orig == NULL || zf_ovld == NULL) { - p++; - continue; - } - - FIX_FUNCTION_ARG_INFO_DTOR(zf_ovld); - zend_hash_str_update_mem(CG(function_table), p->orig_func, strlen(p->orig_func), - zf_orig, sizeof(zend_function)); - function_add_ref(zf_orig); - - FIX_FUNCTION_ARG_INFO_DTOR(zf_orig); - zend_hash_str_del(CG(function_table), p->save_func, strlen(p->save_func)); - - p++; - } - return SUCCESS; -} - -static int timecop_class_override_clear() -{ - const struct timecop_override_class_entry *p; - zend_class_entry *ce_orig; - zend_function *zf_orig; - - p = &(timecop_override_class_table[0]); - while (p->orig_class != NULL) { - ce_orig = zend_hash_str_find_ptr(CG(class_table), - p->orig_class, strlen(p->orig_class)); - if (ce_orig == NULL) { - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't find class %s.", p->orig_class); - p++; - continue; - } - - zf_orig = zend_hash_str_find_ptr(&ce_orig->function_table, - p->save_method, strlen(p->save_method)); - if (zf_orig == NULL) { - php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, - "timecop couldn't find method %s::%s.", - p->orig_class, p->save_method); - p++; - continue; - } - - zend_hash_str_update_mem(&ce_orig->function_table, p->orig_method, strlen(p->orig_method), - zf_orig, sizeof(zend_function)); - function_add_ref(zf_orig); - - zend_hash_str_del(&ce_orig->function_table, p->save_method, strlen(p->save_method)); - - if (strcmp(p->orig_method, "__construct") == 0) { - ce_orig->constructor = zf_orig; - } - p++; - } - return SUCCESS; -} - -static int update_request_time(zend_long unixtime) -{ - zval *server_vars, *request_time, tmp; - - server_vars = zend_hash_str_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER")-1); - if (server_vars != NULL && Z_TYPE_P(server_vars) == IS_ARRAY) { - request_time = zend_hash_str_find(Z_ARRVAL_P(server_vars), "REQUEST_TIME", sizeof("REQUEST_TIME")-1); - if (request_time != NULL) { - if (Z_TYPE(TIMECOP_G(orig_request_time)) == IS_NULL) { - ZVAL_COPY_VALUE(&TIMECOP_G(orig_request_time), request_time); - } - } - ZVAL_LONG(&tmp, unixtime); - zend_hash_str_update(Z_ARRVAL_P(server_vars), - "REQUEST_TIME", sizeof("REQUEST_TIME")-1, - &tmp); - } - - return SUCCESS; -} - -static int restore_request_time() -{ - zval *server_vars; - - server_vars = zend_hash_str_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER")-1); - if (Z_TYPE(TIMECOP_G(orig_request_time)) != IS_NULL && - server_vars != NULL && Z_TYPE_P(server_vars) == IS_ARRAY) { - zend_hash_str_update(Z_ARRVAL_P(server_vars), - "REQUEST_TIME", sizeof("REQUEST_TIME")-1, - &TIMECOP_G(orig_request_time)); - ZVAL_NULL(&TIMECOP_G(orig_request_time)); - } - - return SUCCESS; -} - -static int fill_mktime_params(zval *fill_params, const char *date_function_name, int from) -{ - char *formats[MKTIME_NUM_ARGS] = {"H", "i", "s", "n", "j", "Y"}; - zval format, timestamp; - int i; - - ZVAL_LONG(×tamp, mock_timestamp()); - - for (i = from; i < MKTIME_NUM_ARGS; i++) { - ZVAL_STRING(&format, formats[i]); - call_php_function_with_2_params(date_function_name, &fill_params[i], &format, ×tamp); - zval_ptr_dtor(&format); - } - - return MKTIME_NUM_ARGS; -} - -/* - * get_formatted_mock_time() : return formatted mock time like "2000-12-30 01:02:03.456000" - * - * pseudo code: - * - * function get_formatted_mock_time($time, $timezone_obj) { - * if ($time === null || $time === false || $time === "") { - * $time = "now"; - * } - * $now = get_mock_timeval(); - * if ($timezone_obj) { - * // save default timezone - * $zonename = $timezone_obj->getName() - * $orig_zonename = date_default_timezone_get(); - * date_default_timezone_set($zonename); - * } - * $fixed_sec = strtotime($time, $now->sec); - * if ($timezone_obj && $orig_zonename) { - * // restore default timezone - * date_default_timezone_set($orig_zonename); - * } - * if ($fixed_sec === FALSE) { - * return false; - * } - * $fixed_usec = get_mock_fraction($time, $timezone_obj); - * if ($fixed_usec === -1) { - * $fixed_usec = $now->usec; - * } - * $dt = date_create($time, $timezone_obj); - * $dt->setTimestamp($fixed_sec); - * $format = sprintf("Y-m-d H:i:s.%06d", $fixed_usec); - * $formatted_time = $dt->format($format); - * return $formatted_time; - * } - */ -static int get_formatted_mock_time(zval *time, zval *timezone_obj, zval *retval_time, zval *retval_timezone) -{ - zval fixed_sec, orig_zonename; - zval now_timestamp, str_now; - tc_timeval now; - zend_long fixed_usec; - - if (TIMECOP_G(timecop_mode) == TIMECOP_MODE_REALTIME) { - ZVAL_FALSE(retval_time); - ZVAL_NULL(retval_timezone); - return -1; - } - - if (time == NULL || Z_TYPE_P(time) == IS_NULL || - Z_TYPE_P(time) == IS_FALSE || - (Z_TYPE_P(time) == IS_STRING && Z_STRLEN_P(time) == 0)) { - ZVAL_STRING(&str_now, "now"); - time = &str_now; - } - - get_mock_timeval(&now, NULL); - - // @todo Restore removed timezone handling code? https://github.com/kiddivouchers/php-timecop/pull/6 - - ZVAL_LONG(&now_timestamp, now.sec); - call_php_function_with_2_params(ORIG_FUNC_NAME("strtotime"), &fixed_sec, time, &now_timestamp); - - if (Z_TYPE(fixed_sec) == IS_FALSE) { - ZVAL_FALSE(retval_time); - ZVAL_NULL(retval_timezone); - return -1; - } - - fixed_usec = get_mock_fraction(time, timezone_obj); - if (fixed_usec == -1) { - fixed_usec = now.usec; - } - - { - zval dt; - zval format_str; - - char buf[64]; - - call_php_function_with_2_params(ORIG_FUNC_NAME("date_create"), &dt, time, timezone_obj); - if (Z_TYPE(dt) == IS_FALSE) { - ZVAL_FALSE(retval_time); - ZVAL_NULL(retval_timezone); - return -1; - } - sprintf(buf, "Y-m-d H:i:s.%06ld", fixed_usec); - ZVAL_STRING(&format_str, buf); - call_php_method_with_1_params(&dt, TIMECOP_G(ce_DateTime), "settimestamp", NULL, &fixed_sec); - call_php_method_with_0_params(&dt, TIMECOP_G(ce_DateTime), "gettimezone", retval_timezone); - call_php_method_with_1_params(&dt, TIMECOP_G(ce_DateTime), "format", retval_time, &format_str); - zval_ptr_dtor(&fixed_sec); - zval_ptr_dtor(&format_str); - zval_ptr_dtor(&dt); - } - - if (time == &str_now) { - zval_ptr_dtor(&str_now); - } - return 0; -} - -/* - * get_mock_fraction() - * - * pseudo code: - * - * function get_mock_fraction($time, $timezone_obj) { - * $dt1 = date_create($time, $timezone_obj); - * usleep(1); - * $dt2 = date_create($time, $timezone_obj); - * $usec1 = $dt1->format("u"); - * $usec2 = $dt2->format("u"); - * if ($usec1 === $usec2) { - * $fixed_usec = $usec1; - * } else { - * $fixed_usec = -1; - * } - * return $fixed_usec; - * } - */ -static long get_mock_fraction(zval *time, zval *timezone_obj TSRMLS_DC) -{ - zval dt1, dt2, usec1, usec2; - zend_long fixed_usec; - zval u_str, sleep_usec; - - call_php_function_with_2_params(ORIG_FUNC_NAME("date_create"), &dt1, time, timezone_obj); - if (Z_TYPE(dt1) == IS_FALSE) { - return -1; - } - - ZVAL_LONG(&sleep_usec, 1); - call_php_function_with_1_params("usleep", NULL, &sleep_usec); - - call_php_function_with_2_params(ORIG_FUNC_NAME("date_create"), &dt2, time, timezone_obj); - if (Z_TYPE(dt2) == IS_FALSE) { - zval_ptr_dtor(&dt1); - return -1; - } - ZVAL_STRING(&u_str, "u"); - call_php_method_with_1_params(&dt1, TIMECOP_G(ce_DateTime), "format", &usec1, &u_str); - call_php_method_with_1_params(&dt2, TIMECOP_G(ce_DateTime), "format", &usec2, &u_str); - convert_to_long(&usec1); - convert_to_long(&usec2); - - if (Z_LVAL(usec1) == Z_LVAL(usec2)) { - fixed_usec = Z_LVAL(usec1); - } else { - fixed_usec = -1; - } - zval_ptr_dtor(&dt1); - zval_ptr_dtor(&dt2); - zval_ptr_dtor(&u_str); - - return fixed_usec; -} - - -static void _timecop_call_function(INTERNAL_FUNCTION_PARAMETERS, const char *function_name, int index_to_fill_timestamp) -{ - zval *params; - uint32_t param_count; - - param_count = MAX(ZEND_NUM_ARGS(), index_to_fill_timestamp+1); - params = (zval *)safe_emalloc(param_count, sizeof(zval), 0); - - if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), params) == FAILURE) { - efree(params); - return; - } - - param_count = ZEND_NUM_ARGS(); - if (param_count == index_to_fill_timestamp) { - ZVAL_LONG(¶ms[param_count], mock_timestamp()); - param_count++; - } - - _call_php_function_with_params(function_name, return_value, param_count, params); - - efree(params); -} - -/* {{{ _timecop_call_mktime - timecop_(gm)mktime helper */ -static void _timecop_call_mktime(INTERNAL_FUNCTION_PARAMETERS, const char *mktime_function_name, const char *date_function_name) -{ - zval *params; - uint32_t param_count; - - int i; - - param_count = MAX(ZEND_NUM_ARGS(), MKTIME_NUM_ARGS); - params = (zval *)safe_emalloc(param_count, sizeof(zval), 0); - - if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), params) == FAILURE) { - efree(params); - zend_throw_error(NULL, "Cannot get arguments for calling"); - return; - } - - param_count = ZEND_NUM_ARGS(); - if (param_count < MKTIME_NUM_ARGS) { - fill_mktime_params(params, date_function_name, param_count); - param_count = MKTIME_NUM_ARGS; - } - - if (ZEND_NUM_ARGS() == 0) { - php_error_docref(NULL, E_DEPRECATED, "You should be using the time() function instead"); - } - - _call_php_function_with_params(mktime_function_name, return_value, param_count, params); - - for (i = ZEND_NUM_ARGS(); i < MKTIME_NUM_ARGS; i++) { - zval_ptr_dtor(¶ms[i]); - } - efree(params); -} -/* }}} */ - -/* {{{ proto int timecop_freeze(long timestamp) - Time travel to specified timestamp and freeze */ -PHP_FUNCTION(timecop_freeze) -{ - zval *dt; - zend_long timestamp; - tc_timeval freezed_tv; - - if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "O", &dt, TIMECOP_G(ce_DateTimeInterface)) != FAILURE) { - get_timeval_from_datetime(&freezed_tv, dt); - } else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "l", ×tamp) != FAILURE) { - freezed_tv.sec = timestamp; - freezed_tv.usec = 0; - } else { - php_error_docref(NULL, E_WARNING, "This function accepts either (DateTimeInterface) OR (int) as arguments."); - RETURN_FALSE; - } - - TIMECOP_G(timecop_mode) = TIMECOP_MODE_FREEZE; - TIMECOP_G(freezed_time) = freezed_tv; - - if (TIMECOP_G(sync_request_time)){ - update_request_time(freezed_tv.sec); - } - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto int timecop_travel(long timestamp) - Time travel to specified timestamp */ -PHP_FUNCTION(timecop_travel) -{ - zval *dt; - zend_long timestamp; - tc_timeval now, mock_tv; - - if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "O", &dt, TIMECOP_G(ce_DateTimeInterface)) != FAILURE) { - get_timeval_from_datetime(&mock_tv, dt); - } else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "l", ×tamp) != FAILURE) { - mock_tv.sec = timestamp; - mock_tv.usec = 0; - } else { - php_error_docref(NULL, E_WARNING, "This function accepts either (DateTimeInterface) OR (int) as arguments."); - RETURN_FALSE; - } - - TIMECOP_G(timecop_mode) = TIMECOP_MODE_TRAVEL; - get_current_time(&now); - tc_timeval_sub(&TIMECOP_G(travel_offset), &mock_tv, &now); - TIMECOP_G(travel_origin) = now; - - if (TIMECOP_G(sync_request_time)){ - update_request_time(mock_tv.sec); - } - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto int timecop_scale(long scale) - Accelerate time with specified scale */ -PHP_FUNCTION(timecop_scale) -{ - zend_long scale; - tc_timeval now, mock_time; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &scale) == FAILURE) { - RETURN_FALSE; - } - if (scale < 0) { - RETURN_FALSE; - } - get_current_time(&now); - get_mock_timeval(&mock_time, &now); - TIMECOP_G(timecop_mode) = TIMECOP_MODE_TRAVEL; - TIMECOP_G(travel_origin) = now; - tc_timeval_sub(&TIMECOP_G(travel_offset), &mock_time, &now); - TIMECOP_G(scaling_factor) = scale; - - if (TIMECOP_G(sync_request_time)){ - update_request_time(mock_time.sec); - } - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto int timecop_return(void) - Return to Time travel to specified timestamp */ -PHP_FUNCTION(timecop_return) -{ - TIMECOP_G(timecop_mode) = TIMECOP_MODE_REALTIME; - - if (TIMECOP_G(sync_request_time)){ - restore_request_time(); - } - - RETURN_TRUE; -} -/* }}} */ - -/* {{{ proto int timecop_time(void) - Return virtual timestamp */ -PHP_FUNCTION(timecop_time) -{ - RETURN_LONG(mock_timestamp()); -} -/* }}} */ - -/* {{{ proto int timecop_mktime([int hour [, int min [, int sec [, int mon [, int day [, int year]]]]]]) - Get UNIX timestamp for a date */ -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; - - 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(); - - TIMECOP_CALL_MKTIME("mktime", "date"); -} -/* }}} */ - -/* {{{ proto int timecop_gmmktime([int hour [, int min [, int sec [, int mon [, int day [, int year]]]]]]) - Get UNIX timestamp for a GMT date */ -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; - - 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(); - - TIMECOP_CALL_MKTIME("gmmktime", "gmdate"); -} -/* }}} */ - -/* {{{ proto string timecop_date(string format [, long timestamp]) - Format a local date/time */ -PHP_FUNCTION(timecop_date) -{ - zend_string *format; - zend_long ts; - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STR(format) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(ts) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - TIMECOP_CALL_FUNCTION("date", 1); -} -/* }}} */ - -/* {{{ proto string timecop_gmdate(string format [, long timestamp]) - Format a GMT date/time */ -PHP_FUNCTION(timecop_gmdate) -{ - zend_string *format; - zend_long ts; - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STR(format) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(ts) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - TIMECOP_CALL_FUNCTION("gmdate", 1); -} -/* }}} */ - -/* {{{ proto int timecop_idate(string format [, int timestamp]) - Format a local time/date as integer */ -PHP_FUNCTION(timecop_idate) -{ - zend_string *format; - zend_long ts; - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STR(format) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(ts) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - TIMECOP_CALL_FUNCTION("idate", 1); -} -/* }}} */ - -/* {{{ proto array timecop_getdate([int timestamp]) - Get date/time information */ -PHP_FUNCTION(timecop_getdate) -{ - zend_long ts; - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(ts) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - TIMECOP_CALL_FUNCTION("getdate", 0); -} -/* }}} */ - -/* {{{ proto array timecop_localtime([int timestamp [, bool associative_array]]) - Returns the results of the C system call localtime as an associative array if - the associative_array argument is set to 1 other wise it is a regular array */ -PHP_FUNCTION(timecop_localtime) -{ - zend_long timestamp; - zend_bool associative; - ZEND_PARSE_PARAMETERS_START(0, 2) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(timestamp) - Z_PARAM_BOOL(associative) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - TIMECOP_CALL_FUNCTION("localtime", 0); -} -/* }}} */ - -/* {{{ proto int timecop_strtotime(string time [, int now ]) - Convert string representation of date and time to a timestamp */ -PHP_FUNCTION(timecop_strtotime) -{ - zend_string *times; - zend_long preset_ts; - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STR(times) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(preset_ts) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - TIMECOP_CALL_FUNCTION("strtotime", 1); -} -/* }}} */ - -/* - * get_mock_timeval(fixed, now) - * - * - * delta - * |<----------------------->| - * travel_offset delta * scaling_factor - * |<------------->|<------------------------------------------------->| - * ==@===============@=========@=========================================@== - * ^ ^ ^ - * | | | - * travel_origin orig_time traveled_time - * - * - * delta = orig_time - travel_origin - * traveled_time = travel_origin + travel_offset + delta * scaling_factor - */ -static int get_mock_timeval(tc_timeval *fixed, const tc_timeval *now) -{ - if (TIMECOP_G(timecop_mode) == TIMECOP_MODE_FREEZE) { - *fixed = TIMECOP_G(freezed_time); - } else if (TIMECOP_G(timecop_mode) == TIMECOP_MODE_TRAVEL) { - tc_timeval delta, origin = TIMECOP_G(travel_origin); - zend_long scale = TIMECOP_G(scaling_factor); - if (now == NULL) { - get_current_time(&delta); - } else { - delta = *now; - } - tc_timeval_sub(&delta, &delta, &origin); - tc_timeval_mul(&delta, &delta, scale); - tc_timeval_add(fixed, &origin, &TIMECOP_G(travel_offset)); - tc_timeval_add(fixed, fixed, &delta); - } else { - if (now == NULL) { - get_current_time(fixed); - } else { - *fixed = *now; - } - } - return 0; -} - -static zend_long mock_timestamp() -{ - tc_timeval tv; - get_mock_timeval(&tv, NULL); - return tv.sec; -} - -static int get_timeval_from_datetime(tc_timeval *tp, zval *dt) -{ - zval sec, usec; - zval u_str; - - call_php_method_with_0_params(dt, Z_OBJCE_P(dt), "gettimestamp", &sec); - ZVAL_STRING(&u_str, "u"); - call_php_method_with_1_params(dt, Z_OBJCE_P(dt), "format", &usec, &u_str); - zval_ptr_dtor(&u_str); - convert_to_long(&usec); - - tp->sec = Z_LVAL(sec); - tp->usec = Z_LVAL(usec); - - return 0; -} - -static int get_current_time(tc_timeval *now) -{ - int ret = 0; -#if HAVE_GETTIMEOFDAY - struct timeval tv; - ret = gettimeofday(&tv, NULL); - if (ret == 0) { - now->sec = (zend_long)tv.tv_sec; - now->usec = (zend_long)tv.tv_usec; - } -#else - time_t ts = time(NULL); - if (ts == -1) { - ret = -1; - } else { - now->sec = (zend_long)ts; - now->usec = 0; - } -#endif - return ret; -} - -#ifdef HAVE_GETTIMEOFDAY - -#define MICRO_IN_SEC 1000000.00 -#define SEC_IN_MIN 60 - -static void _timecop_gettimeofday(INTERNAL_FUNCTION_PARAMETERS, int mode) -{ - zend_bool get_as_float = 0; - tc_timeval fixed; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &get_as_float) == FAILURE) { - RETURN_FALSE; - } - - if (get_mock_timeval(&fixed, NULL)) { - RETURN_FALSE; - } - - if (get_as_float) { - RETURN_DOUBLE((double)(fixed.sec + fixed.usec / MICRO_IN_SEC)); - } - if (mode) { - zval zv_offset, zv_dst, format, timestamp; - zend_long offset = 0, is_dst = 0; - - ZVAL_LONG(×tamp, fixed.sec); - - /* offset */ - ZVAL_STRING(&format, "Z"); - call_php_function_with_2_params(ORIG_FUNC_NAME("date"), &zv_offset, &format, ×tamp); - convert_to_long(&zv_offset); - offset = Z_LVAL(zv_offset); - zval_ptr_dtor(&zv_offset); - zval_ptr_dtor(&format); - - /* is_dst */ - ZVAL_STRING(&format, "I"); - call_php_function_with_2_params(ORIG_FUNC_NAME("date"), &zv_dst, &format, ×tamp); - convert_to_long(&zv_dst); - is_dst = Z_LVAL(zv_dst); - zval_ptr_dtor(&zv_dst); - zval_ptr_dtor(&format); - - array_init(return_value); - add_assoc_long(return_value, "sec", fixed.sec); - add_assoc_long(return_value, "usec", fixed.usec); - add_assoc_long(return_value, "minuteswest", -offset / SEC_IN_MIN); - add_assoc_long(return_value, "dsttime", is_dst); - } else { - char ret[100]; - snprintf(ret, 100, "%.8F %ld", fixed.usec / MICRO_IN_SEC, fixed.sec); - RETURN_STRING(ret); - } -} - -/* {{{ proto mixed microtime([bool get_as_float]) - Returns either a string or a float containing the current time in seconds and microseconds */ -PHP_FUNCTION(timecop_microtime) -{ - _timecop_gettimeofday(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); -} -/* }}} */ - -/* {{{ proto array gettimeofday([bool get_as_float]) - Returns the current time as array */ -PHP_FUNCTION(timecop_gettimeofday) -{ - _timecop_gettimeofday(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); -} -/* }}} */ -#endif - -/* {{{ proto int timecop_unixtojd([int timestamp]) - Convert UNIX timestamp to Julian Day */ -PHP_FUNCTION(timecop_unixtojd) -{ - TIMECOP_CALL_FUNCTION("unixtojd", 0); -} -/* }}} */ - -static void _timecop_orig_datetime_constructor(INTERNAL_FUNCTION_PARAMETERS, int immutable) -{ - zval *arg1 = NULL, *arg2 = NULL; - zend_class_entry *real_ce; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|zz", &arg1, &arg2) == FAILURE) { - RETURN_FALSE; - } - - if (immutable) { - real_ce = TIMECOP_G(ce_DateTimeImmutable); - } else { - real_ce = TIMECOP_G(ce_DateTime); - } - - call_php_method_with_2_params(getThis(), real_ce, ORIG_FUNC_NAME("__construct"), NULL, arg1, arg2); -} - -static inline void _timecop_datetime_constructor(INTERNAL_FUNCTION_PARAMETERS, int immutable) -{ - _timecop_datetime_constructor_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, getThis(), immutable); -} - -static inline void _timecop_date_create(INTERNAL_FUNCTION_PARAMETERS, int immutable) -{ - _timecop_datetime_constructor_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, NULL, immutable); -} - -static void _timecop_datetime_constructor_ex(INTERNAL_FUNCTION_PARAMETERS, zval *obj, int immutable) -{ - zval orig_time, *orig_timezone = NULL; - zval fixed_time, fixed_timezone, *arg1, *arg2; - char *orig_time_str = NULL; - size_t orig_time_len = 0; - const char *real_func; - zend_class_entry *real_ce; - - ZEND_PARSE_PARAMETERS_START(0, 2) - Z_PARAM_OPTIONAL - Z_PARAM_STRING(orig_time_str, orig_time_len) - Z_PARAM_OBJECT_OF_CLASS_OR_NULL(orig_timezone, TIMECOP_G(ce_DateTimeZone)) - ZEND_PARSE_PARAMETERS_END(); - - ZVAL_STRINGL(&orig_time, orig_time_str, orig_time_len); - - if (immutable) { - real_func = ORIG_FUNC_NAME("date_create_immutable"); - real_ce = TIMECOP_G(ce_DateTimeImmutable); - } else { - real_func = ORIG_FUNC_NAME("date_create"); - real_ce = TIMECOP_G(ce_DateTime); - } - - if (get_formatted_mock_time(&orig_time, orig_timezone, &fixed_time, &fixed_timezone TSRMLS_CC) == 0) { - arg1 = &fixed_time; - arg2 = &fixed_timezone; - } else { - arg1 = &orig_time; - arg2 = orig_timezone; - } - if (obj == NULL) { - call_php_function_with_2_params(real_func, return_value, arg1, arg2); - } else { - call_php_method_with_2_params(obj, real_ce, ORIG_FUNC_NAME("__construct"), NULL, arg1, arg2); - } - zval_ptr_dtor(&orig_time); - zval_ptr_dtor(&fixed_time); - zval_ptr_dtor(&fixed_timezone); -} - -/* {{{ proto DateTime timecop_date_create([string time[, DateTimeZone object]]) - Returns new DateTime object initialized with traveled time */ -PHP_FUNCTION(timecop_date_create) -{ - _timecop_date_create(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); -} -/* }}} */ - -static void _timecop_date_create_from_format(INTERNAL_FUNCTION_PARAMETERS, int immutable) -{ - zval *orig_timezone = NULL; - zval orig_format, orig_time, fixed_format, fixed_time, new_format, new_time; - zval dt, now_timestamp, tmp; - char *orig_format_str, *orig_time_str; - size_t orig_format_len, orig_time_len; - tc_timeval now; - char buf[64]; - const char *real_func; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|O!", &orig_format_str, &orig_format_len, &orig_time_str, &orig_time_len, &orig_timezone, TIMECOP_G(ce_DateTimeZone)) == FAILURE) { - RETURN_FALSE; - } - - ZVAL_STRINGL(&orig_format, orig_format_str, orig_format_len); - ZVAL_STRINGL(&orig_time, orig_time_str, orig_time_len); - - call_php_function_with_3_params(ORIG_FUNC_NAME("date_create_from_format"), &dt, &orig_format, &orig_time, orig_timezone); - if (Z_TYPE(dt) == IS_FALSE) { - RETURN_FALSE; - } - - if (memchr(orig_format_str, '!', orig_format_len) || - memchr(orig_format_str, '|', orig_format_len)) { - - if (immutable) { - call_php_function_with_3_params(ORIG_FUNC_NAME("date_create_immutable_from_format"), &dt, &orig_format, &orig_time, orig_timezone); - } - - zval_ptr_dtor(&orig_format); - zval_ptr_dtor(&orig_time); - RETURN_ZVAL(&dt, 1, 1); - } - - get_mock_timeval(&now, NULL); - - ZVAL_LONG(&now_timestamp, now.sec); - call_php_method_with_1_params(&dt, TIMECOP_G(ce_DateTime), "settimestamp", NULL, &now_timestamp); - sprintf(buf, "Y-m-d H:i:s.%06ld ", now.usec); - ZVAL_STRING(&tmp, buf); - call_php_method_with_1_params(&dt, TIMECOP_G(ce_DateTime), "format", &fixed_time, &tmp); - zval_ptr_dtor(&tmp); - - if (memchr(orig_format_str, 'g', orig_format_len) || - memchr(orig_format_str, 'h', orig_format_len) || - memchr(orig_format_str, 'G', orig_format_len) || - memchr(orig_format_str, 'H', orig_format_len) || - memchr(orig_format_str, 'i', orig_format_len) || - memchr(orig_format_str, 's', orig_format_len)) { - ZVAL_STRING(&fixed_format, "Y-m-d ??:??:??.??????"); - } else if (memchr(orig_format_str, 'Y', orig_format_len) || - memchr(orig_format_str, 'y', orig_format_len) || - memchr(orig_format_str, 'F', orig_format_len) || - memchr(orig_format_str, 'M', orig_format_len) || - memchr(orig_format_str, 'm', orig_format_len) || - memchr(orig_format_str, 'n', orig_format_len) || - memchr(orig_format_str, 'd', orig_format_len) || - memchr(orig_format_str, 'j', orig_format_len) || - memchr(orig_format_str, 'D', orig_format_len) || - memchr(orig_format_str, 'l', orig_format_len) || - memchr(orig_format_str, 'U', orig_format_len)) { - ZVAL_STRING(&fixed_format, "Y-m-d H:i:s.??????"); - } else if (memchr(orig_format_str, 'u', orig_format_len)) { - ZVAL_STRING(&fixed_format, "Y-m-d ??:??:??.??????"); - } else { - ZVAL_STRING(&fixed_format, "Y-m-d H:i:s.u"); - } - - ZVAL_STRING(&tmp, "%s %s"); - call_php_function_with_3_params("sprintf", &new_format, &tmp, &fixed_format, &orig_format); - call_php_function_with_3_params("sprintf", &new_time, &tmp, &fixed_time, &orig_time); - zval_ptr_dtor(&tmp); - - if (immutable) { - real_func = ORIG_FUNC_NAME("date_create_immutable_from_format"); - } else { - real_func = ORIG_FUNC_NAME("date_create_from_format"); - } - call_php_function_with_3_params(real_func, return_value, &new_format, &new_time, orig_timezone); - - zval_ptr_dtor(&dt); - zval_ptr_dtor(&orig_format); - zval_ptr_dtor(&orig_time); - zval_ptr_dtor(&fixed_format); - zval_ptr_dtor(&fixed_time); - zval_ptr_dtor(&new_format); - zval_ptr_dtor(&new_time); -} - -/* {{{ proto DateTime timecop_date_create_from_format(string format, string time[, DateTimeZone object]) - Returns new DateTime object initialized with traveled time */ -PHP_FUNCTION(timecop_date_create_from_format) -{ - _timecop_date_create_from_format(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); -} -/* }}} */ - -/* {{{ proto DateTimeImmutable timecop_date_create_immutable([string time[, DateTimeZone object]]) - Returns new DateTimeImmutable object initialized with traveled time */ -PHP_FUNCTION(timecop_date_create_immutable) -{ - _timecop_date_create(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); -} -/* }}} */ - -/* {{{ proto DateTimeImmutable timecop_date_create_immutable_from_format(string format, string time[, DateTimeZone object]) - Returns new DateTimeImmutable object initialized with traveled time */ -PHP_FUNCTION(timecop_date_create_immutable_from_format) -{ - _timecop_date_create_from_format(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); -} -/* }}} */ - -/* {{{ proto TimecopDateTime::__construct([string time[, DateTimeZone object]]) - Creates new TimecopDateTime object */ -PHP_METHOD(TimecopDateTime, __construct) -{ - _timecop_datetime_constructor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); -} -/* }}} */ - -/* {{{ proto TimecopOrigDateTime::__construct([string time[, DateTimeZone object]]) - Creates new TimecopOrigDateTime object */ -PHP_METHOD(TimecopOrigDateTime, __construct) -{ - _timecop_orig_datetime_constructor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); -} -/* }}} */ - -/* {{{ proto TimecopDateTimeImmutable::__construct([string time[, DateTimeZone object]]) - Creates new TimecopDateTimeImmutable object */ -PHP_METHOD(TimecopDateTimeImmutable, __construct) -{ - _timecop_datetime_constructor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); -} -/* }}} */ - -/* {{{ proto TimecopOrigDateTimeImmutable::__construct([string time[, DateTimeZone object]]) - Creates new TimecopOrigDateTimeImmutable object */ -PHP_METHOD(TimecopOrigDateTimeImmutable, __construct) -{ - _timecop_orig_datetime_constructor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); -} -/* }}} */ - -static inline zval* _call_php_method_with_0_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr) -{ - return _call_php_method(object_pp, obj_ce, method_name, retval_ptr, 0, NULL, NULL); -} - -static inline zval* _call_php_method_with_1_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, zval *arg1) -{ - int nparams = 1; - if (arg1 == NULL) { - nparams = 0; - } - return _call_php_method(object_pp, obj_ce, method_name, retval_ptr, nparams, arg1, NULL); -} - -static inline zval* _call_php_method_with_2_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, zval *arg1, zval *arg2) -{ - int nparams = 2; - if (arg1 == NULL) { - nparams = 0; - } else if (arg2 == NULL) { - nparams = 1; - } - return _call_php_method(object_pp, obj_ce, method_name, retval_ptr, nparams, arg1, arg2); -} - -static inline zval* _call_php_method(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, int param_count, zval* arg1, zval* arg2) -{ - return zend_call_method( - object_pp == NULL ? NULL : Z_OBJ_P(object_pp), - obj_ce, - NULL, - method_name, - strlen(method_name), - retval_ptr, - param_count, - arg1, - arg2 - ); -} - -static inline void _call_php_function_with_3_params(const char *function_name, zval *retval_ptr, zval *arg1, zval *arg2, zval *arg3) -{ - if (arg3 == NULL) { - call_php_function_with_2_params(function_name, retval_ptr, arg1, arg2); - } else { - zval params[3]; - ZVAL_COPY(¶ms[0], arg1); - ZVAL_COPY(¶ms[1], arg2); - ZVAL_COPY(¶ms[2], arg3); - _call_php_function_with_params(function_name, retval_ptr, 3, params); - zval_ptr_dtor(¶ms[0]); - zval_ptr_dtor(¶ms[1]); - zval_ptr_dtor(¶ms[2]); - } -} - -static inline void _call_php_function_with_params(const char *function_name, zval *retval_ptr, uint32_t param_count, zval *params) -{ - zval callable; - ZVAL_STRING(&callable, function_name); - - call_user_function(EG(function_table), NULL, &callable, retval_ptr, param_count, params); - - zval_ptr_dtor(&callable); -} - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: noet sw=4 ts=4 fdm=marker - * vim<600: noet sw=4 ts=4 - */ + #ifdef HAVE_CONFIG_H + #include "config.h" + #endif + + #include "php.h" + #include "php_ini.h" + #include "ext/standard/info.h" + + #include "php_timecop.h" + + #ifdef ZFS + #include "TSRM.h" + #endif + + ZEND_DECLARE_MODULE_GLOBALS(timecop) + + static void timecop_globals_ctor(zend_timecop_globals *globals) { + /* Initialize your global struct */ + globals->func_override = 1; + globals->sync_request_time = 1; + ZVAL_NULL(&globals->orig_request_time); + globals->timecop_mode = TIMECOP_MODE_REALTIME; + globals->freezed_time.sec = 0; + globals->freezed_time.usec = 0; + globals->travel_origin.sec = 0; + globals->travel_origin.usec = 0; + globals->travel_offset.sec = 0; + globals->travel_offset.usec = 0; + globals->scaling_factor = 1; + globals->ce_DateTimeZone = NULL; + globals->ce_DateTimeInterface = NULL; + globals->ce_DateTime = NULL; + globals->ce_TimecopDateTime = NULL; + globals->ce_DateTimeImmutable = NULL; + globals->ce_TimecopDateTimeImmutable = NULL; + } + + static const struct timecop_override_func_entry timecop_override_func_table[] = { + TIMECOP_OFE("time"), + TIMECOP_OFE("mktime"), + TIMECOP_OFE("gmmktime"), + TIMECOP_OFE("date"), + TIMECOP_OFE("gmdate"), + TIMECOP_OFE("idate"), + TIMECOP_OFE("getdate"), + TIMECOP_OFE("localtime"), + TIMECOP_OFE("strtotime"), + #ifdef HAVE_GETTIMEOFDAY + TIMECOP_OFE("microtime"), + TIMECOP_OFE("gettimeofday"), + #endif + TIMECOP_OFE("unixtojd"), + TIMECOP_OFE("date_create"), + TIMECOP_OFE("date_create_from_format"), + TIMECOP_OFE("date_create_immutable"), + TIMECOP_OFE("date_create_immutable_from_format"), + {NULL, NULL, NULL} + }; + + static const struct timecop_override_class_entry timecop_override_class_table[] = { + TIMECOP_OCE("datetime", "__construct"), + TIMECOP_OCE("datetime", "createfromformat"), + TIMECOP_OCE("datetimeimmutable", "__construct"), + TIMECOP_OCE("datetimeimmutable", "createfromformat"), + {NULL, NULL, NULL, NULL} + }; + + #include "timecop_php8_arginfo.h" + + /* {{{ timecop_functions[] */ + const zend_function_entry timecop_functions[] = { + PHP_FE(timecop_freeze, arginfo_timecop_freeze) + PHP_FE(timecop_travel, arginfo_timecop_travel) + PHP_FE(timecop_scale, arginfo_timecop_scale) + PHP_FE(timecop_return, arginfo_timecop_return) + PHP_FE(timecop_time, arginfo_timecop_time) + PHP_FE(timecop_mktime, arginfo_timecop_mktime) + PHP_FE(timecop_gmmktime, arginfo_timecop_gmmktime) + PHP_FE(timecop_date, arginfo_timecop_date) + PHP_FE(timecop_gmdate, arginfo_timecop_gmdate) + PHP_FE(timecop_idate, arginfo_timecop_idate) + PHP_FE(timecop_getdate, arginfo_timecop_getdate) + PHP_FE(timecop_localtime, arginfo_timecop_localtime) + PHP_FE(timecop_strtotime, arginfo_timecop_strtotime) + #ifdef HAVE_GETTIMEOFDAY + PHP_FE(timecop_microtime, arginfo_timecop_microtime) + PHP_FE(timecop_gettimeofday, arginfo_timecop_gettimeofday) + #endif + PHP_FE(timecop_unixtojd, arginfo_timecop_unixtojd) + PHP_FE(timecop_date_create, arginfo_timecop_date_create) + PHP_FE(timecop_date_create_from_format, arginfo_timecop_date_create_from_format) + PHP_FE(timecop_date_create_immutable, arginfo_timecop_date_create_immutable) + PHP_FE(timecop_date_create_immutable_from_format, arginfo_timecop_date_create_immutable_from_format) + {NULL, NULL, NULL} + }; + /* }}} */ + + /* declare method parameters, */ + + /* each method can have its own parameters and visibility */ + static zend_function_entry timecop_funcs_timecop[] = { + PHP_ME_MAPPING(freeze, timecop_freeze, arginfo_timecop_travel, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + PHP_ME_MAPPING(travel, timecop_travel, arginfo_timecop_travel, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + PHP_ME_MAPPING(scale, timecop_scale, arginfo_timecop_scale, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + PHP_ME_MAPPING(return, timecop_return, arginfo_timecop_return, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + {NULL, NULL, NULL} + }; + + 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_class_TimecopDateTime_createFromFormat, + ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + {NULL, NULL, NULL} + }; + + static zend_function_entry timecop_funcs_orig_date[] = { + PHP_ME(TimecopOrigDateTime, __construct, arginfo_class_TimecopDateTime___construct, + ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) + {NULL, NULL, NULL} + }; + + 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_class_TimecopDateTimeImmutable_createFromFormat, + ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + {NULL, NULL, NULL} + }; + + static zend_function_entry timecop_funcs_orig_immutable[] = { + PHP_ME(TimecopOrigDateTimeImmutable, __construct, arginfo_class_TimecopDateTimeImmutable___construct, + ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) + {NULL, NULL, NULL} + }; + + #define MKTIME_NUM_ARGS 6 + + #define TIMECOP_CALL_FUNCTION(func_name, index_to_fill_timestamp) \ + _timecop_call_function(INTERNAL_FUNCTION_PARAM_PASSTHRU, ORIG_FUNC_NAME(func_name), index_to_fill_timestamp); + + #define TIMECOP_CALL_MKTIME(mktime_func_name, date_func_name) \ + _timecop_call_mktime(INTERNAL_FUNCTION_PARAM_PASSTHRU, ORIG_FUNC_NAME(mktime_func_name), ORIG_FUNC_NAME(date_func_name)); + + static void timecop_globals_ctor(zend_timecop_globals *globals); + + static int register_timecop_classes(); + static int timecop_func_override(); + static int timecop_class_override(); + static int timecop_func_override_clear(); + static int timecop_class_override_clear(); + + static int update_request_time(zend_long unixtime); + static int restore_request_time(); + + static int fill_mktime_params(zval *fill_params, const char *date_function_name, int from); + static int get_formatted_mock_time(zval *time, zval *timezone_obj, zval *retval_time, zval *retval_timezone); + static long get_mock_fraction(zval *time, zval *timezone_obj); + + static void _timecop_call_function(INTERNAL_FUNCTION_PARAMETERS, const char *function_name, int index_to_fill_timestamp); + static void _timecop_call_mktime(INTERNAL_FUNCTION_PARAMETERS, const char *mktime_function_name, const char *date_function_name); + + static int get_mock_timeval(tc_timeval *fixed, const tc_timeval *now); + static inline zend_long mock_timestamp(); + + static int get_timeval_from_datetime(tc_timeval *tp, zval *dt); + static int get_current_time(tc_timeval *now); + + static void _timecop_orig_datetime_constructor(INTERNAL_FUNCTION_PARAMETERS, int immutable); + static inline void _timecop_date_create(INTERNAL_FUNCTION_PARAMETERS, int immutable); + static inline void _timecop_datetime_constructor(INTERNAL_FUNCTION_PARAMETERS, int immutable); + static void _timecop_datetime_constructor_ex(INTERNAL_FUNCTION_PARAMETERS, zval *obj, int immutable); + + static inline zval* _call_php_method_with_0_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr); + static inline zval* _call_php_method_with_1_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, zval *arg1); + static inline zval* _call_php_method_with_2_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, zval *arg1, zval *arg2); + static inline zval* _call_php_method(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, int param_count, zval* arg1, zval* arg2); + static void _call_php_function_with_3_params(const char *function_name, zval *retval_ptr, zval *arg1, zval *arg2, zval *arg3); + static inline void _call_php_function_with_params(const char *function_name, zval *retval_ptr, uint32_t param_count, zval params[]); + + static const zend_module_dep timecop_module_deps[] = { + ZEND_MOD_REQUIRED("Date") + ZEND_MOD_END + }; + + /* {{{ timecop_module_entry + */ + zend_module_entry timecop_module_entry = { + STANDARD_MODULE_HEADER_EX, + NULL, + timecop_module_deps, + "timecop", + timecop_functions, + PHP_MINIT(timecop), + PHP_MSHUTDOWN(timecop), + PHP_RINIT(timecop), + PHP_RSHUTDOWN(timecop), + PHP_MINFO(timecop), + PHP_TIMECOP_VERSION, + STANDARD_MODULE_PROPERTIES + }; + /* }}} */ + + #ifdef COMPILE_DL_TIMECOP + # ifdef ZTS + ZEND_TSRMLS_CACHE_DEFINE(); + # endif + ZEND_GET_MODULE(timecop) + #endif + + /* {{{ PHP_INI + */ + PHP_INI_BEGIN() + STD_PHP_INI_ENTRY("timecop.func_override", "1", + PHP_INI_SYSTEM, OnUpdateLong, func_override, zend_timecop_globals, timecop_globals) + STD_PHP_INI_ENTRY("timecop.sync_request_time", "1", + PHP_INI_SYSTEM, OnUpdateLong, sync_request_time, zend_timecop_globals, timecop_globals) + PHP_INI_END() + /* }}} */ + + /* {{{ PHP_MINIT_FUNCTION + */ + PHP_MINIT_FUNCTION(timecop) + { + ZEND_INIT_MODULE_GLOBALS(timecop, timecop_globals_ctor, NULL); + REGISTER_INI_ENTRIES(); + register_timecop_classes(); + + return SUCCESS; + } + /* }}} */ + + /* {{{ PHP_MSHUTDOWN_FUNCTION + */ + PHP_MSHUTDOWN_FUNCTION(timecop) + { + UNREGISTER_INI_ENTRIES(); + + return SUCCESS; + } + /* }}} */ + + /* {{{ PHP_RINIT_FUNCTION(timecop) */ + PHP_RINIT_FUNCTION(timecop) + { + #if defined(COMPILE_DL_TIMECOP) && defined(ZTS) + ZEND_TSRMLS_CACHE_UPDATE(); + #endif + + if (TIMECOP_G(func_override)) { + if (SUCCESS != timecop_func_override() || + SUCCESS != timecop_class_override()) { + return FAILURE; + } + } + + return SUCCESS; + } + /* }}} */ + + /* {{{ PHP_RSHUTDOWN_FUNCTION(timecop) */ + PHP_RSHUTDOWN_FUNCTION(timecop) + { + if (TIMECOP_G(func_override)) { + timecop_func_override_clear(); + timecop_class_override_clear(); + } + + if (Z_TYPE(TIMECOP_G(orig_request_time)) != IS_NULL) { + restore_request_time(); + } + + TIMECOP_G(timecop_mode) = TIMECOP_MODE_REALTIME; + TIMECOP_G(scaling_factor) = 1; + + return SUCCESS; + } + /* }}} */ + + + /* {{{ PHP_MINFO_FUNCTION + */ + PHP_MINFO_FUNCTION(timecop) + { + php_info_print_table_start(); + php_info_print_table_header(2, "timecop", "enabled"); + php_info_print_table_row(2, "Version", PHP_TIMECOP_VERSION); + php_info_print_table_end(); + + DISPLAY_INI_ENTRIES(); + } + /* }}} */ + + static int register_timecop_classes() + { + zend_class_entry ce; + zend_class_entry *tmp, *date_ce, *timezone_ce, *immutable_ce, *interface_ce; + + date_ce = zend_hash_str_find_ptr(CG(class_table), "datetime", sizeof("datetime")-1); + if (date_ce == NULL) { + /* DateTime must be initialized before */ + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't find class %s.", "DateTime"); + return SUCCESS; + } + + timezone_ce = zend_hash_str_find_ptr(CG(class_table), "datetimezone", sizeof("datetimezone")-1); + if (timezone_ce == NULL) { + /* DateTime must be initialized before */ + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't find class %s.", "DateTimeZone"); + return SUCCESS; + } + + immutable_ce = zend_hash_str_find_ptr(CG(class_table), "datetimeimmutable", sizeof("datetimeimmutable")-1); + if (immutable_ce == NULL) { + /* DateTimeImmutable must be initialized before */ + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't find class %s.", "DateTimeImmutable"); + return SUCCESS; + } + + interface_ce = zend_hash_str_find_ptr(CG(class_table), "datetimeinterface", sizeof("datetimeinterface")-1); + if (interface_ce == NULL) { + /* DateTimeInterface must be initialized before */ + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't find interface %s.", "DateTimeInterface"); + return SUCCESS; + } + + INIT_CLASS_ENTRY(ce, "Timecop", timecop_funcs_timecop); + zend_register_internal_class(&ce); + + TIMECOP_G(ce_DateTimeZone) = timezone_ce; + TIMECOP_G(ce_DateTimeInterface) = interface_ce; + + /* replace DateTime */ + INIT_CLASS_ENTRY(ce, "TimecopDateTime", timecop_funcs_date); + tmp = zend_register_internal_class_ex(&ce, date_ce); + tmp->create_object = date_ce->create_object; + + TIMECOP_G(ce_DateTime) = date_ce; + TIMECOP_G(ce_TimecopDateTime) = tmp; + + INIT_CLASS_ENTRY(ce, "TimecopOrigDateTime", timecop_funcs_orig_date); + tmp = zend_register_internal_class_ex(&ce, date_ce); + tmp->create_object = date_ce->create_object; + + /* replace DateTimeImmutable */ + INIT_CLASS_ENTRY(ce, "TimecopDateTimeImmutable", timecop_funcs_immutable); + tmp = zend_register_internal_class_ex(&ce, immutable_ce); + tmp->create_object = immutable_ce->create_object; + + TIMECOP_G(ce_DateTimeImmutable) = immutable_ce; + TIMECOP_G(ce_TimecopDateTimeImmutable) = tmp; + + INIT_CLASS_ENTRY(ce, "TimecopOrigDateTimeImmutable", timecop_funcs_orig_immutable); + tmp = zend_register_internal_class_ex(&ce, immutable_ce); + tmp->create_object = immutable_ce->create_object; + + return SUCCESS; + } + + static int timecop_func_override() + { + const struct timecop_override_func_entry *p; + zend_function *zf_orig, *zf_ovrd, *zf_save; + + p = &(timecop_override_func_table[0]); + while (p->orig_func != NULL) { + zf_orig = zend_hash_str_find_ptr(CG(function_table), p->orig_func, strlen(p->orig_func)); + if (zf_orig == NULL) { + /* Do nothing. Because some functions are introduced by optional extensions. */ + p++; + continue; + } + + zf_ovrd = zend_hash_str_find_ptr(CG(function_table), p->ovrd_func, strlen(p->ovrd_func)); + if (zf_ovrd == NULL) { + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't find function %s.", p->ovrd_func); + p++; + continue; + } + + zf_save = zend_hash_str_find_ptr(CG(function_table), p->save_func, strlen(p->save_func)); + if (zf_save != NULL) { + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't create function %s because already exists.", + p->save_func); + p++; + continue; + } + + TIMECOP_ASSERT(zf_orig->type == ZEND_INTERNAL_FUNCTION); + TIMECOP_ASSERT(zf_ovrd->type == ZEND_INTERNAL_FUNCTION); + + zend_hash_str_add_mem(CG(function_table), p->save_func, strlen(p->save_func), + zf_orig, sizeof(zend_function)); + function_add_ref(zf_orig); + + FIX_FUNCTION_ARG_INFO_DTOR(zf_orig); + zend_hash_str_update_mem(CG(function_table), p->orig_func, strlen(p->orig_func), + zf_ovrd, sizeof(zend_function)); + function_add_ref(zf_ovrd); + + p++; + } + return SUCCESS; + } + + static int timecop_class_override() + { + const struct timecop_override_class_entry *p; + zend_class_entry *ce_orig, *ce_ovrd; + zend_function *zf_orig, *zf_ovrd, *zf_save, *zf_new; + + p = &(timecop_override_class_table[0]); + while (p->orig_class != NULL) { + ce_orig = zend_hash_str_find_ptr(CG(class_table), p->orig_class, strlen(p->orig_class)); + if (ce_orig == NULL) { + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't find class %s.", p->orig_class); + p++; + continue; + } + + ce_ovrd = zend_hash_str_find_ptr(CG(class_table), p->ovrd_class, strlen(p->ovrd_class)); + if (ce_ovrd == NULL) { + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't find class %s.", p->ovrd_class); + p++; + continue; + } + + zf_orig = zend_hash_str_find_ptr(&ce_orig->function_table, + p->orig_method, strlen(p->orig_method)); + if (zf_orig == NULL) { + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't find method %s::%s.", + p->orig_class, p->orig_method); + p++; + continue; + } + + zf_ovrd = zend_hash_str_find_ptr(&ce_ovrd->function_table, + p->orig_method, strlen(p->orig_method)); + if (zf_ovrd == NULL) { + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't find method %s::%s.", + p->ovrd_class, p->orig_method); + p++; + continue; + } + + zf_save = zend_hash_str_find_ptr(&ce_orig->function_table, + p->save_method, strlen(p->save_method)); + if (zf_save != NULL) { + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't create method %s::%s because already exists.", + p->orig_class, p->save_method); + p++; + continue; + } + + TIMECOP_ASSERT(zf_orig->type == ZEND_INTERNAL_FUNCTION); + TIMECOP_ASSERT(ce_orig->type & ZEND_INTERNAL_CLASS); + TIMECOP_ASSERT(zf_ovrd->type == ZEND_INTERNAL_FUNCTION); + TIMECOP_ASSERT(ce_ovrd->type & ZEND_INTERNAL_CLASS); + + zend_hash_str_add_mem(&ce_orig->function_table, + p->save_method, strlen(p->save_method), + zf_orig, sizeof(zend_function)); + function_add_ref(zf_orig); + + zf_new = zend_hash_str_update_mem(&ce_orig->function_table, + p->orig_method, strlen(p->orig_method), + zf_ovrd, sizeof(zend_function)); + function_add_ref(zf_ovrd); + + TIMECOP_ASSERT(zf_new != NULL); + TIMECOP_ASSERT(zf_new != zf_orig); + + if (strcmp(p->orig_method, "__construct") == 0) { + ce_orig->constructor = zf_new; + } + p++; + } + return SUCCESS; + } + + /* clear function overriding. */ + static int timecop_func_override_clear() + { + const struct timecop_override_func_entry *p; + zend_function *zf_orig, *zf_ovld; + + p = &(timecop_override_func_table[0]); + while (p->orig_func != NULL) { + zf_orig = zend_hash_str_find_ptr(CG(function_table), + p->save_func, strlen(p->save_func)); + zf_ovld = zend_hash_str_find_ptr(CG(function_table), + p->orig_func, strlen(p->orig_func)); + if (zf_orig == NULL || zf_ovld == NULL) { + p++; + continue; + } + + FIX_FUNCTION_ARG_INFO_DTOR(zf_ovld); + zend_hash_str_update_mem(CG(function_table), p->orig_func, strlen(p->orig_func), + zf_orig, sizeof(zend_function)); + function_add_ref(zf_orig); + + FIX_FUNCTION_ARG_INFO_DTOR(zf_orig); + zend_hash_str_del(CG(function_table), p->save_func, strlen(p->save_func)); + + p++; + } + return SUCCESS; + } + + static int timecop_class_override_clear() + { + const struct timecop_override_class_entry *p; + zend_class_entry *ce_orig; + zend_function *zf_orig; + + p = &(timecop_override_class_table[0]); + while (p->orig_class != NULL) { + ce_orig = zend_hash_str_find_ptr(CG(class_table), + p->orig_class, strlen(p->orig_class)); + if (ce_orig == NULL) { + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't find class %s.", p->orig_class); + p++; + continue; + } + + zf_orig = zend_hash_str_find_ptr(&ce_orig->function_table, + p->save_method, strlen(p->save_method)); + if (zf_orig == NULL) { + php_error_docref("https://github.com/hnw/php-timecop", E_WARNING, + "timecop couldn't find method %s::%s.", + p->orig_class, p->save_method); + p++; + continue; + } + + zend_hash_str_update_mem(&ce_orig->function_table, p->orig_method, strlen(p->orig_method), + zf_orig, sizeof(zend_function)); + function_add_ref(zf_orig); + + zend_hash_str_del(&ce_orig->function_table, p->save_method, strlen(p->save_method)); + + if (strcmp(p->orig_method, "__construct") == 0) { + ce_orig->constructor = zf_orig; + } + p++; + } + return SUCCESS; + } + + static int update_request_time(zend_long unixtime) + { + zval *server_vars, *request_time, tmp; + + server_vars = zend_hash_str_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER")-1); + if (server_vars != NULL && Z_TYPE_P(server_vars) == IS_ARRAY) { + request_time = zend_hash_str_find(Z_ARRVAL_P(server_vars), "REQUEST_TIME", sizeof("REQUEST_TIME")-1); + if (request_time != NULL) { + if (Z_TYPE(TIMECOP_G(orig_request_time)) == IS_NULL) { + ZVAL_COPY_VALUE(&TIMECOP_G(orig_request_time), request_time); + } + } + ZVAL_LONG(&tmp, unixtime); + zend_hash_str_update(Z_ARRVAL_P(server_vars), + "REQUEST_TIME", sizeof("REQUEST_TIME")-1, + &tmp); + } + + return SUCCESS; + } + + static int restore_request_time() + { + zval *server_vars; + + server_vars = zend_hash_str_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER")-1); + if (Z_TYPE(TIMECOP_G(orig_request_time)) != IS_NULL && + server_vars != NULL && Z_TYPE_P(server_vars) == IS_ARRAY) { + zend_hash_str_update(Z_ARRVAL_P(server_vars), + "REQUEST_TIME", sizeof("REQUEST_TIME")-1, + &TIMECOP_G(orig_request_time)); + ZVAL_NULL(&TIMECOP_G(orig_request_time)); + } + + return SUCCESS; + } + + static int fill_mktime_params(zval *fill_params, const char *date_function_name, int from) + { + char *formats[MKTIME_NUM_ARGS] = {"H", "i", "s", "n", "j", "Y"}; + zval format, timestamp; + int i; + + ZVAL_LONG(×tamp, mock_timestamp()); + + for (i = from; i < MKTIME_NUM_ARGS; i++) { + ZVAL_STRING(&format, formats[i]); + call_php_function_with_2_params(date_function_name, &fill_params[i], &format, ×tamp); + zval_ptr_dtor(&format); + } + + return MKTIME_NUM_ARGS; + } + + /* + * get_formatted_mock_time() : return formatted mock time like "2000-12-30 01:02:03.456000" + * + * pseudo code: + * + * function get_formatted_mock_time($time, $timezone_obj) { + * if ($time === null || $time === false || $time === "") { + * $time = "now"; + * } + * $now = get_mock_timeval(); + * if ($timezone_obj) { + * // save default timezone + * $zonename = $timezone_obj->getName() + * $orig_zonename = date_default_timezone_get(); + * date_default_timezone_set($zonename); + * } + * $fixed_sec = strtotime($time, $now->sec); + * if ($timezone_obj && $orig_zonename) { + * // restore default timezone + * date_default_timezone_set($orig_zonename); + * } + * if ($fixed_sec === FALSE) { + * return false; + * } + * $fixed_usec = get_mock_fraction($time, $timezone_obj); + * if ($fixed_usec === -1) { + * $fixed_usec = $now->usec; + * } + * $dt = date_create($time, $timezone_obj); + * $dt->setTimestamp($fixed_sec); + * $format = sprintf("Y-m-d H:i:s.%06d", $fixed_usec); + * $formatted_time = $dt->format($format); + * return $formatted_time; + * } + */ + static int get_formatted_mock_time(zval *time, zval *timezone_obj, zval *retval_time, zval *retval_timezone) + { + zval fixed_sec, orig_zonename; + zval now_timestamp, str_now; + tc_timeval now; + zend_long fixed_usec; + + if (TIMECOP_G(timecop_mode) == TIMECOP_MODE_REALTIME) { + ZVAL_FALSE(retval_time); + ZVAL_NULL(retval_timezone); + return -1; + } + + if (time == NULL || Z_TYPE_P(time) == IS_NULL || + Z_TYPE_P(time) == IS_FALSE || + (Z_TYPE_P(time) == IS_STRING && Z_STRLEN_P(time) == 0)) { + ZVAL_STRING(&str_now, "now"); + time = &str_now; + } + + get_mock_timeval(&now, NULL); + + // @todo Restore removed timezone handling code? https://github.com/kiddivouchers/php-timecop/pull/6 + + ZVAL_LONG(&now_timestamp, now.sec); + call_php_function_with_2_params(ORIG_FUNC_NAME("strtotime"), &fixed_sec, time, &now_timestamp); + + if (Z_TYPE(fixed_sec) == IS_FALSE) { + ZVAL_FALSE(retval_time); + ZVAL_NULL(retval_timezone); + return -1; + } + + fixed_usec = get_mock_fraction(time, timezone_obj); + if (fixed_usec == -1) { + fixed_usec = now.usec; + } + + { + zval dt; + zval format_str; + + char buf[64]; + + call_php_function_with_2_params(ORIG_FUNC_NAME("date_create"), &dt, time, timezone_obj); + if (Z_TYPE(dt) == IS_FALSE) { + ZVAL_FALSE(retval_time); + ZVAL_NULL(retval_timezone); + return -1; + } + sprintf(buf, "Y-m-d H:i:s.%06ld", fixed_usec); + ZVAL_STRING(&format_str, buf); + call_php_method_with_1_params(&dt, TIMECOP_G(ce_DateTime), "settimestamp", NULL, &fixed_sec); + call_php_method_with_0_params(&dt, TIMECOP_G(ce_DateTime), "gettimezone", retval_timezone); + call_php_method_with_1_params(&dt, TIMECOP_G(ce_DateTime), "format", retval_time, &format_str); + zval_ptr_dtor(&fixed_sec); + zval_ptr_dtor(&format_str); + zval_ptr_dtor(&dt); + } + + if (time == &str_now) { + zval_ptr_dtor(&str_now); + } + return 0; + } + + /* + * get_mock_fraction() + * + * pseudo code: + * + * function get_mock_fraction($time, $timezone_obj) { + * $dt1 = date_create($time, $timezone_obj); + * usleep(1); + * $dt2 = date_create($time, $timezone_obj); + * $usec1 = $dt1->format("u"); + * $usec2 = $dt2->format("u"); + * if ($usec1 === $usec2) { + * $fixed_usec = $usec1; + * } else { + * $fixed_usec = -1; + * } + * return $fixed_usec; + * } + */ + static long get_mock_fraction(zval *time, zval *timezone_obj TSRMLS_DC) + { + zval dt1, dt2, usec1, usec2; + zend_long fixed_usec; + zval u_str, sleep_usec; + + call_php_function_with_2_params(ORIG_FUNC_NAME("date_create"), &dt1, time, timezone_obj); + if (Z_TYPE(dt1) == IS_FALSE) { + return -1; + } + + ZVAL_LONG(&sleep_usec, 1); + call_php_function_with_1_params("usleep", NULL, &sleep_usec); + + call_php_function_with_2_params(ORIG_FUNC_NAME("date_create"), &dt2, time, timezone_obj); + if (Z_TYPE(dt2) == IS_FALSE) { + zval_ptr_dtor(&dt1); + return -1; + } + ZVAL_STRING(&u_str, "u"); + call_php_method_with_1_params(&dt1, TIMECOP_G(ce_DateTime), "format", &usec1, &u_str); + call_php_method_with_1_params(&dt2, TIMECOP_G(ce_DateTime), "format", &usec2, &u_str); + convert_to_long(&usec1); + convert_to_long(&usec2); + + if (Z_LVAL(usec1) == Z_LVAL(usec2)) { + fixed_usec = Z_LVAL(usec1); + } else { + fixed_usec = -1; + } + zval_ptr_dtor(&dt1); + zval_ptr_dtor(&dt2); + zval_ptr_dtor(&u_str); + + return fixed_usec; + } + + + static void _timecop_call_function(INTERNAL_FUNCTION_PARAMETERS, const char *function_name, int index_to_fill_timestamp) + { + zval *params; + uint32_t param_count; + + param_count = MAX(ZEND_NUM_ARGS(), index_to_fill_timestamp+1); + params = (zval *)safe_emalloc(param_count, sizeof(zval), 0); + + if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), params) == FAILURE) { + efree(params); + return; + } + + param_count = ZEND_NUM_ARGS(); + if (param_count == index_to_fill_timestamp) { + ZVAL_LONG(¶ms[param_count], mock_timestamp()); + param_count++; + } + + _call_php_function_with_params(function_name, return_value, param_count, params); + + efree(params); + } + + /* {{{ _timecop_call_mktime - timecop_(gm)mktime helper */ + static void _timecop_call_mktime(INTERNAL_FUNCTION_PARAMETERS, const char *mktime_function_name, const char *date_function_name) + { + zval *params; + uint32_t param_count; + + int i; + + param_count = MAX(ZEND_NUM_ARGS(), MKTIME_NUM_ARGS); + params = (zval *)safe_emalloc(param_count, sizeof(zval), 0); + + if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), params) == FAILURE) { + efree(params); + zend_throw_error(NULL, "Cannot get arguments for calling"); + return; + } + + param_count = ZEND_NUM_ARGS(); + if (param_count < MKTIME_NUM_ARGS) { + fill_mktime_params(params, date_function_name, param_count); + param_count = MKTIME_NUM_ARGS; + } + + if (ZEND_NUM_ARGS() == 0) { + php_error_docref(NULL, E_DEPRECATED, "You should be using the time() function instead"); + } + + _call_php_function_with_params(mktime_function_name, return_value, param_count, params); + + for (i = ZEND_NUM_ARGS(); i < MKTIME_NUM_ARGS; i++) { + zval_ptr_dtor(¶ms[i]); + } + efree(params); + } + /* }}} */ + + /* {{{ proto int timecop_freeze(long timestamp) + Time travel to specified timestamp and freeze */ + PHP_FUNCTION(timecop_freeze) + { + zval *dt; + zend_long timestamp; + tc_timeval freezed_tv; + + if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "O", &dt, TIMECOP_G(ce_DateTimeInterface)) != FAILURE) { + get_timeval_from_datetime(&freezed_tv, dt); + } else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "l", ×tamp) != FAILURE) { + freezed_tv.sec = timestamp; + freezed_tv.usec = 0; + } else { + php_error_docref(NULL, E_WARNING, "This function accepts either (DateTimeInterface) OR (int) as arguments."); + RETURN_FALSE; + } + + TIMECOP_G(timecop_mode) = TIMECOP_MODE_FREEZE; + TIMECOP_G(freezed_time) = freezed_tv; + + if (TIMECOP_G(sync_request_time)){ + update_request_time(freezed_tv.sec); + } + + RETURN_TRUE; + } + /* }}} */ + + /* {{{ proto int timecop_travel(long timestamp) + Time travel to specified timestamp */ + PHP_FUNCTION(timecop_travel) + { + zval *dt; + zend_long timestamp; + tc_timeval now, mock_tv; + + if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "O", &dt, TIMECOP_G(ce_DateTimeInterface)) != FAILURE) { + get_timeval_from_datetime(&mock_tv, dt); + } else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "l", ×tamp) != FAILURE) { + mock_tv.sec = timestamp; + mock_tv.usec = 0; + } else { + php_error_docref(NULL, E_WARNING, "This function accepts either (DateTimeInterface) OR (int) as arguments."); + RETURN_FALSE; + } + + TIMECOP_G(timecop_mode) = TIMECOP_MODE_TRAVEL; + get_current_time(&now); + tc_timeval_sub(&TIMECOP_G(travel_offset), &mock_tv, &now); + TIMECOP_G(travel_origin) = now; + + if (TIMECOP_G(sync_request_time)){ + update_request_time(mock_tv.sec); + } + + RETURN_TRUE; + } + /* }}} */ + + /* {{{ proto int timecop_scale(long scale) + Accelerate time with specified scale */ + PHP_FUNCTION(timecop_scale) + { + zend_long scale; + tc_timeval now, mock_time; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &scale) == FAILURE) { + RETURN_FALSE; + } + if (scale < 0) { + RETURN_FALSE; + } + get_current_time(&now); + get_mock_timeval(&mock_time, &now); + TIMECOP_G(timecop_mode) = TIMECOP_MODE_TRAVEL; + TIMECOP_G(travel_origin) = now; + tc_timeval_sub(&TIMECOP_G(travel_offset), &mock_time, &now); + TIMECOP_G(scaling_factor) = scale; + + if (TIMECOP_G(sync_request_time)){ + update_request_time(mock_time.sec); + } + + RETURN_TRUE; + } + /* }}} */ + + /* {{{ proto int timecop_return(void) + Return to Time travel to specified timestamp */ + PHP_FUNCTION(timecop_return) + { + TIMECOP_G(timecop_mode) = TIMECOP_MODE_REALTIME; + + if (TIMECOP_G(sync_request_time)){ + restore_request_time(); + } + + RETURN_TRUE; + } + /* }}} */ + + /* {{{ proto int timecop_time(void) + Return virtual timestamp */ + PHP_FUNCTION(timecop_time) + { + RETURN_LONG(mock_timestamp()); + } + /* }}} */ + + /* {{{ proto int timecop_mktime([int hour [, int min [, int sec [, int mon [, int day [, int year]]]]]]) + Get UNIX timestamp for a date */ + 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; + + 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(); + + TIMECOP_CALL_MKTIME("mktime", "date"); + } + /* }}} */ + + /* {{{ proto int timecop_gmmktime([int hour [, int min [, int sec [, int mon [, int day [, int year]]]]]]) + Get UNIX timestamp for a GMT date */ + 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; + + 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(); + + TIMECOP_CALL_MKTIME("gmmktime", "gmdate"); + } + /* }}} */ + + /* {{{ proto string timecop_date(string format [, long timestamp]) + Format a local date/time */ + PHP_FUNCTION(timecop_date) + { + zend_string *format; + zend_long ts; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(format) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(ts) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + TIMECOP_CALL_FUNCTION("date", 1); + } + /* }}} */ + + /* {{{ proto string timecop_gmdate(string format [, long timestamp]) + Format a GMT date/time */ + PHP_FUNCTION(timecop_gmdate) + { + zend_string *format; + zend_long ts; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(format) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(ts) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + TIMECOP_CALL_FUNCTION("gmdate", 1); + } + /* }}} */ + + /* {{{ proto int timecop_idate(string format [, int timestamp]) + Format a local time/date as integer */ + PHP_FUNCTION(timecop_idate) + { + zend_string *format; + zend_long ts; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(format) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(ts) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + TIMECOP_CALL_FUNCTION("idate", 1); + } + /* }}} */ + + /* {{{ proto array timecop_getdate([int timestamp]) + Get date/time information */ + PHP_FUNCTION(timecop_getdate) + { + zend_long ts; + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(ts) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + TIMECOP_CALL_FUNCTION("getdate", 0); + } + /* }}} */ + + /* {{{ proto array timecop_localtime([int timestamp [, bool associative_array]]) + Returns the results of the C system call localtime as an associative array if + the associative_array argument is set to 1 other wise it is a regular array */ + PHP_FUNCTION(timecop_localtime) + { + zend_long timestamp; + zend_bool associative; + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(timestamp) + Z_PARAM_BOOL(associative) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + TIMECOP_CALL_FUNCTION("localtime", 0); + } + /* }}} */ + + /* {{{ proto int timecop_strtotime(string time [, int now ]) + Convert string representation of date and time to a timestamp */ + PHP_FUNCTION(timecop_strtotime) + { + zend_string *times; + zend_long preset_ts; + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(times) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(preset_ts) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + TIMECOP_CALL_FUNCTION("strtotime", 1); + } + /* }}} */ + + /* + * get_mock_timeval(fixed, now) + * + * + * delta + * |<----------------------->| + * travel_offset delta * scaling_factor + * |<------------->|<------------------------------------------------->| + * ==@===============@=========@=========================================@== + * ^ ^ ^ + * | | | + * travel_origin orig_time traveled_time + * + * + * delta = orig_time - travel_origin + * traveled_time = travel_origin + travel_offset + delta * scaling_factor + */ + static int get_mock_timeval(tc_timeval *fixed, const tc_timeval *now) + { + if (TIMECOP_G(timecop_mode) == TIMECOP_MODE_FREEZE) { + *fixed = TIMECOP_G(freezed_time); + } else if (TIMECOP_G(timecop_mode) == TIMECOP_MODE_TRAVEL) { + tc_timeval delta, origin = TIMECOP_G(travel_origin); + zend_long scale = TIMECOP_G(scaling_factor); + if (now == NULL) { + get_current_time(&delta); + } else { + delta = *now; + } + tc_timeval_sub(&delta, &delta, &origin); + tc_timeval_mul(&delta, &delta, scale); + tc_timeval_add(fixed, &origin, &TIMECOP_G(travel_offset)); + tc_timeval_add(fixed, fixed, &delta); + } else { + if (now == NULL) { + get_current_time(fixed); + } else { + *fixed = *now; + } + } + return 0; + } + + static zend_long mock_timestamp() + { + tc_timeval tv; + get_mock_timeval(&tv, NULL); + return tv.sec; + } + + static int get_timeval_from_datetime(tc_timeval *tp, zval *dt) + { + zval sec, usec; + zval u_str; + + call_php_method_with_0_params(dt, Z_OBJCE_P(dt), "gettimestamp", &sec); + ZVAL_STRING(&u_str, "u"); + call_php_method_with_1_params(dt, Z_OBJCE_P(dt), "format", &usec, &u_str); + zval_ptr_dtor(&u_str); + convert_to_long(&usec); + + tp->sec = Z_LVAL(sec); + tp->usec = Z_LVAL(usec); + + return 0; + } + + static int get_current_time(tc_timeval *now) + { + int ret = 0; + #if HAVE_GETTIMEOFDAY + struct timeval tv; + ret = gettimeofday(&tv, NULL); + if (ret == 0) { + now->sec = (zend_long)tv.tv_sec; + now->usec = (zend_long)tv.tv_usec; + } + #else + time_t ts = time(NULL); + if (ts == -1) { + ret = -1; + } else { + now->sec = (zend_long)ts; + now->usec = 0; + } + #endif + return ret; + } + + #ifdef HAVE_GETTIMEOFDAY + + #define MICRO_IN_SEC 1000000.00 + #define SEC_IN_MIN 60 + + static void _timecop_gettimeofday(INTERNAL_FUNCTION_PARAMETERS, int mode) + { + zend_bool get_as_float = 0; + tc_timeval fixed; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &get_as_float) == FAILURE) { + RETURN_FALSE; + } + + if (get_mock_timeval(&fixed, NULL)) { + RETURN_FALSE; + } + + if (get_as_float) { + RETURN_DOUBLE((double)(fixed.sec + fixed.usec / MICRO_IN_SEC)); + } + if (mode) { + zval zv_offset, zv_dst, format, timestamp; + zend_long offset = 0, is_dst = 0; + + ZVAL_LONG(×tamp, fixed.sec); + + /* offset */ + ZVAL_STRING(&format, "Z"); + call_php_function_with_2_params(ORIG_FUNC_NAME("date"), &zv_offset, &format, ×tamp); + convert_to_long(&zv_offset); + offset = Z_LVAL(zv_offset); + zval_ptr_dtor(&zv_offset); + zval_ptr_dtor(&format); + + /* is_dst */ + ZVAL_STRING(&format, "I"); + call_php_function_with_2_params(ORIG_FUNC_NAME("date"), &zv_dst, &format, ×tamp); + convert_to_long(&zv_dst); + is_dst = Z_LVAL(zv_dst); + zval_ptr_dtor(&zv_dst); + zval_ptr_dtor(&format); + + array_init(return_value); + add_assoc_long(return_value, "sec", fixed.sec); + add_assoc_long(return_value, "usec", fixed.usec); + add_assoc_long(return_value, "minuteswest", -offset / SEC_IN_MIN); + add_assoc_long(return_value, "dsttime", is_dst); + } else { + char ret[100]; + snprintf(ret, 100, "%.8F %ld", fixed.usec / MICRO_IN_SEC, fixed.sec); + RETURN_STRING(ret); + } + } + + /* {{{ proto mixed microtime([bool get_as_float]) + Returns either a string or a float containing the current time in seconds and microseconds */ + PHP_FUNCTION(timecop_microtime) + { + _timecop_gettimeofday(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); + } + /* }}} */ + + /* {{{ proto array gettimeofday([bool get_as_float]) + Returns the current time as array */ + PHP_FUNCTION(timecop_gettimeofday) + { + _timecop_gettimeofday(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); + } + /* }}} */ + #endif + + /* {{{ proto int timecop_unixtojd([int timestamp]) + Convert UNIX timestamp to Julian Day */ + PHP_FUNCTION(timecop_unixtojd) + { + TIMECOP_CALL_FUNCTION("unixtojd", 0); + } + /* }}} */ + + static void _timecop_orig_datetime_constructor(INTERNAL_FUNCTION_PARAMETERS, int immutable) + { + zval *arg1 = NULL, *arg2 = NULL; + zend_class_entry *real_ce; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|zz", &arg1, &arg2) == FAILURE) { + RETURN_FALSE; + } + + if (immutable) { + real_ce = TIMECOP_G(ce_DateTimeImmutable); + } else { + real_ce = TIMECOP_G(ce_DateTime); + } + + call_php_method_with_2_params(getThis(), real_ce, ORIG_FUNC_NAME("__construct"), NULL, arg1, arg2); + } + + static inline void _timecop_datetime_constructor(INTERNAL_FUNCTION_PARAMETERS, int immutable) + { + _timecop_datetime_constructor_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, getThis(), immutable); + } + + static inline void _timecop_date_create(INTERNAL_FUNCTION_PARAMETERS, int immutable) + { + _timecop_datetime_constructor_ex(INTERNAL_FUNCTION_PARAM_PASSTHRU, NULL, immutable); + } + + static void _timecop_datetime_constructor_ex(INTERNAL_FUNCTION_PARAMETERS, zval *obj, int immutable) + { + zval orig_time, *orig_timezone = NULL; + zval fixed_time, fixed_timezone, *arg1, *arg2; + char *orig_time_str = NULL; + size_t orig_time_len = 0; + const char *real_func; + zend_class_entry *real_ce; + + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_STRING(orig_time_str, orig_time_len) + Z_PARAM_OBJECT_OF_CLASS_OR_NULL(orig_timezone, TIMECOP_G(ce_DateTimeZone)) + ZEND_PARSE_PARAMETERS_END(); + + ZVAL_STRINGL(&orig_time, orig_time_str, orig_time_len); + + if (immutable) { + real_func = ORIG_FUNC_NAME("date_create_immutable"); + real_ce = TIMECOP_G(ce_DateTimeImmutable); + } else { + real_func = ORIG_FUNC_NAME("date_create"); + real_ce = TIMECOP_G(ce_DateTime); + } + + if (get_formatted_mock_time(&orig_time, orig_timezone, &fixed_time, &fixed_timezone TSRMLS_CC) == 0) { + arg1 = &fixed_time; + arg2 = &fixed_timezone; + } else { + arg1 = &orig_time; + arg2 = orig_timezone; + } + if (obj == NULL) { + call_php_function_with_2_params(real_func, return_value, arg1, arg2); + } else { + call_php_method_with_2_params(obj, real_ce, ORIG_FUNC_NAME("__construct"), NULL, arg1, arg2); + } + zval_ptr_dtor(&orig_time); + zval_ptr_dtor(&fixed_time); + zval_ptr_dtor(&fixed_timezone); + } + + /* {{{ proto DateTime timecop_date_create([string time[, DateTimeZone object]]) + Returns new DateTime object initialized with traveled time */ + PHP_FUNCTION(timecop_date_create) + { + _timecop_date_create(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); + } + /* }}} */ + + static void _timecop_date_create_from_format(INTERNAL_FUNCTION_PARAMETERS, int immutable) + { + zval *orig_timezone = NULL; + zval orig_format, orig_time, fixed_format, fixed_time, new_format, new_time; + zval dt, now_timestamp, tmp; + char *orig_format_str, *orig_time_str; + size_t orig_format_len, orig_time_len; + tc_timeval now; + char buf[64]; + const char *real_func; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|O!", &orig_format_str, &orig_format_len, &orig_time_str, &orig_time_len, &orig_timezone, TIMECOP_G(ce_DateTimeZone)) == FAILURE) { + RETURN_FALSE; + } + + ZVAL_STRINGL(&orig_format, orig_format_str, orig_format_len); + ZVAL_STRINGL(&orig_time, orig_time_str, orig_time_len); + + call_php_function_with_3_params(ORIG_FUNC_NAME("date_create_from_format"), &dt, &orig_format, &orig_time, orig_timezone); + if (Z_TYPE(dt) == IS_FALSE) { + RETURN_FALSE; + } + + if (memchr(orig_format_str, '!', orig_format_len) || + memchr(orig_format_str, '|', orig_format_len)) { + + if (immutable) { + call_php_function_with_3_params(ORIG_FUNC_NAME("date_create_immutable_from_format"), &dt, &orig_format, &orig_time, orig_timezone); + } + + zval_ptr_dtor(&orig_format); + zval_ptr_dtor(&orig_time); + RETURN_ZVAL(&dt, 1, 1); + } + + get_mock_timeval(&now, NULL); + + ZVAL_LONG(&now_timestamp, now.sec); + call_php_method_with_1_params(&dt, TIMECOP_G(ce_DateTime), "settimestamp", NULL, &now_timestamp); + sprintf(buf, "Y-m-d H:i:s.%06ld ", now.usec); + ZVAL_STRING(&tmp, buf); + call_php_method_with_1_params(&dt, TIMECOP_G(ce_DateTime), "format", &fixed_time, &tmp); + zval_ptr_dtor(&tmp); + + if (memchr(orig_format_str, 'g', orig_format_len) || + memchr(orig_format_str, 'h', orig_format_len) || + memchr(orig_format_str, 'G', orig_format_len) || + memchr(orig_format_str, 'H', orig_format_len) || + memchr(orig_format_str, 'i', orig_format_len) || + memchr(orig_format_str, 's', orig_format_len)) { + ZVAL_STRING(&fixed_format, "Y-m-d ??:??:??.??????"); + } else if (memchr(orig_format_str, 'Y', orig_format_len) || + memchr(orig_format_str, 'y', orig_format_len) || + memchr(orig_format_str, 'F', orig_format_len) || + memchr(orig_format_str, 'M', orig_format_len) || + memchr(orig_format_str, 'm', orig_format_len) || + memchr(orig_format_str, 'n', orig_format_len) || + memchr(orig_format_str, 'd', orig_format_len) || + memchr(orig_format_str, 'j', orig_format_len) || + memchr(orig_format_str, 'D', orig_format_len) || + memchr(orig_format_str, 'l', orig_format_len) || + memchr(orig_format_str, 'U', orig_format_len)) { + ZVAL_STRING(&fixed_format, "Y-m-d H:i:s.??????"); + } else if (memchr(orig_format_str, 'u', orig_format_len)) { + ZVAL_STRING(&fixed_format, "Y-m-d ??:??:??.??????"); + } else { + ZVAL_STRING(&fixed_format, "Y-m-d H:i:s.u"); + } + + ZVAL_STRING(&tmp, "%s %s"); + call_php_function_with_3_params("sprintf", &new_format, &tmp, &fixed_format, &orig_format); + call_php_function_with_3_params("sprintf", &new_time, &tmp, &fixed_time, &orig_time); + zval_ptr_dtor(&tmp); + + if (immutable) { + real_func = ORIG_FUNC_NAME("date_create_immutable_from_format"); + } else { + real_func = ORIG_FUNC_NAME("date_create_from_format"); + } + call_php_function_with_3_params(real_func, return_value, &new_format, &new_time, orig_timezone); + + zval_ptr_dtor(&dt); + zval_ptr_dtor(&orig_format); + zval_ptr_dtor(&orig_time); + zval_ptr_dtor(&fixed_format); + zval_ptr_dtor(&fixed_time); + zval_ptr_dtor(&new_format); + zval_ptr_dtor(&new_time); + } + + /* {{{ proto DateTime timecop_date_create_from_format(string format, string time[, DateTimeZone object]) + Returns new DateTime object initialized with traveled time */ + PHP_FUNCTION(timecop_date_create_from_format) + { + _timecop_date_create_from_format(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); + } + /* }}} */ + + /* {{{ proto DateTimeImmutable timecop_date_create_immutable([string time[, DateTimeZone object]]) + Returns new DateTimeImmutable object initialized with traveled time */ + PHP_FUNCTION(timecop_date_create_immutable) + { + _timecop_date_create(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); + } + /* }}} */ + + /* {{{ proto DateTimeImmutable timecop_date_create_immutable_from_format(string format, string time[, DateTimeZone object]) + Returns new DateTimeImmutable object initialized with traveled time */ + PHP_FUNCTION(timecop_date_create_immutable_from_format) + { + _timecop_date_create_from_format(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); + } + /* }}} */ + + /* {{{ proto TimecopDateTime::__construct([string time[, DateTimeZone object]]) + Creates new TimecopDateTime object */ + PHP_METHOD(TimecopDateTime, __construct) + { + _timecop_datetime_constructor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); + } + /* }}} */ + + /* {{{ proto TimecopOrigDateTime::__construct([string time[, DateTimeZone object]]) + Creates new TimecopOrigDateTime object */ + PHP_METHOD(TimecopOrigDateTime, __construct) + { + _timecop_orig_datetime_constructor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); + } + /* }}} */ + + /* {{{ proto TimecopDateTimeImmutable::__construct([string time[, DateTimeZone object]]) + Creates new TimecopDateTimeImmutable object */ + PHP_METHOD(TimecopDateTimeImmutable, __construct) + { + _timecop_datetime_constructor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); + } + /* }}} */ + + /* {{{ proto TimecopOrigDateTimeImmutable::__construct([string time[, DateTimeZone object]]) + Creates new TimecopOrigDateTimeImmutable object */ + PHP_METHOD(TimecopOrigDateTimeImmutable, __construct) + { + _timecop_orig_datetime_constructor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); + } + /* }}} */ + + static inline zval* _call_php_method_with_0_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr) + { + return _call_php_method(object_pp, obj_ce, method_name, retval_ptr, 0, NULL, NULL); + } + + static inline zval* _call_php_method_with_1_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, zval *arg1) + { + int nparams = 1; + if (arg1 == NULL) { + nparams = 0; + } + return _call_php_method(object_pp, obj_ce, method_name, retval_ptr, nparams, arg1, NULL); + } + + static inline zval* _call_php_method_with_2_params(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, zval *arg1, zval *arg2) + { + int nparams = 2; + if (arg1 == NULL) { + nparams = 0; + } else if (arg2 == NULL) { + nparams = 1; + } + return _call_php_method(object_pp, obj_ce, method_name, retval_ptr, nparams, arg1, arg2); + } + + static inline zval* _call_php_method(zval *object_pp, zend_class_entry *obj_ce, const char *method_name, zval *retval_ptr, int param_count, zval* arg1, zval* arg2) + { + return zend_call_method( + object_pp == NULL ? NULL : Z_OBJ_P(object_pp), + obj_ce, + NULL, + method_name, + strlen(method_name), + retval_ptr, + param_count, + arg1, + arg2 + ); + } + + static inline void _call_php_function_with_3_params(const char *function_name, zval *retval_ptr, zval *arg1, zval *arg2, zval *arg3) + { + if (arg3 == NULL) { + call_php_function_with_2_params(function_name, retval_ptr, arg1, arg2); + } else { + zval params[3]; + ZVAL_COPY(¶ms[0], arg1); + ZVAL_COPY(¶ms[1], arg2); + ZVAL_COPY(¶ms[2], arg3); + _call_php_function_with_params(function_name, retval_ptr, 3, params); + zval_ptr_dtor(¶ms[0]); + zval_ptr_dtor(¶ms[1]); + zval_ptr_dtor(¶ms[2]); + } + } + + static inline void _call_php_function_with_params(const char *function_name, zval *retval_ptr, uint32_t param_count, zval *params) + { + zval callable; + ZVAL_STRING(&callable, function_name); + + call_user_function(EG(function_table), NULL, &callable, retval_ptr, param_count, params); + + zval_ptr_dtor(&callable); + } + + /* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + \ No newline at end of file diff --git a/timecop_php8_arginfo.h b/timecop_php8_arginfo.h index 602de63..67608ca 100644 --- a/timecop_php8_arginfo.h +++ b/timecop_php8_arginfo.h @@ -23,16 +23,16 @@ 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_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_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_ARG_INFO(0, scale) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_timecop_return, 0) @@ -42,93 +42,93 @@ 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_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_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_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_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_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_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() #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_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_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_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_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_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_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() ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_TimecopDateTime_createFromFormat, 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_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() // 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_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_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 @@ -136,7 +136,7 @@ ZEND_END_ARG_INFO() // TimecopDateTimeImmutable::createFromFormat ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_TimecopDateTimeImmutable_createFromFormat, 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_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()