In the old routing system, we had the ability to automatically handle tail placeholders in a path. That is:

$items['foo/bar'] = array( ... );

// GET /foo/bar/baz/beep/whee
function page_callback($a = '', $b = '') {
  $args = func_get_args();
  // $args contains [baz, beep, whee]
}

The new Symfony-based router does not. All arguments have to be specified explicitly, and can have defaults specified in the route, rather than the controller. However, there is no way to handle the things that we've previously done via func_get_args().

There's a good argument to be made that is a feature, not a bug. For one it's a slight security hole to allow those as it means a path of /node/5/something-nsfw will render and route properly, which makes it way easy to google bomb a site. For another, it just obfuscates the code.

We discussed this briefly in the WSCCI meeting this morning, and the unanimous conclusion was "screw it, don't support that anymore, if you have something like that use a GET parameter like a normal person." That would mostly affect autocomplete callbacks, which would need to switch to using /callback/path?autocomplete=hel instead of /callback/path/hel.

I think this is a good approach. However, I'll leave this issue open until Friday 2 November (middle of BADCamp) for discussion in case there's a very strong argument for why we cannot do that and need to go to the work of figuring out how to support tail arguments.

I know that's not a huge discussion window, but changing that strategy would imply a non-trivial amount of additional work for WSCCI and we're running out of time in which to do it. :-)

Comments

cweagans’s picture

+1, We should drop menu tail arguments and use $_GET instead

xjm’s picture

Ugh. I think we should really consider this carefully. The non-trivial amount of work saved for WSCCI might be offset with a non-trivial amount of work to prevent autocomplete regressions.

xjm’s picture

sun’s picture

