Basically this patch tries to be a complement for: #625920: Commands with explicit callback functions skip drush_invoke() API, bearing in mind this (from doxygen-docs related to drush_invoke()):

In many cases, drush commands that are functionally part of a common collection of similar commands will all be declared in the same file, and every command defined in that file will start with the same command prefix.
For example, the command file "pm.drush.inc" defines commands such as "pm-enable" and "pm-disable". In the case of "pm-enable", the command file is "pm", and and command name is "pm-enable". When the command name starts with the same sequence of characters as the command file, then the repeated sequence is dropped; thus, the command hook for "pm-enable" is "drush_pm_enable", not "drush_pm_pm_enable".

Bug found thanks to this report: #926534: Drush user-create function not working.

Comments

luchochs’s picture

Better.
Now 'command-hook' does not depend on the name of the command defined in the key in the $items array.

moshe weitzman’s picture

Assigned: Unassigned » greg.1.anderson
moshe weitzman’s picture

Status: Active » Fixed

I committed this since it does fix user-create. Hopefully Greg can take a look and validate it still.

greg.1.anderson’s picture

Status: Fixed » Needs work

What we have is that in a commandfile user.drush.inc we have a function with a callback drush_user_create. Drush wants all of the callbacks in the "user" commandfile to begin with drush_user. In the past, the callback for user-create in user.drush.inc would be drush_user_user_create, but this was shortened in drush 3.x to drush_user_create for readability purposes. The problem that arises here is that when we have an explicit callback function (e.g. drush_user_create), we need to derive the command hook. In this case, the command hook should be "user_create", but because of the overlap between "drush_user_" and "user-create", we get "create".

The correct solution is to insure that the length of the command hook is never less than the length of the command.

I recommend backing out the submitted patch. I object slightly to the constant "6" (strlen("drush_")), but my larger issue is this line:

