Parse and validate AT Protocol Lexicons with DTO generation for Laravel
1<?php
2
3namespace SocialDept\AtpSchema\Data\Types;
4
5use SocialDept\AtpSchema\Data\TypeDefinition;
6use SocialDept\AtpSchema\Exceptions\RecordValidationException;
7
8class BytesType extends TypeDefinition
9{
10 /**
11 * Minimum byte length.
12 */
13 public readonly ?int $minLength;
14
15 /**
16 * Maximum byte length.
17 */
18 public readonly ?int $maxLength;
19
20 /**
21 * Create a new BytesType.
22 */
23 public function __construct(
24 ?string $description = null,
25 ?int $minLength = null,
26 ?int $maxLength = null
27 ) {
28 parent::__construct('bytes', $description);
29
30 $this->minLength = $minLength;
31 $this->maxLength = $maxLength;
32 }
33
34 /**
35 * Create from array data.
36 */
37 public static function fromArray(array $data): self
38 {
39 return new self(
40 description: $data['description'] ?? null,
41 minLength: $data['minLength'] ?? null,
42 maxLength: $data['maxLength'] ?? null
43 );
44 }
45
46 /**
47 * Convert to array.
48 */
49 public function toArray(): array
50 {
51 $array = ['type' => $this->type];
52
53 if ($this->description !== null) {
54 $array['description'] = $this->description;
55 }
56
57 if ($this->minLength !== null) {
58 $array['minLength'] = $this->minLength;
59 }
60
61 if ($this->maxLength !== null) {
62 $array['maxLength'] = $this->maxLength;
63 }
64
65 return $array;
66 }
67
68 /**
69 * Validate a value against this type definition.
70 */
71 public function validate(mixed $value, string $path = ''): void
72 {
73 if (! is_string($value)) {
74 throw RecordValidationException::invalidType($path, 'bytes (base64 string)', gettype($value));
75 }
76
77 // Validate base64 encoding
78 $decoded = base64_decode($value, true);
79
80 if ($decoded === false || base64_encode($decoded) !== $value) {
81 throw RecordValidationException::invalidValue($path, 'must be valid base64-encoded data');
82 }
83
84 // Length validation on decoded bytes
85 $length = strlen($decoded);
86
87 if ($this->minLength !== null && $length < $this->minLength) {
88 throw RecordValidationException::invalidValue($path, "must be at least {$this->minLength} bytes");
89 }
90
91 if ($this->maxLength !== null && $length > $this->maxLength) {
92 throw RecordValidationException::invalidValue($path, "must be at most {$this->maxLength} bytes");
93 }
94 }
95}