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());
127 void raise_hack_arr_compat_serialize_notice(const ArrayData
* arr
) {
128 if (UNLIKELY(RID().getSuppressHackArrayCompatNotices())) return;
129 auto const type
= [&]{
130 if (arr
->isVecArray()) return "vec";
131 if (arr
->isDict()) return "dict";
132 if (arr
->isKeyset()) return "keyset";
135 raise_notice("Hack Array Compat: Serializing %s", type
);
140 void raise_hackarr_type_hint_impl(const Func
* func
,
143 folly::Optional
<int> param
) {
144 if (UNLIKELY(RID().getSuppressHackArrayCompatNotices())) return;
146 auto const name
= [&]{
147 if (at
== AnnotType::VArray
) return "varray";
148 if (at
== AnnotType::DArray
) return "darray";
149 if (at
== AnnotType::VArrOrDArr
) return "varray_or_darray";
150 if (at
== AnnotType::Array
) return "array";
151 always_assert(false);
154 auto const given
= [&]{
155 if (ad
->isVArray()) return "varray";
156 if (ad
->isDArray()) return "darray";
162 "Hack Array Compat: Argument %d to %s() must be of type %s, %s given",
163 *param
+ 1, func
->fullDisplayName()->data(), name
, given
167 "Hack Array Compat: Value returned from %s() must be of type %s, "
169 func
->fullDisplayName()->data(), name
, given
176 void raise_hackarr_type_hint_param_notice(const Func
* func
,
180 raise_hackarr_type_hint_impl(func
, ad
, at
, param
);
183 void raise_hackarr_type_hint_ret_notice(const Func
* func
,
186 raise_hackarr_type_hint_impl(func
, ad
, at
, folly::none
);
189 void raise_hackarr_type_hint_outparam_notice(const Func
* func
,
193 if (UNLIKELY(RID().getSuppressHackArrayCompatNotices())) return;
195 auto const name
= [&]{
196 if (at
== AnnotType::VArray
) return "varray";
197 if (at
== AnnotType::DArray
) return "darray";
198 if (at
== AnnotType::VArrOrDArr
) return "varray_or_darray";
199 if (at
== AnnotType::Array
) return "array";
200 always_assert(false);
203 auto const given
= [&]{
204 if (ad
->isVArray()) return "varray";
205 if (ad
->isDArray()) return "darray";
210 "Hack Array Compat: Argument %d returned from %s() as an inout parameter "
211 "must be of type %s, %s given",
212 param
+ 1, func
->fullDisplayName()->data(), name
, given
216 void raise_call_to_undefined(const StringData
* name
, const Class
* cls
) {
217 if (LIKELY(!needsStripInOut(name
))) {
219 raise_error("Call to undefined method %s::%s()", cls
->name()->data(),
222 raise_error("Call to undefined function %s()", name
->data());
224 auto stripped
= stripInOutSuffix(name
);
226 if (cls
->lookupMethod(stripped
)) {
227 raise_error("Call to method %s::%s() with incorrectly annotated inout "
228 "parameter", cls
->name()->data(), stripped
->data());
230 raise_error("Call to undefined method %s::%s()", cls
->name()->data(),
232 } else if (Unit::lookupFunc(stripped
)) {
233 raise_error("Call to function %s() with incorrectly annotated inout "
234 "parameter", stripped
->data());
236 raise_error("Call to undefined function %s()", stripped
->data());
240 void raise_recoverable_error(const char *fmt
, ...) {
244 string_vsnprintf(msg
, fmt
, ap
);
246 raise_recoverable_error(msg
);
249 static int64_t g_notice_counter
= 0;
251 static bool notice_freq_check(ErrorMode mode
) {
252 if (!g_context
->getThrowAllErrors() &&
253 (RuntimeOption::NoticeFrequency
<= 0 ||
254 g_notice_counter
++ % RuntimeOption::NoticeFrequency
!= 0)) {
257 return g_context
->errorNeedsHandling(
258 static_cast<int>(mode
), true, ExecutionContext::ErrorThrowMode::Never
);
261 #define HANDLE_ERROR(userHandle, throwMode, str, skip) \
262 g_context->handleError(msg, static_cast<int>(mode), userHandle, \
263 ExecutionContext::ErrorThrowMode::throwMode, \
267 static void raise_notice_helper(ErrorMode mode
, bool skipTop
,
268 const std::string
& msg
) {
270 case ErrorMode::STRICT
:
271 HANDLE_ERROR(true, Never
, "\nStrict Warning: ", skipTop
);
273 case ErrorMode::NOTICE
:
274 HANDLE_ERROR(true, Never
, "\nNotice: ", skipTop
);
276 case ErrorMode::PHP_DEPRECATED
:
277 HANDLE_ERROR(true, Never
, "\nDeprecated: ", skipTop
);
280 always_assert(!"Unhandled type of error");
284 void raise_strict_warning(const std::string
&msg
) {
285 if (notice_freq_check(ErrorMode::STRICT
)) {
286 raise_notice_helper(ErrorMode::STRICT
, false, msg
);
290 void raise_strict_warning_without_first_frame(const std::string
&msg
) {
291 if (notice_freq_check(ErrorMode::STRICT
)) {
292 raise_notice_helper(ErrorMode::STRICT
, true, msg
);
296 void raise_strict_warning(const char *fmt
, ...) {
297 if (!notice_freq_check(ErrorMode::STRICT
)) return;
302 string_vsnprintf(msg
, fmt
, ap
);
304 raise_notice_helper(ErrorMode::STRICT
, false, msg
);
307 static int64_t g_warning_counter
= 0;
309 bool warning_freq_check() {
310 if (!g_context
->getThrowAllErrors() &&
311 (RuntimeOption::WarningFrequency
<= 0 ||
312 g_warning_counter
++ % RuntimeOption::WarningFrequency
!= 0)) {
315 return g_context
->errorNeedsHandling(
316 static_cast<int>(ErrorMode::WARNING
), true,
317 ExecutionContext::ErrorThrowMode::Never
);
320 void raise_warning_helper(bool skipTop
, const std::string
& msg
) {
321 auto mode
= ErrorMode::WARNING
;
322 HANDLE_ERROR(true, Never
, "\nWarning: ", skipTop
);
325 void raise_warning(const std::string
&msg
) {
326 if (warning_freq_check()) {
327 raise_warning_helper(false, msg
);
331 void raise_warning_without_first_frame(const std::string
&msg
) {
332 if (warning_freq_check()) {
333 raise_warning_helper(true, msg
);
337 void raise_warning(const char *fmt
, ...) {
338 if (!warning_freq_check()) return;
342 string_vsnprintf(msg
, fmt
, ap
);
344 raise_warning_helper(false, msg
);
347 static void raise_hack_strict_helper(
348 HackStrictOption option
, const char *ini_setting
, const std::string
& msg
) {
349 if (option
== HackStrictOption::WARN
) {
350 raise_warning_helper(
351 false, std::string("(hhvm.hack.") + ini_setting
+ "=warn) " + msg
);
352 } else if (option
== HackStrictOption::ON
) {
353 raise_error(std::string("(hhvm.hack.") + ini_setting
+ "=error) " + msg
);
359 * For use with the HackStrictOption settings. This will warn, error, or do
360 * nothing depending on what the user chose for the option. The second param
361 * should be the ini setting name after "hhvm.hack."
363 void raise_hack_strict(HackStrictOption option
, const char *ini_setting
,
364 const std::string
& msg
) {
365 if (option
== HackStrictOption::WARN
?
366 !warning_freq_check() : (option
!= HackStrictOption::ON
)) {
369 raise_hack_strict_helper(option
, ini_setting
, msg
);
372 void raise_hack_strict(HackStrictOption option
, const char *ini_setting
,
373 const char *fmt
, ...) {
374 if (option
== HackStrictOption::WARN
?
375 !warning_freq_check() : (option
!= HackStrictOption::ON
)) {
381 string_vsnprintf(msg
, fmt
, ap
);
383 raise_hack_strict_helper(option
, ini_setting
, msg
);
387 * Warnings are currently sampled. raise_warning_unsampled can help when
388 * migrating warnings to errors.
390 * In general, RaiseDebuggingFrequency should be kept at 1.
392 static int64_t g_raise_warning_unsampled_counter
= 0;
394 void raise_warning_unsampled(const std::string
&msg
) {
395 if (RuntimeOption::RaiseDebuggingFrequency
<= 0 ||
396 (g_raise_warning_unsampled_counter
++) %
397 RuntimeOption::RaiseDebuggingFrequency
!= 0) {
400 if (g_context
->errorNeedsHandling(
401 static_cast<int>(ErrorMode::WARNING
), true,
402 ExecutionContext::ErrorThrowMode::Never
)) {
403 raise_warning_helper(false, msg
);
407 void raise_warning_unsampled(const char *fmt
, ...) {
411 string_vsnprintf(msg
, fmt
, ap
);
413 raise_warning_unsampled(msg
);
416 void raise_notice(const std::string
&msg
) {
417 if (notice_freq_check(ErrorMode::NOTICE
)) {
418 raise_notice_helper(ErrorMode::NOTICE
, false, msg
);
422 void raise_notice_without_first_frame(const std::string
&msg
) {
423 if (notice_freq_check(ErrorMode::NOTICE
)) {
424 raise_notice_helper(ErrorMode::NOTICE
, true, msg
);
428 void raise_notice(const char *fmt
, ...) {
429 if (!notice_freq_check(ErrorMode::NOTICE
)) return;
433 string_vsnprintf(msg
, fmt
, ap
);
435 raise_notice_helper(ErrorMode::NOTICE
, false, msg
);
438 void raise_deprecated(const std::string
&msg
) {
439 if (notice_freq_check(ErrorMode::PHP_DEPRECATED
)) {
440 raise_notice_helper(ErrorMode::PHP_DEPRECATED
, false, msg
);
444 void raise_deprecated_without_first_frame(const std::string
&msg
) {
445 if (notice_freq_check(ErrorMode::PHP_DEPRECATED
)) {
446 raise_notice_helper(ErrorMode::PHP_DEPRECATED
, true, msg
);
450 void raise_deprecated(const char *fmt
, ...) {
451 if (!notice_freq_check(ErrorMode::PHP_DEPRECATED
)) return;
455 string_vsnprintf(msg
, fmt
, ap
);
457 raise_notice_helper(ErrorMode::PHP_DEPRECATED
, false, msg
);
460 void raise_param_type_warning(
461 const char* func_name
,
463 DataType expected_type
,
464 DataType actual_type
) {
466 // its ok to do this before munging, because it only looks at the
468 auto is_constructor
= is_constructor_name(func_name
);
469 if (!is_constructor
&& !warning_freq_check()) return;
471 if (strncmp(func_name
, "fg1_", 4) == 0) {
473 } else if (strncmp(func_name
, "tg1_", 4) == 0) {
476 assertx(param_num
> 0);
477 auto msg
= folly::sformat(
478 "{}() expects parameter {} to be {}, {} given",
481 getDataTypeString(expected_type
).data(),
482 getDataTypeString(actual_type
).data());
484 if (is_constructor
) {
485 SystemLib::throwExceptionObject(msg
);
488 raise_warning_helper(false, msg
);
491 void raise_message(ErrorMode mode
,
495 string_vsnprintf(msg
, fmt
, ap
);
496 raise_message(mode
, false, msg
);
499 void raise_message(ErrorMode mode
,
505 string_vsnprintf(msg
, fmt
, ap
);
507 raise_message(mode
, false, msg
);
510 void raise_message(ErrorMode mode
,
512 const std::string
&msg
) {
513 if (mode
== ErrorMode::ERROR
) {
514 HANDLE_ERROR(false, Always
, "\nFatal error: ", skipTop
);
518 if (mode
== ErrorMode::RECOVERABLE_ERROR
) {
519 HANDLE_ERROR(true, IfUnhandled
, "\nCatchable fatal error: ", skipTop
);
523 if (!g_context
->errorNeedsHandling(static_cast<int>(mode
), true,
524 ExecutionContext::ErrorThrowMode::Never
)) {
528 if (mode
== ErrorMode::WARNING
) {
529 if (RuntimeOption::WarningFrequency
<= 0 ||
530 (g_warning_counter
++) % RuntimeOption::WarningFrequency
!= 0) {
533 HANDLE_ERROR(true, Never
, "\nWarning: ", skipTop
);
537 if (RuntimeOption::NoticeFrequency
<= 0 ||
538 (g_notice_counter
++) % RuntimeOption::NoticeFrequency
!= 0) {
542 raise_notice_helper(mode
, skipTop
, msg
);
545 ///////////////////////////////////////////////////////////////////////////////
547 SuppressHackArrCompatNotices::SuppressHackArrCompatNotices()
548 : old
{RID().getSuppressHackArrayCompatNotices()} {
549 RID().setSuppressHackArrayCompatNotices(true);
552 SuppressHackArrCompatNotices::~SuppressHackArrCompatNotices() {
553 RID().setSuppressHackArrayCompatNotices(old
);
556 ///////////////////////////////////////////////////////////////////////////////