Electronics & Programming

develissimo

Open Source electronics development and programming

  • You are not logged in.
  • Root
  • » PHP
  • » Patch: Marking DateTime Instances Immutable [RSS Feed]

#1 Dec. 5, 2010 00:12:45

Benjamin E.
Registered: 2009-11-02
Reputation: +  0  -
Profile   Send e-mail  

Patch: Marking DateTime Instances Immutable


In the current implementation DateTime is not a value object, but its

internal state can be modified at any given time. This can lead to very

obscure bugs when references to DateTime objects are shared or objects are

passed through a chain of methods/functions that modify it. Using DateTime

is not side-effect free.



I propose to allow to work with DateTime objects that are marked as

immutable optionally. This means that all methods add, sub, modify,

setDate, setTime, setISODate, setTimestamp and setTimezone will return a

NEW instance of Datetime instead of reusing the old one.



I also talked to Derick about this and he agrees that immutable DateTime

objects would be desirable. I have talked to many other people who agreed

that the current behavior is weird.



My proposed syntax would be:



$immutableDateTime = date_create("2010-12-05", null, true);

$immutableDateTime = new DateTime("2010-12-05", null, true);

$immutableDateTime = DateTime::createFromFormat("%Y-%m-%d", "2010-12-05",

null, true);



Where the third and fourth variable respectivly are boolean flags

$immutable yes or no. Also the DatePeriod iterator would be modified. If an

immutable start date is passed the date iterator would also create

immutable dates.



I have attached a patch that implements this functionality and a little

test-script that shows how it would work. This is the first complex bit of

C-code that I did so please bear with any mistakes I made ;-) Also i havent

followed the coding standards.



Any feedback is greatly appreciated. My C-Skills arent that good so i am

not finished with an additional solution allowing to call a method

"setImmutable()" on any datetime instance, marking it as immutable.

Obviously this would only mark the instance as immutable, allowing to

accept a flag to reset it to be mutable would be counter-productive.



The only drawback I see to this patch is the additional int variable on

the _php_date_obj and _php_date_period structs. I am not sure if they

affect memory in such a way that this solution isn't viable.



If this topic needs more discussion or pro/cons I am willing to open up an

RFC for a more detailed discussion.<?php

ini_set("date.timezone", "Europe/Berlin");

$d = new DateTime("now", new DateTimeZone("Europe/Berlin"), true);

$d2 = $d->add(DateInterval::createFromDateString("1 month"));
$d3 = $d->sub(DateInterval::createFromDateString("1 month"));

echo $d2->format('d.m.Y') . " " . $d->format('d.m.Y') . " " . $d3->format('d.m.Y');
echo "\nd===d2: ";
echo $d === $d2 ? "equals" : "new";
echo "\nd===d3: ";
echo $d === $d3 ? "equals" : "new";
echo "\n";

$begin = new DateTime( '2007-12-31', new DateTimeZone("Europe/Berlin"), true);
$end = new DateTime( '2009-12-31 23:59:59', new DateTimeZone("Europe/Berlin"), true);

$interval = DateInterval::createFromDateString('last thursday of next month');
$period = new DatePeriod($begin, $interval, $end, DatePeriod::EXCLUDE_START_DATE);

$dates = array();
foreach ( $period as $dt ) {
$newdate = $dt->add(DateInterval::createFromDateString("1 month"));
echo "Iterator: ";
echo $newdate === $dt ? "equal" : "new";
echo "\n";
}Index: ext/date/php_date.c
===================================================================
--- ext/date/php_date.c (revision 305965)
+++ ext/date/php_date.c (working copy)
@@ -29,6 +29,7 @@
#include "php_date.h"
#include "zend_interfaces.h"
#include "lib/timelib.h"
+#include "Zend/zend_operators.h"
#include <time.h>

