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
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.
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):
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]
On Wed, Aug 25, 2021 at 7:46 AM Rowan Tommins rowan.collins@gmail.com
wrote:
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):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
HI Nikita,
What if you consider this instead? Rather allowing STD class as an
exception to dynamic properties, why not introduce STDAbstract?
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
>
>> 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]
>
> --
>
> To unsubscribe, visit: https://www.php.net/unsub.php
- 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]); }
}
On Wed, Aug 25, 2021 at 12:45 PM Rowan Tommins rowan.collins@gmail.com
wrote:
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):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
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]
On Wed, Aug 25, 2021 at 3:51 PM Rowan Tommins rowan.collins@gmail.com
wrote:
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.
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
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;"
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
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]
On Wed, Aug 25, 2021 at 9:51 AM Rowan Tommins rowan.collins@gmail.com
wrote:
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]--
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
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
On Wed, Aug 25, 2021 at 9:51 AM Rowan Tommins rowan.collins@gmail.com
wrote: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]--
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.
While I tend to really object to BC breakage, when I saw this proposal I was all for it because it disallows a practice that I have seen used all too often in cases where there was no good reason to not declare the properties, and I know that to be fact because the cases I am referring to are ones where I refactored to use declared properties.
That said, I'd be really interested in seeing use-cases where having dynamic properties is essential to an architecture and where it could not be easily refactored. I cannot envision any, but I am sure that I am just limited by the extent of my vision and so would like to know what those use-cases would be.
Speaking of, it would seem like if dynamic properties were to be deprecated PHP should also add a function that would allow registering a callable as global hook to be called every time ANY object has a property dynamically accessed or assigned — conceptually like how spl_autoload_register()
works — so that developers could run their programs to see what properties are used. The hook could collect class names, property names and data types to help developers who need to update their code discover the information required for their class declarations. Because doing it manually is a real PITA. As would be adding __get()/__set() to every class when you have lots of classes and you'd need to delete all that code later anyway.
-Mike
Le 25/08/2021 à 15:51, Rowan Tommins a écrit :
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,
Hello,
And why not adding an extra keyword, such as:
<?php
dynamic class Foo { } // Dynamic allowed
class Bar {} // Dynamic disallowed
?>
Regards,
--
Pierre
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.
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.
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
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.
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
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
On Wed, Aug 25, 2021 at 4:26 PM tyson andre tysonandre775@hotmail.com
wrote:
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).
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
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):
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
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):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
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
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
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):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 crux of the issue is what our end goal is:
- Require users to explicitly annotate classes that use dynamic
properties, but otherwise keep dynamic properties as a fully supported part
of the core language. - Remove support for dynamic properties entirely. The core language only
has declared properties and __get/__set, and nothing else. stdClass is just
a very efficient implementation of __get/__set.
The RFC is currently written under the assumption that the end goal is (2).
To support that, I believe that "extends stdClass" and "use
DynamicProperties" are the only viable approaches. The former, because it
inherits stdClass behavior, the latter because it's literally __get/__set.
An attribute would require retaining the capability to have arbitrary
classes with dynamic properties.
Now, if the actual goal is (1) instead, then I fully agree that using a
#[SupportsDynamicProperties] attribute is the ideal solution. It can be
easily applied anywhere and has no BC concerns. Heck, someone who just
doesn't care could easily slap it on their whole codebase without spending
brain cycles on more appropriate solutions.
I do think that just (1) by itself would already be very worthwhile. If
that's all we can reasonably target, then I'd be fine with the
#[SupportsDynamicProperties] solution. Though if (2) is viable without too
many complications, then I think that would be the better final state to
reach.
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.
I think the argument isn't quite symmetric: A 3rd-party library in
maintenance-only mode has essentially no incentive to go out of their way
and add a "locked" keyword/attribute to their classes. Adding some missing
property declarations to keep working on new PHP versions is a different
matter.
Regards,
Nikita
Hi!
The crux of the issue is what our end goal is:
- Require users to explicitly annotate classes that use dynamic
properties, but otherwise keep dynamic properties as a fully supported part
of the core language.- Remove support for dynamic properties entirely. The core language only
has declared properties and __get/__set, and nothing else. stdClass is just
a very efficient implementation of __get/__set.
I think goal (1) is reasonable for the long run, and goal (2) is taking
it a bit too far for PHP.
--
Stas Malyshev
smalyshev@gmail.com
Hi Stan,
Hi!
The crux of the issue is what our end goal is:
- Require users to explicitly annotate classes that use dynamic
properties, but otherwise keep dynamic properties as a fully supported part
of the core language.- Remove support for dynamic properties entirely. The core language only
has declared properties and __get/__set, and nothing else. stdClass is just
a very efficient implementation of __get/__set.I think goal (1) is reasonable for the long run, and goal (2) is taking
it a bit too far for PHP.
I feel the same way. However my gut feeling tells me that we will end
up with (2) sooner or later. Having StdClass behaving differently
(even if it is technically not, I don't think the "it is a more
efficient get/set implementation" will make it through our users base)
looks inconsistent.
I don't know how others feel about it, I think most want to go the
path to (2) and a more strict, if not almost fully strict PHP. Core
devs to simplify the engine, users to allow them easier checks and
safety in their frameworks or libs. End user don't really bother as
far as I can see. We may have to decide once the long term vision for
PHP and then do the changes accordingly, and more importantly, with
consistency.
Best,
Pierre
@pierrejoye | http://www.libgd.org
Just giving my 2 cents:
- Remove support for dynamic properties entirely.
I support this RFC and I like the end goal to be to remove the dynamic properties entirely.
Dynamic properties are just confusing for beginners. “This is how you declare properties, the scope, the type, name etc.. but you don’t have too…”
I also remember that I’ve fixed more than one bug because I misspelled a property name.
I understand there will be an impact on legacy code but there is help to find dynamic properties (static analysers) and there is also a clear upgrade path.
// Tobias
I don't know how others feel about it, I think most want to go the
path to (2) and a more strict, if not almost fully strict PHP. Core
devs to simplify the engine, users to allow them easier checks and
safety in their frameworks or libs. End user don't really bother as
far as I can see. We may have to decide once the long term vision for
PHP and then do the changes accordingly, and more importantly, with
consistency.
I tend to concur that path (2) would provide the better outcome.
However, eliminating dynamic variables would be eliminating the ability to simulate a(t least one) language feature that some (many?) people have been using dynamic variables to simulate. Maybe as part of deprecation of dynamic variables we should also consider adding this(ese) feature(s) to the PHP language?
There are two approaches I can envision, Both would better enable the S.O.L.I.D. "open-closed" principle in PHP.
1.) Allow PHP class declaration across multiple files — A use-case for this is what I think is effectively the command dispatcher pattern where properties contain instances of classes that might be contributed by 3rd party code to allow those developers to access the instances by named property: e.g. $cms->map->display() where $map is added by a 3rd party library to the class for the $cms instance before $cms is instantiated. See:
https://gist.github.com/mikeschinkel/ed4f9594c105dae7371bfce413399948
Some might view this as "monkey patching" but I view it as a more structured approach, like C# partial classes.
2.) Allow trait usage for a class from another file — This is just a slightly different spin on #1. See:
https://gist.github.com/mikeschinkel/5b433532dc54adf53f5b239c8086fd63
Each approach has its own pros and cons but it probably depends on which would be easier and more performant to implement.
-Mike
The crux of the issue is what our end goal is:
- Require users to explicitly annotate classes that use dynamic
properties,
but otherwise keep dynamic properties as a fully supported part of the
core language.
That's how I initially read your RFC, what I'm hearing here is that your
quite explicit goal is:
- Remove support for dynamic properties entirely. The core language only
has
declared properties and __get/__set, and nothing else. stdClass is just a
very
efficient implementation of __get/__set.
If that's your position, then the extends stdClass
approach makes sense,
because the dynamic properties HashTable can live in the object's extra
data offset block and the get_prop/set_prop implementations in stdClass'
handlers are the only things which know about dynamic properties. That
represents a significant advantage to the engine for the "happy path" of
declared properties only. Every (non-dynamic) object is a word (8 bytes)
leaner, and the branch points for dynamic property access are limited to
the only place they can be used, and where the typical result is positive.
Starting from that argument, your RFC begins to look more compelling. I
was definitely not starting there on an initial read.
I do still have reservations about enforcing stdClass as a root for any
class which wants to use dynamic properties. It's messy, but... to
paraphrase what you said about going with an attribute approach... yeah,
someone could make every current root class in their application explicitly
inherit from stdClass without any ill effects in PHP < 8.2, with only the
exception of external libraries being a problem.
So that's the spectrum:
- Opt-in to strict behavior: No BC impact, least benefit to the ecosystem
or the engine - Attribute/Interface opt-out (goal#1): Moderate BC impact, ecosystem
benefits well, engine benefits a little, but not much. - Extend stdClass opt-out (goal#2): Highest BC impact, ecosystem and engine
benefit most.
Goal#2 is bold. Possibly not-too-bold, but it's not clear to me where
dynamic props are being used today and how many of those places can't "just
extend stdClass" for one reason or another.
Goal#1 might be a stepping stone along the way to Goal#2, as it will give
us something clear to scan for across major frameworks at the very least,
but that is going to push us from removal in 9.0 all the way out to removal
in 10.0. If you're keeping track, that's gonna be circa 2030 give or take
a year or two. That might be dangling the carrot out a little too far...
The purist in me wants to be bold. It also wants some hard data on
existing usages. A simple grep isn't going to cut it this time. We're
going to need to run some static analyzers on some frameworks and
libraries. Who's got it in them to do the research?
-Sara
We're
going to need to run some static analyzers on some frameworks and
libraries. Who's got it in them to do the research?-Sara
I'm not volunteering, but I forked Nikita's package analysis to add Psalm
scanning a while ago: https://github.com/muglug/popular-package-analysis
It's not trivial (downloading the requisite files takes up a lot of HDD
space) but Psalm's output can be parsed and analysed looking for
UndefinedPropertyFetch and UndefinedThisPropertyFetch issues.
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.
Hi.
A chunk in the RFC:
abstract class Constraint {
public $groups;
public function __construct() {
unset($this->groups);
}
public function __get($name) {
// Will get called on first access, but once initialized.
$this->groups = ...;
}
}
Interesting! new to me.
Maybe someday we will have this one:
abstract class Constraint {
public lazy $groups;
public function __construct() {
// no need to do this:
// unset($this->groups);
}
public function __get($name) {
// Will get called on first access, but once initialized.
$this->groups = ...;
}
}
Regards,
Hendra Gunawan.
Le Wed, 25 Aug 2021 12:02:49 +0200,
Nikita Popov nikita.ppv@gmail.com a écrit :
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):
If I understand correctly the RFC, in these two classes:
class A {
public $prop;
public function __construct() {
unset($this->prop);
}
}
class B {
public $prop;
}
The property $prop is not in the same state in the end? What is the difference?
isset returns FALSE
for both, no? And property_exists?
Is it something that was the same before the RFC and would be different after,
or is it already two different cases and how?
Côme
On Mon, Aug 30, 2021 at 2:14 PM Côme Chilliet <
come.chilliet@fusiondirectory.org> wrote:
Le Wed, 25 Aug 2021 12:02:49 +0200,
Nikita Popov nikita.ppv@gmail.com a écrit :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):If I understand correctly the RFC, in these two classes:
class A {
public $prop;public function __construct() { unset($this->prop); }
}
class B {
public $prop;
}The property $prop is not in the same state in the end? What is the
difference?
isset returnsFALSE
for both, no? And property_exists?
In the latter case, the property has value null. In the former case, it is
unset. In both cases, it is declared. Accessing an unset property will
trigger __get/__set. Accessing a null property will not (assuming it is
visible).
Is it something that was the same before the RFC and would be different
after,
or is it already two different cases and how?
This RFC does not have any impact on this behavior. This is a "standard"
pattern used for lazy initialization.
Regards,
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.Regards,
Nikita
Based on the received feedback, I've updated the RFC to instead provide an
#[AllowDynamicProperties] attribute as a way to opt-in to the use of
dynamic properties. As previously discussed, this won't allow us to
completely remove dynamic properties from the language model anymore, but
it will make the upgrade path smoother by avoiding multiple inheritance
issues. Especially given recent feedback on backwards-incompatible changes,
I think it's prudent to go with the more conservative approach.
Regards,
Nikita
Based on the received feedback, I've updated the RFC to instead provide an
#[AllowDynamicProperties] attribute as a way to opt-in to the use of
dynamic properties. As previously discussed, this won't allow us to
completely remove dynamic properties from the language model anymore, but
it will make the upgrade path smoother by avoiding multiple inheritance
issues.
Quoting the updated RFC:
We may still wish to remove dynamic properties entirely at some later
point. Having the #[AllowDynamicProperties] attribute will make it much
easier to evaluate such a move, as it will be easier to analyze how much
and in what way dynamic properties are used in the ecosystem.
But in this place it does not mention PHP 9.0. Other places still
mention converting it to an error for PHP 9.0. Is that still the plan?
On Tue, Oct 12, 2021 at 3:52 PM Levi Morrison levi.morrison@datadoghq.com
wrote:
Based on the received feedback, I've updated the RFC to instead provide
an
#[AllowDynamicProperties] attribute as a way to opt-in to the use of
dynamic properties. As previously discussed, this won't allow us to
completely remove dynamic properties from the language model anymore, but
it will make the upgrade path smoother by avoiding multiple inheritance
issues.Quoting the updated RFC:
We may still wish to remove dynamic properties entirely at some later
point. Having the #[AllowDynamicProperties] attribute will make it much
easier to evaluate such a move, as it will be easier to analyze how much
and in what way dynamic properties are used in the ecosystem.But in this place it does not mention PHP 9.0. Other places still
mention converting it to an error for PHP 9.0. Is that still the plan?
The current RFC proposes to make dynamic properties an error for classes
without #[AllowDynamicProperties] in PHP 9.0. On the other hand, classes
using the attribute will be able to continue using dynamic properties
without error. (That is, as usual: Deprecations are converted to error in
the next major version.)
Regards,
Nikita
Based on the received feedback, I've updated the RFC to instead provide an
#[AllowDynamicProperties] attribute as a way to opt-in to the use of
dynamic properties. As previously discussed, this won't allow us to
completely remove dynamic properties from the language model anymore, but
it will make the upgrade path smoother by avoiding multiple inheritance
issues. Especially given recent feedback on backwards-incompatible changes,
I think it's prudent to go with the more conservative approach.
I think it would still be the biggest compatibility break since PHP
4->5. Adding a custom property is a common way for an extension to
attach data to an object generated by an uncooperative application or
library.
As the RFC notes, you could migrate to WeakMap, but it will be very
difficult to write code that works on both 7.x and 8.2, since both
attributes and WeakMap were only introduced in 8.0. In MediaWiki
pingback data for the month of September, only 5.2% of users are on
PHP 8.0 or later.
Also, in the last week, I've been analyzing memory usage of our
application. I've come to a new appreciation of the compactness of
undeclared properties on classes with sparse data. You can reduce
memory usage by removing the declaration of any property that is null
on more than about 75% of instances. CPU time may also benefit due to
improved L2 cache hit ratio and reduced allocator overhead.
So if the point of the RFC is to eventually get rid of property
hashtables from the engine, I'm not sure if that would actually be a
win for performance. I'm more thinking about the need for a sparse
attribute which moves declared properties out of properties_table.
I would support an opt-in mechanism, although I think 8.2 is too soon
for all core classes to opt in to it. We already have classes that
throw an exception in __set(), so it would be nice to have some
syntactic sugar for that.
-- Tim Starling
On Wed, Oct 13, 2021 at 3:43 AM Tim Starling tstarling@wikimedia.org
wrote:
Based on the received feedback, I've updated the RFC to instead provide
an
#[AllowDynamicProperties] attribute as a way to opt-in to the use of
dynamic properties. As previously discussed, this won't allow us to
completely remove dynamic properties from the language model anymore, but
it will make the upgrade path smoother by avoiding multiple inheritance
issues. Especially given recent feedback on backwards-incompatible
changes,
I think it's prudent to go with the more conservative approach.I think it would still be the biggest compatibility break since PHP
4->5. Adding a custom property is a common way for an extension to
attach data to an object generated by an uncooperative application or
library.As the RFC notes, you could migrate to WeakMap, but it will be very
difficult to write code that works on both 7.x and 8.2, since both
attributes and WeakMap were only introduced in 8.0. In MediaWiki
pingback data for the month of September, only 5.2% of users are on
PHP 8.0 or later.
Just to be clear on this point: While attributes have only been introduced
in 8.0, they can still be used in earlier versions and are simply ignored
there. It is safe to use #[AllowDynamicProperties] without version
compatibility considerations.
Also, in the last week, I've been analyzing memory usage of our
application. I've come to a new appreciation of the compactness of
undeclared properties on classes with sparse data. You can reduce
memory usage by removing the declaration of any property that is null
on more than about 75% of instances. CPU time may also benefit due to
improved L2 cache hit ratio and reduced allocator overhead.
Huh, that's an interesting problem. Usually I only see the reverse
situation, where accidentally materializing the dynamic property table
(through foreach, array casts, etc) causes issues, because it uses much
more memory than declared properties. Based on a quick calculation, the
cost of having dynamic properties clocks in at 24 declared properties (376
bytes for the smallest non-empty HT vs 16 bytes per declared property), so
that seems like it would usually be a bad trade off unless you already end
up materializing dynamic properties for other reasons.
Did you make sure that you do not materialize dynamic properties (already
before un-declaring some properties)?
So if the point of the RFC is to eventually get rid of property
hashtables from the engine, I'm not sure if that would actually be a
win for performance. I'm more thinking about the need for a sparse
attribute which moves declared properties out of properties_table.
The ability to opt-in to dynamic properties would always remain in some
form (if only through stdClass extension as originally proposed), so if you
have a case where it makes sense, the option would still be there.
Regards,
Nikita
I think it would still be the biggest compatibility break since PHP
4->5.
I think this is a rather large exaggeration. It's certainly a use case
we need to consider, but it's not on the scale of call-time
pass-by-reference, or removing the mysql extension, or a dozen other
changes that have happened over the years.
As the RFC notes, you could migrate to WeakMap, but it will be very
difficult to write code that works on both 7.x and 8.2, since both
attributes and WeakMap were only introduced in 8.0.
Firstly, writing code that works on both 7.x and 8.2 will be easy:
ignore the deprecation notices. As discussed elsewhere, treating a
deprecation as a breaking change makes a nonsense of deprecating anything.
Secondly, I would be surprised if libraries using this trick don't
already have helper functions for doing it, in which case changing those
to use WeakMap and falling back to the dynamic property implementation
on old versions is easy. Maybe I'm missing some difficulty here?
if ( class_exists('\WeakMap') ) {
class Attacher {
private static $map;
public static function setAttachment(object $object, string
$name, $value): void
{
self::$map ??= new WeakMap;
$attachments = self::$map[$object] ?? [];
$attachments[$name] = $value;
self::$map[$object] = $attachments;
}
public static function getAttachment(object $object, string $name)
{
self::$map ??= new WeakMap;
return self::$map[$object][$name] ?? null;
}
}
}
else {
class Attacher {
public static function setAttachment(object $object, string
$name, $value): void
{
$attachments = $object->attached_values ?? [];
$attachments[$name] = $value;
$object->attached_values = $attachments;
}
public static function getAttachment(object $object, string $name)
{
return $object->attached_values[$name] ?? null;
}
}
}
$foo = new Foo;
Attacher::setAttachment($foo, 'hello', 'world');
echo Attacher::getAttachment($foo, 'hello'), "\n";
I would support an opt-in mechanism, although I think 8.2 is too soon
for all core classes to opt in to it. We already have classes that
throw an exception in __set(), so it would be nice to have some
syntactic sugar for that.
I proposed "locked classes" a while ago, but consensus was that this was
the wrong way around.
One suggestion that did come up there was a block-scopable declare which
would allow manipulating dynamic properties, even without the target
class's co-operation. However, I think WeakMap (with a helper and a
fallback like above) is probably a superior solution for that, because
adding properties outside the class always carries risk of name
collision, interaction with __get, etc
Regards,
--
Rowan Tommins
[IMSoP]
Off-topic:
if ( class_exists('\WeakMap') ) {
People should stop using a leading slash in FQCN strings. \Foo::class is
"Foo", not "\Foo". It might work generally but that's asking for problems
(e.g. for autoloaders).
(Sorry.)
On Wed, Oct 13, 2021 at 11:17 AM Guilliam Xavier guilliam.xavier@gmail.com
wrote:
Off-topic:
if ( class_exists('\WeakMap') ) {
People should stop using a leading slash in FQCN strings. \Foo::class
is "Foo", not "\Foo". It might work generally but that's asking for
problems (e.g. for autoloaders).(Sorry.)
And of course I mistyped "leading backslash"... Also externals.io seems to
strip them, so: \Foo::class is "Foo", not "\Foo".
Le 13/10/2021 à 11:17, Guilliam Xavier a écrit :
Off-topic:
if ( class_exists('\WeakMap') ) {
People should stop using a leading slash in FQCN strings. \Foo::class is
"Foo", not "\Foo". It might work generally but that's asking for problems
(e.g. for autoloaders).(Sorry.)
And why not ? using the FQDN is 100% valid, autoloaders should take care
of this, not the end user.
For what it worth, I'm using both, depending upon context, both can make
sense.
Regards,
--
Pierre
Off-topic:
if ( class_exists('\\WeakMap') ) {
People should stop using a leading slash in FQCN strings.
\Foo::class is "Foo", not "\Foo". It might work generally but that's
asking for problems (e.g. for autoloaders).(Sorry.)
Hah, good point. I should probably just have just used ::class anyway
(in which case the leading backslash is the quickest way of the sample
code being pasteable into some other file):
if ( class_exists(\WeakMap::class) ) {
There's probably a bunch of other style no-noes in that example, but I
did at least test it worked. :)
Regards,
--
Rowan Tommins
[IMSoP]
Le 13/10/2021 à 14:42, Rowan Tommins a écrit :
Off-topic:
if ( class_exists('\WeakMap') ) {
People should stop using a leading slash in FQCN strings.
\Foo::class is "Foo", not "\Foo". It might work generally but that's
asking for problems (e.g. for autoloaders).(Sorry.)
Hah, good point. I should probably just have just used ::class anyway
(in which case the leading backslash is the quickest way of the sample
code being pasteable into some other file):if ( class_exists(\WeakMap::class) ) {
There's probably a bunch of other style no-noes in that example, but I
did at least test it worked. :)Regards,
I am surprised that:
if ( class_exists(\WeakMap::class) ) {
Actually works when the class does not exist.
It's not really surprising, I mean the FQDN::class in the end only
resolve to a simple string no matter that the class exists or not, but,
I'm being the devil's advocate here, there are use case were you would
want the engine to crash when you explicitly reference/use a non
existing class, even when it's only its name.
I'm sorry this is off-topic.
--
Pierre
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 plan to open voting on this RFC soon. Most of the feedback was positive,
apart from the initial choice of opt-int mechanism, and that part should be
addressed by the switch to the #[AllowDynamicProperties] attribute.
Regards,
Nikita
Dear Internals,
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 plan to open voting on this RFC soon. Most of the feedback was
positive, apart from the initial choice of opt-int mechanism, and that
part should be addressed by the switch to the
#[AllowDynamicProperties] attribute.
The voting is now open, but I think one thing was not taken into account
here, the many small changes that push work to maintainers of Open
Source library and CI related tools.
In the last few years, the release cadence of PHP has increased, which
is great for new features. It however has not been helpful to introduce
many small deprecations and BC breaks in every single release.
This invariably is making maintainers of Open Source anxious, and
frustrated as so much work is need to keep things up to date. I know
they are deprecations, and applications can turn these off, but that's
not the case for maintainers of libraries.
Before we introduce many more of this into PHP 8.2, I think it would be
wise to figure out a way how to:
- improve the langauge with new features
- keep maintenance cost for open source library and CI tools much lower
- come up with a set of guidelines for when it is necessary to introduce
BC breaks and deprecations.
I am all for improving the language and making it more feature rich, but
we have not spend enough time considering the impacts to the full
ecosystem.
I have therefore voted "no" on this RFC, and I hope you will too.
cheers,
Derick
--
PHP 7.4 Release Manager
Host of PHP Internals News: https://phpinternals.news
Like Xdebug? Consider supporting me: https://xdebug.org/support
https://derickrethans.nl | https://xdebug.org | https://dram.io
twitter: @derickr and @xdebug
Hea all.
Dear Internals,
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 plan to open voting on this RFC soon. Most of the feedback was
positive, apart from the initial choice of opt-int mechanism, and that
part should be addressed by the switch to the
#[AllowDynamicProperties] attribute.The voting is now open, but I think one thing was not taken into account
here, the many small changes that push work to maintainers of Open
Source library and CI related tools.In the last few years, the release cadence of PHP has increased, which
is great for new features. It however has not been helpful to introduce
many small deprecations and BC breaks in every single release.This invariably is making maintainers of Open Source anxious, and
frustrated as so much work is need to keep things up to date. I know
they are deprecations, and applications can turn these off, but that's
not the case for maintainers of libraries.Before we introduce many more of this into PHP 8.2, I think it would be
wise to figure out a way how to:
- improve the langauge with new features
- keep maintenance cost for open source library and CI tools much lower
- come up with a set of guidelines for when it is necessary to introduce
BC breaks and deprecations.I am all for improving the language and making it more feature rich, but
we have not spend enough time considering the impacts to the full
ecosystem.I have therefore voted "no" on this RFC, and I hope you will too.
cheers,
Derick
After some thoughs on this RFC I have reverted my original vote and
voted "No" due to several reasons.
For one thing it is not clear to me what the benefits are. Yes: The
language evolution RFC talks about "Forbidding dynamic object
properties" but it also specifies that "there is also a lot of old code
that does not declare properties, so this needs to be opt-in"[1].
And as far as I can see from the PR associated with this RFC it will not
make life easier for the internals team. It is not like there will be
hundreds of lines code less to maintain. On the contrary. There is more
code and more logic to maintain [2].
So when the only reason for the change is that one line in the RFC ("In
modern code, this is rarely done intentionally"[3]) then that is not
enough of a reasoning for me for such a code change that requires a lot
of existing code to change.
Those that want a cleaner code can already use static code analysis to
find such issues (if not, I'm sure that there will be some analyzers
around before PHP8.2 will be around) or write appropriate tests to make
sure that they do not use undeclared properties.
While I personally would really like to deprecate dynamic properties I
believe that it is the wrong thing to do for the language. At least
given the presented arguments why we should do it.
Cheers
Andreas
PS: Am I the only one missing whether this is a 2/3 or a 50%+1 vote in
the RFC?
[1]
https://github.com/nikic/php-rfcs/blob/language-evolution/rfcs/0000-language-evolution.md#forbidding-dynamic-object-properties
[2] https://github.com/php/php-src/pull/7571/files
[3] https://wiki.php.net/rfc/deprecate_dynamic_properties
--
,,,
(o o)
+---------------------------------------------------------ooO-(_)-Ooo-+
| Andreas Heigl |
| mailto:andreas@heigl.org N 50°22'59.5" E 08°23'58" |
| https://andreas.heigl.org |
+---------------------------------------------------------------------+
| https://hei.gl/appointmentwithandreas |
+---------------------------------------------------------------------+
PS: Am I the only one missing whether this is a 2/3 or a 50%+1 vote in
the RFC?
All votes require a 2/3 majority as of
https://wiki.php.net/rfc/abolish-narrow-margins
--
Rowan Tommins
[IMSoP]
PS: Am I the only one missing whether this is a 2/3 or a 50%+1 vote in
the RFC?All votes require a 2/3 majority as of
https://wiki.php.net/rfc/abolish-narrow-margins
Thanks!
I just stumbled over the fact that some other recent RFCs still had the
statement "requiring a 2/3 majority" in the "Vote" part which I was
missing here.
Cheers
Andreas
,,,
(o o)
+---------------------------------------------------------ooO-(_)-Ooo-+
| Andreas Heigl |
| mailto:andreas@heigl.org N 50°22'59.5" E 08°23'58" |
| https://andreas.heigl.org |
+---------------------------------------------------------------------+
| https://hei.gl/appointmentwithandreas |
+---------------------------------------------------------------------+
Hea all.
Dear Internals,
On Wed, Aug 25, 2021 at 12:02 PM Nikita Popov nikita.ppv@gmail.com
wrote: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 plan to open voting on this RFC soon. Most of the feedback was
positive, apart from the initial choice of opt-int mechanism, and that
part should be addressed by the switch to the
#[AllowDynamicProperties] attribute.The voting is now open, but I think one thing was not taken into account
here, the many small changes that push work to maintainers of Open
Source library and CI related tools.In the last few years, the release cadence of PHP has increased, which
is great for new features. It however has not been helpful to introduce
many small deprecations and BC breaks in every single release.This invariably is making maintainers of Open Source anxious, and
frustrated as so much work is need to keep things up to date. I know
they are deprecations, and applications can turn these off, but that's
not the case for maintainers of libraries.Before we introduce many more of this into PHP 8.2, I think it would be
wise to figure out a way how to:
- improve the langauge with new features
- keep maintenance cost for open source library and CI tools much lower
- come up with a set of guidelines for when it is necessary to introduce
BC breaks and deprecations.I am all for improving the language and making it more feature rich, but
we have not spend enough time considering the impacts to the full
ecosystem.I have therefore voted "no" on this RFC, and I hope you will too.
cheers,
DerickAfter some thoughs on this RFC I have reverted my original vote and
voted "No" due to several reasons.For one thing it is not clear to me what the benefits are. Yes: The
language evolution RFC talks about "Forbidding dynamic object
properties" but it also specifies that "there is also a lot of old code
that does not declare properties, so this needs to be opt-in"[1].And as far as I can see from the PR associated with this RFC it will not
make life easier for the internals team. It is not like there will be
hundreds of lines code less to maintain. On the contrary. There is more
code and more logic to maintain [2].
This RFCs goal is not to have less code to maintain, but to fix a nasty
class of errors in user errors where they accidently write/read to a
dynamic property due to a typo, instead of accessing the declared one.
True this is a mistake of the RFC not to highlight more.
So when the only reason for the change is that one line in the RFC ("In
modern code, this is rarely done intentionally"[3]) then that is not
enough of a reasoning for me for such a code change that requires a lot
of existing code to change.Those that want a cleaner code can already use static code analysis to
find such issues (if not, I'm sure that there will be some analyzers
around before PHP8.2 will be around) or write appropriate tests to make
sure that they do not use undeclared properties.
Code that intentionally or unintentionally uses dynamic properties often
does not write each propery explicitly:
$object->$columnName = $value;
This cannot be detected by static analysers.
For the case where you explitly write a property name, While static
analysis and IDEs do help detecing these as problems, this class of bugs
happens because you are not using an IDE but a text editor like
Vim/Notepad++ where you maybe add a typo to a property name while writing
code.
While I personally would really like to deprecate dynamic properties I
believe that it is the wrong thing to do for the language. At least
given the presented arguments why we should do it.Cheers
Andreas
PS: Am I the only one missing whether this is a 2/3 or a 50%+1 vote in
the RFC?[1]
https://github.com/nikic/php-rfcs/blob/language-evolution/rfcs/0000-language-evolution.md#forbidding-dynamic-object-properties
[2] https://github.com/php/php-src/pull/7571/files
[3] https://wiki.php.net/rfc/deprecate_dynamic_properties--
,,,
(o o)
+---------------------------------------------------------ooO-(_)-Ooo-+
| Andreas Heigl |
| mailto:andreas@heigl.org N 50°22'59.5" E 08°23'58" |
| https://andreas.heigl.org |
+---------------------------------------------------------------------+
| https://hei.gl/appointmentwithandreas |
+---------------------------------------------------------------------+
Hea all.
Dear Internals,
On Wed, Aug 25, 2021 at 12:02 PM Nikita Popov nikita.ppv@gmail.com
wrote: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 plan to open voting on this RFC soon. Most of the feedback was
positive, apart from the initial choice of opt-int mechanism, and that
part should be addressed by the switch to the
#[AllowDynamicProperties] attribute.The voting is now open, but I think one thing was not taken into account
here, the many small changes that push work to maintainers of Open
Source library and CI related tools.In the last few years, the release cadence of PHP has increased, which
is great for new features. It however has not been helpful to introduce
many small deprecations and BC breaks in every single release.This invariably is making maintainers of Open Source anxious, and
frustrated as so much work is need to keep things up to date. I know
they are deprecations, and applications can turn these off, but that's
not the case for maintainers of libraries.Before we introduce many more of this into PHP 8.2, I think it would be
wise to figure out a way how to:
- improve the langauge with new features
- keep maintenance cost for open source library and CI tools much lower
- come up with a set of guidelines for when it is necessary to introduce
BC breaks and deprecations.I am all for improving the language and making it more feature rich, but
we have not spend enough time considering the impacts to the full
ecosystem.I have therefore voted "no" on this RFC, and I hope you will too.
cheers,
DerickAfter some thoughs on this RFC I have reverted my original vote and
voted "No" due to several reasons.For one thing it is not clear to me what the benefits are.
That's my bad! The RFC did not really go into the motivation for the
change, especially after I dropped the discussion of internal motivations
in a later revision.
I hope that the new "Motivation" section clarifies things a bit, especially
in regards to why "static analysis" is only a partial solution to this
problem: https://wiki.php.net/rfc/deprecate_dynamic_properties#motivation
Regards,
Nikita
Hey Nikita.
[...]
I hope that the new "Motivation" section clarifies things a bit, especially
in regards to why "static analysis" is only a partial solution to this
problem: https://wiki.php.net/rfc/deprecate_dynamic_properties#motivation
Thanks for the clarification!
One thing, that can verify the correct working of properties, whether
that is dynamic or static ones, is testing. So when one wants to make
sure that their code behaves as it should, then proper testing can help
with that. So while the static analysis is one possibility, the other
one is writing appropriate tests. While that will still not eliminate
the usage of external tools it is fore sure something that most of the
projects using modern code already implement.
So the mistakes-part would be easy to handle.
Being explicit on the other hand is something that I would very much
appreciate. And using an Annotation for that is great. What I am still
missing is the differentiation between "everything is strict and you
have to explicitly opt-in to make it dynamic" and an "everything is
dynamic and you can use a marker to mark this explicitly an intended
behaviour". That would allow users to mark a class explicitly to use
dynamic features even though it would make no difference code-wise.
And that could already be implemented via a userland-shim right now
which would then use the Core-Attribute as of PHP8.2
The other thing, that I noticed is that you were speaking of a possible
reduction of the object-size by 8 byte. Is there a comparison of how
much that would be in percent for some default applications? So is that
something that actually "pays off"?
Thanks again for your insights!
Cheers
Andreas
,,,
(o o)
+---------------------------------------------------------ooO-(_)-Ooo-+
| Andreas Heigl |
| mailto:andreas@heigl.org N 50°22'59.5" E 08°23'58" |
| https://andreas.heigl.org |
+---------------------------------------------------------------------+
| https://hei.gl/appointmentwithandreas |
+---------------------------------------------------------------------+
One thing, that can verify the correct working of properties, whether
that is dynamic or static ones, is testing.
Can it? I can't actually see how that would work, without also having a
way to detect when dynamic properties were accessed, which brings us
full circle. Also:
So the mistakes-part would be easy to handle.
If you are working with a million lines of code going back 20 years,
"write tests for everything" is not "easy to handle"; it's a long-term
ambition which you chip away at when priorities allow.
"Logging all deprecations and warnings on a production workload",
however, is easy to handle. Diagnostic messages in logs are the only
way this behaviour will be spotted in old code.
What I am still missing is the differentiation between "everything is
strict and you have to explicitly opt-in to make it dynamic" and an
"everything is dynamic and you can use a marker to mark this
explicitly an intended behaviour". That would allow users to mark a
class explicitly to use dynamic features even though it would make no
difference code-wise.
I'm not sure what you mean by that. Do you mean, create the attribute,
but don't actually do anything with it? Would the plan be to deprecate
later? Never remove at all?
Regards,
--
Rowan Tommins
[IMSoP]
One thing, that can verify the correct working of properties, whether
that is dynamic or static ones, is testing.Can it? I can't actually see how that would work, without also having a
way to detect when dynamic properties were accessed, which brings us
full circle. Also:So the mistakes-part would be easy to handle.
If you are working with a million lines of code going back 20 years,
"write tests for everything" is not "easy to handle"; it's a long-term
ambition which you chip away at when priorities allow."Logging all deprecations and warnings on a production workload",
however, is easy to handle. Diagnostic messages in logs are the only
way this behaviour will be spotted in old code.What I am still missing is the differentiation between "everything is
strict and you have to explicitly opt-in to make it dynamic" and an
"everything is dynamic and you can use a marker to mark this
explicitly an intended behaviour". That would allow users to mark a
class explicitly to use dynamic features even though it would make no
difference code-wise.I'm not sure what you mean by that. Do you mean, create the attribute,
but don't actually do anything with it? Would the plan be to deprecate
later? Never remove at all?
A possible idea to help make this transition (which I do support) more gradual:
Instead of an "allow" attribute, introduce a boolean flag attribute.
#[DynamicProperties(true)]
class Beep {}
The attribute marks whether dynamic properties are enabled or disabled via boolean. If disabled, then they're an error if used.
8.2: Introduce the attribute, with a default of TRUE. Exactly zero code breaks, but people can start adding the attribute now. That means people who want to lock-out dynamic properties can do so starting now, and those cases that do need them (or can't easily get away from them) can be flagged far in advance.
8.something: Change the default to FALSE. Using dynamic properties when false throws a deprecation, not an error. People have had some number of years to add the attribute either direction if desired.
9.0: Change the deprecation to an error, if the attribute is set false (which by now is the default) and dynamic properties are used.
Sometime in the future: Setting the attribute to true triggers a deprecation.
Sometime in the future: Remove dynamic properties entirely, and the attribute along with it. People can use __get/__set if they really need.
This still gets us to the same place in the end, but introduces another stage along the way to make the transition more gradual. We can debate which version the default should flip in, I don't much mind. That could even wait for 9 if we want to be really really gradual about it.
Meanwhile, IDEs and code templates can start including the attribute set to false by default on any new classes that get written, just like they have done for years with strict types.
Nikita, would that be feasible? Matthew, Derick, would that be satisfactory?
--Larry Garfield
On Mon, Nov 15, 2021 at 11:21 AM Larry Garfield larry@garfieldtech.com
wrote:
A possible idea to help make this transition (which I do support) more
gradual:Instead of an "allow" attribute, introduce a boolean flag attribute.
#[DynamicProperties(true)]
class Beep {}The attribute marks whether dynamic properties are enabled or disabled via
boolean. If disabled, then they're an error if used.8.2: Introduce the attribute, with a default of TRUE. Exactly zero code
breaks, but people can start adding the attribute now. That means people
who want to lock-out dynamic properties can do so starting now, and those
cases that do need them (or can't easily get away from them) can be flagged
far in advance.
8.something: Change the default to FALSE. Using dynamic properties when
false throws a deprecation, not an error. People have had some number of
years to add the attribute either direction if desired.
This is exactly what Nikita is proposing, except that instead of the
attribute being introduced in PHP 8.2, he's proposing to introduce it
EARLIER. Roughly PHP 2 sort of timeframe.
This is because attributes are forward compatible by design and developers
can already start adding #[AllowDynamicProperties] to their code NOW. Even
their code that was written to run cleanly on PHP 5.6 because users are
terrible at upgrading their servers despite a 2x performance increase.
</rant>
The point is, we don't need 8.2 to be a soft-launch before deprecation
because precisely nobody is prevented from adding this attribute
preemptively.
-Sara
On Mon, Nov 15, 2021 at 11:21 AM Larry Garfield larry@garfieldtech.com
wrote:A possible idea to help make this transition (which I do support) more
gradual:Instead of an "allow" attribute, introduce a boolean flag attribute.
#[DynamicProperties(true)]
class Beep {}The attribute marks whether dynamic properties are enabled or disabled via
boolean. If disabled, then they're an error if used.8.2: Introduce the attribute, with a default of TRUE. Exactly zero code
breaks, but people can start adding the attribute now. That means people
who want to lock-out dynamic properties can do so starting now, and those
cases that do need them (or can't easily get away from them) can be flagged
far in advance.
8.something: Change the default to FALSE. Using dynamic properties when
false throws a deprecation, not an error. People have had some number of
years to add the attribute either direction if desired.This is exactly what Nikita is proposing, except that instead of the
attribute being introduced in PHP 8.2, he's proposing to introduce it
EARLIER. Roughly PHP 2 sort of timeframe.This is because attributes are forward compatible by design and developers
can already start adding #[AllowDynamicProperties] to their code NOW. Even
their code that was written to run cleanly on PHP 5.6 because users are
terrible at upgrading their servers despite a 2x performance increase.
</rant>The point is, we don't need 8.2 to be a soft-launch before deprecation
because precisely nobody is prevented from adding this attribute
preemptively.-Sara
It's not quite the same, although your point about preemptive attributes is valid.
-
If we adopt the RFC right now as-is, the market has ~12 months to add the attribute. If we instead have a default-true flag that changes to default false in the future, it means at minimum 24 months in which to add the attribute to opt-in to dynamic properties.
-
The RFC as-is has no way for developers to opt-in to actively rejecting dynamic properties. They'll get a deprecation, but... we're shouting from the rooftops that deprecations shouldn't be a big red warning, so if you want a big red warning you can't get that until PHP 9. With the flag attribute, developers could opt into "please slap me across the face if I use dynamic properties by accident" in ~12 months when 8.2 ships, rather than waiting 36-48 months until the likely PHP 9 release.
So it gives the nitpickers what they want sooner, and gives the old-codies more time to get their ducks in a row.
--Larry Garfield
On Tue, Nov 16, 2021 at 5:55 PM Larry Garfield larry@garfieldtech.com
wrote:
- If we adopt the RFC right now as-is, the market has ~12 months to add
the attribute. If we instead have a default-true flag that changes to
default false in the future, it means at minimum 24 months in which to add
the attribute to opt-in to dynamic properties.
Okay sure, but that's an argument about lead time, not about
implementation. If we agree on targeting 9.0 for this becoming an error
(and I think we do), then the argument that's left is whether users get
notified as early as 8.2, or if they only get notified as of 8.3.
Personally, I want to give users MORE lead time, but having the deprecation
warnings come up sooner, rather than later. Assuming 9.0 comes out in late
2025 (guesstimate based on the cadence of 7.x), then including the
deprecation with 8.2 (released in late 2022) will give users three years to
attach an attribute where needed. If we delay the deprecation notice until
8.3 (released in late 2023), then they only have two years.
I know this is the opposite end of lead time than you're talking about (you
want max lead time before a deprecation notice even shows up), and here's
why you're wrong: Most users don't pay attention to internals. That extra
year you give them until notices show up will be wasted in ignorance as
they don't know they have any work to do at all. All you will have done is
robbed them of a year to implement the escape hatch (though let's be
honest, they don't even need ONE year to do it). The only developers
paying attention to internals@ traffic are the ones who have literally
already added this attribute to their code bases in anticipation of this
change.
- The RFC as-is has no way for developers to opt-in to actively rejecting
dynamic properties. They'll get a deprecation, but... we're shouting from
the rooftops that deprecations shouldn't be a big red warning, so if you
want a big red warning you can't get that until PHP 9. With the flag
attribute, developers could opt into "please slap me across the face if I
use dynamic properties by accident" in ~12 months when 8.2 ships, rather
than waiting 36-48 months until the likely PHP 9 release.
Your premise is that, since deprecation warnings will be ignored, we should
increase the verbosity level of the warning, but only for people who
clearly know that a problem exists and can opt in to it? I'm sorry, but I
don't track the logic of that.
-Sara
Hey Rowan, hey all
One thing, that can verify the correct working of properties, whether
that is dynamic or static ones, is testing.Can it? I can't actually see how that would work, without also having a
way to detect when dynamic properties were accessed, which brings us
full circle. Also:
When you are testing whether writing 'X' to a property and then reading
from that property gives that 'X' back, then everything should be good.
You either typed the name of the property correctly or you have a typo
in the setter and getter (or however you set and got the value).
Nevertheless you should be able to figure out whether you accidentally
misstyped a property name somewhere as that should break the test
(unless you misstyped the name twice). ANd whether you are doing the
assignement to a static property or to a dynamic one should be
irrelevant, the reading and the writing process are the same.
Yes: That rips off a completely different topic: Testing "getters" and
"setters".
So the mistakes-part would be easy to handle.
If you are working with a million lines of code going back 20 years,
"write tests for everything" is not "easy to handle"; it's a long-term
ambition which you chip away at when priorities allow.
The intention is not to write tests for existing code. As the intention
is exactly to be able to leave existing code as it is. Otherwise the
approach to "add the Attribute and be done" would be much easier.
The intention is much more to make sure that new code does not write
accidentally to the wrong property. Which is what this RFC is all about.
Make sure that dynamic properties are not accidentally used.
"Logging all deprecations and warnings on a production workload",
however, is easy to handle. Diagnostic messages in logs are the only
way this behaviour will be spotted in old code.
That is absolutely correct. The main question is: "Do we need to spot
this behaviour in old code"? Not "Do we want to spot this behaviour in
old code".
What I am still missing is the differentiation between "everything is
strict and you have to explicitly opt-in to make it dynamic" and an
"everything is dynamic and you can use a marker to mark this
explicitly an intended behaviour". That would allow users to mark a
class explicitly to use dynamic features even though it would make no
difference code-wise.I'm not sure what you mean by that. Do you mean, create the attribute,
but don't actually do anything with it? Would the plan be to deprecate
later? Never remove at all?
As currently there is no direct intention to remove the feature for
good, why should we force it? And espechialy why do we need to force
those maintinaing existing code to adapt their code?
For now the possibility could be to keep everything as is. Plus add an
attribute to make absolutely explicit that we want to use this feature.
The next step could then be to create a setting that will notify about
dynamic assignments in classes that are not marked by the attribute. I'm
not talking about a deprecation note here. More something like a notice
(not a warning as that is too severe!). That way the behaviour can come
up in log files. Perhaps a new Level of notice like a
"bad_coding_practice". Or we use different "lanes" of reporting. One for
notices, errors, warnings et al and one for deprecations and such
asignment-oddities.
When that has been done (should so far all be BC) and code-maintainers
have had enough time to modify their codebase (definition of "enough
time" is here clearly the main topic and needs discussion with
maintainers!) then we can talk about possibly phasing out the feature.
My 0.02€
Cheers
Andreas
,,,
(o o)
+---------------------------------------------------------ooO-(_)-Ooo-+
| Andreas Heigl |
| mailto:andreas@heigl.org N 50°22'59.5" E 08°23'58" |
| https://andreas.heigl.org |
+---------------------------------------------------------------------+
| https://hei.gl/appointmentwithandreas |
+---------------------------------------------------------------------+
When you are testing whether writing 'X' to a property and then
reading from that property gives that 'X' back, then everything should
be good.
I see, yes, code that is 100% perfectly tested can get away without the
language performing any error checking at all - the behaviour is all
guaranteed by the tests. I would be very surprised if even 1% of PHP
applications can claim such comprehensive tests.
Yes: That rips off a completely different topic: Testing "getters" and
"setters".
Unless you have zero business logic in your classes, testing getters and
setters is absolutely not sufficient. Everywhere that accesses a private
or protected property has the potential to mistype it and cause subtle bugs.
That is absolutely correct. The main question is: "Do we need to
spot this behaviour in old code"? Not "Do we want to spot this
behaviour in old code".
Yes to both questions. A number of "uses" of this feature are not
actually deliberate uses which need protecting by adding the attribute,
they are mistakes that are going unnoticed in those warrens of untested,
un-analysed code. Those code bases are exactly the ones that benefit
from the language having the run-time checks built in.
Regards,
--
Rowan Tommins
[IMSoP]
Hey all
When you are testing whether writing 'X' to a property and then
reading from that property gives that 'X' back, then everything should
be good.I see, yes, code that is 100% perfectly tested can get away without the
language performing any error checking at all - the behaviour is all
guaranteed by the tests. I would be very surprised if even 1% of PHP
applications can claim such comprehensive tests.
The topic here was that new code can verify the declaration of a
property by using tests. That does not need to happen on the language
level. I was never talking about adding tests for existing code.
Yes: That rips off a completely different topic: Testing "getters" and
"setters".Unless you have zero business logic in your classes, testing getters and
setters is absolutely not sufficient. Everywhere that accesses a private
or protected property has the potential to mistype it and cause subtle
bugs.That is absolutely correct. The main question is: "Do we need to
spot this behaviour in old code"? Not "Do we want to spot this
behaviour in old code".Yes to both questions. A number of "uses" of this feature are not
actually deliberate uses which need protecting by adding the attribute,
they are mistakes that are going unnoticed in those warrens of untested,
un-analysed code. Those code bases are exactly the ones that benefit
from the language having the run-time checks built in.
That highly depends on your view-point. There are a lot of people out
there that do not require that this behaviour needs to be spotted.
Cheers
Andreas
,,,
(o o)
+---------------------------------------------------------ooO-(_)-Ooo-+
| Andreas Heigl |
| mailto:andreas@heigl.org N 50°22'59.5" E 08°23'58" |
| https://andreas.heigl.org |
+---------------------------------------------------------------------+
| https://hei.gl/appointmentwithandreas |
+---------------------------------------------------------------------+
I see, yes, code that is 100% perfectly tested can get away without
the language performing any error checking at all - the behaviour is
all guaranteed by the tests. I would be very surprised if even 1% of
PHP applications can claim such comprehensive tests.The topic here was that new code can verify the declaration of a
property by using tests. That does not need to happen on the language
level. I was never talking about adding tests for existing code.
Whether the code is "new" or "old" is not what matters; what matters is
whether the test suite is comprehensive enough that every possible
mistake will be caught by a test. If it will, we can remove a whole
bunch of language features - why use parameter and property types, for
instance, if your tests guarantee that they will always be given correct
values?
For most code bases, even new ones being written from scratch in PHP
8.0, that level of testing simply doesn't exist, and having the language
tell you "hey, you wrote $this->loger instead of $this->logger" is a
useful feature. And, in a lot of cases, more useful than having the
language say "OK, I've created your dynamic $loger property for you",
which is what currently happens.
Regards,
--
Rowan Tommins
[IMSoP]
On Tue, Nov 16, 2021 at 4:23 AM Rowan Tommins rowan.collins@gmail.com
wrote:
I see, yes, code that is 100% perfectly tested can get away without
the language performing any error checking at all - the behaviour is
all guaranteed by the tests. I would be very surprised if even 1% of
PHP applications can claim such comprehensive tests.The topic here was that new code can verify the declaration of a
property by using tests. That does not need to happen on the language
level. I was never talking about adding tests for existing code.For most code bases, even new ones being written from scratch in PHP
8.0, that level of testing simply doesn't exist, and having the language
tell you "hey, you wrote $this->loger instead of $this->logger" is a
useful feature. And, in a lot of cases, more useful than having the
language say "OK, I've created your dynamic $loger property for you",
which is what currently happens.
What you described there sounds like a warning and not a fatal error. Maybe
that's where some of the trepidation is coming from. I know I'm less
worried about the deprecation notice and more worried about what happens in
PHP 9 when it's a fatal error.
On Tue, Nov 16, 2021 at 4:23 AM Rowan Tommins rowan.collins@gmail.com
wrote:I see, yes, code that is 100% perfectly tested can get away without
the language performing any error checking at all - the behaviour is
all guaranteed by the tests. I would be very surprised if even 1% of
PHP applications can claim such comprehensive tests.The topic here was that new code can verify the declaration of a
property by using tests. That does not need to happen on the language
level. I was never talking about adding tests for existing code.For most code bases, even new ones being written from scratch in PHP
8.0, that level of testing simply doesn't exist, and having the language
tell you "hey, you wrote $this->loger instead of $this->logger" is a
useful feature. And, in a lot of cases, more useful than having the
language say "OK, I've created your dynamic $loger property for you",
which is what currently happens.What you described there sounds like a warning and not a fatal error. Maybe
that's where some of the trepidation is coming from. I know I'm less
worried about the deprecation notice and more worried about what happens in
PHP 9 when it's a fatal error.
I can't say that this line of reasoning has its merits, but then there's no
benefit to the engine itself. Issuing a warning and carry on materializing
dynamic properties will never bring the original performance improvement
that was part of the original state of the RFC.
--
Marco Aurélio Deleu
Hey list.
On Tue, Nov 16, 2021 at 4:23 AM Rowan Tommins rowan.collins@gmail.com
wrote:I see, yes, code that is 100% perfectly tested can get away without
the language performing any error checking at all - the behaviour is
all guaranteed by the tests. I would be very surprised if even 1% of
PHP applications can claim such comprehensive tests.The topic here was that new code can verify the declaration of a
property by using tests. That does not need to happen on the language
level. I was never talking about adding tests for existing code.For most code bases, even new ones being written from scratch in PHP
8.0, that level of testing simply doesn't exist, and having the language
tell you "hey, you wrote $this->loger instead of $this->logger" is a
useful feature. And, in a lot of cases, more useful than having the
language say "OK, I've created your dynamic $loger property for you",
which is what currently happens.What you described there sounds like a warning and not a fatal error. Maybe
that's where some of the trepidation is coming from. I know I'm less
worried about the deprecation notice and more worried about what happens in
PHP 9 when it's a fatal error.I can't say that this line of reasoning has its merits, but then there's no
benefit to the engine itself. Issuing a warning and carry on materializing
dynamic properties will never bring the original performance improvement
that was part of the original state of the RFC.
Which performance improvement of the "original state" of the RFC? As
that was one of the questions that were not 100% answered: What are the
benefits for the language. And while those 8 bit that Nikita mentioned
in the "Motivation" part of the RFC look nice, he also stated that "this
is a fairly long-term benefit that will require additional technical
work to realize".
Or did I miss something?
Cheers
Andreas
,,,
(o o)
+---------------------------------------------------------ooO-(_)-Ooo-+
| Andreas Heigl |
| mailto:andreas@heigl.org N 50°22'59.5" E 08°23'58" |
| https://andreas.heigl.org |
+---------------------------------------------------------------------+
| https://hei.gl/appointmentwithandreas |
+---------------------------------------------------------------------+
<snip>Hey list.
Which performance improvement of the "original state" of the RFC? As
that was one of the questions that were not 100% answered: What are the
benefits for the language. And while those 8 bit that Nikita mentioned
in the "Motivation" part of the RFC look nice, he also stated that "this
is a fairly long-term benefit that will require additional technical
work to realize".
I think these two messages might have some information about the
performance improvements:
https://externals.io/message/115800#115848
https://externals.io/message/115800#115872
Yes, maybe not everything was captured in the final RFC text. I also think
all the evaluated options should have been present in the RFC as it brings
more context to voters.
Regards,
Alex
And as far as I can see from the PR associated with this RFC it will not
make life easier for the internals team. It is not like there will be
hundreds of lines code less to maintain. On the contrary. There is more
code and more logic to maintain [2].
Sometimes it needs to be worse until it's better.
Some points that evolved during discussion also mentioned the intention of
how easy to allow it to be to opt-in and in the end the attribute was
chosen as the easiest one.
Even if the intention was to simplify the code to maintain, it was not
clear how much PHP users would want to stay without this feature.
And the problem was that using the attribute, it would not be easy to
remove it in PHP 9.
But at least it would give a better sense of usage once we get to PHP 9 so
it can be completely removed only in PHP 10+ or to use a more strict
opt-in mechanism.
So yes, you are right, having it like this would make the code a bit worse
to maintain for 8 years and easier to maintain after that, if I got it
right.
But the benefit related to the dynamic properties bugs reduction would be
seen in userland starting with PHP 8.2.
Regards,
Alex
Dear Internals,
On Wed, Aug 25, 2021 at 12:02 PM Nikita Popov nikita.ppv@gmail.com
wrote: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 plan to open voting on this RFC soon. Most of the feedback was
positive, apart from the initial choice of opt-int mechanism, and that
part should be addressed by the switch to the
#[AllowDynamicProperties] attribute.The voting is now open, but I think one thing was not taken into account
here, the many small changes that push work to maintainers of Open
Source library and CI related tools.In the last few years, the release cadence of PHP has increased, which
is great for new features. It however has not been helpful to introduce
many small deprecations and BC breaks in every single release.This invariably is making maintainers of Open Source anxious, and
frustrated as so much work is need to keep things up to date. I know
they are deprecations, and applications can turn these off, but that's
not the case for maintainers of libraries.Before we introduce many more of this into PHP 8.2, I think it would be
wise to figure out a way how to:
- improve the langauge with new features
- keep maintenance cost for open source library and CI tools much lower
- come up with a set of guidelines for when it is necessary to introduce
BC breaks and deprecations.I am all for improving the language and making it more feature rich, but
we have not spend enough time considering the impacts to the full
ecosystem.I have therefore voted "no" on this RFC, and I hope you will too.
cheers,
Derick
I appreciate that Derick. I know there have been some pretty big efforts
required for some recent php updates in Drupal. And I've mentioned in a
couple threads on Twitter but Drupal requires a pretty heavy change to
support this. It adds properties to classes outside its codebase which
means none of the mitigations in the RFC apply. It was on my radar when the
vote popped up because the system in question has long been known to be
less than ideal but "php wouldn't ever remove this ancient feature right?"
and every other option is just a ton more complicated. I've talked with
some maintainers and it looks like we're going to deal with the cost of
converting the system but if this dirty hack hadn't been on our radar it
could have really hurt. Like maybe not getting the fix in place before the
next major release in time for backwards compatibility commitments bad.
So I worry what sort of earthquake this might trigger through libraries.
Like, I'm pretty sure the OpenApiGenerator templates used dynamic
properties for some things so how many little internal libraries are going
to have to be regenerated? What other lightly maintained library are people
going to be stuck waiting to update because someone's CI didn't catch the
deprecation?
I think this sort of change is probably for the better, but I worry about
how disruptive this could end up being.
Dear Internals,
On Wed, Aug 25, 2021 at 12:02 PM Nikita Popov nikita.ppv@gmail.com
wrote: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 plan to open voting on this RFC soon. Most of the feedback was
positive, apart from the initial choice of opt-int mechanism, and that
part should be addressed by the switch to the
#[AllowDynamicProperties] attribute.The voting is now open, but I think one thing was not taken into account
here, the many small changes that push work to maintainers of Open
Source library and CI related tools.In the last few years, the release cadence of PHP has increased, which
is great for new features. It however has not been helpful to introduce
many small deprecations and BC breaks in every single release.This invariably is making maintainers of Open Source anxious, and
frustrated as so much work is need to keep things up to date. I know
they are deprecations, and applications can turn these off, but that's
not the case for maintainers of libraries.Before we introduce many more of this into PHP 8.2, I think it would be
wise to figure out a way how to:
- improve the langauge with new features
- keep maintenance cost for open source library and CI tools much lower
- come up with a set of guidelines for when it is necessary to introduce
BC breaks and deprecations.I am all for improving the language and making it more feature rich, but
we have not spend enough time considering the impacts to the full
ecosystem.I have therefore voted "no" on this RFC, and I hope you will too.
cheers,
DerickI appreciate that Derick. I know there have been some pretty big efforts
required for some recent php updates in Drupal. And I've mentioned in a
couple threads on Twitter but Drupal requires a pretty heavy change to
support this. It adds properties to classes outside its codebase which
means none of the mitigations in the RFC apply. It was on my radar when the
vote popped up because the system in question has long been known to be
less than ideal but "php wouldn't ever remove this ancient feature right?"
and every other option is just a ton more complicated. I've talked with
some maintainers and it looks like we're going to deal with the cost of
converting the system but if this dirty hack hadn't been on our radar it
could have really hurt. Like maybe not getting the fix in place before the
next major release in time for backwards compatibility commitments bad.So I worry what sort of earthquake this might trigger through libraries.
Like, I'm pretty sure the OpenApiGenerator templates used dynamic
properties for some things so how many little internal libraries are going
to have to be regenerated? What other lightly maintained library are people
going to be stuck waiting to update because someone's CI didn't catch the
deprecation?
By design my REST API utilizes dynamic properties. I've always found it to
be a feature of PHP, not a bug.
I think this sort of change is probably for the better, but I worry about
how disruptive this could end up being.
--
Chase Peeler
chasepeeler@gmail.com
By design my REST API utilizes dynamic properties. I've always found it to
be a feature of PHP, not a bug.--
Chase Peeler
chasepeeler@gmail.com
I am in the unfortunate position of inheriting a system with such REST API
design. I can never build new REST APIs to replace the old ones because
nobody can ever know how many combinations of possible input parameters
there are.
--
Marco Aurélio Deleu
By design my REST API utilizes dynamic properties. I've always found it to
be a feature of PHP, not a bug.
--
Chase Peeler
chasepeeler@gmail.comI am in the unfortunate position of inheriting a system with such REST API
design. I can never build new REST APIs to replace the old ones because
nobody can ever know how many combinations of possible input parameters
there are.
Our inputs and outputs are both well defined. I've built a framework around
our entities that convert them into API payloads - attributes (symfony, but
eventually we might update it to use native attributes) define what fields
are visible based on user and whether it's the short (e.g. part of a
collection) or full version. The thing is that occasionally we'll have a
payload to return that requires additional attributes. Dynamic properties
allow us to just tack that on to the existing entity without having to
define it as a property on the entity (outside of the one use case the
property isn't needed and it definitely doesn't correspond to a database
column).
--
Marco Aurélio Deleu
--
Chase Peeler
chasepeeler@gmail.com
Den 2021-11-15 kl. 10:52, skrev Derick Rethans:
Dear Internals,
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 plan to open voting on this RFC soon. Most of the feedback was
positive, apart from the initial choice of opt-int mechanism, and that
part should be addressed by the switch to the
#[AllowDynamicProperties] attribute.The voting is now open, but I think one thing was not taken into account
here, the many small changes that push work to maintainers of Open
Source library and CI related tools.In the last few years, the release cadence of PHP has increased, which
is great for new features. It however has not been helpful to introduce
many small deprecations and BC breaks in every single release.This invariably is making maintainers of Open Source anxious, and
frustrated as so much work is need to keep things up to date. I know
they are deprecations, and applications can turn these off, but that's
not the case for maintainers of libraries.Before we introduce many more of this into PHP 8.2, I think it would be
wise to figure out a way how to:
- improve the langauge with new features
- keep maintenance cost for open source library and CI tools much lower
- come up with a set of guidelines for when it is necessary to introduce
BC breaks and deprecations.I am all for improving the language and making it more feature rich, but
we have not spend enough time considering the impacts to the full
ecosystem.I have therefore voted "no" on this RFC, and I hope you will too.
cheers,
Derick
Hi,
Maybe the RM's should have a mandate to keep track on deprecations for
a specific release and be able to say: "Sorry the shop are closed for
further deprecations in this release".
What do you think?
One could count the deprecations in the 8.x track and have a straw poll
on it and/or ask what key deprecations do we see further for the 8.x?
Is even the "Dynamic properties" one, one of the last ones?
r//Björn L
Hi Nikita, hi everybody,
Le mer. 25 août 2021 à 12:03, Nikita Popov nikita.ppv@gmail.com a écrit :
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.
Thanks for the RFC, it makes sense to me and I support the move.
Since Symfony is mentioned in the RFC, I thought you might want to know
about this PR, that removes dynamic properties from the Symfony codebase:
https://github.com/symfony/symfony/pull/44037/files
What Nikita describes in the RFC is correct: declaring+unsetting the
"groups" property works.
There's just one more thing I had to do; I also had to replace two calls to
property_exists:
-
if (!property_exists($this, 'groups')) {
-
if (!isset(((array) $this)['groups'])) {
The rest are test cases where we've been lazily accepting fixtures with
undeclared properties. No big deal, and I'm happy the engine might soon
help us get a bit stricter in this regard.
I read that some think that this PR is not required because static
analysers (SA) can report when a dynamic property is used. Although that's
correct, I think it would be detrimental to PHP as a language if SA tools
(or any tools actually) were a requirement to code in the safe-n-modern
way. You should not have to install any complex safeguarding tooling
infrastructure to start coding; both for newcomers, but also for
no-so-new-comers.
About the discussion related to deprecations. I've yet to see a better
reporting system than the current one.
It's true that too many userland error handlers are throwing instead of
collecting/logging/skipping deprecations.
But these can be fixed (and many are being fixed these days, which is nice!)
Cheers,
Nicolas
On Mon, Nov 15, 2021 at 11:26 AM Nicolas Grekas <
nicolas.grekas+php@gmail.com> wrote:
Hi Nikita, hi everybody,
Le mer. 25 août 2021 à 12:03, Nikita Popov nikita.ppv@gmail.com a écrit
: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.Thanks for the RFC, it makes sense to me and I support the move.
Since Symfony is mentioned in the RFC, I thought you might want to know
about this PR, that removes dynamic properties from the Symfony codebase:
https://github.com/symfony/symfony/pull/44037/filesWhat Nikita describes in the RFC is correct: declaring+unsetting the
"groups" property works.
There's just one more thing I had to do; I also had to replace two calls to
property_exists:
if (!property_exists($this, 'groups')) {
if (!isset(((array) $this)['groups'])) {
The rest are test cases where we've been lazily accepting fixtures with
undeclared properties. No big deal, and I'm happy the engine might soon
help us get a bit stricter in this regard.I read that some think that this PR is not required because static
analysers (SA) can report when a dynamic property is used. Although that's
correct, I think it would be detrimental to PHP as a language if SA tools
(or any tools actually) were a requirement to code in the safe-n-modern
way. You should not have to install any complex safeguarding tooling
infrastructure to start coding; both for newcomers, but also for
no-so-new-comers.
Its not so true from my POV that static analysis can avoid having this
deprecation:
- static analysis does not work for dynamic assignments,
$object = new SomeDataObject();
$row = $pdo->fetch();
foreach ($row as $column => $value) {
$object->$column = $value;
}
arguably this is one of the important use cases this deprecation fixes.
A second example of this is when doing deserialization into an object from
JSON or XML:
$object = new SomeDataObject();
$objectPayload = json_decode($input, true);
foreach ($objectPayload as $prop => $value) {
$object->$prop = $value;
}
This doesn't apply sole to user input where maybe more validation of input
is necessary, but also for mapping config files to an object.
All this kind of generic code cannot be statically analysed, but this
deprecation and removal has the most value in exactly that use-case.
About the discussion related to deprecations. I've yet to see a better
reporting system than the current one.
It's true that too many userland error handlers are throwing instead of
collecting/logging/skipping deprecations.
But these can be fixed (and many are being fixed these days, which is
nice!)Cheers,
Nicolas
I would be very sad to see this RFC not go through. I have voted Yes as I
believe this "feature":is a bug that needs to be fixed. There is also an
opt-in proposed for people who really do consider it a feature.
I don't see why it would cause much trouble for maintainers of OSS. At the
moment it is proposed to make this change in PHP 9.0, which is a couple
years away. That is a lot of time to fix the code. The deprecation message
will inform us about the number of uses, whether accidental or intentional.
If we decide that removal of this feature would cause too much disruption,
could we not have RFC in PHP 8.3 to remove the deprecation?
Deprecated or not, I still believe that OSS should avoid dynamic
properties. They are really difficult to identify, even with static
analysers. Having the deprecation message would at least help us identify
where dynamic properties were used, so that we can fix it.
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
Hello everyone,
This is going to be a bit wordy, so sorry in advance, but I want to set the
context correctly.
As a userland developer, the overall idea concept I agree with, but the
thing that irks me here is that this is opt-out.
My day to day reality is dealing with complex big projects with codebases
that go into 5 digit file numbers excluding the ./vendor folder.
The projects are not legacy projects - they are being updated and are
actually run on 7.4 in E_ALL
mode, we just finished upgrading it to Symfony
5.3
and are going to go full PHP8 soon. There are parts of the project that are
older style code and we update those as we go - we actually do have
tasks assigned to team members to specifically update the code, so it's
planned steady progress that has been going on for 2 years now.
We are not a small dev team too, but we also have our tasks to do features,
fix bugs and in general keep the system running and evolving.
Most our code is strict_types=1 and PHP 8 ready.
It is going to be a major undertaking that would halt all work to migrate
to a newer version of PHP where we need to inject the allow dynamic
attribute
without hitting a brick wall. It is not even going to be us, the
developers, who are going to have the biggest chunk of it - it's going to
be the QA who are
going to have to go through the whole project and identify all the issues
that crop up.
It is a complex and big piece of software that has been developed for the
past 10 years - there is no easy way to do one-shot big upgrades
without prior planning way in advance. And while we can certainly deal
with it eventually (and stay on older PHP versions for a while)
all the 3rd party packages that we use are going to be an even bigger pain
to deal with and manage.
If a 3rd party package combines fixing major bugs with a release of a
significant version and including also the new attribute that's available
only on newer PHP version?
You end up in an "all-or-nothing" situation. I hope I do not have to
explain why this is not good?
This also goes against the general way PHP has been doing the strict stuff
- you opt-in into typed properties, arguments and return types. You also
opt-in into strict type mode.
Feels like this should follow the same approach.
If we are talking about having a blanket depreciation and then a fatal
error like that, I feel like it should be part of the
declare(strict_types=1)
mode for the simple reason that
most code in the wild is not really dynamic when the developer chooses to
run in strict mode (and a few cases where they need it - the RFC shows good
ways to convert the code).
Meaning chances of things breaking in a major way and requiring a lot of
work to update the code.
Automated tooling is nice if you know you can safely do "all or nothing".
In complex systems that are ongoing projects where you have to balance
progress with upgrading older parts it's rarely that simple.
Something to think about and my 0.02$ on the subject.
--
Arvīds Godjuks
+371 26 851 664
arvids.godjuks@gmail.com
Telegram: @psihius https://t.me/psihius
If a 3rd party package combines fixing major bugs with a release of a
significant version and including also the new attribute that's available
only on newer PHP version?
Just to be clear, attributes are designed in such a way that you, and
third-party packages, can add this attribute right now, in your PHP
7.4 code, with no ill effects. In older versions, it's a comment (as
long as it's on its own line); and in 8.0 and 8.1 it will be parsed as
an attribute, but simply do nothing.
You can add the attribute to every single class, and slowly remove it
when you're confident of each class not needing it; or you can collect
the E_DEPRECATED
messages and add the attribute to every class
mentioned. Even in PHP 9.0, there will be no subtle behaviour
differences; the code will either run, or you will get an error in
exactly the same circumstances as the E_DEPRECATED
messages.
Regards,
--
Rowan Tommins
[IMSoP]