Merge lp:~nnewton-drupal/pressflow/pathalias into lp:pressflow
- pathalias
- Merge into 6
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 |
Related bugs: |
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.
Commit message
Description of the change
David Strauss (davidstrauss) wrote : Posted in a previous version of this proposal | # |
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
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.
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:/
You are requested to review the proposed merge of lp:~nnewton-drupal/pressflow/pathalias into lp:pressflow.
David Strauss (davidstrauss) wrote : | # |
This looks great, especially because the replacement is optional. Merging now.
David Strauss (davidstrauss) wrote : | # |
This looks great, especially because the replacement is optional. Merging now.
Preview Diff
1 | === modified file 'includes/path.inc' | |||
2 | --- includes/path.inc 2009-12-17 00:30:08 +0000 | |||
3 | +++ includes/path.inc 2010-01-13 23:26:10 +0000 | |||
4 | @@ -25,6 +25,51 @@ | |||
5 | 25 | /** | 25 | /** |
6 | 26 | * Given an alias, return its Drupal system URL if one exists. Given a Drupal | 26 | * Given an alias, return its Drupal system URL if one exists. Given a Drupal |
7 | 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, |
8 | 28 | * return FALSE. This function is a hook and modules can impliment lookup_path | ||
9 | 29 | * to override the core behavior. | ||
10 | 30 | * | ||
11 | 31 | * @param $action | ||
12 | 32 | * One of the following values: | ||
13 | 33 | * - wipe: delete the alias cache. | ||
14 | 34 | * - alias: return an alias for a given Drupal system path (if one exists). | ||
15 | 35 | * - source: return the Drupal system URL for a path alias (if one exists). | ||
16 | 36 | * @param $path | ||
17 | 37 | * The path to investigate for corresponding aliases or system URLs. | ||
18 | 38 | * @param $path_language | ||
19 | 39 | * Optional language code to search the path with. Defaults to the page language. | ||
20 | 40 | * If there's no path defined for that language it will search paths without | ||
21 | 41 | * language. | ||
22 | 42 | * | ||
23 | 43 | * @return | ||
24 | 44 | * Either a Drupal system path, an aliased path, or FALSE if no path was | ||
25 | 45 | * found. | ||
26 | 46 | * | ||
27 | 47 | */ | ||
28 | 48 | function drupal_lookup_path($action, $path = '', $path_language = '') { | ||
29 | 49 | static $hook_module; | ||
30 | 50 | |||
31 | 51 | if (!isset($hook_module)) { | ||
32 | 52 | $modules = module_implements('lookup_path'); | ||
33 | 53 | if (count($modules) > 0) { | ||
34 | 54 | $hook_module = $modules[0]; | ||
35 | 55 | } else { | ||
36 | 56 | $hook_module = FALSE; | ||
37 | 57 | } | ||
38 | 58 | } | ||
39 | 59 | |||
40 | 60 | if ($hook_module !== FALSE) { | ||
41 | 61 | $retval = module_invoke($hook_module, 'lookup_path', $action, $path, $path_language); | ||
42 | 62 | } else { | ||
43 | 63 | $retval = _drupal_lookup_path_direct($action, $path, $path_language); | ||
44 | 64 | } | ||
45 | 65 | return $retval; | ||
46 | 66 | } | ||
47 | 67 | |||
48 | 68 | /** | ||
49 | 69 | * Internal function: The base path alias functionality as provided by Core. | ||
50 | 70 | * | ||
51 | 71 | * Given an alias, return its Drupal system URL if one exists. Given a Drupal | ||
52 | 72 | * system URL return one of its aliases if such a one exists. Otherwise, | ||
53 | 28 | * return FALSE. | 73 | * return FALSE. |
54 | 29 | * | 74 | * |
55 | 30 | * @param $action | 75 | * @param $action |
56 | @@ -43,7 +88,7 @@ | |||
57 | 43 | * Either a Drupal system path, an aliased path, or FALSE if no path was | 88 | * Either a Drupal system path, an aliased path, or FALSE if no path was |
58 | 44 | * found. | 89 | * found. |
59 | 45 | */ | 90 | */ |
61 | 46 | function drupal_lookup_path($action, $path = '', $path_language = '') { | 91 | function _drupal_lookup_path_direct($action, $path = '', $path_language = '') { |
62 | 47 | global $language; | 92 | global $language; |
63 | 48 | // $map is an array with language keys, holding arrays of Drupal paths to alias relations | 93 | // $map is an array with language keys, holding arrays of Drupal paths to alias relations |
64 | 49 | static $map = array(), $no_src = array(), $has_paths; | 94 | static $map = array(), $no_src = array(), $has_paths; |
65 | 50 | 95 | ||
66 | === added directory 'modules/path_alias_cache' | |||
67 | === added file 'modules/path_alias_cache/path_alias_cache.info' | |||
68 | --- modules/path_alias_cache/path_alias_cache.info 1970-01-01 00:00:00 +0000 | |||
69 | +++ modules/path_alias_cache/path_alias_cache.info 2010-01-13 23:26:10 +0000 | |||
70 | @@ -0,0 +1,3 @@ | |||
71 | 1 | name = Path Alias Cache | ||
72 | 2 | description = A path alias implementation which adds a cache to the core version. | ||
73 | 3 | core = 6.x | ||
74 | 0 | 4 | ||
75 | === added file 'modules/path_alias_cache/path_alias_cache.install' | |||
76 | --- modules/path_alias_cache/path_alias_cache.install 1970-01-01 00:00:00 +0000 | |||
77 | +++ modules/path_alias_cache/path_alias_cache.install 2010-01-13 23:26:10 +0000 | |||
78 | @@ -0,0 +1,26 @@ | |||
79 | 1 | <?php | ||
80 | 2 | |||
81 | 3 | /** | ||
82 | 4 | * Implementation of hook_install(). | ||
83 | 5 | */ | ||
84 | 6 | function path_alias_cache_install() { | ||
85 | 7 | // Create tables. | ||
86 | 8 | drupal_install_schema('path_alias_cache'); | ||
87 | 9 | } | ||
88 | 10 | |||
89 | 11 | /** | ||
90 | 12 | * Implementation of hook_uninstall(). | ||
91 | 13 | */ | ||
92 | 14 | function path_alias_cache_uninstall() { | ||
93 | 15 | // Remove tables. | ||
94 | 16 | drupal_uninstall_schema('path_alias_cache'); | ||
95 | 17 | } | ||
96 | 18 | |||
97 | 19 | /** | ||
98 | 20 | * Implementation of hook_schema(). | ||
99 | 21 | */ | ||
100 | 22 | function path_alias_cache_schema() { | ||
101 | 23 | $schema['cache_path'] = drupal_get_schema_unprocessed('system', 'cache'); | ||
102 | 24 | return $schema; | ||
103 | 25 | } | ||
104 | 26 | |||
105 | 0 | 27 | ||
106 | === added file 'modules/path_alias_cache/path_alias_cache.module' | |||
107 | --- modules/path_alias_cache/path_alias_cache.module 1970-01-01 00:00:00 +0000 | |||
108 | +++ modules/path_alias_cache/path_alias_cache.module 2010-01-13 23:26:10 +0000 | |||
109 | @@ -0,0 +1,199 @@ | |||
110 | 1 | <?php | ||
111 | 2 | |||
112 | 3 | /* | ||
113 | 4 | * Implementation of hook_lookup_path | ||
114 | 5 | * | ||
115 | 6 | * This version of lookup_path is almost exactly what is in core, but adds a cache table. | ||
116 | 7 | * | ||
117 | 8 | */ | ||
118 | 9 | function path_alias_cache_lookup_path($action, $path = '', $path_language = '') { | ||
119 | 10 | global $language; | ||
120 | 11 | $cache = &path_alias_cache_static(__FUNCTION__, array( | ||
121 | 12 | 'map' => array(), | ||
122 | 13 | 'no_src' => array(), | ||
123 | 14 | 'whitelist' => NULL, | ||
124 | 15 | 'system_paths' => array(), | ||
125 | 16 | 'no_aliases' => array(), | ||
126 | 17 | 'first_call' => TRUE, | ||
127 | 18 | )); | ||
128 | 19 | |||
129 | 20 | // Retrieve the path alias whitelist. | ||
130 | 21 | if (!isset($cache['whitelist'])) { | ||
131 | 22 | if ($cached = cache_get('path_alias_whitelist', 'cache_path')) { | ||
132 | 23 | $cache['whitelist'] = $cached->data; | ||
133 | 24 | } else { | ||
134 | 25 | $cache['whitelist'] = path_alias_cache_path_alias_whitelist_rebuild(); | ||
135 | 26 | } | ||
136 | 27 | } | ||
137 | 28 | |||
138 | 29 | $path_language = $path_language ? $path_language : $language->language; | ||
139 | 30 | |||
140 | 31 | if ($action == 'wipe') { | ||
141 | 32 | $cache = array(); | ||
142 | 33 | $cache['whitelist'] = path_alias_cache_path_alias_whitelist_rebuild(); | ||
143 | 34 | } | ||
144 | 35 | elseif ($cache['whitelist'] && $path != '') { | ||
145 | 36 | if ($action == 'alias') { | ||
146 | 37 | // During the first call to path_alias_cache_lookup_path() per language, load the | ||
147 | 38 | // expected system paths for the page from cache. | ||
148 | 39 | if (!empty($cache['first_call'])) { | ||
149 | 40 | $cache['first_call'] = FALSE; | ||
150 | 41 | |||
151 | 42 | if (!isset($cache['map'][$path_language]) || !is_array($cache['map'][$path_language])) { | ||
152 | 43 | $cache['map'][$path_language] = array(); | ||
153 | 44 | } | ||
154 | 45 | // Load system paths from cache. | ||
155 | 46 | $cid = $_GET['q']; | ||
156 | 47 | if ($cached = cache_get($cid, 'cache_path')) { | ||
157 | 48 | $cache['system_paths'] = $cached->data; | ||
158 | 49 | // Now fetch the aliases corresponding to these system paths. | ||
159 | 50 | // We order by ASC and overwrite array keys to ensure the correct | ||
160 | 51 | // alias is used when there are multiple aliases per path. | ||
161 | 52 | $placeholders = db_placeholders($cache['system_paths'], 'varchar'); | ||
162 | 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); | ||
163 | 54 | while ($record = db_fetch_object($result)) { | ||
164 | 55 | if (!isset($cache['map'][$path_language][$record->src])) { | ||
165 | 56 | $cache['map'][$path_language][$record->src] = $record->dst; | ||
166 | 57 | } | ||
167 | 58 | } | ||
168 | 59 | // Keep a record of paths with no alias to avoid querying twice. | ||
169 | 60 | $cache['no_aliases'][$path_language] = array_flip(array_diff_key($cache['system_paths'], array_keys($cache['map'][$path_language]))); | ||
170 | 61 | } | ||
171 | 62 | } | ||
172 | 63 | // If the alias has already been loaded, return it. | ||
173 | 64 | if (isset($cache['map'][$path_language][$path])) { | ||
174 | 65 | return $cache['map'][$path_language][$path]; | ||
175 | 66 | } | ||
176 | 67 | // Check the path whitelist, if the top_level part before the first "/" | ||
177 | 68 | // is not in the list, then there is no need to do anything further, | ||
178 | 69 | // it is not in the database. | ||
179 | 70 | elseif (!isset($cache['whitelist'][strtok($path, '/')])) { | ||
180 | 71 | return FALSE; | ||
181 | 72 | } | ||
182 | 73 | // For system paths which were not cached, query aliases individually. | ||
183 | 74 | else if (!isset($cache['no_aliases'][$path_language][$path])) { | ||
184 | 75 | // Get the most fitting result falling back with alias without language | ||
185 | 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)); | ||
186 | 77 | $cache['map'][$path_language][$path] = $alias; | ||
187 | 78 | return $alias; | ||
188 | 79 | } | ||
189 | 80 | } | ||
190 | 81 | // Check no_src for this $path in case we've already determined that there | ||
191 | 82 | // isn't a path that has this alias | ||
192 | 83 | elseif ($action == 'source' && !isset($cache['no_src'][$path_language][$path])) { | ||
193 | 84 | // Look for the value $path within the cached map | ||
194 | 85 | $src = ''; | ||
195 | 86 | if (!isset($cache['map'][$path_language]) || !($src = array_search($path, $cache['map'][$path_language]))) { | ||
196 | 87 | // Get the most fitting result falling back with alias without language | ||
197 | 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))) { | ||
198 | 89 | $cache['map'][$path_language][$src] = $path; | ||
199 | 90 | } | ||
200 | 91 | else { | ||
201 | 92 | // We can't record anything into map because we do not have a valid | ||
202 | 93 | // index and there is no need because we have not learned anything | ||
203 | 94 | // about any Drupal path. Thus cache to no_src. | ||
204 | 95 | $cache['no_src'][$path_language][$path] = TRUE; | ||
205 | 96 | } | ||
206 | 97 | } | ||
207 | 98 | return $src; | ||
208 | 99 | } | ||
209 | 100 | } | ||
210 | 101 | |||
211 | 102 | return FALSE; | ||
212 | 103 | } | ||
213 | 104 | |||
214 | 105 | /** | ||
215 | 106 | * Implementation of hook_exit | ||
216 | 107 | * We use this to cache the paths on a page, for later requests. | ||
217 | 108 | */ | ||
218 | 109 | function path_alias_cache_exit() { | ||
219 | 110 | path_alias_cache_cache_system_paths(); | ||
220 | 111 | } | ||
221 | 112 | |||
222 | 113 | /** | ||
223 | 114 | * Cache system paths for a page. | ||
224 | 115 | * | ||
225 | 116 | * Cache an array of the system paths available on each page. We assume | ||
226 | 117 | * that aliases will be needed for the majority of these paths during | ||
227 | 118 | * subsequent requests, and load them in a single query during | ||
228 | 119 | * drupal_lookup_path(). | ||
229 | 120 | */ | ||
230 | 121 | function path_alias_cache_cache_system_paths() { | ||
231 | 122 | // Check if the system paths for this page were loaded from cache in this | ||
232 | 123 | // request to avoid writing to cache on every request. | ||
233 | 124 | $cache = &path_alias_cache_static('path_alias_cache_lookup_path', array()); | ||
234 | 125 | if (!$cache['system_paths']) { | ||
235 | 126 | // Generate a cache ID (cid) specifically for this page. | ||
236 | 127 | $cid = $_GET['q']; | ||
237 | 128 | // The static $map array used by drupal_lookup_path() includes all | ||
238 | 129 | // system paths for the page request. | ||
239 | 130 | if ($paths = current($cache['map'])) { | ||
240 | 131 | $data = array_keys($paths); | ||
241 | 132 | $expire = $_SERVER['REQUEST_TIME'] + variable_get('cache_lifetime', 0); | ||
242 | 133 | cache_set($cid, $data, 'cache_path', $expire); | ||
243 | 134 | } | ||
244 | 135 | } | ||
245 | 136 | } | ||
246 | 137 | |||
247 | 138 | |||
248 | 139 | /** | ||
249 | 140 | * Central static variable storage. | ||
250 | 141 | * | ||
251 | 142 | * @param $name | ||
252 | 143 | * Globally unique name for the variable. For a function with only one static, | ||
253 | 144 | * variable, the function name (e.g. via the PHP magic __FUNCTION__ constant) | ||
254 | 145 | * is recommended. For a function with multiple static variables add a | ||
255 | 146 | * distinguishing suffix to the function name for each one. | ||
256 | 147 | * @param $default_value | ||
257 | 148 | * Optional default value. | ||
258 | 149 | * @param $reset | ||
259 | 150 | * TRUE to reset a specific named variable, or all variables if $name is NULL. | ||
260 | 151 | * Resetting every variable should only be used, for example, for running | ||
261 | 152 | * unit tests with a clean environment. Should be used only though via | ||
262 | 153 | * function drupal_static_reset(). | ||
263 | 154 | * | ||
264 | 155 | * @return | ||
265 | 156 | * Returns a variable by reference if $reset is FALSE. | ||
266 | 157 | */ | ||
267 | 158 | function &path_alias_cache_static($name, $default_value = NULL, $reset = FALSE) { | ||
268 | 159 | static $data = array(); | ||
269 | 160 | |||
270 | 161 | // Reset a single value, or all values. | ||
271 | 162 | if ($reset) { | ||
272 | 163 | if (isset($name)) { | ||
273 | 164 | unset($data[$name]); | ||
274 | 165 | } | ||
275 | 166 | else { | ||
276 | 167 | $data = array(); | ||
277 | 168 | } | ||
278 | 169 | // We must return a reference to a variable. | ||
279 | 170 | $dummy = NULL; | ||
280 | 171 | return $dummy; | ||
281 | 172 | } | ||
282 | 173 | |||
283 | 174 | if (!isset($data[$name])) { | ||
284 | 175 | $data[$name] = $default_value; | ||
285 | 176 | } | ||
286 | 177 | |||
287 | 178 | return $data[$name]; | ||
288 | 179 | } | ||
289 | 180 | |||
290 | 181 | /** | ||
291 | 182 | * Rebuild the path alias white list. | ||
292 | 183 | * | ||
293 | 184 | * @return | ||
294 | 185 | * An array containing a white list of path aliases. | ||
295 | 186 | */ | ||
296 | 187 | function path_alias_cache_path_alias_whitelist_rebuild() { | ||
297 | 188 | // For each alias in the database, get the top level component of the system | ||
298 | 189 | // path it corresponds to. This is the portion of the path before the first "/" | ||
299 | 190 | // if present, otherwise the whole path itself. | ||
300 | 191 | $whitelist = array(); | ||
301 | 192 | $result = db_query("SELECT SUBSTRING_INDEX(src, '/', 1) AS path FROM {url_alias} GROUP BY path"); | ||
302 | 193 | while ($row = db_fetch_object($result)) { | ||
303 | 194 | $whitelist[$row->path] = TRUE; | ||
304 | 195 | } | ||
305 | 196 | cache_set('path_alias_whitelist', $whitelist, 'cache_path'); | ||
306 | 197 | return $whitelist; | ||
307 | 198 | } | ||
308 | 199 |
The whitelist should be handled as a cache item, not a variable.