1: <?php
2:
3: namespace LaravelUi5\Core\Introspection\App;
4:
5: use DOMDocument;
6: use DOMElement;
7: use DOMXPath;
8: use JsonException;
9: use LogicException;
10:
11: final readonly class Ui5Bootstrap
12: {
13: private function __construct(
14: private array $attributes,
15: private array $resourceNamespaces,
16: private string $inlineScript,
17: private string $inlineCss
18: )
19: {
20: }
21:
22: /* -- API -------------------------------------------------------------- */
23:
24: /**
25: * @return array<string, string>
26: */
27: public function getAttributes(): array
28: {
29: return $this->attributes;
30: }
31:
32: /**
33: * @return string[]
34: */
35: public function getResourceNamespaces(): array
36: {
37: return $this->resourceNamespaces;
38: }
39:
40: public function getInlineScript(): string
41: {
42: return $this->inlineScript;
43: }
44:
45: public function getInlineCss(): string
46: {
47: return $this->inlineCss;
48: }
49:
50: /* -- Factory ---------------------------------------------------------- */
51:
52: /**
53: * @throws JsonException
54: */
55: public static function fromIndexHtml(string $path): self
56: {
57: $indexPath = "{$path}/index.html";
58:
59: if (!is_file($indexPath)) {
60: throw new LogicException("index.html not found at {$indexPath}");
61: }
62:
63: $html = file_get_contents($indexPath);
64:
65: $dom = new DOMDocument();
66: libxml_use_internal_errors(true);
67: $dom->loadHTML($html);
68: libxml_clear_errors();
69:
70: $xpath = new DOMXPath($dom);
71:
72: $bootstrapScript = $xpath
73: ->query('//script[@id="sap-ui-bootstrap"]')
74: ->item(0);
75:
76: if (!$bootstrapScript instanceof DOMElement) {
77: throw new LogicException('sap-ui-bootstrap script tag not found in index.html');
78: }
79:
80: $attributes = [];
81: $namespaces = [];
82:
83: foreach ($bootstrapScript->attributes as $attr) {
84: if (str_starts_with($attr->name, 'data-sap-ui-')) {
85: $key = str_replace('data-sap-ui-', '', $attr->name);
86:
87: if ($key === 'resourceroots') {
88: $roots = json_decode(
89: $attr->value,
90: true,
91: 512,
92: JSON_THROW_ON_ERROR
93: );
94: $namespaces = array_keys($roots);
95: } else {
96: $attributes[$key] = $attr->value;
97: }
98: }
99: }
100:
101: // First inline <script> without src
102: $inlineScript = trim(
103: $xpath->query('//script[not(@src)]')->item(0)?->nodeValue ?? ''
104: );
105:
106: // First <style>
107: $inlineCss = trim(
108: $xpath->query('//style')->item(0)?->nodeValue ?? ''
109: );
110:
111: return new self(
112: attributes: $attributes,
113: resourceNamespaces: $namespaces,
114: inlineScript: $inlineScript,
115: inlineCss: $inlineCss
116: );
117: }
118: }
119:
120: