Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:107077 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 12859 invoked from network); 13 Sep 2019 14:54:07 -0000 Received: from unknown (HELO php-smtp3.php.net) (208.43.231.12) by pb1.pair.com with SMTP; 13 Sep 2019 14:54:07 -0000 Received: from php-smtp3.php.net (localhost [127.0.0.1]) by php-smtp3.php.net (Postfix) with ESMTP id B8CA12C050C for ; Fri, 13 Sep 2019 05:30:15 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp3.php.net X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,HTML_MESSAGE,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS3215 2.6.0.0/16 X-Spam-Virus: No Received: from mail-ot1-x330.google.com (mail-ot1-x330.google.com [IPv6:2607:f8b0:4864:20::330]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by php-smtp3.php.net (Postfix) with ESMTPS for ; Fri, 13 Sep 2019 05:30:15 -0700 (PDT) Received: by mail-ot1-x330.google.com with SMTP id z26so19974950oto.1 for ; Fri, 13 Sep 2019 05:30:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to; bh=1YtMY4BTN5KlgQrlXdkqve/HH13D8YaPMiesuDEmb48=; b=HBQERtY+BX6fXcdkzOPL0Y3U6t4S5vEsixfLnVHYgEUiWYbnu1uMAQiecdkMZ0UPZ3 KJ5qHnRxlYpM7k9Ot32QkRbdOcjZ5VUvZ9FXhdQ5Rq5C59qB9f/0rWwfwggR6g5l1gA3 oBqWVUH0TYLxkF82w9Ryr9uTphowFUO/DgsrqA75YNmPnHHFPE66tmYBW9/Y3kUKjBSB upJ9Ax5uPKTQzmmMKdIJYhipxIdsjWaZ2XK0Z8vWkame6QYSBLN1v6t1rxQw+D8lhMRP +ZgeCTaSbi2Pn5FO/UrZ4yJZ/RMQKDnnPP31Tkba1KqSRri839v8PwpsWFelaUxHdD4U EltQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to; bh=1YtMY4BTN5KlgQrlXdkqve/HH13D8YaPMiesuDEmb48=; b=tVgZe7/9EMNRJvDfSc1lZQfpNVDz7Ny7ZkO5syiYNQdLYycb5rdshgdmHVX7zMD2CI OapcJ58Si59owYWYP1y8BF+J8rvK0q8nx4aoHWwMrJexMJWidAPv8cp4WDoEs8ndS1ZH ijlDEaWs7A6QawGT+cP1uJk+r78HQOiVwTu1B8ap8A7+hQe7x6PXiRcrdNd/ER4DyjL9 aszYYYkdBuTu5Kw+oVoc9gdrnQy34qW7TyEMFE3NFCCgyGtYC650mrNWP/AxqEMdhe36 cM56v4OSfOns895KD+/X4nOAvGqjH0wh+muvYZtdl4ioU3soqD201PH9hsUVkawfT488 s0Ow== X-Gm-Message-State: APjAAAXdl0/K56JET7CxOWy5wzeIlkvT7cGcD9PLhon+w9kyUklU/+Ta RG/oZSjMhBMICUqXc3qr3QioXf70WFcAJcZrtR0wZ6Y3Rlc= X-Google-Smtp-Source: APXvYqx2vTWMkX8gN08ti0IZ5sMr55fmyrwAJ+8jaAZYRuhdNK7jpKUFS8/ugEYqWsKQ9WzGkAIG0YZaxKR2i527K1U= X-Received: by 2002:a9d:4102:: with SMTP id o2mr41655342ote.187.1568377814434; Fri, 13 Sep 2019 05:30:14 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: Date: Fri, 13 Sep 2019 14:30:02 +0200 Message-ID: To: PHP Internals Content-Type: multipart/alternative; boundary="00000000000080c81205926e6a96" X-Envelope-From: Subject: Re: [PHP-DEV] [RFC] Object Initializer From: arnold.adaniels.nl@gmail.com (Arnold Daniels) --00000000000080c81205926e6a96 Content-Type: text/plain; charset="UTF-8" On Fri, Sep 13, 2019 at 12:18 PM Rasmus Schultz wrote: > I'd like to address the examples - and why I think they don't demonstrate > that this feature is really useful in practice. > > There are several examples similar to this one: > > class Car > { > public int $yearOfProduction; > public string $vin; > } > > This is either lacking a constructor to initialize the properties to the > defined types - or it's lacking nullable type-hints. As it is, this class > doesn't guarantee initialization according to it's own property type > constraints. > > Assuming the fields of this entity are required, you would probably prefer > to add a constructor - but then property initializers aren't really useful > anymore. This seems to be the case for most of the examples. > Using initializers and a constructor and not mutually exclusive. class User { public string $id; public ?string $name; public ?string $email; public function __construct() { $this->id = bin2hex(random_bytes(16)); } } > > Models often have other constraints besides just the type - in those cases, > your models would likely have private/protected fields and setter-methods > that implement those constraints. The visibility example demonstrates the > use of a static factory method: > > class Customer > { > private string $name = ''; > protected ?string $email = null; > > public static function create(string $name, ?string $email = null): self > { > return new self { > name = $name, // assign private property within the same class > email = $email, // assign protected property within the same class > }; > } > } > > This may be fine for some use-cases - but many model types are only going > to have one valid way to construct an instance, and constructors are the > idiomatic way to do that. Unfortunately, this language feature works for > the new-statement only, so a desire to use this language feature will drive > architecture. > This example doesn't make a lot of sense. In this case, the `create` method has no added benefit. You can just as well just use the constructor. class Customer { private string $name; protected ?string $email; public function __construct(string $name = '', ?string $email = null) { $this->name = $name; $this->email = $email; } } Using constructor arguments isn't a great approach classes that have a large number of properties, which is typically the case with data objects. > All in all, I find this feature is useful or applicable only to a few, > select patterns within the language - it isn't general enough. > In my opinion, language features should be as general as possible - a > feature like this "looks nice", being very abbreviated and clean-looking, > and, as I believe the examples in the RFC itself demonstrates, this will > provide the wrong kind of motivation to make what are, effectively, > architectural decisions. > It seems like you consider the use of public properties as bad practice in general. However, I do not think such a hard stance should be taken in the ongoing discussion about public properties vs getters and setters. There are valid arguments on both sides. If you don't use public properties, this RFC will not affect you at all. If you do, there is a clear benefit in this approach. > > Some models are immutable by design. Those cases don't seem to be well > supported by this feature. > Immutable objects are not well supported in general in PHP. This RFC doesn't affect it. > My strong preference over this feature would be named parameters, which can > provide the same abbreviated initializations, but works more consistently > with the language, e.g. for all of the use-cases I cited above. > > It works for constructors: > > class Car > { > public int $yearOfProduction; > public string $vin; > > public function __construct(int $yearOfProduction, string $vin) { > if ($yearOfProduction < 1900 || $yearOfProduction > date("Y")) { > throw new InvalidArgumentException("year of production out of range: > {$yearOfProduction}"); > } > > $this->yearOfProduction = $yearOfProduction; > $this->vin = $vin; > } > } > > $car = new Car({ yearOfProduction = 1975, vin = "12345678"}); > > It works for static factory-methods: > > $car = Car::create({ yearOfProduction = 1975, vin = "12345678"}); > > It works for models with private/protected fields, classes with accessors, > classes with validations in constructors or factory-methods, and so on. > > In other words, it works more generally with all common patterns and > practices - in many ways, it just seems like a better fit for the language. > I see how named parameters competes with this RFC. They are two different things, that may both be implemented. The need to define all properties as constructor arguments and then setting them all isn't a great approach for classes with 10+ properties as it really bloats the class. Also having property guard for public properties that are only in the constructor isn't that great. The common criticism against named parameters, is they create coupling to > parameter-names. Object initializers create coupling to property-names - if > you can live with that, I don't think named parameters or object > initializers are very different in that regard. > You always have coupling to public property names. Public properties and public methods are what make up the api of the class. Nothing in this RFC changes that. > > The only problem I see with named parameters as an alternative to object > initializers, is in terms of versioning - renaming an argument, today, is > not a breaking change, and now it would be. That could be addressed, for > example, by making named parameters explicit at the declaration site - for > example, use curly braces in the declaration to designate named arguments: > > function makeCar({ int $yearOfProduction, string $vin }) { > // ... > } > > This way, adding named parameters is not a breaking change - it's an opt-in > feature for those cases where it's useful and meaningful, but it's still > applicable to all the patterns and cases that someone might want to use it > for. > This is a discussion on itself. It has nothing to do with this RFC. Please create a new RFC to discuss it. > > Named parameters is just one possible alternative - I'm just naming it for > practical comparison, to demonstrate how some features may have more > general applications than others. > I'd prefer to see new features that work everywhere, all the time, for > everyone - and for existing code. Rather than adding more features and > syntax for very specific (even relatively rare) use-cases. > There is really nothing rare about the use of public properties. The RFC shouldn't be invalidated because it doesn't benefit one particular programming style, especially if that style is under heavy debate. --00000000000080c81205926e6a96--