Skip to content

Cutting off an empty body of a function #8420

@roxblnfk

Description

@roxblnfk

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.

References

  1. PHP RFC: Constructor Property Promotion
  2. External discussion: Body-less __construct
  3. PHP RFC: Allow null and false as stand-alone types
  4. PHP RFC: Add true type
  5. PHP RFC: Short Functions
  6. Branch with patch (will be replaced with PR)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions