ci4

Форк
0
/
DataCaster.php 
188 строк · 5.7 Кб
1
<?php
2

3
declare(strict_types=1);
4

5
/**
6
 * This file is part of CodeIgniter 4 framework.
7
 *
8
 * (c) CodeIgniter Foundation <admin@codeigniter.com>
9
 *
10
 * For the full copyright and license information, please view
11
 * the LICENSE file that was distributed with this source code.
12
 */
13

14
namespace CodeIgniter\DataCaster;
15

16
use CodeIgniter\DataCaster\Cast\ArrayCast;
17
use CodeIgniter\DataCaster\Cast\BooleanCast;
18
use CodeIgniter\DataCaster\Cast\CastInterface;
19
use CodeIgniter\DataCaster\Cast\CSVCast;
20
use CodeIgniter\DataCaster\Cast\DatetimeCast;
21
use CodeIgniter\DataCaster\Cast\FloatCast;
22
use CodeIgniter\DataCaster\Cast\IntBoolCast;
23
use CodeIgniter\DataCaster\Cast\IntegerCast;
24
use CodeIgniter\DataCaster\Cast\JsonCast;
25
use CodeIgniter\DataCaster\Cast\TimestampCast;
26
use CodeIgniter\DataCaster\Cast\URICast;
27
use CodeIgniter\Entity\Cast\CastInterface as EntityCastInterface;
28
use CodeIgniter\Entity\Exceptions\CastException;
29
use InvalidArgumentException;
30

31
final class DataCaster
32
{
33
    /**
34
     * Array of field names and the type of value to cast.
35
     *
36
     * @var array<string, string> [field => type]
37
     */
38
    private array $types = [];
39

40
    /**
41
     * Convert handlers
42
     *
43
     * @var array<string, class-string> [type => classname]
44
     */
45
    private array $castHandlers = [
46
        'array'     => ArrayCast::class,
47
        'bool'      => BooleanCast::class,
48
        'boolean'   => BooleanCast::class,
49
        'csv'       => CSVCast::class,
50
        'datetime'  => DatetimeCast::class,
51
        'double'    => FloatCast::class,
52
        'float'     => FloatCast::class,
53
        'int'       => IntegerCast::class,
54
        'integer'   => IntegerCast::class,
55
        'int-bool'  => IntBoolCast::class,
56
        'json'      => JsonCast::class,
57
        'timestamp' => TimestampCast::class,
58
        'uri'       => URICast::class,
59
    ];
60

61
    /**
62
     * @param array<string, class-string>|null $castHandlers Custom convert handlers
63
     * @param array<string, string>|null       $types        [field => type]
64
     * @param object|null                      $helper       Helper object.
65
     * @param bool                             $strict       Strict mode? Set to false for casts for Entity.
66
     */
67
    public function __construct(
68
        ?array $castHandlers = null,
69
        ?array $types = null,
70
        private readonly ?object $helper = null,
71
        private readonly bool $strict = true
72
    ) {
73
        $this->castHandlers = array_merge($this->castHandlers, $castHandlers);
74

75
        if ($types !== null) {
76
            $this->setTypes($types);
77
        }
78

79
        if ($this->strict) {
80
            foreach ($this->castHandlers as $handler) {
81
                if (
82
                    ! is_subclass_of($handler, CastInterface::class)
83
                    && ! is_subclass_of($handler, EntityCastInterface::class)
84
                ) {
85
                    throw new InvalidArgumentException(
86
                        'Invalid class type. It must implement CastInterface. class: ' . $handler
87
                    );
88
                }
89
            }
90
        }
91
    }
92

93
    /**
94
     * This method is only for Entity.
95
     *
96
     * @TODO if Entity::$casts is readonly, we don't need this method.
97
     *
98
     * @param array<string, string> $types [field => type]
99
     *
100
     * @return $this
101
     *
102
     * @internal
103
     */
104
    public function setTypes(array $types): static
105
    {
106
        $this->types = $types;
107

108
        return $this;
109
    }
110

111
    /**
112
     * Provides the ability to cast an item as a specific data type.
113
     * Add ? at the beginning of the type (i.e. ?string) to get `null`
114
     * instead of casting $value when $value is null.
115
     *
116
     * @param         mixed       $value  The value to convert
117
     * @param         string      $field  The field name
118
     * @param         string      $method Allowed to "get" and "set"
119
     * @phpstan-param 'get'|'set' $method
120
     */
121
    public function castAs(mixed $value, string $field, string $method = 'get'): mixed
122
    {
123
        // If the type is not defined, return as it is.
124
        if (! isset($this->types[$field])) {
125
            return $value;
126
        }
127

128
        $type = $this->types[$field];
129

130
        $isNullable = false;
131

132
        // Is nullable?
133
        if (str_starts_with($type, '?')) {
134
            $isNullable = true;
135

136
            if ($value === null) {
137
                return null;
138
            }
139

140
            $type = substr($type, 1);
141
        } elseif ($value === null) {
142
            if ($this->strict) {
143
                $message = 'Field "' . $field . '" is not nullable, but null was passed.';
144

145
                throw new InvalidArgumentException($message);
146
            }
147
        }
148

149
        // In order not to create a separate handler for the
150
        // json-array type, we transform the required one.
151
        $type = ($type === 'json-array') ? 'json[array]' : $type;
152

153
        $params = [];
154

155
        // Attempt to retrieve additional parameters if specified
156
        // type[param, param2,param3]
157
        if (preg_match('/\A(.+)\[(.+)\]\z/', $type, $matches)) {
158
            $type   = $matches[1];
159
            $params = array_map(trim(...), explode(',', $matches[2]));
160
        }
161

162
        if ($isNullable && ! $this->strict) {
163
            $params[] = 'nullable';
164
        }
165

166
        $type = trim($type, '[]');
167

168
        $handlers = $this->castHandlers;
169

170
        if (! isset($handlers[$type])) {
171
            throw new InvalidArgumentException(
172
                'No such handler for "' . $field . '". Invalid type: ' . $type
173
            );
174
        }
175

176
        $handler = $handlers[$type];
177

178
        if (
179
            ! $this->strict
180
            && ! is_subclass_of($handler, CastInterface::class)
181
            && ! is_subclass_of($handler, EntityCastInterface::class)
182
        ) {
183
            throw CastException::forInvalidInterface($handler);
184
        }
185

186
        return $handler::$method($value, $params, $this->helper);
187
    }
188
}
189

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.