|
1 | 1 | <?php |
2 | 2 |
|
3 | | -/** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */ |
4 | | - |
5 | 3 | declare(strict_types=1); |
6 | 4 |
|
7 | 5 | namespace OpenTelemetry\Context; |
8 | 6 |
|
9 | 7 | use function assert; |
10 | | -use function class_exists; |
11 | 8 | use const E_USER_WARNING; |
12 | 9 | use Fiber; |
| 10 | +use function spl_object_id; |
| 11 | +use function sprintf; |
13 | 12 | use function trigger_error; |
| 13 | +use WeakMap; |
14 | 14 |
|
15 | 15 | /** |
16 | 16 | * @internal |
17 | | - * |
18 | | - * @phan-file-suppress PhanUndeclaredClassReference |
19 | | - * @phan-file-suppress PhanUndeclaredClassMethod |
20 | 17 | */ |
21 | | -final class FiberBoundContextStorage implements ContextStorageInterface, ExecutionContextAwareInterface |
| 18 | +final class FiberBoundContextStorage implements ContextStorageInterface, ContextStorageHeadAware |
22 | 19 | { |
23 | | - public function __construct(private readonly ContextStorageInterface&ExecutionContextAwareInterface $storage) |
24 | | - { |
25 | | - } |
26 | | - |
27 | | - public function fork(int|string $id): void |
28 | | - { |
29 | | - $this->storage->fork($id); |
30 | | - } |
| 20 | + /** @var WeakMap<object, ContextStorageHead> */ |
| 21 | + private WeakMap $heads; |
31 | 22 |
|
32 | | - public function switch(int|string $id): void |
| 23 | + public function __construct() |
33 | 24 | { |
34 | | - $this->storage->switch($id); |
| 25 | + $this->heads = new WeakMap(); |
| 26 | + $this->heads[$this] = new ContextStorageHead($this); |
35 | 27 | } |
36 | 28 |
|
37 | | - public function destroy(int|string $id): void |
| 29 | + public function head(): ?ContextStorageHead |
38 | 30 | { |
39 | | - $this->storage->destroy($id); |
| 31 | + return $this->heads[Fiber::getCurrent() ?? $this] ?? null; |
40 | 32 | } |
41 | 33 |
|
42 | 34 | public function scope(): ?ContextStorageScopeInterface |
43 | 35 | { |
44 | | - $this->checkFiberMismatch(); |
| 36 | + $head = $this->heads[Fiber::getCurrent() ?? $this] ?? null; |
| 37 | + |
| 38 | + if (!$head?->node && Fiber::getCurrent()) { |
| 39 | + self::triggerNotInitializedFiberContextWarning(); |
45 | 40 |
|
46 | | - if (($scope = $this->storage->scope()) === null) { |
47 | 41 | return null; |
48 | 42 | } |
49 | 43 |
|
50 | | - return new FiberBoundContextStorageScope($scope); |
| 44 | + // Starts with empty head instead of cloned parent -> no need to check for head mismatch |
| 45 | + return $head->node; |
51 | 46 | } |
52 | 47 |
|
53 | 48 | public function current(): ContextInterface |
54 | 49 | { |
55 | | - $this->checkFiberMismatch(); |
| 50 | + $head = $this->heads[Fiber::getCurrent() ?? $this] ?? null; |
| 51 | + |
| 52 | + if (!$head?->node && Fiber::getCurrent()) { |
| 53 | + self::triggerNotInitializedFiberContextWarning(); |
| 54 | + |
| 55 | + // Fallback to {main} to preserve BC |
| 56 | + $head = $this->heads[$this]; |
| 57 | + } |
56 | 58 |
|
57 | | - return $this->storage->current(); |
| 59 | + return $head->node->context ?? Context::getRoot(); |
58 | 60 | } |
59 | 61 |
|
60 | 62 | public function attach(ContextInterface $context): ContextStorageScopeInterface |
61 | 63 | { |
62 | | - $scope = $this->storage->attach($context); |
63 | | - assert(class_exists(Fiber::class, false)); |
64 | | - $scope[Fiber::class] = Fiber::getCurrent(); |
| 64 | + $head = $this->heads[Fiber::getCurrent() ?? $this] ??= new ContextStorageHead($this); |
65 | 65 |
|
66 | | - return new FiberBoundContextStorageScope($scope); |
| 66 | + return $head->node = new ContextStorageNode($context, $head, $head->node); |
67 | 67 | } |
68 | 68 |
|
69 | | - private function checkFiberMismatch(): void |
| 69 | + private static function triggerNotInitializedFiberContextWarning(): void |
70 | 70 | { |
71 | | - $scope = $this->storage->scope(); |
72 | | - assert(class_exists(Fiber::class, false)); |
73 | | - if ($scope && $scope[Fiber::class] !== Fiber::getCurrent()) { |
74 | | - trigger_error('Fiber context switching not supported', E_USER_WARNING); |
75 | | - } |
| 71 | + $fiber = Fiber::getCurrent(); |
| 72 | + assert($fiber !== null); |
| 73 | + |
| 74 | + trigger_error(sprintf( |
| 75 | + 'Access to not initialized OpenTelemetry context in fiber (id: %d), automatic forking not supported, must attach initial fiber context manually', |
| 76 | + spl_object_id($fiber), |
| 77 | + ), E_USER_WARNING); |
76 | 78 | } |
77 | 79 | } |
0 commit comments