Issues (27)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/PhoneNumber.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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 Illuminate\Support\Traits\Macroable;
9
use JsonSerializable;
10
use libphonenumber\NumberParseException as libNumberParseException;
11
use libphonenumber\PhoneNumberFormat;
12
use libphonenumber\PhoneNumberType;
13
use libphonenumber\PhoneNumberUtil;
14
use Propaganistas\LaravelPhone\Exceptions\NumberFormatException;
15
use Propaganistas\LaravelPhone\Exceptions\CountryCodeException;
16
use Propaganistas\LaravelPhone\Exceptions\NumberParseException;
17
use Propaganistas\LaravelPhone\Traits\ParsesCountries;
18
use Propaganistas\LaravelPhone\Traits\ParsesFormats;
19
use Propaganistas\LaravelPhone\Traits\ParsesTypes;
20
use Serializable;
21
22
class PhoneNumber implements Jsonable, JsonSerializable, Serializable
23
{
24
    use Macroable,
25
        ParsesCountries,
26
        ParsesFormats,
27
        ParsesTypes;
28
29
    /**
30
     * The provided phone number.
31
     *
32
     * @var string
33
     */
34
    protected $number;
35
36
    /**
37
     * The provided phone country.
38
     *
39
     * @var array
40
     */
41
    protected $countries = [];
42
43
    /**
44
     * The detected phone country.
45
     *
46
     * @var string
47
     */
48
    protected $country;
49
50
    /**
51
     * Whether to allow lenient checks (i.e. landline numbers without area codes).
52
     *
53
     * @var bool
54
     */
55
    protected $lenient = false;
56
57
    /**
58
     * @var \libphonenumber\PhoneNumberUtil
59
     */
60
    protected $lib;
61
62
    /**
63
     * Phone constructor.
64
     *
65
     * @param string $number
66
     */
67 135
    public function __construct($number)
68
    {
69 135
        $this->number = $number;
70 135
        $this->lib = PhoneNumberUtil::getInstance();
71 135
    }
72
73
    /**
74
     * Create a phone instance.
75
     *
76
     * @param string       $number
77
     * @param string|array $country
78
     * @return static
79
     */
80 48
    public static function make($number, $country = [])
81
    {
82 48
        $instance = new static($number);
83
84 48
        return $instance->ofCountry($country);
85
    }
86
87
    /**
88
     * Set the country to which the phone number belongs to.
89
     *
90
     * @param string|array $country
91
     * @return static
92
     */
93 114
    public function ofCountry($country)
94
    {
95 114
        $countries = is_array($country) ? $country : func_get_args();
96
97 114
        $instance = clone $this;
98 114
        $instance->countries = array_unique(
99 114
            array_merge($instance->countries, static::parseCountries($countries))
100
        );
101
102 114
        return $instance;
103
    }
104
105
    /**
106
     * Format the phone number in international format.
107
     *
108
     * @return string
109
     */
110 3
    public function formatInternational()
111
    {
112 3
        return $this->format(PhoneNumberFormat::INTERNATIONAL);
113
    }
114
115
    /**
116
     * Format the phone number in national format.
117
     *
118
     * @return string
119
     */
120 3
    public function formatNational()
121
    {
122 3
        return $this->format(PhoneNumberFormat::NATIONAL);
123
    }
124
125
    /**
126
     * Format the phone number in E164 format.
127
     *
128
     * @return string
129
     */
130 27
    public function formatE164()
131
    {
132 27
        return $this->format(PhoneNumberFormat::E164);
133
    }
134
135
    /**
136
     * Format the phone number in RFC3966 format.
137
     *
138
     * @return string
139
     */
140 12
    public function formatRFC3966()
141
    {
142 12
        return $this->format(PhoneNumberFormat::RFC3966);
143
    }
144
145
    /**
146
     * Format the phone number in a given format.
147
     *
148
     * @param string $format
149
     * @return string
150
     * @throws \Propaganistas\LaravelPhone\Exceptions\NumberFormatException
151
     */
152 60
    public function format($format)
153
    {
154 60
        $parsedFormat = static::parseFormat($format);
155
156 60
        if (is_null($parsedFormat)) {
157 3
            throw NumberFormatException::invalid($format);
158
        }
159
160 57
        return $this->lib->format(
161 57
            $this->getPhoneNumberInstance(),
162
            $parsedFormat
163
        );
164
    }
165
166
    /**
167
     * Format the phone number in a way that it can be dialled from the provided country.
168
     *
169
     * @param string $country
170
     * @return string
171
     * @throws \Propaganistas\LaravelPhone\Exceptions\CountryCodeException
172
     */
173 6 View Code Duplication
    public function formatForCountry($country)
174
    {
175 6
        if (! static::isValidCountryCode($country)) {
176 3
            throw CountryCodeException::invalid($country);
177
        }
178
179 3
        return $this->lib->formatOutOfCountryCallingNumber(
180 3
            $this->getPhoneNumberInstance(),
181
            $country
182
        );
183
    }
184
185
    /**
186
     * Format the phone number in a way that it can be dialled from the provided country using a cellphone.
187
     *
188
     * @param string $country
189
     * @param bool   $removeFormatting
190
     * @return string
191
     * @throws \Propaganistas\LaravelPhone\Exceptions\CountryCodeException
192
     */
193 6 View Code Duplication
    public function formatForMobileDialingInCountry($country, $removeFormatting = false)
194
    {
195 6
        if (! static::isValidCountryCode($country)) {
196 3
            throw CountryCodeException::invalid($country);
197
        }
198
199 3
        return $this->lib->formatNumberForMobileDialing(
200 3
            $this->getPhoneNumberInstance(),
201
            $country,
202
            $removeFormatting
203
        );
204
    }
205
206
    /**
207
     * Get the phone number's country.
208
     *
209
     * @return string
210
     */
211 126
    public function getCountry()
212
    {
213 126
        if (! $this->country) {
214 126
            $this->country = $this->filterValidCountry($this->countries);
215
        }
216
217 105
        return $this->country;
218
    }
219
220
    /**
221
     * Check if the phone number is of (a) given country(ies).
222
     *
223
     * @param string|array $country
224
     * @return bool
225
     */
226 3
    public function isOfCountry($country)
227
    {
228 3
        $countries = static::parseCountries($country);
229
230 3
        return in_array($this->getCountry(), $countries);
231
    }
232
233
    /**
234
     * Filter the provided countries to the one that is valid for the number.
235
     *
236
     * @param string|array $countries
237
     * @return string
238
     * @throws \Propaganistas\LaravelPhone\Exceptions\NumberParseException
239
     */
240 126
    protected function filterValidCountry($countries)
241
    {
242 126
        $result = Collection::make($countries)
243
            ->filter(function ($country) {
244
                try {
245 111
                    $instance = $this->lib->parse($this->number, $country);
246
247 111
                    return $this->lenient
248 18
                        ? $this->lib->isPossibleNumber($instance, $country)
249 111
                        : $this->lib->isValidNumberForRegion($instance, $country);
250 3
                } catch (libNumberParseException $e) {
251 3
                    return false;
252
                }
253 126
            })->first();
254
255
        // If we got a new result, return it.
256 126
        if ($result) {
257 102
            return $result;
258
        }
259
260
        // Last resort: try to detect it from an international number.
261 66
        if ($this->numberLooksInternational()) {
262 33
            $countries[] = null;
263
        }
264
265 66
        foreach ($countries as $country) {
0 ignored issues
show
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...
266 51
            $instance = $this->lib->parse($this->number, $country);
267
268 51
            if ($this->lib->isValidNumber($instance)) {
269 30
                return $this->lib->getRegionCodeForNumber($instance);
270
            }
271
        }
272
273 48
        if ($countries = array_filter($countries)) {
274 24
            throw NumberParseException::countryMismatch($this->number, $countries);
275
        }
276
277 30
        throw NumberParseException::countryRequired($this->number);
278
    }
279
280
    /**
281
     * Get the phone number's type.
282
     *
283
     * @param bool $asConstant
284
     * @return string|int|null
285
     */
286 33
    public function getType($asConstant = false)
287
    {
288 33
        $type = $this->lib->getNumberType($this->getPhoneNumberInstance());
289
290 33
        if ($asConstant) {
291 33
            return $type;
292
        }
293
294 3
        $stringType = Arr::get(static::parseTypesAsStrings($type), 0);
295
296 3
        return $stringType ? strtolower($stringType) : null;
297
    }
298
299
    /**
300
     * Check if the phone number is of (a) given type(s).
301
     *
302
     * @param string $type
303
     * @return bool
304
     */
305 30
    public function isOfType($type)
306
    {
307 30
        $types = static::parseTypes($type);
308
309
        // Add the unsure type when applicable.
310 30
        if (array_intersect([PhoneNumberType::FIXED_LINE, PhoneNumberType::MOBILE], $types)) {
311 30
            $types[] = PhoneNumberType::FIXED_LINE_OR_MOBILE;
312
        }
313
314 30
        return in_array($this->getType(true), $types, true);
315
    }
316
317
    /**
318
     * Get the PhoneNumber instance of the current number.
319
     *
320
     * @return \libphonenumber\PhoneNumber
321
     */
322 111
    public function getPhoneNumberInstance()
323
    {
324 111
        return $this->lib->parse($this->number, $this->getCountry());
325
    }
326
327
    /**
328
     * Determine whether the phone number seems to be in international format.
329
     *
330
     * @return bool
331
     */
332 66
    protected function numberLooksInternational()
333
    {
334 66
        return Str::startsWith($this->number, '+');
335
    }
336
337
    /**
338
     * Enable lenient number parsing.
339
     *
340
     * @return $this
341
     */
342 39
    public function lenient()
343
    {
344 39
        $this->lenient = true;
345
346 39
        return $this;
347
    }
348
349
    /**
350
     * Convert the phone instance to JSON.
351
     *
352
     * @param  int $options
353
     * @return string
354
     */
355 3
    public function toJson($options = 0)
356
    {
357 3
        return json_encode($this->jsonSerialize(), $options);
358
    }
359
360
    /**
361
     * Convert the phone instance into something JSON serializable.
362
     *
363
     * @return string
364
     */
365 3
    public function jsonSerialize()
366
    {
367 3
        return $this->formatE164();
368
    }
369
370
    /**
371
     * Convert the phone instance into a string representation.
372
     *
373
     * @return string
374
     */
375 3
    public function serialize()
376
    {
377 3
        return $this->formatE164();
378
    }
379
380
    /**
381
     * Reconstructs the phone instance from a string representation.
382
     *
383
     * @param string $serialized
384
     */
385 3
    public function unserialize($serialized)
386
    {
387 3
        $this->lib = PhoneNumberUtil::getInstance();
388 3
        $this->number = $serialized;
389 3
        $this->country = $this->lib->getRegionCodeForNumber($this->getPhoneNumberInstance());
390 3
    }
391
392
    /**
393
     * Convert the phone instance to a formatted number.
394
     *
395
     * @return string
396
     */
397 21
    public function __toString()
398
    {
399
        // Formatting the phone number could throw an exception, but __toString() doesn't cope well with that.
400
        // Let's just return the original number in that case.
401
        try {
402 21
            return $this->formatE164();
403 12
        } catch (Exception $exception) {
404 12
            return (string) $this->number;
405
        }
406
    }
407
}
408