Parse and validate AT Protocol Lexicons with DTO generation for Laravel
1<?php
2
3namespace SocialDept\AtpSchema\Generator;
4
5use SocialDept\AtpSchema\Exceptions\GenerationException;
6
7class TemplateRenderer
8{
9 /**
10 * Templates directory.
11 */
12 protected ?string $templatesDirectory = null;
13
14 /**
15 * In-memory templates.
16 *
17 * @var array<string, string>
18 */
19 protected array $templates = [];
20
21 /**
22 * Create a new TemplateRenderer.
23 */
24 public function __construct(?string $templatesDirectory = null)
25 {
26 $this->templatesDirectory = $templatesDirectory;
27 $this->registerDefaultTemplates();
28 }
29
30 /**
31 * Render a template with given data.
32 */
33 public function render(string $templateName, array $data): string
34 {
35 $template = $this->getTemplate($templateName);
36
37 return $this->renderTemplate($template, $data);
38 }
39
40 /**
41 * Get template content.
42 */
43 protected function getTemplate(string $templateName): string
44 {
45 // Check in-memory templates first
46 if (isset($this->templates[$templateName])) {
47 return $this->templates[$templateName];
48 }
49
50 // Check templates directory
51 if ($this->templatesDirectory !== null) {
52 $path = $this->templatesDirectory.'/'.$templateName.'.php.template';
53
54 if (file_exists($path)) {
55 return file_get_contents($path);
56 }
57 }
58
59 throw GenerationException::templateNotFound($templateName);
60 }
61
62 /**
63 * Render template with data.
64 */
65 protected function renderTemplate(string $template, array $data): string
66 {
67 // Simple variable replacement
68 foreach ($data as $key => $value) {
69 if (is_scalar($value) || $value === null) {
70 $template = str_replace("{{{$key}}}", (string) $value, $template);
71 }
72 }
73
74 // Handle property lists
75 if (isset($data['properties']) && is_array($data['properties'])) {
76 $template = $this->renderProperties($template, $data['properties']);
77 }
78
79 return $template;
80 }
81
82 /**
83 * Render properties section.
84 */
85 protected function renderProperties(string $template, array $properties): string
86 {
87 $propertiesCode = [];
88
89 foreach ($properties as $prop) {
90 $typeHint = $prop['phpType'];
91 $nullable = ! ($prop['required'] ?? true);
92
93 if ($nullable && $typeHint !== 'mixed') {
94 $typeHint = '?'.$typeHint;
95 }
96
97 $docComment = '';
98 if ($prop['description'] ?? null) {
99 $docComment = " /**\n * {$prop['description']}\n */\n";
100 }
101
102 $propertiesCode[] = sprintf(
103 '%s public readonly %s $%s;',
104 $docComment,
105 $typeHint,
106 $prop['name']
107 );
108 }
109
110 $propertiesString = implode("\n\n", $propertiesCode);
111
112 return str_replace('{{properties}}', $propertiesString, $template);
113 }
114
115 /**
116 * Register default templates.
117 */
118 protected function registerDefaultTemplates(): void
119 {
120 $this->templates['record'] = <<<'PHP'
121<?php
122
123namespace {{namespace}};
124
125/**
126 * {{description}}
127 *
128 * NSID: {{nsid}}
129 */
130class {{className}}
131{
132{{properties}}
133
134 /**
135 * Create a new {{className}}.
136 */
137 public function __construct(
138 // Constructor parameters will be generated
139 ) {
140 // Property assignments will be generated
141 }
142
143 /**
144 * Create from array data.
145 */
146 public static function fromArray(array $data): self
147 {
148 return new self(
149 // Assignments will be generated
150 );
151 }
152
153 /**
154 * Convert to array.
155 */
156 public function toArray(): array
157 {
158 return [
159 // Array conversion will be generated
160 ];
161 }
162}
163
164PHP;
165
166 $this->templates['object'] = <<<'PHP'
167<?php
168
169namespace {{namespace}};
170
171/**
172 * {{description}}
173 */
174class {{className}}
175{
176{{properties}}
177
178 /**
179 * Create a new {{className}}.
180 */
181 public function __construct(
182 // Constructor parameters will be generated
183 ) {
184 // Property assignments will be generated
185 }
186
187 /**
188 * Create from array data.
189 */
190 public static function fromArray(array $data): self
191 {
192 return new self(
193 // Assignments will be generated
194 );
195 }
196
197 /**
198 * Convert to array.
199 */
200 public function toArray(): array
201 {
202 return [
203 // Array conversion will be generated
204 ];
205 }
206}
207
208PHP;
209 }
210
211 /**
212 * Register a custom template.
213 */
214 public function registerTemplate(string $name, string $template): void
215 {
216 $this->templates[$name] = $template;
217 }
218
219 /**
220 * Set templates directory.
221 */
222 public function setTemplatesDirectory(string $directory): void
223 {
224 $this->templatesDirectory = $directory;
225 }
226}