Chris@0: export(); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Performs the actual property expansion. Chris@0: * Chris@0: * @param Data $data Chris@0: * A data object, containing the $array. Chris@0: * @param array $array Chris@0: * The original, unmodified array. Chris@0: * @param string $parent_keys Chris@0: * The parent keys of the current key in dot notation. This is used to Chris@0: * track the absolute path to the current key in recursive cases. Chris@0: * @param Data|null $reference_data Chris@0: * A reference data object. This is not operated upon but is used as a Chris@0: * reference to provide supplemental values for property expansion. Chris@0: */ Chris@0: protected static function doExpandArrayProperties( Chris@0: $data, Chris@0: $array, Chris@0: $parent_keys = '', Chris@0: $reference_data = null Chris@0: ) { Chris@0: foreach ($array as $key => $value) { Chris@0: // Boundary condition(s). Chris@0: if (is_null($value) || is_bool($value)) { Chris@0: continue; Chris@0: } Chris@0: // Recursive case. Chris@0: if (is_array($value)) { Chris@0: self::doExpandArrayProperties($data, $value, $parent_keys . "$key.", $reference_data); Chris@0: } // Base case. Chris@0: else { Chris@0: self::expandStringProperties($data, $parent_keys, $reference_data, $value, $key); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Expand a single property. Chris@0: * Chris@0: * @param Data $data Chris@0: * A data object, containing the $array. Chris@0: * @param string $parent_keys Chris@0: * The parent keys of the current key in dot notation. This is used to Chris@0: * track the absolute path to the current key in recursive cases. Chris@0: * @param Data|null $reference_data Chris@0: * A reference data object. This is not operated upon but is used as a Chris@0: * reference to provide supplemental values for property expansion. Chris@0: * @param string $value Chris@0: * The unexpanded property value. Chris@0: * @param string $key Chris@0: * The immediate key of the property. Chris@0: * Chris@0: * @return mixed Chris@0: */ Chris@0: protected static function expandStringProperties( Chris@0: $data, Chris@0: $parent_keys, Chris@0: $reference_data, Chris@0: $value, Chris@0: $key Chris@0: ) { Chris@0: // We loop through all placeholders in a given string. Chris@0: // E.g., '${placeholder1} ${placeholder2}' requires two replacements. Chris@0: while (strpos($value, '${') !== false) { Chris@0: $original_value = $value; Chris@0: $value = preg_replace_callback( Chris@0: '/\$\{([^\$}]+)\}/', Chris@0: function ($matches) use ($data, $reference_data) { Chris@0: return self::expandStringPropertiesCallback( Chris@0: $matches, Chris@0: $data, Chris@0: $reference_data Chris@0: ); Chris@0: }, Chris@0: $value Chris@0: ); Chris@0: Chris@0: // If no replacement occurred at all, break to prevent Chris@0: // infinite loop. Chris@0: if ($original_value == $value) { Chris@0: break; Chris@0: } Chris@0: Chris@0: // Set value on $data object. Chris@0: if ($parent_keys) { Chris@0: $full_key = $parent_keys . "$key"; Chris@0: } else { Chris@0: $full_key = $key; Chris@0: } Chris@0: $data->set($full_key, $value); Chris@0: } Chris@0: return $value; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Expansion callback used by preg_replace_callback() in expandProperty(). Chris@0: * Chris@0: * @param array $matches Chris@0: * An array of matches created by preg_replace_callback(). Chris@0: * @param Data $data Chris@0: * A data object containing the complete array being operated upon. Chris@0: * @param Data|null $reference_data Chris@0: * A reference data object. This is not operated upon but is used as a Chris@0: * reference to provide supplemental values for property expansion. Chris@0: * Chris@0: * @return mixed Chris@0: */ Chris@0: public static function expandStringPropertiesCallback( Chris@0: $matches, Chris@0: $data, Chris@0: $reference_data = null Chris@0: ) { Chris@0: $property_name = $matches[1]; Chris@0: $unexpanded_value = $matches[0]; Chris@0: Chris@0: // Use only values within the subject array's data. Chris@0: if (!$reference_data) { Chris@0: return self::expandProperty($property_name, $unexpanded_value, $data); Chris@0: } // Search both the subject array's data and the reference data for a value. Chris@0: else { Chris@0: return self::expandPropertyWithReferenceData( Chris@0: $property_name, Chris@0: $unexpanded_value, Chris@0: $data, Chris@0: $reference_data Chris@0: ); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Searches both the subject data and the reference data for value. Chris@0: * Chris@0: * @param string $property_name Chris@0: * The name of the value for which to search. Chris@0: * @param string $unexpanded_value Chris@0: * The original, unexpanded value, containing the placeholder. Chris@0: * @param Data $data Chris@0: * A data object containing the complete array being operated upon. Chris@0: * @param Data|null $reference_data Chris@0: * A reference data object. This is not operated upon but is used as a Chris@0: * reference to provide supplemental values for property expansion. Chris@0: * Chris@0: * @return string Chris@0: * The expanded string. Chris@0: */ Chris@0: public static function expandPropertyWithReferenceData( Chris@0: $property_name, Chris@0: $unexpanded_value, Chris@0: $data, Chris@0: $reference_data Chris@0: ) { Chris@0: $expanded_value = self::expandProperty( Chris@0: $property_name, Chris@0: $unexpanded_value, Chris@0: $data Chris@0: ); Chris@0: // If the string was not changed using the subject data, try using Chris@0: // the reference data. Chris@0: if ($expanded_value == $unexpanded_value) { Chris@0: $expanded_value = self::expandProperty( Chris@0: $property_name, Chris@0: $unexpanded_value, Chris@0: $reference_data Chris@0: ); Chris@0: } Chris@0: Chris@0: return $expanded_value; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Searches a data object for a value. Chris@0: * Chris@0: * @param string $property_name Chris@0: * The name of the value for which to search. Chris@0: * @param string $unexpanded_value Chris@0: * The original, unexpanded value, containing the placeholder. Chris@0: * @param Data $data Chris@0: * A data object containing possible replacement values. Chris@0: * Chris@0: * @return mixed Chris@0: */ Chris@0: public static function expandProperty($property_name, $unexpanded_value, $data) Chris@0: { Chris@0: if (strpos($property_name, "env.") === 0 && Chris@0: !$data->has($property_name)) { Chris@0: $env_key = substr($property_name, 4); Chris@0: if (getenv($env_key)) { Chris@0: $data->set($property_name, getenv($env_key)); Chris@0: } Chris@0: } Chris@0: Chris@0: if (!$data->has($property_name)) { Chris@0: self::log("Property \${'$property_name'} could not be expanded."); Chris@0: return $unexpanded_value; Chris@0: } else { Chris@0: $expanded_value = $data->get($property_name); Chris@0: if (is_array($expanded_value)) { Chris@0: $expanded_value = Yaml::dump($expanded_value, 0); Chris@0: return $expanded_value; Chris@0: } Chris@0: self::log("Expanding property \${'$property_name'} => $expanded_value."); Chris@0: return $expanded_value; Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * @param $message Chris@0: */ Chris@0: public static function log($message) Chris@0: { Chris@0: // print "$message\n"; Chris@0: } Chris@0: }