#ifdef PHP_WIN32
@@ -140,12 +141,14 @@
ZEND_BEGIN_ARG_INFO_EX(arginfo_date_create, 0, 0, 0)
ZEND_ARG_INFO(0, time)
ZEND_ARG_INFO(0, object)
+ ZEND_ARG_INFO(0, immutable)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_date_create_from_format, 0, 0, 2)
ZEND_ARG_INFO(0, format)
ZEND_ARG_INFO(0, time)
ZEND_ARG_INFO(0, object)
+ ZEND_ARG_INFO(0, immutable)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_date_parse, 0, 0, 1)
@@ -1855,6 +1858,7 @@
newdateobj = (php_date_obj *) zend_object_store_get_object(iterator->current TSRMLS_CC);
newdateobj->time = timelib_time_ctor();
*newdateobj->time = *it_time;
+ newdateobj->immutable = object->immutable;
if (it_time->tz_abbr) {
newdateobj->time->tz_abbr = strdup(it_time->tz_abbr);
}
@@ -2022,6 +2026,7 @@
if (ptr) {
*ptr = intern;
}
+ intern->immutable = 0;

zend_object_std_init(&intern->std, class_type TSRMLS_CC);
zend_hash_copy(intern->std.properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
@@ -2037,6 +2042,18 @@
return date_object_new_date_ex(class_type, NULL TSRMLS_CC);
}

+static zval* date_object_clone(zval *old TSRMLS_DC)
+{
+ zval *new;
+ zend_object_value oldvalue = date_object_clone_date(old);
+
+ MAKE_STD_ZVAL(new);
+ Z_TYPE_P(new) = IS_OBJECT;
+ Z_OBJVAL_P(new) = oldvalue;
+
+ return new;
+}
+
static zend_object_value date_object_clone_date(zval *this_ptr TSRMLS_DC)
{
php_date_obj *new_obj = NULL;
@@ -2372,7 +2389,7 @@
DATEG(last_errors) = last_errors;
}

-PHPAPI int php_date_initialize(php_date_obj *dateobj, /*const*/ char *time_str, int time_str_len, char *format, zval *timezone_object, int ctor TSRMLS_DC)
+PHPAPI int php_date_initialize(php_date_obj *dateobj, /*const*/ char *time_str, int time_str_len, char *format, zval *timezone_object, zend_bool immutable_flag, int ctor TSRMLS_DC)
{
timelib_time *now;
timelib_tzinfo *tzi;
@@ -2390,6 +2407,8 @@
dateobj->time = timelib_strtotime(time_str_len ? time_str : "now", time_str_len ? time_str_len : sizeof("now") -1, &err, DATE_TIMEZONEDB);
}

+ dateobj->immutable = immutable_flag;
+
/* update last errors and warnings */
update_errors_warnings(err TSRMLS_CC);

@@ -2459,16 +2478,17 @@
*/
PHP_FUNCTION(date_create)
{
+ zend_bool immutable_flag = 0;
zval *timezone_object = NULL;
char *time_str = NULL;
int time_str_len = 0;

- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sO", &time_str, &time_str_len, &timezone_object, date_ce_timezone) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sOb", &time_str, &time_str_len, &timezone_object, date_ce_timezone, &immutable_flag) == FAILURE) {
RETURN_FALSE;
}

php_date_instantiate(date_ce_date, return_value TSRMLS_CC);
- if (!php_date_initialize(zend_object_store_get_object(return_value TSRMLS_CC), time_str, time_str_len, NULL, timezone_object, 0 TSRMLS_CC)) {
+ if (!php_date_initialize(zend_object_store_get_object(return_value TSRMLS_CC), time_str, time_str_len, NULL, timezone_object, immutable_flag, 0 TSRMLS_CC)) {
RETURN_FALSE;
}
}
@@ -2479,16 +2499,17 @@
*/
PHP_FUNCTION(date_create_from_format)
{
+ zend_bool immutable_flag = 0;
zval *timezone_object = NULL;
char *time_str = NULL, *format_str = NULL;
int time_str_len = 0, format_str_len = 0;

- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|O", &format_str, &format_str_len, &time_str, &time_str_len, &timezone_object, date_ce_timezone) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|Ob", &format_str, &format_str_len, &time_str, &time_str_len, &timezone_object, date_ce_timezone, &immutable_flag) == FAILURE) {
RETURN_FALSE;
}

