Skip to content

Commit af5495c

Browse files
committed
Fix incorrect handling of KeyValues - including inheritance and wrapping in _G when needed
1 parent 2bb0ef9 commit af5495c

File tree

4 files changed

+95
-31
lines changed

4 files changed

+95
-31
lines changed

src/Frame.php

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
class Frame
1111
{
1212
private readonly ?self $originalParent;
13+
/** @var array<string, KeyValueDTO> */
14+
private array $keyValues;
1315

1416
public function __construct(
1517
private readonly string $name,
@@ -147,43 +149,33 @@ public function getInherits(): array
147149
}
148150

149151
/**
150-
* @return array<string, array{1: string, 2: string}> [key => [formattedValue, type]] value is formatted to be written directly into lua
152+
* @return array<string, KeyValueDTO> [key => KeyValueDTO]
151153
*/
152154
public function getKeyValues(): array
153155
{
154156
if (!isset($this->xmlElement->KeyValues)) {
155157
return [];
156158
}
157-
$keyValues = [];
159+
if (isset($this->keyValues)) {
160+
return $this->keyValues;
161+
}
162+
$this->keyValues = [];
158163
foreach ($this->xmlElement->KeyValues as $child) {
159164
if (!isset($child->KeyValue)) {
160165
continue;
161166
}
162-
$keyValue = $child->KeyValue;
163-
$key = (string) $keyValue->attributes()['key'] ?? '';
164-
$value = (string) $keyValue->attributes()['value'] ?? '';
165-
$type = (string) $keyValue->attributes()['type'] ?: 'string';
166-
if ($key === '') {
167-
continue;
167+
foreach ($child->KeyValue as $keyValue) {
168+
$key = (string) $keyValue->attributes()['key'] ?? '';
169+
$value = (string) $keyValue->attributes()['value'] ?? '';
170+
$type = (string) $keyValue->attributes()['type'] ?: 'string';
171+
if ($key === '') {
172+
continue;
173+
}
174+
$this->keyValues[$key] = new KeyValueDTO($key, $value, KeyValueTypeEnum::from($type));
168175
}
169-
$value = match($type) {
170-
'number', 'global', 'boolean' => $value,
171-
'nil' => 'nil',
172-
'string' => json_encode($value), // json_encodes adds quotes to the string
173-
default => throw new RuntimeException("Unknown type: $type"),
174-
};
175-
$type = match($type) {
176-
'number' => 'number',
177-
'boolean' => 'boolean',
178-
'global' => 'any',
179-
'nil' => 'nil',
180-
'string' => 'string',
181-
default => throw new RuntimeException("Unknown type: $type"),
182-
};
183-
$keyValues[$key] = [$value, $type];
184176
}
185177

186-
return $keyValues;
178+
return $this->keyValues;
187179
}
188180

189181
public function getLineNumber(): int

src/KeyValueDTO.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App;
6+
7+
readonly class KeyValueDTO
8+
{
9+
public function __construct(
10+
public string $key,
11+
public string $value,
12+
public KeyValueTypeEnum $type,
13+
) {
14+
}
15+
}

src/KeyValueTypeEnum.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App;
6+
7+
enum KeyValueTypeEnum: string
8+
{
9+
case NUMBER = 'number';
10+
case STRING = 'string';
11+
case BOOLEAN = 'boolean';
12+
case GLOBAL = 'global';
13+
case NIL = 'nil';
14+
15+
public function luaType(): string
16+
{
17+
return match ($this) {
18+
self::NUMBER => 'number',
19+
self::STRING => 'string',
20+
self::BOOLEAN => 'boolean',
21+
self::GLOBAL => 'any',
22+
self::NIL => 'nil',
23+
};
24+
}
25+
}

src/XmlFileParser.php

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,9 @@ private function childHasInterestingData(Frame $child): bool
193193

194194
private function wrapInGIfNeeded(string $name): string
195195
{
196-
if (str_contains($name, '$') || str_contains($name, '-')) {
197-
return '_G["' . $name . '"]';
196+
if (!preg_match('/^[A-z][A-z0-9]*$/', $name)) {
197+
// json_encode adds and properly escapes quotes
198+
return '_G[' . json_encode($name) . ']';
198199
}
199200

200201
return $name;
@@ -209,6 +210,7 @@ private function writeFrame(Frame $frame, ?string $linkPrefix, ?string $typeOver
209210

210211
$data = '';
211212
$globalChildrenWithParentKey = [];
213+
/** @var array<string, KeyValueDTO> $inheritedKeyValues */
212214
$inheritedKeyValues = [];
213215
foreach ($frame->getChildren() as $child) {
214216
if ($this->childHasInterestingData($child)) {
@@ -268,12 +270,15 @@ private function writeFrame(Frame $frame, ?string $linkPrefix, ?string $typeOver
268270
return $data . "\n";
269271
}
270272

273+
/**
274+
* @param array<string, KeyValueDTO> $inheritedKeyValues
275+
*/
271276
private function handleInherits(Frame $frame, array &$inheritedKeyValues, ?string $linkPrefix, string &$data): void
272277
{
273278
foreach ($this->iterateInherits($frame) as $template) {
274279
$template = $template->withParent($frame);
275280
foreach ($template->getKeyValues() as $key => $value) {
276-
$inheritedKeyValues[$key] = $value;
281+
$inheritedKeyValues[$key] ??= $value;
277282
}
278283
foreach ($template->getChildren() as $child) {
279284
$clone = $child->withParent($frame);
@@ -287,7 +292,11 @@ private function handleInherits(Frame $frame, array &$inheritedKeyValues, ?strin
287292
: $child->getType(),
288293
);
289294
if ($clone->getParentKey()) {
290-
$inheritedKeyValues[$clone->getParentKey()] = [$clone->getName()];
295+
$inheritedKeyValues[$clone->getParentKey()] ??= new KeyValueDTO(
296+
$clone->getParentKey(),
297+
$clone->getName(),
298+
KeyValueTypeEnum::GLOBAL,
299+
);
291300
}
292301
}
293302
}
@@ -308,7 +317,7 @@ private function writeClassAndFieldHints(Frame $frame): string
308317
}
309318
$data .= "\n";
310319
foreach ($frame->getKeyValues() as $key => $value) {
311-
$data .= '--- @field ' . $key . ' ' . $value[1] . ' # ' . $value[0] . "\n";
320+
$data .= '--- @field ' . $key . ' ' . $value->type->luaType() . ' # ' . $value->value . "\n";
312321
}
313322
$allParentKeys = [];
314323
$allParentArrays = [];
@@ -383,23 +392,46 @@ private function iterateInherits(Frame $frame): iterable
383392
}
384393
}
385394

395+
/**
396+
* @param array<string, KeyValueDTO> $inheritedKeyValues
397+
*/
386398
private function writeExplicitGlobal(
387399
Frame $frame,
388400
array $globalChildrenWithParentKey,
389401
array $inheritedKeyValues,
390402
): string {
391403
$name = $this->wrapInGIfNeeded($frame->getName());
392404
$data = $name . " = {}\n";
405+
$definedKeys = [];
393406
foreach ($globalChildrenWithParentKey as $key => $value) {
407+
$definedKeys[$key] = true;
394408
$data .= $name . '["' . $key . '"] = ' . $this->wrapInGIfNeeded($value) . "\n";
395409
}
396410
foreach ($frame->getKeyValues() as $key => $value) {
397-
$data .= $name . '["' . $key . '"] = ' . $this->wrapInGIfNeeded($value[0]) . "\n";
411+
if (isset($definedKeys[$key])) {
412+
continue;
413+
}
414+
$definedKeys[$key] = true;
415+
$data .= $name . '["' . $key . '"] = ' . $this->formatKeyValue($value) . "\n";
398416
}
399417
foreach ($inheritedKeyValues as $key => $value) {
400-
$data .= $name . '["' . $key . '"] = ' . $this->wrapInGIfNeeded($value[0]) . " -- inherited\n";
418+
if (isset($definedKeys[$key])) {
419+
continue;
420+
}
421+
$definedKeys[$key] = true;
422+
$data .= $name . '["' . $key . '"] = ' . $this->formatKeyValue($value) . " -- inherited\n";
401423
}
402424

403425
return $data;
404426
}
427+
428+
private function formatKeyValue(KeyValueDTO $keyValue): string
429+
{
430+
return match($keyValue->type) {
431+
KeyValueTypeEnum::STRING => json_encode($keyValue->value), // json_encode adds and properly escapes quotes
432+
KeyValueTypeEnum::NUMBER, KeyValueTypeEnum::BOOLEAN => $keyValue->value,
433+
KeyValueTypeEnum::GLOBAL => $this->wrapInGIfNeeded($keyValue->value),
434+
KeyValueTypeEnum::NIL => 'nil',
435+
};
436+
}
405437
}

0 commit comments

Comments
 (0)