$v ) { if ( is_string( $k ) && in_array( strtolower( $k ), self::$sensitive_keys, true ) ) { $data[ $k ] = '***REDACTED***'; } else { $data[ $k ] = self::redact_data( $v ); } } } elseif ( is_object( $data ) ) { foreach ( get_object_vars( $data ) as $k => $v ) { if ( is_string( $k ) && in_array( strtolower( $k ), self::$sensitive_keys, true ) ) { $data->$k = '***REDACTED***'; } else { $data->$k = self::redact_data( $v ); } } } return $data; } /** * Sanitizes a message string for safe external transport/logging. * * @param string $message Raw message. * @return string Sanitized message. * @since 3.9.0 */ private static function sanitize_message( $message ) { if ( ! is_string( $message ) ) { return ''; } $message = \wp_strip_all_tags( $message ); // Normalize whitespace & limit extremely long messages to prevent log flooding. $message = preg_replace( '/\s+/u', ' ', $message ); if ( strlen( $message ) > 5000 ) { $message = substr( $message, 0, 5000 ) . '…[truncated]'; } return $message; } /** * Stores the original wp_die handles. * * @var callable|null * * @since 1.9.5 */ public static $original_wp_die_handler = null; /** * WP wp_die handler callback. * * @param callable $handler - The handler to call, this method will store the original and call it later on. * * @return callable * * @since 1.9.5 */ public static function wp_die_handler( $handler ) { self::$original_wp_die_handler = $handler; return array( __CLASS__, 'wp_die_handler_callback' ); } /** * Custom wp_die handler callback * * @param string $message - The message to display. * @param string $title - The title of the error. * @param array $args - Additional arguments for the error. * * @return callable|null * * @since 1.9.5 */ public static function wp_die_handler_callback( $message, $title = '', $args = array() ) { if ( ! empty( $message ) ) { [ $get_message, $get_title, $parsed_args ] = _wp_die_process_input( $message, $title, $args ); if ( is_string( $get_message ) && ! empty( $get_message ) ) { if ( ! empty( $parsed_args['additional_errors'] ) ) { $get_message = array_merge( array( $get_message ), \wp_list_pluck( $parsed_args['additional_errors'], 'message' ) ); $get_message = implode( ', ', $get_message ); } $get_message = str_replace( array( "\n", "\r" ), ' ', $get_message ); self::handle_error( \E_USER_NOTICE, \esc_html( $get_message ), '', '', null, 2, 0 ); } } // If the original handler is set, call it. if ( self::$original_wp_die_handler ) { return ( self::$original_wp_die_handler )( $message, $title, $args ); } } /** * Catches errors which come from the doing_it_wrong() function, WP core does not provide much information about what is really going on and where, this method adds some more information to the error log. * * @param bool $errno - Whether to trigger the error for _doing_it_wrong() calls. Default true. * @param string $errstr - The WP error string (message). * @param string $errfile - The name of the function that triggered the error (this is the WP function which is not called right, not the real function that actually called it). * @param string $errline - Since which WP version given error was added. * @param int $errcontext - The number of the error (type of the error - that probably never get set by WP and always falls to the default which is E_USER_NOTICE). * * @return bool * * @since 1.1.1 */ public static function handle_error( $errno, $errstr, $errfile, $errline, $errcontext = null, int $remove_from = 1, int $count_to = 1 ) { /* // if ( ! ( error_reporting(E_ERROR) & $errno ) ) { // This error code is not included in error_reporting, so let it fall. // through to the standard PHP error handler. // return false; // } */ $defaults = array( 'line' => '', 'file' => '', 'class' => '', 'function' => '', ); // $errfile = self::clean_file_path( $errfile ); $php_error_name = self::error_code_to_string( $errno ); $out = "PHP $php_error_name: $errstr" . PHP_EOL . 'Stack trace:' . PHP_EOL; $trace = debug_backtrace(); $main_shown = false; $thrown_file = ''; $thrown_line = ''; // skip current function and require() in /index.php . $counter = count( $trace ) - $count_to; for ( $i = $remove_from; $i < $counter; $i++ ) { $sf = (object) \shortcode_atts( $defaults, $trace[ $i ] ); $index = $i - 1; $file = $sf->file; // $file = self::clean_file_path( $sf->file ); if ( $remove_from === $i ) { $thrown_file = $file; $thrown_line = $sf->line; } $caller = ''; if ( ! empty( $sf->class ) && ! empty( $sf->function ) ) { $caller = $sf->class . '::' . $sf->function . '()'; } elseif ( ! empty( $sf->function ) ) { $caller = $sf->function . '()'; } else { $main_shown = true; $caller = '{main}'; } $out .= "#$index $file({$sf->line}): $caller" . PHP_EOL; } if ( ! $main_shown ) { $out .= '#' . ( ++$index ) . ' {main}' . PHP_EOL; } $out .= ' thrown in ' . $thrown_file . ' on line ' . $thrown_line; if ( WP_DEBUG_LOG || Settings::get_option( 'plugin_debug_enable' ) || Settings::get_option( 'keep_reading_error_log' ) ) { \error_log( $out ); } return true; } /** * Catches errors which come from the doing_it_wrong() function, WP core does not provide much information about what is really going on and where, this method adds some more information to the error log. * * @param bool $status - Whether to trigger the error for _doing_it_wrong() calls. Default true. * @param string $function_name - The name of the function that triggered the error (this is the WP function which is not called right, not the real function that actually called it). * @param string $errstr - The WP error string (message). * @param string $version - Since which WP version given error was added. * @param int $errno - The number of the error (type of the error - that probably never get set by WP and always falls to the default which is E_USER_NOTICE). * * @return bool * * @since 1.1.1 */ public static function trigger_error( $status, string $function_name, $errstr, $version, $errno = E_USER_NOTICE ) { if ( false === $status ) { return $status; } if ( Advanced_Analytics::is_just_in_time_for_0_day_domain( $function_name, $errstr ) ) { // This error code is not included in error_reporting, so let it fall. // through to the standard PHP error handler. return false; } /** * Shall we trigger that error or not - sending the error and message so others can check it. * * @param string $function_name - The name of the function that triggered the error (this is the WP function which is not called right, not the real function that actually called it). * @param string $errstr - The WP error string (message). * * @since 2.4.2.1 */ $shall_trigger = \apply_filters( ADVAN_PREFIX . 'trigger_error', true, $function_name, $errstr ); if ( false === $shall_trigger ) { return $status; } $php_error_name = self::error_code_to_string( $errno ); $out = "PHP $php_error_name: $errstr" . PHP_EOL . 'Stack trace:' . PHP_EOL; self::trace_log( $out ); return $status; } /** * Fires when a deprecated constructor is called. * * @param string $deprecated_name The class containing the deprecated constructor. * @param string $version The version of WordPress that deprecated the function. * @param string $parent_class The parent class calling the deprecated constructor. * * @since 2.4.2.1 */ public static function deprecated_constructor( $deprecated_name, $version, $parent_class ) { if ( empty( $deprecated_name ) ) { $deprecated_name = 'Unknown'; } if ( ! empty( $version ) ) { $version = ' as of version ' . $version; } if ( ! empty( $parent_class ) ) { $parent_class = '. Parent class: ' . $parent_class; } $php_error_name = 'DEPRECATED'; $out = "PHP $php_error_name: $deprecated_name is deprecated" . $version . $parent_class . PHP_EOL . 'Stack trace:' . PHP_EOL; self::trace_log( $out ); } /** * Catches errors which come from the doing_it_wrong() function, WP core does not provide much information about what is really going on and where, this method adds some more information to the error log. * * @param string $deprecated_name - Name of the deprecated. * @param string $replacement - What can be used as replacement. * @param string $version - Since which WP version given error was added. * @param string $message - A message regarding the change. * * @return void * * @since 1.1.1 */ public static function deprecated_error( string $deprecated_name, $replacement, $version, $message = '' ) { if ( empty( $deprecated_name ) ) { $deprecated_name = 'Unknown'; } if ( ! empty( $version ) ) { $version = ' as of version ' . $version; } if ( ! empty( $replacement ) ) { $replacement = '. Replacement: ' . $replacement; } if ( ! empty( $message ) ) { $message = '. Message: ' . $message; } $php_error_name = 'DEPRECATED'; $out = "PHP $php_error_name: $deprecated_name is deprecated" . $version . $replacement . $message . PHP_EOL . 'Stack trace:' . PHP_EOL; self::trace_log( $out ); } /** * Removes root path of WordPress from a given directory. * * @param string $path - The path string to strip from. * * @return string * * @since 1.1.0 */ private static function clean_file_path( $path ) { return str_replace( ABSPATH, '/', $path ); } /** * Gets the equivalent error code in string * * @param int $code - The code of the error. * * @return string * * @since 1.1.0 */ public static function error_code_to_string( $code ) { $errors = array( 1 => 'FATAL', // E_ERROR. 2 => 'WARNING', // E_WARNING. 4 => 'PARSE', // E_PARSE. 8 => 'NOTICE', // E_NOTICE. 16 => 'CORE ERROR', // E_CORE_ERROR. 32 => 'CORE WARNING', // E_CORE_WARNING. 64 => 'COMPILE ERROR', // E_COMPILE_ERROR. 128 => 'COMPILE WARNING', // E_COMPILE_WARNING. 256 => 'USER ERROR', // E_USER_ERROR. 512 => 'USER WARNING', // E_USER_WARNING. 1024 => 'USER NOTICE', // E_USER_NOTICE. 2048 => 'STRICT', // E_STRICT. 4096 => 'RECOVERABLE ERROR', // E_RECOVERABLE_ERROR. 8192 => 'DEPRECATED', // E_DEPRECATED. 16384 => 'USER DEPRECATED', // E_USER_DEPRECATED. 32767 => 'ALL ERRORS', // E_ALL. ); if ( isset( $errors[ $code ] ) ) { return $errors[ $code ]; } else { return 'UNKNOWN ERROR'; } } /** * Log REST API errors * * @param WP_REST_Response $result Result that will be sent to the client. * @param WP_REST_Server $server The API server instance. * @param WP_REST_Request $request The request used to generate the response. * * @since 1.9.3 */ public static function log_rest_api_errors( $result, $server, $request ) { // At `rest_post_dispatch` the result is normally a WP_REST_Response, but a // WP_Error can pass through unchanged (e.g. a failed permission check), and // only WP_REST_Response exposes is_error()/get_data(). Handle each type // explicitly and ignore anything else so we never call an undefined method. if ( $result instanceof \WP_REST_Response ) { if ( ! $result->is_error() ) { return $result; } $data = $result->get_data(); $code = ( is_array( $data ) && isset( $data['code'] ) ) ? self::sanitize_message( (string) $data['code'] ) : 'unknown_code'; $message = ( is_array( $data ) && isset( $data['message'] ) ) ? self::sanitize_message( (string) $data['message'] ) : 'unknown_message'; $data_dump = var_export( $data, true ); } elseif ( \is_wp_error( $result ) ) { $code = self::sanitize_message( (string) $result->get_error_code() ); $message = self::sanitize_message( (string) $result->get_error_message() ); $data_dump = var_export( $result->get_error_data(), true ); } else { // Not an error-capable result; nothing to log. return $result; } $params = self::redact_data( $request->get_params() ); // Do not leak potentially sensitive request params; keep a compact representation. $encoded_params = var_export( $params, true ); error_log( sprintf( 'REST API request: %s:', $request->get_route() ) . \PHP_EOL . $encoded_params ); error_log( sprintf( 'REST API %s: %s.', $code, $message ) . \PHP_EOL . $data_dump ); return $result; } /** * Shutdown function to handle errors. * * @since 1.0.0 * * @return void */ public static function shutdown() { $error = error_get_last(); if ( null !== $error && ( \in_array( $error['type'], array( 1, 4 ), true ) ) ) { $url = ''; if ( ! WP_Helper::is_multisite() ) { if ( ! function_exists( 'wp_generate_password' ) ) { require_once ABSPATH . WPINC . '/pluggable.php'; } $key_service = new \WP_Recovery_Mode_Key_Service(); $token = $key_service->generate_recovery_mode_token(); $key = $key_service->generate_and_store_recovery_mode_key( $token ); $url = \add_query_arg( array( 'action' => 'enter_recovery_mode', 'rm_token' => $token, 'rm_key' => $key, ), \wp_login_url() ); } $errno = $error['type']; $errfile = $error['file']; $errline = $error['line']; $errstr = $error['message']; $sanitized_errstr = self::sanitize_message( $errstr ); $public_path = self::clean_file_path( $errfile ); $notification_body = WP_Helper::get_blog_domain() . "\n" . self::error_code_to_string( $errno ) . ' ' . $sanitized_errstr . ' ' . $public_path . ' ' . (int) $errline . "\n" . $url; if ( \class_exists( '\ADVAN\Controllers\Slack' ) && Slack::is_set() ) { // Send sanitized error to Slack (without sensitive recovery token). Slack_API::send_slack_message_via_api( null, null, $notification_body ); } if ( \class_exists( '\ADVAN\Controllers\Telegram' ) && Telegram::is_set() ) { // Send sanitized error to Telegram. Telegram_API::send_telegram_message_via_api( null, null, $notification_body ); } if ( Settings::get_option( 'fatals_module_enabled' ) ) { WP_Fatals_Entity::prepare_fatality_to_store( array( 'message' => $error['message'], 'file' => $error['file'], 'line' => $error['line'], 'severity' => self::error_code_to_string( $error['type'] ), 'backtrace' => '', ) ); } } elseif ( null !== $error ) { if ( Settings::get_option( 'fatals_module_enabled' ) ) { WP_Fatals_Entity::prepare_fatality_to_store( array( 'message' => $error['message'], 'file' => $error['file'], 'line' => $error['line'], 'severity' => self::error_code_to_string( $error['type'] ), 'backtrace' => '', ) ); } } } /** * Uncaught error handler. * * @param Throwable $e - The error or exception. * * @return void * * @since 1.8.4 */ public static function exception_handler( $e ) { $error = 'Uncaught Error'; if ( $e instanceof \Exception ) { $error = 'Uncaught Exception'; } $url = ''; if ( ! WP_Helper::is_multisite() ) { if ( ! function_exists( 'wp_generate_password' ) ) { require_once ABSPATH . WPINC . '/pluggable.php'; } $key_service = new \WP_Recovery_Mode_Key_Service(); $token = $key_service->generate_recovery_mode_token(); $key = $key_service->generate_and_store_recovery_mode_key( $token ); $url = \add_query_arg( array( 'action' => 'enter_recovery_mode', 'rm_token' => $token, 'rm_key' => $key, ), \wp_login_url() ); } $sanitized_message = self::sanitize_message( $e->getMessage() ); $public_path = self::clean_file_path( $e->getFile() ); $notification_body = WP_Helper::get_blog_domain() . "\n" . $error . ' ' . $sanitized_message . ' ' . $public_path . ' ' . (int) $e->getLine() . "\n" . $url; if ( \class_exists( '\ADVAN\Controllers\Slack' ) && Slack::is_set() ) { // Send sanitized error to Slack. Slack_API::send_slack_message_via_api( null, null, $notification_body ); } if ( \class_exists( '\ADVAN\Controllers\Telegram' ) && Telegram::is_set() ) { // Send sanitized error to Telegram. Telegram_API::send_telegram_message_via_api( null, null, $notification_body ); } $main_shown = false; $previous = $e->getPrevious(); $out = sprintf( 'PHP FATAL: %s in %s on line %d', $e->getMessage(), $e->getFile(), $e->getLine(), ); if ( $previous instanceof \Error || $previous instanceof \Exception ) { $out .= ' (Previous Error: ' . $previous->getMessage() . ' in ' . $previous->getFile() . ' on line ' . $previous->getLine() . ')'; } $out .= PHP_EOL . 'Stack trace:' . PHP_EOL; $defaults = array( 'line' => '', 'file' => '', 'class' => '', 'function' => '', ); $counter = count( $e->getTrace() ); $index = 0; for ( $i = 0; $i < $counter; $i++ ) { $sf = (object) \shortcode_atts( $defaults, $e->getTrace()[ $i ] ); $index = $i; $file = $sf->file; if ( 1 === $i ) { $thrown_file = $file; $thrown_line = $sf->line; } $caller = ''; if ( ! empty( $sf->class ) && ! empty( $sf->function ) ) { $caller = $sf->class . '::' . $sf->function . '()'; } elseif ( ! empty( $sf->function ) ) { $caller = $sf->function . '()'; } else { $main_shown = true; $caller = '{main}'; } $trc = $e->getTrace(); if ( ! $main_shown && isset( $trc[ $i + 3 ]['args'] ) && ! empty( $trc[ $i + 3 ]['args'] ) ) { $args = ' Arguments ' . \htmlentities( (string) \wp_json_encode( $trc[ $i + 3 ]['args'] ) ); } else { $args = ''; } $out .= "#$index $file({$sf->line}): $caller $args" . PHP_EOL; } if ( $previous ) { if ( ! ( $previous instanceof \Error ) && ! ( $previous instanceof \Exception ) ) { if ( ! $main_shown ) { $out .= '#' . ( ++$index ) . ' {main}' . PHP_EOL; } } $out .= ' thrown in ' . $thrown_file . ' on line ' . $thrown_line; if ( $previous instanceof \Error || $previous instanceof \Exception ) { $main_shown = false; $out .= PHP_EOL . sprintf( 'Previous Error: %s in %s(%d)', $previous->getMessage(), $previous->getFile(), $previous->getLine(), ); $out .= PHP_EOL . 'Stack trace:' . PHP_EOL; $defaults = array( 'line' => '', 'file' => '', 'class' => '', 'function' => '', ); $counter = count( $previous->getTrace() ); for ( $i = 0; $i < $counter; $i++ ) { $sf = (object) \shortcode_atts( $defaults, $previous->getTrace()[ $i ] ); $index = $i; $file = $sf->file; if ( 1 === $i ) { $thrown_file = $file; $thrown_line = $sf->line; } $caller = ''; if ( ! empty( $sf->class ) && ! empty( $sf->function ) ) { $caller = $sf->class . '::' . $sf->function . '()'; } elseif ( ! empty( $sf->function ) ) { $caller = $sf->function . '()'; } else { $main_shown = true; $caller = '{main}'; } $trc = $previous->getTrace(); if ( ! $main_shown && isset( $trc[ $i + 3 ]['args'] ) && ! empty( $trc[ $i + 3 ]['args'] ) ) { $args = ' Arguments ' . \htmlentities( (string) \wp_json_encode( $trc[ $i + 3 ]['args'] ) ); } else { $args = ''; } $out .= "#$index $file({$sf->line}): $caller $args" . PHP_EOL; } if ( ! $main_shown ) { $out .= '#' . ( ++$index ) . ' {main}' . PHP_EOL; } $out .= ' thrown in ' . $thrown_file . ' on line ' . $thrown_line; } } if ( WP_DEBUG_LOG || Settings::get_option( 'plugin_debug_enable' ) || Settings::get_option( 'keep_reading_error_log' ) ) { \error_log( $out ); } if ( Settings::get_option( 'fatals_module_enabled' ) ) { WP_Fatals_Entity::prepare_fatality_to_store( array( 'message' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine(), 'severity' => 'FATAL', 'backtrace' => $e->getTrace(), ) ); } \restore_exception_handler(); } /** * Triggers on mail error * * @param \WP_error $wp_error - The actual error. * * @return void * * @since 2.1.0 */ public static function on_mail_error( $wp_error ) { if ( \is_wp_error( $wp_error ) ) { self::log_wp_error( $wp_error ); } } /** * Logs error of type WP_Error in the error log * * @param \WP_Error $error - The error to log. * @param string $additional_message - More to add to the message. * * @return void * * @since 2.1.0 */ public static function log_wp_error( \WP_Error $error, ?string $additional_message = '' ) { $error_data = $error->get_all_error_data(); \array_walk_recursive( $error_data, function( &$leaf ) { if ( is_string( $leaf ) ) { $leaf = \esc_html( $leaf ); } } ); if ( empty( $error_data ) ) { $error_data = ''; } else { $error_data = var_export( $error_data, true ); } $out = sprintf( 'WP_Error error: %s: %s', self::sanitize_message( (string) $error->get_error_code() ), self::sanitize_message( (string) $error->get_error_message() ), ) . \PHP_EOL . $error_data . self::sanitize_message( (string) $additional_message ) . PHP_EOL . 'Stack trace:' . PHP_EOL; self::trace_log( $out ); // $trace = wp_debug_backtrace_summary('\ADVAN\Helpers\WP_Error_Handler', 8, false); // error_log( // sprintf( // 'WP_Error error: %s: %s.', // $error->get_error_code(), // $error->get_error_message().$additional_message, // ) . \PHP_EOL . // $error_data, // ); } /** * Writes a stack trace to the error log. * * @param string $out - String with the lead error log line. * @param bool $just_return - Whether to just return the trace string instead of logging it. * * @return string * * @since 2.4.2.1 * @since 3.8.0 - Added just return the output instead of logging it. */ private static function trace_log( string $out, bool $just_return = false ) { // These are default values for a single trace. // To prevent errors when a trace ommits some values. $defaults = array( 'line' => '', 'file' => '', 'class' => '', 'function' => '', ); $trace = ( new \Exception( '' ) )->getTrace(); $main_shown = false; $thrown_file = ''; $thrown_line = ''; $args = ''; $index = 0; $how_back = 3; // skip current function and require() in /index.php . $counter = count( $trace ) - $how_back; if ( $counter <= 0 ) { // Looks like WE are the source - act accordingly. $counter = count( $trace ); $how_back = 0; } for ( $i = 1; $i < $counter; $i++ ) { $sf = (object) shortcode_atts( $defaults, $trace[ $i + $how_back ] ); $index = $i - 1; $file = $sf->file; // $file = self::clean_file_path( $sf->file ); if ( 1 === $i ) { $thrown_file = $file; $thrown_line = $sf->line; } $caller = ''; if ( ! empty( $sf->class ) && ! empty( $sf->function ) ) { $caller = $sf->class . '::' . $sf->function . '()'; } elseif ( ! empty( $sf->function ) ) { $caller = $sf->function . '()'; } else { $main_shown = true; $caller = '{main}'; } if ( ! $main_shown && isset( $trace[ $i + $how_back ]['args'] ) && ! empty( $trace[ $i + $how_back ]['args'] ) ) { $args = ' Arguments ' . \htmlentities( \wp_json_encode( $trace[ $i + $how_back ]['args'] ) ); } else { $args = ''; } $out .= "#$index $file({$sf->line}): $caller $args" . PHP_EOL; } if ( ! $main_shown ) { $out .= '#' . ( ++$index ) . ' {main}' . PHP_EOL; } $out .= ' thrown in ' . $thrown_file . ' on line ' . $thrown_line; if ( $just_return ) { return $out; } if ( WP_DEBUG_LOG || Settings::get_option( 'plugin_debug_enable' ) || Settings::get_option( 'keep_reading_error_log' ) ) { \error_log( $out ); } } /** * Fires after an HTTP API response is received and before the response is returned. * * @param array|\WP_Error $response HTTP response or WP_Error object. * @param string $context Context under which the hook is fired. * @param string $class HTTP transport used. * @param array $parsed_args HTTP request arguments. * @param string $url The request URL. * * @since 2.7.0 */ public static function capture_request( $response, $context, $class, $parsed_args, $url ) { // Check if the response is an error. if ( \is_wp_error( $response ) ) { unset( $parsed_args['_info'] ); unset( $parsed_args['_redirection'] ); self::log_wp_error( $response, sprintf( // translators: %1$s is the URL of the request, %2$s are the arguments of the request. __( ' HTTP API request: %1$s. Arguments: %2$s', '0-day-analytics' ), $url, \implode( "\n", \array_map( function( $key, $value ) { return sprintf( '%s: %s', $key, \is_array( $value ) ? \wp_json_encode( $value ) : var_export( $value, true ) ); }, \array_keys( $parsed_args ), \array_values( $parsed_args ) ) ), ) ); } } /** * Action for _doing_it_wrong() calls. * * @param string $function_name The function that was called. * @param string $message A message explaining what has been done incorrectly. * @param string $version The version of WordPress where the message was added. * * @return void * * @since 1.9.2.2 */ public static function action_doing_it_wrong_run( $function_name, $message, $version ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed if ( Advanced_Analytics::is_just_in_time_for_0_day_domain( $function_name, $message ) ) { // This error code is not included in error_reporting, so let it fall. // through to the standard PHP error handler. return; } /** * Shall we trigger that error or not - sending the error and message so others can check it. * * @param string $function_name - The name of the function that triggered the error (this is the WP function which is not called right, not the real function that actually called it). * @param string $message - The WP error string (message). * @param string $message - The WP eversion when that was originally added. * * @since 3.3.1 */ $shall_trigger = \apply_filters( ADVAN_PREFIX . 'trigger_error_doing_it_wrong', true, $function_name, $message, $version ); if ( $shall_trigger ) { $function_name = (string) $function_name; $message = (string) $message; $out = sprintf( 'Doing it wrong run: %s: %s', $function_name, $message, ) . \PHP_EOL . 'As of version: ' . $version . PHP_EOL . 'Stack trace:' . PHP_EOL; self::trace_log( $out ); } } } }