Skip to content

Commit

Permalink
TASK: Move the logic of handling the mvc.routes setting into Configur…
Browse files Browse the repository at this point in the history
…ationRoutesProvider and include the Annotation handling there.
  • Loading branch information
mficzel committed Mar 14, 2024
1 parent 55eaaf6 commit fee7d9b
Show file tree
Hide file tree
Showing 5 changed files with 268 additions and 273 deletions.
169 changes: 1 addition & 168 deletions Neos.Flow/Classes/Configuration/Loader/RoutesLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,9 @@
use Neos\Flow\Configuration\Exception\RecursionException;
use Neos\Flow\Configuration\Source\YamlSource;
use Neos\Flow\Core\ApplicationContext;
use Neos\Flow\Package\FlowPackageInterface;
use Neos\Flow\Package\PackageInterface;
use Neos\Utility\Arrays;
use Neos\Utility\PositionalArraySorter;

class RoutesLoader implements LoaderInterface
{
/**
* The maximum number of recursions when merging subroute configurations.
*
* @var integer
*/
private const MAXIMUM_SUBROUTE_RECURSIONS = 99;

/**
* @var YamlSource
*/
Expand Down Expand Up @@ -66,162 +55,6 @@ public function load(array $packages, ApplicationContext $context): array
}
$routesConfiguration[] = $this->yamlSource->load(FLOW_PATH_CONFIGURATION . ConfigurationManager::CONFIGURATION_TYPE_ROUTES);
$routesConfiguration = array_merge([], ...$routesConfiguration);

$routeSettings = $this->configurationManager->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, 'Neos.Flow.mvc.routes') ?? [];

$routesConfiguration = $this->includeSubRoutesFromSettings($routesConfiguration, $routeSettings);
return $this->mergeRoutesWithSubRoutes($packages, $context, $routesConfiguration);
}

/**
* Merges routes from Neos.Flow.mvc.routes settings into $routeDefinitions
* NOTE: Routes from settings will always be appended to existing route definitions from the main Routes configuration!
*
* @param array $routeDefinitions
* @param array $routeSettings
* @return array
*/
protected function includeSubRoutesFromSettings(array $routeDefinitions, array $routeSettings): array
{
$sortedRouteSettings = (new PositionalArraySorter($routeSettings))->toArray();
foreach ($sortedRouteSettings as $packageKey => $routeFromSettings) {
if ($routeFromSettings === false) {
continue;
}
$subRoutesName = $packageKey . 'SubRoutes';
$subRoutesConfiguration = ['package' => $packageKey];
if (isset($routeFromSettings['variables'])) {
$subRoutesConfiguration['variables'] = $routeFromSettings['variables'];
}
if (isset($routeFromSettings['suffix'])) {
$subRoutesConfiguration['suffix'] = $routeFromSettings['suffix'];
}
$routeDefinitions[] = [
'name' => $packageKey,
'uriPattern' => '<' . $subRoutesName . '>',
'subRoutes' => [
$subRoutesName => $subRoutesConfiguration
]
];
}

return $routeDefinitions;
}

/**
* Loads specified sub routes and builds composite routes.
*
* @param PackageInterface[] $packages
* @param ApplicationContext $context
* @param array $routesConfiguration
* @param int $subRoutesRecursionLevel Counts how many SubRoutes have been loaded. If this number exceeds MAXIMUM_SUBROUTE_RECURSIONS, an exception is thrown
* @return array
* @throws ParseErrorException | RecursionException| ConfigurationException
*/
protected function mergeRoutesWithSubRoutes(array $packages, ApplicationContext $context, array $routesConfiguration, int $subRoutesRecursionLevel = 0): array
{
$mergedRoutesConfiguration = [];
foreach ($routesConfiguration as $routeConfiguration) {
if (!isset($routeConfiguration['subRoutes'])) {
$mergedRoutesConfiguration[] = $routeConfiguration;
continue;
}
$mergedSubRoutesConfiguration = [$routeConfiguration];
foreach ($routeConfiguration['subRoutes'] as $subRouteKey => $subRouteOptions) {
if (!isset($subRouteOptions['package'])) {
throw new ParseErrorException(sprintf('Missing package configuration for SubRoute in Route "%s".', ($routeConfiguration['name'] ?? 'unnamed Route')), 1318414040);
}
if (!isset($packages[$subRouteOptions['package']])) {
throw new ParseErrorException(sprintf('The SubRoute Package "%s" referenced in Route "%s" is not available.', $subRouteOptions['package'], ($routeConfiguration['name'] ?? 'unnamed Route')), 1318414040);
}
/** @var FlowPackageInterface $package */
$package = $packages[$subRouteOptions['package']];
$subRouteFilename = 'Routes';
if (isset($subRouteOptions['suffix'])) {
$subRouteFilename .= '.' . $subRouteOptions['suffix'];
}
$subRouteConfiguration = [];
foreach (array_reverse($context->getHierarchy()) as $contextName) {
$subRouteFilePathAndName = $package->getConfigurationPath() . $contextName . '/' . $subRouteFilename;
$subRouteConfiguration[] = $this->yamlSource->load($subRouteFilePathAndName);
}
$subRouteFilePathAndName = $package->getConfigurationPath() . $subRouteFilename;
$subRouteConfiguration[] = $this->yamlSource->load($subRouteFilePathAndName);
if ($subRoutesRecursionLevel > self::MAXIMUM_SUBROUTE_RECURSIONS) {
throw new RecursionException(sprintf('Recursion level of SubRoutes exceed ' . self::MAXIMUM_SUBROUTE_RECURSIONS . ', probably because of a circular reference. Last successfully loaded route configuration is "%s".', $subRouteFilePathAndName), 1361535753);
}
$subRouteConfiguration = array_merge([], ...$subRouteConfiguration);

$subRouteConfiguration = $this->mergeRoutesWithSubRoutes($packages, $context, $subRouteConfiguration, $subRoutesRecursionLevel + 1);
$mergedSubRoutesConfiguration = $this->buildSubRouteConfigurations($mergedSubRoutesConfiguration, $subRouteConfiguration, $subRouteKey, $subRouteOptions);
}
$mergedRoutesConfiguration = array_merge($mergedRoutesConfiguration, $mergedSubRoutesConfiguration);
}

return $mergedRoutesConfiguration;
}

