Edit report at https://bugs.php.net/bug.php?id=71419&edit=1
ID: 71419
User updated by: andreas at dqxtech dot net
Reported by: andreas at dqxtech dot net
Summary: Allow ::new() syntax.
Status: Open
Type: Feature/Change Request
Package: Class/Object related
PHP Version: 7.0.2
Block user comment: N
Private report: N
New Comment:
@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.
Previous Comments:
------------------------------------------------------------------------
[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.
------------------------------------------------------------------------
[2016-01-25 15:54:49] andreas at dqxtech dot net
I think before accepting the RFC it would have been a good idea to check which of these reserved
words could have a useful meaning with "::", and thus, which doors are being closed by
allowing these keywords as method names.
E.g. for C::while() I don't really see anything useful other than interpreting it as a method.
So it is fine to make "while" a valid method name.
For C::new(), this could be an alternative syntax for "new C()". So maybe a good idea to
keep this reserved..
C::class already has a meaning. C::namespace could be given a meaning.
For now, C::new() is the only one that sticks out to me for method syntax.
------------------------------------------------------------------------
[2016-01-25 15:44:49] andreas at dqxtech dot net
Thanks for the RFC link!
> Anyway, you could use a trait to make your wished pattern easier to implement (on 7.0+):
Sure.. but of course the idea here was to make this a language feature, so it would work for all
classes, including those from 3rd party libraries, where I cannot add a "use Newable".
I guess it is not going to happen.
------------------------------------------------------------------------
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