startup.inc

  1. 8.0.x includes/startup.inc
  2. master includes/startup.inc

Functions used when Drush is starting up.

This file is included and used early in the Drush startup process, before any other Drush APIs are available, and before the autoloader has been included.

Functions

Namesort descending Description
drush_env Get the current enviornment.
drush_escapeshellarg
drush_is_windows Determine whether current OS is a Windows variant.
drush_run_main Run drush_main() and then exit. Used when we cannot hand over execution to the launcher.
drush_startup drush_startup is called once, by the Drush "finder" script -- the "drush" script at the Drush root. It finds the correct Drush "wrapper" or "launcher" script to use, and executes it with process replacement.
find_wrapper_or_launcher Checks the provided location and return the appropriate Drush wrapper or Drush launcher script, if found.
find_wrapper_or_launcher_at_location We are somewhat "loose" about whether we are looking for "drush" or "drush.launcher", because in old versions of Drush, the "drush launcher" was named "drush". Otherwise, there wouldn't be any…
find_wrapper_or_launcher_in_specific_locations
find_wrapper_or_launcher_in_vicinity We look for a "Drush wrapper" script that might be stored in the root of a site. If there is no wrapper script, then we look for the drush.launcher script in vendor/bin. We try just a few of the most common locations; if the user…
_drush_escapeshellarg_linux Linux version of escapeshellarg().
_drush_escapeshellarg_windows Windows version of escapeshellarg().

File

