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]

Reply via email to