-
Notifications
You must be signed in to change notification settings - Fork 8k
Description
Roadmap
- Make implementation
- Prepare full RFC draft
- Prepare a message for
internals@lists.php.net
to get wiki RFC karma (https://externals.io/message/117771) - Get RFC karma
- Publish RFC draft on the wiki.php.net
- Open PR with the implementation
RFC Draft:
Introduction
Since PHP 8.0 we have the Constructor Property Promotion feature.
Often, using this feature, we get a constructor with an empty body:
class Point {
public function __construct(
public float $x = 0.0,
public float $y = 0.0,
public float $z = 0.0,
) {
}
}
Sometimes we need to make constructor private. Just try to find this fragment in the vendor dir of any your project.
final class Foo {
private function __construct()
{
}
}
Sometimes we also need to describe empty methods because an interface or an abstract class requires it.
namespace Monolog\Handler;
interface HandlerInterface
{
// ...
public function close(): void;
}
abstract class Handler implements HandlerInterface
{
// ...
public function close(): void
{
}
}
All these extra brackets could be replaced by semicolons, just as we can do now in loops:
foreach ($generator as $_);
while (Loop::handle());
for (;$g(););
// instead of
foreach ($generator as $_) {
// do nothing
}
while (Loop::handle()) {
// do nothing
}
for (;$g();) {
// do nothing
}
A similar topic has been raised before [2]. The opinions of the participants differed, but the RFC was never created. At least, I didn't find it.
In that discussion Nikita Popov mentioned that quite a few people suggested adding this functionality.
I can reinforce these words: many developers in my environment, including myself, expected a similar syntax already in PHP 8.0, 8.1 and then in PHP 8.2.
Some people may think that the change should be in the coding standard, but not in the language. However, I believe it would be a positive change in terms of development experience (DX). You can move the braces to one line, but the ugliness will not go anywhere.
final class Dto {
public function __construct(
public int $x,
public int $y,
public int $z,
) {}
}
Among other things, there is an opinion that there is a difference between a marked empty method body and a semicolon: in the first case, the developer explicitly declares that the method does nothing, while in the second, the implementation is not yet defined. This is specific to some other languages, but our language already has the abstract keyword for it. Feel free to correct me if I'm wrong.
Proposal
Add the ability to replace an empty function body with a semicolon:
class Point {
public function __construct(
public float $x = 0.0,
public float $y = 0.0,
public float $z = 0.0,
);
}
Not only for constructors, but also for regular class methods that have the void return type:
class Foo {
public function foo(): void;
public static function bar(int $baz): void;
}
(new Foo())->foo();
Foo::bar(42);
All other cases when the return type is defined and it differs from the void are not covered by this RFC. They are invalid.
class Foo() {
// Ambiguous values
public function a(): int;
public function b(): string;
public function c(): Foo&Bar;
public function d(): iterable;
public function e(): array;
public function f(): self;
public function g(): static;
public function h(): ?Foo;
public function h(): mixed;
// ...
// And unambiguous
public function x(): null;
public function y(): false;
public function z(): true;
}
Backward incompatible changes
This RFC doesn't contain any backwards incompatible changes.
Proposed PHP version
PHP 8.3
RFC impact
The error text "Non-abstract method %s::%s() must contain body" might be changed.
The effect on the opcache I have not investigated.
Open issues
Body-less methods without return type
Should we allow trimming the empty body of methods that have no returning type? On the one hand, we want more consistency, because the constructor doesn't have a return type either. On the other hand, we want more strictness.
class Foo {
public function bar();
}
Body-less methods in abstract classes
There is a concern that if the developer put a semicolon, he forgot to specify the method as abstract. I'm not sure if this is a language problem, but there is a solution: disallow body-less methods in abstract classes. If necessary, this constraint can be removed in a next minor version of PHP.
abstract class Foo {
public function bar(): mixed; // Exception will be thrown
}
The constraint is questionable because it violates the consistency between abstract and non-abstract classes.
Future scope
Body-less classes
This would be a good precedent for an RFC about body-less classes.
class DefaultMyLibraryException extends \RuntimeException implements MyLibraryException;
// instead of
class DefaultMyLibraryException extends \RuntimeException implements MyLibraryException
{
}
Body-less methods with returning types
Given that the return values of functions can be uniquely defined through their types (null, false [3], true [4]) we can talk about the possibility of such syntactic sugar:
interface User {
function isAdmin(): bool;
function getFoo(): ?Foo;
}
class Admin implemets User
{
function isAdmin(): true
{
return true;
}
function getFoo(): null
{
return null;
}
}
// can be replaced with
class Admin extends User
{
function isAdmin(): true;
function getFoo(): null;
}
This is not the same as short functions from declined [5], because no expressions are used, types only.
Patches and Tests
I made the patch (and tests) [6] that adds ability to describe body-less methods in abstract and non-abstract classes that are:
- constructor
- methods with the void return type
- methods without return type
I will make adjustments after the open issues are resolved.