drush.inc

  1. 8.0.x includes/drush.inc
  2. 6.x includes/drush.inc
  3. 7.x includes/drush.inc
  4. 3.x includes/drush.inc
  5. 4.x includes/drush.inc
  6. 5.x includes/drush.inc
  7. master includes/drush.inc

The drush API implementation and helpers.

Functions

Namesort descending Description
dlm Run print_r on a variable and log the output.
drush_backend_packet_log Backend command callback. Add a log message to the log history.
drush_check_self_update Check to see if a newer version of drush is available
drush_choice Ask the user to select an item from a list. From a provided associative array, drush_choice will display all of the questions, numbered from 1 to N, and return the item the user selected. "0" is always cancel; entering a blank line is also…
drush_choice_multiple Ask the user to select multiple items from a list. This is a wrapper around drush_choice, that repeats the selection process, allowing users to toggle a number of items in a list. The number of values that can be constrained by both min and max: the…
drush_clear_error Clear error context.
drush_cmp_error Check if a specific error status has been set.
drush_complete_cache_clear Clears completion caches.
drush_confirm Asks the user a basic yes/no question.
drush_die Exits with a message. In general, you should use drush_set_error() instead of this function. That lets drush proceed with other tasks. TODO: Exit with a correct status code.
drush_download_file Download a file using wget, curl or file_get_contents, or via download cache.
drush_do_command_redispatch Redispatch the specified command using the same options that were passed to this invocation of drush.
drush_do_multiple_command Used by functions that operate on lists of sites, moving information from the source to the destination. Currenlty this includes 'drush rsync' and 'drush sql sync'.
drush_errors_off Turn PHP error handling off.
drush_errors_on Turn PHP error handling on.
drush_export_info Generate code friendly to the Drupal .info format from a structured array. Mostly copied from http://drupalcode.org/viewvc/drupal/contributions/modules/features/featu....
drush_export_ini Generate an .ini file. used by archive-dump."
drush_file_is_tarball Check whether a file is a supported tarball.
drush_format_size
drush_generate_password Generate a random alphanumeric password. Copied from user.module.
drush_get_engines Return a structured array of engines of a specific type.
drush_get_engine_types_info Obtain all engine types info and normalize with defaults.
drush_get_error Return the current error handling status
drush_get_error_log Return the current list of errors that have occurred.
drush_get_global_options Get the available global options. Used by help command. Command files may modify this list using hook_drush_help_alter().
drush_get_log Retrieve the log messages from the log history
drush_include Include a file, selecting a version specific file if available.
drush_include_engine Include the engine code for a specific named engine of a certain type.
drush_lib_fetch Download and extract a tarball to the lib directory.
drush_log Add a log message to the log history.
drush_map_assoc Form an associative array from a linear array.
drush_memory_limit Get the PHP memory_limit value in bytes.
drush_mime_content_type Determines the MIME content type of the specified file.
drush_op Calls a given function, passing through all arguments unchanged.
drush_pipe_output Display the pipe output for the current request.
drush_preflight_command_dispatch Handle any command preprocessing that may need to be done, including potentially redispatching the command immediately (e.g. for remote commands).
drush_print_timers
drush_prompt Prompt the user for input
drush_set_error Set an error code for the error handling system.
drush_tarball_extract Extract a tarball.
drush_unset_recursive Unset the named key anywhere in the provided data structure.
drush_user_abort Exit due to user declining a confirmation prompt.
drush_version_control_reserved_files Return a list of VCSs reserved files and directories.
_convert_csv_to_array Convert a csv string, or an array of items which may contain csv strings, into an array of items.
_drush_download_file Download a file using wget, curl or file_get_contents. Does not use download cache.
_drush_is_drush_shebang_line
_drush_is_drush_shebang_script
_drush_log_drupal_messages Turn drupal_set_message errors into drush_log errors
_drush_print_log Display the log message
_drush_should_remove_command_arg Determine whether or not an argument should be removed from the DRUSH_COMMAND_ARGS context. This method is used when a Drush command has set the 'strict-option-handling' flag indicating that it will pass through all commandline arguments…

Constants

Namesort descending Description
DRUSH_APPLICATION_ERROR The command that was executed resulted in an application error, The most commom causes for this is invalid PHP or a broken SSH pipe when using drush_backend_invoke in a distributed manner.
DRUSH_CACHE_LIFETIME_DEFAULT Default amount of time, in seconds, to cache downloads via drush_download_file(). One day is 86400 seconds.
DRUSH_DRUPAL_KILOBYTE The number of bytes in a kilobyte. Copied from Drupal.
DRUSH_FRAMEWORK_ERROR The command could not be completed because the framework has specified errors that have occured.
DRUSH_SUCCESS The command completed successfully.

File

includes/drush.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * The drush API implementation and helpers.
  5. */
  6. /**
  7. * @name Error status definitions
  8. * @{
  9. * Error code definitions for interpreting the current error status.
  10. * @see drush_set_error(), drush_get_error(), drush_get_error_log(), drush_cmp_error()
  11. */
  12. /** The command completed successfully. */
  13. define('DRUSH_SUCCESS', 0);
  14. /** The command could not be completed because the framework has specified errors that have occured. */
  15. define('DRUSH_FRAMEWORK_ERROR', 1);
  16. /** The command that was executed resulted in an application error,
  17. The most commom causes for this is invalid PHP or a broken SSH
  18. pipe when using drush_backend_invoke in a distributed manner. */
  19. define('DRUSH_APPLICATION_ERROR', 255);
  20. /**
  21. * @} End of "name Error status defintions".
  22. */
  23. /**
  24. * The number of bytes in a kilobyte. Copied from Drupal.
  25. */
  26. define('DRUSH_DRUPAL_KILOBYTE', 1024);
  27. /**
  28. * Default amount of time, in seconds, to cache downloads via
  29. * drush_download_file(). One day is 86400 seconds.
  30. */
  31. define('DRUSH_CACHE_LIFETIME_DEFAULT', 86400);
  32. /**
  33. * Include a file, selecting a version specific file if available.
  34. *
  35. * For example, if you pass the path "/var/drush" and the name
  36. * "update" when bootstrapped on a Drupal 6 site it will first check for
  37. * the presence of "/var/drush/update_6.inc" in include it if exists. If this
  38. * file does NOT exist it will proceed and check for "/var/drush/update.inc".
  39. * If neither file exists, it will return FALSE.
  40. *
  41. * @param $path
  42. * The path you want to search.
  43. * @param $name
  44. * The file base name you want to include (not including a version suffix
  45. * or extension).
  46. * @param $version
  47. * The version suffix you want to include (could be specific to the software
  48. * or platform your are connecting to) - defaults to the current Drupal core
  49. * major version.
  50. * @param $extension
  51. * The extension - defaults to ".inc".
  52. *
  53. * @return
  54. * TRUE if the file was found and included.
  55. */
  56. function drush_include($path, $name, $version = NULL, $extension = 'inc') {
  57. $version = ($version) ? $version : drush_drupal_major_version();
  58. $file = sprintf("%s/%s_%s.%s", $path, $name, $version, $extension);
  59. if (file_exists($file)) {
  60. //drush_log(dt('Including version specific file : @file', array('@file' => $file)));
  61. include_once($file);
  62. return TRUE;
  63. }
  64. $file = sprintf("%s/%s.%s", $path, $name, $extension);
  65. if (file_exists($file)) {
  66. //drush_log(dt('Including non-version specific file : @file', array('@file' => $file)));
  67. include_once($file);
  68. return TRUE;
  69. }
  70. }
  71. /**
  72. * Obtain all engine types info and normalize with defaults.
  73. *
  74. * @see hook_drush_engine_type_info().
  75. */
  76. function drush_get_engine_types_info() {
  77. $info = drush_command_invoke_all('drush_engine_type_info');
  78. foreach ($info as $type => $data) {
  79. $info[$type] += array(
  80. 'description' => '',
  81. 'option' => FALSE,
  82. 'default' => NULL,
  83. 'options' => array(),
  84. 'add-options-to-command' => FALSE,
  85. );
  86. }
  87. return $info;
  88. }
  89. /**
  90. * Return a structured array of engines of a specific type.
  91. *
  92. * Engines are pluggable subsystems. Each engine of a specific type will
  93. * implement the same set of API functions and perform the same high-level
  94. * task using a different backend or approach.
  95. *
  96. * This function/hook is useful when you have a selection of several mutually
  97. * exclusive options to present to a user to select from.
  98. *
  99. * Other commands are able to extend this list and provide their own engines.
  100. * The hook can return useful information to help users decide which engine
  101. * they need, such as description or list of available engine options.
  102. *
  103. * The engine path element will automatically default to a subdirectory (within
  104. * the directory of the commandfile that implemented the hook) with the name of
  105. * the type of engine - e.g. an engine "wget" of type "handler" provided by
  106. * the "pm" commandfile would automatically be found if the file
  107. * "pm/handler/wget.inc" exists and a specific path is not provided.
  108. *
  109. * @param $engine_type
  110. * The type of engine.
  111. *
  112. * @return
  113. * A structured array of engines.
  114. */
  115. function drush_get_engines($engine_type) {
  116. $info = drush_get_engine_types_info();
  117. if (!isset($info[$engine_type])) {
  118. return drush_set_error('DRUSH_UNKNOWN_ENGINE_TYPE', dt('Unknown engine type !engine_type', array('!engine_type' => $engine_type)));
  119. }
  120. $engines = array(
  121. 'info' => $info[$engine_type],
  122. 'engines' => array(),
  123. );
  124. $list = drush_commandfile_list();
  125. $hook = 'drush_engine_' . $engine_type;
  126. foreach ($list as $commandfile => $path) {
  127. if (drush_command_hook($commandfile, $hook)) {
  128. $function = $commandfile . '_' . $hook;
  129. $result = $function();
  130. foreach ($result as $key => $engine) {
  131. // Add some defaults
  132. $engine += array(
  133. 'commandfile' => $commandfile,
  134. // Engines by default live in a subdirectory of the commandfile that
  135. // declared them, named as per the type of engine they are.
  136. 'path' => sprintf("%s/%s", dirname($path), $engine_type),
  137. );
  138. $engines['engines'][$key] = $engine;
  139. }
  140. }
  141. }
  142. return $engines;
  143. }
  144. /**
  145. * Include the engine code for a specific named engine of a certain type.
  146. *
  147. * If the engine type has implemented hook_drush_engine_$type the path to the
  148. * engine specified in the array will be used.
  149. *
  150. * If a class named in the form drush_$type_$engine exists, it will be an
  151. * object of that class will be created and returned.
  152. *
  153. * If you don't need to present any user options for selecting the engine
  154. * (which is common if the selection is implied by the running environment)
  155. * and you don't need to allow other modules to define their own engines you can
  156. * simply pass the $path to the directory where the engines are, and the
  157. * appropriate one will be included.
  158. *
  159. * Unlike drush_include this function will set errors if the requested engine
  160. * cannot be found.
  161. *
  162. * @param $type
  163. * The type of engine.
  164. * @param $engine
  165. * The key for the engine to be included.
  166. * @param $version
  167. * The version of the engine to be included - defaults to the current Drupal core
  168. * major version.
  169. * @param $path
  170. * A path to include from, if the engine has no corresponding
  171. * hook_drush_engine_$type item path.
  172. * @return TRUE or instanced object of available class on success. FALSE on fail.
  173. */
  174. function drush_include_engine($type, $engine, $version = NULL, $path = NULL) {
  175. $engine_info = drush_get_engines($type);
  176. if (!$path && isset($engine_info['engines'][$engine])) {
  177. $path = $engine_info['engines'][$engine]['path'];
  178. }
  179. if (!$path) {
  180. return drush_set_error('DRUSH_ENGINE INCLUDE_NO_PATH', dt('No path was set for including the !type engine !engine.', array('!type' => $type, '!engine' => $engine)));
  181. }
  182. if (drush_include($path, $engine, $version)) {
  183. $class = 'drush_' . $type . '_' . $engine;
  184. if (class_exists($class)) {
  185. return new $class();
  186. }
  187. return TRUE;
  188. }
  189. return drush_set_error('DRUSH_ENGINE INCLUDE_FAILED', dt('Unable to include the !type engine !engine from !path.' , array('!path' => $path, '!type' => $type, '!engine' => $engine)));
  190. }
  191. /**
  192. * Check to see if a newer version of drush is available
  193. *
  194. * @return
  195. * TRUE - A new version is available.
  196. * FALSE - Error.
  197. * NULL - No release available.
  198. */
  199. function drush_check_self_update() {
  200. $explicit = FALSE;
  201. $update = FALSE;
  202. $error = "";
  203. // Don't check unless we have a datestamp in drush.info
  204. $drush_info = drush_read_drush_info();
  205. if (($drush_info === FALSE) || (!array_key_exists('datestamp', $drush_info))) {
  206. drush_log(dt('Cannot determine release date for drush'), 'notice');
  207. return FALSE;
  208. }
  209. $is_dev = FALSE;
  210. // Get release info for drush.
  211. drush_include_engine('release_info', 'updatexml');
  212. $request = pm_parse_project_version(array('drush'));
  213. $info = release_info_get_releases($request);
  214. // Check for newer releases based on the datestamp.
  215. // We add 60 seconds to the drush.info date because of a drupal.org WTF. See http://drupal.org/node/1019356.
  216. $version_date = $drush_info['datestamp'] + 60;
  217. $newer_version = FALSE;
  218. foreach ($info['drush']['releases'] as $version => $release_info) {
  219. // We deliberately skip any dev releases unless the current release is a dev release.
  220. if ((!array_key_exists('version_extra', $release_info) || ($release_info['version_extra'] != 'dev'))) {
  221. if ($release_info['date'] > $version_date) {
  222. $newer_version = $release_info['version'];
  223. $version_date = $release_info['date'];
  224. $is_dev = isset($release_info['version_extra']) && $release_info['version_extra'] == 'dev';
  225. if ($is_dev) {
  226. $newer_version .= " (" . date('Y-M-d', $version_date) . ")";
  227. }
  228. }
  229. }
  230. }
  231. if ($newer_version) {
  232. drush_print(dt('A newer version of drush, !version, is available. You are currently running drush version !currentversion. The update process depends on how you installed drush. Some common update commands are: `pear upgrade drush/drush`, `git pull`, `drush dl drush --destination=[/path/to/drush]`.' . "\n", array('!version' => $newer_version, '!currentversion' => DRUSH_VERSION)));
  233. return TRUE;
  234. }
  235. else {
  236. drush_log(dt("drush self-update check: drush !version is up-to-date.", array('!version' => DRUSH_VERSION)), 'notice');
  237. }
  238. return NULL;
  239. }
  240. /**
  241. * Generate an .ini file. used by archive-dump."
  242. *
  243. * @param array $ini
  244. * A two dimensional associative array where top level are sections and
  245. * second level are key => value pairs.
  246. *
  247. * @return string
  248. * .ini formatted text.
  249. */
  250. function drush_export_ini($ini) {
  251. $output = '';
  252. foreach ($ini as $section => $pairs) {
  253. if ($section) {
  254. $output .= "[$section]\n";
  255. }
  256. foreach ($pairs as $k => $v) {
  257. if ($v) {
  258. $output .= "$k = \"$v\"\n";
  259. }
  260. }
  261. }
  262. return $output;
  263. }
  264. /**
  265. * Generate code friendly to the Drupal .info format from a structured array.
  266. * Mostly copied from http://drupalcode.org/viewvc/drupal/contributions/modules/features/features.export.inc.
  267. *
  268. * @param $info
  269. * An array or single value to put in a module's .info file.
  270. *
  271. * @param boolean $integer_keys
  272. * Use integer in keys.
  273. *
  274. * @param $parents
  275. * Array of parent keys (internal use only).
  276. *
  277. * @return
  278. * A code string ready to be written to a module's .info file.
  279. */
  280. function drush_export_info($info, $integer_keys = FALSE, $parents = array()) {
  281. $output = '';
  282. if (is_array($info)) {
  283. foreach ($info as $k => $v) {
  284. $child = $parents;
  285. $child[] = $k;
  286. $output .= drush_export_info($v, $integer_keys, $child);
  287. }
  288. }
  289. else if (!empty($info) && count($parents)) {
  290. $line = array_shift($parents);
  291. foreach ($parents as $key) {
  292. $line .= (!$integer_keys && is_numeric($key)) ? "[]" : "[{$key}]";
  293. }
  294. $line .= " = \"{$info}\"\n";
  295. return $line;
  296. }
  297. return $output;
  298. }
  299. /**
  300. * Convert a csv string, or an array of items which
  301. * may contain csv strings, into an array of items.
  302. *
  303. * @param $args
  304. * A simple csv string; e.g. 'a,b,c'
  305. * or a simple list of items; e.g. array('a','b','c')
  306. * or some combination; e.g. array('a,b','c') or array('a,','b,','c,')
  307. *
  308. * @returns array
  309. * A simple list of items (e.g. array('a','b','c')
  310. */
  311. function _convert_csv_to_array($args) {
  312. //
  313. // Step 1: implode(',',$args) converts from, say, array('a,','b,','c,') to 'a,,b,,c,'
  314. // Step 2: explode(',', ...) converts to array('a','','b','','c','')
  315. // Step 3: array_filter(...) removes the empty items
  316. //
  317. return array_filter(explode(',', is_array($args) ? implode(',',$args) : $args));
  318. }
  319. /**
  320. * Get the available global options. Used by help command. Command files may
  321. * modify this list using hook_drush_help_alter().
  322. *
  323. * @param boolean $brief
  324. * Return a reduced set of important options. Used by help command.
  325. *
  326. * @return
  327. * An associative array containing the option definition as the key,
  328. * and a descriptive array for each of the available options. The array
  329. * elements for each item are:
  330. *
  331. * - short-form: The shortcut form for specifying the key on the commandline.
  332. * - context: The drush context where the value of this item is cached. Used
  333. * by backend invoke to propagate values set in code.
  334. * - never-post: If TRUE, backend invoke will never POST this item's value
  335. * on STDIN; it will always be sent as a commandline option.
  336. * - never-propagate: If TRUE, backend invoke will never pass this item on
  337. * to the subcommand being executed.
  338. * - local-context-only: Backend invoke will only pass this value on for local calls.
  339. * - merge: For options such as $options['shell-aliases'] that consist of an array
  340. * of items, make a merged array that contains all of the values specified for
  341. * all of the contexts (config files) where the option is defined. The value is stored in
  342. * the specified 'context', or in a context named after the option itself if the
  343. * context flag is not specified.
  344. * IMPORTANT: When the merge flag is used, the option value must be obtained via
  345. * drush_get_context('option') rather than drush_get_option('option').
  346. * - merge-pathlist: For options such as --include and --config, make a merged list
  347. * of options from all contexts; works like the 'merge' flag, but also handles string
  348. * values separated by the PATH_SEPARATOR.
  349. * - merge-associative: Like 'merge-pathlist', but key values are preserved.
  350. * - propagate-cli-value: Used to tell backend invoke to include the value for
  351. * this item as specified on the cli. This can either override 'context'
  352. * (e.g., propagate --include from cli value instead of DRUSH_INCLUDE context),
  353. * or for an independent global setting (e.g. --user)
  354. * - description: The help text for this item. displayed by `drush help`.
  355. */
  356. function drush_get_global_options($brief = FALSE) {
  357. $options['root'] = array('short-form' => 'r', 'short-has-arg' => TRUE, 'never-post' => TRUE, 'description' => "Drupal root directory to use (default: current directory).", 'example-value' => 'path');
  358. $options['uri'] = array('short-form' => 'l', 'short-has-arg' => TRUE, 'never-post' => TRUE, 'description' => 'URI of the drupal site to use (only needed in multisite environments or when running on an alternate port).', 'example-value' => 'http://example.com:8888');
  359. $options['verbose'] = array('short-form' => 'v', 'context' => 'DRUSH_VERBOSE', 'description' => 'Display extra information about the command.');
  360. $options['debug'] = array('short-form' => 'd', 'context' => 'DRUSH_DEBUG', 'description' => 'Display even more information, including internal messages.');
  361. $options['yes'] = array('short-form' => 'y', 'context' => 'DRUSH_AFFIRMATIVE', 'description' => "Assume 'yes' as answer to all prompts.");
  362. $options['no'] = array('short-form' => 'n', 'context' => 'DRUSH_NEGATIVE', 'description' => "Assume 'no' as answer to all prompts.");
  363. $options['simulate'] = array('short-form' => 's', 'context' => 'DRUSH_SIMULATE', 'description' => "Simulate all relevant actions (don't actually change the system).");
  364. $options['pipe'] = array('short-form' => 'p', 'description' => "Emit a compact representation of the command for scripting.");
  365. $options['help'] = array('short-form' => 'h', 'description' => "This help system.");
  366. $options['version'] = array('description' => "Show drush version.");
  367. $options['php'] = array('description' => "The absolute path to your PHP intepreter, if not 'php' in the path.", 'example-value' => '/path/to/file', 'never-propagate' => TRUE);
  368. $options['interactive'] = array('short-form' => 'ia', 'description' => "Force interactive mode for commands run on multiple targets (e.g. `drush @site1,@site2 cc --ia`).");
  369. if (!$brief) {
  370. $options['quiet'] = array('short-form' => 'q', 'description' => 'Suppress non-error messages.');
  371. $options['include'] = array('short-form' => 'i', 'short-has-arg' => TRUE, 'context' => 'DRUSH_INCLUDE', 'local-context-only' => TRUE, 'never-post' => TRUE, 'propagate-cli-value' => TRUE, 'merge-pathlist' => TRUE, 'description' => "A list of additional directory paths to search for drush commands.", 'example-value' => '/path/to/directory');
  372. $options['config'] = array('short-form' => 'c', 'short-has-arg' => TRUE, 'context' => 'DRUSH_CONFIG', 'local-context-only' => TRUE, 'merge-pathlist' => TRUE, 'description' => "Specify an additional config file to load. See example.drushrc.php.");
  373. $options['user'] = array('short-form' => 'u', 'short-has-arg' => TRUE, 'propagate-cli-value' => TRUE, 'description' => "Specify a Drupal user to login with. May be a name or a number.", 'example-value' => 'name_or_number');
  374. $options['backend'] = array('short-form' => 'b', 'never-propagate' => TRUE, 'description' => "Hide all output and return structured data (internal use only).");
  375. $options['invoke'] = array('hidden' => TRUE, 'description' => 'Invoked from a script; skip option verification.');
  376. $options['choice'] = array('description' => "Provide an answer to a multiple-choice prompt.", 'example-value' => 'number');
  377. $options['variables'] = array('description' => "Comma delimited list of name=value pairs. These values take precedence even over settings.php variable overrides.", 'example-value' => 'foo=bar,baz=yaz');
  378. $options['search-depth'] = array('description' => "Control the depth that drush will search for alias files.", 'example-value' => 'number');
  379. $options['ignored-modules'] = array('description' => "Exclude some modules from consideration when searching for drush command files.");
  380. $options['no-label'] = array('description' => "Remove the site label that drush includes in multi-site command output(e.g. `drush @site1,@site2 status`).");
  381. $options['nocolor'] = array('context' => 'DRUSH_NOCOLOR', 'propagate-cli-value' => TRUE, 'description' => "Suppress color highlighting on log messages.");
  382. $options['show-passwords'] = array('description' => "Show database passwords in commands that display connection information.");
  383. $options['show-invoke'] = array('description' => "Show all function names which could have been called for the current command. See drush_invoke().");
  384. $options['watchdog'] = array('description' => "Control logging of Drupal's watchdog() to drush log. Recognized values are 'log', 'print', 'disabled'. Defaults to log. 'print' shows calls to admin but does not add them to the log.");
  385. $options['cache-default-class'] = array('description' => "A cache backend class that implements DrushCacheInterface.");
  386. $options['cache-class-<bin>'] = array('description' => "A cache backend class that implements DrushCacheInterface to use for a specific cache bin.");
  387. $options['early'] = array('description' => "Include a file (with relative or full path) and call the drush_early_hook() function (where 'hook' is the filename). The function is called pre-bootstrap and offers an opportunity to alter the drush bootstrap environment or process (returning FALSE from the function will continue the bootstrap), or return output very rapidly (e.g. from caches). See includes/complete.inc for an example.");
  388. $options['alias-path'] = array('context' => 'ALIAS_PATH', 'local-context-only' => TRUE, 'merge-pathlist' => TRUE, 'propagate-cli-value' => TRUE, 'description' => "Specifies the list of paths where drush will search for alias files. Separate paths with ':'.");
  389. $options['backup-location'] = array('description' => "Specifies the directory where drush will store backups.");
  390. $options['confirm-rollback'] = array('description' => 'Wait for confirmation before doing a rollback when something goes wrong.');
  391. $options['complete-debug'] = array('hidden' => TRUE, 'description' => "Turn on debug mode forf completion code");
  392. $options['php-options'] = array('description' => "Options to pass to php when running drush. Only effective when using the `drush` script.", 'never-propagate' => TRUE);
  393. $options['deferred-sanitization'] = array('hidden' => TRUE, 'description' => "Defer calculating the sanitization operations until after the database has been copied. This is done automatically if the source database is remote.");
  394. $options['remote-host'] = array('hidden' => TRUE, 'description' => 'Remote site to execute drush command on. Managed by site alias.');
  395. $options['remote-user'] = array('hidden' => TRUE, 'description' => 'User account to use with a remote drush command. Managed by site alias.');
  396. $options['remote-os'] = array('hidden' => TRUE, 'description' => 'The operating system used on the remote host. Managed by site alias.');
  397. $options['site-list'] = array('hidden' => TRUE, 'description' => 'List of sites to run commands on. Managed by site alias.');
  398. $options['reserve-margin'] = array('hidden' => TRUE, 'description' => 'Remove columns from formatted opions. Managed by multi-site command handling.');
  399. $options['strict'] = array('hidden' => TRUE, 'description' => 'Check requirements more strictly / remove backwards-compatibility features for unit tests.');
  400. $options['command-specific'] = array('hidden' => TRUE, 'merge-associative' => TRUE, 'description' => 'Command-specific options.');
  401. $options['site-aliases'] = array('hidden' => TRUE, 'merge-associative' => TRUE, 'description' => 'List of site aliases.');
  402. $options['shell-aliases'] = array('hidden' => TRUE, 'merge' => TRUE, 'never-propagate' => TRUE, 'description' => 'List of shell aliases.');
  403. $options['path-aliases'] = array('hidden' => TRUE, 'never-propagate' => TRUE, 'description' => 'Path aliases from site alias.');
  404. $options['ssh-options'] = array('never-propagate' => TRUE, 'description' => 'A string of extra options that will be passed to the ssh command (e.g. "-p 100")');
  405. $options['db-url'] = array('hidden' => TRUE, 'description' => 'A Drupal 6 style database URL. Used by various commands.', 'example-value' => 'mysql://root:pass@127.0.0.1/db');
  406. $command = array(
  407. 'options' => $options,
  408. '#brief' => FALSE,
  409. ) + drush_command_defaults('global-options', 'global_options', __FILE__);
  410. drush_command_invoke_all_ref('drush_help_alter', $command);
  411. $options = $command['options'];
  412. }
  413. return $options;
  414. }
  415. /**
  416. * Exits with a message. In general, you should use drush_set_error() instead of
  417. * this function. That lets drush proceed with other tasks.
  418. * TODO: Exit with a correct status code.
  419. */
  420. function drush_die($msg = NULL, $status = NULL) {
  421. die($msg ? "drush: $msg\n" : '');
  422. }
  423. /*
  424. * Check to see if the provided line is a "#!/usr/bin/env drush"
  425. * "shebang" script line.
  426. */
  427. function _drush_is_drush_shebang_line($line) {
  428. return ((substr($line,0,2) == '#!') && (strstr($line, 'drush') !== FALSE));
  429. }
  430. /*
  431. * Check to see if the provided script file is a "#!/usr/bin/env drush"
  432. * "shebang" script line.
  433. */
  434. function _drush_is_drush_shebang_script($script_filename) {
  435. $result = FALSE;
  436. if (file_exists($script_filename)) {
  437. $fp = fopen($script_filename, "r");
  438. if ($fp !== FALSE) {
  439. $line = fgets($fp);
  440. $result = _drush_is_drush_shebang_line($line);
  441. fclose($fp);
  442. }
  443. }
  444. return $result;
  445. }
  446. /**
  447. * @defgroup userinput Get input from the user.
  448. * @{
  449. */
  450. /**
  451. * Asks the user a basic yes/no question.
  452. *
  453. * @param string $msg
  454. * The question to ask.
  455. * @param int $indent
  456. * The number of spaces to indent the message.
  457. *
  458. * @return bool
  459. * TRUE if the user enters "y" or FALSE if "n".
  460. */
  461. function drush_confirm($msg, $indent = 0) {
  462. drush_print_prompt((string)$msg . " (y/n): ", $indent);
  463. // Automatically accept confirmations if the --yes argument was supplied.
  464. if (drush_get_context('DRUSH_AFFIRMATIVE')) {
  465. drush_print("y");
  466. return TRUE;
  467. }
  468. // Automatically cancel confirmations if the --no argument was supplied.
  469. elseif (drush_get_context('DRUSH_NEGATIVE')) {
  470. drush_print("n");
  471. return FALSE;
  472. }
  473. // See http://drupal.org/node/499758 before changing this.
  474. $stdin = fopen("php://stdin","r");
  475. while ($line = fgets($stdin)) {
  476. $line = trim($line);
  477. if ($line == 'y') {
  478. return TRUE;
  479. }
  480. if ($line == 'n') {
  481. return FALSE;
  482. }
  483. drush_print_prompt((string)$msg . " (y/n): ", $indent);
  484. }
  485. }
  486. /**
  487. * Ask the user to select an item from a list.
  488. * From a provided associative array, drush_choice will
  489. * display all of the questions, numbered from 1 to N,
  490. * and return the item the user selected. "0" is always
  491. * cancel; entering a blank line is also interpreted
  492. * as cancelling.
  493. *
  494. * @param $options
  495. * A list of questions to display to the user. The
  496. * KEYS of the array are the result codes to return to the
  497. * caller; the VALUES are the messages to display on
  498. * each line. Special keys of the form '-- something --' can be
  499. * provided as separator between choices groups. Separator keys
  500. * don't alter the numbering.
  501. * @param $prompt
  502. * The message to display to the user prompting for input.
  503. * @param $label
  504. * Controls the display of each line. Defaults to
  505. * '!value', which displays the value of each item
  506. * in the $options array to the user. Use '!key' to
  507. * display the key instead. In some instances, it may
  508. * be useful to display both the key and the value; for
  509. * example, if the key is a user id and the value is the
  510. * user name, use '!value (uid=!key)'.
  511. */
  512. function drush_choice($options, $prompt = 'Enter a number.', $label = '!value', $widths = array()) {
  513. drush_print(dt($prompt));
  514. // Preflight so that all rows will be padded out to the same number of columns
  515. $array_pad = 0;
  516. foreach ($options as $key => $option) {
  517. if (is_array($option) && (count($option) > $array_pad)) {
  518. $array_pad = count($option);
  519. }
  520. }
  521. $rows[] = array_pad(array('[0]', ':', 'Cancel'), $array_pad + 2, '');
  522. $selection_number = 0;
  523. foreach ($options as $key => $option) {
  524. if ((substr($key, 0, 3) == '-- ') && (substr($key, -3) == ' --')) {
  525. $rows[] = array_pad(array('', '', $option), $array_pad + 2, '');
  526. }
  527. else {
  528. $selection_number++;
  529. $row = array("[$selection_number]", ':');
  530. if (is_array($option)) {
  531. $row = array_merge($row, $option);
  532. }
  533. else {
  534. $row[] = dt($label, array('!number' => $selection_number, '!key' => $key, '!value' => $option));
  535. }
  536. $rows[] = $row;
  537. $selection_list[$selection_number] = $key;
  538. }
  539. }
  540. drush_print_table($rows, FALSE, $widths);
  541. drush_print_pipe(array_keys($options));
  542. // If the user specified --choice, then make an
  543. // automatic selection. Cancel if the choice is
  544. // not an available option.
  545. if (($choice = drush_get_option('choice', FALSE)) !== FALSE) {
  546. // First check to see if $choice is one of the symbolic options
  547. if (array_key_exists($choice, $options)) {
  548. return $choice;
  549. }
  550. // Next handle numeric selections
  551. elseif (array_key_exists($choice, $selection_list)) {
  552. return $selection_list[$choice];
  553. }
  554. return FALSE;
  555. }
  556. // If the user specified --no, then cancel; also avoid
  557. // getting hung up waiting for user input in --pipe and
  558. // backend modes. If none of these apply, then wait,
  559. // for user input and return the selected result.
  560. if (!drush_get_context('DRUSH_NEGATIVE') && !drush_get_context('DRUSH_AFFIRMATIVE') && !drush_get_context('DRUSH_PIPE')) {
  561. while ($line = trim(fgets(STDIN))) {
  562. if (array_key_exists($line, $selection_list)) {
  563. return $selection_list[$line];
  564. }
  565. }
  566. }
  567. // We will allow --yes to confirm input if there is only
  568. // one choice; otherwise, --yes will cancel to avoid ambiguity
  569. if (drush_get_context('DRUSH_AFFIRMATIVE') && (count($options) == 1)) {
  570. return $selection_list[1];
  571. }
  572. drush_print(dt('Cancelled'));
  573. return FALSE;
  574. }
  575. /**
  576. * Ask the user to select multiple items from a list.
  577. * This is a wrapper around drush_choice, that repeats the selection process,
  578. * allowing users to toggle a number of items in a list. The number of values
  579. * that can be constrained by both min and max: the user will only be allowed
  580. * finalize selection once the minimum number has been selected, and the oldest
  581. * selected value will "drop off" the list, if they exceed the maximum number.
  582. *
  583. * @param $options
  584. * Same as drush_choice() (see above).
  585. * @param $defaults
  586. * This can take 3 forms:
  587. * - FALSE: (Default) All options are unselected by default.
  588. * - TRUE: All options are selected by default.
  589. * - Array of $options keys to be selected by default.
  590. * @param $prompt
  591. * Same as drush_choice() (see above).
  592. * @param $label
  593. * Same as drush_choice() (see above).
  594. * @param $mark
  595. * Controls how selected values are marked. Defaults to '!value (selected)'.
  596. * @param $min
  597. * Constraint on minimum number of selections. Defaults to zero. When fewer
  598. * options than this are selected, no final options will be available.
  599. * @param $max
  600. * Constraint on minimum number of selections. Defaults to NULL (unlimited).
  601. * If the a new selection causes this value to be exceeded, the oldest
  602. * previously selected value is automatically unselected.
  603. * @param $final_options
  604. * An array of additional options in the same format as $options.
  605. * When the minimum number of selections is met, this array is merged into the
  606. * array of options. If the user selects one of these values and the
  607. * selection process will complete (the key for the final option is included
  608. * in the return value). If this is an empty array (default), then a built in
  609. * final option of "Done" will be added to the available options (in this case
  610. * no additional keys are added to the return value).
  611. */
  612. function drush_choice_multiple($options, $defaults = FALSE, $prompt = 'Select some numbers.', $label = '!value', $mark = '!value (selected)', $min = 0, $max = NULL, $final_options = array()) {
  613. $selections = array();
  614. // Load default selections.
  615. if (is_array($defaults)) {
  616. $selections = $defaults;
  617. }
  618. elseif ($defaults === TRUE) {
  619. $selections = array_keys($options);
  620. }
  621. $complete = FALSE;
  622. $final_builtin = array();
  623. if (empty($final_options)) {
  624. $final_builtin['done'] = dt('Done');
  625. }
  626. $final_options_keys = array_keys($final_options);
  627. while (TRUE) {
  628. $current_options = $options;
  629. // Mark selections.
  630. foreach ($selections as $selection) {
  631. $current_options[$selection] = dt($mark, array('!key' => $selection, '!value' => $options[$selection]));
  632. }
  633. // Add final options, if the minimum number of selections has been reached.
  634. if (count($selections) >= $min) {
  635. $current_options = array_merge($current_options, $final_options, $final_builtin);
  636. }
  637. $toggle = drush_choice($current_options, $prompt, $label);
  638. if ($toggle === FALSE) {
  639. return FALSE;
  640. }
  641. // Don't include the built in final option in the return value.
  642. if (count($selections) >= $min && empty($final_options) && $toggle == 'done') {
  643. return $selections;
  644. }
  645. // Toggle the selected value.
  646. $item = array_search($toggle, $selections);
  647. if ($item === FALSE) {
  648. array_unshift($selections, $toggle);
  649. }
  650. else {
  651. unset($selections[$item]);
  652. }
  653. // If the user selected one of the final options, return.
  654. if (count($selections) >= $min && in_array($toggle, $final_options_keys)) {
  655. return $selections;
  656. }
  657. // If the user selected too many options, drop the oldest selection.
  658. if (isset($max) && count($selections) > $max) {
  659. array_pop($selections);
  660. }
  661. }
  662. }
  663. /**
  664. * Prompt the user for input
  665. *
  666. * The input can be anything that fits on a single line (not only y/n),
  667. * so we can't use drush_confirm()
  668. *
  669. * @param $prompt
  670. * The text which is displayed to the user.
  671. * @param $default
  672. * The default value of the input.
  673. * @param $required
  674. * If TRUE, user may continue even when no value is in the input.
  675. * @param $password
  676. * If TRUE, surpress printing of the input.
  677. *
  678. * @see drush_confirm()
  679. */
  680. function drush_prompt($prompt, $default = NULL, $required = TRUE, $password = FALSE) {
  681. if (!is_null($default)) {
  682. $prompt .= " [" . $default . "]";
  683. }
  684. $prompt .= ": ";
  685. drush_print_prompt($prompt);
  686. if (drush_get_context('DRUSH_AFFIRMATIVE')) {
  687. return $default;
  688. }
  689. $stdin = fopen('php://stdin', 'r');
  690. if ($password) drush_shell_exec("stty -echo");
  691. stream_set_blocking($stdin, TRUE);
  692. while (($line = fgets($stdin)) !== FALSE) {
  693. $line = trim($line);
  694. if ($line === "") {
  695. $line = $default;
  696. }
  697. if ($line || !$required) {
  698. break;
  699. }
  700. drush_print_prompt($prompt);
  701. }
  702. fclose($stdin);
  703. if ($password) {
  704. drush_shell_exec("stty echo");
  705. print "\n";
  706. }
  707. return $line;
  708. }
  709. /**
  710. * @} End of "defgroup userinput".
  711. */
  712. /**
  713. * Calls a given function, passing through all arguments unchanged.
  714. *
  715. * This should be used when calling possibly mutative or destructive functions
  716. * (e.g. unlink() and other file system functions) so that can be suppressed
  717. * if the simulation mode is enabled.
  718. *
  719. * Important: Call @see drush_op_system() to execute a shell command,
  720. * or @see drush_shell_exec() to execute a shell command and capture the
  721. * shell output.
  722. *
  723. * @param $function
  724. * The name of the function. Any additional arguments are passed along.
  725. * @return
  726. * The return value of the function, or TRUE if simulation mode is enabled.
  727. *
  728. */
  729. function drush_op($function) {
  730. $args_printed = array();
  731. $args = func_get_args();
  732. array_shift($args); // Skip function name
  733. foreach ($args as $arg) {
  734. $args_printed[] = is_scalar($arg) ? $arg : (is_array($arg) ? 'Array' : 'Object');
  735. }
  736. // Special checking for drush_op('system')
  737. if ($function == 'system') {
  738. drush_log(dt("Do not call drush_op('system'); use drush_op_system instead"), 'debug');
  739. }
  740. if (drush_get_context('DRUSH_VERBOSE') || drush_get_context('DRUSH_SIMULATE')) {
  741. drush_log(sprintf("Calling %s(%s)", $function, implode(", ", $args_printed)), 'debug');
  742. }
  743. if (drush_get_context('DRUSH_SIMULATE')) {
  744. return TRUE;
  745. }
  746. return call_user_func_array($function, $args);
  747. }
  748. /**
  749. * Download a file using wget, curl or file_get_contents, or via download cache.
  750. *
  751. * @param string $url
  752. * The url of the file to download.
  753. * @param string $destination
  754. * The name of the file to be saved, which may include the full path.
  755. * Optional, if omitted the filename will be extracted from the url and the
  756. * file downloaded to the current working directory (Drupal root if
  757. * bootstrapped).
  758. * @param integer $cache_duration
  759. * The acceptable age of a cached file. If cached file is too old, a fetch
  760. * will occur and cache will be updated. Optional, if ommitted the file will
  761. * be fetched directly.
  762. *
  763. * @return string
  764. * The path to the downloaded file, or FALSE if the file could not be
  765. * downloaded.
  766. */
  767. function drush_download_file($url, $destination = FALSE, $cache_duration = 0) {
  768. // Generate destination of omitted.
  769. if (!$destination) {
  770. $file = basename(current(explode('?', $url, 2)));
  771. $destination = getcwd() . '/' . basename($file);
  772. }
  773. if (drush_get_option('cache') && $cache_duration !== 0 && $cache_dir = drush_directory_cache('download')) {
  774. $cache_name = str_replace(array(':', '/', '?', '='), '-', $url);
  775. $cache_file = $cache_dir . "/" . $cache_name;
  776. // Check for cached, unexpired file.
  777. if (file_exists($cache_file) && filectime($cache_file) > ($_SERVER['REQUEST_TIME']-$cache_duration)) {
  778. drush_log(dt('!name retrieved from cache.', array('!name' => $cache_name)));
  779. }
  780. else {
  781. if (_drush_download_file($url, $cache_file, TRUE)) {
  782. // Cache was set just by downloading file to right location.
  783. }
  784. elseif (file_exists($cache_file)) {
  785. drush_log(dt('!name retrieved from an expired cache since refresh failed.', array('!name' => $cache_name)), 'warning');
  786. }
  787. else {
  788. $cache_file = FALSE;
  789. }
  790. }
  791. if ($cache_file && copy($cache_file, $destination)) {
  792. // Copy cached file to the destination
  793. return $destination;
  794. }
  795. }
  796. elseif ($return = _drush_download_file($url, $destination)) {
  797. drush_register_file_for_deletion($return);
  798. return $return;
  799. }
  800. // Unable to retrieve from cache nor download.
  801. return FALSE;
  802. }
  803. /**
  804. * Download a file using wget, curl or file_get_contents. Does not use download
  805. * cache.
  806. *
  807. * @param string $url
  808. * The url of the file to download.
  809. * @param string $destination
  810. * The name of the file to be saved, which may include the full path.
  811. * @param boolean $overwrite
  812. * Overwrite any file thats already at the destination.
  813. * @return string
  814. * The path to the downloaded file, or FALSE if the file could not be
  815. * downloaded.
  816. */
  817. function _drush_download_file($url, $destination, $overwrite = TRUE) {
  818. static $use_wget;
  819. if ($use_wget === NULL) {
  820. $use_wget = drush_shell_exec('wget --version');
  821. }
  822. $destination_tmp = drush_tempnam('download_file');
  823. if ($use_wget) {
  824. drush_shell_exec("wget -q --timeout=30 -O %s %s", $destination_tmp, $url);
  825. }
  826. else {
  827. drush_shell_exec("curl --fail -s -L --connect-timeout 30 -o %s %s", $destination_tmp, $url);
  828. }
  829. if (!drush_file_not_empty($destination_tmp) && $file = @file_get_contents($url)) {
  830. @file_put_contents($destination_tmp, $file);
  831. }
  832. if (!drush_file_not_empty($destination_tmp)) {
  833. // Download failed.
  834. return FALSE;
  835. }
  836. drush_move_dir($destination_tmp, $destination, $overwrite);
  837. return $destination;
  838. }
  839. /**
  840. * Determines the MIME content type of the specified file.
  841. *
  842. * The power of this function depends on whether the PHP installation
  843. * has either mime_content_type() or finfo installed -- if not, only tar,
  844. * gz, zip and bzip2 types can be detected.
  845. *
  846. * If mime type can't be obtained, an error will be set.
  847. *
  848. * @return mixed
  849. * The MIME content type of the file or FALSE.
  850. */
  851. function drush_mime_content_type($filename) {
  852. // 1. Try to use php built-ins.
  853. $content_type = FALSE;
  854. if (class_exists('finfo')) {
  855. drush_log(dt('Fileinfo extension available.'), 'debug');
  856. // For pecl's fileinfo on php 5.2 there is quite some inconsistency in
  857. // distributions and loading of magic files.
  858. // TODO: remove @ and use FILEINFO_MIME_TYPE when drush requires php 5.3
  859. $finfo = @new finfo(FILEINFO_MIME);
  860. $ct = @$finfo->file($filename);
  861. if ($ct) {
  862. // We only want the first part, before the ;
  863. $content_type = current(explode(';', $ct));
  864. }
  865. }
  866. // TODO: to be removed in php 5.3 (deprecated).
  867. if ((!$content_type) && (function_exists('mime_content_type'))) {
  868. drush_log(dt('mime_magic support enabled.'), 'debug');
  869. $content_type = trim(mime_content_type($filename));
  870. }
  871. if (!$content_type) {
  872. drush_log(dt('No fileinfo or mime_magic support available.'), 'debug');
  873. }
  874. elseif ($content_type == 'application/octet-stream') {
  875. drush_log(dt('Mime type for !file is application/octet-stream.', array('!file' => $filename)), 'debug');
  876. $content_type = FALSE;
  877. }
  878. // 2. if PHP's built-ins aren't present or PHP is configured in such a way
  879. // that all these files are considered octet-stream (e.g with mod_mime_magic
  880. // and an http conf that's serving all archives as octet-stream for other
  881. // reasons) we'll detect (a few select) mime types on our own by examing the
  882. // file's magic header bytes.
  883. if (!$content_type) {
  884. drush_log(dt('Examining !file headers.', array('!file' => $filename)), 'debug');
  885. if ($file = fopen($filename, 'rb')) {
  886. $first = fread($file, 2);
  887. fclose($file);
  888. if ($first !== FALSE) {
  889. // Interpret the two bytes as a little endian 16-bit unsigned int.
  890. $data = unpack('v', $first);
  891. switch ($data[1]) {
  892. case 0x8b1f:
  893. // First two bytes of gzip files are 0x1f, 0x8b (little-endian).
  894. // See http://www.gzip.org/zlib/rfc-gzip.html#header-trailer
  895. $content_type = 'application/x-gzip';
  896. break;
  897. case 0x4b50:
  898. // First two bytes of zip files are 0x50, 0x4b ('PK') (little-endian).
  899. // See http://en.wikipedia.org/wiki/Zip_(file_format)#File_headers
  900. $content_type = 'application/zip';
  901. break;
  902. case 0x5a42:
  903. // First two bytes of bzip2 files are 0x5a, 0x42 ('BZ') (big-endian).
  904. // See http://en.wikipedia.org/wiki/Bzip2#File_format
  905. $content_type = 'application/x-bzip2';
  906. break;
  907. default:
  908. drush_log(dt('Unable to determine mime type from header bytes 0x!hex of !file.', array('!hex' => dechex($data[1]), '!file' => $filename,), 'debug'));
  909. }
  910. }
  911. else {
  912. drush_log(dt('Unable to read !file.', array('!file' => $filename)), 'warning');
  913. }
  914. }
  915. else {
  916. drush_log(dt('Unable to open !file.', array('!file' => $filename)), 'warning');
  917. }
  918. }
  919. // 3. Lastly if above methods didn't work, try to guess the mime type from
  920. // the file extension. This is useful if the file has no identificable magic
  921. // header bytes (for example tarballs).
  922. if (!$content_type) {
  923. drush_log(dt('Examining !file extension.', array('!file' => $filename)), 'debug');
  924. // Remove querystring from the filename, if present.
  925. $filename = basename(current(explode('?', $filename, 2)));
  926. $extension_mimetype = array(
  927. '.tar' => 'application/x-tar',
  928. );
  929. foreach ($extension_mimetype as $extension => $ct) {
  930. if (substr($filename, -strlen($extension)) === $extension) {
  931. $content_type = $ct;
  932. break;
  933. }
  934. }
  935. }
  936. if ($content_type) {
  937. drush_log(dt('Mime type for !file is !mt', array('!file' => $filename, '!mt' => $content_type)), 'notice');
  938. return $content_type;
  939. }
  940. return drush_set_error('MIME_CONTENT_TYPE_UNKNOWN', dt('Unable to determine mime type for !file.', array('!file' => $filename)));
  941. }
  942. /**
  943. * Check whether a file is a supported tarball.
  944. *
  945. * @return mixed
  946. * The file content type if it's a tarball. FALSE otherwise.
  947. */
  948. function drush_file_is_tarball($path) {
  949. $content_type = drush_mime_content_type($path);
  950. $supported = array(
  951. 'application/x-bzip2',
  952. 'application/x-gzip',
  953. 'application/x-tar',
  954. 'application/x-zip',
  955. 'application/zip',
  956. );
  957. if (in_array($content_type, $supported)) {
  958. return $content_type;
  959. }
  960. return FALSE;
  961. }
  962. /**
  963. * Extract a tarball.
  964. *
  965. * @param string $path
  966. * Path to the archive to be extracted.
  967. * @param string $destination
  968. * The destination directory the tarball should be extracted into.
  969. * Optional, if ommitted the tarball directory will be used as destination.
  970. * @param boolean $listing
  971. * If TRUE, a listing of the tar contents will be returned on success.
  972. *
  973. * @return mixed
  974. * TRUE on success, FALSE on fail. If $listing is TRUE, a file listing of the
  975. * tarball is returned if the extraction reported success, instead of TRUE.
  976. */
  977. function drush_tarball_extract($path, $destination = FALSE, $listing = FALSE) {
  978. // Check if tarball is supported.
  979. if (!($mimetype = drush_file_is_tarball($path))) {
  980. return drush_set_error('TARBALL_EXTRACT_UNKNOWN_FORMAT', dt('Unable to extract !path. Unknown archive format.', array('!path' => $path)));
  981. }
  982. // Check if destination is valid.
  983. if (!$destination) {
  984. $destination = dirname($path);
  985. }
  986. if (!drush_mkdir($destination)) {
  987. // drush_mkdir already set an error.
  988. return FALSE;
  989. }
  990. // Perform the extraction of a zip file.
  991. if (($mimetype == 'application/zip') || ($mimetype == 'application/x-zip')) {
  992. $return = drush_shell_cd_and_exec(dirname($path), "unzip %s -d %s", $path, $destination);
  993. if (!$return) {
  994. return drush_set_error('DRUSH_TARBALL_EXTRACT_ERROR', dt('Unable to unzip !filename.', array('!filename' => $path)));
  995. }
  996. if ($listing) {
  997. // unzip prefixes the file listing output with a header line,
  998. // and prefixes each line with a verb representing the compression type.
  999. $output = drush_shell_exec_output();
  1000. // Remove the header line.
  1001. array_shift($output);
  1002. // Remove the prefix verb from each line.
  1003. $output = array_map(create_function('$str', 'return substr($str, strpos($str, ":") + 3 + ' . strlen($destination) . ');'), $output);
  1004. // Remove any remaining blank lines.
  1005. $return = array_filter($output, create_function('$str', 'return $str != "";'));
  1006. }
  1007. }
  1008. // Otherwise we have a possibly-compressed Tar file.
  1009. // If we are not on Windows, then try to do "tar" in a single operation.
  1010. elseif (!drush_is_windows()) {
  1011. $tar = drush_get_tar_executable();
  1012. $tar_compression_flag = '';
  1013. if ($mimetype == 'application/x-gzip') {
  1014. $tar_compression_flag = 'z';
  1015. }
  1016. elseif ($mimetype == 'application/x-bzip2') {
  1017. $tar_compression_flag = 'j';
  1018. }
  1019. $return = drush_shell_cd_and_exec(dirname($path), "$tar -C %s -x%sf %s", $destination, $tar_compression_flag, basename($path));
  1020. if (!$return) {
  1021. return drush_set_error('DRUSH_TARBALL_EXTRACT_ERROR', dt('Unable to untar !filename.', array('!filename' => $path)));
  1022. }
  1023. if ($listing) {
  1024. // We use a separate tar -tf instead of -xvf above because
  1025. // the output is not the same in Mac.
  1026. drush_shell_cd_and_exec(dirname($path), "$tar -t%sf %s", $tar_compression_flag, basename($path));
  1027. $return = drush_shell_exec_output();
  1028. }
  1029. }
  1030. // In windows, do the extraction by its primitive steps.
  1031. else {
  1032. // 1. copy the source tarball to the destination directory. Rename to a
  1033. // temp name in case the destination directory == dirname($path)
  1034. $tmpfile = drush_tempnam(basename($path), $destination);
  1035. drush_copy_dir($path, $tmpfile, FILE_EXISTS_OVERWRITE);
  1036. // 2. uncompress the tarball, if compressed.
  1037. if (($mimetype == 'application/x-gzip') || ($mimetype == 'application/x-bzip2')) {
  1038. if ($mimetype == 'application/x-gzip') {
  1039. $compressed = $tmpfile . '.gz';
  1040. // We used to use gzip --decompress in --stdout > out, but the output
  1041. // redirection sometimes failed on Windows for some binary output.
  1042. $command = 'gzip --decompress %s';
  1043. }
  1044. elseif ($mimetype == 'application/x-bzip2') {
  1045. $compressed = $tmpfile . '.bz2';
  1046. $command = 'bzip2 --decompress %s';
  1047. }
  1048. drush_op('rename', $tmpfile, $compressed);
  1049. $return = drush_shell_cd_and_exec(dirname($compressed), $command, $compressed);
  1050. if (!$return || !file_exists($tmpfile)) {
  1051. return drush_set_error('DRUSH_TARBALL_EXTRACT_ERROR', dt('Unable to decompress !filename.', array('!filename' => $compressed)));
  1052. }
  1053. }
  1054. // 3. Untar.
  1055. $tar = drush_get_tar_executable();
  1056. $return = drush_shell_cd_and_exec(dirname($tmpfile), "$tar -xvf %s", basename($tmpfile));
  1057. if (!$return) {
  1058. return drush_set_error('DRUSH_TARBALL_EXTRACT_ERROR', dt('Unable to untar !filename.', array('!filename' => $tmpfile)));
  1059. }
  1060. if ($listing) {
  1061. $return = drush_shell_exec_output();
  1062. // Cut off the 'x ' prefix for the each line of the tar output
  1063. // See http://drupal.org/node/1775520
  1064. foreach($return as &$line) {
  1065. if(strpos($line, "x ") === 0)
  1066. $line = substr($line, 2);
  1067. }
  1068. }
  1069. // Remove the temporary file so the md5 hash is accurate.
  1070. unlink($tmpfile);
  1071. }
  1072. return $return;
  1073. }
  1074. /**
  1075. * Download and extract a tarball to the lib directory.
  1076. *
  1077. * Checks for reported success, but callers should normally check for existence
  1078. * of specific expected file(s) in the library.
  1079. *
  1080. * @param string $url
  1081. * The URL to the library tarball.
  1082. *
  1083. * @return boolean
  1084. * TRUE is the download and extraction reported success, FALSE otherwise.
  1085. */
  1086. function drush_lib_fetch($url) {
  1087. $lib = drush_get_option('lib', DRUSH_BASE_PATH . '/lib');
  1088. if (!is_writable($lib)) {
  1089. return drush_set_error('DRUSH_LIB_UNWRITABLE', dt("Drush needs to download a library from !url in order to function, and the attempt to download this file automatically failed because you do not have permission to write to the library directory !path. To continue you will need to manually download the package from !url, extract it, and copy the directory into your !path directory.", array('!path' => $lib, '!url' => $url)));
  1090. }
  1091. $destination = $lib . '/drush-library-' . mt_rand();
  1092. $path = drush_download_file($url, $destination);
  1093. if (!$path) {
  1094. return FALSE;
  1095. }
  1096. return drush_tarball_extract($path);
  1097. }
  1098. /**
  1099. * @defgroup commandprocessing Command processing functions.
  1100. * @{
  1101. *
  1102. * These functions manage command processing by the
  1103. * main function in drush.php.
  1104. */
  1105. /**
  1106. * Handle any command preprocessing that may need to be done, including
  1107. * potentially redispatching the command immediately (e.g. for remote
  1108. * commands).
  1109. *
  1110. * @return
  1111. * TRUE if the command was handled remotely.
  1112. */
  1113. function drush_preflight_command_dispatch() {
  1114. $interactive = drush_get_option('interactive', FALSE);
  1115. // The command will be executed remotely if the --remote-host flag
  1116. // is set; note that if a site alias is provided on the command line,
  1117. // and the site alias references a remote server, then the --remote-host
  1118. // option will be set when the site alias is processed.
  1119. // @see drush_sitealias_check_arg
  1120. $remote_host = drush_get_option('remote-host');
  1121. // Get the command early so that we can allow commands to directly handle remote aliases if they wish
  1122. $command = drush_parse_command();
  1123. if (isset($command)) {
  1124. drush_command_default_options($command);
  1125. }
  1126. // If the command sets the 'strict-option-handling' flag, then we will remove
  1127. // any cli options that appear after the command name form the 'cli' context.
  1128. // The cli options that appear before the command name are stored in the
  1129. // 'DRUSH_GLOBAL_CLI_OPTIONS' context, so we will just overwrite the cli context
  1130. // with this, after doing the neccessary fixup from short-form to long-form options.
  1131. // After we do that, we put back any local drush options identified by $command['options'].
  1132. if (is_array($command) && !empty($command['strict-option-handling'])) {
  1133. $cli_options = drush_get_context('DRUSH_GLOBAL_CLI_OPTIONS', array());
  1134. // Now we are going to sort out any options that exist in $command['options'];
  1135. // we will remove these from DRUSH_COMMAND_ARGS and put them back into the
  1136. // cli options.
  1137. $cli_context = drush_get_context('cli');
  1138. $remove_from_command_args = array();
  1139. foreach ($command['options'] as $option => $info) {
  1140. if (array_key_exists($option, $cli_context)) {
  1141. $cli_options[$option] = $cli_context[$option];
  1142. $remove_from_command_args[$option] = $option;
  1143. }
  1144. }
  1145. if (!empty($remove_from_command_args)) {
  1146. $drush_command_args = array();
  1147. foreach (drush_get_context('DRUSH_COMMAND_ARGS') as $arg) {
  1148. if (!_drush_should_remove_command_arg($arg, $remove_from_command_args)) {
  1149. $drush_command_args[] = $arg;
  1150. }
  1151. }
  1152. drush_set_context('DRUSH_COMMAND_ARGS', $drush_command_args);
  1153. }
  1154. drush_expand_short_form_options($cli_options);
  1155. drush_set_context('cli', $cli_options);
  1156. _drush_bootstrap_global_options();
  1157. }
  1158. // If the command sets the 'handle-remote-commands' flag, then we will short-circuit
  1159. // remote command dispatching and site-list command dispatching, and always let
  1160. // the command handler run on the local machine.
  1161. if (is_array($command) && !empty($command['handle-remote-commands'])) {
  1162. return FALSE;
  1163. }
  1164. if (isset($remote_host)) {
  1165. $args = drush_get_arguments();
  1166. $command_name = array_shift($args);
  1167. $remote_user = drush_get_option('remote-user');
  1168. // Force interactive mode if there is a single remote target. #interactive is added by drush_do_command_redispatch
  1169. drush_set_option('interactive', TRUE);
  1170. $values = drush_do_command_redispatch(isset($command) ? $command : $command_name, $args, $remote_host, $remote_user);
  1171. // In 'interactive' mode, $values is the result code from drush_shell_proc_open.
  1172. // TODO: in _drush_backend_invoke, return array('error_status' => $ret) instead for uniformity.
  1173. if (!is_array($values) && ($values != 0)) {
  1174. // Force an error result code. Note that drush_shutdown() will still run.
  1175. drush_set_context('DRUSH_EXECUTION_COMPLETED', TRUE);
  1176. exit($values);
  1177. }
  1178. return TRUE;
  1179. }
  1180. // If the --site-list flag is set, then we will execute the specified
  1181. // command once for every site listed in the site list.
  1182. $site_list = drush_get_option('site-list');
  1183. if (isset($site_list)) {
  1184. if (!is_array($site_list)) {
  1185. $site_list = explode(',', $site_list);
  1186. }
  1187. $site_record = array('site-list' => $site_list);
  1188. $args = drush_get_arguments();
  1189. if (!drush_get_context('DRUSH_SIMULATE') && !$interactive) {
  1190. drush_print(dt("You are about to execute '!command' non-interactively (--yes forced) on all of the following targets:", array('!command' => implode(" ", $args))));
  1191. foreach ($site_list as $one_destination) {
  1192. drush_print(dt(' !target', array('!target' => $one_destination)));
  1193. }
  1194. if (drush_confirm('Continue? ') === FALSE) {
  1195. drush_user_abort();
  1196. return TRUE;
  1197. }
  1198. }
  1199. $command_name = array_shift($args);
  1200. $multi_options = drush_get_context('cli');
  1201. $backend_options = array();
  1202. if (drush_get_option('pipe') || drush_get_option('interactive')) {
  1203. $backend_options['interactive'] = TRUE;
  1204. }
  1205. if (drush_get_option('no-label', FALSE)) {
  1206. $backend_options['no-label'] = TRUE;
  1207. }
  1208. $values = drush_invoke_process($site_record, $command_name, $args, $multi_options, $backend_options);
  1209. return TRUE;
  1210. }
  1211. return FALSE;
  1212. }
  1213. /**
  1214. * Determine whether or not an argument should be removed from the
  1215. * DRUSH_COMMAND_ARGS context. This method is used when a Drush
  1216. * command has set the 'strict-option-handling' flag indicating
  1217. * that it will pass through all commandline arguments and any
  1218. * additional options (not known to Drush) to some shell command.
  1219. *
  1220. * Take as an example the following call to core-rsync:
  1221. *
  1222. * drush --yes core-rsync -v -az --exclude-paths='.git:.svn' local-files/ @site:%files
  1223. *
  1224. * In this instance:
  1225. *
  1226. * --yes is a global Drush option
  1227. *
  1228. * -v is an rsync option. It will make rsync run in verbose mode,
  1229. * but will not make Drush run in verbose mode due to the fact that
  1230. * core-rsync sets the 'strict-option-handling' flag.
  1231. *
  1232. * --exclude-paths is a local Drush option. It will be converted by
  1233. * Drush into --exclude='.git' and --exclude='.svn', and then passed
  1234. * on to the rsync command.
  1235. *
  1236. * The parameter $arg passed to this function is one of the elements
  1237. * of DRUSH_COMMAND_ARGS. It will have values such as:
  1238. * -v
  1239. * -az
  1240. * --exclude-paths='.git:.svn'
  1241. * local-files/
  1242. * @site:%files
  1243. *
  1244. * Our job in this function is to determine if $arg should be removed
  1245. * by virtue of appearing in $removal_list. $removal_list is an array
  1246. * that will contain values such as 'exclude-paths'. Both the key and
  1247. * the value of $removal_list is the same.
  1248. */
  1249. function _drush_should_remove_command_arg($arg, $removal_list) {
  1250. foreach ($removal_list as $candidate) {
  1251. if (($arg == "-$candidate") ||
  1252. ($arg == "--$candidate") ||
  1253. (substr($arg,0,strlen($candidate)+3) == "--$candidate=") ) {
  1254. return TRUE;
  1255. }
  1256. }
  1257. return FALSE;
  1258. }
  1259. /**
  1260. * Used by functions that operate on lists of sites, moving
  1261. * information from the source to the destination. Currenlty
  1262. * this includes 'drush rsync' and 'drush sql sync'.
  1263. */
  1264. function drush_do_multiple_command($command, $source_record, $destination_record, $allow_single_source = FALSE) {
  1265. $is_multiple_command = FALSE;
  1266. if ((($allow_single_source == TRUE) || array_key_exists('site-list', $source_record)) && array_key_exists('site-list', $destination_record)) {
  1267. $is_multiple_command = TRUE;
  1268. $source_path = array_key_exists('path-component', $source_record) ? $source_record['path-component'] : '';
  1269. $destination_path = array_key_exists('path-component', $destination_record) ? $destination_record['path-component'] : '';
  1270. $target_list = array_values(drush_sitealias_resolve_sitelist($destination_record));
  1271. if (array_key_exists('site-list', $source_record)) {
  1272. $source_list = array_values(drush_sitealias_resolve_sitelist($source_record));
  1273. if (drush_sitealias_check_lists_alignment($source_list, $target_list) === FALSE) {
  1274. if (array_key_exists('unordered-list', $source_record) || array_key_exists('unordered-list', $destination_record)) {
  1275. drush_sitelist_align_lists($source_list, $target_list, $aligned_source, $aligned_target);
  1276. $source_list = $aligned_source;
  1277. $target_list = $aligned_target;
  1278. }
  1279. }
  1280. }
  1281. else {
  1282. $source_list = array_fill(0, count($target_list), $source_record);
  1283. }
  1284. if (!drush_get_context('DRUSH_SIMULATE')) {
  1285. drush_print(dt('You are about to !command between all of the following targets:', array('!command' => $command)));
  1286. $i = 0;
  1287. foreach ($source_list as $one_source) {
  1288. $one_target = $target_list[$i];
  1289. ++$i;
  1290. drush_print(dt(' !source will overwrite !target', array('!source' => drush_sitealias_alias_record_to_spec($one_source) . $source_path, '!target' => drush_sitealias_alias_record_to_spec($one_target) . $destination_path)));
  1291. }
  1292. if (drush_confirm('Continue? ') === FALSE) {
  1293. return drush_user_abort();
  1294. }
  1295. }
  1296. $data = drush_redispatch_get_options();
  1297. $i = 0;
  1298. foreach ($source_list as $one_source) {
  1299. $one_target = $target_list[$i];
  1300. ++$i;
  1301. $source_spec = drush_sitealias_alias_record_to_spec($one_source);
  1302. $target_spec = drush_sitealias_alias_record_to_spec($one_target);
  1303. drush_log(dt('Begin do_multiple !command via backend invoke', array('!command' => $command)));
  1304. $values = drush_invoke_process('@self', $command, array($source_spec . $source_path, $target_spec . $destination_path), $data);
  1305. drush_log(dt('Backend invoke is complete'));
  1306. }
  1307. }
  1308. return $is_multiple_command;
  1309. }
  1310. /**
  1311. * Run a command on the site specified by the provided command record.
  1312. *
  1313. * The standard function that provides this service is called
  1314. * drush_invoke_process. Please call the standard function.
  1315. *
  1316. * @param backend_options
  1317. * TRUE - integrate errors
  1318. * FALSE - do not integrate errors
  1319. * array - @see drush_backend_invoke_concurrent
  1320. *
  1321. function drush_do_site_command($site_record, $command, $args = array(), $command_options = array(), $backend_options = FALSE) {
  1322. return drush_invoke_process($site_record, $command, $args, $command_options, $backend_options);
  1323. }
  1324. */
  1325. /**
  1326. * Redispatch the specified command using the same
  1327. * options that were passed to this invocation of drush.
  1328. */
  1329. function drush_do_command_redispatch($command, $args = array(), $remote_host = NULL, $remote_user = NULL, $drush_path = NULL) {
  1330. $additional_global_options = array();
  1331. $command_options = drush_redispatch_get_options();
  1332. if (is_array($command)) {
  1333. $command_name = $command['command'];
  1334. // If we are executing a remote command that uses strict option handling,
  1335. // then mark all of the options in the alias context as global, so that they
  1336. // will appear before the command name.
  1337. if (!empty($command['strict-option-handling'])) {
  1338. foreach(drush_get_context('alias') as $alias_key => $alias_value) {
  1339. if (array_key_exists($alias_key, $command_options) && !array_key_exists($alias_key, $command['options'])) {
  1340. $additional_global_options[$alias_key] = $alias_value;
  1341. }
  1342. }
  1343. }
  1344. }
  1345. else {
  1346. $command_name = $command;
  1347. }
  1348. // If the path to drush was supplied, then use it to invoke the new command.
  1349. if ($drush_path == NULL) {
  1350. $drush_path = drush_get_option('drush-script');
  1351. if (!isset($drush_path)) {
  1352. $drush_folder = drush_get_option('drush');
  1353. if (isset($drush)) {
  1354. $drush_path = $drush_folder . '/drush';
  1355. }
  1356. }
  1357. }
  1358. $backend_options = array('drush-script' => $drush_path, 'remote-host' => $remote_host, 'remote-user' => $remote_user, 'integrate' => TRUE, 'additional-global-options' => $additional_global_options);
  1359. if (drush_get_option('interactive')) {
  1360. $backend_options['interactive'] = TRUE;
  1361. }
  1362. // Run the command in a new process.
  1363. drush_log(dt('Begin redispatch via invoke process'));
  1364. $values = drush_invoke_process('@self', $command_name, $args, $command_options, $backend_options);
  1365. drush_log(dt('Invoke process is complete'));
  1366. return $values;
  1367. }
  1368. /**
  1369. * @} End of "defgroup commandprocessing".
  1370. */
  1371. /**
  1372. * @defgroup logging Logging information to be provided as output.
  1373. * @{
  1374. *
  1375. * These functions are primarily for diagnostic purposes, but also provide an overview of tasks that were taken
  1376. * by drush.
  1377. */
  1378. /**
  1379. * Add a log message to the log history.
  1380. *
  1381. * This function calls the callback stored in the 'DRUSH_LOG_CALLBACK' context with
  1382. * the resulting entry at the end of execution.
  1383. *
  1384. * This allows you to replace it with custom logging implementations if needed,
  1385. * such as logging to a file or logging to a database (drupal or otherwise).
  1386. *
  1387. * The default callback is the _drush_print_log() function with prints the messages
  1388. * to the shell.
  1389. *
  1390. * @param message
  1391. * String containing the message to be logged.
  1392. * @param type
  1393. * The type of message to be logged. Common types are 'warning', 'error', 'success' and 'notice'.
  1394. * A type of 'failed' can also be supplied to flag as an 'error'.
  1395. * A type of 'ok' or 'completed' can also be supplied to flag as a 'success'.
  1396. * If you want your log messages to print to screen without the user entering
  1397. * a -v or --verbose flag, use type 'ok', this prints log messages out to
  1398. * STDERR, which prints to screen (unless you have redirected it). All other
  1399. * types of messages will be assumed to be notices.
  1400. */
  1401. function drush_log($message, $type = 'notice', $error = null) {
  1402. $log =& drush_get_context('DRUSH_LOG', array());
  1403. $callback = drush_get_context('DRUSH_LOG_CALLBACK', '_drush_print_log');
  1404. $entry = array(
  1405. 'type' => $type,
  1406. 'message' => $message,
  1407. 'timestamp' => microtime(TRUE),
  1408. 'memory' => memory_get_usage(),
  1409. );
  1410. $entry['error'] = $error;
  1411. $log[] = $entry;
  1412. drush_backend_packet('log', $entry);
  1413. return $callback($entry);
  1414. }
  1415. /**
  1416. * Backend command callback. Add a log message to the log history.
  1417. *
  1418. * @param entry
  1419. * The log entry.
  1420. */
  1421. function drush_backend_packet_log($entry, $backend_options) {
  1422. if (!$backend_options['log']) {
  1423. return;
  1424. }
  1425. if (!is_string($entry['message'])) {
  1426. $entry['message'] = implode("\n", (array)$entry['message']);
  1427. }
  1428. $entry['message'] = $entry['message'];
  1429. $log =& drush_get_context('DRUSH_LOG', array());
  1430. $log[] = $entry;
  1431. // Yes, this looks odd, but we might in fact be a backend command
  1432. // that ran another backend command.
  1433. drush_backend_packet('log', $entry);
  1434. if (array_key_exists('#output-label', $backend_options)) {
  1435. $entry['message'] = $backend_options['#output-label'] . $entry['message'];
  1436. }
  1437. // If integrate is FALSE, then log messages are stored in DRUSH_LOG,
  1438. // but are -not- printed to the console.
  1439. if ($backend_options['integrate']) {
  1440. $callback = drush_get_context('DRUSH_LOG_CALLBACK', '_drush_print_log');
  1441. return $callback($entry);
  1442. }
  1443. }
  1444. /**
  1445. * Retrieve the log messages from the log history
  1446. *
  1447. * @return
  1448. * Entire log history
  1449. */
  1450. function drush_get_log() {
  1451. return drush_get_context('DRUSH_LOG', array());
  1452. }
  1453. /**
  1454. * Run print_r on a variable and log the output.
  1455. */
  1456. function dlm($object) {
  1457. drush_log(print_r($object, TRUE));
  1458. }
  1459. /**
  1460. * Display the pipe output for the current request.
  1461. */
  1462. function drush_pipe_output() {
  1463. $pipe = drush_get_context('DRUSH_PIPE_BUFFER');
  1464. if (!empty($pipe)) {
  1465. drush_print_r($pipe, NULL, FALSE);
  1466. }
  1467. }
  1468. /**
  1469. * Display the log message
  1470. *
  1471. * By default, only warnings and errors will be displayed, if 'verbose' is specified, it will also display notices.
  1472. *
  1473. * @param
  1474. * The associative array for the entry.
  1475. *
  1476. * @return
  1477. * False in case of an error or failed type, True in all other cases.
  1478. */
  1479. function _drush_print_log($entry) {
  1480. if (drush_get_context('DRUSH_NOCOLOR')) {
  1481. $red = "[%s]";
  1482. $yellow = "[%s]";
  1483. $green = "[%s]";
  1484. }
  1485. else {
  1486. $red = "\033[31;40m\033[1m[%s]\033[0m";
  1487. $yellow = "\033[1;33;40m\033[1m[%s]\033[0m";
  1488. $green = "\033[1;32;40m\033[1m[%s]\033[0m";
  1489. }
  1490. $verbose = drush_get_context('DRUSH_VERBOSE');
  1491. $debug = drush_get_context('DRUSH_DEBUG');
  1492. $return = TRUE;
  1493. switch ($entry['type']) {
  1494. case 'warning' :
  1495. case 'cancel' :
  1496. $type_msg = sprintf($yellow, $entry['type']);
  1497. break;
  1498. case 'failed' :
  1499. case 'error' :
  1500. $type_msg = sprintf($red, $entry['type']);
  1501. $return = FALSE;
  1502. break;
  1503. case 'ok' :
  1504. case 'completed' :
  1505. case 'success' :
  1506. case 'status':
  1507. // In quiet mode, suppress progress messages
  1508. if (drush_get_context('DRUSH_QUIET')) {
  1509. return TRUE;
  1510. }
  1511. $type_msg = sprintf($green, $entry['type']);
  1512. break;
  1513. case 'notice' :
  1514. case 'message' :
  1515. case 'info' :
  1516. if (!$verbose) {
  1517. // print nothing. exit cleanly.
  1518. return TRUE;
  1519. }
  1520. $type_msg = sprintf("[%s]", $entry['type']);
  1521. break;
  1522. default :
  1523. if (!$debug) {
  1524. // print nothing. exit cleanly.
  1525. return TRUE;
  1526. }
  1527. $type_msg = sprintf("[%s]", $entry['type']);
  1528. break;
  1529. }
  1530. // When running in backend mode, log messages are not displayed, as they will
  1531. // be returned in the JSON encoded associative array.
  1532. if (drush_get_context('DRUSH_BACKEND')) {
  1533. return $return;
  1534. }
  1535. $columns = drush_get_context('DRUSH_COLUMNS', 80);
  1536. $width[1] = 11;
  1537. // Append timer and memory values.
  1538. if ($debug) {
  1539. $timer = sprintf('[%s sec, %s]', round($entry['timestamp']-DRUSH_REQUEST_TIME, 2), drush_format_size($entry['memory']));
  1540. $entry['message'] = $entry['message'] . ' ' . $timer;
  1541. }
  1542. $width[0] = ($columns - 11);
  1543. $format = sprintf("%%-%ds%%%ds", $width[0], $width[1]);
  1544. // Place the status message right aligned with the top line of the error message.
  1545. $message = wordwrap($entry['message'], $width[0]);
  1546. $lines = explode("\n", $message);
  1547. $lines[0] = sprintf($format, $lines[0], $type_msg);
  1548. $message = implode("\n", $lines);
  1549. drush_print($message, 0, STDERR);
  1550. return $return;
  1551. }
  1552. // Print all timers for the request.
  1553. function drush_print_timers() {
  1554. global $timers;
  1555. $temparray = array();
  1556. foreach ((array)$timers as $name => $timerec) {
  1557. // We have to use timer_read() for active timers, and check the record for others
  1558. if (isset($timerec['start'])) {
  1559. $temparray[$name] = timer_read($name);
  1560. }
  1561. else {
  1562. $temparray[$name] = $timerec['time'];
  1563. }
  1564. }
  1565. // Go no farther if there were no timers
  1566. if (count($temparray) > 0) {
  1567. // Put the highest cumulative times first
  1568. arsort($temparray);
  1569. $table = array();
  1570. $table[] = array('Timer', 'Cum (sec)', 'Count', 'Avg (msec)');
  1571. foreach ($temparray as $name => $time) {
  1572. $cum = round($time/1000, 3);
  1573. $count = $timers[$name]['count'];
  1574. if ($count > 0) {
  1575. $avg = round($time/$count, 3);
  1576. }
  1577. else {
  1578. $avg = 'N/A';
  1579. }
  1580. $table[] = array($name, $cum, $count, $avg);
  1581. }
  1582. drush_print_table($table, TRUE, array(), STDERR);
  1583. }
  1584. }
  1585. /**
  1586. * Turn drupal_set_message errors into drush_log errors
  1587. */
  1588. function _drush_log_drupal_messages() {
  1589. if (function_exists('drupal_get_messages')) {
  1590. $messages = drupal_get_messages(NULL, TRUE);
  1591. if (array_key_exists('error', $messages)) {
  1592. //Drupal message errors.
  1593. foreach ((array) $messages['error'] as $error) {
  1594. $error = strip_tags($error);
  1595. $header = preg_match('/^warning: Cannot modify header information - headers already sent by /i', $error);
  1596. $session = preg_match('/^warning: session_start\(\): Cannot send session /i', $error);
  1597. if ($header || $session) {
  1598. //These are special cases for an unavoidable warnings
  1599. //that are generated by generating output before Drupal is bootstrapped.
  1600. //or sending a session cookie (seems to affect d7 only?)
  1601. //Simply ignore them.
  1602. continue;
  1603. }
  1604. elseif (preg_match('/^warning:/i', $error)) {
  1605. drush_log(preg_replace('/^warning: /i', '', $error), 'warning');
  1606. }
  1607. elseif (preg_match('/^notice:/i', $error)) {
  1608. drush_log(preg_replace('/^notice: /i', '', $error), 'notice');
  1609. }
  1610. elseif (preg_match('/^user warning:/i', $error)) {
  1611. // This is a special case. PHP logs sql errors as 'User Warnings', not errors.
  1612. drush_set_error('DRUSH_DRUPAL_ERROR_MESSAGE', preg_replace('/^user warning: /i', '', $error));
  1613. }
  1614. else {
  1615. drush_set_error('DRUSH_DRUPAL_ERROR_MESSAGE', $error);
  1616. }
  1617. }
  1618. }
  1619. unset($messages['error']);
  1620. // Log non-error messages.
  1621. foreach ($messages as $type => $items) {
  1622. foreach ($items as $item) {
  1623. drush_log(strip_tags($item), $type);
  1624. }
  1625. }
  1626. }
  1627. }
  1628. // Copy of format_size() in Drupal.
  1629. function drush_format_size($size, $langcode = NULL) {
  1630. if ($size < DRUSH_DRUPAL_KILOBYTE) {
  1631. // format_plural() not always available.
  1632. return dt('@count bytes', array('@count' => $size));
  1633. }
  1634. else {
  1635. $size = $size / DRUSH_DRUPAL_KILOBYTE; // Convert bytes to kilobytes.
  1636. $units = array(
  1637. dt('@size KB', array(), array('langcode' => $langcode)),
  1638. dt('@size MB', array(), array('langcode' => $langcode)),
  1639. dt('@size GB', array(), array('langcode' => $langcode)),
  1640. dt('@size TB', array(), array('langcode' => $langcode)),
  1641. dt('@size PB', array(), array('langcode' => $langcode)),
  1642. dt('@size EB', array(), array('langcode' => $langcode)),
  1643. dt('@size ZB', array(), array('langcode' => $langcode)),
  1644. dt('@size YB', array(), array('langcode' => $langcode)),
  1645. );
  1646. foreach ($units as $unit) {
  1647. if (round($size, 2) >= DRUSH_DRUPAL_KILOBYTE) {
  1648. $size = $size / DRUSH_DRUPAL_KILOBYTE;
  1649. }
  1650. else {
  1651. break;
  1652. }
  1653. }
  1654. return str_replace('@size', round($size, 2), $unit);
  1655. }
  1656. }
  1657. /**
  1658. * @} End of "defgroup logging".
  1659. */
  1660. /**
  1661. * @defgroup errorhandling Managing errors that occur in the Drush framework.
  1662. * @{
  1663. * Functions that manage the current error status of the Drush framework.
  1664. *
  1665. * These functions operate by maintaining a static variable that is a equal to the constant DRUSH_FRAMEWORK_ERROR if an
  1666. * error has occurred.
  1667. * This error code is returned at the end of program execution, and provide the shell or calling application with
  1668. * more information on how to diagnose any problems that may have occurred.
  1669. */
  1670. /**
  1671. * Set an error code for the error handling system.
  1672. *
  1673. * @param error
  1674. * A text string identifying the type of error.
  1675. *
  1676. * @param message
  1677. * Optional. Error message to be logged. If no message is specified, hook_drush_help will be consulted,
  1678. * using a key of 'error:MY_ERROR_STRING'.
  1679. *
  1680. * @return
  1681. * Always returns FALSE, to allow you to return with false in the calling functions,
  1682. * such as <code>return drush_set_error('DRUSH_FRAMEWORK_ERROR')</code>
  1683. */
  1684. function drush_set_error($error, $message = null, $output_label = "") {
  1685. $error_code =& drush_get_context('DRUSH_ERROR_CODE', DRUSH_SUCCESS);
  1686. $error_code = DRUSH_FRAMEWORK_ERROR;
  1687. $error_log =& drush_get_context('DRUSH_ERROR_LOG', array());
  1688. if (is_numeric($error)) {
  1689. $error = 'DRUSH_FRAMEWORK_ERROR';
  1690. }
  1691. $message = ($message) ? $message : drush_command_invoke_all('drush_help', 'error:' . $error);
  1692. if (is_array($message)) {
  1693. $message = implode("\n", $message);
  1694. }
  1695. $error_log[$error][] = $message;
  1696. if (!drush_backend_packet('set_error', array('error' => $error, 'message' => $message))) {
  1697. drush_log(($message) ? $output_label . $message : $output_label . $error, 'error', $error);
  1698. }
  1699. return FALSE;
  1700. }
  1701. /**
  1702. * Return the current error handling status
  1703. *
  1704. * @return
  1705. * The current aggregate error status
  1706. */
  1707. function drush_get_error() {
  1708. return drush_get_context('DRUSH_ERROR_CODE', DRUSH_SUCCESS);
  1709. }
  1710. /**
  1711. * Return the current list of errors that have occurred.
  1712. *
  1713. * @return
  1714. * An associative array of error messages indexed by the type of message.
  1715. */
  1716. function drush_get_error_log() {
  1717. return drush_get_context('DRUSH_ERROR_LOG', array());
  1718. }
  1719. /**
  1720. * Check if a specific error status has been set.
  1721. *
  1722. * @param error
  1723. * A text string identifying the error that has occurred.
  1724. * @return
  1725. * TRUE if the specified error has been set, FALSE if not
  1726. */
  1727. function drush_cmp_error($error) {
  1728. $error_log = drush_get_error_log();
  1729. if (is_numeric($error)) {
  1730. $error = 'DRUSH_FRAMEWORK_ERROR';
  1731. }
  1732. return array_key_exists($error, $error_log);
  1733. }
  1734. /**
  1735. * Clear error context.
  1736. */
  1737. function drush_clear_error() {
  1738. drush_set_context('DRUSH_ERROR_CODE', DRUSH_SUCCESS);
  1739. }
  1740. /**
  1741. * Exit due to user declining a confirmation prompt.
  1742. *
  1743. * Usage: return drush_user_abort();
  1744. */
  1745. function drush_user_abort($msg = NULL) {
  1746. drush_set_context('DRUSH_USER_ABORT', TRUE);
  1747. drush_log($msg ? $msg : dt('Aborting.'), 'cancel');
  1748. return FALSE;
  1749. }
  1750. /**
  1751. * Turn PHP error handling off.
  1752. *
  1753. * This is commonly used while bootstrapping Drupal for install
  1754. * or updates.
  1755. *
  1756. * This also records the previous error_reporting setting, in
  1757. * case it wasn't recorded previously.
  1758. *
  1759. * @see drush_errors_off()
  1760. */
  1761. function drush_errors_off() {
  1762. drush_get_context('DRUSH_ERROR_REPORTING', error_reporting(0));
  1763. ini_set('display_errors', FALSE);
  1764. }
  1765. /**
  1766. * Turn PHP error handling on.
  1767. *
  1768. * We default to error_reporting() here just in
  1769. * case drush_errors_on() is called before drush_errors_off() and
  1770. * the context is not yet set.
  1771. *
  1772. * @arg $errors string
  1773. * The default error level to set in drush. This error level will be
  1774. * carried through further drush_errors_on()/off() calls even if not
  1775. * provided in later calls.
  1776. *
  1777. * @see error_reporting()
  1778. * @see drush_errors_off()
  1779. */
  1780. function drush_errors_on($errors = null) {
  1781. if (is_null($errors)) {
  1782. $errors = error_reporting();
  1783. }
  1784. else {
  1785. drush_set_context('DRUSH_ERROR_REPORTING', $errors);
  1786. }
  1787. error_reporting(drush_get_context('DRUSH_ERROR_REPORTING', $errors));
  1788. ini_set('display_errors', TRUE);
  1789. }
  1790. /**
  1791. * @} End of "defgroup errorhandling".
  1792. */
  1793. /**
  1794. * Get the PHP memory_limit value in bytes.
  1795. */
  1796. function drush_memory_limit() {
  1797. $value = trim(ini_get('memory_limit'));
  1798. $last = strtolower($value[strlen($value)-1]);
  1799. switch ($last) {
  1800. case 'g':
  1801. $value *= DRUSH_DRUPAL_KILOBYTE;
  1802. case 'm':
  1803. $value *= DRUSH_DRUPAL_KILOBYTE;
  1804. case 'k':
  1805. $value *= DRUSH_DRUPAL_KILOBYTE;
  1806. }
  1807. return $value;
  1808. }
  1809. /**
  1810. * Unset the named key anywhere in the provided
  1811. * data structure.
  1812. */
  1813. function drush_unset_recursive(&$data, $unset_key) {
  1814. if (!empty($data) && is_array($data)) {
  1815. unset($data[$unset_key]);
  1816. foreach ($data as $key => $value) {
  1817. if (is_array($value)) {
  1818. drush_unset_recursive($data[$key], $unset_key);
  1819. }
  1820. }
  1821. }
  1822. }
  1823. /**
  1824. * Return a list of VCSs reserved files and directories.
  1825. */
  1826. function drush_version_control_reserved_files() {
  1827. static $files = FALSE;
  1828. if (!$files) {
  1829. // Also support VCSs that are not drush vc engines.
  1830. $files = array('.git', '.gitignore', '.hg', '.hgignore', '.hgrags');
  1831. $engine_info = drush_get_engines('version_control');
  1832. $vcs = array_keys($engine_info['engines']);
  1833. foreach ($vcs as $name) {
  1834. $version_control = drush_include_engine('version_control', $name);
  1835. $files = array_merge($files, $version_control->reserved_files());
  1836. }
  1837. }
  1838. return $files;
  1839. }
  1840. /**
  1841. * Generate a random alphanumeric password. Copied from user.module.
  1842. */
  1843. function drush_generate_password($length = 10) {
  1844. // This variable contains the list of allowable characters for the
  1845. // password. Note that the number 0 and the letter 'O' have been
  1846. // removed to avoid confusion between the two. The same is true
  1847. // of 'I', 1, and 'l'.
  1848. $allowable_characters = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
  1849. // Zero-based count of characters in the allowable list:
  1850. $len = strlen($allowable_characters) - 1;
  1851. // Declare the password as a blank string.
  1852. $pass = '';
  1853. // Loop the number of times specified by $length.
  1854. for ($i = 0; $i < $length; $i++) {
  1855. // Each iteration, pick a random character from the
  1856. // allowable string and append it to the password:
  1857. $pass .= $allowable_characters[mt_rand(0, $len)];
  1858. }
  1859. return $pass;
  1860. }
  1861. /**
  1862. * Form an associative array from a linear array.
  1863. *
  1864. * This function walks through the provided array and constructs an associative
  1865. * array out of it. The keys of the resulting array will be the values of the
  1866. * input array. The values will be the same as the keys unless a function is
  1867. * specified, in which case the output of the function is used for the values
  1868. * instead.
  1869. *
  1870. * @param $array
  1871. * A linear array.
  1872. * @param $function
  1873. * A name of a function to apply to all values before output.
  1874. *
  1875. * @return
  1876. * An associative array.
  1877. */
  1878. function drush_map_assoc($array, $function = NULL) {
  1879. // array_combine() fails with empty arrays:
  1880. // http://bugs.php.net/bug.php?id=34857.
  1881. $array = !empty($array) ? array_combine($array, $array) : array();
  1882. if (is_callable($function)) {
  1883. $array = array_map($function, $array);
  1884. }
  1885. return $array;
  1886. }
  1887. /**
  1888. * Clears completion caches.
  1889. *
  1890. * If called with no parameters the entire complete cache will be cleared.
  1891. * If called with just the $type parameter the global cache for that type will
  1892. * be cleared (in the site context, if any). If called with both $type and
  1893. * $command parameters the command cache of that type will be cleared (in the
  1894. * site context, if any).
  1895. *
  1896. * This is included in drush.inc as complete.inc is only loaded conditionally.
  1897. *
  1898. * @param $type
  1899. * The completion type (optional).
  1900. * @param $command
  1901. * The command name (optional), if command specific cache is to be cleared.
  1902. * If specifying a command, $type is not optional.
  1903. */
  1904. function drush_complete_cache_clear($type = NULL, $command = NULL) {
  1905. require_once DRUSH_BASE_PATH . '/includes/complete.inc';
  1906. if ($type) {
  1907. drush_cache_clear_all(drush_complete_cache_cid($type, $command), 'complete');
  1908. return;
  1909. }
  1910. // No type or command, so clear the entire complete cache.
  1911. drush_cache_clear_all('*', 'complete', TRUE);
  1912. }