--- title: Context weight: 55 cSpell:ignore: Swoole description: Learn how the context API works in instrumented applications. --- OpenTelemetry works by storing and propagating telemetry data. For example, when an instrumented application receives a request and a span starts, the span must be available to a component which creates child spans. To address this need, OpenTelemetry stores the span in the active context. ## PHP execution context The context API is globally available within a single PHP execution context, and there can only be one [active context](#active-context) in the current execution context. ### Storage Context can store values (for example, a `Span`), and it uses `Storage` to keep track of the stored values. By default, a generic `ContextStorage` is used. OpenTelemetry for PHP supports other context storage for less common use cases, like asynchronous or concurrent execution with `fibers`. ## Context keys Values as stored in context as key-value pairs. Context keys are used to store and retrieve values from context. Keys can be created by calling `OpenTelemetry\Context\Context::createKey()`, for example: ```php use OpenTelemetry\Context\Context; $key1 = Context::createKey('My first key'); $key2 = Context::createKey('My second key'); ``` ## Active context The active context is the context which is returned by `Context::getCurrent()`. The context object contains entries which allow telemetry components to communicate with each other. For example, when a span is created it can be activated, which creates a new active context and stores the span. Later, when another span is created it can use the span from the active context as its parent span. If no context is active, the root context is returned, which is just the empty context object. ```php use OpenTelemetry\Context\Context; // Returns the active context // If no context is active, the root context is returned $context = Context::getCurrent(); ``` ### Set and get context values Values are stored in Context by using the `$context->with($key, $value)` method. Setting a context entry creates a new context with the new entry in its storage, containing `$value`. Context is immutable. Setting a context entry creates a new context with the new entry in its storage: `$context->with($key, $value)`. Retrieve values using `$context->get($key)`, for example: ```php use OpenTelemetry\Context\Context; $key = Context::createKey('some key'); // add a new entry $ctx2 = Context::getCurrent()->with($key, 'context 2'); // ctx2 contains the new entry var_dump($ctx2->get($key)); // "context 2" // active context is unchanged var_dump(Context::getCurrent()->get($key)); // NULL ``` If a value is not found in the current context, then each parent is checked until either the key is found, or the root context is reached. ### Activate a context A context can be made active by calling `$context->activate()`. ```php use OpenTelemetry\Context\Context; $key = Context::createKey('my-key'); $ctx = Context::getCurrent(); $ctx2 = $ctx->with($key, 'context 2'); $ctx2->activate(); assert($ctx2 === Context::getCurrent()); ``` #### Scope The return value of `$context->activate()` is a `Scope`. You must `detach()` the scope to deactivate that context, which reactivates the previously-active context. The return value of `$scope->detach()` is an integer. A return value of `0` means that the scope was successfully detached. A non-zero value means that the call was unexpected. This could happen if the context associated with the scope was: - Already detached - Not a part of the current execution context - Not the active context #### DebugScope To assist developers in locating issues with context and scope, there is `DebugScope`. In a PHP runtime with assertions enabled, an activated `Context` is wrapped in a `DebugScope`. The `DebugScope` keeps track of when the scope was activated, and has a destructor which triggers an error if the scope was not detached. The error output contains a backtrace of which code activated the context. The following code would trigger an error, complaining that a scope was not detached, and giving a backtrace of where the scope was created: ```php use OpenTelemetry\Context\Context; $key = Context::createKey('my-key'); $scope = Context::getCurrent()->with($key, 'value')->activate(); //exit without detaching $scope ``` This can be problematic in some situations, particularly in legacy applications which might `exit` or `die`. In that case, active spans are not completed and exported, and the `DebugScope` complains loudly. If you understand why `DebugScope` is complaining and accept the risks, then you can disable the feature entirely by setting `OTEL_PHP_DEBUG_SCOPES_DISABLED` to a truthy value. ### Nested context Active context executions can be nested. This is how traces can have nested spans: ```php use OpenTelemetry\Context\Context; $key = Context::createKey('my-key'); var_dump(Context::getCurrent()->get($key)); //NULL $scope2 = Context::getCurrent()->with($key, 'context 2')->activate(); var_dump(Context::getCurrent()->get($key)); //'context 2' $scope3 = Context::getCurrent()->with($key, 'context 3')->activate(); var_dump(Context::getCurrent()->get($key)); //'context 3' $scope3->detach(); //context 2 is active $scope2->detach(); //original context is active var_dump(Context::getCurrent()->get($key)); //NULL ``` ### Context in asynchronous environments For asynchronous PHP programming, for example `Swoole` or the Fiber-based `Revolt` event loop, there can be multiple active contexts, but still only one active context per execution context. For fiber-based implementations, `Context` is associated with the active fiber, and forks, switches and is destroyed as appropriate by hooking into PHP's fiber initialization, forking, and destruction handlers. For other async implementations, custom context storage might be needed to interoperate correctly. Check the [registry](/ecosystem/registry/?language=php) for storage implementations.