3 namespace Grasmash\YamlExpander;
5 use Dflydev\DotAccessData\Data;
6 use Symfony\Component\Yaml\Yaml;
10 * @package Grasmash\YamlExpander
16 * Parses a YAML string and expands property placeholders.
18 * Placeholders should formatted as ${parent.child}.
20 * @param string $yaml_string
22 * @param array $reference_array
23 * Optional. An array of reference values. This is not operated upon but is used as a
24 * reference to provide supplemental values for property expansion.
27 * The modified array in which placeholders have been replaced with
30 public static function parse($yaml_string, $reference_array = [])
32 $array = Yaml::parse($yaml_string);
33 return self::expandArrayProperties($array, $reference_array);
38 * Expands property placeholders in an array.
40 * Placeholders should formatted as ${parent.child}.
43 * An array containing properties to expand.
46 * The modified array in which placeholders have been replaced with
49 public static function expandArrayProperties($array, $reference_array = [])
51 $data = new Data($array);
52 if ($reference_array) {
53 $reference_data = new Data($reference_array);
54 self::doExpandArrayProperties($data, $array, '', $reference_data);
56 self::doExpandArrayProperties($data, $array);
59 return $data->export();
63 * Performs the actual property expansion.
66 * A data object, containing the $array.
68 * The original, unmodified array.
69 * @param string $parent_keys
70 * The parent keys of the current key in dot notation. This is used to
71 * track the absolute path to the current key in recursive cases.
72 * @param Data|null $reference_data
73 * A reference data object. This is not operated upon but is used as a
74 * reference to provide supplemental values for property expansion.
76 protected static function doExpandArrayProperties(
80 $reference_data = null
82 foreach ($array as $key => $value) {
83 // Boundary condition(s).
84 if (is_null($value) || is_bool($value)) {
88 if (is_array($value)) {
89 self::doExpandArrayProperties($data, $value, $parent_keys . "$key.", $reference_data);
92 self::expandStringProperties($data, $parent_keys, $reference_data, $value, $key);
98 * Expand a single property.
101 * A data object, containing the $array.
102 * @param string $parent_keys
103 * The parent keys of the current key in dot notation. This is used to
104 * track the absolute path to the current key in recursive cases.
105 * @param Data|null $reference_data
106 * A reference data object. This is not operated upon but is used as a
107 * reference to provide supplemental values for property expansion.
108 * @param string $value
109 * The unexpanded property value.
111 * The immediate key of the property.
115 protected static function expandStringProperties(
122 // We loop through all placeholders in a given string.
123 // E.g., '${placeholder1} ${placeholder2}' requires two replacements.
124 while (strpos($value, '${') !== false) {
125 $original_value = $value;
126 $value = preg_replace_callback(
128 function ($matches) use ($data, $reference_data) {
129 return self::expandStringPropertiesCallback(
138 // If no replacement occurred at all, break to prevent
140 if ($original_value == $value) {
144 // Set value on $data object.
146 $full_key = $parent_keys . "$key";
150 $data->set($full_key, $value);
156 * Expansion callback used by preg_replace_callback() in expandProperty().
158 * @param array $matches
159 * An array of matches created by preg_replace_callback().
161 * A data object containing the complete array being operated upon.
162 * @param Data|null $reference_data
163 * A reference data object. This is not operated upon but is used as a
164 * reference to provide supplemental values for property expansion.
168 public static function expandStringPropertiesCallback(
171 $reference_data = null
173 $property_name = $matches[1];
174 $unexpanded_value = $matches[0];
176 // Use only values within the subject array's data.
177 if (!$reference_data) {
178 return self::expandProperty($property_name, $unexpanded_value, $data);
179 } // Search both the subject array's data and the reference data for a value.
181 return self::expandPropertyWithReferenceData(
191 * Searches both the subject data and the reference data for value.
193 * @param string $property_name
194 * The name of the value for which to search.
195 * @param string $unexpanded_value
196 * The original, unexpanded value, containing the placeholder.
198 * A data object containing the complete array being operated upon.
199 * @param Data|null $reference_data
200 * A reference data object. This is not operated upon but is used as a
201 * reference to provide supplemental values for property expansion.
204 * The expanded string.
206 public static function expandPropertyWithReferenceData(
212 $expanded_value = self::expandProperty(
217 // If the string was not changed using the subject data, try using
218 // the reference data.
219 if ($expanded_value == $unexpanded_value) {
220 $expanded_value = self::expandProperty(
227 return $expanded_value;
231 * Searches a data object for a value.
233 * @param string $property_name
234 * The name of the value for which to search.
235 * @param string $unexpanded_value
236 * The original, unexpanded value, containing the placeholder.
238 * A data object containing possible replacement values.
242 public static function expandProperty($property_name, $unexpanded_value, $data)
244 if (strpos($property_name, "env.") === 0 &&
245 !$data->has($property_name)) {
246 $env_key = substr($property_name, 4);
247 if (getenv($env_key)) {
248 $data->set($property_name, getenv($env_key));
252 if (!$data->has($property_name)) {
253 self::log("Property \${'$property_name'} could not be expanded.");
254 return $unexpanded_value;
256 $expanded_value = $data->get($property_name);
257 if (is_array($expanded_value)) {
258 $expanded_value = Yaml::dump($expanded_value, 0);
259 return $expanded_value;
261 self::log("Expanding property \${'$property_name'} => $expanded_value.");
262 return $expanded_value;
269 public static function log($message)
271 // print "$message\n";