Skip to content

Instantly share code, notes, and snippets.

@amcgowanca
Created July 22, 2018 11:13
Show Gist options
  • Save amcgowanca/fc5dca57785d9094aab99af2d5631959 to your computer and use it in GitHub Desktop.
Save amcgowanca/fc5dca57785d9094aab99af2d5631959 to your computer and use it in GitHub Desktop.
Drupal 7.59 reroll of Path Cache modifications
diff --git a/includes/path.inc b/includes/path.inc
index 6bd48d3..d3b8d9f 100644
--- a/includes/path.inc
+++ b/includes/path.inc
@@ -77,8 +77,7 @@ function drupal_lookup_path($action, $path = '', $path_language = NULL) {
$path_language = $path_language ? $path_language : $language_url->language;
if ($action == 'wipe') {
- $cache = array();
- $cache['whitelist'] = drupal_path_alias_whitelist_rebuild();
+ $cache = NULL;
}
elseif ($cache['whitelist'] && $path != '') {
if ($action == 'alias') {
@@ -90,8 +89,47 @@ function drupal_lookup_path($action, $path = '', $path_language = NULL) {
$cache['map'][$path_language] = array();
// Load system paths from cache.
$cid = current_path();
- if ($cached = cache_get($cid, 'cache_path')) {
- $cache['system_paths'] = $cached->data;
+
+ // Small hack to allow pre-filling of system paths cache via
+ // drupal_static(), you can set $cache['system_paths'] and
+ // $cache['first_call'] then get multiple lookups for any
+ // arbitrary set of system paths..
+ $cached = FALSE;
+ if (!empty($cache['system_paths']) || ($cached = cache_get($cid, 'cache_path'))) {
+ if ($cached) {
+ $cache['system_paths'] = $cached->data;
+ }
+ // End hack.
+
+ // Now fetch the aliases corresponding to these system paths.
+ // We order by ASC and overwrite array keys to ensure the correct
+ // alias is used when there are multiple aliases per path.
+
+ // First try to fetch the path aliaes from cache. By using
+ // cache_get_multiple() we allow caching backends which support it to
+ // fetch all cached paths in a single request. $cid needs to be built
+ // from both path language and path.
+ foreach ($cache['system_paths'] as $system_path) {
+ $system_cids[] = $path_language . ':' . $system_path;
+ $system_paths[$system_path] = $system_path;
+ }
+ if ($cached_paths = cache_get_multiple($system_cids, 'cache_path_alias')) {
+ foreach ($cached_paths as $cached) {
+ $system_path = str_replace($path_language . ':', '', $cached->cid);
+ // Remove this path from the aliases to query against the
+ // database.
+ unset($system_paths[$system_path]);
+ if ($cached->data) {
+ $cache['map'][$path_language][$system_path] = $cached->data;
+ }
+ else {
+ $cache['no_aliases'][$path_language][$system_path] = $system_path;
+ }
+ }
+ }
+ }
+ // Fetch remaining paths from the database.
+ if (!empty($system_paths)) {
// Now fetch the aliases corresponding to these system paths.
$args = array(
':system' => $cache['system_paths'],
@@ -116,9 +154,20 @@ function drupal_lookup_path($action, $path = '', $path_language = NULL) {
else {
$result = db_query('SELECT source, alias FROM {url_alias} WHERE source IN (:system) AND language IN (:language, :language_none) ORDER BY language DESC, pid ASC', $args);
}
- $cache['map'][$path_language] = $result->fetchAllKeyed();
+ $new_paths = $result->fetchAllKeyed();
+ $cache['map'][$path_language] += $new_paths;
// Keep a record of paths with no alias to avoid querying twice.
- $cache['no_aliases'][$path_language] = array_flip(array_diff_key($cache['system_paths'], array_keys($cache['map'][$path_language])));
+ $new_misses = array_diff_key($cache['system_paths'], array_keys($cache['map'][$path_language]));
+ foreach ($new_misses as $new_miss) {
+ $path_cid = $path_language . ':' . $new_miss;
+ cache_set($path_cid, '', 'cache_path_alias');
+ $cache['no_aliases'][$path_language][$new_miss] = $new_miss;
+ }
+ // Cache the individual paths.
+ foreach ($new_paths as $new_path => $new_alias) {
+ $path_cid = $path_language . ':' . $new_path;
+ cache_set($path_cid, $new_alias, 'cache_path_alias');
+ }
}
}
// If the alias has already been loaded, return it.
@@ -133,6 +182,12 @@ function drupal_lookup_path($action, $path = '', $path_language = NULL) {
}
// For system paths which were not cached, query aliases individually.
elseif (!isset($cache['no_aliases'][$path_language][$path])) {
+ $path_cid = $path_language . ':' . $path;
+ $cached = cache_get($path_cid, 'cache_path_alias');
+ if ($cached) {
+ $cache['map'][$path_language][$path] = $alias = $cached->data;
+ return $alias;
+ }
$args = array(
':source' => $path,
':language' => $path_language,
@@ -150,6 +205,15 @@ function drupal_lookup_path($action, $path = '', $path_language = NULL) {
$alias = db_query("SELECT alias FROM {url_alias} WHERE source = :source AND language IN (:language, :language_none) ORDER BY language ASC, pid DESC", $args)->fetchField();
}
$cache['map'][$path_language][$path] = $alias;
+ if ($alias) {
+ cache_set($path_cid, $alias, 'cache_path_alias');
+ }
+ else {
+ // When there is no alias, cache this fact as well, but only when
+ // the user is anonymous to avoid bloating the cache with paths
+ // like node/n/edit.
+ cache_set($path_cid, '', 'cache_path_alias');
+ }
return $alias;
}
}
@@ -159,6 +223,17 @@ function drupal_lookup_path($action, $path = '', $path_language = NULL) {
// Look for the value $path within the cached $map
$source = FALSE;
if (!isset($cache['map'][$path_language]) || !($source = array_search($path, $cache['map'][$path_language]))) {
+ $path_cid = $path_language . ':' . $path;
+
+ if ($cached = cache_get($path_cid, 'cache_path_source')) {
+ if (!empty($cached->data)) {
+ $cache['map'][$path_language][$source] = $source = $cached->data;
+ }
+ else {
+ $cache['no_source'][$path_language][$path] = TRUE;
+ }
+ return $source;
+ }
$args = array(
':alias' => $path,
':language' => $path_language,
@@ -177,12 +252,22 @@ function drupal_lookup_path($action, $path = '', $path_language = NULL) {
}
if ($source = $result->fetchField()) {
$cache['map'][$path_language][$source] = $path;
+ cache_set($path_cid, $source, 'cache_path_source');
}
else {
// We can't record anything into $map because we do not have a valid
// index and there is no need because we have not learned anything
// about any Drupal path. Thus cache to $no_source.
$cache['no_source'][$path_language][$path] = TRUE;
+ // Add a cache entry with an empty string - this saves hitting the
+ // db for zero results. We can afford to do this because the
+ // whitelist will prevent attempts to cache paths like comment/$cid.
+ // And because memcache operates an LRU cache. However, only do this
+ // for anonymous users to avoid bloating the cache with paths like
+ // node/n/edit.
+ if (empty($GLOBALS['user']->uid)) {
+ cache_set($path_cid, '', 'cache_path_source');
+ }
}
}
return $source;
@@ -432,27 +517,21 @@ function path_load($conditions) {
* - language: (optional) The language of the alias.
*/
function path_save(&$path) {
- $path += array('language' => LANGUAGE_NONE);
+ $path += array('pid' => NULL, 'language' => LANGUAGE_NONE);
- // Load the stored alias, if any.
- if (!empty($path['pid']) && !isset($path['original'])) {
- $path['original'] = path_load($path['pid']);
- }
+ // Insert or update the alias.
+ $status = drupal_write_record('url_alias', $path, (!empty($path['pid']) ? 'pid' : array()));
- if (empty($path['pid'])) {
- drupal_write_record('url_alias', $path);
- module_invoke_all('path_insert', $path);
- }
- else {
- drupal_write_record('url_alias', $path, array('pid'));
- module_invoke_all('path_update', $path);
+ // Verify that a record was written.
+ if ($status) {
+ if ($status === SAVED_NEW) {
+ module_invoke_all('path_insert', $path);
+ }
+ else {
+ module_invoke_all('path_update', $path);
+ }
+ drupal_clear_path_cache($path['source']);
}
-
- // Clear internal properties.
- unset($path['original']);
-
- // Clear the static alias cache.
- drupal_clear_path_cache($path['source']);
}
/**
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment