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_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_cmp_error Check if a specific error status has been set.
drush_confirm Ask the user a basic yes/no question.
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_dispatch Dispatch a given set of commands. Modules can add commands by implementing hook_drush_command().
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
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_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_log Retrieve the log messages from the log history
drush_get_option_help Get the available options for Drush for use by help page.
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_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_move_dir Move $src to $dest. If the php 'rename' function doesn't work, then we'll try rsync.
drush_op Calls a given function, passing through all arguments unchanged.
drush_pipe_output
drush_print Prints a message with optional indentation. In general, drush_log($message, 'ok') is often a better choice than this function. That gets your confirmation message (for example) into the logs for this drush request. Consider that drush…
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_redispatch_get_options Get the options for this command.
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_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_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_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.
dt Rudimentary replacement for Drupal API t() function.
system_watchdog Log Drupal watchdog() calls.
_drush_delete_registered_files Delete all of the registered temporary files.
_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_replace_query_placeholders Replace named placeholders in a WHERE snippet.
_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. * Dispatch a given set of commands.
  12. * Modules can add commands by implementing hook_drush_command().
  13. *
  14. * @param
  15. * Command whose callback you want to call, defaults to current command.
  16. */
  17. function drush_dispatch($command = NULL) {
  18. $command = ($command) ? $command : drush_get_command();
  19. $return = FALSE;
  20. if ($command) {
  21. // Add command-specific options, if applicable
  22. drush_command_default_options($command);
  23. if (isset($command['must-replace-spaces'])) {
  24. $required_name = str_replace(' ', '-', $command['command']);
  25. drush_set_error(dt('Notice: "!name" must be renamed to "!requiredname" in order to work with this version of drush. If you are the maintainer for the module that defines this command, please rename it immediately. If you are a user of this command, you may enable spaces in commands for a while by setting "allow-spaces-in-commands" in your drush configuration file. See example.drushrc.php.', array('!name' => $command['command'], '!requiredname' => $required_name)));
  26. return FALSE;
  27. }
  28. // Print a warning if someone tries to use a deprecated alias.
  29. if (isset($command['deprecated'])) {
  30. drush_log(dt('Warning: The command name "!deprecated" is deprecated. Please use a recommended form instead (!recommended).', array('!deprecated' => $command['deprecated-name'], '!recommended' => implode(',', array_merge(array($command['command']), $command['aliases'])))), 'warning');
  31. if (isset($command['must-not-use-spaces'])) {
  32. drush_set_error(dt('You may enable spaces in commands for a while by setting "allow-spaces-in-commands" in your drush configuration file. See example.drushrc.php.', array('!name' => $command['command'], '!requiredname' => $required_name)));
  33. return FALSE;
  34. }
  35. }
  36. // Print a warning if a command callback function is misnamed
  37. if (isset($command['callback-required-prefix'])) {
  38. drush_log(dt('Warning: The command callback function !callback has a deprecated name. It must begin with !requiredprefix. Skipping hook functions.', array('!callback' => $command['callback'], '!requiredprefix' => $command['callback-required-prefix'])));
  39. }
  40. // Call the callback function of the active command.
  41. // TODO: If we make the required prefix actually required rather than just emitting a
  42. // warning, then this could become a direct call to drush_command (all commands with
  43. // the required prefix will now go through drush_command + drush_invoke).
  44. $return = call_user_func_array($command['callback'], $command['arguments']);
  45. }
  46. // prevent a '1' at the end of the output
  47. if ($return === TRUE) {
  48. $return = '';
  49. }
  50. // Add a final log entry, just so a timestamp appears.
  51. drush_log(dt('Command dispatch complete'), 'notice');
  52. return $return;
  53. }
  54. /**
  55. * Include a file, selecting a version specific file if available.
  56. *
  57. * For example, if you pass the path "/var/drush" and the name
  58. * "update" when bootstrapped on a Drupal 6 site it will first check for
  59. * the presence of "/var/drush/update_6.inc" in include it if exists. If this
  60. * file does NOT exist it will proceed and check for "/var/drush/update.inc".
  61. * If neither file exists, it will return FALSE.
  62. *
  63. * @param $path
  64. * The path you want to search.
  65. * @param $name
  66. * The file base name you want to include (not including a version suffix
  67. * or extension).
  68. * @param $version
  69. * The version suffix you want to include (could be specific to the software
  70. * or platform your are connecting to) - defaults to the current Drupal core
  71. * major version.
  72. * @param $extension
  73. * The extension - defaults to ".inc".
  74. *
  75. * @return
  76. * TRUE if the file was found and included.
  77. */
  78. function drush_include($path, $name, $version = NULL, $extension = 'inc') {
  79. $version = ($version) ? $version : drush_drupal_major_version();
  80. $file = sprintf("%s/%s_%s.%s", $path, $name, $version, $extension);
  81. if (file_exists($file)) {
  82. // drush_log(dt('Including version specific file : @file', array('@file' => $file)));
  83. include_once($file);
  84. return TRUE;
  85. }
  86. $file = sprintf("%s/%s.%s", $path, $name, $extension);
  87. if (file_exists($file)) {
  88. // drush_log(dt('Including non-version specific file : @file', array('@file' => $file)));
  89. include_once($file);
  90. return TRUE;
  91. }
  92. }
  93. /**
  94. * Return a structured array of engines of a specific type from commandfiles
  95. * implementing hook_drush_engine_$type.
  96. *
  97. * Engines are pluggable subsystems. Each engine of a specific type will
  98. * implement the same set of API functions and perform the same high-level
  99. * task using a different backend or approach.
  100. *
  101. * This function/hook is useful when you have a selection of several mutually
  102. * exclusive options to present to a user to select from.
  103. *
  104. * Other commands are able to extend this list and provide their own engines.
  105. * The hook can return useful information to help users decide which engine
  106. * they need, such as description or list of available engine options.
  107. *
  108. * The engine path element will automatically default to a subdirectory (within
  109. * the directory of the commandfile that implemented the hook) with the name of
  110. * the type of engine - e.g. an engine "wget" of type "handler" provided by
  111. * the "pm" commandfile would automatically be found if the file
  112. * "pm/handler/wget.inc" exists and a specific path is not provided.
  113. *
  114. * @param $type
  115. * The type of engine.
  116. *
  117. * @return
  118. * A structured array of engines.
  119. */
  120. function drush_get_engines($type) {
  121. $engines = array();
  122. $list = drush_commandfile_list();
  123. foreach ($list as $commandfile => $path) {
  124. if (drush_command_hook($commandfile, 'drush_engine_' . $type)) {
  125. $function = $commandfile . '_drush_engine_' . $type;
  126. $result = $function();
  127. foreach ((array)$result as $key => $engine) {
  128. // Add some defaults
  129. $engine += array(
  130. 'commandfile' => $commandfile,
  131. // Engines by default live in a subdirectory of the commandfile that
  132. // declared them, named as per the type of engine they are.
  133. 'path' => sprintf("%s/%s", dirname($path), $type),
  134. );
  135. $engines[$key] = $engine;
  136. }
  137. }
  138. }
  139. return $engines;
  140. }
  141. /**
  142. * Include the engine code for a specific named engine of a certain type.
  143. *
  144. * If the engine type has implemented hook_drush_engine_$type the path to the
  145. * engine specified in the array will be used.
  146. *
  147. * If you don't need to present any user options for selecting the engine
  148. * (which is common if the selection is implied by the running environment)
  149. * and you don't need to allow other modules to define their own engines you can
  150. * simply pass the $path to the directory where the engines are, and the
  151. * appropriate one will be included.
  152. *
  153. * Unlike drush_include this function will set errors if the requested engine
  154. * cannot be found.
  155. *
  156. * @param $type
  157. * The type of engine.
  158. * @param $engine
  159. * The key for the engine to be included.
  160. * @param $version
  161. * The version of the engine to be included - defaults to the current Drupal core
  162. * major version.
  163. * @param $path
  164. * A path to include from, if the engine has no corresponding
  165. * hook_drush_engine_$type item path.
  166. * @return unknown_type
  167. */
  168. function drush_include_engine($type, $engine, $version = NULL, $path = NULL) {
  169. $engines = drush_get_engines($type);
  170. if (!$path && isset($engines[$engine])) {
  171. $path = $engines[$engine]['path'];
  172. }
  173. if (!$path) {
  174. return drush_set_error('DRUSH_ENGINE INCLUDE_NO_PATH', dt('No !path was set for including the !type engine !engine.', array('!path' => $path, '!type' => $type, '!engine' => $engine)));
  175. }
  176. if (drush_include($path, $engine, $version)) {
  177. return TRUE;
  178. }
  179. 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)));
  180. }
  181. /**
  182. * Detects the version number of the current Drupal installation,
  183. * if any. Returns FALSE if there is no current Drupal installation,
  184. * or it is somehow broken.
  185. *
  186. * @return
  187. * A string containing the version number of the current
  188. * Drupal installation, if any. Otherwise, return FALSE.
  189. */
  190. function drush_drupal_version() {
  191. static $version = FALSE;
  192. if (!$version) {
  193. if ($drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT')) {
  194. // D7 stores VERSION in bootstrap.inc
  195. $version_constant_paths = array('/modules/system/system.module', '/includes/bootstrap.inc');
  196. foreach ($version_constant_paths as $path) {
  197. if (file_exists($drupal_root . $path)) {
  198. require_once($drupal_root . $path);
  199. }
  200. }
  201. // We just might be dealing with an early Drupal version (pre 4.7)
  202. if (defined('VERSION')) {
  203. $version = VERSION;
  204. }
  205. }
  206. }
  207. return $version;
  208. }
  209. function drush_drupal_cache_clear_all() {
  210. $prior = drush_get_context('DRUSH_AFFIRMATIVE');
  211. drush_set_context('DRUSH_AFFIRMATIVE', TRUE);
  212. drush_invoke('cache-clear', 'all');
  213. drush_set_context('DRUSH_AFFIRMATIVE', $prior);
  214. }
  215. /**
  216. * Returns the Drupal major version number (5, 6, 7 ...)
  217. */
  218. function drush_drupal_major_version() {
  219. $major_version = FALSE;
  220. if ($version = drush_drupal_version()) {
  221. $version_parts = explode('.', $version);
  222. if (is_numeric($version_parts[0])) {
  223. $major_version = (integer)$version_parts[0];
  224. }
  225. }
  226. return $major_version;
  227. }
  228. /**
  229. * Replace named placeholders in a WHERE snippet.
  230. *
  231. * Helper function to allow the usage of Drupal 7 WHERE snippets
  232. * with named placeholders in code for Drupal 5 and 6.
  233. *
  234. * @param $where
  235. * Stringwith a WHERE snippet using named placeholders.
  236. * @param $args
  237. * Array of placeholder values.
  238. * @return
  239. * String. $where filled with literals from $args.
  240. */
  241. function _drush_replace_query_placeholders($where, $args) {
  242. foreach ($args as $key => $data) {
  243. if (is_array($data)) {
  244. $new_keys = array();
  245. // $data can't have keys that are a prefix of other keys to
  246. // prevent a corrupted result in the below calls to str_replace().
  247. // To avoid this we will use a zero padded indexed array of the values of $data.
  248. $pad_length = strlen((string)count(array_values($data)));
  249. foreach (array_values($data) as $i => $value) {
  250. if (!is_numeric($value)) {
  251. $value = "'".$value."'";
  252. }
  253. $new_keys[$key . '_' . str_pad($i, $pad_length, '0', STR_PAD_LEFT)] = $value;
  254. }
  255. $where = preg_replace('#' . $key . '\b#', implode(', ', array_keys($new_keys)), $where);
  256. unset($args[$key]);
  257. $args += $new_keys;
  258. }
  259. else if (!is_numeric($data)) {
  260. $args[$key] = "'".$data."'";
  261. }
  262. }
  263. foreach ($args as $key => $data) {
  264. $where = str_replace($key, $data, $where);
  265. }
  266. return $where;
  267. }
  268. /**
  269. * A db_select() that works for any version of Drupal.
  270. *
  271. * @param $table
  272. * String. The table to operate on.
  273. * @param $fields
  274. * Array or string. Fields affected in this operation. Valid string values are '*' or a single column name.
  275. * @param $where
  276. * String. WHERE snippet for the operation. It uses named placeholders. see @_drush_replace_query_placeholders()
  277. * @param $args
  278. * Array. Arguments for the WHERE snippet.
  279. * @param $start
  280. * Int. Value for OFFSET.
  281. * @param $length
  282. * Int. Value for LIMIT.
  283. * @param $order_by_field
  284. * String. Database column to order by.
  285. * @param $order_by_direction
  286. * ('ASC', 'DESC'). Ordering direction.
  287. * @return
  288. * A database resource.
  289. */
  290. function drush_db_select($table, $fields = '*', $where = NULL, $args = NULL, $start = NULL, $length = NULL, $order_by_field = NULL, $order_by_direction = 'ASC') {
  291. if (drush_drupal_major_version() >= 7) {
  292. if (!is_array($fields)) {
  293. if ($fields == '*') {
  294. $fields = array();
  295. }
  296. else {
  297. $fields = array($fields);
  298. }
  299. }
  300. $query = db_select($table, $table)
  301. ->fields($table, $fields);
  302. if (!empty($where)) {
  303. $query = $query->where($where, $args);
  304. }
  305. if (!is_null($order_by_field)) {
  306. $query = $query->orderBy($order_by_field, $order_by_direction);
  307. }
  308. if (!is_null($length)) {
  309. $query = $query->range($start, $length);
  310. }
  311. return $query->execute();
  312. }
  313. else {
  314. if (is_array($fields)) {
  315. $fields = implode(', ', $fields);
  316. }
  317. $query = "SELECT $fields FROM {{$table}}";
  318. if (!empty($where)) {
  319. $where = _drush_replace_query_placeholders($where, $args);
  320. $query .= " WHERE ".$where;
  321. }
  322. if (!is_null($order_by_field)) {
  323. $query .= " ORDER BY $order_by_field $order_by_direction";
  324. }
  325. if (!is_null($length)) {
  326. $limit = " LIMIT $length";
  327. if (!is_null($start)) {
  328. $limit .= " OFFSET $start";
  329. }
  330. $query .= $limit;
  331. }
  332. return db_query($query, $args);
  333. }
  334. }
  335. /**
  336. * A db_delete() that works for any version of Drupal.
  337. *
  338. * @param $table
  339. * String. The table to operate on.
  340. * @param $where
  341. * String. WHERE snippet for the operation. It uses named placeholders. see @_drush_replace_query_placeholders()
  342. * @param $args
  343. * Array. Arguments for the WHERE snippet.
  344. * @return
  345. * Affected rows or FALSE.
  346. */
  347. function drush_db_delete($table, $where = NULL, $args = NULL) {
  348. if (drush_drupal_major_version() >= 7) {
  349. if (!empty($where)) {
  350. $query = db_delete($table)->where($where, $args);
  351. return $query->execute();
  352. }
  353. else {
  354. return db_truncate($table)->execute();
  355. }
  356. }
  357. else {
  358. $query = "DELETE FROM {{$table}}";
  359. if (!empty($where)) {
  360. $where = _drush_replace_query_placeholders($where, $args);
  361. $query .= ' WHERE '.$where;
  362. }
  363. if (!db_query($query, $args)) {
  364. return FALSE;
  365. }
  366. return db_affected_rows();
  367. }
  368. }
  369. /**
  370. * A db_result() that works consistently for any version of Drupal.
  371. *
  372. * @param
  373. * A Database result object.
  374. */
  375. function drush_db_result($result) {
  376. switch (drush_drupal_major_version()) {
  377. case 5:
  378. // In versions of Drupal <= 5, db_result only returns the first row no matter how
  379. // many times you call it. So instead of calling it here, we use db_fetch_array which
  380. // does increment the pointer to the next row (as db_result does on Drupal 6)
  381. if ($array = db_fetch_array($result)) {
  382. return array_shift($array); // return first element in array.
  383. }
  384. case 6:
  385. return db_result($result);
  386. case 7:
  387. default:
  388. return $result->fetchField();
  389. }
  390. }
  391. /**
  392. * A db_fetch_object() that works for any version of Drupal.
  393. *
  394. * @param
  395. * A Database result object.
  396. */
  397. function drush_db_fetch_object($result) {
  398. return drush_drupal_major_version() >= 7 ? $result->fetchObject() : db_fetch_object($result);
  399. }
  400. /**
  401. * Save a string to a temporary file. Does not depend on Drupal's API.
  402. * The temporary file will be automatically deleted when drush exits.
  403. *
  404. * @param string $data
  405. * @return string
  406. * A path to the file.
  407. */
  408. function drush_save_data_to_temp_file($data) {
  409. static $fp;
  410. $fp = tmpfile();
  411. fwrite($fp, $data);
  412. $meta_data = stream_get_meta_data($fp);
  413. $file = $meta_data['uri'];
  414. drush_register_file_for_deletion($file);
  415. return $file;
  416. }
  417. /**
  418. * Creates a temporary file, and registers it so that
  419. * it will be deleted when drush exits. Whenever possible,
  420. * drush_save_data_to_temp_file() should be used instead
  421. * of this function.
  422. */
  423. function drush_tempnam($pattern, $tmp_dir = NULL) {
  424. if ($tmp_dir == NULL) {
  425. $tmp_dir = sys_get_temp_dir();
  426. }
  427. $tmp_file = tempnam($tmp_dir, $pattern);
  428. drush_register_file_for_deletion($tmp_file);
  429. return $tmp_file;
  430. }
  431. /**
  432. * Any file passed in to this function will be deleted
  433. * when drush exits.
  434. */
  435. function drush_register_file_for_deletion($file = NULL) {
  436. static $registered_files = array();
  437. if (isset($file)) {
  438. if (empty($registered_files)) {
  439. register_shutdown_function('_drush_delete_registered_files');
  440. }
  441. $registered_files[] = $file;
  442. }
  443. return $registered_files;
  444. }
  445. /**
  446. * Delete all of the registered temporary files.
  447. */
  448. function _drush_delete_registered_files() {
  449. $files_to_delete = drush_register_file_for_deletion();
  450. foreach ($files_to_delete as $file) {
  451. // We'll make sure that the file still exists, just
  452. // in case someone came along and deleted it, even
  453. // though they did not need to.
  454. if (file_exists($file)) {
  455. unlink($file);
  456. }
  457. }
  458. }
  459. /**
  460. * Deletes the provided file or folder and
  461. * everything inside it.
  462. *
  463. * @param $dir
  464. * The directory to delete
  465. * @return
  466. * FALSE on failure, TRUE if everything was deleted
  467. */
  468. function drush_delete_dir($dir) {
  469. if (!file_exists($dir)) {
  470. return TRUE;
  471. }
  472. if (!is_dir($dir)) {
  473. return unlink($dir);
  474. }
  475. foreach (scandir($dir) as $item) {
  476. if ($item == '.' || $item == '..') {
  477. continue;
  478. }
  479. if (!drush_delete_dir($dir.DIRECTORY_SEPARATOR.$item)) {
  480. return FALSE;
  481. }
  482. }
  483. return rmdir($dir);
  484. }
  485. /**
  486. * Move $src to $dest. If the php 'rename' function
  487. * doesn't work, then we'll try rsync.
  488. *
  489. * @param $src
  490. * The directory to move.
  491. * @param $dest
  492. * The destination to move the source to, including
  493. * the new name of the folder. To move folder "a"
  494. * from "/b" to "/c", then $src = "/b/a" and $dest = "/c/a".
  495. * To move "a" to "/c" and rename it to "d", then
  496. * $dest = "/c/d" (just like php rename function).
  497. * @param $overwrite
  498. * If TRUE, the destination will be deleted if it
  499. * exists. Defaults to FALSE.
  500. * @return
  501. * TRUE on success, FALSE on failure
  502. */
  503. function drush_move_dir($src, $dest, $overwrite = FALSE) {
  504. // Preflight based on $overwrite if $dest exists.
  505. if (is_dir($dest)) {
  506. if ($overwrite) {
  507. drush_op('drush_delete_dir', $dest);
  508. }
  509. else {
  510. return drush_set_error('DRUSH_DESTINATION_EXISTS', dt('Destination directory !dest already exists.', array('!dest' => $dest)));
  511. }
  512. }
  513. // If rename works, then we're done.
  514. if (@drush_op('rename', $src, $dest)) {
  515. return TRUE;
  516. }
  517. // Bail if we can't make a directory at the
  518. // destination (e.g. permissions)
  519. if (!is_dir($dest) && (drush_op('mkdir', $dest) === FALSE)) {
  520. return FALSE;
  521. }
  522. // If rename doesn't work, then try rsync.
  523. $exec = 'rsync -raz --remove-source-files ' . $src . DIRECTORY_SEPARATOR . ' ' . $dest;
  524. $rsync_result = drush_op('system', $exec);
  525. if($rsync_result !== FALSE) {
  526. // --remove-source-files deletes all of the files, but
  527. // we still need to get rid of the directories.
  528. drush_op('drush_delete_dir', $src);
  529. return TRUE;
  530. }
  531. return FALSE;
  532. }
  533. /**
  534. * Calls a given function, passing through all arguments unchanged.
  535. *
  536. * This should be used when calling possibly mutative or destructive functions
  537. * (e.g. unlink() and other file system functions) so that can be suppressed
  538. * if the simulation mode is enabled.
  539. *
  540. * @param $function
  541. * The name of the function.
  542. * @return
  543. * The return value of the function, or TRUE if simulation mode is enabled.
  544. */
  545. function drush_op($function) {
  546. $args = func_get_args();
  547. array_shift($args); // Skip function name
  548. if (drush_get_context('DRUSH_VERBOSE') || drush_get_context('DRUSH_SIMULATE')) {
  549. drush_print("Calling $function(". implode(", ", $args) .')');
  550. }
  551. if (drush_get_context('DRUSH_SIMULATE')) {
  552. return TRUE;
  553. }
  554. return call_user_func_array($function, $args);
  555. }
  556. /**
  557. * Rudimentary replacement for Drupal API t() function.
  558. *
  559. * @param string
  560. * String to process, possibly with replacement item.
  561. * @param array
  562. * An associative array of replacement items.
  563. *
  564. * @return
  565. * The processed string.
  566. *
  567. * @see t()
  568. */
  569. function dt($string, $args = array()) {
  570. if (function_exists('t')) {
  571. return t($string, $args);
  572. }
  573. else {
  574. if (!empty($args)) {
  575. return strtr($string, $args);
  576. }
  577. else {
  578. return $string;
  579. }
  580. }
  581. }
  582. /**
  583. * Get the available options for Drush for use by help page.
  584. *
  585. * @return
  586. * An associative array containing the option definition as the key, and the description as the value,
  587. * for each of the available options.
  588. */
  589. function drush_get_option_help() {
  590. // TODO: Add a hook for this, to allow other modules to add their global options
  591. $options['-r <path>, --root=<path>'] = dt("Drupal root directory to use (default: current directory)");
  592. $options['-l <uri>, --uri=http://example.com'] = dt('URI of the drupal site to use (only needed in multisite environments)');
  593. $options['-v, --verbose'] = dt('Display extra information about the command.');
  594. $options['-d, --debug'] = dt('Display even more information, including internal messages.');
  595. $options['-q, --quiet'] = dt('Hide all output');
  596. $options['-y, --yes'] = dt("Assume 'yes' as answer to all prompts");
  597. $options['-n, --no'] = dt("Assume 'no' as answer to all prompts");
  598. $options['-s, --simulate'] = dt("Simulate all relevant actions (don't actually change the system)");
  599. $options['-i, --include'] = dt("A list of paths to search for drush commands");
  600. $options['-c, --config'] = dt("Specify a config file to use. See example.drushrc.php");
  601. $options['-u, --user'] = dt("Specify a user to login with. May be a name or a number.");
  602. $options['-b, --backend'] = dt("Hide all output and return structured data (internal use only).");
  603. $options['-p, --pipe'] = dt("Emit a compact representation of the command for scripting.");
  604. $options['--nocolor'] = dt("Suppress color highlighting on log messages.");
  605. $options['--show-passwords'] = dt("Show database passwords in commands that display connection information.");
  606. $options['-h, --help'] = dt("This help system.");
  607. $options['--php'] = dt("The absolute path to your PHP intepreter, if not 'php' in the path.");
  608. return $options;
  609. }
  610. /**
  611. * Prints out help for a given command.
  612. */
  613. function drush_show_help($commands) {
  614. $phases = _drush_bootstrap_phases();
  615. $commandstring = implode(" ", $commands);
  616. foreach ($phases as $phase_index) {
  617. if ($validated = drush_bootstrap_validate($phase_index)) {
  618. if ($phase_index > drush_get_context('DRUSH_BOOTSTRAP_PHASE')) {
  619. drush_bootstrap($phase_index);
  620. }
  621. }
  622. if (!drush_get_error()) {
  623. $commands = drush_get_commands();
  624. if (array_key_exists($commandstring, $commands)) {
  625. $command = $commands[$commandstring];
  626. if ($validated && $command['bootstrap'] > $phase_index) {
  627. continue;
  628. }
  629. // Merge in engine specific help.
  630. foreach ($command['engines'] as $type => $description) {
  631. $all_engines = drush_get_engines($type);
  632. foreach ($all_engines as $name => $engine) {
  633. $command = array_merge_recursive($command, $engine);
  634. }
  635. }
  636. if (!$help = drush_command_invoke_all('drush_help', 'drush:'. $command['command'])) {
  637. $help = array($command['description']);
  638. }
  639. drush_print(wordwrap(implode("\n", $help), drush_get_context('DRUSH_COLUMNS', 80)));
  640. drush_print();
  641. // TODO: Let commands define additional sections.
  642. $sections = array(
  643. 'examples' => 'Examples',
  644. 'arguments' => 'Arguments',
  645. 'options' => 'Options',
  646. );
  647. foreach ($sections as $key => $value) {
  648. if (!empty($command[$key])) {
  649. drush_print(dt($value) . ':');
  650. foreach ($command[$key] as $name => $description) {
  651. // '[command] is a token representing the current command. @see pm_drush_engine_version_control().
  652. $rows[] = array(str_replace('[command]', $commandstring, $name), dt($description));
  653. }
  654. drush_print_table($rows, false, array(40));
  655. unset($rows);
  656. drush_print();
  657. }
  658. }
  659. // Append aliases if any.
  660. if ($command['aliases']) {
  661. drush_print(dt("Aliases: ") . implode(', ', $command['aliases']));
  662. }
  663. return TRUE;
  664. }
  665. }
  666. else {
  667. break;
  668. }
  669. }
  670. return drush_set_error('DRUSH_COMMAND_NOT_FOUND', dt('Invalid command !command.', array('!command' => $commandstring)));
  671. }
  672. /**
  673. * Executes a shell command.
  674. * Output is only printed if in verbose mode.
  675. * Output is stored and can be retrieved using drush_shell_exec_output().
  676. * If in simulation mode, no action is taken.
  677. *
  678. * @param $cmd
  679. * The command to execute. May include placeholders used for sprintf.
  680. * @param ...
  681. * 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.
  682. * @return
  683. * 0 if success.
  684. */
  685. function drush_shell_exec($cmd) {
  686. $args = func_get_args();
  687. //do not change the command itself, just the parameters.
  688. for ($x = 1; $x < sizeof($args); $x++) {
  689. $args[$x] = escapeshellarg($args[$x]);
  690. }
  691. $command = call_user_func_array('sprintf', $args);
  692. if (drush_get_context('DRUSH_VERBOSE') || drush_get_context('DRUSH_SIMULATE')) {
  693. drush_log('Executing: ' . $command);
  694. }
  695. if (!drush_get_context('DRUSH_SIMULATE')) {
  696. exec($command . ' 2>&1', $output, $result);
  697. _drush_shell_exec_output_set($output);
  698. if (drush_get_context('DRUSH_DEBUG')) {
  699. foreach ($output as $line) {
  700. drush_print($line, 2);
  701. }
  702. }
  703. // Exit code 0 means success.
  704. return ($result == 0);
  705. }
  706. else {
  707. return 0;
  708. }
  709. }
  710. /**
  711. * Stores output for the most recent shell command.
  712. * This should only be run from drush_shell_exec().
  713. *
  714. * @param $output
  715. * The output of the most recent shell command.
  716. * If this is not set the stored value will be returned.
  717. */
  718. function _drush_shell_exec_output_set($output = FALSE) {
  719. static $stored_output;
  720. if ($output === FALSE) return $stored_output;
  721. $stored_output = $output;
  722. }
  723. /**
  724. * Returns the output of the most recent shell command as an array of lines.
  725. */
  726. function drush_shell_exec_output() {
  727. return _drush_shell_exec_output_set();
  728. }
  729. /**
  730. * Exits with a message. In general, you should use drush_set_error() instead of
  731. * this function. That lets drush proceed with other tasks.
  732. * TODO: Exit with a correct status code.
  733. */
  734. function drush_die($msg = NULL, $status = NULL) {
  735. die($msg ? "drush: $msg\n" : '');
  736. }
  737. /**
  738. * Prints a message with optional indentation. In general,
  739. * drush_log($message, 'ok') is often a better choice than this function.
  740. * That gets your confirmation message (for example) into the logs for this
  741. * drush request. Consider that drush requests may be executed remotely and
  742. * non interactively.
  743. *
  744. * @param $message
  745. * The message to print.
  746. * @param $indent
  747. * The indentation (space chars)
  748. */
  749. function drush_print($message = '', $indent = 0) {
  750. $msg = str_repeat(' ', $indent) . (string)$message . "\n";
  751. if ($charset = drush_get_option('output_charset') && function_exists('iconv')) {
  752. $msg = iconv('UTF-8', $charset, $msg);
  753. }
  754. print $msg;
  755. }
  756. /**
  757. * Stores a message which is printed during drush_shutdown() if in compact mode.
  758. * @param $message
  759. * The message to print. If $message is an array,
  760. * then each element of the array is printed on a
  761. * separate line.
  762. */
  763. function drush_print_pipe($message = '') {
  764. $buffer = &drush_get_context('DRUSH_PIPE_BUFFER' , '');
  765. if (is_array($message)) {
  766. $message = implode("\n", $message) . "\n";
  767. }
  768. $buffer .= $message;
  769. }
  770. /**
  771. * Prints an array or string.
  772. * @param $array
  773. * The array to print.
  774. */
  775. function drush_print_r($array) {
  776. print_r($array);
  777. }
  778. /**
  779. * Ask the user a basic yes/no question.
  780. *
  781. * @param $msg The question to ask
  782. * @return TRUE if the user entered 'y', FALSE if he entered 'n'
  783. */
  784. function drush_confirm($msg, $indent = 0) {
  785. print str_repeat(' ', $indent) . (string)$msg . " (y/n): ";
  786. // Automatically accept confirmations if the --yes argument was supplied.
  787. if (drush_get_context('DRUSH_AFFIRMATIVE')) {
  788. print "y\n";
  789. return TRUE;
  790. }
  791. // Automatically cancel confirmations if the --no argument was supplied.
  792. elseif (drush_get_context('DRUSH_NEGATIVE')) {
  793. print "n\n";
  794. return FALSE;
  795. }
  796. // See http://drupal.org/node/499758 before changing this.
  797. $stdin = fopen("php://stdin","r");
  798. while ($line = fgets($stdin)) {
  799. $line = trim($line);
  800. if ($line == 'y') {
  801. return TRUE;
  802. }
  803. if ($line == 'n') {
  804. return FALSE;
  805. }
  806. print str_repeat(' ', $indent) . (string)$msg . " (y/n): ";
  807. }
  808. }
  809. /**
  810. * Ask the user to select an item from a list.
  811. * From a provided associative array, drush_choice will
  812. * display all of the questions, numbered from 1 to N,
  813. * and return the item the user selected. "0" is always
  814. * cancel; entering a blank line is also interpreted
  815. * as cancelling.
  816. *
  817. * @param $options
  818. * A list of questions to display to the user. The
  819. * KEYS of the array are the result codes to return to the
  820. * caller; the VALUES are the messages to display on
  821. * each line. Special keys of the form '-- something --' can be
  822. * provided as separator between choices groups. Separator keys
  823. * don't alter the numbering.
  824. * @param $prompt
  825. * The message to display to the user prompting for input.
  826. * @param $label
  827. * Controls the display of each line. Defaults to
  828. * '!value', which displays the value of each item
  829. * in the $options array to the user. Use '!key' to
  830. * display the key instead. In some instances, it may
  831. * be useful to display both the key and the value; for
  832. * example, if the key is a user id and the value is the
  833. * user name, use '!value (uid=!key)'.
  834. */
  835. function drush_choice($options, $prompt = 'Enter a number.', $label = '!value') {
  836. print dt($prompt) . "\n";
  837. drush_print(' [0] : Cancel');
  838. $selection_number = 0;
  839. foreach ($options as $key => $option) {
  840. if ((substr($key, 0, 3) == '-- ') && (substr($key, -3) == ' --')) {
  841. drush_print(" ".$option);
  842. continue;
  843. }
  844. $selection_number++;
  845. $message = dt($label, array('!number' => $selection_number, '!key' => $key, '!value' => $option));
  846. drush_print(dt(" [!number] : !message", array('!number' => $selection_number, '!message' => $message)));
  847. $selection_list[$selection_number] = $key;
  848. }
  849. while ($line = trim(fgets(STDIN))) {
  850. if (array_key_exists($line, $selection_list)) {
  851. return $selection_list[$line];
  852. }
  853. }
  854. drush_print(dt('Cancelled'));
  855. return FALSE;
  856. }
  857. /**
  858. * Prompt the user for input
  859. *
  860. * The input can be anything that fits on a single line (not only y/n),
  861. * so we can't use drush_confirm()
  862. *
  863. * @param $prompt
  864. * The text which is displayed to the user.
  865. * @param $default
  866. * The default value of the input.
  867. * @param $optional
  868. * If TRUE, user may continue even when no value is in the input.
  869. *
  870. * @see drush_confirm()
  871. */
  872. function drush_prompt($prompt, $default = NULL, $optional = FALSE) {
  873. if (!is_null($default)) {
  874. $prompt .= " [" . $default . "]";
  875. }
  876. $prompt .= ": ";
  877. print $prompt;
  878. if (drush_get_context('DRUSH_AFFIRMATIVE')) {
  879. return $default;
  880. }
  881. $stdin = fopen('php://stdin', 'r');
  882. stream_set_blocking($stdin, TRUE);
  883. while (($line = fgets($stdin)) !== FALSE) {
  884. $line = trim($line);
  885. if ($line === "") {
  886. $line = $default;
  887. }
  888. if ($line || $optional) {
  889. break;
  890. }
  891. print $prompt;
  892. }
  893. fclose($stdin);
  894. return $line;
  895. }
  896. /**
  897. * Print a formatted table.
  898. *
  899. * @param $rows
  900. * The rows to print.
  901. * @param $header
  902. * If TRUE, the first line will be treated as table header and therefore be
  903. * underlined.
  904. * @param $widths
  905. * The widths of each column (in characters) to use - if not specified this
  906. * will be determined automatically, based on a "best fit" algorithm.
  907. */
  908. function drush_print_table($rows, $header = FALSE, $widths = array()) {
  909. $tbl = new Console_Table(CONSOLE_TABLE_ALIGN_LEFT , '');
  910. $auto_widths = drush_table_column_autowidth($rows, $widths);
  911. // Do wordwrap on all cells.
  912. $newrows = array();
  913. foreach ($rows as $rowkey => $row) {
  914. foreach ($row as $col_num => $cell) {
  915. $newrows[$rowkey][$col_num] = wordwrap($cell, $auto_widths[$col_num], "\n", TRUE);
  916. if (isset($widths[$col_num])) {
  917. $newrows[$rowkey][$col_num] = str_pad($newrows[$rowkey][$col_num], $widths[$col_num]);
  918. }
  919. }
  920. }
  921. if ($header) {
  922. $headers = array_shift($newrows);
  923. $tbl->setHeaders($headers);
  924. }
  925. $tbl->addData($newrows);
  926. drush_print($tbl->getTable());
  927. return $tbl;
  928. }
  929. /**
  930. * Convert an associative array of key : value pairs into
  931. * a table suitable for processing by drush_print_table.
  932. *
  933. * @param $keyvalue_table
  934. * An associative array of key : value pairs.
  935. * @return
  936. * An array of arrays, where the keys from the input
  937. * array are stored in the first column, and the values
  938. * are stored in the third. A second colum is created
  939. * specifically to hold the ':' separator.
  940. */
  941. function drush_key_value_to_array_table($keyvalue_table) {
  942. $table = array();
  943. foreach ($keyvalue_table as $key => $value) {
  944. if (isset($value)) {
  945. $table[] = array($key, ' :', $value);
  946. }
  947. else {
  948. $table[] = array($key . ':', '', '');
  949. }
  950. }
  951. return $table;
  952. }
  953. /**
  954. * Determine the best fit for column widths.
  955. *
  956. * @param $rows
  957. * The rows to use for calculations.
  958. * @param $widths
  959. * Manually specified widths of each column (in characters) - these will be
  960. * left as is.
  961. */
  962. function drush_table_column_autowidth($rows, $widths) {
  963. $auto_widths = $widths;
  964. // First we determine the distribution of row lengths in each column.
  965. // This is an array of descending character length keys (i.e. starting at
  966. // the rightmost character column), with the value indicating the number
  967. // of rows where that character column is present.
  968. $col_dist = array();
  969. foreach ($rows as $rowkey => $row) {
  970. foreach ($row as $col_num => $cell) {
  971. if (empty($widths[$col_num])) {
  972. $length = strlen($cell);
  973. while ($length > 0) {
  974. if (!isset($col_dist[$col_num][$length])) {
  975. $col_dist[$col_num][$length] = 0;
  976. }
  977. $col_dist[$col_num][$length]++;
  978. $length--;
  979. }
  980. }
  981. }
  982. }
  983. foreach ($col_dist as $col_num => $count) {
  984. // Sort the distribution in decending key order.
  985. krsort($col_dist[$col_num]);
  986. // Initially we set all columns to their "ideal" longest width
  987. // - i.e. the width of their longest column.
  988. $auto_widths[$col_num] = max(array_keys($col_dist[$col_num]));
  989. }
  990. // We determine what width we have available to use, and what width the
  991. // above "ideal" columns take up.
  992. $available_width = drush_get_context('DRUSH_COLUMNS', 80) - (count($auto_widths) * 2);
  993. $auto_width_current = array_sum($auto_widths);
  994. // If we need to reduce a column so that we can fit the space we use this
  995. // loop to figure out which column will cause the "least wrapping",
  996. // (relative to the other columns) and reduce the width of that column.
  997. while ($auto_width_current > $available_width) {
  998. $count = 0;
  999. $width = 0;
  1000. foreach ($col_dist as $col_num => $counts) {
  1001. // If we are just starting out, select the first column.
  1002. if ($count == 0 ||
  1003. // OR: if this column would cause less wrapping than the currently
  1004. // selected column, then select it.
  1005. (current($counts) < $count) ||
  1006. // OR: if this column would cause the same amount of wrapping, but is
  1007. // longer, then we choose to wrap the longer column (proportionally
  1008. // less wrapping, and helps avoid triple line wraps).
  1009. (current($counts) == $count && key($counts) > $width)) {
  1010. // Select the column number, and record the count and current width
  1011. // for later comparisons.
  1012. $column = $col_num;
  1013. $count = current($counts);
  1014. $width = key($counts);
  1015. }
  1016. }
  1017. if ($width <= 1) {
  1018. // If we have reached a width of 1 then give up, so wordwrap can still progress.
  1019. break;
  1020. }
  1021. // Reduce the width of the selected column.
  1022. $auto_widths[$column]--;
  1023. // Reduce our overall table width counter.
  1024. $auto_width_current--;
  1025. // Remove the corresponding data from the disctribution, so next time
  1026. // around we use the data for the row to the left.
  1027. unset($col_dist[$column][$width]);
  1028. }
  1029. return $auto_widths;
  1030. }
  1031. /**
  1032. * @defgroup dispatching Command dispatching functions.
  1033. * @{
  1034. *
  1035. * These functions manage parameter and option manipulation
  1036. * for calls to drush backend invoke.
  1037. */
  1038. /**
  1039. * Process commands that are executed on a remote drush instance.
  1040. *
  1041. * @return
  1042. * TRUE if the command was handled remotely.
  1043. */
  1044. function drush_remote_command() {
  1045. // The command will be executed remotely if the --remote-host flag
  1046. // is set; note that if a site alias is provided on the command line,
  1047. // and the site alias references a remote server, then the --remote-host
  1048. // option will be set when the site alias is processed.
  1049. // @see _drush_process_site_alias
  1050. $remote_host = drush_get_option('remote-host');
  1051. if (isset($remote_host)) {
  1052. $args = drush_get_arguments();
  1053. $command = array_shift($args);
  1054. $remote_user = drush_get_option('remote-user');
  1055. drush_do_command_redispatch($command, $args, $remote_host, $remote_user);
  1056. return TRUE;
  1057. }
  1058. // If the --site-list flag is set, then we will execute the specified
  1059. // command once for every site listed in the site list.
  1060. $site_list = drush_get_option('site-list');
  1061. if (isset($site_list)) {
  1062. if (!is_array($site_list)) {
  1063. $site_list = explode(',', $site_list);
  1064. }
  1065. $args = drush_get_arguments();
  1066. if (!drush_get_context('DRUSH_SIMULATE')) {
  1067. drush_print(dt("You are about to execute '!command' on all of the following targets:", array('!command' => implode(" ", $args))));
  1068. foreach ($site_list as $one_destination) {
  1069. drush_print(dt(' !target', array('!target' => $one_destination)));
  1070. }
  1071. if (drush_confirm('Continue? ') === FALSE) {
  1072. drush_die('Aborting.');
  1073. }
  1074. }
  1075. $command = array_shift($args);
  1076. foreach ($site_list as $site_spec) {
  1077. $values = drush_do_site_command(_drush_sitealias_get_record($site_spec), $command, $args);
  1078. drush_print($values['output']);
  1079. }
  1080. return TRUE;
  1081. }
  1082. return FALSE;
  1083. }
  1084. /**
  1085. * Used by functions that operate on lists of sites, moving
  1086. * information from the source to the destination. Currenlty
  1087. * this includes 'drush rsync' and 'drush sql sync'.
  1088. */
  1089. function drush_do_multiple_command($command, $source_record, $destination_record, $allow_single_source = FALSE) {
  1090. $is_multiple_command = FALSE;
  1091. if ((($allow_single_source == TRUE) || array_key_exists('site-list', $source_record)) && array_key_exists('site-list', $destination_record)) {
  1092. $is_multiple_command = TRUE;
  1093. $source_path = array_key_exists('path-component', $source_record) ? $source_record['path-component'] : '';
  1094. $destination_path = array_key_exists('path-component', $destination_record) ? $destination_record['path-component'] : '';
  1095. $target_list = array_values(drush_sitealias_resolve_sitelist($destination_record));
  1096. if (array_key_exists('site-list', $source_record)) {
  1097. $source_list = array_values(drush_sitealias_resolve_sitelist($source_record));
  1098. if (drush_sitealias_check_lists_alignment($source_list, $target_list) === FALSE) {
  1099. if (array_key_exists('unordered-list', $source_record) || array_key_exists('unordered-list', $destination_record)) {
  1100. drush_sitelist_align_lists($source_list, $target_list, $aligned_source, $aligned_target);
  1101. $source_list = $aligned_source;
  1102. $target_list = $aligned_target;
  1103. }
  1104. }
  1105. }
  1106. else {
  1107. $source_list = array_fill(0, count($target_list), $source_record);
  1108. }
  1109. if (!drush_get_context('DRUSH_SIMULATE')) {
  1110. drush_print(dt('You are about to !command between all of the following targets:', array('!command' => $command)));
  1111. $i = 0;
  1112. foreach ($source_list as $one_source) {
  1113. $one_target = $target_list[$i];
  1114. ++$i;
  1115. 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)));
  1116. }
  1117. if (drush_confirm('Continue? ') === FALSE) {
  1118. drush_die('Aborting.');
  1119. }
  1120. }
  1121. $data = drush_redispatch_get_options();
  1122. $i = 0;
  1123. foreach ($source_list as $one_source) {
  1124. $one_target = $target_list[$i];
  1125. ++$i;
  1126. $source_spec = drush_sitealias_alias_record_to_spec($one_source);
  1127. $target_spec = drush_sitealias_alias_record_to_spec($one_target);
  1128. drush_log(dt('Begin do_multiple !command via backend invoke', array('!command' => $command)));
  1129. $values = drush_backend_invoke_args($command, array($source_spec . $source_path, $target_spec . $destination_path), $data, 'GET', TRUE);
  1130. drush_log(dt('Backend invoke is complete'));
  1131. }
  1132. }
  1133. return $is_multiple_command;
  1134. }
  1135. function drush_do_site_command($site_record, $command, $args = array(), $data = array(), $integrate = FALSE) {
  1136. $values = NULL;
  1137. if (!empty($site_record)) {
  1138. foreach ($site_record as $key => $value) {
  1139. if (!isset($data[$key]) && !in_array($key, drush_sitealias_site_selection_keys())) {
  1140. $data[$key] = $site_record[$key];
  1141. }
  1142. }
  1143. $drush_path = NULL;
  1144. if (array_key_exists('path-aliases', $site_record)) {
  1145. if (array_key_exists('%drush-script', $site_record['path-aliases'])) {
  1146. $drush_path = $site_record['path-aliases']['%drush-script'];
  1147. }
  1148. }
  1149. $values = drush_backend_invoke_args($command, $args, $data, 'GET', $integrate, $drush_path, array_key_exists('remote-host', $site_record) ? $site_record['remote-host'] : NULL, array_key_exists('remote-user', $site_record) ? $site_record['remote-user'] : NULL, array_key_exists('ssh-options', $site_record) ? $site_record['ssh-options'] : NULL);
  1150. }
  1151. return $values;
  1152. }
  1153. /**
  1154. * Redispatch the specified command using the same
  1155. * options that were passed to this invocation of drush.
  1156. */
  1157. function drush_do_command_redispatch($command, $args = array(), $remote_host = NULL, $remote_user = NULL, $drush_path = NULL) {
  1158. $data = drush_redispatch_get_options();
  1159. // If the path to drush was supplied, then pass it to backend invoke.
  1160. if ($drush_path == NULL) {
  1161. $drush_path = drush_get_option('drush-script');
  1162. if (!isset($drush_path)) {
  1163. $drush_folder = drush_get_option('drush');
  1164. if (isset($drush)) {
  1165. $drush_path = $drush_folder . '/drush';
  1166. }
  1167. }
  1168. }
  1169. // Call through to backend invoke.
  1170. drush_log(dt('Begin redispatch via backend invoke'));
  1171. $values = drush_backend_invoke_args($command, $args, $data, 'GET', TRUE, $drush_path, $remote_host, $remote_user);
  1172. drush_log(dt('Backend invoke is complete'));
  1173. return $values;
  1174. }
  1175. /**
  1176. * Get the options for this command.
  1177. *
  1178. * This function returns an array that contains all of the options
  1179. * that are appropriate for forwarding along to backend invoke.
  1180. * Pass the result from this function to backend invoke in the $data
  1181. * parameter when doing a redispatch.
  1182. */
  1183. function drush_redispatch_get_options() {
  1184. // Start off by taking everything from the site alias and command line
  1185. // ('options' context)
  1186. $alias_context = array_diff_key(drush_get_context('alias'), array_flip(drush_sitealias_site_selection_keys()));
  1187. $options = array_merge($alias_context, drush_get_context('options'));
  1188. unset($options['command-specific']);
  1189. unset($options['path-aliases']);
  1190. // If we can parse the current command, then examine all contexts
  1191. // in order for any option that is directly related to the current command
  1192. $command = drush_parse_command();
  1193. if (is_array($command)) {
  1194. foreach ($command['options'] as $key => $value) {
  1195. // Strip leading --
  1196. $key = ltrim($key, '-');
  1197. $value = drush_get_option($key);
  1198. if (isset($value)) {
  1199. $options[$key] = $value;
  1200. }
  1201. }
  1202. }
  1203. // 'php', if needed, will be included in DRUSH_COMMAND. If DRUSH_COMMAND
  1204. // is not used (e.g. when calling a remote instance of drush), then --php
  1205. // should not be passed along.
  1206. unset($options['php']);
  1207. return $options;
  1208. }
  1209. /**
  1210. * @} End of "defgroup dispatching".
  1211. */
  1212. /**
  1213. * @defgroup logging Logging information to be provided as output.
  1214. * @{
  1215. *
  1216. * These functions are primarily for diagnostic purposes, but also provide an overview of tasks that were taken
  1217. * by drush.
  1218. */
  1219. /**
  1220. * Add a log message to the log history.
  1221. *
  1222. * This function calls the callback stored in the 'DRUSH_LOG_CALLBACK' context with
  1223. * the resulting entry at the end of execution.
  1224. *
  1225. * This allows you to replace it with custom logging implementations if needed,
  1226. * such as logging to a file or logging to a database (drupal or otherwise).
  1227. *
  1228. * The default callback is the _drush_print_log() function with prints the messages
  1229. * to the shell.
  1230. *
  1231. * @param message
  1232. * String containing the message to be logged.
  1233. * @param type
  1234. * The type of message to be logged. Common types are 'warning', 'error', 'success' and 'notice'.
  1235. * A type of 'failed' can also be supplied to flag as an 'error'.
  1236. * A type of 'ok' or 'completed' can also be supplied to flag as a 'success'
  1237. * All other types of messages will be assumed to be notices.
  1238. */
  1239. function drush_log($message, $type = 'notice', $error = null) {
  1240. $log =& drush_get_context('DRUSH_LOG', array());
  1241. $callback = drush_get_context('DRUSH_LOG_CALLBACK', '_drush_print_log');
  1242. $entry = array(
  1243. 'type' => $type,
  1244. 'message' => $message,
  1245. 'timestamp' => microtime(TRUE),
  1246. 'memory' => memory_get_usage(),
  1247. );
  1248. $entry['error'] = $error;
  1249. $log[] = $entry;
  1250. return $callback($entry);
  1251. }
  1252. /**
  1253. * Retrieve the log messages from the log history
  1254. *
  1255. * @return
  1256. * Entire log history
  1257. */
  1258. function drush_get_log() {
  1259. return drush_get_context('DRUSH_LOG', array());
  1260. }
  1261. /**
  1262. * Run print_r on a variable and log the output.
  1263. */
  1264. function dlm($object) {
  1265. ob_start();
  1266. print_r($object);
  1267. $contents = ob_get_contents();
  1268. ob_end_clean();
  1269. drush_log($contents);
  1270. }
  1271. /*
  1272. * Display the pipe output for the current request.
  1273. */
  1274. function drush_pipe_output() {
  1275. $pipe = drush_get_context('DRUSH_PIPE_BUFFER');
  1276. drush_print_r($pipe);
  1277. }
  1278. /**
  1279. * Display the log message
  1280. *
  1281. * By default, only warnings and errors will be displayed, if 'verbose' is specified, it will also display notices.
  1282. *
  1283. * @param
  1284. * The associative array for the entry.
  1285. *
  1286. * @return
  1287. * False in case of an error or failed type, True in all other cases.
  1288. */
  1289. function _drush_print_log($entry) {
  1290. if (drush_get_context('DRUSH_NOCOLOR')) {
  1291. $red = "[%s]";
  1292. $yellow = "[%s]";
  1293. $green = "[%s]";
  1294. }
  1295. else {
  1296. $red = "\033[31;40m\033[1m[%s]\033[0m";
  1297. $yellow = "\033[1;33;40m\033[1m[%s]\033[0m";
  1298. $green = "\033[0;33;40m\033[1m[%s]\033[0m";
  1299. }
  1300. $verbose = drush_get_context('DRUSH_VERBOSE');
  1301. $debug = drush_get_context('DRUSH_DEBUG');
  1302. $return = TRUE;
  1303. switch ($entry['type']) {
  1304. case 'warning' :
  1305. $type_msg = sprintf($yellow, $entry['type']);
  1306. break;
  1307. case 'failed' :
  1308. case 'error' :
  1309. $type_msg = sprintf($red, $entry['type']);
  1310. $return = FALSE;
  1311. break;
  1312. case 'ok' :
  1313. case 'completed' :
  1314. case 'success' :
  1315. $type_msg = sprintf($green, $entry['type']);
  1316. break;
  1317. case 'notice' :
  1318. case 'message' :
  1319. case 'info' :
  1320. if (!$verbose) {
  1321. // print nothing. exit cleanly.
  1322. return TRUE;
  1323. }
  1324. $type_msg = sprintf("[%s]", $entry['type']);
  1325. break;
  1326. default :
  1327. if (!$debug) {
  1328. // print nothing. exit cleanly.
  1329. return TRUE;
  1330. }
  1331. $type_msg = sprintf("[%s]", $entry['type']);
  1332. break;
  1333. }
  1334. // When running in backend mode, log messages are not displayed, as they will
  1335. // be returned in the JSON encoded associative array.
  1336. if (drush_get_context('DRUSH_BACKEND')) {
  1337. return $return;
  1338. }
  1339. $columns = drush_get_context('DRUSH_COLUMNS', 80);
  1340. $width[1] = 11;
  1341. // Append timer and memory values.
  1342. if ($debug) {
  1343. $timer = sprintf('[%s sec, %s]', round($entry['timestamp']-DRUSH_REQUEST_TIME, 2), drush_format_size($entry['memory']));
  1344. $entry['message'] = $entry['message'] . ' ' . $timer;
  1345. }
  1346. $width[0] = ($columns - 11);
  1347. $format = sprintf("%%-%ds%%%ds", $width[0], $width[1]);
  1348. // Place the status message right aligned with the top line of the error message.
  1349. $message = wordwrap($entry['message'], $width[0]);
  1350. $lines = explode("\n", $message);
  1351. $lines[0] = sprintf($format, $lines[0], $type_msg);
  1352. $message = implode("\n", $lines);
  1353. drush_print($message);
  1354. return $return;
  1355. }
  1356. // Print all timers for the request.
  1357. function drush_print_timers() {
  1358. global $timers;
  1359. $temparray = array();
  1360. foreach ((array)$timers as $name => $timerec) {
  1361. // We have to use timer_read() for active timers, and check the record for others
  1362. if (isset($timerec['start'])) {
  1363. $temparray[$name] = timer_read($name);
  1364. }
  1365. else {
  1366. $temparray[$name] = $timerec['time'];
  1367. }
  1368. }
  1369. // Go no farther if there were no timers
  1370. if (count($temparray) > 0) {
  1371. // Put the highest cumulative times first
  1372. arsort($temparray);
  1373. $table = array();
  1374. $table[] = array('Timer', 'Cum (sec)', 'Count', 'Avg (msec)');
  1375. foreach ($temparray as $name => $time) {
  1376. $cum = round($time/1000, 3);
  1377. $count = $timers[$name]['count'];
  1378. if ($count > 0) {
  1379. $avg = round($time/$count, 3);
  1380. }
  1381. else {
  1382. $avg = 'N/A';
  1383. }
  1384. $table[] = array($name, $cum, $count, $avg);
  1385. }
  1386. drush_print_table($table, TRUE);
  1387. }
  1388. }
  1389. /**
  1390. * Turn drupal_set_message errors into drush_log errors
  1391. */
  1392. function _drush_log_drupal_messages() {
  1393. if (function_exists('drupal_get_messages')) {
  1394. $messages = drupal_get_messages();
  1395. if (array_key_exists('error', $messages)) {
  1396. //Drupal message errors.
  1397. foreach ((array) $messages['error'] as $error) {
  1398. $error = strip_tags($error);
  1399. $header = preg_match('/^warning: Cannot modify header information - headers already sent by /i', $error);
  1400. $session = preg_match('/^warning: session_start\(\): Cannot send session /i', $error);
  1401. if ($header || $session) {
  1402. //These are special cases for an unavoidable warnings
  1403. //that are generated by generating output before Drupal is bootstrapped.
  1404. //or sending a session cookie (seems to affect d7 only?)
  1405. //Simply ignore them.
  1406. continue;
  1407. }
  1408. elseif (preg_match('/^warning:/i', $error)) {
  1409. drush_log(preg_replace('/^warning: /i', '', $error), 'warning');
  1410. }
  1411. elseif (preg_match('/^notice:/i', $error)) {
  1412. drush_log(preg_replace('/^notice: /i', '', $error), 'notice');
  1413. }
  1414. elseif (preg_match('/^user warning:/i', $error)) {
  1415. // This is a special case. PHP logs sql errors as 'User Warnings', not errors.
  1416. drush_set_error('DRUSH_DRUPAL_ERROR_MESSAGE', preg_replace('/^user warning: /i', '', $error));
  1417. }
  1418. else {
  1419. drush_set_error('DRUSH_DRUPAL_ERROR_MESSAGE', $error);
  1420. }
  1421. }
  1422. }
  1423. unset($messages['error']);
  1424. // Log non-error messages.
  1425. foreach ($messages as $type => $items) {
  1426. foreach ($items as $item) {
  1427. drush_log(strip_tags($item), $type);
  1428. }
  1429. }
  1430. }
  1431. }
  1432. // Copy of format_size() in Drupal.
  1433. function drush_format_size($size, $langcode = NULL) {
  1434. if ($size < DRUSH_DRUPAL_KILOBYTE) {
  1435. // format_plural() not always available.
  1436. return dt('@count bytes', array('@count' => $size));
  1437. }
  1438. else {
  1439. $size = $size / DRUSH_DRUPAL_KILOBYTE; // Convert bytes to kilobytes.
  1440. $units = array(
  1441. dt('@size KB', array(), array('langcode' => $langcode)),
  1442. dt('@size MB', array(), array('langcode' => $langcode)),
  1443. dt('@size GB', array(), array('langcode' => $langcode)),
  1444. dt('@size TB', array(), array('langcode' => $langcode)),
  1445. dt('@size PB', array(), array('langcode' => $langcode)),
  1446. dt('@size EB', array(), array('langcode' => $langcode)),
  1447. dt('@size ZB', array(), array('langcode' => $langcode)),
  1448. dt('@size YB', array(), array('langcode' => $langcode)),
  1449. );
  1450. foreach ($units as $unit) {
  1451. if (round($size, 2) >= DRUSH_DRUPAL_KILOBYTE) {
  1452. $size = $size / DRUSH_DRUPAL_KILOBYTE;
  1453. }
  1454. else {
  1455. break;
  1456. }
  1457. }
  1458. return str_replace('@size', round($size, 2), $unit);
  1459. }
  1460. }
  1461. /**
  1462. * Log Drupal watchdog() calls.
  1463. *
  1464. * A sneaky implementation of hook_watchdog().
  1465. */
  1466. function system_watchdog($log_entry) {
  1467. // Transform non informative severity levels to 'error' for compatibility with _drush_print_log.
  1468. // Other severity levels are coincident with the ones we use in drush.
  1469. if (drush_drupal_major_version() >= 6 && $log_entry['severity'] <= 2) {
  1470. $severity = 'error';
  1471. }
  1472. else {
  1473. drush_include_engine('drupal', 'environment');
  1474. $levels = core_watchdog_severity_levels();
  1475. $severity = $levels[$log_entry['severity']];
  1476. }
  1477. // Format the message.
  1478. if (is_array($log_entry['variables'])) {
  1479. $message = strtr($log_entry['message'], $log_entry['variables']);
  1480. }
  1481. else {
  1482. $message = $log_entry['message'];
  1483. }
  1484. $message = strip_tags(decode_entities($message));
  1485. // Log it.
  1486. drush_log('WD '. $log_entry['type'] . ': '.$message, $severity);
  1487. }
  1488. /**
  1489. * Log the return value of Drupal hook_update_n functions.
  1490. *
  1491. * This is used during install and update to log the output
  1492. * of the update process to the logging system.
  1493. */
  1494. function _drush_log_update_sql($ret) {
  1495. if (sizeof($ret)) {
  1496. foreach ($ret as $info) {
  1497. if (is_array($info)) {
  1498. if (!$info['success']) {
  1499. drush_set_error('DRUPAL_UPDATE_FAILED', $info['query']);
  1500. }
  1501. else {
  1502. drush_log($info['query'], ($info['success']) ? 'success' : 'error');
  1503. }
  1504. }
  1505. }
  1506. }
  1507. }
  1508. /**
  1509. * @} End of "defgroup logging".
  1510. */
  1511. /**
  1512. * @name Error status definitions
  1513. * @{
  1514. * Error code definitions for interpreting the current error status.
  1515. * @see drush_set_error(), drush_get_error(), drush_get_error_log(), drush_cmp_error()
  1516. */
  1517. /** The command completed successfully. */
  1518. define('DRUSH_SUCCESS', 0);
  1519. /** The command could not be completed because the framework has specified errors that have occured. */
  1520. define('DRUSH_FRAMEWORK_ERROR', 1);
  1521. /** The command that was executed resulted in an application error,
  1522. The most commom causes for this is invalid PHP or a broken SSH
  1523. pipe when using drush_backend_invoke in a distributed manner. */
  1524. define('DRUSH_APPLICATION_ERROR', 255);
  1525. /**
  1526. * @} End of "name Error status defintions".
  1527. */
  1528. /**
  1529. * @defgroup errorhandling Managing errors that occur in the Drush framework.
  1530. * @{
  1531. * Functions that manage the current error status of the Drush framework.
  1532. *
  1533. * These functions operate by maintaining a static variable that is a equal to the constant DRUSH_FRAMEWORK_ERROR if an
  1534. * error has occurred.
  1535. * This error code is returned at the end of program execution, and provide the shell or calling application with
  1536. * more information on how to diagnose any problems that may have occurred.
  1537. */
  1538. /**
  1539. * Set an error code for the error handling system.
  1540. *
  1541. * @param error
  1542. * A text string identifying the type of error.
  1543. *
  1544. * @param message
  1545. * Optional. Error message to be logged. If no message is specified, hook_drush_help will be consulted,
  1546. * using a key of 'error:MY_ERROR_STRING'.
  1547. *
  1548. * @return
  1549. * Always returns FALSE, to allow you to return with false in the calling functions,
  1550. * such as <code>return drush_set_error('DRUSH_FRAMEWORK_ERROR')</code>
  1551. */
  1552. function drush_set_error($error, $message = null) {
  1553. $error_code =& drush_get_context('DRUSH_ERROR_CODE', DRUSH_SUCCESS);
  1554. $error_code = DRUSH_FRAMEWORK_ERROR;
  1555. $error_log =& drush_get_context('DRUSH_ERROR_LOG', array());
  1556. if (is_numeric($error)) {
  1557. $error = 'DRUSH_FRAMEWORK_ERROR';
  1558. }
  1559. $message = ($message) ? $message : drush_command_invoke_all('drush_help', 'error:' . $error);
  1560. if (is_array($message)) {
  1561. $message = implode("\n", $message);
  1562. }
  1563. $error_log[$error][] = $message;
  1564. drush_log(($message) ? $message : $error, 'error', $error);
  1565. return FALSE;
  1566. }
  1567. /**
  1568. * Return the current error handling status
  1569. *
  1570. * @return
  1571. * The current aggregate error status
  1572. */
  1573. function drush_get_error() {
  1574. return drush_get_context('DRUSH_ERROR_CODE', DRUSH_SUCCESS);
  1575. }
  1576. /**
  1577. * Return the current list of errors that have occurred.
  1578. *
  1579. * @return
  1580. * An associative array of error messages indexed by the type of message.
  1581. */
  1582. function drush_get_error_log() {
  1583. return drush_get_context('DRUSH_ERROR_LOG', array());
  1584. }
  1585. /**
  1586. * Check if a specific error status has been set.
  1587. *
  1588. * @param error
  1589. * A text string identifying the error that has occurred.
  1590. * @return
  1591. * TRUE if the specified error has been set, FALSE if not
  1592. */
  1593. function drush_cmp_error($error) {
  1594. $error_log = drush_get_error_log();
  1595. if (is_numeric($error)) {
  1596. $error = 'DRUSH_FRAMEWORK_ERROR';
  1597. }
  1598. return array_key_exists($error, $error_log);
  1599. }
  1600. /**
  1601. * Turn PHP error handling off.
  1602. *
  1603. * This is commonly used while bootstrapping Drupal for install
  1604. * or updates.
  1605. */
  1606. function drush_errors_off() {
  1607. $errors =& drush_get_context('DRUSH_ERROR_REPORTING', 0);
  1608. $errors = error_reporting(0);
  1609. ini_set('display_errors', FALSE);
  1610. }
  1611. /**
  1612. * Turn PHP error handling on.
  1613. */
  1614. function drush_errors_on() {
  1615. $errors =& drush_get_context('DRUSH_ERROR_REPORTING', E_ALL ^ E_NOTICE);
  1616. $errors = error_reporting($errors);
  1617. ini_set('display_errors', TRUE);
  1618. }
  1619. /**
  1620. * @} End of "defgroup errorhandling".
  1621. */
  1622. /**
  1623. * Get the PHP memory_limit value in bytes.
  1624. */
  1625. function drush_memory_limit() {
  1626. $value = trim(ini_get('memory_limit'));
  1627. $last = strtolower($value[strlen($value)-1]);
  1628. switch ($last) {
  1629. case 'g':
  1630. $value *= 1024;
  1631. case 'm':
  1632. $value *= 1024;
  1633. case 'k':
  1634. $value *= 1024;
  1635. }
  1636. return $value;
  1637. }
  1638. /**
  1639. * Unset the named key anywhere in the provided
  1640. * data structure.
  1641. */
  1642. function drush_unset_recursive(&$data, $unset_key) {
  1643. unset($data[$unset_key]);
  1644. foreach ($data as $key => $value) {
  1645. if (is_array($value)) {
  1646. drush_unset_recursive($data[$key], $unset_key);
  1647. }
  1648. }
  1649. }