1670 lines
61 KiB
PHP
1670 lines
61 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @file
|
|
* The site alias API.
|
|
*
|
|
* Run commands on remote server(s).
|
|
* @see example.aliases.drushrc.php
|
|
* @see http://drupal.org/node/670460
|
|
*/
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* @return boolean
|
|
* TRUE if a site alias was found and processed.
|
|
*/
|
|
function drush_sitealias_check_arg() {
|
|
$args = drush_get_arguments();
|
|
|
|
// Test to see if the first arg is a site specification
|
|
if (_drush_sitealias_set_context_by_name($args[0])) {
|
|
array_shift($args);
|
|
// We only need to expand the site specification
|
|
// once, then we are done.
|
|
drush_set_arguments($args);
|
|
return TRUE;
|
|
}
|
|
|
|
// Return false to indicate that no site alias was specified.
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Given a list of alias records, shorten the name used if possible
|
|
*/
|
|
function drush_sitealias_simplify_names($site_list) {
|
|
$result = array();
|
|
foreach ($site_list as $original_name => $alias_record) {
|
|
$adjusted_name = $alias_record['#name'];
|
|
$hashpos = strpos($original_name, '#');
|
|
if ($hashpos !== FALSE) {
|
|
$adjusted_name = substr($original_name, $hashpos);
|
|
if (array_key_exists('remote-host', $alias_record)) {
|
|
$adjusted_name = $alias_record['remote-host'] . $adjusted_name;
|
|
}
|
|
}
|
|
$result[$adjusted_name] = $alias_record;
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* @param $site_specifications
|
|
* An array of site specificatins. @see drush_sitealias_get_record
|
|
* @return
|
|
* An array of alias records
|
|
*/
|
|
function drush_sitealias_resolve_sitespecs($site_specifications) {
|
|
$result_list = array();
|
|
if (!empty($site_specifications)) {
|
|
foreach ($site_specifications as $site) {
|
|
$alias_record = drush_sitealias_get_record($site);
|
|
$result_list = array_merge($result_list, drush_sitealias_resolve_sitelist($alias_record));
|
|
}
|
|
}
|
|
return $result_list;
|
|
}
|
|
|
|
/**
|
|
* Get a site alias record given an alias name or site specification.
|
|
*
|
|
* If it is the name of a site alias, return the alias record from
|
|
* the site aliases array.
|
|
*
|
|
* If it is the name of a folder in the 'sites' folder, construct
|
|
* an alias record from values stored in settings.php.
|
|
*
|
|
* If it is a site specification, construct an alias record from the
|
|
* values in the specification.
|
|
*
|
|
* Site specifications come in several forms:
|
|
*
|
|
* 1.) /path/to/drupal#sitename
|
|
* 2.) user@server/path/to/drupal#sitename
|
|
* 3.) user@server/path/to/drupal (sitename == server)
|
|
* 4.) user@server#sitename (only if $option['r'] set in some drushrc file on server)
|
|
* 5.) #sitename (only if $option['r'] already set, and 'sitename' is a folder in $option['r']/sites)
|
|
* 6.) sitename (only if $option['r'] already set, and 'sitename' is a folder in $option['r']/sites)
|
|
*
|
|
* Note that in the case of the first four forms, it is also possible
|
|
* to add additional site variable to the specification using uri query
|
|
* syntax. For example:
|
|
*
|
|
* user@server/path/to/drupal?db-url=...#sitename
|
|
*
|
|
* @param alias
|
|
* An alias name or site specification
|
|
* @return array
|
|
* An alias record, or empty if none found.
|
|
*/
|
|
function drush_sitealias_get_record($alias) {
|
|
// Check to see if the alias contains commas. If it does, then
|
|
// we will go ahead and make a site list record
|
|
$alias_record = array();
|
|
if (strpos($alias, ',') !== false) {
|
|
// TODO: If the site list contains any site lists, or site
|
|
// search paths, then we should expand those and merge them
|
|
// into this list longhand.
|
|
$alias_record['site-list'] = explode(',', $alias);
|
|
}
|
|
else {
|
|
$alias_record = _drush_sitealias_get_record($alias);
|
|
}
|
|
if (!empty($alias_record)) {
|
|
if (!array_key_exists('#name', $alias_record)) {
|
|
$alias_record['#name'] = drush_sitealias_uri_to_site_dir($alias);
|
|
}
|
|
|
|
// Handle nested alias definitions and command-specific options.
|
|
drush_set_config_special_contexts($alias_record);
|
|
}
|
|
return $alias_record;
|
|
}
|
|
|
|
/**
|
|
* This is a continuation of drush_sitealias_get_record, above. It is
|
|
* not intended to be called directly.
|
|
*/
|
|
function _drush_sitealias_get_record($alias, $alias_context = NULL) {
|
|
// Before we do anything else, load $alias if it needs to be loaded
|
|
_drush_sitealias_load_alias($alias, $alias_context);
|
|
|
|
// Check to see if the provided parameter is in fact a defined alias.
|
|
$all_site_aliases =& drush_get_context('site-aliases');
|
|
if (array_key_exists($alias, $all_site_aliases)) {
|
|
$alias_record = $all_site_aliases[$alias];
|
|
}
|
|
// If the parameter is not an alias, then it is some form of
|
|
// site specification (or it is nothing at all)
|
|
else {
|
|
if (isset($alias)) {
|
|
// Cases 1.) - 4.):
|
|
// We will check for a site specification if the alias has at least
|
|
// two characters from the set '@', '/', '#'.
|
|
if ((strpos($alias, '@') === FALSE ? 0 : 1) + (strpos($alias, '/') === FALSE ? 0 : 1) + (strpos($alias, '#') === FALSE ? 0 : 1) >= 2) {
|
|
if ((substr($alias,0,7) != 'http://') && (substr($alias,0,1) != '/')) {
|
|
// Add on a scheme so that "user:pass@server" will always parse correctly
|
|
$parsed = parse_url('http://' . $alias);
|
|
}
|
|
else {
|
|
$parsed = parse_url($alias);
|
|
}
|
|
// Copy various parts of the parsed URL into the appropriate records of the alias record
|
|
foreach (array('user' => 'remote-user', 'pass' => 'remote-pass', 'host' => 'remote-host', 'fragment' => 'uri', 'path' => 'root') as $url_key => $option_key) {
|
|
if (array_key_exists($url_key, $parsed)) {
|
|
_drush_sitealias_set_record_element($alias_record, $option_key, $parsed[$url_key]);
|
|
}
|
|
}
|
|
// If the site specification has a query, also set the query items
|
|
// in the alias record. This allows passing db_url as part of the
|
|
// site specification, for example.
|
|
if (array_key_exists('query', $parsed)) {
|
|
foreach (explode('&', $parsed['query']) as $query_arg) {
|
|
$query_components = explode('=', $query_arg);
|
|
_drush_sitealias_set_record_element($alias_record, urldecode($query_components[0]), urldecode($query_components[1]));
|
|
}
|
|
}
|
|
|
|
// Case 3.): If the URL contains a 'host' portion but no fragment, then set the uri to the host
|
|
// Note: We presume that 'server' is the best default for case 3; without this code, the default would
|
|
// be whatever is set in $options['l'] on the target machine's drushrc.php settings file.
|
|
if (array_key_exists('host', $parsed) && !array_key_exists('fragment', $parsed)) {
|
|
$alias_record['uri'] = $parsed['host'];
|
|
}
|
|
|
|
// Special checking: relative aliases embedded in a path
|
|
$relative_alias_pos = strpos($alias_record['root'], '/@');
|
|
if ($relative_alias_pos !== FALSE) {
|
|
// Special checking: /path/@sites
|
|
$base = substr($alias_record['root'], 0, $relative_alias_pos);
|
|
$relative_alias = substr($alias_record['root'], $relative_alias_pos + 1);
|
|
if (drush_valid_drupal_root($base) || ($relative_alias == '@sites')) {
|
|
drush_sitealias_create_sites_alias($base);
|
|
$alias_record = drush_sitealias_get_record($relative_alias);
|
|
}
|
|
else {
|
|
$alias_record = array();
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// Case 5.) and 6.):
|
|
// If the alias is the name of a folder in the 'sites' directory,
|
|
// then use it as a local site specification.
|
|
$alias_record = _drush_sitealias_find_record_for_local_site($alias);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!empty($alias_record)) {
|
|
// Load the drush config file if there is one associated with this alias
|
|
if (!isset($alias_record['remote']) && !isset($alias_record['loaded-config'])) {
|
|
if (array_key_exists('root', $alias_record)) {
|
|
drush_sitealias_add_to_alias_path($alias_record['root']);
|
|
}
|
|
$alias_site_dir = drush_sitealias_local_site_path($alias_record);
|
|
|
|
if (isset($alias_site_dir)) {
|
|
// Add the sites folder of this site to the alias search path list
|
|
drush_sitealias_add_to_alias_path($alias_site_dir);
|
|
|
|
if (!isset($alias_record['config'])) {
|
|
$alias_record['config'] = realpath($alias_site_dir . '/drushrc.php');
|
|
}
|
|
}
|
|
if (isset($alias_record['config']) && file_exists($alias_record['config'])) {
|
|
drush_load_config_file('site', $alias_record['config']);
|
|
$alias_record['loaded-config'] = TRUE;
|
|
}
|
|
unset($alias_record['config']);
|
|
}
|
|
|
|
// Add the static defaults
|
|
_drush_sitealias_add_static_defaults($alias_record);
|
|
|
|
// Cache the result with all of its calculated values
|
|
$all_site_aliases[$alias] = $alias_record;
|
|
}
|
|
|
|
return $alias_record;
|
|
}
|
|
|
|
/**
|
|
* Add a path to the array of paths where alias files are searched for.
|
|
*
|
|
* @param $add_path
|
|
* A path to add to the search path (or NULL to not add any).
|
|
* Once added, the new path will remain available until drush
|
|
* exits.
|
|
* @return
|
|
* An array of paths containing all values added so far
|
|
*/
|
|
function drush_sitealias_add_to_alias_path($add_path) {
|
|
static $site_paths = array();
|
|
|
|
if ($add_path != NULL) {
|
|
if (!is_array($add_path)) {
|
|
$add_path = explode(':', $add_path);
|
|
}
|
|
$site_paths = array_unique(array_merge($site_paths, $add_path));
|
|
}
|
|
return $site_paths;
|
|
}
|
|
|
|
/**
|
|
* Return the array of paths where alias files are searched for.
|
|
*
|
|
* @param $alias_path_context
|
|
* If the alias being looked up is part of a relative alias,
|
|
* the alias path context specifies the context of the primary
|
|
* alias the new alias is rooted from. Alias files stored in
|
|
* the sites folder of this context, or inside the context itself
|
|
* takes priority over any other search path that might define
|
|
* a similarly-named alias. In this way, multiple sites can define
|
|
* a '@peer' alias.
|
|
* @return
|
|
* An array of paths
|
|
*/
|
|
function drush_sitealias_alias_path($alias_path_context = NULL) {
|
|
if (isset($alias_path_context)) {
|
|
return array(drush_sitealias_local_site_path($alias_path_context));
|
|
}
|
|
else {
|
|
// We get the current list of site paths by adding NULL
|
|
// (nothing) to the path list, which is a no-op
|
|
$site_paths = drush_sitealias_add_to_alias_path(NULL);
|
|
|
|
// If the user defined the root of a drupal site, then also
|
|
// look for alias files there.
|
|
$drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT');
|
|
if (empty($drupal_root)) {
|
|
$drupal_root = drush_get_option(array('root', 'r'), NULL);
|
|
}
|
|
if (isset($drupal_root)) {
|
|
$site_paths[] = $drupal_root;
|
|
}
|
|
$alias_path = (array) drush_get_option('alias-path', array());
|
|
if (empty($alias_path)) {
|
|
$alias_path[] = drush_get_context('ETC_PREFIX', '') . '/etc/drush';
|
|
$alias_path[] = dirname(__FILE__) . '/..';
|
|
$alias_path[] = dirname(__FILE__) . '/../aliases';
|
|
if(!is_null(drush_server_home())) {
|
|
$alias_path[] = drush_server_home() . '/.drush';
|
|
}
|
|
}
|
|
|
|
return array_unique(array_merge($alias_path, $site_paths));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the full path to the site directory of the
|
|
* given alias record.
|
|
*
|
|
* @param $alias_record
|
|
* The alias record
|
|
* @return
|
|
* The path to the site directory of the associated
|
|
* alias record, or NULL if the record is not a local site.
|
|
*/
|
|
function drush_sitealias_local_site_path($alias_record) {
|
|
$result = NULL;
|
|
|
|
if (isset($alias_record['uri']) && isset($alias_record['root']) && !isset($alias_record['remote-host'])) {
|
|
$result = realpath($alias_record['root'] . '/sites/' . drush_sitealias_uri_to_site_dir($alias_record['uri']));
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* @param $alias
|
|
* The name of the alias to load in ordinary form ('@name')
|
|
* @param $alias_path_context
|
|
* When looking up a relative alias, the alias path context is
|
|
* the primary alias that we will start our search from.
|
|
*/
|
|
function _drush_sitealias_load_alias($alias, $alias_path_context = NULL) {
|
|
$all_site_aliases = drush_get_context('site-aliases');
|
|
$result = array();
|
|
|
|
// Check to see if this is a relative alias ('@site/@peer')
|
|
$relative_alias_pos = strpos($alias, '/@');
|
|
if ($relative_alias_pos !== false) {
|
|
$primary_alias = substr($alias,0,$relative_alias_pos);
|
|
$relative_alias = substr($alias,$relative_alias_pos + 1);
|
|
$primary_record = drush_sitealias_get_record($primary_alias);
|
|
_drush_sitealias_find_and_load_alias(substr($relative_alias,1), $primary_record);
|
|
$result = drush_sitealias_get_record($relative_alias);
|
|
if (!empty($result)) {
|
|
if (array_key_exists('inherited', $result)) {
|
|
$result = array_merge($primary_record, $result);
|
|
}
|
|
$result['#name'] = $relative_alias;
|
|
_drush_sitealias_cache_alias(substr($alias, 1), $result);
|
|
}
|
|
}
|
|
else {
|
|
// Only aliases--those named entities that begin with '@'--can be loaded this way.
|
|
// We also skip any alias that has already been loaded.
|
|
if ((substr($alias,0,1) == '@') && !array_key_exists($alias,$all_site_aliases)) {
|
|
drush_log(dt('Load alias !alias', array('!alias' => $alias)));
|
|
$aliasname = substr($alias,1);
|
|
$result = _drush_sitealias_find_and_load_alias($aliasname, $alias_path_context);
|
|
if (!empty($result)) {
|
|
$alias_options = array('site-aliases' => array($aliasname => $result));
|
|
_drush_sitealias_add_inherited_values($alias_options['site-aliases']);
|
|
drush_set_config_special_contexts($alias_options);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Load every alias file that can be found anywhere in the
|
|
* alias search path.
|
|
*/
|
|
function drush_sitealias_load_all($resolve_parent = TRUE) {
|
|
$result = _drush_sitealias_find_and_load_alias(NULL);
|
|
if (!empty($result) && ($resolve_parent == TRUE)) {
|
|
// If any aliases were returned, then check for
|
|
// inheritance and then store the aliases into the
|
|
// alias cache
|
|
_drush_sitealias_add_inherited_values($result);
|
|
$alias_options = array('site-aliases' => $result);
|
|
drush_set_config_special_contexts($alias_options);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Worker function called by _drush_sitealias_load_alias and
|
|
* drush_sitealias_load_all. Traverses the alias search path
|
|
* and finds the specified alias record.
|
|
*
|
|
* @param $aliasname
|
|
* The name of the alias without the leading '@' (i.e. '#name')
|
|
* or NULL to load every alias found in every alias file.
|
|
* @param $alias_path_context
|
|
* When looking up a relative alias, the alias path context is
|
|
* the primary alias that we will start our search from.
|
|
* @return
|
|
* An empty array if nothing was loaded. If $aliasname is
|
|
* not null, then the array returned is the alias record for
|
|
* $aliasname. If $aliasname is NULL, then the array returned
|
|
* is a $kay => $value pair of alias names and alias records
|
|
* loaded.
|
|
*/
|
|
function _drush_sitealias_find_and_load_alias($aliasname, $alias_path_context = NULL) {
|
|
$result = array();
|
|
|
|
// Special checking for '@sites' alias
|
|
if ($aliasname == 'sites') {
|
|
$drupal_root = NULL;
|
|
if ($alias_path_context != null) {
|
|
if (array_key_exists('root', $alias_path_context) && !array_key_exists('remote-host', $alias_path_context)) {
|
|
$drupal_root = $alias_path_context['root'];
|
|
}
|
|
}
|
|
else {
|
|
$drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT');
|
|
if ($drupal_root == NULL) {
|
|
$drupal_root = drush_get_option(array('r', 'root'), drush_locate_root());
|
|
}
|
|
}
|
|
if (isset($drupal_root) && !is_array($drupal_root)) {
|
|
drush_sitealias_create_sites_alias($drupal_root);
|
|
}
|
|
}
|
|
|
|
// The alias path is a list of folders to search for alias settings files
|
|
$alias_path = drush_sitealias_alias_path($alias_path_context);
|
|
|
|
// $alias_files contains a list of filename patterns
|
|
// to search for. We will find any matching file in
|
|
// any folder in the alias path. The directory scan
|
|
// is not deep, though; only files immediately in the
|
|
// search path are considered.
|
|
$alias_files = array('/.*aliases\.drushrc\.php$/');
|
|
if ($aliasname == NULL) {
|
|
$alias_files[] = '/.*\.alias\.drushrc\.php$/';
|
|
}
|
|
else {
|
|
$alias_files[] = '/' . preg_quote($aliasname, '/') . '\.alias\.drushrc\.php$/';
|
|
}
|
|
|
|
// Search each path in turn
|
|
foreach ($alias_path as $path) {
|
|
// Find all of the matching files in this location
|
|
$alias_files_to_consider = array();
|
|
foreach ($alias_files as $file_pattern_to_search_for) {
|
|
$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)));
|
|
}
|
|
|
|
// For every file that matches, check inside it for
|
|
// an alias with a matching name.
|
|
foreach ($alias_files_to_consider as $filename) {
|
|
if (file_exists($filename)) {
|
|
$aliases = $options = array();
|
|
// silently ignore files we can't include
|
|
if ((@include $filename) === FALSE) {
|
|
drush_log(dt('Cannot open alias file "!alias", ignoring.', array('!alias' => realpath($filename))), 'bootstrap');
|
|
continue;
|
|
}
|
|
unset($options['site-aliases']); // maybe unnecessary
|
|
|
|
// If $aliases are not set, but $options are, then define one alias named
|
|
// after the first word of the file, before '.alias.drushrc.php.
|
|
if (empty($aliases) && !empty($options)) {
|
|
$this_alias_name = substr(basename($filename),0,strpos(basename($filename),'.'));
|
|
$aliases[$this_alias_name] = $options;
|
|
$options = array();
|
|
}
|
|
// If this is a group alias file, then make an
|
|
// implicit alias from the group name that contains
|
|
// a site-list of all of the aliases in the file
|
|
if (substr($filename, -20) == ".aliases.drushrc.php") {
|
|
$group_name = basename($filename,".aliases.drushrc.php");
|
|
if (!empty($aliases) && !array_key_exists($group_name, $aliases)) {
|
|
$alias_names = array();
|
|
foreach (array_keys($aliases) as $one_alias) {
|
|
$alias_names[] = "@$group_name.$one_alias";
|
|
$aliases["$group_name.$one_alias"] = $aliases[$one_alias];
|
|
$aliases[$one_alias]["#hidden"] = TRUE;
|
|
}
|
|
$aliases[$group_name] = array('site-list' => implode(',', $alias_names));
|
|
}
|
|
}
|
|
// If aliasname is NULL, then we will store
|
|
// all $aliases into the alias cache
|
|
$alias_recorded = FALSE;
|
|
if ($aliasname == NULL) {
|
|
if (!empty($aliases)) {
|
|
if (!empty($options)) {
|
|
foreach ($aliases as $name => $value) {
|
|
$aliases[$name] = array_merge($options, $value);
|
|
}
|
|
$options = array();
|
|
}
|
|
|
|
foreach ($aliases as $name => $value) {
|
|
_drush_sitealias_initialize_alias_record($aliases[$name]);
|
|
}
|
|
|
|
$result = array_merge($result, $aliases);
|
|
$alias_recorded = TRUE;
|
|
}
|
|
}
|
|
// If aliasname is not NULL, then we will store
|
|
// only the named alias into the alias cache
|
|
elseif ((isset($aliases)) && array_key_exists($aliasname, $aliases)) {
|
|
drush_set_config_special_contexts($options); // maybe unnecessary
|
|
$result = array_merge($options, $aliases[$aliasname]);
|
|
_drush_sitealias_initialize_alias_record($result);
|
|
$alias_recorded = TRUE;
|
|
}
|
|
// If we found at least one alias from this file
|
|
// then record it in the drush-alias-files context.
|
|
if ($alias_recorded) {
|
|
$drush_alias_files = drush_get_context('drush-alias-files');
|
|
if (!in_array($filename, $drush_alias_files)) {
|
|
$drush_alias_files[] = $filename;
|
|
}
|
|
drush_set_context('drush-alias-files', $drush_alias_files);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* @param $aliases
|
|
* An array of alias records that are modified in-place.
|
|
*/
|
|
function _drush_sitealias_add_inherited_values(&$aliases) {
|
|
foreach ($aliases as $alias_name => $alias_value) {
|
|
if (isset($alias_value['parent'])) {
|
|
// Prevent circular references from causing an infinite loop
|
|
_drush_sitealias_cache_alias($alias_name, array());
|
|
// Fetch and merge in each parent
|
|
foreach (explode(',', $alias_value['parent']) as $parent) {
|
|
$parent_record = drush_sitealias_get_record($parent);
|
|
unset($parent_record['#name']);
|
|
unset($parent_record['#hidden']);
|
|
$array_based_keys = array_merge(drush_get_special_keys(), array('path-aliases'));
|
|
foreach ($array_based_keys as $array_based_key) {
|
|
if (isset($aliases[$alias_name][$array_based_key]) && isset($parent_record[$array_based_key])) {
|
|
$aliases[$alias_name][$array_based_key] = array_merge($parent_record[$array_based_key], $aliases[$alias_name][$array_based_key]);
|
|
}
|
|
}
|
|
$aliases[$alias_name] = array_merge($parent_record, $aliases[$alias_name]);
|
|
}
|
|
unset($aliases[$alias_name]['parent']);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add an empty record for the specified alias name
|
|
*
|
|
* @param $alias_name
|
|
* The name of the alias, without the leading "@"
|
|
*/
|
|
function _drush_sitealias_cache_alias($alias_name, $alias_record) {
|
|
$cache =& drush_get_context('site-aliases');
|
|
// If the alias already exists in the cache, then merge
|
|
// the new alias with the existing alias
|
|
if (array_key_exists("@$alias_name", $cache)) {
|
|
$alias_record = array_merge($cache["@$alias_name"], $alias_record);
|
|
}
|
|
$cache["@$alias_name"] = $alias_record;
|
|
|
|
// If the alias record points at a local site, make sure
|
|
// that both the drupal root and the site folder for that site
|
|
// are added to the alias path, so that other alias files
|
|
// stored in those locations become searchable.
|
|
if (!array_key_exists('remote-host', $alias_record) && array_key_exists('root', $alias_record)) {
|
|
drush_sitealias_add_to_alias_path($alias_record['root']);
|
|
$site_dir = drush_sitealias_local_site_path($alias_record);
|
|
if (isset($site_dir)) {
|
|
drush_sitealias_add_to_alias_path($site_dir);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 'db_url' is not available (e.g. on a D7 site)
|
|
*
|
|
* @param $alias_record
|
|
* The full alias record to populate with database settings
|
|
*/
|
|
function drush_sitealias_add_db_url(&$alias_record) {
|
|
if (!isset($alias_record['db-url']) && !isset($alias_record['databases']) && !isset($alias_record['site-list'])) {
|
|
$values = drush_invoke_sitealias_args($alias_record, "sql-conf", array(), array('db-url' => TRUE));
|
|
if (isset($values['object']['db-url'])) {
|
|
$alias_record['db-url'] = $values['object']['db-url'];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the databases record from the alias record
|
|
*
|
|
* @param $alias_record
|
|
* A record returned from drush_sitealias_get_record
|
|
* @returns
|
|
* A databases record (always in D7 format) or NULL
|
|
* if the databases record could not be found.
|
|
*/
|
|
function sitealias_get_databases_from_record(&$alias_record) {
|
|
$altered_record = drush_sitealias_add_db_settings($alias_record);
|
|
|
|
return array_key_exists('databases', $alias_record) ? $alias_record['databases'] : NULL;
|
|
}
|
|
|
|
/**
|
|
* 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 converted to 'databases' if necessary.
|
|
*
|
|
* @param $alias_record
|
|
* The full alias record to populate with database settings
|
|
*/
|
|
function drush_sitealias_add_db_settings(&$alias_record)
|
|
{
|
|
$altered_record = FALSE;
|
|
|
|
// If the alias record does not have a defined 'databases' entry,
|
|
// then we'll need to look one up
|
|
if (!isset($alias_record['db-url']) && !isset($alias_record['databases']) && !isset($alias_record['site-list'])) {
|
|
$values = drush_invoke_sitealias_args($alias_record, "sql-conf", array(), array('all' => TRUE));
|
|
if (isset($values) && ($values['error_status'] == 0)) {
|
|
$altered_record = TRUE;
|
|
// If there are any special settings in the '@self' record returned by drush_invoke_sitealias_args,
|
|
// then add those into our altered record as well
|
|
if (array_key_exists('self', $values)) {
|
|
$alias_record = array_merge($values['self'], $alias_record);
|
|
}
|
|
drush_sitealias_cache_db_settings($alias_record, $values['object']);
|
|
}
|
|
}
|
|
|
|
return $altered_record;
|
|
}
|
|
|
|
function drush_sitealias_cache_db_settings(&$alias_record, $databases) {
|
|
if (!empty($databases)) {
|
|
$alias_record['databases'] = $databases;
|
|
}
|
|
|
|
// If the name is set, then re-cache the record after we fetch the databases
|
|
if (array_key_exists('#name', $alias_record)) {
|
|
$all_site_aliases =& drush_get_context('site-aliases');
|
|
$all_site_aliases[$alias_record['#name']] = $alias_record;
|
|
// Check and see if this record is a copy of 'self'
|
|
if (($alias_record['#name'] != 'self') && array_key_exists('@self', $all_site_aliases) && ($all_site_aliases['@self']['#name'] == $alias_record['#name'])) {
|
|
$all_site_aliases['@self'] = $alias_record;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check to see if we have already bootstrapped to a site.
|
|
*/
|
|
function drush_sitealias_is_bootstrapped_site($alias_record) {
|
|
if (!isset($alias_record['remote-host'])) {
|
|
$self_record = drush_sitealias_get_record("@self");
|
|
if (empty($self_record)) {
|
|
// TODO: If we have not bootstrapped to a site yet, we could
|
|
// perhaps bootstrap to $alias_record here.
|
|
return FALSE;
|
|
}
|
|
elseif(($alias_record['root'] == $self_record['root']) && ($alias_record['uri'] == $self_record['uri'])) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* @param $alias_record
|
|
* The full alias record to use in path alias expansion
|
|
* @param $test_string
|
|
* A slash-separated list of path aliases to resolve
|
|
* e.g. "%files/%special".
|
|
*/
|
|
function drush_sitealias_resolve_path_references(&$alias_record, $test_string = '') {
|
|
// Convert the test string into an array of items, and
|
|
// from this make a comma-separated list of projects
|
|
// that we can pass to 'drush status'.
|
|
$test_array = explode('/', $test_string);
|
|
$project_array = array();
|
|
foreach($test_array as $one_item) {
|
|
if (substr($one_item,0,1) == '%') {
|
|
$project_array[] = substr($one_item,1);
|
|
}
|
|
}
|
|
|
|
// If we already have a path in the path aliases, then
|
|
// there is no need to search for it remotely; we can remove
|
|
// it from the project array.
|
|
if (array_key_exists('path-aliases', $alias_record)) {
|
|
foreach ($alias_record['path-aliases'] as $key => $value) {
|
|
if (substr($key,0,1) == '%') {
|
|
unset($project_array['%' . substr($key,1)]);
|
|
}
|
|
}
|
|
}
|
|
$project_list = implode(',', $project_array);
|
|
|
|
if (!empty($project_array)) {
|
|
// Optimization: if we're already bootstrapped to the
|
|
// site specified by $alias_record, then we can just
|
|
// call _core_site_status_table() rather than use backend invoke.
|
|
if (drush_sitealias_is_bootstrapped_site($alias_record)) {
|
|
// Make sure that we are bootstrapped at least to the 'site'
|
|
// level, and include file.inc to insure that we have access
|
|
// to the %file path.
|
|
if (drush_bootstrap(DRUSH_BOOTSTRAP_DRUPAL_SITE)) {
|
|
include_once DRUPAL_ROOT . '/includes/file.inc';
|
|
}
|
|
$status_values = _core_site_status_table($project_list);
|
|
}
|
|
else {
|
|
$values = drush_invoke_sitealias_args($alias_record, "status", array(), empty($project_list) ? array() : array('project' => $project_list));
|
|
$status_values = $values['object'];
|
|
}
|
|
if (isset($status_values['%paths'])) {
|
|
foreach ($status_values['%paths'] as $key => $path) {
|
|
$alias_record['path-aliases'][$key] = $path;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* @param $alias_record
|
|
* The site list alias record array
|
|
* @return
|
|
* An array of individual site alias records
|
|
*/
|
|
function drush_sitealias_resolve_sitelist($alias_record) {
|
|
$result_list = array();
|
|
if (isset($alias_record)) {
|
|
if (array_key_exists('site-list', $alias_record)) {
|
|
foreach ($alias_record['site-list'] as $sitespec) {
|
|
$one_result = drush_sitealias_get_record($sitespec);
|
|
$result_list = array_merge($result_list, drush_sitealias_resolve_sitelist($one_result));
|
|
}
|
|
}
|
|
elseif (array_key_exists('#name', $alias_record)) {
|
|
$result_list[$alias_record['#name']] = $alias_record;
|
|
}
|
|
}
|
|
|
|
return $result_list;
|
|
}
|
|
|
|
/**
|
|
* 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 uris match, then it is easier to rsync between remote machines.
|
|
*
|
|
* @param $source
|
|
* Array of source alias records
|
|
* @param $target
|
|
* Array of target alias records to compare against source list
|
|
* @return
|
|
* TRUE iff the uris of the sources and targets are in alignment
|
|
*/
|
|
function drush_sitealias_check_lists_alignment($source, $target) {
|
|
$is_aligned = TRUE;
|
|
|
|
$i = 0;
|
|
foreach ($source as $one_source) {
|
|
if ((!isset($target[$i])) || (!_drush_sitelist_check_site_records($one_source, $target[$i]))) {
|
|
$is_aligned = FALSE;
|
|
break;
|
|
}
|
|
++$i;
|
|
}
|
|
|
|
return $is_aligned;
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* TODO: Review the advisability of this operation.
|
|
*/
|
|
function drush_sitelist_align_lists(&$source, &$target, &$source_result, &$target_result) {
|
|
$source_result = array();
|
|
$target_result = array();
|
|
|
|
foreach ($source as $key => $one_source) {
|
|
$one_target = _drush_sitelist_find_in_list($one_source, $target);
|
|
if ($one_target !== FALSE) {
|
|
$source_result[] = $one_source;
|
|
$target_result[] = $one_target;
|
|
unset($source[$key]);
|
|
}
|
|
}
|
|
|
|
$source = $source_result;
|
|
$target = $target_result;
|
|
}
|
|
|
|
function _drush_sitelist_find_in_list($one_source, &$target) {
|
|
$result = FALSE;
|
|
|
|
foreach ($target as $key => $one_target) {
|
|
if(_drush_sitelist_check_site_records($one_source, $one_target)) {
|
|
$result = $one_target;
|
|
unset($target[$key]);
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
function _drush_sitelist_check_site_records($source, $target) {
|
|
if ((array_key_exists('uri', $source)) && (array_key_exists('uri', $target)) && ($source['uri'] == $target['uri'])) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Initialize an alias record; called as soon as the alias
|
|
* record is loaded from its alias file, before it is stored
|
|
* in the cache.
|
|
*
|
|
* @param alias_record
|
|
* The alias record to be initialized; paramter is modified in place.
|
|
*/
|
|
function _drush_sitealias_initialize_alias_record(&$alias_record) {
|
|
// If there is a 'from-list' entry, then build a derived
|
|
// list based on the site list with the given name.
|
|
if (array_key_exists('from-list', $alias_record)) {
|
|
// danger of infinite loops... move to transient defaults?
|
|
$from_record = drush_sitealias_get_record($alias_record['from-list']);
|
|
$from_list = drush_sitealias_resolve_sitelist($from_record);
|
|
$derived_list = array();
|
|
foreach ($from_list as $one_record) {
|
|
$derived_record = _drush_sitealias_derive_record($one_record, $alias_record);
|
|
$derived_list[] = drush_sitealias_alias_record_to_spec($derived_record);
|
|
}
|
|
|
|
$alias_record = array();
|
|
if (!empty($derived_list)) {
|
|
$alias_record['site-list'] = $derived_list;
|
|
}
|
|
}
|
|
// If there is a 'site-search-path' entry, then build
|
|
// a 'site-list' entry from all of the sites that can be
|
|
// found in the search path.
|
|
if (array_key_exists('site-search-path', $alias_record)) {
|
|
// TODO: Is there any point in merging the sites from
|
|
// the search path with any sites already listed in the
|
|
// 'site-list' entry? For now we'll just overwrite.
|
|
$search_path = $alias_record['site-search-path'];
|
|
if (!is_array($search_path)) {
|
|
$search_path = explode(',', $search_path);
|
|
}
|
|
$found_sites = _drush_sitealias_find_local_sites($search_path);
|
|
$alias_record['site-list'] = $found_sites;
|
|
// The 'unordered-list' flag indicates that the order of the items in the site list is not stable.
|
|
$alias_record['unordered-list'] = '1';
|
|
// DEBUG: var_export($alias_record, FALSE);
|
|
}
|
|
if (array_key_exists('site-list', $alias_record)) {
|
|
if (!is_array($alias_record['site-list'])) {
|
|
$alias_record['site-list'] = explode(',', $alias_record['site-list']);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 added
|
|
* if the given drush command explicitly adds them.
|
|
*
|
|
* @param alias_record
|
|
* An alias record with most values already filled in
|
|
*/
|
|
function _drush_sitealias_add_static_defaults(&$alias_record) {
|
|
// If there is a 'db-url' entry but not 'databases' entry, then we will
|
|
// build 'databases' from 'db-url' so that drush commands that use aliases
|
|
// can always count on using a uniform 'databases' array.
|
|
if (isset($alias_record['db-url']) && !isset($alias_record['databases'])) {
|
|
$alias_record['databases'] = drush_sitealias_convert_db_from_db_url($alias_record['db-url']);
|
|
}
|
|
// Adjustments for aliases to drupal instances (as opposed to aliases that are site lists)
|
|
if (array_key_exists('uri', $alias_record)) {
|
|
// Make sure that there is always a 'path-aliases' array
|
|
if (!array_key_exists('path-aliases', $alias_record)) {
|
|
$alias_record['path-aliases'] = array();
|
|
}
|
|
// If there is a 'root' entry, then copy it to the '%root' path alias
|
|
$alias_record['path-aliases']['%root'] = $alias_record['root'];
|
|
}
|
|
}
|
|
|
|
function _drush_sitealias_derive_record($from_record, $modifying_record) {
|
|
$result = $from_record;
|
|
|
|
// If there is a 'remote-user' in the modifying record, copy it.
|
|
if (array_key_exists('remote-user', $modifying_record)) {
|
|
$result['remote-user'] = $from_record['remote_user'];
|
|
}
|
|
// If there is a 'remote-host', then:
|
|
// If it is empty, clear the remote host in the result record
|
|
// If it ends in '.', then prepend it to the remote host in the result record
|
|
// Otherwise, copy it to the result record
|
|
if (array_key_exists('remote-host', $modifying_record)) {
|
|
$remote_host_modifier = $modifying_record['remote-host'];
|
|
if(empty($remote_host_modifier)) {
|
|
unset($result['remote-host']);
|
|
unset($result['remote-user']);
|
|
}
|
|
elseif ($remote_host_modifier[strlen($remote_host_modifier)-1] == '.') {
|
|
$result['remote-host'] = $remote_host_modifier . $result['remote-host'];
|
|
}
|
|
else {
|
|
$result['remote-host'] = $remote_host_modifier;
|
|
}
|
|
}
|
|
// If there is a 'root', then:
|
|
// If it begins with '/', copy it to the result record
|
|
// Otherwise, append it to the result record
|
|
if (array_key_exists('root', $modifying_record)) {
|
|
$root_modifier = $modifying_record['root'];
|
|
if($root_modifier[0] == '/') {
|
|
$result['root'] = $root_modifier;
|
|
}
|
|
else {
|
|
$result['root'] = $result['root'] . '/' . $root_modifier;
|
|
}
|
|
}
|
|
// Poor man's realpath: take out the /../ with preg_replace.
|
|
// (realpath fails if the files in the path do not exist)
|
|
while(strpos($result['root'], '/../') !== FALSE) {
|
|
$result['root'] = preg_replace('/\w+\/\.\.\//', '', $result['root']);
|
|
}
|
|
|
|
// TODO: Should we allow the uri to be transformed?
|
|
// I think that if the uri does not match, then you should
|
|
// always build the list by hand, and not rely on '_drush_sitealias_derive_record'.
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Convert from an alias record to a site specification
|
|
*
|
|
* @param alias_record
|
|
* The full alias record to convert
|
|
*
|
|
* @param with_db
|
|
* True if the site specification should include a ?db-url term
|
|
*
|
|
* @return string
|
|
* The site specification
|
|
*/
|
|
function drush_sitealias_alias_record_to_spec($alias_record, $with_db = false) {
|
|
$result = '';
|
|
|
|
// TODO: we should handle 'site-list' records too.
|
|
if (array_key_exists('site-list', $alias_record)) {
|
|
// TODO: we should actually expand the site list and recompose it
|
|
$result = implode(',', $alias_record['site-list']);
|
|
}
|
|
else {
|
|
// There should always be a uri
|
|
if (array_key_exists('uri', $alias_record)) {
|
|
$result = '#' . drush_sitealias_uri_to_site_dir($alias_record['uri']);
|
|
}
|
|
// There should always be a root
|
|
if (array_key_exists('root', $alias_record)) {
|
|
$result = $alias_record['root'] . $result;
|
|
}
|
|
if (array_key_exists('remote-host', $alias_record)) {
|
|
$result = $alias_record['remote-host'] . $result;
|
|
if (array_key_exists('remote-user', $alias_record)) {
|
|
$result = $alias_record['remote-user'] . '@' . $result;
|
|
}
|
|
}
|
|
|
|
// Add the database info to the specification if desired
|
|
if ($with_db) {
|
|
// If db-url is not supplied, look it up from the remote
|
|
// or local site and add it to the site alias
|
|
if (!isset($alias_record['db-url'])) {
|
|
drush_sitealias_add_db_url($alias_record);
|
|
}
|
|
$result = $result . '?db-url=' . urlencode(is_array($alias_record['db-url']) ? $alias_record['db-url']['default'] : $alias_record['db-url']);
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Search for drupal installations in the search path.
|
|
*
|
|
* @param search_path
|
|
* An array of drupal root folders
|
|
*
|
|
* @return
|
|
* An array of site specifications (/path/to/root#sitename.com)
|
|
*/
|
|
function _drush_sitealias_find_local_sites($search_path) {
|
|
$result = array();
|
|
foreach ($search_path as $a_drupal_root) {
|
|
$result = array_merge($result, _drush_find_local_sites_at_root($a_drupal_root));
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Return a list of all of the local sites at the specified drupal root.
|
|
*/
|
|
function _drush_find_local_sites_at_root($a_drupal_root = '', $search_depth = 1) {
|
|
$site_list = array();
|
|
$base_path = (empty($a_drupal_root) ? drush_get_context('DRUSH_DRUPAL_ROOT') : $a_drupal_root );
|
|
if (!empty($base_path)) {
|
|
if (drush_valid_drupal_root($base_path)) {
|
|
// If $a_drupal_root is in fact a valid drupal root, then return
|
|
// all of the sites found inside the 'sites' folder of this drupal instance.
|
|
$site_list = _drush_find_local_sites_in_sites_folder($base_path);
|
|
}
|
|
else {
|
|
$bootstrap_files = drush_scan_directory($base_path, '/' . basename(DRUSH_DRUPAL_BOOTSTRAP) . '/' , array('.', '..', 'CVS'), 0, drush_get_option('search-depth', $search_depth) + 1, 'filename', 1);
|
|
foreach ($bootstrap_files as $one_bootstrap => $info) {
|
|
$includes_dir = dirname($one_bootstrap);
|
|
if (basename($includes_dir) == basename(dirname(DRUSH_DRUPAL_BOOTSTRAP))) {
|
|
$drupal_root = dirname($includes_dir);
|
|
$site_list = array_merge(_drush_find_local_sites_in_sites_folder($drupal_root), $site_list);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return $site_list;
|
|
}
|
|
|
|
/**
|
|
* Return a list of all of the local sites at the specified 'sites' folder.
|
|
*/
|
|
function _drush_find_local_sites_in_sites_folder($a_drupal_root) {
|
|
$site_list = array();
|
|
|
|
// If anyone searches for sites at a given root, then
|
|
// make sure that alias files stored at this root
|
|
// directory are included in the alias search path
|
|
drush_sitealias_add_to_alias_path($a_drupal_root);
|
|
|
|
$base_path = $a_drupal_root . '/sites';
|
|
|
|
// TODO: build a cache keyed off of $base_path (realpath($base_path)?),
|
|
// so that it is guarenteed that the lists returned will definitely be
|
|
// exactly the same should this routine be called twice with the same path.
|
|
|
|
$files = drush_scan_directory($base_path, '/settings\.php/', array('.', '..', 'CVS', 'all'), 0, 1, 'filename', 1);
|
|
foreach ($files as $filename => $info) {
|
|
if ($info->basename == 'settings.php') {
|
|
// First we'll resolve the realpath of the settings.php file,
|
|
// so that we get the correct drupal root when symlinks are in use.
|
|
$real_sitedir = dirname(realpath($filename));
|
|
$real_root = drush_locate_root($filename);
|
|
if ($real_root !== FALSE) {
|
|
$a_drupal_site = $real_root . '#' . basename($real_sitedir);
|
|
}
|
|
// If the symlink points to some folder outside of any drupal
|
|
// root, then we'll use the
|
|
else {
|
|
$uri = drush_sitealias_site_dir_from_filename($filename);
|
|
$a_drupal_site = $a_drupal_root . '#' . $uri;
|
|
}
|
|
// Add the site if it isn't already in the array
|
|
if (!in_array($a_drupal_site, $site_list)) {
|
|
$site_list[] = $a_drupal_site;
|
|
}
|
|
}
|
|
}
|
|
return $site_list;
|
|
}
|
|
|
|
function drush_sitealias_create_sites_alias($a_drupal_root = '') {
|
|
$sites_list = _drush_find_local_sites_at_root($a_drupal_root);
|
|
_drush_sitealias_cache_alias('sites', array('site-list' => $sites_list));
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
* command explicitly calls this function. The other advantage
|
|
* of transient defaults is that it is possible to differentiate
|
|
* between a default value and an unspecified value, since the
|
|
* transient defaults are not added until requested.
|
|
*
|
|
* Since transient defaults are not cached, you should avoid doing
|
|
* expensive operations here. To be safe, drush commands should
|
|
* avoid calling this function more than once.
|
|
*
|
|
* @param alias_record
|
|
* An alias record with most values already filled in
|
|
*/
|
|
function _drush_sitealias_add_transient_defaults(&$alias_record) {
|
|
if (isset($alias_record['path-aliases'])) {
|
|
// Add the path to the drush folder to the path aliases as !drush
|
|
if (!array_key_exists('%drush', $alias_record['path-aliases'])) {
|
|
if (array_key_exists('%drush-script', $alias_record['path-aliases'])) {
|
|
$alias_record['path-aliases']['%drush'] = dirname($alias_record['path-aliases']['%drush-script']);
|
|
}
|
|
else {
|
|
$alias_record['path-aliases']['%drush'] = dirname($GLOBALS['argv'][0]);
|
|
}
|
|
}
|
|
// Add the path to the site folder to the path aliases as !site
|
|
if (!array_key_exists('%site', $alias_record['path-aliases']) && array_key_exists('uri', $alias_record)) {
|
|
$alias_record['path-aliases']['%site'] = 'sites/' . drush_sitealias_uri_to_site_dir($alias_record['uri']) . '/';
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Find the name of a local alias record that has the specified
|
|
* root and uri.
|
|
*/
|
|
function _drush_sitealias_find_local_alias_name($root, $uri) {
|
|
$result = '';
|
|
$all_site_aliases =& drush_get_context('site-aliases');
|
|
|
|
foreach ($all_site_aliases as $alias_name => $alias_values) {
|
|
if (!array_key_exists('remote-host', $alias_values) && array_key_exists('root', $alias_values) && array_key_exists('uri', $alias_values) && ($alias_name != '@self')) {
|
|
if (($root == $alias_values['root']) && ($uri == $alias_values['uri'])) {
|
|
$result = $alias_name;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* If '$alias' is the name of a folder in the sites folder of the given drupal
|
|
* root, then build an alias record for it
|
|
*
|
|
* @param alias
|
|
* The name of the site in the 'sites' folder to convert
|
|
* @return array
|
|
* An alias record, or empty if none found.
|
|
*/
|
|
function _drush_sitealias_find_record_for_local_site($alias, $drupal_root = null) {
|
|
$alias_record = array();
|
|
|
|
// Clip off the leading '#' if it is there
|
|
if (substr($alias,0,1) == '#') {
|
|
$alias = substr($alias,1);
|
|
}
|
|
|
|
if (!isset($drupal_root)) {
|
|
//$drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT');
|
|
$drupal_root = drush_get_option(array('r', 'root'), drush_locate_root());
|
|
}
|
|
|
|
if (isset($drupal_root)) {
|
|
$alias_dir = drush_sitealias_uri_to_site_dir($alias);
|
|
$site_settings_file = $drupal_root . '/sites/' . $alias_dir . '/settings.php';
|
|
$alias_record = drush_sitealias_build_record_from_settings_file($site_settings_file, $alias, $drupal_root);
|
|
}
|
|
|
|
return $alias_record;
|
|
}
|
|
|
|
function drush_sitealias_build_record_from_settings_file($site_settings_file, $alias = null, $drupal_root = null) {
|
|
$alias_record = array();
|
|
|
|
if (file_exists($site_settings_file)) {
|
|
if (!isset($drupal_root)) {
|
|
$drupal_root = drush_locate_root($site_settings_file);
|
|
}
|
|
|
|
$alias_record['root'] = $drupal_root;
|
|
if (isset($alias)) {
|
|
$alias_record['uri'] = $alias;
|
|
}
|
|
else {
|
|
$alias_record['uri'] = _drush_sitealias_site_dir_to_uri(drush_sitealias_site_dir_from_filename($site_settings_file));
|
|
}
|
|
}
|
|
|
|
return $alias_record;
|
|
}
|
|
|
|
/**
|
|
* Pull the site directory from the path to settings.php
|
|
*
|
|
* @param site_settings_file
|
|
* path to settings.php
|
|
*
|
|
* @return string
|
|
* the site directory component of the path to settings.php
|
|
*/
|
|
function drush_sitealias_site_dir_from_filename($site_settings_file) {
|
|
return basename(dirname($site_settings_file));
|
|
}
|
|
|
|
/**
|
|
* Convert from a URI to a site directory.
|
|
*
|
|
* @param uri
|
|
* A uri, such as http://domain.com:8080/drupal
|
|
* @return string
|
|
* A directory, such as domain.com.8080.drupal
|
|
*/
|
|
function drush_sitealias_uri_to_site_dir($uri) {
|
|
return str_replace(array('http://', '/', ':'), array('', '.', '.'), $uri);
|
|
}
|
|
|
|
/**
|
|
* Convert from an old-style database URL to an array of database settings
|
|
*
|
|
* @param db_url
|
|
* A Drupal 6 db-url string to convert, or an array with a 'default' element.
|
|
* @return array
|
|
* An array of database values containing only the 'default' element of
|
|
* the db_url.
|
|
*/
|
|
function drush_convert_db_from_db_url($db_url) {
|
|
if (is_array($db_url)) {
|
|
$url = parse_url($db_url['default']);
|
|
}
|
|
else {
|
|
$url = parse_url($db_url);
|
|
}
|
|
// Fill in defaults to prevent notices.
|
|
$url += array(
|
|
'driver' => NULL,
|
|
'user' => NULL,
|
|
'pass' => NULL,
|
|
'host' => NULL,
|
|
'port' => NULL,
|
|
'database' => NULL,
|
|
);
|
|
$url = (object)array_map('urldecode', $url);
|
|
return array(
|
|
'driver' => $url->scheme == 'mysqli' ? 'mysql' : $url->scheme,
|
|
'username' => $url->user,
|
|
'password' => $url->pass,
|
|
'port' => $url->port,
|
|
'host' => $url->host,
|
|
// Remove leading / character from database names, unless we're installing
|
|
// to SQLite (which won't have a slash there unless it's part of a path).
|
|
'database' => $url->scheme === 'sqlite' ? $url->path : substr($url->path, 1)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Convert from an old-style database URL to an array of database settings
|
|
*
|
|
* @param db_url
|
|
* A Drupal 6 db-url string to convert, or an array with multiple db-urls.
|
|
* @return array
|
|
* An array of database values.
|
|
*/
|
|
function drush_sitealias_convert_db_from_db_url($db_url) {
|
|
$result = array();
|
|
|
|
if (!is_array($db_url)) {
|
|
$result = array('default' => array('default' => drush_convert_db_from_db_url($db_url)));
|
|
}
|
|
else {
|
|
foreach ($db_url as $one_name => $one_db_url) {
|
|
$result[$one_name] = array('default' => drush_convert_db_from_db_url($one_db_url));
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Utility function used by drush_get_alias; keys that start with
|
|
* '%' or '!' are path aliases, the rest are entries in the alias record.
|
|
*/
|
|
function _drush_sitealias_set_record_element(&$alias_record, $key, $value) {
|
|
if ((substr($key,0,1) == '%') || (substr($key,0,1) == '!')) {
|
|
$alias_record['path-aliases'][$key] = $value;
|
|
}
|
|
elseif (!empty($key)) {
|
|
$alias_record[$key] = $value;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Looks up the specified alias record and calls through to
|
|
* drush_sitealias_set_alias_context, below.
|
|
*
|
|
* @param alias
|
|
* The name of the alias record
|
|
* @param prefix
|
|
* The prefix value to afix to the beginning of every
|
|
* key set.
|
|
* @return boolean
|
|
* TRUE is an alias was found and processed.
|
|
*/
|
|
function _drush_sitealias_set_context_by_name($alias, $prefix = '') {
|
|
$site_alias_settings = drush_sitealias_get_record($alias);
|
|
if (!empty($site_alias_settings)) {
|
|
// Create an alias '@self'
|
|
_drush_sitealias_cache_alias('self', $site_alias_settings);
|
|
drush_sitealias_set_alias_context($site_alias_settings, $prefix);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Given a site alias record, copy selected fields from it
|
|
* into the drush 'alias' context. The 'alias' context has
|
|
* lower precedence than the 'cli' context, so values
|
|
* set by an alias record can be overridden by command-line
|
|
* parameters.
|
|
*
|
|
* @param site_alias_settings
|
|
* An alias record
|
|
* @param prefix
|
|
* The prefix value to afix to the beginning of every
|
|
* key set. For example, if this function is called once with
|
|
* 'source-' and again with 'destination-' prefixes, then the
|
|
* source database records will be stored in 'source-databases',
|
|
* and the destination database records will be in
|
|
* 'destination-databases'.
|
|
*/
|
|
function drush_sitealias_set_alias_context($site_alias_settings, $prefix = '') {
|
|
$options = drush_get_context('alias');
|
|
|
|
// There are some items that we should just skip
|
|
$skip_list = drush_get_special_keys();
|
|
// Also skip 'remote-host' and 'remote-user' if 'remote-host' is actually
|
|
// the local machine
|
|
if (array_key_exists('remote-host', $site_alias_settings) && drush_is_local_host($site_alias_settings['remote-host'])) {
|
|
$skip_list[] = 'remote-host';
|
|
$skip_list[] = 'remote-user';
|
|
}
|
|
// If prefix is set, then copy from the 'prefix-' version
|
|
// of the drush special keys ('command-specific', 'path-aliases')
|
|
// into the ordinary version. This will allow us to set
|
|
// 'source-command-specific' options that will only apply when
|
|
// the alias is used as the source option for rsync or sql-sync.
|
|
if (!empty($prefix)) {
|
|
$special_contexts = drush_get_special_keys();
|
|
foreach ($special_contexts as $option_name) {
|
|
if (array_key_exists($prefix . $option_name, $site_alias_settings)) {
|
|
$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];
|
|
}
|
|
}
|
|
}
|
|
// Transfer all options from the site alias to the drush options
|
|
// in the 'alias' context.
|
|
foreach ($site_alias_settings as $key => $value) {
|
|
// Special handling for path aliases:
|
|
if ($key == "path-aliases") {
|
|
$path_aliases = $value;
|
|
foreach (array('%drush-script', '%dump', '%dump-dir', '%include') as $path_key) {
|
|
if (array_key_exists($path_key, $path_aliases)) {
|
|
// Evaluate the path value, and substitute any path references found.
|
|
// ex: '%dump-dir' => '%root/dumps' will store sql-dumps in the folder
|
|
// 'dumps' in the Drupal root folder for the site.
|
|
$evaluated_path = str_replace(array_keys($path_aliases), array_values($path_aliases), $path_aliases[$path_key]);
|
|
$options[$prefix . substr($path_key, 1)] = $evaluated_path;
|
|
}
|
|
}
|
|
}
|
|
// Special handling for command-specific
|
|
elseif ($key == "command-specific") {
|
|
$options[$key] = $value;
|
|
}
|
|
elseif (!in_array($key, $skip_list)) {
|
|
$options[$prefix . $key] = $value;
|
|
}
|
|
}
|
|
drush_set_config_options('alias', $options);
|
|
}
|
|
|
|
/**
|
|
* Call prior to drush_sitealias_evaluate_path to insure
|
|
* that any site-specific aliases associated with any
|
|
* local site in $path are defined.
|
|
*/
|
|
function _drush_sitealias_preflight_path($path) {
|
|
$alias = NULL;
|
|
// Parse site aliases if there is a colon in the path
|
|
$colon_pos = strpos($path, ':');
|
|
if ($colon_pos !== FALSE) {
|
|
$alias = substr($path, 0, $colon_pos);
|
|
$path = substr($path, $colon_pos + 1);
|
|
$site_alias_settings = _drush_sitealias_get_record($alias);
|
|
if (empty($site_alias_settings) && (substr($path,0,1) == '@')) {
|
|
return NULL;
|
|
}
|
|
$machine = $alias;
|
|
}
|
|
else {
|
|
$machine = '';
|
|
// if the path is a site alias or a local site...
|
|
$site_alias_settings = _drush_sitealias_get_record($path);
|
|
if (empty($site_alias_settings) && (substr($path,0,1) == '@')) {
|
|
return NULL;
|
|
}
|
|
if (!empty($site_alias_settings) || drush_is_local_host($path)) {
|
|
$alias = $path;
|
|
$path = '';
|
|
}
|
|
}
|
|
return array('alias' => $alias, 'path' => $path, 'machine' => $machine);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Evaluate a path from its shorthand form to a literal path
|
|
* usable by rsync.
|
|
*
|
|
* A path is "machine:/path" or "machine:path" or "/path" or "path".
|
|
* 'machine' might instead be an alias record, or the name
|
|
* of a site in the 'sites' folder. 'path' might be (or contain)
|
|
* '%root' or some other path alias. This function will examine
|
|
* all components of the path and evaluate them as necessary to
|
|
* come to the final path.
|
|
*
|
|
* @param path
|
|
* The path to evaluate
|
|
* @param additional_options
|
|
* An array of options that overrides whatever was passed in on
|
|
* the command line (like the 'process' context, but only for
|
|
* the scope of this one call).
|
|
* @return
|
|
* The site record for the machine specified in the path, if any,
|
|
* with the path to pass to rsync (including the machine specifier)
|
|
* in the 'evaluated-path' item.
|
|
*/
|
|
function drush_sitealias_evaluate_path($path, &$additional_options, $local_only = FALSE) {
|
|
$site_alias_settings = array();
|
|
$path_aliases = array();
|
|
$remote_user = '';
|
|
|
|
$preflight = _drush_sitealias_preflight_path($path);
|
|
if (!isset($preflight)) {
|
|
return NULL;
|
|
}
|
|
|
|
$alias = $preflight['alias'];
|
|
$path = $preflight['path'];
|
|
$machine = $preflight['machine'];
|
|
|
|
if (isset($alias)) {
|
|
$site_alias_settings = drush_sitealias_get_record($alias);
|
|
}
|
|
|
|
if (!empty($site_alias_settings)) {
|
|
if ($local_only && array_key_exists('remote-host', $site_alias_settings)) {
|
|
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."));
|
|
}
|
|
|
|
// Apply any options from this alias that might affect our rsync
|
|
drush_sitealias_set_alias_context($site_alias_settings);
|
|
|
|
// Use 'remote-host' from settings if available; otherwise site is local
|
|
if (array_key_exists('remote-host', $site_alias_settings) && !drush_is_local_host($site_alias_settings['remote-host'])) {
|
|
if (array_key_exists('remote-user', $site_alias_settings)) {
|
|
$remote_user = $site_alias_settings['remote-user'] . '@';
|
|
}
|
|
$machine = $remote_user . $site_alias_settings['remote-host'];
|
|
}
|
|
else {
|
|
$machine = '';
|
|
}
|
|
}
|
|
else {
|
|
// Strip the machine portion of the path if the
|
|
// alias points to the local machine.
|
|
if (drush_is_local_host($machine)) {
|
|
$machine = '';
|
|
}
|
|
else {
|
|
$machine = "$remote_user$machine";
|
|
}
|
|
}
|
|
|
|
// If the --exclude-other-sites option is specified, then
|
|
// convert that into --include-path='%site' and --exclude-sites.
|
|
if (drush_get_option_override($additional_options, 'exclude-other-sites', FALSE) && !drush_get_option_override($additional_options, 'exclude-other-sites-processed', FALSE, 'process')) {
|
|
$additional_options['include-path'] = '%site,' . drush_get_option_override($additional_options, 'include-path', '');
|
|
$additional_options['exclude-sites'] = TRUE;
|
|
$additional_options['exclude-other-sites-processed'] = TRUE;
|
|
}
|
|
// If the --exclude-files option is specified, then
|
|
// convert that into --exclude-path='%files'.
|
|
if (drush_get_option_override($additional_options, 'exclude-files', FALSE) && !drush_get_option_override($additional_options, 'exclude-files-processed', FALSE, 'process')) {
|
|
$additional_options['exclude-path'] = '%files,' . drush_get_option_override($additional_options, 'exclude-path', '');
|
|
$additional_options['exclude-files-processed'] = TRUE;
|
|
}
|
|
|
|
// If there was no site specification given, and the
|
|
// machine is local, then try to look
|
|
// up an alias record for the default drush site.
|
|
if (empty($site_alias_settings) && empty($machine)) {
|
|
$drush_uri = drush_bootstrap_value('drush_uri', drush_get_option(array('l', 'uri'), 'default'));
|
|
$site_alias_settings = drush_sitealias_get_record($drush_uri);
|
|
}
|
|
|
|
// Always add transient defaults
|
|
_drush_sitealias_add_transient_defaults($site_alias_settings);
|
|
|
|
// The $resolve_path variable is used by drush_sitealias_resolve_path_references
|
|
// to test to see if there are any path references such as %site or %files
|
|
// in it, so that resolution is only done if the path alias is referenced.
|
|
// Therefore, we can concatenate without worrying too much about the structure of
|
|
// this variable's contents.
|
|
$include_path = drush_get_option_override($additional_options, 'include-path', '');
|
|
$exclude_path = drush_get_option_override($additional_options, 'exclude-path', '');
|
|
$resolve_path = $path . $include_path . $exclude_path;
|
|
// Resolve path aliases such as %files, if any exist in the path
|
|
if (!empty($resolve_path)) {
|
|
drush_sitealias_resolve_path_references($site_alias_settings, $resolve_path);
|
|
}
|
|
|
|
if (array_key_exists('path-aliases', $site_alias_settings)) {
|
|
$path_aliases = $site_alias_settings['path-aliases'];
|
|
}
|
|
|
|
// Get the 'root' setting from the alias; if it does not
|
|
// exist, then get the root from the bootstrapped site.
|
|
if (array_key_exists('root', $site_alias_settings)) {
|
|
$drupal_root = $site_alias_settings['root'];
|
|
}
|
|
else {
|
|
drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_SITE);
|
|
$drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT');
|
|
}
|
|
if (empty($drupal_root)) {
|
|
$drupal_root = '';
|
|
}
|
|
// Add a slash to the end of the drupal root, as below.
|
|
elseif ($drupal_root[strlen($drupal_root)-1] != '/') {
|
|
$drupal_root = $drupal_root . '/';
|
|
}
|
|
$full_path_aliases = $path_aliases;
|
|
foreach ($full_path_aliases as $key => $value) {
|
|
// Expand all relative path aliases to be based off of the Drupal root
|
|
if ((substr($value, 0, 1) != '/') && ($key != '%root')) {
|
|
$full_path_aliases[$key] = $drupal_root . $value;
|
|
}
|
|
// We do not want slashes on the end of our path aliases.
|
|
if (substr($value, 0, -1) == '/') {
|
|
$full_path_aliases[$key] = substr($full_path_aliases[$key], -1);
|
|
}
|
|
}
|
|
|
|
// Fill in path aliases in the path, the include path and the exclude path.
|
|
$path = str_replace(array_keys($full_path_aliases), array_values($full_path_aliases), $path);
|
|
if (!empty($include_path)) {
|
|
drush_set_option('include-path', str_replace(array_keys($path_aliases), array_values($path_aliases), $include_path));
|
|
}
|
|
if (!empty($exclude_path)) {
|
|
drush_set_option('exclude-path', str_replace(array_keys($path_aliases), array_values($path_aliases), $exclude_path));
|
|
}
|
|
|
|
// The path component is just the path part of the full
|
|
// machine:path specification (including the colon).
|
|
$path_component = (!empty($path) ? ':' . $path : '');
|
|
|
|
// Next make the rsync path, which includes the machine
|
|
// and path components together.
|
|
// First make empty paths or relative paths start from the drupal root.
|
|
if (empty($path) || ($path[0] != '/')) {
|
|
$path = $drupal_root . $path;
|
|
}
|
|
|
|
// If there is a $machine component, to the path, then
|
|
// add it to the beginning
|
|
$evaluated_path = $path;
|
|
if (!empty($machine)) {
|
|
$evaluated_path = $machine . ':' . $path;
|
|
}
|
|
|
|
//
|
|
// Add our result paths:
|
|
//
|
|
// evaluated-path: machine:/path
|
|
// server-component: machine
|
|
// path-component: :/path
|
|
// path: /path
|
|
// user-path: path (as specified in input parameter)
|
|
//
|
|
$site_alias_settings['evaluated-path'] = $evaluated_path;
|
|
if (!empty($machine)) {
|
|
$site_alias_settings['server-component'] = $machine;
|
|
}
|
|
$site_alias_settings['path-component'] = $path_component;
|
|
$site_alias_settings['path'] = $path;
|
|
$site_alias_settings['user-path'] = $preflight['path'];
|
|
|
|
return $site_alias_settings;
|
|
}
|
|
|
|
/**
|
|
* Option keys used for site selection.
|
|
*/
|
|
function drush_sitealias_site_selection_keys() {
|
|
return array('remote-host', 'remote-user', 'ssh-options', '#name');
|
|
}
|
|
|
|
|
|
function sitealias_find_local_drupal_root($site_list) {
|
|
$drupal_root = NULL;
|
|
|
|
foreach ($site_list as $site) {
|
|
if (($drupal_root == NULL) && (array_key_exists('root', $site) && !array_key_exists('remote-host', $site))) {
|
|
$drupal_root = $site['root'];
|
|
}
|
|
}
|
|
|
|
return $drupal_root;
|
|
}
|
|
|
|
|
|
/**
|
|
* Helper function to obtain the keys' names that need special handling in certain
|
|
* cases.
|
|
* @return
|
|
* A non-associative array containing the needed keys' names.
|
|
*/
|
|
function drush_get_special_keys() {
|
|
$special_keys = array(
|
|
'command-specific',
|
|
'site-aliases',
|
|
);
|
|
return $special_keys;
|
|
}
|