Test Failed
Push — master ( b9b499...9e2edc )
by Adam
12:31
created

Phone::getControlPart()   C

Complexity

Conditions 9
Paths 9

Size

Total Lines 92
Code Lines 52

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 92
rs 5.1048
c 0
b 0
f 0
cc 9
eloc 52
nc 9
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Phone.php
4
 *
5
 * @copyright      More in license.md
6
 * @license        http://www.ipublikuj.eu
7
 * @author         Adam Kadlec <[email protected]>
8
 * @package        iPublikuj:FormPhone!
9
 * @subpackage     Controls
10
 * @since          1.0.0
11
 *
12
 * @date           15.12.15
13
 */
14
15
declare(strict_types = 1);
16
17
namespace IPub\FormPhone\Controls;
18
19
use Nette;
20
use Nette\Forms;
21
use Nette\Localization;
22
use Nette\Utils;
23
24
use IPub;
25
use IPub\FormPhone;
26
use IPub\FormPhone\Exceptions;
27
28
use IPub\Phone\Phone as PhoneUtils;
29
30
use libphonenumber;
31
32
/**
33
 * Form phone control element
34
 *
35
 * @package        iPublikuj:FormPhone!
36
 * @subpackage     Controls
37
 *
38
 * @author         Adam Kadlec <[email protected]>
39
 *
40
 * @property-read string $emptyValue
41
 * @property-read Nette\Forms\Rules $rules
42
 */
