# [RFC] Deprecate dynamic properties

- Thread: https://externals.io/message/115800
- Original archive: https://news-web.php.net/php.internals/115800
- Started by Nikita Popov on 2021-08-25T10:02:49+00:00
- Messages: 82
- RFC: Deprecate dynamic properties (Implemented (PHP 8.2)) — https://externals.io/rfc/deprecate_dynamic_properties.md
- RFC: Locked Classes (Withdrawn) — https://externals.io/rfc/locked-classes.md
- RFC: Namespace-scoped declares (Inactive) — https://externals.io/rfc/namespace_scoped_declares.md
- Page 1 of 4: messages 1 to 25 (25 per page, add `?page=N` to the URL)
- Next page: https://externals.io/message/115800.md?page=2
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #115800 — Nikita Popov — 2021-08-25T10:02:49+00:00

URL: https://externals.io/message/115800#115800

Hi internals,

I'd like to propose the deprecation of "dynamic properties", that is
properties that have not been declared in the class (stdClass and
__get/__set excluded, of course):

https://wiki.php.net/rfc/deprecate_dynamic_properties

This has been discussed in various forms in the past, e.g. in
https://wiki.php.net/rfc/locked-classes as a class modifier and
https://wiki.php.net/rfc/namespace_scoped_declares /
https://github.com/nikic/php-rfcs/blob/language-evolution/rfcs/0000-language-evolution.md
as a declare directive.

This RFC takes the more direct route of deprecating this functionality
entirely. I expect that this will have relatively little impact on modern
code (e.g. in Symfony I could fix the vast majority of deprecation warnings
with a three-line diff), but may have a big impact on legacy code that
doesn't declare properties at all.

Regards,
Nikita

---

## #115801 — Lynn — 2021-08-25T10:14:57+00:00

URL: https://externals.io/message/115800#115801 · In reply to: #115800

> This RFC takes the more direct route of deprecating this functionality
> entirely. I expect that this will have relatively little impact on modern
> code (e.g. in Symfony I could fix the vast majority of deprecation warnings
> with a three-line diff), but may have a big impact on legacy code that
> doesn't declare properties at all.
>

The project I maintain is massive and it's full of code that implicitly
defines properties. As long as the deprecations are clear, I'm 100% behind
this proposal as it will finally give me leverage to fix the code base at
some point in time.

---

## #115802 — Rowan Tommins — 2021-08-25T10:45:27+00:00

URL: https://externals.io/message/115800#115802 · In reply to: #115800

> I'd like to propose the deprecation of "dynamic properties", that is
> properties that have not been declared in the class (stdClass and
> __get/__set excluded, of course):
>
> https://wiki.php.net/rfc/deprecate_dynamic_properties


This is a bold move, and in principle seems sensible, although I'm 
slightly scared how many places will need fixing in legacy code bases.

I have a couple of concerns with using stdClass as the opt-in mechanism:

* The name of that class already leads to a lot of confusion about its 
purpose - it's not actually "standard" in any way, and new users seeing 
it as a base class are even more likely to mistake it as some kind of 
"universal ancestor". Would it be feasible to introduce an alias like 
"DynamicObject" which more clearly defines its role?

* Adding a parent to an existing class isn't always possible, if it 
already inherits from something else. Perhaps the behaviour could also 
be available as a trait, which defined stub __get and __set methods, 
allowing for the replacement of the internal implementation as you've 
described?


Regards,

-- 
Rowan Tommins
[IMSoP]

---

## #115803 — Denis Ryabov — 2021-08-25T10:52:20+00:00

URL: https://externals.io/message/115800#115803 · In reply to: #115800

> Hi internals,
>
> I'd like to propose the deprecation of "dynamic properties", that is
> properties that have not been declared in the class (stdClass and
> __get/__set excluded, of course):
>
> https://wiki.php.net/rfc/deprecate_dynamic_properties
>
> This has been discussed in various forms in the past, e.g. in
> https://wiki.php.net/rfc/locked-classes as a class modifier and
> https://wiki.php.net/rfc/namespace_scoped_declares /
> https://github.com/nikic/php-rfcs/blob/language-evolution/rfcs/0000-language-evolution.md
> as a declare directive.
>
> This RFC takes the more direct route of deprecating this functionality
> entirely. I expect that this will have relatively little impact on modern
> code (e.g. in Symfony I could fix the vast majority of deprecation warnings
> with a three-line diff), but may have a big impact on legacy code that
> doesn't declare properties at all.
>
> Regards,
> Nikita


I use dynamical properties in a legacy code for lazy object creation in 
the dependency container (with properties declared using @property 
phpDoc comment, so that PhpStorm IDE supports them). But in my case it's 
not a problem to refactor that code.

