PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.20
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.20
1.10.20 1.10.19 1.10.18 1.10.17 1.10.16 1.10.15 1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 1.10.1 1.10.0 1.9.17 1.9.15 1.9.16 All 164 releases
woocommerce-pos / vendor_prefixed / sentry / sentry / src / State / Scope.php

Scope.php in WCPOS – Point of Sale (POS) plugin for WooCommerce 1.10.20, at vendor_prefixed/sentry/sentry/src/State/Scope.php

527 lines 16.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare (strict_types=1);
4 namespace WCPOS\Vendor\Sentry\State;
5
6 use WCPOS\Vendor\Sentry\Breadcrumb;
7 use WCPOS\Vendor\Sentry\Event;
8 use WCPOS\Vendor\Sentry\EventHint;
9 use WCPOS\Vendor\Sentry\Options;
10 use WCPOS\Vendor\Sentry\Severity;
11 use WCPOS\Vendor\Sentry\Tracing\DynamicSamplingContext;
12 use WCPOS\Vendor\Sentry\Tracing\PropagationContext;
13 use WCPOS\Vendor\Sentry\Tracing\Span;
14 use WCPOS\Vendor\Sentry\Tracing\Transaction;
15 use WCPOS\Vendor\Sentry\UserDataBag;
16 /**
17 * The scope holds data that should implicitly be sent with Sentry events. It
18 * can hold context data, extra parameters, level overrides, fingerprints etc.
19 */
20 class Scope
21 {
22 /**
23 * Maximum number of flags allowed. We only track the first flags set.
24 *
25 * @internal
26 */
27 public const MAX_FLAGS = 100;
28 /**
29 * @var PropagationContext
30 */
31 private $propagationContext;
32 /**
33 * @var Breadcrumb[] The list of breadcrumbs recorded in this scope
34 */
35 private $breadcrumbs = [];
36 /**
37 * @var UserDataBag|null The user data associated to this scope
38 */
39 private $user;
40 /**
41 * @var array<string, array<string, mixed>> The list of contexts associated to this scope
42 */
43 private $contexts = [];
44 /**
45 * @var array<string, string> The list of tags associated to this scope
46 */
47 private $tags = [];
48 /**
49 * @var array<int, array<string, bool>> The list of flags associated to this scope
50 */
51 private $flags = [];
52 /**
53 * @var array<string, mixed> A set of extra data associated to this scope
54 */
55 private $extra = [];
56 /**
57 * @var string[] List of fingerprints used to group events together in
58 * Sentry
59 */
60 private $fingerprint = [];
61 /**
62 * @var Severity|null The severity to associate to the events captured in
63 * this scope
64 */
65 private $level;
66 /**
67 * @var callable[] List of event processors
68 *
69 * @phpstan-var array<callable(Event, EventHint): ?Event>
70 */
71 private $eventProcessors = [];
72 /**
73 * @var Span|null Set a Span on the Scope
74 */
75 private $span;
76 /**
77 * @var callable[] List of event processors
78 *
79 * @phpstan-var array<callable(Event, EventHint): ?Event>
80 */
81 private static $globalEventProcessors = [];
82 /**
83 * @var callable|null
84 */
85 private static $externalPropagationContextCallback;
86 public function __construct(?PropagationContext $propagationContext = null)
87 {
88 $this->propagationContext = $propagationContext ?? PropagationContext::fromDefaults();
89 }
90 /**
91 * Sets a new tag in the tags context.
92 *
93 * @param string $key The key that uniquely identifies the tag
94 * @param string $value The value
95 *
96 * @return $this
97 */
98 public function setTag(string $key, string $value) : self
99 {
100 $this->tags[$key] = $value;
101 return $this;
102 }
103 /**
104 * Merges the given tags into the current tags context.
105 *
106 * @param array<string, string> $tags The tags to merge into the current context
107 *
108 * @return $this
109 */
110 public function setTags(array $tags) : self
111 {
112 $this->tags = \array_merge($this->tags, $tags);
113 return $this;
114 }
115 /**
116 * Removes a given tag from the tags context.
117 *
118 * @param string $key The key that uniquely identifies the tag
119 *
120 * @return $this
121 */
122 public function removeTag(string $key) : self
123 {
124 unset($this->tags[$key]);
125 return $this;
126 }
127 /**
128 * Adds a feature flag to the scope.
129 *
130 * @return $this
131 */
132 public function addFeatureFlag(string $key, bool $result) : self
133 {
134 // If the flag was already set, remove it first
135 // This basically mimics an LRU cache so that the most recently added flags are kept
136 foreach ($this->flags as $flagIndex => $flag) {
137 if (isset($flag[$key])) {
138 unset($this->flags[$flagIndex]);
139 }
140 }
141 // Keep only the most recent MAX_FLAGS flags
142 if (\count($this->flags) >= self::MAX_FLAGS) {
143 \array_shift($this->flags);
144 }
145 $this->flags[] = [$key => $result];
146 if ($this->span !== null) {
147 $this->span->setFlag($key, $result);
148 }
149 return $this;
150 }
151 /**
152 * Sets data to the context by a given name.
153 *
154 * @param string $name The name that uniquely identifies the context
155 * @param array<string, mixed> $value The value
156 *
157 * @return $this
158 */
159 public function setContext(string $name, array $value) : self
160 {
161 if (!empty($value)) {
162 $this->contexts[$name] = $value;
163 }
164 return $this;
165 }
166 /**
167 * Removes the context from the scope.
168 *
169 * @param string $name The name that uniquely identifies the context
170 *
171 * @return $this
172 */
173 public function removeContext(string $name) : self
174 {
175 unset($this->contexts[$name]);
176 return $this;
177 }
178 /**
179 * Sets a new information in the extra context.
180 *
181 * @param string $key The key that uniquely identifies the information
182 * @param mixed $value The value
183 *
184 * @return $this
185 */
186 public function setExtra(string $key, $value) : self
187 {
188 $this->extra[$key] = $value;
189 return $this;
190 }
191 /**
192 * Merges the given data into the current extras context.
193 *
194 * @param array<string, mixed> $extras Data to merge into the current context
195 *
196 * @return $this
197 */
198 public function setExtras(array $extras) : self
199 {
200 $this->extra = \array_merge($this->extra, $extras);
201 return $this;
202 }
203 /**
204 * Get the user context.
205 */
206 public function getUser() : ?UserDataBag
207 {
208 return $this->user;
209 }
210 /**
211 * Merges the given data in the user context.
212 *
213 * @param array<string, mixed>|UserDataBag $user The user data
214 *
215 * @return $this
216 */
217 public function setUser($user) : self
218 {
219 if (!\is_array($user) && !$user instanceof UserDataBag) {
220 throw new \TypeError(\sprintf('The $user argument must be either an array or an instance of the "%s" class. Got: "%s".', UserDataBag::class, \get_debug_type($user)));
221 }
222 if (\is_array($user)) {
223 $user = UserDataBag::createFromArray($user);
224 }
225 if ($this->user === null) {
226 $this->user = $user;
227 } else {
228 $this->user = $this->user->merge($user);
229 }
230 return $this;
231 }
232 /**
233 * Removes all data of the user context.
234 *
235 * @return $this
236 */
237 public function removeUser() : self
238 {
239 $this->user = null;
240 return $this;
241 }
242 /**
243 * Sets the list of strings used to dictate the deduplication of this event.
244 *
245 * @param string[] $fingerprint The fingerprint values
246 *
247 * @return $this
248 */
249 public function setFingerprint(array $fingerprint) : self
250 {
251 $this->fingerprint = $fingerprint;
252 return $this;
253 }
254 /**
255 * Sets the severity to apply to all events captured in this scope.
256 *
257 * @param Severity|null $level The severity
258 *
259 * @return $this
260 */
261 public function setLevel(?Severity $level) : self
262 {
263 $this->level = $level;
264 return $this;
265 }
266 /**
267 * Add the given breadcrumb to the scope.
268 *
269 * @param Breadcrumb $breadcrumb The breadcrumb to add
270 * @param int $maxBreadcrumbs The maximum number of breadcrumbs to record
271 *
272 * @return $this
273 */
274 public function addBreadcrumb(Breadcrumb $breadcrumb, int $maxBreadcrumbs = 100) : self
275 {
276 $this->breadcrumbs[] = $breadcrumb;
277 $this->breadcrumbs = \array_slice($this->breadcrumbs, -$maxBreadcrumbs);
278 return $this;
279 }
280 /**
281 * Gets the breadcrumbs.
282 *
283 * @return Breadcrumb[]
284 */
285 public function getBreadcrumbs() : array
286 {
287 return $this->breadcrumbs;
288 }
289 /**
290 * Clears all the breadcrumbs.
291 *
292 * @return $this
293 */
294 public function clearBreadcrumbs() : self
295 {
296 $this->breadcrumbs = [];
297 return $this;
298 }
299 /**
300 * Adds a new event processor that will be called after {@see Scope::applyToEvent}
301 * finished its work.
302 *
303 * @param callable $eventProcessor The event processor
304 *
305 * @return $this
306 */
307 public function addEventProcessor(callable $eventProcessor) : self
308 {
309 $this->eventProcessors[] = $eventProcessor;
310 return $this;
311 }
312 /**
313 * Adds a new event processor that will be called after {@see Scope::applyToEvent}
314 * finished its work.
315 *
316 * @param callable $eventProcessor The event processor
317 */
318 public static function addGlobalEventProcessor(callable $eventProcessor) : void
319 {
320 self::$globalEventProcessors[] = $eventProcessor;
321 }
322 public static function registerExternalPropagationContext(callable $callback) : void
323 {
324 self::$externalPropagationContextCallback = $callback;
325 }
326 public static function clearExternalPropagationContext() : void
327 {
328 self::$externalPropagationContextCallback = null;
329 }
330 /**
331 * @return array{trace_id: string, span_id: string}|null
332 */
333 public static function getExternalPropagationContext() : ?array
334 {
335 $callback = self::$externalPropagationContextCallback;
336 if (!\is_callable($callback)) {
337 return null;
338 }
339 try {
340 $context = $callback();
341 } catch (\Throwable $exception) {
342 return null;
343 }
344 if (!\is_array($context)) {
345 return null;
346 }
347 $traceId = $context['trace_id'] ?? null;
348 $spanId = $context['span_id'] ?? null;
349 if (!\is_string($traceId) || \preg_match('/^[0-9a-f]{32}$/i', $traceId) !== 1) {
350 return null;
351 }
352 if (!\is_string($spanId) || \preg_match('/^[0-9a-f]{16}$/i', $spanId) !== 1) {
353 return null;
354 }
355 return ['trace_id' => $traceId, 'span_id' => $spanId];
356 }
357 /**
358 * Clears the scope and resets any data it contains.
359 *
360 * @return $this
361 */
362 public function clear() : self
363 {
364 $this->user = null;
365 $this->level = null;
366 $this->span = null;
367 $this->fingerprint = [];
368 $this->breadcrumbs = [];
369 $this->tags = [];
370 $this->flags = [];
371 $this->extra = [];
372 $this->contexts = [];
373 return $this;
374 }
375 /**
376 * Applies the current context and fingerprint to the event. If the event has
377 * already some breadcrumbs on it, the ones from this scope won't get merged.
378 *
379 * @param Event $event The event object that will be enriched with scope data
380 */
381 public function applyToEvent(Event $event, ?EventHint $hint = null, ?Options $options = null) : ?Event
382 {
383 $event->setFingerprint(\array_merge($event->getFingerprint(), $this->fingerprint));
384 if (empty($event->getBreadcrumbs())) {
385 $event->setBreadcrumb($this->breadcrumbs);
386 }
387 if ($this->level !== null) {
388 $event->setLevel($this->level);
389 }
390 if (!empty($this->tags)) {
391 $event->setTags(\array_merge($this->tags, $event->getTags()));
392 }
393 if (!empty($this->flags)) {
394 $event->setContext('flags', ['values' => \array_map(static function (array $flag) {
395 return ['flag' => \key($flag), 'result' => \current($flag)];
396 }, \array_values($this->flags))]);
397 }
398 if (!empty($this->extra)) {
399 $event->setExtra(\array_merge($this->extra, $event->getExtra()));
400 }
401 if ($this->user !== null) {
402 $user = $event->getUser();
403 if ($user === null) {
404 $user = $this->user;
405 } else {
406 $user = $this->user->merge($user);
407 }
408 $event->setUser($user);
409 }
410 /**
411 * Apply the trace context to errors if there is a Span on the Scope.
412 * Else fallback to the external propagation context or to the
413 * propagation context.
414 * But do not override a trace context already present.
415 */
416 $externalPropagationContext = null;
417 if ($this->span === null) {
418 $externalPropagationContext = self::getExternalPropagationContext();
419 }
420 $traceContext = $this->span !== null ? $this->span->getTraceContext() : $externalPropagationContext ?? $this->propagationContext->getTraceContext();
421 if (!\array_key_exists('trace', $event->getContexts())) {
422 $event->setContext('trace', $traceContext);
423 }
424 if ($this->span !== null) {
425 // Apply the dynamic sampling context to errors if there is a Transaction on the Scope
426 $transaction = $this->span->getTransaction();
427 if ($transaction !== null) {
428 $event->setSdkMetadata('dynamic_sampling_context', $transaction->getDynamicSamplingContext());
429 }
430 } elseif ($externalPropagationContext === null) {
431 $dynamicSamplingContext = $this->propagationContext->getDynamicSamplingContext();
432 if ($dynamicSamplingContext === null && $options !== null) {
433 $dynamicSamplingContext = DynamicSamplingContext::fromOptions($options, $this);
434 }
435 $event->setSdkMetadata('dynamic_sampling_context', $dynamicSamplingContext);
436 }
437 foreach (\array_merge($this->contexts, $event->getContexts()) as $name => $data) {
438 $event->setContext($name, $data);
439 }
440 // We create a empty `EventHint` instance to allow processors to always receive a `EventHint` instance even if there wasn't one
441 if ($hint === null) {
442 $hint = new EventHint();
443 }
444 foreach (\array_merge(self::$globalEventProcessors, $this->eventProcessors) as $processor) {
445 $event = $processor($event, $hint);
446 if ($event === null) {
447 return null;
448 }
449 if (!$event instanceof Event) {
450 throw new \InvalidArgumentException(\sprintf('The event processor must return null or an instance of the %s class', Event::class));
451 }
452 }
453 return $event;
454 }
455 /**
456 * Returns the span that is on the scope.
457 */
458 public function getSpan() : ?Span
459 {
460 return $this->span;
461 }
462 /**
463 * Sets the span on the scope.
464 *
465 * @param Span|null $span The span
466 *
467 * @return $this
468 */
469 public function setSpan(?Span $span) : self
470 {
471 $this->span = $span;
472 return $this;
473 }
474 /**
475 * Returns the transaction attached to the scope (if there is one).
476 */
477 public function getTransaction() : ?Transaction
478 {
479 if ($this->span !== null) {
480 return $this->span->getTransaction();
481 }
482 return null;
483 }
484 public function hasExternalPropagationContext() : bool
485 {
486 return $this->span === null && self::getExternalPropagationContext() !== null;
487 }
488 /**
489 * @return array{
490 * trace_id: string,
491 * span_id: string,
492 * parent_span_id?: string,
493 * data?: array<string, mixed>,
494 * description?: string,
495 * op?: string,
496 * status?: string,
497 * tags?: array<string, string>,
498 * origin?: string
499 * }
500 */
501 public function getTraceContext() : array
502 {
503 if ($this->span !== null) {
504 return $this->span->getTraceContext();
505 }
506 return self::getExternalPropagationContext() ?? $this->propagationContext->getTraceContext();
507 }
508 public function getPropagationContext() : PropagationContext
509 {
510 return $this->propagationContext;
511 }
512 public function setPropagationContext(PropagationContext $propagationContext) : self
513 {
514 $this->propagationContext = $propagationContext;
515 return $this;
516 }
517 public function __clone()
518 {
519 if ($this->user !== null) {
520 $this->user = clone $this->user;
521 }
522 if ($this->propagationContext !== null) {
523 $this->propagationContext = clone $this->propagationContext;
524 }
525 }
526 }
527