/**
* Merges all routes in $routesConfiguration with the sub routes in $subRoutesConfiguration
*
* @param array $routesConfiguration
* @param array $subRoutesConfiguration
* @param string $subRouteKey the key of the sub route: <subRouteKey>
* @param array $subRouteOptions
* @return array the merged route configuration
* @throws ParseErrorException
*/
protected function buildSubRouteConfigurations(array $routesConfiguration, array $subRoutesConfiguration, string $subRouteKey, array $subRouteOptions): array
{
$variables = $subRouteOptions['variables'] ?? [];
$mergedSubRoutesConfigurations = [];
foreach ($subRoutesConfiguration as $subRouteConfiguration) {
foreach ($routesConfiguration as $routeConfiguration) {
$mergedSubRouteConfiguration = $subRouteConfiguration;
$mergedSubRouteConfiguration['name'] = sprintf('%s :: %s', $routeConfiguration['name'] ?? 'Unnamed Route', $subRouteConfiguration['name'] ?? 'Unnamed Subroute');
$mergedSubRouteConfiguration['name'] = $this->replacePlaceholders($mergedSubRouteConfiguration['name'], $variables);
if (!isset($mergedSubRouteConfiguration['uriPattern'])) {
throw new ParseErrorException('No uriPattern defined in route configuration "' . $mergedSubRouteConfiguration['name'] . '".', 1274197615);
}
if ($mergedSubRouteConfiguration['uriPattern'] !== '') {
$mergedSubRouteConfiguration['uriPattern'] = $this->replacePlaceholders($mergedSubRouteConfiguration['uriPattern'], $variables);
$mergedSubRouteConfiguration['uriPattern'] = $this->replacePlaceholders($routeConfiguration['uriPattern'], [$subRouteKey => $mergedSubRouteConfiguration['uriPattern']]);
} else {
$mergedSubRouteConfiguration['uriPattern'] = rtrim($this->replacePlaceholders($routeConfiguration['uriPattern'], [$subRouteKey => '']), '/');
}
if (isset($mergedSubRouteConfiguration['defaults'])) {
$mergedSubRouteConfiguration['defaults'] = $this->replacePlaceholders($mergedSubRouteConfiguration['defaults'], $variables);
}
if (isset($mergedSubRouteConfiguration['routeParts'])) {
$mergedSubRouteConfiguration['routeParts'] = $this->replacePlaceholders($mergedSubRouteConfiguration['routeParts'], $variables);
}
$mergedSubRouteConfiguration = Arrays::arrayMergeRecursiveOverrule($routeConfiguration, $mergedSubRouteConfiguration);
unset($mergedSubRouteConfiguration['subRoutes']);
$mergedSubRoutesConfigurations[] = $mergedSubRouteConfiguration;
}
}

return $mergedSubRoutesConfigurations;
}

/**
* Replaces placeholders in the format <variableName> with the corresponding variable of the specified $variables collection.
*
* @param string|array $value
* @param array $variables
* @return array|string
*/
private function replacePlaceholders($value, array $variables)
{
if (is_array($value)) {
foreach ($value as $arrayKey => $arrayValue) {
$value[$arrayKey] = $this->replacePlaceholders($arrayValue, $variables);
}
} elseif (is_string($value)) {
foreach ($variables as $variableName => $variableValue) {
$value = str_replace('<' . $variableName . '>', $variableValue, $value);
}
}
return $value;
return $routesConfiguration;
}
}
85 changes: 0 additions & 85 deletions Neos.Flow/Classes/Mvc/Routing/AnnotationRoutesProvider.php

This file was deleted.

17 changes: 0 additions & 17 deletions Neos.Flow/Classes/Mvc/Routing/CombinedRoutesProvider.php

This file was deleted.

Loading

0 comments on commit fee7d9b

Please sign in to comment.