This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | namespace Consolidation\SiteAlias; |
||
3 | |||
4 | use Consolidation\Config\Loader\ConfigProcessor; |
||
5 | use Dflydev\DotAccessData\Util as DotAccessDataUtil; |
||
6 | |||
7 | /** |
||
8 | * Discover alias files: |
||
9 | * |
||
10 | * - sitename.site.yml: contains multiple aliases, one for each of the |
||
11 | * environments of 'sitename'. |
||
12 | */ |
||
13 | class SiteAliasFileLoader |
||
14 | { |
||
15 | /** |
||
16 | * @var SiteAliasFileDiscovery |
||
17 | */ |
||
18 | protected $discovery; |
||
19 | |||
20 | /** |
||
21 | * @var array |
||
22 | */ |
||
23 | protected $referenceData; |
||
24 | |||
25 | /** |
||
26 | * @var array |
||
27 | */ |
||
28 | protected $loader; |
||
29 | |||
30 | /** |
||
31 | * @var string |
||
32 | */ |
||
33 | protected $root; |
||
34 | |||
35 | /** |
||
36 | * SiteAliasFileLoader constructor |
||
37 | * |
||
38 | * @param SiteAliasFileDiscovery|null $discovery |
||
39 | */ |
||
40 | public function __construct($discovery = null) |
||
41 | { |
||
42 | $this->discovery = $discovery ?: new SiteAliasFileDiscovery(); |
||
43 | $this->referenceData = []; |
||
44 | $this->loader = []; |
||
45 | } |
||
46 | |||
47 | /** |
||
48 | * Allow configuration data to be used in replacements in the alias file. |
||
49 | */ |
||
50 | public function setReferenceData($data) |
||
51 | { |
||
52 | $this->referenceData = $data; |
||
53 | } |
||
54 | |||
55 | /** |
||
56 | * Allow 'self.site.yml' to be applied to any alias record found. |
||
57 | */ |
||
58 | public function setRoot($root) |
||
59 | { |
||
60 | $this->root = $root; |
||
61 | } |
||
62 | |||
63 | /** |
||
64 | * Add a search location to our discovery object. |
||
65 | * |
||
66 | * @param string $path |
||
67 | * |
||
68 | * @return $this |
||
69 | */ |
||
70 | public function addSearchLocation($path) |
||
71 | { |
||
72 | $this->discovery()->addSearchLocation($path); |
||
73 | return $this; |
||
74 | } |
||
75 | |||
76 | /** |
||
77 | * Return our discovery object. |
||
78 | * |
||
79 | * @return SiteAliasFileDiscovery |
||
80 | */ |
||
81 | public function discovery() |
||
82 | { |
||
83 | return $this->discovery; |
||
84 | } |
||
85 | |||
86 | /** |
||
87 | * Load the file containing the specified alias name. |
||
88 | * |
||
89 | * @param SiteAliasName $aliasName |
||
90 | * |
||
91 | * @return SiteAlias|false |
||
92 | */ |
||
93 | public function load(SiteAliasName $aliasName) |
||
94 | { |
||
95 | // First attempt to load a sitename.site.yml file for the alias. |
||
96 | $aliasRecord = $this->loadSingleAliasFile($aliasName); |
||
97 | if ($aliasRecord) { |
||
98 | return $aliasRecord; |
||
99 | } |
||
100 | |||
101 | // If aliasname was provides as @site.env and we did not find it, |
||
102 | // then we are done. |
||
103 | if ($aliasName->hasSitename()) { |
||
104 | return false; |
||
105 | } |
||
106 | |||
107 | // If $aliasName was provided as `@foo` (`hasSitename()` returned `false` |
||
108 | // above), then this was interpreted as `@self.foo` when we searched |
||
109 | // above. If we could not find an alias record for `@self.foo`, then we |
||
110 | // will try to search again, this time with the assumption that `@foo` |
||
111 | // might be `@foo.<default>`, where `<default>` is the default |
||
112 | // environment for the specified site. Note that in this instance, the |
||
113 | // sitename will be found in $aliasName->env(). |
||
114 | $sitename = $aliasName->env(); |
||
115 | return $this->loadDefaultEnvFromSitename($sitename); |
||
116 | } |
||
117 | |||
118 | /** |
||
119 | * Given only a site name, load the default environment from it. |
||
120 | */ |
||
121 | protected function loadDefaultEnvFromSitename($sitename) |
||
122 | { |
||
123 | $path = $this->discovery()->findSingleSiteAliasFile($sitename); |
||
124 | if (!$path) { |
||
125 | return false; |
||
126 | } |
||
127 | $data = $this->loadSiteDataFromPath($path); |
||
128 | if (!$data) { |
||
129 | return false; |
||
130 | } |
||
131 | $env = $this->getDefaultEnvironmentName($data); |
||
132 | |||
133 | $aliasName = new SiteAliasName($sitename, $env); |
||
134 | $processor = new ConfigProcessor(); |
||
135 | return $this->fetchSiteAliasFromSiteAliasData($aliasName, $processor, $data); |
||
136 | } |
||
137 | |||
138 | /** |
||
139 | * Return a list of all site aliases loadable from any findable path. |
||
140 | * |
||
141 | * @return SiteAlias[] |
||
142 | */ |
||
143 | public function loadAll() |
||
144 | { |
||
145 | $result = []; |
||
146 | $paths = $this->discovery()->findAllSingleAliasFiles(); |
||
147 | foreach ($paths as $path) { |
||
148 | $aliasRecords = $this->loadSingleSiteAliasFileAtPath($path); |
||
149 | if ($aliasRecords) { |
||
150 | foreach ($aliasRecords as $aliasRecord) { |
||
151 | $this->storeSiteAliasInResut($result, $aliasRecord); |
||
152 | } |
||
153 | } |
||
154 | } |
||
155 | ksort($result); |
||
156 | return $result; |
||
157 | } |
||
158 | |||
159 | /** |
||
160 | * Return a list of all available alias files. Does not include |
||
161 | * legacy files. |
||
162 | * |
||
163 | * @param string $location Only consider alias files in the specified location. |
||
164 | * @return string[] |
||
165 | */ |
||
166 | public function listAll($location = '') |
||
167 | { |
||
168 | return $this->discovery()->filterByLocation($location)->findAllSingleAliasFiles(); |
||
169 | } |
||
170 | |||
171 | /** |
||
172 | * Given an alias name that might represent multiple sites, |
||
173 | * return a list of all matching alias records. If nothing was found, |
||
174 | * or the name represents a single site + env, then we take |
||
175 | * no action and return `false`. |
||
176 | * |
||
177 | * @param string $sitename The site name to return all environments for. |
||
178 | * @return SiteAlias[]|false |
||
179 | */ |
||
180 | View Code Duplication | public function loadMultiple($sitename, $location = null) |
|
181 | { |
||
182 | $result = []; |
||
183 | foreach ($this->discovery()->filterByLocation($location)->find($sitename) as $path) { |
||
184 | if ($siteData = $this->loadSiteDataFromPath($path)) { |
||
185 | $location = SiteAliasName::locationFromPath($path); |
||
186 | // Convert the raw array into a list of alias records. |
||
187 | $result = array_merge( |
||
188 | $result, |
||
189 | $this->createSiteAliassFromSiteData($sitename, $siteData, $location) |
||
190 | ); |
||
191 | } |
||
192 | } |
||
193 | return $result; |
||
194 | } |
||
195 | |||
196 | /** |
||
197 | * Given a location, return all alias files located there. |
||
198 | * |
||
199 | * @param string $location The location to filter. |
||
200 | * @return SiteAlias[] |
||
201 | */ |
||
202 | View Code Duplication | public function loadLocation($location) |
|
203 | { |
||
204 | $result = []; |
||
205 | foreach ($this->listAll($location) as $path) { |
||
206 | if ($siteData = $this->loadSiteDataFromPath($path)) { |
||
207 | $location = SiteAliasName::locationFromPath($path); |
||
208 | $sitename = $this->siteNameFromPath($path); |
||
209 | // Convert the raw array into a list of alias records. |
||
210 | $result = array_merge( |
||
211 | $result, |
||
212 | $this->createSiteAliassFromSiteData($sitename, $siteData, $location) |
||
213 | ); |
||
214 | } |
||
215 | } |
||
216 | return $result; |
||
217 | } |
||
218 | |||
219 | /** |
||
220 | * @param array $siteData list of sites with its respective data |
||
221 | * |
||
222 | * @param SiteAliasName $aliasName The name of the record being created |
||
223 | * @param $siteData An associative array of envrionment => site data |
||
224 | * @return SiteAlias[] |
||
225 | */ |
||
226 | protected function createSiteAliassFromSiteData($sitename, $siteData, $location = '') |
||
227 | { |
||
228 | $result = []; |
||
229 | if (!is_array($siteData) || empty($siteData)) { |
||
230 | return $result; |
||
231 | } |
||
232 | foreach ($siteData as $envName => $data) { |
||
233 | if (is_array($data) && $this->isValidEnvName($envName)) { |
||
234 | $aliasName = new SiteAliasName($sitename, $envName, $location); |
||
235 | |||
236 | $processor = new ConfigProcessor(); |
||
237 | $oneRecord = $this->fetchSiteAliasFromSiteAliasData($aliasName, $processor, $siteData); |
||
238 | $this->storeSiteAliasInResut($result, $oneRecord); |
||
0 ignored issues
–
show
|
|||
239 | } |
||
240 | } |
||
241 | return $result; |
||
242 | } |
||
243 | |||
244 | /** |
||
245 | * isValidEnvName determines if a given entry should be skipped or not |
||
246 | * (e.g. the "common" entry). |
||
247 | * |
||
248 | * @param string $envName The environment name to test |
||
249 | */ |
||
250 | protected function isValidEnvName($envName) |
||
251 | { |
||
252 | return $envName != 'common'; |
||
253 | } |
||
254 | |||
255 | /** |
||
256 | * Store an alias record in a list. If the alias record has |
||
257 | * a known name, then the key of the list will be the record's name. |
||
258 | * Otherwise, append the record to the end of the list with |
||
259 | * a numeric index. |
||
260 | * |
||
261 | * @param &SiteAlias[] $result list of alias records |
||
262 | * @param SiteAlias $aliasRecord one more alias to store in the result |
||
263 | */ |
||
264 | protected function storeSiteAliasInResut(&$result, SiteAlias $aliasRecord) |
||
265 | { |
||
266 | if (!$aliasRecord) { |
||
267 | return; |
||
268 | } |
||
269 | $key = $aliasRecord->name(); |
||
270 | if (empty($key)) { |
||
271 | $result[] = $aliasRecord; |
||
272 | return; |
||
273 | } |
||
274 | $result[$key] = $aliasRecord; |
||
275 | } |
||
276 | |||
277 | /** |
||
278 | * If the alias name is '@sitename', or if it is '@sitename.env', then |
||
279 | * look for a sitename.site.yml file that contains it. We also handle |
||
280 | * '@location.sitename.env' here as well. |
||
281 | * |
||
282 | * @param SiteAliasName $aliasName |
||
283 | * |
||
284 | * @return SiteAlias|false |
||
285 | */ |
||
286 | protected function loadSingleAliasFile(SiteAliasName $aliasName) |
||
287 | { |
||
288 | // Check to see if the appropriate sitename.alias.yml file can be |
||
289 | // found. Return if it cannot. |
||
290 | $path = $this->discovery() |
||
291 | ->filterByLocation($aliasName->location()) |
||
292 | ->findSingleSiteAliasFile($aliasName->sitename()); |
||
293 | if (!$path) { |
||
294 | return false; |
||
295 | } |
||
296 | return $this->loadSingleAliasFileWithNameAtPath($aliasName, $path); |
||
297 | } |
||
298 | |||
299 | /** |
||
300 | * Given only the path to an alias file `site.alias.yml`, return all |
||
301 | * of the alias records for every environment stored in that file. |
||
302 | * |
||
303 | * @param string $path |
||
304 | * @return SiteAlias[] |
||
305 | */ |
||
306 | protected function loadSingleSiteAliasFileAtPath($path) |
||
307 | { |
||
308 | $sitename = $this->siteNameFromPath($path); |
||
309 | $location = SiteAliasName::locationFromPath($path); |
||
310 | if ($siteData = $this->loadSiteDataFromPath($path)) { |
||
311 | return $this->createSiteAliassFromSiteData($sitename, $siteData, $location); |
||
312 | } |
||
313 | return false; |
||
314 | } |
||
315 | |||
316 | /** |
||
317 | * Given the path to a single site alias file `site.alias.yml`, |
||
318 | * return the `site` part. |
||
319 | * |
||
320 | * @param string $path |
||
321 | */ |
||
322 | protected function siteNameFromPath($path) |
||
323 | { |
||
324 | return $this->basenameWithoutExtension($path, '.site.yml'); |
||
325 | |||
326 | // OR: |
||
327 | // $filename = basename($path); |
||
328 | // return preg_replace('#\..*##', '', $filename); |
||
329 | } |
||
330 | |||
331 | /** |
||
332 | * Chop off the `aliases.yml` or `alias.yml` part of a path. This works |
||
333 | * just like `basename`, except it will throw if the provided path |
||
334 | * does not end in the specified extension. |
||
335 | * |
||
336 | * @param string $path |
||
337 | * @param string $extension |
||
338 | * @return string |
||
339 | * @throws \Exception |
||
340 | */ |
||
341 | protected function basenameWithoutExtension($path, $extension) |
||
342 | { |
||
343 | $result = basename($path, $extension); |
||
344 | // It is an error if $path does not end with site.yml |
||
345 | if ($result == basename($path)) { |
||
346 | throw new \Exception("$path must end with '$extension'"); |
||
347 | } |
||
348 | return $result; |
||
349 | } |
||
350 | |||
351 | /** |
||
352 | * Given an alias name and a path, load the data from the path |
||
353 | * and process it as needed to generate the alias record. |
||
354 | * |
||
355 | * @param SiteAliasName $aliasName |
||
356 | * @param string $path |
||
357 | * @return SiteAlias|false |
||
358 | */ |
||
359 | protected function loadSingleAliasFileWithNameAtPath(SiteAliasName $aliasName, $path) |
||
360 | { |
||
361 | $data = $this->loadSiteDataFromPath($path); |
||
362 | if (!$data) { |
||
363 | return false; |
||
364 | } |
||
365 | $processor = new ConfigProcessor(); |
||
366 | return $this->fetchSiteAliasFromSiteAliasData($aliasName, $processor, $data); |
||
367 | } |
||
368 | |||
369 | /** |
||
370 | * Load the yml from the given path |
||
371 | * |
||
372 | * @param string $path |
||
373 | * @return array|bool |
||
374 | */ |
||
375 | protected function loadSiteDataFromPath($path) |
||
376 | { |
||
377 | $data = $this->loadData($path); |
||
378 | if (!$data) { |
||
379 | return false; |
||
380 | } |
||
381 | $selfSiteAliases = $this->findSelfSiteAliases($data, $path); |
||
382 | $data = array_merge($data, $selfSiteAliases); |
||
383 | return $data; |
||
384 | } |
||
385 | |||
386 | /** |
||
387 | * Given an array of site aliases, find the first one that is |
||
388 | * local (has no 'host' item) and also contains a 'self.site.yml' file. |
||
389 | * @param array $data |
||
390 | * @return array |
||
391 | */ |
||
392 | protected function findSelfSiteAliases($site_aliases, $path) |
||
393 | { |
||
394 | foreach ($site_aliases as $site => $data) { |
||
395 | if (!isset($data['host']) && isset($data['root'])) { |
||
396 | $data = $this->loadSelfSiteData($data['root']); |
||
397 | if (!empty($data)) { |
||
398 | return $data; |
||
399 | } |
||
400 | } |
||
401 | } |
||
402 | |||
403 | return $this->loadSelfSiteData($this->root); |
||
404 | } |
||
405 | |||
406 | /** |
||
407 | * Check to see if there is a 'drush/sites/self.site.yml' file at |
||
408 | * the provided root, or one directory up from there. |
||
409 | */ |
||
410 | protected function loadSelfSiteData($root) |
||
411 | { |
||
412 | if (!$root) { |
||
413 | return []; |
||
414 | } |
||
415 | foreach (['.', '..'] as $relative_path) { |
||
416 | $candidate = $root . '/' . $relative_path . '/drush/sites/self.site.yml'; |
||
417 | if (file_exists($candidate)) { |
||
418 | return $this->loadData($candidate); |
||
419 | } |
||
420 | } |
||
421 | return []; |
||
422 | } |
||
423 | |||
424 | /** |
||
425 | * Load the contents of the specified file. |
||
426 | * |
||
427 | * @param string $path Path to file to load |
||
428 | * @return array |
||
429 | */ |
||
430 | protected function loadData($path) |
||
431 | { |
||
432 | if (empty($path) || !file_exists($path)) { |
||
433 | return []; |
||
434 | } |
||
435 | $loader = $this->getLoader(pathinfo($path, PATHINFO_EXTENSION)); |
||
436 | if (!$loader) { |
||
437 | return []; |
||
438 | } |
||
439 | return $loader->load($path); |
||
440 | } |
||
441 | |||
442 | /** |
||
443 | * @return DataFileLoaderInterface |
||
444 | */ |
||
445 | public function getLoader($extension) |
||
446 | { |
||
447 | if (!isset($this->loader[$extension])) { |
||
448 | return null; |
||
449 | } |
||
450 | return $this->loader[$extension]; |
||
451 | } |
||
452 | |||
453 | public function addLoader($extension, DataFileLoaderInterface $loader) |
||
454 | { |
||
455 | $this->loader[$extension] = $loader; |
||
456 | } |
||
457 | |||
458 | /** |
||
459 | * Given an array containing site alias data, return an alias record |
||
460 | * containing the data for the requested record. If there is a 'common' |
||
461 | * section, then merge that in as well. |
||
462 | * |
||
463 | * @param SiteAliasName $aliasName the alias we are loading |
||
464 | * @param array $data |
||
465 | * |
||
466 | * @return SiteAlias|false |
||
467 | */ |
||
468 | protected function fetchSiteAliasFromSiteAliasData(SiteAliasName $aliasName, ConfigProcessor $processor, array $data) |
||
469 | { |
||
470 | $data = $this->adjustIfSingleAlias($data); |
||
471 | $env = $this->getEnvironmentName($aliasName, $data); |
||
472 | $env_data = $this->getRequestedEnvData($data, $env); |
||
473 | if (!$env_data) { |
||
474 | return false; |
||
475 | } |
||
476 | |||
477 | // Add the 'common' section if it exists. |
||
478 | if ($this->siteEnvExists($data, 'common')) { |
||
479 | $processor->add($data['common']); |
||
480 | } |
||
481 | |||
482 | // Then add the data from the desired environment. |
||
483 | $processor->add($env_data); |
||
484 | |||
485 | // Export the combined data and create an SiteAlias object to manage it. |
||
486 | return new SiteAlias($processor->export($this->referenceData + ['env-name' => $env]), '@' . $aliasName->sitenameWithLocation(), $env); |
||
487 | } |
||
488 | |||
489 | /** |
||
490 | * getRequestedEnvData fetches the data for the specified environment |
||
491 | * from the provided site record data. |
||
492 | * |
||
493 | * @param array $data The site alias data |
||
494 | * @param string $env The name of the environment desired |
||
495 | * @return array|false |
||
496 | */ |
||
497 | protected function getRequestedEnvData(array $data, $env) |
||
498 | { |
||
499 | // If the requested environment exists, we will use it. |
||
500 | if ($this->siteEnvExists($data, $env)) { |
||
501 | return $data[$env]; |
||
502 | } |
||
503 | |||
504 | // If there is a wildcard environment, then return that instead. |
||
505 | if ($this->siteEnvExists($data, '*')) { |
||
506 | return $data['*']; |
||
507 | } |
||
508 | |||
509 | return false; |
||
510 | } |
||
511 | |||
512 | /** |
||
513 | * Determine whether there is a valid-looking environment '$env' in the |
||
514 | * provided site alias data. |
||
515 | * |
||
516 | * @param array $data |
||
517 | * @param string $env |
||
518 | * @return bool |
||
519 | */ |
||
520 | protected function siteEnvExists(array $data, $env) |
||
521 | { |
||
522 | return ( |
||
523 | is_array($data) && |
||
524 | isset($data[$env]) && |
||
525 | is_array($data[$env]) |
||
526 | ); |
||
527 | } |
||
528 | |||
529 | /** |
||
530 | * Adjust the alias data for a single-site alias. Usually, a .yml alias |
||
531 | * file will contain multiple entries, one for each of the environments |
||
532 | * of an alias. If there are no environments |
||
533 | * |
||
534 | * @param array $data |
||
535 | * @return array |
||
536 | */ |
||
537 | protected function adjustIfSingleAlias($data) |
||
538 | { |
||
539 | if (!$this->detectSingleAlias($data)) { |
||
540 | return $data; |
||
541 | } |
||
542 | |||
543 | $result = [ |
||
544 | 'default' => $data, |
||
545 | ]; |
||
546 | |||
547 | return $result; |
||
548 | } |
||
549 | |||
550 | /** |
||
551 | * A single-environment alias looks something like this: |
||
552 | * |
||
553 | * --- |
||
554 | * root: /path/to/drupal |
||
555 | * uri: https://mysite.org |
||
556 | * |
||
557 | * A multiple-environment alias looks something like this: |
||
558 | * |
||
559 | * --- |
||
560 | * default: dev |
||
561 | * dev: |
||
562 | * root: /path/to/dev |
||
563 | * uri: https://dev.mysite.org |
||
564 | * stage: |
||
565 | * root: /path/to/stage |
||
566 | * uri: https://stage.mysite.org |
||
567 | * |
||
568 | * The differentiator between these two is that the multi-environment |
||
569 | * alias always has top-level elements that are associative arrays, and |
||
570 | * the single-environment alias never does. |
||
571 | * |
||
572 | * @param array $data |
||
573 | * @return bool |
||
574 | */ |
||
575 | protected function detectSingleAlias($data) |
||
576 | { |
||
577 | foreach ($data as $key => $value) { |
||
578 | if (is_array($value) && DotAccessDataUtil::isAssoc($value)) { |
||
579 | return false; |
||
580 | } |
||
581 | } |
||
582 | return true; |
||
583 | } |
||
584 | |||
585 | /** |
||
586 | * Return the name of the environment requested. |
||
587 | * |
||
588 | * @param SiteAliasName $aliasName the alias we are loading |
||
589 | * @param array $data |
||
590 | * |
||
591 | * @return string |
||
592 | */ |
||
593 | protected function getEnvironmentName(SiteAliasName $aliasName, array $data) |
||
594 | { |
||
595 | // If the alias name specifically mentions the environment |
||
596 | // to use, then return it. |
||
597 | if ($aliasName->hasEnv()) { |
||
598 | return $aliasName->env(); |
||
599 | } |
||
600 | return $this->getDefaultEnvironmentName($data); |
||
601 | } |
||
602 | |||
603 | /** |
||
604 | * Given a data array containing site alias environments, determine which |
||
605 | * envirionmnet should be used as the default environment. |
||
606 | * |
||
607 | * @param array $data |
||
608 | * @return string |
||
609 | */ |
||
610 | protected function getDefaultEnvironmentName(array $data) |
||
611 | { |
||
612 | // If there is an entry named 'default', it will either contain the |
||
613 | // name of the environment to use by default, or it will itself be |
||
614 | // the default environment. |
||
615 | if (isset($data['default'])) { |
||
616 | return is_array($data['default']) ? 'default' : $data['default']; |
||
617 | } |
||
618 | // If there is an environment named 'dev', it will be our default. |
||
619 | if (isset($data['dev'])) { |
||
620 | return 'dev'; |
||
621 | } |
||
622 | // If we don't know which environment to use, just take the first one. |
||
623 | $keys = array_keys($data); |
||
624 | return reset($keys); |
||
625 | } |
||
626 | } |
||
627 |
This check looks for type mismatches where the missing type is
false
. This is usually indicative of an error condtion.Consider the follow example
This function either returns a new
DateTime
object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returnedfalse
before passing on the value to another function or method that may not be able to handle afalse
.