Completed
Push — master ( 742524...b3aa26 )
by Jesse
02:51
created

Is::decoratedType()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 4
1
<?php
2
declare(strict_types=1);
3
4
namespace Stratadox\Hydration\Mapper\Instruction;
5
6
use function array_merge;
7
use Stratadox\Hydration\Mapping\Property\Check;
8
use Stratadox\Hydration\Mapping\Property\Scalar\BooleanValue;
9
use Stratadox\Hydration\Mapping\Property\Scalar\CanBeFloat;
10
use Stratadox\Hydration\Mapping\Property\Scalar\CanBeInteger;
11
use Stratadox\Hydration\Mapping\Property\Scalar\CanBeNull;
12
use Stratadox\Hydration\Mapping\Property\Scalar\FloatValue;
13
use Stratadox\Hydration\Mapping\Property\Scalar\IntegerValue;
14
use Stratadox\Hydration\Mapping\Property\Scalar\OriginalValue;
15
use Stratadox\Hydration\Mapping\Property\Scalar\StringValue;
16
use Stratadox\HydrationMapper\DefinesTheType;
17
use Stratadox\HydrationMapper\InstructsHowToMap;
18
use Stratadox\HydrationMapping\MapsProperty;
19
use function call_user_func;
20
use function sprintf;
21
use Stratadox\Specification\Contract\Satisfiable;
22
23
/**
24
 * Indicates the type of a property, optionally changing the data key.
25
 *
26
 * @package Stratadox\Hydrate
27
 * @author  Stratadox
28
 */
29
final class Is implements DefinesTheType
30
{
31
    private const SAME_KEY = 'inProperty';
32
    private const USE_KEY = 'inPropertyWithDifferentKey';
33
34
    private $className;
35
    private $constructorName;
36
    private $decorators;
37
    private $key;
38
    private $isValid;
39
40
    private function __construct(
41
        string $className,
42
        string $constructorName,
43
        array $decorators,
44
        ?string $key
45
    ) {
46
        $this->className = $className;
47
        $this->constructorName = $constructorName;
48
        $this->decorators = $decorators;
49
        $this->key = $key;
50
    }
51
52
    public static function bool(): DefinesTheType
53
    {
54
        return Is::type(BooleanValue::class);
55
    }
56
57
    public static function float(): DefinesTheType
58
    {
59
        return Is::type(FloatValue::class);
60
    }
61
62
    public static function int(): DefinesTheType
63
    {
64
        return Is::type(IntegerValue::class);
65
    }
66
67
    public static function string(): DefinesTheType
68
    {
69
        return Is::type(StringValue::class);
70
    }
71
72
    public static function boolInKey(string $key): DefinesTheType
73
    {
74
        return Is::type(BooleanValue::class, self::USE_KEY, $key);
75
    }
76
77
    public static function floatInKey(string $key): DefinesTheType
78
    {
79
        return Is::type(FloatValue::class, self::USE_KEY, $key);
80
    }
81
82
    public static function intInKey(string $key): DefinesTheType
83
    {
84
        return Is::type(IntegerValue::class, self::USE_KEY, $key);
85
    }
86
87
    public static function stringInKey(string $key): DefinesTheType
88
    {
89
        return Is::type(StringValue::class, self::USE_KEY, $key);
90
    }
91
92
    public static function unchanged(): DefinesTheType
93
    {
94
        return Is::type(OriginalValue::class);
95
    }
96
97
    public static function number(): DefinesTheType
98
    {
99
        return Is::decoratedType(FloatValue::class, self::SAME_KEY, [
100
            CanBeInteger::class . '::or',
101
        ]);
102
    }
103
104
    public static function numberInKey(string $key): DefinesTheType
105
    {
106
        return Is::decoratedType(FloatValue::class, self::USE_KEY, [
107
            CanBeInteger::class . '::or',
108
        ], $key);
109
    }
110
111
    public static function mixed(): DefinesTheType
112
    {
113
        return Is::decoratedType(StringValue::class, self::SAME_KEY, [
114
            CanBeFloat::class . '::or',
115
            CanBeInteger::class . '::or',
116
            CanBeNull::class . '::or',
117
        ]);
118
    }
119
120
    public static function mixedInKey(string $key): DefinesTheType
121
    {
122
        return Is::decoratedType(StringValue::class, self::USE_KEY, [
123
            CanBeFloat::class . '::or',
124
            CanBeInteger::class . '::or',
125
            CanBeNull::class . '::or',
126
        ], $key);
127
    }
128
129
    /**
130
     * Declare that the property is of the type.
131
     *
132
     * @param string      $className   Class name of the property mapping.
133
     * @param string      $constructor Constructor name to use.
134
     * @param string|null $key         Data key to use.
135
     * @return DefinesTheType       The mapping instruction.
136
     */
137
    private static function type(
138
        string $className,
139
        string $constructor = self::SAME_KEY,
140
        string $key = null
141
    ): DefinesTheType {
142
        return new Is($className, $constructor, [], $key);
143
    }
144
145
    /**
146
     * Declare that the property is of the decorated type.
147
     *
148
     * @param string      $className   Class name of the property mapping.
149
     * @param string      $constructor Constructor name to use.
150
     * @param string[]    $decorators  Decorator full constructor names.
151
     * @param string|null $key         Data key to use.
152
     * @return DefinesTheType       The mapping instruction.
153
     */
154
    private static function decoratedType(
155
        string $className,
156
        string $constructor,
157
        array $decorators,
158
        string $key = null
159
    ): DefinesTheType {
160
        return new Is($className, $constructor, $decorators, $key);
161
    }
162
163
    /** @inheritdoc */
164
    public function followFor(string $property): MapsProperty
165
    {
166
        $mapping = call_user_func(
167
            sprintf('%s::%s', $this->className, $this->constructorName),
168
            $property, $this->key
169
        );
170
        foreach ($this->decorators as $decorated) {
171
            $mapping = $decorated($mapping);
172
        }
173
        if (isset($this->isValid)) {
174
            $mapping = Check::that($this->isValid, $mapping);
175
        }
176
        return $mapping;
177
    }
178
179
    /** @inheritdoc */
180
    public function nullable(): DefinesTheType
181
    {
182
        return Is::decoratedType(
183
            $this->className, 
184
            $this->constructorName,
185
            array_merge($this->decorators, [CanBeNull::class . '::or']),
186
            $this->key
187
        );
188
    }
189
190
    /** @inheritdoc */
191
    public function that(Satisfiable $constraint): InstructsHowToMap
192
    {
193
        $new = clone $this;
194
        $new->isValid = $constraint;
195
        return $new;
196
    }
197
}
198