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

Phone::extractParameters()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 31
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 17
nc 4
nop 3
dl 0
loc 31
ccs 4
cts 4
cp 1
crap 5
rs 8.439
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 Propaganistas\LaravelPhone\Exceptions\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 27
    public function __construct()
26
    {
27 27
        $this->lib = PhoneNumberUtil::getInstance();
28 27
    }
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 27
    public function validate($attribute, $value, array $parameters, $validator)
40
    {
41 27
        $data = $validator->getData();
42
43
        list(
44
            $countries,
45
            $types,
46
            $detect,
47 27
            $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 24
        if ($detect || ($lenient && empty($countries))) {
53 6
            array_unshift($countries, null);
54 2
        }
55
56 24
        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 21
                $phoneNumber = PhoneNumber::make($value, $country);
61
62
                // Type validation.
63 21
                if (! empty($types) && ! $phoneNumber->isOfType($types)) {
64 9
                    continue;
65
                }
66
67 21
                $lenientPhoneNumber = $phoneNumber->lenient()->getPhoneNumberInstance();
68
69
                // Lenient validation.
70 21
                if ($lenient && $this->lib->isPossibleNumber($lenientPhoneNumber, $country)) {
71 3
                    return true;
72
                }
73
74 21
                $phoneNumberInstance = $phoneNumber->getPhoneNumberInstance();
75
76
                // Country detection.
77 21
                if ($detect && $this->lib->isValidNumber($phoneNumberInstance)) {
78 3
                    return true;
79
                }
80
81
                // Default number+country validation.
82 21
                if ($this->lib->isValidNumberForRegion($phoneNumberInstance, $country)) {
83 19
                    return true;
84
                }
85 17
            } catch (NumberParseException $e) {
86 19
                continue;
87
            }
88 8
        }
89
90 24
        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 27
    protected function extractParameters($attribute, array $parameters, array $data)
103
    {
104
        // Discover if an input field was provided. If not, guess the field's name.
105 27
        $inputField = Collection::make($parameters)
106 27
                                ->intersect(array_keys(Arr::dot($data)))
107 27
                                ->first() ?: "${attribute}_country";
108
109
        // Attempt to retrieve the field's value. If no field is present, this will be null.
110
        $inputCountry = Arr::get($data, $inputField);
111
112
        $countries = static::parseCountries($inputCountry ? [$inputCountry] : $parameters);
113
        $types = static::parseTypes($parameters);
114
115
        // Force developers to write proper code.
116
        // Since the static parsers return a validated array with preserved keys, we can safely diff against the keys.
117
        // Unfortunately we can't use $collection->diffKeys() as it's not available yet in earlier 5.* versions.
118
        $leftovers = array_diff_key($parameters, $types, $inputCountry ? [] : $countries);
119
        $leftovers = array_diff($leftovers, ['AUTO', 'LENIENT', $inputField]);
120
121
        if (! empty($leftovers)) {
122
            throw InvalidParameterException::parameters($leftovers);
123
        }
124
125
        return [
126
            $countries,
127
            $types,
128
            in_array('AUTO', $parameters),
129
            in_array('LENIENT', $parameters),
130
            $inputField,
131
        ];
132
    }
133
}