2 namespace Drush\SiteAlias;
4 use Consolidation\Config\Config;
5 use Consolidation\Config\ConfigInterface;
6 use Webmozart\PathUtil\Path;
9 * A host path is a path on some machine. The machine may be specified
10 * by a label, and the label may be an @alias or a site specification.
11 * If there is no label, then the local machine is assumed.
19 * user@host/drupal-root#uri:/path
22 * Note that /path does not have to begin with a '/'; it may
23 * be a relative path, or it may begin with a path alias,
26 * It is permissible to have an alias or site specification
27 * without a path, but it is not valid to have just a host
32 /** @var AliasRecord The alias record obtained from the host path */
33 protected $alias_record;
35 /** @var string The entire original host path (e.g. @alias:/path) */
36 protected $original_path;
38 /** @var string The "path" component from the host path */
41 /** @var string The alias record is implicit (e.g. 'path' instead of '@self:path') */
45 * HostPath constructor
47 * @param AliasRecord $alias_record The alias record or site specification record
48 * @param string $original_path The original host path
49 * @param string $path Just the 'path' component
51 protected function __construct($alias_record, $original_path, $path = '', $implicit = false)
53 $this->alias_record = $alias_record;
54 $this->original_path = $original_path;
56 $this->implicit = $implicit;
60 * Factory method to create a host path.
62 * @param SiteAliasManager $manager We need to be provided a reference
63 * to the alias manager to create a host path
64 * @param string $hostPath The path to create.
66 public static function create(SiteAliasManager $manager, $hostPath)
68 // Split the alias path up into
69 // - $parts[0]: everything before the first ":"
70 // - $parts[1]: everything after the ":", if there was one.
71 $parts = explode(':', $hostPath, 2);
73 // Determine whether or not $parts[0] is a site spec or an alias
74 // record. If $parts[0] is not in the right form, the result
75 // will be 'false'. This will throw if $parts[0] is an @alias
76 // record, but the requested alias cannot be found.
77 $alias_record = $manager->get($parts[0]);
79 if (!isset($parts[1])) {
80 return static::determinePathOrAlias($manager, $alias_record, $hostPath, $parts[0]);
83 // If $parts[0] did not resolve to a site spec or alias record,
84 // but there is a $parts[1], then $parts[0] must be a machine name.
85 // Unless it was an alias that could not be found.
86 if ($alias_record === false) {
87 if (SiteAliasName::isAliasName($parts[0])) {
88 throw new \Exception('Site alias ' . $parts[0] . ' not found.');
90 $alias_record = new AliasRecord(['host' => $parts[0]]);
93 // Create our alias path
94 return new HostPath($alias_record, $hostPath, $parts[1]);
98 * Return the alias record portion of the host path.
100 * @return AliasRecord
102 public function getAliasRecord()
104 return $this->alias_record;
108 * Returns true if this host path points at a remote machine
112 public function isRemote()
114 return $this->alias_record->isRemote();
118 * Return the original host path string, as provided to the create() method.
122 public function getOriginal()
124 return $this->original_path;
128 * Return just the path portion, without considering the alias root.
132 public function getOriginalPath()
138 * Return the original path
142 public function getPath()
144 if (empty($this->path)) {
145 return $this->alias_record->root();
147 if ($this->alias_record->hasRoot() && !$this->implicit) {
148 return Path::makeAbsolute($this->path, $this->alias_record->root());
154 * Returns 'true' if the path portion of the host path begins with a
155 * path alias (e.g. '%files'). Path aliases must appear at the beginning
160 public function hasPathAlias()
162 $pathAlias = $this->getPathAlias();
163 return !empty($pathAlias);
167 * Return just the path alias portion of the path (e.g. '%files'), or
168 * empty if there is no alias in the path.
172 public function getPathAlias()
174 if (preg_match('#%([^/]*).*#', $this->path, $matches)) {
181 * Replaces the path alias portion of the path with the resolved path.
183 * @param string $resolvedPath The converted path alias (e.g. 'sites/default/files')
186 public function replacePathAlias($resolvedPath)
188 $pathAlias = $this->getPathAlias();
189 if (empty($pathAlias)) {
192 // Make sure that the resolved path always ends in a '\'.
193 $resolvedPath .= '/';
194 // Avoid double / in path.
195 // $this->path: %files/foo
197 // We add one to the length of $pathAlias to account for the '%' in $this->path.
198 if (strlen($this->path) > (strlen($pathAlias) + 1)) {
199 $resolvedPath = rtrim($resolvedPath, '/');
201 // Once the path alias is resolved, replace the alias in the $path with the result.
202 $this->path = $resolvedPath . substr($this->path, strlen($pathAlias) + 1);
204 // Using a path alias such as %files is equivalent to making explicit
205 // use of @self:%files. We set implicit to false here so that the resolved
206 // path will be returned as an absolute path rather than a relative path.
207 $this->implicit = false;
213 * Return the host portion of the host path, including the user.
217 public function getHost()
219 return $this->alias_record->remoteHostWithUser();
223 * Return the fully resolved path, e.g. user@server:/path/to/drupalroot/sites/default/files
227 public function fullyQualifiedPath()
229 $host = $this->getHost();
231 return $host . ':' . $this->getPath();
233 return $this->getPath();
237 * Our fully qualified path passes the result through Path::makeAbsolute()
238 * which canonicalizes the path, removing any trailing slashes.
239 * That is what we want most of the time; however, the trailing slash is
240 * sometimes significant, e.g. for rsync, so we provide a separate API
241 * for those cases where the trailing slash should be preserved.
245 public function fullyQualifiedPathPreservingTrailingSlash()
247 $fqp = $this->fullyQualifiedPath();
248 if ((substr($this->path, strlen($this->path) - 1) == '/') && (substr($fqp, strlen($fqp) - 1) != '/')) {
255 * Helper method for HostPath::create(). When the host path contains no
256 * ':', this method determines whether the string that was provided is
259 * @param SiteAliasManager $manager
260 * @param AliasRecord|bool $alias_record
261 * @param string $hostPath
262 * @param string $single_part
264 protected static function determinePathOrAlias(SiteAliasManager $manager, $alias_record, $hostPath, $single_part)
266 // If $alias_record is false, then $single_part must be a path.
267 if ($alias_record === false) {
268 return new HostPath($manager->getSelf(), $hostPath, $single_part, true);
271 // Otherwise, we have a alias record without a path.
272 // In this instance, the alias record _must_ have a root.
273 if (!$alias_record->hasRoot()) {
274 throw new \Exception("$hostPath does not define a path.");
276 return new HostPath($alias_record, $hostPath);