4 * The Drush context API implementation.
6 * This API acts as a storage mechanism for all options, arguments and
7 * configuration settings that are loaded into drush.
9 * This API also acts as an IPC mechanism between the different drush commands,
10 * and provides protection from accidentally overriding settings that are
11 * needed by other parts of the system.
13 * It also avoids the necessity to pass references through the command chain
14 * and allows the scripts to keep track of whether any settings have changed
15 * since the previous execution.
17 * This API defines several contexts that are used by default.
20 * These contexts are used by Drush to store information on the command.
21 * They have their own access functions in the forms of
22 * drush_set_arguments(), drush_get_arguments(), drush_set_command(),
23 * drush_get_command().
25 * command : The drush command being executed.
26 * arguments : Any additional arguments that were specified.
29 * These contexts store options that have been passed to the drush.php
30 * script, either through the use of any of the config files, directly from
31 * the command line through --option='value' or through a JSON encoded string
32 * passed through the STDIN pipe.
34 * These contexts are accessible through the drush_get_option() and
35 * drush_set_option() functions. See drush_context_names() for a description
36 * of all of the contexts.
38 * Drush commands may also choose to save settings for a specific context to
39 * the matching configuration file through the drush_save_config() function.
42 use Drush\Log\LogLevel;
46 * Return a list of the valid drush context names.
48 * These context names are carefully ordered from
49 * highest to lowest priority.
51 * These contexts are evaluated in a certain order, and the highest priority value
52 * is returned by default from drush_get_option. This allows scripts to check whether
53 * an option was different before the current execution.
55 * Specified by the script itself :
56 * process : Generated in the current process.
57 * cli : Passed as --option=value to the command line.
58 * stdin : Passed as a JSON encoded string through stdin.
59 * specific : Defined in a command-specific option record, and
60 * set in the command context whenever that command is used.
61 * alias : Defined in an alias record, and set in the
62 * alias context whenever that alias is used.
64 * Specified by config files :
65 * custom : Loaded from the config file specified by --config or -c
66 * site : Loaded from the drushrc.php file in the Drupal site directory.
67 * drupal : Loaded from the drushrc.php file in the Drupal root directory.
68 * user : Loaded from the drushrc.php file in the user's home directory.
69 * home.drush Loaded from the drushrc.php file in the $HOME/.drush directory.
70 * system : Loaded from the drushrc.php file in the system's $PREFIX/etc/drush directory.
71 * drush : Loaded from the drushrc.php file in the same directory as drush.php.
73 * Specified by the script, but has the lowest priority :
74 * default : The script might provide some sensible defaults during init.
76 function drush_context_names() {
77 static $contexts = array(
78 'process', 'cli', 'stdin', 'specific', 'alias',
79 'custom', 'site', 'drupal', 'user', 'home.drush', 'system',
86 * Return a list of possible drushrc file locations.
89 * A valid drush context from drush_context_names().
91 * Optional. Specify a prefix to prepend to ".drushrc.php" when looking
92 * for config files. Most likely used by contrib commands.
94 * An associative array containing possible config files to load
95 * The keys are the 'context' of the files, the values are the file
98 function _drush_config_file($context, $prefix = NULL, $version = '') {
100 $base_name = 'drush' . $version . 'rc.php';
101 $config_file = $prefix ? $prefix . '.' . $base_name : $base_name;
103 // Did the user explicitly specify a config file?
104 if ($config_list = (array)drush_get_context('DRUSH_CONFIG')) {
105 foreach ($config_list as $config) {
106 if (is_dir($config)) {
107 $config = $config . '/' . $config_file;
109 $configs['custom'][] = $config;
113 if ($drupal_root = drush_get_context('DRUSH_SELECTED_DRUPAL_ROOT')) {
114 $configs['drupal'] = array(
115 $drupal_root . '/../drush/' . $config_file,
116 $drupal_root . '/sites/all/drush/' . $config_file,
117 $drupal_root . '/drush/' . $config_file,
120 if ($conf_path = drush_get_context('DRUSH_SELECTED_DRUPAL_SITE_CONF_PATH', 'sites/default')) {
121 $site_path = $drupal_root . '/' . $conf_path;
122 $configs['site'] = $site_path . "/" . $config_file;
126 // in the user home directory
127 $server_home = drush_server_home();
128 if (isset($server_home)) {
129 $configs['user'] = $server_home . '/.' . $config_file;
132 // in $HOME/.drush directory
133 $per_user_config_dir = drush_get_context('DRUSH_PER_USER_CONFIGURATION');
134 if (!empty($per_user_config_dir)) {
135 $configs['home.drush'] = $per_user_config_dir . '/' . $config_file;
138 // In the system wide configuration folder.
139 $configs['system'] = drush_get_context('DRUSH_SITE_WIDE_CONFIGURATION') . '/' . $config_file;
141 // in the drush installation folder
142 $configs['drush'] = dirname(__FILE__) . '/../' . $config_file;
144 return empty($configs[$context]) ? '' : $configs[$context];
149 * Load drushrc files (if available) from several possible locations.
151 function drush_load_config($context) {
152 drush_load_config_file($context, _drush_config_file($context));
153 drush_load_config_file($context, _drush_config_file($context, '', DRUSH_MAJOR_VERSION));
156 function drush_load_config_file($context, $config_list) {
157 foreach ((array)$config_list as $config) {
158 if (file_exists($config)) {
159 $options = $aliases = $command_specific = $override = array();
160 drush_log(dt('Loading drushrc "!config" into "!context" scope.', array('!config' => realpath($config), '!context' => $context)), LogLevel::BOOTSTRAP);
161 $ret = @include_once($config);
162 if ($ret === FALSE) {
163 drush_log(dt('Cannot open drushrc "!config", ignoring.', array('!config' => realpath($config))), LogLevel::WARNING);
166 if (!empty($options) || !empty($aliases) || !empty($command_specific) || !empty($override)) {
167 $options = array_merge(drush_get_context($context), $options);
168 $options['config-file'] = realpath($config);
170 unset($options['site-aliases']);
171 $options['command-specific'] = array_merge(isset($command_specific) ? $command_specific : array(), isset($options['command-specific']) ? $options['command-specific'] : array());
173 drush_set_config_options($context, $options, $override);
179 function drush_set_config_options($context, $options, $override = array()) {
180 // Copy 'config-file' into 'context-path', converting to an array to hold multiple values if necessary
181 if (isset($options['config-file'])) {
182 if (isset($options['context-path'])) {
183 $options['context-path'] = array_merge(array($options['config-file']), is_array($options['context-path']) ? $options['context-path'] : array($options['context-path']));
186 $options['context-path'] = $options['config-file'];
190 // Take out $aliases and $command_specific options
191 drush_set_config_special_contexts($options);
193 drush_set_context($context, $options);
197 * For all global options with a short form, convert all options in the option
198 * array that use the short form into the long form.
200 function drush_expand_short_form_options(&$options) {
201 foreach (drush_get_global_options() as $name => $info) {
202 if (is_array($info)) {
203 // For any option with a short form, check to see if the short form was set in the
204 // options. If it was, then rename it to its long form.
205 if (array_key_exists('short-form', $info) && array_key_exists($info['short-form'], $options)) {
206 if (!array_key_exists($name, $options) || !array_key_exists('merge-pathlist', $info)) {
207 $options[$name] = $options[$info['short-form']];
210 $options[$name] = array_merge((array)$options[$name], (array)$options[$info['short-form']]);
212 unset($options[$info['short-form']]);
219 * There are certain options such as 'site-aliases' and 'command-specific'
220 * that must be merged together if defined in multiple drush configuration
221 * files. If we did not do this merge, then the last configuration file
222 * that defined any of these properties would overwrite all of the options
223 * that came before in previously-loaded configuration files. We place
224 * all of them into their own context so that this does not happen.
226 function drush_set_config_special_contexts(&$options) {
227 if (isset($options) && is_array($options)) {
228 $has_command_specific = array_key_exists('command-specific', $options);
229 // Change the keys of the site aliases from 'alias' to '@alias'
230 if (array_key_exists('site-aliases', $options)) {
231 $user_aliases = $options['site-aliases'];
232 $options['site-aliases'] = array();
233 foreach ($user_aliases as $alias_name => $alias_value) {
234 if (substr($alias_name,0,1) != '@') {
235 $alias_name = "@$alias_name";
237 $options['site-aliases'][$alias_name] = $alias_value;
240 // Expand -s into --simulate, etc.
241 drush_expand_short_form_options($options);
243 foreach (drush_get_global_options() as $name => $info) {
244 if (is_array($info)) {
245 // For any global option with the 'merge-pathlist' or 'merge-associative' flag, set its
246 // value in the specified context. The option is 'merged' because we
247 // load $options with the value from the context prior to including the
248 // configuration file. If the configuration file sets $option['special'][] = 'value',
249 // then the configuration will be merged. $option['special'] = array(...), on the
250 // other hand, will replace rather than merge the values together.
251 if ((array_key_exists($name, $options)) && (array_key_exists('merge', $info) || (array_key_exists('merge-pathlist', $info) || array_key_exists('merge-associative', $info)))) {
252 $context = array_key_exists('context', $info) ? $info['context'] : $name;
253 $cache =& drush_get_context($context);
254 $value = $options[$name];
255 if (!is_array($value) && array_key_exists('merge-pathlist', $info)) {
256 $value = explode(PATH_SEPARATOR, $value);
258 if (array_key_exists('merge-associative', $info)) {
259 foreach ($value as $subkey => $subvalue) {
260 $cache[$subkey] = array_merge(isset($cache[$subkey]) ? $cache[$subkey] : array(), $subvalue);
264 $cache = array_unique(array_merge($cache, $value));
266 // Once we have moved the option to its special context, we
267 // can remove it from its option context -- unless 'propagate-cli-value'
268 // is set, in which case we need to let it stick around in options
269 // in case it is needed in backend invoke.
270 if (!array_key_exists('propagate-cli-value', $info)) {
271 unset($options[$name]);
277 // If command-specific options were set and if we already have
278 // a command, then apply the command-specific options immediately.
279 if ($has_command_specific) {
280 drush_command_default_options();
286 * Set a specific context.
289 * Any of the default defined contexts.
291 * The value to store in the context
294 * An associative array of the settings specified in the request context.
296 function drush_set_context($context, $value) {
297 $cache =& drush_get_context($context);
304 * Return a specific context, or the whole context cache
306 * This function provides a storage mechanism for any information
307 * the currently running process might need to communicate.
309 * This avoids the use of globals, and constants.
311 * Functions that operate on the context cache, can retrieve a reference
312 * to the context cache using :
313 * $cache = &drush_get_context($context);
315 * This is a private function, because it is meant as an internal
316 * generalized API for writing static cache functions, not as a general
317 * purpose function to be used inside commands.
319 * Code that modifies the reference directly might have unexpected consequences,
320 * such as modifying the arguments after they have already been parsed and dispatched
324 * Optional. Any of the default defined contexts.
327 * If context is not supplied, the entire context cache will be returned.
328 * Otherwise only the requested context will be returned.
329 * If the context does not exist yet, it will be initialized to an empty array.
331 function &drush_get_context($context = NULL, $default = NULL) {
332 static $cache = array();
333 if (isset($context)) {
334 if (!isset($cache[$context])) {
335 $default = !isset($default) ? array() : $default;
336 $cache[$context] = $default;
338 return $cache[$context];
344 * Set the arguments passed to the drush.php script.
346 * This function will set the 'arguments' context of the current running script.
348 * When initially called by drush_parse_args, the entire list of arguments will
349 * be populated. Once the command is dispatched, this will be set to only the remaining
350 * arguments to the command (i.e. the command name is removed).
353 * Command line arguments, as an array.
355 function drush_set_arguments($arguments) {
356 drush_set_context('arguments', $arguments);
360 * Gets the command line arguments passed to Drush.
363 * An indexed array of arguments. Until Drush has dispatched the command, the
364 * array will include the command name as the first element. After that point
365 * the array will not include the command name.
367 * @see drush_set_arguments()
369 function drush_get_arguments() {
370 return drush_get_context('arguments');
374 * Set the command being executed.
376 * Drush_dispatch will set the correct command based on it's
377 * matching of the script arguments retrieved from drush_get_arguments
378 * to the implemented commands specified by drush_get_commands.
381 * A numerically indexed array of command components.
383 function drush_set_command($command) {
384 drush_set_context('command', $command);
388 * Return the command being executed.
390 function drush_get_command() {
391 return drush_get_context('command');
395 * Get the value for an option.
397 * If the first argument is an array, then it checks whether one of the options
398 * exists and return the value of the first one found. Useful for allowing both
402 * The name of the option to get
404 * Optional. The value to return if the option has not been set
406 * Optional. The context to check for the option. If this is set, only this context will be searched.
408 function drush_get_option($option, $default = NULL, $context = NULL) {
412 // We have a definite context to check for the presence of an option.
413 $value = _drush_get_option($option, drush_get_context($context));
416 // We are not checking a specific context, so check them in a predefined order of precedence.
417 $contexts = drush_context_names();
419 foreach ($contexts as $context) {
420 $value = _drush_get_option($option, drush_get_context($context));
422 if ($value !== NULL) {
428 if ($value !== NULL) {
436 * Get the value for an option and return it as a list. If the
437 * option in question is passed on the command line, its value should
438 * be a comma-separated list (e.g. --flag=1,2,3). If the option
439 * was set in a drushrc.php file, then its value may be either a
440 * comma-separated list or an array of values (e.g. $option['flag'] = array('1', '2', '3')).
443 * The name of the option to get
445 * Optional. The value to return if the option has not been set
447 * Optional. The context to check for the option. If this is set, only this context will be searched.
449 function drush_get_option_list($option, $default = array(), $context = NULL) {
450 $result = drush_get_option($option, $default, $context);
452 if (!is_array($result)) {
453 $result = array_map('trim', array_filter(explode(',', $result)));
460 * Get the value for an option, but first checks the provided option overrides.
462 * The feature of drush_get_option that allows a list of option names
463 * to be passed in an array is NOT supported.
465 * @param option_overrides
466 * An array to check for values before calling drush_get_option.
468 * The name of the option to get.
470 * Optional. The value to return if the option has not been set.
472 * Optional. The context to check for the option. If this is set, only this context will be searched.
475 function drush_get_option_override($option_overrides, $option, $default = NULL, $context = NULL) {
476 return drush_sitealias_get_option($option_overrides, $option, $default, '', $context);
480 * Get an option out of the specified alias. If it has not been
481 * set in the alias, then get it via drush_get_option.
483 * @param site_alias_record
484 * An array of options for an alias record.
486 * The name of the option to get.
488 * Optional. The value to return if the option does not exist in the site record and has not been set in a context.
490 * Optional. The context to check for the option. If this is set, only this context will be searched.
492 function drush_sitealias_get_option($site_alias_record, $option, $default = NULL, $prefix = '', $context = NULL) {
493 if (is_array($site_alias_record) && array_key_exists($option, $site_alias_record)) {
494 return $site_alias_record[$option];
497 return drush_get_option($prefix . $option, $default, $context);
502 * Get all of the values for an option in every context.
505 * The name of the option to get
507 * An array whose key is the context name and value is
508 * the specific value for the option in that context.
510 function drush_get_context_options($option, $flatten = FALSE) {
513 $contexts = drush_context_names();
514 foreach ($contexts as $context) {
515 $value = _drush_get_option($option, drush_get_context($context));
517 if ($value !== NULL) {
518 if ($flatten && is_array($value)) {
519 $result = array_merge($value, $result);
522 $result[$context] = $value;
531 * Retrieves a collapsed list of all options.
533 function drush_get_merged_options() {
534 $contexts = drush_context_names();
535 $cache = drush_get_context();
537 foreach (array_reverse($contexts) as $context) {
538 if (array_key_exists($context, $cache)) {
539 $result = array_merge($result, $cache[$context]);
547 * Retrieves a collapsed list of all options
548 * with a specified prefix.
550 function drush_get_merged_prefixed_options($prefix) {
551 $merged_options = drush_get_merged_options();
553 foreach ($merged_options as $key => $value) {
554 if ($prefix == substr($key, 0, strlen($prefix))) {
555 $result[substr($key, strlen($prefix))] = $value;
563 * Helper function to recurse through possible option names
565 function _drush_get_option($option, $context) {
566 if (is_array($option)) {
567 foreach ($option as $current) {
568 $current_value = _drush_get_option($current, $context);
569 if (isset($current_value)) {
570 return $current_value;
574 elseif (array_key_exists('no-' . $option, $context)) {
577 elseif (array_key_exists($option, $context)) {
578 return $context[$option];
585 * Set an option in one of the option contexts.
590 * The value to set it to.
592 * Optional. Which context to set it in.
594 * The value parameter. This allows for neater code such as
595 * $myvalue = drush_set_option('http_host', $_SERVER['HTTP_HOST']);
596 * Without having to constantly type out the value parameter.
598 function drush_set_option($option, $value, $context = 'process') {
599 $cache =& drush_get_context($context);
600 $cache[$option] = $value;
605 * A small helper function to set the value in the default context
607 function drush_set_default($option, $value) {
608 return drush_set_option($option, $value, 'default');
612 * Remove a setting from a specific context.
617 * Context in which to unset the value in.
619 function drush_unset_option($option, $context = NULL) {
620 if ($context != NULL) {
621 $cache =& drush_get_context($context);
622 if (array_key_exists($option, $cache)) {
623 unset($cache[$option]);
627 $contexts = drush_context_names();
629 foreach ($contexts as $context) {
630 drush_unset_option($option, $context);
636 * Save the settings in a specific context to the applicable configuration file
637 * This is useful is you want certain settings to be available automatically the next time a command is executed.
640 * The context to save
642 function drush_save_config($context) {
643 $filename = _drush_config_file($context);
644 if (is_array($filename)) {
645 $filename = $filename[0];
649 $cache = drush_get_context($context);
651 $fp = fopen($filename, "w+");
653 return drush_set_error('DRUSH_PERM_ERROR', dt('Drushrc (!filename) could not be written', array('!filename' => $filename)));
656 fwrite($fp, "<?php\n");
657 foreach ($cache as $key => $value) {
658 $line = "\n\$options['$key'] = ". var_export($value, TRUE) .';';
663 drush_log(dt('Drushrc file (!filename) was written successfully', array('!filename' => $filename)));