Re: [PHP-DEV] [RFC] Object Initializer

2019-10-07 Thread Michał Brzuchalski
Hi Arvids,

sorry for the late response, I've been busy recently.

wt., 17 wrz 2019 o 13:08 Arvids Godjuks 
napisał(a):

>
> I've have been keeping up with the thread and at this point, I do somewhat
> agree with people that maybe the RFC should go into V2 version development.
> It really seems like the scope of the RFC is just too small for the feature
> and it might be a good idea to expand it a bit.
>

I'm gonna let it go as it currently is, there's still time before PHP 8.0
for improvements.
IMO with all the goods like list of properties to set and restriction which
forces to properly initialize object instance with all required fields is
enough.


> Also, I have this question - is there any performance to be gained here?
> Cause right now object hydration is an issue and is somewhat slow. Can it
> be optimised?
> Also, usually I don't really want properties to be public (aka writeable),
> so that means `private/protected` and I need to use the methods to set the
> properties or reflection. I might have missed or don't remember it, but I
> don't think I saw a discussion about that use-case. Value object, DTO's -
> all have a big use case for it.
>

This feature has no implementation yet so won't speak out about performance
and optimisation now.
Declaring classes with private/protected fields doesn't mean you cannot to
object initialization using object initializer, you can do this from class
inner scope exactly the same way as you're able to access them from class
inner scope now.
This feature may not be a cure for all diseases, but I believe is the right
to reduce noise and boilerplate when instantiating and initializing simple
objects with type restrictions and ensures valid object state every time
it's used.

BR,
Michał Brzuchalski


Re: [PHP-DEV] [RFC] Object Initializer

2019-09-17 Thread Arvids Godjuks
чт, 12 сент. 2019 г. в 16:00, Michał Brzuchalski <
michal.brzuchal...@gmail.com>:

> 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
> brzuc...@php.net
>


 Thank you Michał for the RFC,

I've have been keeping up with the thread and at this point, I do somewhat
agree with people that maybe the RFC should go into V2 version development.
It really seems like the scope of the RFC is just too small for the feature
and it might be a good idea to expand it a bit.

Also, I have this question - is there any performance to be gained here?
Cause right now object hydration is an issue and is somewhat slow. Can it
be optimised?
Also, usually I don't really want properties to be public (aka writeable),
so that means `private/protected` and I need to use the methods to set the
properties or reflection. I might have missed or don't remember it, but I
don't think I saw a discussion about that use-case. Value object, DTO's -
all have a big use case for it.

-- 
Arvīds Godjuks

+371 26 851 664
arvids.godj...@gmail.com
Skype: psihius
Telegram: @psihius https://t.me/psihius


Re: [PHP-DEV] [RFC] Object Initializer

2019-09-16 Thread Arnold Daniels
Features like `func_get_args(ARGS_OBJECT)` and `hoist` aren't really needed. 
You can use `get_defined_vars()` instead.

Many of my classes have constructors like;

public function __construct(int $foo, string $bar, bool $flag = false)
{
set_object_vars($this, get_defined_vars());

}

FYI `set_object_vars` is a user-space function that does the reverse of 
`get_object_vars`. You could use a `foreach` loop instead.


However when a class for a data object has 20 properties, I don't really want 
to put all 20 as constructor arguments. The object initialize RFC be the 
preferred solution for me in that case, even if named parameters was also 
supported. As such, I don't think the two features are mutually exclusive.

