Pull merge.
[yaffs-website] / vendor / grasmash / expander / src / Expander.php
1 <?php
2
3 namespace Grasmash\Expander;
4
5 use Dflydev\DotAccessData\Data;
6 use Psr\Log\LoggerAwareInterface;
7 use Psr\Log\NullLogger;
8
9 /**
10  * Class Expander
11  * @package Grasmash\Expander
12  */
13 class Expander implements LoggerAwareInterface
14 {
15     /**
16      * @var \Grasmash\Expander\StringifierInterface
17      */
18     protected $stringifier;
19     /**
20      * @var \Psr\Log\LoggerInterface
21      */
22     protected $logger;
23
24     public function __construct()
25     {
26         $this->setLogger(new NullLogger());
27         $this->setStringifier(new Stringifier());
28     }
29
30     /**
31      * @return \Grasmash\Expander\StringifierInterface
32      */
33     public function getStringifier()
34     {
35         return $this->stringifier;
36     }
37
38     /**
39      * @param \Grasmash\Expander\StringifierInterface $stringifier
40      */
41     public function setStringifier(\Grasmash\Expander\StringifierInterface $stringifier)
42     {
43         $this->stringifier = $stringifier;
44     }
45
46     /**
47      * @return \Psr\Log\LoggerInterface
48      */
49     public function getLogger()
50     {
51         return $this->logger;
52     }
53
54     /**
55      * @param \Psr\Log\LoggerInterface $logger
56      */
57     public function setLogger(\Psr\Log\LoggerInterface $logger)
58     {
59         $this->logger = $logger;
60     }
61
62     /**
63      * Expands property placeholders in an array.
64      *
65      * Placeholders should formatted as ${parent.child}.
66      *
67      * @param array $array
68      *   An array containing properties to expand.
69      *
70      * @return array
71      *   The modified array in which placeholders have been replaced with
72      *   values.
73      */
74     public function expandArrayProperties($array, $reference_array = [])
75     {
76         $data = new Data($array);
77         if ($reference_array) {
78             $reference_data = new Data($reference_array);
79             $this->doExpandArrayProperties($data, $array, '', $reference_data);
80         } else {
81             $this->doExpandArrayProperties($data, $array);
82         }
83
84         return $data->export();
85     }
86
87     /**
88      * Performs the actual property expansion.
89      *
90      * @param Data $data
91      *   A data object, containing the $array.
92      * @param array $array
93      *   The original, unmodified array.
94      * @param string $parent_keys
95      *   The parent keys of the current key in dot notation. This is used to
96      *   track the absolute path to the current key in recursive cases.
97      * @param Data|null $reference_data
98      *   A reference data object. This is not operated upon but is used as a
99      *   reference to provide supplemental values for property expansion.
100      */
101     protected function doExpandArrayProperties(
102         $data,
103         $array,
104         $parent_keys = '',
105         $reference_data = null
106     ) {
107         foreach ($array as $key => $value) {
108             // Boundary condition(s).
109             if (is_null($value) || is_bool($value)) {
110                 continue;
111             }
112             // Recursive case.
113             if (is_array($value)) {
114                 $this->doExpandArrayProperties($data, $value, $parent_keys . "$key.", $reference_data);
115             } // Base case.
116             else {
117                 $this->expandStringProperties($data, $parent_keys, $reference_data, $value, $key);
118             }
119         }
120     }
121
122     /**
123      * Expand a single property.
124      *
125      * @param Data $data
126      *   A data object, containing the $array.
127      * @param string $parent_keys
128      *   The parent keys of the current key in dot notation. This is used to
129      *   track the absolute path to the current key in recursive cases.
130      * @param Data|null $reference_data
131      *   A reference data object. This is not operated upon but is used as a
132      *   reference to provide supplemental values for property expansion.
133      * @param string $value
134      *   The unexpanded property value.
135      * @param string $key
136      *   The immediate key of the property.
137      *
138      * @return mixed
139      */
140     protected function expandStringProperties(
141         $data,
142         $parent_keys,
143         $reference_data,
144         $value,
145         $key
146     ) {
147         // We loop through all placeholders in a given string.
148         // E.g., '${placeholder1} ${placeholder2}' requires two replacements.
149         while (strpos($value, '${') !== false) {
150             $original_value = $value;
151             $value = preg_replace_callback(
152                 '/\$\{([^\$}]+)\}/',
153                 function ($matches) use ($data, $reference_data) {
154                     return $this->expandStringPropertiesCallback(
155                         $matches,
156                         $data,
157                         $reference_data
158                     );
159                 },
160                 $value
161             );
162
163             // If no replacement occurred at all, break to prevent
164             // infinite loop.
165             if ($original_value == $value) {
166                 break;
167             }
168
169             // Set value on $data object.
170             if ($parent_keys) {
171                 $full_key = $parent_keys . "$key";
172             } else {
173                 $full_key = $key;
174             }
175             $data->set($full_key, $value);
176         }
177         return $value;
178     }
179
180     /**
181      * Expansion callback used by preg_replace_callback() in expandProperty().
182      *
183      * @param array $matches
184      *   An array of matches created by preg_replace_callback().
185      * @param Data $data
186      *   A data object containing the complete array being operated upon.
187      * @param Data|null $reference_data
188      *   A reference data object. This is not operated upon but is used as a
189      *   reference to provide supplemental values for property expansion.
190      *
191      * @return mixed
192      */
193     public function expandStringPropertiesCallback(
194         $matches,
195         $data,
196         $reference_data = null
197     ) {
198         $property_name = $matches[1];
199         $unexpanded_value = $matches[0];
200
201         // Use only values within the subject array's data.
202         if (!$reference_data) {
203             return $this->expandProperty($property_name, $unexpanded_value, $data);
204         } // Search both the subject array's data and the reference data for a value.
205         else {
206             return $this->expandPropertyWithReferenceData(
207                 $property_name,
208                 $unexpanded_value,
209                 $data,
210                 $reference_data
211             );
212         }
213     }
214
215     /**
216      * Searches both the subject data and the reference data for value.
217      *
218      * @param string $property_name
219      *   The name of the value for which to search.
220      * @param string $unexpanded_value
221      *   The original, unexpanded value, containing the placeholder.
222      * @param Data $data
223      *   A data object containing the complete array being operated upon.
224      * @param Data|null $reference_data
225      *   A reference data object. This is not operated upon but is used as a
226      *   reference to provide supplemental values for property expansion.
227      *
228      * @return string
229      *   The expanded string.
230      */
231     public function expandPropertyWithReferenceData(
232         $property_name,
233         $unexpanded_value,
234         $data,
235         $reference_data
236     ) {
237         $expanded_value = $this->expandProperty(
238             $property_name,
239             $unexpanded_value,
240             $data
241         );
242         // If the string was not changed using the subject data, try using
243         // the reference data.
244         if ($expanded_value == $unexpanded_value) {
245             $expanded_value = $this->expandProperty(
246                 $property_name,
247                 $unexpanded_value,
248                 $reference_data
249             );
250         }
251
252         return $expanded_value;
253     }
254
255     /**
256      * Searches a data object for a value.
257      *
258      * @param string $property_name
259      *   The name of the value for which to search.
260      * @param string $unexpanded_value
261      *   The original, unexpanded value, containing the placeholder.
262      * @param Data $data
263      *   A data object containing possible replacement values.
264      *
265      * @return mixed
266      */
267     public function expandProperty($property_name, $unexpanded_value, $data)
268     {
269         if (strpos($property_name, "env.") === 0 &&
270           !$data->has($property_name)) {
271             $env_key = substr($property_name, 4);
272             if (getenv($env_key)) {
273                 $data->set($property_name, getenv($env_key));
274             }
275         }
276
277         if (!$data->has($property_name)) {
278             $this->log("Property \${'$property_name'} could not be expanded.");
279             return $unexpanded_value;
280         } else {
281             $expanded_value = $data->get($property_name);
282             if (is_array($expanded_value)) {
283                 $expanded_value = $this->getStringifier()->stringifyArray($expanded_value);
284                 return $expanded_value;
285             }
286             $this->log("Expanding property \${'$property_name'} => $expanded_value.");
287             return $expanded_value;
288         }
289     }
290
291     /**
292      * Logs a message using the logger.
293      *
294      * @param string $message
295      *   The message to log.
296      */
297     public function log($message)
298     {
299         if ($this->getLogger()) {
300             $this->getLogger()->debug($message);
301         }
302     }
303 }