PluginProbe ʕ •ᴥ•ʔ
Kirki – Freeform Page Builder, Website Builder & Customizer / 6.0.13
Kirki – Freeform Page Builder, Website Builder & Customizer v6.0.13
6.1.1 6.1.0 6.0.14 6.0.13 6.0.12 6.0.11 6.0.10 6.0.9 6.0.8 6.0.7 6.0.6 6.0.5 6.0.4 6.0.3 6.0.2 6.0.1 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 4.0.19 4.0.20 4.0.21 4.0.22 4.0.23 4.0.24 4.1 4.2.0 5.0.0 5.1.0 5.1.1 5.2.0 5.2.1 5.2.2 5.2.3 6.0.0 trunk 3.0.40 3.0.41 3.0.42 3.0.43 3.0.44 3.0.45 3.1.0 3.1.1 3.1.2
kirki / libraries / framework / DTO.php
kirki / libraries / framework Last commit date
Collections 2 weeks ago Concerns 2 weeks ago Console 2 weeks ago Constants 2 weeks ago Contracts 2 weeks ago Database 2 weeks ago Discovery 2 weeks ago Exceptions 2 weeks ago Filesystem 2 weeks ago Http 2 weeks ago Managers 2 weeks ago Middlewares 2 weeks ago Polyfill 2 weeks ago Supports 2 weeks ago Validation 2 weeks ago Wordpress 2 weeks ago ApiExceptionHandler.php 2 weeks ago Application.php 2 weeks ago Container.php 2 weeks ago CoreServiceProvider.php 2 weeks ago DTO.php 2 weeks ago Facade.php 2 weeks ago Listener.php 2 weeks ago Resource.php 2 weeks ago Route.php 2 weeks ago Sanitizer.php 2 weeks ago ServiceProvider.php 2 weeks ago helpers.php 2 weeks ago
DTO.php
344 lines
1 <?php
2
3 /**
4 * Base data transfer object with array construction, request hydration, and JSON serialization.
5 * Supports attribute casting, field picking/exclusion, and meta field extraction.
6 * Bridges validated HTTP input and API output with a typed property bag.
7 *
8 * @package Framework
9 * @since 1.0.0
10 */
11 namespace Kirki\Framework;
12
13 \defined('ABSPATH') || exit;
14 use Kirki\Framework\Contracts\CastAttribute;
15 use Kirki\Framework\Contracts\Request;
16 use Kirki\Framework\Contracts\Support\Arrayable;
17 use Kirki\Framework\Exceptions\ValidationException;
18 use Exception;
19 use JsonSerializable;
20 class DTO implements JsonSerializable, Arrayable
21 {
22 /**
23 * Fields that are considered not part of "meta" data.
24 *
25 * @var array
26 *
27 * @since 1.0.0
28 */
29 protected static $base_fields = ['id', 'title', 'description', 'slug'];
30 /**
31 * Fields that are considered to cast data when serialized.
32 *
33 * @var array
34 *
35 * @since 1.0.0
36 */
37 protected $casts = [];
38 /**
39 * Tracks which attributes have been prepared for display
40 *
41 * @var bool
42 *
43 * @since 1.0.0
44 */
45 protected $prepare_for_display = \false;
46 /**
47 * Fields to exclude from public attributes.
48 *
49 * @var array
50 *
51 * @since 1.0.0
52 */
53 protected array $excluded_keys = [];
54 /**
55 * Fields to pick from public attributes.
56 *
57 * @var array
58 *
59 * @since 1.0.0
60 */
61 protected array $picked_keys = [];
62 /**
63 * Dto constructor.
64 *
65 * @param array $data The data payload.
66 *
67 * @return void
68 *
69 * @since 1.0.0
70 */
71 public function __construct(array $data = [])
72 {
73 foreach ($data as $key => $value) {
74 if (\property_exists($this, $key)) {
75 $this->{$key} = $value;
76 }
77 }
78 }
79 /**
80 * Create DTO from array
81 *
82 * @param array $data The data payload.
83 *
84 * @return static
85 *
86 * @since 1.0.0
87 */
88 public static function from_array(array $data)
89 {
90 return new static($data);
91 }
92 /**
93 * Create DTO from Request
94 *
95 * @param Request $request The request instance.
96 *
97 * @return static
98 *
99 * @since 1.0.0
100 */
101 public static function from_request(Request $request)
102 {
103 $request->validated();
104 $data = $request->sanitized();
105 return static::from_array($data);
106 }
107 /**
108 * Create DTO from list
109 *
110 * @param array $items The items.
111 *
112 * @return static[]
113 *
114 * @since 1.0.0
115 */
116 public static function from_list(array $items)
117 {
118 $dtos = \array_map(function ($item) {
119 return static::from_array($item);
120 }, $items);
121 return $dtos;
122 }
123 /**
124 * Return an array representation of the object
125 *
126 * @return array
127 *
128 * @since 1.0.0
129 */
130 public function jsonSerialize() : array
131 {
132 $this->cast_attributes();
133 return $this->to_array();
134 }
135 /**
136 * Convert DTO to array
137 *
138 * @return array
139 *
140 * @since 1.0.0
141 */
142 public function to_array()
143 {
144 if (!empty($this->excluded_keys)) {
145 return $this->except($this->excluded_keys);
146 }
147 if (!empty($this->picked_keys)) {
148 return $this->only($this->picked_keys);
149 }
150 return $this->all();
151 }
152 /**
153 * Extract metadata fields only
154 *
155 * @param array $except The except.
156 *
157 * @return array
158 *
159 * @since 1.0.0
160 */
161 public function get_meta(array $except = [])
162 {
163 $fields = $this->all();
164 $meta = [];
165 $fields_to_skip = \array_merge(static::$base_fields, $except);
166 foreach ($fields as $key => $value) {
167 if (!\in_array($key, $fields_to_skip, \true)) {
168 $meta[$key] = $value;
169 }
170 }
171 return $meta;
172 }
173 /**
174 * Get all fields
175 *
176 * @return array
177 *
178 * @since 1.0.0
179 */
180 public function all()
181 {
182 $data = $this->get_public_vars();
183 return $data;
184 }
185 /**
186 * Get all excluded fields
187 *
188 * @param array $keys The keys.
189 *
190 * @return array
191 *
192 * @since 1.0.0
193 */
194 public function except(array $keys)
195 {
196 $data = $this->all();
197 return \array_diff_key($data, \array_flip($keys));
198 }
199 /**
200 * Set all excluded fields
201 *
202 * @param array $keys The keys.
203 *
204 * @return static
205 *
206 * @since 1.0.0
207 */
208 public function exclude(array $keys)
209 {
210 $this->excluded_keys = $keys;
211 return $this;
212 }
213 /**
214 * Get all only included fields
215 *
216 * @param array $keys The keys.
217 *
218 * @return array
219 *
220 * @since 1.0.0
221 */
222 public function only(array $keys)
223 {
224 $data = $this->all();
225 return \array_intersect_key($data, \array_flip($keys));
226 }
227 /**
228 * Set all included fields
229 *
230 * @param array $keys The keys.
231 *
232 * @return static
233 *
234 * @since 1.0.0
235 */
236 public function pick(array $keys)
237 {
238 $this->picked_keys = $keys;
239 return $this;
240 }
241 /**
242 * Get public properties with values
243 *
244 * @return array
245 *
246 * @since 1.0.0
247 */
248 protected function get_public_vars() : array
249 {
250 $vars = [];
251 $reflection = new \ReflectionClass($this);
252 foreach ($reflection->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
253 $name = $property->getName();
254 $vars[$name] = $this->{$name};
255 }
256 return $vars;
257 }
258 /**
259 * Get casts
260 *
261 * @return array
262 *
263 * @since 1.0.0
264 */
265 protected function get_casts()
266 {
267 return $this->casts;
268 }
269 /**
270 * Cast all attributes
271 *
272 * @return $this
273 *
274 * @since 1.0.0
275 */
276 protected function cast_attributes()
277 {
278 if ($this->prepare_for_display) {
279 return $this;
280 }
281 foreach ($this->get_casts() as $key => $cast) {
282 $field = \explode('.', $key);
283 $attribute = $field[0];
284 if (\property_exists($this, $attribute)) {
285 \array_shift($field);
286 $this->{$attribute} = $this->traverse_and_cast_attribute($this->{$attribute}, $field, $cast);
287 }
288 }
289 // Mark as prepared for display
290 $this->prepare_for_display = \true;
291 return $this;
292 }
293 /**
294 * Traverse and cast attribute
295 *
296 * @param mixed $current_field_value The current field value.
297 * @param array $key_segments The key segments.
298 * @param mixed $cast The cast.
299 *
300 * @return mixed
301 *
302 * @throws \Exception
303 *
304 * @since 1.0.0
305 */
306 protected function traverse_and_cast_attribute($current_field_value, array $key_segments, $cast)
307 {
308 if (empty($key_segments)) {
309 if (\is_subclass_of($cast, CastAttribute::class)) {
310 $attribute_class = new $cast();
311 return $attribute_class->get($current_field_value, $current_field_value);
312 } elseif (\is_callable($cast)) {
313 return $cast();
314 }
315 throw new Exception('Cast must be an instance of ' . CastAttribute::class . ' or a callable');
316 }
317 $segment = \array_shift($key_segments);
318 if ($segment === '*') {
319 if (!\is_array($current_field_value)) {
320 return $current_field_value;
321 }
322 foreach ($current_field_value as $key => $value) {
323 $current_field_value[$key] = $this->traverse_and_cast_attribute($value, $key_segments, $cast);
324 }
325 } elseif (\is_array($current_field_value) && \array_key_exists($segment, $current_field_value)) {
326 $current_field_value[$segment] = $this->traverse_and_cast_attribute($current_field_value[$segment], $key_segments, $cast);
327 } elseif (\is_object($current_field_value) && \property_exists($current_field_value, $segment)) {
328 $current_field_value->{$segment} = $this->traverse_and_cast_attribute($current_field_value->{$segment}, $key_segments, $cast);
329 }
330 return $current_field_value;
331 }
332 /**
333 * Get the values.
334 *
335 * @return mixed
336 *
337 * @since 1.0.0
338 */
339 public function get_values()
340 {
341 return $this->cast_attributes();
342 }
343 }
344