Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:125775 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by qa.php.net (Postfix) with ESMTPS id 35D261A00BD for ; Wed, 9 Oct 2024 16:20:56 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1728490993; bh=uPFNr8ttJlOZNC6z6ZDKi10K3DJ/97pKVGd4kzum24s=; h=From:Subject:Date:In-Reply-To:Cc:To:References:From; b=RzN91XO+HWTizwkvnXipzpOhGaMlV339Op0/9f/DLVVVKhIgmjmiCvwnayqlhsvLM daSEl7AHGMOnpogHENCr6ap0cJdEqhBeqbm8H+LHpGAL8BV+zTvxcgL2jBkmWfDpUu Vc8YkTIFMCvRVrrXuexQBOjAHhvqXPyo/Nr6NkQNS80W2N+lCAAtejPmGgtD8WawHy 0JZnRqpSjVVdFD1mhtUiZdlE9PRaM9ohSfdan9G+be3DxNqKMKbfw3dyECGOmGCh8X JiP5FMmObjfinHMrisv5JQfUx4VCOt3Qnwgv7Czga9rM7OaT/t7qXYCzcWeR0ck/m4 Mi9qpSSyhrItw== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 9D210180044 for ; Wed, 9 Oct 2024 16:23:12 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=0.6 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, HTML_MESSAGE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail-lf1-f49.google.com (mail-lf1-f49.google.com [209.85.167.49]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Wed, 9 Oct 2024 16:23:09 +0000 (UTC) Received: by mail-lf1-f49.google.com with SMTP id 2adb3069b0e04-5369f1c7cb8so7953147e87.1 for ; Wed, 09 Oct 2024 09:20:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1728490850; x=1729095650; darn=lists.php.net; h=references:to:cc:in-reply-to:date:subject:mime-version:message-id :from:from:to:cc:subject:date:message-id:reply-to; bh=htXim1XYrES6zJS5p2LTvNLrSdSS/pRTuY89x23rPfI=; b=FX1luLcG/RaXq6XjJhchmTX+clqN/TiNOyNAAQE5wE8wbb8Bzz/HDUgzBfguRKBtgi 2ffRH37R8rb3F79LbnCP4zCLq8Qm1rGLBfvfxZvKOcY8gfzy97fCA72fm0WNLERXWdrS ZOvFYV8+r8Lr9+2Vd5jTFGPBhadTA5C/ztTD31dAQCvIaS2igMiQjF2O4jICg6eTmRHF z/ZgHXXGghvREAaDzkx6D6Le+NY+4lJ+AxqnlNvm/icBVxByiv4zF3JLVryzmGKDdVV/ nC71r/G1qAVYPqlCpI8LzzL50u4i0iwWc9YCuFAjGJbHMKAAtoaaB/luU0e+w6vzkRgA +u1A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1728490850; x=1729095650; h=references:to:cc:in-reply-to:date:subject:mime-version:message-id :from:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=htXim1XYrES6zJS5p2LTvNLrSdSS/pRTuY89x23rPfI=; b=hOssujtNmgLw3HZH/F2a3DZ3Rhq1gqae9T12h8oZ81NQpzrvzEM3hkkHXbEVj1xlDv eTD26U05gvL/K717XClpq0YvJzjaK/JqjeQzwYi2l3tUEYTvvDxkyiChdCaDfZgZ7qaF L8/B42Bwz3U4rZKdc17QR2sk2BTlTY5FxeTg7Gz5GlhxOlE7o5ZHBMxeW9S4zgsxxKoO h/RpeRr6/TEX4dU03oYNHTW9dOdEqKUXVoI5vZZ0V3llZT+w9MtNYdYPAHjZIK3nlHqv bEBXV19S6tD4cd2Ln5kTPKxcY/sqrBwYWfB4xll7nM0ljkFps3vIvCQsEqcUlMqOIfsZ V9MA== X-Gm-Message-State: AOJu0YzUhgwepnP+kVR7ISSe+Qxi8W50nMwmJhzy1Sfru4kv3vD6dK4Q 9DZ+fHxLD/wsA2qhFJworZJVwy74Z2bO0eeELGcpMwb3ocRqBzgK X-Google-Smtp-Source: AGHT+IHJ6S3iql9yHswEEzCTNqO+kwfXmNSUMw6iWWvPVeD87JxVD1Pfj9JRb/ao36towVOcz1eKGQ== X-Received: by 2002:a05:6512:281b:b0:536:a6c6:33f with SMTP id 2adb3069b0e04-539c48c3470mr2304964e87.13.1728490849687; Wed, 09 Oct 2024 09:20:49 -0700 (PDT) Received: from smtpclient.apple ([89.249.45.14]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a99464533e3sm557483666b.178.2024.10.09.09.20.48 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Wed, 09 Oct 2024 09:20:48 -0700 (PDT) Message-ID: <2A7CF24F-3AE3-4125-965F-C65431C42DFB@gmail.com> Content-Type: multipart/alternative; boundary="Apple-Mail=_B0C0C92A-CB8F-4952-AC6A-AF0BA3FF0CA1" Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3776.700.51\)) Subject: Re: [PHP-DEV] Asymmetric visibility is a BC break Date: Wed, 9 Oct 2024 18:20:37 +0200 In-Reply-To: Cc: php internals To: Valentin Udaltsov References: X-Mailer: Apple Mail (2.3776.700.51) From: claude.pache@gmail.com (Claude Pache) --Apple-Mail=_B0C0C92A-CB8F-4952-AC6A-AF0BA3FF0CA1 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 > Le 9 oct. 2024 =C3=A0 17:01, Valentin Udaltsov = a =C3=A9crit : >=20 > Hi, internals! >=20 > Since writing https://externals.io/message/125740 I've realized that > the major problem with aviz is actually simple but fundamental. >=20 > In PHP <=3D8.0 this code is valid for an object of any user class: >=20 > ```php > class User > { > public string $name =3D 'Bob'; > } >=20 > $object =3D new User(); >=20 > foreach ($object as $property =3D> $value) { > $object->{$property} =3D 'Jack'; > } > ``` >=20 > The same is true for Reflection API: once you check that `(new > ReflectionProperty(Foo::class, 'property'))->isPublic()`, you can > safely read property and write to it from any scope. >=20 > Now let's jump to PHP 8.1+ with support for readonly properties. >=20 > A readonly property is two functionalities in one: write-once and > private set. This means that `public readonly $property` is actually > `public(get) private(set) readonly $property`. Although it is marked > as `public`, it is not public because it is not a symmetric public > property! >=20 > In PHP 8.1+, the following User class suddenly breaks the code above: >=20 > ```php > class User > { > public function __construct( > public readonly string $name =3D 'Bob', > ) {} > } >=20 > $object =3D new User(); >=20 > foreach ($object as $property =3D> $value) { > // Fatal error: Uncaught Error: Cannot modify readonly property = User::$name > $object->{$property} =3D 'Jack'; > } > ``` >=20 > In other words, the meaning of `public` has changed in PHP 8.1. > Before, it used to mean "symmetric", now it means "symmetric unless > readonly". >=20 > While not explicitly stated in changelogs, this was a BC break, > because a changed semantic of smth that existed before is a BC break. >=20 > Did it break anything? Of course it did! See: > - https://github.com/doctrine/orm/issues/10049 > - https://github.com/symfony/symfony/pull/46840 > - https://github.com/Ocramius/GeneratedHydrator/issues/656 > - https://github.com/opis/closure/issues/129 >=20 > I believe there are still many places where the concept of "public" > needs to be adjusted to fully support readonly properties. >=20 > Now in PHP 8.4 asymmetry will be made explicit and will allow users to > specify visibility for setters. However, the core issue remains > unresolved: >=20 > ```php > final class User > { > public function __construct( > public private(set) string $name =3D 'Bob', > ) {} > } >=20 > $object =3D new User(); >=20 > foreach ($object as $property =3D> $value) { > // Fatal error: Uncaught Error: Cannot modify private(set) > property User::$name from global scope > $object->{$property} =3D 'Jack'; > } > ``` >=20 > I'd like to draw your attention to the fact that aviz introduces a BC > break, despite saying "Backward Incompatible Changes: None. This > syntax would have been a parse error before." While the syntax is new, > it allows one to alter the old concept of public by changing set > visibility. >=20 > What can we do about it: > 1. Explicitly introduce the concept of getter and setter visibility, > preserve `ReflectionProperty::isPublic()` behavior from PHP <=3D8.0 = and > add `ReflectionProperty::(get|set)Is(Public|Protected|Private)` > methods. I have explained all these ideas in > https://externals.io/message/125740 . If this option is chosen, aviz > will likely need to be reverted and reintroduced in PHP 8.5, since > we're already in the feature freeze period. > 2. Proceed with the current approach, but clearly explain the BC break > in the changelog, and merge this PR > https://github.com/php/php-src/pull/16209 to mitigate reflection > issues as outlined in https://externals.io/message/125740. >=20 > -- > Best regards, > Valentin Hi Valentin, There is no BC break, in the sense that code that worked under PHP 8.3 = (and therefore use PHP 8.3 features only) will not break when run under = PHP 8.4. Of course, code that makes assumptions that are true when using PHP 8.3 = features only, will need to be adapted as soon as PHP 8.4 features are = used. This is unsurprising and expected. Yes, it means that libraries that make use of Reflection are not = automatically compatible with PHP 8.4+ without amendment. By nature, = such libraries cannot claim to be compatible with arbitrary future = versions of PHP.=20 That said, https://github.com/php/php-src/pull/16209 is interesting to = have, but not mandatory. The current = `ReflectionProperty::isPropertySet()` and = `ReflectionProperty::isPrivateSet()` might be somewhat confusing at = first, but they are sufficient in order to obtain the needed = information. =E2=80=94Claude --Apple-Mail=_B0C0C92A-CB8F-4952-AC6A-AF0BA3FF0CA1 Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=utf-8

