prod/native/libphpbridge/code/Exceptions.cpp (97 lines of code) (raw):

/* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch B.V. licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include "PhpBridge.h" #include "Helpers.h" #include <Zend/zend_exceptions.h> #include <Zend/zend_globals.h> #include <Zend/zend_types.h> #include <optional> namespace elasticapm::php { using namespace std::literals; SavedException saveExceptionState() { SavedException savedException; savedException.exception = EG(exception); savedException.prev_exception = EG(prev_exception); savedException.opline_before_exception = EG(opline_before_exception); EG(exception) = nullptr; EG(prev_exception) = nullptr; EG(opline_before_exception) = nullptr; if (EG(current_execute_data)) { savedException.opline = EG(current_execute_data)->opline; } return savedException; } void restoreExceptionState(SavedException savedException) { EG(exception) = savedException.exception; EG(prev_exception) = savedException.prev_exception; EG(opline_before_exception) = savedException.opline_before_exception; if (EG(current_execute_data) && savedException.opline.has_value()) { EG(current_execute_data)->opline = savedException.opline.value(); } } std::optional<std::string_view> getExceptionMessage(zend_object *exception) { return zvalToOptionalStringView(getClassPropertyValue(exception->ce, exception, "message"sv)); } std::optional<std::string_view> getExceptionFileName(zend_object *exception) { return zvalToOptionalStringView(getClassPropertyValue(exception->ce, exception, "file"sv)); } std::optional<long> getExceptionLine(zend_object *exception) { auto value = getClassPropertyValue(exception->ce, exception, "line"sv); if (Z_TYPE_P(value) == IS_LONG) { return Z_LVAL_P(value); } return -1; } std::optional<long> getExceptionCode(zend_object *exception) { auto value = getClassPropertyValue(exception->ce, exception, "code"sv); if (Z_TYPE_P(value) == IS_LONG) { return Z_LVAL_P(value); } return std::nullopt; } std::optional<std::string_view> getExceptionClass(zend_object *exception) { return zvalToOptionalStringView(getClassPropertyValue(exception->ce, exception, "class"sv)); } std::optional<std::string_view> getExceptionFunction(zend_object *exception) { return zvalToOptionalStringView(getClassPropertyValue(exception->ce, exception, "function"sv)); } std::optional<std::string_view> getExceptionStringStackTrace(zend_object *exception) { return zvalToOptionalStringView(getClassPropertyValue(exception->ce, exception, "string"sv)); } std::string_view getExceptionName(zend_object *exception) { zend_string *str = exception->handlers->get_class_name(exception); if (!str) { return {}; } return {ZSTR_VAL(str), ZSTR_LEN(str)}; } std::string exceptionToString(zend_object *exception) { if (!exception || !instanceof_function(exception->ce, zend_ce_throwable)) { return {}; } std::stringstream msg; auto exceptionClass = getExceptionClass(exception); auto exceptionName = getExceptionName(exception); msg << exceptionClass.value_or(exceptionName); msg << " thrown"sv; auto message = getExceptionMessage(exception); if (message.has_value()) { msg << " with message '"sv << *message << "'"; } auto code = getExceptionCode(exception); if (code.has_value() && *code != 0) { msg << " with code "sv << *code; } auto fileName = getExceptionFileName(exception); if (fileName.has_value()) { msg << " in "sv << *fileName; auto line = getExceptionLine(exception); if (line.has_value()) { msg << ":"sv << *line; } } auto stack = getExceptionStringStackTrace(exception); if (stack.has_value() && !stack.value().empty()) { msg << " stacktrace: '"sv << *stack << "'"; } return msg.str(); } }