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_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_copy_dir Copy $src to $dest.
drush_db_delete A db_delete() that works for any version of Drupal.
drush_db_fetch_object A db_fetch_object() that works for any version of Drupal.
drush_db_result A db_result() that works consistently for any version of Drupal.
drush_db_select A db_select() that works for any version of Drupal.
drush_delete_dir Deletes the provided file or folder and everything inside it.
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_do_site_command Run a command on the site specified by the provided command record.
drush_drupal_cache_clear_all
drush_drupal_major_version Returns the Drupal major version number (5, 6, 7 ...)
drush_drupal_version Detects the version number of the current Drupal installation, if any. Returns FALSE if there is no current Drupal installation, or it is somehow broken.
drush_errors_off Turn PHP error handling off.
drush_errors_on Turn PHP error handling on.
drush_escapeshellarg Platform-independent version of escapeshellarg(). This only works for local commands. TODO: Make a unified drush_escapeshellarg that works on Linux and Windows.
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_not_empty Test to see if a file exists and is not empty
drush_find_tmp Returns the path to a temporary directory.
drush_format_help_section Format one named help section from a command record
drush_format_size
drush_get_engines Return a structured array of engines of a specific type from commandfiles implementing hook_drush_engine_$type.
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_help_section_default_formatter The default section formatter. Replaces '[command]' with the command name.
drush_help_section_formatter_options The options section formatter. Adds a "--" in front of each item label. Also handles short-form and example-value components in the help attributes.
drush_html_to_text Convert html to readable text. Compatible API to drupal_html_to_text, but less functional. Caller might prefer to call drupal_html_to_text if there is a bootstrapped Drupal site available.
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_is_windows Check if the operating system is Windows.
drush_json_decode Converts an HTML-safe JSON string into its PHP equivalent.
drush_json_encode Converts a PHP variable into its Javascript equivalent.
drush_key_value_to_array_table Convert an associative array of key : value pairs into a table suitable for processing by drush_print_table.
drush_log Add a log message to the log history.
drush_memory_limit Get the PHP memory_limit value in bytes.
drush_mkdir Cross-platform compatible helper function to recursively create a directory tree.
drush_move_dir Move $src to $dest.
drush_op Calls a given function, passing through all arguments unchanged.
drush_op_system Calls 'system()' function, passing through all arguments unchanged.
drush_os Determine the appropriate os value for the specified site record
drush_pipe_output Display the pipe output for the current request.
drush_preflight_backup_dir Decide where our backup directory should go
drush_prepare_backup_dir Prepare a backup directory
drush_print_file Print the contents of a file.
drush_print_help Print the help for a single command to the screen.
drush_print_pipe Stores a message which is printed during drush_shutdown() if in compact mode.
drush_print_r Prints an array or string.
drush_print_table Print a formatted table.
drush_print_timers
drush_prompt Prompt the user for input
drush_register_file_for_deletion Any file passed in to this function will be deleted when drush exits.
drush_remote_command Process commands that are executed on a remote drush instance.
drush_save_data_to_temp_file Save a string to a temporary file. Does not depend on Drupal's API. The temporary file will be automatically deleted when drush exits.
drush_set_error Set an error code for the error handling system.
drush_shell_cd_and_exec Executes a shell command at a new working directory. The old cwd is restored on exit.
drush_shell_exec Executes a shell command. Output is only printed if in verbose mode. Output is stored and can be retrieved using drush_shell_exec_output(). If in simulation mode, no action is taken.
drush_shell_exec_interactive Executes a command in interactive mode.
drush_shell_exec_output Returns the output of the most recent shell command as an array of lines.
drush_show_help Prints out help for a given command.
drush_table_column_autowidth Determine the best fit for column widths.
drush_tarball_extract Extract a tarball.
drush_tempdir Creates a temporary directory and return its path.
drush_tempnam Creates a temporary file, and registers it so that it will be deleted when drush exits. Whenever possible, drush_save_data_to_temp_file() should be used instead of this function.
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.
dt Rudimentary replacement for Drupal API t() function.
system_watchdog Log Drupal watchdog() calls.
_convert_csv_to_array Convert a csv string, or an array of items which may contain csv strings, into an array of items.
_drush_delete_registered_files Delete all of the registered temporary files.
_drush_download_file Download a file using wget, curl or file_get_contents. Does not use download cache.
_drush_escapeshellarg_windows Windows version of escapeshellarg().
_drush_format_help_subsection Format one named portion of a subsection from a command record. Subsections allow related parts of a help record to be grouped together. For example, in the 'options' section, sub-options that are related to a particular primary option are…
_drush_is_drush_shebang_line
_drush_is_drush_shebang_script
_drush_log_drupal_messages Turn drupal_set_message errors into drush_log errors
_drush_log_update_sql Log the return value of Drupal hook_update_n functions.
_drush_print_log Display the log message
_drush_recursive_copy Internal function called by drush_copy_dir; do not use directly.
_drush_replace_query_placeholders Replace named placeholders in a WHERE snippet.
_drush_shell_exec Internal function: executes a shell command on the local machine. This function should not be used in instances where ssh is utilized to execute a command remotely; otherwise, remote operations would fail if executed from a Windows machine to a…
_drush_shell_exec_output_set Stores output for the most recent shell command. This should only be run from drush_shell_exec().

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_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. * The number of bytes in a kilobyte. Copied from Drupal.
  8. */
  9. define('DRUSH_DRUPAL_KILOBYTE', 1024);
  10. /**
  11. * Include a file, selecting a version specific file if available.
  12. *
  13. * For example, if you pass the path "/var/drush" and the name
  14. * "update" when bootstrapped on a Drupal 6 site it will first check for
  15. * the presence of "/var/drush/update_6.inc" in include it if exists. If this
  16. * file does NOT exist it will proceed and check for "/var/drush/update.inc".
  17. * If neither file exists, it will return FALSE.
  18. *
  19. * @param $path
  20. * The path you want to search.
  21. * @param $name
  22. * The file base name you want to include (not including a version suffix
  23. * or extension).
  24. * @param $version
  25. * The version suffix you want to include (could be specific to the software
  26. * or platform your are connecting to) - defaults to the current Drupal core
  27. * major version.
  28. * @param $extension
  29. * The extension - defaults to ".inc".
  30. *
  31. * @return
  32. * TRUE if the file was found and included.
  33. */
  34. function drush_include($path, $name, $version = NULL, $extension = 'inc') {
  35. $version = ($version) ? $version : drush_drupal_major_version();
  36. $file = sprintf("%s/%s_%s.%s", $path, $name, $version, $extension);
  37. if (file_exists($file)) {
  38. // drush_log(dt('Including version specific file : @file', array('@file' => $file)));
  39. include_once($file);
  40. return TRUE;
  41. }
  42. $file = sprintf("%s/%s.%s", $path, $name, $extension);
  43. if (file_exists($file)) {
  44. // drush_log(dt('Including non-version specific file : @file', array('@file' => $file)));
  45. include_once($file);
  46. return TRUE;
  47. }
  48. }
  49. /**
  50. * Return a structured array of engines of a specific type from commandfiles
  51. * implementing hook_drush_engine_$type.
  52. *
  53. * Engines are pluggable subsystems. Each engine of a specific type will
  54. * implement the same set of API functions and perform the same high-level
  55. * task using a different backend or approach.
  56. *
  57. * This function/hook is useful when you have a selection of several mutually
  58. * exclusive options to present to a user to select from.
  59. *
  60. * Other commands are able to extend this list and provide their own engines.
  61. * The hook can return useful information to help users decide which engine
  62. * they need, such as description or list of available engine options.
  63. *
  64. * The engine path element will automatically default to a subdirectory (within
  65. * the directory of the commandfile that implemented the hook) with the name of
  66. * the type of engine - e.g. an engine "wget" of type "handler" provided by
  67. * the "pm" commandfile would automatically be found if the file
  68. * "pm/handler/wget.inc" exists and a specific path is not provided.
  69. *
  70. * @param $type
  71. * The type of engine.
  72. *
  73. * @return
  74. * A structured array of engines.
  75. */
  76. function drush_get_engines($type) {
  77. $engines = array();
  78. $list = drush_commandfile_list();
  79. foreach ($list as $commandfile => $path) {
  80. if (drush_command_hook($commandfile, 'drush_engine_' . $type)) {
  81. $function = $commandfile . '_drush_engine_' . $type;
  82. $result = $function();
  83. foreach ((array)$result as $key => $engine) {
  84. // Add some defaults
  85. $engine += array(
  86. 'commandfile' => $commandfile,
  87. // Engines by default live in a subdirectory of the commandfile that
  88. // declared them, named as per the type of engine they are.
  89. 'path' => sprintf("%s/%s", dirname($path), $type),
  90. );
  91. $engines[$key] = $engine;
  92. }
  93. }
  94. }
  95. return $engines;
  96. }
  97. /**
  98. * Include the engine code for a specific named engine of a certain type.
  99. *
  100. * If the engine type has implemented hook_drush_engine_$type the path to the
  101. * engine specified in the array will be used.
  102. *
  103. * If you don't need to present any user options for selecting the engine
  104. * (which is common if the selection is implied by the running environment)
  105. * and you don't need to allow other modules to define their own engines you can
  106. * simply pass the $path to the directory where the engines are, and the
  107. * appropriate one will be included.
  108. *
  109. * Unlike drush_include this function will set errors if the requested engine
  110. * cannot be found.
  111. *
  112. * @param $type
  113. * The type of engine.
  114. * @param $engine
  115. * The key for the engine to be included.
  116. * @param $version
  117. * The version of the engine to be included - defaults to the current Drupal core
  118. * major version.
  119. * @param $path
  120. * A path to include from, if the engine has no corresponding
  121. * hook_drush_engine_$type item path.
  122. * @return unknown_type
  123. */
  124. function drush_include_engine($type, $engine, $version = NULL, $path = NULL) {
  125. $engines = drush_get_engines($type);
  126. if (!$path && isset($engines[$engine])) {
  127. $path = $engines[$engine]['path'];
  128. }
  129. if (!$path) {
  130. 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)));
  131. }
  132. if (drush_include($path, $engine, $version)) {
  133. return TRUE;
  134. }
  135. 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)));
  136. }
  137. /**
  138. * Detects the version number of the current Drupal installation,
  139. * if any. Returns FALSE if there is no current Drupal installation,
  140. * or it is somehow broken.
  141. *
  142. * @return
  143. * A string containing the version number of the current
  144. * Drupal installation, if any. Otherwise, return FALSE.
  145. */
  146. function drush_drupal_version($drupal_root = NULL) {
  147. static $version = FALSE;
  148. if (!$version) {
  149. if (($drupal_root != NULL) || ($drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT'))) {
  150. // D7 stores VERSION in bootstrap.inc
  151. $version_constant_paths = array('/modules/system/system.module', '/includes/bootstrap.inc');
  152. foreach ($version_constant_paths as $path) {
  153. if (file_exists($drupal_root . $path)) {
  154. require_once $drupal_root . $path;
  155. }
  156. }
  157. // We just might be dealing with an early Drupal version (pre 4.7)
  158. if (defined('VERSION')) {
  159. $version = VERSION;
  160. }
  161. }
  162. }
  163. return $version;
  164. }
  165. /**
  166. * Check to see if a newer version of drush is available
  167. *
  168. * @return
  169. * TRUE - A new version is available.
  170. * FALSE - Error.
  171. * NULL - No release available.
  172. */
  173. function drush_check_self_update() {
  174. $error = "";
  175. // Don't check unless we have a datestamp in drush.info
  176. $drush_info = drush_read_drush_info();
  177. if (($drush_info === FALSE) || (!array_key_exists('datestamp', $drush_info))) {
  178. drush_log(dt('Cannot determine release date for drush'), 'notice');
  179. return FALSE;
  180. }
  181. // Allow updates to the latest HEAD release if --self-update=head is specified.
  182. // If we are called from `drush self-update`, then --dev will set --self-update=head.
  183. $dev_ok = (drush_get_option('self-update') == 'head');
  184. $is_dev = FALSE;
  185. // Get release info for drush
  186. $info = _drush_pm_get_releases(array('drush'));
  187. // Check for newer releases based on the datestamp.
  188. // We add 60 seconds to the drush.info date because of a drupal.org WTF. See http://drupal.org/node/1019356.
  189. $version_date = $drush_info['datestamp'] + 60;
  190. $newer_version = FALSE;
  191. foreach ($info['drush']['releases'] as $version => $release_info) {
  192. // We deliberately skip any dev releases unless the current release is a dev release.
  193. if ($dev_ok || ((!array_key_exists('version_extra', $release_info) || ($release_info['version_extra'] != 'dev')))) {
  194. if ($release_info['date'] > $version_date) {
  195. $newer_version = $release_info['version'];
  196. $version_date = $release_info['date'];
  197. $is_dev = isset($release_info['version_extra']) && $release_info['version_extra'] == 'dev';
  198. if ($is_dev) {
  199. $newer_version .= " (" . date('Y-M-d', $version_date) . ")";
  200. }
  201. }
  202. }
  203. }
  204. if ($newer_version) {
  205. drush_print(dt('A newer version of drush, !version, is available. You are currently running drush version !currentversion; to update, run `drush self-update`. To disable this check, put "$options[\'self-update\'] = FALSE;" in your drushrc.php configuration file.' . "\n", array('!version' => $newer_version, '!currentversion' => DRUSH_VERSION)));
  206. return TRUE;
  207. }
  208. else {
  209. drush_log(dt("drush self-update check: drush !version is up-to-date.", array('!version' => DRUSH_VERSION)), 'notice');
  210. }
  211. return NULL;
  212. }
  213. /**
  214. * Generate an .ini file. used by archive-dump."
  215. *
  216. * @param array $ini
  217. * A two dimensional associative array where top level are sections and
  218. * second level are key => value pairs.
  219. *
  220. * @return string
  221. * .ini formatted text.
  222. */
  223. function drush_export_ini($ini) {
  224. $output = '';
  225. foreach ($ini as $section => $pairs) {
  226. if ($section) {
  227. $output .= "[$section]\n";
  228. }
  229. foreach ($pairs as $k => $v) {
  230. if ($v) {
  231. $output .= "$k = \"$v\"\n";
  232. }
  233. }
  234. }
  235. return $output;
  236. }
  237. /**
  238. * Generate code friendly to the Drupal .info format from a structured array.
  239. * Mostly copied from http://drupalcode.org/viewvc/drupal/contributions/modules/features/features.export.inc.
  240. *
  241. * @param $info
  242. * An array or single value to put in a module's .info file.
  243. *
  244. * @param boolean $integer_keys
  245. * Use integer in keys.
  246. *
  247. * @param $parents
  248. * Array of parent keys (internal use only).
  249. *
  250. * @return
  251. * A code string ready to be written to a module's .info file.
  252. */
  253. function drush_export_info($info, $integer_keys = FALSE, $parents = array()) {
  254. $output = '';
  255. if (is_array($info)) {
  256. foreach ($info as $k => $v) {
  257. $child = $parents;
  258. $child[] = $k;
  259. $output .= drush_export_info($v, $integer_keys, $child);
  260. }
  261. }
  262. else if (!empty($info) && count($parents)) {
  263. $line = array_shift($parents);
  264. foreach ($parents as $key) {
  265. $line .= (!$integer_keys && is_numeric($key)) ? "[]" : "[{$key}]";
  266. }
  267. $line .= " = \"{$info}\"\n";
  268. return $line;
  269. }
  270. return $output;
  271. }
  272. function drush_drupal_cache_clear_all() {
  273. $prior = drush_get_context('DRUSH_AFFIRMATIVE');
  274. drush_set_context('DRUSH_AFFIRMATIVE', TRUE);
  275. drush_invoke('cache-clear', 'all');
  276. drush_set_context('DRUSH_AFFIRMATIVE', $prior);
  277. }
  278. /**
  279. * Returns the Drupal major version number (5, 6, 7 ...)
  280. */
  281. function drush_drupal_major_version($drupal_root = NULL) {
  282. $major_version = FALSE;
  283. if ($version = drush_drupal_version($drupal_root)) {
  284. $version_parts = explode('.', $version);
  285. if (is_numeric($version_parts[0])) {
  286. $major_version = (integer)$version_parts[0];
  287. }
  288. }
  289. return $major_version;
  290. }
  291. /**
  292. * Convert a csv string, or an array of items which
  293. * may contain csv strings, into an array of items.
  294. *
  295. * @param $args
  296. * A simple csv string; e.g. 'a,b,c'
  297. * or a simple list of items; e.g. array('a','b','c')
  298. * or some combination; e.g. array('a,b','c') or array('a,','b,','c,')
  299. *
  300. * @returns array
  301. * A simple list of items (e.g. array('a','b','c')
  302. */
  303. function _convert_csv_to_array($args) {
  304. //
  305. // Step 1: implode(',',$args) converts from, say, array('a,','b,','c,') to 'a,,b,,c,'
  306. // Step 2: explode(',', ...) converts to array('a','','b','','c','')
  307. // Step 3: array_filter(...) removes the empty items
  308. //
  309. return array_filter(explode(',', is_array($args) ? implode(',',$args) : $args));
  310. }
  311. /**
  312. * Get the available global options. Used by help command. Command files may
  313. * modify this list using hook_drush_help_alter().
  314. *
  315. * @param boolean $brief
  316. * Return a reduced set of important options. Used by help command.
  317. *
  318. * @return
  319. * An associative array containing the option definition as the key, and the description as the value,
  320. * for each of the available options.
  321. */
  322. function drush_get_global_options($brief = FALSE) {
  323. $options['root'] = array('short-form' => 'r', 'description' => dt("Drupal root directory to use (default: current directory)"), 'example-value' => '<path>');
  324. $options['uri'] = array('short-form' => 'l', 'description' => dt('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');
  325. $options['verbose'] = array('short-form' => 'v', 'description' => dt('Display extra information about the command.'));
  326. $options['debug'] = array('short-form' => 'd', 'description' => dt('Display even more information, including internal messages.'));
  327. $options['yes'] = array('short-form' => 'y', 'description' => dt("Assume 'yes' as answer to all prompts"));
  328. $options['no'] = array('short-form' => 'n', 'description' => dt("Assume 'no' as answer to all prompts"));
  329. $options['simulate'] = array('short-form' => 's', 'description' => dt("Simulate all relevant actions (don't actually change the system)"));
  330. $options['pipe'] = array('short-form' => 'p', 'description' => dt("Emit a compact representation of the command for scripting."));
  331. $options['help'] = array('short-form' => 'h', 'description' => dt("This help system."));
  332. $options['version'] = dt("Show drush version.");
  333. $options['php'] = dt("The absolute path to your PHP intepreter, if not 'php' in the path.");
  334. if (!$brief) {
  335. $options['quiet'] = array('short-form' => 'q', 'description' => dt('Hide all output'));
  336. $options['include'] = array('short-form' => 'i', 'description' => dt("A list of paths to search for drush commands"));
  337. $options['config'] = array('short-form' => 'c', 'description' => dt("Specify a config file to use. See example.drushrc.php"));
  338. $options['user'] = array('short-form' => 'u', 'description' => dt("Specify a user to login with. May be a name or a number."));
  339. $options['backend'] = array('short-form' => 'b', 'description' => dt("Hide all output and return structured data (internal use only)."));
  340. $options['choice'] = dt("Provide an answer to a multiple-choice prompt.");
  341. $options['no-label'] = dt("Remove the site label that drush includes in multi-site command output(e.g. `drush @site1,@site2 status`).");
  342. $options['nocolor'] = dt("Suppress color highlighting on log messages.");
  343. $options['show-passwords'] = dt("Show database passwords in commands that display connection information.");
  344. $options['show-invoke'] = dt("Show all function names which could have been called for the current command. See drush_invoke().");
  345. $options['watchdog'] = dt("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.");
  346. $options['confirm-rollback'] = array('description' => 'Wait for confirmation before doing a rollback when something goes wrong.');
  347. }
  348. return $options;
  349. }
  350. /**
  351. * Prints out help for a given command.
  352. */
  353. function drush_show_help($commandstring) {
  354. // First check and see if the command can already be found.
  355. $commands = drush_get_commands();
  356. if (!array_key_exists($commandstring, $commands)) {
  357. // If the command cannot be found, then bootstrap so that
  358. // additional commands will be brought in.
  359. // For speed, only bootstrap up to DRUSH_BOOTSTRAP_DRUPAL_SITE.
  360. drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_SITE);
  361. $commands = drush_get_commands();
  362. }
  363. if (array_key_exists($commandstring, $commands)) {
  364. $command = $commands[$commandstring];
  365. drush_print_help($command);
  366. return TRUE;
  367. }
  368. return drush_set_error('DRUSH_COMMAND_NOT_FOUND', dt('Invalid command !command.', array('!command' => $commandstring)));
  369. }
  370. /**
  371. * Print the help for a single command to the screen.
  372. *
  373. * @param array $command
  374. * A fully loaded $command array.
  375. */
  376. function drush_print_help($command) {
  377. // Merge in engine specific help.
  378. foreach ($command['engines'] as $type => $description) {
  379. $all_engines = drush_get_engines($type);
  380. foreach ($all_engines as $name => $engine) {
  381. $command = array_merge_recursive($command, $engine);
  382. }
  383. }
  384. if (!$help = drush_command_invoke_all('drush_help', 'drush:'. $command['command'])) {
  385. $help = array($command['description']);
  386. }
  387. // Give commandfiles an opportunity to add examples and options to the command.
  388. drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_SITE);
  389. drush_command_invoke_all_ref('drush_help_alter', $command);
  390. drush_print(wordwrap(implode("\n", $help), drush_get_context('DRUSH_COLUMNS', 80)));
  391. drush_print();
  392. foreach ($command['sections'] as $key => $value) {
  393. if (!empty($command[$key])) {
  394. drush_print(dt($value) . ':');
  395. $rows = drush_format_help_section($command, $key);
  396. drush_print_table($rows, FALSE, array(40));
  397. unset($rows);
  398. drush_print();
  399. }
  400. }
  401. // Append aliases if any.
  402. if ($command['aliases']) {
  403. drush_print(dt("Aliases: ") . implode(', ', $command['aliases']));
  404. }
  405. }
  406. /**
  407. * Format one named help section from a command record
  408. *
  409. * @param $command
  410. * A command record with help information
  411. * @param $section
  412. * The name of the section to format ('options', 'topic', etc.)
  413. * @returns array
  414. * Formatted rows, suitable for printing via drush_print_table.
  415. */
  416. function drush_format_help_section($command, $section) {
  417. $formatter = (function_exists('drush_help_section_formatter_' . $section)) ? 'drush_help_section_formatter_' . $section : 'drush_help_section_default_formatter';
  418. foreach ($command[$section] as $name => $help_attributes) {
  419. if (!is_array($help_attributes)) {
  420. $help_attributes = array('description' => $help_attributes);
  421. }
  422. $help_attributes['label'] = $name;
  423. call_user_func_array($formatter, array($command, &$help_attributes));
  424. $rows[] = array($help_attributes['label'], $help_attributes['description']);
  425. // Process the subsections too, if any
  426. if (!empty($command['sub-' . $section]) && array_key_exists($name, $command['sub-' . $section])) {
  427. $rows = array_merge($rows, _drush_format_help_subsection($command, $section, $name, $formatter));
  428. }
  429. }
  430. return $rows;
  431. }
  432. /**
  433. * Format one named portion of a subsection from a command record.
  434. * Subsections allow related parts of a help record to be grouped
  435. * together. For example, in the 'options' section, sub-options that
  436. * are related to a particular primary option are stored in a 'sub-options'
  437. * section whose name == the name of the primary option.
  438. *
  439. * @param $command
  440. * A command record with help information
  441. * @param $section
  442. * The name of the section to format ('options', 'topic', etc.)
  443. * @param $subsection
  444. * The name of the subsection (e.g. the name of the primary option)
  445. * @param $formatter
  446. * The name of a function to use to format the rows of the subsection
  447. * @param $prefix
  448. * Characters to prefix to the front of the label (for indentation)
  449. * @returns array
  450. * Formatted rows, suitable for printing via drush_print_table.
  451. */
  452. function _drush_format_help_subsection($command, $section, $subsection, $formatter, $prefix = ' ') {
  453. foreach ($command['sub-' . $section][$subsection] as $name => $help_attributes) {
  454. if (!is_array($help_attributes)) {
  455. $help_attributes = array('description' => $help_attributes);
  456. }
  457. $help_attributes['label'] = $name;
  458. call_user_func_array($formatter, array($command, &$help_attributes));
  459. $rows[] = array($prefix . $help_attributes['label'], $help_attributes['description']);
  460. // Process the subsections too, if any
  461. if (!empty($command['sub-' . $section]) && array_key_exists($name, $command['sub-' . $section])) {
  462. $rows = array_merge($rows, _drush_format_help_subsection($command, $section, $name, $formatter, $prefix . ' '));
  463. }
  464. }
  465. return $rows;
  466. }
  467. /**
  468. * The options section formatter. Adds a "--" in front of each
  469. * item label. Also handles short-form and example-value
  470. * components in the help attributes.
  471. */
  472. function drush_help_section_formatter_options($command, &$help_attributes) {
  473. if ($help_attributes['label'][0] == '-') {
  474. drush_log(dt("Option '%option' of command %command should instead be declared as '%fixed'", array('%option' => $help_attributes['label'], '%command' => $command['command'], '%fixed' => preg_replace('/^--*/', '', $help_attributes['label']))), 'debug');
  475. }
  476. else {
  477. $help_attributes['label'] = '--' . $help_attributes['label'];
  478. }
  479. if (array_key_exists('example-value', $help_attributes)) {
  480. $help_attributes['label'] .= '=' . $help_attributes['example-value'];
  481. if (array_key_exists('short-form', $help_attributes)) {
  482. $help_attributes['short-form'] .= ' ' . $help_attributes['example-value'];
  483. }
  484. }
  485. if (array_key_exists('short-form', $help_attributes)) {
  486. $help_attributes['label'] = '-' . $help_attributes['short-form'] . ', ' . $help_attributes['label'];
  487. }
  488. drush_help_section_default_formatter($command, $help_attributes);
  489. }
  490. /**
  491. * The default section formatter. Replaces '[command]' with the
  492. * command name.
  493. */
  494. function drush_help_section_default_formatter($command, &$help_attributes) {
  495. // '[command]' is a token representing the current command. @see pm_drush_engine_version_control().
  496. $help_attributes['label'] = str_replace('[command]', $command['command'], $help_attributes['label']);
  497. }
  498. /**
  499. * Exits with a message. In general, you should use drush_set_error() instead of
  500. * this function. That lets drush proceed with other tasks.
  501. * TODO: Exit with a correct status code.
  502. */
  503. function drush_die($msg = NULL, $status = NULL) {
  504. die($msg ? "drush: $msg\n" : '');
  505. }
  506. /*
  507. * Check to see if the provided line is a "#!/usr/bin/env drush"
  508. * "shebang" script line.
  509. */
  510. function _drush_is_drush_shebang_line($line) {
  511. return ((substr($line,0,2) == '#!') && (strstr($line, 'drush') !== FALSE));
  512. }
  513. /*
  514. * Check to see if the provided script file is a "#!/usr/bin/env drush"
  515. * "shebang" script line.
  516. */
  517. function _drush_is_drush_shebang_script($script_filename) {
  518. $result = FALSE;
  519. if (file_exists($script_filename)) {
  520. $fp = fopen($script_filename, "r");
  521. if ($fp !== FALSE) {
  522. $line = fgets($fp);
  523. $result = _drush_is_drush_shebang_line($line);
  524. fclose($fp);
  525. }
  526. }
  527. return $result;
  528. }
  529. /**
  530. * @defgroup outputfunctions Process output text.
  531. * @{
  532. /**
  533. * Prints a message with optional indentation. In general,
  534. * drush_log($message, 'ok') is often a better choice than this function.
  535. * That gets your confirmation message (for example) into the logs for this
  536. * drush request. Consider that drush requests may be executed remotely and
  537. * non interactively.
  538. *
  539. * @param $message
  540. * The message to print.
  541. * @param $indent
  542. * The indentation (space chars)
  543. * @param $handle
  544. * File handle to write to. NULL will write
  545. * to standard output, STDERR will write to the standard
  546. * error. See http://php.net/manual/en/features.commandline.io-streams.php
  547. */
  548. function drush_print($message = '', $indent = 0, $handle = NULL) {
  549. $msg = str_repeat(' ', $indent) . (string)$message . "\n";
  550. if (($charset = drush_get_option('output_charset')) && function_exists('iconv')) {
  551. $msg = iconv('UTF-8', $charset, $msg);
  552. }
  553. if (isset($handle)) {
  554. fwrite($handle, $msg);
  555. }
  556. else {
  557. print $msg;
  558. }
  559. }
  560. /**
  561. * Stores a message which is printed during drush_shutdown() if in compact mode.
  562. * @param $message
  563. * The message to print. If $message is an array,
  564. * then each element of the array is printed on a
  565. * separate line.
  566. */
  567. function drush_print_pipe($message = '') {
  568. $buffer = &drush_get_context('DRUSH_PIPE_BUFFER' , '');
  569. if (is_array($message)) {
  570. $message = implode("\n", $message) . "\n";
  571. }
  572. $buffer .= $message;
  573. }
  574. /**
  575. * Prints an array or string.
  576. * @param $array
  577. * The array to print.
  578. */
  579. function drush_print_r($array, $handle = NULL) {
  580. drush_print(print_r($array, TRUE), 0, $handle);
  581. }
  582. /**
  583. * Rudimentary replacement for Drupal API t() function.
  584. *
  585. * @param string
  586. * String to process, possibly with replacement item.
  587. * @param array
  588. * An associative array of replacement items.
  589. *
  590. * @return
  591. * The processed string.
  592. *
  593. * @see t()
  594. */
  595. function dt($string, $args = array()) {
  596. if (function_exists('t')) {
  597. return t($string, $args);
  598. }
  599. else {
  600. if (!empty($args)) {
  601. return strtr($string, $args);
  602. }
  603. else {
  604. return $string;
  605. }
  606. }
  607. }
  608. /**
  609. * Convert html to readable text. Compatible API to
  610. * drupal_html_to_text, but less functional. Caller
  611. * might prefer to call drupal_html_to_text if there
  612. * is a bootstrapped Drupal site available.
  613. *
  614. * @param string $html
  615. * The html text to convert.
  616. *
  617. * @return string
  618. * The plain-text representation of the input.
  619. */
  620. function drush_html_to_text($html, $allowed_tags = NULL) {
  621. $replacements = array(
  622. '<hr>' => '------------------------------------------------------------------------------',
  623. '<li>' => ' * ',
  624. '<h1>' => '===== ',
  625. '</h1>' => ' =====',
  626. '<h2>' => '---- ',
  627. '</h2>' => ' ----',
  628. '<h3>' => '::: ',
  629. '</h3>' => ' :::',
  630. '<br/>' => "\n",
  631. );
  632. $text = str_replace(array_keys($replacements), array_values($replacements), $html);
  633. return html_entity_decode(preg_replace('/ *<[^>]*> */', ' ', $text));
  634. }
  635. /**
  636. * Print a formatted table.
  637. *
  638. * @param $rows
  639. * The rows to print.
  640. * @param $header
  641. * If TRUE, the first line will be treated as table header.
  642. * @param $widths
  643. * The widths of each column (in characters) to use - if not specified this
  644. * will be determined automatically, based on a "best fit" algorithm.
  645. * @param $handle
  646. * File handle to write to. NULL will write
  647. * to standard output, STDERR will write to the standard
  648. * error. See http://php.net/manual/en/features.commandline.io-streams.php
  649. * @return $tbl
  650. * Use $tbl->getTable() to get the output from the return value.
  651. */
  652. function drush_print_table($rows, $header = FALSE, $widths = array(), $handle = NULL) {
  653. $tbl = new Console_Table(CONSOLE_TABLE_ALIGN_LEFT , '');
  654. $auto_widths = drush_table_column_autowidth($rows, $widths);
  655. // Do wordwrap on all cells.
  656. $newrows = array();
  657. foreach ($rows as $rowkey => $row) {
  658. foreach ($row as $col_num => $cell) {
  659. $newrows[$rowkey][$col_num] = wordwrap($cell, $auto_widths[$col_num], "\n", TRUE);
  660. if (isset($widths[$col_num])) {
  661. $newrows[$rowkey][$col_num] = str_pad($newrows[$rowkey][$col_num], $widths[$col_num]);
  662. }
  663. }
  664. }
  665. if ($header) {
  666. $headers = array_shift($newrows);
  667. $tbl->setHeaders($headers);
  668. }
  669. $tbl->addData($newrows);
  670. $output = $tbl->getTable();
  671. if (!stristr(PHP_OS, 'WIN')) {
  672. $output = str_replace("\r\n", PHP_EOL, $output);
  673. }
  674. // Check if the handle argument is a string to preserve compatability with
  675. // previous versions that accepted a filename instead.
  676. if (is_string($handle)) {
  677. file_put_contents($handle, $output, FILE_APPEND);
  678. }
  679. else {
  680. drush_print($output, 0, $handle);
  681. }
  682. return $tbl;
  683. }
  684. /**
  685. * Convert an associative array of key : value pairs into
  686. * a table suitable for processing by drush_print_table.
  687. *
  688. * @param $keyvalue_table
  689. * An associative array of key : value pairs.
  690. * @return
  691. * An array of arrays, where the keys from the input
  692. * array are stored in the first column, and the values
  693. * are stored in the third. A second colum is created
  694. * specifically to hold the ':' separator.
  695. */
  696. function drush_key_value_to_array_table($keyvalue_table) {
  697. $table = array();
  698. foreach ($keyvalue_table as $key => $value) {
  699. if (isset($value)) {
  700. $table[] = array($key, ' :', $value);
  701. }
  702. else {
  703. $table[] = array($key . ':', '', '');
  704. }
  705. }
  706. return $table;
  707. }
  708. /**
  709. * Determine the best fit for column widths.
  710. *
  711. * @param $rows
  712. * The rows to use for calculations.
  713. * @param $widths
  714. * Manually specified widths of each column (in characters) - these will be
  715. * left as is.
  716. */
  717. function drush_table_column_autowidth($rows, $widths) {
  718. $auto_widths = $widths;
  719. // First we determine the distribution of row lengths in each column.
  720. // This is an array of descending character length keys (i.e. starting at
  721. // the rightmost character column), with the value indicating the number
  722. // of rows where that character column is present.
  723. $col_dist = array();
  724. foreach ($rows as $rowkey => $row) {
  725. foreach ($row as $col_num => $cell) {
  726. if (empty($widths[$col_num])) {
  727. $length = strlen($cell);
  728. while ($length > 0) {
  729. if (!isset($col_dist[$col_num][$length])) {
  730. $col_dist[$col_num][$length] = 0;
  731. }
  732. $col_dist[$col_num][$length]++;
  733. $length--;
  734. }
  735. }
  736. }
  737. }
  738. foreach ($col_dist as $col_num => $count) {
  739. // Sort the distribution in decending key order.
  740. krsort($col_dist[$col_num]);
  741. // Initially we set all columns to their "ideal" longest width
  742. // - i.e. the width of their longest column.
  743. $auto_widths[$col_num] = max(array_keys($col_dist[$col_num]));
  744. }
  745. // We determine what width we have available to use, and what width the
  746. // above "ideal" columns take up.
  747. $available_width = drush_get_context('DRUSH_COLUMNS', 80) - (count($auto_widths) * 2);
  748. $auto_width_current = array_sum($auto_widths);
  749. // If we need to reduce a column so that we can fit the space we use this
  750. // loop to figure out which column will cause the "least wrapping",
  751. // (relative to the other columns) and reduce the width of that column.
  752. while ($auto_width_current > $available_width) {
  753. $count = 0;
  754. $width = 0;
  755. foreach ($col_dist as $col_num => $counts) {
  756. // If we are just starting out, select the first column.
  757. if ($count == 0 ||
  758. // OR: if this column would cause less wrapping than the currently
  759. // selected column, then select it.
  760. (current($counts) < $count) ||
  761. // OR: if this column would cause the same amount of wrapping, but is
  762. // longer, then we choose to wrap the longer column (proportionally
  763. // less wrapping, and helps avoid triple line wraps).
  764. (current($counts) == $count && key($counts) > $width)) {
  765. // Select the column number, and record the count and current width
  766. // for later comparisons.
  767. $column = $col_num;
  768. $count = current($counts);
  769. $width = key($counts);
  770. }
  771. }
  772. if ($width <= 1) {
  773. // If we have reached a width of 1 then give up, so wordwrap can still progress.
  774. break;
  775. }
  776. // Reduce the width of the selected column.
  777. $auto_widths[$column]--;
  778. // Reduce our overall table width counter.
  779. $auto_width_current--;
  780. // Remove the corresponding data from the disctribution, so next time
  781. // around we use the data for the row to the left.
  782. unset($col_dist[$column][$width]);
  783. }
  784. return $auto_widths;
  785. }
  786. /**
  787. * Print the contents of a file.
  788. *
  789. * @param string $file
  790. * Full path to a file.
  791. */
  792. function drush_print_file($file) {
  793. // Don't even bother to print the file in --no mode
  794. if (drush_get_context('DRUSH_NEGATIVE')) {
  795. return;
  796. }
  797. if ((substr($file,-4) == ".htm") || (substr($file,-5) == ".html")) {
  798. $tmp_file = drush_tempnam(basename($file));
  799. file_put_contents($tmp_file, drush_html_to_text(file_get_contents($file)));
  800. $file = $tmp_file;
  801. }
  802. // Do not wait for user input in --yes or --pipe modes
  803. if (drush_get_context('DRUSH_PIPE')) {
  804. drush_print_pipe(file_get_contents($file));
  805. }
  806. elseif (drush_get_context('DRUSH_AFFIRMATIVE')) {
  807. drush_print(file_get_contents($file));
  808. }
  809. elseif (drush_shell_exec_interactive("less %s", $file)) {
  810. return;
  811. }
  812. elseif (drush_shell_exec_interactive("more %s", $file)) {
  813. return;
  814. }
  815. else {
  816. drush_print(file_get_contents($file));
  817. }
  818. }
  819. /**
  820. * Converts a PHP variable into its Javascript equivalent.
  821. *
  822. * We provide a copy of D7's drupal_json_encode since this function is
  823. * unavailable on earlier versions of Drupal.
  824. *
  825. * @see drupal_json_decode()
  826. * @ingroup php_wrappers
  827. */
  828. function drush_json_encode($var) {
  829. // json_encode() does not escape <, > and &, so we do it with str_replace().
  830. return str_replace(array('<', '>', '&'), array('\u003c', '\u003e', '\u0026'), json_encode($var));
  831. }
  832. /**
  833. * Converts an HTML-safe JSON string into its PHP equivalent.
  834. *
  835. * We provide a copy of D7's drupal_json_decode since this function is
  836. * unavailable on earlier versions of Drupal.
  837. *
  838. * @see drupal_json_encode()
  839. * @ingroup php_wrappers
  840. */
  841. function drush_json_decode($var) {
  842. return json_decode($var, TRUE);
  843. }
  844. /**
  845. * @} End of "defgroup outputfunctions".
  846. */
  847. /**
  848. * @defgroup userinput Get input from the user.
  849. * @{
  850. /**
  851. * Ask the user a basic yes/no question.
  852. *
  853. * @param $msg The question to ask
  854. * @return TRUE if the user entered 'y', FALSE if he entered 'n'
  855. */
  856. function drush_confirm($msg, $indent = 0) {
  857. print str_repeat(' ', $indent) . (string)$msg . " (y/n): ";
  858. // Automatically accept confirmations if the --yes argument was supplied.
  859. if (drush_get_context('DRUSH_AFFIRMATIVE')) {
  860. print "y\n";
  861. return TRUE;
  862. }
  863. // Automatically cancel confirmations if the --no argument was supplied.
  864. elseif (drush_get_context('DRUSH_NEGATIVE')) {
  865. print "n\n";
  866. return FALSE;
  867. }
  868. // See http://drupal.org/node/499758 before changing this.
  869. $stdin = fopen("php://stdin","r");
  870. while ($line = fgets($stdin)) {
  871. $line = trim($line);
  872. if ($line == 'y') {
  873. return TRUE;
  874. }
  875. if ($line == 'n') {
  876. return FALSE;
  877. }
  878. print str_repeat(' ', $indent) . (string)$msg . " (y/n): ";
  879. }
  880. }
  881. /**
  882. * Ask the user to select an item from a list.
  883. * From a provided associative array, drush_choice will
  884. * display all of the questions, numbered from 1 to N,
  885. * and return the item the user selected. "0" is always
  886. * cancel; entering a blank line is also interpreted
  887. * as cancelling.
  888. *
  889. * @param $options
  890. * A list of questions to display to the user. The
  891. * KEYS of the array are the result codes to return to the
  892. * caller; the VALUES are the messages to display on
  893. * each line. Special keys of the form '-- something --' can be
  894. * provided as separator between choices groups. Separator keys
  895. * don't alter the numbering.
  896. * @param $prompt
  897. * The message to display to the user prompting for input.
  898. * @param $label
  899. * Controls the display of each line. Defaults to
  900. * '!value', which displays the value of each item
  901. * in the $options array to the user. Use '!key' to
  902. * display the key instead. In some instances, it may
  903. * be useful to display both the key and the value; for
  904. * example, if the key is a user id and the value is the
  905. * user name, use '!value (uid=!key)'.
  906. */
  907. function drush_choice($options, $prompt = 'Enter a number.', $label = '!value') {
  908. print dt($prompt) . "\n";
  909. // Preflight so that all rows will be padded out to the same number of columns
  910. $array_pad = 0;
  911. foreach ($options as $key => $option) {
  912. if (is_array($option) && (count($option) > $array_pad)) {
  913. $array_pad = count($option);
  914. }
  915. }
  916. $rows[] = array_pad(array('[0]', ':', 'Cancel'), $array_pad + 2, '');
  917. $selection_number = 0;
  918. foreach ($options as $key => $option) {
  919. if ((substr($key, 0, 3) == '-- ') && (substr($key, -3) == ' --')) {
  920. $rows[] = array_pad(array('', '', $option), $array_pad + 2, '');
  921. }
  922. else {
  923. $selection_number++;
  924. $row = array("[$selection_number]", ':');
  925. if (is_array($option)) {
  926. $row = array_merge($row, $option);
  927. }
  928. else {
  929. $row[] = dt($label, array('!number' => $selection_number, '!key' => $key, '!value' => $option));
  930. }
  931. $rows[] = $row;
  932. $selection_list[$selection_number] = $key;
  933. }
  934. }
  935. drush_print_table($rows);
  936. drush_print_pipe(array_keys($options));
  937. // If the user specified --choice, then make an
  938. // automatic selection. Cancel if the choice is
  939. // not an available option.
  940. if (($choice = drush_get_option('choice', FALSE)) !== FALSE) {
  941. // First check to see if $choice is one of the symbolic options
  942. if (array_key_exists($choice, $options)) {
  943. return $choice;
  944. }
  945. // Next handle numeric selections
  946. elseif (array_key_exists($choice, $selection_list)) {
  947. return $selection_list[$choice];
  948. }
  949. return FALSE;
  950. }
  951. // If the user specified --no, then cancel; also avoid
  952. // getting hung up waiting for user input in --pipe and
  953. // backend modes. If none of these apply, then wait,
  954. // for user input and return the selected result.
  955. if (!drush_get_context('DRUSH_NEGATIVE') && !drush_get_context('DRUSH_AFFIRMATIVE') && !drush_get_context('DRUSH_PIPE')) {
  956. while ($line = trim(fgets(STDIN))) {
  957. if (array_key_exists($line, $selection_list)) {
  958. return $selection_list[$line];
  959. }
  960. }
  961. }
  962. // We will allow --yes to confirm input if there is only
  963. // one choice; otherwise, --yes will cancel to avoid ambiguity
  964. if (drush_get_context('DRUSH_AFFIRMATIVE') && (count($options) == 1)) {
  965. return $selection_list[1];
  966. }
  967. drush_print(dt('Cancelled'));
  968. return FALSE;
  969. }
  970. /**
  971. * Ask the user to select multiple items from a list.
  972. * This is a wrapper around drush_choice, that repeats the selection process,
  973. * allowing users to toggle a number of items in a list. The number of values
  974. * that can be constrained by both min and max: the user will only be allowed
  975. * finalize selection once the minimum number has been selected, and the oldest
  976. * selected value will "drop off" the list, if they exceed the maximum number.
  977. *
  978. * @param $options
  979. * Same as drush_choice() (see above).
  980. * @param $defaults
  981. * This can take 3 forms:
  982. * - FALSE: (Default) All options are unselected by default.
  983. * - TRUE: All options are selected by default.
  984. * - Array of $options keys to be selected by default.
  985. * @param $prompt
  986. * Same as drush_choice() (see above).
  987. * @param $label
  988. * Same as drush_choice() (see above).
  989. * @param $mark
  990. * Controls how selected values are marked. Defaults to '!value (selected)'.
  991. * @param $min
  992. * Constraint on minimum number of selections. Defaults to zero. When fewer
  993. * options than this are selected, no final options will be available.
  994. * @param $max
  995. * Constraint on minimum number of selections. Defaults to NULL (unlimited).
  996. * If the a new selection causes this value to be exceeded, the oldest
  997. * previously selected value is automatically unselected.
  998. * @param $final_options
  999. * An array of additional options in the same format as $options.
  1000. * When the minimum number of selections is met, this array is merged into the
  1001. * array of options. If the user selects one of these values and the
  1002. * selection process will complete (the key for the final option is included
  1003. * in the return value). If this is an empty array (default), then a built in
  1004. * final option of "Done" will be added to the available options (in this case
  1005. * no additional keys are added to the return value).
  1006. */
  1007. function drush_choice_multiple($options, $defaults = FALSE, $prompt = 'Select some numbers.', $label = '!value', $mark = '!value (selected)', $min = 0, $max = NULL, $final_options = array()) {
  1008. $selections = array();
  1009. // Load default selections.
  1010. if (is_array($defaults)) {
  1011. $selections = $defaults;
  1012. }
  1013. elseif ($defaults === TRUE) {
  1014. $selections = array_keys($options);
  1015. }
  1016. $complete = FALSE;
  1017. $final_builtin = array();
  1018. if (empty($final_options)) {
  1019. $final_builtin['done'] = dt('Done');
  1020. }
  1021. $final_options_keys = array_keys($final_options);
  1022. while (TRUE) {
  1023. $current_options = $options;
  1024. // Mark selections.
  1025. foreach ($selections as $selection) {
  1026. $current_options[$selection] = dt($mark, array('!key' => $selection, '!value' => $options[$selection]));
  1027. }
  1028. // Add final options, if the minimum number of selections has been reached.
  1029. if (count($selections) >= $min) {
  1030. $current_options = array_merge($current_options, $final_options, $final_builtin);
  1031. }
  1032. $toggle = drush_choice($current_options, $prompt, $label);
  1033. if ($toggle === FALSE) {
  1034. return FALSE;
  1035. }
  1036. // Don't include the built in final option in the return value.
  1037. if (count($selections) >= $min && empty($final_options) && $toggle == 'done') {
  1038. return $selections;
  1039. }
  1040. // Toggle the selected value.
  1041. $item = array_search($toggle, $selections);
  1042. if ($item === FALSE) {
  1043. array_unshift($selections, $toggle);
  1044. }
  1045. else {
  1046. unset($selections[$item]);
  1047. }
  1048. // If the user selected one of the final options, return.
  1049. if (count($selections) >= $min && in_array($toggle, $final_options_keys)) {
  1050. return $selections;
  1051. }
  1052. // If the user selected too many options, drop the oldest selection.
  1053. if (isset($max) && count($selections) > $max) {
  1054. array_pop($selections);
  1055. }
  1056. }
  1057. }
  1058. /**
  1059. * Prompt the user for input
  1060. *
  1061. * The input can be anything that fits on a single line (not only y/n),
  1062. * so we can't use drush_confirm()
  1063. *
  1064. * @param $prompt
  1065. * The text which is displayed to the user.
  1066. * @param $default
  1067. * The default value of the input.
  1068. * @param $required
  1069. * If TRUE, user may continue even when no value is in the input.
  1070. *
  1071. * @see drush_confirm()
  1072. */
  1073. function drush_prompt($prompt, $default = NULL, $required = TRUE) {
  1074. if (!is_null($default)) {
  1075. $prompt .= " [" . $default . "]";
  1076. }
  1077. $prompt .= ": ";
  1078. print $prompt;
  1079. if (drush_get_context('DRUSH_AFFIRMATIVE')) {
  1080. return $default;
  1081. }
  1082. $stdin = fopen('php://stdin', 'r');
  1083. stream_set_blocking($stdin, TRUE);
  1084. while (($line = fgets($stdin)) !== FALSE) {
  1085. $line = trim($line);
  1086. if ($line === "") {
  1087. $line = $default;
  1088. }
  1089. if ($line || !$required) {
  1090. break;
  1091. }
  1092. print $prompt;
  1093. }
  1094. fclose($stdin);
  1095. return $line;
  1096. }
  1097. /**
  1098. * @} End of "defgroup userinput".
  1099. */
  1100. /**
  1101. * @defgroup commandwrappers Functions to execute commands.
  1102. * @{
  1103. */
  1104. /**
  1105. * Calls a given function, passing through all arguments unchanged.
  1106. *
  1107. * This should be used when calling possibly mutative or destructive functions
  1108. * (e.g. unlink() and other file system functions) so that can be suppressed
  1109. * if the simulation mode is enabled.
  1110. *
  1111. * Important: Call @see drush_op_system() to execute a shell command,
  1112. * or @see drush_shell_exec() to execute a shell command and capture the
  1113. * shell output.
  1114. *
  1115. * @param $function
  1116. * The name of the function. Any additional arguments are passed along.
  1117. * @return
  1118. * The return value of the function, or TRUE if simulation mode is enabled.
  1119. *
  1120. */
  1121. function drush_op($function) {
  1122. $args = func_get_args();
  1123. array_shift($args); // Skip function name
  1124. foreach ($args as $arg) {
  1125. $args_printed[] = is_scalar($arg) ? $arg : (is_array($arg) ? 'Array' : 'Object');
  1126. }
  1127. // Special checking for drush_op('system')
  1128. if ($function == 'system') {
  1129. drush_log(dt("Do not call drush_op('system'); use drush_op_system instead"), 'debug');
  1130. }
  1131. if (drush_get_context('DRUSH_VERBOSE') || drush_get_context('DRUSH_SIMULATE')) {
  1132. drush_log(sprintf("Calling %s(%s)", $function, implode(", ", $args_printed)), 'debug');
  1133. }
  1134. if (drush_get_context('DRUSH_SIMULATE')) {
  1135. return TRUE;
  1136. }
  1137. return call_user_func_array($function, $args);
  1138. }
  1139. /**
  1140. * Calls 'system()' function, passing through all arguments unchanged.
  1141. *
  1142. * This should be used when calling possibly mutative or destructive functions
  1143. * (e.g. unlink() and other file system functions) so that can be suppressed
  1144. * if the simulation mode is enabled.
  1145. *
  1146. * @param $exec
  1147. * The shell command to execute. Parameters should already be escaped.
  1148. * @return
  1149. * The result code from system(): 0 == success.
  1150. *
  1151. * @see drush_shell_exec()
  1152. */
  1153. function drush_op_system($exec) {
  1154. if (drush_get_context('DRUSH_VERBOSE') || drush_get_context('DRUSH_SIMULATE')) {
  1155. drush_print("Calling system($exec);");
  1156. }
  1157. if (drush_get_context('DRUSH_SIMULATE')) {
  1158. return 0;
  1159. }
  1160. // Throw away output. Use drush_shell_exec() to capture output.
  1161. system($exec, $result_code);
  1162. return $result_code;
  1163. }
  1164. /**
  1165. * Executes a shell command at a new working directory.
  1166. * The old cwd is restored on exit.
  1167. *
  1168. * @param $effective_wd
  1169. * The new working directory to execute the shell command at.
  1170. * @param $cmd
  1171. * The command to execute. May include placeholders used for sprintf.
  1172. * @param ...
  1173. * Values for the placeholders specified in $cmd. Each of these will be passed through escapeshellarg() to ensure they are safe to use on the command line.
  1174. * @return
  1175. * TRUE on success, FALSE on failure
  1176. */
  1177. function drush_shell_cd_and_exec($effective_wd, $cmd) {
  1178. $args = func_get_args();
  1179. $effective_wd = array_shift($args);
  1180. $cwd = getcwd();
  1181. drush_op('chdir', $effective_wd);
  1182. $result = call_user_func_array('drush_shell_exec', $args);
  1183. drush_op('chdir', $cwd);
  1184. return $result;
  1185. }
  1186. /**
  1187. * Executes a shell command.
  1188. * Output is only printed if in verbose mode.
  1189. * Output is stored and can be retrieved using drush_shell_exec_output().
  1190. * If in simulation mode, no action is taken.
  1191. *
  1192. * @param $cmd
  1193. * The command to execute. May include placeholders used for sprintf.
  1194. * @param ...
  1195. * Values for the placeholders specified in $cmd. Each of these will be passed through escapeshellarg() to ensure they are safe to use on the command line.
  1196. * @return
  1197. * TRUE on success, FALSE on failure
  1198. */
  1199. function drush_shell_exec($cmd) {
  1200. return _drush_shell_exec(func_get_args());
  1201. }
  1202. /**
  1203. * Executes a command in interactive mode.
  1204. *
  1205. * @see drush_shell_exec.
  1206. */
  1207. function drush_shell_exec_interactive($cmd) {
  1208. return _drush_shell_exec(func_get_args(), TRUE);
  1209. }
  1210. /**
  1211. * Internal function: executes a shell command on the
  1212. * local machine. This function should not be used
  1213. * in instances where ssh is utilized to execute a
  1214. * command remotely; otherwise, remote operations would
  1215. * fail if executed from a Windows machine to a remote
  1216. * Linux server.
  1217. *
  1218. * @param $args
  1219. * The command and its arguments.
  1220. * @param $interactive
  1221. * Whether to run in
  1222. *
  1223. * @return
  1224. * TRUE on success, FALSE on failure
  1225. *
  1226. * @see drush_shell_exec.
  1227. */
  1228. function _drush_shell_exec($args, $interactive = FALSE) {
  1229. //do not change the command itself, just the parameters.
  1230. for ($x = 1; $x < sizeof($args); $x++) {
  1231. $args[$x] = drush_escapeshellarg($args[$x]);
  1232. }
  1233. $command = call_user_func_array('sprintf', $args);
  1234. if (drush_get_context('DRUSH_VERBOSE') || drush_get_context('DRUSH_SIMULATE')) {
  1235. drush_print('Executing: ' . $command);
  1236. }
  1237. if (!drush_get_context('DRUSH_SIMULATE')) {
  1238. if ($interactive) {
  1239. $result = proc_open($command, array(0 => STDIN, 1 => STDOUT, 2 => STDERR), $pipes);
  1240. proc_close($result);
  1241. // proc_open returns FALSE on failure, or a resource on success.
  1242. return ($result === FALSE) ? FALSE : TRUE;
  1243. }
  1244. else {
  1245. exec($command . ' 2>&1', $output, $result);
  1246. _drush_shell_exec_output_set($output);
  1247. if (drush_get_context('DRUSH_DEBUG')) {
  1248. foreach ($output as $line) {
  1249. drush_print($line, 2);
  1250. }
  1251. }
  1252. // Exit code 0 means success.
  1253. return ($result == 0);
  1254. }
  1255. }
  1256. else {
  1257. return TRUE;
  1258. }
  1259. }
  1260. /**
  1261. * Determine the appropriate os value for the
  1262. * specified site record
  1263. *
  1264. * @returns
  1265. * NULL for 'same as local machine', 'Windows' or 'Linux'.
  1266. */
  1267. function drush_os($site_record = NULL) {
  1268. // Default to $os = NULL, meaning 'same as local machine'
  1269. $os = NULL;
  1270. // If the site record has an 'os' element, use it
  1271. if (isset($site_record) && array_key_exists('os', $site_record)) {
  1272. $os = $site_record['os'];
  1273. }
  1274. // Otherwise, we will assume that all remote machines are Linux
  1275. // (or whatever value 'remote-os' is set to in drushrc.php).
  1276. elseif (isset($site_record) && array_key_exists('remote-host', $site_record) && !empty($site_record['remote-host'])) {
  1277. $os = drush_get_option('remote-os', 'Linux');
  1278. }
  1279. return $os;
  1280. }
  1281. /**
  1282. * Platform-independent version of escapeshellarg().
  1283. * This only works for local commands.
  1284. * TODO: Make a unified drush_escapeshellarg
  1285. * that works on Linux and Windows.
  1286. */
  1287. function drush_escapeshellarg($arg) {
  1288. if (drush_is_windows()) {
  1289. return _drush_escapeshellarg_windows($arg);
  1290. }
  1291. else {
  1292. return escapeshellarg($arg);
  1293. }
  1294. }
  1295. /**
  1296. * Check if the operating system is Windows.
  1297. */
  1298. function drush_is_windows() {
  1299. if (substr(php_uname(), 0, 7) == 'Windows') {
  1300. return TRUE;
  1301. }
  1302. else {
  1303. return FALSE;
  1304. }
  1305. }
  1306. /**
  1307. * Windows version of escapeshellarg().
  1308. *
  1309. * @deprecated escapeshellarg needs to be cross-platform,
  1310. * because drush does not always know in advance whether an
  1311. * escaped arg will be used locally or on a remote system.
  1312. * See http://drupal.org/node/766080
  1313. */
  1314. function _drush_escapeshellarg_windows($arg) {
  1315. // Double the backslashes before any double quotes. Escape the double quotes.
  1316. // (\" => \\\") && (" => \") =
  1317. // (\" => \\") +
  1318. $arg = preg_replace('/\\\"/', '\\\\\\"', $arg);
  1319. // + (" => \")
  1320. $arg = preg_replace('/"/', '\\"', $arg);
  1321. // The same with single quotes.
  1322. // (\' => \\\') && (' => \') =
  1323. // (\' => \\') +
  1324. $arg = preg_replace('/\\\'/', '\\\\\\\'', $arg);
  1325. // + (' => \')
  1326. $arg = preg_replace('/\'/', '\\\'', $arg);
  1327. // Replace "\t", "\n", "\r", "\0", "\x0B" with a whitespace.
  1328. $arg = str_replace(array("\t", "\n", "\r", "\0", "\x0B"), ' ', $arg);
  1329. // Add surrounding quotes.
  1330. $arg = '"' . $arg . '"';
  1331. return $arg;
  1332. }
  1333. /**
  1334. * Stores output for the most recent shell command.
  1335. * This should only be run from drush_shell_exec().
  1336. *
  1337. * @param $output
  1338. * The output of the most recent shell command.
  1339. * If this is not set the stored value will be returned.
  1340. */
  1341. function _drush_shell_exec_output_set($output = FALSE) {
  1342. static $stored_output;
  1343. if ($output === FALSE) return $stored_output;
  1344. $stored_output = $output;
  1345. }
  1346. /**
  1347. * Returns the output of the most recent shell command as an array of lines.
  1348. */
  1349. function drush_shell_exec_output() {
  1350. return _drush_shell_exec_output_set();
  1351. }
  1352. /**
  1353. * Download a file using wget, curl or file_get_contents, or via download cache.
  1354. *
  1355. * @param string $url
  1356. * The url of the file to download.
  1357. * @param string $destination
  1358. * The name of the file to be saved, which may include the full path.
  1359. * Optional, if omitted the filename will be extracted from the url and the
  1360. * file downloaded to the current working directory (Drupal root if
  1361. * bootstrapped).
  1362. * @param integer $cache_duration
  1363. * The acceptable age of a cached file. If cached file is too old, a fetch
  1364. * will occur and cache will be updated. Optional, if ommitted the file will
  1365. * be fetched directly.
  1366. *
  1367. * @return string
  1368. * The path to the downloaded file, or FALSE if the file could not be
  1369. * downloaded.
  1370. */
  1371. function drush_download_file($url, $destination = FALSE, $cache_duration = 0) {
  1372. if (drush_get_option('cache') && $cache_duration !== 0 && $cache_dir = drush_directory_cache() . '/download') {
  1373. drush_mkdir($cache_dir);
  1374. $cache_name = str_replace(array(':', '/'), '-', $url);
  1375. $cache_file = $cache_dir . "/" . $cache_name;
  1376. // Check for cached, unexpired file.
  1377. if (file_exists($cache_file) && filectime($cache_file) > ($_SERVER['REQUEST_TIME']-$cache_duration)) {
  1378. drush_log(dt('!name retrieved from cache.', array('!name' => $cache_name)));
  1379. return $cache_file;
  1380. }
  1381. else {
  1382. if (_drush_download_file($url, $cache_file, TRUE)) {
  1383. // Cache was set just by downloading file to right location.
  1384. return $cache_file;
  1385. }
  1386. elseif (file_exists($cache_file)) {
  1387. drush_log(dt('!name retrieved from an expired cache since refresh failed.', array('!name' => $cache_name)), 'warning');
  1388. return $cache_file;
  1389. }
  1390. }
  1391. }
  1392. elseif ($return = _drush_download_file($url, $destination)) {
  1393. drush_register_file_for_deletion($return);
  1394. return $return;
  1395. }
  1396. // Unable to retrieve from cache nor download.
  1397. return FALSE;
  1398. }
  1399. /**
  1400. * Download a file using wget, curl or file_get_contents. Does not use download
  1401. * cache.
  1402. *
  1403. * @param string $url
  1404. * The url of the file to download.
  1405. * @param string $destination
  1406. * The name of the file to be saved, which may include the full path.
  1407. * Optional, if omitted the filename will be extracted from the url and the
  1408. * file downloaded to the current working directory (Drupal root if
  1409. * bootstrapped).
  1410. * @param boolean $overwrite
  1411. * Overwrite any file thats already at the destination.
  1412. * @return string
  1413. * The path to the downloaded file, or FALSE if the file could not be
  1414. * downloaded.
  1415. */
  1416. function _drush_download_file($url, $destination = FALSE, $overwrite = TRUE) {
  1417. if (!$destination) {
  1418. $destination = getcwd() . '/' . basename($url);
  1419. }
  1420. $destination_tmp = drush_tempnam('download_file');
  1421. drush_shell_exec("wget -q --timeout=30 -O %s %s", $destination_tmp, $url);
  1422. if (!drush_file_not_empty($destination_tmp)) {
  1423. drush_shell_exec("curl -s -L --connect-timeout 30 -o %s %s", $destination_tmp, $url);
  1424. }
  1425. if (!drush_file_not_empty($destination_tmp) && $file = @file_get_contents($url)) {
  1426. @file_put_contents($destination_tmp, $file);
  1427. }
  1428. if (!drush_file_not_empty($destination_tmp)) {
  1429. // Download failed.
  1430. return FALSE;
  1431. }
  1432. drush_move_dir($destination_tmp, $destination, $overwrite);
  1433. return $destination;
  1434. }
  1435. /**
  1436. * Extract a tarball.
  1437. *
  1438. * @param string $path
  1439. * The name of the .tar.gz or .tgz file to be extracted.
  1440. * @param string $destination
  1441. * The destination directory the tarball should be extracted into.
  1442. * Optional, if ommitted the tarball directory will be used as destination.
  1443. * @param boolean $listing
  1444. * If TRUE, a listing of the tar contents will be returned on success.
  1445. *
  1446. * @return string
  1447. * TRUE on success, FALSE on fail. If $listing is TRUE, a file listing of the
  1448. * tarball is returned if the extraction reported success, instead of TRUE.
  1449. */
  1450. function drush_tarball_extract($path, $destination = FALSE, $listing = FALSE) {
  1451. if (!file_exists($path)) {
  1452. return drush_set_error('TARBALL_EXTRACT_NOT_FOUND', dt('Tarball !path could not be found.', array('!path' => $path)));
  1453. }
  1454. $olddir = getcwd();
  1455. if (!$destination) {
  1456. $destination = dirname($path);
  1457. }
  1458. if (!is_writeable($destination)) {
  1459. return drush_set_error('TARBALL_EXTRACT_DESTINATION', dt('Extracting !path failed, as the destination directory !dest was not found or could not be written to.', array('!path' => $path, '!dest' => $dest)));
  1460. }
  1461. // If we are not on Windows, then try to do "tar" in a single operation.
  1462. if ((!drush_is_windows()) && (drush_shell_cd_and_exec(dirname($path), "tar -C %s -xzf %s", $destination, basename($path)))) {
  1463. if ($listing) {
  1464. // We use a separate tar -tzf instead of -xvf above because
  1465. // the output is not the same in Mac.
  1466. drush_shell_cd_and_exec(dirname($path), "tar -tzf %s", basename($path));
  1467. return drush_shell_exec_output();
  1468. }
  1469. return TRUE;
  1470. }
  1471. // If we could not get the single-op tar to work, do it in three steps.
  1472. // Copy the source tarball to the destination directory. Rename to a temp name in case the destination directory == dirname($path)
  1473. $paths_basename = basename(basename($path, '.tar.gz'), '.tgz');
  1474. $tarball = drush_tempnam($paths_basename, $destination) . ".tar.gz";
  1475. drush_register_file_for_deletion($tarball);
  1476. drush_copy_dir($path, $tarball);
  1477. $unzipped = $destination . '/' . basename($tarball, ".tar.gz") . ".tar";
  1478. // We used to use gzip --decompress in --stdout > out, but the output redirection sometimes failed on Windows for some binary output
  1479. drush_shell_cd_and_exec(dirname($tarball), "gzip --decompress %s", $tarball);
  1480. if (file_exists($unzipped)) {
  1481. drush_register_file_for_deletion($unzipped);
  1482. if (drush_shell_cd_and_exec(dirname($unzipped), "tar -xf %s", basename($unzipped))) {
  1483. if ($listing) {
  1484. // We use a separate tar -tf instead of -xf above because
  1485. // the output is not the same in Mac.
  1486. drush_shell_cd_and_exec(dirname($unzipped), "tar -tf %s", basename($unzipped));
  1487. return drush_shell_exec_output();
  1488. }
  1489. return TRUE;
  1490. }
  1491. return drush_set_error('TARBALL_EXTRACT_TAR_FAIL', dt('Extracting !path using the tar command failed.', array('!path' => $path)));
  1492. }
  1493. else {
  1494. return drush_set_error('TARBALL_EXTRACT_GZIP_FAIL', dt('Uncompressing !path using the gzip command failed.', array('!path' => $path)));
  1495. }
  1496. }
  1497. /**
  1498. * @} End of "defgroup commandwrappers".
  1499. */
  1500. /**
  1501. * @defgroup filesystemfunctions Filesystem convenience functions.
  1502. * @{
  1503. */
  1504. /**
  1505. * Deletes the provided file or folder and everything inside it.
  1506. *
  1507. * @param $dir
  1508. * The directory to delete
  1509. * @return
  1510. * FALSE on failure, TRUE if everything was deleted
  1511. */
  1512. function drush_delete_dir($dir) {
  1513. if (!file_exists($dir)) {
  1514. return TRUE;
  1515. }
  1516. if (!is_dir($dir)) {
  1517. return unlink($dir);
  1518. }
  1519. foreach (scandir($dir) as $item) {
  1520. if ($item == '.' || $item == '..') {
  1521. continue;
  1522. }
  1523. if (!drush_delete_dir($dir.'/'.$item)) {
  1524. return FALSE;
  1525. }
  1526. }
  1527. return rmdir($dir);
  1528. }
  1529. /**
  1530. * Copy $src to $dest.
  1531. *
  1532. * @param $src
  1533. * The directory to copy.
  1534. * @param $dest
  1535. * The destination to copy the source to, including the new name of
  1536. * the directory. To copy directory "a" from "/b" to "/c", then
  1537. * $src = "/b/a" and $dest = "/c/a". To copy "a" to "/c" and rename
  1538. * it to "d", then $dest = "/c/d".
  1539. * @param $overwrite
  1540. * If TRUE, the destination will be deleted if it exists.
  1541. * @return
  1542. * TRUE on success, FALSE on failure.
  1543. */
  1544. function drush_copy_dir($src, $dest, $overwrite = FALSE) {
  1545. // Preflight based on $overwrite if $dest exists.
  1546. if (file_exists($dest)) {
  1547. if ($overwrite) {
  1548. drush_op('drush_delete_dir', $dest, TRUE);
  1549. }
  1550. else {
  1551. return drush_set_error('DRUSH_DESTINATION_EXISTS', dt('Destination directory !dest already exists.', array('!dest' => $dest)));
  1552. }
  1553. }
  1554. // $src readable?
  1555. if (!drush_op('is_readable', $src)) {
  1556. return drush_set_error('DRUSH_SOURCE_NOT_EXISTS', dt('Source directory !src is not readable or does not exist.', array('!src' => $src)));
  1557. }
  1558. // $dest writable?
  1559. if (!drush_op('is_writable', dirname($dest))) {
  1560. return drush_set_error('DRUSH_DESTINATION_NOT_WRITABLE', dt('Destination directory !dest is not writable.', array('!dest' => dirname($dest))));
  1561. }
  1562. // Try to do a recursive copy.
  1563. if (@drush_op('_drush_recursive_copy', $src, $dest)) {
  1564. return TRUE;
  1565. }
  1566. return drush_set_error('DRUSH_COPY_DIR_FAILURE', dt('Unable to copy !src to !dest.', array('src' => $src, 'dest' => $dest)));
  1567. }
  1568. /**
  1569. * Internal function called by drush_copy_dir; do not use directly.
  1570. */
  1571. function _drush_recursive_copy($src, $dest) {
  1572. // all subdirectories and contents:
  1573. if(is_dir($src)) {
  1574. drush_mkdir($dest);
  1575. $dir_handle = opendir($src);
  1576. while($file = readdir($dir_handle)) {
  1577. if ($file != "." && $file != "..") {
  1578. if (_drush_recursive_copy("$src/$file", "$dest/$file") !== TRUE) {
  1579. return FALSE;
  1580. }
  1581. }
  1582. }
  1583. closedir($dir_handle);
  1584. }
  1585. elseif (drush_op('copy', $src, $dest) !== TRUE) {
  1586. return FALSE;
  1587. }
  1588. // Preserve permissions
  1589. if (!drush_is_windows()) {
  1590. chmod($dest, intval(fileperms($src), 8));
  1591. }
  1592. return TRUE;
  1593. }
  1594. /**
  1595. * Move $src to $dest.
  1596. *
  1597. * If the php 'rename' function doesn't work, then we'll do copy & delete.
  1598. *
  1599. * @param $src
  1600. * The directory to move.
  1601. * @param $dest
  1602. * The destination to move the source to, including the new name of
  1603. * the directory. To move directory "a" from "/b" to "/c", then
  1604. * $src = "/b/a" and $dest = "/c/a". To move "a" to "/c" and rename
  1605. * it to "d", then $dest = "/c/d" (just like php rename function).
  1606. * @param $overwrite
  1607. * If TRUE, the destination will be deleted if it exists.
  1608. * @return
  1609. * TRUE on success, FALSE on failure.
  1610. */
  1611. function drush_move_dir($src, $dest, $overwrite = FALSE) {
  1612. // Preflight based on $overwrite if $dest exists.
  1613. if (file_exists($dest)) {
  1614. if ($overwrite) {
  1615. drush_op('drush_delete_dir', $dest, TRUE);
  1616. }
  1617. else {
  1618. return drush_set_error('DRUSH_DESTINATION_EXISTS', dt('Destination directory !dest already exists.', array('!dest' => $dest)));
  1619. }
  1620. }
  1621. // $src readable?
  1622. if (!drush_op('is_readable', $src)) {
  1623. return drush_set_error('DRUSH_SOURCE_NOT_EXISTS', dt('Source directory !src is not readable or does not exist.', array('!src' => $src)));
  1624. }
  1625. // $dest writable?
  1626. if (!drush_op('is_writable', dirname($dest))) {
  1627. return drush_set_error('DRUSH_DESTINATION_NOT_WRITABLE', dt('Destination directory !dest is not writable.', array('!dest' => dirname($dest))));
  1628. }
  1629. // Try rename. It will fail if $src and $dest are not in the same partition.
  1630. if (@drush_op('rename', $src, $dest)) {
  1631. return TRUE;
  1632. }
  1633. // Eventually it will create an empty file in $dest. See
  1634. // http://www.php.net/manual/es/function.rename.php#90025
  1635. elseif (is_file($dest)) {
  1636. drush_op('unlink', $dest);
  1637. }
  1638. // If 'rename' fails, then we will use copy followed
  1639. // by a delete of the source.
  1640. if (drush_copy_dir($src, $dest)) {
  1641. drush_op('drush_delete_dir', $src, TRUE);
  1642. return TRUE;
  1643. }
  1644. return drush_set_error('DRUSH_MOVE_DIR_FAILURE', dt('Unable to move !src to !dest.', array('!src' => $src, '!dest' => $dest)));
  1645. }
  1646. /**
  1647. * Cross-platform compatible helper function to recursively create a directory tree.
  1648. */
  1649. function drush_mkdir($path) {
  1650. if (!is_dir($path)) {
  1651. if (drush_mkdir(dirname($path))) {
  1652. if (@mkdir($path)) {
  1653. return TRUE;
  1654. }
  1655. else {
  1656. if (is_writable(dirname($path))) {
  1657. drush_set_error('DRUSH_CREATE_DIR_FAILURE', dt('Unable to create !dir.', array('!dir' => preg_replace('/\w+\/\.\.\//', '', $path))));
  1658. }
  1659. else {
  1660. drush_set_error('DRUSH_DESTINATION_NOT_WRITABLE', dt('Unable to write in !dir. Please check directory permissions.', array('!dir' => realpath(dirname($path)))));
  1661. }
  1662. }
  1663. }
  1664. }
  1665. else {
  1666. return TRUE;
  1667. }
  1668. }
  1669. /**
  1670. * Save a string to a temporary file. Does not depend on Drupal's API.
  1671. * The temporary file will be automatically deleted when drush exits.
  1672. *
  1673. * @param string $data
  1674. * @return string
  1675. * A path to the file.
  1676. */
  1677. function drush_save_data_to_temp_file($data) {
  1678. static $fp;
  1679. $fp = tmpfile();
  1680. fwrite($fp, $data);
  1681. $meta_data = stream_get_meta_data($fp);
  1682. $file = $meta_data['uri'];
  1683. drush_register_file_for_deletion($file);
  1684. return $file;
  1685. }
  1686. /**
  1687. * Returns the path to a temporary directory.
  1688. *
  1689. * This is a custom version of file_directory_path().
  1690. * We can't directly rely on sys_get_temp_dir() as this
  1691. * path is not valid in some setups for Mac.
  1692. */
  1693. function drush_find_tmp() {
  1694. static $temporary_directory = NULL;
  1695. if (is_null($temporary_directory)) {
  1696. $directories = array();
  1697. // Operating system specific dirs.
  1698. if (substr(PHP_OS, 0, 3) == 'WIN') {
  1699. $directories[] = 'c:\\windows\\temp';
  1700. $directories[] = 'c:\\winnt\\temp';
  1701. }
  1702. else {
  1703. $directories[] = '/tmp';
  1704. }
  1705. // This function exists in PHP 5 >= 5.2.1, but drush
  1706. // requires PHP 5 >= 5.2.0, so we check for it.
  1707. if (function_exists('sys_get_temp_dir')) {
  1708. $directories[] = sys_get_temp_dir();
  1709. }
  1710. foreach ($directories as $directory) {
  1711. if (is_dir($directory) && is_writable($directory)) {
  1712. $temporary_directory = $directory;
  1713. break;
  1714. }
  1715. }
  1716. if (empty($temporary_directory)) {
  1717. // If no directory has been found, create one in cwd.
  1718. $temporary_directory = drush_cwd() . '/tmp';
  1719. drush_mkdir($temporary_directory);
  1720. if (!is_dir($directory)) {
  1721. return drush_set_error('DRUSH_UNABLE_TO_CREATE_TMP_DIR', dt("Unable to create a temporary directory."));
  1722. }
  1723. drush_register_file_for_deletion($temporary_directory);
  1724. }
  1725. }
  1726. return $temporary_directory;
  1727. }
  1728. /**
  1729. * Creates a temporary file, and registers it so that
  1730. * it will be deleted when drush exits. Whenever possible,
  1731. * drush_save_data_to_temp_file() should be used instead
  1732. * of this function.
  1733. */
  1734. function drush_tempnam($pattern, $tmp_dir = NULL) {
  1735. if ($tmp_dir == NULL) {
  1736. $tmp_dir = drush_find_tmp();
  1737. }
  1738. $tmp_file = tempnam($tmp_dir, $pattern);
  1739. drush_register_file_for_deletion($tmp_file);
  1740. return $tmp_file;
  1741. }
  1742. /**
  1743. * Creates a temporary directory and return its path.
  1744. */
  1745. function drush_tempdir() {
  1746. $tmp_dir = rtrim(drush_find_tmp(), DIRECTORY_SEPARATOR);
  1747. $tmp_dir .= '/' . 'drush_tmp_' . time();
  1748. drush_mkdir($tmp_dir);
  1749. drush_register_file_for_deletion($tmp_dir);
  1750. return $tmp_dir;
  1751. }
  1752. /**
  1753. * Any file passed in to this function will be deleted
  1754. * when drush exits.
  1755. */
  1756. function drush_register_file_for_deletion($file = NULL) {
  1757. static $registered_files = array();
  1758. if (isset($file)) {
  1759. if (empty($registered_files)) {
  1760. register_shutdown_function('_drush_delete_registered_files');
  1761. }
  1762. $registered_files[] = $file;
  1763. }
  1764. return $registered_files;
  1765. }
  1766. /**
  1767. * Delete all of the registered temporary files.
  1768. */
  1769. function _drush_delete_registered_files() {
  1770. $files_to_delete = drush_register_file_for_deletion();
  1771. foreach ($files_to_delete as $file) {
  1772. // We'll make sure that the file still exists, just
  1773. // in case someone came along and deleted it, even
  1774. // though they did not need to.
  1775. if (file_exists($file)) {
  1776. if (is_dir($file)) {
  1777. drush_delete_dir($file, TRUE);
  1778. }
  1779. else {
  1780. unlink($file);
  1781. }
  1782. }
  1783. }
  1784. }
  1785. /**
  1786. * Decide where our backup directory should go
  1787. *
  1788. * @param string $subdir
  1789. * The name of the desired subdirectory(s) under drush-backups.
  1790. * Usually a database name.
  1791. */
  1792. function drush_preflight_backup_dir($subdir = NULL) {
  1793. $backup_dir = drush_get_context('DRUSH_BACKUP_DIR', drush_get_option('backup-location'));
  1794. if (empty($backup_dir)) {
  1795. // Try to use db name as subdir if none was provided.
  1796. if (empty($subdir)) {
  1797. $subdir = 'unknown';
  1798. if ($creds = drush_get_context('DRUSH_DB_CREDENTIALS')) {
  1799. $subdir = $creds['name'];
  1800. }
  1801. }
  1802. // Save the date to be used in the backup directory's path name.
  1803. $date = gmdate('YmdHis', $_SERVER['REQUEST_TIME']);
  1804. $backup_dir = drush_get_option('backup-dir', drush_server_home() . '/' . 'drush-backups');
  1805. $backup_dir = rtrim($backup_dir, DIRECTORY_SEPARATOR) . '/' . $subdir . '/' . $date;
  1806. drush_set_context('DRUSH_BACKUP_DIR', $backup_dir);
  1807. }
  1808. return $backup_dir;
  1809. }
  1810. /**
  1811. * Prepare a backup directory
  1812. */
  1813. function drush_prepare_backup_dir($subdir = NULL) {
  1814. $backup_dir = drush_preflight_backup_dir($subdir);
  1815. $backup_parent = dirname($backup_dir);
  1816. $drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT');
  1817. $drupal_root .= '/';
  1818. if ((!empty($drupal_root)) && (strpos($backup_parent, $drupal_root) === 0)) {
  1819. return drush_set_error('DRUSH_PM_BACKUP_FAILED', dt('It\'s not allowed to store backups inside the Drupal root directory.'));
  1820. }
  1821. if (!file_exists($backup_parent)) {
  1822. if (!drush_mkdir($backup_parent)) {
  1823. return drush_set_error('DRUSH_PM_BACKUP_FAILED', dt('Unable to create backup directory !dir.', array('!dir' => $backup_parent)));
  1824. }
  1825. }
  1826. if (!is_writable($backup_parent)) {
  1827. return drush_set_error('DRUSH_PM_BACKUP_FAILED', dt('Backup directory !dir is not writable.', array('!dir' => $backup_parent)));
  1828. }
  1829. drush_mkdir($backup_dir);
  1830. return $backup_dir;
  1831. }
  1832. /**
  1833. * @} End of "defgroup filesystemfunctions".
  1834. */
  1835. /**
  1836. * @defgroup dbfunctions Database convenience functions.
  1837. * @{
  1838. */
  1839. /**
  1840. * Replace named placeholders in a WHERE snippet.
  1841. *
  1842. * Helper function to allow the usage of Drupal 7 WHERE snippets
  1843. * with named placeholders in code for Drupal 5 and 6.
  1844. *
  1845. * @param $where
  1846. * String with a WHERE snippet using named placeholders.
  1847. * @param $args
  1848. * Array of placeholder values.
  1849. * @return
  1850. * String. $where filled with literals from $args.
  1851. */
  1852. function _drush_replace_query_placeholders($where, $args) {
  1853. foreach ($args as $key => $data) {
  1854. if (is_array($data)) {
  1855. $new_keys = array();
  1856. // $data can't have keys that are a prefix of other keys to
  1857. // prevent a corrupted result in the below calls to str_replace().
  1858. // To avoid this we will use a zero padded indexed array of the values of $data.
  1859. $pad_length = strlen((string)count(array_values($data)));
  1860. foreach (array_values($data) as $i => $value) {
  1861. if (!is_numeric($value)) {
  1862. $value = "'".$value."'";
  1863. }
  1864. $new_keys[$key . '_' . str_pad($i, $pad_length, '0', STR_PAD_LEFT)] = $value;
  1865. }
  1866. $where = preg_replace('#' . $key . '\b#', implode(', ', array_keys($new_keys)), $where);
  1867. unset($args[$key]);
  1868. $args += $new_keys;
  1869. }
  1870. else if (!is_numeric($data)) {
  1871. $args[$key] = "'".$data."'";
  1872. }
  1873. }
  1874. foreach ($args as $key => $data) {
  1875. $where = str_replace($key, $data, $where);
  1876. }
  1877. return $where;
  1878. }
  1879. /**
  1880. * A db_select() that works for any version of Drupal.
  1881. *
  1882. * @param $table
  1883. * String. The table to operate on.
  1884. * @param $fields
  1885. * Array or string. Fields affected in this operation. Valid string values are '*' or a single column name.
  1886. * @param $where
  1887. * String. WHERE snippet for the operation. It uses named placeholders. see @_drush_replace_query_placeholders()
  1888. * @param $args
  1889. * Array. Arguments for the WHERE snippet.
  1890. * @param $start
  1891. * Int. Value for OFFSET.
  1892. * @param $length
  1893. * Int. Value for LIMIT.
  1894. * @param $order_by_field
  1895. * String. Database column to order by.
  1896. * @param $order_by_direction
  1897. * ('ASC', 'DESC'). Ordering direction.
  1898. * @return
  1899. * A database resource.
  1900. */
  1901. function drush_db_select($table, $fields = '*', $where = NULL, $args = NULL, $start = NULL, $length = NULL, $order_by_field = NULL, $order_by_direction = 'ASC') {
  1902. if (drush_drupal_major_version() >= 7) {
  1903. if (!is_array($fields)) {
  1904. if ($fields == '*') {
  1905. $fields = array();
  1906. }
  1907. else {
  1908. $fields = array($fields);
  1909. }
  1910. }
  1911. $query = db_select($table, $table)
  1912. ->fields($table, $fields);
  1913. if (!empty($where)) {
  1914. $query = $query->where($where, $args);
  1915. }
  1916. if (!is_null($order_by_field)) {
  1917. $query = $query->orderBy($order_by_field, $order_by_direction);
  1918. }
  1919. if (!is_null($length)) {
  1920. $query = $query->range($start, $length);
  1921. }
  1922. return $query->execute();
  1923. }
  1924. else {
  1925. if (is_array($fields)) {
  1926. $fields = implode(', ', $fields);
  1927. }
  1928. $query = "SELECT $fields FROM {{$table}}";
  1929. if (!empty($where)) {
  1930. $where = _drush_replace_query_placeholders($where, $args);
  1931. $query .= " WHERE ".$where;
  1932. }
  1933. if (!is_null($order_by_field)) {
  1934. $query .= " ORDER BY $order_by_field $order_by_direction";
  1935. }
  1936. if (!is_null($length)) {
  1937. $db_spec = _drush_sql_get_db_spec();
  1938. $db_scheme = _drush_sql_get_scheme($db_spec);
  1939. $limit = " LIMIT $length";
  1940. if (!is_null($start)) {
  1941. $limit .= " OFFSET $start";
  1942. }
  1943. $query .= $limit;
  1944. }
  1945. return db_query($query, $args);
  1946. }
  1947. }
  1948. /**
  1949. * A db_delete() that works for any version of Drupal.
  1950. *
  1951. * @param $table
  1952. * String. The table to operate on.
  1953. * @param $where
  1954. * String. WHERE snippet for the operation. It uses named placeholders. see @_drush_replace_query_placeholders()
  1955. * @param $args
  1956. * Array. Arguments for the WHERE snippet.
  1957. * @return
  1958. * Affected rows (except on D7+mysql without a WHERE clause - returns TRUE) or FALSE.
  1959. */
  1960. function drush_db_delete($table, $where = NULL, $args = NULL) {
  1961. if (drush_drupal_major_version() >= 7) {
  1962. if (!empty($where)) {
  1963. $query = db_delete($table)->where($where, $args);
  1964. return $query->execute();
  1965. }
  1966. else {
  1967. return db_truncate($table)->execute();
  1968. }
  1969. }
  1970. else {
  1971. $query = "DELETE FROM {{$table}}";
  1972. if (!empty($where)) {
  1973. $where = _drush_replace_query_placeholders($where, $args);
  1974. $query .= ' WHERE '.$where;
  1975. }
  1976. if (!db_query($query, $args)) {
  1977. return FALSE;
  1978. }
  1979. return db_affected_rows();
  1980. }
  1981. }
  1982. /**
  1983. * A db_result() that works consistently for any version of Drupal.
  1984. *
  1985. * @param
  1986. * A Database result object.
  1987. */
  1988. function drush_db_result($result) {
  1989. switch (drush_drupal_major_version()) {
  1990. case 5:
  1991. // In versions of Drupal <= 5, db_result only returns the first row no matter how
  1992. // many times you call it. So instead of calling it here, we use db_fetch_array which
  1993. // does increment the pointer to the next row (as db_result does on Drupal 6)
  1994. if ($array = db_fetch_array($result)) {
  1995. return array_shift($array); // return first element in array.
  1996. }
  1997. return FALSE;
  1998. case 6:
  1999. return db_result($result);
  2000. case 7:
  2001. default:
  2002. return $result->fetchField();
  2003. }
  2004. }
  2005. /**
  2006. * A db_fetch_object() that works for any version of Drupal.
  2007. *
  2008. * @param
  2009. * A Database result object.
  2010. */
  2011. function drush_db_fetch_object($result) {
  2012. return drush_drupal_major_version() >= 7 ? $result->fetchObject() : db_fetch_object($result);
  2013. }
  2014. /**
  2015. * @} End of "defgroup dbfunctions".
  2016. */
  2017. /**
  2018. * @defgroup commandprocessing Command processing functions.
  2019. * @{
  2020. *
  2021. * These functions manage command processing by the
  2022. * main function in drush.php.
  2023. */
  2024. /**
  2025. * Process commands that are executed on a remote drush instance.
  2026. *
  2027. * @return
  2028. * TRUE if the command was handled remotely.
  2029. */
  2030. function drush_remote_command() {
  2031. // The command will be executed remotely if the --remote-host flag
  2032. // is set; note that if a site alias is provided on the command line,
  2033. // and the site alias references a remote server, then the --remote-host
  2034. // option will be set when the site alias is processed.
  2035. // @see _drush_process_site_alias
  2036. $remote_host = drush_get_option('remote-host');
  2037. if (isset($remote_host)) {
  2038. $args = drush_get_arguments();
  2039. $command = array_shift($args);
  2040. $remote_user = drush_get_option('remote-user');
  2041. drush_do_command_redispatch($command, $args, $remote_host, $remote_user);
  2042. return TRUE;
  2043. }
  2044. // If the --site-list flag is set, then we will execute the specified
  2045. // command once for every site listed in the site list.
  2046. $site_list = drush_get_option('site-list');
  2047. if (isset($site_list)) {
  2048. if (!is_array($site_list)) {
  2049. $site_list = explode(',', $site_list);
  2050. }
  2051. $site_list = drush_sitealias_resolve_sitespecs($site_list);
  2052. $site_list = drush_sitealias_simplify_names($site_list);
  2053. $args = drush_get_arguments();
  2054. if (!drush_get_context('DRUSH_SIMULATE')) {
  2055. drush_print(dt("You are about to execute '!command' on all of the following targets:", array('!command' => implode(" ", $args))));
  2056. foreach ($site_list as $one_destination => $one_record) {
  2057. drush_print(dt(' !target', array('!target' => $one_destination)));
  2058. }
  2059. if (drush_confirm('Continue? ') === FALSE) {
  2060. drush_user_abort();
  2061. return TRUE;
  2062. }
  2063. }
  2064. $command = array_shift($args);
  2065. $multi_options = drush_get_context('cli');
  2066. if (!drush_get_option('no-label', FALSE)) {
  2067. $label_separator = ' >> ';
  2068. $max_name_length = 0;
  2069. foreach ($site_list as $alias_name => $alias_record) {
  2070. if (strlen($alias_name) > $max_name_length) {
  2071. $max_name_length = strlen($alias_name);
  2072. }
  2073. }
  2074. $multi_options['reserve-margin'] = $max_name_length + strlen($label_separator);
  2075. foreach ($site_list as $alias_name => $alias_record) {
  2076. $values = drush_do_site_command($alias_record, $command, $args, $multi_options);
  2077. foreach (explode("\n", $values['output']) as $line) {
  2078. if (empty($line)) {
  2079. drush_print();
  2080. }
  2081. else {
  2082. drush_print(str_pad($alias_name, $max_name_length, " ") . $label_separator . $line);
  2083. }
  2084. }
  2085. }
  2086. }
  2087. else {
  2088. foreach ($site_list as $alias_name => $alias_record) {
  2089. $values = drush_do_site_command($alias_record, $command, $args, $multi_options);
  2090. drush_print($values['output']);
  2091. }
  2092. }
  2093. return TRUE;
  2094. }
  2095. return FALSE;
  2096. }
  2097. /**
  2098. * Used by functions that operate on lists of sites, moving
  2099. * information from the source to the destination. Currenlty
  2100. * this includes 'drush rsync' and 'drush sql sync'.
  2101. */
  2102. function drush_do_multiple_command($command, $source_record, $destination_record, $allow_single_source = FALSE) {
  2103. $is_multiple_command = FALSE;
  2104. if ((($allow_single_source == TRUE) || array_key_exists('site-list', $source_record)) && array_key_exists('site-list', $destination_record)) {
  2105. $is_multiple_command = TRUE;
  2106. $source_path = array_key_exists('path-component', $source_record) ? $source_record['path-component'] : '';
  2107. $destination_path = array_key_exists('path-component', $destination_record) ? $destination_record['path-component'] : '';
  2108. $target_list = array_values(drush_sitealias_resolve_sitelist($destination_record));
  2109. if (array_key_exists('site-list', $source_record)) {
  2110. $source_list = array_values(drush_sitealias_resolve_sitelist($source_record));
  2111. if (drush_sitealias_check_lists_alignment($source_list, $target_list) === FALSE) {
  2112. if (array_key_exists('unordered-list', $source_record) || array_key_exists('unordered-list', $destination_record)) {
  2113. drush_sitelist_align_lists($source_list, $target_list, $aligned_source, $aligned_target);
  2114. $source_list = $aligned_source;
  2115. $target_list = $aligned_target;
  2116. }
  2117. }
  2118. }
  2119. else {
  2120. $source_list = array_fill(0, count($target_list), $source_record);
  2121. }
  2122. if (!drush_get_context('DRUSH_SIMULATE')) {
  2123. drush_print(dt('You are about to !command between all of the following targets:', array('!command' => $command)));
  2124. $i = 0;
  2125. foreach ($source_list as $one_source) {
  2126. $one_target = $target_list[$i];
  2127. ++$i;
  2128. 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)));
  2129. }
  2130. if (drush_confirm('Continue? ') === FALSE) {
  2131. return drush_user_abort();
  2132. }
  2133. }
  2134. $data = drush_redispatch_get_options();
  2135. $i = 0;
  2136. foreach ($source_list as $one_source) {
  2137. $one_target = $target_list[$i];
  2138. ++$i;
  2139. $source_spec = drush_sitealias_alias_record_to_spec($one_source);
  2140. $target_spec = drush_sitealias_alias_record_to_spec($one_target);
  2141. drush_log(dt('Begin do_multiple !command via backend invoke', array('!command' => $command)));
  2142. $values = drush_backend_invoke_args($command, array($source_spec . $source_path, $target_spec . $destination_path), $data, 'GET', TRUE);
  2143. drush_log(dt('Backend invoke is complete'));
  2144. }
  2145. }
  2146. return $is_multiple_command;
  2147. }
  2148. /**
  2149. * Run a command on the site specified by the provided command record.
  2150. *
  2151. * The standard function that provides this service is called
  2152. * drush_invoke_sitealias_args. Please call the standard function
  2153. * unless you need to set $integrate = TRUE.
  2154. */
  2155. function drush_do_site_command($site_record, $command, $args = array(), $data = array(), $integrate = FALSE) {
  2156. $values = NULL;
  2157. if (!empty($site_record)) {
  2158. foreach ($site_record as $key => $value) {
  2159. if (!isset($data[$key]) && !in_array($key, drush_sitealias_site_selection_keys())) {
  2160. $data[$key] = $site_record[$key];
  2161. }
  2162. }
  2163. $values = drush_backend_invoke_sitealias($site_record, $command, $args, $data, 'GET', $integrate);
  2164. }
  2165. return $values;
  2166. }
  2167. /**
  2168. * Redispatch the specified command using the same
  2169. * options that were passed to this invocation of drush.
  2170. */
  2171. function drush_do_command_redispatch($command, $args = array(), $remote_host = NULL, $remote_user = NULL, $drush_path = NULL) {
  2172. $data = drush_redispatch_get_options();
  2173. // If the path to drush was supplied, then pass it to backend invoke.
  2174. if ($drush_path == NULL) {
  2175. $drush_path = drush_get_option('drush-script');
  2176. if (!isset($drush_path)) {
  2177. $drush_folder = drush_get_option('drush');
  2178. if (isset($drush)) {
  2179. $drush_path = $drush_folder . '/drush';
  2180. }
  2181. }
  2182. }
  2183. // Call through to backend invoke.
  2184. drush_log(dt('Begin redispatch via backend invoke'));
  2185. $values = drush_backend_invoke_args($command, $args, $data, 'GET', TRUE, $drush_path, $remote_host, $remote_user);
  2186. drush_log(dt('Backend invoke is complete'));
  2187. return $values;
  2188. }
  2189. /**
  2190. * @} End of "defgroup commandprocessing".
  2191. */
  2192. /**
  2193. * @defgroup logging Logging information to be provided as output.
  2194. * @{
  2195. *
  2196. * These functions are primarily for diagnostic purposes, but also provide an overview of tasks that were taken
  2197. * by drush.
  2198. */
  2199. /**
  2200. * Add a log message to the log history.
  2201. *
  2202. * This function calls the callback stored in the 'DRUSH_LOG_CALLBACK' context with
  2203. * the resulting entry at the end of execution.
  2204. *
  2205. * This allows you to replace it with custom logging implementations if needed,
  2206. * such as logging to a file or logging to a database (drupal or otherwise).
  2207. *
  2208. * The default callback is the _drush_print_log() function with prints the messages
  2209. * to the shell.
  2210. *
  2211. * @param message
  2212. * String containing the message to be logged.
  2213. * @param type
  2214. * The type of message to be logged. Common types are 'warning', 'error', 'success' and 'notice'.
  2215. * A type of 'failed' can also be supplied to flag as an 'error'.
  2216. * A type of 'ok' or 'completed' can also be supplied to flag as a 'success'
  2217. * All other types of messages will be assumed to be notices.
  2218. */
  2219. function drush_log($message, $type = 'notice', $error = null) {
  2220. $log =& drush_get_context('DRUSH_LOG', array());
  2221. $callback = drush_get_context('DRUSH_LOG_CALLBACK', '_drush_print_log');
  2222. $entry = array(
  2223. 'type' => $type,
  2224. 'message' => $message,
  2225. 'timestamp' => microtime(TRUE),
  2226. 'memory' => memory_get_usage(),
  2227. );
  2228. $entry['error'] = $error;
  2229. $log[] = $entry;
  2230. return $callback($entry);
  2231. }
  2232. /**
  2233. * Retrieve the log messages from the log history
  2234. *
  2235. * @return
  2236. * Entire log history
  2237. */
  2238. function drush_get_log() {
  2239. return drush_get_context('DRUSH_LOG', array());
  2240. }
  2241. /**
  2242. * Run print_r on a variable and log the output.
  2243. */
  2244. function dlm($object) {
  2245. ob_start();
  2246. print_r($object);
  2247. $contents = ob_get_contents();
  2248. ob_end_clean();
  2249. drush_log($contents);
  2250. }
  2251. /**
  2252. * Display the pipe output for the current request.
  2253. */
  2254. function drush_pipe_output() {
  2255. $pipe = drush_get_context('DRUSH_PIPE_BUFFER');
  2256. if (!empty($pipe)) {
  2257. drush_print_r($pipe);
  2258. }
  2259. }
  2260. /**
  2261. * Display the log message
  2262. *
  2263. * By default, only warnings and errors will be displayed, if 'verbose' is specified, it will also display notices.
  2264. *
  2265. * @param
  2266. * The associative array for the entry.
  2267. *
  2268. * @return
  2269. * False in case of an error or failed type, True in all other cases.
  2270. */
  2271. function _drush_print_log($entry) {
  2272. if (drush_get_context('DRUSH_NOCOLOR')) {
  2273. $red = "[%s]";
  2274. $yellow = "[%s]";
  2275. $green = "[%s]";
  2276. }
  2277. else {
  2278. $red = "\033[31;40m\033[1m[%s]\033[0m";
  2279. $yellow = "\033[1;33;40m\033[1m[%s]\033[0m";
  2280. $green = "\033[1;32;40m\033[1m[%s]\033[0m";
  2281. }
  2282. $verbose = drush_get_context('DRUSH_VERBOSE');
  2283. $debug = drush_get_context('DRUSH_DEBUG');
  2284. $return = TRUE;
  2285. switch ($entry['type']) {
  2286. case 'warning' :
  2287. case 'cancel' :
  2288. $type_msg = sprintf($yellow, $entry['type']);
  2289. break;
  2290. case 'failed' :
  2291. case 'error' :
  2292. $type_msg = sprintf($red, $entry['type']);
  2293. $return = FALSE;
  2294. break;
  2295. case 'ok' :
  2296. case 'completed' :
  2297. case 'success' :
  2298. case 'status':
  2299. $type_msg = sprintf($green, $entry['type']);
  2300. break;
  2301. case 'notice' :
  2302. case 'message' :
  2303. case 'info' :
  2304. if (!$verbose) {
  2305. // print nothing. exit cleanly.
  2306. return TRUE;
  2307. }
  2308. $type_msg = sprintf("[%s]", $entry['type']);
  2309. break;
  2310. default :
  2311. if (!$debug) {
  2312. // print nothing. exit cleanly.
  2313. return TRUE;
  2314. }
  2315. $type_msg = sprintf("[%s]", $entry['type']);
  2316. break;
  2317. }
  2318. // When running in backend mode, log messages are not displayed, as they will
  2319. // be returned in the JSON encoded associative array. In quiet mode, we
  2320. // just drop log messages.
  2321. if (drush_get_context('DRUSH_BACKEND') || drush_get_context('DRUSH_QUIET')) {
  2322. return $return;
  2323. }
  2324. $columns = drush_get_context('DRUSH_COLUMNS', 80);
  2325. $width[1] = 11;
  2326. // Append timer and memory values.
  2327. if ($debug) {
  2328. $timer = sprintf('[%s sec, %s]', round($entry['timestamp']-DRUSH_REQUEST_TIME, 2), drush_format_size($entry['memory']));
  2329. $entry['message'] = $entry['message'] . ' ' . $timer;
  2330. }
  2331. $width[0] = ($columns - 11);
  2332. $format = sprintf("%%-%ds%%%ds", $width[0], $width[1]);
  2333. // Place the status message right aligned with the top line of the error message.
  2334. $message = wordwrap($entry['message'], $width[0]);
  2335. $lines = explode("\n", $message);
  2336. $lines[0] = sprintf($format, $lines[0], $type_msg);
  2337. $message = implode("\n", $lines);
  2338. drush_print($message, 0, STDERR);
  2339. return $return;
  2340. }
  2341. // Print all timers for the request.
  2342. function drush_print_timers() {
  2343. global $timers;
  2344. $temparray = array();
  2345. foreach ((array)$timers as $name => $timerec) {
  2346. // We have to use timer_read() for active timers, and check the record for others
  2347. if (isset($timerec['start'])) {
  2348. $temparray[$name] = timer_read($name);
  2349. }
  2350. else {
  2351. $temparray[$name] = $timerec['time'];
  2352. }
  2353. }
  2354. // Go no farther if there were no timers
  2355. if (count($temparray) > 0) {
  2356. // Put the highest cumulative times first
  2357. arsort($temparray);
  2358. $table = array();
  2359. $table[] = array('Timer', 'Cum (sec)', 'Count', 'Avg (msec)');
  2360. foreach ($temparray as $name => $time) {
  2361. $cum = round($time/1000, 3);
  2362. $count = $timers[$name]['count'];
  2363. if ($count > 0) {
  2364. $avg = round($time/$count, 3);
  2365. }
  2366. else {
  2367. $avg = 'N/A';
  2368. }
  2369. $table[] = array($name, $cum, $count, $avg);
  2370. }
  2371. drush_print_table($table, TRUE, array(), STDERR);
  2372. }
  2373. }
  2374. /**
  2375. * Turn drupal_set_message errors into drush_log errors
  2376. */
  2377. function _drush_log_drupal_messages() {
  2378. if (function_exists('drupal_get_messages')) {
  2379. $messages = drupal_get_messages(NULL, TRUE);
  2380. if (array_key_exists('error', $messages)) {
  2381. //Drupal message errors.
  2382. foreach ((array) $messages['error'] as $error) {
  2383. $error = strip_tags($error);
  2384. $header = preg_match('/^warning: Cannot modify header information - headers already sent by /i', $error);
  2385. $session = preg_match('/^warning: session_start\(\): Cannot send session /i', $error);
  2386. if ($header || $session) {
  2387. //These are special cases for an unavoidable warnings
  2388. //that are generated by generating output before Drupal is bootstrapped.
  2389. //or sending a session cookie (seems to affect d7 only?)
  2390. //Simply ignore them.
  2391. continue;
  2392. }
  2393. elseif (preg_match('/^warning:/i', $error)) {
  2394. drush_log(preg_replace('/^warning: /i', '', $error), 'warning');
  2395. }
  2396. elseif (preg_match('/^notice:/i', $error)) {
  2397. drush_log(preg_replace('/^notice: /i', '', $error), 'notice');
  2398. }
  2399. elseif (preg_match('/^user warning:/i', $error)) {
  2400. // This is a special case. PHP logs sql errors as 'User Warnings', not errors.
  2401. drush_set_error('DRUSH_DRUPAL_ERROR_MESSAGE', preg_replace('/^user warning: /i', '', $error));
  2402. }
  2403. else {
  2404. drush_set_error('DRUSH_DRUPAL_ERROR_MESSAGE', $error);
  2405. }
  2406. }
  2407. }
  2408. unset($messages['error']);
  2409. // Log non-error messages.
  2410. foreach ($messages as $type => $items) {
  2411. foreach ($items as $item) {
  2412. drush_log(strip_tags($item), $type);
  2413. }
  2414. }
  2415. }
  2416. }
  2417. // Copy of format_size() in Drupal.
  2418. function drush_format_size($size, $langcode = NULL) {
  2419. if ($size < DRUSH_DRUPAL_KILOBYTE) {
  2420. // format_plural() not always available.
  2421. return dt('@count bytes', array('@count' => $size));
  2422. }
  2423. else {
  2424. $size = $size / DRUSH_DRUPAL_KILOBYTE; // Convert bytes to kilobytes.
  2425. $units = array(
  2426. dt('@size KB', array(), array('langcode' => $langcode)),
  2427. dt('@size MB', array(), array('langcode' => $langcode)),
  2428. dt('@size GB', array(), array('langcode' => $langcode)),
  2429. dt('@size TB', array(), array('langcode' => $langcode)),
  2430. dt('@size PB', array(), array('langcode' => $langcode)),
  2431. dt('@size EB', array(), array('langcode' => $langcode)),
  2432. dt('@size ZB', array(), array('langcode' => $langcode)),
  2433. dt('@size YB', array(), array('langcode' => $langcode)),
  2434. );
  2435. foreach ($units as $unit) {
  2436. if (round($size, 2) >= DRUSH_DRUPAL_KILOBYTE) {
  2437. $size = $size / DRUSH_DRUPAL_KILOBYTE;
  2438. }
  2439. else {
  2440. break;
  2441. }
  2442. }
  2443. return str_replace('@size', round($size, 2), $unit);
  2444. }
  2445. }
  2446. /**
  2447. * Log Drupal watchdog() calls.
  2448. *
  2449. * A sneaky implementation of hook_watchdog().
  2450. */
  2451. function system_watchdog($log_entry) {
  2452. // Transform non informative severity levels to 'error' for compatibility with _drush_print_log.
  2453. // Other severity levels are coincident with the ones we use in drush.
  2454. if (drush_drupal_major_version() >= 6 && $log_entry['severity'] <= 2) {
  2455. $severity = 'error';
  2456. }
  2457. else {
  2458. drush_include_engine('drupal', 'environment');
  2459. $levels = core_watchdog_severity_levels();
  2460. $severity = $levels[$log_entry['severity']];
  2461. }
  2462. // Format the message.
  2463. if (is_array($log_entry['variables'])) {
  2464. $message = strtr($log_entry['message'], $log_entry['variables']);
  2465. }
  2466. else {
  2467. $message = $log_entry['message'];
  2468. }
  2469. // decode_entities() only loaded after FULL bootstrap.
  2470. if (function_exists('decode_entities')) {
  2471. $message = decode_entities($message);
  2472. }
  2473. $message = strip_tags($message);
  2474. // Log or print or ignore. Just printing saves memory but thats rarely needed.
  2475. switch (drush_get_option('watchdog', 'log')) {
  2476. case 'log':
  2477. drush_log('WD '. $log_entry['type'] . ': ' . $message, $severity);
  2478. break;
  2479. case 'print':
  2480. // Disable in backend mode since it logs output and the goal is to conserve memory.
  2481. // @see _drush_bootstrap_drush().
  2482. if (ob_get_length() === FALSE) {
  2483. drush_print('WD '. $severity . ' ' . $log_entry['type'] . ': ' . $message);
  2484. }
  2485. break;
  2486. default:
  2487. // Do nothing.
  2488. }
  2489. }
  2490. /**
  2491. * Log the return value of Drupal hook_update_n functions.
  2492. *
  2493. * This is used during install and update to log the output
  2494. * of the update process to the logging system.
  2495. */
  2496. function _drush_log_update_sql($ret) {
  2497. if (sizeof($ret)) {
  2498. foreach ($ret as $info) {
  2499. if (is_array($info)) {
  2500. if (!$info['success']) {
  2501. drush_set_error('DRUPAL_UPDATE_FAILED', $info['query']);
  2502. }
  2503. else {
  2504. drush_log($info['query'], ($info['success']) ? 'success' : 'error');
  2505. }
  2506. }
  2507. }
  2508. }
  2509. }
  2510. /**
  2511. * @} End of "defgroup logging".
  2512. */
  2513. /**
  2514. * @name Error status definitions
  2515. * @{
  2516. * Error code definitions for interpreting the current error status.
  2517. * @see drush_set_error(), drush_get_error(), drush_get_error_log(), drush_cmp_error()
  2518. */
  2519. /** The command completed successfully. */
  2520. define('DRUSH_SUCCESS', 0);
  2521. /** The command could not be completed because the framework has specified errors that have occured. */
  2522. define('DRUSH_FRAMEWORK_ERROR', 1);
  2523. /** The command that was executed resulted in an application error,
  2524. The most commom causes for this is invalid PHP or a broken SSH
  2525. pipe when using drush_backend_invoke in a distributed manner. */
  2526. define('DRUSH_APPLICATION_ERROR', 255);
  2527. /**
  2528. * @} End of "name Error status defintions".
  2529. */
  2530. /**
  2531. * @defgroup errorhandling Managing errors that occur in the Drush framework.
  2532. * @{
  2533. * Functions that manage the current error status of the Drush framework.
  2534. *
  2535. * These functions operate by maintaining a static variable that is a equal to the constant DRUSH_FRAMEWORK_ERROR if an
  2536. * error has occurred.
  2537. * This error code is returned at the end of program execution, and provide the shell or calling application with
  2538. * more information on how to diagnose any problems that may have occurred.
  2539. */
  2540. /**
  2541. * Set an error code for the error handling system.
  2542. *
  2543. * @param error
  2544. * A text string identifying the type of error.
  2545. *
  2546. * @param message
  2547. * Optional. Error message to be logged. If no message is specified, hook_drush_help will be consulted,
  2548. * using a key of 'error:MY_ERROR_STRING'.
  2549. *
  2550. * @return
  2551. * Always returns FALSE, to allow you to return with false in the calling functions,
  2552. * such as <code>return drush_set_error('DRUSH_FRAMEWORK_ERROR')</code>
  2553. */
  2554. function drush_set_error($error, $message = null) {
  2555. $error_code =& drush_get_context('DRUSH_ERROR_CODE', DRUSH_SUCCESS);
  2556. $error_code = DRUSH_FRAMEWORK_ERROR;
  2557. $error_log =& drush_get_context('DRUSH_ERROR_LOG', array());
  2558. if (is_numeric($error)) {
  2559. $error = 'DRUSH_FRAMEWORK_ERROR';
  2560. }
  2561. $message = ($message) ? $message : drush_command_invoke_all('drush_help', 'error:' . $error);
  2562. if (is_array($message)) {
  2563. $message = implode("\n", $message);
  2564. }
  2565. $error_log[$error][] = $message;
  2566. drush_log(($message) ? $message : $error, 'error', $error);
  2567. return FALSE;
  2568. }
  2569. /**
  2570. * Return the current error handling status
  2571. *
  2572. * @return
  2573. * The current aggregate error status
  2574. */
  2575. function drush_get_error() {
  2576. return drush_get_context('DRUSH_ERROR_CODE', DRUSH_SUCCESS);
  2577. }
  2578. /**
  2579. * Return the current list of errors that have occurred.
  2580. *
  2581. * @return
  2582. * An associative array of error messages indexed by the type of message.
  2583. */
  2584. function drush_get_error_log() {
  2585. return drush_get_context('DRUSH_ERROR_LOG', array());
  2586. }
  2587. /**
  2588. * Check if a specific error status has been set.
  2589. *
  2590. * @param error
  2591. * A text string identifying the error that has occurred.
  2592. * @return
  2593. * TRUE if the specified error has been set, FALSE if not
  2594. */
  2595. function drush_cmp_error($error) {
  2596. $error_log = drush_get_error_log();
  2597. if (is_numeric($error)) {
  2598. $error = 'DRUSH_FRAMEWORK_ERROR';
  2599. }
  2600. return array_key_exists($error, $error_log);
  2601. }
  2602. /**
  2603. * Clear error context.
  2604. */
  2605. function drush_clear_error() {
  2606. drush_set_context('DRUSH_ERROR_CODE', DRUSH_SUCCESS);
  2607. }
  2608. /**
  2609. * Exit due to user declining a confirmation prompt.
  2610. *
  2611. * Usage: return drush_user_abort();
  2612. */
  2613. function drush_user_abort($msg = NULL) {
  2614. drush_set_context('DRUSH_USER_ABORT', TRUE);
  2615. drush_log($msg ? $msg : dt('Aborting.'), 'cancel');
  2616. return FALSE;
  2617. }
  2618. /**
  2619. * Turn PHP error handling off.
  2620. *
  2621. * This is commonly used while bootstrapping Drupal for install
  2622. * or updates.
  2623. *
  2624. * This also records the previous error_reporting setting, in
  2625. * case it wasn't recorded previously.
  2626. *
  2627. * @see drush_errors_off()
  2628. */
  2629. function drush_errors_off() {
  2630. drush_get_context('DRUSH_ERROR_REPORTING', error_reporting(0));
  2631. ini_set('display_errors', FALSE);
  2632. }
  2633. /**
  2634. * Turn PHP error handling on.
  2635. *
  2636. * We default to error_reporting() here just in
  2637. * case drush_errors_on() is called before drush_errors_off() and
  2638. * the context is not yet set.
  2639. *
  2640. * @arg $errors string
  2641. * The default error level to set in drush. This error level will be
  2642. * carried through further drush_errors_on()/off() calls even if not
  2643. * provided in later calls.
  2644. *
  2645. * @see error_reporting()
  2646. * @see drush_errors_off()
  2647. */
  2648. function drush_errors_on($errors = null) {
  2649. if (is_null($errors)) {
  2650. $errors = error_reporting();
  2651. }
  2652. else {
  2653. drush_set_context('DRUSH_ERROR_REPORTING', $errors);
  2654. }
  2655. error_reporting(drush_get_context('DRUSH_ERROR_REPORTING', $errors));
  2656. ini_set('display_errors', TRUE);
  2657. }
  2658. /**
  2659. * @} End of "defgroup errorhandling".
  2660. */
  2661. /**
  2662. * Test to see if a file exists and is not empty
  2663. */
  2664. function drush_file_not_empty($file_to_test) {
  2665. if (file_exists($file_to_test)) {
  2666. $stat = stat($file_to_test);
  2667. if ($stat['size'] > 0) {
  2668. return TRUE;
  2669. }
  2670. }
  2671. return FALSE;
  2672. }
  2673. /**
  2674. * Get the PHP memory_limit value in bytes.
  2675. */
  2676. function drush_memory_limit() {
  2677. $value = trim(ini_get('memory_limit'));
  2678. $last = strtolower($value[strlen($value)-1]);
  2679. switch ($last) {
  2680. case 'g':
  2681. $value *= DRUSH_DRUPAL_KILOBYTE;
  2682. case 'm':
  2683. $value *= DRUSH_DRUPAL_KILOBYTE;
  2684. case 'k':
  2685. $value *= DRUSH_DRUPAL_KILOBYTE;
  2686. }
  2687. return $value;
  2688. }
  2689. /**
  2690. * Unset the named key anywhere in the provided
  2691. * data structure.
  2692. */
  2693. function drush_unset_recursive(&$data, $unset_key) {
  2694. if (!empty($data) && is_array($data)) {
  2695. unset($data[$unset_key]);
  2696. foreach ($data as $key => $value) {
  2697. if (is_array($value)) {
  2698. drush_unset_recursive($data[$key], $unset_key);
  2699. }
  2700. }
  2701. }
  2702. }
  2703. /**
  2704. * Return a list of VCSs reserved files and directories.
  2705. */
  2706. function drush_version_control_reserved_files() {
  2707. static $files = FALSE;
  2708. if (!$files) {
  2709. // Also support VCSs that are not drush vc engines.
  2710. $files = array('.git', '.gitignore', '.hg', '.hgignore', '.hgrags');
  2711. $vcs = array_keys(drush_get_engines('version_control'));
  2712. foreach ($vcs as $name) {
  2713. drush_include_engine('version_control', $name);
  2714. $class = 'drush_pm_version_control_' . $name;
  2715. // For php < 5.3 we can't access a static method by referencing the class
  2716. // using a variable.
  2717. $version_control = new $class();
  2718. $files = array_merge($files, $version_control->reserved_files());
  2719. }
  2720. }
  2721. return $files;
  2722. }