Making it default to TRUE would be safest but might require quite a bit of code to be patched. Certainly, I can live with that. It also mandates that we stop using $_GET['q'] and that we run everything through arg().

Dries, usually I fulfill your requests much faster, this particular one took off only after a year you have written about it, but still, here it is. The $_GET['q'] part is not yet done but that's piece of cake.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

chx’s picture

Note that this is only the first step. The second will be patching menu_execute_active_handler... But first let's get this in a working shape... I guess there will be lot of things broken.

Dries’s picture

Thanks for working on this, chx. It's an important feature. However, this is not 100% what I had in mind. What I had in mind was a simple (optional) "type system". For example:

arg(1, 'integer');

Then, the first argument would be cast to an integer. Other types could be 'raw' and 'plain' (escaped text). The 'problem' with your version is that arg() can have various return values / types, which is confusing. I think it should always return the value. The comparison shouldn't take place in arg(), IMO.

The problem is that often, people trust the return value of arg(1), which they shouldn't.

chx’s picture

FileSize
362 bytes

Maybe we could introduce wrappers so it's less confusing?

Now that you mention plain... yep. Raw is never needed, so the attached patch would also be enough just for security.

chx’s picture

Status: Needs review » Needs work

neh, the second patch is not good, for example profile uses something close to raw. I will think on it.

But I still like my solution best :)

Wim Leers’s picture

Version: x.y.z » 7.x-dev

Moving to 7.x. This would be a small - yet nice feature :)

Damien Tournoud’s picture

@chx: can you confirm this can be closed?

chx’s picture

Title: Secure arg() » Secure argument passing

Not at all. There are ideas brewing about validating path parts in the new menu system.

chx’s picture

Status: Needs work » Needs review
FileSize
6.52 KB

The wish for something like this harkens back to the days when I was a Drupal freshie.

We have a new menu property: mask.

mask can be a regular expression as demonstrated -- not for the faint of heart, mind you -- in taxonomy module.

In order to save people from writing regular expressions for the most common cases, we are introducing some shorthands: '+' any character, %d means a number, %c means a word character, %t means a character that can appear in a node type -- lower and upper case letters and the underscore.

What this makes this interesting is that you can also mask the parts beyond the router path. So if you have defined foo/bar then foo/bar/%d means that foo/bar and foo/bar/123 are acceptable paths here, otherwise the path will not be found. foo/bar/%t/%d means that foo/bar/article/12 is good and so is foo/bar/article. If you want to not allow foo/bar/article, never forget that you can supply a regexp yourself. %d+ means any number of numeric arguments so foo/bar/123/1243 etc are also good.

If router path is $path then "$path/+" is what currently Drupal does. Do not let the additional slash fool you: as detailed above, the parts after the router path are always optional.

chx’s picture

To clarify, node/%edit gets translated automatically to node/\d+/edit so no need to define a mask. You need a mask if your percent sign is not a positive integer or if your callback can accept arguments after the router path. The latter is automatic right now and while extermely powerful, a little validation can't hurt.

Damien Tournoud’s picture

After an insightful chat with chx on IRC, I'm convinced that we need the extra flexibility of this new 'mask' property.

To clarify, there is three routes hook_menu implementations might take while dealing with this new property:

  • Don't bother, if you only have numeric placeholders
  • Use the shorthand forms, if you only have simple needs (mixing numeric and text arguments)
  • Go the full regexp route, if you have complex urls or if you don't like simple solutions

The shorthand forms ease the definition of matchers for menu parts: you can use '%d', '%c' and '%t' as a menu part and the regexp will be built for you.