I'd suggest to consider "use stdClassTrait" (the name is just for 
example) as an alternative to "extends stdClass" and "implements 
stdClassInterface", because 1) It allows to further implement 
__get/__set in that trait, and 2) It's possible to don't require base 
abstract class to extend stdClass when dynamical properties are used in 
a child class only.

---

## #115804 — flaviohbatista@gmail.com — 2021-08-25T10:52:22+00:00

URL: https://externals.io/message/115800#115804 · In reply to: #115802

On Wed, Aug 25, 2021 at 7:46 AM Rowan Tommins <rowan.collins@gmail.com>
wrote:

> 
> > […]
>
>
> This is a bold move, and in principle seems sensible, although I'm
> slightly scared how many places will need fixing in legacy code bases.
>
> I have a couple of concerns with using stdClass as the opt-in mechanism:
>
> * The name of that class already leads to a lot of confusion about its
> purpose - it's not actually "standard" in any way, and new users seeing
> it as a base class are even more likely to mistake it as some kind of
> "universal ancestor". Would it be feasible to introduce an alias like
> "DynamicObject" which more clearly defines its role?
>
> * Adding a parent to an existing class isn't always possible, if it
> already inherits from something else. Perhaps the behaviour could also
> be available as a trait, which defined stub __get and __set methods,
> allowing for the replacement of the internal implementation as you've
> described?
>
>
> Regards,
>
> --
> Rowan Tommins
> [IMSoP]
>
> --
> 
> To unsubscribe, visit: https://www.php.net/unsub.php
>
>
Although not having voting karma, I'm +1 on this.

I've always preferred explicit over implicit code as it makes the learning
path to newcomers a lot easier to grasp.

When we talk about legacy code, more frequently than not is that legacy
code is unlikely to run on the latest version of PHP,
thus I'm not entirely sure if that's a super strong argument against it,
but I may be missing something.

-- 
Atenciosamente,

    Flávio Heleno

---

## #115805 — Marco Pivetta — 2021-08-25T10:55:41+00:00

URL: https://externals.io/message/115800#115805 · In reply to: #115800

Hey Nikita



> Hi internals,
>
> I'd like to propose the deprecation of "dynamic properties", that is
> properties that have not been declared in the class (stdClass and
> __get/__set excluded, of course):
>
> https://wiki.php.net/rfc/deprecate_dynamic_properties
>
> This has been discussed in various forms in the past, e.g. in
> https://wiki.php.net/rfc/locked-classes as a class modifier and
> https://wiki.php.net/rfc/namespace_scoped_declares /
>
> https://github.com/nikic/php-rfcs/blob/language-evolution/rfcs/0000-language-evolution.md
> as a declare directive.
>
> This RFC takes the more direct route of deprecating this functionality
> entirely. I expect that this will have relatively little impact on modern
> code (e.g. in Symfony I could fix the vast majority of deprecation warnings
> with a three-line diff), but may have a big impact on legacy code that
> doesn't declare properties at all.
>

I'm totally on board with this!

Others in the thread have mentioned that these dynamic properties can be
used for lazy-loading purposes, but I think we've demonstrated (over the
years) that this can be achieved in a type-safe way anyway, and dynamic
properties are not needed at all.

Over the past decade, I've not seen a single valid use of dynamic
properties either.

If this passes, I'll also deprecate and discontinue experiments like
https://github.com/Ocramius/LazyProperty

Greets,

Marco Pivetta

http://twitter.com/Ocramius

http://ocramius.github.com/

---

## #115806 — Hamza Ahmad — 2021-08-25T11:05:41+00:00

URL: https://externals.io/message/115800#115806 · In reply to: #115802

HI Nikita,

