Completed
Push — master ( a18f4a...b0c220 )
by Greg
01:31
created

HostPath::replacePathAlias()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 25
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
rs 8.8571
c 0
b 0
f 0
cc 3
eloc 10
nc 3
nop 1
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 the original host path string, as provided to the create() method.
119
     *
120
     * @return string
121
     */
122
    public function getOriginal()
123
    {
124
        return $this->original_path;
125
    }
126
127
    /**
128
     * Return just the path portion of the host path
129
     *
130
     * @return string
131
     */
132
    public function getPath()
133
    {
134
        if (empty($this->path)) {
135
            return $this->alias_record->root();
136
        }
137
        if ($this->alias_record->hasRoot() && !$this->implicit) {
138
            return Path::makeAbsolute($this->path, $this->alias_record->root());
139
        }
140
        return $this->path;
141
    }
142
143
    /**
144
     * Returns 'true' if the path portion of the host path begins with a
145
     * path alias (e.g. '%files'). Path aliases must appear at the beginning
146
     * of the path.
147
     *
148
     * @return bool
149
     */
150
    public function hasPathAlias()
151
    {
152
        $pathAlias = $this->getPathAlias();
153
        return !empty($pathAlias);
154
    }
155
156
    /**
157
     * Return just the path alias portion of the path (e.g. '%files'), or
158
     * empty if there is no alias in the path.
159
     *
160
     * @return string
161
     */
162
    public function getPathAlias()
163
    {
164
        if (preg_match('#%([^/]*).*#', $this->path, $matches)) {
165
            return $matches[1];
166
        }
167
        return '';
168
    }
169
170
    /**
171
     * Replaces the path alias portion of the path with the resolved path.
172
     *
173
     * @param string $resolvedPath The converted path alias (e.g. 'sites/default/files')
174
     * @return $this
175
     */
176
    public function replacePathAlias($resolvedPath)
177
    {
178
        $pathAlias = $this->getPathAlias();
179
        if (empty($pathAlias)) {
180
            return $this;
181
        }
182
        // Make sure that the resolved path always ends in a '\'.
183
        $resolvedPath .= '/';
184
        // Avoid double / in path.
185
        //   $this->path: %files/foo
186
        //   $pathAlias:   files
187
        // We add one to the length of $pathAlias to account for the '%' in $this->path.
188
        if (strlen($this->path) > (strlen($pathAlias) + 1)) {
189
            $resolvedPath = rtrim($resolvedPath, '/');
190
        }
191
        // Once the path alias is resolved, replace the alias in the $path with the result.
192
        $this->path = $resolvedPath . substr($this->path, strlen($pathAlias) + 1);
193
194
        // Using a path alias such as %files is equivalent to making explicit
195
        // use of @self:%files. We set implicit to false here so that the resolved
196
        // path will be returned as an absolute path rather than a relative path.
197
        $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...
198
199
        return $this;
200
    }
201
202
    /**
203
     * Return the host portion of the host path, including the user.
204
     *
205
     * @return string
206
     */
207
    public function getHost()
208
    {
209
        return $this->alias_record->remoteHostWithUser();
210
    }
211
212
    /**
213
     * Return the fully resolved path, e.g. user@server:/path/to/drupalroot/sites/default/files
214
     *
215
     * @return string
216
     */
217
    public function fullyQualifiedPath()
218
    {
219
        $host = $this->getHost();
220
        if (!empty($host)) {
221
            return $host . ':' . $this->getPath();
222
        }
223
        return $this->getPath();
224
    }
225
226
    /**
227
     * Our fully qualified path passes the result through Path::makeAbsolute()
228
     * which canonicallizes the path, removing any trailing slashes.
229
     * That is what we want most of the time; however, the trailing slash is
230
     * sometimes significant, e.g. for rsync, so we provide a separate API
231
     * for those cases where the trailing slash should be preserved.
232
     *
233
     * @return string
234
     */
235
    public function fullyQualifiedPathPreservingTrailingSlash()
236
    {
237
        $fqp = $this->fullyQualifiedPath();
238
        if ((substr($this->path, strlen($this->path) - 1) == '/') && (substr($fqp, strlen($fqp) - 1) != '/')) {
239
            $fqp .= '/';
240
        }
241
        return $fqp;
242
    }
243
244
    /**
245
     * Helper method for HostPath::create(). When the host path contains no
246
     * ':', this method determines whether the string that was provided is
247
     * a host or a path.
248
     *
249
     * @param SiteAliasManager $manager
250
     * @param AliasRecord|bool $alias_record
251
     * @param string $hostPath
252
     * @param string $single_part
253
     */
254
    protected static function determinePathOrAlias(SiteAliasManager $manager, $alias_record, $hostPath, $single_part)
255
    {
256
        // If $alias_record is false, then $single_part must be a path.
257
        if ($alias_record === false) {
258
            return new HostPath($manager->getSelf(), $hostPath, $single_part, true);
259
        }
260
261
        // Otherwise, we have a alias record without a path.
262
        // In this instance, the alias record _must_ have a root.
263
        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...
264
            throw new \Exception("$hostPath does not define a path.");
265
        }
266
        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 254 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...
267
    }
268
}
269