Move io_tests to folly/io/async/test
[hiphop-php.git] / hphp / runtime / ext / std / ext_std_errorfunc.cpp
blobd91d6883797be732fc89eadae92648ad782c2af9
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1997-2010 The PHP Group |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
17 #include "hphp/runtime/ext/std/ext_std_errorfunc.h"
19 #include <iostream>
21 #include <folly/Format.h>
22 #include <folly/Likely.h>
23 #include <folly/Random.h>
25 #include "hphp/runtime/base/array-init.h"
26 #include "hphp/runtime/base/array-iterator.h"
27 #include "hphp/runtime/base/backtrace.h"
28 #include "hphp/runtime/base/bespoke-runtime.h"
29 #include "hphp/runtime/base/builtin-functions.h"
30 #include "hphp/runtime/base/exceptions.h"
31 #include "hphp/runtime/base/execution-context.h"
32 #include "hphp/runtime/base/string-buffer.h"
33 #include "hphp/runtime/base/request-info.h"
34 #include "hphp/runtime/base/backtrace.h"
35 #include "hphp/runtime/ext/std/ext_std_file.h"
36 #include "hphp/util/logger.h"
38 namespace HPHP {
40 ///////////////////////////////////////////////////////////////////////////////
42 const StaticString
43 s_file("file"),
44 s_line("line"),
45 s_function("function"),
46 s_class("class"),
47 s_type("type"),
48 s_args("args"),
49 s_message("message"),
50 s_call_user_func("call_user_func"),
51 s_call_user_func_array("call_user_func_array"),
52 s_hphp_debug_caller_info("hphp_debug_caller_info");
54 ///////////////////////////////////////////////////////////////////////////////
56 const int64_t k_DEBUG_BACKTRACE_PROVIDE_OBJECT = (1 << 0);
57 const int64_t k_DEBUG_BACKTRACE_IGNORE_ARGS = (1 << 1);
58 const int64_t k_DEBUG_BACKTRACE_PROVIDE_METADATA = (1 << 16);
60 const int64_t k_DEBUG_BACKTRACE_HASH_CONSIDER_METADATA = (1 << 0);
63 Array HHVM_FUNCTION(debug_backtrace, int64_t options /* = 1 */,
64 int64_t limit /* = 0 */) {
65 bool provide_object = options & k_DEBUG_BACKTRACE_PROVIDE_OBJECT;
66 bool provide_metadata = options & k_DEBUG_BACKTRACE_PROVIDE_METADATA;
67 bool ignore_args = options & k_DEBUG_BACKTRACE_IGNORE_ARGS;
68 return createBacktrace(BacktraceArgs()
69 .withThis(provide_object)
70 .withMetadata(provide_metadata)
71 .ignoreArgs(ignore_args)
72 .setLimit(limit));
75 ArrayData* debug_backtrace_jit(int64_t options) {
76 return HHVM_FN(debug_backtrace)(options).detach();
79 bool hphp_debug_caller_info_impl(
80 Array& result, bool& skipped, const Func* func, Offset offset) {
81 if (!skipped && func->isSkipFrame()) return false;
82 if (!skipped) {
83 skipped = true;
84 return false;
87 if (func->name() == s_call_user_func.get()) return false;
88 if (func->name() == s_call_user_func_array.get()) return false;
90 auto const line = func->getLineNumber(offset);
91 if (line == -1) return false;
93 static constexpr auto s_file_idx = 0;
94 static constexpr auto s_line_idx = 1;
95 static constexpr auto s_class_idx = 2;
96 static constexpr auto s_function_idx = 3;
97 static const RuntimeStruct::FieldIndexVector s_fields = {
98 {s_file_idx, s_file},
99 {s_line_idx, s_line},
100 {s_class_idx, s_class},
101 {s_function_idx, s_function},
103 static auto const s_struct =
104 RuntimeStruct::registerRuntimeStruct(s_hphp_debug_caller_info, s_fields);
106 auto const cls = func->cls();
107 auto const path = func->originalFilename() ?
108 func->originalFilename() : func->unit()->filepath();
110 auto const has_cls = cls && !func->isClosureBody();
111 StructDictInit init(s_struct, has_cls ? 4 : 3);
112 if (has_cls) {
113 init.set(s_class_idx, s_class,
114 Variant(VarNR(const_cast<StringData*>(cls->name()))));
116 init.set(s_file_idx, s_file,
117 Variant(VarNR(const_cast<StringData*>(path))));
118 init.set(s_function_idx, s_function, func->nameWithClosureName());
119 init.set(s_line_idx, s_line, line);
120 result = init.toArray();
121 return true;
125 * hphp_debug_caller_info - returns an array of info about the "caller"
127 * For clarity, we refer to the function that called hphp_debug_caller_info()
128 * as the "callee", and we refer to the function that called the callee as
129 * the "caller".
131 * This function returns an array containing keys "file", "function", "line" and
132 * optionally "class" which indicate the filename, function, line number and
133 * class name (if in class context) where the "caller" called the "callee".
135 ArrayRet HHVM_FUNCTION(hphp_debug_caller_info) {
136 Array result = empty_dict_array();
137 bool skipped = false;
138 walkStack([&] (const BTFrame& frm) {
139 return hphp_debug_caller_info_impl(
140 result, skipped, frm.func(), frm.bcOff());
142 return result;
146 bool hphp_debug_caller_identifier_impl(
147 String& result, bool& skipped, const Func* func) {
148 if (!skipped && func->isSkipFrame()) return false;
149 if (!skipped) {
150 skipped = true;
151 return false;
154 if (func->name()->fsame(s_call_user_func.get())) return false;
155 if (func->name()->fsame(s_call_user_func_array.get())) return false;
157 result = func->fullNameWithClosureName();
158 return true;
162 * hphp_debug_caller_identifier - returns the full function name of
163 * the "caller"
165 * For clarity, we refer to the function that called
166 * hphp_debug_caller_identifier() as the "callee", and we refer to the
167 * function that called the callee as the "caller".
169 String HHVM_FUNCTION(hphp_debug_caller_identifier) {
170 String result = empty_string();
171 bool skipped = false;
172 walkStack([&] (const BTFrame& frm) {
173 return hphp_debug_caller_identifier_impl(
174 result, skipped, frm.func());
176 return result;
179 int64_t HHVM_FUNCTION(hphp_debug_backtrace_hash, int64_t options /* = 0 */) {
180 return createBacktraceHash(
181 options & k_DEBUG_BACKTRACE_HASH_CONSIDER_METADATA
185 void HHVM_FUNCTION(debug_print_backtrace, int64_t options /* = 0 */,
186 int64_t limit /* = 0 */) {
187 bool ignore_args = options & k_DEBUG_BACKTRACE_IGNORE_ARGS;
188 g_context->write(debug_string_backtrace(false, ignore_args, limit));
191 String stringify_backtrace(const Array& bt, bool ignore_args) {
192 StringBuffer buf;
193 int i = 0;
194 for (ArrayIter it = bt.begin(); !it.end(); it.next(), i++) {
195 Array frame = it.second().toArray();
196 buf.append('#');
197 buf.append(i);
198 if (i < 10) buf.append(' ');
199 buf.append(' ');
200 if (frame.exists(s_class)) {
201 buf.append(tvCastToString(frame->get(s_class)));
202 buf.append(tvCastToString(frame->get(s_type)));
204 buf.append(tvCastToString(frame->get(s_function)));
205 buf.append("(");
206 if (!ignore_args) {
207 bool first = true;
208 for (ArrayIter argsIt(tvCastToArrayLike(frame->get(s_args)));
209 !argsIt.end();
210 argsIt.next()) {
211 if (!first) {
212 buf.append(", ");
213 } else {
214 first = false;
216 try {
217 buf.append(argsIt.second().toString());
218 } catch (FatalErrorException& fe) {
219 buf.append(fe.getMessage());
223 buf.append(")");
224 if (frame.exists(s_file)) {
225 buf.append(" called at [");
226 buf.append(tvCastToString(frame->get(s_file)));
227 buf.append(':');
228 buf.append(tvCastToString(frame->get(s_line)));
229 buf.append(']');
231 buf.append('\n');
233 return buf.detach();
236 String debug_string_backtrace(bool skip, bool ignore_args /* = false */,
237 int64_t limit /* = 0 */) {
238 Array bt;
239 bt = createBacktrace(BacktraceArgs()
240 .skipTop(skip)
241 .ignoreArgs(ignore_args)
242 .setLimit(limit));
243 return stringify_backtrace(bt, ignore_args);
246 Array HHVM_FUNCTION(error_get_last) {
247 String lastError = g_context->getLastError();
248 if (lastError.isNull()) {
249 return null_array;
251 return make_dict_array(
252 s_type, g_context->getLastErrorNumber(),
253 s_message, g_context->getLastError(),
254 s_file, g_context->getLastErrorPath(),
255 s_line, g_context->getLastErrorLine()
259 bool HHVM_FUNCTION(error_log, const String& message, int64_t message_type /* = 0 */,
260 const Variant& destination /* = null */,
261 const Variant& /*extra_headers*/ /* = null */) {
262 // error_log() should not invoke the user error handler,
263 // so we use Logger::Error() instead of raise_warning() or raise_error()
264 switch (message_type) {
265 case 0:
267 std::string line(message.data(),
268 // Truncate to 512k
269 message.size() > (1<<19) ? (1<<19) : message.size());
270 Logger::Error(line);
271 return true;
273 case 3:
275 // open for append only
276 auto outfile = HHVM_FN(fopen)(destination.toString(), "a");
277 if (outfile.isNull()) {
278 Logger::Error("can't open error_log file!\n");
279 return false;
281 HHVM_FN(fwrite)(outfile.toResource(), message);
282 HHVM_FN(fclose)(outfile.toResource());
283 return true;
285 case 2: // not used per PHP
286 default:
287 Logger::Error("error_log does not support message_type %ld!", message_type);
288 break;
290 return false;
293 int64_t HHVM_FUNCTION(error_reporting, const Variant& level /* = null */) {
294 auto& id = RequestInfo::s_requestInfo.getNoCheck()->m_reqInjectionData;
295 int oldErrorReportingLevel = id.getErrorReportingLevel();
296 if (!level.isNull()) {
297 id.setErrorReportingLevel((int)level.toInt64());
299 return oldErrorReportingLevel;
302 bool HHVM_FUNCTION(restore_error_handler) {
303 g_context->popUserErrorHandler();
304 return true;
307 bool HHVM_FUNCTION(restore_exception_handler) {
308 g_context->popUserExceptionHandler();
309 return true;
312 Variant HHVM_FUNCTION(set_error_handler, const Variant& error_handler,
313 int64_t error_types /* = ErrorMode::PHP_ALL | STRICT */) {
314 if (!is_null(error_handler.asTypedValue())) {
315 return g_context->pushUserErrorHandler(error_handler, error_types);
316 } else {
317 g_context->clearUserErrorHandlers();
318 return init_null_variant;
322 Variant HHVM_FUNCTION(set_exception_handler, const Variant& exception_handler) {
323 return g_context->pushUserExceptionHandler(exception_handler);
326 void HHVM_FUNCTION(hphp_set_error_page, const String& page) {
327 g_context->setErrorPage(page);
330 void HHVM_FUNCTION(hphp_throw_fatal_error, const String& error_msg) {
331 std::string msg = error_msg.data();
332 raise_error(msg);
335 void HHVM_FUNCTION(hphp_clear_unflushed) {
336 g_context->obEndAll();
337 g_context->obStart();
338 g_context->obProtect(true);
341 bool HHVM_FUNCTION(trigger_error, const String& error_msg,
342 int64_t error_type /* = ErrorMode::USER_NOTICE */) {
343 std::string msg = error_msg.data(); // not toCppString()
344 if (UNLIKELY(g_context->getThrowAllErrors())) {
345 throw Exception(folly::sformat("throwAllErrors: {}", error_type));
347 if (error_type == (int)ErrorMode::USER_ERROR) {
348 g_context->handleError(msg, error_type, true,
349 ExecutionContext::ErrorThrowMode::IfUnhandled,
350 "\nFatal error: ");
351 return true;
353 if (error_type == (int)ErrorMode::USER_WARNING) {
354 g_context->handleError(msg, error_type, true,
355 ExecutionContext::ErrorThrowMode::Never,
356 "\nWarning: ");
357 return true;
359 if (error_type == (int)ErrorMode::USER_NOTICE) {
360 g_context->handleError(msg, error_type, true,
361 ExecutionContext::ErrorThrowMode::Never,
362 "\nNotice: ");
363 return true;
365 if (error_type == (int)ErrorMode::USER_DEPRECATED) {
366 g_context->handleError(msg, error_type, true,
367 ExecutionContext::ErrorThrowMode::Never,
368 "\nDeprecated: ");
369 return true;
371 if (error_type == (int)ErrorMode::STRICT) {
372 // So that we can raise strict warnings for mismatched
373 // params in FCallBuiltin
374 raise_strict_warning(msg);
375 return true;
378 auto const f = fromCaller(
379 [] (const BTFrame& frm) { return frm.func(); }
382 if (f && f->isBuiltin()) {
383 if (error_type == (int)ErrorMode::ERROR) {
384 raise_error_without_first_frame(msg);
385 return true;
387 if (error_type == (int)ErrorMode::WARNING) {
388 raise_warning_without_first_frame(msg);
389 return true;
391 if (error_type == (int)ErrorMode::NOTICE) {
392 raise_notice_without_first_frame(msg);
393 return true;
395 if (error_type == (int)ErrorMode::PHP_DEPRECATED) {
396 raise_deprecated_without_first_frame(msg);
397 return true;
399 if (error_type == (int)ErrorMode::RECOVERABLE_ERROR) {
400 raise_recoverable_error_without_first_frame(msg);
401 return true;
404 raise_warning("Invalid error type specified");
405 return false;
408 bool HHVM_FUNCTION(trigger_sampled_error, const String& error_msg,
409 int64_t sample_rate,
410 int64_t error_type /* = (int)ErrorMode::USER_NOTICE */) {
411 if (!folly::Random::oneIn(sample_rate)) {
412 return true;
414 return HHVM_FN(trigger_error)(error_msg, error_type);
417 bool HHVM_FUNCTION(user_error, const String& error_msg,
418 int64_t error_type /* = (int)ErrorMode::USER_NOTICE */) {
419 return HHVM_FN(trigger_error)(error_msg, error_type);
422 ///////////////////////////////////////////////////////////////////////////////
424 Array HHVM_FUNCTION(HH_deferred_errors) {
425 return g_context->releaseDeferredErrors();
428 ///////////////////////////////////////////////////////////////////////////////
430 void StandardExtension::registerNativeErrorFunc() {
431 HHVM_FE(debug_backtrace);
432 HHVM_FE(hphp_debug_caller_info);
433 HHVM_FE(hphp_debug_caller_identifier);
434 HHVM_FE(hphp_debug_backtrace_hash);
435 HHVM_FE(debug_print_backtrace);
436 HHVM_FE(error_get_last);
437 HHVM_FE(error_log);
438 HHVM_FE(error_reporting);
439 HHVM_FE(restore_error_handler);
440 HHVM_FE(restore_exception_handler);
441 HHVM_FE(set_error_handler);
442 HHVM_FE(set_exception_handler);
443 HHVM_FE(hphp_set_error_page);
444 HHVM_FE(hphp_throw_fatal_error);
445 HHVM_FE(hphp_clear_unflushed);
446 HHVM_FE(trigger_error);
447 HHVM_FE(trigger_sampled_error);
448 HHVM_FE(user_error);
449 HHVM_FALIAS(HH\\deferred_errors, HH_deferred_errors);
450 HHVM_RC_INT(DEBUG_BACKTRACE_PROVIDE_OBJECT, k_DEBUG_BACKTRACE_PROVIDE_OBJECT);
451 HHVM_RC_INT(DEBUG_BACKTRACE_IGNORE_ARGS, k_DEBUG_BACKTRACE_IGNORE_ARGS);
452 HHVM_RC_INT(DEBUG_BACKTRACE_PROVIDE_METADATA,
453 k_DEBUG_BACKTRACE_PROVIDE_METADATA);
454 HHVM_RC_INT(DEBUG_BACKTRACE_HASH_CONSIDER_METADATA,
455 k_DEBUG_BACKTRACE_HASH_CONSIDER_METADATA);
456 HHVM_RC_INT(E_ERROR, (int)ErrorMode::ERROR);
457 HHVM_RC_INT(E_WARNING, (int)ErrorMode::WARNING);
458 HHVM_RC_INT(E_PARSE, (int)ErrorMode::PARSE);
459 HHVM_RC_INT(E_NOTICE, (int)ErrorMode::NOTICE);
460 HHVM_RC_INT(E_CORE_ERROR, (int)ErrorMode::CORE_ERROR);
461 HHVM_RC_INT(E_CORE_WARNING, (int)ErrorMode::CORE_WARNING);
462 HHVM_RC_INT(E_COMPILE_ERROR, (int)ErrorMode::COMPILE_ERROR);
463 HHVM_RC_INT(E_COMPILE_WARNING, (int)ErrorMode::COMPILE_WARNING);
464 HHVM_RC_INT(E_USER_ERROR, (int)ErrorMode::USER_ERROR);
465 HHVM_RC_INT(E_USER_WARNING, (int)ErrorMode::USER_WARNING);
466 HHVM_RC_INT(E_USER_NOTICE, (int)ErrorMode::USER_NOTICE);
467 HHVM_RC_INT(E_STRICT, (int)ErrorMode::STRICT);
468 HHVM_RC_INT(E_RECOVERABLE_ERROR, (int)ErrorMode::RECOVERABLE_ERROR);
469 HHVM_RC_INT(E_DEPRECATED, (int)ErrorMode::PHP_DEPRECATED);
470 HHVM_RC_INT(E_USER_DEPRECATED, (int)ErrorMode::USER_DEPRECATED);
471 HHVM_RC_INT(E_ALL, (int)ErrorMode::PHP_ALL | (int)ErrorMode::STRICT);
473 HHVM_RC_INT(E_HHVM_FATAL_ERROR, (int)ErrorMode::FATAL_ERROR);