From 0d551577421414b356489a5bbfdccf8ff4dbddd0 Mon Sep 17 00:00:00 2001 From: Martin Linzmayer Date: Tue, 4 Nov 2025 16:03:06 +0100 Subject: [PATCH 1/5] ref(client): replace nullable client with NullClient --- src/Integration/ErrorListenerIntegration.php | 7 +- .../FatalErrorListenerIntegration.php | 7 +- .../FrameContextifierIntegration.php | 4 - src/Integration/RequestIntegration.php | 7 +- src/Logs/LogsAggregator.php | 5 -- src/NullClient.php | 83 +++++++++++++++++++ src/SentrySdk.php | 9 +- src/State/Hub.php | 69 ++++----------- src/State/HubAdapter.php | 2 +- src/State/HubInterface.php | 2 +- src/State/Layer.php | 14 ++-- src/Tracing/DynamicSamplingContext.php | 26 +++--- src/Tracing/GuzzleTracingMiddleware.php | 6 +- src/Tracing/PropagationContext.php | 14 +--- src/Tracing/Transaction.php | 5 +- src/functions.php | 32 +++---- tests/FunctionsTest.php | 9 +- tests/SentrySdkTest.php | 3 +- tests/State/HubAdapterTest.php | 3 +- tests/State/HubTest.php | 21 ++--- tests/Tracing/DynamicSamplingContextTest.php | 3 +- 21 files changed, 175 insertions(+), 156 deletions(-) create mode 100644 src/NullClient.php diff --git a/src/Integration/ErrorListenerIntegration.php b/src/Integration/ErrorListenerIntegration.php index 5dd75ed60..a4b95c2ba 100644 --- a/src/Integration/ErrorListenerIntegration.php +++ b/src/Integration/ErrorListenerIntegration.php @@ -35,14 +35,13 @@ public function setupOnce(): void static function (\ErrorException $exception): void { $currentHub = SentrySdk::getCurrentHub(); $integration = $currentHub->getIntegration(self::class); - $client = $currentHub->getClient(); - // The client bound to the current hub, if any, could not have this - // integration enabled. If this is the case, bail out - if ($integration === null || $client === null) { + if ($integration === null) { return; } + $client = $currentHub->getClient(); + if ($exception instanceof SilencedErrorException && !$client->getOptions()->shouldCaptureSilencedErrors()) { return; } diff --git a/src/Integration/FatalErrorListenerIntegration.php b/src/Integration/FatalErrorListenerIntegration.php index 688103cd4..3cc056668 100644 --- a/src/Integration/FatalErrorListenerIntegration.php +++ b/src/Integration/FatalErrorListenerIntegration.php @@ -24,14 +24,13 @@ public function setupOnce(): void $errorHandler->addFatalErrorHandlerListener(static function (FatalErrorException $exception): void { $currentHub = SentrySdk::getCurrentHub(); $integration = $currentHub->getIntegration(self::class); - $client = $currentHub->getClient(); - // The client bound to the current hub, if any, could not have this - // integration enabled. If this is the case, bail out - if ($integration === null || $client === null) { + if ($integration === null) { return; } + $client = $currentHub->getClient(); + if (!($client->getOptions()->getErrorTypes() & $exception->getSeverity())) { return; } diff --git a/src/Integration/FrameContextifierIntegration.php b/src/Integration/FrameContextifierIntegration.php index 8a541d13d..a49693d58 100644 --- a/src/Integration/FrameContextifierIntegration.php +++ b/src/Integration/FrameContextifierIntegration.php @@ -43,10 +43,6 @@ public function setupOnce(): void Scope::addGlobalEventProcessor(static function (Event $event): Event { $client = SentrySdk::getCurrentHub()->getClient(); - if ($client === null) { - return $event; - } - $maxContextLines = $client->getOptions()->getContextLines(); $integration = $client->getIntegration(self::class); diff --git a/src/Integration/RequestIntegration.php b/src/Integration/RequestIntegration.php index 3bffdc29c..431d7fccf 100644 --- a/src/Integration/RequestIntegration.php +++ b/src/Integration/RequestIntegration.php @@ -94,14 +94,13 @@ public function setupOnce(): void Scope::addGlobalEventProcessor(function (Event $event): Event { $currentHub = SentrySdk::getCurrentHub(); $integration = $currentHub->getIntegration(self::class); - $client = $currentHub->getClient(); - // The client bound to the current hub, if any, could not have this - // integration enabled. If this is the case, bail out - if ($integration === null || $client === null) { + if ($integration === null) { return $event; } + $client = $currentHub->getClient(); + $this->processEvent($event, $client->getOptions()); return $event; diff --git a/src/Logs/LogsAggregator.php b/src/Logs/LogsAggregator.php index d17b04939..d73f89d3e 100644 --- a/src/Logs/LogsAggregator.php +++ b/src/Logs/LogsAggregator.php @@ -40,11 +40,6 @@ public function add( $hub = SentrySdk::getCurrentHub(); $client = $hub->getClient(); - // There is no need to continue if there is no client - if ($client === null) { - return; - } - $options = $client->getOptions(); $sdkLogger = $options->getLogger(); diff --git a/src/NullClient.php b/src/NullClient.php new file mode 100644 index 000000000..a5302b8cf --- /dev/null +++ b/src/NullClient.php @@ -0,0 +1,83 @@ + $options + */ + public function __construct(array $options = []) + { + $this->options = new Options($options); + $this->stacktraceBuilder = new StacktraceBuilder($this->options, new RepresentationSerializer($this->options)); + } + + public function getOptions(): Options + { + return $this->options; + } + + public function getCspReportUrl(): ?string + { + return null; + } + + public function captureMessage(string $message, ?Severity $level = null, ?Scope $scope = null, ?EventHint $hint = null): ?EventId + { + return null; + } + + public function captureException(\Throwable $exception, ?Scope $scope = null, ?EventHint $hint = null): ?EventId + { + return null; + } + + public function captureLastError(?Scope $scope = null, ?EventHint $hint = null): ?EventId + { + return null; + } + + public function captureEvent(Event $event, ?EventHint $hint = null, ?Scope $scope = null): ?EventId + { + return null; + } + + public function getIntegration(string $className): ?IntegrationInterface + { + return null; + } + + public function flush(?int $timeout = null): Result + { + return new Result(ResultStatus::skipped()); + } + + public function getStacktraceBuilder(): StacktraceBuilder + { + return $this->stacktraceBuilder; + } +} diff --git a/src/SentrySdk.php b/src/SentrySdk.php index 4b41fc30a..57233966b 100644 --- a/src/SentrySdk.php +++ b/src/SentrySdk.php @@ -30,9 +30,12 @@ private function __construct() * Initializes the SDK by creating a new hub instance each time this method * gets called. */ - public static function init(): HubInterface + public static function init(?ClientInterface $client = null): HubInterface { - self::$currentHub = new Hub(); + if ($client === null) { + $client = new NullClient(); + } + self::$currentHub = new Hub($client); return self::$currentHub; } @@ -44,7 +47,7 @@ public static function init(): HubInterface public static function getCurrentHub(): HubInterface { if (self::$currentHub === null) { - self::$currentHub = new Hub(); + self::$currentHub = new Hub(new NullClient()); } return self::$currentHub; diff --git a/src/State/Hub.php b/src/State/Hub.php index 2ab9930e9..5e1bf4ce8 100644 --- a/src/State/Hub.php +++ b/src/State/Hub.php @@ -4,7 +4,6 @@ namespace Sentry\State; -use Psr\Log\NullLogger; use Sentry\Attachment\Attachment; use Sentry\Breadcrumb; use Sentry\CheckIn; @@ -15,6 +14,7 @@ use Sentry\EventId; use Sentry\Integration\IntegrationInterface; use Sentry\MonitorConfig; +use Sentry\NullClient; use Sentry\Severity; use Sentry\Tracing\SamplingContext; use Sentry\Tracing\Span; @@ -39,10 +39,10 @@ class Hub implements HubInterface /** * Hub constructor. * - * @param ClientInterface|null $client The client bound to the hub - * @param Scope|null $scope The scope bound to the hub + * @param ClientInterface $client The client bound to the hub + * @param Scope|null $scope The scope bound to the hub */ - public function __construct(?ClientInterface $client = null, ?Scope $scope = null) + public function __construct(ClientInterface $client, ?Scope $scope = null) { $this->stack[] = new Layer($client, $scope ?? new Scope()); } @@ -50,7 +50,7 @@ public function __construct(?ClientInterface $client = null, ?Scope $scope = nul /** * {@inheritdoc} */ - public function getClient(): ?ClientInterface + public function getClient(): ClientInterface { return $this->getStackTop()->getClient(); } @@ -123,13 +123,7 @@ public function bindClient(ClientInterface $client): void */ public function captureMessage(string $message, ?Severity $level = null, ?EventHint $hint = null): ?EventId { - $client = $this->getClient(); - - if ($client !== null) { - return $this->lastEventId = $client->captureMessage($message, $level, $this->getScope(), $hint); - } - - return null; + return $this->lastEventId = $this->getClient()->captureMessage($message, $level, $this->getScope(), $hint); } /** @@ -137,13 +131,7 @@ public function captureMessage(string $message, ?Severity $level = null, ?EventH */ public function captureException(\Throwable $exception, ?EventHint $hint = null): ?EventId { - $client = $this->getClient(); - - if ($client !== null) { - return $this->lastEventId = $client->captureException($exception, $this->getScope(), $hint); - } - - return null; + return $this->lastEventId = $this->getClient()->captureException($exception, $this->getScope(), $hint); } /** @@ -151,13 +139,7 @@ public function captureException(\Throwable $exception, ?EventHint $hint = null) */ public function captureEvent(Event $event, ?EventHint $hint = null): ?EventId { - $client = $this->getClient(); - - if ($client !== null) { - return $this->lastEventId = $client->captureEvent($event, $hint, $this->getScope()); - } - - return null; + return $this->lastEventId = $this->getClient()->captureEvent($event, $hint, $this->getScope()); } /** @@ -165,13 +147,7 @@ public function captureEvent(Event $event, ?EventHint $hint = null): ?EventId */ public function captureLastError(?EventHint $hint = null): ?EventId { - $client = $this->getClient(); - - if ($client !== null) { - return $this->lastEventId = $client->captureLastError($this->getScope(), $hint); - } - - return null; + return $this->lastEventId = $this->getClient()->captureLastError($this->getScope(), $hint); } /** @@ -183,10 +159,6 @@ public function captureCheckIn(string $slug, CheckInStatus $status, $duration = { $client = $this->getClient(); - if ($client === null) { - return null; - } - $options = $client->getOptions(); $event = Event::createCheckIn(); $checkIn = new CheckIn( @@ -211,7 +183,8 @@ public function addBreadcrumb(Breadcrumb $breadcrumb): bool { $client = $this->getClient(); - if ($client === null) { + // No point in storing breadcrumbs if the client will never send them + if ($client instanceof NullClient) { return false; } @@ -234,9 +207,8 @@ public function addBreadcrumb(Breadcrumb $breadcrumb): bool public function addAttachment(Attachment $attachment): bool { - $client = $this->getClient(); - - if ($client === null) { + // No point in storing attachments if the client will never send them + if ($this->getClient() instanceof NullClient) { return false; } @@ -250,13 +222,7 @@ public function addAttachment(Attachment $attachment): bool */ public function getIntegration(string $className): ?IntegrationInterface { - $client = $this->getClient(); - - if ($client !== null) { - return $client->getIntegration($className); - } - - return null; + return $this->getClient()->getIntegration($className); } /** @@ -267,11 +233,10 @@ public function getIntegration(string $className): ?IntegrationInterface public function startTransaction(TransactionContext $context, array $customSamplingContext = []): Transaction { $transaction = new Transaction($context, $this); - $client = $this->getClient(); - $options = $client !== null ? $client->getOptions() : null; - $logger = $options !== null ? $options->getLoggerOrNullLogger() : new NullLogger(); + $options = $this->getClient()->getOptions(); + $logger = $options->getLoggerOrNullLogger(); - if ($options === null || !$options->isTracingEnabled()) { + if (!$options->isTracingEnabled()) { $transaction->setSampled(false); $logger->warning(\sprintf('Transaction [%s] was started but tracing is not enabled.', (string) $transaction->getTraceId()), ['context' => $context]); diff --git a/src/State/HubAdapter.php b/src/State/HubAdapter.php index 1c10b4956..9c52108d7 100644 --- a/src/State/HubAdapter.php +++ b/src/State/HubAdapter.php @@ -53,7 +53,7 @@ public static function getInstance(): self /** * {@inheritdoc} */ - public function getClient(): ?ClientInterface + public function getClient(): ClientInterface { return SentrySdk::getCurrentHub()->getClient(); } diff --git a/src/State/HubInterface.php b/src/State/HubInterface.php index 27751564f..dcce154ab 100644 --- a/src/State/HubInterface.php +++ b/src/State/HubInterface.php @@ -23,7 +23,7 @@ interface HubInterface /** * Gets the client bound to the top of the stack. */ - public function getClient(): ?ClientInterface; + public function getClient(): ClientInterface; /** * Gets the ID of the last captured event. diff --git a/src/State/Layer.php b/src/State/Layer.php index 0e0e734c6..3befbd80a 100644 --- a/src/State/Layer.php +++ b/src/State/Layer.php @@ -15,7 +15,7 @@ final class Layer { /** - * @var ClientInterface|null The client held by this layer + * @var ClientInterface The client held by this layer */ private $client; @@ -27,10 +27,10 @@ final class Layer /** * Constructor. * - * @param ClientInterface|null $client The client held by this layer - * @param Scope $scope The scope held by this layer + * @param ClientInterface $client The client held by this layer + * @param Scope $scope The scope held by this layer */ - public function __construct(?ClientInterface $client, Scope $scope) + public function __construct(ClientInterface $client, Scope $scope) { $this->client = $client; $this->scope = $scope; @@ -39,7 +39,7 @@ public function __construct(?ClientInterface $client, Scope $scope) /** * Gets the client held by this layer. */ - public function getClient(): ?ClientInterface + public function getClient(): ClientInterface { return $this->client; } @@ -47,11 +47,11 @@ public function getClient(): ?ClientInterface /** * Sets the client held by this layer. * - * @param ClientInterface|null $client The client instance + * @param ClientInterface $client The client instance * * @return $this */ - public function setClient(?ClientInterface $client): self + public function setClient(ClientInterface $client): self { $this->client = $client; diff --git a/src/Tracing/DynamicSamplingContext.php b/src/Tracing/DynamicSamplingContext.php index 594af8e99..2f6f3f3ad 100644 --- a/src/Tracing/DynamicSamplingContext.php +++ b/src/Tracing/DynamicSamplingContext.php @@ -166,23 +166,21 @@ public static function fromTransaction(Transaction $transaction, HubInterface $h $client = $hub->getClient(); - if ($client !== null) { - $options = $client->getOptions(); + $options = $client->getOptions(); - if ($options->getDsn() !== null && $options->getDsn()->getPublicKey() !== null) { - $samplingContext->set('public_key', $options->getDsn()->getPublicKey()); - } - if ($options->getDsn() !== null && $options->getDsn()->getOrgId() !== null) { - $samplingContext->set('org_id', (string) $options->getDsn()->getOrgId()); - } + if ($options->getDsn() !== null && $options->getDsn()->getPublicKey() !== null) { + $samplingContext->set('public_key', $options->getDsn()->getPublicKey()); + } + if ($options->getDsn() !== null && $options->getDsn()->getOrgId() !== null) { + $samplingContext->set('org_id', (string) $options->getDsn()->getOrgId()); + } - if ($options->getRelease() !== null) { - $samplingContext->set('release', $options->getRelease()); - } + if ($options->getRelease() !== null) { + $samplingContext->set('release', $options->getRelease()); + } - if ($options->getEnvironment() !== null) { - $samplingContext->set('environment', $options->getEnvironment()); - } + if ($options->getEnvironment() !== null) { + $samplingContext->set('environment', $options->getEnvironment()); } if ($transaction->getSampled() !== null) { diff --git a/src/Tracing/GuzzleTracingMiddleware.php b/src/Tracing/GuzzleTracingMiddleware.php index ee2ce0f0f..c1615dff5 100644 --- a/src/Tracing/GuzzleTracingMiddleware.php +++ b/src/Tracing/GuzzleTracingMiddleware.php @@ -130,12 +130,8 @@ public static function trace(?HubInterface $hub = null): \Closure }; } - private static function shouldAttachTracingHeaders(?ClientInterface $client, RequestInterface $request): bool + private static function shouldAttachTracingHeaders(ClientInterface $client, RequestInterface $request): bool { - if ($client === null) { - return false; - } - $sdkOptions = $client->getOptions(); // Check if the request destination is allow listed in the trace_propagation_targets option. diff --git a/src/Tracing/PropagationContext.php b/src/Tracing/PropagationContext.php index 1a0c4e325..b8c7f6a29 100644 --- a/src/Tracing/PropagationContext.php +++ b/src/Tracing/PropagationContext.php @@ -85,17 +85,11 @@ public function toBaggage(): string { if ($this->dynamicSamplingContext === null) { $hub = SentrySdk::getCurrentHub(); - $client = $hub->getClient(); + $options = $hub->getClient()->getOptions(); - if ($client !== null) { - $options = $client->getOptions(); - - if ($options !== null) { - $hub->configureScope(function (Scope $scope) use ($options) { - $this->dynamicSamplingContext = DynamicSamplingContext::fromOptions($options, $scope); - }); - } - } + $hub->configureScope(function (Scope $scope) use ($options) { + $this->dynamicSamplingContext = DynamicSamplingContext::fromOptions($options, $scope); + }); } return (string) $this->dynamicSamplingContext; diff --git a/src/Tracing/Transaction.php b/src/Tracing/Transaction.php index 5e4d727d2..e2baa09b9 100644 --- a/src/Tracing/Transaction.php +++ b/src/Tracing/Transaction.php @@ -122,10 +122,7 @@ public function initSpanRecorder(int $maxSpans = 1000): self public function initProfiler(): Profiler { if ($this->profiler === null) { - $client = $this->hub->getClient(); - $options = $client !== null ? $client->getOptions() : null; - - $this->profiler = new Profiler($options); + $this->profiler = new Profiler($this->hub->getClient()->getOptions()); } return $this->profiler; diff --git a/src/functions.php b/src/functions.php index 17cde1d09..2f6d7293d 100644 --- a/src/functions.php +++ b/src/functions.php @@ -67,7 +67,7 @@ function init(array $options = []): void { $client = ClientBuilder::create($options)->getClient(); - SentrySdk::init()->bindClient($client); + SentrySdk::init($client); } /** @@ -279,16 +279,11 @@ function trace(callable $trace, SpanContext $context) function getTraceparent(): string { $hub = SentrySdk::getCurrentHub(); - $client = $hub->getClient(); - - if ($client !== null) { - $options = $client->getOptions(); - - if ($options !== null && $options->isTracingEnabled()) { - $span = SentrySdk::getCurrentHub()->getSpan(); - if ($span !== null) { - return $span->toTraceparent(); - } + $options = $hub->getClient()->getOptions(); + if ($options->isTracingEnabled()) { + $span = SentrySdk::getCurrentHub()->getSpan(); + if ($span !== null) { + return $span->toTraceparent(); } } @@ -309,16 +304,11 @@ function getTraceparent(): string function getBaggage(): string { $hub = SentrySdk::getCurrentHub(); - $client = $hub->getClient(); - - if ($client !== null) { - $options = $client->getOptions(); - - if ($options !== null && $options->isTracingEnabled()) { - $span = SentrySdk::getCurrentHub()->getSpan(); - if ($span !== null) { - return $span->toBaggage(); - } + $options = $hub->getClient()->getOptions(); + if ($options->isTracingEnabled()) { + $span = SentrySdk::getCurrentHub()->getSpan(); + if ($span !== null) { + return $span->toBaggage(); } } diff --git a/tests/FunctionsTest.php b/tests/FunctionsTest.php index 949f9fadd..fc8b8903b 100644 --- a/tests/FunctionsTest.php +++ b/tests/FunctionsTest.php @@ -14,6 +14,7 @@ use Sentry\EventId; use Sentry\MonitorConfig; use Sentry\MonitorSchedule; +use Sentry\NullClient; use Sentry\Options; use Sentry\SentrySdk; use Sentry\Severity; @@ -361,7 +362,7 @@ public function testTraceReturnsClosureResult(): void public function testTraceCorrectlyReplacesAndRestoresCurrentSpan(): void { - $hub = new Hub(); + $hub = new Hub(new NullClient()); $transaction = new Transaction(TransactionContext::make()); $transaction->setSampled(true); @@ -391,7 +392,7 @@ public function testTraceDoesntCreateSpanIfTransactionIsNotSampled(): void { $scope = $this->createMock(Scope::class); - $hub = new Hub(null, $scope); + $hub = new Hub(new NullClient(), $scope); $transaction = new Transaction(TransactionContext::make()); $transaction->setSampled(false); @@ -419,7 +420,7 @@ public function testTraceparentWithTracingDisabled(): void $scope = new Scope($propagationContext); - $hub = new Hub(null, $scope); + $hub = new Hub(new NullClient(), $scope); SentrySdk::setCurrentHub($hub); @@ -514,7 +515,7 @@ public function testBaggageWithTracingEnabled(): void public function testContinueTrace(): void { - $hub = new Hub(); + $hub = new Hub(new NullClient()); SentrySdk::setCurrentHub($hub); diff --git a/tests/SentrySdkTest.php b/tests/SentrySdkTest.php index f2f9d3960..7c57f94ab 100644 --- a/tests/SentrySdkTest.php +++ b/tests/SentrySdkTest.php @@ -5,6 +5,7 @@ namespace Sentry\Tests; use PHPUnit\Framework\TestCase; +use Sentry\NullClient; use Sentry\SentrySdk; use Sentry\State\Hub; @@ -31,7 +32,7 @@ public function testGetCurrentHub(): void public function testSetCurrentHub(): void { - $hub = new Hub(); + $hub = new Hub(new NullClient()); $this->assertSame($hub, SentrySdk::setCurrentHub($hub)); $this->assertSame($hub, SentrySdk::getCurrentHub()); diff --git a/tests/State/HubAdapterTest.php b/tests/State/HubAdapterTest.php index 5f4a6fbf4..463d2a7f2 100644 --- a/tests/State/HubAdapterTest.php +++ b/tests/State/HubAdapterTest.php @@ -15,6 +15,7 @@ use Sentry\Integration\IntegrationInterface; use Sentry\MonitorConfig; use Sentry\MonitorSchedule; +use Sentry\NullClient; use Sentry\Options; use Sentry\SentrySdk; use Sentry\Severity; @@ -272,7 +273,7 @@ public static function captureLastErrorDataProvider(): \Generator public function testCaptureCheckIn() { - $hub = new Hub(); + $hub = new Hub(new NullClient()); $options = new Options([ 'environment' => Event::DEFAULT_ENVIRONMENT, diff --git a/tests/State/HubTest.php b/tests/State/HubTest.php index bb735cb96..085a3340a 100644 --- a/tests/State/HubTest.php +++ b/tests/State/HubTest.php @@ -16,6 +16,7 @@ use Sentry\Integration\IntegrationInterface; use Sentry\MonitorConfig; use Sentry\MonitorSchedule; +use Sentry\NullClient; use Sentry\Options; use Sentry\Severity; use Sentry\State\Hub; @@ -145,7 +146,7 @@ public function testPopScope(): void public function testWithScope(): void { $scope = new Scope(); - $hub = new Hub(null, $scope); + $hub = new Hub(new NullClient(), $scope); $callbackReturn = $hub->withScope(function (Scope $scopeArg) use ($scope): string { $this->assertNotSame($scope, $scopeArg); @@ -202,7 +203,7 @@ public function testWithScopeWhenExceptionIsThrown(): void public function testConfigureScope(): void { $scope = new Scope(); - $hub = new Hub(null, $scope); + $hub = new Hub(new NullClient(), $scope); $callbackInvoked = false; @@ -238,7 +239,7 @@ public function testBindClient(): void public function testCaptureMessage(array $functionCallArgs, array $expectedFunctionCallArgs, PropagationContext $propagationContext): void { $eventId = EventId::generate(); - $hub = new Hub(); + $hub = new Hub(new NullClient()); $hub->configureScope(function (Scope $scope) use ($propagationContext): void { $scope->setPropagationContext($propagationContext); @@ -297,7 +298,7 @@ public static function captureMessageDataProvider(): \Generator public function testCaptureException(array $functionCallArgs, array $expectedFunctionCallArgs, PropagationContext $propagationContext): void { $eventId = EventId::generate(); - $hub = new Hub(); + $hub = new Hub(new NullClient()); $hub->configureScope(function (Scope $scope) use ($propagationContext): void { $scope->setPropagationContext($propagationContext); @@ -352,7 +353,7 @@ public static function captureExceptionDataProvider(): \Generator public function testCaptureLastError(array $functionCallArgs, array $expectedFunctionCallArgs, PropagationContext $propagationContext): void { $eventId = EventId::generate(); - $hub = new Hub(); + $hub = new Hub(new NullClient()); $hub->configureScope(function (Scope $scope) use ($propagationContext): void { $scope->setPropagationContext($propagationContext); @@ -442,7 +443,7 @@ public function testCaptureCheckIn(): void public function testCaptureEvent(): void { - $hub = new Hub(); + $hub = new Hub(new NullClient()); $event = Event::createEvent(); /** @var ClientInterface&MockObject $client */ @@ -468,7 +469,7 @@ public function testAddBreadcrumb(): void ->willReturn(new Options()); $callbackInvoked = false; - $hub = new Hub(); + $hub = new Hub(new NullClient()); $breadcrumb = new Breadcrumb(Breadcrumb::LEVEL_ERROR, Breadcrumb::TYPE_ERROR, 'error_reporting'); $hub->addBreadcrumb($breadcrumb); @@ -611,7 +612,7 @@ public function testGetIntegration(): void ->with('Foo\\Bar') ->willReturn($integration); - $hub = new Hub(); + $hub = new Hub(new NullClient()); $this->assertNull($hub->getIntegration('Foo\\Bar')); @@ -877,7 +878,7 @@ public function testGetTransactionReturnsNullIfNoTransactionIsSetOnTheScope(): v public function testEventTraceContextIsAlwaysFilled(): void { - $hub = new Hub(); + $hub = new Hub(new NullClient()); $event = Event::createEvent(); @@ -890,7 +891,7 @@ public function testEventTraceContextIsAlwaysFilled(): void public function testEventTraceContextIsNotOverridenWhenPresent(): void { - $hub = new Hub(); + $hub = new Hub(new NullClient()); $traceContext = ['foo' => 'bar']; diff --git a/tests/Tracing/DynamicSamplingContextTest.php b/tests/Tracing/DynamicSamplingContextTest.php index 066aef3dd..8501c3ec6 100644 --- a/tests/Tracing/DynamicSamplingContextTest.php +++ b/tests/Tracing/DynamicSamplingContextTest.php @@ -6,6 +6,7 @@ use PHPUnit\Framework\TestCase; use Sentry\ClientInterface; +use Sentry\NullClient; use Sentry\Options; use Sentry\State\Hub; use Sentry\State\Scope; @@ -113,7 +114,7 @@ public function testFromTransaction(): void public function testFromTransactionSourceUrl(): void { - $hub = new Hub(); + $hub = new Hub(new NullClient()); $transactionContext = new TransactionContext(); $transactionContext->setName('/foo/bar/123'); From 0659fc2fe25fbd4c8441ec03379249ca8aedee9a Mon Sep 17 00:00:00 2001 From: Martin Linzmayer Date: Tue, 4 Nov 2025 16:30:02 +0100 Subject: [PATCH 2/5] return null for checkin when using NullClient --- src/State/Hub.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/State/Hub.php b/src/State/Hub.php index 5e1bf4ce8..6d7a32571 100644 --- a/src/State/Hub.php +++ b/src/State/Hub.php @@ -159,6 +159,10 @@ public function captureCheckIn(string $slug, CheckInStatus $status, $duration = { $client = $this->getClient(); + if ($client instanceof NullClient) { + return null; + } + $options = $client->getOptions(); $event = Event::createCheckIn(); $checkIn = new CheckIn( From f607182d71fedb9c4a2f55143680f23b47d4a660 Mon Sep 17 00:00:00 2001 From: Martin Linzmayer Date: Wed, 5 Nov 2025 11:52:56 +0100 Subject: [PATCH 3/5] rename NullClient -> NoOpClient --- src/{NullClient.php => NoOpClient.php} | 2 +- src/SentrySdk.php | 4 ++-- src/State/Hub.php | 8 +++---- tests/FunctionsTest.php | 10 ++++----- tests/SentrySdkTest.php | 4 ++-- tests/State/HubAdapterTest.php | 4 ++-- tests/State/HubTest.php | 22 ++++++++++---------- tests/Tracing/DynamicSamplingContextTest.php | 4 ++-- 8 files changed, 29 insertions(+), 29 deletions(-) rename src/{NullClient.php => NoOpClient.php} (97%) diff --git a/src/NullClient.php b/src/NoOpClient.php similarity index 97% rename from src/NullClient.php rename to src/NoOpClient.php index a5302b8cf..8bb369ca0 100644 --- a/src/NullClient.php +++ b/src/NoOpClient.php @@ -15,7 +15,7 @@ * simply workflows where previously the client was null. * It also holds options which helps with situations where no options were available if the client was set to `null`. */ -class NullClient implements ClientInterface +class NoOpClient implements ClientInterface { /** * @var Options diff --git a/src/SentrySdk.php b/src/SentrySdk.php index 57233966b..29d43931d 100644 --- a/src/SentrySdk.php +++ b/src/SentrySdk.php @@ -33,7 +33,7 @@ private function __construct() public static function init(?ClientInterface $client = null): HubInterface { if ($client === null) { - $client = new NullClient(); + $client = new NoOpClient(); } self::$currentHub = new Hub($client); @@ -47,7 +47,7 @@ public static function init(?ClientInterface $client = null): HubInterface public static function getCurrentHub(): HubInterface { if (self::$currentHub === null) { - self::$currentHub = new Hub(new NullClient()); + self::$currentHub = new Hub(new NoOpClient()); } return self::$currentHub; diff --git a/src/State/Hub.php b/src/State/Hub.php index 6d7a32571..330e13345 100644 --- a/src/State/Hub.php +++ b/src/State/Hub.php @@ -14,7 +14,7 @@ use Sentry\EventId; use Sentry\Integration\IntegrationInterface; use Sentry\MonitorConfig; -use Sentry\NullClient; +use Sentry\NoOpClient; use Sentry\Severity; use Sentry\Tracing\SamplingContext; use Sentry\Tracing\Span; @@ -159,7 +159,7 @@ public function captureCheckIn(string $slug, CheckInStatus $status, $duration = { $client = $this->getClient(); - if ($client instanceof NullClient) { + if ($client instanceof NoOpClient) { return null; } @@ -188,7 +188,7 @@ public function addBreadcrumb(Breadcrumb $breadcrumb): bool $client = $this->getClient(); // No point in storing breadcrumbs if the client will never send them - if ($client instanceof NullClient) { + if ($client instanceof NoOpClient) { return false; } @@ -212,7 +212,7 @@ public function addBreadcrumb(Breadcrumb $breadcrumb): bool public function addAttachment(Attachment $attachment): bool { // No point in storing attachments if the client will never send them - if ($this->getClient() instanceof NullClient) { + if ($this->getClient() instanceof NoOpClient) { return false; } diff --git a/tests/FunctionsTest.php b/tests/FunctionsTest.php index fc8b8903b..488a40c7a 100644 --- a/tests/FunctionsTest.php +++ b/tests/FunctionsTest.php @@ -14,7 +14,7 @@ use Sentry\EventId; use Sentry\MonitorConfig; use Sentry\MonitorSchedule; -use Sentry\NullClient; +use Sentry\NoOpClient; use Sentry\Options; use Sentry\SentrySdk; use Sentry\Severity; @@ -362,7 +362,7 @@ public function testTraceReturnsClosureResult(): void public function testTraceCorrectlyReplacesAndRestoresCurrentSpan(): void { - $hub = new Hub(new NullClient()); + $hub = new Hub(new NoOpClient()); $transaction = new Transaction(TransactionContext::make()); $transaction->setSampled(true); @@ -392,7 +392,7 @@ public function testTraceDoesntCreateSpanIfTransactionIsNotSampled(): void { $scope = $this->createMock(Scope::class); - $hub = new Hub(new NullClient(), $scope); + $hub = new Hub(new NoOpClient(), $scope); $transaction = new Transaction(TransactionContext::make()); $transaction->setSampled(false); @@ -420,7 +420,7 @@ public function testTraceparentWithTracingDisabled(): void $scope = new Scope($propagationContext); - $hub = new Hub(new NullClient(), $scope); + $hub = new Hub(new NoOpClient(), $scope); SentrySdk::setCurrentHub($hub); @@ -515,7 +515,7 @@ public function testBaggageWithTracingEnabled(): void public function testContinueTrace(): void { - $hub = new Hub(new NullClient()); + $hub = new Hub(new NoOpClient()); SentrySdk::setCurrentHub($hub); diff --git a/tests/SentrySdkTest.php b/tests/SentrySdkTest.php index 7c57f94ab..f85fbc9b8 100644 --- a/tests/SentrySdkTest.php +++ b/tests/SentrySdkTest.php @@ -5,7 +5,7 @@ namespace Sentry\Tests; use PHPUnit\Framework\TestCase; -use Sentry\NullClient; +use Sentry\NoOpClient; use Sentry\SentrySdk; use Sentry\State\Hub; @@ -32,7 +32,7 @@ public function testGetCurrentHub(): void public function testSetCurrentHub(): void { - $hub = new Hub(new NullClient()); + $hub = new Hub(new NoOpClient()); $this->assertSame($hub, SentrySdk::setCurrentHub($hub)); $this->assertSame($hub, SentrySdk::getCurrentHub()); diff --git a/tests/State/HubAdapterTest.php b/tests/State/HubAdapterTest.php index 463d2a7f2..736ec703d 100644 --- a/tests/State/HubAdapterTest.php +++ b/tests/State/HubAdapterTest.php @@ -15,7 +15,7 @@ use Sentry\Integration\IntegrationInterface; use Sentry\MonitorConfig; use Sentry\MonitorSchedule; -use Sentry\NullClient; +use Sentry\NoOpClient; use Sentry\Options; use Sentry\SentrySdk; use Sentry\Severity; @@ -273,7 +273,7 @@ public static function captureLastErrorDataProvider(): \Generator public function testCaptureCheckIn() { - $hub = new Hub(new NullClient()); + $hub = new Hub(new NoOpClient()); $options = new Options([ 'environment' => Event::DEFAULT_ENVIRONMENT, diff --git a/tests/State/HubTest.php b/tests/State/HubTest.php index 085a3340a..0b8c2286e 100644 --- a/tests/State/HubTest.php +++ b/tests/State/HubTest.php @@ -16,7 +16,7 @@ use Sentry\Integration\IntegrationInterface; use Sentry\MonitorConfig; use Sentry\MonitorSchedule; -use Sentry\NullClient; +use Sentry\NoOpClient; use Sentry\Options; use Sentry\Severity; use Sentry\State\Hub; @@ -146,7 +146,7 @@ public function testPopScope(): void public function testWithScope(): void { $scope = new Scope(); - $hub = new Hub(new NullClient(), $scope); + $hub = new Hub(new NoOpClient(), $scope); $callbackReturn = $hub->withScope(function (Scope $scopeArg) use ($scope): string { $this->assertNotSame($scope, $scopeArg); @@ -203,7 +203,7 @@ public function testWithScopeWhenExceptionIsThrown(): void public function testConfigureScope(): void { $scope = new Scope(); - $hub = new Hub(new NullClient(), $scope); + $hub = new Hub(new NoOpClient(), $scope); $callbackInvoked = false; @@ -239,7 +239,7 @@ public function testBindClient(): void public function testCaptureMessage(array $functionCallArgs, array $expectedFunctionCallArgs, PropagationContext $propagationContext): void { $eventId = EventId::generate(); - $hub = new Hub(new NullClient()); + $hub = new Hub(new NoOpClient()); $hub->configureScope(function (Scope $scope) use ($propagationContext): void { $scope->setPropagationContext($propagationContext); @@ -298,7 +298,7 @@ public static function captureMessageDataProvider(): \Generator public function testCaptureException(array $functionCallArgs, array $expectedFunctionCallArgs, PropagationContext $propagationContext): void { $eventId = EventId::generate(); - $hub = new Hub(new NullClient()); + $hub = new Hub(new NoOpClient()); $hub->configureScope(function (Scope $scope) use ($propagationContext): void { $scope->setPropagationContext($propagationContext); @@ -353,7 +353,7 @@ public static function captureExceptionDataProvider(): \Generator public function testCaptureLastError(array $functionCallArgs, array $expectedFunctionCallArgs, PropagationContext $propagationContext): void { $eventId = EventId::generate(); - $hub = new Hub(new NullClient()); + $hub = new Hub(new NoOpClient()); $hub->configureScope(function (Scope $scope) use ($propagationContext): void { $scope->setPropagationContext($propagationContext); @@ -443,7 +443,7 @@ public function testCaptureCheckIn(): void public function testCaptureEvent(): void { - $hub = new Hub(new NullClient()); + $hub = new Hub(new NoOpClient()); $event = Event::createEvent(); /** @var ClientInterface&MockObject $client */ @@ -469,7 +469,7 @@ public function testAddBreadcrumb(): void ->willReturn(new Options()); $callbackInvoked = false; - $hub = new Hub(new NullClient()); + $hub = new Hub(new NoOpClient()); $breadcrumb = new Breadcrumb(Breadcrumb::LEVEL_ERROR, Breadcrumb::TYPE_ERROR, 'error_reporting'); $hub->addBreadcrumb($breadcrumb); @@ -612,7 +612,7 @@ public function testGetIntegration(): void ->with('Foo\\Bar') ->willReturn($integration); - $hub = new Hub(new NullClient()); + $hub = new Hub(new NoOpClient()); $this->assertNull($hub->getIntegration('Foo\\Bar')); @@ -878,7 +878,7 @@ public function testGetTransactionReturnsNullIfNoTransactionIsSetOnTheScope(): v public function testEventTraceContextIsAlwaysFilled(): void { - $hub = new Hub(new NullClient()); + $hub = new Hub(new NoOpClient()); $event = Event::createEvent(); @@ -891,7 +891,7 @@ public function testEventTraceContextIsAlwaysFilled(): void public function testEventTraceContextIsNotOverridenWhenPresent(): void { - $hub = new Hub(new NullClient()); + $hub = new Hub(new NoOpClient()); $traceContext = ['foo' => 'bar']; diff --git a/tests/Tracing/DynamicSamplingContextTest.php b/tests/Tracing/DynamicSamplingContextTest.php index 8501c3ec6..574aaf4da 100644 --- a/tests/Tracing/DynamicSamplingContextTest.php +++ b/tests/Tracing/DynamicSamplingContextTest.php @@ -6,7 +6,7 @@ use PHPUnit\Framework\TestCase; use Sentry\ClientInterface; -use Sentry\NullClient; +use Sentry\NoOpClient; use Sentry\Options; use Sentry\State\Hub; use Sentry\State\Scope; @@ -114,7 +114,7 @@ public function testFromTransaction(): void public function testFromTransactionSourceUrl(): void { - $hub = new Hub(new NullClient()); + $hub = new Hub(new NoOpClient()); $transactionContext = new TransactionContext(); $transactionContext->setName('/foo/bar/123'); From 677ea95a6d87bca3cc908f5cfb0ceb656ef988fb Mon Sep 17 00:00:00 2001 From: Martin Linzmayer Date: Wed, 5 Nov 2025 11:57:44 +0100 Subject: [PATCH 4/5] make NoOpClient lazy --- src/NoOpClient.php | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/NoOpClient.php b/src/NoOpClient.php index 8bb369ca0..0fb16909e 100644 --- a/src/NoOpClient.php +++ b/src/NoOpClient.php @@ -18,12 +18,17 @@ class NoOpClient implements ClientInterface { /** - * @var Options + * @var array */ private $options; /** - * @var StacktraceBuilder + * @var Options + */ + private $sentryOptions; + + /** + * @var StacktraceBuilder|null */ private $stacktraceBuilder; @@ -32,13 +37,16 @@ class NoOpClient implements ClientInterface */ public function __construct(array $options = []) { - $this->options = new Options($options); - $this->stacktraceBuilder = new StacktraceBuilder($this->options, new RepresentationSerializer($this->options)); + $this->options = $options; } public function getOptions(): Options { - return $this->options; + if ($this->sentryOptions === null) { + $this->sentryOptions = new Options($this->options); + } + + return $this->sentryOptions; } public function getCspReportUrl(): ?string @@ -78,6 +86,10 @@ public function flush(?int $timeout = null): Result public function getStacktraceBuilder(): StacktraceBuilder { + if ($this->stacktraceBuilder === null) { + $this->stacktraceBuilder = new StacktraceBuilder($this->sentryOptions, new RepresentationSerializer($this->sentryOptions)); + } + return $this->stacktraceBuilder; } } From 006230d6e518eb46fab375cf7cfcfe78e36fbe1a Mon Sep 17 00:00:00 2001 From: Martin Linzmayer Date: Wed, 5 Nov 2025 12:06:06 +0100 Subject: [PATCH 5/5] fix --- src/NoOpClient.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NoOpClient.php b/src/NoOpClient.php index 0fb16909e..364b6073e 100644 --- a/src/NoOpClient.php +++ b/src/NoOpClient.php @@ -87,7 +87,7 @@ public function flush(?int $timeout = null): Result public function getStacktraceBuilder(): StacktraceBuilder { if ($this->stacktraceBuilder === null) { - $this->stacktraceBuilder = new StacktraceBuilder($this->sentryOptions, new RepresentationSerializer($this->sentryOptions)); + $this->stacktraceBuilder = new StacktraceBuilder($this->getOptions(), new RepresentationSerializer($this->getOptions())); } return $this->stacktraceBuilder;