c84ae77a451172ccf6808673b80c77bceecd2491
[yaffs-website] / vendor / consolidation / site-alias / src / SiteSpecParser.php
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 }