runserver.drush.inc

  1. 8.0.x commands/runserver/runserver.drush.inc
  2. 6.x commands/runserver/runserver.drush.inc
  3. 7.x commands/runserver/runserver.drush.inc
  4. 5.x commands/runserver/runserver.drush.inc
  5. master commands/runserver/runserver.drush.inc

Built in http server commands.

This uses an excellent php http server library developed by Jesse Young with support from the Envaya project. See https://github.com/youngj/httpserver/ and http://envaya.org/.

Functions

Namesort descending Description
drush_core_runserver Callback for runserver command.
drush_core_runserver_validate Validate callback for runserver command.
runserver_drush_command Implementation of hook_drush_command().
runserver_drush_help Implementation of hook_drush_help().
runserver_parse_uri Parse a URI or partial URI (including just a port, host IP or path).

Constants

Namesort descending Description
DRUSH_HTTPSERVER_BASE_URL Base URL for automatic download of supported version of httpserver.
DRUSH_HTTPSERVER_DIR_BASE Directory name for httpserver. This is displayed in the manual install help.
DRUSH_HTTPSERVER_VERSION Supported version of httpserver. This is displayed in the manual install help.

File

commands/runserver/runserver.drush.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * Built in http server commands.
  5. *
  6. * This uses an excellent php http server library developed by Jesse Young
  7. * with support from the Envaya project.
  8. * See https://github.com/youngj/httpserver/ and http://envaya.org/.
  9. */
  10. /**
  11. * Supported version of httpserver. This is displayed in the manual install help.
  12. */
  13. define('DRUSH_HTTPSERVER_VERSION', '41dd2b7160b8cbd25d7b5383e3ffc6d8a9a59478');
  14. /**
  15. * Directory name for httpserver. This is displayed in the manual install help.
  16. */
  17. define('DRUSH_HTTPSERVER_DIR_BASE', 'youngj-httpserver-');
  18. /**
  19. * Base URL for automatic download of supported version of httpserver.
  20. */
  21. define('DRUSH_HTTPSERVER_BASE_URL', 'https://github.com/youngj/httpserver/tarball/');
  22. /**
  23. * Implementation of hook_drush_help().
  24. */
  25. function runserver_drush_help($section) {
  26. switch ($section) {
  27. case 'meta:runserver:title':
  28. return dt("Runserver commands");
  29. case 'drush:runserver':
  30. return dt("Runs a lightweight built in http server for development.
  31. - Don't use this for production, it is neither scalable nor secure for this use.
  32. - If you run multiple servers simultaneously, you will need to assign each a unique port.
  33. - Use Ctrl-C or equivalent to stop the server when complete.");
  34. }
  35. }
  36. /**
  37. * Implementation of hook_drush_command().
  38. */
  39. function runserver_drush_command() {
  40. $items = array();
  41. $items['runserver'] = array(
  42. 'description' => 'Runs a lightweight built in http server for development.',
  43. 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_FULL,
  44. 'arguments' => array(
  45. 'addr:port/path' => 'Host IP address and port number to bind to and path to open in web browser. Format is addr:port/path, default 127.0.0.1:8888, all elements optional. See examples for shorthand.',
  46. ),
  47. 'options' => array(
  48. 'server' => 'Which http server to use - either: "cgi" for a CGI based httpserver (default, requires php 5.3 and php-cgi binary) or "builtin" for php 5.4 built in http server.',
  49. 'php-cgi' => 'Name of the php-cgi binary. If it is not on your current $PATH you should include the full path. You can include command line parameters to pass into php-cgi.',
  50. 'variables' => 'Key-value array of variables to override in the $conf array for the running site. By default disables drupal_http_request_fails to avoid errors on Windows (which supports only one connection at a time). Comma delimited list of name=value pairs (or array in drushrc).',
  51. 'default-server' => 'A default addr:port/path to use for any values not specified as an argument.',
  52. 'user' => 'If opening a web browser, automatically log in as this user (user ID or username).',
  53. 'browser' => 'If opening a web browser, which browser to user (defaults to operating system default).',
  54. 'dns' => 'Resolve hostnames/IPs using DNS/rDNS (if possible) to determine binding IPs and/or human friendly hostnames for URLs and browser.',
  55. ),
  56. 'aliases' => array('rs'),
  57. 'examples' => array(
  58. 'drush rs 8080' => 'Start runserver on 127.0.0.1, port 8080.',
  59. 'drush rs 10.0.0.28:80' => 'Start runserver on 10.0.0.28, port 80.',
  60. 'drush rs [::1]:80' => 'Start runserver on IPv6 localhost ::1, port 80.',
  61. 'drush rs --php-cgi=php5-cgi --dns localhost:8888/user' => 'Start runserver on localhost (using rDNS to determine binding IP), port 8888, and open /user in browser. Use "php5-cgi" as the php-cgi binary.',
  62. 'drush rs /' => 'Start runserver on default IP/port (127.0.0.1, port 8888), and open / in browser.',
  63. 'drush rs --default-server=127.0.0.1:8080/ -' => 'Use a default (would be specified in your drushrc) that starts runserver on port 8080, and opens a browser to the front page. Set path to a single hyphen path in argument to prevent opening browser for this session.',
  64. 'drush rs --server=builtin :9000/admin' => 'Start builtin php 5.4 runserver on 127.0.0.1, port 9000, and open /admin in browser. Note that you need a colon when you specify port and path, but no IP.',
  65. ),
  66. );
  67. return $items;
  68. }
  69. /**
  70. * Validate callback for runserver command.
  71. */
  72. function drush_core_runserver_validate() {
  73. $php_cgi = drush_shell_exec('which ' . drush_get_option('php-cgi', 'php-cgi'));
  74. $builtin = version_compare(PHP_VERSION, '5.4.0') >= 0;
  75. $server = drush_get_option('server', FALSE);
  76. if (!$server) {
  77. // No server specified, try and find a valid server option, preferring cgi.
  78. if ($php_cgi) {
  79. $server = 'cgi';
  80. drush_set_option('server', 'cgi');
  81. }
  82. else if ($builtin) {
  83. $server = 'builtin';
  84. drush_set_option('server', 'builtin');
  85. }
  86. else {
  87. return drush_set_error('RUNSERVER_PHP_CGI54', dt('The runserver command requires either the php-cgi binary, or php 5.4 (or higher). Neither could not be found.'));
  88. }
  89. }
  90. else if ($server == 'cgi' && !$php_cgi) {
  91. // Validate user specified cgi server option.
  92. return drush_set_error('RUNSERVER_PHP_CGI', dt('The runserver command with the "cgi" server requires the php-cgi binary, which could not be found.'));
  93. }
  94. else if ($server == 'builtin' && !$builtin) {
  95. // Validate user specified builtin server option.
  96. return drush_set_error('RUNSERVER_PHP_CGI', dt('The runserver command with the "builtin" server requires php 5.4 (or higher), which could not be found.'));
  97. }
  98. // Update with detected configuration.
  99. $server = drush_get_option('server', FALSE);
  100. if ($server == 'cgi') {
  101. // Fetch httpserver cgi based server to our /lib directory, if needed.
  102. $lib = drush_get_option('lib', DRUSH_BASE_PATH . '/lib');
  103. $httpserverfile = $lib . '/' . DRUSH_HTTPSERVER_DIR_BASE . substr(DRUSH_HTTPSERVER_VERSION, 0, 7) . '/httpserver.php';
  104. if (!drush_file_not_empty($httpserverfile)) {
  105. // Download and extract httpserver, and confirm success.
  106. drush_lib_fetch(DRUSH_HTTPSERVER_BASE_URL . DRUSH_HTTPSERVER_VERSION);
  107. if (!drush_file_not_empty($httpserverfile)) {
  108. // Something went wrong - the library is still not present.
  109. return drush_set_error('RUNSERVER_HTTPSERVER_LIB_NOT_FOUND', dt("The runserver command needs a copy of the httpserver library in order to function, and the attempt to download this file automatically failed. To continue you will need to download the package from !url, extract it into the !lib directory, such that httpserver.php exists at !httpserverfile.", array('!version' => DRUSH_HTTPSERVER_VERSION, '!url' => DRUSH_HTTPSERVER_BASE_URL . DRUSH_HTTPSERVER_VERSION, '!httpserverfile' => $httpserverfile, '!lib' => $lib)));
  110. }
  111. }
  112. }
  113. // Check we have a valid server.
  114. if (!in_array($server, array('cgi', 'builtin'))) {
  115. return drush_set_error('RUNSERVER_INVALID', dt('Invalid server specified.'));
  116. }
  117. }
  118. /**
  119. * Callback for runserver command.
  120. */
  121. function drush_core_runserver($uri = NULL) {
  122. global $user, $base_url;
  123. // Determine active configuration.
  124. $drush_default = array(
  125. 'host' => '127.0.0.1',
  126. 'port' => '8888',
  127. 'path' => '',
  128. );
  129. $user_default = runserver_parse_uri(drush_get_option('default-server', '127.0.0.1:8888'));
  130. $uri = runserver_parse_uri($uri);
  131. if (!$uri) {
  132. return $uri;
  133. }
  134. // Populate defaults.
  135. $uri = $uri + $user_default + $drush_default;
  136. if (ltrim($uri['path'], '/') == '-') {
  137. // Allow a path of a single hyphen to clear a default path.
  138. $uri['path'] = '';
  139. }
  140. // Remove any leading slashes from the path, since that is what url() expects.
  141. $path = ltrim($uri['path'], '/');
  142. // Determine and set the new URI.
  143. $hostname = $addr = $uri['host'];
  144. if (drush_get_option('dns', FALSE)) {
  145. if (ip2long($hostname)) {
  146. $hostname = gethostbyaddr($hostname);
  147. }
  148. else {
  149. $addr = gethostbyname($hostname);
  150. }
  151. }
  152. drush_set_context('DRUSH_URI', 'http://' . $hostname . ':' . $uri['port']);
  153. // We pass in the currently logged in user (if set via the --user option),
  154. // which will automatically log this user in the browser during the first
  155. // request.
  156. if (drush_get_option('user', FALSE)) {
  157. drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_LOGIN);
  158. }
  159. // We delete any registered files here, since they are not caught by Ctrl-C.
  160. _drush_delete_registered_files();
  161. // We set the effective base_url, since we have now detected the current site,
  162. // and need to ensure generated URLs point to our runserver host.
  163. // We also pass in the effective base_url to our auto_prepend_script via the
  164. // CGI environment. This allows Drupal to generate working URLs to this http
  165. // server, whilst finding the correct multisite from the HTTP_HOST header.
  166. $base_url = 'http://' . $addr . ':' . $uri['port'];
  167. $env['RUNSERVER_BASE_URL'] = $base_url;
  168. // We pass in an array of $conf overrides using the same approach.
  169. // By default we set drupal_http_request_fails to FALSE, as the httpserver
  170. // is unable to process simultaneous requests on some systems.
  171. // This is available as an option for developers to pass in their own
  172. // favorite $conf overrides (e.g. disabling css aggregation).
  173. $current_override = drush_get_option_list('variables', array('drupal_http_request_fails' => FALSE));
  174. foreach ($current_override as $name => $value) {
  175. if (is_numeric($name) && (strpos($value, '=') !== FALSE)) {
  176. list($name, $value) = explode('=', $value, 2);
  177. }
  178. $override[$name] = $value;
  179. }
  180. $env['RUNSERVER_CONF'] = urlencode(serialize($override));
  181. // We log in with the specified user ID (if set) via the password reset URL.
  182. $user_message = '';
  183. if ($user->uid) {
  184. $options = array('query' => array('destination' => $path));
  185. $browse = url(user_pass_reset_url($user) . '/login', $options);
  186. $user_message = ', logged in as ' . $user->name;
  187. }
  188. else {
  189. $browse = url($path);
  190. }
  191. drush_print(dt('HTTP server listening on !addr, port !port (see http://!hostname:!port/!path), serving site !site!user...', array('!addr' => $addr, '!hostname' => $hostname, '!port' => $uri['port'], '!path' => $path, '!site' => drush_get_context('DRUSH_DRUPAL_SITE', 'default'), '!user' => $user_message)));
  192. if (drush_get_option('server', FALSE) == 'builtin') {
  193. // Start php 5.4 builtin server.
  194. // Store data used by runserver-prepend.php in the shell environment.
  195. foreach ($env as $key => $value) {
  196. putenv($key . '=' . $value);
  197. }
  198. if (!empty($uri['path'])) {
  199. // Start a browser if desired. Include a 2 second delay to allow the
  200. // server to come up.
  201. drush_start_browser($browse, 2);
  202. }
  203. // Start the server using 'php -S'.
  204. $php = drush_get_option('php');
  205. drush_shell_exec_interactive($php . ' -S ' . $addr . ':' . $uri['port'] . ' --define auto_prepend_file="' . DRUSH_BASE_PATH . '/commands/runserver/runserver-prepend.php"');
  206. }
  207. else {
  208. // Include the library and our class that extends it.
  209. $lib = drush_get_option('lib', DRUSH_BASE_PATH . '/lib');
  210. $httpserverfile = $lib . '/' . DRUSH_HTTPSERVER_DIR_BASE . substr(DRUSH_HTTPSERVER_VERSION, 0, 7) . '/httpserver.php';
  211. require_once $httpserverfile;
  212. require_once 'runserver-drupal.inc';
  213. // Create a new httpserver instance and start it running.
  214. $server = new DrupalServer(array(
  215. 'addr' => $addr,
  216. 'port' => $uri['port'],
  217. 'browse' => $browse,
  218. 'hostname' => $hostname,
  219. 'site' => drush_get_context('DRUSH_DRUPAL_SITE', 'default'),
  220. 'server_id' => 'Drush runserver',
  221. 'php_cgi' => drush_get_option('php-cgi', 'php-cgi') . ' --define auto_prepend_file="' . DRUSH_BASE_PATH . '/commands/runserver/runserver-prepend.php"',
  222. 'env' => $env,
  223. 'debug' => drush_get_context('DRUSH_DEBUG'),
  224. ));
  225. $server->run_forever();
  226. }
  227. }
  228. /**
  229. * Parse a URI or partial URI (including just a port, host IP or path).
  230. *
  231. * @param $uri
  232. * String that can contain partial URI.
  233. * @return array
  234. * URI array as returned by parse_url.
  235. */
  236. function runserver_parse_uri($uri) {
  237. if ($uri[0] == ':') {
  238. // ':port/path' shorthand, insert a placeholder hostname to allow parsing.
  239. $uri = 'placeholder-hostname' . $uri;
  240. }
  241. // FILTER_VALIDATE_IP expects '[' and ']' to be removed from IPv6 addresses.
  242. // We check for colon from the right, since IPv6 addresses contain colons.
  243. $to_path = trim(substr($uri, 0, strpos($uri, '/')), '[]');
  244. $to_port = trim(substr($uri, 0, strrpos($uri, ':')), '[]');
  245. if (filter_var(trim($uri, '[]'), FILTER_VALIDATE_IP) || filter_var($to_path, FILTER_VALIDATE_IP) || filter_var($to_port, FILTER_VALIDATE_IP)) {
  246. // 'IP', 'IP/path' or 'IP:port' shorthand, insert a schema to allow parsing.
  247. $uri = 'http://' . $uri;
  248. }
  249. $uri = parse_url($uri);
  250. if (empty($uri)) {
  251. return drush_set_error('RUNSERVER_INVALID_ADDRPORT', dt('Invalid argument - should be in the "host:port/path" format, numeric (port only) or non-numeric (path only).'));
  252. }
  253. if (count($uri) == 1 && isset($uri['path'])) {
  254. if (is_numeric($uri['path'])) {
  255. // Port only shorthand.
  256. $uri['port'] = $uri['path'];
  257. unset($uri['path']);
  258. }
  259. }
  260. if (isset($uri['host']) && $uri['host'] == 'placeholder-hostname') {
  261. unset($uri['host']);
  262. }
  263. return $uri;
  264. }