Skip to content

Implement readonly properties #7089

New issue

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

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

Already on GitHub? Sign in to your account

Closed
wants to merge 20 commits into from
Closed

Conversation

nikic
Copy link
Member

@nikic nikic commented Jun 2, 2021

@nikic nikic added the RFC label Jun 2, 2021
@matthieu88160
Copy link

Hi all,
Shouldn't this feature be extended to the class constant rather than properties only?

@iluuu1994
Copy link
Member

iluuu1994 commented Jun 16, 2021

@matthieu88160 Constants are by definition readonly. Not sure how that would make sense. Also note that the mailing list is the primary tool for feedback when it comes to specification.

@matthieu88160
Copy link

@matthieu88160 Constants are by definition readonly. Not sure how that would make sense. Also note that the mailing list is the primary tool for feedback when it comes to specification.

My bad, I was thinking modification was allowed.

@iluuu1994
Copy link
Member

@nikic Here are two more tests for useful use-cases of overriding a readonly property:

--TEST--
Visibility can change in readonly property
--FILE--
<?php

class A {
    protected readonly int $prop;

    public function __construct() {
        $this->prop = 42;
    }
}
class B extends A {
    public readonly int $prop;
}

$a = new A();
try {
    var_dump($a->prop);
} catch (\Error $error) {
    echo $error->getMessage() . "\n";
}

$b = new B();
var_dump($b->prop);

?>
--EXPECT--
Cannot access protected property A::$prop
int(42)

--TEST--
Can override readonly property with attributes
--FILE--
<?php

#[Attribute]
class FooAttribute {}

class A {
    public readonly int $prop;

    public function __construct() {
        $this->prop = 42;
    }
}
class B extends A {
    #[FooAttribute]
    public readonly int $prop;
}

var_dump((new ReflectionProperty(B::class, 'prop'))->getAttributes()[0]->newInstance());

?>
--EXPECT--
object(FooAttribute)#1 (0) {
}

@reyadkhan
Copy link

reyadkhan commented Jul 2, 2021

Method parameters can be readonly:
foo(readonly string $bar){}

@kolardavid
Copy link

Hey guys, I would like to add my few objections you might want to consider:

  1. Personally, I would allow unset() on readonly property, which would unlock it for writing again. This can be done only in private scope and can be useful. It would then work as controlled lockable property within scope, which has knowledge about the object. Also it would be consistent with typed properties, which can be unset, which returns them back in state, where reading throws exception (in oppose to writing).
  2. PHP is imho missing simple function to check if typed or readonly property is initialized, which might be harder to safely detect if reading/writing will throw an exception or is permitted. People usually use isset(), which gives false negative if typed/readonly is already set to explicit null. It would be probably helpful to add is_initialized() function. You can't even use property_exists() for this, since it returns (correctly) true for uninitialized properties.
  3. I would personally vote for not using readonly keyword, rather use writeonce/immutable/locked. readonly imho does not describe the usage that well as the other. Also, I would reserve readonly for other purpose:
  4. readonly would be imho much more useful for different scenario, where property is writable within private scope and readonly outside of private scope. Actually I believe I would use this scenario even more than the currently proposed readonly feature. It would safely expose private properties to outside without risk of modification. It would be basically syntactic sugar for private property and public getter without setter (the above proposed behavior of being able to unset and set it again would be more close to this scenario, even though it would always require unset before set to be safe, which is annoying). I can imagine 2 ways of behavior:
    a. private readonly int $prop; would mean that property is writable in private scope and readonly in protected and public scope (in which case public readonly would be non-practical).
    b. public readonly int $prop; would mean the same, but changes meaning of visibility specifier (in which case private readonly would be non-practical)
    Basically the only difference would be if private/protected/public would specify where is readonly visible and it would be always writable only in private scope (which would work similarly to current readonly behavior, but you will be unable to make it writable from protected scope) or it would be always publicly visible, but you would specify if it is writable in private/protected/public scope. That would be imho more practical, but would be confusing and required explanation, because it would make visibility modifiers works differently than usual.

Of course there is option to merge both behaviors by adding modifier to readonly - so having public readonly working as readonly from outside, writable from inside and public readonly writeonce as current readonly proposal, making both things more consistent. The downside is that readonly writeonce is longer to write, but makes most sense to me.

Thank you for your attention and considering this opinions :)

@kocsismate
Copy link
Member

Hey guys, I would like to add my few objections you might want to consider:

Please note that the feature is currently in voting, so any change should be warranted very much.

Points 3 and 4 have already been discussed to death, I believe the RFC provides background information why the readonly keyword (rather than writeonce) was chosen, and it also talks about the relation of "readonly properties" to the "asymmetric visibility" feature you also described.

Implementing an is_initialized() function could make sense, but it's IMO also orthogonal to this feature, since it doesn't only affect readonly properties. Also, it's already possible to detect if a property has been initialized via reflection.

Point 1 would render the feature broken, since any property value could be changed in private scope at any time, simply by unsetting it first.

