summaryrefslogtreecommitdiff
path: root/vendor/open-telemetry/context/DebugScope.php
blob: e9e4d53c5b89020d498b49c7780b8f52843e5667 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
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";
    }
}