DateTimeElement::toPhp()   B
last analyzed

Complexity

Conditions 9
Paths 18

Size

Total Lines 40
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 9

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 20
c 1
b 0
f 0
dl 0
loc 40
ccs 19
cts 19
cp 1
rs 8.0555
cc 9
nc 18
nop 1
crap 9
1
<?php
2
3
namespace Bdf\Form\Leaf\Date;
4
5
use Bdf\Form\Choice\ChoiceInterface;
6
use Bdf\Form\Leaf\LeafElement;
7
use Bdf\Form\Transformer\TransformerInterface;
8
use Bdf\Form\Validator\ValueValidatorInterface;
9
use DateTime;
10
use DateTimeInterface;
11
use DateTimeZone;
12
use InvalidArgumentException;
13
use TypeError;
14
15
/**
16
 * Handle DateTime form element
17
 * The element use a formatted string as http value, and can return any implementation of DateTimeInterface
18
 *
19
 * @method DateTimeInterface value()
20
 * @extends LeafElement<DateTimeInterface>
21
 */
22
final class DateTimeElement extends LeafElement
23
{
24
    /**
25
     * @var class-string<DateTimeInterface>
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<DateTimeInterface> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<DateTimeInterface>.
Loading history...
26
     */
27
    private $className;
28
29
    /**
30
     * @var string
31
     */
32
    private $format;
33
34
    /**
35
     * @var DateTimeZone|null
36
     */
37
    private $timezone;
38
39
    /**
40
     * Reset the fields value which are not provided by the format
41
     *
42
     * @var bool
43
     */
44
    private $resetNotProvidedFields;
45
46
    /**
47
     * DateTimeType constructor.
48
     *
49
     * @param ValueValidatorInterface|null $validator
50
     * @param TransformerInterface|null $transformer
51
     * @param ChoiceInterface|null $choices
52
     * @param class-string<DateTimeInterface> $className The date time class name to use
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<DateTimeInterface> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<DateTimeInterface>.
Loading history...
53
     * @param string $format The time format string
54
     * @param DateTimeZone|null $timezone Timezone to use. Use null to not define a timezone
55
     * @param bool $resetNotProvidedFields Does the fields which are not provided by the format will be reset ? (and set to UNIX time)
56
     */
57 69
    public function __construct(?ValueValidatorInterface $validator = null, ?TransformerInterface $transformer = null, ?ChoiceInterface $choices = null, string $className = DateTime::class, string $format = DateTime::ATOM, ?DateTimeZone $timezone = null, bool $resetNotProvidedFields = true)
58
    {
59 69
        parent::__construct($validator, $transformer, $choices);
60
61 69
        $this->className = $className;
62 69
        $this->format = $format;
63 69
        $this->timezone = $timezone;
64 69
        $this->resetNotProvidedFields = $resetNotProvidedFields;
65 69
    }
66
67
    /**
68
     * Get the timezone of the element
69
     *
70
     * @return DateTimeZone|null The timezone, or null if not defined
71
     */
72 3
    public function timezone(): ?DateTimeZone
73
    {
74 3
        return $this->timezone;
75
    }
76
77
    /**
78
     * Get the handled date time class name
79
     *
80
     * @return class-string<DateTimeInterface>
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<DateTimeInterface> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<DateTimeInterface>.
Loading history...
81
     */
82 2
    public function dateTimeClassName(): string
83
    {
84 2
        return $this->className;
85
    }
86
87
    /**
88
     * {@inheritdoc}
89
     */
90 39
    protected function toPhp($httpValue): ?DateTimeInterface
91
    {
92 39
        if ($httpValue === null) {
93 3
            return null;
94
        }
95
96
        switch (true) {
97 36
            case $httpValue instanceof $this->className:
98 2
                $dateTime = $httpValue; // Clone ?
99 2
                break;
100
101
            /** @psalm-suppress ParadoxicalCondition */
102 34
            case $httpValue instanceof DateTimeInterface:
103 3
                $httpValue = $httpValue->format($this->format);
104
                // No break
105
106
            default:
107 34
                if (!method_exists($this->className, 'createFromFormat')) {
108 1
                    throw new \LogicException('Invalid DateTime class name "'.$this->className.'" : method createFromFormat() is not found.');
109
                }
110
111 33
                $format = $this->format;
112
113 33
                if ($this->resetNotProvidedFields && !str_contains($format, '|')) {
114 32
                    $format .= '|';
115
                }
116
117 33
                $dateTime = ($this->className)::createFromFormat($format, $httpValue, $this->timezone);
118
        }
119
120 35
        if ($dateTime === false) {
121 2
            throw new InvalidArgumentException('Invalid date format');
122
        }
123
124
        /** @var DateTime|\DateTimeImmutable $dateTime */
125 33
        if ($this->timezone !== null) {
126 3
            $dateTime = $dateTime->setTimezone($this->timezone);
127
        }
128
129 33
        return $dateTime;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $dateTime returns the type DateTime|DateTimeImmutable 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...
130
    }
131
132
    /**
133
     * {@inheritdoc}
134
     */
135 13
    protected function toHttp($phpValue)
136
    {
137 13
        if (is_string($phpValue)) {
138 3
            return $phpValue;
139
        }
140
141 10
        if (!$phpValue instanceof DateTimeInterface) {
142 2
            return null;
143
        }
144
145 8
        return $phpValue->format($this->format);
146
    }
147
148
    /**
149
     * {@inheritdoc}
150
     *
151
     * @return DateTimeInterface|null
152
     */
153 27
    protected function tryCast($value): ?DateTimeInterface
154
    {
155 27
        if ($value === null) {
156 3
            return null;
157
        }
158
159 26
        if (!$value instanceof $this->className) {
160 10
            throw new TypeError('The import()\'ed value of a '.static::class.' must be an instance of '.$this->className.' or null');
161
        }
162
163 16
        return $value;
164
    }
165
}
166