Parse and validate AT Protocol Lexicons with DTO generation for Laravel
1<?php
2
3namespace SocialDept\AtpSchema\Generator;
4
5class PropertyGenerator
6{
7 /**
8 * Type mapper instance.
9 */
10 protected TypeMapper $typeMapper;
11
12 /**
13 * Stub renderer instance.
14 */
15 protected StubRenderer $renderer;
16
17 /**
18 * Create a new PropertyGenerator.
19 */
20 public function __construct(?TypeMapper $typeMapper = null, ?StubRenderer $renderer = null)
21 {
22 $this->typeMapper = $typeMapper ?? new TypeMapper();
23 $this->renderer = $renderer ?? new StubRenderer();
24 }
25
26 /**
27 * Generate a single property.
28 *
29 * @param array<string, mixed> $definition
30 * @param array<string> $required
31 */
32 public function generate(string $name, array $definition, array $required = []): string
33 {
34 $isRequired = in_array($name, $required);
35 $phpType = $this->typeMapper->toPhpType($definition, ! $isRequired);
36 $docType = $this->typeMapper->toPhpDocType($definition, ! $isRequired);
37 $description = $definition['description'] ?? null;
38 $default = $this->getDefaultValue($definition, $isRequired);
39
40 return $this->renderer->render('property', [
41 'docBlock' => $this->generateDocBlock($description, $docType),
42 'visibility' => 'public ',
43 'static' => '',
44 'readonly' => 'readonly ',
45 'type' => $phpType,
46 'name' => $name,
47 'default' => $default,
48 ]);
49 }
50
51 /**
52 * Generate multiple properties.
53 *
54 * @param array<string, array<string, mixed>> $properties
55 * @param array<string> $required
56 * @return array<string>
57 */
58 public function generateMultiple(array $properties, array $required = []): array
59 {
60 $result = [];
61
62 foreach ($properties as $name => $definition) {
63 $result[] = $this->generate($name, $definition, $required);
64 }
65
66 return $result;
67 }
68
69 /**
70 * Generate property documentation block.
71 */
72 protected function generateDocBlock(?string $description, string $type): string
73 {
74 $lines = ['/**'];
75
76 if ($description) {
77 $lines[] = ' * '.$description;
78 $lines[] = ' *';
79 }
80
81 $lines[] = ' * @var '.$type;
82 $lines[] = ' */';
83
84 return implode("\n", $lines);
85 }
86
87 /**
88 * Get default value for property.
89 *
90 * @param array<string, mixed> $definition
91 */
92 protected function getDefaultValue(array $definition, bool $isRequired): string
93 {
94 if ($isRequired) {
95 return '';
96 }
97
98 $default = $this->typeMapper->getDefaultValue($definition);
99
100 if ($default !== null) {
101 return ' = '.$default;
102 }
103
104 return '';
105 }
106
107 /**
108 * Generate property signature (for constructor parameters).
109 *
110 * @param array<string, mixed> $definition
111 * @param array<string> $required
112 */
113 public function generateSignature(string $name, array $definition, array $required = []): string
114 {
115 $isRequired = in_array($name, $required);
116 $phpType = $this->typeMapper->toPhpType($definition, ! $isRequired);
117 $default = $this->getDefaultValue($definition, $isRequired);
118
119 return $phpType.' $'.$name.$default;
120 }
121
122 /**
123 * Generate promoted property signature (for constructor).
124 *
125 * @param array<string, mixed> $definition
126 * @param array<string> $required
127 */
128 public function generatePromoted(string $name, array $definition, array $required = []): string
129 {
130 $isRequired = in_array($name, $required);
131 $phpType = $this->typeMapper->toPhpType($definition, ! $isRequired);
132 $default = $this->getDefaultValue($definition, $isRequired);
133
134 return 'public readonly '.$phpType.' $'.$name.$default;
135 }
136
137 /**
138 * Check if property should be nullable.
139 *
140 * @param array<string, mixed> $definition
141 * @param array<string> $required
142 */
143 public function isNullable(string $name, array $definition, array $required = []): bool
144 {
145 return ! in_array($name, $required);
146 }
147
148 /**
149 * Get property type.
150 *
151 * @param array<string, mixed> $definition
152 */
153 public function getType(array $definition, bool $nullable = false): string
154 {
155 return $this->typeMapper->toPhpType($definition, $nullable);
156 }
157
158 /**
159 * Get property doc type.
160 *
161 * @param array<string, mixed> $definition
162 */
163 public function getDocType(array $definition, bool $nullable = false): string
164 {
165 return $this->typeMapper->toPhpDocType($definition, $nullable);
166 }
167}