| 1 |
<?php
|
| 2 |
// $Id: drush.php,v 1.73 2009/10/26 02:26:36 weitzman Exp $
|
| 3 |
|
| 4 |
/**
|
| 5 |
* @file
|
| 6 |
* drush is a PHP script implementing a command line shell for Drupal.
|
| 7 |
*
|
| 8 |
* @requires PHP CLI 5.2.0, or newer.
|
| 9 |
*/
|
| 10 |
|
| 11 |
// Terminate immediately unless invoked as a command line script
|
| 12 |
if (!drush_verify_cli()) {
|
| 13 |
die('drush.php is designed to run via the command line.');
|
| 14 |
}
|
| 15 |
|
| 16 |
// Check supported version of PHP.
|
| 17 |
define('DRUSH_MINIMUM_PHP', '5.2.0');
|
| 18 |
if (version_compare(phpversion(), DRUSH_MINIMUM_PHP) < 0) {
|
| 19 |
die('Your command line PHP installation is too old. Drush requires at least PHP ' . DRUSH_MINIMUM_PHP . "\n");
|
| 20 |
}
|
| 21 |
|
| 22 |
define('DRUSH_BASE_PATH', dirname(__FILE__));
|
| 23 |
define('DRUSH_COMMAND', $GLOBALS['argv'][0]);
|
| 24 |
define('DRUSH_REQUEST_TIME', microtime(TRUE));
|
| 25 |
|
| 26 |
require_once DRUSH_BASE_PATH . '/includes/environment.inc';
|
| 27 |
require_once DRUSH_BASE_PATH . '/includes/command.inc';
|
| 28 |
require_once DRUSH_BASE_PATH . '/includes/drush.inc';
|
| 29 |
require_once DRUSH_BASE_PATH . '/includes/backend.inc';
|
| 30 |
require_once DRUSH_BASE_PATH . '/includes/context.inc';
|
| 31 |
require_once DRUSH_BASE_PATH . '/includes/sitealias.inc';
|
| 32 |
|
| 33 |
drush_set_context('argc', $GLOBALS['argc']);
|
| 34 |
drush_set_context('argv', $GLOBALS['argv']);
|
| 35 |
|
| 36 |
set_error_handler('drush_error_handler');
|
| 37 |
|
| 38 |
exit(drush_main());
|
| 39 |
|
| 40 |
/**
|
| 41 |
* Verify that we are running PHP through the command line interface.
|
| 42 |
*
|
| 43 |
* This function is useful for making sure that code cannot be run via the web server,
|
| 44 |
* such as a function that needs to write files to which the web server should not have
|
| 45 |
* access to.
|
| 46 |
*
|
| 47 |
* @return
|
| 48 |
* A boolean value that is true when PHP is being run through the command line,
|
| 49 |
* and false if being run through cgi or mod_php.
|
| 50 |
*/
|
| 51 |
function drush_verify_cli() {
|
| 52 |
return (php_sapi_name() == 'cli' || (is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0));
|
| 53 |
}
|
| 54 |
|
| 55 |
/**
|
| 56 |
* The main Drush function.
|
| 57 |
*
|
| 58 |
* - Parses the command line arguments, configuration files and environment.
|
| 59 |
* - Prepares and executes a Drupal bootstrap, if possible,
|
| 60 |
* - Dispatches the given command.
|
| 61 |
*
|
| 62 |
* @return
|
| 63 |
* Whatever the given command returns.
|
| 64 |
*/
|
| 65 |
function drush_main() {
|
| 66 |
$phases = _drush_bootstrap_phases();
|
| 67 |
|
| 68 |
$return = '';
|
| 69 |
$command_found = FALSE;
|
| 70 |
|
| 71 |
foreach ($phases as $phase) {
|
| 72 |
if (drush_bootstrap($phase)) {
|
| 73 |
$command = drush_parse_command();
|
| 74 |
|
| 75 |
// Process a remote command if 'remote-host' option is set.
|
| 76 |
if (drush_remote_command()) {
|
| 77 |
$command_found = TRUE;
|
| 78 |
break;
|
| 79 |
}
|
| 80 |
|
| 81 |
if (is_array($command)) {
|
| 82 |
if ($command['bootstrap'] == $phase && empty($command['bootstrap_errors'])) {
|
| 83 |
drush_log(dt("Found command: !command", array('!command' => $command['command'])), 'bootstrap');
|
| 84 |
$command_found = TRUE;
|
| 85 |
// Dispatch the command(s).
|
| 86 |
$return = drush_dispatch($command);
|
| 87 |
|
| 88 |
drush_log_timers();
|
| 89 |
break;
|
| 90 |
}
|
| 91 |
}
|
| 92 |
}
|
| 93 |
else {
|
| 94 |
break;
|
| 95 |
}
|
| 96 |
}
|
| 97 |
|
| 98 |
if (!$command_found) {
|
| 99 |
// If we reach this point, we have not found either a valid or matching command.
|
| 100 |
$args = implode(' ', drush_get_arguments());
|
| 101 |
$drush_command = array_pop(explode('/', DRUSH_COMMAND));
|
| 102 |
if (isset($command) && is_array($command)) {
|
| 103 |
foreach ($command['bootstrap_errors'] as $key => $error) {
|
| 104 |
drush_set_error($key, $error);
|
| 105 |
}
|
| 106 |
drush_set_error('DRUSH_COMMAND_NOT_EXECUTABLE', dt("The command '!drush_command !args' could not be executed.", array('!drush_command' => $drush_command, '!args' => $args)));
|
| 107 |
}
|
| 108 |
elseif (!empty($args)) {
|
| 109 |
drush_set_error('DRUSH_COMMAND_NOT_FOUND', dt("The command '!drush_command !args' could not be found.", array('!drush_command' => $drush_command, '!args' => $args)));
|
| 110 |
}
|
| 111 |
else {
|
| 112 |
// This can occur if we get an error during _drush_bootstrap_drush_validate();
|
| 113 |
drush_set_error('DRUSH_COULD_NOT_EXECUTE', dt("Drush could not execute."));
|
| 114 |
}
|
| 115 |
}
|
| 116 |
|
| 117 |
// We set this context to let the shutdown function know we reached the end of drush_main();
|
| 118 |
drush_set_context("DRUSH_EXECUTION_COMPLETED", TRUE);
|
| 119 |
|
| 120 |
// After this point the drush_shutdown function will run,
|
| 121 |
// exiting with the correct exit code.
|
| 122 |
return $return;
|
| 123 |
}
|
| 124 |
|
| 125 |
/**
|
| 126 |
* Process commands that are executed on a remote drush instance.
|
| 127 |
*
|
| 128 |
* @return
|
| 129 |
* TRUE if the command was handled remotely.
|
| 130 |
*/
|
| 131 |
function drush_remote_command() {
|
| 132 |
// The command will be executed remotely if the --remote-host flag
|
| 133 |
// is set; note that if a site alias is provided on the command line,
|
| 134 |
// and the site alias references a remote server, then the --remote-host
|
| 135 |
// option will be set when the site alias is processed.
|
| 136 |
// @see _drush_process_site_alias
|
| 137 |
$remote_host = drush_get_option('remote-host');
|
| 138 |
if (isset($remote_host)) {
|
| 139 |
|
| 140 |
$args = drush_get_arguments();
|
| 141 |
$command = array_shift($args);
|
| 142 |
$data = drush_get_context('options');
|
| 143 |
|
| 144 |
// Most command options are forwarded on to the remote
|
| 145 |
// server; however, we will clear certain flags such as
|
| 146 |
// -v, -d and -i from the 'options' context (defaults, and
|
| 147 |
// options passed in on the command line).
|
| 148 |
foreach (array('v', 'd', 'i') as $key) {
|
| 149 |
unset($data[$key]);
|
| 150 |
}
|
| 151 |
|
| 152 |
// After we clear out the flags we do not want from the
|
| 153 |
// 'options' context, we will add in the 'root' and 'uri'
|
| 154 |
// options from any context. Note that these will usually
|
| 155 |
// come from the 'alias' context, which has a higher precedence
|
| 156 |
// than the 'site' context (where 'r' and 'l' from settings.php
|
| 157 |
// are stored).
|
| 158 |
foreach (array('root', 'r', 'uri', 'l') as $key) {
|
| 159 |
$value = drush_get_option($key);
|
| 160 |
if (isset($value)) {
|
| 161 |
$data[$key] = drush_get_option($key);
|
| 162 |
}
|
| 163 |
}
|
| 164 |
|
| 165 |
// Finally, we call backend invoke with the
|
| 166 |
// specified remote host, remote user and drush path.
|
| 167 |
$drush_path = drush_get_option('drush-script');
|
| 168 |
if (!isset($drush_path)) {
|
| 169 |
$drush_folder = drush_get_option('drush');
|
| 170 |
if (isset($drush)) {
|
| 171 |
$drush_path = $drush_folder . '/drush';
|
| 172 |
}
|
| 173 |
}
|
| 174 |
$remote_user = drush_get_option('remote-user');
|
| 175 |
drush_log(dt('Begin drush_backend_invoke'));
|
| 176 |
$values = drush_backend_invoke_args($command, $args, $data, 'GET', TRUE, $drush_path, $remote_host, $remote_user);
|
| 177 |
drush_log(dt('drush_backend_invoke is complete'));
|
| 178 |
|
| 179 |
return TRUE;
|
| 180 |
}
|
| 181 |
return FALSE;
|
| 182 |
}
|
| 183 |
|
| 184 |
/**
|
| 185 |
* Shutdown function for use while Drupal is bootstrapping and to return any
|
| 186 |
* registered errors.
|
| 187 |
*
|
| 188 |
* The shutdown command checks whether certain options are set to reliably
|
| 189 |
* detect and log some common Drupal initialization errors.
|
| 190 |
*
|
| 191 |
* If the command is being executed with the --backend option, the script
|
| 192 |
* will return a json string containing the options and log information
|
| 193 |
* used by the script.
|
| 194 |
*
|
| 195 |
* The command will exit with '1' if it was successfully executed, and the
|
| 196 |
* result of drush_get_error() if it wasn't.
|
| 197 |
*/
|
| 198 |
function drush_shutdown() {
|
| 199 |
// Mysteriously make $user available during sess_write(). Avoids a NOTICE.
|
| 200 |
global $user;
|
| 201 |
|
| 202 |
if (!drush_get_context('DRUSH_EXECUTION_COMPLETED', FALSE)) {
|
| 203 |
// We did not reach the end of the drush_main function,
|
| 204 |
// this generally means somewhere in the code a call to exit(),
|
| 205 |
// was made. We catch this, so that we can trigger an error in
|
| 206 |
// those cases.
|
| 207 |
drush_set_error("DRUSH_NOT_COMPLETED", dt("Drush command could not be completed."));
|
| 208 |
}
|
| 209 |
|
| 210 |
$phase = drush_get_context('DRUSH_BOOTSTRAP_PHASE');
|
| 211 |
if (drush_get_context('DRUSH_BOOTSTRAPPING')) {
|
| 212 |
switch ($phase) {
|
| 213 |
case DRUSH_BOOTSTRAP_DRUPAL_FULL :
|
| 214 |
ob_end_clean();
|
| 215 |
_drush_log_drupal_messages();
|
| 216 |
drush_set_error('DRUSH_DRUPAL_BOOTSTRAP_ERROR');
|
| 217 |
break;
|
| 218 |
}
|
| 219 |
}
|
| 220 |
|
| 221 |
if (drush_get_context('DRUSH_BACKEND')) {
|
| 222 |
drush_backend_output();
|
| 223 |
}
|
| 224 |
elseif (drush_get_context('DRUSH_QUIET')) {
|
| 225 |
ob_end_clean();
|
| 226 |
}
|
| 227 |
|
| 228 |
// If we are in pipe mode, emit the compact representation of the command, if available.
|
| 229 |
if (drush_get_context('DRUSH_PIPE')) {
|
| 230 |
drush_pipe_output();
|
| 231 |
}
|
| 232 |
|
| 233 |
// this way drush_return_status will always be the last shutdown function (unless other shutdown functions register shutdown functions...)
|
| 234 |
// and won't prevent other registered shutdown functions (IE from numerous cron methods) from running by calling exit() before they get a chance.
|
| 235 |
register_shutdown_function('drush_return_status');
|
| 236 |
}
|
| 237 |
|
| 238 |
function drush_return_status() {
|
| 239 |
exit((drush_get_error()) ? DRUSH_FRAMEWORK_ERROR : DRUSH_SUCCESS);
|
| 240 |
}
|
| 241 |
|
| 242 |
/**
|
| 243 |
* Log the given user in to a bootstrapped Drupal site.
|
| 244 |
*
|
| 245 |
* @param mixed
|
| 246 |
* Numeric user id or user name.
|
| 247 |
*
|
| 248 |
* @return boolean
|
| 249 |
* TRUE if user was logged in, otherwise FALSE.
|
| 250 |
*/
|
| 251 |
function drush_drupal_login($drush_user) {
|
| 252 |
global $user;
|
| 253 |
if (drush_drupal_major_version() >= 7) {
|
| 254 |
$user = is_numeric($drush_user) ? user_load($drush_user) : user_load_by_name($drush_user);
|
| 255 |
}
|
| 256 |
else {
|
| 257 |
$user = user_load(is_numeric($drush_user) ? array('uid' => $drush_user) : array('name' => $drush_user));
|
| 258 |
}
|
| 259 |
|
| 260 |
if (empty($user)) {
|
| 261 |
if (is_numeric($drush_user)) {
|
| 262 |
$message = dt('Could not login with user ID #%user.', array('%user' => $drush_user));
|
| 263 |
}
|
| 264 |
else {
|
| 265 |
$message = dt('Could not login with user account `%user\'.', array('%user' => $drush_user));
|
| 266 |
}
|
| 267 |
return drush_set_error('DRUPAL_USER_LOGIN_FAILED', $message);
|
| 268 |
}
|
| 269 |
|
| 270 |
return TRUE;
|
| 271 |
}
|
| 272 |
|