<?php 
 
declare(strict_types=1); 
 
namespace Sentry; 
 
use Http\Client\Common\Plugin as PluginInterface; 
use Http\Client\HttpAsyncClient; 
use Http\Discovery\MessageFactoryDiscovery; 
use Http\Discovery\StreamFactoryDiscovery; 
use Http\Discovery\UriFactoryDiscovery; 
use Http\Message\MessageFactory as MessageFactoryInterface; 
use Http\Message\StreamFactory as StreamFactoryInterface; 
use Http\Message\UriFactory as UriFactoryInterface; 
use Jean85\PrettyVersions; 
use Psr\Log\LoggerInterface; 
use Sentry\HttpClient\HttpClientFactory; 
use Sentry\HttpClient\PluggableHttpClientFactory; 
use Sentry\Serializer\RepresentationSerializer; 
use Sentry\Serializer\RepresentationSerializerInterface; 
use Sentry\Serializer\Serializer; 
use Sentry\Serializer\SerializerInterface; 
use Sentry\Transport\DefaultTransportFactory; 
use Sentry\Transport\TransportFactoryInterface; 
use Sentry\Transport\TransportInterface; 
 
/** 
 * The default implementation of {@link ClientBuilderInterface}. 
 * 
 * @author Stefano Arlandini <sarlandini@alice.it> 
 */ 
final class ClientBuilder implements ClientBuilderInterface 
{ 
    /** 
     * @var Options The client options 
     */ 
    private $options; 
 
    /** 
     * @var UriFactoryInterface|null The PSR-7 URI factory 
     */ 
    private $uriFactory; 
 
    /** 
     * @var StreamFactoryInterface|null The PSR-17 stream factory 
     */ 
    private $streamFactory; 
 
    /** 
     * @var MessageFactoryInterface|null The PSR-7 message factory 
     */ 
    private $messageFactory; 
 
    /** 
     * @var TransportFactoryInterface|null The transport factory 
     */ 
    private $transportFactory; 
 
    /** 
     * @var TransportInterface|null The transport 
     */ 
    private $transport; 
 
    /** 
     * @var HttpAsyncClient|null The HTTP client 
     */ 
    private $httpClient; 
 
    /** 
     * @var PluginInterface[] The list of Httplug plugins 
     */ 
    private $httpClientPlugins = []; 
 
    /** 
     * @var SerializerInterface|null The serializer to be injected in the client 
     */ 
    private $serializer; 
 
    /** 
     * @var RepresentationSerializerInterface|null The representation serializer to be injected in the client 
     */ 
    private $representationSerializer; 
 
    /** 
     * @var LoggerInterface|null A PSR-3 logger to log internal errors and debug messages 
     */ 
    private $logger; 
 
    /** 
     * @var string The SDK identifier, to be used in {@see Event} and {@see SentryAuth} 
     */ 
    private $sdkIdentifier = Client::SDK_IDENTIFIER; 
 
    /** 
     * @var string The SDK version of the Client 
     */ 
    private $sdkVersion; 
 
    /** 
     * Class constructor. 
     * 
     * @param Options|null $options The client options 
     */ 
    public function __construct(Options $options = null) 
    { 
        $this->options = $options ?? new Options(); 
        $this->sdkVersion = PrettyVersions::getVersion('sentry/sentry')->getPrettyVersion(); 
    } 
 
    /** 
     * {@inheritdoc} 
     */ 
    public static function create(array $options = []): ClientBuilderInterface 
    { 
        return new static(new Options($options)); 
    } 
 
    /** 
     * {@inheritdoc} 
     */ 
    public function getOptions(): Options 
    { 
        return $this->options; 
    } 
 
    /** 
     * {@inheritdoc} 
     */ 
    public function setUriFactory(UriFactoryInterface $uriFactory): ClientBuilderInterface 
    { 
        @trigger_error(sprintf('Method %s() is deprecated since version 2.3 and will be removed in 3.0.', __METHOD__), \E_USER_DEPRECATED); 
 
        $this->uriFactory = $uriFactory; 
 
        return $this; 
    } 
 
    /** 
     * {@inheritdoc} 
     */ 
    public function setMessageFactory(MessageFactoryInterface $messageFactory): ClientBuilderInterface 
    { 
        @trigger_error(sprintf('Method %s() is deprecated since version 2.3 and will be removed in 3.0.', __METHOD__), \E_USER_DEPRECATED); 
 
        $this->messageFactory = $messageFactory; 
 
        return $this; 
    } 
 
    /** 
     * {@inheritdoc} 
     */ 
    public function setTransport(TransportInterface $transport): ClientBuilderInterface 
    { 
        @trigger_error(sprintf('Method %s() is deprecated since version 2.3 and will be removed in 3.0. Use the setTransportFactory() method instead.', __METHOD__), \E_USER_DEPRECATED); 
 
        $this->transport = $transport; 
 
        return $this; 
    } 
 
    /** 
     * {@inheritdoc} 
     */ 
    public function setHttpClient(HttpAsyncClient $httpClient): ClientBuilderInterface 
    { 
        @trigger_error(sprintf('Method %s() is deprecated since version 2.3 and will be removed in 3.0.', __METHOD__), \E_USER_DEPRECATED); 
 
        $this->httpClient = $httpClient; 
 
        return $this; 
    } 
 
