PageRenderTime 21ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/hphp/runtime/base/config.cpp

https://gitlab.com/Blueprint-Marketing/hhvm
C++ | 322 lines | 256 code | 26 blank | 40 comment | 55 complexity | 985d15d31b09c793bb1aab73b785efbd MD5 | raw file
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. */
  16. #include "hphp/runtime/base/config.h"
  17. #include <boost/algorithm/string.hpp>
  18. #include <boost/algorithm/string/predicate.hpp>
  19. #include <fstream>
  20. #include "hphp/compiler/option.h"
  21. #include "hphp/runtime/base/ini-setting.h"
  22. namespace HPHP {
  23. ///////////////////////////////////////////////////////////////////////////////
  24. std::string Config::IniName(const Hdf& config,
  25. bool prepend_hhvm /* = true */) {
  26. return Config::IniName(config.getFullPath());
  27. }
  28. std::string Config::IniName(const std::string& config,
  29. bool prepend_hhvm /* = true */) {
  30. std::string out = "";
  31. if (prepend_hhvm) {
  32. out += "hhvm.";
  33. }
  34. size_t idx = 0;
  35. for (auto& c : config) {
  36. // This is the first or last character
  37. if (idx == 0 || idx == config.length() - 1) {
  38. out += tolower(c);
  39. } else if (!isalpha(c)) {
  40. // Any . or _ or numeral is just output with no special behavior
  41. out += c;
  42. } else {
  43. if (isupper(c) && isupper(config[idx - 1 ]) && islower(config[idx + 1])) {
  44. // Handle something like "SSLPort", and c = "P", which will then put
  45. // the underscore between the "L" and "P"
  46. out += "_";
  47. out += tolower(c);
  48. } else if (islower(c) && isupper(config[idx + 1])) {
  49. // Handle something like "PathDebug", and c = "h", which will then put
  50. // the underscore between the "h" and "D"
  51. out += tolower(c);
  52. out += "_";
  53. } else {
  54. // Otherwise we just output as lower
  55. out += tolower(c);
  56. }
  57. }
  58. idx++;
  59. }
  60. boost::replace_first(out,
  61. "hhvm.server.upload.max_file_uploads",
  62. "max_file_uploads");
  63. // Make sure IPv6 or IPv4 are handled correctly
  64. boost::replace_first(out, "_i_pv", "_ipv");
  65. boost::replace_first(out, ".i_pv", ".ipv");
  66. // urls are special too. Let's not have "ur_ls"
  67. boost::replace_first(out, "_ur_ls", "_urls");
  68. boost::replace_first(out, ".ur_ls", ".urls");
  69. // No use of Eval in our ini strings
  70. boost::replace_first(out, ".eval.", ".");
  71. boost::replace_first(out, ".my_sql.", ".mysql.");
  72. boost::replace_first(out, ".enable_hip_hop_syntax", ".force_hh");
  73. // Fix "XDebug" turning into "x_debug".
  74. boost::replace_first(out, "hhvm.debugger.x_debug_", "xdebug.");
  75. // HHVM-specific option, leave it as such.
  76. boost::replace_first(out, "xdebug.chrome", "hhvm.debugger.xdebug_chrome");
  77. return out;
  78. }
  79. void Config::ParseIniString(const std::string iniStr, IniSetting::Map &ini) {
  80. Config::SetParsedIni(ini, iniStr, "", false);
  81. }
  82. void Config::ParseHdfString(const std::string hdfStr, Hdf &hdf) {
  83. hdf.fromString(hdfStr.c_str());
  84. }
  85. void Config::ParseConfigFile(const std::string &filename, IniSetting::Map &ini,
  86. Hdf &hdf) {
  87. // We don't allow a filename of just ".ini"
  88. if (boost::ends_with(filename, ".ini") && filename.length() > 4) {
  89. Config::ParseIniFile(filename, ini);
  90. } else {
  91. // For now, assume anything else is an hdf file
  92. // TODO(#5151773): Have a non-invasive warning if HDF file does not end
  93. // .hdf
  94. Config::ParseHdfFile(filename, hdf);
  95. }
  96. }
  97. void Config::ParseIniFile(const std::string &filename) {
  98. IniSetting::Map ini = IniSetting::Map::object;;
  99. Config::ParseIniFile(filename, ini, false);
  100. }
  101. void Config::ParseIniFile(const std::string &filename, IniSetting::Map &ini,
  102. const bool constants_only /* = false */) {
  103. std::ifstream ifs(filename);
  104. const std::string str((std::istreambuf_iterator<char>(ifs)),
  105. std::istreambuf_iterator<char>());
  106. Config::SetParsedIni(ini, str, filename, constants_only);
  107. }
  108. void Config::ParseHdfFile(const std::string &filename, Hdf &hdf) {
  109. hdf.append(filename);
  110. }
  111. void Config::SetParsedIni(IniSetting::Map &ini, const std::string confStr,
  112. const std::string filename, bool constants_only) {
  113. assert(ini != nullptr);
  114. auto parsed_ini = IniSetting::FromStringAsMap(confStr, filename);
  115. for (auto &pair : parsed_ini.items()) {
  116. ini[pair.first] = pair.second;
  117. if (constants_only) {
  118. IniSetting::FillInConstant(pair.first.data(), pair.second,
  119. IniSetting::FollyDynamic());
  120. } else {
  121. IniSetting::Set(pair.first.data(), pair.second,
  122. IniSetting::FollyDynamic());
  123. }
  124. }
  125. }
  126. const char* Config::Get(const IniSetting::Map &ini, const Hdf& config,
  127. const std::string& name /* = "" */,
  128. const char *defValue /* = nullptr */,
  129. const bool prepend_hhvm /* = true */) {
  130. auto ini_name = IniName(name, prepend_hhvm);
  131. Hdf hdf = name != "" ? config[name] : config;
  132. auto* value = ini_iterate(ini, ini_name);
  133. if (value && value->isString()) {
  134. // See generic Get##METHOD below for why we are doing this
  135. const char* ini_ret = value->data();
  136. const char* hdf_ret = hdf.configGet(value->data());
  137. if (hdf_ret != ini_ret) {
  138. ini_ret = hdf_ret;
  139. IniSetting::Set(ini_name, ini_ret);
  140. }
  141. return ini_ret;
  142. }
  143. return hdf.configGet(defValue);
  144. }
  145. template<class T> static T variant_init(T v) {
  146. return v;
  147. }
  148. static int64_t variant_init(uint32_t v) {
  149. return v;
  150. }
  151. #define CONFIG_BODY(T, METHOD) \
  152. T Config::Get##METHOD(const IniSetting::Map &ini, const Hdf& config, \
  153. const std::string &name /* = "" */, \
  154. const T defValue /* = 0ish */, \
  155. const bool prepend_hhvm /* = true */) { \
  156. auto ini_name = IniName(name, prepend_hhvm); \
  157. /* If we don't pass a name, then we just use the raw config as-is. */ \
  158. /* This could happen when we are at a known leaf of a config node. */ \
  159. Hdf hdf = name != "" ? config[name] : config; \
  160. auto* value = ini_iterate(ini, ini_name); \
  161. if (value && value->isString()) { \
  162. T ini_ret, hdf_ret; \
  163. ini_on_update(value->data(), ini_ret); \
  164. /* I don't care what the ini_ret was if it isn't equal to what */ \
  165. /* is returned back from from an HDF get call, which it will be */ \
  166. /* if the call just passes back ini_ret because either they are */ \
  167. /* the same or the hdf option associated with this name does */ \
  168. /* not exist.... REMEMBER HDF WINS OVER INI UNTIL WE WIPE HDF */ \
  169. hdf_ret = hdf.configGet##METHOD(ini_ret); \
  170. if (hdf_ret != ini_ret) { \
  171. ini_ret = hdf_ret; \
  172. IniSetting::Set(ini_name, variant_init(ini_ret)); \
  173. } \
  174. return ini_ret; \
  175. } \
  176. /* If there is a value associated with this setting in the hdf config */ \
  177. /* then return it; otherwise the defValue will be returned as it is */ \
  178. /* assigned to the return value for this call when nothing exists */ \
  179. return hdf.configGet##METHOD(defValue); \
  180. } \
  181. void Config::Bind(T& loc, const IniSetting::Map &ini, const Hdf& config, \
  182. const std::string& name /* = "" */, \
  183. const T defValue /* = 0ish */, \
  184. const bool prepend_hhvm /* = true */) { \
  185. loc = Get##METHOD(ini, config, name, defValue, prepend_hhvm); \
  186. IniSetting::Bind(IniSetting::CORE, IniSetting::PHP_INI_SYSTEM, \
  187. IniName(name, prepend_hhvm), &loc); \
  188. }
  189. CONFIG_BODY(bool, Bool)
  190. CONFIG_BODY(char, Byte)
  191. CONFIG_BODY(unsigned char, UByte)
  192. CONFIG_BODY(int16_t, Int16)
  193. CONFIG_BODY(uint16_t, UInt16)
  194. CONFIG_BODY(int32_t, Int32)
  195. CONFIG_BODY(uint32_t, UInt32)
  196. CONFIG_BODY(int64_t, Int64)
  197. CONFIG_BODY(uint64_t, UInt64)
  198. CONFIG_BODY(double, Double)
  199. CONFIG_BODY(std::string, String)
  200. #define CONTAINER_CONFIG_BODY(T, METHOD) \
  201. T Config::Get##METHOD(const IniSetting::Map& ini, const Hdf& config, \
  202. const std::string& name /* = "" */, \
  203. const T& defValue /* = T() */, \
  204. const bool prepend_hhvm /* = true */) { \
  205. auto ini_name = IniName(name, prepend_hhvm); \
  206. Hdf hdf = name != "" ? config[name] : config; \
  207. T ini_ret, hdf_ret; \
  208. const folly::dynamic* value = ini_iterate(ini, ini_name); \
  209. if (value && (value->isArray() || value->isObject())) { \
  210. ini_on_update(*value, ini_ret); \
  211. /** Make sure that even if we have an ini value, that if we also **/ \
  212. /** have an hdf value, that it maintains its edge as beating out **/ \
  213. /** ini **/ \
  214. if (hdf.exists() && !hdf.isEmpty()) { \
  215. hdf.configGet(hdf_ret); \
  216. if (hdf_ret != ini_ret) { \
  217. ini_ret = hdf_ret; \
  218. IniSetting::Set(ini_name, ini_get(ini_ret), \
  219. IniSetting::FollyDynamic()); \
  220. } \
  221. } \
  222. return ini_ret; \
  223. } \
  224. if (hdf.exists() && !hdf.isEmpty()) { \
  225. hdf.configGet(hdf_ret); \
  226. return hdf_ret; \
  227. } \
  228. return defValue; \
  229. } \
  230. void Config::Bind(T& loc, const IniSetting::Map& ini, const Hdf& config, \
  231. const std::string& name /* = "" */, \
  232. const T& defValue /* = T() */, \
  233. const bool prepend_hhvm /* = true */) { \
  234. loc = Get##METHOD(ini, config, name, defValue, prepend_hhvm); \
  235. IniSetting::Bind(IniSetting::CORE, IniSetting::PHP_INI_SYSTEM, \
  236. IniName(name, prepend_hhvm), &loc); \
  237. }
  238. CONTAINER_CONFIG_BODY(ConfigVector, Vector)
  239. CONTAINER_CONFIG_BODY(ConfigMap, Map)
  240. CONTAINER_CONFIG_BODY(ConfigSet, Set)
  241. CONTAINER_CONFIG_BODY(ConfigSetC, SetC)
  242. CONTAINER_CONFIG_BODY(ConfigFlatSet, FlatSet)
  243. CONTAINER_CONFIG_BODY(ConfigIMap, IMap)
  244. static HackStrictOption GetHackStrictOption(const IniSettingMap& ini,
  245. const Hdf& config,
  246. const std::string& name /* = "" */
  247. ) {
  248. auto val = Config::GetString(ini, config, name);
  249. if (val.empty()) {
  250. if (Option::EnableHipHopSyntax || RuntimeOption::EnableHipHopSyntax) {
  251. return HackStrictOption::ON;
  252. }
  253. return HackStrictOption::OFF;
  254. }
  255. if (val == "warn") {
  256. return HackStrictOption::WARN;
  257. }
  258. bool ret;
  259. ini_on_update(val, ret);
  260. return ret ? HackStrictOption::ON : HackStrictOption::OFF;
  261. }
  262. void Config::Bind(HackStrictOption& loc, const IniSettingMap& ini,
  263. const Hdf& config, const std::string& name /* = "" */) {
  264. // Currently this doens't bind to ini_get since it is hard to thread through
  265. // an enum
  266. loc = GetHackStrictOption(ini, config, name);
  267. }
  268. // No `ini` binding yet. Hdf still takes precedence but will be removed
  269. // once we have made all options ini-aware. All new settings should
  270. // use the ini path of this method (i.e., pass a bogus Hdf or keep it null)
  271. void Config::Iterate(std::function<void (const IniSettingMap&,
  272. const Hdf&,
  273. const std::string&)> cb,
  274. const IniSettingMap &ini, const Hdf& config,
  275. const std::string &name,
  276. const bool prepend_hhvm /* = true */) {
  277. // We shouldn't be passing a leaf here. That's why name is not
  278. // optional.
  279. assert(!name.empty());
  280. Hdf hdf = config[name];
  281. if (hdf.exists() && !hdf.isEmpty()) {
  282. for (Hdf c = hdf.firstChild(); c.exists(); c = c.next()) {
  283. cb(IniSetting::Map::object, c, "");
  284. }
  285. } else {
  286. Hdf empty;
  287. auto ini_name = IniName(name, prepend_hhvm);
  288. auto* ini_value = ini_iterate(ini, ini_name);
  289. if (ini_value && ini_value->isObject()) {
  290. for (auto& pair : ini_value->items()) {
  291. cb(pair.second, empty, pair.first.data());
  292. }
  293. }
  294. }
  295. }
  296. }