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.

Functions

Namesort descending Description
drush_env Get the current enviornment.
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.
_drush_escapeshellarg_linux Linux 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.
  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. function find_wrapper_or_launcher($location) {
  47. // We look for a "Drush wrapper" script that might
  48. // be stored in the root of a site. If there is
  49. // no wrapper script, then we look for the
  50. // drush.launcher script in vendor/bin. We try just a
  51. // few of the most common locations; if the user relocates
  52. // their vendor directory anywhere else, then they must
  53. // use a wrapper script to locate it. See the comment in
  54. // 'examples/drush' for details.
  55. //
  56. // We are somewhat "loose" about whether we are looking
  57. // for "drush" or "drush.launcher", because in old versions
  58. // of Drush, the "drush launcher" was named "drush".
  59. // Otherwise, there wouldn't be any point in looking for
  60. // "drush.launcher" at the root, or "drush" in a vendor directory.
  61. // We also allow users to rename their drush wrapper to
  62. // 'drush.wrapper' to avoid conflicting with a directory named
  63. // 'drush' at the site root.
  64. $drush_locations = array(
  65. "drush",
  66. "vendor/bin/drush",
  67. "../vendor/bin/drush",
  68. "sites/all/vendor/bin/drush",
  69. "sites/all/vendor/drush/drush/drush",
  70. "sites/all/drush/drush/drush",
  71. "drush/drush/drush",
  72. );
  73. foreach ($drush_locations as $d) {
  74. foreach (array('.launcher', '.wrapper', '') as $suffix) {
  75. if (is_file("$location/$d$suffix")) {
  76. return "$location/$d$suffix";
  77. }
  78. }
  79. }
  80. return "";
  81. }
  82. /**
  83. * Linux version of escapeshellarg().
  84. *
  85. * This is intended to work the same way that escapeshellarg() does on
  86. * Linux. If we need to escape a string that will be used remotely on
  87. * a Linux system, then we need our own implementation of escapeshellarg,
  88. * because the Windows version behaves differently.
  89. */
  90. function _drush_escapeshellarg_linux($arg, $raw = FALSE) {
  91. // For single quotes existing in the string, we will "exit"
  92. // single-quote mode, add a \' and then "re-enter"
  93. // single-quote mode. The result of this is that
  94. // 'quote' becomes '\''quote'\''
  95. $arg = preg_replace('/\'/', '\'\\\'\'', $arg);
  96. // Replace "\t", "\n", "\r", "\0", "\x0B" with a whitespace.
  97. // Note that this replacement makes Drush's escapeshellarg work differently
  98. // than the built-in escapeshellarg in PHP on Linux, as these characters
  99. // usually are NOT replaced. However, this was done deliberately to be more
  100. // conservative when running _drush_escapeshellarg_linux on Windows
  101. // (this can happen when generating a command to run on a remote Linux server.)
  102. $arg = str_replace(array("\t", "\n", "\r", "\0", "\x0B"), ' ', $arg);
  103. // Only wrap with quotes when needed.
  104. if(!$raw) {
  105. // Add surrounding quotes.
  106. $arg = "'" . $arg . "'";
  107. }
  108. return $arg;
  109. }
  110. /**
  111. * drush_startup is called once, by the Drush "finder"
  112. * script -- the "drush" script at the Drush root.
  113. * It finds the correct Drush "wrapper" or "launcher"
  114. * script to use, and executes it with process replacement.
  115. */
  116. function drush_startup($argv) {
  117. $found_script = "";
  118. $home = getenv("HOME");
  119. $use_dir = "$home/.drush/use";
  120. // Get the arguments for the command. Shift off argv[0],
  121. // which contains the name of this script.
  122. $arguments = $argv;
  123. array_shift($arguments);
  124. //
  125. // We need to do at least a partial parsing of the options,
  126. // so that we can find --root / -r and so on.
  127. //
  128. $VERBOSE=FALSE;
  129. $DEBUG=FALSE;
  130. $ROOT=FALSE;
  131. $COMMAND=FALSE;
  132. $ALIAS=FALSE;
  133. $VAR=FALSE;
  134. foreach ($arguments as $arg) {
  135. // If a variable to set was indicated on the
  136. // previous iteration, then set the value of
  137. // the named variable (e.g. "ROOT") to "$arg".
  138. if ($VAR) {
  139. $$VAR = "$arg";
  140. $VAR = FALSE;
  141. }
  142. else {
  143. switch ($arg) {
  144. case "-r":
  145. $VAR = "ROOT";
  146. break;
  147. case "--debug":
  148. case "-d":
  149. $DEBUG = TRUE;
  150. break;
  151. case "--verbose":
  152. case "-v":
  153. $VERBOSE = TRUE;
  154. break;
  155. }
  156. if (!$COMMAND && !$ALIAS && ($arg[0] == '@')) {
  157. $ALIAS = $arg;
  158. }
  159. elseif (!$COMMAND && ($arg[0] != '-')) {
  160. $COMMAND = $arg;
  161. }
  162. if (substr($arg, 0, 7) == "--root=") {
  163. $ROOT = substr($arg, 7);
  164. }
  165. }
  166. }
  167. $NONE=($ALIAS == "@none");
  168. //
  169. // If the @none alias is used, then we skip the Drush wrapper,
  170. // and call the Drush launcher directly.
  171. //
  172. // In this instance, we are assuming that the 'drush' that is being
  173. // called is:
  174. //
  175. // a) The global 'drush', or
  176. // b) A site-local 'drush' in a vendor/bin directory.
  177. //
  178. // In either event, the appropriate 'drush.launcher' should be right next
  179. // to this script (stored in the same directory).
  180. if ($NONE) {
  181. if (is_file(dirname(__DIR__) . "/drush.launcher")) {
  182. $found_script = dirname(__DIR__) . "/drush.launcher";
  183. }
  184. else {
  185. fwrite(STDERR, "Could not find drush.launcher in " . dirname(__DIR__) . ". Check your installation.\n");
  186. exit(1);
  187. }
  188. }
  189. //
  190. // Check for a root option:
  191. //
  192. // drush --root=/path
  193. //
  194. // If the site root is specified via a commandline option, then we
  195. // should always use the Drush stored at this root, if there is one.
  196. // We will first check for a "wrapper" script at the root, and then
  197. // we will look for a "launcher" script in vendor/bin.
  198. //
  199. if (empty($found_script) && !empty($ROOT)) {
  200. $found_script = find_wrapper_or_launcher($ROOT);
  201. if (!empty($found_script)) {
  202. chdir($ROOT);
  203. }
  204. }
  205. //
  206. // If there is a .drush-use file, then its contents will
  207. // contain the path to the Drush to use.
  208. //
  209. if (empty($found_script)) {
  210. if (is_file(".drush-use")) {
  211. $found_script = file_get_contents(".drush-use");
  212. }
  213. }
  214. //
  215. // Look for a 'drush' wrapper or launcher at the cwd,
  216. // and in each of the directories above the cwd. If
  217. // we find one, use it.
  218. //
  219. if (empty($found_script)) {
  220. $c = getcwd();
  221. // Windows can give us lots of different strings to represent the root
  222. // directory as it often includes the drive letter. If we get the same
  223. // result from dirname() twice in a row, then we know we're at the root.
  224. $last = '';
  225. while (!empty($c) && ($c != $last) && file_exists("$c/index.php")) {
  226. $found_script = find_wrapper_or_launcher($c);
  227. if ($found_script) {
  228. chdir($c);
  229. break;
  230. }
  231. $last = $c;
  232. $c = dirname($c);
  233. }
  234. }
  235. if (!empty($found_script)) {
  236. $found_script = realpath($found_script);
  237. // Guard against errors: if we have found a "drush" script
  238. // (that is, theoretically a drush wrapper script), and
  239. // there is a "drush.launcher" script in the same directory,
  240. // then we will skip the "drush" script and use the drush launcher
  241. // instead. This is because drush "wrapper" scripts should
  242. // only ever exist at the root of a site, and there should
  243. // never be a drush "launcher" at the root of a site.
  244. // Therefore, if we find a "drush.launcher" next to a script
  245. // called "drush", we have probably found a Drush install directory,
  246. // not a site root. Adjust appropriately. Note that this
  247. // also catches the case where a drush "finder" script finds itself.
  248. if (is_file(dirname($found_script) . "/drush.launcher")) {
  249. $found_script = dirname($found_script) . "/drush.launcher";
  250. }
  251. }
  252. // Didn't find any site-local Drush, or @use'd Drush?
  253. // In that case, there should always be a drush.launcher in
  254. // the same directory this script is stored in; use that.
  255. if (empty($found_script)) {
  256. $found_script = dirname(__DIR__) . "/drush.launcher";
  257. if (!is_executable($found_script)) {
  258. // Can't execute the found launcher, so proceed with the current drush,
  259. // bypassing the Bash niceties of the launcher.
  260. // Emit a message in debug mode advertising how we proceeded.
  261. if ($DEBUG) {
  262. fwrite(STDERR, "Phar detected. Proceeding to drush_main().");
  263. }
  264. require __DIR__ . '/preflight.inc';
  265. exit(drush_main());
  266. }
  267. }
  268. // Always use pcntl_exec if it exists.
  269. $use_pcntl_exec = function_exists("pcntl_exec");
  270. // If we have posix_getppid, then pass in the shell pid so
  271. // that 'site-set' et. al. can work correctly.
  272. if (function_exists('posix_getppid')) {
  273. putenv("DRUSH_SHELL_PID=" . posix_getppid());
  274. }
  275. // Emit a message in debug mode advertising the location of the
  276. // script we found.
  277. if ($DEBUG) {
  278. $launch_method = $use_pcntl_exec ? 'pcntl_exec' : 'proc_open';
  279. fwrite(STDERR, "Using the Drush script found at $found_script using $launch_method\n");
  280. }
  281. if ($use_pcntl_exec) {
  282. // Get the current environment for pnctl_exec.
  283. $env = drush_env();
  284. // Launch the new script in the same process.
  285. // If the launch succeeds, then it will not return.
  286. $error = pcntl_exec($found_script, $arguments, $env);
  287. if (!$error) {
  288. $errno = pcntl_get_last_error();
  289. $strerror = pcntl_strerror($errno);
  290. fwrite(STDERR, "Error has occurred executing the Drush script found at $found_script\n");
  291. fwrite(STDERR, "(errno {$errno}) $strerror\n");
  292. }
  293. exit(1);
  294. }
  295. else {
  296. $escaped_args = array_map(function($item) { return _drush_escapeshellarg_linux($item); }, $arguments);
  297. $process = proc_open($found_script . ' ' . implode(' ', $escaped_args), array(0 => STDIN, 1 => STDOUT, 2 => STDERR), $pipes);
  298. $proc_status = proc_get_status($process);
  299. $exit_code = proc_close($process);
  300. exit($proc_status["running"] ? $exit_code : $proc_status["exitcode"] );
  301. }
  302. }