    /** 
     * {@inheritdoc} 
     */ 
    public function addHttpClientPlugin(PluginInterface $plugin): ClientBuilderInterface 
    { 
        @trigger_error(sprintf('Method %s() is deprecated since version 2.3 and will be removed in 3.0.', __METHOD__), \E_USER_DEPRECATED); 
 
        $this->httpClientPlugins[] = $plugin; 
 
        return $this; 
    } 
 
    /** 
     * {@inheritdoc} 
     */ 
    public function removeHttpClientPlugin(string $className): ClientBuilderInterface 
    { 
        @trigger_error(sprintf('Method %s() is deprecated since version 2.3 and will be removed in 3.0.', __METHOD__), \E_USER_DEPRECATED); 
 
        foreach ($this->httpClientPlugins as $index => $httpClientPlugin) { 
            if (!$httpClientPlugin instanceof $className) { 
                continue; 
            } 
 
            unset($this->httpClientPlugins[$index]); 
        } 
 
        return $this; 
    } 
 
    /** 
     * {@inheritdoc} 
     */ 
    public function setSerializer(SerializerInterface $serializer): ClientBuilderInterface 
    { 
        $this->serializer = $serializer; 
 
        return $this; 
    } 
 
    /** 
     * {@inheritdoc} 
     */ 
    public function setRepresentationSerializer(RepresentationSerializerInterface $representationSerializer): ClientBuilderInterface 
    { 
        $this->representationSerializer = $representationSerializer; 
 
        return $this; 
    } 
 
    /** 
     * {@inheritdoc} 
     */ 
    public function setLogger(LoggerInterface $logger): ClientBuilderInterface 
    { 
        $this->logger = $logger; 
 
        return $this; 
    } 
 
    /** 
     * {@inheritdoc} 
     */ 
    public function setSdkIdentifier(string $sdkIdentifier): ClientBuilderInterface 
    { 
        $this->sdkIdentifier = $sdkIdentifier; 
 
        return $this; 
    } 
 
    /** 
     * {@inheritdoc} 
     */ 
    public function setSdkVersion(string $sdkVersion): ClientBuilderInterface 
    { 
        $this->sdkVersion = $sdkVersion; 
 
        return $this; 
    } 
 
    /** 
     * Sets the version of the SDK package that generated this Event using the Packagist name. 
     * 
     * @param string $packageName The package name that will be used to get the version from (i.e. "sentry/sentry") 
     * 
     * @return $this 
     * 
     * @deprecated since version 2.2, to be removed in 3.0 
     */ 
    public function setSdkVersionByPackageName(string $packageName): ClientBuilderInterface 
    { 
        @trigger_error(sprintf('Method %s() is deprecated since version 2.2 and will be removed in 3.0.', __METHOD__), \E_USER_DEPRECATED); 
 
        $this->sdkVersion = PrettyVersions::getVersion($packageName)->getPrettyVersion(); 
 
        return $this; 
    } 
 
    /** 
     * {@inheritdoc} 
     */ 
    public function getClient(): ClientInterface 
    { 
        $this->transport = $this->transport ?? $this->createTransportInstance(); 
 
        return new Client($this->options, $this->transport, $this->createEventFactory(), $this->logger); 
    } 
 
    /** 
     * Sets the transport factory. 
     * 
     * @param TransportFactoryInterface $transportFactory The transport factory 
     * 
     * @return $this 
     */ 
    public function setTransportFactory(TransportFactoryInterface $transportFactory): ClientBuilderInterface 
    { 
        $this->transportFactory = $transportFactory; 
 
        return $this; 
    } 
 
    /** 
     * Creates a new instance of the transport mechanism. 
     */ 
    private function createTransportInstance(): TransportInterface 
    { 
        if (null !== $this->transport) { 
            return $this->transport; 
        } 
 
        $transportFactory = $this->transportFactory ?? $this->createDefaultTransportFactory(); 
 
        return $transportFactory->create($this->options); 
    } 
 
    /** 
     * Instantiate the {@see EventFactory} with the configured serializers. 
     */ 
    private function createEventFactory(): EventFactoryInterface 
    { 
        $this->serializer = $this->serializer ?? new Serializer($this->options); 
        $this->representationSerializer = $this->representationSerializer ?? new RepresentationSerializer($this->options); 
 
        return new EventFactory($this->serializer, $this->representationSerializer, $this->options, $this->sdkIdentifier, $this->sdkVersion); 
    } 
 
    /** 
     * Creates a new instance of the {@see DefaultTransportFactory} factory. 
     */ 
    private function createDefaultTransportFactory(): DefaultTransportFactory 
    { 
        $this->messageFactory = $this->messageFactory ?? MessageFactoryDiscovery::find(); 
        $this->uriFactory = $this->uriFactory ?? UriFactoryDiscovery::find(); 
        $this->streamFactory = $this->streamFactory ?? StreamFactoryDiscovery::find(); 
 
        $httpClientFactory = new HttpClientFactory( 
            $this->uriFactory, 
            $this->messageFactory, 
            $this->streamFactory, 
            $this->httpClient, 
            $this->sdkIdentifier, 
            $this->sdkVersion 
        ); 
 
        if (!empty($this->httpClientPlugins)) { 
            $httpClientFactory = new PluggableHttpClientFactory($httpClientFactory, $this->httpClientPlugins); 
        } 
 
        return new DefaultTransportFactory($this->messageFactory, $httpClientFactory, $this->logger); 
    } 
}