Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.70% covered (success)
98.70%
76 / 77
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
ExceptionService
98.70% covered (success)
98.70%
76 / 77
75.00% covered (warning)
75.00%
3 / 4
28
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
100.00% covered (success)
100.00%
74 / 74
100.00% covered (success)
100.00%
1 / 1
25
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        switch ($exceptionName) {
36            // Process exceptions
37            case 'BO\\Zmsapi\\Exception\\Process\\ProcessNotFound':
38                $error = self::getError('appointmentNotFound');
39
40                break;
41            case 'BO\\Zmsapi\\Exception\\Process\\AuthKeyMatchFailed':
42                $error = self::getError('authKeyMismatch');
43
44                break;
45            case 'BO\\Zmsapi\\Exception\\Process\\ProcessAlreadyCalled':
46                $error = self::getError('processAlreadyCalled');
47
48                break;
49            case 'BO\\Zmsapi\\Exception\\Process\\ProcessNotReservedAnymore':
50                $error = self::getError('processNotReservedAnymore');
51
52                break;
53            case 'BO\\Zmsapi\\Exception\\Process\\ProcessNotPreconfirmedAnymore':
54                $error = self::getError('processNotPreconfirmedAnymore');
55
56                break;
57            case 'BO\\Zmsapi\\Exception\\Process\\ProcessDeleteFailed':
58                $error = self::getError('processDeleteFailed');
59
60                break;
61            case 'BO\\Zmsapi\\Exception\\Process\\ProcessInvalid':
62                $error = self::getError('processInvalid');
63
64                break;
65            case 'BO\\Zmsapi\\Exception\\Process\\ProcessAlreadyExists':
66                $error = self::getError('processAlreadyExists');
67
68                break;
69            case 'BO\\Zmsapi\\Exception\\Process\\EmailRequired':
70                $error = self::getError('emailIsRequired');
71
72                break;
73            case 'BO\\Zmsapi\\Exception\\Process\\TelephoneRequired':
74                $error = self::getError('telephoneIsRequired');
75
76                break;
77            case 'BO\\Zmsapi\\Exception\\Process\\MoreThanAllowedAppointmentsPerMail':
78                $error = self::getError('tooManyAppointmentsWithSameMail');
79
80                break;
81            case 'BO\\Zmsapi\\Exception\\Process\\PreconfirmationExpired':
82                $error = self::getError('preconfirmationExpired');
83
84                break;
85            case 'BO\\Zmsapi\\Exception\\Process\\ApiclientInvalid':
86                $error = self::getError('invalidApiClient');
87
88                break;
89            // Calendar exceptions
90            case 'BO\\Zmsapi\\Exception\\Calendar\\InvalidFirstDay':
91                $error = self::getError('invalidDateRange');
92
93                break;
94            case 'BO\\Zmsapi\\Exception\\Calendar\\AppointmentsMissed':
95                $error = self::getError('noAppointmentsAtLocation');
96
97                break;
98            // Other entity exceptions
99            case 'BO\\Zmsapi\\Exception\\Department\\DepartmentNotFound':
100                $error = self::getError('departmentNotFound');
101
102                break;
103            case 'BO\\Zmsapi\\Exception\\Mail\\MailNotFound':
104                $error = self::getError('mailNotFound');
105
106                break;
107            case 'BO\\Zmsapi\\Exception\\Organisation\\OrganisationNotFound':
108                $error = self::getError('organisationNotFound');
109
110                break;
111            case 'BO\\Zmsapi\\Exception\\Provider\\ProviderNotFound':
112                $error = self::getError('providerNotFound');
113
114                break;
115            case 'BO\\Zmsapi\\Exception\\Request\\RequestNotFound':
116                $error = self::getError('requestNotFound');
117
118                break;
119            case 'BO\\Zmsapi\\Exception\\Scope\\ScopeNotFound':
120                $error = self::getError('scopeNotFound');
121
122                break;
123            case 'BO\\Zmsapi\\Exception\\Source\\SourceNotFound':
124                $error = self::getError('sourceNotFound');
125
126                break;
127            // Use original message for unmapped exceptions
128            default:
129                $error = [
130                    'errorCode' => $exceptionName ?? 'unknown',
131                    'errorMessage' => $e->getMessage(),
132                    'statusCode' => $e->getCode() ?: 500
133                ];
134        }
135
136        throw new \RuntimeException($error['errorCode'] . ': ' . $error['errorMessage'], $error['statusCode'], $e);
137    }
138}