Completed
Push — master ( 8f7adf...0c234d )
by Propa
09:38
created

PhoneNumber   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 372
Duplicated Lines 6.18 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
dl 23
loc 372
ccs 103
cts 103
cp 1
rs 8.8
c 0
b 0
f 0
wmc 36
lcom 1
cbo 10

23 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A make() 0 6 1
A formatInternational() 0 4 1
A formatNational() 0 4 1
A formatE164() 0 4 1
A formatRFC3966() 0 4 1
A ofCountry() 0 11 2
A format() 0 13 2
A formatForCountry() 11 11 2
A formatForMobileDialingInCountry() 12 12 2
A getCountry() 0 8 2
A isOfCountry() 0 6 1
B filterValidCountry() 0 31 6
A getType() 0 12 3
A isOfType() 0 6 1
A getPhoneNumberInstance() 0 4 1
A numberLooksInternational() 0 4 1
A lenient() 0 6 1
A toJson() 0 4 1
A jsonSerialize() 0 4 1
A serialize() 0 4 1
A unserialize() 0 6 1
A __toString() 0 10 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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 108
    public function __construct($number)
64
    {
65 108
        $this->number = $number;
66 108
        $this->lib = PhoneNumberUtil::getInstance();
67 108
    }
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 93
    public function ofCountry($country)
90
    {
91 93
        $countries = is_array($country) ? $country : func_get_args();
92
93 93
        $instance = clone $this;
94 93
        $instance->countries = array_unique(
95 93
            array_merge($instance->countries, static::parseCountries($countries))
96 31
        );
97
98 93
        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)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 87
    public function getCountry()
208
    {
209 87
        if (! $this->country) {
210 87
            $this->country = $this->filterValidCountry($this->countries);
211 26
        }
212
213 78
        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 87
    protected function filterValidCountry($countries)
237
    {
238 87
        $result = Collection::make($countries)
239 87
                            ->filter(function ($country) {
240 81
                                $instance = $this->lib->parse($this->number, $country);
241
242 81
                                return $this->lenient
243 39
                                    ? $this->lib->isPossibleNumber($instance, $country)
244 81
                                    : $this->lib->isValidNumberForRegion($instance, $country);
245 87
                            })->first();
246
247
        // If we got a new result, return it.
248 87
        if ($result) {
249 75
            return $result;
250
        }
251
252
        // Last resort: try to detect it from an international number.
253 45
        if ($this->numberLooksInternational()) {
254 27
            $countries[] = null;
255 9
        }
256
257 45
        foreach ($countries as $country) {
0 ignored issues
show
Bug introduced by
The expression $countries of type string|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
258 42
            $instance = $this->lib->parse($this->number, $country);
259
260 42
            if ($this->lib->isValidNumber($instance)) {
261 37
                return $this->lib->getRegionCodeForNumber($instance);
262
            }
263 9
        }
264
265 27
        throw NumberParseException::countryRequired($this->number);
266
    }
267
268
    /**
269
     * Get the phone number's type.
270
     *
271
     * @param bool $asConstant
272
     * @return string|int|null
273
     */
274 15
    public function getType($asConstant = false)
275
    {
276 15
        $type = $this->lib->getNumberType($this->getPhoneNumberInstance());
277
278 15
        if ($asConstant) {
279 15
            return $type;
280
        }
281
282 3
        $stringType = Arr::get(static::parseTypesAsStrings($type), 0);
283
284 3
        return $stringType ? strtolower($stringType) : null;
285
    }
286
287
    /**
288
     * Check if the phone number is of (a) given type(s).
289
     *
290
     * @param string $type
291
     * @return bool
292
     */
293 12
    public function isOfType($type)
294
    {
295 12
        $types = static::parseTypes($type);
296
297 12
        return in_array($this->getType(true), $types, true);
298
    }
299
300
    /**
301
     * Get the PhoneNumber instance of the current number.
302
     *
303
     * @return \libphonenumber\PhoneNumber
304
     */
305 78
    public function getPhoneNumberInstance()
306
    {
307 78
        return $this->lib->parse($this->number, $this->getCountry());
308
    }
309
310
    /**
311
     * Determine whether the phone number seems to be in international format.
312
     *
313
     * @return bool
314
     */
315 45
    protected function numberLooksInternational()
316
    {
317 45
        return Str::startsWith($this->number, '+');
318
    }
319
320
    /**
321
     * Enable lenient number parsing.
322
     *
323
     * @return $this
324
     */
325 24
    public function lenient()
326
    {
327 24
        $this->lenient = true;
328
329 24
        return $this;
330
    }
331
332
    /**
333
     * Convert the phone instance to JSON.
334
     *
335
     * @param  int $options
336
     * @return string
337
     */
338 3
    public function toJson($options = 0)
339
    {
340 3
        return json_encode($this->jsonSerialize(), $options);
341
    }
342
343
    /**
344
     * Convert the phone instance into something JSON serializable.
345
     *
346
     * @return string
347
     */
348 3
    public function jsonSerialize()
349
    {
350 3
        return $this->formatE164();
351
    }
352
353
    /**
354
     * Convert the phone instance into a string representation.
355
     *
356
     * @return string
357
     */
358 3
    public function serialize()
359
    {
360 3
        return $this->formatE164();
361
    }
362
363
    /**
364
     * Reconstructs the phone instance from a string representation.
365
     *
366
     * @param string $serialized
367
     */
368 3
    public function unserialize($serialized)
369
    {
370 3
        $this->lib = PhoneNumberUtil::getInstance();
371 3
        $this->number = $serialized;
372 3
        $this->country = $this->lib->getRegionCodeForNumber($this->getPhoneNumberInstance());
373 3
    }
374
375
    /**
376
     * Convert the phone instance to a formatted number.
377
     *
378
     * @return string
379
     */
380 9
    public function __toString()
381
    {
382
        // Formatting the phone number could throw an exception, but __toString() doesn't cope well with that.
383
        // Let's just return the original number in that case.
384
        try {
385 9
            return $this->formatE164();
386 3
        } catch (Exception $exception) {
387 3
            return $this->number;
388
        }
389
    }
390
}