Conditions | 213 |
Paths | > 20000 |
Total Lines | 869 |
Code Lines | 399 |
Lines | 0 |
Ratio | 0 % |
Changes | 4 | ||
Bugs | 0 | Features | 0 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
1 | <?php |
||
882 | function updateSettingsFile($config_vars, $keep_quotes = null, $rebuild = false) |
||
883 | { |
||
884 | // In this function we intentionally don't declare any global variables. |
||
885 | // This allows us to work with everything cleanly. |
||
886 | |||
887 | static $mtime; |
||
888 | |||
889 | // Should we try to unescape the strings? |
||
890 | if (empty($keep_quotes)) |
||
891 | { |
||
892 | foreach ($config_vars as $var => $val) |
||
893 | { |
||
894 | if (is_string($val) && ($keep_quotes === false || strpos($val, '\'') === 0 && strrpos($val, '\'') === strlen($val) - 1)) |
||
895 | $config_vars[$var] = trim(stripcslashes($val), '\''); |
||
896 | } |
||
897 | } |
||
898 | |||
899 | // Updating the db_last_error, then don't mess around with Settings.php |
||
900 | if (isset($config_vars['db_last_error'])) |
||
901 | { |
||
902 | updateDbLastError($config_vars['db_last_error']); |
||
903 | |||
904 | if (count($config_vars) === 1 && empty($rebuild)) |
||
905 | return true; |
||
906 | |||
907 | // Make sure we delete this from Settings.php, if present. |
||
908 | $config_vars['db_last_error'] = 0; |
||
909 | } |
||
910 | |||
911 | // Rebuilding should not be undertaken lightly, so we're picky about the parameter. |
||
912 | if (!is_bool($rebuild)) |
||
|
|||
913 | $rebuild = false; |
||
914 | |||
915 | $mtime = isset($mtime) ? (int) $mtime : (defined('TIME_START') ? TIME_START : $_SERVER['REQUEST_TIME']); |
||
916 | |||
917 | /***************** |
||
918 | * PART 1: Setup * |
||
919 | *****************/ |
||
920 | |||
921 | // Typically Settings.php is in $boarddir, but maybe this is a custom setup... |
||
922 | foreach (get_included_files() as $settingsFile) |
||
923 | if (basename($settingsFile) === 'Settings.php') |
||
924 | break; |
||
925 | |||
926 | // Fallback in case Settings.php isn't loaded (e.g. while installing) |
||
927 | if (basename($settingsFile) !== 'Settings.php') |
||
928 | $settingsFile = (!empty($GLOBALS['boarddir']) && @realpath($GLOBALS['boarddir']) ? $GLOBALS['boarddir'] : (!empty($_SERVER['SCRIPT_FILENAME']) ? dirname($_SERVER['SCRIPT_FILENAME']) : dirname(__DIR__))) . '/Settings.php'; |
||
929 | |||
930 | // File not found? Attempt an emergency on-the-fly fix! |
||
931 | if (!file_exists($settingsFile)) |
||
932 | @touch($settingsFile); |
||
933 | |||
934 | // When was Settings.php last changed? |
||
935 | $last_settings_change = filemtime($settingsFile); |
||
936 | |||
937 | // Get the current values of everything in Settings.php. |
||
938 | $settings_vars = get_current_settings($mtime, $settingsFile); |
||
939 | |||
940 | // If Settings.php is empty for some reason, see if we can use the backup. |
||
941 | if (empty($settings_vars) && file_exists(dirname($settingsFile) . '/Settings_bak.php')) |
||
942 | $settings_vars = get_current_settings($mtime, dirname($settingsFile) . '/Settings_bak.php'); |
||
943 | |||
944 | // False means there was a problem with the file and we can't safely continue. |
||
945 | if ($settings_vars === false) |
||
946 | return false; |
||
947 | |||
948 | // It works best to set everything afresh. |
||
949 | $new_settings_vars = array_merge($settings_vars, $config_vars); |
||
950 | |||
951 | // Are we using UTF-8? |
||
952 | $utf8 = isset($GLOBALS['context']['utf8']) ? $GLOBALS['context']['utf8'] : (isset($GLOBALS['utf8']) ? $GLOBALS['utf8'] : (isset($settings_vars['db_character_set']) ? $settings_vars['db_character_set'] === 'utf8' : false)); |
||
953 | |||
954 | // Get our definitions for all known Settings.php variables and other content. |
||
955 | $settings_defs = get_settings_defs(); |
||
956 | |||
957 | // If Settings.php is empty or invalid, try to recover using whatever is in $GLOBALS. |
||
958 | if ($settings_vars === array()) |
||
959 | { |
||
960 | foreach ($settings_defs as $var => $setting_def) |
||
961 | if (isset($GLOBALS[$var])) |
||
962 | $settings_vars[$var] = $GLOBALS[$var]; |
||
963 | |||
964 | $new_settings_vars = array_merge($settings_vars, $config_vars); |
||
965 | } |
||
966 | |||
967 | // During install/upgrade, don't set anything until we're ready for it. |
||
968 | if (defined('SMF_INSTALLING') && empty($rebuild)) |
||
969 | { |
||
970 | foreach ($settings_defs as $var => $setting_def) |
||
971 | if (!in_array($var, array_keys($new_settings_vars)) && !is_int($var)) |
||
972 | unset($settings_defs[$var]); |
||
973 | } |
||
974 | |||
975 | /******************************* |
||
976 | * PART 2: Build substitutions * |
||
977 | *******************************/ |
||
978 | |||
979 | $type_regex = array( |
||
980 | 'string' => |
||
981 | '(?:' . |
||
982 | // match the opening quotation mark... |
||
983 | '(["\'])' . |
||
984 | // then any number of other characters or escaped quotation marks... |
||
985 | '(?:.(?!\\1)|\\\(?=\\1))*.?' . |
||
986 | // then the closing quotation mark. |
||
987 | '\\1' . |
||
988 | // Maybe there's a second string concatenated to this one. |
||
989 | '(?:\s*\.\s*)*' . |
||
990 | ')+', |
||
991 | // Some numeric values might have been stored as strings. |
||
992 | 'integer' => '["\']?[+-]?\d+["\']?', |
||
993 | 'double' => '["\']?[+-]?\d+\.\d+([Ee][+-]\d+)?["\']?', |
||
994 | // Some boolean values might have been stored as integers. |
||
995 | 'boolean' => '(?i:TRUE|FALSE|(["\']?)[01]\b\\1)', |
||
996 | 'NULL' => '(?i:NULL)', |
||
997 | // These use a PCRE subroutine to match nested arrays. |
||
998 | 'array' => 'array\s*(\((?'.'>[^()]|(?1))*\))', |
||
999 | 'object' => '\w+::__set_state\(array\s*(\((?'.'>[^()]|(?1))*\))\)', |
||
1000 | ); |
||
1001 | |||
1002 | /* |
||
1003 | * The substitutions take place in one of two ways: |
||
1004 | * |
||
1005 | * 1: The search_pattern regex finds a string in Settings.php, which is |
||
1006 | * temporarily replaced by a placeholder. Once all the placeholders |
||
1007 | * have been inserted, each is replaced by the final replacement string |
||
1008 | * that we want to use. This is the standard method. |
||
1009 | * |
||
1010 | * 2: The search_pattern regex finds a string in Settings.php, which is |
||
1011 | * then deleted by replacing it with an empty placeholder. Then after |
||
1012 | * all the real placeholders have been dealt with, the replace_pattern |
||
1013 | * regex finds where to insert the final replacement string that we |
||
1014 | * want to use. This method is for special cases. |
||
1015 | */ |
||
1016 | $prefix = mt_rand() . '-'; |
||
1017 | $neg_index = -1; |
||
1018 | $substitutions = array( |
||
1019 | $neg_index-- => array( |
||
1020 | 'search_pattern' => '~^\s*<\?(php\b)?\n?~', |
||
1021 | 'placeholder' => '', |
||
1022 | 'replace_pattern' => '~^~', |
||
1023 | 'replacement' => '<' . "?php\n", |
||
1024 | ), |
||
1025 | $neg_index-- => array( |
||
1026 | 'search_pattern' => '~\S\K\s*(\?' . '>)?\s*$~', |
||
1027 | 'placeholder' => "\n" . md5($prefix . '?' . '>'), |
||
1028 | 'replacement' => "\n\n?" . '>', |
||
1029 | ), |
||
1030 | // Remove the code that redirects to the installer. |
||
1031 | $neg_index-- => array( |
||
1032 | 'search_pattern' => '~^if\s*\(file_exists\(dirname\(__FILE__\)\s*\.\s*\'/install\.php\'\)\)\s*(?:({(?'.'>[^{}]|(?1))*})\h*|header(\((?' . '>[^()]|(?2))*\));\n)~m', |
||
1033 | 'placeholder' => '', |
||
1034 | ), |
||
1035 | ); |
||
1036 | |||
1037 | if (defined('SMF_INSTALLING')) |
||
1038 | $substitutions[$neg_index--] = array( |
||
1039 | 'search_pattern' => '~/\*.*?SMF\s+1\.\d.*?\*/~s', |
||
1040 | 'placeholder' => '', |
||
1041 | ); |
||
1042 | |||
1043 | foreach ($settings_defs as $var => $setting_def) |
||
1044 | { |
||
1045 | $placeholder = md5($prefix . $var); |
||
1046 | $replacement = ''; |
||
1047 | |||
1048 | if (!empty($setting_def['text'])) |
||
1049 | { |
||
1050 | // Special handling for the license block: always at the beginning. |
||
1051 | if (strpos($setting_def['text'], "* @package SMF\n") !== false) |
||
1052 | { |
||
1053 | $substitutions[$var]['search_pattern'] = $setting_def['search_pattern']; |
||
1054 | $substitutions[$var]['placeholder'] = ''; |
||
1055 | $substitutions[-1]['replacement'] .= $setting_def['text'] . "\n"; |
||
1056 | } |
||
1057 | // Special handling for the Error-Catching block: always at the end. |
||
1058 | elseif (strpos($setting_def['text'], 'Error-Catching') !== false) |
||
1059 | { |
||
1060 | $errcatch_var = $var; |
||
1061 | $substitutions[$var]['search_pattern'] = $setting_def['search_pattern']; |
||
1062 | $substitutions[$var]['placeholder'] = ''; |
||
1063 | $substitutions[-2]['replacement'] = "\n" . $setting_def['text'] . $substitutions[-2]['replacement']; |
||
1064 | } |
||
1065 | // The text is the whole thing (code blocks, etc.) |
||
1066 | elseif (is_int($var)) |
||
1067 | { |
||
1068 | // Remember the path correcting code for later. |
||
1069 | if (strpos($setting_def['text'], '# Make sure the paths are correct') !== false) |
||
1070 | $pathcode_var = $var; |
||
1071 | |||
1072 | if (!empty($setting_def['search_pattern'])) |
||
1073 | $substitutions[$var]['search_pattern'] = $setting_def['search_pattern']; |
||
1074 | else |
||
1075 | $substitutions[$var]['search_pattern'] = '~' . preg_quote($setting_def['text'], '~') . '~'; |
||
1076 | |||
1077 | $substitutions[$var]['placeholder'] = $placeholder; |
||
1078 | |||
1079 | $replacement .= $setting_def['text'] . "\n"; |
||
1080 | } |
||
1081 | // We only include comments when rebuilding. |
||
1082 | elseif (!empty($rebuild)) |
||
1083 | $replacement .= $setting_def['text'] . "\n"; |
||
1084 | } |
||
1085 | |||
1086 | if (is_string($var)) |
||
1087 | { |
||
1088 | // Ensure the value is good. |
||
1089 | if (in_array($var, array_keys($new_settings_vars))) |
||
1090 | { |
||
1091 | // Objects without a __set_state method need a fallback. |
||
1092 | if (is_object($new_settings_vars[$var]) && !method_exists($new_settings_vars[$var], '__set_state')) |
||
1093 | { |
||
1094 | if (method_exists($new_settings_vars[$var], '__toString')) |
||
1095 | $new_settings_vars[$var] = (string) $new_settings_vars[$var]; |
||
1096 | else |
||
1097 | $new_settings_vars[$var] = (array) $new_settings_vars[$var]; |
||
1098 | } |
||
1099 | |||
1100 | // Normalize the type if necessary. |
||
1101 | if (isset($setting_def['type'])) |
||
1102 | { |
||
1103 | $expected_types = (array) $setting_def['type']; |
||
1104 | $var_type = gettype($new_settings_vars[$var]); |
||
1105 | |||
1106 | // Variable is not of an expected type. |
||
1107 | if (!in_array($var_type, $expected_types)) |
||
1108 | { |
||
1109 | // Passed in an unexpected array. |
||
1110 | if ($var_type == 'array') |
||
1111 | { |
||
1112 | $temp = reset($new_settings_vars[$var]); |
||
1113 | |||
1114 | // Use the first element if there's only one and it is a scalar. |
||
1115 | if (count($new_settings_vars[$var]) === 1 && is_scalar($temp)) |
||
1116 | $new_settings_vars[$var] = $temp; |
||
1117 | |||
1118 | // Or keep the old value, if that is good. |
||
1119 | elseif (isset($settings_vars[$var]) && in_array(gettype($settings_vars[$var]), $expected_types)) |
||
1120 | $new_settings_vars[$var] = $settings_vars[$var]; |
||
1121 | |||
1122 | // Fall back to the default |
||
1123 | else |
||
1124 | $new_settings_vars[$var] = $setting_def['default']; |
||
1125 | } |
||
1126 | |||
1127 | // Cast it to whatever type was expected. |
||
1128 | // Note: the order of the types in this loop matters. |
||
1129 | foreach (array('boolean', 'integer', 'double', 'string', 'array') as $to_type) |
||
1130 | { |
||
1131 | if (in_array($to_type, $expected_types)) |
||
1132 | { |
||
1133 | settype($new_settings_vars[$var], $to_type); |
||
1134 | break; |
||
1135 | } |
||
1136 | } |
||
1137 | } |
||
1138 | } |
||
1139 | } |
||
1140 | // Abort if a required one is undefined (unless we're installing). |
||
1141 | elseif (!empty($setting_def['required']) && !defined('SMF_INSTALLING')) |
||
1142 | return false; |
||
1143 | |||
1144 | // Create the search pattern. |
||
1145 | if (!empty($setting_def['search_pattern'])) |
||
1146 | $substitutions[$var]['search_pattern'] = $setting_def['search_pattern']; |
||
1147 | else |
||
1148 | { |
||
1149 | $var_pattern = array(); |
||
1150 | |||
1151 | if (isset($setting_def['type'])) |
||
1152 | { |
||
1153 | foreach ((array) $setting_def['type'] as $type) |
||
1154 | $var_pattern[] = $type_regex[$type]; |
||
1155 | } |
||
1156 | |||
1157 | if (in_array($var, array_keys($config_vars))) |
||
1158 | { |
||
1159 | $var_pattern[] = @$type_regex[gettype($config_vars[$var])]; |
||
1160 | |||
1161 | if (is_string($config_vars[$var]) && strpos($config_vars[$var], dirname($settingsFile)) === 0) |
||
1162 | $var_pattern[] = '(?:__DIR__|dirname\(__FILE__\)) . \'' . (preg_quote(str_replace(dirname($settingsFile), '', $config_vars[$var]), '~')) . '\''; |
||
1163 | } |
||
1164 | |||
1165 | if (in_array($var, array_keys($settings_vars))) |
||
1166 | { |
||
1167 | $var_pattern[] = @$type_regex[gettype($settings_vars[$var])]; |
||
1168 | |||
1169 | if (is_string($settings_vars[$var]) && strpos($settings_vars[$var], dirname($settingsFile)) === 0) |
||
1170 | $var_pattern[] = '(?:__DIR__|dirname\(__FILE__\)) . \'' . (preg_quote(str_replace(dirname($settingsFile), '', $settings_vars[$var]), '~')) . '\''; |
||
1171 | } |
||
1172 | |||
1173 | if (!empty($setting_def['raw_default']) && $setting_def['default'] !== '') |
||
1174 | { |
||
1175 | $var_pattern[] = preg_replace('/\s+/', '\s+', preg_quote($setting_def['default'], '~')); |
||
1176 | |||
1177 | if (strpos($setting_def['default'], 'dirname(__FILE__)') !== false) |
||
1178 | $var_pattern[] = preg_replace('/\s+/', '\s+', preg_quote(str_replace('dirname(__FILE__)', '__DIR__', $setting_def['default']), '~')); |
||
1179 | |||
1180 | if (strpos($setting_def['default'], '__DIR__') !== false) |
||
1181 | $var_pattern[] = preg_replace('/\s+/', '\s+', preg_quote(str_replace('__DIR__', 'dirname(__FILE__)', $setting_def['default']), '~')); |
||
1182 | } |
||
1183 | |||
1184 | $var_pattern = array_unique($var_pattern); |
||
1185 | |||
1186 | $var_pattern = count($var_pattern) > 1 ? '(?:' . (implode('|', $var_pattern)) . ')' : $var_pattern[0]; |
||
1187 | |||
1188 | $substitutions[$var]['search_pattern'] = '~(?<=^|\s)\h*\$' . preg_quote($var, '~') . '\s*=\s*' . $var_pattern . ';~' . (!empty($utf8) ? 'u' : ''); |
||
1189 | } |
||
1190 | |||
1191 | // Next create the placeholder or replace_pattern. |
||
1192 | if (!empty($setting_def['replace_pattern'])) |
||
1193 | $substitutions[$var]['replace_pattern'] = $setting_def['replace_pattern']; |
||
1194 | else |
||
1195 | $substitutions[$var]['placeholder'] = $placeholder; |
||
1196 | |||
1197 | // Now create the replacement. |
||
1198 | // A setting to delete. |
||
1199 | if (!empty($setting_def['auto_delete']) && empty($new_settings_vars[$var])) |
||
1200 | { |
||
1201 | if ($setting_def['auto_delete'] === 2 && empty($rebuild) && in_array($var, array_keys($new_settings_vars))) |
||
1202 | { |
||
1203 | $replacement .= '$' . $var . ' = ' . ($new_settings_vars[$var] === $setting_def['default'] && !empty($setting_def['raw_default']) ? sprintf($new_settings_vars[$var]) : smf_var_export($new_settings_vars[$var], true)) . ";"; |
||
1204 | } |
||
1205 | else |
||
1206 | { |
||
1207 | $replacement = ''; |
||
1208 | $substitutions[$var]['placeholder'] = ''; |
||
1209 | |||
1210 | // This is just for cosmetic purposes. Removes the blank line. |
||
1211 | $substitutions[$var]['search_pattern'] = str_replace('(?<=^|\s)', '\n?', $substitutions[$var]['search_pattern']); |
||
1212 | } |
||
1213 | } |
||
1214 | // Add this setting's value. |
||
1215 | elseif (in_array($var, array_keys($new_settings_vars))) |
||
1216 | { |
||
1217 | $replacement .= '$' . $var . ' = ' . ($new_settings_vars[$var] === $setting_def['default'] && !empty($setting_def['raw_default']) ? sprintf($new_settings_vars[$var]) : smf_var_export($new_settings_vars[$var], true)) . ";"; |
||
1218 | } |
||
1219 | // Fall back to the default value. |
||
1220 | elseif (isset($setting_def['default'])) |
||
1221 | { |
||
1222 | $replacement .= '$' . $var . ' = ' . (!empty($setting_def['raw_default']) ? sprintf($setting_def['default']) : smf_var_export($setting_def['default'], true)) . ';'; |
||
1223 | } |
||
1224 | // This shouldn't happen, but we've got nothing. |
||
1225 | else |
||
1226 | $replacement .= '$' . $var . ' = null;'; |
||
1227 | } |
||
1228 | |||
1229 | $substitutions[$var]['replacement'] = $replacement; |
||
1230 | |||
1231 | // We're done with this one. |
||
1232 | unset($new_settings_vars[$var]); |
||
1233 | } |
||
1234 | |||
1235 | // Any leftovers to deal with? |
||
1236 | foreach ($new_settings_vars as $var => $val) |
||
1237 | { |
||
1238 | $var_pattern = array(); |
||
1239 | |||
1240 | if (in_array($var, array_keys($config_vars))) |
||
1241 | $var_pattern[] = $type_regex[gettype($config_vars[$var])]; |
||
1242 | |||
1243 | if (in_array($var, array_keys($settings_vars))) |
||
1244 | $var_pattern[] = $type_regex[gettype($settings_vars[$var])]; |
||
1245 | |||
1246 | $var_pattern = array_unique($var_pattern); |
||
1247 | |||
1248 | $var_pattern = count($var_pattern) > 1 ? '(?:' . (implode('|', $var_pattern)) . ')' : $var_pattern[0]; |
||
1249 | |||
1250 | $placeholder = md5($prefix . $var); |
||
1251 | |||
1252 | $substitutions[$var]['search_pattern'] = '~(?<=^|\s)\h*\$' . preg_quote($var, '~') . '\s*=\s*' . $var_pattern . ';~' . (!empty($utf8) ? 'u' : ''); |
||
1253 | $substitutions[$var]['placeholder'] = $placeholder; |
||
1254 | $substitutions[$var]['replacement'] = '$' . $var . ' = ' . smf_var_export($val, true) . ";"; |
||
1255 | } |
||
1256 | |||
1257 | // During an upgrade, some of the path variables may not have been declared yet. |
||
1258 | if (defined('SMF_INSTALLING') && empty($rebuild)) |
||
1259 | { |
||
1260 | preg_match_all('~^\h*\$(\w+)\s*=\s*~m', $substitutions[$pathcode_var]['replacement'], $matches); |
||
1261 | $missing_pathvars = array_diff($matches[1], array_keys($substitutions)); |
||
1262 | |||
1263 | if (!empty($missing_pathvars)) |
||
1264 | { |
||
1265 | foreach ($missing_pathvars as $var) |
||
1266 | { |
||
1267 | $substitutions[$pathcode_var]['replacement'] = preg_replace('~\nif[^\n]+\$' . $var . '[^\n]+\n\h*\$' . $var . ' = [^\n]+~', '', $substitutions[$pathcode_var]['replacement']); |
||
1268 | } |
||
1269 | } |
||
1270 | } |
||
1271 | |||
1272 | // It's important to do the numbered ones before the named ones, or messes happen. |
||
1273 | uksort( |
||
1274 | $substitutions, |
||
1275 | function($a, $b) { |
||
1276 | if (is_int($a) && is_int($b)) |
||
1277 | return $a > $b ? 1 : ($a < $b ? -1 : 0); |
||
1278 | elseif (is_int($a)) |
||
1279 | return -1; |
||
1280 | elseif (is_int($b)) |
||
1281 | return 1; |
||
1282 | else |
||
1283 | return strcasecmp($b, $a); |
||
1284 | } |
||
1285 | ); |
||
1286 | |||
1287 | /****************************** |
||
1288 | * PART 3: Content processing * |
||
1289 | ******************************/ |
||
1290 | |||
1291 | /* 3.a: Get the content of Settings.php and make sure it is good. */ |
||
1292 | |||
1293 | // Retrieve the contents of Settings.php and normalize the line endings. |
||
1294 | $settingsText = trim(strtr(file_get_contents($settingsFile), array("\r\n" => "\n", "\r" => "\n"))); |
||
1295 | |||
1296 | // If Settings.php is empty or corrupt for some reason, see if we can recover. |
||
1297 | if ($settingsText == '' || substr($settingsText, 0, 5) !== '<' . '?php') |
||
1298 | { |
||
1299 | // Try restoring from the backup. |
||
1300 | if (file_exists(dirname($settingsFile) . '/Settings_bak.php')) |
||
1301 | $settingsText = strtr(file_get_contents(dirname($settingsFile) . '/Settings_bak.php'), array("\r\n" => "\n", "\r" => "\n")); |
||
1302 | |||
1303 | // Backup is bad too? Our only option is to create one from scratch. |
||
1304 | if ($settingsText == '' || substr($settingsText, 0, 5) !== '<' . '?php' || substr($settingsText, -2) !== '?' . '>') |
||
1305 | { |
||
1306 | $settingsText = '<' . "?php\n"; |
||
1307 | foreach ($settings_defs as $var => $setting_def) |
||
1308 | { |
||
1309 | if (is_string($var) && !empty($setting_def['text']) && strpos($substitutions[$var]['replacement'], $setting_def['text']) === false) |
||
1310 | $substitutions[$var]['replacement'] = $setting_def['text'] . "\n" . $substitutions[$var]['replacement']; |
||
1311 | |||
1312 | $settingsText .= $substitutions[$var]['replacement'] . "\n"; |
||
1313 | } |
||
1314 | $settingsText .= "\n\n?" . '>'; |
||
1315 | $rebuild = true; |
||
1316 | } |
||
1317 | } |
||
1318 | |||
1319 | // Settings.php is unlikely to contain any heredocs, but just in case... |
||
1320 | if (preg_match_all('/<<<([\'"]?)(\w+)\1\R(.*?)\R\h*\2;$/ms', $settingsText, $matches)) |
||
1321 | { |
||
1322 | foreach ($matches[0] as $mkey => $heredoc) |
||
1323 | { |
||
1324 | if (!empty($matches[1][$mkey]) && $matches[1][$mkey] === '\'') |
||
1325 | $heredoc_replacements[$heredoc] = var_export($matches[3][$mkey], true) . ';'; |
||
1326 | else |
||
1327 | $heredoc_replacements[$heredoc] = '"' . strtr(substr(var_export($matches[3][$mkey], true), 1, -1), array("\\'" => "'", '"' => '\"')) . '";'; |
||
1328 | } |
||
1329 | |||
1330 | $settingsText = strtr($settingsText, $heredoc_replacements); |
||
1331 | } |
||
1332 | |||
1333 | /* 3.b: Loop through all our substitutions to insert placeholders, etc. */ |
||
1334 | |||
1335 | $last_var = null; |
||
1336 | $bare_settingsText = $settingsText; |
||
1337 | $force_before_pathcode = array(); |
||
1338 | foreach ($substitutions as $var => $substitution) |
||
1339 | { |
||
1340 | $placeholders[$var] = $substitution['placeholder']; |
||
1341 | |||
1342 | if (!empty($substitution['placeholder'])) |
||
1343 | { |
||
1344 | $simple_replacements[$substitution['placeholder']] = $substitution['replacement']; |
||
1345 | } |
||
1346 | elseif (!empty($substitution['replace_pattern'])) |
||
1347 | { |
||
1348 | $replace_patterns[$var] = $substitution['replace_pattern']; |
||
1349 | $replace_strings[$var] = $substitution['replacement']; |
||
1350 | } |
||
1351 | |||
1352 | if (strpos($substitutions[$pathcode_var]['replacement'], '$' . $var . ' = ') !== false) |
||
1353 | $force_before_pathcode[] = $var; |
||
1354 | |||
1355 | // Look before you leap. |
||
1356 | preg_match_all($substitution['search_pattern'], $bare_settingsText, $matches); |
||
1357 | |||
1358 | if ((is_string($var) || $var === $pathcode_var) && count($matches[0]) !== 1 && $substitution['replacement'] !== '') |
||
1359 | { |
||
1360 | // More than one instance of the variable = not good. |
||
1361 | if (count($matches[0]) > 1) |
||
1362 | { |
||
1363 | if (is_string($var)) |
||
1364 | { |
||
1365 | // Maybe we can try something more interesting? |
||
1366 | $sp = substr($substitution['search_pattern'], 1); |
||
1367 | |||
1368 | if (strpos($sp, '(?<=^|\s)') === 0) |
||
1369 | $sp = substr($sp, 9); |
||
1370 | |||
1371 | if (strpos($sp, '^') === 0 || strpos($sp, '(?<') === 0) |
||
1372 | return false; |
||
1373 | |||
1374 | // See if we can exclude `if` blocks, etc., to narrow down the matches. |
||
1375 | // @todo Multiple layers of nested brackets might confuse this. |
||
1376 | $sp = '~(?:^|//[^\n]+c\n|\*/|[;}]|' . implode('|', array_filter($placeholders)) . ')\s*' . (strpos($sp, '\K') === false ? '\K' : '') . $sp; |
||
1377 | |||
1378 | preg_match_all($sp, $settingsText, $matches); |
||
1379 | } |
||
1380 | else |
||
1381 | $sp = $substitution['search_pattern']; |
||
1382 | |||
1383 | // Found at least some that are simple assignment statements. |
||
1384 | if (count($matches[0]) > 0) |
||
1385 | { |
||
1386 | // Remove any duplicates. |
||
1387 | if (count($matches[0]) > 1) |
||
1388 | $settingsText = preg_replace($sp, '', $settingsText, count($matches[0]) - 1); |
||
1389 | |||
1390 | // Insert placeholder for the last one. |
||
1391 | $settingsText = preg_replace($sp, $substitution['placeholder'], $settingsText, 1); |
||
1392 | } |
||
1393 | |||
1394 | // All instances are inside more complex code structures. |
||
1395 | else |
||
1396 | { |
||
1397 | // Only safe option at this point is to skip it. |
||
1398 | unset($substitutions[$var], $new_settings_vars[$var], $settings_defs[$var], $simple_replacements[$substitution['placeholder']], $replace_patterns[$var], $replace_strings[$var]); |
||
1399 | |||
1400 | continue; |
||
1401 | } |
||
1402 | } |
||
1403 | // No matches found. |
||
1404 | elseif (count($matches[0]) === 0) |
||
1405 | { |
||
1406 | $found = false; |
||
1407 | $in_c = in_array($var, array_keys($config_vars)); |
||
1408 | $in_s = in_array($var, array_keys($settings_vars)); |
||
1409 | |||
1410 | // Is it in there at all? |
||
1411 | if (!preg_match('~(^|\s)\$' . preg_quote($var, '~') . '\s*=\s*~', $bare_settingsText)) |
||
1412 | { |
||
1413 | // It's defined by Settings.php, but not by code in the file. |
||
1414 | // Probably done via an include or something. Skip it. |
||
1415 | if ($in_s) |
||
1416 | unset($substitutions[$var], $settings_defs[$var]); |
||
1417 | |||
1418 | // Admin is explicitly trying to set this one, so we'll handle |
||
1419 | // it as if it were a new custom setting being added. |
||
1420 | elseif ($in_c) |
||
1421 | $new_settings_vars[$var] = $config_vars[$var]; |
||
1422 | |||
1423 | continue; |
||
1424 | } |
||
1425 | |||
1426 | // It's in there somewhere, so check if the value changed type. |
||
1427 | foreach (array('scalar', 'object', 'array') as $type) |
||
1428 | { |
||
1429 | // Try all the other scalar types first. |
||
1430 | if ($type == 'scalar') |
||
1431 | $sp = '(?:' . (implode('|', array_diff_key($type_regex, array($in_c ? gettype($config_vars[$var]) : ($in_s ? gettype($settings_vars[$var]) : PHP_INT_MAX) => '', 'array' => '', 'object' => '')))) . ')'; |
||
1432 | |||
1433 | // Maybe it's an object? (Probably not, but we should check.) |
||
1434 | elseif ($type == 'object') |
||
1435 | { |
||
1436 | if (strpos($settingsText, '__set_state') === false) |
||
1437 | continue; |
||
1438 | |||
1439 | $sp = $type_regex['object']; |
||
1440 | } |
||
1441 | |||
1442 | // Maybe it's an array? |
||
1443 | else |
||
1444 | $sp = $type_regex['array']; |
||
1445 | |||
1446 | if (preg_match('~(^|\s)\$' . preg_quote($var, '~') . '\s*=\s*' . $sp . '~', $bare_settingsText, $derp)) |
||
1447 | { |
||
1448 | $settingsText = preg_replace('~(^|\s)\$' . preg_quote($var, '~') . '\s*=\s*' . $sp . '~', $substitution['placeholder'], $settingsText); |
||
1449 | $found = true; |
||
1450 | break; |
||
1451 | } |
||
1452 | } |
||
1453 | |||
1454 | // Something weird is going on. Better just leave it alone. |
||
1455 | if (!$found) |
||
1456 | { |
||
1457 | // $var? What $var? Never heard of it. |
||
1458 | unset($substitutions[$var], $new_settings_vars[$var], $settings_defs[$var], $simple_replacements[$substitution['placeholder']], $replace_patterns[$var], $replace_strings[$var]); |
||
1459 | continue; |
||
1460 | } |
||
1461 | } |
||
1462 | } |
||
1463 | // Good to go, so insert our placeholder. |
||
1464 | else |
||
1465 | $settingsText = preg_replace($substitution['search_pattern'], $substitution['placeholder'], $settingsText); |
||
1466 | |||
1467 | // Once the code blocks are done, we want to compare to a version without comments. |
||
1468 | if (is_int($last_var) && is_string($var)) |
||
1469 | $bare_settingsText = strip_php_comments($settingsText); |
||
1470 | |||
1471 | $last_var = $var; |
||
1472 | } |
||
1473 | |||
1474 | // Rebuilding requires more work. |
||
1475 | if (!empty($rebuild)) |
||
1476 | { |
||
1477 | // Strip out the leading and trailing placeholders to prevent duplication. |
||
1478 | $settingsText = str_replace(array($substitutions[-1]['placeholder'], $substitutions[-2]['placeholder']), '', $settingsText); |
||
1479 | |||
1480 | // Strip out all our standard comments. |
||
1481 | foreach ($settings_defs as $var => $setting_def) |
||
1482 | { |
||
1483 | if (isset($setting_def['text'])) |
||
1484 | $settingsText = strtr($settingsText, array($setting_def['text'] . "\n" => '', $setting_def['text'] => '',)); |
||
1485 | } |
||
1486 | |||
1487 | // We need to refresh $bare_settingsText at this point. |
||
1488 | $bare_settingsText = strip_php_comments($settingsText); |
||
1489 | |||
1490 | // Fix up whitespace to make comparison easier. |
||
1491 | foreach ($placeholders as $placeholder) |
||
1492 | { |
||
1493 | $bare_settingsText = str_replace(array($placeholder . "\n\n", $placeholder), $placeholder . "\n", $bare_settingsText); |
||
1494 | } |
||
1495 | $bare_settingsText = preg_replace('/\h+$/m', '', rtrim($bare_settingsText)); |
||
1496 | |||
1497 | /* |
||
1498 | * Divide the existing content into sections. |
||
1499 | * The idea here is to make sure we don't mess with the relative position |
||
1500 | * of any code blocks in the file, since that could break things. Within |
||
1501 | * each section, however, we'll reorganize the content to match the |
||
1502 | * default layout as closely as we can. |
||
1503 | */ |
||
1504 | $sections = array(array()); |
||
1505 | $section_num = 0; |
||
1506 | $trimmed_placeholders = array_filter(array_map('trim', $placeholders)); |
||
1507 | $newsection_placeholders = array(); |
||
1508 | $all_custom_content = ''; |
||
1509 | foreach ($substitutions as $var => $substitution) |
||
1510 | { |
||
1511 | if (is_int($var) && ($var === -2 || $var > 0) && isset($trimmed_placeholders[$var]) && strpos($bare_settingsText, $trimmed_placeholders[$var]) !== false) |
||
1512 | $newsection_placeholders[$var] = $trimmed_placeholders[$var]; |
||
1513 | } |
||
1514 | foreach (preg_split('~(?<=' . implode('|', $trimmed_placeholders) . ')|(?=' . implode('|', $trimmed_placeholders) . ')~', $bare_settingsText) as $part) |
||
1515 | { |
||
1516 | $part = trim($part); |
||
1517 | |||
1518 | if (empty($part)) |
||
1519 | continue; |
||
1520 | |||
1521 | // Build a list of placeholders for this section. |
||
1522 | if (in_array($part, $trimmed_placeholders) && !in_array($part, $newsection_placeholders)) |
||
1523 | { |
||
1524 | $sections[$section_num][] = $part; |
||
1525 | } |
||
1526 | // Custom content and newsection_placeholders get their own sections. |
||
1527 | else |
||
1528 | { |
||
1529 | if (!empty($sections[$section_num])) |
||
1530 | ++$section_num; |
||
1531 | |||
1532 | $sections[$section_num][] = $part; |
||
1533 | |||
1534 | ++$section_num; |
||
1535 | |||
1536 | if (!in_array($part, $trimmed_placeholders)) |
||
1537 | $all_custom_content .= "\n" . $part; |
||
1538 | } |
||
1539 | } |
||
1540 | |||
1541 | // And now, rebuild the content! |
||
1542 | $new_settingsText = ''; |
||
1543 | $done_defs = array(); |
||
1544 | $sectionkeys = array_keys($sections); |
||
1545 | foreach ($sections as $sectionkey => $section) |
||
1546 | { |
||
1547 | // Custom content needs to be preserved. |
||
1548 | if (count($section) === 1 && !in_array($section[0], $trimmed_placeholders)) |
||
1549 | { |
||
1550 | $prev_section_end = $sectionkey < 1 ? 0 : strpos($settingsText, end($sections[$sectionkey - 1])) + strlen(end($sections[$sectionkey - 1])); |
||
1551 | $next_section_start = $sectionkey == end($sectionkeys) ? strlen($settingsText) : strpos($settingsText, $sections[$sectionkey + 1][0]); |
||
1552 | |||
1553 | $new_settingsText .= "\n" . substr($settingsText, $prev_section_end, $next_section_start - $prev_section_end) . "\n"; |
||
1554 | } |
||
1555 | // Put the placeholders in this section into canonical order. |
||
1556 | else |
||
1557 | { |
||
1558 | $section_parts = array_flip($section); |
||
1559 | $pathcode_reached = false; |
||
1560 | foreach ($settings_defs as $var => $setting_def) |
||
1561 | { |
||
1562 | if ($var === $pathcode_var) |
||
1563 | $pathcode_reached = true; |
||
1564 | |||
1565 | // Already did this setting, so move on to the next. |
||
1566 | if (in_array($var, $done_defs)) |
||
1567 | continue; |
||
1568 | |||
1569 | // Stop when we hit a setting definition that will start a later section. |
||
1570 | if (isset($newsection_placeholders[$var]) && count($section) !== 1) |
||
1571 | break; |
||
1572 | |||
1573 | // Stop when everything in this section is done, unless it's the last. |
||
1574 | // This helps maintain the relative position of any custom content. |
||
1575 | if (empty($section_parts) && $sectionkey < (count($sections) - 1)) |
||
1576 | break; |
||
1577 | |||
1578 | $p = trim($substitutions[$var]['placeholder']); |
||
1579 | |||
1580 | // Can't do anything with an empty placeholder. |
||
1581 | if ($p === '') |
||
1582 | continue; |
||
1583 | |||
1584 | // Does this need to be inserted before the path correction code? |
||
1585 | if (strpos($new_settingsText, trim($substitutions[$pathcode_var]['placeholder'])) !== false && in_array($var, $force_before_pathcode)) |
||
1586 | { |
||
1587 | $new_settingsText = strtr($new_settingsText, array($substitutions[$pathcode_var]['placeholder'] => $p . "\n" . $substitutions[$pathcode_var]['placeholder'])); |
||
1588 | |||
1589 | $bare_settingsText .= "\n" . $substitutions[$var]['placeholder']; |
||
1590 | $done_defs[] = $var; |
||
1591 | unset($section_parts[trim($substitutions[$var]['placeholder'])]); |
||
1592 | } |
||
1593 | |||
1594 | // If it's in this section, add it to the new text now. |
||
1595 | elseif (in_array($p, $section)) |
||
1596 | { |
||
1597 | $new_settingsText .= "\n" . $substitutions[$var]['placeholder']; |
||
1598 | $done_defs[] = $var; |
||
1599 | unset($section_parts[trim($substitutions[$var]['placeholder'])]); |
||
1600 | } |
||
1601 | |||
1602 | // Perhaps it is safe to reposition it anyway. |
||
1603 | elseif (is_string($var) && strpos($new_settingsText, $p) === false && strpos($all_custom_content, '$' . $var) === false) |
||
1604 | { |
||
1605 | $new_settingsText .= "\n" . $substitutions[$var]['placeholder']; |
||
1606 | $done_defs[] = $var; |
||
1607 | unset($section_parts[trim($substitutions[$var]['placeholder'])]); |
||
1608 | } |
||
1609 | |||
1610 | // If this setting is missing entirely, fix it. |
||
1611 | elseif (strpos($bare_settingsText, $p) === false) |
||
1612 | { |
||
1613 | // Special case if the path code is missing. Put it near the end, |
||
1614 | // and also anything else that is missing that normally follows it. |
||
1615 | if (!isset($newsection_placeholders[$pathcode_var]) && $pathcode_reached === true && $sectionkey < (count($sections) - 1)) |
||
1616 | break; |
||
1617 | |||
1618 | $new_settingsText .= "\n" . $substitutions[$var]['placeholder']; |
||
1619 | $bare_settingsText .= "\n" . $substitutions[$var]['placeholder']; |
||
1620 | $done_defs[] = $var; |
||
1621 | unset($section_parts[trim($substitutions[$var]['placeholder'])]); |
||
1622 | } |
||
1623 | } |
||
1624 | } |
||
1625 | } |
||
1626 | $settingsText = $new_settingsText; |
||
1627 | |||
1628 | // Restore the leading and trailing placeholders as necessary. |
||
1629 | foreach (array(-1, -2) as $var) |
||
1630 | { |
||
1631 | if (!empty($substitutions[$var]['placeholder']) && strpos($settingsText, $substitutions[$var]['placeholder']) === false); |
||
1632 | { |
||
1633 | $settingsText = ($var == -1 ? $substitutions[$var]['placeholder'] : '') . $settingsText . ($var == -2 ? $substitutions[$var]['placeholder'] : ''); |
||
1634 | } |
||
1635 | } |
||
1636 | } |
||
1637 | // Even if not rebuilding, there are a few variables that may need to be moved around. |
||
1638 | else |
||
1639 | { |
||
1640 | $pathcode_pos = strpos($settingsText, $substitutions[$pathcode_var]['placeholder']); |
||
1641 | |||
1642 | if ($pathcode_pos !== false) |
||
1643 | { |
||
1644 | foreach ($force_before_pathcode as $var) |
||
1645 | { |
||
1646 | if (!empty($substitutions[$var]['placeholder']) && strpos($settingsText, $substitutions[$var]['placeholder']) > $pathcode_pos) |
||
1647 | { |
||
1648 | $settingsText = strtr($settingsText, array( |
||
1649 | $substitutions[$var]['placeholder'] => '', |
||
1650 | $substitutions[$pathcode_var]['placeholder'] => $substitutions[$var]['placeholder'] . "\n" . $substitutions[$pathcode_var]['placeholder'], |
||
1651 | )); |
||
1652 | } |
||
1653 | } |
||
1654 | } |
||
1655 | } |
||
1656 | |||
1657 | /* 3.c: Replace the placeholders with the final values */ |
||
1658 | |||
1659 | // Where possible, perform simple substitutions. |
||
1660 | $settingsText = strtr($settingsText, $simple_replacements); |
||
1661 | |||
1662 | // Deal with any complicated ones. |
||
1663 | if (!empty($replace_patterns)) |
||
1664 | $settingsText = preg_replace($replace_patterns, $replace_strings, $settingsText); |
||
1665 | |||
1666 | // Make absolutely sure that the path correction code is included. |
||
1667 | if (strpos($settingsText, $substitutions[$pathcode_var]['replacement']) === false) |
||
1668 | $settingsText = preg_replace('~(?=\n#+ Error.Catching #+)~', "\n" . $substitutions[$pathcode_var]['replacement'] . "\n", $settingsText); |
||
1669 | |||
1670 | // If we did not rebuild, do just enough to make sure the thing is viable. |
||
1671 | if (empty($rebuild)) |
||
1672 | { |
||
1673 | // We need to refresh $bare_settingsText again, and remove the code blocks from it. |
||
1674 | $bare_settingsText = $settingsText; |
||
1675 | foreach ($substitutions as $var => $substitution) |
||
1676 | { |
||
1677 | if (!is_int($var)) |
||
1678 | break; |
||
1679 | |||
1680 | if (isset($substitution['replacement'])) |
||
1681 | $bare_settingsText = str_replace($substitution['replacement'], '', $bare_settingsText); |
||
1682 | } |
||
1683 | $bare_settingsText = strip_php_comments($bare_settingsText); |
||
1684 | |||
1685 | // Now insert any defined settings that are missing. |
||
1686 | $pathcode_reached = false; |
||
1687 | foreach ($settings_defs as $var => $setting_def) |
||
1688 | { |
||
1689 | if ($var === $pathcode_var) |
||
1690 | $pathcode_reached = true; |
||
1691 | |||
1692 | if (is_int($var)) |
||
1693 | continue; |
||
1694 | |||
1695 | // Do nothing if it is already in there. |
||
1696 | if (preg_match($substitutions[$var]['search_pattern'], $bare_settingsText)) |
||
1697 | continue; |
||
1698 | |||
1699 | // Insert it either before or after the path correction code, whichever is appropriate. |
||
1700 | if (!$pathcode_reached || in_array($var, $force_before_pathcode)) |
||
1701 | { |
||
1702 | $settingsText = preg_replace($substitutions[$pathcode_var]['search_pattern'], $substitutions[$var]['replacement'] . "\n\n$0", $settingsText); |
||
1703 | } |
||
1704 | else |
||
1705 | { |
||
1706 | $settingsText = preg_replace($substitutions[$pathcode_var]['search_pattern'], "$0\n\n" . $substitutions[$var]['replacement'], $settingsText); |
||
1707 | } |
||
1708 | } |
||
1709 | } |
||
1710 | |||
1711 | // If we have any brand new settings to add, do so. |
||
1712 | foreach ($new_settings_vars as $var => $val) |
||
1713 | { |
||
1714 | if (isset($substitutions[$var]) && !preg_match($substitutions[$var]['search_pattern'], $settingsText)) |
||
1715 | { |
||
1716 | if (!isset($settings_defs[$var]) && strpos($settingsText, '# Custom Settings #') === false) |
||
1717 | $settingsText = preg_replace('~(?=\n#+ Error.Catching #+)~', "\n\n######### Custom Settings #########\n", $settingsText); |
||
1718 | |||
1719 | $settingsText = preg_replace('~(?=\n#+ Error.Catching #+)~', $substitutions[$var]['replacement'] . "\n", $settingsText); |
||
1720 | } |
||
1721 | } |
||
1722 | |||
1723 | // This is just cosmetic. Get rid of extra lines of whitespace. |
||
1724 | $settingsText = preg_replace('~\n\s*\n~', "\n\n", $settingsText); |
||
1725 | |||
1726 | /************************************** |
||
1727 | * PART 4: Check syntax before saving * |
||
1728 | **************************************/ |
||
1729 | |||
1730 | $temp_sfile = tempnam(sm_temp_dir(), md5($prefix . 'Settings.php')); |
||
1731 | file_put_contents($temp_sfile, $settingsText); |
||
1732 | |||
1733 | $result = get_current_settings(filemtime($temp_sfile), $temp_sfile); |
||
1734 | |||
1735 | unlink($temp_sfile); |
||
1736 | |||
1737 | // If the syntax is borked, try rebuilding to see if that fixes it. |
||
1738 | if ($result === false) |
||
1739 | return empty($rebuild) ? updateSettingsFile($config_vars, $keep_quotes, true) : false; |
||
1740 | |||
1741 | /****************************************** |
||
1742 | * PART 5: Write updated settings to file * |
||
1743 | ******************************************/ |
||
1744 | |||
1745 | $success = safe_file_write($settingsFile, $settingsText, dirname($settingsFile) . '/Settings_bak.php', $last_settings_change); |
||
1746 | |||
1747 | // Remember this in case updateSettingsFile is called twice. |
||
1748 | $mtime = filemtime($settingsFile); |
||
1749 | |||
1750 | return $success; |
||
1751 | } |
||
2402 | ?> |