New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
explicit __construct
call for \DateTimeImmutable object would break immutable
#8351
Comments
That would require to actually have these properties, but so far their state is tracked internally. Something like the following might solve the issue, though: ext/date/php_date.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/ext/date/php_date.c b/ext/date/php_date.c
index d607a07420..db21ba21a5 100644
--- a/ext/date/php_date.c
+++ b/ext/date/php_date.c
@@ -2208,6 +2208,9 @@ PHPAPI bool php_date_initialize(php_date_obj *dateobj, const char *time_str, siz
int options = 0;
if (dateobj->time) {
+ if (dateobj->std.ce == date_ce_immutable && flags & PHP_DATE_INIT_CTOR) {
+ zend_throw_exception_ex(NULL, 0, "nonono");
+ }
timelib_time_dtor(dateobj->time);
}
if (format) { @derickr, what do you think? |
That does not appear to help with internal classes. The
|
I am wondering of how much of a problem this actually is. Calling __construct directly is basically openly saying "I know what I am doing". In my opinion it's more of an academical problem than actually a problem in practice. There is, in my opinion, nothing wrong with giving users powerful tooling, allowing them to re-initialize state, as long as it's not part of the default contract. I do not really consider this a bug, rather a feature. |
The constructor's implementation circumvents the readonly protection. Instead of just replacing the zval, the diff Zend/zend_attributes.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c
index 9f7b8f0144..f060e9d23c 100644
--- a/Zend/zend_attributes.c
+++ b/Zend/zend_attributes.c
@@ -109,7 +109,9 @@ ZEND_METHOD(SensitiveParameterValue, __construct)
Z_PARAM_ZVAL(value)
ZEND_PARSE_PARAMETERS_END();
- ZVAL_COPY(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), value);
+ zend_string *name = zend_string_init("value", sizeof("value")-1, 0);
+ Z_OBJ_P(ZEND_THIS)->handlers->write_property(Z_OBJ_P(ZEND_THIS), name, value, NULL);
+ zend_string_release(name);
}
ZEND_METHOD(SensitiveParameterValue, getValue) |
Hi, all. The problem with this issue is that a class named With PHP 8.1, I thought that an immutable class could be coded as follows Therefore, I was proposing a change to grant readonly. <?php
final class OtherDateTimeImmutable
{
public function __construct(
private readonly string $date
) {}
}
$date = new OtherDateTimeImmutable('now');
$date->__construct('yesterday'); // Fatal error: Uncaught Error: Cannot modify readonly property OtherDateTimeImmutable::$date However, in retrospect, I think this is an encapsulation violation, since the internal structure of the Therefore, as per cmb69's first comment, it would be preferable to throw an exception if it is initialized. |
That is not true. It encapsulates a lot more things then the three properties that you see when doing serialisation or These properties should indeed be read only, but that wasn't functionality that didn't exist until PHP 8.1. I am also unsure as to whether we should do anything here. I don't really want them as "real properties" (they're more like SQL's materialised views), and marking them "readonly" is only possible from 8.2 onwards, as we can't really break existing code (calling As such, I agree with @bwoebi that this is similar to using reflection overriding a private property, and that we shouldn't really do anything here, even though it isn't technically 100% encapsulated. |
!!! What I wanted to say is that "I think this is an encapsulation violation, since the internal structure of the DateTimeImmutable class is should not known to the user." (lack of "should not" when posting) |
I agree with @bwoebi and @derickr personally. "Encapsulation" relates to the public API of some software design. A constructor doesn't belong to what "encapsulation" refers to. Yes, you can mutate the object. So does reflection with private properties. Calling the constructor is like using reflection to me: do it if you know why you shouldn't 🤷 ... |
Thank you feedbacks, all. I agree with @bwoebi 's point that I do not really consider this a bug that should be addressed in 8.0, 8.1. Therefore, I hope it will be addressed in 8.2 or 9 if possible. |
The bug exists in the value by reference while with clone by value the constructor does not change the reference with the exception of the object variable used for the construct. <?php
$tz = new \DateTimeZone('Europe/Rome');
$dateoriginal = new \DateTimeImmutable('1970-01-01 00:00:00.000000', $tz);
$old = $dateoriginal;
$date = (clone $dateoriginal); // DateImmutable::clone from php >= 5.5.0
var_dump($date);
$date->__construct('2000-01-01 00:00:00.000000', $tz);
var_dump($date, $dateoriginal, $old); Correct:
<?php
$tz = new \DateTimeZone('Europe/Rome');
$dateoriginal = new \DateTimeImmutable('1970-01-01 00:00:00.000000', $tz);
$old = $dateoriginal;
$date = $dateoriginal;
var_dump($date);
$date->__construct('2000-01-01 00:00:00.000000', $tz);
var_dump($date, $dateoriginal, $old); incorrect:
Regardless of the read-only property, it must change the reference inside the variable container and not any one even if of the same value but different variable container. |
@hormus You're just changing the same object in your last example, so of course they update "both". |
I'm closing this as we've come to the conclusion that this is the same as working around encapsulation through Reflection, and that calling |
Description
The following code:
Resulted in this output:
https://3v4l.org/hnnIE
But I expected this output instead:
I did learn this issue when reading this slide.
https://speakerdeck.com/twada/growing-reliable-code-phperkaigi-2022?slide=84
According to the author of this slide, twada, he noticed this problem himself. (after reading https://www.simonholywell.com/post/2017/03/php-and-immutability/ )
I propose to give readonly to each of the properties of \DateTimeImmutable.PHP Version
PHP 8.1.4
Operating System
No response
The text was updated successfully, but these errors were encountered: