Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions src/Integration/ErrorListenerIntegration.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
7 changes: 3 additions & 4 deletions src/Integration/FatalErrorListenerIntegration.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
4 changes: 0 additions & 4 deletions src/Integration/FrameContextifierIntegration.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
7 changes: 3 additions & 4 deletions src/Integration/RequestIntegration.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 0 additions & 5 deletions src/Logs/LogsAggregator.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
95 changes: 95 additions & 0 deletions src/NoOpClient.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

declare(strict_types=1);

namespace Sentry;

use Sentry\Integration\IntegrationInterface;
use Sentry\Serializer\RepresentationSerializer;
use Sentry\State\Scope;
use Sentry\Transport\Result;
use Sentry\Transport\ResultStatus;

/**
* This client does not perform any operations, it acts as an interface compatible layer in order to
* 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 NoOpClient implements ClientInterface
{
/**
* @var array<string, mixed>
*/
private $options;

/**
* @var Options
*/
private $sentryOptions;

/**
* @var StacktraceBuilder|null
*/
private $stacktraceBuilder;

/**
* @param array<string, mixed> $options
*/
public function __construct(array $options = [])
{
$this->options = $options;
}

public function getOptions(): Options
{
if ($this->sentryOptions === null) {
$this->sentryOptions = new Options($this->options);
}

return $this->sentryOptions;
}

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
{
if ($this->stacktraceBuilder === null) {
$this->stacktraceBuilder = new StacktraceBuilder($this->sentryOptions, new RepresentationSerializer($this->sentryOptions));
}

return $this->stacktraceBuilder;
}
}
9 changes: 6 additions & 3 deletions src/SentrySdk.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 NoOpClient();
}
self::$currentHub = new Hub($client);

return self::$currentHub;
}
Expand All @@ -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 NoOpClient());
}

return self::$currentHub;
Expand Down
67 changes: 18 additions & 49 deletions src/State/Hub.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace Sentry\State;

use Psr\Log\NullLogger;
use Sentry\Attachment\Attachment;
use Sentry\Breadcrumb;
use Sentry\CheckIn;
Expand All @@ -15,6 +14,7 @@
use Sentry\EventId;
use Sentry\Integration\IntegrationInterface;
use Sentry\MonitorConfig;
use Sentry\NoOpClient;
use Sentry\Severity;
use Sentry\Tracing\SamplingContext;
use Sentry\Tracing\Span;
Expand All @@ -39,18 +39,18 @@ 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());
}

/**
* {@inheritdoc}
*/
public function getClient(): ?ClientInterface
public function getClient(): ClientInterface
{
return $this->getStackTop()->getClient();
}
Expand Down Expand Up @@ -123,55 +123,31 @@ 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);
}

/**
* {@inheritdoc}
*/
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);
}

/**
* {@inheritdoc}
*/
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());
}

/**
* {@inheritdoc}
*/
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);
}

/**
Expand All @@ -183,7 +159,7 @@ public function captureCheckIn(string $slug, CheckInStatus $status, $duration =
{
$client = $this->getClient();

if ($client === null) {
if ($client instanceof NoOpClient) {
return null;
}

Expand Down Expand Up @@ -211,7 +187,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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: NullClient misreports success on captureCheckIn

The captureCheckIn method returns a check-in ID when a NullClient is active, implying success even though no event is sent. This behavior is inconsistent with its previous return of null and with addBreadcrumb/addAttachment which correctly short-circuit for NullClient.

Fix in Cursor Fix in Web

if ($client instanceof NoOpClient) {
return false;
}

Expand All @@ -234,9 +211,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 NoOpClient) {
return false;
}

Expand All @@ -250,13 +226,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);
}

/**
Expand All @@ -267,11 +237,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]);
Expand Down
2 changes: 1 addition & 1 deletion src/State/HubAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public static function getInstance(): self
/**
* {@inheritdoc}
*/
public function getClient(): ?ClientInterface
public function getClient(): ClientInterface
{
return SentrySdk::getCurrentHub()->getClient();
}
Expand Down
2 changes: 1 addition & 1 deletion src/State/HubInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading