Completed
Push — master ( 877b15...bcf172 )
by Greg
03:52
created

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