Completed
Push — master ( 198800...1a9f17 )
by Propa
07:51
created

Phone::extractParameters()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
nc 8
nop 3
dl 0
loc 30
ccs 14
cts 14
cp 1
crap 5
rs 9.1288
c 0
b 0
f 0
1
<?php namespace Propaganistas\LaravelPhone\Validation;
2
3
use Illuminate\Support\Arr;
4
use Illuminate\Support\Collection;
5
use libphonenumber\PhoneNumberUtil;
6
use Propaganistas\LaravelPhone\Exceptions\InvalidParameterException;
7
use libphonenumber\NumberParseException;
8
use Propaganistas\LaravelPhone\PhoneNumber;
9
use Propaganistas\LaravelPhone\Traits\ParsesCountries;
10
use Propaganistas\LaravelPhone\Traits\ParsesTypes;
11
12
class Phone
13
{
14
    use ParsesCountries,
15
        ParsesTypes;
16
17
    /**
18
     * @var \libphonenumber\PhoneNumberUtil
19
     */
20
    protected $lib;
21
22
    /**
23
     * Phone constructor.
24
     */
25 44
    public function __construct()
26
    {
27 44
        $this->lib = PhoneNumberUtil::getInstance();
28 44
    }
29
30
    /**
31
     * Validates a phone number.
32
     *
33
     * @param  string $attribute
34
     * @param  mixed  $value
35
     * @param  array  $parameters
36
     * @param  object $validator
37
     * @return bool
38
     */
39 44
    public function validate($attribute, $value, array $parameters, $validator)
40
    {
41 44
        $data = $validator->getData();
42
43
        list(
44
            $countries,
45
            $types,
46
            $detect,
47 44
            $lenient) = $this->extractParameters($attribute, $parameters, $data);
48
49
        // A "null" country is prepended:
50
        // 1. In case of auto-detection to have the validation run first without supplying a country.
51
        // 2. In case of lenient validation without provided countries; we still might have some luck...
52 41
        if ($detect || ($lenient && empty($countries))) {
53 12
            array_unshift($countries, null);
54
        }
55
56 41
        foreach ($countries as $country) {
57
            try {
58
                // Parsing the phone number also validates the country, so no need to do this explicitly.
59
                // It'll throw a PhoneCountryException upon failure.
60 35
                $phoneNumber = PhoneNumber::make($value, $country);
61
62
                // Type validation.
63 35
                if (! empty($types) && ! $phoneNumber->isOfType($types)) {
64 14
                    continue;
65
                }
66
67 32
                $lenientPhoneNumber = $phoneNumber->lenient()->getPhoneNumberInstance();
68
69
                // Lenient validation.
70 32
                if ($lenient && $this->lib->isPossibleNumber($lenientPhoneNumber, $country)) {
71 3
                    return true;
72
                }
73
74 32
                $phoneNumberInstance = $phoneNumber->getPhoneNumberInstance();
75
76
                // Country detection.
77 32
                if ($detect && $this->lib->isValidNumber($phoneNumberInstance)) {
78 9
                    return true;
79
                }
80
81
                // Default number+country validation.
82 32
                if ($this->lib->isValidNumberForRegion($phoneNumberInstance, $country)) {
83 32
                    return true;
84
                }
85 20
            } catch (NumberParseException $e) {
86 24
                continue;
87
            }
88
        }
89
90 38
        return false;
91
    }
92
93
    /**
94
     * Parse and extract parameters in the appropriate validation arguments.
95
     *
96
     * @param string $attribute
97
     * @param array  $parameters
98
     * @param array  $data
99
     * @return array
100
     * @throws \Propaganistas\LaravelPhone\Exceptions\InvalidParameterException
101
     */
102 44
    protected function extractParameters($attribute, array $parameters, array $data)
103
    {
104 44
        $parameters = array_map('strtolower', $parameters);
105
106
        // Discover if an input field was provided. If not, guess the field's name.
107 44
        $inputField = Collection::make($parameters)
108 44
                                ->intersect(array_keys(Arr::dot($data)))
109 44
                                ->first() ?: "${attribute}_country";
110
111
        // Attempt to retrieve the field's value.
112 30
        if ($inputCountry = Arr::get($data, $inputField)) {
113
114 18
            if (static::isValidType($inputField)) {
115 2
                throw InvalidParameterException::ambiguous($inputField);
116
            }
117
118
            // Invalid country field values should just validate to false.
119
            // This will also prevent parameter hijacking through the country field.
120 16
            if (static::isValidCountryCode($inputCountry)) {
121 12
                $parameters[] = $inputCountry;
122
            }
123
        }
124
125
        return [
126 28
            static::parseCountries($parameters),
127 28
            static::parseTypes($parameters),
128 28
            in_array('auto', $parameters),
129 28
            in_array('lenient', $parameters)
130
        ];
131
    }
132
}
133