PhoneElement::tryCast()   A
last analyzed

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 60
    public function __construct(?ValueValidatorInterface $validator = null, ?TransformerInterface $transformer = null, ?callable $regionResolver = null, ?PhoneNumberUtil $formatter = null)
45
    {
46 60
        parent::__construct($validator, $transformer);
47
48 60
        $this->regionResolver = $regionResolver;
49 60
        $this->formatter = $formatter ?? PhoneNumberUtil::getInstance();
50 60
    }
51
52
    /**
53
     * {@inheritdoc}
54
     */
55 28
    protected function toPhp($httpValue)
56
    {
57 28
        if ($httpValue === null) {
58 7
            return null;
59
        }
60
61 23
        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
     * @return PhoneNumber|null
80
     */
81 22
    protected function tryCast($value): ?PhoneNumber
82
    {
83 22
        if ($value === null) {
84 3
            return null;
85
        }
86
87 19
        if (!$value instanceof PhoneNumber) {
88 9
            throw new TypeError('The import()\'ed value of a '.static::class.' must be an instance of '.PhoneNumber::class.' or null');
89
        }
90
91 10
        return $value;
92
    }
93
94
    /**
95
     * Get the resolved region string
96
     *
97
     * @return string
98
     */
99 28
    private function resolveRegion(): string
100
    {
101 28
        if (!$this->regionResolver) {
102 15
            return PhoneNumberUtil::UNKNOWN_REGION;
103
        }
104
105 13
        return strtoupper(($this->regionResolver)($this));
106
    }
107
108
    /**
109
     * @internal
110
     */
111 8
    public function getFormatter(): PhoneNumberUtil
112
    {
113 8
        return $this->formatter;
114
    }
115
116
    /**
117
     * Parse a phone number string
118
     *
119
     * @param string $rawPhoneNumber The string value of the phone number
120
     * @return PhoneNumber The parsed instance
121
     *
122
     * @internal
123
     */
124 28
    public function parseValue(string $rawPhoneNumber): PhoneNumber
125
    {
126
        try {
127 28
            return $this->formatter->parse($rawPhoneNumber, $this->resolveRegion(), null, true);
128 11
        } catch (NumberParseException $e) {
129 11
            return (new PhoneNumber())->setRawInput($rawPhoneNumber);
130
        }
131
    }
132
}
133