Edit: The example in the first paragraph is a contrast, not a proposal. Symfony has event dispatcher listeners and mixins, at least in version 1.0.
To new Drupal devs, the lack of OO in the system is striking; however, after becoming familiar with the way that Drupal hooks work, it is apparent that OO concepts are used -- or more accurately, a system that is more akin to aspects is in place. Drupal hooks are nicer to use than aspects, though; they are more like a dynamic behavior system. (Note 1: whole article not relevant to the point of this post, but the diagram on the first page is interesting. Note 2: in the article I implement behaviors with standard Java language features + an optional code generator, but I also wrote a version using aspects). Drupal hooks avoid the complexity of head-and-tail patching by defining multiple stages of hooks (c.f. form_alter), and limiting each to head-patches only. Drush commands have the same concept, with validate_hook, pre_hook, hook, post_hook and hook_rollback available for each command function.
This is relevant to Drush OO, because we want Drush commands to continue to be easy to write. Any OO that we add on top of the existing system should maintain the current convenience that we have today. A simple Drush command drush cmd a b c
can be easily implemented via function drush_modulename_cmd($a, $b, $c)
, which is great.
One thing that makes it hard to write Drush commands for some Drupal modules is when the code is not factored out in a way to allow the existing functions to be called procedurally from outside of Drupal (c.f. drush_system_modules_form_submit()
and function system_modules_submit($form, &$form_state)
). Similarly, one thing that makes it hard to call Drush command functions from different Drush commands is the use of drush_get_option
, which is to say the global context system. drush_set_option()
and drush_invoke_process()
are of course of great help here, but I think it would be worthwhile to define a context class that can be used as the basis for passing options to commands. Our simple (but as yet still procedural) command definition would then become function drush_modulename_cmd($context, $a, $b, $c)
.
The interesting thing about contexts is that the Drush concept of contexts and site aliases are really similar enough to be the same thing. See also the tragic drush_sitealias_evaluate_path()
function, which perturbs the contexts concepts with 'additional options' (really just another context) and the values it needs to fetch from 'source-command-specific' or 'target-command-specific' sections of one of the two site alias parameters to the rsync function (which themselves might contain additional values: @dev:%files passes a reference to the "dev" site alias and also the %files path alias in one string). The best cure for the rube-goldberg spaghetti in this function would be a context object that additional contexts could be attached to, much in the same way the existing procedural context system chains together the process, alias, command-specific (global), cli and other contexts. It should be possible to pick up the values from 'source-command-specific' in the source site alias and attach them to the local $context variable such that $context->get('foo') will return the appropriate overridden value, should it exist. Similarly, when Drush processes drush @site cmd ...
, the values from @site should be attached to the global $context object.
Another thing that makes it hard to call other Drush commands is embedded calls to drush_confirm and drush_choice. In Drush 6, all user input methods should be called through an object attached to the context. There should be an identifier associated with each prompt, much in the same way that each call to drush_set_error gets its own ID. It should be possible to set the value for the answer to any prompt via a property in the context that is named per the prompt ID. Alternately, the caller could replace the input handler with an arbitrary object that handles requests for input.
Drush has a nice pluggable logging system; the active logger should also be attached to the local context object.
I don't have any strong opinions about OOp-ing the commands themselves; anything that maintains the ease of the current procedural commands is fine. Reconciling contexts, options and site aliases is the most important thing. See also related issue #1316322: Add PSR-0 autoloader to drush for comments on symfony and finding the class loader.
Comments
Comment #0.0
greg.1.anderson CreditAttribution: greg.1.anderson commentedAdd a clarifying note at the beginning; no other change.
Comment #2
greg.1.anderson CreditAttribution: greg.1.anderson commentedHere are some Symfony assets that might be useful in Drush.
Symfony has a nice 'Console' class that we might make good use of: http://symfony.com/doc/current/components/console.html
There is also a 'Config' class, but at first glance it does not seem to be well-suited for our needs of nested name-value pair stores.
The 'Process' and PhpProcess class that could be the basis for a new DrushProcess (backend invoke objectified): http://symfony.com/doc/current/components/process.html
The 'Finder' class looks like a good way to find files: http://symfony.com/doc/current/components/finder.html
Comment #4
RobLoachThere are a few things that would be helpful here. We should evaluate whether or not they make sense for Drush. Like you mentioned though, we'd need a way to bring these dependencies in and #1316322: Add PSR-0 autoloader to drush is properly that, so I'm marking this postponed.
Thanks for bringing this up, Greg.
Comment #5
moshe weitzman CreditAttribution: moshe weitzman commentedLet's not postpone this discussion. The fact that implementation is dependant on PSR-0 is not currently important.
Comment #6
greg.1.anderson CreditAttribution: greg.1.anderson commentedc.f. #1816192: Beware the poison pill: flaw in drush_get_context can expose Drush to mysterious failures
Comment #7
greg.1.anderson CreditAttribution: greg.1.anderson commentedLet's continue at #1837280: Discussion: Use Symfony Console Component to decrease the Drush/Drupal specific code parts of Drush..
Comment #7.0
greg.1.anderson CreditAttribution: greg.1.anderson commentedAdd links to symfony docs.