sitealias.inc

  1. 8.0.x includes/sitealias.inc
  2. 6.x includes/sitealias.inc
  3. 7.x includes/sitealias.inc
  4. 3.x includes/sitealias.inc
  5. 4.x includes/sitealias.inc
  6. 5.x includes/sitealias.inc
  7. master includes/sitealias.inc

The site alias API.

Run commands on remote server(s).

See also

example.drushrc.php

http://drupal.org/node/670460

Functions

Namesort descending Description
drush_convert_db_from_db_url Convert from an old-style database URL to an array of database settings
drush_sitealias_add_db_settings If the alias record does not contain a 'databases' or 'db-url' entry, then use backend invoke to look up the settings value from the remote or local site. The 'databases' form is preferred; 'db_url' will be…
drush_sitealias_add_db_url If the alias record does not contain a 'databases' or 'db-url' entry, then use backend invoke to look up the settings value from the remote or local site. The 'db_url' form is preferred; nothing is done if…
drush_sitealias_add_to_alias_path Add a path to the array of paths where alias files are searched for.
drush_sitealias_alias_path Return the array of paths where alias files are searched for.
drush_sitealias_alias_record_to_spec Convert from an alias record to a site specification
drush_sitealias_build_record_from_settings_file
drush_sitealias_check_arg Check to see if the first command-line arg or the -l option is a site alias; if it is, copy its record values to the 'alias' context.
drush_sitealias_check_lists_alignment Check to see if the uri is the same in the source and target lists for all items in the array. This is a strong requirement in D6; in D7, it is still highly convenient for the uri to be the same, because the site folder name == the uri, and if the…
drush_sitealias_convert_db_from_db_url
drush_sitealias_create_sites_alias
drush_sitealias_evaluate_path Evaluate a path from its shorthand form to a literal path usable by rsync.
drush_sitealias_get_record Get a site alias record given an alias name or site specification.
drush_sitealias_is_bootstrapped_site Check to see if we have already bootstrapped to a site.
drush_sitealias_load_all Load every alias file that can be found anywhere in the alias search path.
drush_sitealias_local_site_path Return the full path to the site directory of the given alias record.
drush_sitealias_resolve_path_references If there are any path aliases (items beginning with "%") in the test string, then resolve them as path aliases and add them to the provided alias record.
drush_sitealias_resolve_sitelist Given an alias record that is a site list (contains a 'site-list' entry), resolve all of the members of the site list and return them is an array of alias records.
drush_sitealias_resolve_sitespecs Given an array of site specifications, resolve each one in turn and return an array of alias records. If you only want a single record, it is preferable to simply call drush_sitealias_get_record directly.
drush_sitealias_set_alias_context Given a site alias record, copy selected fields from it into the drush 'alias' context. The 'alias' context has lower precedence than the 'options' context, so values set by an alias record can be overridden by…
drush_sitealias_site_dir_from_filename Pull the site directory from the path to settings.php
drush_sitealias_site_selection_keys Option keys used for site selection.
drush_sitealias_uri_to_site_dir Convert from a URI to a site directory.
drush_sitelist_align_lists If the source and target lists contain alias records to the same sets of sites, but in different orders, this routine will re-order the lists so that they are in alignment.
sitealias_get_databases_from_record Return the databases record from the alias record
_drush_find_local_sites_at_root Return a list of all of the local sites at the specified drupal root.
_drush_find_local_sites_in_sites_folder Return a list of all of the local sites at the specified 'sites' folder.
_drush_sitealias_add_inherited_values Check to see if there is a 'parent' item in the alias; if there is, then load the parent alias record and overlay the entries in the current alias record on top of the items from the parent record.
_drush_sitealias_add_static_defaults Add "static" default values to the given alias record. The difference between a static default and a transient default is that static defaults -always- exist in the alias record, and they are cached, whereas transient defaults are only…
_drush_sitealias_add_transient_defaults Add "transient" default values to the given alias record. The difference between a static default and a transient default is that static defaults -always- exist in the alias record, whereas transient defaults are only added if the given…
_drush_sitealias_cache_alias Add an empty record for the specified alias name
_drush_sitealias_derive_record
_drush_sitealias_find_and_load_alias Worker function called by _drush_sitealias_load_alias and drush_sitealias_load_all. Traverses the alias search path and finds the specified alias record.
_drush_sitealias_find_local_sites Search for drupal installations in the search path.
_drush_sitealias_find_record_for_local_site If '$alias' is the name of a folder in the sites folder of the given drupal root, then build an alias record for it
_drush_sitealias_get_record
_drush_sitealias_initialize_alias_record Initialize an alias record; called as soon as the alias record is loaded from its alias file, before it is stored in the cache.
_drush_sitealias_load_alias Check and see if an alias definition for $alias is available. If it is, load it into the list of aliases cached in the 'site-aliases' context.
_drush_sitealias_preflight_path
_drush_sitealias_set_context_by_name Looks up the specified alias record and calls through to drush_sitealias_set_alias_context, below.
_drush_sitealias_set_record_element Utility function used by drush_get_alias; keys that start with '%' or '!' are path aliases, the rest are entries in the alias record.
_drush_sitelist_check_site_records
_drush_sitelist_find_in_list

File

