Parse and validate AT Protocol Lexicons with DTO generation for Laravel
1<?php
2
3namespace SocialDept\AtpSchema\Validation\Rules;
4
5use Closure;
6use Illuminate\Contracts\Validation\ValidationRule;
7
8class Cid implements ValidationRule
9{
10 /**
11 * Run the validation rule.
12 */
13 public function validate(string $attribute, mixed $value, Closure $fail): void
14 {
15 if (! is_string($value)) {
16 $fail("The {$attribute} must be a string.");
17
18 return;
19 }
20
21 if (! $this->isValidCid($value)) {
22 $fail("The {$attribute} is not a valid CID.");
23 }
24 }
25
26 /**
27 * Validate CID format.
28 *
29 * CID (Content Identifier) is typically base58 or base32 encoded
30 * CIDv0: Qm... (base58, 46 characters)
31 * CIDv1: b... (base32) or z... (base58)
32 */
33 protected function isValidCid(string $value): bool
34 {
35 $length = strlen($value);
36
37 // CIDv0: Starts with Qm and is 46 characters
38 if (str_starts_with($value, 'Qm') && $length === 46) {
39 return $this->isBase58($value);
40 }
41
42 // CIDv1: Starts with 'b' (base32) or 'z' (base58)
43 if (str_starts_with($value, 'b') && $length > 10) {
44 return $this->isBase32($value);
45 }
46
47 if (str_starts_with($value, 'z') && $length > 10) {
48 return $this->isBase58($value);
49 }
50
51 // Also accept bafy... (base32 CIDv1)
52 if (str_starts_with($value, 'bafy') && $length > 10) {
53 return $this->isBase32($value);
54 }
55
56 return false;
57 }
58
59 /**
60 * Check if string is valid base58.
61 */
62 protected function isBase58(string $value): bool
63 {
64 // Base58 alphabet (no 0, O, I, l)
65 return (bool) preg_match('/^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$/', $value);
66 }
67
68 /**
69 * Check if string is valid base32.
70 */
71 protected function isBase32(string $value): bool
72 {
73 // Base32 lowercase alphabet
74 return (bool) preg_match('/^[a-z2-7]+$/', $value);
75 }
76}