1: <?php
2:
3: namespace LaravelUi5\Core\Introspection\Library;
4:
5: use Illuminate\Support\Facades\File;
6: use JsonException;
7: use LaravelUi5\Core\Contracts\Ui5Descriptor;
8: use LogicException;
9: use SimpleXMLElement;
10:
11: /**
12: * Represents the descriptor of a UI5 library as derived from the `.library` file.
13: *
14: * Note on naming:
15: * ----------------
16: * In UI5, the XML element `<library><name>` does NOT represent a human-readable
17: * name, but the technical UI5 namespace of the library (e.g. `com.laravelui5.core`).
18: *
19: * SAP historically uses the term "name" for this value, even though it semantically
20: * represents a namespace. To preserve this distinction and make the origin explicit,
21: * this descriptor intentionally stores the raw value as `$name`, while exposing it
22: * via {@see getNamespace()}.
23: *
24: * This makes the SAP-originated terminology visible at the boundary, while providing
25: * a clear and consistent semantic API to consumers.
26: */
27: final readonly class Ui5LibraryDescriptor extends Ui5Descriptor
28: {
29: public function __construct(
30: private string $name,
31: private string $vendor,
32: private string $version,
33: private string $title,
34: private string $documentation,
35: private array $dependencies
36: )
37: {
38: }
39:
40: /* -- Introspection API ------------------------------------------------ */
41:
42: public function getNamespace(): string
43: {
44: return $this->name;
45: }
46:
47: public function getVersion(): string
48: {
49: return $this->version;
50: }
51:
52: public function getTitle(): string
53: {
54: return $this->title;
55: }
56:
57: public function getDescription(): string
58: {
59: return $this->documentation;
60: }
61:
62: public function getVendor(): string
63: {
64: return $this->vendor;
65: }
66:
67: public function getDependencies(): array
68: {
69: return $this->dependencies;
70: }
71:
72: /* -- Factory ---------------------------------------------------------- */
73:
74: public static function fromLibraryXml(string $path, string $namespace, string $builder): self
75: {
76: $srcPath = $path
77: . '/dist/resources/'
78: . str_replace('.', '/', $namespace)
79: . '/.library';
80:
81: if (!File::exists($srcPath)) {
82: throw new LogicException("Missing .library file at {$path}. Run `$builder` first.");
83: }
84:
85: $xml = simplexml_load_file($srcPath);
86:
87: if (!$xml instanceof SimpleXMLElement) {
88: throw new LogicException("Invalid .library XML at {$srcPath}");
89: }
90:
91: $dependencies = [];
92:
93: if (isset($xml->dependencies->dependency)) {
94: foreach ($xml->dependencies->dependency as $dep) {
95: $dependencies[] = (string)$dep->libraryName;
96: }
97: }
98:
99: return new self(
100: name: (string)$xml->name,
101: vendor: (string)$xml->vendor,
102: version: (string)$xml->version,
103: title: (string)$xml->title,
104: documentation: (string)$xml->documentation,
105: dependencies: $dependencies
106: );
107: }
108:
109: /**
110: * @throws JsonException
111: */
112: public static function fromLibraryManifest(string $path, string $vendor): self
113: {
114: $manifestPath = "{$path}/manifest.json";
115:
116: if (!is_file($manifestPath)) {
117: throw new LogicException("manifest.json not found at {$manifestPath}");
118: }
119:
120: $manifest = json_decode(
121: file_get_contents($manifestPath),
122: true,
123: 512,
124: JSON_THROW_ON_ERROR
125: );
126:
127: if (!isset($manifest['sap.app']['id'])) {
128: throw new LogicException('Invalid manifest.json: missing sap.app.id');
129: }
130:
131: $namespace = $manifest['sap.app']['id'];
132:
133: $title = $manifest['sap.app']['title'] ?? 'Missing title';
134:
135: $description = $manifest['sap.app']['description'] ?? 'Missing description';
136:
137: $version = $manifest['sap.app']['applicationVersion']['version'] ?? '0.0.0';
138:
139: // Dependencies: sap.ui5.dependencies.libs (keys only!)
140: $libs = $manifest['sap.ui5']['dependencies']['libs'] ?? [];
141: if (!is_array($libs)) {
142: throw new LogicException('Invalid manifest.json: sap.ui5.dependencies.libs must be an object');
143: }
144:
145: $dependencies = array_keys($libs);
146:
147: return new self(
148: name: $namespace,
149: vendor: $vendor,
150: version: $version,
151: title: $title,
152: documentation: $description,
153: dependencies: $dependencies
154: );
155: }
156: }
157: