Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
39 changes: 36 additions & 3 deletions src/SentrySdk.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Sentry\State\HubInterface;
use Sentry\State\RuntimeContext;
use Sentry\State\RuntimeContextManager;
use Sentry\State\Scope;

/**
* This class is the main entry point for all the most common SDK features.
Expand All @@ -23,6 +24,11 @@ final class SentrySdk
*/
private static $currentHub;

/**
* @var Scope|null The process-global scope
*/
private static $globalScope;

/**
* @var RuntimeContextManager|null
*/
Expand All @@ -41,10 +47,12 @@ private function __construct()
*/
public static function init(?ClientInterface $client = null): HubInterface
{
if ($client === null) {
$client = new NoOpClient();
$hubClient = $client ?? new NoOpClient();

if ($client !== null) {
self::getGlobalScope()->setClient($client);
}
self::$currentHub = new Hub($client);
self::$currentHub = new Hub($hubClient);
self::$runtimeContextManager = new RuntimeContextManager(self::$currentHub);

return self::getCurrentHub();
Expand Down Expand Up @@ -79,6 +87,31 @@ public static function setCurrentHub(HubInterface $hub): HubInterface
return $hub;
}

public static function getGlobalScope(): Scope
{
if (self::$globalScope === null) {
self::$globalScope = new Scope();
}

return self::$globalScope;
}

public static function getIsolationScope(): Scope
{
return self::getCurrentRuntimeContext()->getIsolationScope();
}

public static function getClient(): ClientInterface
{
$client = self::getIsolationScope()->getClient();

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

return self::getGlobalScope()->getClient();
}

public static function startContext(): void
{
self::getRuntimeContextManager()->startContext();
Expand Down
18 changes: 17 additions & 1 deletion src/State/RuntimeContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ final class RuntimeContext
*/
private $hub;

/**
* @var Scope
*/
private $isolationScope;

/**
* @var LogsAggregator
*/
Expand All @@ -37,10 +42,11 @@ final class RuntimeContext
*/
private $metricsAggregator;

public function __construct(string $id, HubInterface $hub)
public function __construct(string $id, HubInterface $hub, ?Scope $isolationScope = null)
{
$this->id = $id;
$this->hub = $hub;
$this->isolationScope = $isolationScope ?? new Scope();
$this->logsAggregator = new LogsAggregator();
$this->metricsAggregator = new MetricsAggregator();
}
Expand All @@ -60,6 +66,16 @@ public function setHub(HubInterface $hub): void
$this->hub = $hub;
}

public function getIsolationScope(): Scope
{
return $this->isolationScope;
}

public function setIsolationScope(Scope $isolationScope): void
{
$this->isolationScope = $isolationScope;
}

public function getLogsAggregator(): LogsAggregator
{
return $this->logsAggregator;
Expand Down
55 changes: 53 additions & 2 deletions src/State/Scope.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@

use Sentry\Attachment\Attachment;
use Sentry\Breadcrumb;
use Sentry\ClientInterface;
use Sentry\Event;
use Sentry\EventHint;
use Sentry\EventId;
use Sentry\EventType;
use Sentry\NoOpClient;
use Sentry\Options;
use Sentry\Severity;
use Sentry\Tracing\DynamicSamplingContext;
Expand Down Expand Up @@ -36,6 +39,16 @@ class Scope
*/
private $propagationContext;

/**
* @var ClientInterface The client bound to this scope
*/
private $client;

/**
* @var EventId|null The ID of the last captured event
*/
private $lastEventId;

/**
* @var Breadcrumb[] The list of breadcrumbs recorded in this scope
*/
Expand Down Expand Up @@ -110,6 +123,7 @@ class Scope
public function __construct(?PropagationContext $propagationContext = null)
{
$this->propagationContext = $propagationContext ?? PropagationContext::fromDefaults();
$this->client = new NoOpClient();
}

/**
Expand Down Expand Up @@ -143,6 +157,42 @@ public static function mergeScopes(self $globalScope, self $isolationScope): sel
return $mergedScope;
}

/**
* Returns the client bound to this scope.
*/
public function getClient(): ClientInterface
{
return $this->client;
}

/**
* Sets the client bound to this scope.
*
* @return $this
*/
public function setClient(ClientInterface $client): self
{
$this->client = $client;

return $this;
}

/**
* Returns the ID of the last captured event.
*/
public function getLastEventId(): ?EventId
{
return $this->lastEventId;
}

/**
* @internal
*/
public function setLastEventId(?EventId $lastEventId): void
{
$this->lastEventId = $lastEventId;
}

/**
* @param array<int, array<string, bool>> $globalFlags
* @param array<int, array<string, bool>> $isolationFlags
Expand All @@ -161,7 +211,7 @@ private static function mergeFlags(array $globalFlags, array $isolationFlags): a
}

unset($flagsByKey[$flagKey]);
$flagsByKey[$flagKey] = (bool) current($flag);
$flagsByKey[$flagKey] = current($flag);
}

$flagsByKey = \array_slice($flagsByKey, -self::MAX_FLAGS, self::MAX_FLAGS, true);
Expand Down Expand Up @@ -483,7 +533,8 @@ public static function getExternalPropagationContext(): ?array
}

/**
* Clears the scope and resets any data it contains.
* Clears event payload data from the scope. The client binding and last
* event ID are preserved.
*
* @return $this
*/
Expand Down
2 changes: 1 addition & 1 deletion src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ function init(array $options = []): void
{
$client = ClientBuilder::create($options)->getClient();

SentrySdk::init()->bindClient($client);
SentrySdk::init($client);
}

/**
Expand Down
17 changes: 17 additions & 0 deletions tests/FunctionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,23 @@ public function testInit(): void
init(['default_integrations' => false]);

$this->assertNotNull(SentrySdk::getCurrentHub()->getClient());
$this->assertSame(SentrySdk::getCurrentHub()->getClient(), SentrySdk::getClient());
}

public function testInitPreservesGlobalScope(): void
{
$globalScope = SentrySdk::getGlobalScope();
$globalScope->setTag('baseline', 'yes');

init(['default_integrations' => false]);

$this->assertSame($globalScope, SentrySdk::getGlobalScope());
$this->assertSame(SentrySdk::getCurrentHub()->getClient(), $globalScope->getClient());

$event = $globalScope->applyToEvent(Event::createEvent());

$this->assertNotNull($event);
$this->assertSame(['baseline' => 'yes'], $event->getTags());
}

/**
Expand Down
9 changes: 9 additions & 0 deletions tests/SentrySdkExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ public function executeBeforeTest(string $test): void
$reflectionProperty->setAccessible(false);
}

$reflectionProperty = new \ReflectionProperty(SentrySdk::class, 'globalScope');
if (\PHP_VERSION_ID < 80100) {
$reflectionProperty->setAccessible(true);
}
$reflectionProperty->setValue(null, null);
if (\PHP_VERSION_ID < 80100) {
$reflectionProperty->setAccessible(false);
}

$reflectionProperty = new \ReflectionProperty(SentrySdk::class, 'runtimeContextManager');
if (\PHP_VERSION_ID < 80100) {
$reflectionProperty->setAccessible(true);
Expand Down
81 changes: 81 additions & 0 deletions tests/SentrySdkTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,87 @@ public function testSetCurrentHub(): void
$this->assertSame($hub, SentrySdk::getCurrentHub());
}

public function testGetGlobalScope(): void
{
$scope = SentrySdk::getGlobalScope();

$this->assertSame($scope, SentrySdk::getGlobalScope());
}

public function testGetIsolationScope(): void
{
$scope = SentrySdk::getIsolationScope();

$this->assertSame($scope, SentrySdk::getIsolationScope());
}

public function testGetClientReturnsCachedNoOpFallbackBeforeInit(): void
{
$client = SentrySdk::getClient();

$this->assertInstanceOf(NoOpClient::class, $client);
$this->assertSame($client, SentrySdk::getClient());
}

public function testGetClientReturnsGlobalScopeClient(): void
{
$client = $this->createMock(ClientInterface::class);

SentrySdk::getGlobalScope()->setClient($client);

$this->assertSame($client, SentrySdk::getClient());
}

public function testGetClientReturnsIsolationScopeClientBeforeGlobalScopeClient(): void
{
$globalClient = $this->createMock(ClientInterface::class);
$isolationClient = $this->createMock(ClientInterface::class);

SentrySdk::getGlobalScope()->setClient($globalClient);
SentrySdk::getIsolationScope()->setClient($isolationClient);

$this->assertSame($isolationClient, SentrySdk::getClient());
}

public function testStartContextUsesSeparateIsolationScope(): void
{
$globalIsolationScope = SentrySdk::getIsolationScope();

SentrySdk::startContext();

$contextIsolationScope = SentrySdk::getIsolationScope();

$this->assertNotSame($globalIsolationScope, $contextIsolationScope);

SentrySdk::endContext();

$this->assertSame($globalIsolationScope, SentrySdk::getIsolationScope());
}

public function testInitWithClientSetsGlobalScopeClient(): void
{
$client = $this->createMock(ClientInterface::class);

SentrySdk::init($client);

$this->assertSame($client, SentrySdk::getClient());
}

public function testInitDoesNotResetGlobalScope(): void
{
$globalScope = SentrySdk::getGlobalScope();
$globalScope->setTag('baseline', 'yes');

SentrySdk::init();

$this->assertSame($globalScope, SentrySdk::getGlobalScope());

$event = $globalScope->applyToEvent(Event::createEvent());

$this->assertNotNull($event);
$this->assertSame(['baseline' => 'yes'], $event->getTags());
}

public function testStartAndEndContextIsolateScopeData(): void
{
SentrySdk::init();
Expand Down
Loading
Loading