43
class Phone extends Forms\Controls\TextInput
44
{
45
	/**
46
	 * Define filed attributes
47
	 */
48
	const FIELD_COUNTRY = 'country';
49
	const FIELD_NUMBER = 'number';
50
51
	/**
52
	 * @var IPub\Phone\Phone
53
	 */
54
	private $phoneUtils;
55
56
	/**
57
	 * List of allowed countries
58
	 *
59
	 * @var array
60
	 */
61
	private $allowedCountries = [];
62
63
	/**
64
	 * List of allowed phone types
65
	 *
66
	 * @var array
67
	 */
68
	private $allowedTypes = [];
69
70
	/**
71
	 * @var string|NULL
72
	 */
73
	private $number = NULL;
74
75
	/**
76
	 * @var string|NULL
77
	 */
78
	private $country = NULL;
79
80
	/**
81
	 * @var string
82
	 */
83
	private $defaultCountry;
84
85
	/**
86
	 * @var bool
87
	 */
88
	private static $registered = FALSE;
89
90
	/**
91
	 * @param PhoneUtils $phoneUtils
92
	 * @param string|NULL $label
93
	 * @param int|NULL $maxLength
94
	 */
95
	public function __construct(PhoneUtils $phoneUtils, string $label = NULL, int $maxLength = NULL)
96
	{
97
		parent::__construct($label, $maxLength);
98
99
		$this->phoneUtils = $phoneUtils;
100
	}
101
102
	/**
103
	 * @param array $countries
104
	 *
105
	 * @return $this
106
	 *
107
	 * @throws Exceptions\NoValidCountryException
108
	 */
109
	public function setAllowedCountries(array $countries = [])
110
	{
111
		$this->allowedCountries = [];
112
113
		foreach ($countries as $country) {
114
			$country = $this->validateCountry($country);
115
			$this->allowedCountries[] = strtoupper($country);
116
		}
117
118
		// Check for auto country detection
119
		if (in_array('AUTO', $this->allowedCountries)) {
120
			$this->allowedCountries = ['AUTO'];
121
		}
122
123
		// Remove duplicities
124
		array_unique($this->allowedCountries);
125
126
		return $this;
127
	}
128
129
	/**
130
	 * @param string $country
131
	 *
132
	 * @return $this
133
	 *
134
	 * @throws Exceptions\NoValidCountryException
135
	 */
136
	public function addAllowedCountry(string $country)
137
	{
138
		$country = $this->validateCountry($country);
139
		$this->allowedCountries[] = strtoupper($country);
140
141
		// Remove duplicities
142
		array_unique($this->allowedCountries);
143
144
		if (strtoupper($country) === 'AUTO') {
145
			$this->allowedCountries = ['AUTO'];
146
147
		} elseif (($key = array_search('AUTO', $this->allowedCountries)) && $key !== FALSE) {
148
			unset($this->allowedCountries[$key]);
149
		}
150
151
		return $this;
152
	}
153
154
	/**
155
	 * @return array
156
	 */
157
	public function getAllowedCountries() : array
158
	{
159
		if (in_array('AUTO', $this->allowedCountries, TRUE) || $this->allowedCountries === []) {
160
			return $this->phoneUtils->getSupportedCountries();
161
		}
162
163
		return $this->allowedCountries;
164
	}
165
166
	/**
167
	 * @param string|NULL $country
168
	 *
169
	 * @return $this
170
	 *
171
	 * @throws Exceptions\NoValidCountryException
172
	 */
173
	public function setDefaultCountry(string $country = NULL)
174
	{
175
		if ($country === NULL) {
176
			$this->defaultCountry = NULL;
177
178
		} else {
179
			$country = $this->validateCountry($country);
180
181
			$this->defaultCountry = strtoupper($country);
182
		}
183
184
		return $this;
185
	}
186
187
	/**
188
	 * @param array $types
189
	 *
190
	 * @return $this
191
	 *
192
	 * @throws Exceptions\NoValidTypeException
193
	 */
194
	public function setAllowedPhoneTypes(array $types = [])
195
	{
196
		$this->allowedTypes = [];
197
198
		foreach ($types as $type) {
199
			$type = $this->validateType($type);
200
			$this->allowedTypes[] = strtoupper($type);
201
		}
202
203
		// Remove duplicities
204
		array_unique($this->allowedTypes);
205
206
		return $this;
207
	}
208
209
	/**
210
	 * @param string $type
211
	 *
212
	 * @return $this
213
	 *
214
	 * @throws Exceptions\NoValidTypeException
215
	 */
216
	public function addAllowedPhoneType(string $type)
217
	{
218
		$type = $this->validateType($type);
219
		$this->allowedTypes[] = strtoupper($type);
220
221
		// Remove duplicities
222
		array_unique($this->allowedTypes);
223
224
		return $this;
225
	}
226
227
	/**
228
	 * @return array
229
	 */
230
	public function getAllowedPhoneTypes() : array
231
	{
232
		return $this->allowedTypes;
233
	}
234
235
	/**
236
	 * @param string
237
	 *
238
	 * @return $this
239
	 *
240
	 * @throws Exceptions\InvalidArgumentException
241
	 * @throws IPub\Phone\Exceptions\NoValidCountryException
242
	 * @throws IPub\Phone\Exceptions\NoValidPhoneException
243
	 */
244
	public function setValue($value)
245
	{
246
		if ($value === NULL) {
247
			$this->country = NULL;
248
			$this->number = NULL;
249
250
			return $this;
251
		}
252
253
		if ($value instanceof IPub\Phone\Entities\Phone) {
254
			if (in_array($value->getCountry(), $this->getAllowedCountries(), TRUE)) {
255
				$this->country = $value->getCountry();
256
				$this->number = str_replace(' ', '', $value->getNationalNumber());
257
258
				return $this;
259
			}
260
261
		} else {
262
			foreach ($this->getAllowedCountries() as $country) {
263
				if ($this->phoneUtils->isValid((string) $value, $country)) {
264
					$phone = IPub\Phone\Entities\Phone::fromNumber((string) $value, $country);
265
266
					$this->country = $phone->getCountry();
267
					$this->number = str_replace(' ', '', $phone->getNationalNumber());
268
269
					return $this;
270
				}
271
			}
272
		}
273
274
		throw new Exceptions\InvalidArgumentException(sprintf('Provided value is not valid phone number, or is out of list of allowed countries, "%s" given.', $value));
275
	}
276
277
	/**
278
	 * @param string $key
279
	 *
280
	 * @return string
281
	 *
282
	 * @throws Exceptions\InvalidArgumentException
283
	 */
284
	public function getValuePart(string $key) : string
285
	{
286
		if ($key === self::FIELD_COUNTRY) {
287
			return $this->country;
288
289
		} elseif ($key === self::FIELD_NUMBER) {
290
			return $this->number;
291
		}
292
293
		throw new Exceptions\InvalidArgumentException('Invalid field key provided.');
294
	}
295
296
	/**
297
	 * @return IPub\Phone\Entities\Phone|NULL|FALSE
298
	 */
299
	public function getValue()
300
	{
301
		if ($this->country === NULL || $this->number === NULL) {
302
			return NULL;
303
		}
304
305
		try {
306
			// Try to parse number & country
307
			return IPub\Phone\Entities\Phone::fromNumber($this->number, $this->country);
308
309
		} catch (IPub\Phone\Exceptions\NoValidCountryException $ex) {
310
			return FALSE;
311
312
		} catch (IPub\Phone\Exceptions\NoValidPhoneException $ex) {
313
			return FALSE;
314
		}
315
	}
316
317
	/**
318
	 * Loads HTTP data
319
	 *
320
	 * @return void
321
	 */
322
	public function loadHttpData()
323
	{
324
		$country = $this->getHttpData(Forms\Form::DATA_LINE, '[' . self::FIELD_COUNTRY . ']');
325
		$this->country = ($country === '' || $country === NULL) ? NULL : (string) $country;
326
327
		$number = $this->getHttpData(Forms\Form::DATA_LINE, '[' . self::FIELD_NUMBER . ']');
328
		$this->number = ($number === '' || $number === NULL) ? NULL : (string) $number;
329
	}
330
331
	/**
332
	 * @return Utils\Html
333
	 */
334
	public function getControl()
335
	{
336
		$el = Utils\Html::el();
337
		$el->addHtml($this->getControlPart(self::FIELD_COUNTRY) . $this->getControlPart(self::FIELD_NUMBER));
338
339
		return $el;
340
	}
341
342
	/**
343
	 * @return Utils\Html
344
	 *
345
	 * @throws Exceptions\InvalidArgumentException
346
	 */
347
	public function getControlPart()
348
	{
349
		$args = func_get_args();
350
		$key = reset($args);
351
352
		$name = $this->getHtmlName();
353
354
		if ($key === self::FIELD_COUNTRY) {
355
			// Try to get translator
356
			$translator = $this->getTranslator();
357
358
			$locale = 'en_US';
359
360
			if ($translator instanceof Localization\ITranslator && method_exists($translator, 'getLocale') === TRUE) {
361
				try {
362
					$locale = $translator->getLocale();
0 ignored issues
show
Bug introduced by
The method getLocale() does not seem to exist on object<Nette\Localization\ITranslator>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
363
364
				} catch (\Exception $ex) {
365
					$locale = 'en_US';
366
				}
367
			}
368
369
			$items = array_reduce($this->getAllowedCountries(), function (array $result, $row) use ($locale) {
370
				$countryName = FormPhone\Locale\Locale::getDisplayRegion(
371
					FormPhone\Locale\Locale::countryCodeToLocale($row),
372
					$locale
373
				);
374
375
				$result[$row] = Utils\Html::el('option');
376
				$result[$row]->setText('+' . $this->phoneUtils->getCountryCodeForCountry($row) . ' (' . $countryName . ')');
377
				$result[$row]->data('mask', preg_replace('/[0-9]/', '9', $this->phoneUtils->getExampleNationalNumber($row)));
378
				$result[$row]->addAttributes([
379
					'value' => $row,
380
				]);
381
382
				return $result;
383
			}, []);
384
385
			$control = Forms\Helpers::createSelectBox(
386
				$items,
387
				[
388
					'selected?' => $this->country === NULL ? $this->defaultCountry : $this->country,
389
				]
390
			);
391
392
			$control->addAttributes([
393
				'name' => $name . '[' . self::FIELD_COUNTRY . ']',
394
				'id'   => $this->getHtmlId() . '-' . self::FIELD_COUNTRY,
395
			]);
396
			$control->data('ipub-forms-phone', '');
397
			$control->data('settings', json_encode([
398
					'field' => $name . '[' . self::FIELD_NUMBER . ']'
399
				])
400
			);
401
402
			if ($this->isDisabled()) {
403
				$control->addAttributes([
404
					'disabled' => TRUE,
405
				]);
406
			}
407
408
			return $control;
409
410
		} elseif ($key === self::FIELD_NUMBER) {
411
			$prototype = $this->getControlPrototype();
412
413
			$input = parent::getControl();
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (getControl() instead of getControlPart()). Are you sure this is correct? If so, you might want to change this to $this->getControl().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
414
415
			$control = Utils\Html::el('input');
416
417
			$control->addAttributes([
418
				'name'  => $name . '[' . self::FIELD_NUMBER . ']',
419
				'id'    => $this->getHtmlId() . '-' . self::FIELD_NUMBER,
420
				'value' => $this->number,
421
				'type'  => 'text',
422
				'class' => $prototype->getAttribute('class'),
423
			]);
424
425
			$control->data('nette-empty-value', Utils\Strings::trim($this->translate($this->emptyValue)));
426
			$control->data('nette-rules', $input->{'data-nette-rules'});
427
428
			if ($this->isDisabled()) {
429
				$control->addAttributes([
430
					'disabled' => TRUE,
431
				]);
432
			}
433
434
			return $control;
435
		}
436
437
		throw new Exceptions\InvalidArgumentException(sprintf('Part "%s" does not exist.', $key));
438
	}
439
440
	/**
441
	 * {@inheritdoc}
442
	 */
443
	public function getLabel($caption = NULL)
444
	{
445
		$label = parent::getLabel($caption);
446
		$label->for = $this->getHtmlId() . '-' . self::FIELD_NUMBER;
447
448
		return $label;
449
	}
450
451
	/**
452
	 * {@inheritdoc}
453
	 */
454
	public function getLabelPart()
455
	{
456
		return NULL;
457
	}
458
459
	/**
460
	 * @param string $country
461
	 *
462
	 * @return string
463
	 *
464
	 * @throws Exceptions\NoValidCountryException
465
	 */
466
	private function validateCountry(string $country) : string
467
	{
468
		// Country code have to be upper-cased
469
		$country = strtoupper($country);
470
471
		if ((strlen($country) === 2 && ctype_alpha($country) && ctype_upper($country) && in_array($country, $this->phoneUtils->getSupportedCountries())) || $country === 'AUTO') {
472
			return $country;
473
474
		} else {
475
			throw new Exceptions\NoValidCountryException(sprintf('Provided country code "%s" is not valid. Provide valid country code or AUTO for automatic detection.', $country));
476
		}
477
	}
478
479
	/**
480
	 * @param string $type
481
	 *
482
	 * @return string
483
	 *
484
	 * @throws Exceptions\NoValidTypeException
485
	 */
486
	private function validateType(string $type) : string
487
	{
488
		// Phone type have to be upper-cased
489
		$type = strtoupper($type);
490
491
		if (defined('\IPub\Phone\Phone::TYPE_' . $type)) {
492
			return $type;
493
494
		} else {
495
			throw new Exceptions\NoValidTypeException(sprintf('Provided phone type "%s" is not valid. Provide valid phone type.', $type));
496
		}
497
	}
498
499
	/**
500
	 * @param PhoneUtils $phoneUtils
501
	 * @param string $method
502
	 */
503 View Code Duplication
	public static function register(PhoneUtils $phoneUtils, string $method = 'addPhone')
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...
504
	{
505
		// Check for multiple registration
506
		if (self::$registered) {
507
			throw new Nette\InvalidStateException('Phone control already registered.');
508
		}
509
510
		self::$registered = TRUE;
511
512
		$class = function_exists('get_called_class') ? get_called_class() : __CLASS__;
513
		Forms\Container::extensionMethod(
514
			$method, function (Forms\Container $form, $name, $label = NULL, $maxLength = NULL) use ($class, $phoneUtils) {
515
			$component = new $class($phoneUtils, $label, $maxLength);
516
			$form->addComponent($component, $name);
517
518
			return $component;
519
		}
520
		);
521
	}
522
}
523