7 * Run commands on remote server(s).
8 * @see example.aliases.drushrc.php
9 * @see http://drupal.org/node/670460
12 use Drush\Log\LogLevel;
13 use Webmozart\PathUtil\Path;
16 * Check to see if the user specified an alias
17 * in an arguement, or via site-set. If so, return
18 * the name of the alias.
20 * If the alias came from args, then remove it
23 function drush_sitealias_check_arg_and_site_set() {
24 $args = drush_get_arguments();
25 $target_alias = FALSE;
27 // Test to see if the first arg is a valid alias identifier.
28 // If the first arguement is a well-formed identifier, but we
29 // cannot find a record for it, then we will fail with an error.
30 if (!empty($args) && drush_sitealias_valid_alias_format($args[0])) {
31 // Pop the alias off the arguments list first thing.
32 $target_alias = array_shift($args);
33 drush_set_arguments($args);
36 // If the user did not specify an alias via an argument,
37 // check to see if a site env was set.
38 $target_alias = drush_sitealias_site_get();
41 // Record the user's desired target alias name
43 drush_set_context('DRUSH_TARGET_SITE_ALIAS', $target_alias);
49 * Check to see if the first command-line arg or the
50 * -l option is a site alias; if it is, copy its record
51 * values to the 'alias' context.
54 * TRUE if a site alias was found and processed.
56 function drush_sitealias_check_arg() {
57 $args = drush_get_arguments();
59 // Test to see if the first arg is a site specification
60 if (!empty($args) && _drush_sitealias_set_context_by_name($args[0])) {
61 drush_set_context('DRUSH_TARGET_SITE_ALIAS', $args[0]);
63 // We only need to expand the site specification
64 // once, then we are done.
65 drush_set_arguments($args);
68 // Return false to indicate that no site alias was specified.
73 * Check to see if user has selected a site via site-set command.
75 function drush_sitealias_check_site_env() {
76 $site = drush_get_context('DRUSH_TARGET_SITE_ALIAS');
78 $site_env = drush_sitealias_site_get();
79 if (!empty($site_env) && (_drush_sitealias_set_context_by_name($site_env))) {
80 drush_set_context('DRUSH_TARGET_SITE_ALIAS', $site_env);
84 // Return false to indicate that no site alias was specified.
89 * Check to see if a '@self' record was created during bootstrap.
90 * If not, make one now.
92 function drush_sitealias_create_self_alias() {
93 $self_record = drush_sitealias_get_record('@self');
94 if (!array_key_exists('root', $self_record) && !array_key_exists('remote-host', $self_record)) {
95 $drupal_root = drush_get_context('DRUSH_SELECTED_DRUPAL_ROOT');
96 $uri = drush_get_context('DRUSH_SELECTED_URI');
97 if (!empty($drupal_root) && !empty($uri)) {
98 // Create an alias '@self'
99 _drush_sitealias_cache_alias('@self', array('root' => $drupal_root, 'uri' => $uri));
105 * Given a list of alias records, shorten the name used if possible
107 function drush_sitealias_simplify_names($site_list) {
109 foreach ($site_list as $original_name => $alias_record) {
110 $adjusted_name = $alias_record['#name'];
111 $hashpos = strpos($original_name, '#');
112 if ($hashpos !== FALSE) {
113 $adjusted_name = substr($original_name, $hashpos);
114 if (array_key_exists('remote-host', $alias_record)) {
115 $adjusted_name = $alias_record['remote-host'] . $adjusted_name;
118 $result[$adjusted_name] = $alias_record;
124 * Given an array of site specifications, resolve each one in turn and
125 * return an array of alias records. If you only want a single record,
126 * it is preferable to simply call drush_sitealias_get_record() directly.
128 * @param $site_specifications
130 * A comma-separated list of site specifications: '@site1,@site2'
131 * An array of site specifications: array('@site1','@site2')
132 * An array of alias records:
134 * 'site1' => array('root' => ...),
135 * 'site2' => array('root' => ...)
137 * An array of site specifications.
138 * @see drush_sitealias_get_record() for the format of site specifications.
140 * An array of alias records
142 function drush_sitealias_resolve_sitespecs($site_specifications, $alias_path_context = NULL) {
143 $result_list = array();
144 $not_found = array();
145 if (!is_array($site_specifications)) {
146 $site_specifications = explode(',', $site_specifications);
148 if (!empty($site_specifications)) {
149 foreach ($site_specifications as $site) {
150 if (is_array($site)) {
151 $result_list[] = $site;
154 $alias_record = drush_sitealias_get_record($site, $alias_path_context);
155 if (!$alias_record) {
156 $not_found[] = $site;
159 $result_list = array_merge($result_list, drush_sitealias_resolve_sitelist($alias_record));
164 return array($result_list, $not_found);
168 * Returns TRUE if $alias is a valid format for an alias name.
170 * Mirrors the allowed formats shown below for drush_sitealias_get_record.
172 function drush_sitealias_valid_alias_format($alias) {
173 return ( (strpos($alias, ',') !== false) ||
174 ((strpos($alias, '@') === FALSE ? 0 : 1) + (strpos($alias, '/') === FALSE ? 0 : 1) + (strpos($alias, '#') === FALSE ? 0 : 1) >= 2) ||
175 ($alias{0} == '#') ||
181 * Get a site alias record given an alias name or site specification.
183 * If it is the name of a site alias, return the alias record from
184 * the site aliases array.
186 * If it is the name of a folder in the 'sites' folder, construct
187 * an alias record from values stored in settings.php.
189 * If it is a site specification, construct an alias record from the
190 * values in the specification.
192 * Site specifications come in several forms:
193 * - /path/to/drupal#sitename
194 * - user@server/path/to/drupal#sitename
195 * - user@server/path/to/drupal (sitename == server)
196 * - user@server#sitename (only if $option['r'] set in some drushrc file on server)
197 * - #sitename (only if $option['r'] already set, and 'sitename' is a folder in $option['r']/sites)
198 * - sitename (only if $option['r'] already set, and 'sitename' is a folder in $option['r']/sites)
200 * Note that in the case of the first four forms, it is also possible
201 * to add additional site variable to the specification using uri query
202 * syntax. For example:
204 * user@server/path/to/drupal?db-url=...#sitename
207 * An alias name or site specification
209 * An alias record, or empty if none found.
211 function drush_sitealias_get_record($alias, $alias_context = NULL) {
212 // Check to see if the alias contains commas. If it does, then
213 // we will go ahead and make a site list record
214 $alias_record = array();
215 if (strpos($alias, ',') !== false) {
216 // TODO: If the site list contains any site lists, or site
217 // search paths, then we should expand those and merge them
218 // into this list longhand.
219 $alias_record['site-list'] = explode(',', $alias);
222 $alias_record = _drush_sitealias_get_record($alias, $alias_context);
224 if (!empty($alias_record)) {
225 if (array_key_exists('#name', $alias_record)) {
226 if ($alias_record['#name'] == 'self') {
227 $path = drush_sitealias_local_site_path($alias_record);
229 $cached_alias_record = drush_sitealias_lookup_alias_by_path($path);
230 // Don't overrite keys which have already been negotiated.
231 unset($cached_alias_record['#name'], $cached_alias_record['root'], $cached_alias_record['uri']);
232 $alias_record = array_merge($alias_record, $cached_alias_record);
237 $alias_record['#name'] = drush_sitealias_uri_to_site_dir($alias);
240 return $alias_record;
244 * This is a continuation of drush_sitealias_get_record, above. It is
245 * not intended to be called directly.
247 function _drush_sitealias_get_record($alias, $alias_context = NULL) {
248 $alias_record = array();
249 // Before we do anything else, load $alias if it needs to be loaded
250 _drush_sitealias_load_alias($alias, $alias_context);
252 // Check to see if the provided parameter is in fact a defined alias.
253 $all_site_aliases =& drush_get_context('site-aliases');
254 if (array_key_exists($alias, $all_site_aliases)) {
255 $alias_record = $all_site_aliases[$alias];
257 // If the parameter is not an alias, then it is some form of
258 // site specification (or it is nothing at all)
262 // We will check for a site specification if the alias has at least
263 // two characters from the set '@', '/', '#'.
264 if ((strpos($alias, '@') === FALSE ? 0 : 1) + ((strpos($alias, '/') === FALSE && strpos($alias, '\\') === FALSE) ? 0 : 1) + (strpos($alias, '#') === FALSE ? 0 : 1) >= 2) {
265 if ((substr($alias,0,7) != 'http://') && !drush_is_absolute_path($alias)) {
266 // Add on a scheme so that "user:pass@server" will always parse correctly
267 $parsed = parse_url('http://' . $alias);
269 else if (drush_is_windows() && drush_is_absolute_path($alias)) {
270 // On windows if alias begins with a filesystem path we must add file:// scheme to make it parse correcly
271 $parsed = parse_url('file:///' . $alias);
274 $parsed = parse_url($alias);
276 // Copy various parts of the parsed URL into the appropriate records of the alias record
277 foreach (array('user' => 'remote-user', 'pass' => 'remote-pass', 'host' => 'remote-host', 'fragment' => 'uri', 'path' => 'root') as $url_key => $option_key) {
278 if (array_key_exists($url_key, $parsed)) {
279 _drush_sitealias_set_record_element($alias_record, $option_key, $parsed[$url_key]);
282 // If the site specification has a query, also set the query items
283 // in the alias record. This allows passing db_url as part of the
284 // site specification, for example.
285 if (array_key_exists('query', $parsed)) {
286 foreach (explode('&', $parsed['query']) as $query_arg) {
287 $query_components = explode('=', $query_arg);
288 _drush_sitealias_set_record_element($alias_record, urldecode($query_components[0]), urldecode($query_components[1]));
292 // Case 3.): If the URL contains a 'host' portion but no fragment, then set the uri to the host
293 // Note: We presume that 'server' is the best default for case 3; without this code, the default would
294 // be whatever is set in $options['l'] on the target machine's drushrc.php settings file.
295 if (array_key_exists('host', $parsed) && !array_key_exists('fragment', $parsed)) {
296 $alias_record['uri'] = $parsed['host'];
299 // Special checking: relative aliases embedded in a path
300 $relative_alias_pos = strpos($alias_record['root'], '/@');
301 if ($relative_alias_pos !== FALSE) {
302 // Special checking: /path/@sites
303 $base = substr($alias_record['root'], 0, $relative_alias_pos);
304 $relative_alias = substr($alias_record['root'], $relative_alias_pos + 1);
305 if (drush_valid_root($base) || ($relative_alias == '@sites')) {
306 drush_sitealias_create_sites_alias($base);
307 $alias_record = drush_sitealias_get_record($relative_alias);
310 $alias_record = array();
316 // If the alias is the name of a folder in the 'sites' directory,
317 // then use it as a local site specification.
318 $alias_record = _drush_sitealias_find_record_for_local_site($alias);
323 if (!empty($alias_record)) {
324 if (!isset($alias_record['remote']) && !isset($alias_record['#loaded-config'])) {
325 if (array_key_exists('root', $alias_record)) {
326 drush_sitealias_add_to_alias_path($alias_record['root'] . '/drush');
327 drush_sitealias_add_to_alias_path($alias_record['root'] . '/sites/all/drush');
329 // TODO: We should probably remove this feature, and put it back
330 // in, but in different places (e.g. site selection, sql-sync + rsync
332 $alias_site_dir = drush_sitealias_local_site_path($alias_record);
334 if (isset($alias_site_dir)) {
335 // Add the sites folder of this site to the alias search path list
336 drush_sitealias_add_to_alias_path($alias_site_dir);
338 if (isset($alias_record['config']) && file_exists($alias_record['config'])) {
339 drush_load_config_file('site', $alias_record['config']);
340 $alias_record['#loaded-config'] = TRUE;
342 unset($alias_record['config']);
345 // Add the static defaults
346 _drush_sitealias_add_static_defaults($alias_record);
348 // Cache the result with all of its calculated values
349 $all_site_aliases[$alias] = $alias_record;
352 return $alias_record;
356 * Add a path to the array of paths where alias files are searched for.
359 * A path to add to the search path (or NULL to not add any).
360 * Once added, the new path will remain available until drush
363 * An array of paths containing all values added so far
365 function drush_sitealias_add_to_alias_path($add_path) {
366 static $site_paths = array();
368 if ($add_path != NULL) {
369 if (!is_array($add_path)) {
370 $add_path = explode(PATH_SEPARATOR, $add_path);
372 // Normalize path to make sure we don't add the same path twice on
373 // windows due to different spelling. e.g. c:\tmp and c:/tmp
374 foreach($add_path as &$path) {
375 $path = drush_normalize_path($path);
377 $site_paths = array_unique(array_merge($site_paths, $add_path));
383 * Return the array of paths where alias files are searched for.
385 * @param $alias_path_context
386 * If the alias being looked up is part of a relative alias,
387 * the alias path context specifies the context of the primary
388 * alias the new alias is rooted from. Alias files stored in
389 * the sites folder of this context, or inside the context itself
390 * takes priority over any other search path that might define
391 * a similarly-named alias. In this way, multiple sites can define
396 function drush_sitealias_alias_path($alias_path_context = NULL) {
397 $context_path = array();
398 if (isset($alias_path_context)) {
399 $context_path = array(drush_sitealias_local_site_path($alias_path_context));
401 // We get the current list of site paths by adding NULL
402 // (nothing) to the path list, which is a no-op
403 $site_paths = drush_sitealias_add_to_alias_path(NULL);
405 // If the user defined the root of a drupal site, then also
406 // look for alias files in /drush and /sites/all/drush.
407 $drupal_root = drush_get_context('DRUSH_SELECTED_DRUPAL_ROOT');
408 if (!empty($drupal_root)) {
409 $site_paths[] = drush_sitealias_alias_base_directory($drupal_root . '/../drush');
410 $site_paths[] = drush_sitealias_alias_base_directory($drupal_root . '/drush');
411 $site_paths[] = drush_sitealias_alias_base_directory($drupal_root . '/sites/all/drush');
412 $uri = drush_get_context('DRUSH_SELECTED_URI');
416 $site_dir = drush_sitealias_uri_to_site_dir($uri, $drupal_root);
418 $site_paths[] = drush_sitealias_alias_base_directory("$drupal_root/sites/$site_dir");
421 $alias_path = (array) drush_get_context('ALIAS_PATH', array());
422 return array_unique(array_merge($context_path, $alias_path, $site_paths));
426 * If there is a directory 'site-aliases' in the specified search location,
427 * then search ONLY in that directory for aliases. Otherwise, search
428 * anywhere inside the specified directory for aliases.
430 function drush_sitealias_alias_base_directory($dir) {
431 $potential_location = $dir . '/site-aliases';
432 if (is_dir($potential_location)) {
433 return $potential_location;
439 * Return the full path to the site directory of the
440 * given alias record.
442 * @param $alias_record
445 * The path to the site directory of the associated
446 * alias record, or NULL if the record is not a local site.
448 function drush_sitealias_local_site_path($alias_record) {
451 if (isset($alias_record['root']) && !isset($alias_record['remote-host'])) {
452 if (isset($alias_record['uri'])) {
453 $uri = $alias_record['uri'];
454 $uri = preg_replace('#^[^:]*://#', '', $uri);
455 while (!$result && !empty($uri)) {
456 if (file_exists($alias_record['root'] . '/sites/sites.php')) {
458 include($alias_record['root'] . '/sites/sites.php');
459 if (array_key_exists($uri, $sites)) {
460 $result = $alias_record['root'] . '/sites/' . $sites[$uri];
464 $result = ($alias_record['root'] . '/sites/' . drush_sitealias_uri_to_site_dir($uri, drush_sitealias_get_root($alias_record)));
466 $result = realpath($result);
467 $uri = preg_replace('#^[^.]*\.*#', '', $uri);
471 $result = realpath($alias_record['root'] . '/sites/default');
479 * Check and see if an alias definition for $alias is available.
480 * If it is, load it into the list of aliases cached in the
481 * 'site-aliases' context.
484 * The name of the alias to load in ordinary form ('@name')
485 * @param $alias_path_context
486 * When looking up a relative alias, the alias path context is
487 * the primary alias that we will start our search from.
489 function _drush_sitealias_load_alias($alias, $alias_path_context = NULL) {
490 $all_site_aliases = drush_get_context('site-aliases');
493 // Only aliases--those named entities that begin with '@'--can be loaded this way.
494 // We also skip any alias that has already been loaded.
495 if ((substr($alias,0,1) == '@') && !array_key_exists($alias,$all_site_aliases)) {
496 $aliasname = substr($alias,1);
497 $result = _drush_sitealias_find_and_load_alias($aliasname, $alias_path_context);
498 if (!empty($result)) {
499 $alias_options = array('site-aliases' => array($aliasname => $result));
500 _drush_sitealias_add_inherited_values($alias_options['site-aliases']);
501 drush_set_config_special_contexts($alias_options);
502 if (array_key_exists('#file', $result)) {
503 drush_log(dt('Loaded alias !alias from file !file', array('!alias' => $alias, '!file' => $result['#file'])));
512 * Load every alias file that can be found anywhere in the
515 function drush_sitealias_load_all($resolve_parent = TRUE) {
516 $result = _drush_sitealias_find_and_load_all_aliases();
517 if (!empty($result) && ($resolve_parent == TRUE)) {
518 // If any aliases were returned, then check for
519 // inheritance and then store the aliases into the
521 _drush_sitealias_add_inherited_values($result);
522 $alias_options = array('site-aliases' => $result);
523 drush_set_config_special_contexts($alias_options);
528 * Worker function called by _drush_sitealias_load_alias and
529 * drush_sitealias_load_all. Traverses the alias search path
530 * and finds the specified alias record.
533 * An array of $kay => $value pair of alias names and alias records
536 function _drush_sitealias_find_and_load_all_aliases() {
539 $drush_alias_files = _drush_sitealias_find_alias_files();
540 drush_set_context('drush-alias-files', $drush_alias_files);
542 // For every file that matches, check inside it for
543 // an alias with a matching name.
544 foreach ($drush_alias_files as $filename) {
545 if (file_exists($filename)) {
546 $aliases = $options = array();
547 // silently ignore files we can't include
548 if ((@include $filename) === FALSE) {
549 drush_log(dt('Cannot open alias file "!alias", ignoring.', array('!alias' => realpath($filename))), LogLevel::BOOTSTRAP);
552 unset($options['site-aliases']); // maybe unnecessary
554 // If $aliases are not set, but $options are, then define one alias named
555 // after the first word of the file, before '.alias.drushrc.php.
556 if (empty($aliases) && !empty($options)) {
557 $this_alias_name = substr(basename($filename),0,strpos(basename($filename),'.'));
558 $aliases[$this_alias_name] = $options;
561 // If this is a group alias file, then make an
562 // implicit alias from the group name that contains
563 // a site-list of all of the aliases in the file
565 if (substr($filename, -20) == ".aliases.drushrc.php") {
566 $group_name = basename($filename,".aliases.drushrc.php");
567 if (!empty($aliases) && !array_key_exists($group_name, $aliases)) {
568 $alias_names = array();
569 foreach (array_keys($aliases) as $one_alias) {
570 $alias_names[] = "@$group_name.$one_alias";
571 $aliases["$group_name.$one_alias"] = $aliases[$one_alias];
572 unset($aliases[$one_alias]);
574 $aliases[$group_name] = array('site-list' => implode(',', $alias_names));
577 if (!empty($aliases)) {
578 if (!empty($options)) {
579 foreach ($aliases as $name => $value) {
580 $aliases[$name] = array_merge($options, $value);
585 foreach ($aliases as $name => $value) {
586 _drush_sitealias_initialize_alias_record($aliases[$name]);
587 $aliases[$name]['#name'] = $name;
588 $aliases[$name]['#file'] = $filename;
591 $result = _sitealias_array_merge($result, $aliases);
592 // If we found at least one alias from this file
593 // then record it in the drush-alias-files context.
594 $drush_alias_files = drush_get_context('drush-alias-files');
595 if (!in_array($filename, $drush_alias_files)) {
596 $drush_alias_files[] = $filename;
598 drush_set_context('drush-alias-files', $drush_alias_files);
607 * Function to find all alias files that might contain aliases
608 * that match the requested alias name.
610 function _drush_sitealias_find_alias_files($aliasname = NULL, $alias_path_context = NULL) {
611 $alias_files_to_consider = array();
613 // The alias path is a list of folders to search for alias settings files
614 $alias_path = drush_sitealias_alias_path($alias_path_context);
616 // $alias_files contains a list of filename patterns
617 // to search for. We will find any matching file in
618 // any folder in the alias path. The directory scan
619 // is not deep, though; only files immediately in the
620 // search path are considered.
621 $alias_files = array('/.*aliases\.drush(' . DRUSH_MAJOR_VERSION . '|)rc\.php$/');
622 if ($aliasname == NULL) {
623 $alias_files[] = '/.*\.alias\.drush(' . DRUSH_MAJOR_VERSION . '|)rc\.php$/';
626 $alias_files[] = '/' . preg_quote($aliasname, '/') . '\.alias\.drush(' . DRUSH_MAJOR_VERSION . '|)rc\.php$/';
629 // Do not scan into the files directory.
630 $blacklist = array_merge(array('files'), drush_filename_blacklist());
632 // Search each path in turn.
633 foreach ($alias_path as $path) {
634 // Find all of the matching files in this location
635 foreach ($alias_files as $file_pattern_to_search_for) {
636 drush_log(dt('Scanning into @path for @pattern', array('@path' => $path, '@pattern' => $file_pattern_to_search_for)), LogLevel::DEBUG_NOTIFY);
637 $alias_files_to_consider = array_merge($alias_files_to_consider, array_keys(drush_scan_directory($path, $file_pattern_to_search_for, $blacklist, 0, TRUE)));
641 return $alias_files_to_consider;
645 * Traverses the alias search path and finds the specified alias record.
648 * The name of the alias without the leading '@' (i.e. '#name')
649 * or NULL to load every alias found in every alias file.
650 * @param $alias_path_context
651 * When looking up a relative alias, the alias path context is
652 * the primary alias that we will start our search from.
654 * An empty array if nothing was loaded. If $aliasname is
655 * not null, then the array returned is the alias record for
656 * $aliasname. If $aliasname is NULL, then the array returned
657 * is a $kay => $value pair of alias names and alias records
660 function _drush_sitealias_find_and_load_alias($aliasname, $alias_path_context = NULL) {
661 // Special checking for '@sites' alias
662 if ($aliasname == 'sites') {
664 if ($alias_path_context != null) {
665 if (array_key_exists('root', $alias_path_context) && !array_key_exists('remote-host', $alias_path_context)) {
666 $drupal_root = $alias_path_context['root'];
670 $drupal_root = drush_get_context('DRUSH_SELECTED_DRUPAL_ROOT');
672 if (isset($drupal_root) && !is_array($drupal_root)) {
673 drush_sitealias_create_sites_alias($drupal_root);
677 $alias_files_to_consider = _drush_sitealias_find_alias_files($aliasname, $alias_path_context);
679 return _drush_sitealias_find_and_load_alias_from_file($aliasname, $alias_files_to_consider);
682 function _drush_sitealias_find_and_load_alias_from_file($aliasname, $alias_files_to_consider) {
684 $result_names = array();
686 // For every file that matches, check inside it for
687 // an alias with a matching name.
688 $recorded_files = array();
689 foreach ($alias_files_to_consider as $filename) {
690 if (file_exists($filename)) {
691 $aliases = $options = array();
692 // silently ignore files we can't include
693 if ((@include $filename) === FALSE) {
694 drush_log(dt('Cannot open alias file "!alias", ignoring.', array('!alias' => realpath($filename))), LogLevel::BOOTSTRAP);
697 unset($options['site-aliases']); // maybe unnecessary
699 // If $aliases are not set, but $options are, then define one alias named
700 // after the first word of the file, before '.alias.drushrc.php.
701 if (empty($aliases) && !empty($options)) {
702 $this_alias_name = substr(basename($filename),0,strpos(basename($filename),'.'));
703 $aliases[$this_alias_name] = $options;
706 // If this is a group alias file, then make an
707 // implicit alias from the group name that contains
708 // a site-list of all of the aliases in the file
710 if (substr($filename, -20) == ".aliases.drushrc.php") {
711 $group_name = basename($filename,".aliases.drushrc.php");
712 $group_prefix = $group_name . '.';
713 if (!empty($aliases) && !array_key_exists($group_name, $aliases)) {
714 $alias_names = array();
715 foreach (array_keys($aliases) as $one_alias) {
716 $alias_names[] = "@$group_name.$one_alias";
717 $aliases[$one_alias]['#name'] = "$group_name.$one_alias";
718 $aliases[$one_alias]['#group'] = $group_name;
719 $aliases["$group_name.$one_alias"] = $aliases[$one_alias];
720 $aliases[$one_alias]["#hidden"] = TRUE;
722 $aliases[$group_name] = array('site-list' => implode(',', $alias_names), '#group' => $group_name, '#name' => $group_name);
725 // Store only the named alias into the alias cache
726 if ((isset($aliases)) && !empty($aliasname) && array_key_exists($aliasname, $aliases)) {
727 drush_set_config_special_contexts($options); // maybe unnecessary
728 $one_result = array_merge($options, $aliases[$aliasname]);
729 $one_result['#file'] = $filename;
730 if (!array_key_exists('#name', $one_result)) {
731 $one_result['#name'] = $aliasname;
733 _drush_sitealias_initialize_alias_record($one_result);
734 // If the alias name is exactly the same as a previous match, then
735 // merge the two records together
736 if (!empty($result) && ($result['#name'] == $one_result['#name'])) {
737 $result = _sitealias_array_merge($result, $one_result);
739 // Add the name of the found record to the list of results
741 $result_names[] = "@" . $one_result['#name'];
742 $result = $one_result;
747 // If there are multiple matches, then return a list of results.
748 if (count($result_names) > 1) {
749 $result = array('site-list' => $result_names);
756 * Merges two site aliases.
758 * array_merge_recursive is too much; we only want to run
759 * array_merge on the common top-level keys of the array.
761 * @param array $site_alias_a
762 * A site alias array.
763 * @param array $site_alias_b
764 * A site alias array.
766 * A site alias array where the keys from $site_alias_a are overwritten by the
767 * keys from $site_alias_b.
769 function _sitealias_array_merge($site_alias_a, $site_alias_b) {
770 $result = $site_alias_a;
772 foreach($site_alias_b as $key => $value) {
773 if (is_array($value) && array_key_exists($key, $result)) {
774 $result[$key] = array_merge($result[$key], $value);
777 $result[$key] = $value;
785 * Check to see if there is a 'parent' item in the alias; if there is,
786 * then load the parent alias record and overlay the entries in the
787 * current alias record on top of the items from the parent record.
790 * An array of alias records that are modified in-place.
792 function _drush_sitealias_add_inherited_values(&$aliases) {
793 foreach ($aliases as $alias_name => $alias_value) {
794 // Prevent circular references from causing an infinite loop
795 _drush_sitealias_cache_alias("@$alias_name", array());
796 _drush_sitealias_add_inherited_values_to_record($alias_value);
797 $aliases[$alias_name] = $alias_value;
801 function _drush_sitealias_add_inherited_values_to_record(&$alias_value) {
802 drush_command_invoke_all_ref('drush_sitealias_alter', $alias_value);
803 if (isset($alias_value['parent'])) {
804 drush_log(dt("Using deprecated 'parent' element '!parent' in '!name'.", array('!parent' => $alias_value['parent'], '!name' => $alias_value['#name'])), LogLevel::DEBUG);
805 // Fetch and merge in each parent
806 foreach (explode(',', $alias_value['parent']) as $parent) {
807 $parent_record = drush_sitealias_get_record($parent);
808 unset($parent_record['#name']);
809 unset($parent_record['#file']);
810 unset($parent_record['#hidden']);
811 $array_based_keys = array_merge(drush_get_special_keys(), array('path-aliases'));
812 foreach ($array_based_keys as $array_based_key) {
813 if (isset($alias_value[$array_based_key]) && isset($parent_record[$array_based_key])) {
814 $alias_value[$array_based_key] = array_merge($parent_record[$array_based_key], $alias_value[$array_based_key]);
817 $alias_value = array_merge($parent_record, $alias_value);
820 unset($alias_value['parent']);
824 * Add an empty record for the specified alias name
827 * The name of the alias, including the leading "@"
829 function _drush_sitealias_cache_alias($alias_name, $alias_record) {
830 $cache =& drush_get_context('site-aliases');
831 // If the alias already exists in the cache, then merge
832 // the new alias with the existing alias
833 if (array_key_exists($alias_name, $cache)) {
834 $alias_record = array_merge($cache[$alias_name], $alias_record);
836 if (!isset($alias_record['#name'])) {
837 $alias_record['#name'] = trim($alias_name, '@');
839 $cache[$alias_name] = $alias_record;
841 // If the alias record points at a local site, make sure
842 // that /drush, /sites/all/drush and the site folder for that site
843 // are added to the alias path, so that other alias files
844 // stored in those locations become searchable.
845 if (!array_key_exists('remote-host', $alias_record) && !empty($alias_record['root'])) {
846 drush_sitealias_add_to_alias_path($alias_record['root'] . '/drush');
847 drush_sitealias_add_to_alias_path($alias_record['root'] . '/sites/all/drush');
848 $site_dir = drush_sitealias_local_site_path($alias_record);
849 if (isset($site_dir)) {
850 drush_sitealias_add_to_alias_path($site_dir);
856 * If the alias record does not contain a 'databases' or 'db-url'
857 * entry, then use backend invoke to look up the settings value
858 * from the remote or local site. The 'db_url' form is preferred;
859 * nothing is done if 'db_url' is not available (e.g. on a D7 site)
861 * @param $alias_record
862 * The full alias record to populate with database settings
864 function drush_sitealias_add_db_url(&$alias_record) {
865 if (!isset($alias_record['db-url']) && !isset($alias_record['databases']) && !isset($alias_record['site-list'])) {
866 drush_sitealias_add_db_settings($alias_record);
868 if (!isset($alias_record['db-url']) && isset($alias_record['databases'])) {
869 $alias_record['db-url'] = drush_sitealias_convert_databases_to_db_url($alias_record['databases']);
874 * Drush still accepts --db-url format database specifications as
875 * cli parameters; it is therefore useful to be able to convert
876 * from a database record back to a db-url sometimes.
878 function drush_sitealias_convert_db_spec_to_db_url($db_spec) {
879 $result = urlencode($db_spec["driver"]) . "://";
880 if (isset($db_spec["username"])) {
881 $result .= urlencode($db_spec["username"]);
882 if (isset($db_spec["password"])) {
883 $result .= ":" . urlencode($db_spec["password"]);
887 // Host is required, unless this is an sqlite db.
888 if (isset($db_spec["host"])) {
889 $result .= urlencode($db_spec["host"]);
890 if (isset($db_spec["port"])) {
891 $result .= ":" . urlencode($db_spec["port"]);
893 $result .= '/' . urlencode($db_spec["database"]);
896 // URL-encode the database, but convert slashes
897 // back to their original form for readability.
898 // This portion is the "path" of the URL, so it may
899 // contain slashes. This is important for sqlite.
900 $result .= str_replace("%2F", "/", urlencode(ltrim($db_spec["database"], '/')));
906 * Create a db-url from the databases record.
908 function drush_sitealias_convert_databases_to_db_url($databases) {
909 if ((count($databases) == 1) && isset($databases['default'])) {
910 $result = drush_sitealias_convert_db_spec_to_db_url($databases['default']['default']);
913 foreach ($databases as $key => $db_info) {
914 $result[$key] = drush_sitealias_convert_db_spec_to_db_url($db_info['default']);
921 * Return the databases record from the alias record
923 * @param $alias_record
924 * A record returned from drush_sitealias_get_record
926 * A databases record (always in D7 format) or NULL
927 * if the databases record could not be found.
929 function sitealias_get_databases_from_record(&$alias_record) {
930 $altered_record = drush_sitealias_add_db_settings($alias_record);
932 return array_key_exists('databases', $alias_record) ? $alias_record['databases'] : NULL;
936 * Return the $db_spec record for the database associated with
937 * the provided alias record. @see drush_sitealias_add_db_settings(),
938 * which will be used to first add the database information to the
939 * alias records, invoking sql-conf to look them up if necessary.
941 * The options 'database' and 'target' are used to specify which
942 * specific database should be fetched from the database record;
943 * they may appear in the alias definition, or may be taken from the
944 * command line options. The values 'default' and 'default' are
945 * used if these options are not specified in either location.
947 * Note that in the context of sql-sync, the site alias record will
948 * be taken from one of the source or target aliases
949 * (e.g. `drush sql-sync @source @target`), which will be overlayed with
950 * any options that begin with 'source-' or 'target-', respectively.
951 * Therefore, the commandline options 'source-database' and 'source-target'
952 * (or 'target-database' and 'source-target') may also affect the operation
955 function drush_sitealias_get_db_spec(&$alias_record, $default_to_self = FALSE, $prefix = '') {
957 $databases = sitealias_get_databases_from_record($alias_record);
958 if (isset($databases) && !empty($databases)) {
959 $database = drush_sitealias_get_option($alias_record, 'database', 'default', $prefix);
960 $target = drush_sitealias_get_option($alias_record, 'target', 'default', $prefix);
961 if (array_key_exists($database, $databases) && array_key_exists($target, $databases[$database])) {
962 $db_spec = $databases[$database][$target];
965 elseif ($default_to_self) {
966 $db_spec = _drush_sql_get_db_spec();
969 if (isset($db_spec)) {
970 $remote_host = drush_sitealias_get_option($alias_record, 'remote-host', NULL, $prefix);
971 if (!drush_is_local_host($remote_host)) {
972 $db_spec['remote-host'] = $remote_host;
973 $db_spec['port'] = drush_sitealias_get_option($alias_record, 'remote-port', (isset($db_spec['port']) ? $db_spec['port'] : NULL), $prefix);
981 * If the alias record does not contain a 'databases' or 'db-url'
982 * entry, then use backend invoke to look up the settings value
983 * from the remote or local site. The 'databases' form is
984 * preferred; 'db_url' will be converted to 'databases' if necessary.
986 * @param $alias_record
987 * The full alias record to populate with database settings
989 function drush_sitealias_add_db_settings(&$alias_record) {
990 $altered_record = FALSE;
991 if (isset($alias_record['root'])) {
992 // If the alias record does not have a defined 'databases' entry,
993 // then we'll need to look one up
994 if (!isset($alias_record['db-url']) && !isset($alias_record['databases']) && !isset($alias_record['site-list'])) {
995 $values = drush_invoke_process($alias_record, "sql-conf", array(), array('all' => TRUE), array('integrate' => FALSE, 'override-simulated' => TRUE));
996 if (is_array($values) && ($values['error_status'] == 0)) {
997 $altered_record = TRUE;
998 // If there are any special settings in the '@self' record returned by drush_invoke_process,
999 // then add those into our altered record as well
1000 if (array_key_exists('self', $values)) {
1001 $alias_record = array_merge($values['self'], $alias_record);
1003 drush_sitealias_cache_db_settings($alias_record, $values['object']);
1007 return $altered_record;
1010 function drush_sitealias_cache_db_settings(&$alias_record, $databases) {
1011 if (!empty($databases)) {
1012 $alias_record['databases'] = $databases;
1015 // If the name is set, then re-cache the record after we fetch the databases
1016 if (array_key_exists('#name', $alias_record)) {
1017 $all_site_aliases =& drush_get_context('site-aliases');
1018 $all_site_aliases['@' . $alias_record['#name']] = $alias_record;
1019 // Check and see if this record is a copy of 'self'
1020 if (($alias_record['#name'] != 'self') && array_key_exists('@self', $all_site_aliases) && array_key_exists('#name', $all_site_aliases['@self']) && ($all_site_aliases['@self']['#name'] == $alias_record['#name'])) {
1021 $all_site_aliases['@self'] = $alias_record;
1027 * Check to see if we have already bootstrapped to a site.
1029 function drush_sitealias_is_bootstrapped_site($alias_record) {
1030 if (!isset($alias_record['remote-host']) && array_key_exists('root', $alias_record)) {
1031 $self_record = drush_sitealias_get_record("@self");
1032 if (empty($self_record) || !array_key_exists('root', $self_record)) {
1033 // TODO: If we have not bootstrapped to a site yet, we could
1034 // perhaps bootstrap to $alias_record here.
1037 elseif(($alias_record['root'] == $self_record['root']) && ($alias_record['uri'] == $self_record['uri'])) {
1045 * Determines whether a given site alias is for a remote site.
1047 * @param string $alias
1048 * An alias name or site specification.
1051 * Returns TRUE if the alias refers to a remote site, FALSE if it does not, or NULL is unsure.
1053 function drush_sitealias_is_remote_site($alias) {
1054 if (is_array($alias) && !empty($alias['remote-host'])) {
1057 if (!is_string($alias) || !strlen($alias)) {
1061 $site_record = drush_sitealias_get_record($alias);
1063 if (!empty($site_record['remote-host'])) {
1071 drush_set_error('Unrecognized site alias.');
1076 * Get the name of the current bootstrapped site
1078 function drush_sitealias_bootstrapped_site_name() {
1080 $self_record = drush_sitealias_get_record('@self');
1081 if (array_key_exists('#name', $self_record)) {
1082 $site_name = $self_record['#name'];
1084 if (!isset($site_name) || ($site_name == '@self')) {
1085 $drupal_root = drush_get_context('DRUSH_SELECTED_DRUPAL_ROOT');
1086 if (isset($drupal_root)) {
1087 $drupal_uri = drush_get_context('DRUSH_SELECTED_URI', 'default');
1088 $drupal_uri = str_replace('http://', '', $drupal_uri);
1089 // TODO: Maybe use _drush_sitealias_find_local_alias_name?
1090 $site_name = $drupal_root . '#' . $drupal_uri;
1097 * If there are any path aliases (items beginning with "%") in the test
1098 * string, then resolve them as path aliases and add them to the provided
1101 * @param $alias_record
1102 * The full alias record to use in path alias expansion
1103 * @param $test_string
1104 * A slash-separated list of path aliases to resolve
1105 * e.g. "%files/%special".
1107 function drush_sitealias_resolve_path_references(&$alias_record, $test_string = '') {
1108 $path_aliases = array_key_exists('path-aliases', $alias_record) ? $alias_record['path-aliases'] : array();
1109 // Convert the test string into an array of items, and
1110 // from this make a comma-separated list of projects
1111 // that we can pass to 'drush status'.
1112 $test_array = explode('/', $test_string);
1113 $project_array = array();
1114 foreach($test_array as $one_item) {
1115 if (!empty($one_item) && ($one_item[0] == '%') && (!array_key_exists($one_item,$path_aliases))) {
1116 $project_array[] = substr($one_item,1);
1119 $project_list = implode(',', $project_array);
1121 if (!empty($project_array)) {
1122 // Optimization: if we're already bootstrapped to the
1123 // site specified by $alias_record, then we can just
1124 // call _core_site_status_table() rather than use backend invoke.
1125 if (drush_sitealias_is_bootstrapped_site($alias_record) && drush_has_boostrapped(DRUSH_BOOTSTRAP_DRUPAL_FULL)) {
1126 $status_values = _core_site_status_table($project_list);
1129 $values = drush_invoke_process($alias_record, "core-status", array(), empty($project_list) ? array() : array('project' => $project_list), array('integrate' => FALSE, 'override-simulated' => TRUE));
1130 $status_values = $values['object'];
1132 if (isset($status_values['%paths'])) {
1133 foreach ($status_values['%paths'] as $key => $path) {
1134 $alias_record['path-aliases'][$key] = $path;
1137 // If 'root' is not set in the alias, then fill it in from the status values.
1138 if (!isset($alias_record['root']) && isset($status_values['root'])) {
1139 $alias_record['root'] = $status_values['root'];
1145 * Given an alias record that is a site list (contains a 'site-list' entry),
1146 * resolve all of the members of the site list and return them
1147 * is an array of alias records.
1149 * @param $alias_record
1150 * The site list alias record array
1152 * An array of individual site alias records
1154 function drush_sitealias_resolve_sitelist($alias_record) {
1155 $result_list = array();
1156 if (isset($alias_record)) {
1157 if (array_key_exists('site-list', $alias_record)) {
1158 foreach ($alias_record['site-list'] as $sitespec) {
1159 $one_result = drush_sitealias_get_record($sitespec);
1160 $result_list = array_merge($result_list, drush_sitealias_resolve_sitelist($one_result));
1163 elseif (array_key_exists('#name', $alias_record)) {
1164 $result_list[$alias_record['#name']] = $alias_record;
1168 return $result_list;
1171 function _drush_sitelist_find_in_list($one_source, &$target) {
1174 foreach ($target as $key => $one_target) {
1175 if(_drush_sitelist_check_site_records($one_source, $one_target)) {
1176 $result = $one_target;
1177 unset($target[$key]);
1184 function _drush_sitelist_check_site_records($source, $target) {
1185 if ((array_key_exists('uri', $source)) && (array_key_exists('uri', $target)) && ($source['uri'] == $target['uri'])) {
1192 * Initialize an alias record; called as soon as the alias
1193 * record is loaded from its alias file, before it is stored
1196 * @param alias_record
1197 * The alias record to be initialized; parameter is modified in place.
1199 function _drush_sitealias_initialize_alias_record(&$alias_record) {
1200 // If there is a 'from-list' entry, then build a derived
1201 // list based on the site list with the given name.
1202 if (array_key_exists('from-list', $alias_record)) {
1203 // danger of infinite loops... move to transient defaults?
1204 $from_record = drush_sitealias_get_record($alias_record['from-list']);
1205 $from_list = drush_sitealias_resolve_sitelist($from_record);
1206 $derived_list = array();
1207 foreach ($from_list as $one_record) {
1208 $derived_record = _drush_sitealias_derive_record($one_record, $alias_record);
1209 $derived_list[] = drush_sitealias_alias_record_to_spec($derived_record);
1212 $alias_record = array();
1213 if (!empty($derived_list)) {
1214 $alias_record['site-list'] = $derived_list;
1217 // If there is a 'site-search-path' entry, then build
1218 // a 'site-list' entry from all of the sites that can be
1219 // found in the search path.
1220 if (array_key_exists('site-search-path', $alias_record)) {
1221 // TODO: Is there any point in merging the sites from
1222 // the search path with any sites already listed in the
1223 // 'site-list' entry? For now we'll just overwrite.
1224 $search_path = $alias_record['site-search-path'];
1225 if (!is_array($search_path)) {
1226 $search_path = explode(',', $search_path);
1228 $found_sites = _drush_sitealias_find_local_sites($search_path);
1229 $alias_record['site-list'] = $found_sites;
1230 // The 'unordered-list' flag indicates that the order of the items in the site list is not stable.
1231 $alias_record['unordered-list'] = '1';
1232 // DEBUG: var_export($alias_record, FALSE);
1234 if (array_key_exists('site-list', $alias_record)) {
1235 if (!is_array($alias_record['site-list'])) {
1236 $alias_record['site-list'] = explode(',', $alias_record['site-list']);
1240 if (isset($alias_record['root']) && !isset($alias_recort['uri'])) {
1241 $alias_recort['uri'] = 'default';
1247 * Add "static" default values to the given alias record. The
1248 * difference between a static default and a transient default is
1249 * that static defaults -always- exist in the alias record, and
1250 * they are cached, whereas transient defaults are only added
1251 * if the given drush command explicitly adds them.
1253 * @param alias_record
1254 * An alias record with most values already filled in
1256 function _drush_sitealias_add_static_defaults(&$alias_record) {
1257 // If there is a 'db-url' entry but not 'databases' entry, then we will
1258 // build 'databases' from 'db-url' so that drush commands that use aliases
1259 // can always count on using a uniform 'databases' array.
1260 if (isset($alias_record['db-url']) && !isset($alias_record['databases'])) {
1261 $alias_record['databases'] = drush_sitealias_convert_db_from_db_url($alias_record['db-url']);
1264 // Canonicalize paths.
1265 if (!empty($alias_record['root'])) {
1266 $alias_record['root'] = Path::canonicalize($alias_record['root']);
1269 // Adjustments for aliases to drupal instances (as opposed to aliases that are site lists)
1270 if (array_key_exists('uri', $alias_record)) {
1271 // Make sure that there is always a 'path-aliases' array
1272 if (!array_key_exists('path-aliases', $alias_record)) {
1273 $alias_record['path-aliases'] = array();
1275 // If there is a 'root' entry, then copy it to the '%root' path alias
1276 if (isset($alias_record['root'])) {
1277 $alias_record['path-aliases']['%root'] = $alias_record['root'];
1282 function _drush_sitealias_derive_record($from_record, $modifying_record) {
1283 $result = $from_record;
1285 // If there is a 'remote-user' in the modifying record, copy it.
1286 if (array_key_exists('remote-user', $modifying_record)) {
1287 $result['remote-user'] = $from_record['remote_user'];
1289 // If there is a 'remote-host', then:
1290 // If it is empty, clear the remote host in the result record
1291 // If it ends in '.', then prepend it to the remote host in the result record
1292 // Otherwise, copy it to the result record
1293 if (array_key_exists('remote-host', $modifying_record)) {
1294 $remote_host_modifier = $modifying_record['remote-host'];
1295 if(empty($remote_host_modifier)) {
1296 unset($result['remote-host']);
1297 unset($result['remote-user']);
1299 elseif ($remote_host_modifier[strlen($remote_host_modifier)-1] == '.') {
1300 $result['remote-host'] = $remote_host_modifier . $result['remote-host'];
1303 $result['remote-host'] = $remote_host_modifier;
1306 // If there is a 'root', then:
1307 // If it begins with '/', copy it to the result record
1308 // Otherwise, append it to the result record
1309 if (array_key_exists('root', $modifying_record)) {
1310 $root_modifier = $modifying_record['root'];
1311 if($root_modifier[0] == '/') {
1312 $result['root'] = $root_modifier;
1315 $result['root'] = $result['root'] . '/' . $root_modifier;
1318 // Poor man's realpath: take out the /../ with preg_replace.
1319 // (realpath fails if the files in the path do not exist)
1320 while(strpos($result['root'], '/../') !== FALSE) {
1321 $result['root'] = preg_replace('/\w+\/\.\.\//', '', $result['root']);
1324 // TODO: Should we allow the uri to be transformed?
1325 // I think that if the uri does not match, then you should
1326 // always build the list by hand, and not rely on '_drush_sitealias_derive_record'.
1332 * Convert from an alias record to a site specification
1334 * @param alias_record
1335 * The full alias record to convert
1338 * True if the site specification should include a ?db-url term
1341 * The site specification
1343 function drush_sitealias_alias_record_to_spec($alias_record, $with_db = false) {
1346 // TODO: we should handle 'site-list' records too.
1347 if (array_key_exists('site-list', $alias_record)) {
1348 // TODO: we should actually expand the site list and recompose it
1349 $result = implode(',', $alias_record['site-list']);
1352 // There should always be a uri
1353 if (array_key_exists('uri', $alias_record)) {
1354 $result = '#' . drush_sitealias_uri_to_site_dir($alias_record['uri'], drush_sitealias_get_root($alias_record));
1356 // There should always be a root
1357 if (array_key_exists('root', $alias_record)) {
1358 $result = $alias_record['root'] . $result;
1360 if (array_key_exists('remote-host', $alias_record)) {
1361 $result = drush_remote_host($alias_record) . $result;
1364 // Add the database info to the specification if desired
1366 // If db-url is not supplied, look it up from the remote
1367 // or local site and add it to the site alias
1368 if (!isset($alias_record['db-url'])) {
1369 drush_sitealias_add_db_url($alias_record);
1371 $result = $result . '?db-url=' . urlencode(is_array($alias_record['db-url']) ? $alias_record['db-url']['default'] : $alias_record['db-url']);
1379 * Search for drupal installations in the search path.
1381 * @param search_path
1382 * An array of drupal root folders
1385 * An array of site specifications (/path/to/root#sitename.com)
1387 function _drush_sitealias_find_local_sites($search_path) {
1389 foreach ($search_path as $a_drupal_root) {
1390 $result = array_merge($result, _drush_find_local_sites_at_root($a_drupal_root));
1396 * Return a list of all of the local sites at the specified drupal root.
1398 function _drush_find_local_sites_at_root($a_drupal_root = '', $search_depth = 1) {
1399 $site_list = array();
1400 $base_path = (empty($a_drupal_root) ? drush_get_context('DRUSH_DRUPAL_ROOT') : $a_drupal_root );
1401 if (!empty($base_path)) {
1402 if (drush_valid_root($base_path)) {
1403 // If $a_drupal_root is in fact a valid drupal root, then return
1404 // all of the sites found inside the 'sites' folder of this drupal instance.
1405 $site_list = _drush_find_local_sites_in_sites_folder($base_path);
1408 $bootstrap_files = drush_scan_directory($base_path, '/' . basename(DRUSH_DRUPAL_SIGNATURE) . '/' , array('.', '..', 'CVS', 'examples'), 0, drush_get_option('search-depth', $search_depth) + 1, 'filename', 1);
1409 foreach ($bootstrap_files as $one_bootstrap => $info) {
1410 $includes_dir = dirname($one_bootstrap);
1411 if (basename($includes_dir) == basename(dirname(DRUSH_DRUPAL_SIGNATURE))) {
1412 $drupal_root = dirname($includes_dir);
1413 $site_list = array_merge(_drush_find_local_sites_in_sites_folder($drupal_root), $site_list);
1422 * Return a list of all of the local sites at the specified 'sites' folder.
1424 function _drush_find_local_sites_in_sites_folder($a_drupal_root) {
1425 $site_list = array();
1427 // If anyone searches for sites at a given root, then
1428 // make sure that alias files stored at this root
1429 // directory are included in the alias search path
1430 drush_sitealias_add_to_alias_path($a_drupal_root);
1432 $base_path = $a_drupal_root . '/sites';
1434 // TODO: build a cache keyed off of $base_path (realpath($base_path)?),
1435 // so that it is guarenteed that the lists returned will definitely be
1436 // exactly the same should this routine be called twice with the same path.
1438 $files = drush_scan_directory($base_path, '/settings\.php/', array('.', '..', 'CVS', 'all'), 0, 1, 'filename', 1);
1439 foreach ($files as $filename => $info) {
1440 if ($info->basename == 'settings.php') {
1441 // First we'll resolve the realpath of the settings.php file,
1442 // so that we get the correct drupal root when symlinks are in use.
1443 $real_sitedir = dirname(realpath($filename));
1444 $real_root = drush_locate_root($filename);
1445 if ($real_root !== FALSE) {
1446 $a_drupal_site = $real_root . '#' . basename($real_sitedir);
1448 // If the symlink points to some folder outside of any drupal
1449 // root, then we'll use the
1451 $uri = drush_sitealias_site_dir_from_filename($filename);
1452 $a_drupal_site = $a_drupal_root . '#' . $uri;
1454 // Add the site if it isn't already in the array
1455 if (!in_array($a_drupal_site, $site_list)) {
1456 $site_list[] = $a_drupal_site;
1463 function drush_sitealias_create_sites_alias($a_drupal_root = '') {
1464 $sites_list = _drush_find_local_sites_at_root($a_drupal_root);
1465 _drush_sitealias_cache_alias('@sites', array('site-list' => $sites_list));
1469 * Add "transient" default values to the given alias record. The
1470 * difference between a static default and a transient default is
1471 * that static defaults -always- exist in the alias record,
1472 * whereas transient defaults are only added if the given drush
1473 * command explicitly calls this function. The other advantage
1474 * of transient defaults is that it is possible to differentiate
1475 * between a default value and an unspecified value, since the
1476 * transient defaults are not added until requested.
1478 * Since transient defaults are not cached, you should avoid doing
1479 * expensive operations here. To be safe, drush commands should
1480 * avoid calling this function more than once.
1482 * @param alias_record
1483 * An alias record with most values already filled in
1485 function _drush_sitealias_add_transient_defaults(&$alias_record) {
1486 if (isset($alias_record['path-aliases'])) {
1487 // Add the path to the drush folder to the path aliases as !drush
1488 if (!array_key_exists('%drush', $alias_record['path-aliases'])) {
1489 if (array_key_exists('%drush-script', $alias_record['path-aliases'])) {
1490 $alias_record['path-aliases']['%drush'] = dirname($alias_record['path-aliases']['%drush-script']);
1493 $alias_record['path-aliases']['%drush'] = dirname(drush_find_drush());
1496 // Add the path to the site folder to the path aliases as !site
1497 if (!array_key_exists('%site', $alias_record['path-aliases']) && array_key_exists('uri', $alias_record)) {
1498 $alias_record['path-aliases']['%site'] = 'sites/' . drush_sitealias_uri_to_site_dir($alias_record['uri'], drush_sitealias_get_root($alias_record)) . '/';
1504 * Find the name of a local alias record that has the specified
1507 function _drush_sitealias_find_local_alias_name($root, $uri) {
1509 $all_site_aliases =& drush_get_context('site-aliases');
1511 foreach ($all_site_aliases as $alias_name => $alias_values) {
1512 if (!array_key_exists('remote-host', $alias_values) && array_key_exists('root', $alias_values) && array_key_exists('uri', $alias_values) && ($alias_name != '@self')) {
1513 if (($root == $alias_values['root']) && ($uri == $alias_values['uri'])) {
1514 $result = $alias_name;
1523 * If '$alias' is the name of a folder in the sites folder of the given drupal
1524 * root, then build an alias record for it
1527 * The name of the site in the 'sites' folder to convert
1529 * An alias record, or empty if none found.
1531 function _drush_sitealias_find_record_for_local_site($alias, $drupal_root = NULL) {
1532 $alias_record = array();
1534 // Clip off the leading '#' if it is there
1535 if (substr($alias,0,1) == '#') {
1536 $alias = substr($alias,1);
1539 if (!isset($drupal_root)) {
1540 $drupal_root = drush_get_context('DRUSH_SELECTED_DRUPAL_ROOT');
1543 if (!empty($drupal_root)) {
1544 $alias_dir = drush_sitealias_uri_to_site_dir($alias, $drupal_root);
1545 $site_settings_file = $drupal_root . '/sites/' . $alias_dir . '/settings.php';
1546 $alias_record = drush_sitealias_build_record_from_settings_file($site_settings_file, $alias, $drupal_root);
1549 return $alias_record;
1552 function drush_sitealias_build_record_from_settings_file($site_settings_file, $alias = null, $drupal_root = null) {
1553 $alias_record = array();
1555 if (file_exists($site_settings_file)) {
1556 if (!isset($drupal_root)) {
1557 $drupal_root = drush_locate_root($site_settings_file);
1560 $alias_record['root'] = $drupal_root;
1561 if (isset($alias)) {
1562 $alias_record['uri'] = $alias;
1565 $alias_record['uri'] = _drush_sitealias_site_dir_to_uri(drush_sitealias_site_dir_from_filename($site_settings_file));
1569 return $alias_record;
1573 * Pull the site directory from the path to settings.php
1575 * @param site_settings_file
1576 * path to settings.php
1579 * the site directory component of the path to settings.php
1581 function drush_sitealias_site_dir_from_filename($site_settings_file) {
1582 return basename(dirname($site_settings_file));
1586 * Convert from a URI to a site directory.
1589 * A uri, such as http://domain.com:8080/drupal
1591 * A directory, such as domain.com.8080.drupal
1593 function drush_sitealias_uri_to_site_dir($uri, $site_root = NULL) {
1594 $uri = str_replace('http://', '', $uri);
1595 $uri = str_replace('https://', '', $uri);
1596 if (drush_is_windows()) {
1597 // Handle absolute paths on windows
1598 $uri = str_replace(array(':/', ':\\'), array('.', '.'), $uri);
1601 $hostname = str_replace(array('/', ':', '\\'), array('.', '.', '.'), $uri);
1603 // Check sites.php mappings
1604 $site_dir = drush_site_dir_lookup_from_hostname($hostname, $site_root);
1606 return $site_dir ? $site_dir : $hostname;
1610 * Convert from an old-style database URL to an array of database settings.
1613 * A Drupal 6 db url string to convert, or an array with a 'default' element.
1615 * An array of database values containing only the 'default' element of
1616 * the db url. If the parse fails the array is empty.
1618 function drush_convert_db_from_db_url($db_url) {
1621 if (is_array($db_url)) {
1622 $db_url_default = $db_url['default'];
1625 $db_url_default = $db_url;
1628 // If it's a sqlite database, pick the database path and we're done.
1629 if (strpos($db_url_default, 'sqlite://') === 0) {
1631 'driver' => 'sqlite',
1632 'database' => substr($db_url_default, strlen('sqlite://')),
1636 $url = parse_url($db_url_default);
1638 // Fill in defaults to prevent notices.
1647 $url = (object)array_map('urldecode', $url);
1649 'driver' => $url->scheme == 'mysqli' ? 'mysql' : $url->scheme,
1650 'username' => $url->user,
1651 'password' => $url->pass,
1652 'host' => $url->host,
1653 'port' => $url->port,
1654 'database' => ltrim($url->path, '/'),
1663 * Convert from an old-style database URL to an array of database settings
1666 * A Drupal 6 db-url string to convert, or an array with multiple db-urls.
1668 * An array of database values.
1670 function drush_sitealias_convert_db_from_db_url($db_url) {
1673 if (!is_array($db_url)) {
1674 $result = array('default' => array('default' => drush_convert_db_from_db_url($db_url)));
1677 foreach ($db_url as $one_name => $one_db_url) {
1678 $result[$one_name] = array('default' => drush_convert_db_from_db_url($one_db_url));
1686 * Utility function used by drush_get_alias; keys that start with
1687 * '%' or '!' are path aliases, the rest are entries in the alias record.
1689 function _drush_sitealias_set_record_element(&$alias_record, $key, $value) {
1690 if ((substr($key,0,1) == '%') || (substr($key,0,1) == '!')) {
1691 $alias_record['path-aliases'][$key] = $value;
1693 elseif (!empty($key)) {
1694 $alias_record[$key] = $value;
1699 * Looks up the specified alias record and calls through to
1700 * drush_sitealias_set_alias_context, below.
1703 * The name of the alias record
1705 * The prefix value to afix to the beginning of every
1708 * TRUE is an alias was found and processed.
1710 function _drush_sitealias_set_context_by_name($alias, $prefix = '') {
1712 $site_alias_settings = drush_sitealias_get_record($alias);
1713 if (!empty($site_alias_settings)) {
1714 drush_sitealias_set_alias_context($site_alias_settings, $prefix);
1715 drush_sitealias_cache_alias_by_path($site_alias_settings);
1716 if (empty($prefix)) {
1718 // Create an alias '@self'
1719 // Allow 'uri' from the commandline to override
1720 $drush_uri = drush_get_option(array('uri', 'l'), FALSE);
1722 $site_alias_settings['uri'] = $drush_uri;
1725 _drush_sitealias_cache_alias('@self', $site_alias_settings);
1726 // Change the selected site to match the new --root and --uri, if any were set.
1727 _drush_preflight_root_uri();
1729 return $site_alias_settings;
1736 * Given an alias record, overwrite its values with options
1737 * from the command line and other drush contexts as specified
1738 * by the provided prefix. For example, if the prefix is 'source-',
1739 * then any option 'source-foo' will set the value 'foo' in the
1742 function drush_sitealias_overlay_options($site_alias_record, $prefix) {
1743 return array_merge($site_alias_record, drush_get_merged_prefixed_options($prefix));
1747 * First return an option set via drush_sitealias_overlay_options, if
1748 * any, then fall back on "%" . $option from the path aliases.
1750 function drush_sitealias_get_path_option($site_alias_record, $option, $default = NULL) {
1751 if (isset($site_alias_record) && array_key_exists($option, $site_alias_record)) {
1752 return $site_alias_record[$option];
1754 if (isset($site_alias_record) && array_key_exists('path-aliases', $site_alias_record) && array_key_exists("%$option", $site_alias_record['path-aliases'])) {
1755 return $site_alias_record['path-aliases']["%$option"];
1758 return drush_get_option($option, $default);
1763 * Given a site alias record, copy selected fields from it
1764 * into the drush 'alias' context. The 'alias' context has
1765 * lower precedence than the 'cli' context, so values
1766 * set by an alias record can be overridden by command-line
1769 * @param site_alias_settings
1772 * The prefix value to affix to the beginning of every
1773 * key set. For example, if this function is called once with
1774 * 'source-' and again with 'destination-' prefixes, then the
1775 * source database records will be stored in 'source-databases',
1776 * and the destination database records will be in
1777 * 'destination-databases'.
1779 function drush_sitealias_set_alias_context($site_alias_settings, $prefix = '') {
1780 $options = drush_get_context('alias');
1782 // There are some items that we should just skip
1783 $skip_list = drush_get_special_keys();
1784 // If 'php-options' are set in the alias, then we will force drush
1785 // to redispatch via the remote dispatch mechanism even if the target is localhost.
1786 if ((array_key_exists('php-options', $site_alias_settings) || array_key_exists('php', $site_alias_settings)) && !drush_get_context('DRUSH_BACKEND', FALSE)) {
1787 if (!array_key_exists('remote-host', $site_alias_settings)) {
1788 $site_alias_settings['remote-host'] = 'localhost';
1791 // If 'php-options' are not set in the alias, then skip 'remote-host'
1792 // and 'remote-user' if 'remote-host' is actually the local machine.
1793 // This prevents drush from using the remote dispatch mechanism (the command
1794 // is just run directly on the local machine, bootstrapping to the specified alias)
1795 elseif (array_key_exists('remote-host', $site_alias_settings) && drush_is_local_host($site_alias_settings['remote-host'])) {
1796 $skip_list[] = 'remote-host';
1797 $skip_list[] = 'remote-user';
1799 // If prefix is set, then copy from the 'prefix-' version
1800 // of the drush special keys ('command-specific', 'path-aliases')
1801 // into the ordinary version. This will allow us to set
1802 // 'source-command-specific' options that will only apply when
1803 // the alias is used as the source option for rsync or sql-sync.
1804 if (!empty($prefix)) {
1805 $special_contexts = drush_get_special_keys();
1806 foreach ($special_contexts as $option_name) {
1807 if (array_key_exists($prefix . $option_name, $site_alias_settings)) {
1808 $site_alias_settings[$option_name] = array_key_exists($option_name, $site_alias_settings) ? array_merge($site_alias_settings[$option_name], $site_alias_settings[$prefix . $option_name]) : $site_alias_settings[$prefix . $option_name];
1812 // Transfer all options from the site alias to the drush options
1813 // in the 'alias' context.
1814 foreach ($site_alias_settings as $key => $value) {
1815 // Special handling for path aliases:
1816 if ($key == "path-aliases") {
1817 $path_aliases = $value;
1818 foreach (array('%drush-script', '%dump', '%dump-dir', '%include') as $path_key) {
1819 if (array_key_exists($path_key, $path_aliases)) {
1820 // Evaluate the path value, and substitute any path references found.
1821 // ex: '%dump-dir' => '%root/dumps' will store sql-dumps in the folder
1822 // 'dumps' in the Drupal root folder for the site.
1823 $evaluated_path = str_replace(array_keys($path_aliases), array_values($path_aliases), $path_aliases[$path_key]);
1824 $options[$prefix . substr($path_key, 1)] = $evaluated_path;
1828 // Special handling for command-specific
1829 elseif ($key == "command-specific") {
1830 $options[$key] = $value;
1832 elseif (!in_array($key, $skip_list)) {
1833 $options[$prefix . $key] = $value;
1836 drush_set_config_options('alias', $options);
1840 * Call prior to drush_sitealias_evaluate_path to insure
1841 * that any site-specific aliases associated with any
1842 * local site in $path are defined.
1844 function _drush_sitealias_preflight_path($path) {
1846 // Parse site aliases if there is a colon in the path
1849 // machine.domain.com:/path
1851 // Note that paths in the form "c:/path" are converted to
1852 // "/cygdrive/c/path" later; we do not want them to confuse
1853 // us here, so we skip paths that start with a single character
1854 // before the colon if we are running on Windows. Single-character
1855 // machine names are allowed in Linux only.
1856 $colon_pos = strpos($path, ':');
1857 if ($colon_pos > (drush_is_windows("LOCAL") ? 1 : 0)) {
1858 $alias = substr($path, 0, $colon_pos);
1859 $path = substr($path, $colon_pos + 1);
1860 $site_alias_settings = drush_sitealias_get_record($alias);
1861 if (empty($site_alias_settings) && (substr($path,0,1) == '@')) {
1868 // if the path is a site alias or a local site...
1869 $site_alias_settings = drush_sitealias_get_record($path);
1870 if (empty($site_alias_settings) && (substr($path,0,1) == '@')) {
1873 if (!empty($site_alias_settings) || drush_is_local_host($path)) {
1878 return array('alias' => $alias, 'path' => $path, 'machine' => $machine);
1882 * Given a properly-escaped options string, replace any occurance of
1883 * %files and so on embedded inside it with its corresponding path.
1885 function drush_sitealias_evaluate_paths_in_options($option_string) {
1886 $path_aliases = _core_path_aliases();
1887 return str_replace(array_keys($path_aliases), array_values($path_aliases), $option_string);
1891 * Evaluate a path from its shorthand form to a literal path
1894 * A path is "machine:/path" or "machine:path" or "/path" or "path".
1895 * 'machine' might instead be an alias record, or the name
1896 * of a site in the 'sites' folder. 'path' might be (or contain)
1897 * '%root' or some other path alias. This function will examine
1898 * all components of the path and evaluate them as necessary to
1899 * come to the final path.
1902 * The path to evaluate
1903 * @param additional_options
1904 * An array of options that overrides whatever was passed in on
1905 * the command line (like the 'process' context, but only for
1906 * the scope of this one call).
1908 * If TRUE, force an error if the provided path points to a remote
1911 * This should be the local system os, unless evaluate path is
1912 * being called for rsync, in which case it should be "CWRSYNC"
1913 * if cwrsync is being used, or "rsync" to automatically select
1914 * between "LOCAL" and "CWRSYNC" based on the platform.
1916 * The site record for the machine specified in the path, if any,
1917 * with the path to pass to rsync (including the machine specifier)
1918 * in the 'evaluated-path' item.
1920 function drush_sitealias_evaluate_path($path, &$additional_options, $local_only = FALSE, $os = NULL, $command_specific_prefix = '') {
1921 $site_alias_settings = array();
1922 $path_aliases = array();
1925 $preflight = _drush_sitealias_preflight_path($path);
1926 if (!isset($preflight)) {
1930 $alias = $preflight['alias'];
1931 $path = $preflight['path'];
1932 $machine = $preflight['machine'];
1934 if (isset($alias)) {
1935 // Note that the alias settings may have an 'os' component, but we do
1936 // not want to use it here. The paths passed to rsync should always be
1937 // escaped per the LOCAL rules, without regard to the remote platform type.
1938 $site_alias_settings = drush_sitealias_get_record($alias);
1939 if (!empty($command_specific_prefix)) {
1940 drush_command_set_command_specific_options($command_specific_prefix);
1941 drush_sitealias_command_default_options($site_alias_settings, $command_specific_prefix);
1945 if (!empty($site_alias_settings)) {
1946 if ($local_only && array_key_exists('remote-host', $site_alias_settings)) {
1947 return drush_set_error('DRUSH_REMOTE_SITE_IN_LOCAL_CONTEXT', dt("A remote site alias was used in a context where only a local alias is appropriate."));
1950 // Apply any options from this alias that might affect our rsync
1951 drush_sitealias_set_alias_context($site_alias_settings);
1953 // Use 'remote-host' from settings if available; otherwise site is local
1954 if (drush_sitealias_is_remote_site($site_alias_settings)) {
1955 $machine = drush_remote_host($site_alias_settings);
1962 // Strip the machine portion of the path if the
1963 // alias points to the local machine.
1964 if (drush_is_local_host($machine)) {
1968 $machine = "$remote_user$machine";
1972 // TOD: The code below is a little rube-goldberg-ish, and needs to be
1973 // reworked. core-rsync will call this function twice: once to
1974 // evaluate the destination, and then again to evaluate the source. Things
1975 // get odd with --exclude-paths, especially in conjunction with command-specific
1976 // and the --exclude-files option. @see testCommandSpecific()
1978 // If the --exclude-other-sites option is specified, then
1979 // convert that into --include-paths='%site' and --exclude-sites.
1980 if (drush_get_option_override($additional_options, 'exclude-other-sites', FALSE) && !drush_get_context('exclude-other-sites-processed', FALSE)) {
1981 $include_path_option = drush_get_option_override($additional_options, 'include-paths', '');
1982 $additional_options['include-paths'] = '%site';
1983 if (!empty($include_path_option)) {
1984 // We use PATH_SEPARATOR here because we are later going to explicitly explode() this variable using PATH_SEPARATOR.
1985 $additional_options['include-paths'] .= PATH_SEPARATOR . $include_path_option;
1987 $additional_options['exclude-sites'] = TRUE;
1988 drush_set_context('exclude-other-sites-processed', TRUE);
1991 unset($additional_options['include-paths']);
1993 // If the --exclude-files option is specified, then
1994 // convert that into --exclude-paths='%files'.
1995 if (drush_get_option_override($additional_options, 'exclude-files', FALSE) && !drush_get_option_override($additional_options, 'exclude-files-processed', FALSE, 'process')) {
1996 $exclude_path_option = drush_get_option_override($additional_options, 'exclude-paths', '');
1997 $additional_options['exclude-paths'] = '%files';
1998 if (!empty($exclude_path_option)) {
1999 // We use PATH_SEPARATOR here because we are later going to explicitly explode() this variable using PATH_SEPARATOR.
2000 $additional_options['exclude-paths'] .= PATH_SEPARATOR . $exclude_path_option;
2002 $additional_options['exclude-files-processed'] = TRUE;
2005 unset($additional_options['exclude-paths']);
2008 // If there was no site specification given, and the
2009 // machine is local, then try to look
2010 // up an alias record for the default drush site.
2011 if (empty($site_alias_settings) && empty($machine)) {
2012 $drush_uri = drush_get_context('DRUSH_SELECTED_URI', 'default');
2013 $site_alias_settings = drush_sitealias_get_record($drush_uri);
2016 // Always add transient defaults
2017 _drush_sitealias_add_transient_defaults($site_alias_settings);
2019 // The $resolve_path variable is used by drush_sitealias_resolve_path_references
2020 // to test to see if there are any path references such as %site or %files
2021 // in it, so that resolution is only done if the path alias is referenced.
2022 // Therefore, we can concatenate without worrying too much about the structure of
2023 // this variable's contents.
2024 $include_path = drush_get_option_override($additional_options, 'include-paths', '');
2025 $exclude_path = drush_get_option_override($additional_options, 'exclude-paths', '');
2026 if (is_array($include_path)) {
2027 $include_path = implode('/', $include_path);
2029 if (is_array($exclude_path)) {
2030 $include_path = implode('/', $exclude_path);
2032 $resolve_path = "$path/$include_path/$exclude_path";
2033 // Resolve path aliases such as %files, if any exist in the path
2034 if (!empty($resolve_path)) {
2035 drush_sitealias_resolve_path_references($site_alias_settings, $resolve_path);
2038 if (array_key_exists('path-aliases', $site_alias_settings)) {
2039 $path_aliases = $site_alias_settings['path-aliases'];
2042 // Get the 'root' setting from the alias; if it does not
2043 // exist, then get the root from the bootstrapped site.
2044 if (array_key_exists('root', $site_alias_settings)) {
2045 $drupal_root = $site_alias_settings['root'];
2047 elseif (!drush_sitealias_is_remote_site($site_alias_settings)) {
2048 drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_SITE);
2049 $drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT');
2051 if (empty($drupal_root)) {
2055 // Add a slash to the end of the drupal root, as below.
2056 $drupal_root = drush_trim_path($drupal_root) . "/";
2058 $full_path_aliases = $path_aliases;
2059 foreach ($full_path_aliases as $key => $value) {
2060 // Expand all relative path aliases to be based off of the Drupal root
2061 if (!drush_is_absolute_path($value, "LOCAL") && ($key != '%root')) {
2062 $full_path_aliases[$key] = $drupal_root . $value;
2064 // We do not want slashes on the end of our path aliases.
2065 $full_path_aliases[$key] = drush_trim_path($full_path_aliases[$key]);
2068 // Fill in path aliases in the path, the include path and the exclude path.
2069 $path = str_replace(array_keys($full_path_aliases), array_values($full_path_aliases), $path);
2070 if (!empty($include_path)) {
2071 drush_set_option('include-paths', str_replace(array_keys($path_aliases), array_values($path_aliases), $include_path));
2073 if (!empty($exclude_path)) {
2074 drush_set_option('exclude-paths', str_replace(array_keys($path_aliases), array_values($path_aliases), $exclude_path));
2076 // Next make the rsync path, which includes the machine
2077 // and path components together.
2078 // First make empty paths or relative paths start from the drupal root.
2079 if (empty($path) || (!drush_is_absolute_path($path, "LOCAL"))) {
2080 $path = $drupal_root . $path;
2082 // When calculating a path for use with rsync, we must correct
2083 // absolute paths in the form c:\path when cwrsync is in use.
2084 $path = drush_correct_absolute_path_for_exec($path, $os);
2086 // If there is a $machine component, to the path, then
2087 // add it to the beginning
2088 $evaluated_path = drush_escapeshellarg($path, $os);
2089 if (!empty($machine)) {
2090 $evaluated_path = $machine . ':' . $evaluated_path;
2094 // Add our result paths:
2096 // evaluated-path: machine:/path
2097 // server-component: machine
2098 // path-component: :/path
2100 // user-path: path (as specified in input parameter)
2102 $site_alias_settings['evaluated-path'] = $evaluated_path;
2103 if (!empty($machine)) {
2104 $site_alias_settings['server-component'] = $machine;
2106 $site_alias_settings['path-component'] = (!empty($path) ? ':' . $path : '');
2107 $site_alias_settings['path'] = $path;
2108 $site_alias_settings['user-path'] = $preflight['path'];
2110 return $site_alias_settings;
2114 * Option keys used for site selection.
2116 function drush_sitealias_site_selection_keys() {
2117 return array('remote-host', 'remote-user', 'ssh-options', '#name', 'os');
2121 function sitealias_find_local_drupal_root($site_list) {
2122 $drupal_root = NULL;
2124 foreach ($site_list as $site) {
2125 if (($drupal_root == NULL) && (array_key_exists('root', $site) && !array_key_exists('remote-host', $site))) {
2126 $drupal_root = $site['root'];
2130 return $drupal_root;
2135 * Helper function to obtain the keys' names that need special handling in certain
2138 * A non-associative array containing the needed keys' names.
2140 function drush_get_special_keys() {
2141 $special_keys = array(
2145 return $special_keys;
2149 * Read the tmp file where the persistent site setting is stored.
2152 * A valid site specification.
2154 function drush_sitealias_site_get() {
2155 if (($filename = drush_sitealias_get_envar_filename()) && file_exists($filename)) {
2156 $site = file_get_contents($filename);
2165 * Un-set the currently use'd site alias.
2167 function drush_sitealias_site_clear() {
2168 if ($filename = drush_sitealias_get_envar_filename()) {
2169 return drush_delete_dir($filename);
2175 * Returns the filename for the file that stores the DRUPAL_SITE variable.
2177 * @param string $filename_prefix
2178 * An arbitrary string to prefix the filename with.
2180 * @return string|false
2181 * Returns the full path to temp file if possible, or FALSE if not.
2183 function drush_sitealias_get_envar_filename($filename_prefix = 'drush-drupal-site-') {
2184 $shell_pid = getenv('DRUSH_SHELL_PID');
2185 if (!$shell_pid && function_exists('posix_getppid')) {
2186 $shell_pid = posix_getppid();
2192 $tmp = getenv('TMPDIR') ? getenv('TMPDIR') : '/tmp';
2193 $username = drush_get_username();
2195 return "{$tmp}/drush-env-{$username}/{$filename_prefix}" . $shell_pid;
2199 * Cache the specified alias in the alias path cache. The
2200 * alias path cache creates a lookup from the site folder
2201 * (/path/to/drupal/sites/default) to the provided alias record.
2203 * Only the name of the alias and the path to the file it
2204 * is stored in is cached; when it is retrieved, it is
2205 * loaded directly from the correct file.
2207 function drush_sitealias_cache_alias_by_path($alias_record) {
2208 if (!isset($alias_record['remote-host']) && isset($alias_record['root']) && isset($alias_record['uri']) && isset($alias_record['#name']) && isset($alias_record['#file'])) {
2209 $path = drush_sitealias_local_site_path($alias_record);
2211 $cid = drush_get_cid('alias-path-', array(), array($path));
2212 $alias_path_data = array(
2213 '#name' => $alias_record['#name'],
2214 '#file' => $alias_record['#file'],
2216 drush_cache_set($cid, $alias_path_data);
2222 * Look for a defined alias that points to the specified
2223 * site directory. The cache is tested first; if nothing
2224 * is cached, then an exhaustive search is done for the
2225 * specified site. If the exhaustive search returns a
2226 * match, then it is cached.
2229 * /path/to/drupal/sites/default
2231 * An alias record for the provided path
2233 function drush_sitealias_lookup_alias_by_path($path, $allow_best_match=FALSE) {
2234 $result = drush_sitealias_quick_lookup_cached_alias_by_path($path);
2235 $fallback = array();
2236 if (empty($result)) {
2237 $aliases = _drush_sitealias_find_and_load_all_aliases();
2238 foreach ($aliases as $name => $alias_record) {
2239 if (!isset($alias_record['remote-host']) && isset($alias_record['root']) && isset($alias_record['uri']) && isset($alias_record['#name']) && isset($alias_record['#file'])) {
2240 if ($path == drush_sitealias_local_site_path($alias_record)) {
2241 $result = $alias_record;
2244 if (substr($path, 0, strlen($alias_record['root'])) == $alias_record['root']) {
2245 $fallback = $alias_record;
2250 if (empty($result) && $allow_best_match) {
2251 $result = $fallback;
2253 if (!empty($result)) {
2254 _drush_sitealias_add_inherited_values_to_record($result);
2255 drush_sitealias_cache_alias_by_path($result);
2261 * Look for a cached alias that points to the specified
2262 * site directory. Nothing is returned if there is no
2263 * matching cached alias.
2266 * /path/to/drupal/sites/default
2268 * An alias record for the provided path
2270 function drush_sitealias_quick_lookup_cached_alias_by_path($path) {
2271 $alias_record = array();
2272 $cid = drush_get_cid('alias-path-', array(), array($path));
2273 $alias_path_cache = drush_cache_get($cid);
2274 if (isset($alias_path_cache->data)) {
2275 $alias_name = $alias_path_cache->data['#name'];
2276 $alias_file = $alias_path_cache->data['#file'];
2278 $alias_record = _drush_sitealias_find_and_load_alias_from_file($alias_name, array($alias_file));
2279 _drush_sitealias_add_inherited_values_to_record($alias_record);
2280 $alias_record['#name'] = $alias_name;
2282 return $alias_record;
2286 * Return the site root, if there is one in the record.
2288 function drush_sitealias_get_root($alias_record) {
2289 return array_key_exists('root', $alias_record) ? $alias_record['root'] : NULL;
2293 * Decide on which side to run a core-rsync.
2296 * @param $destination
2297 * @param $runner Where to run the rsync operation: 'destination', 'source',
2298 * 'auto' ('destination' if both are remote, otherwise '@self') or FALSE (@self)
2301 function drush_get_runner($source, $destination, $runner = FALSE) {
2302 if (is_string($source)) {
2303 $source = drush_sitealias_get_record($site);
2305 if (is_string($destination)) {
2306 $destination = drush_sitealias_get_record($destination);
2309 // If both sites are remote, and --runner=auto, then we'll use the destination site.
2310 if (drush_sitealias_is_remote_site($source) && drush_sitealias_is_remote_site($destination)) {
2311 if ($runner == 'auto') {
2312 $runner = 'destination';
2316 // If the user explicitly requests a remote site, then return the selected one.
2317 if ($runner == 'destination') {
2318 return "@" . $destination['#name'];
2320 if ($runner == 'source') {
2321 return "@" . $source['#name'];
2324 // Default to running rsync locally. When in doubt, local is best, because
2325 // we can always resolve aliases here.