Obviously the code needs work (I'm not even convinced it works in its current form), but the architecture looks sane.

Oh, one more thing: I'm not convinced about the usefulness of '%c'. What are the use cases for this? (to be clear: this will prevent any non-word characters (such as . , - etc.) to be used).

moshe weitzman’s picture

Could you provide some example paths from Contrib where this will be useful? There is only one instance in this patch for core. (taxonomy/term)

chx’s picture

Moshe, I am fairly sure that's not the case -- any core function that takes arguments after its router path needs a mask. Contrib... for example, you have og/subscribe/%node and the callback function is function og_subscribe($node, $uid = NULL) the mask is og/subscribe/%d/%d. Thus the $uid is ensured to be a number. Without the mask you can not pass in a $uid. Edit: but even with the mask the $uid is not mandatory. I admit this is not 100% intuitive.

Or you have og/approve/%node/%user/% where the last percent sign is a token. its mask could be og/approve/%d/%d/[a-fA-F0-9] or a simpler, less strict mask could be og/approve/%d/%d/%c. Without the mask you can't pass in hex only numbers.

Note that if you dislike copypaste then X/X/%d/%d/%c would do or I think the code would happily deal with even //%d/%d/%c. Edit: this is a safety belt against typos. We might say that this feature falls in the "we do not babysit broken code" category and remove it.

dropcube’s picture

subscribing, will review later.

chx’s picture

FileSize
6.93 KB

cwgordon7 argues that using an array would be better than the paths. I tend to agree -- while in the router path we can skip the literal parts and most of the times we can also skip the wildcards as they are numeric anyways.

cwgordon7’s picture

FileSize
11.47 KB

Updated patch that removes the %t, and instead lets you define constants of the form strtoupper(placeholdername) . '_MASK' (e.g. NODE_TYPE_MASK). It will then concatenate the value of that constant into the regular expression.

Also removes an accidental menu_rebuild() from index.php.

Also adds schema definition/update functions.

Also moves a lot of the heavy work of building the mask from the array into a separate function.

Also adds a test case.

cwgordon7’s picture

FileSize
11.29 KB

After discussion with chx in irc, we agreed that the /? was insufficient. We are now using rtrim($path, '/') before checking the path, thus maintaining the old behavior of having node/1, as well as node/1/, as well as node/1///// be valid paths. Whether this behavior should continue is up to community debate.

Note that this is at code needs review because there is no architecture needs review status, there are many known bugs that can only be fixed once the new API is fully adapted throughout all modules, which we don't want to do until we have a solid, supported architecture.

cwgordon7’s picture

FileSize
11.64 KB

Updated patch posted, with better function names and removed trailing slash handling after realizing that path.inc does that for us.

Damien Tournoud’s picture

#17 looks all clean, good and shiny, except for that:

  $mask_length = max(array_keys($item['mask']));
  for ($i = 0; $i <= $mask_length; $i++) {
    // build $mask_part
    $mask .= $prefix . $mask_part;
  }
  $mask = '#^'. $mask . $suffix .'$#';

First it would be cleaner to have the $suffix addition moved inside the loop (to be consistent with the $prefix). Then, I think the $mask_length is wrong, it should probably be something like:

  $mask_length = max(array_keys($item['mask'])),max(array_keys($item['_parts']));

Finally, because %d is equal to %, why not dropping it?

chx’s picture

$suffix inside the loop won't work, it's stack like. $mask1 . $mask2 . $postfix2 . $postfix1 -- if you concat $mask1 to $postfix1 how will you stick the next ones between them. % works because it was the quickest way to code a fallback but we won't really advertise this :) . %d is the consistent mask.

Damien Tournoud’s picture

@chx, ok drop my first and last comment from #18. What about the comment about $mask_length?

chx’s picture

FileSize
10.75 KB

That's one valid concern -- but it's a bit of an edge case so I commented it.

pwolanin’s picture

just commenting to subscribe for now. chx and I discuessed this a few weeks ago - hopefully it can add some robustness to callback handling.

chx’s picture

FileSize
11.04 KB

A 'rest' key is supported so that you can define any number of closing arguments.

dmitrig01’s picture

FileSize
12.63 KB

fixed an edge case, added tests for 'rest', and moved the test file to the correct location

chx’s picture

Spot the bug! I know where it is and time permitting I will fix it :) but the patch is still in "architecture (needs review)". (hint. it's with the rest key)

chx’s picture

FileSize
11.42 KB

The problem was that the suffix needs to go after the rest, otherwise rest could replace the specified optional arguments. Adjusted the test too.

dmitrig01’s picture

BTW, the status would be "architecture (needs review)" if we had one like that. the patch will generate errors in head, because it's the architecture that needs review. if we like the architecture then the rest of the patch will be fixed.

maartenvg’s picture

Status: Needs review » Needs work

Patch no longer applies. (menu.test already exists)

patching file includes/menu.inc
Hunk #5 succeeded at 2408 (offset 16 lines).
Hunk #6 succeeded at 2436 (offset 16 lines).
The next patch would create the file modules/simpletest/tests/menu.test,
which already exists!  Assume -R? [n] 
Apply anyway? [n] y
patching file modules/simpletest/tests/menu.test
Patch attempted to create file modules/simpletest/tests/menu.test, which already exists.
Hunk #1 FAILED at 1.
1 out of 1 hunk FAILED -- saving rejects to file modules/simpletest/tests/menu.test.rej
patching file modules/system/system.install
Hunk #1 succeeded at 876 (offset -4 lines).
Hunk #2 succeeded at 3056 (offset 7 lines).
patching file modules/taxonomy/taxonomy.module
patching file modules/taxonomy/taxonomy.pages.inc
chx’s picture

chx’s picture

Status: Needs work » Needs review
FileSize
9.26 KB

Keeping up with HEAD.

Status: Needs review » Needs work

The last submitted patch failed testing.

moshe weitzman’s picture

I tried to review this but really don't feel qualified. I did notice that it is missing a patch to menu.api.php which described the new mask property.

chx’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch failed testing.

chx’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch failed testing.

chx’s picture

Status: Needs work » Needs review
FileSize
8.94 KB

Reroll against HEAD.

Status: Needs review » Needs work

The last submitted patch failed testing.

chx’s picture

Status: Needs work » Needs review
FileSize
10.24 KB

Poor testbot. I left in a menu_rebuild in index.php.

Edit: phew, caught it before cron ran so the previous comment is now deleted.

chx’s picture

FileSize
9.85 KB

Aaaaand I managed to upload the same but wrong patch. Damn.

Status: Needs review » Needs work

The last submitted patch failed testing.

chx’s picture

Status: Needs work » Needs review
FileSize
11.06 KB

The logic was a bit broken.

Status: Needs review » Needs work

The last submitted patch failed testing.

chx’s picture

Status: Needs work » Needs review
FileSize
13.42 KB

I just saw dblog passing without fixing. I fixed a few modules manually but dblog passed here. Let's see what else is breaking. Note that breaking is good because it signals the patch does something useful.

Status: Needs review » Needs work

The last submitted patch failed testing.

pwolanin’s picture

Minor comment - we already use "mask" for a different aspect of the menu system, so that seems like a sub-optimal term for it. Maybe 'match' or 'filter' or 'expect'?

Also, this assumes people will write the regex correctly- can we maybe have an alias for using is_numeric() or have that as a default?

pwolanin’s picture

Version: 7.x-dev » 8.x-dev

I guess this is too big an API change for 7.x

webchick’s picture

subscribe.

greggles’s picture

pwolanin’s picture

Status: Needs work » Needs review
FileSize
1.36 KB

Here is a simple patch for 7.x and 8.x that delivers a 404 if there are extra path arguments.

Status: Needs review » Needs work

The last submitted patch, 63390-no-excess-args-51.patch, failed testing.

pwolanin’s picture

Status: Needs work » Needs review
FileSize
5.07 KB

ok, well that's revealing cruft in comment, forum, and other modules. This fixes some of it.

Status: Needs review » Needs work

The last submitted patch, 63390-no-excess-args-53.patch, failed testing.

pwolanin’s picture

Status: Needs work » Needs review
FileSize
7.06 KB

Oops - put the if in the wrong place before - hopefully this should remove most of the exceptions.

Status: Needs review » Needs work

The last submitted patch, 63390-no-excess-args-55.patch, failed testing.

pwolanin’s picture

Status: Needs work » Needs review
FileSize
7.07 KB

oops missed a {

Status: Needs review » Needs work

The last submitted patch, 63390-no-excess-args-57.patch, failed testing.

soyarma’s picture

subscribing

chx’s picture

We are definitely not dropping this very useful feature. Drupal 8 is already going in directions I am extremely uncomfortable with and dropping another Drupal-only, Drupal-defining feature is not in the cards.

pwolanin’s picture

Is this really a Drupal-defining feature? It seems rather a legacy of the old menu system.

greggles’s picture

I don't really feel like this is a "feature" of Drupal. @chx, can you provide some examples? Also, you went from working on this to being opposed to it. Is it pwolanin's approach or you've had a change of mind? Can you expand on your thinking?

moshe weitzman’s picture

Status: Needs work » Closed (duplicate)