# [RFC] Object Initializer

- Thread: https://externals.io/message/106947
- Original archive: https://news-web.php.net/php.internals/106947
- Started by michal.brzuchalski@gmail.com on 2019-09-12T14:00:27+00:00
- Messages: 46
- RFC: Object initializer (Declined) — https://externals.io/rfc/object-initializer.md
- Page 1 of 2: messages 1 to 25 (25 per page, add `?page=N` to the URL)
- Next page: https://externals.io/message/106947.md?page=2
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #106947 — michal.brzuchalski@gmail.com — 2019-09-12T14:00:27+00:00

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

Hi internals,

I'd like to open discussion about RFC: Object Initializer.

This proposal reduces boilerplate of object instantiation and properties
initialization in case of classes without required constructor arguments as
a single expression with initializer block.

https://wiki.php.net/rfc/object-initializer

I appreciate any feedback you all can provide.

Thanks,
--
Michał Brzuchalski
brzuchal@php.net

---

## #106969 — Lynn — 2019-09-12T15:01:13+00:00

URL: https://externals.io/message/106947#106969 · In reply to: #106947

On Thu, Sep 12, 2019 at 4:00 PM Michał Brzuchalski <
michal.brzuchalski@gmail.com> wrote:

> Hi internals,
>
> I'd like to open discussion about RFC: Object Initializer.
>
> This proposal reduces boilerplate of object instantiation and properties
> initialization in case of classes without required constructor arguments as
> a single expression with initializer block.
>
> https://wiki.php.net/rfc/object-initializer
>
> I appreciate any feedback you all can provide.
>
> Thanks,
> --
> Michał Brzuchalski
> brzuchal@php.net
>
>
Heya,

What's the added benefit of this compared to implementing a constructor?

The part I like is that this can be used to replace stdClass/structured
arrays. Perhaps something like this would nice to have in PHP:

```
$people = [];

foreach ($peopleFromDatabase as [$id, $username, $name]) {
    $people[] = {
        Uuid id => $id,
        string username => $username,
        string name => $name,
    };
    // and possible automatic assignment:
    $people[] = {Uuid $id, string $username, string $name};
}
```

Regards,
Lynn van der Berg

---

## #106971 — Olumide Samson — 2019-09-12T15:07:31+00:00

URL: https://externals.io/message/106947#106971 · In reply to: #106969

The RFC is a beautiful feature suggestion, but something is telling me as
beautiful and straightforward the syntax is, what can the use case be?

I really love Constructor or Object initialization to be implemented in
PHP, but is there something I think you're missing in the RFC?

Thanks for the RFC.



> On Thu, Sep 12, 2019 at 4:00 PM Michał Brzuchalski <
> michal.brzuchalski@gmail.com> wrote:
>
> > […]
> as
> > […]
> Heya,
>
> What's the added benefit of this compared to implementing a constructor?
>
> The part I like is that this can be used to replace stdClass/structured
> arrays. Perhaps something like this would nice to have in PHP:
>
> ```
> $people = [];
>
> foreach ($peopleFromDatabase as [$id, $username, $name]) {
>     $people[] = {
>         Uuid id => $id,
>         string username => $username,
>         string name => $name,
>     };
>     // and possible automatic assignment:
>     $people[] = {Uuid $id, string $username, string $name};
> }
> ```
>
> Regards,
> Lynn van der Berg

---

## #107041 — michal.brzuchalski@gmail.com — 2019-09-13T05:49:00+00:00

URL: https://externals.io/message/106947#107041 · In reply to: #106969

Hi Lynn,

czw., 12 wrz 2019 o 17:01 Lynn <kjarli@gmail.com> napisał(a):

> Heya,
>
> What's the added benefit of this compared to implementing a constructor?
>
> The part I like is that this can be used to replace stdClass/structured
> arrays. Perhaps something like this would nice to have in PHP:
>
> ```
> $people = [];
>
> foreach ($peopleFromDatabase as [$id, $username, $name]) {
>     $people[] = {
>         Uuid id => $id,
>         string username => $username,
>         string name => $name,
>     };
>     // and possible automatic assignment:
>     $people[] = {Uuid $id, string $username, string $name};
> }
> ```
>

Removing stdClass for instantiation and initialization of simple objects is
one of a future scope proposal.

This RFC tries to address instantiation and initialization boilerplate
reduction with a syntax which would
not be restricted to stdClass only.

Although it's not a game-changer, simple addition to the language which
reduces boilerplate when dealing
with objects which don't need complex constructors like for eg. DTO objects.

Regards,
> Lynn van der Berg
>
>

Regards,
Michał Brzuchalski

---

## #107042 — michal.brzuchalski@gmail.com — 2019-09-13T06:18:16+00:00