Le = 9 oct. 2024 =C3=A0 17:01, Valentin Udaltsov = <udaltsov.valentin@gmail.com> a =C3=A9crit :

Hi, = internals!

Since writing https://externals.io/message/125740 I've = realized that
the major problem with aviz is actually simple but = fundamental.

In PHP <=3D8.0 this code is valid for an object = of any user class:

```php
class User
{
=   public string $name =3D 'Bob';
}

$object =3D new = User();

foreach ($object as $property =3D> $value) {
=    $object->{$property} =3D = 'Jack';
}
```

The same is true for Reflection API: once you = check that `(new
ReflectionProperty(Foo::class, = 'property'))->isPublic()`, you can
safely read property and write = to it from any scope.

Now let's jump to PHP 8.1+ with support for = readonly properties.

A readonly property is two functionalities = in one: write-once and
private set. This means that `public readonly = $property` is actually
`public(get) private(set) readonly $property`. = Although it is marked
as `public`, it is not public because it is not = a symmetric public
property!

In PHP 8.1+, the following User = class suddenly breaks the code above:

```php
class = User
{
  public function __construct(
=       public readonly string $name =3D = 'Bob',
  ) {}
}

$object =3D new = User();

foreach ($object as $property =3D> $value) {
=   // Fatal error: Uncaught Error: Cannot modify readonly = property User::$name
  $object->{$property} =3D = 'Jack';
}
```

