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]