environment.inc

  1. 8.0.x includes/environment.inc
  2. 8.0.x commands/core/drupal/environment.inc
  3. 6.x includes/environment.inc
  4. 6.x commands/core/drupal/environment.inc
  5. 7.x includes/environment.inc
  6. 7.x commands/core/drupal/environment.inc
  7. 3.x includes/environment.inc
  8. 4.x commands/core/drupal/environment.inc
  9. 4.x includes/environment.inc
  10. 5.x commands/core/drupal/environment.inc
  11. 5.x includes/environment.inc
  12. master commands/core/drupal/environment.inc
  13. master includes/environment.inc

Functions used by drush to query the environment and setting the current configuration.

Functions

Namesort descending Description
drush_bootstrap Bootstrap Drush to the desired phase.
drush_bootstrap_error Helper function to collect any errors that occur during the bootstrap process. Always returns FALSE, for convenience.
drush_bootstrap_max Bootstrap to the highest level possible, without triggering any errors.
drush_bootstrap_max_to_sitealias Bootstrap the specified site alias. The site alias must be a valid alias to a local site.
drush_bootstrap_to_phase Bootstrap to the specified phase.
drush_bootstrap_validate Validate whether a bootstrap phases can be reached.
drush_bootstrap_value Helper function to store any context settings that are being validated.
drush_cwd Returns the current working directory.
drush_directory_cache
drush_drupal_required_modules
drush_environment_check_os
drush_environment_table_inc
drush_error_handler Log PHP errors to the Drush log. This is in effect until Drupal's error handler takes over.
drush_extension_check_incompatibility Test compatibility of a extension with version of drupal core and php.
drush_find_drush Determine a proper way to call drush again
drush_get_extensions Get complete information for all available extensions (modules and themes).
drush_has_boostrapped Determine whether a given bootstrap phase has been completed
drush_is_local_host Make a determination whether or not the given host is local or not.
drush_locate_root Exhaustive depth-first search to try and locate the Drupal root directory. This makes it possible to run drush from a subdirectory of the drupal root.
drush_read_drush_info Read the drush info file.
drush_server_home Return the user's home directory.
drush_site_path Like Drupal conf_path, but searching from beneath. Allows proper site uri detection in site sub-directories.
drush_theme_get_admin Return the administration theme.
drush_theme_get_default Return the default theme.
drush_valid_db_credentials Tests the currently loaded database credentials to ensure a database connection can be made.
drush_valid_drupal_root Checks whether given path qualifies as a Drupal root.
_drush_bootstrap_do_drupal_site Called by _drush_bootstrap_drupal_site to do the main work of the drush drupal site bootstrap.
_drush_bootstrap_drupal_configuration Initialize and load the Drupal configuration files.
_drush_bootstrap_drupal_database Boostrap the Drupal database.
_drush_bootstrap_drupal_database_validate Validate the DRUSH_BOOTSTRAP_DRUPAL_DATABASE phase
_drush_bootstrap_drupal_full Attempt to load the full Drupal system.
_drush_bootstrap_drupal_login Log into the bootstrapped Drupal site with a specific username or user id.
_drush_bootstrap_drupal_root Bootstrap Drush with a valid Drupal Directory.
_drush_bootstrap_drupal_root_validate Validate the DRUSH_BOOTSTRAP_DRUPAL_ROOT phase.
_drush_bootstrap_drupal_site Initialize a site on the Drupal root.
_drush_bootstrap_drupal_site_validate VALIDATE the DRUSH_BOOTSTRAP_DRUPAL_SITE phase.
_drush_bootstrap_drush Initial Drush bootstrap phase.
_drush_bootstrap_drush_validate Validate that Drush is running in a suitable environment.
_drush_bootstrap_global_options
_drush_bootstrap_phases Helper function listing phases.
_drush_bootstrap_redo_drupal_site Re-do the drupal site bootstrap (and possibly the drupal root bootstrap) if a site alias was processed after the site bootstrap phase completed. This will happen when processing "drush sitealias command" for a site alias defined in a…
_drush_convert_path Converts a Windows path (dir1\dir2\dir3) into a Unix path (dir1/dir2/dir3). Also converts a cygwin "drive emulation" path (/cygdrive/c/dir1) into a proper drive path, still with Unix slashes (c:/dir1).
_drush_environment_check_php_ini Evaluate the environment before command bootstrapping begins. If the php environment is too restrictive, then notify the user that a setting change is needed and abort.
_drush_php_ini_loaded_file_message
_drush_postmortem Evalute the environment after an abnormal termination and see if we can determine any configuration settings that the user might want to adjust.
_drush_shift_path_up Returns parent directory.

Constants

Namesort descending Description
DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION Load the settings from the Drupal sites directory.
DRUSH_BOOTSTRAP_DRUPAL_DATABASE Connect to the Drupal database using the database credentials loaded during the previous bootstrap phase.
DRUSH_BOOTSTRAP_DRUPAL_FULL Fully initialize Drupal.
DRUSH_BOOTSTRAP_DRUPAL_LOGIN Log in to the initialiased Drupal site.
DRUSH_BOOTSTRAP_DRUPAL_ROOT Set up and test for a valid drupal root, either through the -r/--root options, or evaluated based on the current working directory.
DRUSH_BOOTSTRAP_DRUPAL_SITE Set up a Drupal site directory and the correct environment variables to allow Drupal to find the configuration file.
DRUSH_BOOTSTRAP_DRUSH Only bootstrap Drush, without any Drupal specific code.
DRUSH_BOOTSTRAP_MAX Use drush_bootstrap_max instead of drush_bootstrap_to_phase
DRUSH_BOOTSTRAP_NONE No bootstrap.
DRUSH_DRUPAL_BOOTSTRAP The indicator for a Drupal installation folder.
DRUSH_TABLE_URL URL for automatic file download for supported version of Console Table.
DRUSH_TABLE_VERSION Supported version of Console Table. This is displayed in the manual install help.

File

