diff options
Diffstat (limited to 'vendor/open-telemetry/context')
28 files changed, 1406 insertions, 0 deletions
diff --git a/vendor/open-telemetry/context/Context.php b/vendor/open-telemetry/context/Context.php new file mode 100644 index 000000000..32b0162a3 --- /dev/null +++ b/vendor/open-telemetry/context/Context.php @@ -0,0 +1,131 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\Context; + +use function assert; +use function spl_object_id; + +/** + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/README.md#context + */ +final class Context implements ContextInterface +{ + /** @var ContextStorageInterface&ExecutionContextAwareInterface */ + private static ContextStorageInterface $storage; + + // Optimization for spans to avoid copying the context array. + private static ContextKeyInterface $spanContextKey; + private ?object $span = null; + /** @var array<int, mixed> */ + private array $context = []; + /** @var array<int, ContextKeyInterface> */ + private array $contextKeys = []; + + private function __construct() + { + self::$spanContextKey = ContextKeys::span(); + } + + public static function createKey(string $key): ContextKeyInterface + { + return new ContextKey($key); + } + + /** + * @param ContextStorageInterface&ExecutionContextAwareInterface $storage + */ + public static function setStorage(ContextStorageInterface $storage): void + { + self::$storage = $storage; + } + + /** + * @return ContextStorageInterface&ExecutionContextAwareInterface + */ + public static function storage(): ContextStorageInterface + { + /** @psalm-suppress RedundantPropertyInitializationCheck */ + return self::$storage ??= new ContextStorage(); + } + + /** + * @param ContextInterface|false|null $context + * + * @internal OpenTelemetry + */ + public static function resolve($context, ?ContextStorageInterface $contextStorage = null): ContextInterface + { + return $context + ?? ($contextStorage ?? self::storage())->current() + ?: self::getRoot(); + } + + /** + * @internal + */ + public static function getRoot(): ContextInterface + { + static $empty; + + return $empty ??= new self(); + } + + public static function getCurrent(): ContextInterface + { + return self::storage()->current(); + } + + public function activate(): ScopeInterface + { + $scope = self::storage()->attach($this); + /** @psalm-suppress RedundantCondition */ + assert((bool) $scope = new DebugScope($scope)); + + return $scope; + } + + public function withContextValue(ImplicitContextKeyedInterface $value): ContextInterface + { + return $value->storeInContext($this); + } + + 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; + } + + public function get(ContextKeyInterface $key) + { + if ($key === self::$spanContextKey) { + /** @psalm-suppress InvalidReturnStatement */ + return $this->span; + } + + return $this->context[spl_object_id($key)] ?? null; + } +} diff --git a/vendor/open-telemetry/context/ContextInterface.php b/vendor/open-telemetry/context/ContextInterface.php new file mode 100644 index 000000000..17a3fb9a2 --- /dev/null +++ b/vendor/open-telemetry/context/ContextInterface.php @@ -0,0 +1,86 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\Context; + +/** + * Immutable execution scoped propagation mechanism. + * + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/README.md#context + */ +interface ContextInterface +{ + /** + * Creates a new context key. + * + * @param non-empty-string $key name of the key + * @return ContextKeyInterface created key + * + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/README.md#create-a-key + */ + public static function createKey(string $key): ContextKeyInterface; + + /** + * Returns the current context. + * + * @return ContextInterface current context + * + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/README.md#get-current-context + */ + public static function getCurrent(): ContextInterface; + + /** + * Attaches this context as active context. + * + * The returned scope has to be {@link ScopeInterface::detach()}ed. In most + * cases this should be done using a `try-finally` statement: + * ```php + * $scope = $context->activate(); + * try { + * // ... + * } finally { + * $scope->detach(); + * } + * ``` + * + * @return ScopeInterface scope to detach the context and restore the previous + * context + * + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/README.md#attach-context + */ + public function activate(): ScopeInterface; + + /** + * Returns a context with the given key set to the given value. + * + * @template T + * @param ContextKeyInterface<T> $key key to set + * @param T|null $value value to set + * @return ContextInterface a context with the given key set to `$value` + * + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/README.md#set-value + */ + public function with(ContextKeyInterface $key, $value): ContextInterface; + + /** + * Returns a context with the given value set. + * + * @param ImplicitContextKeyedInterface $value value to set + * @return ContextInterface a context with the given `$value` + * + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/README.md#set-value + */ + public function withContextValue(ImplicitContextKeyedInterface $value): ContextInterface; + + /** + * Returns the value assigned to the given key. + * + * @template T + * @param ContextKeyInterface<T> $key key to get + * @return T|null value assigned to `$key`, or null if no such value exists + * + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/README.md#get-value + */ + public function get(ContextKeyInterface $key); +} diff --git a/vendor/open-telemetry/context/ContextKey.php b/vendor/open-telemetry/context/ContextKey.php new file mode 100644 index 000000000..f7450249e --- /dev/null +++ b/vendor/open-telemetry/context/ContextKey.php @@ -0,0 +1,23 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\Context; + +/** + * @internal + */ +final class ContextKey implements ContextKeyInterface +{ + private ?string $name; + + public function __construct(?string $name=null) + { + $this->name = $name; + } + + public function name(): ?string + { + return $this->name; + } +} diff --git a/vendor/open-telemetry/context/ContextKeyInterface.php b/vendor/open-telemetry/context/ContextKeyInterface.php new file mode 100644 index 000000000..b3ad00814 --- /dev/null +++ b/vendor/open-telemetry/context/ContextKeyInterface.php @@ -0,0 +1,12 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\Context; + +/** + * @template-covariant T + */ +interface ContextKeyInterface +{ +} diff --git a/vendor/open-telemetry/context/ContextKeys.php b/vendor/open-telemetry/context/ContextKeys.php new file mode 100644 index 000000000..bc1022568 --- /dev/null +++ b/vendor/open-telemetry/context/ContextKeys.php @@ -0,0 +1,25 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\Context; + +/** + * @psalm-internal OpenTelemetry + */ +final class ContextKeys +{ + public static function span(): ContextKeyInterface + { + static $instance; + + return $instance ??= Context::createKey('opentelemetry-trace-span-key'); + } + + public static function baggage(): ContextKeyInterface + { + static $instance; + + return $instance ??= Context::createKey('opentelemetry-trace-baggage-key'); + } +} diff --git a/vendor/open-telemetry/context/ContextStorage.php b/vendor/open-telemetry/context/ContextStorage.php new file mode 100644 index 000000000..e82d3d161 --- /dev/null +++ b/vendor/open-telemetry/context/ContextStorage.php @@ -0,0 +1,57 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\Context; + +/** + * @internal + */ +final class ContextStorage implements ContextStorageInterface, ExecutionContextAwareInterface +{ + public ContextStorageHead $current; + private ContextStorageHead $main; + /** @var array<int|string, ContextStorageHead> */ + private array $forks = []; + + public function __construct() + { + $this->current = $this->main = new ContextStorageHead($this); + } + + public function fork($id): void + { + $this->forks[$id] = clone $this->current; + } + + public function switch($id): void + { + $this->current = $this->forks[$id] ?? $this->main; + } + + public function destroy($id): void + { + unset($this->forks[$id]); + } + + public function scope(): ?ContextStorageScopeInterface + { + return ($this->current->node->head ?? null) === $this->current + ? $this->current->node + : null; + } + + public function current(): ContextInterface + { + return $this->current->node->context ?? Context::getRoot(); + } + + public function attach(ContextInterface $context): ContextStorageScopeInterface + { + return $this->current->node = new ContextStorageNode($context, $this->current, $this->current->node); + } + + private function __clone() + { + } +} diff --git a/vendor/open-telemetry/context/ContextStorageHead.php b/vendor/open-telemetry/context/ContextStorageHead.php new file mode 100644 index 000000000..3cc4d7181 --- /dev/null +++ b/vendor/open-telemetry/context/ContextStorageHead.php @@ -0,0 +1,19 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\Context; + +/** + * @internal + */ +final class ContextStorageHead +{ + public ContextStorage $storage; + public ?ContextStorageNode $node = null; + + public function __construct(ContextStorage $storage) + { + $this->storage = $storage; + } +} diff --git a/vendor/open-telemetry/context/ContextStorageInterface.php b/vendor/open-telemetry/context/ContextStorageInterface.php new file mode 100644 index 000000000..e5a105074 --- /dev/null +++ b/vendor/open-telemetry/context/ContextStorageInterface.php @@ -0,0 +1,32 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\Context; + +interface ContextStorageInterface +{ + /** + * Returns the current scope. + * + * @return ContextStorageScopeInterface|null current scope, or null if no + * scope was attached in the current execution unit + */ + public function scope(): ?ContextStorageScopeInterface; + + /** + * Returns the current context. + * + * @return ContextInterface current context + */ + public function current(): ContextInterface; + + /** + * Attaches the context as active context. + * + * @param ContextInterface $context context to attach + * @return ContextStorageScopeInterface scope to detach the context and + * restore the previous context + */ + public function attach(ContextInterface $context): ContextStorageScopeInterface; +} diff --git a/vendor/open-telemetry/context/ContextStorageNode.php b/vendor/open-telemetry/context/ContextStorageNode.php new file mode 100644 index 000000000..12d521660 --- /dev/null +++ b/vendor/open-telemetry/context/ContextStorageNode.php @@ -0,0 +1,92 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\Context; + +use function assert; + +/** + * @internal + */ +final class ContextStorageNode implements ScopeInterface, ContextStorageScopeInterface +{ + public ContextInterface $context; + public ContextStorageHead $head; + private ?ContextStorageNode $previous; + private array $localStorage = []; + + public function __construct( + ContextInterface $context, + ContextStorageHead $head, + ?ContextStorageNode $previous = null + ) { + $this->context = $context; + $this->head = $head; + $this->previous = $previous; + } + + public function offsetExists($offset): bool + { + return isset($this->localStorage[$offset]); + } + + /** + * @phan-suppress PhanUndeclaredClassAttribute + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->localStorage[$offset]; + } + + public function offsetSet($offset, $value): void + { + $this->localStorage[$offset] = $value; + } + + public function offsetUnset($offset): void + { + unset($this->localStorage[$offset]); + } + + public function context(): ContextInterface + { + return $this->context; + } + + public function detach(): int + { + $flags = 0; + if ($this->head !== $this->head->storage->current) { + $flags |= ScopeInterface::INACTIVE; + } + + if ($this === $this->head->node) { + assert($this->previous !== $this); + $this->head->node = $this->previous; + $this->previous = $this; + + return $flags; + } + + if ($this->previous === $this) { + return $flags | ScopeInterface::DETACHED; + } + + assert($this->head->node !== null); + for ($n = $this->head->node, $depth = 1; + $n->previous !== $this; + $n = $n->previous, $depth++) { + assert($n->previous !== null); + } + $n->previous = $this->previous; + $this->previous = $this; + + return $flags | ScopeInterface::MISMATCH | $depth; + } + + private function __clone() + { + } +} diff --git a/vendor/open-telemetry/context/ContextStorageScopeInterface.php b/vendor/open-telemetry/context/ContextStorageScopeInterface.php new file mode 100644 index 000000000..5fe58d6eb --- /dev/null +++ b/vendor/open-telemetry/context/ContextStorageScopeInterface.php @@ -0,0 +1,22 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\Context; + +use ArrayAccess; + +interface ContextStorageScopeInterface extends ScopeInterface, ArrayAccess +{ + /** + * Returns the context associated with this scope. + * + * @return ContextInterface associated context + */ + public function context(): ContextInterface; + + /** + * @param string $offset + */ + public function offsetSet($offset, $value): void; +} diff --git a/vendor/open-telemetry/context/DebugScope.php b/vendor/open-telemetry/context/DebugScope.php new file mode 100644 index 000000000..e9e4d53c5 --- /dev/null +++ b/vendor/open-telemetry/context/DebugScope.php @@ -0,0 +1,94 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\Context; + +use function basename; +use function count; +use function debug_backtrace; +use const DEBUG_BACKTRACE_IGNORE_ARGS; +use function sprintf; +use function trigger_error; + +/** + * @internal + */ +final class DebugScope implements ScopeInterface +{ + private const DEBUG_TRACE_CREATE = '__debug_trace_create'; + private const DEBUG_TRACE_DETACH = '__debug_trace_detach'; + + private ContextStorageScopeInterface $scope; + + public function __construct(ContextStorageScopeInterface $node) + { + $this->scope = $node; + $this->scope[self::DEBUG_TRACE_CREATE] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + } + + public function detach(): int + { + $this->scope[self::DEBUG_TRACE_DETACH] ??= debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + + $flags = $this->scope->detach(); + + if (($flags & ScopeInterface::DETACHED) !== 0) { + trigger_error(sprintf( + 'Scope: unexpected call to Scope::detach() for scope #%d, scope was already detached %s', + spl_object_id($this), + self::formatBacktrace($this->scope[self::DEBUG_TRACE_DETACH]), + )); + } elseif (($flags & ScopeInterface::MISMATCH) !== 0) { + trigger_error(sprintf( + 'Scope: unexpected call to Scope::detach() for scope #%d, scope successfully detached but another scope should have been detached first', + spl_object_id($this), + )); + } elseif (($flags & ScopeInterface::INACTIVE) !== 0) { + trigger_error(sprintf( + 'Scope: unexpected call to Scope::detach() for scope #%d, scope successfully detached from different execution context', + spl_object_id($this), + )); + } + + return $flags; + } + + public function __destruct() + { + if (!isset($this->scope[self::DEBUG_TRACE_DETACH])) { + trigger_error(sprintf( + 'Scope: missing call to Scope::detach() for scope #%d, created %s', + spl_object_id($this->scope), + self::formatBacktrace($this->scope[self::DEBUG_TRACE_CREATE]), + )); + } + } + + private static function formatBacktrace(array $trace): string + { + $s = ''; + for ($i = 0, $n = count($trace) + 1; ++$i < $n;) { + $s .= "\n\t"; + $s .= 'at '; + if (isset($trace[$i]['class'])) { + $s .= strtr($trace[$i]['class'], ['\\' => '.']); + $s .= '.'; + } + $s .= strtr($trace[$i]['function'] ?? '{main}', ['\\' => '.']); + $s .= '('; + if (isset($trace[$i - 1]['file'])) { + $s .= basename($trace[$i - 1]['file']); + if (isset($trace[$i - 1]['line'])) { + $s .= ':'; + $s .= $trace[$i - 1]['line']; + } + } else { + $s .= 'Unknown Source'; + } + $s .= ')'; + } + + return $s . "\n"; + } +} diff --git a/vendor/open-telemetry/context/ExecutionContextAwareInterface.php b/vendor/open-telemetry/context/ExecutionContextAwareInterface.php new file mode 100644 index 000000000..3a955bfae --- /dev/null +++ b/vendor/open-telemetry/context/ExecutionContextAwareInterface.php @@ -0,0 +1,23 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\Context; + +interface ExecutionContextAwareInterface +{ + /** + * @param int|string $id + */ + public function fork($id): void; + + /** + * @param int|string $id + */ + public function switch($id): void; + + /** + * @param int|string $id + */ + public function destroy($id): void; +} diff --git a/vendor/open-telemetry/context/FiberBoundContextStorage.php b/vendor/open-telemetry/context/FiberBoundContextStorage.php new file mode 100644 index 000000000..667f73b3d --- /dev/null +++ b/vendor/open-telemetry/context/FiberBoundContextStorage.php @@ -0,0 +1,84 @@ +<?php + +/** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */ + +declare(strict_types=1); + +namespace OpenTelemetry\Context; + +use function assert; +use function class_exists; +use const E_USER_WARNING; +use Fiber; +use function trigger_error; + +/** + * @internal + * + * @phan-file-suppress PhanUndeclaredClassReference + * @phan-file-suppress PhanUndeclaredClassMethod + */ +final class FiberBoundContextStorage implements ContextStorageInterface, ExecutionContextAwareInterface +{ + /** @var ContextStorageInterface&ExecutionContextAwareInterface */ + private ContextStorageInterface $storage; + + /** + * @param ContextStorageInterface&ExecutionContextAwareInterface $storage + */ + public function __construct(ContextStorageInterface $storage) + { + $this->storage = $storage; + } + + public function fork($id): void + { + $this->storage->fork($id); + } + + public function switch($id): void + { + $this->storage->switch($id); + } + + public function destroy($id): void + { + $this->storage->destroy($id); + } + + public function scope(): ?ContextStorageScopeInterface + { + $this->checkFiberMismatch(); + + if (($scope = $this->storage->scope()) === null) { + return null; + } + + return new FiberBoundContextStorageScope($scope); + } + + public function current(): ContextInterface + { + $this->checkFiberMismatch(); + + return $this->storage->current(); + } + + public function attach(ContextInterface $context): ContextStorageScopeInterface + { + $scope = $this->storage->attach($context); + assert(class_exists(Fiber::class, false)); + $scope[Fiber::class] = Fiber::getCurrent(); + + return new FiberBoundContextStorageScope($scope); + } + + private function checkFiberMismatch(): void + { + $scope = $this->storage->scope(); + assert(class_exists(Fiber::class, false)); + if ($scope && $scope[Fiber::class] !== Fiber::getCurrent()) { + trigger_error('Fiber context switching not supported', E_USER_WARNING); + } + } +} diff --git a/vendor/open-telemetry/context/FiberBoundContextStorageScope.php b/vendor/open-telemetry/context/FiberBoundContextStorageScope.php new file mode 100644 index 000000000..647552244 --- /dev/null +++ b/vendor/open-telemetry/context/FiberBoundContextStorageScope.php @@ -0,0 +1,67 @@ +<?php + +/** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */ + +declare(strict_types=1); + +namespace OpenTelemetry\Context; + +use function assert; +use function class_exists; +use Fiber; + +/** + * @internal + * + * @phan-file-suppress PhanUndeclaredClassReference + * @phan-file-suppress PhanUndeclaredClassMethod + */ +final class FiberBoundContextStorageScope implements ScopeInterface, ContextStorageScopeInterface +{ + private ContextStorageScopeInterface $scope; + + public function __construct(ContextStorageScopeInterface $scope) + { + $this->scope = $scope; + } + + public function offsetExists($offset): bool + { + return $this->scope->offsetExists($offset); + } + + /** + * @phan-suppress PhanUndeclaredClassAttribute + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->scope->offsetGet($offset); + } + + public function offsetSet($offset, $value): void + { + $this->scope->offsetSet($offset, $value); + } + + public function offsetUnset($offset): void + { + $this->scope->offsetUnset($offset); + } + + public function context(): ContextInterface + { + return $this->scope->context(); + } + + public function detach(): int + { + $flags = $this->scope->detach(); + assert(class_exists(Fiber::class, false)); + if ($this->scope[Fiber::class] !== Fiber::getCurrent()) { + $flags |= ScopeInterface::INACTIVE; + } + + return $flags; + } +} diff --git a/vendor/open-telemetry/context/ImplicitContextKeyedInterface.php b/vendor/open-telemetry/context/ImplicitContextKeyedInterface.php new file mode 100644 index 000000000..0af93122c --- /dev/null +++ b/vendor/open-telemetry/context/ImplicitContextKeyedInterface.php @@ -0,0 +1,32 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\Context; + +/** + * Represents a value that can be stored within {@see ContextInterface}. + * Allows storing themselves without exposing a {@see ContextKeyInterface}. + * + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#context-interaction + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/baggage/api.md#context-interaction + */ +interface ImplicitContextKeyedInterface +{ + /** + * Adds `$this` to the {@see Context::getCurrent() current context} and makes + * the new {@see Context} the current context. + * + * {@see ScopeInterface::detach()} _MUST_ be called to properly restore the previous context. + * + * This method is equivalent to `Context::getCurrent().with($value).activate();`. + * + * @todo: Update this to suggest using the new helper method way to doing something in a specific context/span. + */ + public function activate(): ScopeInterface; + + /** + * Returns a new {@see ContextInterface} created by setting `$this` into the provided [@see ContextInterface}. + */ + public function storeInContext(ContextInterface $context): ContextInterface; +} diff --git a/vendor/open-telemetry/context/Propagation/ArrayAccessGetterSetter.php b/vendor/open-telemetry/context/Propagation/ArrayAccessGetterSetter.php new file mode 100644 index 000000000..51263044d --- /dev/null +++ b/vendor/open-telemetry/context/Propagation/ArrayAccessGetterSetter.php @@ -0,0 +1,129 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\Context\Propagation; + +use function array_key_first; +use ArrayAccess; +use function get_class; +use function gettype; +use InvalidArgumentException; +use function is_array; +use function is_object; +use function is_string; +use function sprintf; +use function strcasecmp; +use Traversable; + +/** + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/context/api-propagators.md#textmap-propagator Getter and Setter. + * + * Default implementation of {@see PropagationGetterInterface} and {@see PropagationSetterInterface}. + * This type is used if no custom getter/setter is provided to {@see TextMapPropagatorInterface::inject()} or {@see TextMapPropagatorInterface::extract()}. + */ +final class ArrayAccessGetterSetter implements PropagationGetterInterface, PropagationSetterInterface +{ + private static ?self $instance = null; + + /** + * Returns a singleton instance of `self` to avoid, multiple runtime allocations. + */ + public static function getInstance(): self + { + if (null === self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** {@inheritdoc} */ + public function keys($carrier): array + { + if ($this->isSupportedCarrier($carrier)) { + $keys = []; + foreach ($carrier as $key => $_) { + $keys[] = (string) $key; + } + + return $keys; + } + + throw new InvalidArgumentException( + sprintf( + 'Unsupported carrier type: %s.', + is_object($carrier) ? get_class($carrier) : gettype($carrier), + ) + ); + } + + /** {@inheritdoc} */ + public function get($carrier, string $key): ?string + { + if ($this->isSupportedCarrier($carrier)) { + $value = $carrier[$this->resolveKey($carrier, $key)] ?? null; + if (is_array($value) && $value) { + $value = $value[array_key_first($value)]; + } + + return is_string($value) + ? $value + : null; + } + + throw new InvalidArgumentException( + sprintf( + 'Unsupported carrier type: %s. Unable to get value associated with key:%s', + is_object($carrier) ? get_class($carrier) : gettype($carrier), + $key + ) + ); + } + + /** {@inheritdoc} */ + public function set(&$carrier, string $key, string $value): void + { + if ($key === '') { + throw new InvalidArgumentException('Unable to set value with an empty key'); + } + if ($this->isSupportedCarrier($carrier)) { + if (($r = $this->resolveKey($carrier, $key)) !== $key) { + unset($carrier[$r]); + } + + $carrier[$key] = $value; + + return; + } + + throw new InvalidArgumentException( + sprintf( + 'Unsupported carrier type: %s. Unable to set value associated with key:%s', + is_object($carrier) ? get_class($carrier) : gettype($carrier), + $key + ) + ); + } + + private function isSupportedCarrier($carrier): bool + { + return is_array($carrier) || $carrier instanceof ArrayAccess && $carrier instanceof Traversable; + } + + private function resolveKey($carrier, string $key): string + { + if (isset($carrier[$key])) { + return $key; + } + + foreach ($carrier as $k => $_) { + $k = (string) $k; + if (strcasecmp($k, $key) === 0) { + return $k; + } + } + + return $key; + } +} diff --git a/vendor/open-telemetry/context/Propagation/MultiTextMapPropagator.php b/vendor/open-telemetry/context/Propagation/MultiTextMapPropagator.php new file mode 100644 index 000000000..075fe98fe --- /dev/null +++ b/vendor/open-telemetry/context/Propagation/MultiTextMapPropagator.php @@ -0,0 +1,83 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\Context\Propagation; + +use function array_map; +use function array_merge; +use function array_unique; +use function array_values; +use OpenTelemetry\Context\Context; +use OpenTelemetry\Context\ContextInterface; + +final class MultiTextMapPropagator implements TextMapPropagatorInterface +{ + /** + * @readonly + * + * @var list<TextMapPropagatorInterface> + */ + private array $propagators = []; + + /** + * @readonly + * + * @var list<string> + */ + private array $fields; + + /** + * @no-named-arguments + * + * @param list<TextMapPropagatorInterface> $propagators + */ + public function __construct(array $propagators) + { + $this->propagators = $propagators; + $this->fields = $this->extractFields($propagators); + } + + public function fields(): array + { + return $this->fields; + } + + public function inject(&$carrier, PropagationSetterInterface $setter = null, ContextInterface $context = null): void + { + foreach ($this->propagators as $propagator) { + $propagator->inject($carrier, $setter, $context); + } + } + + public function extract($carrier, PropagationGetterInterface $getter = null, ContextInterface $context = null): ContextInterface + { + $context ??= Context::getCurrent(); + + foreach ($this->propagators as $propagator) { + $context = $propagator->extract($carrier, $getter, $context); + } + + return $context; + } + + /** + * @param list<TextMapPropagatorInterface> $propagators + * @return list<string> + */ + private function extractFields(array $propagators): array + { + return array_values( + array_unique( + // Phan seems to struggle here with the variadic argument + // @phan-suppress-next-line PhanParamTooFewInternalUnpack + array_merge( + ...array_map( + static fn (TextMapPropagatorInterface $propagator) => $propagator->fields(), + $propagators + ) + ) + ) + ); + } +} diff --git a/vendor/open-telemetry/context/Propagation/NoopTextMapPropagator.php b/vendor/open-telemetry/context/Propagation/NoopTextMapPropagator.php new file mode 100644 index 000000000..c408cfc79 --- /dev/null +++ b/vendor/open-telemetry/context/Propagation/NoopTextMapPropagator.php @@ -0,0 +1,36 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\Context\Propagation; + +use OpenTelemetry\Context\Context; +use OpenTelemetry\Context\ContextInterface; + +final class NoopTextMapPropagator implements TextMapPropagatorInterface +{ + private static ?self $instance = null; + + public static function getInstance(): self + { + if (null === self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + public function fields(): array + { + return []; + } + + public function extract($carrier, PropagationGetterInterface $getter = null, ContextInterface $context = null): ContextInterface + { + return $context ?? Context::getCurrent(); + } + + public function inject(&$carrier, PropagationSetterInterface $setter = null, ContextInterface $context = null): void + { + } +} diff --git a/vendor/open-telemetry/context/Propagation/PropagationGetterInterface.php b/vendor/open-telemetry/context/Propagation/PropagationGetterInterface.php new file mode 100644 index 000000000..d2976c63d --- /dev/null +++ b/vendor/open-telemetry/context/Propagation/PropagationGetterInterface.php @@ -0,0 +1,25 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\Context\Propagation; + +/** + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/context/api-propagators.md#getter-argument + */ +interface PropagationGetterInterface +{ + /** + * Returns the list of all the keys in the carrier. + * + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/context/api-propagators.md#keys + * + * @return list<string> + */ + public function keys($carrier): array; + + /** + * Gets the value of a given key from a carrier. + */ + public function get($carrier, string $key) : ?string; +} diff --git a/vendor/open-telemetry/context/Propagation/PropagationSetterInterface.php b/vendor/open-telemetry/context/Propagation/PropagationSetterInterface.php new file mode 100644 index 000000000..75e205628 --- /dev/null +++ b/vendor/open-telemetry/context/Propagation/PropagationSetterInterface.php @@ -0,0 +1,16 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\Context\Propagation; + +/** + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/context/api-propagators.md#setter-argument + */ +interface PropagationSetterInterface +{ + /** + * Set the value for a given key on the associated carrier. + */ + public function set(&$carrier, string $key, string $value) : void; +} diff --git a/vendor/open-telemetry/context/Propagation/SanitizeCombinedHeadersPropagationGetter.php b/vendor/open-telemetry/context/Propagation/SanitizeCombinedHeadersPropagationGetter.php new file mode 100644 index 000000000..40652982e --- /dev/null +++ b/vendor/open-telemetry/context/Propagation/SanitizeCombinedHeadersPropagationGetter.php @@ -0,0 +1,46 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\Context\Propagation; + +use function preg_replace; + +/** + * Some servers concatenate multiple headers with ';' -- we need to replace these with ',' + * This is still a workaround and doesn't get around the problem fully, specifically it doesn't + * handle edge cases where the header has a trailing ';' or an empty trace state. + * We also need to trim trailing separators from the header, found when a header is empty. + */ +final class SanitizeCombinedHeadersPropagationGetter implements PropagationGetterInterface +{ + private const LIST_MEMBERS_SEPARATOR = ','; + private const SERVER_CONCAT_HEADERS_REGEX = '/;(?=[^,=;]*=|$)/'; + private const TRAILING_LEADING_SEPARATOR_REGEX = '/^' . self::LIST_MEMBERS_SEPARATOR . '+|' . self::LIST_MEMBERS_SEPARATOR . '+$/'; + + private PropagationGetterInterface $getter; + + public function __construct(PropagationGetterInterface $getter) + { + $this->getter = $getter; + } + + public function keys($carrier): array + { + return $this->getter->keys($carrier); + } + + public function get($carrier, string $key): ?string + { + $value = $this->getter->get($carrier, $key); + if ($value === null) { + return null; + } + + return preg_replace( + [self::SERVER_CONCAT_HEADERS_REGEX, self::TRAILING_LEADING_SEPARATOR_REGEX], + [self::LIST_MEMBERS_SEPARATOR], + $value, + ); + } +} diff --git a/vendor/open-telemetry/context/Propagation/TextMapPropagatorInterface.php b/vendor/open-telemetry/context/Propagation/TextMapPropagatorInterface.php new file mode 100644 index 000000000..fdf2d5141 --- /dev/null +++ b/vendor/open-telemetry/context/Propagation/TextMapPropagatorInterface.php @@ -0,0 +1,40 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\Context\Propagation; + +use OpenTelemetry\Context\ContextInterface; + +/** + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/context/api-propagators.md#textmap-propagator + */ +interface TextMapPropagatorInterface +{ + /** + * Returns list of fields that will be used by this propagator. + * + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/context/api-propagators.md#fields + * + * @return list<string> + */ + public function fields() : array; + + /** + * Injects specific values from the provided {@see ContextInterface} into the provided carrier + * via an {@see PropagationSetterInterface}. + * + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/context/api-propagators.md#textmap-inject + * + * @param mixed $carrier + */ + public function inject(&$carrier, PropagationSetterInterface $setter = null, ContextInterface $context = null): void; + + /** + * Extracts specific values from the provided carrier into the provided {@see ContextInterface} + * via an {@see PropagationGetterInterface}. + * + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/context/api-propagators.md#textmap-extract + */ + public function extract($carrier, PropagationGetterInterface $getter = null, ContextInterface $context = null): ContextInterface; +} diff --git a/vendor/open-telemetry/context/README.md b/vendor/open-telemetry/context/README.md new file mode 100644 index 000000000..4dfe0e23f --- /dev/null +++ b/vendor/open-telemetry/context/README.md @@ -0,0 +1,63 @@ +[![Releases](https://img.shields.io/badge/releases-purple)](https://github.com/opentelemetry-php/context/releases) +[![Source](https://img.shields.io/badge/source-context-green)](https://github.com/open-telemetry/opentelemetry-php/tree/main/src/Context) +[![Mirror](https://img.shields.io/badge/mirror-opentelemetry--php:context-blue)](https://github.com/opentelemetry-php/context) +[![Latest Version](http://poser.pugx.org/open-telemetry/context/v/unstable)](https://packagist.org/packages/open-telemetry/context/) +[![Stable](http://poser.pugx.org/open-telemetry/context/v/stable)](https://packagist.org/packages/open-telemetry/context/) + +# OpenTelemetry Context + +Immutable execution scoped propagation mechanism, for further details see [opentelemetry-specification][1]. + +## Installation + +```shell +composer require open-telemetry/context +``` + +## Usage + +### Implicit propagation + +```php +$context = Context::getCurrent(); +// modify context +$scope = $context->activate(); +try { + // run within new context +} finally { + $scope->detach(); +} +``` + +It is recommended to use a `try-finally` statement after `::activate()` to ensure that the created scope is properly `::detach()`ed. + +## Async applications + +### Fiber support + +Requires `PHP >= 8.1`, an NTS build, `ext-ffi`, and setting the environment variable `OTEL_PHP_FIBERS_ENABLED` to a truthy value. Additionally `vendor/autoload.php` has to be preloaded for non-CLI SAPIs if [`ffi.enable`](https://www.php.net/manual/en/ffi.configuration.php#ini.ffi.enable) is set to `preload`. + +### Event loops + +Event loops have to restore the original context on callback execution. A basic implementation could look like the following, though implementations should avoid keeping unnecessary references to arguments if possible: + +```php +function bindContext(Closure $closure): Closure { + $context = Context::getCurrent(); + return static function (mixed ...$args) use ($closure, $context): mixed { + $scope = $context->activate(); + try { + return $closure(...$args); + } finally { + $scope->detach(); + } + }; +} +``` + +## Contributing + +This repository is a read-only git subtree split. +To contribute, please see the main [OpenTelemetry PHP monorepo](https://github.com/open-telemetry/opentelemetry-php). + +[1]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/README.md#context diff --git a/vendor/open-telemetry/context/ScopeInterface.php b/vendor/open-telemetry/context/ScopeInterface.php new file mode 100644 index 000000000..05319b8fc --- /dev/null +++ b/vendor/open-telemetry/context/ScopeInterface.php @@ -0,0 +1,32 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\Context; + +use const PHP_INT_SIZE; + +interface ScopeInterface +{ + /** The associated context was already detached. */ + public const DETACHED = 1 << (PHP_INT_SIZE << 3) - 1; + /** The associated context is not in the active execution context. */ + public const INACTIVE = 1 << (PHP_INT_SIZE << 3) - 2; + /** The associated context is not the active context. */ + public const MISMATCH = 1 << (PHP_INT_SIZE << 3) - 3; + + /** + * Detaches the context associated with this scope and restores the + * previously associated context. + * + * @return int zero indicating an expected call, or a non-zero value + * indicating that the call was unexpected + * + * @see self::DETACHED + * @see self::INACTIVE + * @see self::MISMATCH + * + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/README.md#detach-context + */ + public function detach(): int; +} diff --git a/vendor/open-telemetry/context/ZendObserverFiber.php b/vendor/open-telemetry/context/ZendObserverFiber.php new file mode 100644 index 000000000..4d3d0c5ef --- /dev/null +++ b/vendor/open-telemetry/context/ZendObserverFiber.php @@ -0,0 +1,67 @@ +<?php + +/** @noinspection PhpUndefinedMethodInspection */ +/** @phan-file-suppress PhanUndeclaredClassCatch */ +/** @phan-file-suppress PhanUndeclaredClassMethod */ +/** @phan-file-suppress PhanUndeclaredMethod */ + +declare(strict_types=1); + +namespace OpenTelemetry\Context; + +use function extension_loaded; +use FFI; +use const FILTER_VALIDATE_BOOLEAN; +use function filter_var; +use function is_string; +use const PHP_VERSION_ID; +use const PHP_ZTS; +use function sprintf; +use function trigger_error; + +/** + * @internal + */ +final class ZendObserverFiber +{ + public static function isEnabled(): bool + { + $enabled = $_SERVER['OTEL_PHP_FIBERS_ENABLED'] ?? false; + + return is_string($enabled) + ? filter_var($enabled, FILTER_VALIDATE_BOOLEAN) + : (bool) $enabled; + } + + public static function init(): bool + { + static $fibers; + if ($fibers) { + return true; + } + + if (PHP_ZTS || PHP_VERSION_ID < 80100 || !extension_loaded('ffi')) { + trigger_error('Context: Fiber context switching not supported, requires PHP >= 8.1, an NTS build, and the FFI extension'); + + return false; + } + + try { + $fibers = FFI::scope('OTEL_ZEND_OBSERVER_FIBER'); + } catch (FFI\Exception $e) { + try { + $fibers = FFI::load(__DIR__ . '/fiber/zend_observer_fiber.h'); + } catch (FFI\Exception $e) { + trigger_error(sprintf('Context: Fiber context switching not supported, %s', $e->getMessage())); + + return false; + } + } + + $fibers->zend_observer_fiber_init_register(static fn (int $initializing) => Context::storage()->fork($initializing)); //@phpstan-ignore-line + $fibers->zend_observer_fiber_switch_register(static fn (int $from, int $to) => Context::storage()->switch($to)); //@phpstan-ignore-line + $fibers->zend_observer_fiber_destroy_register(static fn (int $destroying) => Context::storage()->destroy($destroying)); //@phpstan-ignore-line + + return true; + } +} diff --git a/vendor/open-telemetry/context/composer.json b/vendor/open-telemetry/context/composer.json new file mode 100644 index 000000000..348b57f73 --- /dev/null +++ b/vendor/open-telemetry/context/composer.json @@ -0,0 +1,41 @@ +{ + "name": "open-telemetry/context", + "description": "Context implementation for OpenTelemetry PHP.", + "keywords": ["opentelemetry", "otel", "context"], + "type": "library", + "support": { + "issues": "https://github.com/open-telemetry/opentelemetry-php/issues", + "source": "https://github.com/open-telemetry/opentelemetry-php", + "docs": "https://opentelemetry.io/docs/php", + "chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V" + }, + "license": "Apache-2.0", + "authors": [ + { + "name": "opentelemetry-php contributors", + "homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors" + } + ], + "require": { + "php": "^7.4 || ^8.0", + "symfony/polyfill-php80": "^1.26", + "symfony/polyfill-php81": "^1.26", + "symfony/polyfill-php82": "^1.26" + }, + "autoload": { + "psr-4": { + "OpenTelemetry\\Context\\": "." + }, + "files": [ + "fiber/initialize_fiber_handler.php" + ] + }, + "suggest": { + "ext-ffi": "To allow context switching in Fibers" + }, + "extra": { + "branch-alias": { + "dev-main": "1.0.x-dev" + } + } +} diff --git a/vendor/open-telemetry/context/fiber/initialize_fiber_handler.php b/vendor/open-telemetry/context/fiber/initialize_fiber_handler.php new file mode 100644 index 000000000..b9c706395 --- /dev/null +++ b/vendor/open-telemetry/context/fiber/initialize_fiber_handler.php @@ -0,0 +1,20 @@ +<?php + +/** @noinspection PhpElementIsNotAvailableInCurrentPhpVersionInspection */ +/** @phan-file-suppress PhanUndeclaredClassReference */ + +declare(strict_types=1); + +namespace OpenTelemetry\Context; + +use Fiber; + +if (!class_exists(Fiber::class)) { + return; +} + +if (ZendObserverFiber::isEnabled() && ZendObserverFiber::init()) { + // ffi fiber support enabled +} else { + Context::setStorage(new FiberBoundContextStorage(Context::storage())); +} diff --git a/vendor/open-telemetry/context/fiber/zend_observer_fiber.h b/vendor/open-telemetry/context/fiber/zend_observer_fiber.h new file mode 100644 index 000000000..6a8e4e98d --- /dev/null +++ b/vendor/open-telemetry/context/fiber/zend_observer_fiber.h @@ -0,0 +1,9 @@ +#define FFI_SCOPE "OTEL_ZEND_OBSERVER_FIBER" + +typedef void (*zend_observer_fiber_init_handler)(intptr_t initializing); +typedef void (*zend_observer_fiber_switch_handler)(intptr_t from, intptr_t to); +typedef void (*zend_observer_fiber_destroy_handler)(intptr_t destroying); + +void zend_observer_fiber_init_register(zend_observer_fiber_init_handler handler); +void zend_observer_fiber_switch_register(zend_observer_fiber_switch_handler handler); +void zend_observer_fiber_destroy_register(zend_observer_fiber_destroy_handler handler); |