Completed
Push — master ( 77ccc6...f13dc8 )
by Propa
05:09
created

PhoneNumber::getPhoneNumberInstance()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
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 27
    public static function make($number, $country = [])
77
    {
78 27
        $instance = new static($number);
79
80 27
        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 30
        );
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 22
            $parsedFormat
159 11
        );
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 2
            $country
178 1
        );
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 2
            $removeFormatting
199 1
        );
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 25
        }
212
213 75
        return $this->country;
214
    }
215
216
    /**
217
     * Check if the phone number is of (a) given country(ies).
218
     *
219
     * @param string|array $country
220
     * @return bool
221
     */
222 3
    public function isOfCountry($country)
223
    {
224 3
        $countries = static::parseCountries($country);
225
226 3
        return in_array($this->getCountry(), $countries);
227
    }
228
229
    /**
230
     * Filter the provided countries to the one that is valid for the number.
231
     *
232
     * @param string|array $countries
233
     * @return string
234
     * @throws \Propaganistas\LaravelPhone\Exceptions\NumberParseException
235
     */
236 84
    protected function filterValidCountry($countries)
237
    {
238 84
        $result = Collection::make($countries)
239 84
                            ->filter(function ($country) {
240 78
                                $instance = $this->lib->parse($this->number, $country);
241
242 78
                                return $this->lenient
243 36
                                    ? $this->lib->isPossibleNumber($instance, $country)
244 78
                                    : $this->lib->isValidNumberForRegion($instance, $country);
245 84
                            })->first();
246
247
        // If we got a new result, return it.
248 84
        if ($result) {
249 72
            return $result;
250
        }
251
252
        // Last resort: try to detect it from an international number.
253 42
        if ($this->numberLooksInternational()) {
254 24
            $instance = $this->lib->parse($this->number, null);
255
256 24
            if ($this->lib->isValidNumber($instance)) {
257 24
                return $this->lib->getRegionCodeForNumber($instance);
258
            }
259 1
        }
260
261 24
        throw NumberParseException::countryRequired($this->number);
262
    }
263
264
    /**
265
     * Get the phone number's type.
266
     *
267
     * @param bool $asConstant
268
     * @return string|int|null
269
     */
270 15
    public function getType($asConstant = false)
271
    {
272 15
        $type = $this->lib->getNumberType($this->getPhoneNumberInstance());
273
274 15
        if ($asConstant) {
275 15
            return $type;
276
        }
277
278 3
        $stringType = Arr::get(static::parseTypesAsStrings($type), 0);
279
280 3
        return $stringType ? strtolower($stringType) : null;
281
    }
282
283
    /**
284
     * Check if the phone number is of (a) given type(s).
285
     *
286
     * @param string $type
287
     * @return bool
288
     */
289 12
    public function isOfType($type)
290
    {
291 12
        $types = static::parseTypes($type);
292
293 12
        return in_array($this->getType(true), $types, true);
294
    }
295
296
    /**
297
     * Get the PhoneNumber instance of the current number.
298
     *
299
     * @return \libphonenumber\PhoneNumber
300
     */
301 75
    public function getPhoneNumberInstance()
302
    {
303 75
        return $this->lib->parse($this->number, $this->getCountry());
304
    }
305
306
    /**
307
     * Determine whether the phone number seems to be in international format.
308
     *
309
     * @return bool
310
     */
311 42
    protected function numberLooksInternational()
312
    {
313 42
        return Str::startsWith($this->number, '+');
314
    }
315
316
    /**
317
     * Enable lenient number parsing.
318
     *
319
     * @return $this
320
     */
321 21
    public function lenient()
322
    {
323 21
        $this->lenient = true;
324
325 21
        return $this;
326
    }
327
328
    /**
329
     * Convert the phone instance to JSON.
330
     *
331
     * @param  int $options
332
     * @return string
333
     */
334 3
    public function toJson($options = 0)
335
    {
336 3
        return json_encode($this->jsonSerialize(), $options);
337
    }
338
339
    /**
340
     * Convert the phone instance into something JSON serializable.
341
     *
342
     * @return string
343
     */
344 3
    public function jsonSerialize()
345
    {
346 3
        return $this->formatE164();
347
    }
348
349
    /**
350
     * Convert the phone instance into a string representation.
351
     *
352
     * @return string
353
     */
354 3
    public function serialize()
355
    {
356 3
        return $this->formatE164();
357
    }
358
359
    /**
360
     * Reconstructs the phone instance from a string representation.
361
     *
362
     * @param string $serialized
363
     */
364 3
    public function unserialize($serialized)
365
    {
366 3
        $this->lib = PhoneNumberUtil::getInstance();
367 3
        $this->number = $serialized;
368 3
        $this->country = $this->lib->getRegionCodeForNumber($this->getPhoneNumberInstance());
369 3
    }
370
371
    /**
372
     * Convert the phone instance to a formatted number.
373
     *
374
     * @return string
375
     */
376 9
    public function __toString()
377
    {
378
        // Formatting the phone number could throw an exception, but __toString() doesn't cope well with that.
379
        // Let's just return the original number in that case.
380
        try {
381 9
            return $this->formatE164();
382 3
        } catch (Exception $exception) {
383 3
            return $this->number;
384
        }
385
    }
386
}