| Server IP : 172.67.71.254 / Your IP : 216.73.216.4 Web Server : nginx/1.27.1 System : Linux in-5 5.15.0-143-generic #153-Ubuntu SMP Fri Jun 13 19:10:45 UTC 2025 x86_64 User : arabianexpress ( 1872) PHP Version : 8.0.30 Disable Function : exec,passthru,shell_exec,system,proc_open,popen,parse_ini_file,show_source MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /storage/v9321/leapdubai/public_html/wp-content/plugins/kirki/libraries/framework/ |
Upload File : |
<?php
/**
* Base data transfer object with array construction, request hydration, and JSON serialization.
* Supports attribute casting, field picking/exclusion, and meta field extraction.
* Bridges validated HTTP input and API output with a typed property bag.
*
* @package Framework
* @since 1.0.0
*/
namespace Kirki\Framework;
\defined('ABSPATH') || exit;
use Kirki\Framework\Contracts\CastAttribute;
use Kirki\Framework\Contracts\Request;
use Kirki\Framework\Contracts\Support\Arrayable;
use Kirki\Framework\Exceptions\ValidationException;
use Exception;
use JsonSerializable;
class DTO implements JsonSerializable, Arrayable
{
/**
* Fields that are considered not part of "meta" data.
*
* @var array
*
* @since 1.0.0
*/
protected static $base_fields = ['id', 'title', 'description', 'slug'];
/**
* Fields that are considered to cast data when serialized.
*
* @var array
*
* @since 1.0.0
*/
protected $casts = [];
/**
* Tracks which attributes have been prepared for display
*
* @var bool
*
* @since 1.0.0
*/
protected $prepare_for_display = \false;
/**
* Fields to exclude from public attributes.
*
* @var array
*
* @since 1.0.0
*/
protected array $excluded_keys = [];
/**
* Fields to pick from public attributes.
*
* @var array
*
* @since 1.0.0
*/
protected array $picked_keys = [];
/**
* Dto constructor.
*
* @param array $data The data payload.
*
* @return void
*
* @since 1.0.0
*/
public function __construct(array $data = [])
{
foreach ($data as $key => $value) {
if (\property_exists($this, $key)) {
$this->{$key} = $value;
}
}
}
/**
* Create DTO from array
*
* @param array $data The data payload.
*
* @return static
*
* @since 1.0.0
*/
public static function from_array(array $data)
{
return new static($data);
}
/**
* Create DTO from Request
*
* @param Request $request The request instance.
*
* @return static
*
* @since 1.0.0
*/
public static function from_request(Request $request)
{
$request->validated();
$data = $request->sanitized();
return static::from_array($data);
}
/**
* Create DTO from list
*
* @param array $items The items.
*
* @return static[]
*
* @since 1.0.0
*/
public static function from_list(array $items)
{
$dtos = \array_map(function ($item) {
return static::from_array($item);
}, $items);
return $dtos;
}
/**
* Return an array representation of the object
*
* @return array
*
* @since 1.0.0
*/
public function jsonSerialize() : array
{
$this->cast_attributes();
return $this->to_array();
}
/**
* Convert DTO to array
*
* @return array
*
* @since 1.0.0
*/
public function to_array()
{
if (!empty($this->excluded_keys)) {
return $this->except($this->excluded_keys);
}
if (!empty($this->picked_keys)) {
return $this->only($this->picked_keys);
}
return $this->all();
}
/**
* Extract metadata fields only
*
* @param array $except The except.
*
* @return array
*
* @since 1.0.0
*/
public function get_meta(array $except = [])
{
$fields = $this->all();
$meta = [];
$fields_to_skip = \array_merge(static::$base_fields, $except);
foreach ($fields as $key => $value) {
if (!\in_array($key, $fields_to_skip, \true)) {
$meta[$key] = $value;
}
}
return $meta;
}
/**
* Get all fields
*
* @return array
*
* @since 1.0.0
*/
public function all()
{
$data = $this->get_public_vars();
return $data;
}
/**
* Get all excluded fields
*
* @param array $keys The keys.
*
* @return array
*
* @since 1.0.0
*/
public function except(array $keys)
{
$data = $this->all();
return \array_diff_key($data, \array_flip($keys));
}
/**
* Set all excluded fields
*
* @param array $keys The keys.
*
* @return static
*
* @since 1.0.0
*/
public function exclude(array $keys)
{
$this->excluded_keys = $keys;
return $this;
}
/**
* Get all only included fields
*
* @param array $keys The keys.
*
* @return array
*
* @since 1.0.0
*/
public function only(array $keys)
{
$data = $this->all();
return \array_intersect_key($data, \array_flip($keys));
}
/**
* Set all included fields
*
* @param array $keys The keys.
*
* @return static
*
* @since 1.0.0
*/
public function pick(array $keys)
{
$this->picked_keys = $keys;
return $this;
}
/**
* Get public properties with values
*
* @return array
*
* @since 1.0.0
*/
protected function get_public_vars() : array
{
$vars = [];
$reflection = new \ReflectionClass($this);
foreach ($reflection->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
$name = $property->getName();
$vars[$name] = $this->{$name};
}
return $vars;
}
/**
* Get casts
*
* @return array
*
* @since 1.0.0
*/
protected function get_casts()
{
return $this->casts;
}
/**
* Cast all attributes
*
* @return $this
*
* @since 1.0.0
*/
protected function cast_attributes()
{
if ($this->prepare_for_display) {
return $this;
}
foreach ($this->get_casts() as $key => $cast) {
$field = \explode('.', $key);
$attribute = $field[0];
if (\property_exists($this, $attribute)) {
\array_shift($field);
$this->{$attribute} = $this->traverse_and_cast_attribute($this->{$attribute}, $field, $cast);
}
}
// Mark as prepared for display
$this->prepare_for_display = \true;
return $this;
}
/**
* Traverse and cast attribute
*
* @param mixed $current_field_value The current field value.
* @param array $key_segments The key segments.
* @param mixed $cast The cast.
*
* @return mixed
*
* @throws \Exception
*
* @since 1.0.0
*/
protected function traverse_and_cast_attribute($current_field_value, array $key_segments, $cast)
{
if (empty($key_segments)) {
if (\is_subclass_of($cast, CastAttribute::class)) {
$attribute_class = new $cast();
return $attribute_class->get($current_field_value, $current_field_value);
} elseif (\is_callable($cast)) {
return $cast();
}
throw new Exception('Cast must be an instance of ' . CastAttribute::class . ' or a callable');
}
$segment = \array_shift($key_segments);
if ($segment === '*') {
if (!\is_array($current_field_value)) {
return $current_field_value;
}
foreach ($current_field_value as $key => $value) {
$current_field_value[$key] = $this->traverse_and_cast_attribute($value, $key_segments, $cast);
}
} elseif (\is_array($current_field_value) && \array_key_exists($segment, $current_field_value)) {
$current_field_value[$segment] = $this->traverse_and_cast_attribute($current_field_value[$segment], $key_segments, $cast);
} elseif (\is_object($current_field_value) && \property_exists($current_field_value, $segment)) {
$current_field_value->{$segment} = $this->traverse_and_cast_attribute($current_field_value->{$segment}, $key_segments, $cast);
}
return $current_field_value;
}
/**
* Get the values.
*
* @return mixed
*
* @since 1.0.0
*/
public function get_values()
{
return $this->cast_attributes();
}
}