@nikic
Copy link
Member Author

nikic commented Jul 5, 2021

is_initialized() was recently discussed and declined (https://externals.io/message/114607).

@kolardavid
Copy link

OK guys, thanks for your reply and sorry to bother you :)

@nikic nikic added this to the PHP 8.1 milestone Jul 9, 2021
@nikic nikic force-pushed the readonly-properties branch 7 times, most recently from 1bc5f49 to c890185 Compare July 15, 2021 13:07
@rela589n

This comment has been minimized.

@kolardavid

This comment has been minimized.

@rela589n

This comment has been minimized.

@rela589n

This comment has been minimized.

@rela589n

This comment has been minimized.

@KalleZ
Copy link
Member

KalleZ commented Jul 16, 2021

Please keep these comments to the mailing list, the PR is not a forum for such

@rela589n
Copy link

rela589n commented Jul 16, 2021 via email

@nikic nikic force-pushed the readonly-properties branch 3 times, most recently from fe4c9e0 to 2137465 Compare July 16, 2021 14:00
@nikic nikic force-pushed the readonly-properties branch from 8af4aae to 968a399 Compare July 20, 2021 09:12
@nikic nikic closed this in 6780aaa Jul 20, 2021
@jellynoone
Copy link
Contributor

Hi, @nikic just tried this out, is it intentional that it's now possible to skip the public visibility modifer in promoted properties and regular readonly properties?

class ClassName
{
    readonly int $prop1;
    
    function __construct(
        readonly array $prop2,
    ) {
        $this->prop1 = \count($prop2);
    }
}
\var_dump(new ClassName([1,2]));

@jrfnl
Copy link
Contributor

jrfnl commented Aug 3, 2021

Just FYI as I didn't see mention of the impact of readonly becoming a semi-reserved keyword in the potential for BC-breaks section of the RFC, nor a code-scan of top PHP projects to gauge the impact:
Global functions, classes et al named readonly will have to be renamed as this will now be a parse error.
And by extension, all calls to these functions/instantiations of these classes will have to be adjusted too as those will also be a parse error.

If nothing else, this affects WordPress - and by extension potentially all plugins and themes in its ecosphere. So far, the impact is estimated to be relatively small as the function (to add the readonly attribute to HTML input tags) is not that widely used, but still...

@rela589n

This comment has been minimized.

@iluuu1994
Copy link
Member

Please use the mailing list for these discussions. The repercussions of a keyword are clear to voters. The suggestion of no $ was mentioned on the mailing list and not well received. While it avoids the BC break it's very subtle and doesn't communicate intent well. It's also way to late for this discussion in the first place. This RFC was in discussion for months.

jrfnl added a commit to PHPCompatibility/PHPCompatibility that referenced this pull request Mar 9, 2022
cmb69 pushed a commit to php/doc-en that referenced this pull request Mar 11, 2022
* PHP 8.1 | MigrationGuide/New constants: add missing constants [1]

> * Added CURLOPT_DOH_URL option

> * Added certificate blob options when for libcurl >= 7.71.0:
>
>         CURLOPT_ISSUERCERT_BLOB
>         CURLOPT_PROXY_ISSUERCERT
>         CURLOPT_PROXY_ISSUERCERT_BLOB
>         CURLOPT_PROXY_SSLCERT_BLOB
>         CURLOPT_PROXY_SSLKEY_BLOB
>         CURLOPT_SSLCERT_BLOB
>         CURLOPT_SSLKEY_BLOB

Refs:
* https://github.com/php/php-src/blob/f67986a9218f4889d9352a87c29337a5b6eaa4bd/UPGRADING#L220-L229
* php/php-src#6612
* php/php-src@3dad63b
* php/php-src#7194
* php/php-src@b11785c

* PHP 8.1 | MigrationGuide/New constants: add missing constants [2]

> GD:
> * Avif support is now available through the `imagecreatefromavif()` and
>     `imageavif()` functions, if libgd has been built with avif support.

While not mentioned in the changelog entry, the commit to PHP does contain a new constant declaration...

Refs:
* https://github.com/php/php-src/blob/f67986a9218f4889d9352a87c29337a5b6eaa4bd/UPGRADING#L245-L247
* php/php-src#7026
* php/php-src@81f6d36#diff-00d1efef2247b288c86a6c3bfefac111a4774fbc5453fdc02dcf36c4a23da283R373

> GD:
> * `imagewebp()` can do lossless WebP encoding by passing `IMG_WEBP_LOSSLESS` as
>    quality. This constant is only defined, if a libgd is used which supports
>    lossless WebP encoding.

Refs:
* https://github.com/php/php-src/blob/3a71fcf5caf042a4ce8a586a6b554fd70432e1e2/UPGRADING#L568-L571
* php/php-src#7348
* php/php-src@eb6c9eb

* PHP 8.1 | MigrationGuide/New constants: add missing constants [3]

