Copy in old dumping logic. Still being refactored.
parent
eba77ad5c6
commit
2c79c025d2
|
@ -3,17 +3,21 @@
|
||||||
namespace Drupal\Core\Routing;
|
namespace Drupal\Core\Routing;
|
||||||
|
|
||||||
use Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface;
|
use Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface;
|
||||||
|
use Symfony\Component\Routing\Route;
|
||||||
use Symfony\Component\Routing\RouteCollection;
|
use Symfony\Component\Routing\RouteCollection;
|
||||||
|
|
||||||
use Drupal\Core\Database\Connection;
|
use Drupal\Core\Database\Connection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Description of UrlMatcherDumper
|
* Description of UrlMatcherDumper.
|
||||||
*
|
|
||||||
* @author crell
|
|
||||||
*/
|
*/
|
||||||
class UrlMatcherDumper implements MatcherDumperInterface {
|
class UrlMatcherDumper implements MatcherDumperInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of path elements for a route pattern;
|
||||||
|
*/
|
||||||
|
const MAX_PARTS = 9;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The database connection to which to dump route information.
|
* The database connection to which to dump route information.
|
||||||
*
|
*
|
||||||
|
@ -58,7 +62,34 @@ class UrlMatcherDumper implements MatcherDumperInterface {
|
||||||
*
|
*
|
||||||
* @return string A PHP class representing the matcher class
|
* @return string A PHP class representing the matcher class
|
||||||
*/
|
*/
|
||||||
function dump(array $options = array()) {
|
public function dump(array $options = array()) {
|
||||||
|
$options += array(
|
||||||
|
'route_set' => '',
|
||||||
|
);
|
||||||
|
|
||||||
|
$compiled = $this->compileRoutes($this->routes, $route_set);
|
||||||
|
|
||||||
|
// Convert all of the routes into database records.
|
||||||
|
$insert = $this->connection->insert('router');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
foreach ($this->routes as $name => $route) {
|
||||||
|
$insert->values($record);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete any old records in this route set first, then insert the new ones.
|
||||||
|
// That avoids stale data. The transaction makes it atomic to avoid
|
||||||
|
// unstable router states due to random failures.
|
||||||
|
$txn = $this->connection->startTransaction();
|
||||||
|
|
||||||
|
$this->connection->delete('router')
|
||||||
|
->condition('route_set', $options['route_set'])
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$insert->execute();
|
||||||
|
|
||||||
|
// Transaction ends here.
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,8 +98,187 @@ class UrlMatcherDumper implements MatcherDumperInterface {
|
||||||
*
|
*
|
||||||
* @return RouteCollection A RouteCollection instance
|
* @return RouteCollection A RouteCollection instance
|
||||||
*/
|
*/
|
||||||
function getRoutes() {
|
public function getRoutes() {
|
||||||
return $this->routes;
|
return $this->routes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function compileRoutes(RouteCollection $routes, $route_set) {
|
||||||
|
|
||||||
|
// First pass: separate callbacks from paths, making paths ready for
|
||||||
|
// matching. Calculate fitness, and fill some default values.
|
||||||
|
$menu = array();
|
||||||
|
$masks = array();
|
||||||
|
foreach ($routes as $name => $item) {
|
||||||
|
$path = $item->getPattern();
|
||||||
|
$move = FALSE;
|
||||||
|
|
||||||
|
$parts = explode('/', $path, static::MAX_PARTS);
|
||||||
|
$number_parts = count($parts);
|
||||||
|
// We store the highest index of parts here to save some work in the fit
|
||||||
|
// calculation loop.
|
||||||
|
$slashes = $number_parts - 1;
|
||||||
|
|
||||||
|
$num_placeholders = count(array_filter($parts, function($value) {
|
||||||
|
return strpos($value, '{') !== FALSE;
|
||||||
|
}));
|
||||||
|
|
||||||
|
$fit = $this->getFit($path);
|
||||||
|
|
||||||
|
if ($fit) {
|
||||||
|
$move = TRUE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// If there is no placeholder, it fits maximally.
|
||||||
|
$fit = (1 << $number_parts) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$masks[$fit] = 1;
|
||||||
|
$item += array(
|
||||||
|
'title' => '',
|
||||||
|
'weight' => 0,
|
||||||
|
'type' => MENU_NORMAL_ITEM,
|
||||||
|
'module' => '',
|
||||||
|
'_number_parts' => $number_parts,
|
||||||
|
'_parts' => $parts,
|
||||||
|
'_fit' => $fit,
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($move) {
|
||||||
|
$new_path = implode('/', $item['_parts']);
|
||||||
|
$menu[$new_path] = $item;
|
||||||
|
$sort[$new_path] = $number_parts;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$menu[$path] = $item;
|
||||||
|
$sort[$path] = $number_parts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the route list.
|
||||||
|
array_multisort($sort, SORT_NUMERIC, $menu);
|
||||||
|
// Apply inheritance rules.
|
||||||
|
foreach ($menu as $path => $v) {
|
||||||
|
$item = &$menu[$path];
|
||||||
|
|
||||||
|
for ($i = $item['_number_parts'] - 1; $i; $i--) {
|
||||||
|
$parent_path = implode('/', array_slice($item['_parts'], 0, $i));
|
||||||
|
if (isset($menu[$parent_path])) {
|
||||||
|
|
||||||
|
$parent = &$menu[$parent_path];
|
||||||
|
|
||||||
|
// If an access callback is not found for a default local task we use
|
||||||
|
// the callback from the parent, since we expect them to be identical.
|
||||||
|
// In all other cases, the access parameters must be specified.
|
||||||
|
if (($item['type'] == MENU_DEFAULT_LOCAL_TASK) && !isset($item['access callback']) && isset($parent['access callback'])) {
|
||||||
|
$item['access callback'] = $parent['access callback'];
|
||||||
|
if (!isset($item['access arguments']) && isset($parent['access arguments'])) {
|
||||||
|
$item['access arguments'] = $parent['access arguments'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same for theme callbacks.
|
||||||
|
if (!isset($item['theme callback']) && isset($parent['theme callback'])) {
|
||||||
|
$item['theme callback'] = $parent['theme callback'];
|
||||||
|
if (!isset($item['theme arguments']) && isset($parent['theme arguments'])) {
|
||||||
|
$item['theme arguments'] = $parent['theme arguments'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isset($item['access callback']) && isset($item['access arguments'])) {
|
||||||
|
// Default callback.
|
||||||
|
$item['access callback'] = 'user_access';
|
||||||
|
}
|
||||||
|
if (!isset($item['access callback']) || empty($item['page callback'])) {
|
||||||
|
$item['access callback'] = 0;
|
||||||
|
}
|
||||||
|
if (is_bool($item['access callback'])) {
|
||||||
|
$item['access callback'] = intval($item['access callback']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$item += array(
|
||||||
|
'access arguments' => array(),
|
||||||
|
'access callback' => '',
|
||||||
|
'page arguments' => array(),
|
||||||
|
'page callback' => '',
|
||||||
|
'delivery callback' => '',
|
||||||
|
'title arguments' => array(),
|
||||||
|
'title callback' => 't',
|
||||||
|
'theme arguments' => array(),
|
||||||
|
'theme callback' => '',
|
||||||
|
'description' => '',
|
||||||
|
'position' => '',
|
||||||
|
'context' => 0,
|
||||||
|
'tab_parent' => '',
|
||||||
|
'tab_root' => $path,
|
||||||
|
'path' => $path,
|
||||||
|
'file' => '',
|
||||||
|
'file path' => '',
|
||||||
|
'include file' => '',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calculate out the file to be included for each callback, if any.
|
||||||
|
if ($item['file']) {
|
||||||
|
$file_path = $item['file path'] ? $item['file path'] : drupal_get_path('module', $item['module']);
|
||||||
|
$item['include file'] = $file_path . '/' . $item['file'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the masks so they are in order of descending fit.
|
||||||
|
$masks = array_keys($masks);
|
||||||
|
rsort($masks);
|
||||||
|
|
||||||
|
return array($menu, $masks);
|
||||||
|
|
||||||
|
|
||||||
|
// The old menu_router record structure, copied here for easy referencing.
|
||||||
|
array(
|
||||||
|
'path' => $item['path'],
|
||||||
|
'load_functions' => $item['load_functions'],
|
||||||
|
'to_arg_functions' => $item['to_arg_functions'],
|
||||||
|
'access_callback' => $item['access callback'],
|
||||||
|
'access_arguments' => serialize($item['access arguments']),
|
||||||
|
'page_callback' => $item['page callback'],
|
||||||
|
'page_arguments' => serialize($item['page arguments']),
|
||||||
|
'delivery_callback' => $item['delivery callback'],
|
||||||
|
'fit' => $item['_fit'],
|
||||||
|
'number_parts' => $item['_number_parts'],
|
||||||
|
'context' => $item['context'],
|
||||||
|
'tab_parent' => $item['tab_parent'],
|
||||||
|
'tab_root' => $item['tab_root'],
|
||||||
|
'title' => $item['title'],
|
||||||
|
'title_callback' => $item['title callback'],
|
||||||
|
'title_arguments' => ($item['title arguments'] ? serialize($item['title arguments']) : ''),
|
||||||
|
'theme_callback' => $item['theme callback'],
|
||||||
|
'theme_arguments' => serialize($item['theme arguments']),
|
||||||
|
'type' => $item['type'],
|
||||||
|
'description' => $item['description'],
|
||||||
|
'position' => $item['position'],
|
||||||
|
'weight' => $item['weight'],
|
||||||
|
'include_file' => $item['include file'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the fitness of the provided path.
|
||||||
|
*
|
||||||
|
* @param string $path
|
||||||
|
* The path whose fitness we want.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
* The fitness of the path, as an integer.
|
||||||
|
*/
|
||||||
|
public function getFit($path) {
|
||||||
|
$fit = 0;
|
||||||
|
|
||||||
|
$parts = explode('/', $path, static::MAX_PARTS);
|
||||||
|
foreach ($parts as $k => $part) {
|
||||||
|
if (strpos($part, '{') === FALSE) {
|
||||||
|
$fit |= 1 << ($slashes - $k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $fit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue