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.
43 use Drush\Log\LogLevel;
47 * Return a list of the valid drush context names.
49 * These context names are carefully ordered from
50 * highest to lowest priority.
52 * These contexts are evaluated in a certain order, and the highest priority value
53 * is returned by default from drush_get_option. This allows scripts to check whether
54 * an option was different before the current execution.
56 * Specified by the script itself :
57 * process : Generated in the current process.
58 * cli : Passed as --option=value to the command line.
59 * stdin : Passed as a JSON encoded string through stdin.
60 * specific : Defined in a command-specific option record, and
61 * set in the command context whenever that command is used.
62 * alias : Defined in an alias record, and set in the
63 * alias context whenever that alias is used.
65 * Specified by config files :
66 * custom : Loaded from the config file specified by --config or -c
67 * site : Loaded from the drushrc.php file in the Drupal site directory.
68 * drupal : Loaded from the drushrc.php file in the Drupal root directory.
69 * user : Loaded from the drushrc.php file in the user's home directory.
70 * home.drush Loaded from the drushrc.php file in the $HOME/.drush directory.
71 * system : Loaded from the drushrc.php file in the system's $PREFIX/etc/drush directory.
72 * drush : Loaded from the drushrc.php file in the same directory as drush.php.
74 * Specified by the script, but has the lowest priority :
75 * default : The script might provide some sensible defaults during init.
77 function drush_context_names() {
79 'process', 'cli', 'stdin', 'specific', 'alias',
80 'custom', 'site', 'drupal', 'user', 'home.drush', 'system',
87 function drush_set_config_options($context, $options, $override = []) {
88 // Copy 'config-file' into 'context-path', converting to an array to hold multiple values if necessary
89 if (isset($options['config-file'])) {
90 if (isset($options['context-path'])) {
91 $options['context-path'] = array_merge([$options['config-file']], is_array($options['context-path']) ? $options['context-path'] : [$options['context-path']]);
94 $options['context-path'] = $options['config-file'];
98 // Take out $aliases and $command_specific options
99 drush_set_config_special_contexts($options);
101 drush_set_context($context, $options);
105 * For all global options with a short form, convert all options in the option
106 * array that use the short form into the long form.
108 function drush_expand_short_form_options(&$options) {
109 foreach (drush_get_global_options() as $name => $info) {
110 if (is_array($info)) {
111 // For any option with a short form, check to see if the short form was set in the
112 // options. If it was, then rename it to its long form.
113 if (array_key_exists('short-form', $info) && array_key_exists($info['short-form'], $options)) {
114 if (!array_key_exists($name, $options) || !array_key_exists('merge-pathlist', $info)) {
115 $options[$name] = $options[$info['short-form']];
118 $options[$name] = array_merge((array)$options[$name], (array)$options[$info['short-form']]);
120 unset($options[$info['short-form']]);
127 * There are certain options such as 'site-aliases' and 'command-specific'
128 * that must be merged together if defined in multiple drush configuration
129 * files. If we did not do this merge, then the last configuration file
130 * that defined any of these properties would overwrite all of the options
131 * that came before in previously-loaded configuration files. We place
132 * all of them into their own context so that this does not happen.
134 function drush_set_config_special_contexts(&$options) {
135 if (isset($options) && is_array($options)) {
136 $has_command_specific = array_key_exists('command-specific', $options);
137 // Change the keys of the site aliases from 'alias' to '@alias'
138 if (array_key_exists('site-aliases', $options)) {
139 $user_aliases = $options['site-aliases'];
140 $options['site-aliases'] = [];
141 foreach ($user_aliases as $alias_name => $alias_value) {
142 if (substr($alias_name,0,1) != '@') {
143 $alias_name = "@$alias_name";
145 $options['site-aliases'][$alias_name] = $alias_value;
148 // Expand -s into --simulate, etc.
149 drush_expand_short_form_options($options);
151 foreach (drush_get_global_options() as $name => $info) {
152 if (is_array($info)) {
153 // For any global option with the 'merge-pathlist' or 'merge-associative' flag, set its
154 // value in the specified context. The option is 'merged' because we
155 // load $options with the value from the context prior to including the
156 // configuration file. If the configuration file sets $option['special'][] = 'value',
157 // then the configuration will be merged. $option['special'] = array(...), on the
158 // other hand, will replace rather than merge the values together.
159 if ((array_key_exists($name, $options)) && (array_key_exists('merge', $info) || (array_key_exists('merge-pathlist', $info) || array_key_exists('merge-associative', $info)))) {
160 $context = array_key_exists('context', $info) ? $info['context'] : $name;
161 $cache =& drush_get_context($context);
162 $value = $options[$name];
163 if (!is_array($value) && array_key_exists('merge-pathlist', $info)) {
164 $value = explode(PATH_SEPARATOR, $value);
166 if (array_key_exists('merge-associative', $info)) {
167 foreach ($value as $subkey => $subvalue) {
168 $cache[$subkey] = array_merge(isset($cache[$subkey]) ? $cache[$subkey] : [], $subvalue);
172 $cache = array_unique(array_merge($cache, $value));
174 // Once we have moved the option to its special context, we
175 // can remove it from its option context -- unless 'propagate-cli-value'
176 // is set, in which case we need to let it stick around in options
177 // in case it is needed in backend invoke.
178 if (!array_key_exists('propagate-cli-value', $info)) {
179 unset($options[$name]);
185 // If command-specific options were set and if we already have
186 // a command, then apply the command-specific options immediately.
187 if ($has_command_specific) {
188 drush_command_default_options();
194 * Set a specific context.
197 * Any of the default defined contexts.
199 * The value to store in the context
202 * An associative array of the settings specified in the request context.
204 function drush_set_context($context, $value) {
205 $cache =& drush_get_context($context);
212 * Return a specific context, or the whole context cache
214 * This function provides a storage mechanism for any information
215 * the currently running process might need to communicate.
217 * This avoids the use of globals, and constants.
219 * Functions that operate on the context cache, can retrieve a reference
220 * to the context cache using :
221 * $cache = &drush_get_context($context);
223 * This is a private function, because it is meant as an internal
224 * generalized API for writing static cache functions, not as a general
225 * purpose function to be used inside commands.
227 * Code that modifies the reference directly might have unexpected consequences,
228 * such as modifying the arguments after they have already been parsed and dispatched
232 * Optional. Any of the default defined contexts.
235 * If context is not supplied, the entire context cache will be returned.
236 * Otherwise only the requested context will be returned.
237 * If the context does not exist yet, it will be initialized to an empty array.
239 function &drush_get_context($context = NULL, $default = NULL) {
241 if (isset($context)) {
242 if (!isset($cache[$context])) {
243 $default = !isset($default) ? [] : $default;
244 $cache[$context] = $default;
246 return $cache[$context];
252 * Set the arguments passed to the drush.php script.
254 * This function will set the 'arguments' context of the current running script.
256 * When initially called by drush_parse_args, the entire list of arguments will
257 * be populated. Once the command is dispatched, this will be set to only the remaining
258 * arguments to the command (i.e. the command name is removed).
261 * Command line arguments, as an array.
263 function drush_set_arguments($arguments) {
264 drush_set_context('arguments', $arguments);
268 * Gets the command line arguments passed to Drush.
271 * An indexed array of arguments. Until Drush has dispatched the command, the
272 * array will include the command name as the first element. After that point
273 * the array will not include the command name.
275 * @see drush_set_arguments()
277 function drush_get_arguments() {
278 return drush_get_context('arguments');
282 * Set the command being executed.
284 * Drush_dispatch will set the correct command based on it's
285 * matching of the script arguments retrieved from drush_get_arguments
286 * to the implemented commands specified by drush_get_commands.
289 * A numerically indexed array of command components.
291 function drush_set_command($command) {
292 drush_set_context('command', $command);
296 * Return the command being executed.
298 function drush_get_command() {
299 return drush_get_context('command');
303 * Get the value for an option.
305 * If the first argument is an array, then it checks whether one of the options
306 * exists and return the value of the first one found. Useful for allowing both
310 * The name of the option to get
312 * Optional. The value to return if the option has not been set
314 * Optional. The context to check for the option. If this is set, only this context will be searched.
316 function drush_get_option($option, $default = NULL, $context = NULL) {
317 // Uncomment when fumigating.
318 // $backtrace = debug_backtrace()[1];
319 // if (!strpos($backtrace['file'], 'engines') && !strpos($backtrace['file'], 'preflight') && !strpos($backtrace['file'], 'backend')) {
320 // drush_log('drush_get_option() has been deprecated and is unreliable. Called by '. $backtrace['function']. ' in '. $backtrace['file']. ':'. $backtrace['line'], LogLevel::WARNING);
326 // We have a definite context to check for the presence of an option.
327 $value = _drush_get_option($option, drush_get_context($context));
330 // We are not checking a specific context, so check them in a predefined order of precedence.
331 $contexts = drush_context_names();
333 foreach ($contexts as $context) {
334 $value = _drush_get_option($option, drush_get_context($context));
336 if ($value !== NULL) {
342 if ($value !== NULL) {
350 * Get the value for an option and return it as a list. If the
351 * option in question is passed on the command line, its value should
352 * be a comma-separated list (e.g. --flag=1,2,3). If the option
353 * was set in a drushrc.php file, then its value may be either a
354 * comma-separated list or an array of values (e.g. $option['flag'] = array('1', '2', '3')).
357 * The name of the option to get
359 * Optional. The value to return if the option has not been set
361 * Optional. The context to check for the option. If this is set, only this context will be searched.
363 function drush_get_option_list($option, $default = [], $context = NULL) {
364 $result = drush_get_option($option, $default, $context);
366 if (!is_array($result)) {
367 $result = array_map('trim', array_filter(explode(',', $result)));
374 * Get the value for an option, but first checks the provided option overrides.
376 * The feature of drush_get_option that allows a list of option names
377 * to be passed in an array is NOT supported.
379 * @param option_overrides
380 * An array to check for values before calling drush_get_option.
382 * The name of the option to get.
384 * Optional. The value to return if the option has not been set.
386 * Optional. The context to check for the option. If this is set, only this context will be searched.
389 function drush_get_option_override($option_overrides, $option, $default = NULL, $context = NULL) {
390 return drush_sitealias_get_option($option_overrides, $option, $default, '', $context);
394 * Get an option out of the specified alias. If it has not been
395 * set in the alias, then get it via drush_get_option.
397 * @param site_alias_record
398 * An array of options for an alias record.
400 * The name of the option to get.
402 * Optional. The value to return if the option does not exist in the site record and has not been set in a context.
404 * Optional. The context to check for the option. If this is set, only this context will be searched.
406 function drush_sitealias_get_option($site_alias_record, $option, $default = NULL, $prefix = '', $context = NULL) {
407 if (is_array($site_alias_record) && array_key_exists($option, $site_alias_record)) {
408 return $site_alias_record[$option];
411 return drush_get_option($prefix . $option, $default, $context);
416 * Get all of the values for an option in every context.
419 * The name of the option to get
421 * An array whose key is the context name and value is
422 * the specific value for the option in that context.
424 function drush_get_context_options($option, $flatten = FALSE) {
427 $contexts = drush_context_names();
428 foreach ($contexts as $context) {
429 $value = _drush_get_option($option, drush_get_context($context));
431 if ($value !== NULL) {
432 if ($flatten && is_array($value)) {
433 $result = array_merge($value, $result);
436 $result[$context] = $value;
445 * Retrieves a collapsed list of all options.
447 function drush_get_merged_options() {
448 $contexts = drush_context_names();
449 $cache = drush_get_context();
451 foreach (array_reverse($contexts) as $context) {
452 if (array_key_exists($context, $cache)) {
453 $result = array_merge($result, $cache[$context]);
461 * Helper function to recurse through possible option names
463 function _drush_get_option($option, $context) {
464 if (is_array($option)) {
465 foreach ($option as $current) {
466 $current_value = _drush_get_option($current, $context);
467 if (isset($current_value)) {
468 return $current_value;
472 elseif (array_key_exists('no-' . $option, $context)) {
475 elseif (array_key_exists($option, $context)) {
476 return $context[$option];
483 * Set an option in one of the option contexts.
488 * The value to set it to.
490 * Optional. Which context to set it in.
492 * The value parameter. This allows for neater code such as
493 * $myvalue = drush_set_option('http_host', $_SERVER['HTTP_HOST']);
494 * Without having to constantly type out the value parameter.
496 function drush_set_option($option, $value, $context = 'process') {
497 $cache =& drush_get_context($context);
498 $cache[$option] = $value;
503 * A small helper function to set the value in the default context
505 function drush_set_default($option, $value) {
506 return drush_set_option($option, $value, 'default');
510 * Remove a setting from a specific context.
515 * Context in which to unset the value in.
517 function drush_unset_option($option, $context = NULL) {
518 if ($context != NULL) {
519 $cache =& drush_get_context($context);
520 if (array_key_exists($option, $cache)) {
521 unset($cache[$option]);
525 $contexts = drush_context_names();
527 foreach ($contexts as $context) {
528 drush_unset_option($option, $context);