====== 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.
===== 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) if:
- 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)).
- The object $object is an instance of the current class ($object instanceof self).
The method() defined in ClassName will be called, not $object->method().
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 and Free-standing functions ====
Free-standing functions however may not be called with this:.
function example() { }
example(this: new stdClass()); // Not allowed.
Although not particularly useful, static methods are supported, similarly to how it is legal to call static methods using the -> operator. The static::class value matches the class of the passed object:
class ParentClass {
public static function staticMethod() {
var_dump(static::class);
}
}
class ChildClass extends ParentClass { }
ParentClass::staticMethod(this: new ChildClass()); // string(10) "ChildClass"
It remains illegal to define a parameter called $this:
final class Example {
public static function staticMethod(
$this, // Not allowed.
) { }
}
==== 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);
=== From Within The Class ===
The method lookup rules for direct calls apply equally to partial function application. The self at the time of the PFA declaration is relevant.
class GrandparentClass {
public function method() {
return "Grandparent";
}
}
class ParentClass extends GrandparentClass {
public function method() {
return "Parent";
}
}
class Example extends ParentClass {
public function method() {
return "Example";
}
public static function run($objects) {
return array_map(GrandparentClass::method(this: ?), $objects);
}
public static function runWithPfa($pfa, $objects) {
return array_map($pfa, $objects);
}
public static function getPfa() {
return GrandparentClass::method(this: ?);
}
}
class ChildClass extends Example {
public function method() {
return "Child";
}
}
$objects = [
new GrandparentClass(),
new ParentClass(),
new Example(),
new ChildClass(),
];
// PFA is defined and called within Example.
// Echos "Grandparent, Parent, Grandparent, Grandparent"
echo implode(", ", Example::run($objects));
// PFA is defined within Example and called within the global scope.
// Echos "Grandparent, Parent, Grandparent, Grandparent"
echo implode(", ", array_map(Example::getPfa(), $objects));
// PFA is defined and called within the global scope.
// Echos "Grandparent, Parent, Example, Child"
echo implode(", ", array_map(GrandparentClass::method(this: ?), $objects));
// PFA is defined within the global scope and called within Example
// Echos "Grandparent, Parent, Example, Child"
echo implode(", ", Example::runWithPfa(GrandparentClass::method(this: ?), $objects));
==== Examples ====
There are two main use cases. The primary use case is enabling partial function application for the object instance:
The other is making calls to grandparent methods explicit:
===== 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:
* A script from nikic that checks the top 1000 Composer packages https://gist.github.com/nikic/a2bfa3e2f604f66115c3e4b8963a6c72
* GitHub regexp search
* https://grep.app/
* https://about.sourcegraph.com/
===== 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:
* Yes
* No
* Abstain
===== Patches and Tests =====
tbd
===== Implementation =====
After the RFC is implemented, this section should contain:
- the version(s) it was merged into
- a link to the git commit(s)
- 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