The primary consumer of this is Search, not autocomplete, since the autocomplete requests are not user-facing in the first place, so can be changed at will (and doing so will eliminate #3-alike issues entirely).

There's a case to be made that search should use the quasi industry standard of a q= GET query parameter.
(and I'd +1 that, since that would heavily simplify the search block form handling; see also #1789768: search_box_form_submit() improperly calls form_set_error()).

However, the are also strong positions and good points in favor of menu tails: Primarily SEO.

There are a lot of contributed modules like Search API, Facet API, and others that could make their lives a tonne easier by using query parameters instead of path parameters, but they intentionally do not and make their lives harder, since the path parameters lead to vastly better search engine result rankings (a.k.a. SEO 101), and partially also, since path parameters are more user-friendly.

I do not think we want or can make a decision against menu tail support here without sufficient buy-in from maintainers of such modules.

chx’s picture

Title: [Discussion] Drop menu tail handling: Y/N » [Discussion] Drop automated passing of extra argument: Y/N

%menu_tail is something totally else and could easily replace this functionality.

Crell’s picture

Thank you for the clarification, chx.

sun: Peter Wolanin (of core search and apachesolr fame) was in the meeting back in Denver where we first discussed this, and seemed in favor of it. He was the one who alerted us to the minor security issue in the first place. :-)

swentel’s picture

sun’s picture

#4 is obsolete if the new router will still support %menu_tail - i.e., allowing to explicitly opt-in/enable functionality for a particular route to receive any (0-n) overflow path parameters in the controller.

If that is the case, there's not really something that needs discussion here, IMHO. The automated passing of extra parameters is rarely used today; IIRC, usage and use-cases significantly dropped with the new menu system in D6.

To summarize:

  1. Routes only apply to defined path parameters.

    E.g., /node/123 yields a node view, /node/123/what/the/path yields a 404.

  2. Autocomplete should be changed to a GET query parameter. Inherently eliminating a dozen of bug reports in the queue.
  3. Search still works like it does now.

    E.g., /search/node/what/the/path yields a search for "what/the/path" (or whatever it currently returns).

Crell’s picture

I don't know what chx is referring to with %menu_tail. Given the way the new routing system works, we'd get a 404 before we even get to the point that we could extract that information, I think.

tstoeckler’s picture

If this includes dropping %menu_tail we can't get away with this, IMO. I completely agree with #8. I personally have had to use %menu_tail multiple times in modules and would find not having that at hand a serious regression.

jpstrikesback’s picture

Re: menu_tail. I don't know if anyone has the time for this but my concern like Crell was that a 404 would happen before menu_tail_to_arg() ever got a chance to do anything, chx could you give an example? Are you thinking of implementations in a menu item?

The SEO impact of turning /this/that/theother/thing into /this/that/theother?q=thing if not mitigated somehow is not small IIUC, but if there's a way to address it in contrib then I guess it doesn't matter?

klausi’s picture

@tstoeckler: could you elaborate on your use cases? Most of the time you have a maximum number of allowed arguments. You would only need a dynamic number if you allow infinite arguments.

Example: I have a maximum of 4 arguments. Just register multiple routes:

/my-page/{arg1}
/my-page/{arg1}/{arg2}
/my-page/{arg1}/{arg2}/{arg3}
/my-page/{arg1}/{arg2}/{arg3}/{arg4}

They all use the same page callback method:

public function myPageCallback($arg1, $arg2 = NULL, $arg3 = NULL, $arg4 = NULL) {
  ...
}

Am I missing something?

Crell’s picture

klausi: Actually you'd specify the defaults in the route, not in the controller, but yes. You can have lots of optional parameters.

I'm actually unclear on the use case for variable tails, to be honest. For search, as sun notes q= is a quasi-standard. And you really shouldn't have / in the tail string anyway, and that already causes parsing problems. (See comment #3.)

tstoeckler: What's your actual use case? And does it justify wading deep into the system to try and shoe-horn that capability in somehow? (I am not sure how we'd do that at this point, which is another reason I'd rather just let it go.)

tstoeckler’s picture

My use-case was webmail.module, where the name of an IMAP folder is part of the URL. Something like.
example.com/my-imap-connection/Inbox
Since an IMAP folder name can contain slashes, I needed %menu_tail. Most of the time a slash in the mailbox name signifies nested folders, which is why the nested URL maps nicely to the use-case. I.e.:
example.com/my-imap-connection/Work/Clients
example.com/my-imap-connection/Work/Internal
example.com/my-imap-connection/Work/Financial
Where the actual mailbox names are "Work/Clients", etc. but that's not obvious when seeing the URL (which is a good thing).
Of course
example.com/my-imap-connection?mailbox="Work/Clients"
is possible it is just not as nice, by far.
I cannot even in the slightest imagine what the effort would be to support it, but nevertheless my vote is to keep on supporting it.

Crell’s picture

I think that's sufficiently edge case-y that core doesn't need to directly support it. We would need to change, I believe, the SQL route retrieval logic, the regex that we're borrowing from Symfony, *and* figure out how those would even map to controller arguments. Likely we'd need to hack ControllerResolver. I really don't think that level of work is justified.

(To be frank, I don't know why you'd want to build a webmail client into a Drupal module in the first place, especially when with HttpKernel you could sub-embed a separate application, but that's neither here nor there.)

dasjo’s picture

I'm maintainer of facet_api_pretty_paths and interested in having access to a potentially dynamic-length menu_tail for search specific routes

pdrake’s picture

Would it be possible to have an explicit path matcher which would allow trailing path elements to be passed as an argument to the route handler as a higher priority match than standard paths? This type of explicit path match would mean that any dynamic paths registered "below" the explicit path would never get hit (this is the trade off), but I expect this would significantly reduce the effort required to support dynamic path lengths for the few cases they are necessary.

Basically, "GET /my/defined/path/test/argument" would match the explicit route /my/defined/path/%trailing_args instead of the standard route /my/defined/path/test/% or even /my/defined/%/%/% (which would, by nature of having both an explicit route and a standard route defined, never be called). The relatively few explicit paths could be matched by direct string matching before route matching is handed off to the standard path matcher.

greggles’s picture

For folks who say they want this can you also say if #12 doesn't handle it sufficiently for you?

#14 - you say it's the imap folder structure included in the url - why not make it a paramter like ?folder=blah/blah/blah ?

dasjo’s picture

ad #13: search as get-parameter only is not a standard in my opinion. neither drupal core search nor apachesolr module do so. having support for search arguments in the url is essential for SEO.

ad #18: i'd prefer being able to register my-page/* over the approach stated in #12. still that's not a hard requirement as we might just have to register routes for up to for example max. 10 allowed routes.

i'm wondering how views in core and it implementation of contextual filters would handle such cases?

greggles’s picture

#19 part 1 - that seems contrary to Google's on search results pages:

Use robots.txt to prevent crawling of search results pages or other auto-generated pages that don't add much value for users coming from search engines.

Indeed core blocks it by default in robots.txt.

It seems like a bit of an exaggeration to say it is "essential."

dasjo’s picture

i can hear your objections and essential might be an exaggeration. on the other hand, sun explained in #4 that there are existing use cases for such menu tails.

if we don't want to support this out-of-the-box, i'm still interested in

  1. how views will handle contextual filters? - will it register them dynamically as it knows how many contextual filters a given view uses?
  2. will there be a possibility to implement a custom router / path matcher to support menu tails? - i guess so
tstoeckler’s picture

Re #18: Sure that's possible, just like doing search queries with a query parameter is possible. It's just not as nice. So we definitely *can* drop this feature without making anything impossible in Contrib, it's just the question, whether that is a good idea.

Damien Tournoud’s picture

+1 for dropping this.

Let's remember that there are two types of URLs:

  • User facing URLs: those are the ones that you give to your users. They can have mostly any format that you want. They get transformed into internal URLs during the first step of handling the request (KernelEvents::REQUEST in Symfony, which is called a "middleware" mostly everywhere else)
  • Internal URLs: those are the ones managed by the controlller.

This issue is for removing support for arbitrary number of arguments in Internal URLs. I sounds like an arbitrary limitation, but I personally can live with it.

All the use cases mentioned here can be supported by writing a simple rewriting middleware subscribing to KernelEvents::REQUEST.

tstoeckler’s picture

Re #23: Just to make sure I understand that correctly: even if we drop %menu_tail as it exists now we can still get to a situation where by doing what you propose in #23 either in contrib or in core somehow, I can
1. Register a page callback route at my/route
2. Hit http://example.com/my/route/i/am/a/menu/trail
3. In my Controller (or whatever is the new word for 'page callback') I can access $_GET['q'] (or the new request-based equivalent thereof) and have that evaluate to "i/am/a/menu/tail".
If that is the case then I don't think there is much to debate about, unless I'm missing something.

EDIT: page callback should be route above. All the fancy new words have got me confused...

Crell’s picture

tstoeckler: No, that's not the case. Currently, if you register /my/route, and then visit /my/route/goes/here, your controller will never even be found. A 404 happens before that.

What Damien is suggesting is to, effectively, treat tail arguments as a fancy case of URL aliasing, and futz with it in a request listener. Currently we have more or less eliminated all hard coded path futzing (including aliasing) and hook_url_inbound_alter() and replace both with "just use a request listener". Adding an additional listener should be easy. How complex the logic for it would be, I don't know. My concern is performance.

Damien, could you perhaps whip up a proof of concept of what you're talking about? I'm not sure it would work.

Crell’s picture

Status: Active » Fixed

The answer was apparently Yes. :-)

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.