[Arnold Daniels - Chat @ 
Spike](https://www.spikenow.com/?ref=spike-organic-signature&_ts=5w9qi)
[5w9qi]

On September 16, 2019 at 6:22 GMT, Michał Brzuchalski 
 wrote:

Re: [PHP-DEV] [RFC] Object Initializer

2019-09-16 Thread Michał Brzuchalski
Hi Paul,

niedz., 15 wrz 2019 o 15:48 Paul M. Jones  napisał(a):

>
>
> > On Sep 12, 2019, at 09:00, Michał Brzuchalski <
> michal.brzuchal...@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
>
> This reminds me of how Hack/HHVM would initialize object properties from
> constructor parameters; I remember finding it very appealing.
>
> IIRC, you would provide the property signature as a constructor parameter
> to trigger the initialization:
>
> ```php
> class Foo
> {
> protected int $bar;
>
> public function __construct(protected int $bar)
> {
> }
>
> public function getBar() : int
> {
> return $this->bar;
> }
> }
>
> $foo = new Foo(1);
> echo $foo->getBar(); // 1
> ```
>
> Perhaps something like that is worth adopting here.
>

What you're describing AFAIR was called "constructor argument promotion"
and this is not kind of problem current RFC tries to solve.
Argument promotion looks useful for constructors, in the name of good
practice constructors and methods|functions in general
should not require a lot of arguments.

Thank you for your feedback but I believe the suggested solution is
off-topic.

Thanks,
Michał Brzuchalski


Re: [PHP-DEV] [RFC] Object Initializer

2019-09-15 Thread Paul M. Jones



> On Sep 12, 2019, at 09:00, Michał Brzuchalski  
> 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

This reminds me of how Hack/HHVM would initialize object properties from 
constructor parameters; I remember finding it very appealing.

IIRC, you would provide the property signature as a constructor parameter to 
trigger the initialization:

```php
class Foo
{
protected int $bar;

public function __construct(protected int $bar)
{
}

public function getBar() : int
{
return $this->bar;
}
}

$foo = new Foo(1);
echo $foo->getBar(); // 1
```

Perhaps something like that is worth adopting here.



-- 
Paul M. Jones
pmjo...@pmjones.io
http://paul-m-jones.com

Modernizing Legacy Applications in PHP
https://leanpub.com/mlaphp

Solving the N+1 Problem in PHP
https://leanpub.com/sn1php

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] [RFC] Object Initializer

2019-09-14 Thread Rowan Tommins

On 14/09/2019 09:49, Mike Schinkel wrote:


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.


I think that's only true because you've actually proposed a number of 
related but different features.




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",
},
})



I'm not sure what "nested parameters" could ever mean, other than 
initializing additional objects. And if these are additional objects, 
then named parameters work just as well in the nested case as in the 
non-nested one:


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


Indeed, since these are named parameters to a function, you could also 
use a static factory method in the same place, which special-case 
initializer syntax wouldn't allow you to:


$car = new Car(
   yearOfProduction => 1975,
   vin => "12345678",
   engine => Engine::fromSerialNo(
  format => 'ISO',
  id => '123abc567'
   ),
);



2. Local usage
...
$stats = new class {
 int total   => 0,
 int mean=> 0,
 int average => 0,
 int median  => 0,
}



This already works:

$stats = new class {
var int $total   = 0;
var int $mean= 0;
var int $average = 0;
var int $median  = 0;
};


What doesn't currently work is using variables from lexical scope in the 
definition:


$stats = new class {
var int $total   = $a + $b;
var int $mean= $c;
var int $average = $d;
var int $median  = $e;
};


However, initializer syntax on its own doesn't solve this, because you 
wouldn't be able to specify the types for the properties; just combining 
existing syntax with initializers would give something like this:


$stats = new class {
var int $total;
var int $mean;
var int $average;
var int $median;
}{
total => $a + $a,
mean => $c,
average => $d,
median => $e
}


You could certainly make the syntax of initializers and anonymous class 
definitions similar, but they're not really the same thing.





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

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,
});



The QueryBuilder class in this example has not benefited from object 
initializers at all; passing the Query object rather than separate 
parameters is a refactoring you can (and probably should) do right now.


The Query class, meanwhile, is no different from the previous examples, 
and the call would look just the same with named parameters to the 
constructor:


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


As before, the definition of the class itself is simpler, but I think a 
short-hand syntax for constructors would be a better compromise than a 
syntax that only worked with public properties and parameterless 
constructors.




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.



This is an interesting additional proposal, which I admit would be 
awkward to implement without initializer syntax. One limitation is that 
it only works if dependencies are declared as concrete classes not 
interfaces - which like public properties is not "wrong" per se, but 
limits the scope of the feature.