includes/sitealias.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * The site alias API.
  5. *
  6. * Run commands on remote server(s).
  7. * @see example.drushrc.php
  8. * @see http://drupal.org/node/670460
  9. */
  10. /**
  11. * Check to see if the first command-line arg or the
  12. * -l option is a site alias; if it is, copy its record
  13. * values to the 'alias' context.
  14. *
  15. * @return boolean
  16. * TRUE if a site alias was found and processed.
  17. */
  18. function drush_sitealias_check_arg() {
  19. $args = drush_get_arguments();
  20. // Test to see if the first arg is a site specification
  21. if (_drush_sitealias_set_context_by_name($args[0])) {
  22. array_shift($args);
  23. // We only need to expand the site specification
  24. // once, then we are done.
  25. drush_set_arguments($args);
  26. return TRUE;
  27. }
  28. // Return false to indicate that no site alias was specified.
  29. return FALSE;
  30. }
  31. /**
  32. * Given an array of site specifications, resolve each one in turn and
  33. * return an array of alias records. If you only want a single record,
  34. * it is preferable to simply call drush_sitealias_get_record directly.
  35. *
  36. * @param $site_specifications
  37. * An array of site specificatins. @see drush_sitealias_get_record
  38. * @return
  39. * An array of alias records
  40. */
  41. function drush_sitealias_resolve_sitespecs($site_specifications) {
  42. $result_list = array();
  43. if (!empty($site_specifications)) {
  44. foreach ($site_specifications as $site) {
  45. $alias_record = drush_sitealias_get_record($site);
  46. $result_list = array_merge($result_list, drush_sitealias_resolve_sitelist($alias_record));
  47. }
  48. }
  49. return $result_list;
  50. }
  51. /**
  52. * Get a site alias record given an alias name or site specification.
  53. *
  54. * If it is the name of a site alias, return the alias record from
  55. * the site aliases array.
  56. *
  57. * If it is the name of a folder in the 'sites' folder, construct
  58. * an alias record from values stored in settings.php.
  59. *
  60. * If it is a site specification, construct an alias record from the
  61. * values in the specification.
  62. *
  63. * Site specifications come in several forms:
  64. *
  65. * 1.) /path/to/drupal#sitename
  66. * 2.) user@server/path/to/drupal#sitename
  67. * 3.) user@server/path/to/drupal (sitename == server)
  68. * 4.) user@server#sitename (only if $option['r'] set in some drushrc file on server)
  69. * 5.) #sitename (only if $option['r'] already set, and 'sitename' is a folder in $option['r']/sites)
  70. * 6.) sitename (only if $option['r'] already set, and 'sitename' is a folder in $option['r']/sites)
  71. *
  72. * Note that in the case of the first four forms, it is also possible
  73. * to add additional site variable to the specification using uri query
  74. * syntax. For example:
  75. *
  76. * user@server/path/to/drupal?db-url=...#sitename
  77. *
  78. * @param alias
  79. * An alias name or site specification
  80. * @return array
  81. * An alias record, or empty if none found.
  82. */
  83. function drush_sitealias_get_record($alias) {
  84. // Check to see if the alias contains commas. If it does, then
  85. // we will go ahead and make a site list record
  86. $alias_record = array();
  87. if (strpos($alias, ',') !== false) {
  88. // TODO: If the site list contains any site lists, or site
  89. // search paths, then we should expand those and merge them
  90. // into this list longhand.
  91. $alias_record['site-list'] = explode(',', $alias);
  92. }
  93. else {
  94. $alias_record = _drush_sitealias_get_record($alias);
  95. }
  96. if (!empty($alias_record)) {
  97. if (!array_key_exists('name', $alias_record)) {
  98. $alias_record['name'] = drush_sitealias_uri_to_site_dir($alias);
  99. }
  100. // Handle nested alias definitions and command-specific options.
  101. drush_set_config_special_contexts($alias_record);
  102. }
  103. return $alias_record;
  104. }
  105. /*
  106. * This is a continuation of drush_sitealias_get_record, above. It is
  107. * not intended to be called directly.
  108. */
  109. function _drush_sitealias_get_record($alias, $alias_context = NULL) {
  110. // Before we do anything else, load $alias if it needs to be loaded
  111. _drush_sitealias_load_alias($alias, $alias_context);
  112. // Check to see if the provided parameter is in fact a defined alias.
  113. $all_site_aliases =& drush_get_context('site-aliases');
  114. if (array_key_exists($alias, $all_site_aliases)) {
  115. $alias_record = $all_site_aliases[$alias];
  116. }
  117. // If the parameter is not an alias, then it is some form of
  118. // site specification (or it is nothing at all)
  119. else {
  120. if (isset($alias)) {
  121. // Cases 1.) - 4.):
  122. // We will check for a site specification if the alias has at least
  123. // two characters from the set '@', '/', '#'.
  124. if ((strpos($alias, '@') === FALSE ? 0 : 1) + (strpos($alias, '/') === FALSE ? 0 : 1) + (strpos($alias, '#') === FALSE ? 0 : 1) >= 2) {
  125. if ((substr($alias,0,7) != 'http://') && (substr($alias,0,1) != '/')) {
  126. // Add on a scheme so that "user:pass@server" will always parse correctly
  127. $parsed = parse_url('http://' . $alias);
  128. }
  129. else {
  130. $parsed = parse_url($alias);
  131. }
  132. // Copy various parts of the parsed URL into the appropriate records of the alias record
  133. foreach (array('user' => 'remote-user', 'pass' => 'remote-pass', 'host' => 'remote-host', 'fragment' => 'uri', 'path' => 'root') as $url_key => $option_key) {
  134. if (array_key_exists($url_key, $parsed)) {
  135. _drush_sitealias_set_record_element($alias_record, $option_key, $parsed[$url_key]);
  136. }
  137. }
  138. // If the site specification has a query, also set the query items
  139. // in the alias record. This allows passing db_url as part of the
  140. // site specification, for example.
  141. if (array_key_exists('query', $parsed)) {
  142. foreach (explode('&', $parsed['query']) as $query_arg) {
  143. $query_components = explode('=', $query_arg);
  144. _drush_sitealias_set_record_element($alias_record, urldecode($query_components[0]), urldecode($query_components[1]));
  145. }
  146. }
  147. // Case 3.): If the URL contains a 'host' portion but no fragment, then set the uri to the host
  148. // Note: We presume that 'server' is the best default for case 3; without this code, the default would
  149. // be whatever is set in $options['l'] on the target machine's drushrc.php settings file.
  150. if (array_key_exists('host', $parsed) && !array_key_exists('fragment', $parsed)) {
  151. $alias_record['uri'] = $parsed['host'];
  152. }
  153. // Special checking: relative aliases embedded in a path
  154. $relative_alias_pos = strpos($alias_record['root'], '/@');
  155. if ($relative_alias_pos !== FALSE) {
  156. // Special checking: /path/@sites
  157. $base = substr($alias_record['root'], 0, $relative_alias_pos);
  158. $relative_alias = substr($alias_record['root'], $relative_alias_pos + 1);
  159. if (drush_valid_drupal_root($base) || ($relative_alias == '@sites')) {
  160. drush_sitealias_create_sites_alias($base);
  161. $alias_record = drush_sitealias_get_record($relative_alias);
  162. }
  163. else {
  164. $alias_record = array();
  165. }
  166. }
  167. }
  168. else {
  169. // Case 5.) and 6.):
  170. // If the alias is the name of a folder in the 'sites' directory,
  171. // then use it as a local site specification.
  172. $alias_record = _drush_sitealias_find_record_for_local_site($alias);
  173. }
  174. }
  175. }
  176. if (!empty($alias_record)) {
  177. // Load the drush config file if there is one associated with this alias
  178. if (!isset($alias_record['remote']) && !isset($alias_record['loaded-config'])) {
  179. $alias_site_dir = drush_sitealias_local_site_path($alias_record);
  180. if (isset($alias_site_dir)) {
  181. // Add the sites folder of this site to the alias search path list
  182. drush_sitealias_add_to_alias_path($alias_site_dir);
  183. if (!isset($alias_record['config'])) {
  184. $alias_record['config'] = realpath($alias_site_dir . '/drushrc.php');
  185. }
  186. }
  187. if (isset($alias_record['config']) && file_exists($alias_record['config'])) {
  188. drush_load_config_file('site', $alias_record['config']);
  189. $alias_record['loaded-config'] = TRUE;
  190. }
  191. unset($alias_record['config']);
  192. }
  193. // Add the static defaults
  194. _drush_sitealias_add_static_defaults($alias_record);
  195. // Cache the result with all of its calculated values
  196. $all_site_aliases[$alias] = $alias_record;
  197. }
  198. return $alias_record;
  199. }
  200. /**
  201. * Add a path to the array of paths where alias files are searched for.
  202. *
  203. * @param $add_path
  204. * A path to add to the search path (or NULL to not add any).
  205. * Once added, the new path will remain available until drush
  206. * exits.
  207. * @return
  208. * An array of paths containing all values added so far
  209. */
  210. function drush_sitealias_add_to_alias_path($add_path) {
  211. static $site_paths = array();
  212. if ($add_path != NULL) {
  213. if (!is_array($add_path)) {
  214. $add_path = explode(':', $add_path);
  215. }
  216. $site_paths = array_unique(array_merge($site_paths, $add_path));
  217. }
  218. return $site_paths;
  219. }
  220. /**
  221. * Return the array of paths where alias files are searched for.
  222. *
  223. * @param $alias_path_context
  224. * If the alias being looked up is part of a relative alias,
  225. * the alias path context specifies the context of the primary
  226. * alias the new alias is rooted from. Alias files stored in
  227. * the sites folder of this context, or inside the context itself
  228. * takes priority over any other search path that might define
  229. * a similarly-named alias. In this way, multiple sites can define
  230. * a '@peer' alias.
  231. * @return
  232. * An array of paths
  233. */
  234. function drush_sitealias_alias_path($alias_path_context = NULL) {
  235. if (isset($alias_path_context)) {
  236. return array(drush_sitealias_local_site_path($alias_path_context));
  237. }
  238. else {
  239. // We get the current list of site paths by adding NULL
  240. // (nothing) to the path list, which is a no-op
  241. $site_paths = drush_sitealias_add_to_alias_path(NULL);
  242. $alias_path = (array) drush_get_option('alias-path', array());
  243. if (empty($alias_path)) {
  244. $alias_path[] = drush_get_context('ETC_PREFIX', '') . '/etc/drush';
  245. $alias_path[] = dirname(__FILE__) . '/..';
  246. $alias_path[] = dirname(__FILE__) . '/../aliases';
  247. if(!is_null(drush_server_home())) {
  248. $alias_path[] = drush_server_home() . '/.drush';
  249. }
  250. }
  251. return array_merge($alias_path, $site_paths);
  252. }
  253. }
  254. /**
  255. * Return the full path to the site directory of the
  256. * given alias record.
  257. *
  258. * @param $alias_record
  259. * The alias record
  260. * @return
  261. * The path to the site directory of the associated
  262. * alias record, or NULL if the record is not a local site.
  263. */
  264. function drush_sitealias_local_site_path($alias_record) {
  265. $result = NULL;
  266. if (isset($alias_record['uri']) && isset($alias_record['root']) && !isset($alias_record['remote_host'])) {
  267. $result = realpath($alias_record['root'] . '/sites/' . drush_sitealias_uri_to_site_dir($alias_record['uri']));
  268. }
  269. return $result;
  270. }
  271. /**
  272. * Check and see if an alias definition for $alias is available.
  273. * If it is, load it into the list of aliases cached in the
  274. * 'site-aliases' context.
  275. *
  276. * @param $alias
  277. * The name of the alias to load in ordinary form ('@name')
  278. * @param $alias_path_context
  279. * When looking up a relative alias, the alias path context is
  280. * the primary alias that we will start our search from.
  281. */
  282. function _drush_sitealias_load_alias($alias, $alias_path_context = NULL) {
  283. $all_site_aliases = drush_get_context('site-aliases');
  284. $result = array();
  285. // Check to see if this is a relative alias ('@site/@peer')
  286. $relative_alias_pos = strpos($alias, '/@');
  287. if ($relative_alias_pos !== false) {
  288. $primary_alias = substr($alias,0,$relative_alias_pos);
  289. $relative_alias = substr($alias,$relative_alias_pos + 1);
  290. $primary_record = drush_sitealias_get_record($primary_alias);
  291. _drush_sitealias_find_and_load_alias(substr($relative_alias,1), $primary_record);
  292. $result = drush_sitealias_get_record($relative_alias);
  293. if (!empty($result)) {
  294. if (array_key_exists('inherited', $result)) {
  295. $result = array_merge($primary_record, $result);
  296. }
  297. $result['name'] = $relative_alias;
  298. _drush_sitealias_cache_alias(substr($alias, 1), $result);
  299. }
  300. }
  301. else {
  302. // Only aliases--those named entities that begin with '@'--can be loaded this way.
  303. // We also skip any alias that has already been loaded.
  304. if ((substr($alias,0,1) == '@') && !array_key_exists($alias,$all_site_aliases)) {
  305. drush_log(dt('Load alias !alias', array('!alias' => $alias)));
  306. $aliasname = substr($alias,1);
  307. $result = _drush_sitealias_find_and_load_alias($aliasname, $alias_path_context);
  308. if (!empty($result)) {
  309. $alias_options = array('site-aliases' => array($aliasname => $result));
  310. _drush_sitealias_add_inherited_values($alias_options['site-aliases']);
  311. drush_set_config_special_contexts($alias_options);
  312. }
  313. }
  314. }
  315. return $result;
  316. }
  317. /**
  318. * Load every alias file that can be found anywhere in the
  319. * alias search path.
  320. */
  321. function drush_sitealias_load_all($resolve_parent = TRUE) {
  322. $result = _drush_sitealias_find_and_load_alias(NULL);
  323. if (!empty($result) && ($resolve_parent == TRUE)) {
  324. // If any aliases were returned, then check for
  325. // inheritance and then store the aliases into the
  326. // alias cache
  327. _drush_sitealias_add_inherited_values($result);
  328. $alias_options = array('site-aliases' => $result);
  329. drush_set_config_special_contexts($alias_options);
  330. }
  331. }
  332. /**
  333. * Worker function called by _drush_sitealias_load_alias and
  334. * drush_sitealias_load_all. Traverses the alias search path
  335. * and finds the specified alias record.
  336. *
  337. * @param $aliasname
  338. * The name of the alias without the leading '@' (i.e. 'name')
  339. * or NULL to load every alias found in every alias file.
  340. * @param $alias_path_context
  341. * When looking up a relative alias, the alias path context is
  342. * the primary alias that we will start our search from.
  343. * @return
  344. * An empty array if nothing was loaded. If $aliasname is
  345. * not null, then the array returned is the alias record for
  346. * $aliasname. If $aliasname is NULL, then the array returned
  347. * is a $kay => $value pair of alias names and alias records
  348. * loaded.
  349. */
  350. function _drush_sitealias_find_and_load_alias($aliasname, $alias_path_context = NULL) {
  351. $result = array();
  352. // Special checking for '@sites' alias
  353. if ($aliasname == 'sites') {
  354. $drupal_root = NULL;
  355. if ($alias_path_context != null) {
  356. if (array_key_exists('root', $alias_path_context) && !array_key_exists('remote-host', $alias_path_context)) {
  357. $drupal_root = $alias_path_context['root'];
  358. }
  359. }
  360. else {
  361. $drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT');
  362. if ($drupal_root == NULL) {
  363. $drupal_root = drush_get_option(array('r', 'root'), drush_locate_root());
  364. }
  365. }
  366. if (isset($drupal_root) && !is_array($drupal_root)) {
  367. drush_sitealias_create_sites_alias($drupal_root);
  368. }
  369. }
  370. // The alias path is a list of folders to search for alias settings files
  371. $alias_path = drush_sitealias_alias_path($alias_path_context);
  372. // $alias_files contains a list of filename patterns
  373. // to search for. We will find any matching file in
  374. // any folder in the alias path. The directory scan
  375. // is not deep, though; only files immediately in the
  376. // search path are considered.
  377. $alias_files = array('/.*aliases\.drushrc\.php/');
  378. if ($aliasname == NULL) {
  379. $alias_files[] = '/.*\.alias\.drushrc\.php/';
  380. }
  381. else {
  382. $alias_files[] = '/' . preg_quote($aliasname) . '\.alias\.drushrc\.php/';
  383. }
  384. // Search each path in turn
  385. foreach ($alias_path as $path) {
  386. // Find all of the matching files in this location
  387. $alias_files_to_consider = array();
  388. foreach ($alias_files as $file_pattern_to_search_for) {
  389. $alias_files_to_consider = array_merge($alias_files_to_consider, array_keys(drush_scan_directory($path, $file_pattern_to_search_for, array('.', '..', 'CVS'), 0, FALSE)));
  390. }
  391. // For every file that matches, check inside it for
  392. // an alias with a matching name.
  393. foreach ($alias_files_to_consider as $filename) {
  394. if (file_exists($filename)) {
  395. $aliases = $options = array();
  396. include $filename;
  397. unset($options['site-aliases']); // maybe unnecessary
  398. // If $aliases are not set, but $options are, then define one alias named
  399. // after the first word of the file, before '.alias.drushrc.php.
  400. if (empty($aliases) && !empty($options)) {
  401. $this_alias_name = substr(basename($filename),0,strpos(basename($filename),'.'));
  402. $aliases[$this_alias_name] = $options;
  403. $options = array();
  404. }
  405. // If this is a group alias file, then make an
  406. // implicit alias from the group name that contains
  407. // a site-list of all of the aliases in the file
  408. if (substr($filename, -20) == ".aliases.drushrc.php") {
  409. $group_name = basename($filename,".aliases.drushrc.php");
  410. if (!array_key_exists($group_name, $aliases)) {
  411. $alias_names = array();
  412. foreach (array_keys($aliases) as $one_alias) {
  413. $alias_names[] = "@$one_alias";
  414. }
  415. $aliases[$group_name] = array('site-list' => implode(',', $alias_names));
  416. }
  417. }
  418. // If aliasname is NULL, then we will store
  419. // all $aliases into the alias cache
  420. if ($aliasname == NULL) {
  421. if (!empty($aliases)) {
  422. if (!empty($options)) {
  423. foreach ($aliases as $name => $value) {
  424. $aliases[$name] = array_merge($options, $value);
  425. }
  426. $options = array();
  427. }
  428. foreach ($aliases as $name => $value) {
  429. _drush_sitealias_initialize_alias_record($aliases[$name]);
  430. }
  431. $result = array_merge($result, $aliases);
  432. }
  433. }
  434. // If aliasname is not NULL, then we will store
  435. // only the named alias into the alias cache
  436. elseif ((isset($aliases)) && array_key_exists($aliasname, $aliases)) {
  437. drush_set_config_special_contexts($options); // maybe unnecessary
  438. $result = array_merge($options, $aliases[$aliasname]);
  439. _drush_sitealias_initialize_alias_record($result);
  440. }
  441. }
  442. }
  443. }
  444. return $result;
  445. }
  446. /**
  447. * Check to see if there is a 'parent' item in the alias; if there is,
  448. * then load the parent alias record and overlay the entries in the
  449. * current alias record on top of the items from the parent record.
  450. *
  451. * @param $aliases
  452. * An array of alias records that are modified in-place.
  453. */
  454. function _drush_sitealias_add_inherited_values(&$aliases) {
  455. foreach ($aliases as $alias_name => $alias_value) {
  456. if (array_key_exists('parent', $alias_value)) {
  457. // Prevent circular references from causing an infinite loop
  458. _drush_sitealias_cache_alias($alias_name, array());
  459. // Fetch and merge in each parent
  460. foreach (explode(',', $alias_value['parent']) as $parent) {
  461. $parent_record = drush_sitealias_get_record($parent);
  462. if ($parent_record != NULL) {
  463. unset($parent_record['name']);
  464. $aliases[$alias_name] = array_merge($parent_record, $aliases[$alias_name]);
  465. }
  466. }
  467. unset($aliases[$alias_name]['parent']);
  468. }
  469. }
  470. }
  471. /**
  472. * Add an empty record for the specified alias name
  473. *
  474. * @param $alias_name
  475. * The name of the alias, without the leading "@"
  476. */
  477. function _drush_sitealias_cache_alias($alias_name, $alias_record) {
  478. $cache =& drush_get_context('site-aliases');
  479. $cache["@$alias_name"] = $alias_record;
  480. // If the alias record points at a local site, make sure
  481. // that both the drupal root and the site folder for that site
  482. // are added to the alias path, so that other alias files
  483. // stored in those locations become searchable.
  484. if (!array_key_exists('remote-host', $alias_record) && array_key_exists('root', $alias_record)) {
  485. drush_sitealias_add_to_alias_path($alias_record['root']);
  486. $site_dir = drush_sitealias_local_site_path($alias_record);
  487. if (isset($site_dir)) {
  488. drush_sitealias_add_to_alias_path($site_dir);
  489. }
  490. }
  491. }
  492. /**
  493. * If the alias record does not contain a 'databases' or 'db-url'
  494. * entry, then use backend invoke to look up the settings value
  495. * from the remote or local site. The 'db_url' form is preferred;
  496. * nothing is done if 'db_url' is not available (e.g. on a D7 site)
  497. *
  498. * @param $alias_record
  499. * The full alias record to populate with database settings
  500. */
  501. function drush_sitealias_add_db_url(&$alias_record) {
  502. if (!isset($alias_record['db-url']) && !isset($alias_record['databases']) && !isset($alias_record['site-list'])) {
  503. $values = drush_do_site_command($alias_record, "sql-conf", array(), array('db-url' => TRUE));
  504. if (isset($values['object']['db-url'])) {
  505. $alias_record['db-url'] = $values['object']['db-url'];
  506. }
  507. }
  508. }
  509. /**
  510. * Return the databases record from the alias record
  511. *
  512. * @param $alias_record
  513. * A record returned from drush_sitealias_get_record
  514. * @returns
  515. * A databases record (always in D7 format) or NULL
  516. * if the databases record could not be found.
  517. */
  518. function sitealias_get_databases_from_record(&$alias_record) {
  519. $altered_record = drush_sitealias_add_db_settings($alias_record);
  520. return array_key_exists('databases', $alias_record) ? $alias_record['databases'] : NULL;
  521. }
  522. /**
  523. * If the alias record does not contain a 'databases' or 'db-url'
  524. * entry, then use backend invoke to look up the settings value
  525. * from the remote or local site. The 'databases' form is
  526. * preferred; 'db_url' will be converted to 'databases' if necessary.
  527. *
  528. * @param $alias_record
  529. * The full alias record to populate with database settings
  530. */
  531. function drush_sitealias_add_db_settings(&$alias_record)
  532. {
  533. $altered_record = FALSE;
  534. // If the alias record does not have a defined 'databases' entry,
  535. // then we'll need to look one up
  536. if (!isset($alias_record['db-url']) && !isset($alias_record['databases']) && !isset($alias_record['site-list'])) {
  537. $values = drush_do_site_command($alias_record, "sql-conf", array(), array('all' => TRUE));
  538. if (isset($values['object'])) {
  539. $alias_record['databases'] = $values['object'];
  540. $altered_record = TRUE;
  541. // If the name is set, then re-cache the record after we fetch the databases
  542. if (array_key_exists('name', $alias_record)) {
  543. $all_site_aliases =& drush_get_context('site-aliases');
  544. $all_site_aliases[$alias_record['name']] = $alias_record;
  545. }
  546. }
  547. }
  548. return $altered_record;
  549. }
  550. /**
  551. * Check to see if we have already bootstrapped to a site.
  552. */
  553. function drush_sitealias_is_bootstrapped_site($alias_record) {
  554. if (!isset($alias_record['remote-host'])) {
  555. $self_record = drush_sitealias_get_record("@self");
  556. if (empty($self_record)) {
  557. // TODO: If we have not bootstrapped to a site yet, we could
  558. // perhaps bootstrap to $alias_record here.
  559. return FALSE;
  560. }
  561. elseif(($alias_record['root'] == $self_record['root']) && ($alias_record['uri'] == $self_record['uri'])) {
  562. return TRUE;
  563. }
  564. }
  565. return FALSE;
  566. }
  567. /**
  568. * If there are any path aliases (items beginning with "%") in the test
  569. * string, then resolve them as path aliases and add them to the provided
  570. * alias record.
  571. *
  572. * @param $alias_record
  573. * The full alias record to use in path alias expansion
  574. * @param $test_string
  575. * A slash-separated list of path aliases to resolve
  576. * e.g. "%files/%special".
  577. */
  578. function drush_sitealias_resolve_path_references(&$alias_record, $test_string = '') {
  579. // Convert the test string into an array of items, and
  580. // from this make a comma-separated list of projects
  581. // that we can pass to 'drush status'.
  582. $test_array = explode('/', $test_string);
  583. $project_array = array();
  584. foreach($test_array as $one_item) {
  585. if (substr($one_item,0,1) == '%') {
  586. $project_array[] = substr($one_item,1);
  587. }
  588. }
  589. // If we already have a path in the path aliases, then
  590. // there is no need to search for it remotely; we can remove
  591. // it from the project array.
  592. if (array_key_exists('path-aliases', $alias_record)) {
  593. foreach ($alias_record['path-aliases'] as $key => $value) {
  594. if (substr($key,0,1) == '%') {
  595. unset($project_array['%' . substr($key,1)]);
  596. }
  597. }
  598. }
  599. $project_list = implode(',', $project_array);
  600. if (!empty($project_array)) {
  601. // Optimization: if we're already bootstrapped to the
  602. // site specified by $alias_record, then we can just
  603. // call _core_site_status_table() rather than use backend invoke.
  604. if (drush_sitealias_is_bootstrapped_site($alias_record)) {
  605. // Make sure that we are bootstrapped at least to the 'site'
  606. // level, and include file.inc to insure that we have access
  607. // to the %file path.
  608. if (drush_bootstrap(DRUSH_BOOTSTRAP_DRUPAL_SITE)) {
  609. include_once DRUPAL_ROOT . '/includes/file.inc';
  610. }
  611. $status_values = _core_site_status_table($project_list);
  612. }
  613. else {
  614. $values = drush_do_site_command($alias_record, "status", array(), empty($project_list) ? array() : array('project' => $project_list));
  615. $status_values = $values['object'];
  616. }
  617. if (isset($status_values['%paths'])) {
  618. foreach ($status_values['%paths'] as $key => $path) {
  619. $alias_record['path-aliases'][$key] = $path;
  620. }
  621. }
  622. }
  623. }
  624. /**
  625. * Given an alias record that is a site list (contains a 'site-list' entry),
  626. * resolve all of the members of the site list and return them
  627. * is an array of alias records.
  628. *
  629. * @param $alias_record
  630. * The site list alias record array
  631. * @return
  632. * An array of individual site alias records
  633. */
  634. function drush_sitealias_resolve_sitelist($alias_record) {
  635. $result_list = array();
  636. if (isset($alias_record)) {
  637. if (array_key_exists('site-list', $alias_record)) {
  638. foreach ($alias_record['site-list'] as $sitespec) {
  639. $one_result = drush_sitealias_get_record($sitespec);
  640. $result_list = array_merge($result_list, drush_sitealias_resolve_sitelist($one_result));
  641. }
  642. }
  643. elseif (array_key_exists('name', $alias_record)) {
  644. $result_list[$alias_record['name']] = $alias_record;
  645. }
  646. }
  647. return $result_list;
  648. }
  649. /**
  650. * Check to see if the uri is the same in the source and target
  651. * lists for all items in the array. This is a strong requirement
  652. * in D6; in D7, it is still highly convenient for the uri to
  653. * be the same, because the site folder name == the uri, and if
  654. * the uris match, then it is easier to rsync between remote machines.
  655. *
  656. * @param $source
  657. * Array of source alias records
  658. * @param $target
  659. * Array of target alias records to compare against source list
  660. * @return
  661. * TRUE iff the uris of the sources and targets are in alignment
  662. */
  663. function drush_sitealias_check_lists_alignment($source, $target) {
  664. $is_aligned = TRUE;
  665. $i = 0;
  666. foreach ($source as $one_source) {
  667. if ((!isset($target[$i])) || (!_drush_sitelist_check_site_records($one_source, $target[$i]))) {
  668. $is_aligned = FALSE;
  669. break;
  670. }
  671. ++$i;
  672. }
  673. return $is_aligned;
  674. }
  675. /**
  676. * If the source and target lists contain alias records to the same
  677. * sets of sites, but in different orders, this routine will re-order
  678. * the lists so that they are in alignment.
  679. *
  680. * TODO: Review the advisability of this operation.
  681. */
  682. function drush_sitelist_align_lists(&$source, &$target, &$source_result, &$target_result) {
  683. $source_result = array();
  684. $target_result = array();
  685. foreach ($source as $key => $one_source) {
  686. $one_target = _drush_sitelist_find_in_list($one_source, $target);
  687. if ($one_target !== FALSE) {
  688. $source_result[] = $one_source;
  689. $target_result[] = $one_target;
  690. unset($source[$key]);
  691. }
  692. }
  693. $source = $source_result;
  694. $target = $target_result;
  695. }
  696. function _drush_sitelist_find_in_list($one_source, &$target) {
  697. $result = FALSE;
  698. foreach ($target as $key => $one_target) {
  699. if(_drush_sitelist_check_site_records($one_source, $one_target)) {
  700. $result = $one_target;
  701. unset($target[$key]);
  702. }
  703. }
  704. return $result;
  705. }
  706. function _drush_sitelist_check_site_records($source, $target) {
  707. if ((array_key_exists('uri', $source)) && (array_key_exists('uri', $target)) && ($source['uri'] == $target['uri'])) {
  708. return TRUE;
  709. }
  710. return FALSE;
  711. }
  712. /**
  713. * Initialize an alias record; called as soon as the alias
  714. * record is loaded from its alias file, before it is stored
  715. * in the cache.
  716. *
  717. * @param alias_record
  718. * The alias record to be initialized; paramter is modified in place.
  719. */
  720. function _drush_sitealias_initialize_alias_record(&$alias_record) {
  721. // If there is a 'from-list' entry, then build a derived
  722. // list based on the site list with the given name.
  723. if (array_key_exists('from-list', $alias_record)) {
  724. // danger of infinite loops... move to transient defaults?
  725. $from_record = drush_sitealias_get_record($alias_record['from-list']);
  726. $from_list = drush_sitealias_resolve_sitelist($from_record);
  727. $derived_list = array();
  728. foreach ($from_list as $one_record) {
  729. $derived_record = _drush_sitealias_derive_record($one_record, $alias_record);
  730. $derived_list[] = drush_sitealias_alias_record_to_spec($derived_record);
  731. }
  732. $alias_record = array();
  733. if (!empty($derived_list)) {
  734. $alias_record['site-list'] = $derived_list;
  735. }
  736. }
  737. // If there is a 'site-search-path' entry, then build
  738. // a 'site-list' entry from all of the sites that can be
  739. // found in the search path.
  740. if (array_key_exists('site-search-path', $alias_record)) {
  741. // TODO: Is there any point in merging the sites from
  742. // the search path with any sites already listed in the
  743. // 'site-list' entry? For now we'll just overwrite.
  744. $search_path = $alias_record['site-search-path'];
  745. if (!is_array($search_path)) {
  746. $search_path = explode(',', $search_path);
  747. }
  748. $found_sites = _drush_sitealias_find_local_sites($search_path);
  749. $alias_record['site-list'] = $found_sites;
  750. // The 'unordered-list' flag indicates that the order of the items in the site list is not stable.
  751. $alias_record['unordered-list'] = '1';
  752. // DEBUG: var_export($alias_record, FALSE);
  753. }
  754. if (array_key_exists('site-list', $alias_record)) {
  755. if (!is_array($alias_record['site-list'])) {
  756. $alias_record['site-list'] = explode(',', $alias_record['site-list']);
  757. }
  758. }
  759. }
  760. /**
  761. * Add "static" default values to the given alias record. The
  762. * difference between a static default and a transient default is
  763. * that static defaults -always- exist in the alias record, and
  764. * they are cached, whereas transient defaults are only added
  765. * if the given drush command explicitly adds them.
  766. *
  767. * @param alias_record
  768. * An alias record with most values already filled in
  769. */
  770. function _drush_sitealias_add_static_defaults(&$alias_record) {
  771. // If there is a 'db-url' entry but not 'databases' entry, then we will
  772. // build 'databases' from 'db-url' so that drush commands that use aliases
  773. // can always count on using a uniform 'databases' array.
  774. if (isset($alias_record['db-url']) && !isset($alias_record['databases'])) {
  775. $alias_record['databases'] = drush_sitealias_convert_db_from_db_url($alias_record['db-url']);
  776. }
  777. // Adjustments for aliases to drupal instances (as opposed to aliases that are site lists)
  778. if (array_key_exists('uri', $alias_record)) {
  779. // Make sure that there is always a 'path-aliases' array
  780. if (!array_key_exists('path-aliases', $alias_record)) {
  781. $alias_record['path-aliases'] = array();
  782. }
  783. // If there is a 'root' entry, then copy it to the '%root' path alias
  784. $alias_record['path-aliases']['%root'] = $alias_record['root'];
  785. }
  786. }
  787. function _drush_sitealias_derive_record($from_record, $modifying_record) {
  788. $result = $from_record;
  789. // If there is a 'remote-user' in the modifying record, copy it.
  790. if (array_key_exists('remote-user', $modifying_record)) {
  791. $result['remote-user'] = $from_record['remote_user'];
  792. }
  793. // If there is a 'remote-host', then:
  794. // If it is empty, clear the remote host in the result record
  795. // If it ends in '.', then prepend it to the remote host in the result record
  796. // Otherwise, copy it to the result record
  797. if (array_key_exists('remote-host', $modifying_record)) {
  798. $remote_host_modifier = $modifying_record['remote-host'];
  799. if(empty($remote_host_modifier)) {
  800. unset($result['remote-host']);
  801. unset($result['remote-user']);
  802. }
  803. elseif ($remote_host_modifier[strlen($remote_host_modifier)-1] == '.') {
  804. $result['remote-host'] = $remote_host_modifier . $result['remote-host'];
  805. }
  806. else {
  807. $result['remote-host'] = $remote_host_modifier;
  808. }
  809. }
  810. // If there is a 'root', then:
  811. // If it begins with '/', copy it to the result record
  812. // Otherwise, append it to the result record
  813. if (array_key_exists('root', $modifying_record)) {
  814. $root_modifier = $modifying_record['root'];
  815. if($root_modifier[0] == '/') {
  816. $result['root'] = $root_modifier;
  817. }
  818. else {
  819. $result['root'] = $result['root'] . '/' . $root_modifier;
  820. }
  821. }
  822. // Poor man's realpath: take out the /../ with preg_replace.
  823. // (realpath fails if the files in the path do not exist)
  824. while(strpos($result['root'], '/../') !== FALSE) {
  825. $result['root'] = preg_replace('/\w+\/\.\.\//', '', $result['root']);
  826. }
  827. // TODO: Should we allow the uri to be transformed?
  828. // I think that if the uri does not match, then you should
  829. // always build the list by hand, and not rely on '_drush_sitealias_derive_record'.
  830. return $result;
  831. }
  832. /**
  833. * Convert from an alias record to a site specification
  834. *
  835. * @param alias_record
  836. * The full alias record to convert
  837. *
  838. * @param with_db
  839. * True if the site specification should include a ?db-url term
  840. *
  841. * @return string
  842. * The site specification
  843. */
  844. function drush_sitealias_alias_record_to_spec($alias_record, $with_db = false) {
  845. $result = '';
  846. // TODO: we should handle 'site-list' records too.
  847. if (array_key_exists('site-list', $alias_record)) {
  848. // TODO: we should actually expand the site list and recompose it
  849. $result = implode(',', $alias_record['site-list']);
  850. }
  851. else {
  852. // There should always be a uri
  853. if (array_key_exists('uri', $alias_record)) {
  854. $result = '#' . drush_sitealias_uri_to_site_dir($alias_record['uri']);
  855. }
  856. // There should always be a root
  857. if (array_key_exists('root', $alias_record)) {
  858. $result = $alias_record['root'] . $result;
  859. }
  860. if (array_key_exists('remote-host', $alias_record)) {
  861. $result = $alias_record['remote-host'] . $result;
  862. if (array_key_exists('remote-user', $alias_record)) {
  863. $result = $alias_record['remote-user'] . '@' . $result;
  864. }
  865. }
  866. // add the database info to the specification if desired
  867. if ($with_db) {
  868. $result = $result . '?db-url=' . urlencode($alias_record['db-url']);
  869. }
  870. }
  871. return $result;
  872. }
  873. /**
  874. * Search for drupal installations in the search path.
  875. *
  876. * @param search_path
  877. * An array of drupal root folders
  878. *
  879. * @return
  880. * An array of site specifications (/path/to/root#sitename.com)
  881. */
  882. function _drush_sitealias_find_local_sites($search_path) {
  883. $result = array();
  884. foreach ($search_path as $a_drupal_root) {
  885. $result = array_merge($result, _drush_find_local_sites_at_root($a_drupal_root));
  886. }
  887. return $result;
  888. }
  889. /**
  890. * Return a list of all of the local sites at the specified drupal root.
  891. */
  892. function _drush_find_local_sites_at_root($a_drupal_root = '', $search_depth = 1) {
  893. $site_list = array();
  894. $base_path = (empty($a_drupal_root) ? drush_get_context('DRUSH_DRUPAL_ROOT') : $a_drupal_root );
  895. if (drush_valid_drupal_root($base_path)) {
  896. // If $a_drupal_root is in fact a valid drupal root, then return
  897. // all of the sites found inside the 'sites' folder of this drupal instance.
  898. $site_list = _drush_find_local_sites_in_sites_folder($base_path);
  899. }
  900. else {
  901. $bootstrap_files = drush_scan_directory($base_path, '/' . basename(DRUSH_DRUPAL_BOOTSTRAP) . '/' , array('.', '..', 'CVS'), 0, drush_get_option('search-depth', $search_depth) + 1, 'filename', 1);
  902. foreach ($bootstrap_files as $one_bootstrap => $info) {
  903. $includes_dir = dirname($one_bootstrap);
  904. if (basename($includes_dir) == basename(dirname(DRUSH_DRUPAL_BOOTSTRAP))) {
  905. $drupal_root = dirname($includes_dir);
  906. $site_list = array_merge(_drush_find_local_sites_in_sites_folder($drupal_root), $site_list);
  907. }
  908. }
  909. }
  910. return $site_list;
  911. }
  912. /**
  913. * Return a list of all of the local sites at the specified 'sites' folder.
  914. */
  915. function _drush_find_local_sites_in_sites_folder($a_drupal_root) {
  916. $site_list = array();
  917. // If anyone searches for sites at a given root, then
  918. // make sure that alias files stored at this root
  919. // directory are included in the alias search path
  920. drush_sitealias_add_to_alias_path($a_drupal_root);
  921. $base_path = $a_drupal_root . '/sites';
  922. // TODO: build a cache keyed off of $base_path (realpath($base_path)?),
  923. // so that it is guarenteed that the lists returned will definitely be
  924. // exactly the same should this routine be called twice with the same path.
  925. $files = drush_scan_directory($base_path, '/settings\.php/', array('.', '..', 'CVS', 'all'), 0, 1, 'filename', 1);
  926. foreach ($files as $filename => $info) {
  927. if ($info->basename == 'settings.php') {
  928. // First we'll resolve the realpath of the settings.php file,
  929. // so that we get the correct drupal root when symlinks are in use.
  930. $real_sitedir = dirname(realpath($filename));
  931. $real_root = drush_locate_root($filename);
  932. if ($real_root !== FALSE) {
  933. $a_drupal_site = $real_root . '#' . basename($real_sitedir);
  934. }
  935. // If the symlink points to some folder outside of any drupal
  936. // root, then we'll use the
  937. else {
  938. $uri = drush_sitealias_site_dir_from_filename($filename);
  939. $a_drupal_site = $a_drupal_root . '#' . $uri;
  940. }
  941. // Add the site if it isn't already in the array
  942. if (!in_array($a_drupal_site, $site_list)) {
  943. $site_list[] = $a_drupal_site;
  944. }
  945. }
  946. }
  947. return $site_list;
  948. }
  949. function drush_sitealias_create_sites_alias($a_drupal_root = '') {
  950. $sites_list = _drush_find_local_sites_at_root($a_drupal_root);
  951. _drush_sitealias_cache_alias('sites', array('site-list' => $sites_list));
  952. }
  953. /**
  954. * Add "transient" default values to the given alias record. The
  955. * difference between a static default and a transient default is
  956. * that static defaults -always- exist in the alias record,
  957. * whereas transient defaults are only added if the given drush
  958. * command explicitly calls this function. The other advantage
  959. * of transient defaults is that it is possible to differentiate
  960. * between a default value and an unspecified value, since the
  961. * transient defaults are not added until requested.
  962. *
  963. * Since transient defaults are not cached, you should avoid doing
  964. * expensive operations here. To be safe, drush commands should
  965. * avoid calling this function more than once.
  966. *
  967. * @param alias_record
  968. * An alias record with most values already filled in
  969. */
  970. function _drush_sitealias_add_transient_defaults(&$alias_record) {
  971. if (isset($alias_record['path-aliases'])) {
  972. // Add the path to the drush folder to the path aliases as !drush
  973. if (!array_key_exists('%drush', $alias_record['path-aliases'])) {
  974. if (array_key_exists('%drush-script', $alias_record['path-aliases'])) {
  975. $alias_record['path-aliases']['%drush'] = dirname($alias_record['path-aliases']['%drush-script']);
  976. }
  977. else {
  978. $alias_record['path-aliases']['%drush'] = dirname($GLOBALS['argv'][0]);
  979. }
  980. }
  981. // Add the path to the site folder to the path aliases as !site
  982. if (!array_key_exists('%site', $alias_record['path-aliases']) && array_key_exists('uri', $alias_record)) {
  983. $alias_record['path-aliases']['%site'] = 'sites/' . drush_sitealias_uri_to_site_dir($alias_record['uri']) . '/';
  984. }
  985. }
  986. }
  987. /**
  988. * If '$alias' is the name of a folder in the sites folder of the given drupal
  989. * root, then build an alias record for it
  990. *
  991. * @param alias
  992. * The name of the site in the 'sites' folder to convert
  993. * @return array
  994. * An alias record, or empty if none found.
  995. */
  996. function _drush_sitealias_find_record_for_local_site($alias, $drupal_root = null) {
  997. $alias_record = array();
  998. // Clip off the leading '#' if it is there
  999. if (substr($alias,0,1) == '#') {
  1000. $alias = substr($alias,1);
  1001. }
  1002. if (!isset($drupal_root)) {
  1003. //$drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT');
  1004. $drupal_root = drush_get_option(array('r', 'root'), drush_locate_root());
  1005. }
  1006. if (isset($drupal_root)) {
  1007. $alias_dir = drush_sitealias_uri_to_site_dir($alias);
  1008. $site_settings_file = $drupal_root . '/sites/' . $alias_dir . '/settings.php';
  1009. $alias_record = drush_sitealias_build_record_from_settings_file($site_settings_file, $alias, $drupal_root);
  1010. }
  1011. return $alias_record;
  1012. }
  1013. function drush_sitealias_build_record_from_settings_file($site_settings_file, $alias = null, $drupal_root = null) {
  1014. $alias_record = array();
  1015. if (file_exists($site_settings_file)) {
  1016. if (!isset($drupal_root)) {
  1017. $drupal_root = drush_locate_root($site_settings_file);
  1018. }
  1019. $alias_record['root'] = $drupal_root;
  1020. if (isset($alias)) {
  1021. $alias_record['uri'] = $alias;
  1022. }
  1023. else {
  1024. $alias_record['uri'] = _drush_sitealias_site_dir_to_uri(drush_sitealias_site_dir_from_filename($site_settings_file));
  1025. }
  1026. }
  1027. return $alias_record;
  1028. }
  1029. /**
  1030. * Pull the site directory from the path to settings.php
  1031. *
  1032. * @param site_settings_file
  1033. * path to settings.php
  1034. *
  1035. * @return string
  1036. * the site directory component of the path to settings.php
  1037. */
  1038. function drush_sitealias_site_dir_from_filename($site_settings_file) {
  1039. return basename(dirname($site_settings_file));
  1040. }
  1041. /**
  1042. * Convert from a URI to a site directory.
  1043. *
  1044. * @param uri
  1045. * A uri, such as http://domain.com:8080/drupal
  1046. * @return string
  1047. * A directory, such as domain.com.8080.drupal
  1048. */
  1049. function drush_sitealias_uri_to_site_dir($uri) {
  1050. return str_replace(array('http://', '/', ':'), array('', '.', '.'), $uri);
  1051. }
  1052. /**
  1053. * Convert from an old-style database URL to an array of database settings
  1054. *
  1055. * @param db_url
  1056. * A Drupal 6 db-url string to convert.
  1057. * @return array
  1058. * An array of database values.
  1059. */
  1060. function drush_convert_db_from_db_url($db_url) {
  1061. if (is_array($db_url)) {
  1062. $url = parse_url($db_url['default']);
  1063. }
  1064. else {
  1065. $url = parse_url($db_url);
  1066. }
  1067. // Fill in defaults to prevent notices.
  1068. $url += array(
  1069. 'driver' => NULL,
  1070. 'user' => NULL,
  1071. 'pass' => NULL,
  1072. 'port' => NULL,
  1073. 'database' => NULL,
  1074. );
  1075. $url = (object)$url;
  1076. return array(
  1077. 'driver' => $url->scheme == 'mysqli' ? 'mysql' : $url->scheme,
  1078. 'username' => urldecode($url->user),
  1079. 'password' => urldecode($url->pass),
  1080. 'port' => urldecode($url->port),
  1081. 'host' => urldecode($url->host),
  1082. 'database' => substr(urldecode($url->path), 1), // skip leading '/' character
  1083. );
  1084. }
  1085. function drush_sitealias_convert_db_from_db_url($db_url) {
  1086. $result = array();
  1087. if (is_array($db_url)) {
  1088. $default_db = array();
  1089. foreach ($db_url as $db_name => $db_urlstr) {
  1090. $default_db[$db_name] = drush_convert_db_from_db_url($db_urlstr);
  1091. }
  1092. $result['default'] = $default_db;
  1093. }
  1094. else {
  1095. $result = array('default' => array('default' => drush_convert_db_from_db_url($db_url)));
  1096. }
  1097. return $result;
  1098. }
  1099. /**
  1100. * Utility function used by drush_get_alias; keys that start with
  1101. * '%' or '!' are path aliases, the rest are entries in the alias record.
  1102. */
  1103. function _drush_sitealias_set_record_element(&$alias_record, $key, $value) {
  1104. if ((substr($key,0,1) == '%') || (substr($key,0,1) == '!')) {
  1105. $alias_record['path-aliases'][$key] = $value;
  1106. }
  1107. elseif (!empty($key)) {
  1108. $alias_record[$key] = $value;
  1109. }
  1110. }
  1111. /**
  1112. * Looks up the specified alias record and calls through to
  1113. * drush_sitealias_set_alias_context, below.
  1114. *
  1115. * @param alias
  1116. * The name of the alias record
  1117. * @param prefix
  1118. * The prefix value to afix to the beginning of every
  1119. * key set.
  1120. * @return boolean
  1121. * TRUE is an alias was found and processed.
  1122. */
  1123. function _drush_sitealias_set_context_by_name($alias, $prefix = '') {
  1124. $site_alias_settings = drush_sitealias_get_record($alias);
  1125. if (!empty($site_alias_settings)) {
  1126. // Create an alias '@self'
  1127. _drush_sitealias_cache_alias('self', $site_alias_settings);
  1128. drush_sitealias_set_alias_context($site_alias_settings, $prefix);
  1129. return TRUE;
  1130. }
  1131. return FALSE;
  1132. }
  1133. /**
  1134. * Given a site alias record, copy selected fields from it
  1135. * into the drush 'alias' context. The 'alias' context has
  1136. * lower precedence than the 'options' context, so values
  1137. * set by an alias record can be overridden by command-line
  1138. * parameters.
  1139. *
  1140. * @param site_alias_settings
  1141. * An alias record
  1142. * @param prefix
  1143. * The prefix value to afix to the beginning of every
  1144. * key set. For example, if this function is called once with
  1145. * 'source-' and again with 'destination-' prefixes, then the
  1146. * source database records will be stored in 'source-databases',
  1147. * and the destination database records will be in
  1148. * 'destination-databases'.
  1149. */
  1150. function drush_sitealias_set_alias_context($site_alias_settings, $prefix = '') {
  1151. $options = drush_get_context('alias');
  1152. // There are some items that we should just skip
  1153. $skip_list = array('site-aliases', 'command-specific');
  1154. // Also skip 'remote-host' and 'remote-user' if 'remote-host' is actually
  1155. // the local machine
  1156. if (array_key_exists('remote-host', $site_alias_settings) && drush_is_local_host($site_alias_settings['remote-host'])) {
  1157. $skip_list[] = 'remote-host';
  1158. $skip_list[] = 'remote-user';
  1159. }
  1160. // Transfer all options from the site alias to the drush options
  1161. // in the 'alias' context.
  1162. foreach ($site_alias_settings as $key => $value) {
  1163. // Special handling for path aliases:
  1164. if ($key == "path-aliases") {
  1165. foreach (array('%drush-script', '%dump', '%include') as $path_key) {
  1166. if (array_key_exists($path_key, $value)) {
  1167. $options[$prefix . substr($path_key, 1)] = $value[$path_key];
  1168. }
  1169. }
  1170. }
  1171. elseif (!in_array($key, $skip_list)) {
  1172. $options[$prefix . $key] = $value;
  1173. }
  1174. }
  1175. drush_set_config_options('alias', $options);
  1176. }
  1177. /*
  1178. * Call prior to drush_sitealias_evaluate_path to insure
  1179. * that any site-specific aliases associated with any
  1180. * local site in $path are defined.
  1181. */
  1182. function _drush_sitealias_preflight_path($path) {
  1183. $alias = NULL;
  1184. // Parse site aliases if there is a colon in the path
  1185. $colon_pos = strpos($path, ':');
  1186. if ($colon_pos !== FALSE) {
  1187. $alias = substr($path, 0, $colon_pos);
  1188. $path = substr($path, $colon_pos + 1);
  1189. $site_alias_settings = _drush_sitealias_get_record($alias);
  1190. if (empty($site_alias_settings) && (substr($path,0,1) == '@')) {
  1191. return NULL;
  1192. }
  1193. $machine = $alias;
  1194. }
  1195. else {
  1196. $machine = '';
  1197. // if the path is a site alias or a local site...
  1198. $site_alias_settings = _drush_sitealias_get_record($path);
  1199. if (empty($site_alias_settings) && (substr($path,0,1) == '@')) {
  1200. return NULL;
  1201. }
  1202. if (!empty($site_alias_settings) || drush_is_local_host($path)) {
  1203. $alias = $path;
  1204. $path = '';
  1205. }
  1206. }
  1207. return array('alias' => $alias, 'path' => $path, 'machine' => $machine);
  1208. }
  1209. /**
  1210. * Evaluate a path from its shorthand form to a literal path
  1211. * usable by rsync.
  1212. *
  1213. * A path is "machine:/path" or "machine:path" or "/path" or "path".
  1214. * 'machine' might instead be an alias record, or the name
  1215. * of a site in the 'sites' folder. 'path' might be (or contain)
  1216. * '%root' or some other path alias. This function will examine
  1217. * all components of the path and evaluate them as necessary to
  1218. * come to the final path.
  1219. *
  1220. * @param path
  1221. * The path to evaluate
  1222. * @param additional_options
  1223. * An array of options that overrides whatever was passed in on
  1224. * the command line (like the 'process' context, but only for
  1225. * the scope of this one call).
  1226. * @return
  1227. * The site record for the machine specified in the path, if any,
  1228. * with the path to pass to rsync (including the machine specifier)
  1229. * in the 'evaluated-path' item.
  1230. */
  1231. function drush_sitealias_evaluate_path($path, &$additional_options) {
  1232. $site_alias_settings = array();
  1233. $path_aliases = array();
  1234. $remote_user = '';
  1235. $preflight = _drush_sitealias_preflight_path($path);
  1236. if (!isset($preflight)) {
  1237. return NULL;
  1238. }
  1239. $alias = $preflight['alias'];
  1240. $path = $preflight['path'];
  1241. $machine = $preflight['machine'];
  1242. if (isset($alias)) {
  1243. $site_alias_settings = drush_sitealias_get_record($alias);
  1244. }
  1245. if (!empty($site_alias_settings)) {
  1246. // Apply any options from this alias that might affect our rsync
  1247. drush_sitealias_set_alias_context($site_alias_settings);
  1248. // Use 'remote-host' from settings if available; otherwise site is local
  1249. if (array_key_exists('remote-host', $site_alias_settings) && !drush_is_local_host($site_alias_settings['remote-host'])) {
  1250. if (array_key_exists('remote-user', $site_alias_settings)) {
  1251. $remote_user = $site_alias_settings['remote-user'] . '@';
  1252. }
  1253. $machine = $remote_user . $site_alias_settings['remote-host'];
  1254. }
  1255. else {
  1256. $machine = '';
  1257. }
  1258. }
  1259. else {
  1260. // Strip the machine portion of the path if the
  1261. // alias points to the local machine.
  1262. if (drush_is_local_host($machine)) {
  1263. $machine = '';
  1264. }
  1265. else {
  1266. $machine = "$remote_user$machine";
  1267. }
  1268. }
  1269. // If the --exclude-other-sites option is specified, then
  1270. // convert that into --include-path='%site' and --exclude-sites.
  1271. if (drush_get_option_override($additional_options, 'exclude-other-sites', FALSE) && !drush_get_option_override($additional_options, 'exclude-other-sites-processed', FALSE, 'process')) {
  1272. $additional_options['include-path'] = '%site,' . drush_get_option_override($additional_options, 'include-path', '');
  1273. $additional_options['exclude-sites'] = TRUE;
  1274. $additional_options['exclude-other-sites-processed'] = TRUE;
  1275. }
  1276. // If the --exclude-files option is specified, then
  1277. // convert that into --exclude-path='%files'.
  1278. if (drush_get_option_override($additional_options, 'exclude-files', FALSE) && !drush_get_option_override($additional_options, 'exclude-files-processed', FALSE, 'process')) {
  1279. $additional_options['exclude-path'] = '%files,' . drush_get_option_override($additional_options, 'exclude-path', '');
  1280. $additional_options['exclude-files-processed'] = TRUE;
  1281. }
  1282. // If there was no site specification given, and the
  1283. // machine is local, then try to look
  1284. // up an alias record for the default drush site.
  1285. if (empty($site_alias_settings) && empty($machine)) {
  1286. $drush_uri = drush_bootstrap_value('drush_uri', drush_get_option(array('l', 'uri'), 'default'));
  1287. $site_alias_settings = drush_sitealias_get_record($drush_uri);
  1288. }
  1289. // Always add transient defaults
  1290. _drush_sitealias_add_transient_defaults($site_alias_settings);
  1291. // The $resolve_path variable is used by drush_sitealias_resolve_path_references
  1292. // to test to see if there are any path references such as %site or %files
  1293. // in it, so that resolution is only done if the path alias is referenced.
  1294. // Therefore, we can concatenate without worrying too much about the structure of
  1295. // this variable's contents.
  1296. $include_path = drush_get_option_override($additional_options, 'include-path', '');
  1297. $exclude_path = drush_get_option_override($additional_options, 'exclude-path', '');
  1298. $resolve_path = $path . $include_path . $exclude_path;
  1299. // Resolve path aliases such as %files, if any exist in the path
  1300. if (!empty($resolve_path)) {
  1301. drush_sitealias_resolve_path_references($site_alias_settings, $resolve_path);
  1302. }
  1303. if (array_key_exists('path-aliases', $site_alias_settings)) {
  1304. $path_aliases = $site_alias_settings['path-aliases'];
  1305. }
  1306. // Get the 'root' setting from the alias; if it does not
  1307. // exist, then get the root from the bootstrapped site.
  1308. if (array_key_exists('root', $site_alias_settings)) {
  1309. $drupal_root = $site_alias_settings['root'];
  1310. }
  1311. else {
  1312. drush_bootstrap_max();
  1313. $drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT');
  1314. }
  1315. if (empty($drupal_root)) {
  1316. $drupal_root = '';
  1317. }
  1318. // Add a slash to the end of the drupal root, as below.
  1319. elseif ($drupal_root[strlen($drupal_root)-1] != '/') {
  1320. $drupal_root = $drupal_root . '/';
  1321. }
  1322. $full_path_aliases = $path_aliases;
  1323. foreach ($full_path_aliases as $key => $value) {
  1324. // Expand all relative path aliases to be based off of the Drupal root
  1325. if (($value[0] != '/') && ($key != '%root')) {
  1326. $full_path_aliases[$key] = $drupal_root . $value;
  1327. }
  1328. // We do not want slashes on the end of our path aliases.
  1329. if ($value[strlen($value)-1] == '/') {
  1330. $full_path_aliases[$key] = substr($full_path_aliases[$key], -1);
  1331. }
  1332. }
  1333. // Fill in path aliases in the path, the include path and the exclude path.
  1334. $path = str_replace(array_keys($full_path_aliases), array_values($full_path_aliases), $path);
  1335. if (!empty($include_path)) {
  1336. drush_set_option('include-path', str_replace(array_keys($path_aliases), array_values($path_aliases), $include_path));
  1337. }
  1338. if (!empty($exclude_path)) {
  1339. drush_set_option('exclude-path', str_replace(array_keys($path_aliases), array_values($path_aliases), $exclude_path));
  1340. }
  1341. // The path component is just the path part of the full
  1342. // machine:path specification (including the colon).
  1343. $path_component = (!empty($path) ? ':' . $path : '');
  1344. // Next make the rsync path, which includes the machine
  1345. // and path components together.
  1346. // First make empty paths or relative paths start from the drupal root.
  1347. if (empty($path) || ($path[0] != '/')) {
  1348. $path = $drupal_root . $path;
  1349. }
  1350. // If there is a $machine component, to the path, then
  1351. // add it to the beginning
  1352. $evaluated_path = $path;
  1353. if (!empty($machine)) {
  1354. $evaluated_path = $machine . ':' . $path;
  1355. }
  1356. //
  1357. // Add our result paths:
  1358. //
  1359. // evaluated-path: machine:/path
  1360. // server-component: machine
  1361. // path-component: :/path
  1362. // path: /path
  1363. // user-path: path (as specified in input parameter)
  1364. //
  1365. $site_alias_settings['evaluated-path'] = $evaluated_path;
  1366. if (!empty($machine)) {
  1367. $site_alias_settings['server-component'] = $machine;
  1368. }
  1369. $site_alias_settings['path-component'] = $path_component;
  1370. $site_alias_settings['path'] = $path;
  1371. $site_alias_settings['user-path'] = $preflight['path'];
  1372. return $site_alias_settings;
  1373. }
  1374. /**
  1375. * Option keys used for site selection.
  1376. */
  1377. function drush_sitealias_site_selection_keys() {
  1378. return array('remote-host', 'remote-user', 'ssh-options', 'name');
  1379. }