php_date_instantiate(date_ce_date, return_value TSRMLS_CC);
- if (!php_date_initialize(zend_object_store_get_object(return_value TSRMLS_CC), time_str, time_str_len, format_str, timezone_object, 0 TSRMLS_CC)) {
+ if (!php_date_initialize(zend_object_store_get_object(return_value TSRMLS_CC), time_str, time_str_len, format_str, timezone_object, immutable_flag, 0 TSRMLS_CC)) {
RETURN_FALSE;
}
}
@@ -2499,14 +2520,15 @@
*/
PHP_METHOD(DateTime, __construct)
{
+ zend_bool immutable_flag = 0;
zval *timezone_object = NULL;
char *time_str = NULL;
int time_str_len = 0;
zend_error_handling error_handling;

zend_replace_error_handling(EH_THROW, NULL, &error_handling TSRMLS_CC);
- if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sO", &time_str, &time_str_len, &timezone_object, date_ce_timezone)) {
- php_date_initialize(zend_object_store_get_object(getThis() TSRMLS_CC), time_str, time_str_len, NULL, timezone_object, 1 TSRMLS_CC);
+ if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sOb", &time_str, &time_str_len, &timezone_object, date_ce_timezone, &immutable_flag)) {
+ php_date_initialize(zend_object_store_get_object(getThis() TSRMLS_CC), time_str, time_str_len, NULL, timezone_object, immutable_flag, 1 TSRMLS_CC);
}
zend_restore_error_handling(&error_handling TSRMLS_CC);
}
@@ -2533,7 +2555,7 @@
case TIMELIB_ZONETYPE_ABBR: {
char *tmp = emalloc(Z_STRLEN_PP(z_date) + Z_STRLEN_PP(z_timezone) + 2);
snprintf(tmp, Z_STRLEN_PP(z_date) + Z_STRLEN_PP(z_timezone) + 2, "%s %s", Z_STRVAL_PP(z_date), Z_STRVAL_PP(z_timezone));
- php_date_initialize(*dateobj, tmp, Z_STRLEN_PP(z_date) + Z_STRLEN_PP(z_timezone) + 1, NULL, NULL, 0 TSRMLS_CC);
+ php_date_initialize(*dateobj, tmp, Z_STRLEN_PP(z_date) + Z_STRLEN_PP(z_timezone) + 1, NULL, NULL, 0, 0 TSRMLS_CC);
efree(tmp);
return 1;
}
@@ -2549,7 +2571,7 @@
tzobj->tzi.tz = tzi;
tzobj->initialized = 1;

- php_date_initialize(*dateobj, Z_STRVAL_PP(z_date), Z_STRLEN_PP(z_date), NULL, tmp_obj, 0 TSRMLS_CC);
+ php_date_initialize(*dateobj, Z_STRVAL_PP(z_date), Z_STRLEN_PP(z_date), NULL, tmp_obj, 0, 0 TSRMLS_CC);
zval_ptr_dtor(&tmp_obj);
return 1;
}
@@ -2780,6 +2802,11 @@
RETURN_FALSE;
}
dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
+ if (dateobj->immutable) {
+ object = date_object_clone(object);
+ dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
+ }
+
DATE_CHECK_INITIALIZED(dateobj->time, DateTime);

tmp_time = timelib_strtotime(modify, modify_len, &err, DATE_TIMEZONEDB);
@@ -2807,6 +2834,8 @@
}
/* }}} */

+
+
/* {{{ proto DateTime date_add(DateTime object, DateInterval interval)
Adds an interval to the current date in object.
*/
@@ -2821,11 +2850,15 @@
RETURN_FALSE;
}
dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
+ if (dateobj->immutable) {
+ object = date_object_clone(object);
+ dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
+ }
+
DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
intobj = (php_interval_obj *) zend_object_store_get_object(interval TSRMLS_CC);
DATE_CHECK_INITIALIZED(intobj->initialized, DateInterval);

