Hello! I started GitHub issue requesting that property hooks be allowed to be 
`unset()`: https://github.com/php/php-src/issues/17922

While discussing in the issue it was suggested I pitch this to the mailing 
list, so here I am.

You can review the GitHub issue so I won't rehash the whole thing here, but 
I'll summarize the main points for `unset()` of backed property hooks:

1. It is well-established in PHP that one can `unset()` object properties. If 
we cannot `unset()` property hooks - which to a consumer are indistinguishable 
from plain properties - then the consumer must now be aware of the object's 
internal implementation before doing what used to be explicitly allowed. This 
is both poor encapsulation and not compatible with the commonly-understood 
behavior of `unset()`, leading to surprising cases where a consumer of an 
object could cause a fatal error by trying to `unset()` a property that 
unbeknownst to them is implemented as a property hook.

2. Getters may perform a calculation, or fetch from a database, and cache the 
result in the backing value, but without `unset()` we can end up in a state 
where the logic to determine that value can never be executed again. The 
workaround is exposing a public method that contains such logic, but that 
defeats the point of putting logic in property hooks in the first place, and 
exposes internal class implementation details that should not concern the 
consumer. Getting and caching values is a common case in ORM layers, one of the 
biggest users of PHP.

3. It would be convenient to be able to `unset()` backed properties when 
changing data elsewhere in a class, for example to trigger a recalculation or 
re-fetching from the database. It's not enough to set such properties to `null` 
because 1) the actual type of the property may in fact not be nullable, leading 
to inconsistency in the property's PHP type and the database column type, and 
2) in practice `null` is a real value that can come from the database, which is 
a distinct concept from "uninitialized at the PHP level". (This is the old 
`isset()` vs `is_initialized()` problem, a tired struggle going on decades now.)

Some arguments raised against were internal implementation details; that 
`unset()` itself is a bad design choice so we should not cater to it; and that 
catering to `uninitialized` makes typing more complex.

I can't comment on implementation details. 

To the last two points, I would say that for better or worse, `unset()` and 
`uninitialized` are part of PHP and I assume they're here to stay. Therefore if 
we're extending the language, we should extend it in ways that embrace, not 
ignore, its warts, because they're what make the language and they're what 
decades of developers are accustomed to. Ignoring the warts, or leaving them in 
the language but calling them bad practice that must be avoided, results in 
footguns and a language that is inconsistent with itself. (As I'm sure you're 
all aware, inconsistency is one of the favorite charges leveled at PHP by its 
detractors.)

And while `uninitialized` may make types more complex, it can often be very 
useful, especially when filling objects from database rows - one of the most 
common tasks a web developer will ever do. It might be technically purer to 
require that objects only be created with lengthy constructors that initialize 
every property with strongly typed values, and `unset()` and `isset()` never be 
used, but for better or worse that's just not how PHP evolved. Having the 
flexibility of objects with uninitialized, and therefore unsettable, properties 
is very useful. This flexibility is part of what makes PHP so attractive and 
what made it so popular in the first place.

In the GitHub thread it was mentioned that an RFC would be required. I've never 
written one, nor am I familiar with PHP internals, but I can try putting one 
together if there's interest.

Reply via email to