Re: Improving PHP's Object Egonomics: A broad analysis
| From: | Larry Garfield | Date: | Wed, 25 Mar 2020 14:24:25 +0000 |
| Subject: | Re: Improving PHP's Object Egonomics: A broad analysis | ||
| References: | 1 2 3 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-109292@lists.php.net to get a copy of this message | ||
On Wed, Mar 25, 2020, at 5:57 AM, Nicolas Grekas wrote:
> > >
> > > https://hive.blog/php/@crell/improving-php-s-object-ergonomics
> > Named parameters are a pretty tough topic. I think one of the main points
> > of contention is that they make the parameters names part of the API
> > contract, and as such also subject to LSP. Your proposal offers two
> > possible ways to side-step this: First, by making named parameters opt-in
> > with a special syntax {}. Second, by limiting them to constructors. The
> > latter variant still exposes parameter names in the API, but at least does
> > not require their preservation across inheritance, as constructors are
> > excluded from LSP. I'm somewhat torn on this, because it makes named
> > parameters unusable with the very large body of existing methods, and
> > introduces an inconsistency in which methods can use named params and which
> > don't.
> >
>
> I'd like to propose something on the topic.
> I'm adding object literals to the mix because that's another feature of the
> language that we're missing a lot IMHO.
> Actually, there is one existing syntax for objects: (object) [...]
>
> My proposal is to allow any class in the casting operator: (Foo) [...]
> By default, all keys of the casted array would map to properties (with an
> error when no matching property exists in the current visibility scope). We
> would then allow for a new constructor method, either:
> public function __create(array $values)
> or:
> public static function __create(array $values): static
>
> This method would take over the cast operator and decide how to construct
> an instance of such a class.
>
> There is one drawback: accepted keys are not documented. Sure, property
> declarations can give a big hint.
>
> But I think we can solve this issue later: it's not a blocker to still make
> things work nicely. Also, this issue already exists with all methods that
> accept an array of options - and we'll find a solution for those - either
> using docblocks (there are already ways to use them for that) or using
> attributes (would be the best of course, once we have them.)
>
> Note that this __create() method looks a lot like __unserialize(): it's
> just called at a different place, but the implementations could be
> essentially the same.
Interesting. If that allowed populating private properties than that would be more capable than
either of the initializer proposals that have been put forward.
However, this alternate deserialization (as you note, it's basically __unserialize by another
name) brings up other issues:
1) If it's a large number of properties, the __create method would still need to manually
assign them to properties, just like constructors now. It wouldn't benefit from constructor
promotion.
2) What's its order of execution with the constructor? Vis, does the constructor run after
__create or before? Does __construct get any parameters passed to it? If it doesn't get
called, that's yet another way to bypass the constructor and therefore bypass object
validation. ("Make invalid states unrepresentable". If the properties don't
logically make sense with each other that should be rejected as early as possible; syntactically if
we can, via early validation if not.)
Named parameters have neither of those issues, as data still only comes in via the constructor so
both promotion and validation work fine.
> Regarding the remainder, I think that all of readonly properties,
> > asymmetric visibility and property accessors have their place and value,
> > with some overlap between them. As you already mentioned, the previous
> > property accessors proposal also included asymettric visibility as a
> > special case, and that's how I would introduce it as well.
> >
>
> Máté suggested this syntax and it has my preference over the one you menton
> Larry: doubling the visibility keyword could be enough to express
> read+write access:
>
> public private $property; <= public read access, private write access
In that syntax, you have to remember which one comes first. There's no indication for the
casual reader why
public private $property;
and
private public $property;
are different. Plus, just looking at it, "wait, it's public and private? WTF? That
doesn't even make sense."
It also doesn't extend gracefully to property accessors. Whatever accessors do, if they ever
get resolved, would conflict with that, and thus we'd have that many more weird combinations of
property metadata that are incompatible.
The whole point of the syntax I proposed for asymmetric visibility is that it's gracefully
extensible, even if a little more verbose. (If we can find a syntax that is less verbose while
still gracefully extensible, I am on board with that.)
--Larry Garfield