environment.inc 47 KB

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