Completed
Push — master ( 1d624e...5d3011 )
by Greg
01:28
created

HostPath::getOriginalPath()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
namespace Consolidation\SiteAlias;
3
4
use Consolidation\Config\Config;
5
use Consolidation\Config\ConfigInterface;
6
use Webmozart\PathUtil\Path;
7
8
/**
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.
12
 *
13
 * Examples:
14
 *
15
 *   @alias
16
 *   @alias:/path
17
 *   host:/path
18
 *   user@host:/path
19
 *   user@host/drupal-root#uri:/path
20
 *   /path
21
 *
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,
24
 * e.g. '%files'.
25
 *
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
28
 * with no path.
29
 */
30
class HostPath
31
{
32
    /** @var AliasRecord The alias record obtained from the host path */
33
    protected $alias_record;
34
35
    /** @var string The entire original host path (e.g. @alias:/path) */
36
    protected $original_path;
37
38
    /** @var string The "path" component from the host path */
39
    protected $path;
40
41
    /** @var string The alias record is implicit (e.g. 'path' instead of '@self:path') */
42
    protected $implicit;
43
44
    /**
45
     * HostPath constructor
46
     *
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
50
     */
51
    protected function __construct($alias_record, $original_path, $path = '', $implicit = false)
52
    {
53
        $this->alias_record = $alias_record;
54
        $this->original_path = $original_path;
55
        $this->path = $path;
56
        $this->implicit = $implicit;
0 ignored issues
show
Documentation Bug introduced by
The property $implicit was declared of type string, but $implicit is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
57
    }
58
59
    /**
60
     * Factory method to create a host path.
61
     *
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.
65
     */
66
    public static function create(SiteAliasManager $manager, $hostPath)
67
    {
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);
72
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]);
78
79
        if (!isset($parts[1])) {
80
            return static::determinePathOrAlias($manager, $alias_record, $hostPath, $parts[0]);
81
        }
82
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])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression \Consolidation\SiteAlias...:isAliasName($parts[0]) of type false|integer is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
88
                throw new \Exception('Site alias ' . $parts[0] . ' not found.');
89
            }
90
            $alias_record = new AliasRecord(['host' => $parts[0]]);
91
        }
92
93
        // Create our alias path
94
        return new HostPath($alias_record, $hostPath, $parts[1]);
95
    }
96
97
    /**
98
     * Return the alias record portion of the host path.
99
     *
100
     * @return AliasRecord
101
     */
102
    public function getAliasRecord()
103
    {
104
        return $this->alias_record;
105
    }
106
107
    /**
108
     * Returns true if this host path points at a remote machine
109
     *
110
     * @return bool
111
     */
112
    public function isRemote()
113
    {
114
        return $this->alias_record->isRemote();
115
    }
116
117
    /**
118
     * Return just the path portion, without considering the alias root.
119
     *
120
     * @return string
121
     */
122
    public function getOriginalPath()
123
    {
124
        return $this->path;
125
    }
126
127
    /**
128
     * Return the original host path string, as provided to the create() method.
129
     *
130
     * @return string
131
     */
132
    public function getOriginal()
133
    {
134
        return $this->original_path;
135
    }
136
137
    /**
138
     * Return just the path portion of the host path
139
     *
140
     * @return string
141
     */
142
    public function getPath()
143
    {
144
        if (empty($this->path)) {
145
            return $this->alias_record->root();
146
        }
147
        if ($this->alias_record->hasRoot() && !$this->implicit) {
148
            return Path::makeAbsolute($this->path, $this->alias_record->root());
149
        }
150
        return $this->path;
151
    }
152
153
    /**
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
156
     * of the path.
157
     *
158
     * @return bool
159
     */
160
    public function hasPathAlias()
161
    {
162
        $pathAlias = $this->getPathAlias();
163
        return !empty($pathAlias);
164
    }
165
166
    /**
167
     * Return just the path alias portion of the path (e.g. '%files'), or
168
     * empty if there is no alias in the path.
169
     *
170
     * @return string
171
     */
172
    public function getPathAlias()
173
    {
174
        if (preg_match('#%([^/]*).*#', $this->path, $matches)) {
175
            return $matches[1];
176
        }
177
        return '';
178
    }
179
180
    /**
181
     * Replaces the path alias portion of the path with the resolved path.
182
     *
183
     * @param string $resolvedPath The converted path alias (e.g. 'sites/default/files')
184
     * @return $this
185
     */
186
    public function replacePathAlias($resolvedPath)
187
    {
188
        $pathAlias = $this->getPathAlias();
189
        if (empty($pathAlias)) {
190
            return $this;
191
        }
192
        // Make sure that the resolved path always ends in a '\'.
193
        $resolvedPath .= '/';
194
        // Avoid double / in path.
195
        //   $this->path: %files/foo
196
        //   $pathAlias:   files
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, '/');
200
        }
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);
203
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;
0 ignored issues
show
Documentation Bug introduced by
The property $implicit was declared of type string, but false is of type false. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
208
209
        return $this;
210
    }
211
212
    /**
213
     * Return the host portion of the host path, including the user.
214
     *
215
     * @return string
216
     */
217
    public function getHost()
218
    {
219
        return $this->alias_record->remoteHostWithUser();
220
    }
221
222
    /**
223
     * Return the fully resolved path, e.g. user@server:/path/to/drupalroot/sites/default/files
224
     *
225
     * @return string
226
     */
227
    public function fullyQualifiedPath()
228
    {
229
        $host = $this->getHost();
230
        if (!empty($host)) {
231
            return $host . ':' . $this->getPath();
232
        }
233
        return $this->getPath();
234
    }
235
236
    /**
237
     * Our fully qualified path passes the result through Path::makeAbsolute()
238
     * which canonicallizes 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.
242
     *
243
     * @return string
244
     */
245
    public function fullyQualifiedPathPreservingTrailingSlash()
246
    {
247
        $fqp = $this->fullyQualifiedPath();
248
        if ((substr($this->path, strlen($this->path) - 1) == '/') && (substr($fqp, strlen($fqp) - 1) != '/')) {
249
            $fqp .= '/';
250
        }
251
        return $fqp;
252
    }
253
254
    /**
255
     * Helper method for HostPath::create(). When the host path contains no
256
     * ':', this method determines whether the string that was provided is
257
     * a host or a path.
258
     *
259
     * @param SiteAliasManager $manager
260
     * @param AliasRecord|bool $alias_record
261
     * @param string $hostPath
262
     * @param string $single_part
263
     */
264
    protected static function determinePathOrAlias(SiteAliasManager $manager, $alias_record, $hostPath, $single_part)
265
    {
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);
269
        }
270
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()) {
0 ignored issues
show
Bug introduced by
It seems like $alias_record is not always an object, but can also be of type boolean. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
274
            throw new \Exception("$hostPath does not define a path.");
275
        }
276
        return new HostPath($alias_record, $hostPath);
0 ignored issues
show
Bug introduced by
It seems like $alias_record defined by parameter $alias_record on line 264 can also be of type boolean; however, Consolidation\SiteAlias\HostPath::__construct() does only seem to accept object<Consolidation\SiteAlias\AliasRecord>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
277
    }
278
}
279