rfc:explicit_this_parameter

PHP RFC: Explicit $this parameter

  • Version: 1.0
  • Date: 2026-02-08
  • Author: Tim Düsterhus (tim@tideways-gmbh.com), Arnaud Le Blanc (arnaud.lb@gmail.com)
  • Status: Draft
  • Implementation: tbd

Introduction

The elevator pitch for the RFC. The first paragraph of this section will be rendered slightly larger to give it emphasis.

Please write an introduction that helps people by providing concise context and include a representative small code snippet.

<?php
 
echo "Showcase your idea here";
 
?>

Proposal

We propose allowing a named parameter this: to be combined with the “scope resolution operator” syntax of referring to a method to be used as an alternative way of calling an instance method. The value passed to the this: parameter will effectively be used as the left side of the -> operator:

Since the this: parameter does not refer to a parameter in the underlying argument list and thus there is no ambiguity with regard to positional parameters it may be placed at any position of the parameter list. In particular it may appear at the first position, before all positional parameters, mirroring the left-most position for a regular method call.

$now = new DateTimeImmutable();
 
$formatted = DateTimeImmutable::format(this: $now, "c");
// is effectively equivalent to
$formatted = $now->format('c');

Liskov Substitution Principle

The semantics follow that of a “wrapper function” copying all parameter names, parameter types and the return type of the method specified in the method reference. The parameter type of the this: parameter is the class name that is specified in the method reference. If the method is overridden in a child class, the overridden method will be called, not the method that is specified.

This also means that it is legal to specify an interface method and provide any object implementing the interface.

$formatted = DateTimeInterface::format(this: new DateTimeImmutable(), "c");

Since the semantics follow that of a “wrapper function”, the signature does not adapt to the object that is actually passed. If a parameter is widened in a child class only the original types remain valid. If a parameter is renamed, the parameter name of the specified method must be used.

interface MyInterface {
    public function method(string $foo): int|string|bool;
}
 
class ParentClass implements MyInterface {
    public function method(string|array $foo): string {
        return 'parent';
    }
}
 
class ChildClass extends ParentClass {
    public function method(string|array $bar): string {
        return 'child';
    }
}
 
// Throws TypeError, since an array is not a valid parameter on the interface,
// despite being valid on ChildClass::method().
MyInterface::method(this: new ChildClass(), []); 
 
// Throws an Error about an unknown parameter 'bar', because the parameter
// is called 'foo' on ParentClass::method().
ParentClass::method(this: new ChildClass(), bar: []);
 
// Calls ChildClass::method().
var_dump(ParentClass::method(this: new ChildClass(), bar: 'bar')); // string(5) "child"

It is however not allowed to provide an object that is not contravariant with the specified class name, even if it implements a method with a compatible signature:

final class NotDateTimeImmutable {
    public function format(string $format): string { }
}
 
// Throws a TypeError.
$formatted = DateTimeImmutable::format(this: new NotDateTimeImmutable(), "c");

From within the class

There is one exception from the rule that the child class' method will be called if it is overridden. For a call ClassName::method(this: $object):

  1. The referenced class name ClassName matches that of a parent (or grandparent, grandgrandparent, …) class of the current class (is_subclass_of(self::class, ClassName::class)).
  2. The object $object is an instance of the current class ($object instanceof self).
class GrandparentClass {
    public function method() {
        echo "Grandparent";
    }
}
 
class ParentClass extends GrandparentClass {
    public function method() {
        echo "Parent";
    }
}
 
class Example extends ParentClass {
    public function method() {
        echo "Example";
    }
 
    public function sameClass() {
        // Existing behavior: Echos "Grandparent".
        GrandparentClass::method();
 
        // this: parameter with $this as value. Also echos "Grandparent",
        // mirroring the existing behavior, but making it clear that
        // GrandparentClass::method() is not a static method call by
        // explicitly providing $this.
        GrandparentClass::method(this: $this);
 
        // The same happens when specifying a different object instance
        // of MyClass. This also echos "Grandparent".
        GrandparentClass::method(this: new MyClass());
    }
 
    public function childClass() {
        // Existing behavior: Echos "Grandparent".
        (new ChildClass())->bypassOverridden();
 
        // this: parameter with an instance of ChildClass as value.
        // Also echos "Grandparent", mirroring the existing capabilities.
        GrandparentClass::method(this: new ChildClass());
    }
 
    private function bypassOverridden() {
        GrandparentClass::method();
    }
 
    public function parentClass() {
        // It however does not happen when specifying an object instance
        // of a parent class.
        // This echos "Parent".
        GrandparentClass::method(this: new ParentClass());
    }
}
 