Conceptually let me ask, How is { yearOfProduction = 1975, vin = "12345678"} 
really any different from an instance of an anonymous class with the properties 
yearOfProduction and vin?


Named parameters are (or would be) a way of setting a bunch of 

Re: [PHP-DEV] [RFC] Object Initializer

2019-09-14 Thread Larry Garfield
On Fri, Sep 13, 2019, at 9:35 AM, Rowan Tommins wrote:
> Hi Rasmus,
> 
> On Fri, 13 Sep 2019 at 11:18, Rasmus Schultz  wrote:
> 
> > 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]


I'm a big fan of using defined classes over anon arrays for struct-like data, 
for various reasons (cf 
https://steemit.com/php/@crell/php-use-associative-arrays-basically-never), so 
I'm sympathetic toward anything that makes that easier.  However, I have to 
agree with Rasmus and Rowan that the current RFC proposal is too "narrow" to 
solve that effectively.

The problem to be solved is that this:

class Employee {
  public int $age;
  public string $name;
  public ?Employee $boss;

  public function __construct(int $age, string $name, Employee $boss = null) {
$this->age = $age;
$this->name = $name;
$this->boss = $boss;
  }
}

$e = new Employee(34, "Bob", $sally);
// or
$e = new Employee();
$e->age = 34;
$e->name = 'Bob';
$e->boss = $sally;


Is just annoyingly verbose and annoying to work with.  I agree.  However, 
initializers as presented solve only a subset of this problem space: The case 
where:

* The properties are public.
* The constructor can be viable with no parameters.
* There is no needed validation, or we can expect a user to manually call 
validate() or similar afterward.

While that case absolutely exists, it is only a subset of the relevant use 
cases.

As Rasmus and Rowan note, however, breaking the problem apart into two pieces 
would allow it to handle a wider array of use cases while still solving the one 
presented.  For example (and with no thought given to syntax here, 'hoist' is 
almost certainly the wrong word):

class Employee {
  protected int $age;
  protected string $name;
  protected ?Employee $boss;

  public function __construct({hoist int $age, hoist string $name, hoist 
Employee $boss = null}) {
if ($age < 18) throw new ChildLaborException();
  }
}

$e = new Employee({
  age = 34,
  name = 'Bob',
  boss = $sally,
});

Solves the initializer use case with very similar syntax for the caller, BUT 
also works for:

* Any function where named parameters would be useful, independent of object 
initialization.
* Any object where the properties in question are not public.
* Any object where validation is required and you don't trust the caller to 
remember to call it (which you shouldn't trust)
* Any object where only some of the constructor parameters map to object 
properties, a fact that should be hidden from the caller.

And probably other situations.  I could envision taking it a step further and 
writing instead:

class Employee {

  public function __construct({hoist private int $age, hoist protected string 
$name, hoist public ?Employee $boss = null}) {
if ($age < 18) throw new ChildLaborException();
  }
}

And now you have even less typing and repetition.

If you wanted to go really really far, you could even:

class Employee {
  protected int $age;
  protected string $name;
  protected ?Employee $boss;

  public function hoist __construct() {
if ($age < 18) throw new ChildLaborException();
  }
}

As a short hand that enables any property to be passed in, but only using a 
named call, and still enforce that some parameters are required.  I could see 
that being very useful for service objects, as well as value objects, where 
there 

Re: [PHP-DEV] [RFC] Object Initializer

2019-09-14 Thread Benjamin Eberlei
Hi Michal,

thank you for this RFC. In combination with typed properties an object
initializer syntax becomes highly thought after in my opinion, especially
if you consider classes consisting mostly of typed PUBLIC properties, for
example data transfer objects or view models.

I find this especially important, because it helps with definitions that
otherwise create unitialized variables:

class Customer
{
 public DateTime $created;
}

$foo = new Customer();
// $foo->created now unitialized

Using the object initializer would leave the object in a fully consistent
state:

$foo = new Customer{ created=new DateTime('now') }

Two points I want to mention:

1. Currently with typed properties RFC its possible to declare a public
property as not nullable, but don't assign it. PHP will make it use the new
"unitialized" state. This is also done to allow unset() properties later to
allow hooks via __get. But with this new syntax, we could enforce that if a
class is new'ed with the object initialzer, then at end of the object
initializer + constructor, all properties are initialized, otherwise
Exception. This could help with bugs where you add a property to the class,
but forget to assign it in all cases where the class is new'ed +
intialized. One problem could be with RuntimeException that this only
crashes when the code is run, which is often too late, and would rather
warrant to just proceed and assume the developer knows what they are doing.
Also since object initialiizer only sets public properties, the check
should be for all public variables are initialized.

2.
You should add mention how the Reflection API changes. I would assume both
ReflectionClass and ReflectionObject get a new method
newInstanceFields(array $fields) with the following behavior:

$reflectionClass = new ReflectionClass(Customer::class);
$customer = $reflectionClass->newInstanceFields(['name' => 'Bert']);
// the same as
$customer = new Customer {name = 'Bert'};



On Thu, Sep 12, 2019 at 4:01 PM Michał Brzuchalski <
michal.brzuchal...@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
> brzuc...@php.net
>


Re: [PHP-DEV] [RFC] Object Initializer

2019-09-14 Thread Michał Brzuchalski
Hi Mike,

sob., 14 wrz 2019 o 10:32 Mike Schinkel  napisał(a):

>
> 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():
>
>
Thank for your feedback and ideas, but I decided not to include it in the
RFC.

Thanks in advance,
Michał Brzuchalski


Re: [PHP-DEV] [RFC] Object Initializer

2019-09-14 Thread Michał Brzuchalski
Hi Sebastian,

sob., 14 wrz 2019 o 11:05 Sebastian Bergmann  napisał(a):

> Am 13.09.2019 um 15:23 schrieb Matthew Brown:
> > Though this is truly a stylistic complaint, I think it will lead to
> > harder-to-analyse code.
>
> Fully agreed, and not just harder-to-analyse for a static analysis tool
> but also for humans that read the code. -1 from me.
>
>
Could you explain why additional noise caused by constantly repeating
instance variable name and arrow in front of property name and assignment
part
is easier to read? Just wondering why you see object initializer as harder
to read which I've view as easier to read.

Example - easy to read:
function createCustomerDTO(): Customer
{
$customer = new Customer();
$customer->id = Uuid::uuid4;
$customer->name = "John Doe";
$customer->address = "Customer Street 16";
$customer->city = "London";
$customer->country = "GB";
$customer->phoneNumber = PhoneNumber::fromString("+1555 010 020");
$customer->birthDate = new DateTimeImmutable("1983-01-01");
$customer->email = Email::fromString("john@example.com");

return $customer;
}

Example - hard to read: Why?
function createCustomerDTO(): Customer
{
return new Customer {
id = Uuid::uuid4,
name = "John Doe",
address = "Customer Street 16",
city = "London",
country = "GB",
phoneNumber = PhoneNumber::fromString("+1555 010 020"),
birthDate = new DateTimeImmutable("1983-01-01"),
email = Email::fromString("john@example.com"),
};
}

Thanks in advance,
Michał Brzuchalski


Re: [PHP-DEV] [RFC] Object Initializer

2019-09-14 Thread Sebastian Bergmann

Am 13.09.2019 um 15:23 schrieb Matthew Brown:

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


Fully agreed, and not just harder-to-analyse for a static analysis tool 
but also for humans that read the code. -1 from me.


--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php



Re: [PHP-DEV] [RFC] Object Initializer

2019-09-14 Thread Mike Schinkel
> On Sep 14, 2019, at 1:50 AM, Marco Pivetta  wrote:

Thank you for your reply.

> lazy initialisation is already possible in userland: adding more magic to the 
> language for a use-case that is already implemented seems problematic to me.

I think I am missing something.  I was not aware I was advocating for lazy 
initialization, or at least not using those words to describe it.

If it is problematic could you please elaborate on exactly what you mean so I 
can better understand your argument?

Thanks in advance.

-Mike

Re: [PHP-DEV] [RFC] Object Initializer

2019-09-14 Thread Mike Schinkel
(Sorry for sending three replies to one message, but the list server said my 
reply was too long to send as just one. 
I could have made it shorter but then I would have had to omit all the example 
code.)

@rasmus:

HOWEVER, I see no reason we must pit object initializers and named parameters 
against each other as competing features. 

I think there is benefit to both, and ideally could actually be two aspects of 
the same new feature. 

Let me explain using one of your examples.

$car = new Car({ yearOfProduction => 1975, vin => "12345678"});

Conceptually let me ask, How is { yearOfProduction = 1975, vin = "12345678"} 
really any different from an instance of an anonymous class with the properties 
yearOfProduction and vin? 

From the recognition that named parameters that are collected as a group with 
braces ({}) are conceptually identical to anonymous class instances lets us to 
the insight we could use something like func_get_args(ARGS_OBJECT) to allow us 
to capture the grouped parameters as an instance of an object of anonymous 
class containing properties for each named parameter?

public function __construct(int $yearOfProduction, string $vin) {
   $args = func_get_args(ARGS_OBJECT);
   if ($args->yearOfProduction < 1900 ||  $args->yearOfProduction > date("Y")) {
  $msg = "year of production out of range:{$yearOfProduction}";
  throw new InvalidArgumentException($msg);
   }

   $this->yearOfProduction = $args->yearOfProduction;
   $this->vin = $args->vin;
}

Now assume we later realize we would be better passing in an instance instead 
of hardcoding the properties? We could change the signature, leaving all the 
rest of the code the same:

public function __construct(Customer $args) {
   if ($args->yearOfProduction < 1900 ||  $args->yearOfProduction > date("Y")) {
  $msg = "year of production out of range:{$yearOfProduction}";
  throw new InvalidArgumentException($msg);
   }

   $this->yearOfProduction = $args->yearOfProduction;
   $this->vin = $args->vin;
}

This is a critical capability in my book; the ability to refactor code to a 
more enlightened architecture without breaking it:

Of course if someone were to have used ordinal parameters when calling that 
constructor we could match their parameters to the properties of Customer in 
order of their declaration. 

So the following maps 2019 to $yearOfProduction and "abc123xyz789" to $vin:

class Car 
{
   public int $yearOfProduction;
   public string $vin;
   ...
}
$car = new Car(2019,"abc123xyz789"); 

The above could also now throw a warning so developers can find and replace all 
of those calls with calls that use the actual named parameters, but being a 
warning the code would still working until they update the calls.

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

I completely agree with this statement, outside the context of your prior 
argument against object initializers.

Hopefully however, I have made the case that object initializers combined with 
named parameters would address a much broader range of use-cases than either of 
them would on their own?

-Mike



Re: [PHP-DEV] [RFC] Object Initializer

2019-09-14 Thread Marco Pivetta
On Sat, Sep 14, 2019 at 10:47 AM Mike Schinkel  wrote:

> > On Sep 13, 2019, at 3:18 AM, Rasmus Schultz  ras...@mindplay.dk>> wrote:
> >
> > 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 => 'm...@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
>
>
Hey Mike,

lazy initialisation is already possible in userland: adding more magic to
the language for a use-case that is already implemented seems problematic
to me.

Marco Pivetta

http://twitter.com/Ocramius

http://ocramius.github.com/


Re: [PHP-DEV] [RFC] Object Initializer

2019-09-14 Thread Mike Schinkel
> On Sep 13, 2019, at 3:18 AM, Rasmus Schultz  > wrote:
> 

> 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 ) {
   

Re: [PHP-DEV] [RFC] Object Initializer

2019-09-14 Thread Mike Schinkel
> On Sep 13, 2019, at 3:18 AM, Rasmus Schultz  > wrote:
> 
> 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 => 'm...@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



Re: [PHP-DEV] [RFC] Object Initializer

2019-09-13 Thread Rowan Tommins
Hi Rasmus,

On Fri, 13 Sep 2019 at 11:18, Rasmus Schultz  wrote:

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


Re: [PHP-DEV] [RFC] Object Initializer

2019-09-13 Thread Matthew Brown
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.brzuchal...@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
> brzuc...@php.net
>


Re: [PHP-DEV] [RFC] Object Initializer

2019-09-13 Thread Arnold Daniels
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 

Re: [PHP-DEV] [RFC] Object Initializer

2019-09-13 Thread Lynn
On Fri, Sep 13, 2019 at 10:51 AM Arnold Daniels <
arnold.adaniels...@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


Re: [PHP-DEV] [RFC] Object Initializer

2019-09-13 Thread Rasmus Schultz
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.brzuchal...@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 

Re: [PHP-DEV] [RFC] Object Initializer

2019-09-13 Thread Mike Schinkel
> On Sep 13, 2019, at 2:39 AM, Michał Brzuchalski 
>  wrote:
> $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

Re: [PHP-DEV] [RFC] Object Initializer

2019-09-13 Thread Michał Brzuchalski
pt., 13 wrz 2019 o 11:29 Mike Schinkel  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


Re: [PHP-DEV] [RFC] Object Initializer

2019-09-13 Thread Mike Schinkel
> 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

Re: [PHP-DEV] [RFC] Object Initializer

2019-09-13 Thread Michał Brzuchalski
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


Re: [PHP-DEV] [RFC] Object Initializer

2019-09-13 Thread Michał Brzuchalski
Hi Arnold,

pt., 13 wrz 2019 o 10:51 Arnold Daniels 
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


Re: [PHP-DEV] [RFC] Object Initializer

2019-09-13 Thread Mike Schinkel
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

Re: [PHP-DEV] [RFC] Object Initializer

2019-09-13 Thread Arnold Daniels
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.


Re: [PHP-DEV] [RFC] Object Initializer

2019-09-13 Thread Mike Schinkel
> 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

Re: [PHP-DEV] [RFC] Object Initializer

2019-09-13 Thread Mike Schinkel
> 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



Re: [PHP-DEV] [RFC] Object Initializer

2019-09-13 Thread Michał Brzuchalski
Hi Claude,

pt., 13 wrz 2019 o 08:39 Claude Pache  napisał(a):

>
>
> Le 13 sept. 2019 à 07:49, Michał Brzuchalski 
> a écrit :
>
> Hi Lynn,
>
> czw., 12 wrz 2019 o 17:01 Lynn  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


Re: [PHP-DEV] [RFC] Object Initializer

2019-09-13 Thread Mike Schinkel
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




Re: [PHP-DEV] [RFC] Object Initializer

2019-09-13 Thread Claude Pache


> Le 13 sept. 2019 à 07:49, Michał Brzuchalski  a 
> écrit :
> 
> Hi Lynn,
> 
> czw., 12 wrz 2019 o 17:01 Lynn mailto:kja...@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



Re: [PHP-DEV] [RFC] Object Initializer

2019-09-13 Thread Michał Brzuchalski
Hi Olumide,

czw., 12 wrz 2019 o 17:07 Olumide Samson  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


Re: [PHP-DEV] [RFC] Object Initializer

2019-09-12 Thread Michał Brzuchalski
Hi Lynn,

czw., 12 wrz 2019 o 17:01 Lynn  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


Re: [PHP-DEV] [RFC] Object Initializer

2019-09-12 Thread Olumide Samson
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, 4:02 PM Lynn  wrote:

> On Thu, Sep 12, 2019 at 4:00 PM Michał Brzuchalski <
> michal.brzuchal...@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
> > brzuc...@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
>


Re: [PHP-DEV] [RFC] Object Initializer

2019-09-12 Thread Lynn
On Thu, Sep 12, 2019 at 4:00 PM Michał Brzuchalski <
michal.brzuchal...@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
> brzuc...@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