Skip to content

Commit 203d83f

Browse files
committed
feat: enhance compatibility module parsing with robust XML handling
1 parent 789396b commit 203d83f

1 file changed

Lines changed: 55 additions & 25 deletions

File tree

src/Service/VendorFileMapper.php

Lines changed: 55 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ function () {
257257

258258
/**
259259
* Manual fallback to parse etc/frontend/di.xml for compatibility registration.
260-
* This is required because CLI runs in global scope and might not load frontend DI args.
260+
* Use DOMDocument for robust XML parsing.
261261
*
262262
* @param string $moduleName
263263
* @return string|null
@@ -270,36 +270,66 @@ private function parseCompatModuleXml(string $moduleName): ?string
270270
return null;
271271
}
272272

273-
$diPath = $path . '/etc/frontend/di.xml';
274-
if (!file_exists($diPath)) {
275-
return null;
276-
}
273+
// Check frontend/di.xml first, then global di.xml
274+
$diFile = $path . '/etc/frontend/di.xml';
275+
$globalDiFile = $path . '/etc/di.xml';
277276

278-
$xmlContent = file_get_contents($diPath);
279-
if (!$xmlContent) {
280-
return null;
277+
$filesToCheck = [];
278+
if (file_exists($diFile)) {
279+
$filesToCheck[] = $diFile;
280+
}
281+
if (file_exists($globalDiFile)) {
282+
$filesToCheck[] = $globalDiFile;
281283
}
282284

283-
// Simple Regex to extract original_module for this compat module
284-
// We look for the item where compat_module is defined as our module name
285-
// Then extract the sibling item original_module
285+
foreach ($filesToCheck as $diPath) {
286+
if (!is_file($diPath)) { // Extra check for symlinks/is_file
287+
continue;
288+
}
286289

287-
// Pattern to match the array structure for compatModules argument
288-
// <item name="..." xsi:type="array">
289-
// <item name="original_module" xsi:type="string">Original_Module</item>
290-
// <item name="compat_module" xsi:type="string">Compat_Module</item>
291-
// </item>
290+
$content = file_get_contents($diPath);
291+
if (!$content) {
292+
continue;
293+
}
292294

293-
// Note: Order of items inside the array is not guaranteed, so we check if the block contains our module name as compat_module
295+
$dom = new \DOMDocument();
296+
// Suppress warnings for malformed XML or namespace issues
297+
$libxmlState = libxml_use_internal_errors(true);
298+
$dom->loadXML($content);
299+
libxml_use_internal_errors($libxmlState);
294300

295-
if (preg_match_all('/<item\s+name="[^"]+"\s+xsi:type="array">(.*?)<\/item>/s', $xmlContent, $matches)) {
296-
foreach ($matches[1] as $block) {
297-
if (str_contains($block, $moduleName)) {
298-
// This block likely belongs to our module. Extract original_module
299-
if (preg_match('/<item\s+name="original_module"\s+xsi:type="string">([^<]+)<\/item>/', $block, $origMatch)) {
300-
return $origMatch[1];
301-
}
302-
}
301+
$xpath = new \DOMXPath($dom);
302+
// Register namespace? Usually not needed if query is correct
303+
// Try to find the compatModules argument node
304+
$query = "//argument[@name='compatModules'][@xsi:type='array']/item[@xsi:type='array']";
305+
$items = $xpath->query($query);
306+
307+
if ($items === false || $items->length === 0) {
308+
continue;
309+
}
310+
311+
foreach ($items as $item) {
312+
// Check children items for compat_module/original_module keys
313+
$compatModuleValue = null;
314+
$originalModuleValue = null;
315+
316+
/** @var \DOMElement $item */
317+
$childNodes = $xpath->query('item', $item);
318+
foreach ($childNodes as $childNode) {
319+
/** @var \DOMElement $childNode */
320+
$nameAttr = $childNode->getAttribute('name');
321+
$value = trim($childNode->nodeValue);
322+
323+
if ($nameAttr === 'compat_module') {
324+
$compatModuleValue = $value;
325+
} elseif ($nameAttr === 'original_module') {
326+
$originalModuleValue = $value;
327+
}
328+
}
329+
330+
if ($compatModuleValue === $moduleName && $originalModuleValue) {
331+
return $originalModuleValue;
332+
}
303333
}
304334
}
305335
} catch (\Throwable $e) {

0 commit comments

Comments
 (0)