Steps to reproduce
The specification for context: https://github.com/open-telemetry/opentelemetry-specification/tree/v1.44.0/specification/context
Requires a Context instance to be strictly immutable. The current implementation fails this promise.
What is the expected behavior?
Any supported value in Context::with() should either be deeply cloned, or immutable.
What is the actual behavior?
#[\Override]
public function with(ContextKeyInterface $key, $value): self
{
if ($this->get($key) === $value) {
return $this;
}
$self = clone $this;
if ($key === self::$spanContextKey) {
$self->span = $value; // @phan-suppress-current-line PhanTypeMismatchPropertyReal
return $self;
}
$id = spl_object_id($key);
if ($value !== null) {
$self->context[$id] = $value;
$self->contextKeys[$id] ??= $key;
} else {
unset(
$self->context[$id],
$self->contextKeys[$id],
);
}
return $self;
}
On clone the array is cloned, but the values are copied. Any object typed values are therefore not cloned.
This means that storing a mutable object could lead to unexpected behavior:
$context = Context::getCurrent()
$key = Context::createKey("test");
$mutableValue = new ArrayObject(['message' => 'hello world']);
$context1 = $context->with($key, $mutableValue);
echo $context1->get($key)['message']; // hello world
$mutableValue['message'] = 'bye';
echo $context1->get($key)['message']; // bye
$key2 = Context::createKey("test2");
$context2 = $context->with($key2, 'test123');
echo $context2->get($key)['message']; // bye
/**
* meanwhile in another Fiber or any other part of the code:
*/
$mutableValue['message'] = 'hello world';
echo $context2->get($key)['message']; // hello world
While I believe that case one could be functioning as intended, I believe case 2 is definitely not what people will expect.
I believe this is not technically solvable in PHP.
If we define a Context to be a bag of keys and values, where keys are opaque identifiers (ContextKey), one could argue that we maybe should just not support non-scalar values.
The least we should do is put a comment in ContextInterface to explain the limitations and deviations from the spec (if you consider this a deviation).
Additional context
This is more of a theoretical problem than a practical one at this point.
Tip: React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it. Learn more here.
Steps to reproduce
The specification for context: https://github.com/open-telemetry/opentelemetry-specification/tree/v1.44.0/specification/context
Requires a
Contextinstance to be strictly immutable. The current implementation fails this promise.What is the expected behavior?
Any supported value in
Context::with()should either be deeply cloned, or immutable.What is the actual behavior?
#[\Override] public function with(ContextKeyInterface $key, $value): self { if ($this->get($key) === $value) { return $this; } $self = clone $this; if ($key === self::$spanContextKey) { $self->span = $value; // @phan-suppress-current-line PhanTypeMismatchPropertyReal return $self; } $id = spl_object_id($key); if ($value !== null) { $self->context[$id] = $value; $self->contextKeys[$id] ??= $key; } else { unset( $self->context[$id], $self->contextKeys[$id], ); } return $self; }On clone the array is cloned, but the values are copied. Any object typed values are therefore not cloned.
This means that storing a mutable object could lead to unexpected behavior:
While I believe that case one could be functioning as intended, I believe case 2 is definitely not what people will expect.
I believe this is not technically solvable in PHP.
If we define a
Contextto be a bag of keys and values, where keys are opaque identifiers (ContextKey), one could argue that we maybe should just not support non-scalar values.The least we should do is put a comment in
ContextInterfaceto explain the limitations and deviations from the spec (if you consider this a deviation).Additional context
This is more of a theoretical problem than a practical one at this point.
Tip: React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding
+1orme too, to help us triage it. Learn more here.