3 namespace Grasmash\Expander;
5 use Dflydev\DotAccessData\Data;
6 use Psr\Log\LoggerAwareInterface;
7 use Psr\Log\NullLogger;
11 * @package Grasmash\Expander
13 class Expander implements LoggerAwareInterface
16 * @var \Grasmash\Expander\StringifierInterface
18 protected $stringifier;
20 * @var \Psr\Log\LoggerInterface
24 public function __construct()
26 $this->setLogger(new NullLogger());
27 $this->setStringifier(new Stringifier());
31 * @return \Grasmash\Expander\StringifierInterface
33 public function getStringifier()
35 return $this->stringifier;
39 * @param \Grasmash\Expander\StringifierInterface $stringifier
41 public function setStringifier(\Grasmash\Expander\StringifierInterface $stringifier)
43 $this->stringifier = $stringifier;
47 * @return \Psr\Log\LoggerInterface
49 public function getLogger()
55 * @param \Psr\Log\LoggerInterface $logger
57 public function setLogger(\Psr\Log\LoggerInterface $logger)
59 $this->logger = $logger;
63 * Expands property placeholders in an array.
65 * Placeholders should formatted as ${parent.child}.
68 * An array containing properties to expand.
71 * The modified array in which placeholders have been replaced with
74 public function expandArrayProperties($array, $reference_array = [])
76 $data = new Data($array);
77 if ($reference_array) {
78 $reference_data = new Data($reference_array);
79 $this->doExpandArrayProperties($data, $array, '', $reference_data);
81 $this->doExpandArrayProperties($data, $array);
84 return $data->export();
88 * Performs the actual property expansion.
91 * A data object, containing the $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.
101 protected function doExpandArrayProperties(
105 $reference_data = null
107 foreach ($array as $key => $value) {
108 // Boundary condition(s).
109 if (is_null($value) || is_bool($value)) {
113 if (is_array($value)) {
114 $this->doExpandArrayProperties($data, $value, $parent_keys . "$key.", $reference_data);
117 $this->expandStringProperties($data, $parent_keys, $reference_data, $value, $key);
123 * Expand a single property.
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.
136 * The immediate key of the property.
140 protected function expandStringProperties(
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(
153 function ($matches) use ($data, $reference_data) {
154 return $this->expandStringPropertiesCallback(
163 // If no replacement occurred at all, break to prevent
165 if ($original_value == $value) {
169 // Set value on $data object.
171 $full_key = $parent_keys . "$key";
175 $data->set($full_key, $value);
181 * Expansion callback used by preg_replace_callback() in expandProperty().
183 * @param array $matches
184 * An array of matches created by preg_replace_callback().
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.
193 public function expandStringPropertiesCallback(
196 $reference_data = null
198 $property_name = $matches[1];
199 $unexpanded_value = $matches[0];
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.
206 return $this->expandPropertyWithReferenceData(
216 * Searches both the subject data and the reference data for value.
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.
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.
229 * The expanded string.
231 public function expandPropertyWithReferenceData(
237 $expanded_value = $this->expandProperty(
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(
252 return $expanded_value;
256 * Searches a data object for a value.
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.
263 * A data object containing possible replacement values.
267 public function expandProperty($property_name, $unexpanded_value, $data)
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));
277 if (!$data->has($property_name)) {
278 $this->log("Property \${'$property_name'} could not be expanded.");
279 return $unexpanded_value;
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;
286 $this->log("Expanding property \${'$property_name'} => $expanded_value.");
287 return $expanded_value;
292 * Logs a message using the logger.
294 * @param string $message
295 * The message to log.
297 public function log($message)
299 if ($this->getLogger()) {
300 $this->getLogger()->debug($message);