2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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/runtime-error.h"
18 #include "hphp/runtime/base/builtin-functions.h"
19 #include "hphp/runtime/base/execution-context.h"
20 #include "hphp/runtime/base/runtime-option.h"
21 #include "hphp/runtime/base/thread-info.h"
22 #include "hphp/runtime/vm/repo.h"
23 #include "hphp/runtime/vm/repo-global-data.h"
24 #include "hphp/runtime/vm/vm-regs.h"
25 #include "hphp/util/logger.h"
26 #include "hphp/util/string-vsnprintf.h"
36 ///////////////////////////////////////////////////////////////////////////////
39 * Careful in these functions: they can be called when tl_regState is
40 * DIRTY. ExecutionContext::handleError is dirty-reg safe, but
41 * evaluate other functions that you might need here.
44 void raise_error(const std::string
&msg
) {
45 g_context
->handleError(msg
, static_cast<int>(ErrorMode::ERROR
), false,
46 ExecutionContext::ErrorThrowMode::Always
,
52 void raise_error(const char *fmt
, ...) {
56 string_vsnprintf(msg
, fmt
, ap
);
61 void raise_error_without_first_frame(const std::string
&msg
) {
62 g_context
->handleError(msg
, static_cast<int>(ErrorMode::ERROR
), false,
63 ExecutionContext::ErrorThrowMode::Always
,
69 void raise_recoverable_error(const std::string
&msg
) {
70 g_context
->handleError(
71 msg
, static_cast<int>(ErrorMode::RECOVERABLE_ERROR
), true,
72 ExecutionContext::ErrorThrowMode::IfUnhandled
,
73 "\nCatchable Fatal error: ",
77 void raise_recoverable_error_without_first_frame(const std::string
&msg
) {
78 g_context
->handleError(
79 msg
, static_cast<int>(ErrorMode::RECOVERABLE_ERROR
), true,
80 ExecutionContext::ErrorThrowMode::IfUnhandled
,
81 "\nCatchable Fatal error: ",
85 void raise_typehint_error(const std::string
& msg
) {
86 if (RuntimeOption::PHP7_EngineExceptions
) {
88 SystemLib::throwTypeErrorObject(msg
);
90 raise_recoverable_error_without_first_frame(msg
);
91 if (RuntimeOption::EvalHardTypeHints
) {
92 raise_error("Error handler tried to recover from typehint violation");
96 void raise_return_typehint_error(const std::string
& msg
) {
97 if (RuntimeOption::PHP7_EngineExceptions
) {
99 SystemLib::throwTypeErrorObject(msg
);
101 raise_recoverable_error(msg
);
102 if (RuntimeOption::EvalCheckReturnTypeHints
>= 3) {
103 raise_error("Error handler tried to recover from a return typehint "
108 void raise_disallowed_dynamic_call(const Func
* f
) {
110 RuntimeOption::DisallowDynamicVarEnvFuncs
,
111 "disallow_dynamic_var_env_funcs",
112 Strings::DISALLOWED_DYNCALL
, f
->fullName()->data()
116 void raise_intish_index_cast() {
117 if (UNLIKELY(RID().getSuppressHackArrayCompatNotices())) return;
118 raise_notice("Hack Array Compat: Intish index cast");
121 void raise_hackarr_compat_notice(const std::string
& msg
) {
122 if (UNLIKELY(RID().getSuppressHackArrayCompatNotices())) return;
123 raise_notice("Hack Array Compat: %s", msg
.c_str());
128 void raise_hackarr_type_hint_impl(const Func
* func
,
131 folly::Optional
<int> param
) {
132 if (UNLIKELY(RID().getSuppressHackArrayCompatNotices())) return;
134 auto const name
= [&]{
135 if (at
== AnnotType::VArray
) return "varray";
136 if (at
== AnnotType::DArray
) return "darray";
137 if (at
== AnnotType::VArrOrDArr
) return "varray_or_darray";
138 if (at
== AnnotType::Array
) return "array";
139 always_assert(false);
142 auto const given
= [&]{
143 if (ad
->isVArray()) return "varray";
144 if (ad
->isDArray()) return "darray";
150 "Hack Array Compat: Argument %d to %s() must be of type %s, %s given",
151 *param
+ 1, func
->fullDisplayName()->data(), name
, given
155 "Hack Array Compat: Value returned from %s() must be of type %s, "
157 func
->fullDisplayName()->data(), name
, given
164 void raise_hackarr_type_hint_param_notice(const Func
* func
,
168 raise_hackarr_type_hint_impl(func
, ad
, at
, param
);
171 void raise_hackarr_type_hint_ret_notice(const Func
* func
,
174 raise_hackarr_type_hint_impl(func
, ad
, at
, folly::none
);
177 void raise_hackarr_type_hint_outparam_notice(const Func
* func
,
181 if (UNLIKELY(RID().getSuppressHackArrayCompatNotices())) return;
183 auto const name
= [&]{
184 if (at
== AnnotType::VArray
) return "varray";
185 if (at
== AnnotType::DArray
) return "darray";
186 if (at
== AnnotType::VArrOrDArr
) return "varray_or_darray";
187 if (at
== AnnotType::Array
) return "array";
188 always_assert(false);
191 auto const given
= [&]{
192 if (ad
->isVArray()) return "varray";
193 if (ad
->isDArray()) return "darray";
198 "Hack Array Compat: Argument %d returned from %s() as an inout parameter "
199 "must be of type %s, %s given",
200 param
+ 1, func
->fullDisplayName()->data(), name
, given
204 void raise_call_to_undefined(const StringData
* name
, const Class
* cls
) {
205 if (LIKELY(!needsStripInOut(name
))) {
207 raise_error("Call to undefined method %s::%s()", cls
->name()->data(),
210 raise_error("Call to undefined function %s()", name
->data());
212 auto stripped
= stripInOutSuffix(name
);
214 if (cls
->lookupMethod(stripped
)) {
215 raise_error("Call to method %s::%s() with incorrectly annotated inout "
216 "parameter", cls
->name()->data(), stripped
->data());
218 raise_error("Call to undefined method %s::%s()", cls
->name()->data(),
220 } else if (Unit::lookupFunc(stripped
)) {
221 raise_error("Call to function %s() with incorrectly annotated inout "
222 "parameter", stripped
->data());
224 raise_error("Call to undefined function %s()", stripped
->data());
228 void raise_recoverable_error(const char *fmt
, ...) {
232 string_vsnprintf(msg
, fmt
, ap
);
234 raise_recoverable_error(msg
);
237 static int64_t g_notice_counter
= 0;
239 static bool notice_freq_check(ErrorMode mode
) {
240 if (!g_context
->getThrowAllErrors() &&
241 (RuntimeOption::NoticeFrequency
<= 0 ||
242 g_notice_counter
++ % RuntimeOption::NoticeFrequency
!= 0)) {
245 return g_context
->errorNeedsHandling(
246 static_cast<int>(mode
), true, ExecutionContext::ErrorThrowMode::Never
);
249 #define HANDLE_ERROR(userHandle, throwMode, str, skip) \
250 g_context->handleError(msg, static_cast<int>(mode), userHandle, \
251 ExecutionContext::ErrorThrowMode::throwMode, \
255 static void raise_notice_helper(ErrorMode mode
, bool skipTop
,
256 const std::string
& msg
) {
258 case ErrorMode::STRICT
:
259 HANDLE_ERROR(true, Never
, "\nStrict Warning: ", skipTop
);
261 case ErrorMode::NOTICE
:
262 HANDLE_ERROR(true, Never
, "\nNotice: ", skipTop
);
264 case ErrorMode::PHP_DEPRECATED
:
265 HANDLE_ERROR(true, Never
, "\nDeprecated: ", skipTop
);
268 always_assert(!"Unhandled type of error");
272 void raise_strict_warning(const std::string
&msg
) {
273 if (notice_freq_check(ErrorMode::STRICT
)) {
274 raise_notice_helper(ErrorMode::STRICT
, false, msg
);
278 void raise_strict_warning_without_first_frame(const std::string
&msg
) {
279 if (notice_freq_check(ErrorMode::STRICT
)) {
280 raise_notice_helper(ErrorMode::STRICT
, true, msg
);
284 void raise_strict_warning(const char *fmt
, ...) {
285 if (!notice_freq_check(ErrorMode::STRICT
)) return;
290 string_vsnprintf(msg
, fmt
, ap
);
292 raise_notice_helper(ErrorMode::STRICT
, false, msg
);
295 static int64_t g_warning_counter
= 0;
297 bool warning_freq_check() {
298 if (!g_context
->getThrowAllErrors() &&
299 (RuntimeOption::WarningFrequency
<= 0 ||
300 g_warning_counter
++ % RuntimeOption::WarningFrequency
!= 0)) {
303 return g_context
->errorNeedsHandling(
304 static_cast<int>(ErrorMode::WARNING
), true,
305 ExecutionContext::ErrorThrowMode::Never
);
308 void raise_warning_helper(bool skipTop
, const std::string
& msg
) {
309 auto mode
= ErrorMode::WARNING
;
310 HANDLE_ERROR(true, Never
, "\nWarning: ", skipTop
);
313 void raise_warning(const std::string
&msg
) {
314 if (warning_freq_check()) {
315 raise_warning_helper(false, msg
);
319 void raise_warning_without_first_frame(const std::string
&msg
) {
320 if (warning_freq_check()) {
321 raise_warning_helper(true, msg
);
325 void raise_warning(const char *fmt
, ...) {
326 if (!warning_freq_check()) return;
330 string_vsnprintf(msg
, fmt
, ap
);
332 raise_warning_helper(false, msg
);
335 static void raise_hack_strict_helper(
336 HackStrictOption option
, const char *ini_setting
, const std::string
& msg
) {
337 if (option
== HackStrictOption::WARN
) {
338 raise_warning_helper(
339 false, std::string("(hhvm.hack.") + ini_setting
+ "=warn) " + msg
);
340 } else if (option
== HackStrictOption::ON
) {
341 raise_error(std::string("(hhvm.hack.") + ini_setting
+ "=error) " + msg
);
347 * For use with the HackStrictOption settings. This will warn, error, or do
348 * nothing depending on what the user chose for the option. The second param
349 * should be the ini setting name after "hhvm.hack."
351 void raise_hack_strict(HackStrictOption option
, const char *ini_setting
,
352 const std::string
& msg
) {
353 if (option
== HackStrictOption::WARN
?
354 !warning_freq_check() : (option
!= HackStrictOption::ON
)) {
357 raise_hack_strict_helper(option
, ini_setting
, msg
);
360 void raise_hack_strict(HackStrictOption option
, const char *ini_setting
,
361 const char *fmt
, ...) {
362 if (option
== HackStrictOption::WARN
?
363 !warning_freq_check() : (option
!= HackStrictOption::ON
)) {
369 string_vsnprintf(msg
, fmt
, ap
);
371 raise_hack_strict_helper(option
, ini_setting
, msg
);
375 * Warnings are currently sampled. raise_warning_unsampled can help when
376 * migrating warnings to errors.
378 * In general, RaiseDebuggingFrequency should be kept at 1.
380 static int64_t g_raise_warning_unsampled_counter
= 0;
382 void raise_warning_unsampled(const std::string
&msg
) {
383 if (RuntimeOption::RaiseDebuggingFrequency
<= 0 ||
384 (g_raise_warning_unsampled_counter
++) %
385 RuntimeOption::RaiseDebuggingFrequency
!= 0) {
388 if (g_context
->errorNeedsHandling(
389 static_cast<int>(ErrorMode::WARNING
), true,
390 ExecutionContext::ErrorThrowMode::Never
)) {
391 raise_warning_helper(false, msg
);
395 void raise_warning_unsampled(const char *fmt
, ...) {
399 string_vsnprintf(msg
, fmt
, ap
);
401 raise_warning_unsampled(msg
);
404 void raise_notice(const std::string
&msg
) {
405 if (notice_freq_check(ErrorMode::NOTICE
)) {
406 raise_notice_helper(ErrorMode::NOTICE
, false, msg
);
410 void raise_notice_without_first_frame(const std::string
&msg
) {
411 if (notice_freq_check(ErrorMode::NOTICE
)) {
412 raise_notice_helper(ErrorMode::NOTICE
, true, msg
);
416 void raise_notice(const char *fmt
, ...) {
417 if (!notice_freq_check(ErrorMode::NOTICE
)) return;
421 string_vsnprintf(msg
, fmt
, ap
);
423 raise_notice_helper(ErrorMode::NOTICE
, false, msg
);
426 void raise_deprecated(const std::string
&msg
) {
427 if (notice_freq_check(ErrorMode::PHP_DEPRECATED
)) {
428 raise_notice_helper(ErrorMode::PHP_DEPRECATED
, false, msg
);
432 void raise_deprecated_without_first_frame(const std::string
&msg
) {
433 if (notice_freq_check(ErrorMode::PHP_DEPRECATED
)) {
434 raise_notice_helper(ErrorMode::PHP_DEPRECATED
, true, msg
);
438 void raise_deprecated(const char *fmt
, ...) {
439 if (!notice_freq_check(ErrorMode::PHP_DEPRECATED
)) return;
443 string_vsnprintf(msg
, fmt
, ap
);
445 raise_notice_helper(ErrorMode::PHP_DEPRECATED
, false, msg
);
448 void raise_param_type_warning(
449 const char* func_name
,
451 DataType expected_type
,
452 DataType actual_type
) {
454 // its ok to do this before munging, because it only looks at the
456 auto is_constructor
= is_constructor_name(func_name
);
457 if (!is_constructor
&& !warning_freq_check()) return;
459 if (strncmp(func_name
, "fg1_", 4) == 0) {
461 } else if (strncmp(func_name
, "tg1_", 4) == 0) {
464 assertx(param_num
> 0);
465 auto msg
= folly::sformat(
466 "{}() expects parameter {} to be {}, {} given",
469 getDataTypeString(expected_type
).data(),
470 getDataTypeString(actual_type
).data());
472 if (is_constructor
) {
473 SystemLib::throwExceptionObject(msg
);
476 raise_warning_helper(false, msg
);
479 void raise_message(ErrorMode mode
,
483 string_vsnprintf(msg
, fmt
, ap
);
484 raise_message(mode
, false, msg
);
487 void raise_message(ErrorMode mode
,
493 string_vsnprintf(msg
, fmt
, ap
);
495 raise_message(mode
, false, msg
);
498 void raise_message(ErrorMode mode
,
500 const std::string
&msg
) {
501 if (mode
== ErrorMode::ERROR
) {
502 HANDLE_ERROR(false, Always
, "\nFatal error: ", skipTop
);
506 if (mode
== ErrorMode::RECOVERABLE_ERROR
) {
507 HANDLE_ERROR(true, IfUnhandled
, "\nCatchable fatal error: ", skipTop
);
511 if (!g_context
->errorNeedsHandling(static_cast<int>(mode
), true,
512 ExecutionContext::ErrorThrowMode::Never
)) {
516 if (mode
== ErrorMode::WARNING
) {
517 if (RuntimeOption::WarningFrequency
<= 0 ||
518 (g_warning_counter
++) % RuntimeOption::WarningFrequency
!= 0) {
521 HANDLE_ERROR(true, Never
, "\nWarning: ", skipTop
);
525 if (RuntimeOption::NoticeFrequency
<= 0 ||
526 (g_notice_counter
++) % RuntimeOption::NoticeFrequency
!= 0) {
530 raise_notice_helper(mode
, skipTop
, msg
);
533 ///////////////////////////////////////////////////////////////////////////////
535 SuppressHackArrCompatNotices::SuppressHackArrCompatNotices()
536 : old
{RID().getSuppressHackArrayCompatNotices()} {
537 RID().setSuppressHackArrayCompatNotices(true);
540 SuppressHackArrCompatNotices::~SuppressHackArrCompatNotices() {
541 RID().setSuppressHackArrayCompatNotices(old
);
544 ///////////////////////////////////////////////////////////////////////////////