Refactor the parsing and processing of configuration options ... just a bit ...
[hiphop-php.git] / hphp / runtime / base / config.cpp
blobdf2e00a2d6aa33c410025789c819e6d6b0e4cb7d
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 +----------------------------------------------------------------------+
17 #include "hphp/runtime/base/config.h"
19 #include <boost/algorithm/string.hpp>
20 #include <boost/algorithm/string/predicate.hpp>
21 #include <fstream>
23 #include "hphp/compiler/option.h"
24 #include "hphp/runtime/base/ini-setting.h"
25 #include "hphp/runtime/base/runtime-error.h"
27 namespace HPHP {
28 ///////////////////////////////////////////////////////////////////////////////
30 /**
31 * Normalizes hdf string names to their ini counterparts
33 * We have special handling for a few hdf strings such as those containing
34 * MySQL, Eval, IPv[4|6] and EnableHipHopSyntax
36 static std::string normalize(const std::string &name) {
37 std::string out = ".";
38 size_t idx = 0;
39 for (auto &c : name) {
40 // This is the first or last character
41 if (idx == 0 || idx == name.length() - 1) {
42 out += tolower(c);
43 } else if (!isalpha(c)) {
44 // Any . or _ or numeral is just output with no special behavior
45 out += c;
46 } else {
47 if (isupper(c) && isupper(name[idx - 1 ]) && islower(name[idx + 1])) {
48 // Handle something like "SSLPort", and c = "P", which will then put
49 // the underscore between the "L" and "P"
50 out += "_";
51 out += tolower(c);
52 } else if (islower(c) && isupper(name[idx + 1])) {
53 // Handle something like "PathDebug", and c = "h", which will then put
54 // the underscore between the "h" and "D"
55 out += tolower(c);
56 out += "_";
57 } else {
58 // Otherwise we just output as lower
59 out += tolower(c);
62 idx++;
65 // Make sure IPv6 or IPv4 are handled correctly
66 boost::replace_first(out, "_i_pv", "_ipv");
67 boost::replace_first(out, ".i_pv", ".ipv");
68 // urls are special too. Let's not have "ur_ls"
69 boost::replace_first(out, "_ur_ls", "_urls");
70 boost::replace_first(out, ".ur_ls", ".urls");
71 // No use of Eval in our ini strings
72 boost::replace_first(out, ".eval.", ".");
73 boost::replace_first(out, ".my_sql.", ".mysql.");
74 boost::replace_first(out, ".enable_hip_hop_syntax", ".force_hh");
76 return out;
79 std::string Config::IniName(const Hdf& config) {
80 return Config::IniName(config.getFullPath());
83 std::string Config::IniName(const std::string config) {
84 return "hhvm" + normalize(config);
87 void Config::ParseIniString(const std::string iniStr, IniSetting::Map &ini) {
88 Config::SetParsedIni(ini, iniStr, "", false);
91 void Config::ParseHdfString(const std::string hdfStr, Hdf &hdf,
92 IniSetting::Map &ini) {
93 hdf.fromString(hdfStr.c_str());
94 // Make sure we update any provided `-v` option with
95 // an equivalent change to an ini setting, if it
96 // existed already. This way a call in IniSetting::Get
97 // such as ResetSystemDefault(name) has the correct
98 // info.
99 std::string cname = Config::IniName(hdf.getLastFullName());
100 std::string cval = hdf.getLastValue();
101 Config::ParseIniString(cname + "=" + cval, ini);
104 void Config::ParseConfigFile(const std::string &filename, IniSetting::Map &ini,
105 Hdf &hdf) {
106 // We don't allow a filename of just ".ini"
107 if (boost::ends_with(filename, ".ini") && filename.length() > 4) {
108 Config::ParseIniFile(filename, ini);
109 } else if (boost::ends_with(filename, ".hdf") && filename.length() > 4) {
110 Config::ParseHdfFile(filename, hdf);
111 } else {
112 raise_warning("Config files should end with .ini or .hdf. Parsing as .hdf");
113 Config::ParseHdfFile(filename, hdf);
117 void Config::ParseIniFile(const std::string &filename) {
118 IniSetting::Map nul = nullptr;
119 Config::ParseIniFile(filename, nul, false);
122 void Config::ParseIniFile(const std::string &filename, IniSetting::Map &ini,
123 const bool constants_only /* = false */) {
124 std::ifstream ifs(filename);
125 const std::string str((std::istreambuf_iterator<char>(ifs)),
126 std::istreambuf_iterator<char>());
127 Config::SetParsedIni(ini, str, filename, constants_only);
130 void Config::ParseHdfFile(const std::string &filename, Hdf &hdf) {
131 hdf.append(filename);
134 void Config::SetParsedIni(IniSetting::Map &ini, const std::string confStr,
135 const std::string filename, bool constants_only) {
136 auto parsed_ini = IniSetting::FromStringAsMap(confStr, filename);
137 for (auto &pair : parsed_ini.items()) {
138 if (!ini.isNull()) {
139 ini[pair.first] = pair.second;
141 if (constants_only) {
142 IniSetting::FillInConstant(pair.first.data(), pair.second,
143 IniSetting::FollyDynamic());
144 } else {
145 IniSetting::Set(pair.first.data(), pair.second,
146 IniSetting::FollyDynamic());
151 const char* Config::Get(const IniSetting::Map &ini, const Hdf& config,
152 const char *defValue /* = nullptr */) {
153 auto* value = ini.get_ptr(IniName(config));
154 if (value && value->isString()) {
155 return value->data();
157 return config.configGet(defValue);
160 #define CONFIG_BODY(T, METHOD) \
161 T Config::Get##METHOD(const IniSetting::Map &ini, const Hdf& config, \
162 const T defValue /* = 0ish */) { \
163 auto* value = ini.get_ptr(IniName(config)); \
164 if (value && value->isString()) { \
165 T ret; \
166 ini_on_update(value->data(), ret); \
167 /* The HDF still wins because the -v options
168 * are still done via HDF, for now */ \
169 return config.configGet##METHOD(ret); \
171 return config.configGet##METHOD(defValue); \
173 void Config::Bind(T& loc, const IniSetting::Map &ini, const Hdf& config, \
174 const T defValue /* = 0ish */) { \
175 loc = Get##METHOD(ini, config, defValue); \
176 IniSetting::Bind(IniSetting::CORE, IniSetting::PHP_INI_SYSTEM, \
177 IniName(config), &loc); \
179 void Config::Bind(T& loc, const IniSetting::Map &ini, std::string name, \
180 const T defValue /* = 0ish */) { \
181 auto* value = ini.get_ptr(name); \
182 if (value && value->isString()) { \
183 ini_on_update(value->data(), loc); \
184 } else { \
185 loc = defValue; \
187 IniSetting::Bind(IniSetting::CORE, IniSetting::PHP_INI_SYSTEM, \
188 name, &loc); \
191 CONFIG_BODY(bool, Bool)
192 CONFIG_BODY(char, Byte)
193 CONFIG_BODY(unsigned char, UByte)
194 CONFIG_BODY(int16_t, Int16)
195 CONFIG_BODY(uint16_t, UInt16)
196 CONFIG_BODY(int32_t, Int32)
197 CONFIG_BODY(uint32_t, UInt32)
198 CONFIG_BODY(int64_t, Int64)
199 CONFIG_BODY(uint64_t, UInt64)
200 CONFIG_BODY(double, Double)
201 CONFIG_BODY(std::string, String)
203 static HackStrictOption GetHackStrictOption(const IniSettingMap& ini,
204 const Hdf& config) {
205 auto val = Config::GetString(ini, config);
206 if (val.empty()) {
207 if (Option::EnableHipHopSyntax || RuntimeOption::EnableHipHopSyntax) {
208 return HackStrictOption::ERROR;
210 return HackStrictOption::OFF;
212 if (val == "warn") {
213 return HackStrictOption::WARN;
215 bool ret;
216 ini_on_update(val, ret);
217 return ret ? HackStrictOption::ERROR : HackStrictOption::OFF;
220 void Config::Bind(HackStrictOption& loc, const IniSettingMap& ini,
221 const Hdf& config) {
222 // Currently this doens't bind to ini_get since it is hard to thread through
223 // an enum
224 loc = GetHackStrictOption(ini, config);
227 void Config::Bind(std::vector<std::string>& loc, const IniSettingMap& ini,
228 const Hdf& config) {
229 std::vector<std::string> ret;
230 auto ini_name = IniName(config);
231 auto* value = ini.get_ptr(ini_name);
232 if (value && value->isObject()) {
233 ini_on_update(*value, ret);
234 loc = ret;
236 // If there is an HDF setting for the config, then it still wins for
237 // the RuntimeOption value until we obliterate HDFs
238 ret.clear();
239 config.configGet(ret);
240 if (ret.size() > 0) {
241 loc = ret;
243 IniSetting::Bind(IniSetting::CORE, IniSetting::PHP_INI_SYSTEM, ini_name,
244 &loc);