Completed
Push — master ( 95debf...39f9f3 )
by Adam
08:59 queued 11s
created

Phone::getControlPart()   C

Complexity

Conditions 9
Paths 9

Size

Total Lines 93

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 42
CRAP Score 9.3317

Importance

Changes 0
Metric Value
dl 0
loc 93
ccs 42
cts 50
cp 0.84
rs 6.5971
c 0
b 0
f 0
cc 9
nc 9
nop 0
crap 9.3317

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 1
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 1
		parent::__construct($label, $maxLength);
98
99 1
		$this->phoneUtils = $phoneUtils;
100 1
	}
101
102
	/**
103
	 * @param array $countries
104
	 *
105
	 * @return $this
106
	 *
107
	 * @throws Exceptions\NoValidCountryException
108
	 */
109
	public function setAllowedCountries(array $countries = [])
110
	{
111 1
		$this->allowedCountries = [];
112
113 1
		foreach ($countries as $country) {
114 1
			$country = $this->validateCountry($country);
115 1
			$this->allowedCountries[] = strtoupper($country);
116
		}
117
118
		// Check for auto country detection
119 1
		if (in_array('AUTO', $this->allowedCountries)) {
120
			$this->allowedCountries = ['AUTO'];
121
		}
122
123
		// Remove duplicities
124 1
		array_unique($this->allowedCountries);
125
126 1
		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 1
		$country = $this->validateCountry($country);
139 1
		$this->allowedCountries[] = strtoupper($country);
140
141
		// Remove duplicities
142 1
		array_unique($this->allowedCountries);
143
144 1
		if (strtoupper($country) === 'AUTO') {
145
			$this->allowedCountries = ['AUTO'];
146
147 1
		} elseif (($key = array_search('AUTO', $this->allowedCountries)) && $key !== FALSE) {
148
			unset($this->allowedCountries[$key]);
149
		}
150
151 1
		return $this;
152
	}
153
154
	/**
155
	 * @return array
156
	 */
157
	public function getAllowedCountries() : array
158
	{
159 1
		if (in_array('AUTO', $this->allowedCountries, TRUE) || $this->allowedCountries === []) {
160 1
			return $this->phoneUtils->getSupportedCountries();
161
		}
162
163 1
		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 1
		if ($country === NULL) {
176 1
			$this->defaultCountry = NULL;
177
178
		} else {
179 1
			$country = $this->validateCountry($country);
180
181 1
			$this->defaultCountry = strtoupper($country);
182
		}
183
184 1
		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 1
		$type = $this->validateType($type);
219 1
		$this->allowedTypes[] = strtoupper($type);
220
221
		// Remove duplicities
222 1
		array_unique($this->allowedTypes);
223
224 1
		return $this;
225
	}
226
227
	/**
228
	 * @return array
229
	 */
230
	public function getAllowedPhoneTypes() : array
231
	{
232 1
		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 1
		if ($value === NULL) {
247 1
			$this->country = NULL;
248 1
			$this->number = NULL;
249
250 1
			return $this;
251
		}
252
253 1
		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 1
			foreach ($this->getAllowedCountries() as $country) {
263 1
				if ($this->phoneUtils->isValid((string) $value, $country)) {
264 1
					$phone = IPub\Phone\Entities\Phone::fromNumber((string) $value, $country);
265
266 1
					$this->country = $phone->getCountry();
267 1
					$this->number = str_replace(' ', '', $phone->getNationalNumber());
268
269 1
					return $this;
270
				}
271
			}
272
		}
