Skip to content

Commit e245cf7

Browse files
committed
MC-24057: Implement static dependency analysis for wildcard and API urls
- added implementation for checking controller wildcard urls
1 parent 88485d5 commit e245cf7

File tree

3 files changed

+82
-43
lines changed

3 files changed

+82
-43
lines changed

dev/tests/static/framework/Magento/TestFramework/Dependency/PhpRule.php

Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,8 @@ public function __construct(
113113
array $pluginMap = [],
114114
array $whitelists = [],
115115
ClassScanner $classScanner = null
116-
) {
116+
)
117+
{
117118
$this->_mapRouters = $mapRouters;
118119
$this->_mapLayoutBlocks = $mapLayoutBlocks;
119120
$this->configReader = $configReader;
@@ -146,7 +147,7 @@ public function getDependencyInfo($currentModule, $fileType, $file, &$contents)
146147
);
147148
$dependenciesInfo = $this->considerCaseDependencies(
148149
$dependenciesInfo,
149-
$this->_caseGetUrl($currentModule, $contents)
150+
$this->_caseGetUrl($currentModule, $contents, $file)
150151
);
151152
$dependenciesInfo = $this->considerCaseDependencies(
152153
$dependenciesInfo,
@@ -304,12 +305,13 @@ private function isPluginDependency($dependent, $dependency)
304305
*
305306
* @param string $currentModule
306307
* @param string $contents
308+
* @param string $file
307309
* @return array
308310
* @throws LocalizedException
309311
* @throws \Exception
310312
* @SuppressWarnings(PMD.CyclomaticComplexity)
311313
*/
312-
protected function _caseGetUrl(string $currentModule, string &$contents): array
314+
protected function _caseGetUrl(string $currentModule, string &$contents, string $file): array
313315
{
314316
$dependencies = [];
315317
$pattern = '#(\->|:)(?<source>getUrl\(([\'"])(?<path>[a-zA-Z0-9\-_*\/]+)\3)\s*[,)]#';
@@ -321,11 +323,7 @@ protected function _caseGetUrl(string $currentModule, string &$contents): array
321323
$path = $item['path'];
322324
$returnedDependencies = [];
323325
if (strpos($path, '*') !== false) {
324-
/**
325-
* Skip processing wildcard urls since they always resolve to the current
326-
* route_front_name/area_front_name/controller_name
327-
*/
328-
continue;
326+
$returnedDependencies = $this->processWildcardUrl($path, $file);
329327
} elseif (preg_match('#rest(?<service>/V1/\w+)#i', $path, $apiMatch)) {
330328
$returnedDependencies = $this->processApiUrl($apiMatch['service']);
331329
} else {
@@ -347,6 +345,66 @@ protected function _caseGetUrl(string $currentModule, string &$contents): array
347345
return $dependencies;
348346
}
349347

348+
/**
349+
* Helper method to get module dependencies used by a wildcard Url
350+
*
351+
* @param string $urlPath
352+
* @param string $filePath
353+
* @return string[]
354+
* @throws NoSuchActionException
355+
*/
356+
private function processWildcardUrl(string $urlPath, string $filePath)
357+
{
358+
$filePath = strtolower($filePath);
359+
$urlRoutePieces = explode('/', $urlPath);
360+
$routeId = array_shift($urlRoutePieces);
361+
362+
//Skip route wildcard processing as this requires using the routeMapper
363+
if ('*' === $routeId) {
364+
return [];
365+
}
366+
$filePathInfo = pathinfo($filePath);
367+
$fileActionName = $filePathInfo['filename'];
368+
$filePathPieces = explode('/', $filePathInfo['dirname']);
369+
370+
/**
371+
* Only handle Controllers. ie: Ignore Blocks, Templates, and Models due to complexity in static resolution
372+
* of route
373+
*/
374+
if (in_array('block', $filePathPieces)
375+
|| in_array('model', $filePathPieces)
376+
|| $filePathInfo['extension'] === 'phtml'
377+
) {
378+
return [];
379+
}
380+
$fileControllerIndex = array_search('adminhtml', $filePathPieces)
381+
?? array_search('controller', $filePathPieces);
382+
383+
$controllerName = array_shift($urlRoutePieces);
384+
if ('*' === $controllerName) {
385+
$fileControllerName = implode("_", array_slice($filePathPieces, $fileControllerIndex + 1));
386+
$controllerName = $fileControllerName;
387+
}
388+
389+
if (empty($urlRoutePieces) || !$urlRoutePieces[0]) {
390+
return $this->routeMapper->getDependencyByRoutePath(
391+
$routeId,
392+
$controllerName,
393+
UrlInterface::DEFAULT_ACTION_NAME
394+
);
395+
}
396+
397+
$actionName = array_shift($urlRoutePieces);
398+
if ('*' === $actionName) {
399+
$actionName = $fileActionName;
400+
}
401+
return $this->routeMapper->getDependencyByRoutePath(
402+
$routeId,
403+
$controllerName,
404+
$actionName
405+
);
406+
}
407+
350408
/**
351409
* Helper method to get module dependencies used by a standard URL
352410
*
@@ -357,7 +415,7 @@ protected function _caseGetUrl(string $currentModule, string &$contents): array
357415
private function processStandardUrl(string $path)
358416
{
359417
$pattern = '#(?<route_id>[a-z0-9\-_]{3,})'
360-
. '\/?(?<controller_name>[a-z0-9\-_]+)?\/?(?<action_name>[a-z0-9\-_]+)?#i';
418+
. '\/?(?<controller_name>[a-z0-9\-_]+)?\/?(?<action_name>[a-z0-9\-_]+)?#i';
361419
if (preg_match($pattern, $path, $match)) {
362420
$routeId = $match['route_id'];
363421
$controllerName = $match['controller_name'] ?? UrlInterface::DEFAULT_CONTROLLER_NAME;

dev/tests/static/testsuite/Magento/Test/Integrity/Dependency/Converter.php

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,10 @@ class Converter implements \Magento\Framework\Config\ConverterInterface
1313
/**#@+
1414
* Array keys for config internal representation.
1515
*/
16-
const KEY_SERVICE_CLASS = 'class';
17-
const KEY_URL = 'url';
18-
const KEY_SERVICE_METHOD = 'method';
19-
const KEY_SECURE = 'secure';
20-
const KEY_ROUTES = 'routes';
21-
const KEY_ACL_RESOURCES = 'resources';
22-
const KEY_SERVICE = 'service';
23-
const KEY_SERVICES = 'services';
24-
const KEY_FORCE = 'force';
25-
const KEY_VALUE = 'value';
26-
const KEY_DATA_PARAMETERS = 'parameters';
27-
const KEY_SOURCE = 'source';
28-
const KEY_METHOD = 'method';
29-
const KEY_METHODS = 'methods';
30-
const KEY_DESCRIPTION = 'description';
31-
const KEY_REAL_SERVICE_METHOD = 'realMethod';
16+
private const KEY_SERVICE_CLASS = 'class';
17+
private const KEY_SERVICE_METHOD = 'method';
18+
private const KEY_ROUTES = 'routes';
19+
private const KEY_SERVICE = 'service';
3220
/**#@-*/
3321

3422
/**

dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use Magento\TestFramework\Dependency\ReportsConfigRule;
2727
use Magento\TestFramework\Dependency\Route\RouteMapper;
2828
use Magento\TestFramework\Dependency\VirtualType\VirtualTypeMapper;
29+
use Magento\TestFramework\Workaround\Override\Config\ValidationState;
2930

3031
/**
3132
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
@@ -284,25 +285,17 @@ protected static function _initRules()
284285
// In case primary module declaring the table cannot be identified, use any module referencing this table
285286
$tableToModuleMap = array_merge($tableToAnyModuleMap, $tableToPrimaryModuleMap);
286287

287-
$objectManager = Bootstrap::create(BP, $_SERVER)->getObjectManager();
288-
289-
$webApiConfigReader = $objectManager->create(
290-
Reader::class,
288+
$webApiConfigReader = new Reader(
289+
new WebapiFileResolver(self::getComponentRegistrar()),
290+
new Converter(),
291+
new SchemaLocator(self::getComponentRegistrar()),
292+
new ValidationState(),
293+
'webapi.xml',
291294
[
292-
'fileResolver' => new WebapiFileResolver(
293-
self::getComponentRegistrar()
294-
),
295-
'converter' => new Converter(),
296-
'schemaLocator' => new SchemaLocator(
297-
self::getComponentRegistrar()
298-
),
299-
'fileName' => 'webapi.xml',
300-
'idAttributes' => [
301-
'/routes/route' => ['url', 'method'],
302-
'/routes/route/resources/resource' => 'ref',
303-
'/routes/route/data/parameter' => 'name'
304-
],
305-
]
295+
'/routes/route' => ['url', 'method'],
296+
'/routes/route/resources/resource' => 'ref',
297+
'/routes/route/data/parameter' => 'name'
298+
],
306299
);
307300

308301
self::$_rulesInstances = [

0 commit comments

Comments
 (0)