Suggested change: change priority of new and ->

php.internals

Mcmuffin Mcguffin

8 years ago
Hi, It's a common idiom in object-oriented languages to create an object and then immediately call a method on it, such as (in C# or Java): new DateTime ().ToString () However, PHP grammar does not work this way. You have to put an additional round of parentheses around new clause, such as: (new DateTime ())->format ('Y-m-d') This is rather clunky for such a common scenario (you usually have to walk all the way back to the beginning of the expression to fix it). I have a pattern of using static factories in place of constructor just to avoid extra parentheses. Besides, the issue is relatively easy to fix. I've done and tested a change for grammar in this area. It's a slight adjustment to definition of dereferencable: dereferencable:
> variable { $$ = $1; } > | '(' expr ')' { $$ = $2; } > * | new_expr { $$ = $1; }* > | dereferencable_scalar { $$ = $1; } > ; >
There was a problem with shift/reduces though. PHP has a level of complexity in new expression which other languages don't. It's variable class instantiation, such as: new $class (arguments) Expressions in place of $class can be rather complex, though I don't think they really need to be. For example, such stuff is allowed: new $var[0]->prop (arguments) — which seems redundant and confusing. My fix to the problem was to force parentheses around complex expressions such as above: new ($var[0]->prop) (arguments) — while keeping simple cases of "new $class (arguments)" without change. Yes, this moves parentheses from one place to another (and occasionally breaks the old code) but frequency wise I think it's an improvement. What do you think? Jaroslav Wegner

Haralan Dobrev

8 years ago
I really like the proposal and it's definitely improving more common modern use cases. However, I understand why the precedence is like that now in PHP to begin with and so I think this should target PHP 8. This change would be a bit similar to other weird use cases of variables which were changed with PHP 7. Best, Harry On Thu, 8 Feb 2018 at 20:45 Mcmuffin Mcguffin <mcmuffinmcguffin@gmail.com> wrote:
> Hi, > > It's a common idiom in object-oriented languages to create an object and > then immediately call a method on it, such as (in C# or Java): > > new DateTime ().ToString () > > However, PHP grammar does not work this way. You have to put an additional > round of parentheses around new clause, such as: > > (new DateTime ())->format ('Y-m-d') > > This is rather clunky for such a common scenario (you usually have to walk > all the way back to the beginning of the expression to fix it). I have a > pattern of using static factories in place of constructor just to avoid > extra parentheses. Besides, the issue is relatively easy to fix. I've done > and tested a change for grammar in this area. It's a slight adjustment to > definition of dereferencable: > > dereferencable: > > variable { $$ = $1; } > > | '(' expr ')' { $$ = $2; } > > * | new_expr { $$ = $1; }* > > | dereferencable_scalar { $$ = $1; } > > ; > > > > There was a problem with shift/reduces though. PHP has a level of > complexity in new expression which other languages don't. It's variable > class instantiation, such as: > > new $class (arguments) > > Expressions in place of $class can be rather complex, though I don't think > they really need to be. For example, such stuff is allowed: > > new $var[0]->prop (arguments) > > — which seems redundant and confusing. My fix to the problem was to force > parentheses around complex expressions such as above: > > new ($var[0]->prop) (arguments) > > — while keeping simple cases of "new $class (arguments)" without change. > Yes, this moves parentheses from one place to another (and occasionally > breaks the old code) but frequency wise I think it's an improvement. > > What do you think? > > Jaroslav Wegner >
-- Haralan Dobrev CTO @ Clippings.com https://hkdobrev.com

Fleshgrinder

8 years ago
On 2/8/2018 7:38 PM, Mcmuffin Mcguffin wrote:
> What do you think? > > Jaroslav Wegner >
Thanks for the hard work to figure out what the roots of this annoyance is. This could land in the next PHP version, considering that many other breaking changes were also allowed in the past, given the following prerequisites: * RFC with a 2/3 majority vote * Tool to convert any possibly broken code into valid code (basically putting parenthesis around any complex `new (...)()`). * Update to https://github.com/php/php-langspec/blob/master/spec/10-expressions.md#the-new-operator
-- Richard "Fleshgrinder" Fussenegger

Stephen Reay

8 years ago
> On 9 Feb 2018, at 01:38, Mcmuffin Mcguffin <mcmuffinmcguffin@gmail.com> wrote: > > new $var[0]
Is that creating an instance of the class name stored in the 0th index of the array $var or is it creating an instance of the class name stored in $var and accessing the 0th index using ArrayAccess? Cheers Stephen

Pedro Lacerda

8 years ago
Great contribution, very sensible even if subtile. Is the kind of change that improve a lot but cause some trouble to people used to the old way or when upgrading legacy codebases. 2018-02-15 5:32 GMT-03:00 Stephen Reay <php-lists@koalephant.com>:
> > > On 9 Feb 2018, at 01:38, Mcmuffin Mcguffin <mcmuffinmcguffin@gmail.com> > wrote: > > > > new $var[0] > > Is that creating an instance of the class name stored in the 0th index of > the array $var or is it creating an instance of the class name stored in > $var and accessing the 0th index using ArrayAccess? > > Cheers > > Stephen > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > >
-- Atenciosamente, Pedro Lacerda

Mcmuffin Mcguffin

8 years ago
> > Is that creating an instance of the class name stored in the 0th index of > the array $var or is it creating an instance of the class name stored in > $var and accessing the 0th index using ArrayAccess? >
As soon as $var gets involved, it should be prioritized above the new operator. So it's equivalent of "new ($var[0])" Jaroslav

Rowan Collins

8 years ago
On 8 February 2018 18:38:36 GMT+00:00, Mcmuffin Mcguffin <mcmuffinmcguffin@gmail.com> wrote:
>Hi, > >It's a common idiom in object-oriented languages to create an object >and >then immediately call a method on it, such as (in C# or Java): > >new DateTime ().ToString () > >However, PHP grammar does not work this way.
This would definitely be nice to have, and thanks for looking into what the necessary change would look like. I think the next challenge is to work out the different ways a dynamic class construction could happen, and which ones: a) will be unaffected by the change b) are an error now and will be allowed after c) are allowed now and will be an error after d) will work in both versions but change behaviour It's anything that falls into category (d) that is particularly problematic, as it means users will see odd bugs of they don't know about the change. The clearer we can make these examples, the better. Contrary to other responses, I think this would definitely need to happen in a major version, as it is quite clearly a breaking change. There's not much that can be done to deprecate code relying on the current rules, but we can publicise how to make code that is compatible before and after. Regards,
-- Rowan Collins [IMSoP]

Andrew Faulds

8 years ago
Hi, I'm not… quite sure if it changes anything, but it seems worth pointing out PHP has two syntaxes for `new`. You can do `new DateTime()`, but you can /also/ do `new DateTime` without the brackets. So, while you can't do `new DateTime()->format(DATE_ATOM)`, you *can* do `(new DateTime)->format(DATE_ATOM)`. This is just as short. It's only for constructor calls requiring arguments that we get the problem. It also makes me wonder if it's not dereferenceable because then `new DateTime()` would be ambiguous: is it constructing a DateTime, or is it invoking a newly-constructed DateTime as a function? Be aware that __invoke can be implemented by a class to make its objects callable. Thanks!
-- Andrea Faulds https://ajf.me/