includes/environment.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * Functions used by drush to query the environment and
  5. * setting the current configuration.
  6. */
  7. /**
  8. * The indicator for a Drupal installation folder.
  9. */
  10. define('DRUSH_DRUPAL_BOOTSTRAP', 'includes/bootstrap.inc');
  11. /**
  12. * @name Drush bootstrap phases
  13. * @{
  14. * Sequential Drush bootstrapping phases.
  15. */
  16. /**
  17. * No bootstrap.
  18. *
  19. * This constant is only used to indicate that the bootstrap process has
  20. * not started yet. It is not possible to have no bootstrap.
  21. */
  22. define('DRUSH_BOOTSTRAP_NONE', -1);
  23. /**
  24. * Use drush_bootstrap_max instead of drush_bootstrap_to_phase
  25. *
  26. * This constant is only usable as the value of the 'bootstrap'
  27. * item of a command object, or as the parameter to
  28. * drush_bootstrap_to_phase. It is not a real bootstrap state.
  29. */
  30. define('DRUSH_BOOTSTRAP_MAX', -2);
  31. /**
  32. * Only bootstrap Drush, without any Drupal specific code.
  33. *
  34. * Any code that operates on the Drush installation, and not specifically
  35. * any Drupal directory, should bootstrap to this phase.
  36. */
  37. define('DRUSH_BOOTSTRAP_DRUSH', 0);
  38. /**
  39. * Set up and test for a valid drupal root, either through the -r/--root options,
  40. * or evaluated based on the current working directory.
  41. *
  42. * Any code that interacts with an entire Drupal installation, and not a specific
  43. * site on the Drupal installation should use this bootstrap phase.
  44. */
  45. define('DRUSH_BOOTSTRAP_DRUPAL_ROOT', 1);
  46. /**
  47. * Set up a Drupal site directory and the correct environment variables to
  48. * allow Drupal to find the configuration file.
  49. *
  50. * If no site is specified with the -l / --uri options, Drush will assume the
  51. * site is 'default', which mimics Drupal's behaviour.
  52. *
  53. * If you want to avoid this behaviour, it is recommended that you use the
  54. * DRUSH_BOOTSTRAP_DRUPAL_ROOT bootstrap phase instead.
  55. *
  56. * Any code that needs to modify or interact with a specific Drupal site's
  57. * settings.php file should bootstrap to this phase.
  58. */
  59. define('DRUSH_BOOTSTRAP_DRUPAL_SITE', 2);
  60. /**
  61. * Load the settings from the Drupal sites directory.
  62. *
  63. * This phase is analagous to the DRUPAL_BOOTSTRAP_CONFIGURATION bootstrap phase in Drupal
  64. * itself, and this is also the first step where Drupal specific code is included.
  65. *
  66. * This phase is commonly used for code that interacts with the Drupal install API,
  67. * as both install.php and update.php start at this phase.
  68. */
  69. define('DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION', 3);
  70. /**
  71. * Connect to the Drupal database using the database credentials loaded
  72. * during the previous bootstrap phase.
  73. *
  74. * This phase is analogous to the DRUPAL_BOOTSTRAP_DATABASE bootstrap phase in
  75. * Drupal.
  76. *
  77. * Any code that needs to interact with the Drupal database API needs to
  78. * be bootstrapped to at least this phase.
  79. */
  80. define('DRUSH_BOOTSTRAP_DRUPAL_DATABASE', 4);
  81. /**
  82. * Fully initialize Drupal.
  83. *
  84. * This is analogous to the DRUPAL_BOOTSTRAP_FULL bootstrap phase in
  85. * Drupal.
  86. *
  87. * Any code that interacts with the general Drupal API should be
  88. * bootstrapped to this phase.
  89. */
  90. define('DRUSH_BOOTSTRAP_DRUPAL_FULL', 5);
  91. /**
  92. * Log in to the initialiased Drupal site.
  93. *
  94. * This is the default bootstrap phase all commands will try to reach,
  95. * unless otherwise specified.
  96. *
  97. * This bootstrap phase is used after the site has been
  98. * fully bootstrapped.
  99. *
  100. * This phase will log you in to the drupal site with the username
  101. * or user ID specified by the --user/ -u option.
  102. *
  103. * Use this bootstrap phase for your command if you need to have access
  104. * to information for a specific user, such as listing nodes that might
  105. * be different based on who is logged in.
  106. */
  107. define('DRUSH_BOOTSTRAP_DRUPAL_LOGIN', 6);
  108. /**
  109. * Supported version of Console Table. This is displayed in the manual install help.
  110. */
  111. define('DRUSH_TABLE_VERSION', '1.1.3');
  112. /**
  113. * URL for automatic file download for supported version of Console Table.
  114. */
  115. define('DRUSH_TABLE_URL', 'http://svn.php.net/viewvc/pear/packages/Console_Table/trunk/Table.php?revision=267580&view=co');
  116. /**
  117. * Helper function listing phases.
  118. *
  119. * For commands that need to iterate through the phases, such as help
  120. */
  121. function _drush_bootstrap_phases($function_names = FALSE, $init_phases_only = FALSE) {
  122. static $functions = array(
  123. DRUSH_BOOTSTRAP_DRUSH => '_drush_bootstrap_drush',
  124. DRUSH_BOOTSTRAP_DRUPAL_ROOT => '_drush_bootstrap_drupal_root',
  125. DRUSH_BOOTSTRAP_DRUPAL_SITE => '_drush_bootstrap_drupal_site',
  126. DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION => '_drush_bootstrap_drupal_configuration',
  127. DRUSH_BOOTSTRAP_DRUPAL_DATABASE => '_drush_bootstrap_drupal_database',
  128. DRUSH_BOOTSTRAP_DRUPAL_FULL => '_drush_bootstrap_drupal_full',
  129. DRUSH_BOOTSTRAP_DRUPAL_LOGIN => '_drush_bootstrap_drupal_login');
  130. $result = array();
  131. if ($init_phases_only) {
  132. foreach (array(DRUSH_BOOTSTRAP_DRUSH, DRUSH_BOOTSTRAP_DRUPAL_FULL) as $phase) {
  133. $result[$phase] = $functions[$phase];
  134. }
  135. }
  136. else {
  137. $result = $functions;
  138. }
  139. if (!$function_names) {
  140. $result = array_keys($result);
  141. }
  142. return $result;
  143. }
  144. /**
  145. * @} End of Drush bootstrap phases.
  146. */
  147. /**
  148. * Bootstrap Drush to the desired phase.
  149. *
  150. * This function will sequentially bootstrap each
  151. * lower phase up to the phase that has been requested.
  152. *
  153. * @param phase
  154. * The bootstrap phase to bootstrap to.
  155. * Any of the following constants :
  156. * DRUSH_BOOTSTRAP_DRUSH = Only Drush.
  157. * DRUSH_BOOTSTRAP_DRUPAL_ROOT = Find a valid Drupal root.
  158. * DRUSH_BOOTSTRAP_DRUPAL_SITE = Find a valid Drupal site.
  159. * DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION = Load the site's settings.
  160. * DRUSH_BOOTSTRAP_DRUPAL_DATABASE = Initialize the database.
  161. * DRUSH_BOOTSTRAP_DRUPAL_FULL = Initialize Drupal fully.
  162. * DRUSH_BOOTSTRAP_DRUPAL_LOGIN = Log into Drupal with a valid user.
  163. */
  164. function drush_bootstrap($phase, $phase_max = FALSE) {
  165. static $phases;
  166. if (!$phases) {
  167. $phases = _drush_bootstrap_phases(TRUE);
  168. }
  169. static $phase_index = 0;
  170. drush_set_context('DRUSH_BOOTSTRAPPING', TRUE);
  171. while ($phase >= $phase_index && isset($phases[$phase_index])) {
  172. if (drush_bootstrap_validate($phase_index)) {
  173. $current_phase = $phases[$phase_index];
  174. if (function_exists($current_phase) && !drush_get_error()) {
  175. drush_log(dt("Drush bootstrap phase : !function()", array('!function' => $current_phase)), 'bootstrap');
  176. $current_phase();
  177. // Find any command files that are available during this bootstrap phase.
  178. _drush_find_commandfiles($phase_index, $phase_max);
  179. }
  180. drush_set_context('DRUSH_BOOTSTRAP_PHASE', $phase_index);
  181. }
  182. else {
  183. $errors = drush_get_context('DRUSH_BOOTSTRAP_ERRORS', array());
  184. foreach ($errors as $code => $message) {
  185. drush_set_error($code, $message);
  186. }
  187. }
  188. unset($phases[$phase_index++]);
  189. }
  190. drush_set_context('DRUSH_BOOTSTRAPPING', FALSE);
  191. return !drush_get_error();
  192. }
  193. /**
  194. * Determine whether a given bootstrap phase has been completed
  195. *
  196. * @param phase
  197. * The bootstrap phase to test
  198. *
  199. * @returns
  200. * TRUE if the specified bootstrap phase has completed.
  201. */
  202. function drush_has_boostrapped($phase) {
  203. $phase_index = drush_get_context('DRUSH_BOOTSTRAP_PHASE');
  204. return isset($phase_index) && ($phase_index >= $phase);
  205. }
  206. /**
  207. * Validate whether a bootstrap phases can be reached.
  208. *
  209. * This function will validate the settings that will be used
  210. * during the actual bootstrap process, and allow commands to
  211. * progressively bootstrap to the highest level that can be reached.
  212. *
  213. * This function will only run the validation function once, and
  214. * store the result from that execution in a local static. This avoids
  215. * validating phases multiple times.
  216. *
  217. * @param phase
  218. * The bootstrap phase to validate to.
  219. * Any of the following constants :
  220. * DRUSH_BOOTSTRAP_DRUSH = Only Drush.
  221. * DRUSH_BOOTSTRAP_DRUPAL_ROOT = Find a valid Drupal root.
  222. * DRUSH_BOOTSTRAP_DRUPAL_SITE = Find a valid Drupal site.
  223. * DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION = Load the site's settings.
  224. * DRUSH_BOOTSTRAP_DRUPAL_DATABASE = Initialize the database.
  225. * DRUSH_BOOTSTRAP_DRUPAL_FULL = Initialize Drupal fully.
  226. * DRUSH_BOOTSTRAP_DRUPAL_LOGIN = Log into Drupal with a valid user.
  227. *
  228. * @return
  229. * True if bootstrap is possible, False if the validation failed.
  230. *
  231. */
  232. function drush_bootstrap_validate($phase) {
  233. static $phases;
  234. static $result_cache = array();
  235. if (!$phases) {
  236. $phases = _drush_bootstrap_phases(TRUE);
  237. }
  238. static $phase_index = 0;
  239. if (!array_key_exists($phase, $result_cache)) {
  240. drush_set_context('DRUSH_BOOTSTRAP_ERRORS', array());
  241. drush_set_context('DRUSH_BOOTSTRAP_VALUES', array());
  242. while ($phase >= $phase_index && isset($phases[$phase_index])) {
  243. $current_phase = $phases[$phase_index] . '_validate';
  244. if (function_exists($current_phase)) {
  245. $result_cache[$phase_index] = $current_phase();
  246. }
  247. else {
  248. $result_cache[$phase_index] = TRUE;
  249. }
  250. drush_set_context('DRUSH_BOOTSTRAP_VALIDATION_PHASE', $phase_index);
  251. unset($phases[$phase_index++]);
  252. }
  253. }
  254. return $result_cache[$phase];
  255. }
  256. /**
  257. * Bootstrap to the specified phase.
  258. *
  259. * @param $max_phase_index
  260. * Only attempt bootstrap to the specified level.
  261. */
  262. function drush_bootstrap_to_phase($max_phase_index) {
  263. // If $max_phase_index is DRUSH_BOOTSTRAP_MAX, then
  264. // we will bootstrap as far as we can. drush_bootstrap_max
  265. // is different than drush_bootstrap_to_phase in that
  266. // it is not an error if DRUSH_BOOTSTRAP_LOGIN is not reached.
  267. if ($max_phase_index == DRUSH_BOOTSTRAP_MAX) {
  268. drush_bootstrap_max();
  269. return TRUE;
  270. }
  271. drush_log(dt("Bootstrap to phase !phase.", array('!phase' => $max_phase_index)), 'bootstrap');
  272. $phases = _drush_bootstrap_phases();
  273. $result = TRUE;
  274. // Try to bootstrap to the maximum possible level, without generating errors
  275. foreach ($phases as $phase_index) {
  276. if ($phase_index > $max_phase_index) {
  277. // Stop trying, since we achieved what was specified.
  278. break;
  279. }
  280. if (drush_bootstrap_validate($phase_index)) {
  281. if ($phase_index > drush_get_context('DRUSH_BOOTSTRAP_PHASE')) {
  282. $result = drush_bootstrap($phase_index, $max_phase_index);
  283. }
  284. }
  285. else {
  286. break;
  287. }
  288. }
  289. return $result;
  290. }
  291. /**
  292. * Bootstrap to the highest level possible, without triggering any errors.
  293. *
  294. * @param $max_phase_index
  295. * Only attempt bootstrap to the specified level.
  296. */
  297. function drush_bootstrap_max($max_phase_index = FALSE) {
  298. $phases = _drush_bootstrap_phases();
  299. $phase_index = DRUSH_BOOTSTRAP_DRUSH;
  300. if (!$max_phase_index) {
  301. $max_phase_index = count($phases);
  302. }
  303. // Try to bootstrap to the maximum possible level, without generating errors
  304. foreach ($phases as $phase_index) {
  305. if ($phase_index > $max_phase_index) {
  306. // Stop trying, since we achieved what was specified.
  307. break;
  308. }
  309. if (drush_bootstrap_validate($phase_index)) {
  310. if ($phase_index > drush_get_context('DRUSH_BOOTSTRAP_PHASE')) {
  311. drush_bootstrap($phase_index, $max_phase_index);
  312. }
  313. }
  314. else {
  315. break;
  316. }
  317. }
  318. return drush_get_context('DRUSH_BOOTSTRAP_PHASE');
  319. }
  320. /**
  321. * Bootstrap the specified site alias. The site alias must
  322. * be a valid alias to a local site.
  323. *
  324. * @param $site_record
  325. * The alias record for the given site alias.
  326. * @see drush_sitealias_get_record().
  327. * @param $max_phase_index
  328. * Only attempt bootstrap to the specified level.
  329. * @returns TRUE if attempted to bootstrap, or FALSE
  330. * if no bootstrap attempt was made.
  331. */
  332. function drush_bootstrap_max_to_sitealias($site_record, $max_phase_index = NULL) {
  333. if ((array_key_exists('root', $site_record) && !array_key_exists('remote-host', $site_record))) {
  334. drush_sitealias_set_alias_context($site_record);
  335. drush_bootstrap_max($max_phase_index);
  336. return TRUE;
  337. }
  338. return FALSE;
  339. }
  340. /**
  341. * Helper function to collect any errors that occur during the bootstrap process.
  342. * Always returns FALSE, for convenience.
  343. */
  344. function drush_bootstrap_error($code, $message = null) {
  345. $errors = drush_get_context('DRUSH_BOOTSTRAP_ERRORS');
  346. $errors[$code] = $message;
  347. drush_set_context('DRUSH_BOOTSTRAP_ERRORS', $errors);
  348. return FALSE;
  349. }
  350. /**
  351. * Log PHP errors to the Drush log. This is in effect until Drupal's error
  352. * handler takes over.
  353. */
  354. function drush_error_handler($errno, $message, $filename, $line, $context) {
  355. // E_DEPRECATED was added in PHP 5.3. Drupal 6 will not fix all the
  356. // deprecated errors, but suppresses them. So we suppress them as well.
  357. if (defined('E_DEPRECATED')) {
  358. $errno = $errno & ~E_DEPRECATED;
  359. }
  360. // "error_reporting" is usually set in php.ini, but may be changed by
  361. // drush_errors_on() and drush_errors_off().
  362. if ($errno & error_reporting()) {
  363. // By default we log notices.
  364. $type = drush_get_option('php-notices', 'notice');
  365. // Bitmask value that constitutes an error needing to be logged.
  366. $error = E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR;
  367. if ($errno & $error) {
  368. $type = 'error';
  369. }
  370. // Bitmask value that constitutes a warning being logged.
  371. $warning = E_WARNING | E_CORE_WARNING | E_COMPILE_WARNING | E_USER_WARNING;
  372. if ($errno & $warning) {
  373. $type = 'warning';
  374. }
  375. drush_log($message . ' ' . basename($filename) . ':' . $line, $type);
  376. return TRUE;
  377. }
  378. }
  379. /**
  380. * Helper function to store any context settings that are being validated.
  381. */
  382. function drush_bootstrap_value($context, $value = null) {
  383. $values =& drush_get_context('DRUSH_BOOTSTRAP_VALUES', array());
  384. if (!is_null($value)) {
  385. $values[$context] = $value;
  386. }
  387. if (array_key_exists($context, $values)) {
  388. return $values[$context];
  389. }
  390. return null;
  391. }
  392. /*
  393. * Returns a localizable message about php.ini that
  394. * varies depending on whether the php_ini_loaded_file()
  395. * is available or not.
  396. */
  397. function _drush_php_ini_loaded_file_message() {
  398. if (function_exists('php_ini_loaded_file')) {
  399. return dt('Please check your configuration settings in !phpini or in your drush.ini file; see examples/example.drush.ini for details.', array('!phpini' => php_ini_loaded_file()));
  400. }
  401. else {
  402. return dt('Please check your configuration settings in your php.ini file or in your drush.ini file; see examples/example.drush.ini for details.');
  403. }
  404. }
  405. /**
  406. * Evalute the environment after an abnormal termination and
  407. * see if we can determine any configuration settings that the user might
  408. * want to adjust.
  409. */
  410. function _drush_postmortem() {
  411. // Make sure that the memory limit has been bumped up from the minimum default value of 32M.
  412. $php_memory_limit = drush_memory_limit();
  413. if (($php_memory_limit > 0) && ($php_memory_limit <= 32*DRUSH_DRUPAL_KILOBYTE*DRUSH_DRUPAL_KILOBYTE)) {
  414. drush_set_error('DRUSH_MEMORY_LIMIT', dt('Your memory limit is set to !memory_limit; drush needs as much memory to run as Drupal. !php_ini_msg', array('!memory_limit' => $php_memory_limit / (DRUSH_DRUPAL_KILOBYTE*DRUSH_DRUPAL_KILOBYTE) . 'M', '!php_ini_msg' => _drush_php_ini_loaded_file_message())));
  415. }
  416. }
  417. /**
  418. * Evaluate the environment before command bootstrapping
  419. * begins. If the php environment is too restrictive, then
  420. * notify the user that a setting change is needed and abort.
  421. */
  422. function _drush_environment_check_php_ini() {
  423. $ini_checks = array('safe_mode' => '', 'open_basedir' => '', 'disable_functions' => array('exec', 'system'), 'disable_classes' => '');
  424. // Test to insure that certain php ini restrictions have not been enabled
  425. $prohibited_list = array();
  426. foreach ($ini_checks as $prohibited_mode => $disallowed_value) {
  427. $ini_value = ini_get($prohibited_mode);
  428. $invalid_value = FALSE;
  429. if (empty($disallowed_value)) {
  430. $invalid_value = !empty($ini_value);
  431. }
  432. else {
  433. foreach ($disallowed_value as $test_value) {
  434. if (strstr($ini_value, $test_value) !== FALSE) {
  435. $invalid_value = TRUE;
  436. }
  437. }
  438. }
  439. if ($invalid_value) {
  440. $prohibited_list[] = $prohibited_mode;
  441. }
  442. }
  443. if (!empty($prohibited_list)) {
  444. drush_log(dt('The following restricted PHP modes have non-empty values: !prohibited_list. This configuration is incompatible with drush. !php_ini_msg', array('!prohibited_list' => implode(' and ', $prohibited_list), '!php_ini_msg' => _drush_php_ini_loaded_file_message())), 'error');
  445. }
  446. return TRUE;
  447. }
  448. /**
  449. * Validate that Drush is running in a suitable environment.
  450. */
  451. function _drush_bootstrap_drush_validate() {
  452. // @todo _drush_environment_php_ini_checks() always returns TRUE.
  453. $return = _drush_environment_check_php_ini();
  454. if ($return !== TRUE) {
  455. return $return;
  456. }
  457. if (drush_environment_table_inc() === FALSE) {
  458. return FALSE;
  459. }
  460. return TRUE;
  461. }
  462. /*
  463. * To disable the warning about Windows support, set $options['check_os'] = FALSE
  464. * in drushrc.php. See examples/example.drushrc.php.
  465. */
  466. function drush_environment_check_os() {
  467. if (substr(PHP_OS, 0, 3) == 'WIN' && (drush_get_option('check_os', TRUE) !== 'i-want-4.x')) {
  468. $msg = 'Drush 4.x has significant limitations on Windows; it is not advisable to use on that platform. Substantial progress has been made towards supporing Windows on the 5.x branch; please upgrade. See http://drupal.org/project/drush for more information.';
  469. drush_log(dt($msg), 'warning');
  470. }
  471. }
  472. function drush_environment_table_inc() {
  473. // try using the PEAR installed version of Console_Table
  474. $tablefile = 'Console/Table.php';
  475. if (@file_get_contents($tablefile, FILE_USE_INCLUDE_PATH) === FALSE) {
  476. $tablefile = DRUSH_BASE_PATH . '/includes/table.inc';
  477. // Attempt to download Console Table, via various methods.
  478. if (!drush_file_not_empty($tablefile)) {
  479. $targetpath = dirname($tablefile);
  480. // not point continuing if we can't write to the target path
  481. if (!is_writable($targetpath)) {
  482. return drush_bootstrap_error('DRUSH_TABLES_INC', dt("Drush needs a copy of the PEAR Console_Table library in order to function, and the attempt to download this file automatically failed because you do not have permission to write files in !path. To continue you will need to download the !version package from http://pear.php.net/package/Console_Table, extract it, and copy the Table.php file into Drush's directory as !tablefile.", array('!path' => $targetpath, '!version' => DRUSH_TABLE_VERSION ,'!tablefile' => $tablefile)));
  483. }
  484. if ($file = @file_get_contents(DRUSH_TABLE_URL)) {
  485. @file_put_contents($tablefile, $file);
  486. }
  487. if (!file_exists($tablefile)) {
  488. drush_shell_exec("wget -q --timeout=30 -O $tablefile " . DRUSH_TABLE_URL);
  489. // wget creates an empty file on timeout. We remove it here.
  490. if (file_exists($tablefile) && !drush_file_not_empty($tablefile)) {
  491. unlink($tablefile);
  492. }
  493. if (!file_exists($tablefile)) {
  494. drush_shell_exec("curl -s --connect-timeout 30 -o $tablefile " . DRUSH_TABLE_URL);
  495. if (!file_exists($tablefile)) {
  496. return drush_bootstrap_error('DRUSH_TABLES_INC', dt("Drush needs a copy of the PEAR Console_Table library in order to function, and the attempt to download this file automatically failed. To continue you will need to download the !version package from http://pear.php.net/package/Console_Table, extract it, and copy the Table.php file into Drush's directory as !tablefile.", array('!version' => DRUSH_TABLE_VERSION ,'!tablefile' => $tablefile)));
  497. }
  498. }
  499. }
  500. }
  501. }
  502. require_once $tablefile;
  503. }
  504. /**
  505. * Initial Drush bootstrap phase.
  506. *
  507. * During the initialization of Drush,
  508. * this is the first step where all we are
  509. * aware of is Drush itself.
  510. *
  511. * In this step we will register the shutdown function,
  512. * parse the command line arguments and store them in their
  513. * related contexts.
  514. *
  515. * Configuration files (drushrc.php) that are
  516. * a) Specified on the command line
  517. * b) Stored in the root directory of drush.php
  518. * c) Stored in the home directory of the system user.
  519. *
  520. * Additionally the DRUSH_QUIET and DRUSH_BACKEND contexts,
  521. * will be evaluated now, as they need to be set very early in
  522. * the execution flow to be able to take affect/
  523. */
  524. function _drush_bootstrap_drush() {
  525. // Create an alias '@none' to represent no Drupal site
  526. _drush_sitealias_cache_alias('none', array('root' => '', 'uri' => ''));
  527. // Set the terminal width, used for wrapping table output.
  528. // Normally this is exported using tput in the drush script.
  529. // If this is not present we do an additional check using stty here.
  530. if (!($columns = getenv('COLUMNS'))) {
  531. exec('stty size 2>&1', $stty_output, $stty_status);
  532. if (!$stty_status) $columns = preg_replace('/\d+\s(\d+)/', '$1', $stty_output[0], -1, $stty_count);
  533. // If stty failed, or we couldn't parse it's output, we assume 80 columns.
  534. if ($stty_status || !$stty_count) $columns = 80;
  535. }
  536. // If a caller wants to reserve some room to add additional
  537. // information to the drush output via post-processing, the
  538. // --reserve-margin flag can be used to declare how much
  539. // space to leave out. This only affects drush functions
  540. // such as drush_print_table that wrap the output.
  541. $columns -= drush_get_option('reserve-margin', 0);
  542. drush_set_context('DRUSH_COLUMNS', $columns);
  543. // Statically define a way to call drush again.
  544. define('DRUSH_COMMAND', drush_find_drush());
  545. $drush_info = drush_read_drush_info();
  546. define('DRUSH_VERSION', $drush_info['drush_version']);
  547. $version_parts = explode('.', DRUSH_VERSION);
  548. define('DRUSH_MAJOR_VERSION', $version_parts[0]);
  549. define('DRUSH_MINOR_VERSION', $version_parts[1]);
  550. // prime the CWD cache
  551. drush_cwd();
  552. /* Copy ETC_PREFIX and SHARE_PREFIX from environment variables if available.
  553. * This alters where we check for server-wide config and alias files.
  554. * Used by unit test suite to provide a clean environment.
  555. */
  556. if (getenv('ETC_PREFIX')) drush_set_context('ETC_PREFIX', getenv('ETC_PREFIX'));
  557. if (getenv('SHARE_PREFIX')) drush_set_context('SHARE_PREFIX', getenv('SHARE_PREFIX'));
  558. drush_set_context('DOC_PREFIX', DRUSH_BASE_PATH);
  559. if (!file_exists(DRUSH_BASE_PATH . '/README.txt') && file_exists(drush_get_context('SHARE_PREFIX', '/usr') . '/share/doc/drush') . '/README.txt') {
  560. drush_set_context('DOC_PREFIX', drush_get_context('SHARE_PREFIX', '/usr') . '/share/doc/drush');
  561. }
  562. // Load a drushrc.php file in the drush.php's directory.
  563. drush_load_config('drush');
  564. // Load a drushrc.php file in the $ETC_PREFIX/etc/drush directory.
  565. drush_load_config('system');
  566. // Load a drushrc.php file at ~/.drushrc.php.
  567. drush_load_config('user');
  568. // Load a drushrc.php file in the ~/.drush directory.
  569. drush_load_config('home.drush');
  570. // Load a custom config specified with the --config option.
  571. drush_load_config('custom');
  572. // Process the site alias that specifies which instance
  573. // of drush (local or remote) this command will operate on.
  574. // We must do this after we load our config files (so that
  575. // site aliases are available), but before the rest
  576. // of the drush and drupal root bootstrap phases are
  577. // done, since site aliases may set option values that
  578. // affect these phases.
  579. // TODO: Note that this function will call drush_locate_root
  580. // (from within _drush_sitealias_find_record_for_local_site),
  581. // and drush_locate_root will be called again when bootstrapping
  582. // the drupal root below. Is there a good way to refactor this
  583. // so that we do not need to search for the root twice?
  584. drush_sitealias_check_arg();
  585. $backend = drush_set_context('DRUSH_BACKEND', drush_get_option(array('b', 'backend')));
  586. if ($backend) {
  587. // Load options passed as a JSON encoded string through STDIN.
  588. $stdin_options = _drush_backend_get_stdin();
  589. if (is_array($stdin_options)) {
  590. drush_set_context('stdin', $stdin_options);
  591. }
  592. }
  593. // Pipe implies quiet.
  594. $quiet = drush_set_context('DRUSH_QUIET', drush_get_option(array('q', 'quiet', 'p', 'pipe')));
  595. drush_set_context('DRUSH_PIPE', drush_get_option(array('p', 'pipe')));
  596. // When running in backend mode, all output is buffered, and returned
  597. // as a property of a JSON encoded associative array.
  598. if ($backend || $quiet) {
  599. ob_start();
  600. }
  601. _drush_bootstrap_global_options();
  602. }
  603. function _drush_bootstrap_global_options() {
  604. // Debug implies verbose
  605. drush_set_context('DRUSH_VERBOSE', drush_get_option(array('v', 'verbose', 'd', 'debug'), FALSE));
  606. drush_set_context('DRUSH_DEBUG', drush_get_option(array('d', 'debug')));
  607. // Backend implies affirmative unless negative is explicitly specified
  608. drush_set_context('DRUSH_NEGATIVE', drush_get_option(array('n', 'no'), FALSE));
  609. drush_set_context('DRUSH_AFFIRMATIVE', drush_get_option(array('y', 'yes'), FALSE) || (drush_get_context('DRUSH_BACKEND') && !drush_get_context('DRUSH_NEGATIVE')));
  610. drush_set_context('DRUSH_SIMULATE', drush_get_option(array('s', 'simulate'), FALSE));
  611. // Suppress colored logging if --nocolor option is explicitly given or if
  612. // terminal does not support it.
  613. $nocolor = (drush_get_option(array('nocolor'), FALSE));
  614. if (!$nocolor) {
  615. // Check for colorless terminal. If there is no terminal, then
  616. // 'tput colors 2>&1' will return "tput: No value for $TERM and no -T specified",
  617. // which is not numeric and therefore will put us in no-color mode.
  618. $colors = exec('tput colors 2>&1');
  619. $nocolor = !($colors === FALSE || (is_numeric($colors) && $colors >= 3));
  620. }
  621. drush_set_context('DRUSH_NOCOLOR', $nocolor);
  622. }
  623. /**
  624. * Validate the DRUSH_BOOTSTRAP_DRUPAL_ROOT phase.
  625. *
  626. * In this function, we will check if a valid Drupal directory is available.
  627. * We also determine the value that will be stored in the DRUSH_DRUPAL_ROOT
  628. * context and DRUPAL_ROOT constant if it is considered a valid option.
  629. */
  630. function _drush_bootstrap_drupal_root_validate() {
  631. $drupal_root = drush_get_option(array('r', 'root'), drush_locate_root());
  632. if (empty($drupal_root)) {
  633. return drush_bootstrap_error('DRUSH_NO_DRUPAL_ROOT', dt("A Drupal installation directory could not be found"));
  634. }
  635. if (!drush_valid_drupal_root($drupal_root)) {
  636. return drush_bootstrap_error('DRUSH_INVALID_DRUPAL_ROOT', dt("The directory !drupal_root does not contain a valid Drupal installation", array('!drupal_root' => $drupal_root)));
  637. }
  638. drush_bootstrap_value('drupal_root', $drupal_root);
  639. return TRUE;
  640. }
  641. /**
  642. * Bootstrap Drush with a valid Drupal Directory.
  643. *
  644. * In this function, the pwd will be moved to the root
  645. * of the Drupal installation.
  646. *
  647. * The DRUSH_DRUPAL_ROOT context and the DRUPAL_ROOT constant are
  648. * populated from the value that we determined during the validation phase.
  649. *
  650. * We also now load the drushrc.php for this specific platform.
  651. * We can now include files from the Drupal Tree, and figure
  652. * out more context about the platform, such as the version of Drupal.
  653. */
  654. function _drush_bootstrap_drupal_root() {
  655. $drupal_root = drush_set_context('DRUSH_DRUPAL_ROOT', drush_bootstrap_value('drupal_root'));
  656. define('DRUPAL_ROOT', $drupal_root);
  657. chdir($drupal_root);
  658. drush_load_config('drupal');
  659. require_once DRUPAL_ROOT . '/' . DRUSH_DRUPAL_BOOTSTRAP;
  660. $version = drush_set_context('DRUSH_DRUPAL_VERSION', drush_drupal_version());
  661. $major_version = drush_set_context('DRUSH_DRUPAL_MAJOR_VERSION', drush_drupal_major_version());
  662. _drush_bootstrap_global_options();
  663. drush_log(dt("Initialized Drupal !version root directory at !drupal_root", array("!version" => $version, '!drupal_root' => $drupal_root)));
  664. }
  665. /**
  666. * VALIDATE the DRUSH_BOOTSTRAP_DRUPAL_SITE phase.
  667. *
  668. * In this function we determine the URL used for the command,
  669. * and check for a valid settings.php file.
  670. *
  671. * To do this, we need to set up the $_SERVER environment variable,
  672. * to allow us to use conf_path to determine what Drupal will load
  673. * as a configuration file.
  674. */
  675. function _drush_bootstrap_drupal_site_validate() {
  676. $site_path = drush_site_path();
  677. $elements = explode('/', $site_path);
  678. $current = array_pop($elements);
  679. if (!$current) {
  680. $current = 'default';
  681. }
  682. $uri = 'http://'. $current;
  683. $drush_uri = drush_bootstrap_value('drush_uri', drush_get_option(array('l', 'uri'), $uri));
  684. // Fake the necessary HTTP headers that Drupal needs:
  685. if ($drush_uri) {
  686. $drupal_base_url = parse_url($drush_uri);
  687. // If there's no url scheme set, add http:// and re-parse the url
  688. // so the host and path values are set accurately.
  689. if (!array_key_exists('scheme', $drupal_base_url)) {
  690. $drush_uri = 'http://' . $drush_uri;
  691. $drupal_base_url = parse_url($drush_uri);
  692. }
  693. // Fill in defaults.
  694. $drupal_base_url += array(
  695. 'path' => NULL,
  696. 'host' => NULL,
  697. 'port' => NULL,
  698. );
  699. $_SERVER['HTTP_HOST'] = $drupal_base_url['host'];
  700. if ($drupal_base_url['port']) {
  701. $_SERVER['HTTP_HOST'] .= ':' . $drupal_base_url['port'];
  702. }
  703. $_SERVER['SERVER_PORT'] = $drupal_base_url['port'];
  704. if (array_key_exists('path', $drupal_base_url)) {
  705. $_SERVER['PHP_SELF'] = $drupal_base_url['path'] . '/index.php';
  706. }
  707. else {
  708. $_SERVER['PHP_SELF'] = '/index.php';
  709. }
  710. }
  711. else {
  712. $_SERVER['HTTP_HOST'] = 'default';
  713. $_SERVER['PHP_SELF'] = '/index.php';
  714. }
  715. $_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] = $_SERVER['PHP_SELF'];
  716. $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
  717. $_SERVER['REQUEST_METHOD'] = NULL;
  718. $_SERVER['SERVER_SOFTWARE'] = NULL;
  719. $_SERVER['HTTP_USER_AGENT'] = NULL;
  720. $site = drush_bootstrap_value('site', $_SERVER['HTTP_HOST']);
  721. $conf_path = drush_bootstrap_value('conf_path', conf_path(TRUE, TRUE));
  722. $conf_file = "./$conf_path/settings.php";
  723. if (!file_exists($conf_file)) {
  724. return drush_bootstrap_error('DRUPAL_SITE_SETTINGS_NOT_FOUND', dt("Could not find a Drupal settings.php file at !file.",
  725. array('!file' => $conf_file)));
  726. }
  727. return TRUE;
  728. }
  729. /**
  730. * Called by _drush_bootstrap_drupal_site to do the main work
  731. * of the drush drupal site bootstrap.
  732. */
  733. function _drush_bootstrap_do_drupal_site() {
  734. $drush_uri = drush_set_context('DRUSH_URI', drush_bootstrap_value('drush_uri'));
  735. $site = drush_set_context('DRUSH_DRUPAL_SITE', drush_bootstrap_value('site'));
  736. $conf_path = drush_set_context('DRUSH_DRUPAL_SITE_ROOT', drush_bootstrap_value('conf_path'));
  737. // Create an alias '@self'
  738. _drush_sitealias_cache_alias('self', array('root' => drush_get_context('DRUSH_DRUPAL_ROOT'), 'uri' => $drush_uri));
  739. drush_log(dt("Initialized Drupal site !site at !site_root", array('!site' => $site, '!site_root' => $conf_path)));
  740. drush_load_config('site');
  741. _drush_bootstrap_global_options();
  742. }
  743. /**
  744. * Initialize a site on the Drupal root.
  745. *
  746. * We now set various contexts that we determined and confirmed to be valid.
  747. * Additionally we load an optional drushrc.php file in the site directory.
  748. */
  749. function _drush_bootstrap_drupal_site() {
  750. _drush_bootstrap_do_drupal_site();
  751. _drush_bootstrap_redo_drupal_site();
  752. }
  753. /**
  754. * Re-do the drupal site bootstrap (and possibly the
  755. * drupal root bootstrap) if a site alias was processed
  756. * after the site bootstrap phase completed. This will
  757. * happen when processing "drush sitealias command" for
  758. * a site alias defined in a drushrc.php file in the
  759. * default site's drush configuration directory.
  760. */
  761. function _drush_bootstrap_redo_drupal_site() {
  762. // If drush_load_config defined a site alias that did not
  763. // exist before, then sitealias check arg might now match
  764. // against one of those aliases.
  765. if (drush_sitealias_check_arg() === TRUE) {
  766. $remote_host = drush_get_option('remote-host');
  767. if (!isset($remote_host)) {
  768. // Check to see if the drupal root changed.
  769. // If it has, we will set remote-host to cause
  770. // this command to be executed via the backend invoke
  771. // process.
  772. $sitealias_drupal_root = drush_get_option(array('r', 'root'));
  773. if (($sitealias_drupal_root != null) && (DRUPAL_ROOT != $sitealias_drupal_root)) {
  774. drush_set_option('remote-host', 'localhost');
  775. }
  776. else {
  777. // If we set an alias, then we need to bootstrap the
  778. // drupal site once again. It is possible to re-bootstrap
  779. // the site at this point because settings.php has not
  780. // been included yet.
  781. drush_log(dt("Re-bootstrap drupal site."));
  782. _drush_bootstrap_drupal_site_validate();
  783. _drush_bootstrap_do_drupal_site();
  784. }
  785. }
  786. }
  787. }
  788. /**
  789. * Initialize and load the Drupal configuration files.
  790. *
  791. * We process and store a normalized set of database credentials
  792. * from the loaded configuration file, so we can validate them
  793. * and access them easily in the future.
  794. */
  795. function _drush_bootstrap_drupal_configuration() {
  796. global $conf, $drush_conf_override;
  797. drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
  798. // Unset drupal error handler and restore drush's one.
  799. if (drush_drupal_major_version() >= 7) {
  800. restore_error_handler();
  801. }
  802. // Overriding the $conf array from drupal CONFIGURATION bootstrap with the
  803. // Overrides we collected on the loaded config files on DRUSH_BOOTSTRAP_DRUSH
  804. $conf = is_array($conf) && is_array($drush_conf_override) ? array_merge($conf, $drush_conf_override) : $conf;
  805. // Populate the DRUSH_DB_CREDENTIALS with the fields loaded from the configuration.
  806. $creds = array();
  807. switch (drush_drupal_major_version()) {
  808. case 5:
  809. case 6:
  810. if (!empty($GLOBALS['db_url'])) {
  811. $url = $GLOBALS['db_url'];
  812. if (is_array($url)) {
  813. $url = $url['default'];
  814. }
  815. $parts = parse_url($url);
  816. $parts += array('pass' => '', 'port' => '');
  817. $creds['driver'] = $parts['scheme'];
  818. $creds['user'] = urldecode($parts['user']);
  819. $creds['host'] = $parts['host'];
  820. $creds['port'] = $parts['port'];
  821. $creds['pass'] = urldecode($parts['pass']);
  822. $creds['name'] = trim($parts['path'], '/');
  823. }
  824. break;
  825. case 7:
  826. default:
  827. if (!empty($GLOBALS['databases']['default']['default'])) {
  828. $conn = $GLOBALS['databases']['default']['default'];
  829. // Fill in defaults to prevent notices.
  830. $conn += array(
  831. 'username' => NULL,
  832. 'host' => NULL,
  833. 'port' => NULL,
  834. 'password' => NULL,
  835. 'database' => NULL,
  836. );
  837. $creds['driver'] = $conn['driver'];
  838. $creds['user'] = $conn['username'];
  839. $creds['host'] = $conn['host'];
  840. $creds['port'] = $conn['port'];
  841. $creds['name'] = $conn['database'];
  842. $creds['pass'] = $conn['password'];
  843. }
  844. break;
  845. }
  846. drush_set_context('DRUSH_DB_CREDENTIALS', $creds);
  847. }
  848. /**
  849. * Validate the DRUSH_BOOTSTRAP_DRUPAL_DATABASE phase
  850. *
  851. * Attempt to making a working database connection using the
  852. * database credentials that were loaded during the previous
  853. * phase.
  854. */
  855. function _drush_bootstrap_drupal_database_validate() {
  856. if (!drush_valid_db_credentials()) {
  857. return drush_bootstrap_error('DRUSH_DRUPAL_DB_ERROR');
  858. }
  859. return TRUE;
  860. }
  861. /**
  862. * Boostrap the Drupal database.
  863. */
  864. function _drush_bootstrap_drupal_database() {
  865. drush_log(dt("Successfully connected to the Drupal database."), 'bootstrap');
  866. drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE);
  867. }
  868. /**
  869. * Attempt to load the full Drupal system.
  870. */
  871. function _drush_bootstrap_drupal_full() {
  872. ob_start();
  873. drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
  874. ob_end_clean();
  875. // Unset drupal error handler and restore drush's one.
  876. if (drush_drupal_major_version() == 6) {
  877. restore_error_handler();
  878. }
  879. // If needed, prod module_implements() to recognize our system_watchdog() implementation.
  880. $dogs = module_implements('watchdog');
  881. if (!in_array('system', $dogs)) {
  882. // Note that this resets module_implements cache.
  883. module_implements('watchdog', FALSE, TRUE);
  884. }
  885. _drush_log_drupal_messages();
  886. }
  887. /**
  888. * Log into the bootstrapped Drupal site with a specific
  889. * username or user id.
  890. */
  891. function _drush_bootstrap_drupal_login() {
  892. $drush_user = drush_set_context('DRUSH_USER', drush_get_option(array('u', 'user'), 0));
  893. drush_drupal_login($drush_user);
  894. _drush_log_drupal_messages();
  895. }
  896. /**
  897. * Returns the current working directory.
  898. *
  899. * This is the directory as it was when drush was started, not the
  900. * directory we are currently in. For that, use getcwd() directly.
  901. */
  902. function drush_cwd() {
  903. if ($path = drush_get_context('DRUSH_OLDCWD')) {
  904. return $path;
  905. }
  906. // We use PWD if available because getcwd() resolves symlinks, which
  907. // could take us outside of the Drupal root, making it impossible to find.
  908. // $_SERVER['PWD'] isn't set on windows and generates a Notice.
  909. $path = isset($_SERVER['PWD']) ? $_SERVER['PWD'] : '';
  910. if (empty($path)) {
  911. $path = getcwd();
  912. }
  913. // Convert windows paths.
  914. $path = _drush_convert_path($path);
  915. // Save original working dir case some command wants it.
  916. drush_set_context('DRUSH_OLDCWD', $path);
  917. return $path;
  918. }
  919. /**
  920. * Converts a Windows path (dir1\dir2\dir3) into a Unix path (dir1/dir2/dir3).
  921. * Also converts a cygwin "drive emulation" path (/cygdrive/c/dir1) into a
  922. * proper drive path, still with Unix slashes (c:/dir1).
  923. */
  924. function _drush_convert_path($path) {
  925. $path = str_replace('\\','/', $path);
  926. $path = preg_replace('/^\/cygdrive\/([A-Za-z])(.*)$/', '\1:\2', $path);
  927. return $path;
  928. }
  929. /**
  930. * Returns parent directory.
  931. *
  932. * @param string
  933. * Path to start from.
  934. *
  935. * @return string
  936. * Parent path of given path.
  937. */
  938. function _drush_shift_path_up($path) {
  939. if (empty($path)) {
  940. return FALSE;
  941. }
  942. $path = explode('/', $path);
  943. // Move one directory up.
  944. array_pop($path);
  945. return implode('/', $path);
  946. }
  947. /**
  948. * Like Drupal conf_path, but searching from beneath.
  949. * Allows proper site uri detection in site sub-directories.
  950. *
  951. * Essentially looks for a settings.php file.
  952. *
  953. * @param string
  954. * Search starting path. Defaults to current working directory.
  955. *
  956. * @return
  957. * Current site path (folder containing settings.php) or FALSE if not found.
  958. */
  959. function drush_site_path($path = NULL) {
  960. static $site_path;
  961. if (!isset($site_path)) {
  962. $site_path = FALSE;
  963. $path = empty($path) ? drush_cwd() : $path;
  964. // Check the current path.
  965. if (file_exists($path . '/settings.php')) {
  966. $site_path = $path;
  967. }
  968. else {
  969. // Move up dir by dir and check each.
  970. while ($path = _drush_shift_path_up($path)) {
  971. if (file_exists($path . '/settings.php')) {
  972. $site_path = $path;
  973. break;
  974. }
  975. }
  976. }
  977. $site_root = drush_locate_root();
  978. if (file_exists($site_root . '/sites/sites.php')) {
  979. $sites = array();
  980. // This will overwrite $sites with the desired mappings.
  981. include($site_root . '/sites/sites.php');
  982. // We do a reverse lookup here to determine the URL given the site key.
  983. if ($match = array_search($site_path, $sites)) {
  984. $site_path = $match;
  985. }
  986. }
  987. // Last resort: try from site root
  988. if (!$site_path) {
  989. if ($site_root) {
  990. if (file_exists($site_root . '/sites/default/settings.php')) {
  991. $site_path = $site_root . '/sites/default';
  992. }
  993. }
  994. }
  995. }
  996. return $site_path;
  997. }
  998. /**
  999. * Exhaustive depth-first search to try and locate the Drupal root directory.
  1000. * This makes it possible to run drush from a subdirectory of the drupal root.
  1001. *
  1002. * @param
  1003. * Search start path. Defaults to current working directory.
  1004. * @return
  1005. * A path to drupal root, or FALSE if not found.
  1006. */
  1007. function drush_locate_root($start_path = NULL) {
  1008. $drupal_root = FALSE;
  1009. $start_path = empty($start_path) ? drush_cwd() : $start_path;
  1010. foreach (array(TRUE, FALSE) as $follow_symlinks) {
  1011. $path = $start_path;
  1012. if ($follow_symlinks && is_link($path)) {
  1013. $path = realpath($path);
  1014. }
  1015. // Check the start path.
  1016. if (drush_valid_drupal_root($path)) {
  1017. $drupal_root = $path;
  1018. break;
  1019. }
  1020. else {
  1021. // Move up dir by dir and check each.
  1022. while ($path = _drush_shift_path_up($path)) {
  1023. if ($follow_symlinks && is_link($path)) {
  1024. $path = realpath($path);
  1025. }
  1026. if (drush_valid_drupal_root($path)) {
  1027. $drupal_root = $path;
  1028. break 2;
  1029. }
  1030. }
  1031. }
  1032. }
  1033. return $drupal_root;
  1034. }
  1035. /**
  1036. * Checks whether given path qualifies as a Drupal root.
  1037. *
  1038. * @param string
  1039. * Path to check.
  1040. *
  1041. * @return boolean
  1042. * True if given path seems to be a Drupal root, otherwise FALSE.
  1043. */
  1044. function drush_valid_drupal_root($path) {
  1045. return !empty($path) && is_dir($path) && file_exists($path . '/' . DRUSH_DRUPAL_BOOTSTRAP);
  1046. }
  1047. /**
  1048. * Tests the currently loaded database credentials to ensure a database connection can be made.
  1049. */
  1050. function drush_valid_db_credentials() {
  1051. $creds = drush_get_context('DRUSH_DB_CREDENTIALS');
  1052. // Do minimal checking that we have the necessary information.
  1053. if (count($creds) == 0) {
  1054. return FALSE;
  1055. }
  1056. $type = $creds['driver'];
  1057. switch (drush_drupal_major_version()) {
  1058. case 5:
  1059. case 6:
  1060. // Check availability of db extension in PHP and also Drupal support.
  1061. if (file_exists('./includes/install.'. $type .'.inc')) {
  1062. require_once './includes/install.'. $type .'.inc';
  1063. $function = $type .'_is_available';
  1064. if (!$function()) {
  1065. drush_log(dt('!type extension for PHP is not installed. Check your php.ini to see how you can enable it.', array('!type' => $type)), 'bootstrap');
  1066. return FALSE;
  1067. }
  1068. }
  1069. else {
  1070. drush_log(dt('!type database type is unsupported.', array('!type' => $type)), 'bootstrap');
  1071. return FALSE;
  1072. }
  1073. // Verify connection settings.
  1074. switch ($type) {
  1075. case 'mysql':
  1076. $hostspec = $creds['port'] ? $creds['host'] . ':' . $creds['port'] : $creds['host'];
  1077. $connection = @mysql_connect($hostspec, $creds['user'], $creds['pass']);
  1078. if (!$connection || !mysql_select_db($creds['name'])) {
  1079. drush_log(mysql_error(), 'bootstrap');
  1080. return FALSE;
  1081. }
  1082. break;
  1083. case 'mysqli':
  1084. $connection = mysqli_init();
  1085. @mysqli_real_connect($connection, $creds['host'], $creds['user'], $creds['pass'], $creds['name'], (int)$creds['port']);
  1086. if (mysqli_connect_errno() > 0) {
  1087. drush_log(mysqli_connect_error(), 'bootstrap');
  1088. return FALSE;
  1089. }
  1090. break;
  1091. case 'pgsql':
  1092. $conn_string = sprintf("host=%s user=%s password=%s dbname=%s", $creds['host'], $creds['user'], $creds['pass'], $creds['name']);
  1093. if (isset($creds['port'])) {
  1094. $conn_string .= ' port=' . $creds['port'];
  1095. }
  1096. // Copied from d6's database.pgsql.inc:
  1097. // pg_last_error() does not return a useful error message for database
  1098. // connection errors. We must turn on error tracking to get at a good error
  1099. // message, which will be stored in $php_errormsg.
  1100. $track_errors_previous = ini_get('track_errors');
  1101. ini_set('track_errors', 1);
  1102. $connection = @pg_connect($conn_string);
  1103. if (!$connection) {
  1104. require_once './includes/unicode.inc';
  1105. drush_log(decode_entities($php_errormsg), 'bootstrap');
  1106. // Restore error tracking setting
  1107. ini_set('track_errors', $track_errors_previous);
  1108. return FALSE;
  1109. }
  1110. // Restore error tracking setting
  1111. ini_set('track_errors', $track_errors_previous);
  1112. break;
  1113. }
  1114. break;
  1115. case 7:
  1116. default:
  1117. // Drupal >=7 requires PDO and drush requires php 5.2, that ships with PDO
  1118. // but it may be compiled with --disable-pdo.
  1119. if (!class_exists('PDO')) {
  1120. drush_log(dt('PDO support is required.'), 'bootstrap');
  1121. return FALSE;
  1122. }
  1123. // Check the database specific driver is available.
  1124. if (!in_array($type, PDO::getAvailableDrivers())) {
  1125. drush_log(dt('!type extension for PHP PDO is not installed. Check your php.ini to see how you can enable it.', array('!type' => $type)), 'bootstrap');
  1126. return FALSE;
  1127. }
  1128. // Build the connection string.
  1129. if ($type === 'sqlite') {
  1130. $constr = 'sqlite:' . $creds['name'];
  1131. }
  1132. else {
  1133. $constr = sprintf("%s:dbname=%s;host=%s", $type, $creds['name'], $creds['host']);
  1134. if (!empty($creds['port'])) {
  1135. $constr .= sprintf(";port=%d", $creds['port']);
  1136. }
  1137. }
  1138. try {
  1139. $db = new PDO($constr, $creds['user'], $creds['pass']);
  1140. $db = null;
  1141. }
  1142. catch (PDOException $e) {
  1143. drush_log($e->getMessage(), 'bootstrap');
  1144. return FALSE;
  1145. }
  1146. break;
  1147. }
  1148. return TRUE;
  1149. }
  1150. /**
  1151. * Determine a proper way to call drush again
  1152. *
  1153. * This check if we were called directly or as an argument to some
  1154. * wrapper command (php and sudo are checked now).
  1155. *
  1156. * Calling ./drush.php directly yields the following environment:
  1157. *
  1158. * _SERVER["argv"][0] => ./drush.php
  1159. *
  1160. * Calling php ./drush.php also yields the following:
  1161. *
  1162. * _SERVER["argv"][0] => ./drush.php
  1163. *
  1164. * Note that the $_ global is defined only in bash and therefore cannot
  1165. * be relied upon.
  1166. *
  1167. * We will therefore assume PHP is available in the path and is named
  1168. * "php" for execute ourselves. That is, the #!/usr/bin/env php is
  1169. * working and valid, unless a PHP constant is defined, which can be
  1170. * done by the shell wrapper.
  1171. *
  1172. * The DRUSH_COMMAND constant is initialised to the value of this
  1173. * function when environment.inc is loaded.
  1174. *
  1175. * @see DRUSH_COMMAND
  1176. */
  1177. function drush_find_drush() {
  1178. $php = drush_get_option('php');
  1179. if (isset($php)) {
  1180. $drush = $php . " " . realpath($_SERVER['argv'][0]) . " --php='$php'";
  1181. } else {
  1182. $drush = realpath($_SERVER['argv']['0']);
  1183. }
  1184. return $drush;
  1185. }
  1186. /**
  1187. * Read the drush info file.
  1188. */
  1189. function drush_read_drush_info() {
  1190. $drush_info_file = dirname(__FILE__) . '/../drush.info';
  1191. return parse_ini_file($drush_info_file);
  1192. }
  1193. /**
  1194. * Make a determination whether or not the given
  1195. * host is local or not.
  1196. *
  1197. * @param host
  1198. * A hostname, 'localhost' or '127.0.0.1'.
  1199. * @return
  1200. * True if the host is local.
  1201. */
  1202. function drush_is_local_host($host) {
  1203. // In order for this to work right, you must use 'localhost' or '127.0.0.1'
  1204. // or the machine returned by 'uname -n' for your 'remote-host' entry in
  1205. // your site alias.
  1206. if (($host == 'localhost') || ($host == '127.0.0.1')) {
  1207. return TRUE;
  1208. }
  1209. // If uname -n returns an fqdn (that is, uname -n == hostname -f),
  1210. // then we will require that it exactly match the host in order
  1211. // to be considered local. However, the usual convention is for
  1212. // uname -n to return only the node name (that is, uname -n == hostname -a).
  1213. // When this is the case, we will consider $host to be local if the
  1214. // machine portion of it (everything up to the first dot) matches the
  1215. // current value of uname -n. We prefer uname -n to hostname as
  1216. // the output of uname is more regular than hostname.
  1217. $uname = php_uname('n');
  1218. return (strpos($uname,'.') !== FALSE) ? ($host == $uname) : substr($host . '.',0,strlen($uname)+1) == $uname . '.';
  1219. }
  1220. /**
  1221. * Return the user's home directory.
  1222. */
  1223. function drush_server_home() {
  1224. $home = NULL;
  1225. // $_SERVER['HOME'] isn't set on windows and generates a Notice.
  1226. if (!empty($_SERVER['HOME'])) {
  1227. $home = $_SERVER['HOME'];
  1228. }
  1229. elseif (!empty($_SERVER['HOMEDRIVE']) && !empty($_SERVER['HOMEPATH'])) {
  1230. // home on windows
  1231. $home = $_SERVER['HOMEDRIVE'] . $_SERVER['HOMEPATH'];
  1232. }
  1233. return $home;
  1234. }
  1235. /*
  1236. * The path to the global cache directory.
  1237. */
  1238. function drush_directory_cache() {
  1239. if (getenv('CACHE_PREFIX')) {
  1240. return getenv('CACHE_PREFIX') . '/cache';
  1241. }
  1242. elseif ($home = drush_server_home()) {
  1243. return "$home/.drush/cache";
  1244. }
  1245. else {
  1246. return FALSE;
  1247. }
  1248. }
  1249. /**
  1250. * Get complete information for all available extensions (modules and themes).
  1251. *
  1252. * @return
  1253. * An array containing info for all available extensions.
  1254. */
  1255. function drush_get_extensions() {
  1256. drush_include_engine('drupal', 'environment');
  1257. return array_merge(drush_get_modules(), drush_get_themes());
  1258. }
  1259. /**
  1260. * Test compatibility of a extension with version of drupal core and php.
  1261. *
  1262. * @param $file Extension object as returned by system_rebuild_module_data().
  1263. * @return TRUE if the extension is incompatible.
  1264. */
  1265. function drush_extension_check_incompatibility($file) {
  1266. if (!isset($file->info['core'])
  1267. || $file->info['core'] != DRUPAL_CORE_COMPATIBILITY
  1268. || version_compare(phpversion(), $file->info['php']) < 0) {
  1269. return TRUE;
  1270. }
  1271. return FALSE;
  1272. }
  1273. /**
  1274. *
  1275. */
  1276. function drush_drupal_required_modules($modules) {
  1277. drush_include_engine('drupal', 'environment');
  1278. return _drush_drupal_required_modules($modules);
  1279. }
  1280. /**
  1281. * Return the default theme.
  1282. *
  1283. * @return
  1284. * Machine name of the default theme.
  1285. */
  1286. function drush_theme_get_default() {
  1287. return variable_get('theme_default', 'garland');
  1288. }
  1289. /**
  1290. * Return the administration theme.
  1291. *
  1292. * @return
  1293. * Machine name of the administration theme.
  1294. */
  1295. function drush_theme_get_admin() {
  1296. return variable_get('admin_theme', drush_theme_get_default());
  1297. }