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

SiteSpecParser::fixAndCheckUsability()   C

Complexity

Conditions 7
Paths 6

Size

Total Lines 29
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 29
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 11
nc 6
nop 2
1
<?php
2
namespace Consolidation\SiteAlias;
3
4
/**
5
 * Parse a string that contains a site specification.
6
 *
7
 * Site specifications contain some of the following elements:
8
 *   - user
9
 *   - host
10
 *   - path
11
 *   - uri (multisite selector)
12
 */
13
class SiteSpecParser
14
{
15
    /**
16
     * Parse a site specification
17
     *
18
     * @param string $spec
19
     *   A site specification in one of the accepted forms:
20
     *     - /path/to/drupal#uri
21
     *     - user@server/path/to/drupal#uri
22
     *     - user@server/path/to/drupal
23
     *     - user@server#uri
24
     *   or, a site name:
25
     *     - #uri
26
     * @param string $root
27
     *   Drupal root (if provided).
28
     * @return array
29
     *   A site specification array with the specified components filled in:
30
     *     - user
31
     *     - host
32
     *     - path
33
     *     - uri
34
     *   or, an empty array if the provided parameter is not a valid site spec.
35
     */
36
    public function parse($spec, $root = '')
37
    {
38
        $result = $this->match($spec);
39
        return $this->fixAndCheckUsability($result, $root);
40
    }
41
42
    /**
43
     * Determine if the provided specification is valid. Note that this
44
     * tests only for syntactic validity; to see if the specification is
45
     * usable, call 'parse()', which will also filter out specifications
46
     * for local sites that specify a multidev site that does not exist.
47
     *
48
     * @param string $spec
49
     *   @see parse()
50
     * @return bool
51
     */
52
    public function validSiteSpec($spec)
53
    {
54
        $result = $this->match($spec);
55
        return !empty($result);
56
    }
57
58
    /**
59
     * Determine whether or not the provided name is an alias name.
60
     *
61
     * @param string $aliasName
62
     * @return bool
63
     */
64
    public function isAliasName($aliasName)
65
    {
66
        return !empty($aliasName) && ($aliasName[0] == '@');
67
    }
68
69
    /**
70
     * Return the set of regular expression patterns that match the available
71
     * site specification formats.
72
     *
73
     * @return array
74
     *   key: site specification regex
75
     *   value: an array mapping from site specification component names to
76
     *     the elements in the 'matches' array containing the data for that element.
77
     */
78
    protected function patterns()
79
    {
80
        $PATH = '(/[^#]*)';
81
        $USER = '([a-zA-Z0-9\._-]+)';
82
        $SERVER = '([a-zA-Z0-9\._-]+)';
83
        $URI = '([a-zA-Z0-9_-]+)';
84
85
        return [
86
            // /path/to/drupal#uri
87
            "%^{$PATH}#{$URI}\$%" => [
88
                'root' => 1,
89
                'uri' => 2,
90
            ],
91
            // user@server/path/to/drupal#uri
92
            "%^{$USER}@{$SERVER}{$PATH}#{$URI}\$%" => [
93
                'user' => 1,
94
                'host' => 2,
95
                'root' => 3,
96
                'uri' => 4,
97
            ],
98
            // user@server/path/to/drupal
99
            "%^{$USER}@{$SERVER}{$PATH}\$%" => [
100
                'user' => 1,
101
                'host' => 2,
102
                'root' => 3,
103
                'uri' => 'default', // Or '2' if uri should be 'host'
104
            ],
105
            // user@server#uri
106
            "%^{$USER}@{$SERVER}#{$URI}\$%" => [
107
                'user' => 1,
108
                'host' => 2,
109
                'uri' => 3,
110
            ],
111
            // #uri
112
            "%^#{$URI}\$%" => [
113
                'uri' => 1,
114
            ],
115
        ];
116
    }
117
118
    /**
119
     * Run through all of the available regex patterns and determine if
120
     * any match the provided specification.
121
     *
122
     * @return array
123
     *   @see parse()
124
     */
125
    protected function match($spec)
126
    {
127
        foreach ($this->patterns() as $regex => $map) {
128
            if (preg_match($regex, $spec, $matches)) {
129
                return $this->mapResult($map, $matches);
130
            }
131
        }
132
        return [];
133
    }
134
135
    /**
136
     * Inflate the provided array so that it always contains the required
137
     * elements.
138
     *
139
     * @return array
140
     *   @see parse()
141
     */
142
    protected function defaults($result = [])
143
    {
144
        $result += [
145
            'root' => '',
146
            'uri' => '',
147
        ];
148
149
        return $result;
150
    }
151
152
    /**
153
     * Take the data from the matches from the regular expression and
154
     * plug them into the result array per the info in the provided map.
155
     *
156
     * @param array $map
157
     *   An array mapping from result key to matches index.
158
     * @param array $matches
159
     *   The matched strings returned from preg_match
160
     * @return array
161
     *   @see parse()
162
     */
163
    protected function mapResult($map, $matches)
164
    {
165
        $result = [];
166
167
        foreach ($map as $key => $index) {
168
            $value = is_string($index) ? $index : $matches[$index];
169
            $result[$key] = $value;
170
        }
171
172
        if (empty($result)) {
173
            return [];
174
        }
175
176
        return $this->defaults($result);
177
    }
178
179
    /**
180
     * Validate the provided result. If the result is local, then it must
181
     * have a 'root'. If it does not, then fill in the root that was provided
182
     * to us in our consturctor.
183
     *
184
     * @param array $result
185
     *   @see parse() result.
186
     * @return array
187
     *   @see parse()
188
     */
189
    protected function fixAndCheckUsability($result, $root)
190
    {
191
        if (empty($result) || !empty($result['host'])) {
192
            return $result;
193
        }
194
195
        if (empty($result['root'])) {
196
            // TODO: should these throw an exception, so the user knows
197
            // why their site spec was invalid?
198
            if (empty($root) || !is_dir($root)) {
199
                return [];
200
            }
201
202
            $result['root'] = $root;
203
        }
204
205
        // TODO: If using a sitespec `#uri`, then `uri` MUST
206
        // be the name of a folder that exists in __DRUPAL_ROOT__/sites.
207
        // This restriction does NOT apply to the --uri option. Are there
208
        // instances where we need to allow 'uri' to be a literal uri
209
        // rather than the folder name? If so, we need to loosen this check.
210
        // I think it's fine as it is, though.
211
        $path = $result['root'] . '/sites/' . $result['uri'];
212
        if (!is_dir($path)) {
213
            return [];
214
        }
215
216
        return $result;
217
    }
218
}
219