vendor/sentry/sentry/src/State/Hub.php line 88

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Sentry\State;
  4. use Sentry\Breadcrumb;
  5. use Sentry\ClientInterface;
  6. use Sentry\Event;
  7. use Sentry\EventHint;
  8. use Sentry\EventId;
  9. use Sentry\Integration\IntegrationInterface;
  10. use Sentry\Severity;
  11. use Sentry\Tracing\SamplingContext;
  12. use Sentry\Tracing\Span;
  13. use Sentry\Tracing\Transaction;
  14. use Sentry\Tracing\TransactionContext;
  15. /**
  16.  * This class is a basic implementation of the {@see HubInterface} interface.
  17.  */
  18. final class Hub implements HubInterface
  19. {
  20.     /**
  21.      * @var Layer[] The stack of client/scope pairs
  22.      */
  23.     private $stack = [];
  24.     /**
  25.      * @var EventId|null The ID of the last captured event
  26.      */
  27.     private $lastEventId;
  28.     /**
  29.      * Hub constructor.
  30.      *
  31.      * @param ClientInterface|null $client The client bound to the hub
  32.      * @param Scope|null           $scope  The scope bound to the hub
  33.      */
  34.     public function __construct(?ClientInterface $client null, ?Scope $scope null)
  35.     {
  36.         $this->stack[] = new Layer($client$scope ?? new Scope());
  37.     }
  38.     /**
  39.      * {@inheritdoc}
  40.      */
  41.     public function getClient(): ?ClientInterface
  42.     {
  43.         return $this->getStackTop()->getClient();
  44.     }
  45.     /**
  46.      * {@inheritdoc}
  47.      */
  48.     public function getLastEventId(): ?EventId
  49.     {
  50.         return $this->lastEventId;
  51.     }
  52.     /**
  53.      * {@inheritdoc}
  54.      */
  55.     public function pushScope(): Scope
  56.     {
  57.         $clonedScope = clone $this->getScope();
  58.         $this->stack[] = new Layer($this->getClient(), $clonedScope);
  59.         return $clonedScope;
  60.     }
  61.     /**
  62.      * {@inheritdoc}
  63.      */
  64.     public function popScope(): bool
  65.     {
  66.         if (=== \count($this->stack)) {
  67.             return false;
  68.         }
  69.         return null !== array_pop($this->stack);
  70.     }
  71.     /**
  72.      * {@inheritdoc}
  73.      */
  74.     public function withScope(callable $callback)
  75.     {
  76.         $scope $this->pushScope();
  77.         try {
  78.             return $callback($scope);
  79.         } finally {
  80.             $this->popScope();
  81.         }
  82.     }
  83.     /**
  84.      * {@inheritdoc}
  85.      */
  86.     public function configureScope(callable $callback): void
  87.     {
  88.         $callback($this->getScope());
  89.     }
  90.     /**
  91.      * {@inheritdoc}
  92.      */
  93.     public function bindClient(ClientInterface $client): void
  94.     {
  95.         $layer $this->getStackTop();
  96.         $layer->setClient($client);
  97.     }
  98.     /**
  99.      * {@inheritdoc}
  100.      */
  101.     public function captureMessage(string $message, ?Severity $level null, ?EventHint $hint null): ?EventId
  102.     {
  103.         $client $this->getClient();
  104.         if (null !== $client) {
  105.             return $this->lastEventId $client->captureMessage($message$level$this->getScope(), $hint);
  106.         }
  107.         return null;
  108.     }
  109.     /**
  110.      * {@inheritdoc}
  111.      */
  112.     public function captureException(\Throwable $exception, ?EventHint $hint null): ?EventId
  113.     {
  114.         $client $this->getClient();
  115.         if (null !== $client) {
  116.             return $this->lastEventId $client->captureException($exception$this->getScope(), $hint);
  117.         }
  118.         return null;
  119.     }
  120.     /**
  121.      * {@inheritdoc}
  122.      */
  123.     public function captureEvent(Event $event, ?EventHint $hint null): ?EventId
  124.     {
  125.         $client $this->getClient();
  126.         if (null !== $client) {
  127.             return $this->lastEventId $client->captureEvent($event$hint$this->getScope());
  128.         }
  129.         return null;
  130.     }
  131.     /**
  132.      * {@inheritdoc}
  133.      */
  134.     public function captureLastError(?EventHint $hint null): ?EventId
  135.     {
  136.         $client $this->getClient();
  137.         if (null !== $client) {
  138.             return $this->lastEventId $client->captureLastError($this->getScope(), $hint);
  139.         }
  140.         return null;
  141.     }
  142.     /**
  143.      * {@inheritdoc}
  144.      */
  145.     public function addBreadcrumb(Breadcrumb $breadcrumb): bool
  146.     {
  147.         $client $this->getClient();
  148.         if (null === $client) {
  149.             return false;
  150.         }
  151.         $options $client->getOptions();
  152.         $beforeBreadcrumbCallback $options->getBeforeBreadcrumbCallback();
  153.         $maxBreadcrumbs $options->getMaxBreadcrumbs();
  154.         if ($maxBreadcrumbs <= 0) {
  155.             return false;
  156.         }
  157.         $breadcrumb $beforeBreadcrumbCallback($breadcrumb);
  158.         if (null !== $breadcrumb) {
  159.             $this->getScope()->addBreadcrumb($breadcrumb$maxBreadcrumbs);
  160.         }
  161.         return null !== $breadcrumb;
  162.     }
  163.     /**
  164.      * {@inheritdoc}
  165.      */
  166.     public function getIntegration(string $className): ?IntegrationInterface
  167.     {
  168.         $client $this->getClient();
  169.         if (null !== $client) {
  170.             return $client->getIntegration($className);
  171.         }
  172.         return null;
  173.     }
  174.     /**
  175.      * {@inheritdoc}
  176.      *
  177.      * @param array<string, mixed> $customSamplingContext Additional context that will be passed to the {@see SamplingContext}
  178.      */
  179.     public function startTransaction(TransactionContext $context, array $customSamplingContext = []): Transaction
  180.     {
  181.         $transaction = new Transaction($context$this);
  182.         $client $this->getClient();
  183.         $options null !== $client $client->getOptions() : null;
  184.         if (null === $options || !$options->isTracingEnabled()) {
  185.             $transaction->setSampled(false);
  186.             return $transaction;
  187.         }
  188.         $samplingContext SamplingContext::getDefault($context);
  189.         $samplingContext->setAdditionalContext($customSamplingContext);
  190.         $tracesSampler $options->getTracesSampler();
  191.         if (null === $transaction->getSampled()) {
  192.             if (null !== $tracesSampler) {
  193.                 $sampleRate $tracesSampler($samplingContext);
  194.             } else {
  195.                 $sampleRate $this->getSampleRate(
  196.                     $samplingContext->getParentSampled(),
  197.                     $options->getTracesSampleRate() ?? 0
  198.                 );
  199.             }
  200.             if (!$this->isValidSampleRate($sampleRate)) {
  201.                 $transaction->setSampled(false);
  202.                 return $transaction;
  203.             }
  204.             $transaction->getMetadata()->setSamplingRate($sampleRate);
  205.             if (0.0 === $sampleRate) {
  206.                 $transaction->setSampled(false);
  207.                 return $transaction;
  208.             }
  209.             $transaction->setSampled($this->sample($sampleRate));
  210.         }
  211.         if (!$transaction->getSampled()) {
  212.             return $transaction;
  213.         }
  214.         $transaction->initSpanRecorder();
  215.         $profilesSampleRate $options->getProfilesSampleRate();
  216.         if ($this->sample($profilesSampleRate)) {
  217.             $transaction->initProfiler();
  218.             $profiler $transaction->getProfiler();
  219.             if (null !== $profiler) {
  220.                 $profiler->start();
  221.             }
  222.         }
  223.         return $transaction;
  224.     }
  225.     /**
  226.      * {@inheritdoc}
  227.      */
  228.     public function getTransaction(): ?Transaction
  229.     {
  230.         return $this->getScope()->getTransaction();
  231.     }
  232.     /**
  233.      * {@inheritdoc}
  234.      */
  235.     public function setSpan(?Span $span): HubInterface
  236.     {
  237.         $this->getScope()->setSpan($span);
  238.         return $this;
  239.     }
  240.     /**
  241.      * {@inheritdoc}
  242.      */
  243.     public function getSpan(): ?Span
  244.     {
  245.         return $this->getScope()->getSpan();
  246.     }
  247.     /**
  248.      * Gets the scope bound to the top of the stack.
  249.      */
  250.     private function getScope(): Scope
  251.     {
  252.         return $this->getStackTop()->getScope();
  253.     }
  254.     /**
  255.      * Gets the topmost client/layer pair in the stack.
  256.      */
  257.     private function getStackTop(): Layer
  258.     {
  259.         return $this->stack[\count($this->stack) - 1];
  260.     }
  261.     private function getSampleRate(?bool $hasParentBeenSampledfloat $fallbackSampleRate): float
  262.     {
  263.         if (true === $hasParentBeenSampled) {
  264.             return 1;
  265.         }
  266.         if (false === $hasParentBeenSampled) {
  267.             return 0;
  268.         }
  269.         return $fallbackSampleRate;
  270.     }
  271.     /**
  272.      * @param mixed $sampleRate
  273.      */
  274.     private function sample($sampleRate): bool
  275.     {
  276.         if (0.0 === $sampleRate) {
  277.             return false;
  278.         }
  279.         if (1.0 === $sampleRate) {
  280.             return true;
  281.         }
  282.         return mt_rand(0mt_getrandmax() - 1) / mt_getrandmax() < $sampleRate;
  283.     }
  284.     /**
  285.      * @param mixed $sampleRate
  286.      */
  287.     private function isValidSampleRate($sampleRate): bool
  288.     {
  289.         if (!\is_float($sampleRate) && !\is_int($sampleRate)) {
  290.             return false;
  291.         }
  292.         if ($sampleRate || $sampleRate 1) {
  293.             return false;
  294.         }
  295.         return true;
  296.     }
  297. }