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