> Added `POSIX_RLIMIT_KQUEUES` and `POSIX_RLIMIT_NPTS`. These rlimits are only available on FreeBSD.

Refs:
* https://www.php.net/manual/en/migration81.new-features.php#migration81.new-features.posix
* php/php-src#6608
* php/php-src@ebca8de

* PHP 8.1 | MigrationGuide/New constants: add missing constants [4]

Refs:
* https://wiki.php.net/rfc/readonly_properties_v2
* php/php-src#7089
* php/php-src@6780aaa

* PHP 8.1 | MigrationGuide/New constants: add missing constants [5]

While not mentioned anywhere at all, the commit to PHP itself adding support for the Sodium xchacha* functions, does declare a couple of new constants as well...

Refs:
* php/php-src#6868
* php/php-src@f7f1f7f#diff-3fe4027560fd299248af1dc1efe04287cc2b6418e8f01755c05c9db64b668b1eR352-R357

While not mentioned anywhere at all, the commit to PHP itself adding support for the Sodium ristretto255* functions, also declares a number of new constants as well...

Refs:
* php/php-src#6922
* php/php-src@9b794f8#diff-3fe4027560fd299248af1dc1efe04287cc2b6418e8f01755c05c9db64b668b1eR368-R381

Co-authored-by: jrfnl <[email protected]>

Closes GH-1449.
jrfnl added a commit to PHPCompatibility/PHPCompatibility that referenced this pull request Dec 5, 2022
tiffany-taylor pushed a commit to tiffany-taylor/doc-en that referenced this pull request Jan 16, 2023
* PHP 8.1 | MigrationGuide/New constants: add missing constants [1]

> * Added CURLOPT_DOH_URL option

> * Added certificate blob options when for libcurl >= 7.71.0:
>
>         CURLOPT_ISSUERCERT_BLOB
>         CURLOPT_PROXY_ISSUERCERT
>         CURLOPT_PROXY_ISSUERCERT_BLOB
>         CURLOPT_PROXY_SSLCERT_BLOB
>         CURLOPT_PROXY_SSLKEY_BLOB
>         CURLOPT_SSLCERT_BLOB
>         CURLOPT_SSLKEY_BLOB

Refs:
* https://github.com/php/php-src/blob/f67986a9218f4889d9352a87c29337a5b6eaa4bd/UPGRADING#L220-L229
* php/php-src#6612
* php/php-src@3dad63b
* php/php-src#7194
* php/php-src@b11785c

* PHP 8.1 | MigrationGuide/New constants: add missing constants [2]

> GD:
> * Avif support is now available through the `imagecreatefromavif()` and
>     `imageavif()` functions, if libgd has been built with avif support.

While not mentioned in the changelog entry, the commit to PHP does contain a new constant declaration...

Refs:
* https://github.com/php/php-src/blob/f67986a9218f4889d9352a87c29337a5b6eaa4bd/UPGRADING#L245-L247
* php/php-src#7026
* php/php-src@81f6d36#diff-00d1efef2247b288c86a6c3bfefac111a4774fbc5453fdc02dcf36c4a23da283R373

> GD:
> * `imagewebp()` can do lossless WebP encoding by passing `IMG_WEBP_LOSSLESS` as
>    quality. This constant is only defined, if a libgd is used which supports
>    lossless WebP encoding.

Refs:
* https://github.com/php/php-src/blob/3a71fcf5caf042a4ce8a586a6b554fd70432e1e2/UPGRADING#L568-L571
* php/php-src#7348
* php/php-src@eb6c9eb

* PHP 8.1 | MigrationGuide/New constants: add missing constants [3]

> Added `POSIX_RLIMIT_KQUEUES` and `POSIX_RLIMIT_NPTS`. These rlimits are only available on FreeBSD.

Refs:
* https://www.php.net/manual/en/migration81.new-features.php#migration81.new-features.posix
* php/php-src#6608
* php/php-src@ebca8de

* PHP 8.1 | MigrationGuide/New constants: add missing constants [4]

Refs:
* https://wiki.php.net/rfc/readonly_properties_v2
* php/php-src#7089
* php/php-src@6780aaa

* PHP 8.1 | MigrationGuide/New constants: add missing constants [5]

While not mentioned anywhere at all, the commit to PHP itself adding support for the Sodium xchacha* functions, does declare a couple of new constants as well...

Refs:
* php/php-src#6868
* php/php-src@f7f1f7f#diff-3fe4027560fd299248af1dc1efe04287cc2b6418e8f01755c05c9db64b668b1eR352-R357

While not mentioned anywhere at all, the commit to PHP itself adding support for the Sodium ristretto255* functions, also declares a number of new constants as well...

Refs:
* php/php-src#6922
* php/php-src@9b794f8#diff-3fe4027560fd299248af1dc1efe04287cc2b6418e8f01755c05c9db64b668b1eR368-R381

Co-authored-by: jrfnl <[email protected]>

Closes phpGH-1449.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.