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