URL: https://externals.io/message/106947#107042 · In reply to: #106971

Hi Olumide,

czw., 12 wrz 2019 o 17:07 Olumide Samson <oludonsexy@gmail.com> napisał(a):

> The RFC is a beautiful feature suggestion, but something is telling me as
> beautiful and straightforward the syntax is, what can the use case be?
>
> I really love Constructor or Object initialization to be implemented in
> PHP, but is there something I think you're missing in the RFC?
>
> Thanks for the RFC.
>

I'm open to suggestions.

PHP is known for borrowing features from other languages and this feature
is also known from other languages.
* Rust allows whole struct initialization in one expression, although these
are not classes but being able to instantiate, initialize
and return instance directly after it's being initialized reduces a lot of
boilerplate.
* C# uses an object initializer for the same purposes, one expression which
allows to instantiate, initialize and use the object directly after
initialization.
* Java users use hacks to do the same, even more, cause they're allowed to
call logic through method calls inside "initializer" block.

In some cases when you deal a lot with DTO objects, object instantiation
and properties initialization is very verbose and introduces a lot of noise.

class CustomerDTO {
  public Uuid $id;
  public string $name;
  public Email $email;
  public Address $address;
  public City $city;
  public Country $country;
  public PhoneNumber $phoneNumber;
  public etc...
}

Given above DTO class instantiation and initialization now requires:
* INSTANTIATE class and assign to a variable: $obj = new CustomerDTO();
* FOR EACH (can be many) needed property use the variable with object
instance and assign the property value: $obj->property = $value;

Now when you deal a lot with objects like that you can see you're
constantly repeating yourself with $obj-> part all the lines down before
you
probably would use that instance. There is no need for the constructor in
such objects, they're purpose is to transfer data between app layers.

Last months I've been working on a project which often requires to create
an entity with data decoded from JSON format, most of them had
many fields|properties required and only some of them were optional (even
those optional were nullable so possibly I could initialize them with null
once again),
so the case with which I had a lot to do was creating factories which
instantiate and for each property initialize value just to be able to
return newly created instance in the end.

That's when I thought object initializer could reduce a lot of boilerplate.
That's when I started thinking of many places where I could use that, where
my IDE could help me to write code faster without all that noise of
constantly repeating myself.

The syntax is similar to other languages.
The narrow case described in BC changes is already deprecated and in my
opinion, probably even not used or very rare.
The future scope features can potentially save even more strikes but that's
not the main reason about that RFC - nice to have but let's start with
something.

Regards,
Michał Brzuchalski

---

## #107043 — Claude Pache — 2019-09-13T06:39:52+00:00

URL: https://externals.io/message/106947#107043 · In reply to: #107041

> Le 13 sept. 2019 à 07:49, Michał Brzuchalski <michal.brzuchalski@gmail.com> a écrit :
> 
> Hi Lynn,
> 
> czw., 12 wrz 2019 o 17:01 Lynn <kjarli@gmail.com <mailto:kjarli@gmail.com>> napisał(a):
> 
> > […]
> 
> Removing stdClass for instantiation and initialization of simple objects is
> one of a future scope proposal.
> 
> This RFC tries to address instantiation and initialization boilerplate
> reduction with a syntax which would
> not be restricted to stdClass only.
> 
> Although it's not a game-changer, simple addition to the language which
> reduces boilerplate when dealing
> with objects which don't need complex constructors like for eg. DTO objects.
> 

As for `stdClass`, PHP has already a syntax:

    $baz = "baz";
    $obj = (object) [
        "foo" => "bar",
        $baz => true
    ];

For other type of objects, that could be done with a simple helper function

    $customer = object_assign(new Customer, [
        "id" => 123,
        "name" => "John Doe",
    ]);

where:

    function object_assign(object $obj, iterable $data): object {
        foreach ($data as $key => $value) {
            $obj->$key = $value;
        }
        return $obj;
    }

That said, I generally use arrays rather than DTO objects or such, so that I can’t speak from experience.

—Claude

---

## #107044 — Mike Schinkel — 2019-09-13T07:09:05+00:00

URL: https://externals.io/message/106947#107044 · In reply to: #106947

Hi Michal,

> https://wiki.php.net/rfc/object-initializer


I recently joined this list again after many years away, and a primary reason was to eventually propose an RFC that is almost exactly what you are calling Object Initializers.

So a huge thumbs up on this RFC.  Kudos.  I wish I had a vote and I would certainly vote for inclusion.

-Mike

---

## #107045 — michal.brzuchalski@gmail.com — 2019-09-13T07:15:27+00:00

URL: https://externals.io/message/106947#107045 · In reply to: #107043

Hi Claude,

pt., 13 wrz 2019 o 08:39 Claude Pache <claude.pache@gmail.com> napisał(a):

