Completed
Push — upstream-8.3.0 ( 6d22b3...36447c )
by Joshua
29:18 queued 18:23
created

getDescriptionForValidNumber()   C

Complexity

Conditions 7
Paths 7

Size

Total Lines 36
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 7.0071

Importance

Changes 0
Metric Value
dl 0
loc 36
ccs 18
cts 19
cp 0.9474
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 20
nc 7
nop 3
crap 7.0071
1
<?php
2
3
namespace libphonenumber\geocoding;
4
5
use Giggsey\Locale\Locale;
6
use libphonenumber\NumberParseException;
7
use libphonenumber\PhoneNumber;
8
use libphonenumber\PhoneNumberType;
9
use libphonenumber\PhoneNumberUtil;
10
use libphonenumber\prefixmapper\PrefixFileReader;
11
12
class PhoneNumberOfflineGeocoder
13
{
14
    const MAPPING_DATA_DIRECTORY = '/data';
15
    /**
16
     * @var PhoneNumberOfflineGeocoder
17
     */
18
    protected static $instance;
19
    /**
20
     * @var PhoneNumberUtil
21
     */
22
    protected $phoneUtil;
23
    /**
24
     * @var PrefixFileReader
25
     */
26
    protected $prefixFileReader = null;
27
28
    /**
29
     * PhoneNumberOfflineGeocoder constructor.
30
     * @param string $phonePrefixDataDirectory
31
     */
32 246
    protected function __construct($phonePrefixDataDirectory)
33
    {
34 246
        $this->phoneUtil = PhoneNumberUtil::getInstance();
35
36 246
        $this->prefixFileReader = new PrefixFileReader(__DIR__ . DIRECTORY_SEPARATOR . $phonePrefixDataDirectory);
37 246
    }
38
39
    /**
40
     * Gets a PhoneNumberOfflineGeocoder instance to carry out international phone number geocoding.
41
     *
42
     * <p>The PhoneNumberOfflineGeocoder is implemented as a singleton. Therefore, calling this method
43
     * multiple times will only result in one instance being created.
44
     *
45
     * @param string $mappingDir (Optional) Mapping Data Directory
46
     * @return PhoneNumberOfflineGeocoder
47
     */
48 251
    public static function getInstance($mappingDir = self::MAPPING_DATA_DIRECTORY)
49
    {
50 251
        if (static::$instance === null) {
51 246
            static::$instance = new static($mappingDir);
52 246
        }
53
54 251
        return static::$instance;
55
    }
56
57 246
    public static function resetInstance()
58
    {
59 246
        static::$instance = null;
60 246
    }
61
62
    /**
63
     * As per getDescriptionForValidNumber, but explicitly checks the validity of the number
64
     * passed in.
65
     *
66
     *
67
     * @see getDescriptionForValidNumber
68
     * @param PhoneNumber $number a valid phone number for which we want to get a text description
69
     * @param string $locale the language code for which the description should be written
70
     * @param string $userRegion the region code for a given user. This region will be omitted from the
71
     *     description if the phone number comes from this region. It is a two-letter uppercase ISO
72
     *     country code as defined by ISO 3166-1.
73
     * @return string a text description for the given language code for the given phone number, or empty
74
     *     string if the number passed in is invalid
75
     */
76 17
    public function getDescriptionForNumber(PhoneNumber $number, $locale, $userRegion = null)
77
    {
78 17
        $numberType = $this->phoneUtil->getNumberType($number);
79
80 17
        if ($numberType === PhoneNumberType::UNKNOWN) {
81 3
            return "";
82 16
        } elseif (!$this->phoneUtil->isNumberGeographical($numberType, $number->getCountryCode())) {
83 5
            return $this->getCountryNameForNumber($number, $locale);
84
        }
85
86 12
        return $this->getDescriptionForValidNumber($number, $locale, $userRegion);
87
    }
88
89
    /**
90
     * Returns the customary display name in the given language for the given territory the phone
91
     * number is from. If it could be from many territories, nothing is returned.
92
     *
93
     * @param PhoneNumber $number
94
     * @param string $locale
95
     * @return string
96
     */
97 9
    protected function getCountryNameForNumber(PhoneNumber $number, $locale)
98
    {
99 9
        $regionCodes = $this->phoneUtil->getRegionCodesForCountryCode($number->getCountryCode());
100
101 9
        if (count($regionCodes) === 1) {
102 5
            return $this->getRegionDisplayName($regionCodes[0], $locale);
103
        } else {
104 5
            $regionWhereNumberIsValid = 'ZZ';
105 5
            foreach ($regionCodes as $regionCode) {
106 5
                if ($this->phoneUtil->isValidNumberForRegion($number, $regionCode)) {
107 5
                    if ($regionWhereNumberIsValid !== 'ZZ') {
108
                        // If we can't assign the phone number as definitely belonging to only one territory,
109
                        // then we return nothing.
110 1
                        return "";
111
                    }
112 5
                    $regionWhereNumberIsValid = $regionCode;
113 5
                }
114 5
            }
115
116 4
            return $this->getRegionDisplayName($regionWhereNumberIsValid, $locale);
117
        }
118
    }
119
120
    /**
121
     * Returns the customary display name in the given language for the given region.
122
     *
123
     * @param $regionCode
124
     * @param $locale
125
     * @return string
126
     */
127 242
    protected function getRegionDisplayName($regionCode, $locale)
128
    {
129 242
        if ($regionCode === null || $regionCode == 'ZZ' || $regionCode === PhoneNumberUtil::REGION_CODE_FOR_NON_GEO_ENTITY) {
130 1
            return "";
131
        }
132
133 242
        return Locale::getDisplayRegion(
134 242
            '-' . $regionCode,
135
            $locale
136 242
        );
137
    }
138
139
    /**
140
     * Returns a text description for the given phone number, in the language provided. The
141
     * description might consist of the name of the country where the phone number is from, or the
142
     * name of the geographical area the phone number is from if more detailed information is
143
     * available.
144
     *
145
     * <p>This method assumes the validity of the number passed in has already been checked, and that
146
     * the number is suitable for geocoding. We consider fixed-line and mobile numbers possible
147
     * candidates for geocoding.
148
     *
149
     * <p>If $userRegion is set, we also consider the region of the user. If the phone number is from
150
     * the same region as the user, only a lower-level description will be returned, if one exists.
151
     * Otherwise, the phone number's region will be returned, with optionally some more detailed
152
     * information.
153
     *
154
     * <p>For example, for a user from the region "US" (United States), we would show "Mountain View,
155
     * CA" for a particular number, omitting the United States from the description. For a user from
156
     * the United Kingdom (region "GB"), for the same number we may show "Mountain View, CA, United
157
     * States" or even just "United States".
158
     *
159
     * @param PhoneNumber $number a valid phone number for which we want to get a text description
160
     * @param string $locale the language code for which the description should be written
161
     * @param string $userRegion the region code for a given user. This region will be omitted from the
162
     *     description if the phone number comes from this region. It is a two-letter uppercase ISO
163
     *     country code as defined by ISO 3166-1.
164
     * @return string a text description for the given language code for the given phone number
165
     */
166 245
    public function getDescriptionForValidNumber(PhoneNumber $number, $locale, $userRegion = null)
167
    {
168
        // If the user region matches the number's region, then we just show the lower-level
169
        // description, if one exists - if no description exists, we will show the region(country) name
170
        // for the number.
171 245
        $regionCode = $this->phoneUtil->getRegionCodeForNumber($number);
172 245
        if ($userRegion == null || $userRegion == $regionCode) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $userRegion of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
173 12
            $languageStr = Locale::getPrimaryLanguage($locale);
174 12
            $scriptStr = "";
175 12
            $regionStr = Locale::getRegion($locale);
176
177 12
            $mobileToken = PhoneNumberUtil::getCountryMobileToken($number->getCountryCode());
178 12
            $nationalNumber = $this->phoneUtil->getNationalSignificantNumber($number);
179 12
            if ($mobileToken !== "" && (!strncmp($nationalNumber, $mobileToken, strlen($mobileToken)))) {
180
                // In some countries, eg. Argentina, mobile numbers have a mobile token before the national
181
                // destination code, this should be removed before geocoding.
182 1
                $nationalNumber = substr($nationalNumber, strlen($mobileToken));
183 1
                $region = $this->phoneUtil->getRegionCodeForCountryCode($number->getCountryCode());
184
                try {
185 1
                    $copiedNumber = $this->phoneUtil->parse($nationalNumber, $region);
186 1
                } catch (NumberParseException $e) {
187
                    // If this happens, just reuse what we had.
188
                    $copiedNumber = $number;
189
                }
190 1
                $areaDescription = $this->prefixFileReader->getDescriptionForNumber($copiedNumber, $languageStr, $scriptStr, $regionStr);
191 1
            } else {
192 11
                $areaDescription = $this->prefixFileReader->getDescriptionForNumber($number, $languageStr, $scriptStr, $regionStr);
193
            }
194
195 12
            return (strlen($areaDescription) > 0) ? $areaDescription : $this->getCountryNameForNumber($number, $locale);
196
        }
197
        // Otherwise, we just show the region(country) name for now.
198 234
        return $this->getRegionDisplayName($regionCode, $locale);
199
        // TODO: Concatenate the lower-level and country-name information in an appropriate
200
        // way for each language.
201
    }
202
}
203