ldc-boutique-cnc-sable/app/Lib/Helpers/ExceptionsHelper.php

158 lines
4.3 KiB
PHP
Raw Permalink Normal View History

<?php
2026-06-14 21:32:07 +02:00
declare(strict_types=1);
namespace App\Lib\Helpers;
2026-06-14 21:32:07 +02:00
use Sentry\Event;
use Sentry\ExceptionDataBag;
use Sentry\Stacktrace;
/**
* Provides utilities to sanitize and anonymize exceptions before reporting to Sentry.
*/
class ExceptionsHelper
{
/**
2026-06-14 21:32:07 +02:00
* Handle sentry event.
*/
2026-06-14 21:32:07 +02:00
public static function sentryEventHandler(Event $event): ?Event
{
$event->setModules([]);
$event->setOsContext(null);
2026-06-14 21:32:07 +02:00
$exceptions = array_map(
[self::class, 'getAnonymizedSentryException'],
$event->getExceptions()
);
$event->setExceptions($exceptions);
return $event;
}
/**
2026-06-14 21:32:07 +02:00
* Anonymize Sentry Exception bag.
*/
2026-06-14 21:32:07 +02:00
private static function getAnonymizedSentryException(ExceptionDataBag $exceptionBag): ExceptionDataBag
{
$exceptionBag->setValue(self::getAnonymizedMessage($exceptionBag->getValue()));
if ($stackTrace = $exceptionBag->getStacktrace()) {
$exceptionBag->setStacktrace(self::getAnonymizedSentryStackTrace($stackTrace));
}
2026-06-14 21:32:07 +02:00
return $exceptionBag;
}
/**
2026-06-14 21:32:07 +02:00
* Anonymize sentry stacktrace.
*/
2026-06-14 21:32:07 +02:00
private static function getAnonymizedSentryStackTrace(Stacktrace $stackTrace): Stacktrace
{
2026-06-14 21:32:07 +02:00
$frames = $stackTrace->getFrames();
if (empty($frames)) {
return $stackTrace;
}
2026-06-14 21:32:07 +02:00
$firstFrame = $frames[0];
$firstFrame->setPostContext([]);
$firstFrame->setPreContext([]);
$firstFrame->setContextLine('');
$firstFrame->setVars(self::getAnonymizedSentryParams($firstFrame->getVars()));
return new Stacktrace([$firstFrame]);
}
/**
2026-06-14 21:32:07 +02:00
* Get anonymized sentry params.
*
* @template T of mixed
*
* @param array<array-key, T> $params
* @return array<string|T>
*/
private static function getAnonymizedSentryParams(array $params): array
{
foreach ($params as &$param) {
2026-06-14 21:32:07 +02:00
$type = gettype($param);
$param = match (true) {
is_object($param) => $param,
is_array($param) => sprintf('Array(%d)', count($param)),
is_int($param), is_float($param) => ucfirst($type),
is_string($param) => str_starts_with(trim(strtolower($param)), 'lambda')
? $param
: sprintf('String(%d)', strlen($param)),
default => ucfirst($type),
};
}
return $params;
}
/**
2026-06-14 21:32:07 +02:00
* Anonymize at least Sql queries.
*/
public static function getAnonymizedMessage(string $message): string
{
2026-06-14 21:32:07 +02:00
$message = preg_replace('/^(.*)( \(SQL:.*\))$/', '$1', $message) ?? $message;
if (($sqlPos = strpos($message, 'to use near')) !== false) {
$message = mb_strcut($message, 0, $sqlPos);
}
2026-06-14 21:32:07 +02:00
return $message;
}
/**
2026-06-14 21:32:07 +02:00
* Anonymize standard PHP stack trace.
*/
public static function getAnonymizedStackTrace(\Throwable $e): string
{
2026-06-14 21:32:07 +02:00
$projectPath = realpath(app_path('../')) ?: '';
$traceStr = '';
foreach ($e->getTrace() as $stage => $stack) {
$file = str_replace($projectPath, '', $stack['file'] ?? '');
$line = $stack['line'] ?? '';
$traceStr .= "#{$stage} {$file}({$line}): ";
if (isset($stack['class'])) {
2026-06-14 21:32:07 +02:00
$traceStr .= "{$stack['class']}->";
}
2026-06-14 21:32:07 +02:00
$traceStr .= "{$stack['function']}(";
$traceStr .= isset($stack['args']) ? self::handleArgs($stack['args']) : '';
$traceStr .= ")\n";
}
$nextStage = count($e->getTrace());
return $traceStr . "#{$nextStage} {main}";
}
/**
2026-06-14 21:32:07 +02:00
* Handle Args.
*
2026-06-14 21:32:07 +02:00
* @param array<mixed> $args
*/
2026-06-14 21:32:07 +02:00
public static function handleArgs(array $args): string
{
2026-06-14 21:32:07 +02:00
$formattedArgs = array_map(function ($arg) {
$type = gettype($arg);
return match (true) {
is_object($arg) => sprintf('Object(%s)', $arg::class),
is_array($arg) => sprintf('Array(%d)', count($arg)),
is_string($arg) => sprintf('String(%d)', strlen((string) $arg)),
default => $type,
};
}, $args);
return implode(',', $formattedArgs);
}
}