>
>
> Le 13 sept. 2019 à 07:49, Michał Brzuchalski <michal.brzuchalski@gmail.com>
> a écrit :
>
> Hi Lynn,
>
> czw., 12 wrz 2019 o 17:01 Lynn <kjarli@gmail.com> napisał(a):
>
> Heya,
>
> What's the added benefit of this compared to implementing a constructor?
>
> The part I like is that this can be used to replace stdClass/structured
> arrays. Perhaps something like this would nice to have in PHP:
>
> ```
> $people = [];
>
> foreach ($peopleFromDatabase as [$id, $username, $name]) {
>    $people[] = {
>        Uuid id => $id,
>        string username => $username,
>        string name => $name,
>    };
>    // and possible automatic assignment:
>    $people[] = {Uuid $id, string $username, string $name};
> }
> ```
>
>
> Removing stdClass for instantiation and initialization of simple objects is
> one of a future scope proposal.
>
> This RFC tries to address instantiation and initialization boilerplate
> reduction with a syntax which would
> not be restricted to stdClass only.
>
> Although it's not a game-changer, simple addition to the language which
> reduces boilerplate when dealing
> with objects which don't need complex constructors like for eg. DTO
> objects.
>
>
> As for `stdClass`, PHP has already a syntax:
>
>     $baz = "baz";
>     $obj = (object) [
>         "foo" => "bar",
>         $baz => true
>     ];
>
> For other type of objects, that could be done with a simple helper function
>
>     $customer = object_assign(new Customer, [
>         "id" => 123,
>         "name" => "John Doe",
>     ]);
>
> where:
>
>     function object_assign(object $obj, iterable $data): object {
>         foreach ($data as $key => $value) {
>             $obj->$key = $value;
>         }
>         return $obj;
>     }
>
> That said, I generally use arrays rather than DTO objects or such, so that
> I can’t speak from experience.
>
> —Claude
>
>
You're of course right this can be done more or less with a helper function
which does all the required type
validation through reflection, but I believe that feature requests for
language support which reduces boilerplate
can easily spread that simplification and bring all the required type check
validations out of the box, right?

IMO the thing is as you said this would require a helper function and not
all simply like them!

Regards,
Michał Brzuchalski

---

## #107046 — Mike Schinkel — 2019-09-13T07:22:36+00:00

URL: https://externals.io/message/106947#107046 · In reply to: #106969

> What's the added benefit of this compared to implementing a constructor?

Michal my have a different answer but my answer are the following two benefits plus a 3rd related benefit:

1. The ability to add strictness to object initialization vs. using arrays of properties.  PHP itself could flag misspelled or missing properties with a warning or an error which is something PHP cannot really do given that array keys are strings and not identifiers.

2. The ability for tools like PhpStorm, PHStan and others to more easily identify and flag these errors. It is hard (impossible?) for a tool to validate array element keys because there is no way to declare named and typed array elements keys in PHP.

3. It would empowering developers to pass optional elements to functions and methods that can also be type checked. The 2nd example would allow type checking but the first would not:


