|
23 | 23 |
|
24 | 24 | class PhoneFormatter
|
25 | 25 | {
|
26 |
| - private $validMobile = [ |
| 26 | + private const VALID_MOBILE = [ |
27 | 27 | 'NL' => ['00316'],
|
28 | 28 | 'BE' => ['003246', '003247', '003248', '003249'],
|
29 | 29 | 'DE' => ['004915', '004916', '004917'],
|
30 | 30 | 'AT' => ['0049650', '0049660', '0049664','0049676', '0049680', '0049677','0049681', '0049688', '0049699'],
|
31 | 31 | ];
|
32 | 32 |
|
33 |
| - private $invalidNotation = [ |
| 33 | + private const INVALID_NOTATION = [ |
34 | 34 | 'NL' => ['00310', '0310', '310', '31'],
|
35 | 35 | 'BE' => ['00320', '0320', '320', '32'],
|
36 | 36 | ];
|
37 | 37 |
|
38 |
| - private $startingNotation = [ |
39 |
| - 'NL' => ['0', '0', '3', '1'], |
40 |
| - 'BE' => ['0', '0', '3', '2'], |
41 |
| - 'DE' => ['0', '0', '4', '9'], |
42 |
| - 'AT' => ['0', '0', '4', '3'], |
| 38 | + private const STARTING_NOTATION = [ |
| 39 | + 'NL' => '0031', |
| 40 | + 'BE' => '0032', |
| 41 | + 'DE' => '0049', |
| 42 | + 'AT' => '0043', |
43 | 43 | ];
|
| 44 | + |
44 | 45 | private Log $logger;
|
45 | 46 |
|
46 |
| - public function __construct( |
47 |
| - Log $logger |
48 |
| - ) { |
| 47 | + public function __construct(Log $logger) |
| 48 | + { |
49 | 49 | $this->logger = $logger;
|
50 | 50 | }
|
51 | 51 |
|
52 | 52 | /**
|
53 |
| - * @param $phoneNumber |
54 |
| - * @param $country |
| 53 | + * Formats a phone number for a given country. |
55 | 54 | *
|
| 55 | + * @param string|null $phoneNumber |
| 56 | + * @param string $country |
56 | 57 | * @return array
|
57 | 58 | */
|
58 |
| - public function format($phoneNumber, $country) |
| 59 | + public function format(?string $phoneNumber, string $country): array |
59 | 60 | {
|
60 |
| - $this->logger->addDebug(__METHOD__ . '|1|'); |
61 |
| - $this->logger->addDebug(var_export([$phoneNumber, $country], true)); |
62 |
| - |
63 |
| - $return = ["orginal" => $phoneNumber, "clean" => false, "mobile" => false, "valid" => false]; |
| 61 | + $this->logger->addDebug(__METHOD__ . ' - Starting format process', [ |
| 62 | + 'phoneNumber' => $phoneNumber, |
| 63 | + 'country' => $country, |
| 64 | + ]); |
| 65 | + |
| 66 | + $return = [ |
| 67 | + 'original' => $phoneNumber, |
| 68 | + 'clean' => false, |
| 69 | + 'mobile' => false, |
| 70 | + 'valid' => false, |
| 71 | + ]; |
| 72 | + |
| 73 | + // If phone number is null or empty, return default |
| 74 | + if (empty($phoneNumber)) { |
| 75 | + return $return; |
| 76 | + } |
64 | 77 |
|
65 |
| - $match = preg_replace('/[^0-9]/Uis', '', $phoneNumber); |
66 |
| - if ($match) { |
67 |
| - $phoneNumber = $match; |
| 78 | + // If country is unsupported, return the original phone number as clean |
| 79 | + if (!isset(self::STARTING_NOTATION[$country])) { |
| 80 | + $return['clean'] = $phoneNumber; |
| 81 | + return $return; |
68 | 82 | }
|
69 | 83 |
|
70 |
| - $return['clean'] = $this->formatPhoneNumber($phoneNumber, $country); |
71 |
| - $return['mobile'] = $this->isMobileNumber($return['clean'], $country); |
| 84 | + // Clean and format the phone number |
| 85 | + $cleanedNumber = $this->cleanPhoneNumber($phoneNumber); |
| 86 | + $formattedNumber = $this->formatPhoneNumber($cleanedNumber, $country); |
72 | 87 |
|
73 |
| - if (strlen((string)$return['clean']) == 13) { |
74 |
| - $return['valid'] = true; |
75 |
| - } |
| 88 | + // Check if the phone number is mobile and valid |
| 89 | + $return['clean'] = $formattedNumber; |
| 90 | + $return['mobile'] = $this->isMobileNumber($formattedNumber, $country); |
| 91 | + $return['valid'] = strlen($formattedNumber) === 13; |
76 | 92 |
|
77 |
| - $this->logger->addDebug(__METHOD__ . '|2|'); |
78 |
| - $this->logger->addDebug(var_export($return, true)); |
| 93 | + $this->logger->addDebug(__METHOD__ . ' - Format process complete', $return); |
79 | 94 |
|
80 | 95 | return $return;
|
81 | 96 | }
|
82 | 97 |
|
83 | 98 | /**
|
84 |
| - * @param $phoneNumber |
85 |
| - * @param $country |
| 99 | + * Removes all non-numeric characters from the phone number. |
| 100 | + * |
| 101 | + * @param string $phoneNumber |
| 102 | + * @return string |
| 103 | + */ |
| 104 | + private function cleanPhoneNumber(string $phoneNumber): string |
| 105 | + { |
| 106 | + return preg_replace('/\D+/', '', $phoneNumber) ?? ''; |
| 107 | + } |
| 108 | + |
| 109 | + /** |
| 110 | + * Formats the phone number based on the country rules. |
86 | 111 | *
|
| 112 | + * @param string $phoneNumber |
| 113 | + * @param string $country |
87 | 114 | * @return string
|
88 | 115 | */
|
89 |
| - private function formatPhoneNumber($phoneNumber, $country) |
| 116 | + private function formatPhoneNumber(string $phoneNumber, string $country): string |
90 | 117 | {
|
91 |
| - $phoneLength = strlen((string)$phoneNumber); |
| 118 | + $startingNotation = self::STARTING_NOTATION[$country]; |
| 119 | + $phoneLength = strlen($phoneNumber); |
92 | 120 |
|
93 |
| - if ($phoneLength > 10 && $phoneLength != 13) { |
94 |
| - $phoneNumber = $this->isValidNotation($phoneNumber, $country); |
| 121 | + // Apply invalid notation correction |
| 122 | + if ($phoneLength > 10 && $phoneLength !== 13) { |
| 123 | + $phoneNumber = $this->applyInvalidNotationCorrection($phoneNumber, $country); |
95 | 124 | }
|
96 | 125 |
|
97 |
| - if ((in_array($country, ['NL', 'BE']) && ($phoneLength == 10)) |
98 |
| - || |
99 |
| - (in_array($country, ['AT', 'DE'])) |
100 |
| - ) { |
101 |
| - if (isset($this->startingNotation[$country])) { |
102 |
| - $notationStart = implode($this->startingNotation[$country]); |
103 |
| - $phoneNumber = $notationStart . substr($phoneNumber, 1); |
104 |
| - } |
| 126 | + // Prepend starting notation for supported local numbers |
| 127 | + if ($phoneLength === 10 && !str_starts_with($phoneNumber, $startingNotation)) { |
| 128 | + $phoneNumber = $startingNotation . substr($phoneNumber, 1); |
105 | 129 | }
|
106 | 130 |
|
107 | 131 | return $phoneNumber;
|
108 | 132 | }
|
109 | 133 |
|
110 | 134 | /**
|
111 |
| - * @param $phoneNumber |
112 |
| - * @param $country |
| 135 | + * Checks if the phone number is mobile based on country rules. |
113 | 136 | *
|
| 137 | + * @param string $phoneNumber |
| 138 | + * @param string $country |
114 | 139 | * @return bool
|
115 | 140 | */
|
116 |
| - private function isMobileNumber($phoneNumber, $country) |
| 141 | + private function isMobileNumber(string $phoneNumber, string $country): bool |
117 | 142 | {
|
118 |
| - $isMobile = false; |
119 |
| - |
120 |
| - if (isset($this->validMobile[$country])) { |
121 |
| - array_walk( |
122 |
| - $this->validMobile[$country], |
123 |
| - function ($value) use (&$isMobile, $phoneNumber) { |
124 |
| - $phoneNumberPart = substr($phoneNumber, 0, strlen($value)); |
125 |
| - $phoneNumberHasValue = strpos($phoneNumberPart, $value); |
126 |
| - |
127 |
| - if ($phoneNumberHasValue !== false) { |
128 |
| - $isMobile = true; |
129 |
| - } |
130 |
| - } |
131 |
| - ); |
| 143 | + foreach (self::VALID_MOBILE[$country] ?? [] as $prefix) { |
| 144 | + if (str_starts_with($phoneNumber, $prefix)) { |
| 145 | + return true; |
| 146 | + } |
132 | 147 | }
|
133 |
| - |
134 |
| - return $isMobile; |
| 148 | + return false; |
135 | 149 | }
|
136 | 150 |
|
137 | 151 | /**
|
138 |
| - * @param $phoneNumber |
139 |
| - * @param $country |
| 152 | + * Corrects invalid notations in the phone number. |
140 | 153 | *
|
| 154 | + * @param string $phoneNumber |
| 155 | + * @param string $country |
141 | 156 | * @return string
|
142 | 157 | */
|
143 |
| - private function isValidNotation($phoneNumber, $country) |
| 158 | + private function applyInvalidNotationCorrection(string $phoneNumber, string $country): string |
144 | 159 | {
|
145 |
| - if (isset($this->invalidNotation[$country])) { |
146 |
| - array_walk( |
147 |
| - $this->invalidNotation[$country], |
148 |
| - function ($invalid) use (&$phoneNumber, $country) { |
149 |
| - $phoneNumberPart = substr($phoneNumber, 0, strlen($invalid)); |
150 |
| - |
151 |
| - if (strpos($phoneNumberPart, $invalid) !== false) { |
152 |
| - $phoneNumber = $this->formatNotation($phoneNumber, $invalid, $country); |
153 |
| - } |
154 |
| - } |
155 |
| - ); |
| 160 | + foreach (self::INVALID_NOTATION[$country] ?? [] as $invalidPrefix) { |
| 161 | + if (str_starts_with($phoneNumber, $invalidPrefix)) { |
| 162 | + $phoneNumber = $this->replaceInvalidNotation($phoneNumber, $invalidPrefix, $country); |
| 163 | + } |
156 | 164 | }
|
157 |
| - |
158 | 165 | return $phoneNumber;
|
159 | 166 | }
|
160 | 167 |
|
161 | 168 | /**
|
162 |
| - * @param $phoneNumber |
163 |
| - * @param $invalid |
164 |
| - * @param $country |
| 169 | + * Replaces invalid notation with a valid prefix. |
165 | 170 | *
|
| 171 | + * @param string $phoneNumber |
| 172 | + * @param string $invalidPrefix |
| 173 | + * @param string $country |
166 | 174 | * @return string
|
167 | 175 | */
|
168 |
| - private function formatNotation($phoneNumber, $invalid, $country) |
| 176 | + private function replaceInvalidNotation(string $phoneNumber, string $invalidPrefix, string $country): string |
169 | 177 | {
|
170 |
| - if (isset($this->startingNotation[$country])) { |
171 |
| - $valid = substr($invalid, 0, -1); |
172 |
| - $countryNotation = $this->startingNotation[$country]; |
173 |
| - |
174 |
| - if (substr($valid, 0, 2) == $countryNotation[2] . $countryNotation[3]) { |
175 |
| - $valid = $countryNotation[0] . $countryNotation[1] . $valid; |
176 |
| - } |
177 |
| - |
178 |
| - if (substr($valid, 0, 2) == $countryNotation[1] . $countryNotation[2]) { |
179 |
| - $valid = $countryNotation[0] . $valid; |
180 |
| - } |
181 |
| - |
182 |
| - if ($valid == $countryNotation[2]) { |
183 |
| - $valid = $countryNotation[1] . $valid . $countryNotation[3]; |
184 |
| - } |
185 |
| - |
186 |
| - $phoneNumber = substr_replace($phoneNumber, $valid, 0, strlen($invalid)); |
187 |
| - } |
188 |
| - |
189 |
| - return $phoneNumber; |
| 178 | + $validPrefix = self::STARTING_NOTATION[$country]; |
| 179 | + return preg_replace('/^' . preg_quote($invalidPrefix, '/') . '/', $validPrefix, $phoneNumber) ?? $phoneNumber; |
190 | 180 | }
|
191 | 181 | }
|
0 commit comments