Something went wrong. Try again.
Parse and validate AT Protocol Lexicons with DTO generation for Laravel
Something went wrong. Try again.
5.4 kB · 226 lines
PHP
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227<?php
namespace SocialDept\AtpSchema\Data;
use Illuminate\Contracts\Support\Arrayable;use Illuminate\Contracts\Support\Jsonable;use JsonSerializable;use SocialDept\AtpSchema\Contracts\DiscriminatedUnion;use Stringable;
abstract class Data implements Arrayable, DiscriminatedUnion, Jsonable, JsonSerializable, Stringable{ /** * Get the lexicon NSID for this data type. */ abstract public static function getLexicon(): string;
/** * Get the lexicon NSID that identifies this union variant. * * This is an alias for getLexicon() to satisfy the DiscriminatedUnion contract. */ public static function getDiscriminator(): string { return static::getLexicon(); }
/** * Convert the data to an array. */ public function toArray(): array { $result = [];
foreach (get_object_vars($this) as $property => $value) { // Skip null values to exclude optional fields that aren't set if ($value !== null) { $result[$property] = $this->serializeValue($value); } }
return array_filter($result); }
/** * Convert the data to JSON. */ public function toJson($options = 0): string { return json_encode($this->jsonSerialize(), $options); }
/** * Convert the data for JSON serialization. */ public function jsonSerialize(): mixed { return $this->toArray(); }
/** * Convert the data to a string. */ public function __toString(): string { return $this->toJson(); }
/** * Serialize a value for output. */ protected function serializeValue(mixed $value): mixed { // Union variants must include $type for discrimination if ($value instanceof self) { return $value->toRecord(); }
if ($value instanceof Arrayable) { return $value->toArray(); }
// Preserve arrays with $type (open union data) if (is_array($value)) { return array_map(fn ($item) => $this->serializeValue($item), $value); }
if ($value instanceof \DateTimeInterface) { return $value->format(\DateTimeInterface::ATOM); }
return $value; }
/** * Create an instance from an array. */ abstract public static function fromArray(array $data): static;
/** * Create an instance from JSON. */ public static function fromJson(string $json): static { $data = json_decode($json, true);
if (json_last_error() !== JSON_ERROR_NONE) { throw new \InvalidArgumentException('Invalid JSON: '.json_last_error_msg()); }
return static::fromArray($data); }
/** * Create an instance from an AT Protocol record. * * This is an alias for fromArray for semantic clarity * when working with AT Protocol records. */ public static function fromRecord(array $record): static { return static::fromArray($record['value'] ?? $record); }
/** * Convert to an AT Protocol record. * * This is an alias for toArray for semantic clarity * when working with AT Protocol records. */ public function toRecord(): array { return [ ...$this->toArray(), '$type' => $this->getLexicon(), ]; }
/** * Check if two data objects are equal. */ public function equals(self $other): bool { if (! $other instanceof static) { return false; }
return $this->toArray() === $other->toArray(); }
/** * Get a hash of the data. */ public function hash(): string { return hash('sha256', $this->toJson()); }
/** * Validate the data against its lexicon schema. */ public function validate(): bool { if (! function_exists('schema_validate')) { return true; }
try { return schema_validate(static::getLexicon(), $this->toArray()); } catch (\Throwable) { return true; } }
/** * Validate and get errors. * * @return array<string, array<string>> */ public function validateWithErrors(): array { if (! function_exists('SocialDept\AtpSchema\schema')) { return []; }
try { return schema()->validateWithErrors(static::getLexicon(), $this->toArray()); } catch (\Throwable) { return []; } }
/** * Get a property value dynamically. */ public function __get(string $name): mixed { if (property_exists($this, $name)) { return $this->$name; }
throw new \InvalidArgumentException("Property {$name} does not exist on ".static::class); }
/** * Check if a property exists. */ public function __isset(string $name): bool { return property_exists($this, $name); }
/** * Clone the data with modified properties. */ public function with(array $properties): static { $data = $this->toArray();
foreach ($properties as $key => $value) { $data[$key] = $value; }
return static::fromArray($data); }}