-
if (intobj->diff->have_weekday_relative || intobj->diff->have_special_relative) {
memcpy(&dateobj->time->relative, intobj->diff, sizeof(struct timelib_rel_time));
} else {
@@ -2866,6 +2899,11 @@
RETURN_FALSE;
}
dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
+ if (dateobj->immutable) {
+ object = date_object_clone(object);
+ dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
+ }
+
DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
intobj = (php_interval_obj *) zend_object_store_get_object(interval TSRMLS_CC);
DATE_CHECK_INITIALIZED(intobj->initialized, DateInterval);
@@ -2951,6 +2989,10 @@
RETURN_FALSE;
}
dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
+ if (dateobj->immutable) {
+ object = date_object_clone(object);
+ dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
+ }
DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
tzobj = (php_timezone_obj *) zend_object_store_get_object(timezone_object TSRMLS_CC);
if (tzobj->type != TIMELIB_ZONETYPE_ID) {
@@ -3012,6 +3054,11 @@
RETURN_FALSE;
}
dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
+ if (dateobj->immutable) {
+ object = date_object_clone(object);
+ dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
+ }
+
DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
dateobj->time->h = h;
dateobj->time->i = i;
@@ -3035,6 +3082,10 @@
RETURN_FALSE;
}
dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
+ if (dateobj->immutable) {
+ object = date_object_clone(object);
+ dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
+ }
DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
dateobj->time->y = y;
dateobj->time->m = m;
@@ -3058,6 +3109,11 @@
RETURN_FALSE;
}
dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
+ if (dateobj->immutable) {
+ object = date_object_clone(object);
+ dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
+ }
+
DATE_CHECK_INITIALIZED(dateobj->time, DateTime);
dateobj->time->y = y;
dateobj->time->m = 1;
@@ -3084,6 +3140,10 @@
RETURN_FALSE;
}
dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC);
+ if (dateobj->immutable) {
+ object = date_object_clone(object);
+ 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);
@@ -3754,6 +3814,9 @@
if (dateobj->time->tz_info) {
clone->tz_info = dateobj->time->tz_info;
}
+ if (dateobj->immutable) {
+ dpobj->immutable = 1;
+ }
dpobj->start = clone;

/* interval */
Index: ext/date/php_date.h
===================================================================
--- ext/date/php_date.h (revision 305965)
+++ ext/date/php_date.h (working copy)
@@ -111,6 +111,7 @@
zend_object std;
timelib_time *time;
HashTable *props;
+ int immutable;
};

struct _php_timezone_obj {
@@ -142,6 +143,7 @@
timelib_time *current;
timelib_time *end;
timelib_rel_time *interval;
+ int immutable;
int recurrences;
int initialized;
int include_start_date;
@@ -180,7 +182,7 @@

/* Functions for creating DateTime objects, and initializing them from a string */
PHPAPI zval *php_date_instantiate(zend_class_entry *pce, zval *object TSRMLS_DC);
-PHPAPI int php_date_initialize(php_date_obj *dateobj, /*const*/ char *time_str, int time_str_len, char *format, zval *timezone_object, int ctor TSRMLS_DC);
+PHPAPI int php_date_initialize(php_date_obj *dateobj, /*const*/ char *time_str, int time_str_len, char *format, zval *timezone_object, zend_bool immutableFlag, int ctor TSRMLS_DC);


#endif /* PHP_DATE_H */--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit:http://www.php.net/unsub.php

Offline

  • Root
  • » PHP
  • » Patch: Marking DateTime Instances Immutable [RSS Feed]

Board footer

Moderator control

Enjoy the 20th of October
PoweredBy

The Forums are managed by develissimo stuff members, if you find any issues or misplaced content please help us to fix it. Thank you! Tell us via Contact Options
Leave a Message
Welcome to Develissimo Live Support