273
274 1
		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 1
		if ($key === self::FIELD_COUNTRY) {
287 1
			return $this->country;
288
289 1
		} elseif ($key === self::FIELD_NUMBER) {
290 1
			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 1
		if ($this->country === NULL || $this->number === NULL) {
302 1
			return NULL;
303
		}
304
305
		try {
306
			// Try to parse number & country
307 1
			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 1
		$country = $this->getHttpData(Forms\Form::DATA_LINE, '[' . self::FIELD_COUNTRY . ']');
325 1
		$this->country = ($country === '' || $country === NULL) ? NULL : (string) $country;
326
327 1
		$number = $this->getHttpData(Forms\Form::DATA_LINE, '[' . self::FIELD_NUMBER . ']');
328 1
		$this->number = ($number === '' || $number === NULL) ? NULL : (string) $number;
329 1
	}
330
331
	/**
332
	 * @return Utils\Html
333
	 */
334
	public function getControl()
335
	{
336 1
		$el = Utils\Html::el();
337 1
		$el->addHtml($this->getControlPart(self::FIELD_COUNTRY) . $this->getControlPart(self::FIELD_NUMBER));
338
339 1
		return $el;
340
	}
341
342
	/**
343
	 * @return Utils\Html
344
	 *
345
	 * @throws Exceptions\InvalidArgumentException
346
	 */
347
	public function getControlPart()
348
	{
349 1
		$args = func_get_args();
350 1
		$key = reset($args);
351
352 1
		$name = $this->getHtmlName();
353
354 1
		if ($key === self::FIELD_COUNTRY) {
355
			// Try to get translator
356 1
			$translator = $this->getTranslator();
357
358 1
			$locale = 'en_US';
359
360 1
			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 1
			$items = array_reduce($this->getAllowedCountries(), function (array $result, $row) use ($locale) {
370 1
				$countryName = FormPhone\Locale\Locale::getDisplayRegion(
371 1
					FormPhone\Locale\Locale::countryCodeToLocale($row),
372
					$locale
373
				);
374
375 1
				$result[$row] = Utils\Html::el('option');
376 1
				$result[$row]->setText('+' . $this->phoneUtils->getCountryCodeForCountry($row) . ' (' . $countryName . ')');
377 1
				$result[$row]->data('mask', preg_replace('/[0-9]/', '9', $this->phoneUtils->getExampleNationalNumber($row)));
378 1
				$result[$row]->addAttributes([
379 1
					'value' => $row,
380
				]);
381
382 1
				return $result;
383 1
			}, []);
384
385 1
			$control = Forms\Helpers::createSelectBox(
386
				$items,
387
				[
388 1
					'selected?' => $this->country === NULL ? $this->defaultCountry : $this->country,
389
				]
390
			);
391
392 1
			$control->addAttributes([
393 1
				'name' => $name . '[' . self::FIELD_COUNTRY . ']',
394 1
				'id'   => $this->getHtmlId() . '-' . self::FIELD_COUNTRY,
395
			]);
396 1
			$control->data('ipub-forms-phone', '');
397 1
			$control->data('settings', json_encode([
398 1
					'field' => $name . '[' . self::FIELD_NUMBER . ']'
399
				])
400
			);
401
402 1
			if ($this->isDisabled()) {
403
				$control->addAttributes([
404
					'disabled' => TRUE,
405
				]);
406
			}
407
408 1
			return $control;
409
410 1
		} elseif ($key === self::FIELD_NUMBER) {
411 1
			$prototype = $this->getControlPrototype();
412
413 1
			$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 1
			$control = Utils\Html::el('input');
416
417 1
			$control->addAttributes([
418 1
				'name'  => $name . '[' . self::FIELD_NUMBER . ']',
419 1
				'id'    => $this->getHtmlId() . '-' . self::FIELD_NUMBER,
420 1
				'value' => $this->number,
421 1
				'type'  => 'text',
422 1
				'class' => $prototype->getAttribute('class'),
423 1
				'placeholder' => $prototype->getAttribute('placeholder')
424
			]);
425
426 1
			$control->data('nette-empty-value', Utils\Strings::trim($this->translate($this->emptyValue)));
427 1
			$control->data('nette-rules', $input->{'data-nette-rules'});
428
429 1
			if ($this->isDisabled()) {
430
				$control->addAttributes([
431
					'disabled' => TRUE,
432
				]);
433
			}
434
435 1
			return $control;
436
		}
437
438
		throw new Exceptions\InvalidArgumentException(sprintf('Part "%s" does not exist.', $key));
439
	}
440
441
	/**
442
	 * {@inheritdoc}
443
	 */
444
	public function getLabel($caption = NULL)
445
	{
446
		$label = parent::getLabel($caption);
447
		$label->for = $this->getHtmlId() . '-' . self::FIELD_NUMBER;
448
449
		return $label;
450
	}
451
452
	/**
453
	 * {@inheritdoc}
454
	 */
455
	public function getLabelPart()
456
	{
457 1
		return NULL;
458
	}
459
460
	/**
461
	 * @param string $country
462
	 *
463
	 * @return string
464
	 *
465
	 * @throws Exceptions\NoValidCountryException
466
	 */
467
	private function validateCountry(string $country) : string
468
	{
469
		// Country code have to be upper-cased
470 1
		$country = strtoupper($country);
471
472 1
		if ((strlen($country) === 2 && ctype_alpha($country) && ctype_upper($country) && in_array($country, $this->phoneUtils->getSupportedCountries())) || $country === 'AUTO') {
473 1
			return $country;
474
475
		} else {
476 1
			throw new Exceptions\NoValidCountryException(sprintf('Provided country code "%s" is not valid. Provide valid country code or AUTO for automatic detection.', $country));
477
		}
478
	}
479
480
	/**
481
	 * @param string $type
482
	 *
483
	 * @return string
484
	 *
485
	 * @throws Exceptions\NoValidTypeException
486
	 */
487
	private function validateType(string $type) : string
488
	{
489
		// Phone type have to be upper-cased
490 1
		$type = strtoupper($type);
491
492 1
		if (defined('\IPub\Phone\Phone::TYPE_' . $type)) {
493 1
			return $type;
494
495
		} else {
496
			throw new Exceptions\NoValidTypeException(sprintf('Provided phone type "%s" is not valid. Provide valid phone type.', $type));
497
		}
498
	}
499
500
	/**
501
	 * @param PhoneUtils $phoneUtils
502
	 * @param string $method
503
	 */
504
	public static function register(PhoneUtils $phoneUtils, string $method = 'addPhone')
505
	{
506
		// Check for multiple registration
507 1
		if (self::$registered) {
508 1
			throw new Nette\InvalidStateException('Phone control already registered.');
509
		}
510
511 1
		self::$registered = TRUE;
512
513 1
		$class = function_exists('get_called_class') ? get_called_class() : __CLASS__;
514 1
		Forms\Container::extensionMethod(
515 1
			$method, function (Forms\Container $form, $name, $label = NULL, $maxLength = NULL) use ($class, $phoneUtils) {
516 1
			$component = new $class($phoneUtils, $label, $maxLength);
517 1
			$form->addComponent($component, $name);
518
519 1
			return $component;
520 1
		}
521
		);
522 1
	}
523
}
524