Passed
Push — master ( 0a4514...70449d )
by Vincent
04:28
created

PhoneElement::tryCast()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 11
ccs 6
cts 6
cp 1
rs 10
cc 3
nc 3
nop 1
crap 3
1
<?php
2
3
namespace Bdf\Form\Phone;
4
5
use Bdf\Form\Leaf\LeafElement;
6
use Bdf\Form\Transformer\TransformerInterface;
7
use Bdf\Form\Validator\ValueValidatorInterface;
8
use libphonenumber\NumberParseException;
9
use libphonenumber\PhoneNumber;
10
use libphonenumber\PhoneNumberFormat;
11
use libphonenumber\PhoneNumberUtil;
12
use TypeError;
13
14
/**
15
 * Element for handle phone number input
16
 *
17
 * The package "giggsey/libphonenumber-for-php" is required for use this element
18
 * The PHP value of this element is a PhoneNumber instance. Constraints and hydrated entities should support this type.
19
 *
20
 * @method PhoneNumber value()
21
 * @extends LeafElement<PhoneNumber>
22
 */
23
final class PhoneElement extends LeafElement
24
{
25
    /**
26
     * @var callable(PhoneElement):string
27
     */
28
    private $regionResolver;
29
30
    /**
31
     * @var PhoneNumberUtil
32
     */
33
    private $formatter;
34
35
36
    /**
37
     * PhoneElement constructor.
38
     *
39
     * @param ValueValidatorInterface|null $validator
40
     * @param TransformerInterface|null $transformer
41
     * @param callable|null $regionResolver Resolve the region / country code. Takes as parameter the element, and must return the country code as string
42
     * @param PhoneNumberUtil|null $formatter The phone number formatter
43
     */
44 56
    public function __construct(?ValueValidatorInterface $validator = null, ?TransformerInterface $transformer = null, ?callable $regionResolver = null, ?PhoneNumberUtil $formatter = null)
45
    {
46 56
        parent::__construct($validator, $transformer);
47
48 56
        $this->regionResolver = $regionResolver;
49 56
        $this->formatter = $formatter ?? PhoneNumberUtil::getInstance();
50 56
    }
51
52
    /**
53
     * {@inheritdoc}
54
     */
55 24
    protected function toPhp($httpValue)
56
    {
57 24
        if ($httpValue === null) {
58 6
            return null;
59
        }
60
61 20
        return $this->parseValue($httpValue);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->parseValue($httpValue) returns the type libphonenumber\PhoneNumber which is incompatible with the return type mandated by Bdf\Form\Leaf\LeafElement::toPhp() of Bdf\Form\Leaf\T|null.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
62
    }
63
64
    /**
65
     * {@inheritdoc}
66
     */
67 9
    protected function toHttp($phpValue)
68
    {
69 9
        if (!$phpValue) {
70 3
            return null;
71
        }
72
73 7
        return $phpValue->getRawInput() ?? $this->formatter->format($phpValue, PhoneNumberFormat::E164);
74
    }
75
76
    /**
77
     * {@inheritdoc}
78
     */
79 22
    protected function tryCast($value): ?PhoneNumber
80
    {
81 22
        if ($value === null) {
82 3
            return null;
83
        }
84
85 19
        if (!$value instanceof PhoneNumber) {
86 9
            throw new TypeError('The import()\'ed value of a '.static::class.' must be an instance of '.PhoneNumber::class.' or null');
87
        }
88
89 10
        return $value;
90
    }
91
92
    /**
93
     * Get the resolved region string
94
     *
95
     * @return string
96
     */
97 25
    private function resolveRegion(): string
98
    {
99 25
        if (!$this->regionResolver) {
100 15
            return PhoneNumberUtil::UNKNOWN_REGION;
101
        }
102
103 10
        return strtoupper(($this->regionResolver)($this));
104
    }
105
106
    /**
107
     * @internal
108
     */
109 5
    public function getFormatter(): PhoneNumberUtil
110
    {
111 5
        return $this->formatter;
112
    }
113
114
    /**
115
     * Parse a phone number string
116
     *
117
     * @param string $rawPhoneNumber The string value of the phone number
118
     * @return PhoneNumber The parsed instance
119
     *
120
     * @internal
121
     */
122 25
    public function parseValue(string $rawPhoneNumber): PhoneNumber
123
    {
124
        try {
125 25
            return $this->formatter->parse($rawPhoneNumber, $this->resolveRegion(), null, true);
126 10
        } catch (NumberParseException $e) {
127 10
            return (new PhoneNumber())->setRawInput($rawPhoneNumber);
128
        }
129
    }
130
}
131