Req #71419 [Opn->Csd]: Allow ::new() syntax.

From: Date: Sat, 26 Mar 2016 21:27:15 +0000
Subject: Req #71419 [Opn->Csd]: Allow ::new() syntax.
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-200118@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=71419&edit=1 ID: 71419 Updated by: krakjoe@php.net Reported by: andreas at dqxtech dot net Summary: Allow ::new() syntax. -Status: Open +Status: Closed Type: Feature/Change Request Package: Class/Object related PHP Version: 7.0.2 -Assigned To: +Assigned To: krakjoe Block user comment: N Private report: N New Comment: For this kind of change, an RFC is required. Please see: https://wiki.php.net/rfc/howto Previous Comments: ------------------------------------------------------------------------ [2016-03-13 15:29:57] andreas at dqxtech dot net @marcio ::new() would always have the same signature as the constructor. It would implictly call the constructor. C::new($x, $y, $z) would be equivalent with (new C($x, $y, $z)). So, I think the problem you see does not exist. On the other hand, we have to ask if method_exists('C', 'new') would return TRUE or FALSE. And what ReflectionClass would say about the pseudo-method "new". And yes, if we see it as a method, then it would break the Liskov substitution principle - if we say it applies to static methods at all. So maybe the easiest would be to immediately let the interpreter translate C::new() to (new C()), and never treat it as a method. And what about is_callable(['C', 'new']) ? This shows both a problem and an opportunity. Passing a ['C', 'new'] around like any regular callback would be quite powerful, because it would unify factory callbacks. But it also means special casing and discussion for a number of core functions/functionality like method_exists(), is_callable(), and reflection. > So far this is what I've been questioning: > > - Would ::new() always instantiate an empty (and useless) ImmutableArray? No. ImmutableArray::new() will trigger a "Warning: Missing argument 1 for ImmutableArray::__construct()". > - Would it be necessary to implement ::new() to ::new(array $data) so it follows the same spec > of __construct(array $data)? No. The implicit pseudo-method ::new() already has the signature of the constructor. - Would we be able to overwrite ::new() at all? Before PHP 7, I would have said that "new" is a reserved word, and cannot be used as a method name. C::new($x) always means (new C($x)), and triggers the constructor. Hence, if you overwrite the constructor, you have implicitly overwritten the pseudo-method "::new()". Now with PHP 7 out, methods with the name new() are allowed to exist. The only language design option that does not break existing PHP 7 code would be to say that C::new($x) means (new C($x)), *unless* a real method C::new() exists, in which case this method is called instead. But people can still use the old "new C()" syntax to avoid calling the existing static method. On the positive side, this allows to add a "public static function new()", without having to change calling code - if all this calling code is already using the ::new() syntax. ------------------------------------------------------------------------ [2016-03-13 15:29:54] andreas at dqxtech dot net @marcio ::new() would always have the same signature as the constructor. It would implictly call the constructor. C::new($x, $y, $z) would be equivalent with (new C($x, $y, $z)). So, I think the problem you see does not exist. On the other hand, we have to ask if method_exists('C', 'new') would return TRUE or FALSE. And what ReflectionClass would say about the pseudo-method "new". And yes, if we see it as a method, then it would break the Liskov substitution principle - if we say it applies to static methods at all. So maybe the easiest would be to immediately let the interpreter translate C::new() to (new C()), and never treat it as a method. And what about is_callable(['C', 'new']) ? This shows both a problem and an opportunity. Passing a ['C', 'new'] around like any regular callback would be quite powerful, because it would unify factory callbacks. But it also means special casing and discussion for a number of core functions/functionality like method_exists(), is_callable(), and reflection. > So far this is what I've been questioning: > > - Would ::new() always instantiate an empty (and useless) ImmutableArray? No. ImmutableArray::new() will trigger a "Warning: Missing argument 1 for ImmutableArray::__construct()". > - Would it be necessary to implement ::new() to ::new(array $data) so it follows the same spec > of __construct(array $data)? No. The implicit pseudo-method ::new() already has the signature of the constructor. - Would we be able to overwrite ::new() at all? Before PHP 7, I would have said that "new" is a reserved word, and cannot be used as a method name. C::new($x) always means (new C($x)), and triggers the constructor. Hence, if you overwrite the constructor, you have implicitly overwritten the pseudo-method "::new()". Now with PHP 7 out, methods with the name new() are allowed to exist. The only language design option that does not break existing PHP 7 code would be to say that C::new($x) means (new C($x)), *unless* a real method C::new() exists, in which case this method is called instead. But people can still use the old "new C()" syntax to avoid calling the existing static method. On the positive side, this allows to add a "public static function new()", without having to change calling code - if all this calling code is already using the ::new() syntax. ------------------------------------------------------------------------ [2016-03-11 05:45:02] marcio@php.net Hi! The main reason that could make a default ::new() method impracticable, at least to my POV, is that __construct() doesn't follow the 'normal' inheritance rules from other methods (see https://3v4l.org/LeaZ1), a default ::new() method would have to be special cased too and therefore would require a 'magic' behavior. How should ::new() behave when a third party class __construct requires parameters in order to instantiate an usable instance? Consider: class ImmutableArray { private $data = []; function __construct(array $data) { $this->data = $data; } /* then other methods to read $this->data */ } So far this is what I've been questioning: - Would ::new() always instantiate an empty (and useless) ImmutableArray? - Would it be necessary to implement ::new() to ::new(array $data) so it follows the same spec of __construct(array $data)? - Would we be able to overwrite ::new() at all? I'd love to have ::new() from day 1 php was designed but now, unfortunately, having __construct() and ::new() on first class at the same time creates more issues than it should even though there is beauty in symmetry :/ ------------------------------------------------------------------------ [2016-02-04 00:56:59] andreas at dqxtech dot net > You're asking for a factory method as a built-in language construct/feature. Yes, what you describe is exactly what I mean. > If your reasoning lies strictly on method chaining, static methods (factory or not) is a > language feature you should use. But then I need to manually add this to every class. What if the class is from a 3rd party? And does the DX benefit really justify adding empty static methods everywhere? This is why I am looking for a build-in, implicit static method. (you could now ask back, whether this justifies a new language feature.. which is debatable, of course) > The primary difference between "C::new()" and "(new C())" is one character > less in length. The benefit is not in typing one character less, but: - It becomes easier to switch between a static factory call and a new() call. - If you don't know yet whether the class has a static factory method and/or a public constructor, you can start typing the class name and then have the IDE suggest the different options. - You no longer need to think about adding or not adding the round brackets for method chaining. Yes, this is only about DX, which is obviously debatable. Examples: Replacing "C::new()" with "C::create()" is easier than replacing "(new C())" with "C::create()". Extending "C::new()" to "C::new()->foo()" is easier than extending "new C()" to "(new C())->foo()". Reducing "C::new()->foo()" to C::new() is easier than reducing "(new C())->foo() to "new C()". C::new() to C::new() ->foo() is only one line of diff. new C() to (new C()) ->foo() is two lines of diff. > At best, that will lead to a slippery-slope (e.g. I'd like C::delete to call C::__destruct > - even though I can just use $instance = null or unset($instance)). Yes, it would be interesting if there are other keywords where this makes sense. But so far I cannot think of any. C::delete($object) is really not useful, because the class can already be determined from $object. Another examples could be C::reflection() (to create a new reflection class). But I don't think this is as big a use case that it justifies a new language feature. The refactoring from (new C()) to (C::create()), on the other hand, is something I come across all the time. > If you feel strongly about this, you should write up an RFC, and put it out there for > discussion. Will see. ------------------------------------------------------------------------ [2016-02-01 16:00:07] willfitch@php.net > Proposal: Allow a syntax "C::new()", which would be equivalent to "(new > C())" You're asking for a factory method as a built-in language construct/feature. While I won't assume your situation warrants this in your opinion, I have/can never imagine why this is warranted. If your reasoning lies strictly on method chaining, static methods (factory or not) is a language feature you should use. The primary difference between "C::new()" and "(new C())" is one character less in length. Looking at this from a purely language perspective, you're essentially asking for an exception to a static method call. At best, that will lead to a slippery-slope (e.g. I'd like C::delete to call C::__destruct - even though I can just use $instance = null or unset($instance)). If you feel strongly about this, you should write up an RFC, and put it out there for discussion. You won't get much traction in the bugs area. ------------------------------------------------------------------------ The remainder of the comments for this report are too long. To view the rest of the comments, please view the bug report online at https://bugs.php.net/bug.php?id=71419 -- Edit this bug report at https://bugs.php.net/bug.php?id=71419&edit=1

« previous php.bugs (#200118) next »