ci4

Форк
0
/
DataConverter.php 
202 строки · 5.5 Кб
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\DataConverter;
15

16
use Closure;
17
use CodeIgniter\DataCaster\DataCaster;
18
use CodeIgniter\Entity\Entity;
19

20
/**
21
 * PHP data <==> DataSource data converter
22
 *
23
 * @see \CodeIgniter\DataConverter\DataConverterTest
24
 *
25
 * @template TEntity of object
26
 */
27
final class DataConverter
28
{
29
    /**
30
     * The data caster.
31
     */
32
    private readonly DataCaster $dataCaster;
33

34
    /**
35
     * @param array<string, class-string> $castHandlers Custom convert handlers
36
     *
37
     * @internal
38
     */
39
    public function __construct(
40
        /**
41
         * Type definitions.
42
         *
43
         * @var array<string, string> [column => type]
44
         */
45
        private readonly array $types,
46
        array $castHandlers = [],
47
        /**
48
         * Helper object.
49
         */
50
        private readonly ?object $helper = null,
51
        /**
52
         * Static reconstruct method name or closure to reconstruct an object.
53
         * Used by reconstruct().
54
         *
55
         * @phpstan-var (Closure(array<string, mixed>): TEntity)|string|null
56
         */
57
        private readonly Closure|string|null $reconstructor = 'reconstruct',
58
        /**
59
         * Extract method name or closure to extract data from an object.
60
         * Used by extract().
61
         *
62
         * @phpstan-var (Closure(TEntity, bool, bool): array<string, mixed>)|string|null
63
         */
64
        private readonly Closure|string|null $extractor = null,
65
    ) {
66
        $this->dataCaster = new DataCaster($castHandlers, $types, $this->helper);
67
    }
68

69
    /**
70
     * Converts data from DataSource to PHP array with specified type values.
71
     *
72
     * @param array<string, mixed> $data DataSource data
73
     *
74
     * @internal
75
     */
76
    public function fromDataSource(array $data): array
77
    {
78
        foreach (array_keys($this->types) as $field) {
79
            if (array_key_exists($field, $data)) {
80
                $data[$field] = $this->dataCaster->castAs($data[$field], $field, 'get');
81
            }
82
        }
83

84
        return $data;
85
    }
86

87
    /**
88
     * Converts PHP array to data for DataSource field types.
89
     *
90
     * @param array<string, mixed> $phpData PHP data
91
     *
92
     * @internal
93
     */
94
    public function toDataSource(array $phpData): array
95
    {
96
        foreach (array_keys($this->types) as $field) {
97
            if (array_key_exists($field, $phpData)) {
98
                $phpData[$field] = $this->dataCaster->castAs($phpData[$field], $field, 'set');
99
            }
100
        }
101

102
        return $phpData;
103
    }
104

105
    /**
106
     * Takes database data array and creates a specified type object.
107
     *
108
     * @param         class-string          $classname
109
     * @phpstan-param class-string<TEntity> $classname
110
     * @param         array<string, mixed>  $row       Raw data from database
111
     *
112
     * @phpstan-return TEntity
113
     *
114
     * @internal
115
     */
116
    public function reconstruct(string $classname, array $row): object
117
    {
118
        $phpData = $this->fromDataSource($row);
119

120
        // Use static reconstruct method.
121
        if (is_string($this->reconstructor) && method_exists($classname, $this->reconstructor)) {
122
            $method = $this->reconstructor;
123

124
            return $classname::$method($phpData);
125
        }
126

127
        // Use closure to reconstruct.
128
        if ($this->reconstructor instanceof Closure) {
129
            $closure = $this->reconstructor;
130

131
            return $closure($phpData);
132
        }
133

134
        $classObj = new $classname();
135

136
        if ($classObj instanceof Entity) {
137
            $classObj->injectRawData($phpData);
138
            $classObj->syncOriginal();
139

140
            return $classObj;
141
        }
142

143
        $classSet = Closure::bind(function ($key, $value): void {
144
            $this->{$key} = $value;
145
        }, $classObj, $classname);
146

147
        foreach ($phpData as $key => $value) {
148
            $classSet($key, $value);
149
        }
150

151
        return $classObj;
152
    }
153

154
    /**
155
     * Takes an object and extract properties as an array.
156
     *
157
     * @param bool $onlyChanged Only for CodeIgniter's Entity. If true, only returns
158
     *                          values that have changed since object creation.
159
     * @param bool $recursive   Only for CodeIgniter's Entity. If true, inner
160
     *                          entities will be cast as array as well.
161
     *
162
     * @return array<string, mixed>
163
     *
164
     * @internal
165
     */
166
    public function extract(object $object, bool $onlyChanged = false, bool $recursive = false): array
167
    {
168
        // Use extractor method.
169
        if (is_string($this->extractor) && method_exists($object, $this->extractor)) {
170
            $method = $this->extractor;
171
            $row    = $object->{$method}($onlyChanged, $recursive);
172

173
            return $this->toDataSource($row);
174
        }
175

176
        // Use closure to extract.
177
        if ($this->extractor instanceof Closure) {
178
            $closure = $this->extractor;
179
            $row     = $closure($object, $onlyChanged, $recursive);
180

181
            return $this->toDataSource($row);
182
        }
183

184
        if ($object instanceof Entity) {
185
            $row = $object->toRawArray($onlyChanged, $recursive);
186

187
            return $this->toDataSource($row);
188
        }
189

190
        $array = (array) $object;
191

192
        $row = [];
193

194
        foreach ($array as $key => $value) {
195
            $key = preg_replace('/\000.*\000/', '', $key);
196

197
            $row[$key] = $value;
198
        }
199

200
        return $this->toDataSource($row);
201
    }
202
}
203

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

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

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

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