What if you consider this instead? Rather allowing STD class as an
exception to dynamic properties, why not introduce STDAbstract?
```php
interface STDClassInterface
{
public function __get(string $name) : mixed;
public function __set(string $name, mixed $value = null) : mixed;
/* * other magic methods */
};
abstract class STDClassAbstract implements STDClassInterface {/* *
interface methods */}
Class STDClass extends SDTClassAbstract{}
``

Best
Hamza


> 
> > […]
>
>
> This is a bold move, and in principle seems sensible, although I'm
> slightly scared how many places will need fixing in legacy code bases.
>
> I have a couple of concerns with using stdClass as the opt-in mechanism:
>
> * The name of that class already leads to a lot of confusion about its
> purpose - it's not actually "standard" in any way, and new users seeing
> it as a base class are even more likely to mistake it as some kind of
> "universal ancestor". Would it be feasible to introduce an alias like
> "DynamicObject" which more clearly defines its role?
>
> * Adding a parent to an existing class isn't always possible, if it
> already inherits from something else. Perhaps the behaviour could also
> be available as a trait, which defined stub __get and __set methods,
> allowing for the replacement of the internal implementation as you've
> described?
>
>
> Regards,
>
> --
> Rowan Tommins
> [IMSoP]
>
> --
> 
> To unsubscribe, visit: https://www.php.net/unsub.php

---

## #115807 — Ayesh Karunaratne — 2021-08-25T11:28:13+00:00

URL: https://externals.io/message/115800#115807 · In reply to: #115800

>
> Hi internals,
>
> I'd like to propose the deprecation of "dynamic properties", that is
> properties that have not been declared in the class (stdClass and
> __get/__set excluded, of course):
>
> https://wiki.php.net/rfc/deprecate_dynamic_properties
>
> This has been discussed in various forms in the past, e.g. in
> https://wiki.php.net/rfc/locked-classes as a class modifier and
> https://wiki.php.net/rfc/namespace_scoped_declares /
> https://github.com/nikic/php-rfcs/blob/language-evolution/rfcs/0000-language-evolution.md
> as a declare directive.
>
> This RFC takes the more direct route of deprecating this functionality
> entirely. I expect that this will have relatively little impact on modern
> code (e.g. in Symfony I could fix the vast majority of deprecation warnings
> with a three-line diff), but may have a big impact on legacy code that
> doesn't declare properties at all.
>
> Regards,
> Nikita

Hi Nikita,
Thanks for opening this, I'm very supportive of this.

Having some Drupal and other legacy background, I'm afraid this could
disrupt a lot of Drupal applications, which uses dynamic properties
quite extensively in its Entity APIs, Views, etc. I totally agree that
it is a bad practice, but that's what half a million or so Drupal 7
web sites do.

Perhaps, we can make it an easy to opt-in with a keyword (`locked
class Foo {}`) or an interface (`Foo implements LockedClass {}`), or
provide an easy opt-out (similar to the tentative return type
attribute)?

Thank you,
Ayesh.

---

## #115808 — Björn Larsson via internals — 2021-08-25T11:29:19+00:00

URL: https://externals.io/message/115800#115808 · In reply to: #115800

Den 2021-08-25 kl. 12:02, skrev Nikita Popov:
> Hi internals,
> 
> I'd like to propose the deprecation of "dynamic properties", that is
> properties that have not been declared in the class (stdClass and
> __get/__set excluded, of course):
> 
> https://wiki.php.net/rfc/deprecate_dynamic_properties
> 
> This has been discussed in various forms in the past, e.g. in
> https://wiki.php.net/rfc/locked-classes as a class modifier and
> https://wiki.php.net/rfc/namespace_scoped_declares /
> https://github.com/nikic/php-rfcs/blob/language-evolution/rfcs/0000-language-evolution.md
> as a declare directive.
> 
> This RFC takes the more direct route of deprecating this functionality
> entirely. I expect that this will have relatively little impact on modern
> code (e.g. in Symfony I could fix the vast majority of deprecation warnings
> with a three-line diff), but may have a big impact on legacy code that
> doesn't declare properties at all.
> 
> Regards,
> Nikita
> 
I'm in favour of the proposal as long as the stdClass is leaved as is!
For declared classes I think it's a good idea.

We have legacy code that is littered with dynamic properties using
stdClass. It runs today on PHP 7.4.22 and we plan to migrate to PHP 8
as soon as some Open source libraries works under PHP 8.

Being forced to rewrite this if this proposal changes to also disallow
stdClass dynamic properties would have zero benefits for the app itself
and only incur a cost.

Regards //Björn L

---

## #115809 — Andreas Leathley — 2021-08-25T12:35:59+00:00

URL: https://externals.io/message/115800#115809 · In reply to: #115802

> * Adding a parent to an existing class isn't always possible, if it
> already inherits from something else. Perhaps the behaviour could also
> be available as a trait, which defined stub __get and __set methods,
> allowing for the replacement of the internal implementation as you've
> described?
>
Couldn't this be easily done in userland with the ArrayLikeObject (an
example in the RFC) or something similar, just done as a trait? Maybe
having an example trait implementation that basically "imitates"
stdClass in terms of dynamic properties would be helpful to show in the
RFC, to highlight a possible resolution path for legacy code with this
problem. This could be an example implementation:

trait DynamicPropertiesTrait
{
     private array $dynamicProperties = [];
     public function &__get(string $name): mixed { return
$this->dynamicProperties[$name]; }
     public function __isset(string $name): bool { return
isset($this->dynamicProperties[$name]); }
     public function __set(string $name, mixed $value): void {
$this->dynamicProperties[$name] = $value; }
     public function __unset(string $name): void {
unset($this->dynamicProperties[$name]); }
}

---

## #115810 — Nikita Popov — 2021-08-25T12:45:35+00:00

URL: https://externals.io/message/115800#115810 · In reply to: #115802

On Wed, Aug 25, 2021 at 12:45 PM Rowan Tommins <rowan.collins@gmail.com>
wrote:

> 
> > […]
>
>
> This is a bold move, and in principle seems sensible, although I'm
> slightly scared how many places will need fixing in legacy code bases.
>
> I have a couple of concerns with using stdClass as the opt-in mechanism:
>
> * The name of that class already leads to a lot of confusion about its
> purpose - it's not actually "standard" in any way, and new users seeing
> it as a base class are even more likely to mistake it as some kind of
> "universal ancestor". Would it be feasible to introduce an alias like
> "DynamicObject" which more clearly defines its role?
>
> * Adding a parent to an existing class isn't always possible, if it
> already inherits from something else. Perhaps the behaviour could also
> be available as a trait, which defined stub __get and __set methods,
> allowing for the replacement of the internal implementation as you've
> described?
>

So, a few thoughts: First of all, I should say that "extends stdClass" is
not so much an explicit escape hatch I built into this, it's more something
that arises naturally: We obviously need to keep support for dynamic
properties on stdClass, and if we do so, I would expect that to apply to
subclasses as well. As such, I believe that the "extends stdClass" escape
hatch is something that's going to work anyway -- the only question would
be whether we want to provide additional opt-ins in the form of
interfaces/traits/attributes/etc.

Second, I consider "extends stdClass" to be something of a last-ditch
option. If you encounter a dynamic property deprecation warning, you should
generally resolve it in some other way, and only fall back to "extends
stdClass" as the final option.

All that said, yes, we could add a trait. It would basically be
ArrayLikeObject from the RFC with "class" replaced by "trait". However, I'm
not sure this is really worthwhile. The nice thing about extending stdClass
is that it a) gives you much more efficient property access than
__get/__set and b) matches current behavior. Implementing such a trait,
even if bundled, doesn't really give you any advantages over implemening
__get/__set yourself. It would not make use of an optimized implementation
(or at least, the implementation would still be calling real __get/__set
methods) and the behavior would be limited to what the trait provides. A
custom implementation is not significantly harder (the minimal
implementation is five lines of code) but gives you more control, e.g. you
might want just the minimal implementation, or you might want to also
support Traversable, have a custom __debugInfo(), etc.

My preliminary position would be that if a) you can't avoid dynamic
properties in any other way and b) you can't extend stdClass either, then
you should go with c) implementing __get/__set yourself, as appropriate for
the given use-case.

Regards,
Nikita

---

## #115811 — Rowan Tommins — 2021-08-25T13:51:09+00:00

URL: https://externals.io/message/115800#115811 · In reply to: #115810

> We obviously need to keep support for dynamic properties on stdClass, 
> and if we do so, I would expect that to apply to subclasses as well.

Does that actually follow, though? Right now, there is no advantage to 
extending stdClass, so no reason to expect existing code to do so, and 
no reason for people doing so to expect it to affect behaviour.


> Second, I consider "extends stdClass" to be something of a last-ditch 
> option. If you encounter a dynamic property deprecation warning, you 
> should generally resolve it in some other way, and only fall back to 
> "extends stdClass" as the final option.


That's a reasonable argument in terms of the multiple inheritance case.

My concern about the name remains though: people already do get confused 
by the name "stdClass", because it's not in any way "standard", and 
tells you nothing about what it does.

Reading "class Foo extends stdClass" gives the reader no clues what 
magic behaviour is being inherited; "class Foo extends DynamicObject" 
would be much more clear. Similarly, "$foo = new DynamicObject; 
$foo->bar = 42;" is clearer than "$foo = new stdClass; $foo->bar = 42;"

Regards,

-- 
Rowan Tommins
[IMSoP]

---

## #115812 — Nikita Popov — 2021-08-25T14:00:44+00:00

URL: https://externals.io/message/115800#115812 · In reply to: #115811

On Wed, Aug 25, 2021 at 3:51 PM Rowan Tommins <rowan.collins@gmail.com>
wrote:

> 
>
> > […]
>
> Does that actually follow, though? Right now, there is no advantage to
> extending stdClass, so no reason to expect existing code to do so, and
> no reason for people doing so to expect it to affect behaviour.
>

Isn't this a direct consequence of LSP? We can't just drop behavior when
extending.

Only way for this not to follow would be if we disallowed extending from
stdClass entirely, which we presumably don't want to do.

> Second, I consider "extends stdClass" to be something of a last-ditch
> > […]
>
>
> That's a reasonable argument in terms of the multiple inheritance case.
>
> My concern about the name remains though: people already do get confused
> by the name "stdClass", because it's not in any way "standard", and
> tells you nothing about what it does.
>
> Reading "class Foo extends stdClass" gives the reader no clues what
> magic behaviour is being inherited; "class Foo extends DynamicObject"
> would be much more clear. Similarly, "$foo = new DynamicObject;
> $foo->bar = 42;" is clearer than "$foo = new stdClass; $foo->bar = 42;"
>

Yes, I can see the argument for aliasing stdClass to something more
meaningful, even independently of this RFC. I'm not sure it will have a big
impact for this use-case though, because using the new name would require
polyfilling it, so I'd expect people would stick to the old name in the
foreseeable future...

Regards,
Nikita

---

## #115813 — Chase Peeler — 2021-08-25T14:04:38+00:00

URL: https://externals.io/message/115800#115813 · In reply to: #115811

On Wed, Aug 25, 2021 at 9:51 AM Rowan Tommins <rowan.collins@gmail.com>
wrote:

> 
>
> > […]
>
> Does that actually follow, though? Right now, there is no advantage to
> extending stdClass, so no reason to expect existing code to do so, and
> no reason for people doing so to expect it to affect behaviour.
>
>
> > […]
>
>
> That's a reasonable argument in terms of the multiple inheritance case.
>
> My concern about the name remains though: people already do get confused
> by the name "stdClass", because it's not in any way "standard", and
> tells you nothing about what it does.
>
> Reading "class Foo extends stdClass" gives the reader no clues what
> magic behaviour is being inherited; "class Foo extends DynamicObject"
> would be much more clear. Similarly, "$foo = new DynamicObject;
> $foo->bar = 42;" is clearer than "$foo = new stdClass; $foo->bar = 42;"
>
> Regards,
>
> --
> Rowan Tommins
> [IMSoP]
>
> --
> 
> To unsubscribe, visit: https://www.php.net/unsub.php
>
>
Please don't do this. Call it bad coding practices or not, but this was
something I've considered a feature of PHP and have actually built things
around it. It's not something that can be easily refactored since it was
part of the design.

-- 
Chase Peeler
chasepeeler@gmail.com

---

## #115814 — Pierre — 2021-08-25T14:08:04+00:00

URL: https://externals.io/message/115800#115814 · In reply to: #115811

Le 25/08/2021 à 15:51, Rowan Tommins a écrit :
> 
>
> > […]
>
> Does that actually follow, though? Right now, there is no advantage to 
> extending stdClass, so no reason to expect existing code to do so, and 
> no reason for people doing so to expect it to affect behaviour.
>
>
> > […]
>
>
> That's a reasonable argument in terms of the multiple inheritance case.
>
> My concern about the name remains though: people already do get 
> confused by the name "stdClass", because it's not in any way 
> "standard", and tells you nothing about what it does.
>
> Reading "class Foo extends stdClass" gives the reader no clues what 
> magic behaviour is being inherited; "class Foo extends DynamicObject" 
> would be much more clear. Similarly, "$foo = new DynamicObject; 
> $foo->bar = 42;" is clearer than "$foo = new stdClass; $foo->bar = 42;"
>
> Regards,
>
Hello,

And why not adding an extra keyword, such as:

```
<?php
dynamic class Foo { } // Dynamic allowed
class Bar {} // Dynamic disallowed
?>
```

Regards,

--

Pierre

---

## #115815 — Levi Morrison via internals — 2021-08-25T14:18:34+00:00

URL: https://externals.io/message/115800#115815 · In reply to: #115814

> And why not adding an extra keyword, such as:
>
> ```
> <?php
> dynamic class Foo { } // Dynamic allowed
> class Bar {} // Dynamic disallowed
> ?>
> ```

I am opposed. IMO it's not worth adding anything to the language to
preserve this existing behavior other than allowing `__get`/`__set`,
etc, which already exist for this purpose.

Additionally, there are technical benefits to removing dynamic
properties altogether, so I don't think we should "half" remove it.
See https://wiki.php.net/rfc/deprecate_dynamic_properties#internal_impact.

---

## #115816 — tyson andre — 2021-08-25T14:26:12+00:00

URL: https://externals.io/message/115800#115816 · In reply to: #115800

Hi Nikita Popov,

> I'd like to propose the deprecation of "dynamic properties", that is
> properties that have not been declared in the class (stdClass and
> __get/__set excluded, of course):
> 
> https://wiki.php.net/rfc/deprecate_dynamic_properties
> 
> This has been discussed in various forms in the past, e.g. in
> https://wiki.php.net/rfc/locked-classes as a class modifier and
> https://wiki.php.net/rfc/namespace_scoped_declares /
> https://github.com/nikic/php-rfcs/blob/language-evolution/rfcs/0000-language-evolution.md
> as a declare directive.
> 
> This RFC takes the more direct route of deprecating this functionality
> entirely. I expect that this will have relatively little impact on modern
> code (e.g. in Symfony I could fix the vast majority of deprecation warnings
> with a three-line diff), but may have a big impact on legacy code that
> doesn't declare properties at all.

I'd had some concerns.

It might be too soon after your addition of WeakMap.
https://www.php.net/weakmap was introduced in PHP 8.0 (and WeakReference in 7.4),
so applications/libraries fixing the deprecation may need to drop support for php 7.x early.
Applications attempting to polyfill a WeakMap in earlier PHP versions would potentially leak a lot of memory in php 7.x.

- I don't know how many minor versions to expect before 9.0 is out
- Is it feasible for a developer to create a native PECL polyfill for WeakMap for earlier PHP versions that has a
  subset of the functionality the native weak reference counting does?
  (e.g. to only free polyfilled weak references when cyclic garbage collection is triggered and the reference count is 1).

Additionally, it makes it less efficient (but still feasible) to associate additional fields
with libraries or native classes/PECLs you don't own (especially for circular data structures), especially if they need to be serialized later.
(it isn't possible to serialize WeakMap, and the WeakMap would have fields unrelated to the data being serialized)
I guess you can have a wrapper class that iterates over a WeakMap to capture and serialize the values that still exist in SplObjectStorage, though.
(Though other languages do just fine without this functionality)

I'm not sure if a library owner would want to change their class hierarchy to extend stdClass (to avoid changing the behavior of anything using `$x instanceof stdClass`) and the attribute/trait approach might be more acceptable to library owners.
E.g. https://github.com/vimeo/psalm/blob/master/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php 
would set a dynamic property `$stmt->pure` in `PhpParser\Node\Expr\FuncCall $stmt` in a vendor dependency on php-parser.

Regards,
Tyson

---

## #115817 — Nikita Popov — 2021-08-25T14:49:10+00:00

URL: https://externals.io/message/115800#115817 · In reply to: #115816

On Wed, Aug 25, 2021 at 4:26 PM tyson andre <tysonandre775@hotmail.com>
wrote:

> Hi Nikita Popov,
>
> > […]
> https://github.com/nikic/php-rfcs/blob/language-evolution/rfcs/0000-language-evolution.md
> > […]
> warnings
> > […]
>
> I'd had some concerns.
>
> It might be too soon after your addition of WeakMap.
> https://www.php.net/weakmap was introduced in PHP 8.0 (and WeakReference
> in 7.4),
> so applications/libraries fixing the deprecation may need to drop support
> for php 7.x early.
> Applications attempting to polyfill a WeakMap in earlier PHP versions
> would potentially leak a lot of memory in php 7.x.
>
> - I don't know how many minor versions to expect before 9.0 is out
> - Is it feasible for a developer to create a native PECL polyfill for
> WeakMap for earlier PHP versions that has a
>   subset of the functionality the native weak reference counting does?
>   (e.g. to only free polyfilled weak references when cyclic garbage
> collection is triggered and the reference count is 1).
>

For compatibility with < PHP 7.4 in this use case I'd suggest to either
suppress the deprecation for a particular assignment, or to pick the used
mechanism depending on PHP version.

We don't have an official schedule for major versions, but going by the
last few releases, I'd guess that it happens after PHP 8.4, or around that
time :) I think an approximate PECL polyfill (that tries to free on GC)
would be possible, but I'm not sure it would really be useful relative to
having version-dependent behavior.


> Additionally, it makes it less efficient (but still feasible) to associate
> additional fields
> with libraries or native classes/PECLs you don't own (especially for
> circular data structures), especially if they need to be serialized later.
>

Efficiency probably depends on the case here -- while I'd expect the
dynamic property to be more efficient on access than the WeakMap, it will
likely also use more memory, sometimes much more.


> (it isn't possible to serialize WeakMap, and the WeakMap would have fields
> unrelated to the data being serialized)
> I guess you can have a wrapper class that iterates over a WeakMap to
> capture and serialize the values that still exist in SplObjectStorage,
> though.
> (Though other languages do just fine without this functionality)
>

Right, the WeakMap approach will not impact the serialization of the
object, as well as other property based behaviors, such as comparison
semantics. I would usually count that as an advantage (what you do
shouldn't impact the behavior of 3rd-party classes), but there's probably
use cases for everything :)


> I'm not sure if a library owner would want to change their class hierarchy
> to extend stdClass (to avoid changing the behavior of anything using `$x
> instanceof stdClass`) and the attribute/trait approach might be more
> acceptable to library owners.
> E.g.
> https://github.com/vimeo/psalm/blob/master/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php
> would set a dynamic property `$stmt->pure` in
> `PhpParser\Node\Expr\FuncCall $stmt` in a vendor dependency on php-parser.
>

I wouldn't want library authors to change their class hierarchy for
3rd-party use cases, unless those use cases are actually fundamental to the
library in some way. For PHP-Parser this is actually the case, which is why
it offers the Node::setAttribute() API. The intended way to do this would
be $stmt->setAttribute('pure', true) in this case.

Regards,
Nikita

---

## #115818 — Rowan Tommins — 2021-08-25T14:49:32+00:00

URL: https://externals.io/message/115800#115818 · In reply to: #115812

> I'm not sure it will have a big impact for this use-case though, 
> because using the new name would require polyfilling it, so I'd expect 
> people would stick to the old name in the foreseeable future...


Not necessarily - it depends what versions you need to support, and what 
your migration strategy is.

Since it's just a deprecation notice initially, you could support 8.1 
and 8.2 on the same code base with no changes at all - that's what 
deprecation notices are for, to give a lead time before changes are 
mandatory.

Once the code base no longer needed to support 8.1, "extends 
DynamicObject" could be added. For many code bases, that will be long 
before the feature is actually removed - for a private project, maybe as 
soon as it's on 8.2 in production.

Regards,

-- 
Rowan Tommins
[IMSoP]

---

## #115819 — Sara Golemon — 2021-08-25T15:28:00+00:00

URL: https://externals.io/message/115800#115819 · In reply to: #115800

> I'd like to propose the deprecation of "dynamic properties", that is
> properties that have not been declared in the class (stdClass and
> __get/__set excluded, of course):
>
> https://wiki.php.net/rfc/deprecate_dynamic_properties
>

> This RFC offers `extends stdClass` as a way to opt-in to the use of
dynamic properties.
>
This makes the assumption that existing uses of dynamic properties are all
root classes. I don't think that assumption can be made.

> Some people have suggested that we could use a magic marker
> interface (implements SupportsDynamicProperties),
> an attribute (#[SupportsDynamicProperties])
> or a trait (use DynamicProperties;) instead.
>
My gut-check says an Attribute works well enough.  This will unlock the
class (disable deprecation warning) in 8.2+ and be ignored in earlier
releases (8.0 and 8.1 would need an auto-loaded polyfill userspace
attribute obviously, but that's a tractable problem).

#[SupportsDynamicProperties]
class Foo { ... }

> The Locked classes RFC took an alternative approach to this problem space:
> Rather than deprecating/removing dynamic properties and providing an
opt-in
> for specific classes, it instead allowed marking specific classes as
locked in
> order to forbid creation of dynamic properties on them.
>
> I don't believe that this is the right strategy, because in contemporary
code,
> classes being “locked” is the default state, while classes that require
dynamic
> properties are a rare exception. Additionally, this requires that class
owners
> (which may be 3rd party packages) consistently add the “locked” keyword
> to be effective.
>
I struggle here, because the opt-in approach is the most BC, but I actually
think you're right.  I think[citation needed] that dynamic props are
probably rare enough that as long as the escape hatch is clear, we can be a
little more bold about nudging the ecosystem forward.  I will counter
however, that the same issue with 3rd party libraries applies to opt-out as
opt-in.  A third party library that's undermaintained (and uses dynamic
props) won't be able to be used out of the box once we apply this.  I don't
think that's enough to scare us off, it just means that the opt-in side of
that argument cancels out.

-Sara

---

## #115820 — Larry Garfield — 2021-08-25T16:05:05+00:00

URL: https://externals.io/message/115800#115820 · In reply to: #115819

> 
> > […]
> 
> > […]
> dynamic properties.
> > […]
> This makes the assumption that existing uses of dynamic properties are all
> root classes. I don't think that assumption can be made.
> 
> > […]
> My gut-check says an Attribute works well enough.  This will unlock the
> class (disable deprecation warning) in 8.2+ and be ignored in earlier
> releases (8.0 and 8.1 would need an auto-loaded polyfill userspace
> attribute obviously, but that's a tractable problem).
> 
> #[SupportsDynamicProperties]
> class Foo { ... }
> 
> > […]
> opt-in
> > […]
> locked in
> > […]
> code,
> > […]
> dynamic
> > […]
> owners
> > […]
> I struggle here, because the opt-in approach is the most BC, but I actually
> think you're right.  I think[citation needed] that dynamic props are
> probably rare enough that as long as the escape hatch is clear, we can be a
> little more bold about nudging the ecosystem forward.  I will counter
> however, that the same issue with 3rd party libraries applies to opt-out as
> opt-in.  A third party library that's undermaintained (and uses dynamic
> props) won't be able to be used out of the box once we apply this.  I don't
> think that's enough to scare us off, it just means that the opt-in side of
> that argument cancels out.
> 
> -Sara

I am overall in favor of this move and making declared-only the default.  What I'm still undecided on is what escape hatch should be allowed.  I agree that "you can extend stdClass already" isn't a good one, because that isn't viable in many cases.

The most viable options in my mind are "use __get/__set yourself, you heathen" and an attribute.  The benefit of an attribute over an interface or new parent class is that it's silently backward compatible, especially since attributes are comments in <8.0 versions, so legacy code can add that marker and still be compatible with anything from 5.0 on.

The "internal impact" section makes me lean toward the "__get/__set" option, but I don't fully understand the benefits of the impact.  It seems it would make the engine a little tidier and more efficient, but I don't have a good sense for how much more efficient, which would need to be balanced against how much more INefficient user-space code with dynamic properties would become.  (Either using __get/__set itself, or inheriting from a future stdClass that implements them internally.)  IMO we should prioritize engine efficiency over edge-case efficiency (which dynamic properties are these days), but the amount still matters.

I'm currently working on some improvements to the FIG to allow it to release small, incomplete code libraries or shared type libraries.  Perhaps a fully user-space BC trait with the basic behavior (basically what's shown in the RFC) could live there?  (Not speaking on FIG's behalf, here, just musing aloud.)

--Larry Garfield

---

## #115836 — Ben Ramsey — 2021-08-25T20:56:58+00:00

URL: https://externals.io/message/115800#115836 · In reply to: #115813

Chase Peeler wrote on 8/25/21 09:04:
> Please don't do this. Call it bad coding practices or not, but this was
> something I've considered a feature of PHP and have actually built things
> around it. It's not something that can be easily refactored since it was
> part of the design.

Since the stdClass behavior is retained in this proposal, could you
change your base classes to `extend stdClass`?

Cheers,
Ben

---

## #115837 — Ben Ramsey — 2021-08-25T21:02:14+00:00

URL: https://externals.io/message/115800#115837 · In reply to: #115819

Sara Golemon wrote on 8/25/21 10:28:
> My gut-check says an Attribute works well enough.  This will unlock the
> class (disable deprecation warning) in 8.2+ and be ignored in earlier
> releases (8.0 and 8.1 would need an auto-loaded polyfill userspace
> attribute obviously, but that's a tractable problem).
> 
> #[SupportsDynamicProperties]
> class Foo { ... }

I like this approach.

> I struggle here, because the opt-in approach is the most BC, but I actually
> think you're right.  I think[citation needed] that dynamic props are
> probably rare enough that as long as the escape hatch is clear, we can be a
> little more bold about nudging the ecosystem forward.
I'd feel better about this if we had that citation. In modern code, I
agree they are probably rare, but I think there's a lot of legacy code
that makes heavy use of dynamic properties as an important feature of
PHP. (I'm working on one such project right now.)

Cheers,
Ben

---

## #115838 — Jordan LeDoux — 2021-08-25T21:45:02+00:00

URL: https://externals.io/message/115800#115838

> I'd feel better about this if we had that citation. In modern code, I
> agree they are probably rare, but I think there's a lot of legacy code
> that makes heavy use of dynamic properties as an important feature of
> PHP. (I'm working on one such project right now.)
>

I think a question worth asking is whether or not codebases such as that
would have other similar challenges to targeting the intended version. If
the actual removal of dynamic properties, as opposed to deprecation notice,
is targeted for 9.0, it would be safe to assume that older codebases will
already need to do significant work to migrate to that version. I don't say
this to mean that BC breaking doesn't have an impact, but rather, it's
important to consider if other changes already force design updates to old
codebases when considering impact.

We would want to make migration to newer versions as easy as possible, so
providing an escape hatch makes sense. But I think most developers would
anticipate that moving major versions might result in changes such as this
that could be breaking.

Since __get/__set provides an escape hatch and stdClass does as well, I
personally would think that the possible internal optimizations it enables
which Nikita detailed are worth breaking some legacy userland code for a
major version increment.

Jordan

---

## #115839 — Paul Crovella — 2021-08-25T22:05:20+00:00

URL: https://externals.io/message/115800#115839 · In reply to: #115800

>
> Hi internals,
>
> I'd like to propose the deprecation of "dynamic properties", that is
> properties that have not been declared in the class (stdClass and
> __get/__set excluded, of course):
>
> https://wiki.php.net/rfc/deprecate_dynamic_properties
>
> This has been discussed in various forms in the past, e.g. in
> https://wiki.php.net/rfc/locked-classes as a class modifier and
> https://wiki.php.net/rfc/namespace_scoped_declares /
> https://github.com/nikic/php-rfcs/blob/language-evolution/rfcs/0000-language-evolution.md
> as a declare directive.
>
> This RFC takes the more direct route of deprecating this functionality
> entirely. I expect that this will have relatively little impact on modern
> code (e.g. in Symfony I could fix the vast majority of deprecation warnings
> with a three-line diff), but may have a big impact on legacy code that
> doesn't declare properties at all.
>
> Regards,
> Nikita

I'd like to see a section (or just a paragraph) in the RFC summarizing
the benefits of making this change. Right now the introduction says
that using dynamic properties in modern code "is rarely done
intentionally" implying that it's typically done unintentionally,
however I can't remember the last time I saw unintentional use either.
Is this a common cause of bugs? The internal impact section mentions
saving 8 bytes per object plus an unspecified "dramatic" amount for an
object iterated over via `foreach` which sounds nice but leaves me
guesstimating at real-world impact.

I don't much care for dynamic properties, but I also don't much see
them outside legacy code so they're not bringing me troubles either.
Breaking working code, however, would.

---

Next page: https://externals.io/message/115800.md?page=2
