On Mon, 26 Aug 2024, at 10:03, Andreas Leathley wrote:
> interface CompressionInterface
> {
> public function compress(string $data, int $level): string;
> }
>
> class GzipCompression implements CompressionInterface
> {
> public function compress(string $data, int $level = 4): string
> {
> // do something
> }
> }
>
> When I have the GzipCompression class, I would know there is a default
> value for $level, but when using the interface there might or might not
> be a default value, depending on the implementation.
This isn't unique to defaults; GzipCompression could also widen the type to
int|string, for instance, and there's no syntax for detecting that either.
If you have access to change class GzipCompression, you can resolve this by
creating an additional interface:
interface SimplifiedCompressionInterface extends CompressionInterface
{
public function compress(string $data, int $level = 4): string;
}
class GzipCompression implements SimplifiedCompressionInterface ...
Then, if we can agree an implementation, you could write:
/** @var CompressionInterface $comp */
$comp->compress($myData, $comp instanceof SimplifiedCompressionInterface ?
default : MY_DEFAULT_LEVEL);
If you don't have access to change the hierarchy, then what you're probably
looking for is structural typing, or implicit interfaces - i.e. a way to ask
"does this object meet these criteria". For instance, some imaginary pattern
matching on the signature:
/** @var CompressionInterface $comp */
$comp->compress($myData, $comp is { compress(string, optional int) } ? default
: MY_DEFAULT_LEVEL);
Note how, in both cases, we're not asserting anything about the default value
itself, only that the signature defines the parameter as optional. It's
actually a bit of a quirk that the interface has to specify a value, rather
than just stating this:
interface SimplifiedCompressionInterface extends CompressionInterface
{
public function compress(string $data, optional int $level): string;
}
Regards,
--
Rowan Tommins
[IMSoP]