Merge lp:~nnewton-drupal/pressflow/pathalias into lp:pressflow

Proposed by Narayan Newton
Status: Merged
Merge reported by: David Strauss
Merged at revision: not available
Proposed branch: lp:~nnewton-drupal/pressflow/pathalias
Merge into: lp:pressflow
Diff against target: 308 lines (+274/-1)
4 files modified
includes/path.inc (+46/-1)
modules/path_alias_cache/path_alias_cache.info (+3/-0)
modules/path_alias_cache/path_alias_cache.install (+26/-0)
modules/path_alias_cache/path_alias_cache.module (+199/-0)
To merge this branch: bzr merge lp:~nnewton-drupal/pressflow/pathalias
Reviewer Review Type Date Requested Status
David Strauss Approve
Review via email: mp+16705@code.launchpad.net

This proposal supersedes a proposal from 2009-12-21.

To post a comment you must log in.
Revision history for this message
David Strauss (davidstrauss) wrote : Posted in a previous version of this proposal

The whitelist should be handled as a cache item, not a variable.

review: Needs Fixing
Revision history for this message
Narayan Newton (nnewton-drupal) wrote : Posted in a previous version of this proposal

> The whitelist should be handled as a cache item, not a variable.

Fixed, this is now stored in cache_path. I've also made it use cache_lifetime for path cache items instead of the magic number.

-N

75. By Narayan Newton

Style and whitespace changes

Revision history for this message
Narayan Newton (nnewton-drupal) wrote :

Fixed up some issues with comments and changed 0 to FALSE in a few places for style.

Outstanding requests:

1) Check the front page alias cache on cache miss. I'm not going to implement that for this iteration. I believe that significant testing needs to go into that idea to ensure it actually has benefit.

2) Porting the drupal_static function to core. I'm interesting in this, but I think we should do it separately from this issue and then "port" this functionality.

Revision history for this message
David Strauss (davidstrauss) wrote :

I think the remaining work is acceptable to shelve right now. I'll try to merge this today.

------Original Message------
From: Narayan Newton
Sender: <email address hidden>
To: Narayan Newton
ReplyTo: <email address hidden>
Subject: Re: [Merge] lp:~nnewton-drupal/pressflow/pathalias into lp:pressflow
Sent: Jan 13, 2010 23:31

Fixed up some issues with comments and changed 0 to FALSE in a few places for style.

Outstanding requests:

1) Check the front page alias cache on cache miss. I'm not going to implement that for this iteration. I believe that significant testing needs to go into that idea to ensure it actually has benefit.

2) Porting the drupal_static function to core. I'm interesting in this, but I think we should do it separately from this issue and then "port" this functionality.
--
https://code.launchpad.net/~nnewton-drupal/pressflow/pathalias/+merge/16705
You are requested to review the proposed merge of lp:~nnewton-drupal/pressflow/pathalias into lp:pressflow.

Revision history for this message
David Strauss (davidstrauss) wrote :

This looks great, especially because the replacement is optional. Merging now.

review: Approve
Revision history for this message
David Strauss (davidstrauss) wrote :