In other words, the meaning of `public` has = changed in PHP 8.1.
Before, it used to mean "symmetric", now it means = "symmetric unless
readonly".

While not explicitly stated in = changelogs, this was a BC break,
because a changed semantic of smth = that existed before is a BC break.

Did it break anything? Of = course it did! See:
- = https://github.com/doctrine/orm/issues/10049
- = https://github.com/symfony/symfony/pull/46840
- = https://github.com/Ocramius/GeneratedHydrator/issues/656
- = https://github.com/opis/closure/issues/129

I believe there are = still many places where the concept of "public"
needs to be adjusted = to fully support readonly properties.

Now in PHP 8.4 asymmetry = will be made explicit and will allow users to
specify visibility for = setters. However, the core issue = remains
unresolved:

```php
final class User
{
=   public function __construct(
=       public private(set) string $name =3D = 'Bob',
  ) {}
}

$object =3D new = User();

foreach ($object as $property =3D> $value) {
=   // Fatal error:  Uncaught Error: Cannot modify = private(set)
property User::$name from global scope
=   $object->{$property} =3D 'Jack';
}
```

I'd = like to draw your attention to the fact that aviz introduces a = BC
break, despite saying "Backward Incompatible Changes: None. = This
syntax would have been a parse error before." While the syntax = is new,
it allows one to alter the old concept of public by changing = set
visibility.

What can we do about it:
1. Explicitly = introduce the concept of getter and setter visibility,
preserve = `ReflectionProperty::isPublic()` behavior from PHP <=3D8.0 and
add = `ReflectionProperty::(get|set)Is(Public|Protected|Private)`
methods. = I have explained all these ideas = in
https://externals.io/message/125740 . If this option is chosen, = aviz
will likely need to be reverted and reintroduced in PHP 8.5, = since
we're already in the feature freeze period.
2. Proceed with = the current approach, but clearly explain the BC break
in the = changelog, and merge this = PR
https://github.com/php/php-src/pull/16209 to mitigate = reflection
issues as outlined in = https://externals.io/message/125740.

--
Best = regards,
Valentin


=
Hi Valentin,

There is no BC break, in the = sense that code that worked under PHP 8.3 (and therefore use PHP 8.3 = features only) will not break when run under PHP = 8.4.

Of course, code that makes assumptions = that are true when using PHP 8.3 features only, will need to be adapted = as soon as PHP 8.4 features are used. This is unsurprising and = expected.

Yes, it means that libraries that = make use of Reflection are not automatically compatible with PHP 8.4+ = without amendment. By nature, such libraries cannot claim to be = compatible with arbitrary future versions of = PHP. 

That said, https://github.com/php/= php-src/pull/16209 is interesting to have, but not mandatory. = The current `ReflectionProperty::isPropertySet()` and = `ReflectionProperty::isPrivateSet()` might be somewhat confusing at = first, but they are sufficient in order to obtain the needed = information.

=E2=80=94Claude

=

= --Apple-Mail=_B0C0C92A-CB8F-4952-AC6A-AF0BA3FF0CA1--