class ChildClass extends Example {
    public function method() {
        echo "Child";
    }
}

__call()

Virtual methods using __call() are supported as expected.

final class Example {
    public function __call(string $name, array $arguments) {
        var_dump($name);
    }
}
 
Example::magic(this: new Example()); // string(5) "magic"

Static Methods

Although not particularly useful, static methods are supported, similarly to how it is legal to call static methods using the -> operator.

final class Example {
    public static function staticMethod() {
        var_dump("called");
    }
}
 
Example::staticMethod(this: new Example()); // string(6) "called"

Partial Function Application

The this: parameter may be combined with partial function application to create a partial function where the “object instance” will be filled in at call time. Specifying the this: parameter outside the left-most position is particularly useful for partial function application, since it allows to specify the position of the object instance parameter relative to regular parameters. If a variadic placeholder is specified it must come last.

$c = DateTimeImmutable::format(this: ?, "c");
$c = DateTimeImmutable::format(this: ?, format: "c");
// are both effectively equivalent to:
$c = static fn (DateTimeImmutable $__this): string
    => $__this->format('c');
$c = DateTimeImmutable::setTimestamp(this: ?, ?);
$c = DateTimeImmutable::setTimestamp(this: ?, timestamp: ?);
// are both effectively equivalent to:
$c = static fn (DateTimeImmutable $__this, int $timestamp): DateTimeImmutable
    => $__this->setTimestamp($timestamp);
 
// and
 
$c = DateTimeImmutable::setTimestamp(this: ?, ...);
// is effectively equivalent to:
$c = static fn (DateTimeImmutable $__this, int $timestamp): DateTimeImmutable
    => $__this->setTimestamp($timestamp, ...array_slice(func_get_args(), 2));
 
// but
 
$c = DateTimeImmutable::setTimestamp(?, this: ?);
$c = DateTimeImmutable::setTimestamp(timestamp: ?, this: ?);
// are both effectively equivalent to:
$c = static fn (int $timestamp, DateTimeImmutable $__this): DateTimeImmutable
    => $__this->setTimestamp($timestamp);

If the this: parameter is placed after optional parameters, they will become required.

$c = DateTimeImmutable::setTime(this: ?, ?, ?, ?, ?);
// is effectively equivalent to:
$c = static fn (DateTimeImmutable $__this, int $hour, int $minute, int $second = 0, int $microsecond = 0): DateTimeImmutable
    => $__this->setTime($hour, $minute, $second, $microsecond);
 
// but
 
$c = DateTimeImmutable::setTime(?, ?, ?, ?, this: ?);
// is effectively equivalent to:
$c = static fn (int $hour, int $minute, int $second, int $microsecond, DateTimeImmutable $__this): DateTimeImmutable
    => $__this->setTime($hour, $minute, $second, $microsecond);

Examples

Remember that the RFC contents should be easily reusable in the PHP Documentation. This means, if at all possible, they should be runnable as standalone, self-contained code with the proof-of-concept implementation.

Simple example:

<?php
 
echo "First example";
 
?>

Example showing an edge case:

<?php
 
echo "Edge case";
 
?>

Backward Incompatible Changes

What breaks, and what is the justification for it?

Please include all breaking changes, no matter how minor they might appear. All research you did on potential impact should be listed in this section.

For adding new functions, classes or keywords, here are some possibilities to measure potential impact:

Proposed PHP Version(s)

Next PHP 8.x (8.6).

RFC Impact

To the Ecosystem

What effect will the RFC have on IDEs, Language Servers (LSPs), Static Analyzers, Auto-Formatters, Linters and commonly used userland PHP libraries?

To Existing Extensions

None.

To SAPIs

None.

Open Issues

None.

Future Scope

This section should outline areas that you are not planning to work on in the scope of this RFC, but that might be iterated upon in the future by yourself or another contributor.

This helps with long-term planning and ensuring this RFC does not prevent future work.

Voting Choices

Primary Vote requiring a 2/3 majority to accept the RFC:

Implement an explicit $this parameter as outlined in the RFC?
Real name Yes No Abstain
Final result: 0 0 0
This poll has been closed.

Patches and Tests

tbd

Implementation

After the RFC is implemented, this section should contain:

  1. the version(s) it was merged into
  2. a link to the git commit(s)
  3. a link to the PHP manual entry for the feature

References

Links to external references, discussions, or RFCs.

Rejected Features

None.

Changelog

  • 2026-02-08: Initial version
rfc/explicit_this_parameter.txt · Last modified: by timwolla