Completed
Branch dev-v3 (d0b49e)
by Propa
01:27
created

PhoneNumber::ofCountry()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 1
dl 0
loc 11
ccs 6
cts 6
cp 1
crap 2
rs 9.4285
c 0
b 0
f 0
1
<?php namespace Propaganistas\LaravelPhone;
2
3
use Exception;
4
use Illuminate\Contracts\Support\Jsonable;
5
use Illuminate\Support\Arr;
6
use Illuminate\Support\Collection;
7
use Illuminate\Support\Str;
8
use JsonSerializable;
9
use libphonenumber\PhoneNumberFormat;
10
use libphonenumber\PhoneNumberUtil;
11
use Propaganistas\LaravelPhone\Exceptions\NumberFormatException;
12
use Propaganistas\LaravelPhone\Exceptions\CountryCodeException;
13
use Propaganistas\LaravelPhone\Exceptions\NumberParseException;
14
use Propaganistas\LaravelPhone\Traits\ParsesCountries;
15
use Propaganistas\LaravelPhone\Traits\ParsesFormats;
16
use Propaganistas\LaravelPhone\Traits\ParsesTypes;
17
use Serializable;
18
19
class PhoneNumber implements Jsonable, JsonSerializable, Serializable
20
{
21
    use ParsesCountries,
22
        ParsesFormats,
23
        ParsesTypes;
24
25
    /**
26
     * The provided phone number.
27
     *
28
     * @var string
29
     */
30
    protected $number;
31
32
    /**
33
     * The provided phone country.
34
     *
35
     * @var array
36
     */
37
    protected $countries = [];
38
39
    /**
40
     * The detected phone country.
41
     *
42
     * @var string
43
     */
44
    protected $country;
45
46
    /**
47
     * Whether to allow lenient checks (i.e. landline numbers without area codes).
48
     *
49
     * @var bool
50
     */
51
    protected $lenient = false;
52
53
    /**
54
     * @var \libphonenumber\PhoneNumberUtil
55
     */
56
    protected $lib;
57
58
    /**
59
     * Phone constructor.
60
     *
61
     * @param string $number
62
     */
63 105
    public function __construct($number)
64
    {
65 105
        $this->number = $number;
66 105
        $this->lib = PhoneNumberUtil::getInstance();
67 105
    }
68
69
    /**
70
     * Create a phone instance.
71
     *
72
     * @param string       $number
73
     * @param string|array $country
74
     * @return static
75
     */
76 30
    public static function make($number, $country = [])
77
    {
78 30
        $instance = new static($number);
79
80 30
        return $instance->ofCountry($country);
81
    }
82
83
    /**
84
     * Set the country to which the phone number belongs to.
85
     *
86
     * @param string|array $country
87
     * @return static
88
     */
89 90
    public function ofCountry($country)
90
    {
91 90
        $countries = is_array($country) ? $country : func_get_args();
92
93 90
        $instance = clone $this;
94 90
        $instance->countries = array_unique(
95 90
            array_merge($instance->countries, static::parseCountries($countries))
96
        );
97
98 90
        return $instance;
99
    }
100
101
    /**
102
     * Format the phone number in international format.
103
     *
104
     * @return string
105
     */
106 3
    public function formatInternational()
107
    {
108 3
        return $this->format(PhoneNumberFormat::INTERNATIONAL);
109
    }
110
111
    /**
112
     * Format the phone number in national format.
113
     *
114
     * @return string
115
     */
116 3
    public function formatNational()
117
    {
118 3
        return $this->format(PhoneNumberFormat::NATIONAL);
119
    }
120
121
    /**
122
     * Format the phone number in E164 format.
123
     *
124
     * @return string
125
     */
126 18
    public function formatE164()
127
    {
128 18
        return $this->format(PhoneNumberFormat::E164);
129
    }
130
131
    /**
132
     * Format the phone number in RFC3966 format.
133
     *
134
     * @return string
135
     */
136 6
    public function formatRFC3966()
137
    {
138 6
        return $this->format(PhoneNumberFormat::RFC3966);
139
    }
140
141
    /**
142
     * Format the phone number in a given format.
143
     *
144
     * @param string $format
145
     * @return string
146
     * @throws \Propaganistas\LaravelPhone\Exceptions\NumberFormatException
147
     */
148 45
    public function format($format)
149
    {
150 45
        $parsedFormat = static::parseFormat($format);
151
152 45
        if (is_null($parsedFormat)) {
153 3
            throw NumberFormatException::invalid($format);
154
        }
155
156 42
        return $this->lib->format(
157 42
            $this->getPhoneNumberInstance(),
158 33
            $parsedFormat
159
        );
160
    }
161
162
    /**
163
     * Format the phone number in a way that it can be dialled from the provided country.
164
     *
165
     * @param string $country
166
     * @return string
167
     * @throws \Propaganistas\LaravelPhone\Exceptions\CountryCodeException
168
     */
169 6 View Code Duplication
    public function formatForCountry($country)
170
    {
171 6
        if (! static::isValidCountryCode($country)) {
172 3
            throw CountryCodeException::invalid($country);
173
        }
174
175 3
        return $this->lib->formatOutOfCountryCallingNumber(
176 3
            $this->getPhoneNumberInstance(),
177 3
            $country
178
        );
179
    }
180
181
    /**
182
     * Format the phone number in a way that it can be dialled from the provided country using a cellphone.
183
     *
184
     * @param string $country
185
     * @param bool   $removeFormatting
186
     * @return string
187
     * @throws \Propaganistas\LaravelPhone\Exceptions\CountryCodeException
188
     */
189 6 View Code Duplication
    public function formatForMobileDialingInCountry($country, $removeFormatting = false)
190
    {
191 6
        if (! static::isValidCountryCode($country)) {
192 3
            throw CountryCodeException::invalid($country);
193
        }
194
195 3
        return $this->lib->formatNumberForMobileDialing(
196 3
            $this->getPhoneNumberInstance(),
197 3
            $country,
198 3
            $removeFormatting
199
        );
200
    }
201
202
    /**
203
     * Get the phone number's country.
204
     *
205
     * @return string
206
     */
207 84
    public function getCountry()
208
    {
209 84
        if (! $this->country) {
210 84
            $this->country = $this->filterValidCountry($this->countries);
211
        }
212
213 75
        return $this->country;
214
    }
215
216
    /**
217
     * Filter the provided countries to the one that is valid for the number.
218
     *
219
     * @param string|array $countries
220
     * @return string
221
     * @throws \Propaganistas\LaravelPhone\Exceptions\NumberParseException
222
     */
223 84
    protected function filterValidCountry($countries)
224
    {
225 84
        $result = Collection::make($countries)
226 84
                            ->filter(function ($country) {
227 78
                                $instance = $this->lib->parse($this->number, $country);
228
229 78
                                return $this->lenient
230 15
                                    ? $this->lib->isPossibleNumber($instance, $country)
231 78
                                    : $this->lib->isValidNumberForRegion($instance, $country);
232 84
                            })->first();
233
234
        // If we got a new result, return it.
235 84
        if ($result) {
236 72
            return $result;
237
        }
238
239
        // Last resort: try to detect it from an international number.
240 42
        if ($this->numberLooksInternational()) {
241 21
            $instance = $this->lib->parse($this->number, null);
242
243 21
            if ($this->lib->isValidNumber($instance)) {
244 21
                return $this->lib->getRegionCodeForNumber($instance);
245
            }
246
        }
247
248 27
        throw NumberParseException::countryRequired($this->number);
249
    }
250
251
    /**
252
     * Get the phone number's type.
253
     *
254
     * @param bool $asConstant
255
     * @return string|int|null
256
     */
257 18
    public function getType($asConstant = false)
258
    {
259 18
        $type = $this->lib->getNumberType($this->getPhoneNumberInstance());
260
261 18
        if ($asConstant) {
262 18
            return $type;
263
        }
264
265 3
        $stringType = Arr::first(static::parseTypesAsStrings($type));
266
267 3
        return $stringType ? strtolower($stringType) : null;
268
    }
269
270
    /**
271
     * Check if the phone number is of (a) given type(s).
272
     *
273
     * @param string $type
274
     * @return bool
275
     */
276 15
    public function isOfType($type)
277
    {
278 15
        $types = static::parseTypes($type);
279
280 15
        return in_array($this->getType(true), $types, true);
281
    }
282
283
    /**
284
     * Get the PhoneNumber instance of the current number.
285
     *
286
     * @return \libphonenumber\PhoneNumber
287
     */
288 78
    public function getPhoneNumberInstance()
289
    {
290 78
        return $this->lib->parse($this->number, $this->getCountry());
291
    }
292
293
    /**
294
     * Determine whether the phone number seems to be in international format.
295
     *
296
     * @return bool
297
     */
298 42
    protected function numberLooksInternational()
299
    {
300 42
        return Str::startsWith($this->number, '+');
301
    }
302
303
    /**
304
     * Enable lenient number parsing.
305
     *
306
     * @return $this
307
     */
308 24
    public function lenient()
309
    {
310 24
        $this->lenient = true;
311
312 24
        return $this;
313
    }
314
315
    /**
316
     * Convert the phone instance to JSON.
317
     *
318
     * @param  int $options
319
     * @return string
320
     */
321 3
    public function toJson($options = 0)
322
    {
323 3
        return json_encode($this->jsonSerialize(), $options);
324
    }
325
326
    /**
327
     * Convert the phone instance into something JSON serializable.
328
     *
329
     * @return string
330
     */
331 3
    public function jsonSerialize()
332
    {
333 3
        return $this->formatE164();
334
    }
335
336
    /**
337
     * Convert the phone instance into a string representation.
338
     *
339
     * @return string
340
     */
341 3
    public function serialize()
342
    {
343 3
        return $this->formatE164();
344
    }
345
346
    /**
347
     * Reconstructs the phone instance from a string representation.
348
     *
349
     * @param string $serialized
350
     */
351 3
    public function unserialize($serialized)
352
    {
353 3
        $this->lib = PhoneNumberUtil::getInstance();
354 3
        $this->number = $serialized;
355 3
        $this->country = $this->lib->getRegionCodeForNumber($this->getPhoneNumberInstance());
356 3
    }
357
358
    /**
359
     * Convert the phone instance to a formatted number.
360
     *
361
     * @return string
362
     */
363 9
    public function __toString()
364
    {
365
        // Formatting the phone number could throw an exception, but __toString() doesn't cope well with that.
366
        // Let's just return the original number in that case.
367
        try {
368 9
            return $this->formatE164();
369 3
        } catch (Exception $exception) {
370 3
            return $this->number;
371
        }
372
    }
373
}