/
ModelViolationReporter.php
108 lines (86 loc) · 3.42 KB
/
ModelViolationReporter.php
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
<?php
namespace Sentry\Laravel\Integration\ModelViolations;
use Exception;
use Illuminate\Database\Eloquent\Model;
use Sentry\Event;
use Sentry\EventHint;
use Sentry\ExceptionMechanism;
use Sentry\Laravel\Features\Concerns\ResolvesEventOrigin;
use Sentry\SentrySdk;
use Sentry\Severity;
use Sentry\State\Scope;
abstract class ModelViolationReporter
{
use ResolvesEventOrigin;
/** @var callable|null $callback */
private $callback;
/** @var bool $suppressDuplicateReports */
private $suppressDuplicateReports;
/** @var bool $reportAfterResponse */
private $reportAfterResponse;
/** @var array<string, true> $reportedViolations */
private $reportedViolations = [];
public function __construct(?callable $callback, bool $suppressDuplicateReports, bool $reportAfterResponse)
{
$this->callback = $callback;
$this->suppressDuplicateReports = $suppressDuplicateReports;
$this->reportAfterResponse = $reportAfterResponse;
}
/** @param string|array<int, string> $propertyOrProperties */
public function __invoke(Model $model, $propertyOrProperties): void
{
$property = is_array($propertyOrProperties)
? implode(', ', $propertyOrProperties)
: $propertyOrProperties;
if (!$this->shouldReport($model, $property)) {
return;
}
$this->markAsReported($model, $property);
$origin = $this->resolveEventOrigin();
if ($this->reportAfterResponse) {
app()->terminating(function () use ($model, $property, $origin) {
$this->report($model, $property, $origin);
});
} else {
$this->report($model, $property, $origin);
}
}
abstract protected function getViolationContext(Model $model, string $property): array;
abstract protected function getViolationException(Model $model, string $property): Exception;
protected function shouldReport(Model $model, string $property): bool
{
if (!$this->suppressDuplicateReports) {
return true;
}
return !array_key_exists(get_class($model) . $property, $this->reportedViolations);
}
protected function markAsReported(Model $model, string $property): void
{
if (!$this->suppressDuplicateReports) {
return;
}
$this->reportedViolations[get_class($model) . $property] = true;
}
private function report(Model $model, string $property, $origin): void
{
SentrySdk::getCurrentHub()->withScope(function (Scope $scope) use ($model, $property, $origin) {
$scope->setContext('violation', array_merge([
'model' => get_class($model),
'origin' => $origin,
], $this->getViolationContext($model, $property)));
SentrySdk::getCurrentHub()->captureEvent(
tap(Event::createEvent(), static function (Event $event) {
$event->setLevel(Severity::warning());
}),
EventHint::fromArray([
'exception' => $this->getViolationException($model, $property),
'mechanism' => new ExceptionMechanism(ExceptionMechanism::TYPE_GENERIC, true),
])
);
});
// Forward the violation to the next handler if there is one
if ($this->callback !== null) {
call_user_func($this->callback, $model, $property);
}
}
}