I stumbled across this forum post (#329783: How to include CSS from a menu_hook in Drupal 6?) where the creator was trying to upgrade his module to Drupal 6, but unfortunately a change in drupal_add_css, namely the addition of a file_exists() check, has prevented them from being able to add a dynamically generated css file created via a menu callback.
It seems to me that there should be an alternative to file_exists() that actually returns TRUE if the file in question is a Drupal path; drupal_file_exists() would be the logical candidate.
| Comment | File | Size | Author |
|---|---|---|---|
| #4 | 329812-drupal-add-css-js-D7.patch | 1.82 KB | dave reid |
| #2 | 329812-drupal-add-css-js-D7.patch | 2.01 KB | dave reid |
| #1 | 329812-drupal_add_css-D7.patch | 879 bytes | dave reid |
Comments
Comment #1
dave reidSo the problem is we should be able to do this:
This works for JavaScript files defined in hook_menu() but not for CSS files since there is a check for file_exists in drupal_get_css. We can use menu_valid_path to check if a path is defined by a menu path. Interesting to note that drupal_get_js does not perform any checks for file_exists. Is this something we should standardize in that function as well? Also moving this to 7.x so we can get it fixed, and backport if needed. Anyway, code is ready for review.
Comment #2
dave reidAlternate patch that adds the same logic
if (file_exists($file) || menu_valid_path(array('link_path' => $file)))to drupal_add_js.Comment #3
Anonymous (not verified) commentedThe last submitted patch failed testing.
Comment #4
dave reidRe-rolled patch.
Comment #6
dave reidI don't believe you bot. Patch still applies to HEAD.
Comment #7
moshe weitzman commentedIs menu_valid_path() expensive? If so, this needs more thought.
Comment #8
dave reidI'll do some benchmarks to see how it affects performance.
Comment #9
dave reidBefore patch:
After patch:
This was on the homepage with the following js/css added:
Keep in mind that when CSS/JS aggregation/compression is turned on, this will have minimal impact.
Comment #10
dave reidComment #11
moshe weitzman commentedThe standard deviation in the second benchmark is too big to be useful. Perhaps try with more requests.
Comment #13
dave reidRe-rolled and marking needs benchmarks.
Comment #14
cburschkaI have no access to a benchmarking environment right now, but this patch doesn't (or shouldn't) affect performance for any case that currently exists.
file_exists($file) || menu_valid_path($file)is equivalent tofile_exists($file)unless you actually do give it an internal system path, and then the performance cost is linear to the number of system paths you give it. This patch doesn't affect performance itself; it only makes it an option for module developers to affect performance.However, note that menu_valid_path not only queries the router table; it also loads placeholder objects. This can indeed get disproportionally expensive if you include a script on, say, node/%node (for whatever reason).
Comment #15
mfer commentedWhat's the use case for dynamically creating paths like this?
If I wanted to dynamically add css to a page I'd add it inline which is now an option in drupal_add_css(). This saves a full drupal page load.
I imagine I'm missing something. Can someone explain a use case?
Comment #16
cburschkaI just realized something else - you can bypass the file_exists check by running it through
url($path, array('absolute' => TRUE)). Absolute URLs can be loaded as stylesheets without being validated. The only thing you lose that way, compared to menu_valid_path(), is that Drupal will not check if the current user has access, or the path exists - but loading a stylesheet that returns 404/403 will fail silently anyway, so we save ourselves an expensive check.So combined with the inline CSS mfer describes, now I'm even less sure whether we need this.
Comment #17
cburschkaPending other comments, I am setting this to won't fix.
Rationale:
- No module will ever provide a callback for a JS/CSS file that another module includes. Any time a dynamic JS/CSS file is included, your own module includes the code and provides the callback.
- If you have control over the inclusion of the dynamic JS/CSS URL, you can easily make it an absolute URL as outlined above. This will bypass the check.
- Checking for path validity before including a JS file is nice, but it's not vital. If the callback ends up not working, the browser will hit Drupal with a separate request for nothing. But if we check every callback, then all the working callbacks will be checked twice (once during inclusion and once by the separate request). In a trade-off, better to be fast under normal conditions than in error cases.