EXAMPLE 1
function foo( int $id, array $args ) {
	...
}
foo( 1, array(
	"bar" => "abc",
	"baz" => 123,
}


EXAMPLE 2
class FooArgs {
	public string $bar
	public int $baz
}
function foo( int $id, FooArgs $args ) {
	...
}
foo( 1, FooArgs{
	bar = "abc",
	baz = 123,
}



-Mike


> The part I like is that this can be used to replace stdClass/structured
> arrays. Perhaps something like this would nice to have in PHP:
> 
> ```
> $people = [];
> 
> foreach ($peopleFromDatabase as [$id, $username, $name]) {
>    $people[] = {
>        Uuid id => $id,
>        string username => $username,
>        string name => $name,
>    };
>    // and possible automatic assignment:
>    $people[] = {Uuid $id, string $username, string $name};
> }
> ```
> 
> Regards,
> Lynn van der Berg

---

## #107047 — Mike Schinkel — 2019-09-13T07:35:17+00:00

URL: https://externals.io/message/106947#107047 · In reply to: #107043

> That said, I generally use arrays rather than DTO objects

I too have often used arrays. But then I have always been rigorous to ensure that variables and array elements are initialized before use, and fortunately I do not have a lot of typos in code I write.

However I recently took on a large PHP app to refactor where the previous developers had used arrays profusely with very little pre-initialization.  So I started to add in all initialization and checking to be certain variables had been initialized and realized it was an overwhelming task. 

I changed course and am now replacing all all arrays that are emulating a struct (vs a collection) to instances of declared classes and it has made a major improvement in my ability to refactor without bloating the code to 2-3x the original size. I has also allowed me to consolidate lots of repeated code in one place in  methods and constructors.

Given that experience and knowing that using "args" for optional parameters means my function signatures are much less likely to change, it would be super helpful if I could do so with type-checked object initializers instead of array literals.

-Mike

---

## #107059 — Arnold Daniels — 2019-09-13T08:51:39+00:00

URL: https://externals.io/message/106947#107059 · In reply to: #107047

This can easily be done in a number of ways, like the suggested helper
function. The same can be said for many other features that were
implemented recently, like array unpacking. This feature is easy to
implement and will make the code for data objects much more readable, with
additional benefits for static analysis.

Comments:

I don't like how it works for anonymous classes. It's more difficult to
implement since the compiler doesn't know the meaning of the (first)
bracket. It's doesn't make the code more readable, for the same reason. I
think it's better to not support this syntax with anonymous classes.

The examples do not show how constructor arguments are passed. I'm assuming
it's

    $customer = new Customer("foo") {
        name = "John"
    };

About the idea of letting `{ foo = 10 }` create an `stdClass` object (not
in the RFC); While not used much since it has no effect, it's perfectly
okay to put your code in brackets eg `{ { { $foo = 10; } } }`. As such, I
don't think it's a good idea to allow `new stdClass` to be omitted.

---

## #107063 — Mike Schinkel — 2019-09-13T09:04:25+00:00

URL: https://externals.io/message/106947#107063 · In reply to: #107059

Arnold,

> This can easily be done in a number of ways, like the suggested helper
> function.

A helper function can simulate an object initializer but it cannot provide the same potential benefits. 

The helper function uses array keys to identify properties, and as array keys cannot be declared like class properties, neither IDE, tools, nor PHP itself can validate the names and types of those keys like they could if object initializers were used instead.



Michal,

> I don't like how it works for anonymous classes. It's more difficult to
> implement since the compiler doesn't know the meaning of the (first)
> bracket. It's doesn't make the code more readable, for the same reason. I
> think it's better to not support this syntax with anonymous classes.
> 
> The examples do not show how constructor arguments are passed. I'm assuming
> it's
> 
>    $customer = new Customer("foo") {
>        name = "John"
>    };
> 
> About the idea of letting `{ foo = 10 }` create an `stdClass` object (not
> in the RFC); While not used much since it has no effect, it's perfectly
> okay to put your code in brackets eg `{ { { $foo = 10; } } }`. As such, I
> don't think it's a good idea to allow `new stdClass` to be omitted.


I am curious why your RFC uses "=" instead of "=>" for separating properties from values?  

If we used "=>" it is potential — with lookahead parsing — that the following could be unambiguous:

{ foo => 10 }

-Mike

---

## #107065 — michal@brzuchalski.com — 2019-09-13T09:19:02+00:00

URL: https://externals.io/message/106947#107065 · In reply to: #107059

Hi Arnold,

pt., 13 wrz 2019 o 10:51 Arnold Daniels <arnold.adaniels.nl@gmail.com>
napisał(a):

> This can easily be done in a number of ways, like the suggested helper
> function. The same can be said for many other features that were
> implemented recently, like array unpacking. This feature is easy to
> implement and will make the code for data objects much more readable, with
> additional benefits for static analysis.
>
> Comments:
>
> I don't like how it works for anonymous classes. It's more difficult to
> implement since the compiler doesn't know the meaning of the (first)
> bracket. It's doesn't make the code more readable, for the same reason. I
> think it's better to not support this syntax with anonymous classes.
>
>
Probably lexical scope for anon classes would be better here, but due to
fact that proposal is to
use initializer block instead of constructor arguments, that was the reason
why initializer block
got before anon class definitions.


> The examples do not show how constructor arguments are passed. I'm assuming
> it's
>
>     $customer = new Customer("foo") {
>         name = "John"
>     };
>
>
The examples don't show that cause it's forbidden.
There is a note on that in RFC on purpose

> Note! Object instantiation allows only constructors without required
arguments to be used.
> Any class which requires passing arguments to constructor cannot be used
in combination with object initializer.

Using constructor arguments and object initializer would introduce noise
and you'll be potentially initializing object twice:
using object initializer block and using constructor args what may be
misleading.


> About the idea of letting `{ foo = 10 }` create an `stdClass` object (not
> in the RFC); While not used much since it has no effect, it's perfectly
> okay to put your code in brackets eg `{ { { $foo = 10; } } }`. As such, I
> don't think it's a good idea to allow `new stdClass` to be omitted.
>

Future scope mentions only about letting to create stdClass with omitting
of the class name only,
nothing said about removing a new keyword.

Thanks,
Michał Brzuchalski

---

## #107067 — michal.brzuchalski@gmail.com — 2019-09-13T09:22:48+00:00

URL: https://externals.io/message/106947#107067 · In reply to: #107063

Hi Mike,

I am curious why your RFC uses "=" instead of "=>" for separating
> properties from values?
>
> If we used "=>" it is potential — with lookahead parsing — that the
> following could be unambiguous:
>
> { foo => 10 }
>
>
The reason about choosing "=" was a simplification of instantiation and
properties initialisation
and we use "=" to assign property values now.
The "=>" is specific for array key pairs and IMO should stay specific for
arrays only.

Thanks,
Michał Brzuchalski

---

## #107068 — Mike Schinkel — 2019-09-13T09:29:12+00:00

URL: https://externals.io/message/106947#107068 · In reply to: #107067

> IMO should stay specific for arrays only.

Why?  Is there an objective reason?   

Is there some aspect of the syntax that makes it appropriate for arrays but inappropriate for object initialization? 

 These are honest question.

My two cents: I would like to type less too, but I have always assumed that "=>" was the PHP-ish syntax for initialization of a structure so it seems very appropriate to use for object initialization too.

That said, 

1. I don't feel strongly about iti either way, 
2. Unless using "=>" would make naked object initialization possible — i.e. { foo => 10 } — given that it seems, as Arnold said, this would conflict with other meanings: { foo = 10 }

#fwiw

-Mike

---

## #107070 — michal.brzuchalski@gmail.com — 2019-09-13T09:39:25+00:00

URL: https://externals.io/message/106947#107070 · In reply to: #107068

pt., 13 wrz 2019 o 11:29 Mike Schinkel <mike@newclarity.net> napisał(a):

> IMO should stay specific for arrays only.
>
>
> Why?  Is there an objective reason?
>
>
$obj->foo => 123;
Simply, that's not the way you initialize object property values.

When you use "=>" in array context you're pushing a new value to array and
it'll work without labelling it with key
where the key could be number index or string index in what shape you want
(whitespaces etc.).


> Is there some aspect of the syntax that makes it appropriate for arrays
> but inappropriate for object initialization?
>
>
>  These are honest question.
>
>
> My two cents: I would like to type less too, but I have always assumed
> that "=>" was the PHP-ish syntax for initialization of a structure so it
> seems very appropriate to use for object initialization too.
>
>
> That said,
>
>
> 1. I don't feel strongly about iti either way,
>
> 2. Unless using "=>" would make naked object initialization possible —
> i.e. { foo => 10 } — given that it seems, as Arnold said, this would
> conflict with other meanings: { foo = 10 }
>
>
>
{ $foo = 123 }; // unexpected "}" cause of missing ";"
$bar = { $foo = 123 }; // unexpected "{" cause it's not allowed in this
context
Both examples are syntax error.
You can use {} for separating blocks of code, but now if you wanna assign
value.
Everything considered syntax error can be used for feature shaping.

Regards,
Michał Brzuchalski

---

## #107072 — Mike Schinkel — 2019-09-13T09:47:07+00:00

URL: https://externals.io/message/106947#107072 · In reply to: #107070

> 
> $obj->foo => 123;
> Simply, that's not the way you initialize object property values.

So by the same logic this should be PHP's way to initialize array elements, right?

$arr['foo'] => 123;

But it is not. Obviously we both know that this is the correct syntax:

$arr['foo'] = 123;

So that is why I think it would be more consistent in PHP for object initializers to use "=>" instead of "=."

But, I myself will not belabor the point beyond this message. Either others will agree with you or they will suggest to change to "=>" too.

> { $foo = 123 }; // unexpected "}" cause of missing ";"
> $bar = { $foo = 123 }; // unexpected "{" cause it's not allowed in this context
> Both examples are syntax error.
> You can use {} for separating blocks of code, but now if you wanna assign value.
> Everything considered syntax error can be used for feature shaping.

Hmm. 

Ok, I will let Arnold reply to you on this if he feels that your reply did not address his concerns.

-Mike

---

## #107073 — Arnold Daniels — 2019-09-13T10:15:19+00:00

URL: https://externals.io/message/106947#107073

> I don't like how it works for anonymous classes. It's more difficult to
> > […]
> Probably lexical scope for anon classes would be better here, but due to
> fact that proposal is to
> use initializer block instead of constructor arguments, that was the
> reason why initializer block
> got before anon class definitions.
>

This adds a lot of complexity to the implementation for very little
benefit, as the code (as presented for anonymous classes) is
really confusing.


>
> > […]
> The examples don't show that cause it's forbidden.
> There is a note on that in RFC on purpose
>
> > […]
> arguments to be used.
> > […]
> in combination with object initializer.
>
> Using constructor arguments and object initializer would introduce noise
> and you'll be potentially initializing object twice:
> using object initializer block and using constructor args what may be
> misleading.
>

I don't see any technical limitation to justify this.

    $user = new User($organization) {
        name = "John Doe",
        email = "john@example.com",
    };

Is just equivalent to

    $user = new User($organization);
    $user->name = "John Doe";
    $user->email = "john@example.com";


> Setting the properties does not interfere with the constructor. This is
also confirmed by

> constructors are called before initialization takes apart

You may use constructor argument `$organization` to setup the object.

The initiate syntax isn't a replacement for the constructor, it's a
replacement for setting properties after the object has been constructed.


> Ok, I will let Arnold reply to you on this if he feels that your reply did
> not address his concerns.
>
>
Omitting stdClass is outside the scope of the current RFC. I'm inclined not
to discuss it further or take it into consideration for this RFC. New RFC,
new discussion.

However, I do agree with Mike that using `=>` is more intuitive. It's not
specific to array key pairs, because you only use that syntax at
initialization of the array.

    $user = [
        'name' => "John Doe",
        'email' => "john@example.com",
    ];

and

    $user = [];
    $user['name'] = "John Doe";
    $user['email'] = "john@example.com";

The following feels similar

    $user = new User {
        name => "John Doe",
        email => "john@example.com",
    };

and

    $user = new User;
    $user->name = "John Doe";
    $user->email = "john@example.com";

---

## #107074 — Rasmus Schultz — 2019-09-13T10:18:35+00:00

URL: https://externals.io/message/106947#107074 · In reply to: #106947

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.

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.

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.

Some models are immutable by design. Those cases don't seem to be well
supported by this feature.

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.

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.

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.

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.


On Thu, Sep 12, 2019 at 4:00 PM Michał Brzuchalski <
michal.brzuchalski@gmail.com> wrote:

> Hi internals,
>
> I'd like to open discussion about RFC: Object Initializer.
>
> This proposal reduces boilerplate of object instantiation and properties
> initialization in case of classes without required constructor arguments as
> a single expression with initializer block.
>
> https://wiki.php.net/rfc/object-initializer
>
> I appreciate any feedback you all can provide.
>
> Thanks,
> --
> Michał Brzuchalski
> brzuchal@php.net

---

## #107075 — Lynn — 2019-09-13T11:26:39+00:00

URL: https://externals.io/message/106947#107075 · In reply to: #107059

On Fri, Sep 13, 2019 at 10:51 AM Arnold Daniels <
arnold.adaniels.nl@gmail.com> wrote:

> Comments:
>
> I don't like how it works for anonymous classes. It's more difficult to
> implement since the compiler doesn't know the meaning of the (first)
> bracket. It's doesn't make the code more readable, for the same reason. I
> think it's better to not support this syntax with anonymous classes.
>
> The examples do not show how constructor arguments are passed. I'm assuming
> it's
>
>     $customer = new Customer("foo") {
>         name = "John"
>     };
>
> About the idea of letting `{ foo = 10 }` create an `stdClass` object (not
> in the RFC); While not used much since it has no effect, it's perfectly
> okay to put your code in brackets eg `{ { { $foo = 10; } } }`. As such, I
> don't think it's a good idea to allow `new stdClass` to be omitted.
>

If the compiler implementation of `$foo = {...};` would give issues, `$foo
= new {...};` could be an alternative for run-time declared anonymous
classes (preferably with typed properties). In regards of stdClass, I don't
think this should be used.

Regards,
Lynn van der Berg

---

## #107077 — Arnold Daniels — 2019-09-13T12:30:02+00:00

URL: https://externals.io/message/106947#107077 · In reply to: #107074

> 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.

---

## #107078 — Matthew Brown — 2019-09-13T13:23:45+00:00

URL: https://externals.io/message/106947#107078 · In reply to: #106947

Though this is truly a stylistic complaint, I think it will lead to
harder-to-analyse code.

Currently I have a static analysis tool that can verify (in 99% of cases)
whether all properties of a class have been initialised in the constructor.
This check is even more important in PHP 7.4, where use of a property
without instantiation is a fatal error.

Figuring out which properties have been instantiated in a constructor is
non-trivial, and it's only efficient to do it once per class.

If we adopt this into the language and people use both __construct and
object initializers for a given class, analysis would become much more
tricky.

In order to find the error in this:

abstract class A {
  public string $s;
  public int $t;
}

class B extends A {
  public bool $u;

  public function __construct() {
    $this->s = "hello";
  }
}

$b = new C {
  u = true
};

The analyzer needs to understand that the initialisation of B left a
property uninitialised - it warns about it in B's constructor (
https://psalm.dev/r/0e8e40fefc) but I worry that people will start to
dismiss those warnings as false-positives if this pattern becomes popular.

Best wishes,

Matt




On Thu, 12 Sep 2019 at 10:00, Michał Brzuchalski <
michal.brzuchalski@gmail.com> wrote:

> Hi internals,
>
> I'd like to open discussion about RFC: Object Initializer.
>
> This proposal reduces boilerplate of object instantiation and properties
> initialization in case of classes without required constructor arguments as
> a single expression with initializer block.
>
> https://wiki.php.net/rfc/object-initializer
>
> I appreciate any feedback you all can provide.
>
> Thanks,
> --
> Michał Brzuchalski
> brzuchal@php.net

---

## #107080 — Rowan Tommins — 2019-09-13T14:35:57+00:00

URL: https://externals.io/message/106947#107080 · In reply to: #107074

Hi Rasmus,



> 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.
>


I've trimmed the quote for readability, but agree with basically everything
in this message. :)

I like the reasoning behind this RFC, but think it is unnecessarily
limited. It's not about "public properties are bad", it's just that if we
can come up with a feature that works for public properties *and* other
programming styles, we should prefer that.

A related proposal that's come up before is short-hand constructors:

class Foo {
    private int $foo;
    public function __construct($this->foo) {
        // automatically assigns first parameter to $this->foo so body is
simplified
    }
}

Combine that with opt-in named parameters, and the Customer example from
the RFC might look something like this:

class Customer
{
  private $id;
  private $name;
  private DateTimeImmutable $createdAt;

  public function __construct(id => $this->id, name => $this->name,
createdDate => string $dateString = 'now')
  {
    $this->createdAt = new DateTimeImmutable($dateString);
  }
}$customer = new Customer(
   id => 123,
   name => 'John Doe',
   createdDate => '2019-01-01 12:34'
);


It's slightly more verbose, but a lot more flexible.


As a side note, I have always thought stdClass was a bit of a kludge, and
now we have real anonymous classes I would love to see it gradually phased
out. I would much rather see syntax for capturing variables in an anonymous
class declaration than new ways to create stdClass objects.

Regards,
-- 
Rowan Tommins
[IMSoP]

---

## #107084 — Mike Schinkel — 2019-09-14T08:47:25+00:00

URL: https://externals.io/message/106947#107084

> 
> 
> 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. 

I think this is a false dichotomy; I can see benefit to having initializers in addition to constructors, even for the same classes.

However, Object Initializers might be best when paired with a new magic method which let us call __init(), and a required modifier for properties.  

Using your Customer example, the __init() would be automatically called after __construct():

class Customer
{
   private string $name = '';
   required protected ?string $email = null;

   public function __construct(string $name, ?string $email = null)
   {
      $this->name = $name;
      $this->email = $email;
    }

   public function __init()  
   {
      $this->name  = ucwords($this->name);
      $this->email = sanitize_email($this->email);
   }
}

With __init() a developer could separate capturing construct parameters from initializing required fields, but the required modifier would allow developers to indicate which properties should be required. 

And it might even make sense to throw an error if an object initializer does not have an __init() method. Requiring __init() would ensure that classes not designed to work with initializers would never be initialized without required fields set.

When creating an object using object initialization I think it should just bypass and only call __init(). PHP could generate an error if any required fields are not set.

$customer = new Customer{
   name  => 'mike schinkel',
   email => 'mike@newclarity.net <mailto:mike@newclarity.net>'
}
echo $customer->name; // Prints "Mike Schinkel"

Actually, a required modifier would not be required to implement object initializers (no pun intended) but it would enable IDEs, PHP and other tools validate whether or not a required fields has been set.

As an aside, it would be interesting if there were a __validate() magic method although it might harm performance too much.  OTOH, it should not harm performance unless you actually use it:

public function __validate(string $field):bool
{
   switch ($field) {
      case 'name':
         return !empty( $this->name ) && strlen($this->name) >= 3;
      case 'email':
         return validate_email($this->email);
   }
   return true;
}

> a desire to use this language feature will drive architecture. ... this will
> provide the wrong kind of motivation to make what are, effectively,
> architectural decisions.

The reason I badly want object initializers is to empower architectures that currently are not possible in PHP. So I see this as a positive. 

I see architectures used in other languages that reduce tedium and enhance reusability that we cannot implement in PHP because of lack of an object initializer.

But maybe I do not realize what you are seeing?  Can you please elaborate on the architectures you fear will emerge, ideally with examples and reasons why they should be avoided?


-Mike

---

## #107085 — Mike Schinkel — 2019-09-14T08:49:39+00:00

URL: https://externals.io/message/106947#107085

> 
> 

> 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
> ...
> My strong preference over this feature would be named parameters, 
> ...
> 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 mostly agree with the first statements, in abstract principle.  

But ironically I come to the exact opposite conclusion that your latter statements imply, i.e. that named parameters are not a generalizable enough feature and that object initializers are much more generalizable.

Let me explain where I see limitations of named parameters:

1. Nested structures: Named parameters, assuming they are not shorthand for initializing objects could not support nested parameters, or at least not without a lot of other supporting additions to PHP too.

$car = new Car({ 
   yearOfProduction => 1975, 
   vin => "12345678", 
   engine => {
      type => "v8", 
      capacity => "270ci", 
   },
})

2. Local usage: Consider the following construct. I might use this above the top of a loop if I were writing a very add-hoc custom report.  (Please note that I embellished by adding types in a manner I would prefer here.) 

The following example shows anonymous class declared in local scope.  The reason we want to initialize a anonymous class is the same reason we wanted anonymous classes in the first place; often a class is just not important enough to assign a name. 

And since naming is "one of the 2 or 3 hardest things in programming" not having to assign it a name if a name is not needed is a real plus. And this is not a use-case named parameters address, so this is an explicit example of where object initializers are more general purpose than named parameters:

$stats = new class {
    int total   => 0,
    int mean    => 0,
    int average => 0,
    int median  => 0,
}

I won't elaborate on how this class is used since hopefully that code would be obvious . 

But I will say that I would use these types of object initializers almost everywhere I previously have used arrays — and where others have used arrays in code I am refactoring.  That would reduce most of array use-cases to collections as I would no longer really need them as associative arrays, unless and framework or library required me to use them. 

If we had the language features to allow us to concisely move locally-declared associative array code to using anonymous class object instances then IDEs would be able to validate proper use of class properties and cut down on an entire class of coding errors.  

(Yes, theoretically we can already use anonymous classes instead of arrays but declaring the classes and initializing their properties is so tedious that almost nobody ever codes that way in PHP. Or at least none of the code I have ever come across on GitHub or in client's projects.)

3. Passing to subroutines.  This is a contrived example, but it is emblematic of code I write daily. 

The unfortunate aspect is that since I use an array of $args I cannot have my IDE nor PHP validate that I used the proper array keys.

(Note that my example has an error; I used 'field' with  query() instead of 'fields' as I should have, to illustrate the problem):

class Join {
   public string $table;
   public string $from;
   public string $to;
}

class QueryBuilder {
   function query(array $args):object[] {
      if ( ! $this->validate($args) ) {
         throw new Exception("Dude!");
      }
      $query = $this->build($args);
      return $this->run($query);
   }
   function validate(array $args) {
      ...
   }
   function build(array $args) {
      ...
   }
   function run(string $query ) {
      ...
   }
}
$qb = new QueryBuilder();
$rows = $qb->query(array(
   field => ['id','name','cost'],
   table => 'products',
   where => 'id=' . $productId,
));

Now let us take a look at this example using named parameters. Hopefully you can see it is painfully verbose, definitely not DRY, and likely to result in typos or other refactoring errors: 
class QueryBuilder {

function query(string[] $fields, string $table, Join[] $joins, string[] $wheres ):object[] {
      if ( ! $this->validate($fields, $table, $joins, $wheres ) ) {
         throw new Exception("Dude!");
      }
      $query = $this->build($fields, $table, $joins, $wheres);
      return $this->run($query);       
   }
   function validate(string[] $fields, string $table, Join[] $joins, string[] $wheres ) {
      ...
   }
   function build(string[] $fields, string $table, Join[] $joins, string[] $wheres ) {
      ...
   }
   ...
}

If we instead use object initializers, it becomes much readable and maintainable, even though the developer may have initially written the query() method by using named parameters:

And unlike using an array to contain all the values my IDE, PHP and other tools can validate whether or not I used the correct property names for Query when calling $qb->query():

class Query {
   public string[] $fields;
   public string $table; 
   public Join $join; 
   public string[] $where; 
}
class QueryBuilder {
   function query(Query $query):object[] {
      if ( ! $this->validate($query) ) {
         throw new Exception("Dude!");
      }
      $query = $this->build($query);
      return $this->run($query);
   }
   function validate(Query $query) {
      ...
   }
   function build(Query $query) {
      ...
   }
   ...
}
$qb = new QueryBuilder();
$rows = $qb->query(Query{
   fields => [ 'id', 'name', 'cost' ],
   table  => 'products',
   where  => 'id=' . $productId,
});

BTW, if I could have everything I want, I would really like following to work too:

$rows = $qb->query({
   fields => [ 'id', 'name', 'cost' ],
   table  => 'products',
   where  => 'id=' . $productId,
});

Note above I omitted Query{} and just used {} when calling $qb->query() because PHP should be able to see the signature for query() and pass the initialized values to instantiate a Query object instead of a stdClass instance.


-Mike

---

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