Is widening parameter types in a PHP interface implementation a good practice?

I have an interface defined as follows: interface MyInterface { public function setMyMethod(int $a); public function getMyMethod(): int; } And I have a class that widens the parameter type: class MyClass implements MyInterface { public function setMyMethod(int|string $a) {} public function getMyMethod(): int; } In languages like Java or C#, I could use method overloading to define two methods named myMethod: one with int $a to conform to the interface and another with string $a as a new implementation. Note that I am not widening the returning type, only how I set the value. In PHP, method overloading is achieved by using union types, as I did above. The PHP runtime accepts this approach (tested on versions 8.1, 8.2, and 8.3). My question is: Is this acceptable? Am I violating any good practices in PHP by doing this? I am unsure if this breaks the Liskov Substitution Principle. Classes expecting the MyInterface implementation should still work correctly. However, if one part of an application uses MyClass and is later replaced with another class that provides a "pure" implementation of MyInterface, there is a risk of breaking the application. From my perspective, since I can choose which implementation of the interface to use within my application, this should be fine. However, I want to ensure that I am not introducing any potential issues. EDIT: I am adding my real case scenario. I am exteding the PHP Psr7 Interface, an standard to do API request (https://www.php-fig.org/psr/psr-7/ and https://github.com/php-fig/http-message/) The interface is: interface RequestInterface extends MessageInterface { // ... public function getMethod(): string; public function withMethod(string $method): RequestInterface; // .. } My implementation will do: class Request implements RequestInterface { // ... public function getMethod(): string; public function withMethod(MethodEnum|string $method): RequestInterface; // .. } The change is to allow something like this: $request->withMethod(Method::Post); instead of $request->withMethod('POST');

Jan 14, 2025 - 13:35
Is widening parameter types in a PHP interface implementation a good practice?

I have an interface defined as follows:

interface MyInterface
{
    public function setMyMethod(int $a);
    public function getMyMethod(): int;
}

And I have a class that widens the parameter type:

class MyClass implements MyInterface
{
    public function setMyMethod(int|string $a) {}
    public function getMyMethod(): int;
}

In languages like Java or C#, I could use method overloading to define two methods named myMethod: one with int $a to conform to the interface and another with string $a as a new implementation. Note that I am not widening the returning type, only how I set the value.

In PHP, method overloading is achieved by using union types, as I did above.

The PHP runtime accepts this approach (tested on versions 8.1, 8.2, and 8.3).

My question is: Is this acceptable? Am I violating any good practices in PHP by doing this?

I am unsure if this breaks the Liskov Substitution Principle. Classes expecting the MyInterface implementation should still work correctly. However, if one part of an application uses MyClass and is later replaced with another class that provides a "pure" implementation of MyInterface, there is a risk of breaking the application.

From my perspective, since I can choose which implementation of the interface to use within my application, this should be fine. However, I want to ensure that I am not introducing any potential issues.

EDIT: I am adding my real case scenario.

I am exteding the PHP Psr7 Interface, an standard to do API request (https://www.php-fig.org/psr/psr-7/ and https://github.com/php-fig/http-message/)

The interface is:

interface RequestInterface extends MessageInterface
{
    // ...
    public function getMethod(): string;
    public function withMethod(string $method): RequestInterface;
    // ..
}

My implementation will do:

class Request implements RequestInterface
{
    // ...
    public function getMethod(): string;
    public function withMethod(MethodEnum|string $method): RequestInterface;
    // ..
}

The change is to allow something like this:

$request->withMethod(Method::Post);

instead of

$request->withMethod('POST');