| 1 |  |  | <?php declare(strict_types=1); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 2 |  |  | /** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 3 |  |  |  * This file is part of the daikon-cqrs/config project. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 4 |  |  |  * | 
            
                                                                                                            
                            
            
                                    
            
            
                | 5 |  |  |  * For the full copyright and license information, please view the LICENSE | 
            
                                                                                                            
                            
            
                                    
            
            
                | 6 |  |  |  * file that was distributed with this source code. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 7 |  |  |  */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 8 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 9 |  |  | namespace Daikon\Config; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 10 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 11 |  |  | use Daikon\Interop\Assertion; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 12 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 13 |  |  | final class ConfigProvider implements ConfigProviderInterface | 
            
                                                                                                            
                            
            
                                    
            
            
                | 14 |  |  | { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 15 |  |  |     private const INTERPOLATION_PATTERN = '/(\$\{(.*?)\})/'; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 16 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 17 |  |  |     private array $config; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 18 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 19 |  |  |     private ConfigProviderParamsInterface $params; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 20 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 21 |  |  |     private array $paramInterpolations; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 22 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 23 | 3 |  |     public function __construct(ConfigProviderParamsInterface $params) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 24 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 25 | 3 |  |         $this->params = $params; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 26 | 3 |  |         $this->config = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 27 | 3 |  |         $this->paramInterpolations = []; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 28 | 3 |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 29 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 30 | 7 |  |     public function get(string $path, $default = null) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 31 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 32 | 7 |  |         $path = ConfigPath::fromString($path); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 33 | 7 |  |         $scope = $path->getScope(); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 34 | 7 |  |         Assertion::keyNotExists( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 35 | 7 |  |             $this->paramInterpolations, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 36 |  |  |             $scope, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 37 |  |  |             "Recursive interpolations are not allowed when interpolating 'locations' or 'sources'. ". | 
            
                                                                                                            
                            
            
                                    
            
            
                | 38 | 7 |  |             "Trying to recurse into scope '$scope'." | 
            
                                                                                                            
                            
            
                                    
            
            
                | 39 |  |  |         ); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 40 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 41 | 7 |  |         if (!isset($this->config[$scope]) && $this->params->hasScope($scope)) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 42 | 7 |  |             $this->config[$scope] = $this->loadScope($scope); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 43 | 5 |  |         } elseif (!isset($this->config[$scope])) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 44 | 2 |  |             return $default; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 45 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 46 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 47 | 6 |  |         return $this->evaluatePath( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 48 | 6 |  |             $path->getParts(), | 
            
                                                                                                            
                            
            
                                    
            
            
                | 49 | 6 |  |             $this->config[$path->getScope()], | 
            
                                                                                                            
                            
            
                                    
            
            
                | 50 | 6 |  |             $path->getSeparator() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 51 | 6 |  |         ) ?? $default; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 52 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 53 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 54 | 1 |  |     public function has(string $path): bool | 
            
                                                                                                            
                            
            
                                    
            
            
                | 55 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 56 | 1 |  |         return $this->get($path) !== null; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 57 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 58 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 59 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 60 |  |  |     public function __invoke(string $path, $default = null) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 61 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 62 |  |  |         $value = $this->get($path, $default); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 63 |  |  |         Assertion::allNotNull([$value, $default], "Missing required config value at path '$path'."); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 64 |  |  |         return $value; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 65 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 66 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 67 | 7 |  |     private function loadScope(string $scope): array | 
            
                                                                                                            
                            
            
                                    
            
            
                | 68 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 69 | 7 |  |         $this->paramInterpolations[$scope] = true; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 70 | 7 |  |         $locations = $this->params->getLocations($scope); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 71 | 7 |  |         $sources = $this->params->getSources($scope); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 72 | 7 |  |         $loader = $this->params->getLoader($scope); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 73 | 7 |  |         if (!$loader instanceof ArrayConfigLoader) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 74 | 2 |  |             $sources = $this->interpolateConfigValues($sources); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 75 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 76 | 6 |  |         $locations = $this->interpolateConfigValues($locations); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 77 | 6 |  |         unset($this->paramInterpolations[$scope]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 78 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 79 | 6 |  |         $this->config[$scope] = $loader->load($locations, $sources); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 80 | 6 |  |         return $this->interpolateConfigValues($this->config[$scope]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 81 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 82 |  |  |  | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 83 |  |  |     /** @return mixed */ | 
            
                                                                        
                            
            
                                    
            
            
                | 84 | 6 |  |     private function evaluatePath(array $parts, array $values, string $separator) | 
            
                                                                        
                            
            
                                    
            
            
                | 85 |  |  |     { | 
            
                                                                        
                            
            
                                    
            
            
                | 86 | 6 |  |         if (empty($values)) { | 
            
                                                                        
                            
            
                                    
            
            
                | 87 | 1 |  |             return null; | 
            
                                                                        
                            
            
                                    
            
            
                | 88 |  |  |         } | 
            
                                                                        
                            
            
                                    
            
            
                | 89 | 6 |  |         $pos = 0; | 
            
                                                                        
                            
            
                                    
            
            
                | 90 | 6 |  |         $length = count($parts); | 
            
                                                                        
                            
            
                                    
            
            
                | 91 | 6 |  |         $value = &$values; | 
            
                                                                        
                            
            
                                    
            
            
                | 92 | 6 |  |         while (!empty($parts)) { | 
            
                                                                        
                            
            
                                    
            
            
                | 93 | 6 |  |             $pos++; | 
            
                                                                        
                            
            
                                    
            
            
                | 94 | 6 |  |             $part = array_shift($parts); | 
            
                                                                        
                            
            
                                    
            
            
                | 95 | 6 |  |             Assertion::isArray( | 
            
                                                                        
                            
            
                                    
            
            
                | 96 | 6 |  |                 $value, | 
            
                                                                        
                            
            
                                    
            
            
                | 97 | 6 |  |                 sprintf("Trying to traverse non-array value with path part '%s'.", join($separator, $parts)) | 
            
                                                                        
                            
            
                                    
            
            
                | 98 |  |  |             ); | 
            
                                                                        
                            
            
                                    
            
            
                | 99 | 6 |  |             if ($part === ConfigPathInterface::WILDCARD_TOKEN) { | 
            
                                                                        
                            
            
                                    
            
            
                | 100 | 1 |  |                 return $this->expandWildcard($parts, $value, $separator); | 
            
                                                                        
                            
            
                                    
            
            
                | 101 | 6 |  |             } elseif (!isset($value[$part]) && $pos === $length) { | 
            
                                                                        
                            
            
                                    
            
            
                | 102 | 1 |  |                 return null; | 
            
                                                                        
                            
            
                                    
            
            
                | 103 | 6 |  |             } elseif (!isset($value[$part])) { | 
            
                                                                        
                            
            
                                    
            
            
                | 104 | 2 |  |                 array_unshift($parts, $part.$separator.array_shift($parts)); | 
            
                                                                        
                            
            
                                    
            
            
                | 105 | 2 |  |                 continue; | 
            
                                                                        
                            
            
                                    
            
            
                | 106 |  |  |             } | 
            
                                                                        
                            
            
                                    
            
            
                | 107 | 6 |  |             $value = &$value[$part]; | 
            
                                                                        
                            
            
                                    
            
            
                | 108 |  |  |         } | 
            
                                                                        
                            
            
                                    
            
            
                | 109 | 6 |  |         return $value; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 110 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 111 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 112 | 1 |  |     private function expandWildcard(array $parts, array $context, string $separator): array | 
            
                                                                                                            
                            
            
                                    
            
            
                | 113 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 114 | 1 |  |         return array_merge(...array_reduce( | 
            
                                                                                                            
                            
            
                                    
            
            
                | 115 | 1 |  |             $context, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 116 | 1 |  |             function (array $collected, array $ctx) use ($parts, $separator): array { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 117 | 1 |  |                 $expandedValue = $this->evaluatePath($parts, $ctx, $separator); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 118 | 1 |  |                 if (!is_null($expandedValue)) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 119 | 1 |  |                     $collected[] =  (array)$expandedValue; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 120 |  |  |                 } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 121 | 1 |  |                 return $collected; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 122 | 1 |  |             }, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 123 | 1 |  |             [] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 124 |  |  |         )); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 125 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 126 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 127 | 7 |  |     private function interpolateConfigValues(array $config): array | 
            
                                                                                                            
                            
            
                                    
            
            
                | 128 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 129 | 7 |  |         return array_map([$this, 'mapInterpolation'], $config); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 130 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 131 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 132 |  |  |     /** | 
            
                                                                                                            
                            
            
                                    
            
            
                | 133 |  |  |      * @param mixed $value | 
            
                                                                                                            
                            
            
                                    
            
            
                | 134 |  |  |      * @return mixed | 
            
                                                                                                            
                            
            
                                    
            
            
                | 135 |  |  |      */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 136 | 7 |  |     private function mapInterpolation($value) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 137 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 138 | 7 |  |         if (is_array($value)) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 139 | 6 |  |             return $this->interpolateConfigValues($value); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 140 | 7 |  |         } elseif (is_string($value) && preg_match_all(self::INTERPOLATION_PATTERN, $value, $matches)) { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 141 | 3 |  |             return $this->interpolateConfigValue($value, $matches[0], $matches[2]); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 142 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 143 | 6 |  |         return $value; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 144 |  |  |     } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 145 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 146 |  |  |     /** @return mixed */ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 147 | 3 |  |     private function interpolateConfigValue(string $value, array $valueParts, array $interpolations) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 148 |  |  |     { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 149 | 3 |  |         $interpolatedValues = array_map([$this, 'get'], $interpolations); | 
            
                                                                                                            
                            
            
                                    
            
            
                | 150 | 2 |  |         return array_filter($interpolatedValues, 'is_string') === $interpolatedValues | 
            
                                                                                                            
                            
            
                                    
            
            
                | 151 | 2 |  |             ? str_replace($valueParts, $interpolatedValues, $value) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 152 | 2 |  |             : $interpolatedValues[0]; | 
            
                                                                                                            
                            
            
                                    
            
            
                | 153 |  |  |     } | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 154 |  |  | } | 
            
                                                        
            
                                    
            
            
                | 155 |  |  |  |