rfc:this_return_type

PHP RFC: $this return type

Introduction

The static return type introduced in PHP 8.0 allows specifying that a method must return an instance of the late-static-binding (LSB) scope, which is usually the case for named constructors, wither methods and fluent APIs. However, fluent APIs have a more specific contract that requires returning $this. This RFC introduces a $this return type for this purpose.

The $this return type follows an established convention from PHPDoc, where @return $this is commonly used in place of @return static where it comes to fluent interfaces. On a dataset of 2k composer packages, @return $this occurs 29k times, while @return static is used 38k times.

The $this return type is arguably not a type, in that enforces not just the type of the return value (which is the same as static), but also its object identity (that of $this). The object identity is important to the usage of the API, because it determines whether the return value can be safely discarded:

// Definitely valid if methods return $this type (fluent API).
// May not be valid if they only return static (may be wither API).
$obj->method1()
    ->method2()
    ->method3();
 
// Definitely valid if methods return $this or static type (fluent/wither irrelevant).
$obj = $obj->method1()
           ->method2()
           ->method3();

As such, even if a method uses a native static return type, it currently may still have to specify @return $this in addition to clarify the API contract. This is especially relevant for interfaces, where this not only determines valid usage, but also valid implementation.

For example, Symfony's cache ItemInterface specifies:

interface ItemInterface extends CacheItemInterface {
    // Other APIs omitted.
 
    /**
     * @return $this
     */
    public function tag(string|iterable $tags): static;
}

Under this RFC, it would become:

interface ItemInterface extends CacheItemInterface {
    public function tag(string|iterable $tags): $this;
}

Proposal

The new $this type can only be used as a return type, not as a property or parameter type, same as static. Additionally, it can only be applied in cases where an instance scope (potentially) exists, which is instance methods and non-static closures. It cannot be applied to free functions, static methods and static closures, as these can never have a $this value.

A method with $this return type is required to return $this, though it does not need to do so literally. The following is legal code:

class Test {
    public function method(): $this {
        $that = $this;
        return $that;
    }
}

The $this type is a subtype of static. As such, it is possible to restrict a static type to $this in a subclass (but not the other way around):

class A {
    public function method1(): static {}
    public function method2(): $this {}
}
 
class B extends A {
    // This override is legal.
    public function method1(): $this {}
 
    // This override is illegal.
    public function method2(): static {}
}

In reflection, the $this type is represented as a ReflectionNamedType with isBuiltin() == true and getName() == "$this".

Backward Incompatible Changes

There are no backwards-compatible changes, as $this is not a valid class name and could not be used in type position previously.

Code using ReflectionType may need to be updated to support the new $this type.

Vote

Yes/No.

rfc/this_return_type.txt · Last modified: 2021/09/07 10:11 by nikic