'PHP setTime() return type not compatible but setTime() in DateTime class has no return?

I'm updating some PHP 5 code to PHP 7+ standards.

We have a AcmeDate class that extends DateTime and using setTime() is generating deprecated notices.

public function setTime($hours, $minutes, $seconds = 0, $microseconds = 0) {
    parent::setTime ( $hours, $minutes, $seconds, $microseconds );
}

E_DEPRECATED Return type of AcmeDate::setTime($hours, $minutes, $seconds = 0, $microseconds = 0) should either be compatible with DateTime::setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): DateTime, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice

I understand that DateTime::setTime() has a DateTime type because the core code is:

#[TentativeType]
public function setTime(
    #[LanguageLevelTypeAware(['8.0' => 'int'], default: '')] $hour,
    #[LanguageLevelTypeAware(['8.0' => 'int'], default: '')] $minute,
    #[LanguageLevelTypeAware(['8.0' => 'int'], default: '')] $second = 0,
    #[PhpStormStubsElementAvailable(from: '7.1')] #[LanguageLevelTypeAware(['8.0' => 'int'], default: '')] $microsecond = 0
): DateTime {}

But AFAIK the setTime() function doesn't actually return anything.

Adding : DateTime to AcmeDate::setTime() yields a Fatal Error:

public function setTime($hours, $minutes, $seconds = 0, $microseconds = 0):DateTime {
    parent::setTime ( $hours, $minutes, $seconds, $microseconds );
}

Fatal error: Uncaught TypeError: AcmeDate::setTime(): Return value must be of type DateTime, none returned in /home/Acme/AcmeDate.php:116

Using null as a return type also throws an error saying it can't be standalone:

Fatal error: Null can not be used as a standalone type in ...

Using never throws a different error about implicit returns which I don't understand:

AcmeDate::setTime(): never-returning function must not implicitly return in...

I'm at a loss at this point. I'm sure it's something simple.



Solution 1:[1]

Since PHP 8.1, if the parent method explicitly specifies the return type, then the new method must also explicitly specify the type (https://php.watch/versions/8.1/internal-method-return-types - "lack of a return type declaration is also considered a return type mismatch"). As of PHP 9.0 this will be considered a bug. The DateTime::setTime class method has an empty body because it is only a definition and its C++ implementation is called instead: https://github.com/php/php-src/blob/master/ext/date/php_date.c

The error "Return value must be of type DateTime, none returned in /home/Acme/AcmeDate.php" occurs because your method does not return anything and there is a strict check in the php configuration, on my version no error occurs, but without specifying return method will return null (https://www.php.net/manual/en/functions.returning-values.php).

If you still need to change the return type in the overridden method (or not specify it), then you can add the #[\ReturnTypeWillChange] attribute.

So either of these two options will work:

First:

public function setTime($hours, $minutes, $seconds = 0, $microseconds = 0):DateTime {
    return parent::setTime ( $hours, $minutes, $seconds, $microseconds );
}

Second:

#[\ReturnTypeWillChange]
public function setTime($hours, $minutes, $seconds = 0, $microseconds = 0) {
    return parent::setTime ( $hours, $minutes, $seconds, $microseconds );
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1