Passed
Push — master ( 9a0f5d...3454b1 )
by William
20:13
created

Sentry::getSentryTimestamp()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 5
ccs 0
cts 4
cp 0
crap 2
rs 10
1
<?php
2
3
namespace App\Forwarding;
4
5
use App\Report;
6
use Cake\Core\Configure;
7
use Cake\Log\Log;
8
use Exception;
9
10
use function curl_exec;
11
use function curl_init;
12
use function curl_setopt;
13
use function date_default_timezone_set;
14
use function http_build_query;
15
use function is_array;
16
use function is_string;
17
use function json_decode;
18
use function json_encode;
19
use function time;
20
21
use const CURLOPT_HTTPHEADER;
22
use const CURLOPT_POST;
23
use const CURLOPT_POSTFIELDS;
24
use const CURLOPT_RETURNTRANSFER;
25
use const CURLOPT_USERPWD;
26
27
class Sentry
28
{
29
    public static function getSentryTimestamp(): int
30
    {
31
        date_default_timezone_set('UTC');
32
33
        return time();
34
    }
35
36
    /**
37
     * Send report and get ID
38
     *
39
     * @param array<string,mixed> $report The report as an array
40
     * @return string The event ID
41
     *
42
     * @see https://develop.sentry.dev/sdk/store/
43
     */
44
    public static function sendReport(array $report): string
45
    {
46
        $sentryConfig = Configure::read('Forwarding.Sentry');
47
        if ($sentryConfig === null) {
48
            throw new Exception('Missing Sentry config');
49
        }
50
51
        $data = json_encode($report);
52
        $ch = curl_init($sentryConfig['base_url'] . '/api/' . $sentryConfig['project_id'] . '/store/');
53
        if ($ch === false) {
54
            throw new Exception('Could not init cURL');
55
        }
56
57
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
58
        curl_setopt($ch, CURLOPT_USERPWD, $sentryConfig['key'] . ':');
59
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
60
            'Content-type: application/json',
61
            'X-Sentry-Auth: Sentry sentry_version=7, sentry_timestamp=' . self::getSentryTimestamp()
62
            . ', sentry_key=' . $sentryConfig['key'] . ', sentry_client=phpmyadmin-proxy/0.1'
63
            . ', sentry_secret=' . $sentryConfig['secret'],
64
        ]);
65
        curl_setopt($ch, CURLOPT_POST, 1);
66
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
67
68
        $output = curl_exec($ch);
69
        if (! is_string($output)) {
70
            $error = 'Creating the report failed: ' . json_encode($output);
71
            Log::error($error);
72
73
            throw new Exception($error);
74
        }
75
76
        $response = json_decode((string) $output, true);
77
        if (! is_array($response)) {
78
            $error = 'Invalid JSON response: ' . json_encode($output);
79
            Log::error($error);
80
81
            throw new Exception($error);
82
        }
83
84
        if (! isset($response['id'])) {
85
            $error = 'Invalid response Id: ' . json_encode($response);
86
            Log::error($error);
87
88
            throw new Exception($error);
89
        }
90
91
        return (string) $response['id'];
92
    }
93
94
    /**
95
     * Set user feedback to Sentry API
96
     *
97
     * @param string $eventId  The event ID
98
     * @param string $comments The comment sent by the user
99
     * @param string $userId   The unique user id
100
     * @return void nothing
101
     */
102
    public static function sendFeedback(string $eventId, string $comments, string $userId): void
103
    {
104
        $sentryConfig = Configure::read('Forwarding.Sentry');
105
        if ($sentryConfig === null) {
106
            throw new Exception('Missing Sentry config');
107
        }
108
109
        $data = [
110
            'comments' => $comments,
111
            'email' => 'u+' . $userId . '@r.local',// 75 chars max (fake hostname to pass email validation rule)
112
            'name' => 'phpMyAdmin User',
113
        ];
114
        $ch = curl_init(
115
            $sentryConfig['base_url'] . '/api/embed/error-page/?eventId=' . $eventId . '&dsn=' . $sentryConfig['dsn_url']
116
        );
117
        if ($ch === false) {
118
            throw new Exception('Could not init cURL');
119
        }
120
121
        curl_setopt($ch, CURLOPT_USERPWD, $sentryConfig['key'] . ':');
122
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
123
        curl_setopt($ch, CURLOPT_POST, 1);
124
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
125
126
        $output = curl_exec($ch);
127
        if (! is_string($output)) {
128
            $error = 'Creating the report feedback failed: ' . json_encode($output);
129
            Log::error($error);
130
131
            return;
132
        }
133
134
        $response = json_decode((string) $output, true);
135
        if (! is_array($response)) {
136
            $error = 'Invalid JSON response: ' . json_encode($output);
137
            Log::error($error);
138
139
            return;
140
        }
141
142
        if (isset($response['errors'])) {
143
            $error = 'Response has errors: ' . json_encode($response['errors']);
144
            Log::error($error);
145
146
            return;
147
        }
148
    }
149
150
    public static function process(Report $report): void
151
    {
152
        foreach ($report->getReports() as $reportData) {
153
            $eventId = self::sendReport($reportData);
154
            if (! $report->hasUserFeedback()) {
155
                continue;
156
            }
157
158
            self::sendFeedback($eventId, $report->getUserFeedback(), $report->getUserId());
159
        }
160
    }
161
}
162