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):
- The referenced class name
ClassNamematches that of a parent (or grandparent, grandgrandparent, …) class of the current class (is_subclass_of(self::class, ClassName::class)). - The object
$objectis 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.
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:
- A script from nikic that checks the top 1000 Composer packages https://gist.github.com/nikic/a2bfa3e2f604f66115c3e4b8963a6c72
- GitHub regexp search
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:
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