includes/startup.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * Functions used when Drush is starting up.
  5. *
  6. * This file is included and used early in the Drush
  7. * startup process, before any other Drush APIs are
  8. * available, and before the autoloader has been included.
  9. */
  10. /**
  11. * Get the current enviornment.
  12. */
  13. function drush_env() {
  14. // Fetch the current environment. To ensure that
  15. // $_ENV is correctly populated, make sure that
  16. // the value of 'variables-order' in your php.ini
  17. // contains "E" ("Environment"). See:
  18. // http://us.php.net/manual/en/ini.core.php#ini.variables-order
  19. $env = $_ENV;
  20. // If PHP is not configured correctly, $_ENV will be
  21. // empty. Drush counts on the fact that environment
  22. // variables will always be available, though, so we
  23. // need to repair this situation. We can always access
  24. // individual environmnet values via getenv(); however,
  25. // there is no PHP API that will tell us all of the
  26. // available values, so we will get the environment
  27. // variable values using 'printenv'.
  28. if (empty($env)) {
  29. exec('printenv', $env_items);
  30. foreach ($env_items as $item) {
  31. // Each $item is 'key=value' or just 'key'.
  32. // If $item has no value, then explode will return
  33. // a single array, [0 => 'key']. We add a default
  34. // value of [1 => 'value'] to cover this case. If
  35. // explode returns two items, the default value is ignored.
  36. list($key, $value) = explode('=', $item, 2) + array(1 => '');
  37. $env[$key] = $value;
  38. }
  39. }
  40. return $env;
  41. }
  42. /**
  43. * Checks the provided location and return the appropriate
  44. * Drush wrapper or Drush launcher script, if found.
  45. *
  46. * If the provided location looks like it might be a web
  47. * root (i.e., it contains an index.php), then we will search
  48. * in a number of locations in the general vicinity of the
  49. * web root for a Drush executable.
  50. *
  51. * For other locations, we will look only in that specific
  52. * directory, or in vendor/bin.
  53. */
  54. function find_wrapper_or_launcher($location) {
  55. if (file_exists($location. DIRECTORY_SEPARATOR. 'index.php')) {
  56. return find_wrapper_or_launcher_in_vicinity($location);
  57. }
  58. return find_wrapper_or_launcher_in_specific_locations($location, ["", 'vendor'. DIRECTORY_SEPARATOR. 'bin']);
  59. }
  60. /**
  61. * We look for a "Drush wrapper" script that might
  62. * be stored in the root of a site. If there is
  63. * no wrapper script, then we look for the
  64. * drush.launcher script in vendor/bin. We try just a
  65. * few of the most common locations; if the user relocates
  66. * their vendor directory anywhere else, then they must
  67. * use a wrapper script to locate it. See the comment in
  68. * 'examples/drush' for details.
  69. */
  70. function find_wrapper_or_launcher_in_vicinity($location) {
  71. $sep = DIRECTORY_SEPARATOR;
  72. $drush_locations = [
  73. "",
  74. "vendor{$sep}bin/",
  75. "..{$sep}vendor{$sep}bin{$sep}",
  76. "sites{$sep}all{$sep}vendor{$sep}bin{$sep}",
  77. "sites{$sep}all{$sep}vendor{$sep}drush{$sep}drush{$sep}",
  78. "sites{$sep}all{$sep}drush{$sep}drush{$sep}",
  79. "drush{$sep}drush{$sep}",
  80. ];
  81. return find_wrapper_or_launcher_in_specific_locations($location, $drush_locations);
  82. }
  83. function find_wrapper_or_launcher_in_specific_locations($location, $drush_locations) {
  84. $sep = DIRECTORY_SEPARATOR;
  85. foreach ($drush_locations as $d) {
  86. $found_script = find_wrapper_or_launcher_at_location("$location$sep$d");
  87. if (!empty($found_script)) {
  88. return $found_script;
  89. }
  90. }
  91. return "";
  92. }
  93. /**
  94. * We are somewhat "loose" about whether we are looking
  95. * for "drush" or "drush.launcher", because in old versions
  96. * of Drush, the "drush launcher" was named "drush".
  97. * Otherwise, there wouldn't be any point in looking for
  98. * "drush.launcher" at the root, or "drush" in a vendor directory.
  99. * We also allow users to rename their drush wrapper to
  100. * 'drush.wrapper' to avoid conflicting with a directory named
  101. * 'drush' at the site root.
  102. */
  103. function find_wrapper_or_launcher_at_location($location) {
  104. $sep = DIRECTORY_SEPARATOR;
  105. // Sanity-check: empty $location means that we should search
  106. // at the cwd, not at the root of the filesystem.
  107. if (empty($location)) {
  108. $location = ".";
  109. }
  110. foreach (array('.launcher', '.wrapper', '') as $suffix) {
  111. $check_location = "$location{$sep}drush$suffix";
  112. if (is_file($check_location)) {
  113. return $check_location;
  114. }
  115. }
  116. return "";
  117. }
  118. /**
  119. * Determine whether current OS is a Windows variant.
  120. */
  121. function drush_is_windows($os = NULL) {
  122. return strtoupper(substr($os ?: PHP_OS, 0, 3)) === 'WIN';
  123. }
  124. function drush_escapeshellarg($arg, $os = NULL, $raw = FALSE) {
  125. // Short-circuit escaping for simple params (keep stuff readable)
  126. if (preg_match('|^[a-zA-Z0-9.:/_-]*$|', $arg)) {
  127. return $arg;
  128. }
  129. elseif (drush_is_windows($os)) {
  130. return _drush_escapeshellarg_windows($arg, $raw);
  131. }
  132. else {
  133. return _drush_escapeshellarg_linux($arg, $raw);
  134. }
  135. }
  136. /**
  137. * Linux version of escapeshellarg().
  138. *
  139. * This is intended to work the same way that escapeshellarg() does on
  140. * Linux. If we need to escape a string that will be used remotely on
  141. * a Linux system, then we need our own implementation of escapeshellarg,
  142. * because the Windows version behaves differently.
  143. */
  144. function _drush_escapeshellarg_linux($arg, $raw = FALSE) {
  145. // For single quotes existing in the string, we will "exit"
  146. // single-quote mode, add a \' and then "re-enter"
  147. // single-quote mode. The result of this is that
  148. // 'quote' becomes '\''quote'\''
  149. $arg = preg_replace('/\'/', '\'\\\'\'', $arg);
  150. // Replace "\t", "\n", "\r", "\0", "\x0B" with a whitespace.
  151. // Note that this replacement makes Drush's escapeshellarg work differently
  152. // than the built-in escapeshellarg in PHP on Linux, as these characters
  153. // usually are NOT replaced. However, this was done deliberately to be more
  154. // conservative when running _drush_escapeshellarg_linux on Windows
  155. // (this can happen when generating a command to run on a remote Linux server.)
  156. $arg = str_replace(array("\t", "\n", "\r", "\0", "\x0B"), ' ', $arg);
  157. // Only wrap with quotes when needed.
  158. if(!$raw) {
  159. // Add surrounding quotes.
  160. $arg = "'" . $arg . "'";
  161. }
  162. return $arg;
  163. }
  164. /**
  165. * Windows version of escapeshellarg().
  166. */
  167. function _drush_escapeshellarg_windows($arg, $raw = FALSE) {
  168. // Double up existing backslashes
  169. $arg = preg_replace('/\\\/', '\\\\\\\\', $arg);
  170. // Double up double quotes
  171. $arg = preg_replace('/"/', '""', $arg);
  172. // Double up percents.
  173. // $arg = preg_replace('/%/', '%%', $arg);
  174. // Only wrap with quotes when needed.
  175. if(!$raw) {
  176. // Add surrounding quotes.
  177. $arg = '"' . $arg . '"';
  178. }
  179. return $arg;
  180. }
  181. /**
  182. * drush_startup is called once, by the Drush "finder"
  183. * script -- the "drush" script at the Drush root.
  184. * It finds the correct Drush "wrapper" or "launcher"
  185. * script to use, and executes it with process replacement.
  186. */
  187. function drush_startup($argv) {
  188. $sep = DIRECTORY_SEPARATOR;
  189. $found_script = "";
  190. $cwd = getcwd();
  191. $home = getenv("HOME");
  192. $use_dir = "$home{$sep}.drush{$sep}use";
  193. // Get the arguments for the command. Shift off argv[0],
  194. // which contains the name of this script.
  195. $arguments = $argv;
  196. array_shift($arguments);
  197. // We need to do at least a partial parsing of the options,
  198. // so that we can find --root / -r and so on.
  199. $VERBOSE=FALSE;
  200. $DEBUG=FALSE;
  201. $ROOT=FALSE;
  202. $COMMAND=FALSE;
  203. $ALIAS=FALSE;
  204. $VAR=FALSE;
  205. foreach ($arguments as $arg) {
  206. // If a variable to set was indicated on the
  207. // previous iteration, then set the value of
  208. // the named variable (e.g. "ROOT") to "$arg".
  209. if ($VAR) {
  210. $$VAR = "$arg";
  211. $VAR = FALSE;
  212. }
  213. else {
  214. switch ($arg) {
  215. case "-r":
  216. $VAR = "ROOT";
  217. break;
  218. case "-dv":
  219. case "-vd":
  220. case "--debug":
  221. case "-d":
  222. $DEBUG = TRUE;
  223. break;
  224. case "-dv":
  225. case "-vd":
  226. case "--verbose":
  227. case "-v":
  228. $VERBOSE = TRUE;
  229. break;
  230. }
  231. if (!$COMMAND && !$ALIAS && ($arg[0] == '@')) {
  232. $ALIAS = $arg;
  233. }
  234. elseif (!$COMMAND && ($arg[0] != '-')) {
  235. $COMMAND = $arg;
  236. }
  237. if (substr($arg, 0, 7) == "--root=") {
  238. $ROOT = substr($arg, 7);
  239. }
  240. }
  241. }
  242. $NONE=($ALIAS == "@none");
  243. // If we have found the site-local Drush script, then
  244. // do not search for it again; use the environment value
  245. // we set last time.
  246. $found_script = getenv('DRUSH_FINDER_SCRIPT');
  247. // If the @none alias is used, then we skip the Drush wrapper,
  248. // and call the Drush launcher directly.
  249. //
  250. // In this instance, we are assuming that the 'drush' that is being
  251. // called is:
  252. //
  253. // a) The global 'drush', or
  254. // b) A site-local 'drush' in a vendor/bin directory.
  255. //
  256. // In either event, the appropriate 'drush.launcher' should be right next
  257. // to this script (stored in the same directory).
  258. if (empty($found_script) && $NONE) {
  259. if (is_file(dirname(__DIR__) . "{$sep}drush.launcher")) {
  260. $found_script = dirname(__DIR__) . "{$sep}drush.launcher";
  261. }
  262. else {
  263. fwrite(STDERR, "Could not find drush.launcher in " . dirname(__DIR__) . ". Check your installation.\n");
  264. exit(1);
  265. }
  266. }
  267. // Check for a root option:
  268. //
  269. // drush --root=/path
  270. //
  271. // If the site root is specified via a commandline option, then we
  272. // should always use the Drush stored at this root, if there is one.
  273. // We will first check for a "wrapper" script at the root, and then
  274. // we will look for a "launcher" script in vendor/bin.
  275. if (empty($found_script) && !empty($ROOT)) {
  276. $found_script = find_wrapper_or_launcher($ROOT);
  277. if (!empty($found_script)) {
  278. chdir($ROOT);
  279. }
  280. }
  281. // If there is a .drush-use file, then its contents will
  282. // contain the path to the Drush to use.
  283. if (empty($found_script)) {
  284. if (is_file(".drush-use")) {
  285. $found_script = trim(file_get_contents(".drush-use"));
  286. }
  287. }
  288. // Look for a 'drush' wrapper or launcher at the cwd,
  289. // and in each of the directories above the cwd. If
  290. // we find one, use it.
  291. if (empty($found_script)) {
  292. $c = getcwd();
  293. // Windows can give us lots of different strings to represent the root
  294. // directory as it often includes the drive letter. If we get the same
  295. // result from dirname() twice in a row, then we know we're at the root.
  296. $last = '';
  297. while (!empty($c) && ($c != $last)) {
  298. $found_script = find_wrapper_or_launcher($c);
  299. if ($found_script) {
  300. chdir($c);
  301. break;
  302. }
  303. $last = $c;
  304. $c = dirname($c);
  305. }
  306. }
  307. if (!empty($found_script)) {
  308. $found_script = realpath($found_script);
  309. // Guard against errors: if we have found a "drush" script
  310. // (that is, theoretically a drush wrapper script), and
  311. // there is a "drush.launcher" script in the same directory,
  312. // then we will skip the "drush" script and use the drush launcher
  313. // instead. This is because drush "wrapper" scripts should
  314. // only ever exist at the root of a site, and there should
  315. // never be a drush "launcher" at the root of a site.
  316. // Therefore, if we find a "drush.launcher" next to a script
  317. // called "drush", we have probably found a Drush install directory,
  318. // not a site root. Adjust appropriately. Note that this
  319. // also catches the case where a drush "finder" script finds itself.
  320. if (is_file(dirname($found_script) . "{$sep}drush.launcher")) {
  321. $found_script = dirname($found_script) . "{$sep}drush.launcher";
  322. }
  323. }
  324. // Didn't find any site-local Drush, or @use'd Drush.
  325. // Skip the Bash niceties of the launcher and proceed to drush_main() in either case:
  326. // - No script was found and we are running a Phar
  327. // - The found script *is* the Phar https://github.com/drush-ops/drush/pull/2246.
  328. $phar_path = class_exists('Phar') ? Phar::running(FALSE) : '';
  329. if ((empty($found_script) && $phar_path) || !empty($found_script) && $found_script == $phar_path) {
  330. drush_run_main($DEBUG, $sep, "Phar detected. Proceeding to drush_main().");
  331. }
  332. // Didn't find any site-local Drush, or @use'd Drush, or Phar.
  333. // There should be a drush.launcher in same directory as this script.
  334. if (empty($found_script)) {
  335. $found_script = dirname(__DIR__) . "{$sep}drush.launcher";
  336. }
  337. if (drush_is_windows()) {
  338. // Sometimes we found launcher in /bin, and sometimes not. Adjust accordingly.
  339. if (strpos($found_script, 'bin')) {
  340. $found_script = dirname($found_script). $sep. 'drush.php.bat';
  341. }
  342. else {
  343. array_unshift($arguments, dirname($found_script). $sep. 'drush.php');
  344. $found_script = 'php';
  345. }
  346. }
  347. // Always use pcntl_exec if it exists.
  348. $use_pcntl_exec = function_exists("pcntl_exec");
  349. // If we have posix_getppid, then pass in the shell pid so
  350. // that 'site-set' et. al. can work correctly.
  351. if (function_exists('posix_getppid')) {
  352. putenv("DRUSH_SHELL_PID=" . posix_getppid());
  353. }
  354. // Set an environment variable indicating which script
  355. // the Drush finder found. If we end up re-entrantly calling
  356. // another Drush finder, then we will skip searching for
  357. // a site-local Drush, and always use the drush.launcher
  358. // found previously. This environment variable typically should
  359. // not be set by clients.
  360. putenv("DRUSH_FINDER_SCRIPT=$found_script");
  361. // Emit a message in debug mode advertising the location of the
  362. // script we found.
  363. if ($DEBUG) {
  364. $launch_method = $use_pcntl_exec ? 'pcntl_exec' : 'proc_open';
  365. fwrite(STDERR, "Using the Drush script found at $found_script using $launch_method\n");
  366. }
  367. if ($use_pcntl_exec) {
  368. // Get the current environment for pnctl_exec.
  369. $env = drush_env();
  370. // Launch the new script in the same process.
  371. // If the launch succeeds, then it will not return.
  372. $error = pcntl_exec($found_script, $arguments, $env);
  373. if (!$error) {
  374. $errno = pcntl_get_last_error();
  375. $strerror = pcntl_strerror($errno);
  376. fwrite(STDERR, "Error has occurred executing the Drush script found at $found_script\n");
  377. fwrite(STDERR, "(errno {$errno}) $strerror\n");
  378. }
  379. exit(1);
  380. }
  381. else {
  382. $escaped_args = array_map(function($item) { return drush_escapeshellarg($item); }, $arguments);
  383. // Double quotes around $found_script as it can contain spaces.
  384. $cmd = drush_escapeshellarg($found_script). ' '. implode(' ', $escaped_args);
  385. if (drush_is_windows()) {
  386. // Windows requires double quotes around whole command.
  387. // @see https://bugs.php.net/bug.php?id=49139
  388. // @see https://bugs.php.net/bug.php?id=60181
  389. $cmd = '"'. $cmd. '"';
  390. }
  391. $process = proc_open($cmd, array(0 => STDIN, 1 => STDOUT, 2 => STDERR), $pipes, $cwd);
  392. $proc_status = proc_get_status($process);
  393. $exit_code = proc_close($process);
  394. exit($proc_status["running"] ? $exit_code : $proc_status["exitcode"] );
  395. }
  396. }
  397. /**
  398. * Run drush_main() and then exit. Used when we cannot hand over execution to
  399. * the launcher.
  400. *
  401. * @param bool $DEBUG
  402. * Are we in debug mode
  403. * @param string $sep
  404. * Directory separator
  405. * @param string $msg
  406. * Debug message to log before running drush_main()
  407. */
  408. function drush_run_main($DEBUG, $sep, $msg) {
  409. // Emit a message in debug mode advertising how we proceeded.
  410. if ($DEBUG) {
  411. fwrite(STDERR, $msg. "\n");
  412. }
  413. require __DIR__ . "{$sep}preflight.inc";
  414. exit(drush_main());
  415. }