3 * @copyright Copyright (C) 2010-2023, the Friendica project
5 * @license GNU AGPL version 3 or any later version
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 namespace Friendica\Core\Config\Util;
25 * Util to transform back the config array into a string
27 class ConfigFileTransformer
30 * This method takes an array of config values and applies some standard rules for formatting on it
32 * Beware that the applied rules follow some basic formatting principles for node.config.php
33 * and doesn't support any custom formatting rules.
35 * f.e. associative array and list formatting are very complex with newlines and indentations, thus there are
36 * three hardcoded types of formatting for them.
38 * a negative example, what's NOT working:
39 * key => [ value1, [inner_value1, inner_value2], value2]
40 * Since this isn't necessary for config values, there's no further logic handling such complex-list-in-list scenarios
42 * @param array $data A full config array
44 * @return string The config stream, which can be saved
46 public static function encode(array $data): string
48 // Add the typical header values
49 $dataString = '<?php' . PHP_EOL . PHP_EOL;
50 $dataString .= 'return ';
52 $dataString .= static::extractArray($data);
54 // the last array line, close it with a semicolon
55 $dataString .= ";" . PHP_EOL;
61 * Extracts an inner config array.
62 * Either as an associative array or as a list
64 * @param array $config The config array which should get extracted
65 * @param int $level The current level of recursion (necessary for tab-indentation calculation)
66 * @param bool $inList If true, the current array resides inside another list. Different rules may be applicable
68 * @return string The config string
70 protected static function extractArray(array $config, int $level = 0, bool $inList = false): string
72 if (array_values($config) === $config) {
73 return self::extractList($config, $level, $inList);
75 return self::extractAssociativeArray($config, $level, $inList);
80 * Extracts a key-value array and save it into a string
88 * @param array $config The associative/key-value array
89 * @param int $level The current level of recursion (necessary for tab-indentation calculation)
90 * @param bool $inList If true, the current array resides inside another list. Different rules may be applicable
92 * @return string The config string
94 protected static function extractAssociativeArray(array $config, int $level = 0, bool $inList = false): string
98 // Because we're in a list, we have to add a line-break first
100 $string .= PHP_EOL . str_repeat("\t", $level);
103 // Add a typical Line break for a taxative list of key-value pairs
104 $string .= '[' . PHP_EOL;
106 foreach ($config as $configKey => $configValue) {
107 $string .= str_repeat("\t", $level + 1) .
109 static::transformConfigValue($configValue, $level) .
113 $string .= str_repeat("\t", $level) . ']';
119 * Extracts a list and save it into a string
121 * [ value, value, value ]
125 * [ value, value, value ],
133 * @param array $config The list
134 * @param int $level The current level of recursion (necessary for tab-indentation calculation)
135 * @param bool $inList If true, the current array resides inside another list. Different rules may be applicable
137 * @return string The config string
139 protected static function extractList(array $config, int $level = 0, bool $inList = false): string
143 $countConfigValues = count($config);
144 // multiline defines, if each entry uses a new line
147 // Search if any value is an array, because then other formatting rules are applicable
148 foreach ($config as $item) {
149 if (is_array($item)) {
155 for ($i = 0; $i < $countConfigValues; $i++) {
156 $isArray = is_array($config[$i]);
159 * In case this is an array in an array, directly extract this array again and continue
160 * Skip any other logic since this isn't applicable for an array in an array
163 $string .= PHP_EOL . str_repeat("\t", $level + 1);
164 $string .= static::extractArray($config[$i], $level + 1, $inList) . ',';
169 $string .= PHP_EOL . str_repeat("\t", $level + 1);
172 $string .= static::transformConfigValue($config[$i], $level, true);
174 // add trailing commas or whitespaces for certain config entries
175 if (($i < ($countConfigValues - 1))) {
183 // Add a new line for the last bracket as well
185 $string .= PHP_EOL . str_repeat("\t", $level);
194 * Transforms one config value and returns the corresponding text-representation
196 * @param mixed $value Any value to transform
197 * @param int $level The current level of recursion (necessary for tab-indentation calculation)
198 * @param bool $inList If true, the current array resides inside another list. Different rules may be applicable
202 protected static function transformConfigValue($value, int $level = 0, bool $inList = false): string
204 switch (gettype($value)) {
206 return ($value ? 'true' : 'false');
211 return sprintf('\'%s\'', addcslashes($value, '\'\\'));
213 return static::extractArray($value, ++$level, $inList);
217 if (method_exists($value, '__toString')) {
218 return sprintf('\'%s\'', $value);
219 } elseif ($value instanceof \Serializable) {
221 return $value->serialize();
222 } catch (\Exception $e) {
223 throw new \InvalidArgumentException(sprintf('Cannot serialize %s.', gettype($value)), $e);
226 throw new \InvalidArgumentException(sprintf('%s is an object without stringify.', gettype($value)));
229 case "resource (closed)":
230 throw new \InvalidArgumentException(sprintf('%s in configs are not supported yet.', gettype($value)));
232 throw new \InvalidArgumentException(sprintf('%s is an unknown value', $value));
234 throw new \InvalidArgumentException(sprintf('%s is currently unsupported', $value));