if ($commandfile = substr($command['callback'], 6, strlen($commandfile))) {

That "=" should be an "==". As written, the first branch of the if always executes. That could interfere with some people's hook functions. The other problem is that commandfile is assigned to, which could have unpredictable consequences depending on the contents of $command['callback'].

The patch might be close enough if you change the = to ==.

greg.1.anderson’s picture

Update: The correct solution is to use the command name (e.g. "user-create") as the command hook iff the last N characters of the callback match the command name. If they do not match, then the code should be as it currently exists (where it takes the last part of the callback minus the required prefix).

The command name must be adjusted to have "_" instead of "-" before comparing or using as a command hook.

luchochs’s picture

Status: Needs review » Needs work

Yes, I inadvertently omitted the second equality sign. That should be a comparison.
Sorry for the hasty patch.

I agree with #5.
Thanks for the review.

luchochs’s picture

Status: Needs work » Needs review
StatusFileSize
new891 bytes
new891 bytes

New version of the patch (now really better):

greg.1.anderson’s picture

Status: Needs work » Fixed

I appreciate the extra patch; unfortunately, I was somewhat wrong in #5, and the real solution is somewhat more complex. #5 is correct insofar as it goes, but it misses a few cases. Suppose someone has a commmand file foo with commands bar and baz, both with callbacks. Imagine one callback is drush_foo_set. By #5, the command hook would be "set", but it is really much more desirable to have the command hook be "foo_set".

This means that we have come full circle, and the patch in #1 is actually much more correct than #5, as it actually handles all situations. #5 was clearer, but not fully correct, and therefore a red herring. Sorry.

I committed a slight modification to what was previously accepted; the condition in #1 is always true, so now we simply strip off the "drush_" from the callback whenever the required prefix matches its first N characters, so now the code is a bit simpler, and still correct.

luchochs’s picture

By #5, the command hook would be "set", but it is really much more desirable to have the command hook be "foo_set".

Yes, it is much more desirable, but:
- the definition of the function name does not respect the existing convention, i.e.: drush_COMMANDFILE_COMMANDNAME, and
- this case is a super special case, such as site-alias/drush_sitealias_print and sql-dump/drush_sql_dump_execute (both could be avoided respecting the convention).

I prefer #5 because it considers the situation where the callback function and the command are defined in separate files.
Imagine the cache-clear command (located in core.drush.inc) with a callback function (located in clear.cache.inc), i.e, with this 'callback' => 'drush_core_cache_clear'.

My opinion.

greg.1.anderson’s picture

That was sort of my reasoning when I suggested #5 initially. When the callback is something like drush_core_cache_clear, perhaps there is little technical difference between using core_cache_clear or cache_clear as the hook name, so we could choose what we like. cache_clear might be better, hence the suggestion in #5.

Unfortunately, if the callback is drush_COMMANDFILE_verb, where verb is some simple term like create or set, then we must use COMMANDFILE_verb as the hook name, lest two different drush commands pick the same verb for different functions.

I felt that there was no non-ambiguous way to determine if verb is "simple" (likely to conflict), ergo I felt we had to reject #5.

luchochs’s picture

This seems to be a clash between the semantics and the convention.
A developer who just wants to create his command, will limit himself to follow the naming convention, and drush_COMMANDFILE_verb is not an option (maybe I omit something).

E.g.:
site-alias command:
Non-conventional and semantic:
dush_sitealias_print
Conventional:
dush_sitealias_site_alias
Conventional and semantic:
dush_sitealias_site_alias_print => command name = site-alias-print

sql-dump command:
Non-conventional and semantic:
dush_sql_dump_execute
Conventional and semantic:
dush_sql_dump

I.e., my reasoning is:
If we just follow the convention, the ambiguous command names are avoided.

Sorry for being dense and for my English that surely isn't the best.

greg.1.anderson’s picture

@luchochs: You are absolutely correct. If we follow the convention, then we can avoid ambiguous command names, and in fact we can remove the 'callback' item completely, as drush knows what it "should be". If we always followed the convention, then we could even remove support for the 'callback' item, and the code under discussion could be removed completely.

Given that this code exists explicitly to handle callback function names that do not follow the convention, I think that it is best to be conservative about how much reduction to do. Too much reduction is what caused field_create to conflict with user_create in the first place, after all. (n.b. This particular problem also could have been fixed simply by removing the 'callback' item from user_create, since the function name followed the convention. Fixing it that way, though, would have left this bug around waiting to bite the next set of functions that happened to choose the same verb for their function name...)

Hope that makes sense.

luchochs’s picture

Maybe we should wonder if the support for explicit callback functions is really necessary. His existence justifies itself only because it might use as reference to the developer, of course this is only my opinion. Subject for another issue, maybe.

If the solution to this issue will be #8, will we need to document drush_COMMANDFILE_verb and the limitation exposed in #9?

Update: Grammatical mistake corrected.

greg.1.anderson’s picture

We have definitely migrated away from using explicit callback functions. However, even though there are only a couple still in use in drush core (and those could certainly be fixed), completely removing this feature could potentially break a lot of contrib modules with drush commands. Maybe someday.

More documentation on the drush hook mechanism would be welcome. See drush.api.php.

moshe weitzman’s picture

We still want that explicit callback feature. Sometimes you have a situation where you want a handful of commands with different 'callback arguments' or other variances in hook_command. And they need to all point to same callback. For example, core-readme uses drush_print_file() as its callback and I suspect other commands will do that as well.

luchochs’s picture

Then I think that this would be necessary:

luchochs’s picture

Status: Fixed » Needs review
StatusFileSize
new1.07 KB

drush_invoque(), this is really good.
I'm beginning to want something like drush_luchochs_english_validate() or drush_luchochs_english_and_reality_sync().

greg.1.anderson’s picture

Status: Needs review » Reviewed & tested by the community

I think that is an improvement...

greg.1.anderson’s picture

Status: Reviewed & tested by the community » Fixed

Committed.

Status: Fixed » Closed (fixed)

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