Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
92.13% covered (success)
92.13%
82 / 89
50.00% covered (danger)
50.00%
2 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
ExceptionService
92.13% covered (success)
92.13%
82 / 89
50.00% covered (danger)
50.00%
2 / 4
32.50
0.00% covered (danger)
0.00%
0 / 1
 setLanguageContext
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getLanguageContext
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 handleException
93.02% covered (success)
93.02%
80 / 86
0.00% covered (danger)
0.00%
0 / 1
29.29
1<?php
2
3declare(strict_types=1);
4
5namespace BO\Zmscitizenapi\Services\Core;
6
7use BO\Zmscitizenapi\Localization\ErrorMessages;
8
9class ExceptionService
10{
11    private static ?string $currentLanguage = null;
12    public static function setLanguageContext(?string $language): void
13    {
14        self::$currentLanguage = $language;
15    }
16
17    public static function getLanguageContext(): ?string
18    {
19        return self::$currentLanguage;
20    }
21
22    private static function getError(string $key): array
23    {
24        return ErrorMessages::get($key, self::$currentLanguage);
25    }
26
27    /**
28     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
29     * @TODO: Consider using a strategy pattern or error handler chain to reduce method complexity
30     */
31    public static function handleException(\Exception $e): never
32    {
33        $exceptionName = json_decode(json_encode($e), true)['template'] ?? null;
34        $error = null;
35
36        switch ($exceptionName) {
37            // ZmsClient exception
38            case 'BO\\Zmsclient\\Exception':
39                $error = self::getError('zmsClientCommunicationError');
40
41                break;
42            // Missing mail template exceptions
43            case 'Twig\\Error\\RuntimeError':
44                $error = self::getError('mailNotFound');
45
46                break;
47            case 'Twig\\Error\\LoaderError':
48                $error = self::getError('mailNotFound');
49
50                break;
51            case 'BO\\Zmsapi\\Exception\\Mail\\MailNotFound':
52                $error = self::getError('mailNotFound');
53
54                break;
55            // Process exceptions
56            case 'BO\\Zmsapi\\Exception\\Process\\ProcessNotFound':
57                $error = self::getError('appointmentNotFound');
58
59                break;
60            case 'BO\\Zmsapi\\Exception\\Process\\AuthKeyMatchFailed':
61                $error = self::getError('authKeyMismatch');
62
63                break;
64            case 'BO\\Zmsapi\\Exception\\Process\\ProcessAlreadyCalled':
65                $error = self::getError('processAlreadyCalled');
66
67                break;
68            case 'BO\\Zmsapi\\Exception\\Process\\ProcessNotReservedAnymore':
69                $error = self::getError('processNotReservedAnymore');
70
71                break;
72            case 'BO\\Zmsapi\\Exception\\Process\\ProcessNotPreconfirmedAnymore':
73                $error = self::getError('processNotPreconfirmedAnymore');
74
75                break;
76            case 'BO\\Zmsapi\\Exception\\Process\\ProcessDeleteFailed':
77                $error = self::getError('processDeleteFailed');
78
79                break;
80            case 'BO\\Zmsapi\\Exception\\Process\\ProcessInvalid':
81                $error = self::getError('processInvalid');
82
83                break;
84            case 'BO\\Zmsapi\\Exception\\Process\\ProcessAlreadyExists':
85                $error = self::getError('processAlreadyExists');
86
87                break;
88            case 'BO\\Zmsapi\\Exception\\Process\\EmailRequired':
89                $error = self::getError('emailIsRequired');
90
91                break;
92            case 'BO\\Zmsapi\\Exception\\Process\\TelephoneRequired':
93                $error = self::getError('telephoneIsRequired');
94
95                break;
96            case 'BO\\Zmsapi\\Exception\\Process\\MoreThanAllowedAppointmentsPerMail':
97                $error = self::getError('tooManyAppointmentsWithSameMail');
98
99                break;
100            case 'BO\\Zmsapi\\Exception\\Process\\PreconfirmationExpired':
101                $error = self::getError('preconfirmationExpired');
102
103                break;
104            case 'BO\\Zmsapi\\Exception\\Process\\ApiclientInvalid':
105                $error = self::getError('invalidApiClient');
106
107                break;
108            // Calendar exceptions
109            case 'BO\\Zmsapi\\Exception\\Calendar\\InvalidFirstDay':
110                $error = self::getError('invalidDateRange');
111
112                break;
113            case 'BO\\Zmsapi\\Exception\\Calendar\\AppointmentsMissed':
114                $error = self::getError('noAppointmentForThisScope');
115                break;
116            case 'BO\\Zmsdb\\Exception\\CalendarWithoutScopes':
117                $error = self::getError('noAppointmentForThisScope');
118                break;
119            // Other entity exceptions
120            case 'BO\\Zmsapi\\Exception\\Department\\DepartmentNotFound':
121                $error = self::getError('departmentNotFound');
122
123                break;
124            case 'BO\\Zmsapi\\Exception\\Organisation\\OrganisationNotFound':
125                $error = self::getError('organisationNotFound');
126
127                break;
128            case 'BO\\Zmsapi\\Exception\\Provider\\ProviderNotFound':
129                $error = self::getError('providerNotFound');
130
131                break;
132            case 'BO\\Zmsapi\\Exception\\Request\\RequestNotFound':
133                $error = self::getError('requestNotFound');
134
135                break;
136            case 'BO\\Zmsapi\\Exception\\Scope\\ScopeNotFound':
137                $error = self::getError('scopeNotFound');
138
139                break;
140            case 'BO\\Zmsapi\\Exception\\Source\\SourceNotFound':
141                $error = self::getError('sourceNotFound');
142
143                break;
144
145            // Use original message for unmapped exceptions
146            default:
147                $error = [
148                    'errorCode' => $exceptionName ?? 'unknown',
149                    'errorMessage' => $e->getMessage(),
150                    'statusCode' => $e->getCode() ?: 500
151                ];
152        }
153
154        throw new \RuntimeException($error['errorCode'] . ': ' . $error['errorMessage'], $error['statusCode'], $e);
155    }
156}