On Mon, 26 Aug 2024, at 11:43, Mike Schinkel wrote: >> You ask how a library can provide access to that default, and the answer is >> generally pretty trivial: define a public constant, and refer to it in the >> parameter definition. > > A global? Really?
I didn't say "global", I said "public". Since you're keen on real-world examples, here's a simplified version of a real class: class ConfigSource { private HttpClientInterface $httpClient; public const DEFAULT_CONSUL_URL = 'http://localhost:8500'; public function __construct(private string $consulUrl=self::DEFAULT_CONSUL_URL, ?HttpClientInterface $httpClient=null) { if ( $httpClient === null ) { $httpClient = new HttpClient(); $httpClient->setRequestTimeoutSecs(5); $httpClient->setConnectRequestTimeoutSecs(5); } $this->httpClient = $httpClient; } } This constructor has two optional parameters; one of them uses a default which is also referenced explicitly in another class, so is exposed as a constant; the other uses a completely opaque method of creating a default object, which even Reflection could not expose. The caller doesn't need to know how any of this works to make use of the class. The contract is "__construct(optional string $consulUrl, optional ?HttpClientInterface $httpClient)" The purpose of the optional parameters is so that you can write "$configSource = new ConfigSource();" and trust the library to provide you a sensible default behaviour. If it was decided that the code for creating a default HttpClient was needed elsewhere, it could be refactored into a method, with appropriate access: public function __construct(private string $consulUrl=self::DEFAULT_CONSUL_URL, ?HttpClientInterface $httpClient=null) { $this->httpClient = $httpClient ?? $this->getDefaultHttpClient(); } public function getDefaultHttpClient(): HttpClient { $httpClient = new HttpClient(); $httpClient->setRequestTimeoutSecs(5); $httpClient->setConnectRequestTimeoutSecs(5); return $httpClient; } Or perhaps the HttpClient becomes nullable internally: public function __construct(private string $consulUrl=self::DEFAULT_CONSUL_URL, private ?HttpClientInterface $httpClient=null) {} Or maybe we allow explicit nulls, but default to a simple class instantiation: public function __construct(private string $consulUrl=self::DEFAULT_CONSUL_URL, private ?HttpClientInterface $httpClient=new HttpClient) {} None of these are, currently, breaking changes - the contract remains "__construct(optional string $consulUrl, optional ?HttpClientInterface $httpClient)". Regards, --- Rowan Tommins [IMSoP]