Parse and validate AT Protocol Lexicons with DTO generation for Laravel
1<?php
2
3namespace SocialDept\AtpSchema\Generator;
4
5class ConstructorGenerator
6{
7 /**
8 * Property generator instance.
9 */
10 protected PropertyGenerator $propertyGenerator;
11
12 /**
13 * Stub renderer instance.
14 */
15 protected StubRenderer $renderer;
16
17 /**
18 * Create a new ConstructorGenerator.
19 */
20 public function __construct(
21 ?PropertyGenerator $propertyGenerator = null,
22 ?StubRenderer $renderer = null
23 ) {
24 $this->propertyGenerator = $propertyGenerator ?? new PropertyGenerator();
25 $this->renderer = $renderer ?? new StubRenderer();
26 }
27
28 /**
29 * Generate constructor with promoted properties.
30 *
31 * @param array<string, array<string, mixed>> $properties
32 * @param array<string> $required
33 */
34 public function generate(array $properties, array $required = []): string
35 {
36 if (empty($properties)) {
37 return $this->generateEmpty();
38 }
39
40 $parameters = $this->generateParameters($properties, $required);
41 $body = $this->generateBody($properties, $required);
42
43 return $this->renderer->render('constructor', [
44 'docBlock' => $this->generateDocBlock($properties, $required),
45 'parameters' => $parameters,
46 'body' => $body,
47 ]);
48 }
49
50 /**
51 * Generate empty constructor.
52 */
53 protected function generateEmpty(): string
54 {
55 return '';
56 }
57
58 /**
59 * Generate constructor parameters.
60 *
61 * @param array<string, array<string, mixed>> $properties
62 * @param array<string> $required
63 */
64 protected function generateParameters(array $properties, array $required = []): string
65 {
66 $params = [];
67
68 foreach ($properties as $name => $definition) {
69 $promoted = $this->propertyGenerator->generatePromoted($name, $definition, $required);
70 $params[] = ' '.$promoted.',';
71 }
72
73 // Remove trailing comma from last parameter
74 if (! empty($params)) {
75 $params[count($params) - 1] = rtrim($params[count($params) - 1], ',');
76 }
77
78 return implode("\n", $params);
79 }
80
81 /**
82 * Generate constructor body.
83 *
84 * @param array<string, array<string, mixed>> $properties
85 * @param array<string> $required
86 */
87 protected function generateBody(array $properties, array $required = []): string
88 {
89 // For promoted properties, constructor body is usually empty
90 // But we can add validation or initialization logic here if needed
91 return '';
92 }
93
94 /**
95 * Generate constructor documentation block.
96 *
97 * @param array<string, array<string, mixed>> $properties
98 * @param array<string> $required
99 */
100 protected function generateDocBlock(array $properties, array $required = []): string
101 {
102 if (empty($properties)) {
103 return '';
104 }
105
106 $lines = [' /**'];
107 $lines[] = ' * Create a new instance.';
108
109 // Add @param tags for each parameter
110 foreach ($properties as $name => $definition) {
111 $docType = $this->propertyGenerator->getDocType(
112 $definition,
113 ! in_array($name, $required)
114 );
115 $description = $definition['description'] ?? null;
116
117 if ($description) {
118 $lines[] = ' * @param '.$docType.' $'.$name.' '.$description;
119 } else {
120 $lines[] = ' * @param '.$docType.' $'.$name;
121 }
122 }
123
124 $lines[] = ' */';
125
126 return implode("\n", $lines);
127 }
128
129 /**
130 * Generate constructor with assignments (non-promoted).
131 *
132 * @param array<string, array<string, mixed>> $properties
133 * @param array<string> $required
134 */
135 public function generateWithAssignments(array $properties, array $required = []): string
136 {
137 if (empty($properties)) {
138 return $this->generateEmpty();
139 }
140
141 $parameters = [];
142 $assignments = [];
143
144 foreach ($properties as $name => $definition) {
145 $signature = $this->propertyGenerator->generateSignature($name, $definition, $required);
146 $parameters[] = ' '.$signature.',';
147 $assignments[] = ' $this->'.$name.' = $'.$name.';';
148 }
149
150 // Remove trailing comma from last parameter
151 if (! empty($parameters)) {
152 $parameters[count($parameters) - 1] = rtrim($parameters[count($parameters) - 1], ',');
153 }
154
155 $params = implode("\n", $parameters);
156 $body = implode("\n", $assignments);
157
158 return " /**\n".
159 " * Create a new instance.\n".
160 " */\n".
161 " public function __construct(\n".
162 $params."\n".
163 " ) {\n".
164 $body."\n".
165 ' }';
166 }
167
168 /**
169 * Check if constructor should be generated.
170 *
171 * @param array<string, array<string, mixed>> $properties
172 */
173 public function shouldGenerate(array $properties): bool
174 {
175 return ! empty($properties);
176 }
177}