This looks great, especially because the replacement is optional. Merging now.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'includes/path.inc'
--- includes/path.inc 2009-12-17 00:30:08 +0000
+++ includes/path.inc 2010-01-13 23:26:10 +0000
@@ -25,6 +25,51 @@
25/**25/**
26 * Given an alias, return its Drupal system URL if one exists. Given a Drupal26 * Given an alias, return its Drupal system URL if one exists. Given a Drupal
27 * system URL return one of its aliases if such a one exists. Otherwise,27 * system URL return one of its aliases if such a one exists. Otherwise,
28 * return FALSE. This function is a hook and modules can impliment lookup_path
29 * to override the core behavior.
30 *
31 * @param $action
32 * One of the following values:
33 * - wipe: delete the alias cache.
34 * - alias: return an alias for a given Drupal system path (if one exists).
35 * - source: return the Drupal system URL for a path alias (if one exists).
36 * @param $path
37 * The path to investigate for corresponding aliases or system URLs.
38 * @param $path_language
39 * Optional language code to search the path with. Defaults to the page language.
40 * If there's no path defined for that language it will search paths without
41 * language.
42 *
43 * @return
44 * Either a Drupal system path, an aliased path, or FALSE if no path was
45 * found.
46 *
47 */
48function drupal_lookup_path($action, $path = '', $path_language = '') {
49 static $hook_module;
50
51 if (!isset($hook_module)) {
52 $modules = module_implements('lookup_path');
53 if (count($modules) > 0) {
54 $hook_module = $modules[0];
55 } else {
56 $hook_module = FALSE;
57 }
58 }
59
60 if ($hook_module !== FALSE) {
61 $retval = module_invoke($hook_module, 'lookup_path', $action, $path, $path_language);
62 } else {
63 $retval = _drupal_lookup_path_direct($action, $path, $path_language);
64 }
65 return $retval;
66}
67
68/**
69 * Internal function: The base path alias functionality as provided by Core.
70 *
71 * Given an alias, return its Drupal system URL if one exists. Given a Drupal
72 * system URL return one of its aliases if such a one exists. Otherwise,
28 * return FALSE.73 * return FALSE.
29 *74 *
30 * @param $action75 * @param $action
@@ -43,7 +88,7 @@
43 * Either a Drupal system path, an aliased path, or FALSE if no path was88 * Either a Drupal system path, an aliased path, or FALSE if no path was
44 * found.89 * found.
45 */90 */
46function drupal_lookup_path($action, $path = '', $path_language = '') {91function _drupal_lookup_path_direct($action, $path = '', $path_language = '') {
47 global $language;92 global $language;
48 // $map is an array with language keys, holding arrays of Drupal paths to alias relations93 // $map is an array with language keys, holding arrays of Drupal paths to alias relations
49 static $map = array(), $no_src = array(), $has_paths;94 static $map = array(), $no_src = array(), $has_paths;
5095
=== added directory 'modules/path_alias_cache'
=== added file 'modules/path_alias_cache/path_alias_cache.info'
--- modules/path_alias_cache/path_alias_cache.info 1970-01-01 00:00:00 +0000
+++ modules/path_alias_cache/path_alias_cache.info 2010-01-13 23:26:10 +0000
@@ -0,0 +1,3 @@
1name = Path Alias Cache
2description = A path alias implementation which adds a cache to the core version.
3core = 6.x
04
=== added file 'modules/path_alias_cache/path_alias_cache.install'
--- modules/path_alias_cache/path_alias_cache.install 1970-01-01 00:00:00 +0000
+++ modules/path_alias_cache/path_alias_cache.install 2010-01-13 23:26:10 +0000
@@ -0,0 +1,26 @@
1<?php
2
3/**
4 * Implementation of hook_install().
5 */
6function path_alias_cache_install() {
7 // Create tables.
8 drupal_install_schema('path_alias_cache');
9}
10
11/**
12 * Implementation of hook_uninstall().
13 */
14function path_alias_cache_uninstall() {
15 // Remove tables.
16 drupal_uninstall_schema('path_alias_cache');
17}
18
19/**
20 * Implementation of hook_schema().
21 */
22function path_alias_cache_schema() {
23 $schema['cache_path'] = drupal_get_schema_unprocessed('system', 'cache');
24 return $schema;
25}
26
027
=== added file 'modules/path_alias_cache/path_alias_cache.module'
--- modules/path_alias_cache/path_alias_cache.module 1970-01-01 00:00:00 +0000
+++ modules/path_alias_cache/path_alias_cache.module 2010-01-13 23:26:10 +0000
@@ -0,0 +1,199 @@
1<?php
2
3/*
4 * Implementation of hook_lookup_path
5 *
6 * This version of lookup_path is almost exactly what is in core, but adds a cache table.
7 *
8 */
9function path_alias_cache_lookup_path($action, $path = '', $path_language = '') {
10 global $language;
11 $cache = &path_alias_cache_static(__FUNCTION__, array(
12 'map' => array(),
13 'no_src' => array(),
14 'whitelist' => NULL,
15 'system_paths' => array(),
16 'no_aliases' => array(),
17 'first_call' => TRUE,
18 ));
19
20 // Retrieve the path alias whitelist.
21 if (!isset($cache['whitelist'])) {
22 if ($cached = cache_get('path_alias_whitelist', 'cache_path')) {
23 $cache['whitelist'] = $cached->data;
24 } else {
25 $cache['whitelist'] = path_alias_cache_path_alias_whitelist_rebuild();
26 }
27 }
28
29 $path_language = $path_language ? $path_language : $language->language;
30
31 if ($action == 'wipe') {
32 $cache = array();
33 $cache['whitelist'] = path_alias_cache_path_alias_whitelist_rebuild();
34 }
35 elseif ($cache['whitelist'] && $path != '') {
36 if ($action == 'alias') {
37 // During the first call to path_alias_cache_lookup_path() per language, load the
38 // expected system paths for the page from cache.
39 if (!empty($cache['first_call'])) {
40 $cache['first_call'] = FALSE;
41
42 if (!isset($cache['map'][$path_language]) || !is_array($cache['map'][$path_language])) {
43 $cache['map'][$path_language] = array();
44 }
45 // Load system paths from cache.
46 $cid = $_GET['q'];
47 if ($cached = cache_get($cid, 'cache_path')) {
48 $cache['system_paths'] = $cached->data;
49 // Now fetch the aliases corresponding to these system paths.
50 // We order by ASC and overwrite array keys to ensure the correct
51 // alias is used when there are multiple aliases per path.
52 $placeholders = db_placeholders($cache['system_paths'], 'varchar');
53 $result = db_query("SELECT src, dst FROM {url_alias} WHERE src IN($placeholders) AND language IN('%s', '') ORDER BY language ASC", $cache['system_paths'], $path_language);
54 while ($record = db_fetch_object($result)) {
55 if (!isset($cache['map'][$path_language][$record->src])) {
56 $cache['map'][$path_language][$record->src] = $record->dst;
57 }
58 }
59 // Keep a record of paths with no alias to avoid querying twice.
60 $cache['no_aliases'][$path_language] = array_flip(array_diff_key($cache['system_paths'], array_keys($cache['map'][$path_language])));
61 }
62 }
63 // If the alias has already been loaded, return it.
64 if (isset($cache['map'][$path_language][$path])) {
65 return $cache['map'][$path_language][$path];
66 }
67 // Check the path whitelist, if the top_level part before the first "/"
68 // is not in the list, then there is no need to do anything further,
69 // it is not in the database.
70 elseif (!isset($cache['whitelist'][strtok($path, '/')])) {
71 return FALSE;
72 }
73 // For system paths which were not cached, query aliases individually.
74 else if (!isset($cache['no_aliases'][$path_language][$path])) {
75 // Get the most fitting result falling back with alias without language
76 $alias = db_result(db_query("SELECT dst FROM {url_alias} WHERE src = '%s' AND language IN('%s', '') ORDER BY language DESC", $path, $path_language));
77 $cache['map'][$path_language][$path] = $alias;
78 return $alias;
79 }
80 }
81 // Check no_src for this $path in case we've already determined that there
82 // isn't a path that has this alias
83 elseif ($action == 'source' && !isset($cache['no_src'][$path_language][$path])) {
84 // Look for the value $path within the cached map
85 $src = '';
86 if (!isset($cache['map'][$path_language]) || !($src = array_search($path, $cache['map'][$path_language]))) {
87 // Get the most fitting result falling back with alias without language
88 if ($src = db_result(db_query("SELECT src FROM {url_alias} WHERE dst = '%s' AND language IN('%s', '') ORDER BY language DESC", $path, $path_language))) {
89 $cache['map'][$path_language][$src] = $path;
90 }
91 else {
92 // We can't record anything into map because we do not have a valid
93 // index and there is no need because we have not learned anything
94 // about any Drupal path. Thus cache to no_src.
95 $cache['no_src'][$path_language][$path] = TRUE;
96 }
97 }
98 return $src;
99 }
100 }
101
102 return FALSE;
103}
104
105/**
106 * Implementation of hook_exit
107 * We use this to cache the paths on a page, for later requests.
108 */
109function path_alias_cache_exit() {
110 path_alias_cache_cache_system_paths();
111}
112
113/**
114 * Cache system paths for a page.
115 *
116 * Cache an array of the system paths available on each page. We assume
117 * that aliases will be needed for the majority of these paths during
118 * subsequent requests, and load them in a single query during
119 * drupal_lookup_path().
120 */
121function path_alias_cache_cache_system_paths() {
122 // Check if the system paths for this page were loaded from cache in this
123 // request to avoid writing to cache on every request.
124 $cache = &path_alias_cache_static('path_alias_cache_lookup_path', array());
125 if (!$cache['system_paths']) {
126 // Generate a cache ID (cid) specifically for this page.
127 $cid = $_GET['q'];
128 // The static $map array used by drupal_lookup_path() includes all
129 // system paths for the page request.
130 if ($paths = current($cache['map'])) {
131 $data = array_keys($paths);
132 $expire = $_SERVER['REQUEST_TIME'] + variable_get('cache_lifetime', 0);
133 cache_set($cid, $data, 'cache_path', $expire);
134 }
135 }
136}
137
138
139/**
140 * Central static variable storage.
141 *
142 * @param $name
143 * Globally unique name for the variable. For a function with only one static,
144 * variable, the function name (e.g. via the PHP magic __FUNCTION__ constant)
145 * is recommended. For a function with multiple static variables add a
146 * distinguishing suffix to the function name for each one.
147 * @param $default_value
148 * Optional default value.
149 * @param $reset
150 * TRUE to reset a specific named variable, or all variables if $name is NULL.
151 * Resetting every variable should only be used, for example, for running
152 * unit tests with a clean environment. Should be used only though via
153 * function drupal_static_reset().
154 *
155 * @return
156 * Returns a variable by reference if $reset is FALSE.
157 */
158function &path_alias_cache_static($name, $default_value = NULL, $reset = FALSE) {
159 static $data = array();
160
161 // Reset a single value, or all values.
162 if ($reset) {
163 if (isset($name)) {
164 unset($data[$name]);
165 }
166 else {
167 $data = array();
168 }
169 // We must return a reference to a variable.
170 $dummy = NULL;
171 return $dummy;
172 }
173
174 if (!isset($data[$name])) {
175 $data[$name] = $default_value;
176 }
177
178 return $data[$name];
179}
180
181/**
182 * Rebuild the path alias white list.
183 *
184 * @return
185 * An array containing a white list of path aliases.
186 */
187function path_alias_cache_path_alias_whitelist_rebuild() {
188 // For each alias in the database, get the top level component of the system
189 // path it corresponds to. This is the portion of the path before the first "/"
190 // if present, otherwise the whole path itself.
191 $whitelist = array();
192 $result = db_query("SELECT SUBSTRING_INDEX(src, '/', 1) AS path FROM {url_alias} GROUP BY path");
193 while ($row = db_fetch_object($result)) {
194 $whitelist[$row->path] = TRUE;
195 }
196 cache_set('path_alias_whitelist', $whitelist, 'cache_path');
197 return $whitelist;
198}
199

Subscribers

People subscribed via source and target branches

to status/vote changes: